diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..26fb67037 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*.{py,pyi,rst,md,yml,yaml,toml,json}] +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space + +[*.{py,pyi,toml,json}] +indent_size = 4 + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.flake8 b/.flake8 new file mode 100644 index 000000000..13a85deb9 --- /dev/null +++ b/.flake8 @@ -0,0 +1,12 @@ +[flake8] + +max-line-length = 90 +exclude = conformance +ignore = + # irrelevant plugins + B3, + DW12, + # code is sometimes better without this + E129, + # consistency with mypy + W504 diff --git a/.flake8-tests b/.flake8-tests deleted file mode 100644 index 06b437dbf..000000000 --- a/.flake8-tests +++ /dev/null @@ -1,27 +0,0 @@ -# This configuration is specific to test_*.py; you need to invoke it -# by specifically naming this config, like this: -# -# $ flake8 --config=.flake8-tests src/test_typing.py python2/test_typing.py -# -# This will be possibly merged in the future. - -[flake8] -# fake builtins for python2/* -builtins = basestring, unicode -max-line-length = 100 -ignore = - # temporary ignores until we sort it out - E306, - E701, - E704, - F811, - # irrelevant plugins - B3, - DW12 - # consistency with mypy - W504 -exclude = - # This config is NOT for the main module. - setup.py - python2/typing.py, - src/typing.py diff --git a/.github/ISSUE_TEMPLATE/documentation-issue.md b/.github/ISSUE_TEMPLATE/documentation-issue.md new file mode 100644 index 000000000..e8d7bc938 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation-issue.md @@ -0,0 +1,10 @@ +--- +name: Documentation issue +about: Report a problem or suggest changes for the documentation at https://typing.python.org/ +title: '' +labels: 'topic: documentation' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/new-typing-feature.md b/.github/ISSUE_TEMPLATE/new-typing-feature.md new file mode 100644 index 000000000..733df29ea --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-typing-feature.md @@ -0,0 +1,10 @@ +--- +name: New typing feature +about: Suggest a new feature for Python's typing system +title: '' +labels: 'topic: feature' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/other-issue.md b/.github/ISSUE_TEMPLATE/other-issue.md new file mode 100644 index 000000000..484282c71 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other-issue.md @@ -0,0 +1,10 @@ +--- +name: Other issue +about: Report any other issue +title: '' +labels: 'topic: other' +assignees: '' + +--- + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..5c5631448 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: monthly + groups: + actions: + patterns: + - "*" diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml new file mode 100644 index 000000000..8624fa73a --- /dev/null +++ b/.github/workflows/build-docs.yml @@ -0,0 +1,26 @@ +name: Build the documentation + +on: + pull_request: + +permissions: + contents: read + +jobs: + build: + + name: Build documentation + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: 3.9 + - name: Install dependencies + run: | + pip install --upgrade pip + pip install -r docs/requirements.txt + - name: Build the documentation + run: make -C docs html diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..fd5fb80f1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,31 @@ +name: Test and lint + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + linting: + name: Lint + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + - name: Set up Python 3 + uses: actions/setup-python@v6 + with: + python-version: 3 + cache: "pip" + cache-dependency-path: "test-requirements.txt" + + - name: Install dependencies + run: | + pip install --upgrade pip + pip install -r test-requirements.txt + + - name: Lint implementation + run: flake8 diff --git a/.github/workflows/conformance.yml b/.github/workflows/conformance.yml new file mode 100644 index 000000000..be45db465 --- /dev/null +++ b/.github/workflows/conformance.yml @@ -0,0 +1,47 @@ +name: Conformance + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + conformance: + name: Run conformance suite + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v6 + + - name: Set up Python 3.12 + uses: actions/setup-python@v6 + with: + python-version: "3.12" + cache: "pip" + + - name: Install uv + run: | + python -m pip install --upgrade pip + python -m pip install uv + + - name: Run conformance suite + working-directory: conformance + run: | + uv sync --python 3.12 --frozen + uv run --python 3.12 --frozen python src/main.py + + - name: Validate conformance invariants + working-directory: conformance + run: | + uv run --python 3.12 --frozen python src/validate_results.py + + - name: Assert conformance results are up to date + run: | + if [ -n "$(git status --porcelain -- conformance/results)" ]; then + git status --short conformance/results + git diff -- conformance/results + echo "Conformance results are out of date. Run conformance/src/main.py and commit updated results." + exit 1 + fi diff --git a/.gitignore b/.gitignore index 98a79f50c..0ad58f48a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,17 @@ MANIFEST + +__pycache__/ build/ dist/ +tmp/ +venv*/ + +.cache/ +.idea/ .tox/ +.venv*/ .vscode/ -.idea/ -.cache/ -__pycache__/ -tmp/ + *.swp *.pyc *.egg-info/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..61b8f596b --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,24 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + exclude: conformance/results/.*/.*\.toml + - id: end-of-file-fixer + - id: check-yaml + - id: check-toml + - id: check-merge-conflict + - id: mixed-line-ending + args: [--fix=lf] + exclude: docs/make\.bat + - id: check-case-conflict + - repo: meta + hooks: + - id: check-hooks-apply + +ci: + autofix_commit_msg: "[pre-commit.ci] auto fixes from pre-commit.com hooks" + autofix_prs: true + autoupdate_commit_msg: "[pre-commit.ci] pre-commit autoupdate" + autoupdate_schedule: quarterly + submodules: false diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 000000000..b6c9041cf --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,16 @@ +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3" + +sphinx: + configuration: docs/conf.py + +python: + install: + - requirements: docs/requirements.txt diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 38809e319..000000000 --- a/.travis.yml +++ /dev/null @@ -1,50 +0,0 @@ -language: python - -jobs: - include: - - name: "3.8" - dist: xenial - python: 3.8.0 - - name: "3.7.3" - dist: xenial - python: 3.7.3 - - name: "3.7.2" - dist: xenial - python: 3.7.2 - - name: "3.7.1" - dist: xenial - python: 3.7.1 - - name: "3.7.0" - dist: xenial - python: 3.7.0 - - name: "3.6.2" - python: 3.6.2 - - name: "3.6.1" - python: 3.6.1 - - name: "3.6" - python: 3.6 - - name: "3.5.3" - python: 3.5.3 - - name: "3.5.2" - python: 3.5.2 -## - name: "3.5.1" -## dist: trusty -## python: 3.5.1 - - name: "3.5" - python: 3.5 - - name: "3.4" - python: 3.4 - - name: "2.7" - python: 2.7 - -install: -- pip install -r test-requirements.txt - -script: - - export PYTHONPATH=`python -c "import sys; print('python2' if sys.version.startswith('2') else 'src')"`; - if [[ $TRAVIS_PYTHON_VERSION < '3.7' ]]; then py.test $PYTHONPATH; fi - - if [[ $TRAVIS_PYTHON_VERSION < '3.5' ]]; then pip install -U .; fi - - export PYTHONPATH=`python -c "import sys; print('typing_extensions/src_py2' if sys.version.startswith('2') else 'typing_extensions/src_py3')"`; - py.test $PYTHONPATH; - - if [[ $TRAVIS_PYTHON_VERSION == '3.8' ]]; then flake8; fi - - if [[ $TRAVIS_PYTHON_VERSION == '3.8' ]]; then flake8 --config=.flake8-tests src/test_typing.py python2/test_typing.py typing_extensions/src_py2/test_typing_extensions.py typing_extensions/src_py3/test_typing_extensions.py; fi diff --git a/LICENSE b/LICENSE index 583f9f6e6..1df6b3b8d 100644 --- a/LICENSE +++ b/LICENSE @@ -13,12 +13,11 @@ software. In May 2000, Guido and the Python core development team moved to BeOpen.com to form the BeOpen PythonLabs team. In October of the same -year, the PythonLabs team moved to Digital Creations (now Zope -Corporation, see http://www.zope.com). In 2001, the Python Software -Foundation (PSF, see http://www.python.org/psf/) was formed, a -non-profit organization created specifically to own Python-related -Intellectual Property. Zope Corporation is a sponsoring member of -the PSF. +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. All Python releases are Open Source (see http://www.opensource.org for the Open Source Definition). Historically, most, but not all, Python @@ -74,8 +73,9 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are -retained in Python alone or in any derivative version prepared by Licensee. +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python or any part thereof, and wants to make @@ -180,9 +180,9 @@ version prepared by Licensee. Alternately, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6.1 is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the Internet using the following +Python 1.6.1 may be located on the internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the Internet +Agreement may also be obtained from a proxy server on the internet using the following URL: http://hdl.handle.net/1895.22/1013". 3. In the event Licensee prepares a derivative work that is based on diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 3dc64dfeb..000000000 --- a/MANIFEST.in +++ /dev/null @@ -1,7 +0,0 @@ -include LICENSE README.rst -include src/typing.py -include src/test_typing.py -include src/mod_generics_cache.py -include python2/typing.py -include python2/test_typing.py -include python2/mod_generics_cache.py diff --git a/README.md b/README.md index 3c2d45b58..8bc02d48a 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,45 @@ -[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +# Static Typing for Python -PEP 484: Type Hints -=================== +## Documentation and Support -This GitHub repo is used for three separate things: +The documentation for Python's static typing can be found at +[typing.python.org](https://typing.python.org/). You can get +help in our [support forum](https://github.com/python/typing/discussions). -- The issue tracker is used to discuss PEP-level type system issues. - However, - [typing-sig](https://mail.python.org/mailman3/lists/typing-sig.python.org/) - is more appropriate these days. +Improvements to the type system should be discussed on +[Python's Discourse](https://discuss.python.org/c/typing/32), and are +tracked in the [issues](https://github.com/python/typing/issues) in this +repository. -- A copy of the `typing` module for older Python versions (2.7 and - 3.4) is maintained here. Note that the canonical source lives - [upstream](https://github.com/python/cpython/blob/master/Lib/typing.py) - in the CPython repo. +For conversations that are more suitable to a chat platform, you can use one of the following: -- The `typing_extensions` module lives here. +- [gitter](https://gitter.im/python/typing) +- [discord](https://discord.com/invite/python) [#type-hinting](https://discord.com/channels/267624335836053506/891788761371906108) channel -Workflow --------- +## Repository Content -* The typing.py module and its unittests are edited in the `src` - subdirectory of this repo. The `python2` subdirectory contains the - Python 2 backport. +This GitHub repository is used for several things: -Workflow for PyPI releases --------------------------- +- The documentation at [typing.python.org](https://typing.python.org/) + is maintained in the [docs directory](./docs). This includes the + [specification](https://typing.python.org/en/latest/spec/index.html) for the + type system. See especially [the update procedure](https://typing.python.org/en/latest/spec/meta.html) + for the spec. -* Run tests under all supported versions. As of May 2019 this includes - 2.7, 3.4, 3.5, 3.6, 3.7. +- A [discussion forum](https://github.com/python/typing/discussions) for typing-related user + help is hosted here. -* On macOS, you can use `pyenv `_ to - manage multiple Python installations. Long story short: +- [Conformance test](https://github.com/python/typing/blob/main/conformance/README.md) for Python static type checkers. The [latest conformance test results](https://htmlpreview.github.io/?https://github.com/python/typing/blob/main/conformance/results/results.html) are here. - * ``xcode-select --install`` - * ``brew install pyenv`` - * ``echo 'eval "$(pyenv init -)"' >> ~/.bash_profile`` - * Open a new shell - * ``pyenv install 3.5.3`` - * ``pyenv install 3.4.6`` - * (assuming you already have 2.7.13 and 3.6.1 from Homebrew) - * ``pyenv global system 3.5.3 3.4.6`` +Historically, this repository also hosted: -* You can use ``tox`` to automate running tests. +- The `typing_extensions` package, which now lives in the + [typing_extensions](https://github.com/python/typing_extensions) repo. + It used to be in the `typing_extensions` directory. -* Update the version number in ``setup.py``. - -* Build the source and wheel distributions: - - * ``pip3 install -U setuptools wheel`` - * ``pip2 install -U setuptools wheel`` - * ``rm -rf dist/ build/`` - * ``python3 setup.py sdist bdist_wheel`` - * ``rm -rf build/`` (Works around `a Wheel bug `_) - * ``python2 setup.py bdist_wheel`` - -* Install the built distributions locally and test (if you - were using ``tox``, you already tested the source distribution). - -* Make sure twine is up to date, then run ``twine upload dist/*``. +- A backport of the + [`typing` module](https://docs.python.org/3/library/typing.html) for older + Python versions. It was removed after all Python versions that lack `typing` + in the standard library reached end of life. The last released version, + supporting Python 2.7 and 3.4, + is [available at PyPI](https://pypi.org/project/typing/). diff --git a/conformance/.gitignore b/conformance/.gitignore new file mode 100644 index 000000000..6a000697b --- /dev/null +++ b/conformance/.gitignore @@ -0,0 +1,45 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# Environments +.env +.venv + +# Tools +.mypy_cache +.coverage +htmlcov + +# General +.DS_Store + +# Editor temp files +.*.swp + +# Workspace configurations +.vscode diff --git a/conformance/README.md b/conformance/README.md new file mode 100644 index 000000000..0b60a9490 --- /dev/null +++ b/conformance/README.md @@ -0,0 +1,131 @@ +# Type System Conformance + +## Motivation + +[PEP 729](https://peps.python.org/pep-0729/) provides a structured and documented way to specify and evolve the Python type system. In support of this effort, an official [Python typing spec](https://typing.python.org/en/latest/spec/) has been drafted. This spec consolidates details from various historical typing-related PEPs. The spec will be modified over time to clarify unspecified and under-specified parts of the type system. It will also be extended to cover new features of the type system. + +Accompanying the typing specification is this conformance test suite which validates the behavior of static type checkers against the specification. + +## Structure & Name + +This project contains test cases for behaviors defined in the Python typing spec. Tests are structured and grouped in accordance with the specification's chapter headings. + +* [concepts](https://typing.python.org/en/latest/spec/concepts.html) +* [annotations](https://typing.python.org/en/latest/spec/annotations.html) +* [typeforms](https://typing.python.org/en/latest/spec/type-forms.html) +* [specialtypes](https://typing.python.org/en/latest/spec/special-types.html) +* [generics](https://typing.python.org/en/latest/spec/generics.html) +* [qualifiers](https://typing.python.org/en/latest/spec/qualifiers.html) +* [classes](https://typing.python.org/en/latest/spec/class-compat.html) +* [aliases](https://typing.python.org/en/latest/spec/aliases.html) +* [literals](https://typing.python.org/en/latest/spec/literal.html) +* [protocols](https://typing.python.org/en/latest/spec/protocol.html) +* [callables](https://typing.python.org/en/latest/spec/callables.html) +* [constructors](https://typing.python.org/en/latest/spec/constructors.html) +* [overloads](https://typing.python.org/en/latest/spec/overload.html) +* [dataclasses](https://typing.python.org/en/latest/spec/dataclasses.html) +* [typeddicts](https://typing.python.org/en/latest/spec/typeddict.html) +* [tuples](https://typing.python.org/en/latest/spec/tuples.html) +* [namedtuples](https://typing.python.org/en/latest/spec/namedtuples.html) +* [narrowing](https://typing.python.org/en/latest/spec/narrowing.html) +* [directives](https://typing.python.org/en/latest/spec/directives.html) +* [distribution](https://typing.python.org/en/latest/spec/distributing.html) +* [historical](https://typing.python.org/en/latest/spec/historical.html) + +A test file is a ".py" file. The file name should start with one of the above names followed by a description of the test (with words separated by underscores). For example, `generics_paramspec_basic_usage.py` would contain the basic usage tests for `ParamSpec`. Each test file can contain multiple individual unit tests, but these tests should be related to each other. If the number of unit tests in a single test file exceeds ten, it may be desirable to split it into separate test files. This will help maintain a consistent level of granularity across tests. + +## Notes About Tests + +Tests are designed to run on all current and future static type checkers. They must therefore be type-checker agnostic and should not rely on functionality or behaviors that are specific to one type checker or another. + +Test cases are meant to be human readable. They should include comments that help explain their purpose (what is being tested, whether an error should be generated, etc.). They should also contain links to the typing spec, discussions, and issue trackers. + +The test suite focuses on static type checking not general Python semantics. Tests should therefore focus on static analysis behaviors, not runtime behaviors. + +Test cases use the following conventions: + +* Lines that are expected to produce a type checker error should have a comment starting with # E", + either by itself or followed by an explanation after a colon (e.g., "# E: int is not a subtype + of str"). Such explanatory comments are purely for human understanding, but type checkers are not + expected to use their exact wording. There are several syntactic variations; see "Test Case Syntax" + below. +* Lines that may produce an error (e.g., because the spec allows multiple behaviors) should be + marked with "# E?" instead of "# E". +* If a test case tests conformance with a specific passage in the spec, that passage should be + quoted in a comment prefixed with "# > ". + +## Test Case Syntax + +Test cases support the following special comments for declaring where errors should be raised: + +* `# E`: an error must be raised on this line +* `# E?`: an error may be raised on this line +* `# E[tag]`, where `tag` is an arbitrary string: must appear multiple times in a file with the same tag. + Exactly one line with this tag must raise an error. +* `# E[tag+]`: like `# E[tag]`, but errors may be raised on multiple lines. + +Each comment may be followed by a colon plus an explanation of the error; the explanation is ignored +by the scoring system. + +## Running the Conformance Test Tool + +To run the conformance test suite: +* Clone the https://github.com/python/typing repo. +* Install [uv](https://docs.astral.sh/uv/) and ensure Python 3.12 is available. +* Switch to the `conformance` subdirectory and install locked dependencies (`uv sync --python 3.12 --frozen`). +* Run the conformance tool (`uv run --python 3.12 --frozen python src/main.py`). + +Note that some type checkers may not run on some platforms. If a type checker fails to install, tests will be skipped for that type checker. + +## Reporting Conformance Results + +Different type checkers report errors in different ways (with different wording in error messages and different line numbers or character ranges for errors). This variation makes it difficult to fully automate test validation given that tests will want to check for both false positive and false negative type errors. Some level of manual inspection will therefore be needed to determine whether a type checker is fully conformant with all tests in any given test file. This "scoring" process is required only when the output of a test changes — e.g. when a new version of that type checker is released and the tests are rerun. We assume that the output of a type checker will be the same from one run to the next unless/until a new version is released that fixes or introduces a bug. In this case, the output will need to be manually inspected and the conformance results re-scored for those tests whose output has changed. + +[Conformance results](https://htmlpreview.github.io/?https://github.com/python/typing/blob/main/conformance/results/results.html) are reported and summarized for each supported type checker. Currently, results are reported for mypy, pyrefly, pyright, zuban, ty, and pycroscope. It is the goal and desire to add additional type checkers over time. + +## Adding a New Test Case + +To add a new test, create a new ".py" file in the `tests` directory. Its name must begin with one of the above test group names followed by an underscore. Write the contents of the test including a module docstring describing the purpose of the test. Next, run the conformance test tool. This will generate a new `.toml` file in the `results` subdirectory corresponding each type checker. Manually review the output from each type checker and determine whether it conforms to the specification. If so, add `conformant = "Pass"` to the `.toml` file. If it does not fully comply, add `conformant = "Partial"` and a `notes` section detailing where it is not compliant. If the type checker doesn't support the feature in the test add `conformant = "Unsupported"`. Once the conformance status has been updated, rerun the conformance test tool to regenerate the summary report. + +## Updating a Test Case + +If a test is updated (augmented or fixed), the process is similar to when adding a new test. Run the conformance test tool to generate new results and manually examine the output of each supported type checker. Then update the conformance status accordingly. Once the conformance status has been updated, rerun the conformance test tool to regenerate the summary report. + +## Updating a Type Checker + +Type checker versions are locked in `uv.lock`. + +To bump all supported checkers to their latest released versions: + +```bash +python scripts/bump_type_checkers.py +``` + +After bumping, install the new lockfile and rerun conformance: + +```bash +uv sync --python 3.12 --frozen +uv run --python 3.12 --frozen python src/main.py +``` + +If checker output changes for any test cases, examine those deltas to determine whether the conformance status has changed. Once the conformance status has been updated, rerun the tool to regenerate the summary report. + +## Automated Conformance Checking + +In addition to manual scoring, we provide an experimental tool that automatically checks type checkers for conformance. This tool relies on the "# E" comments present in the stubs and on parsing type checker output. This logic is run automatically as part of the conformance test tool. It produces the following fields in the `.toml` output files: + +* `errors_diff`: a string describing all issues found with the type checker's behavior: either expected errors that were not emitted, or extra errors that the conformance test suite does not allow. +* `conformance_automated`: either "Pass" or "Fail" based on whether there are any discrepancies with the expected behavior. + +This tool does not yet work reliably on all test cases. The script `conformance/src/unexpected_fails.py` can be run to find all test cases where the automated tool's conformance judgment differs from the manual judgment entered in the `.toml` files. + +Some common problems with automated checks: + +* Sometimes the spec is imprecise or allows multiple options. In this case, use "# E?" to mark an error as optional. +* Type checkers may produce additional errors for issues unrelated to the topic being tested. In this case, add an extra field `ignore_errors` in the type checker's `.toml` file that contains the text of the irrelevant errors. Any error message that contains a substring in the `ignore_errors` list is ignored. For example, if `ignore_errors = ["Too many arguments"]`, then a mypy error `dataclasses_usage.py:127: error: Too many arguments for "DC7" [call-arg]` will be ignored. +* Type checkers may differ in the line on which they report an error. In this case, on each of the lines where an error could + reasonably be shown, write `# E[]`, where `` is an arbitrary string that is unique in the file. The test will be marked as passing if the type checker produces an error on exactly one of the lines where this tag appears. + +## Contributing + +Contributions are welcome! diff --git a/conformance/pyproject.toml b/conformance/pyproject.toml new file mode 100644 index 000000000..e44dadf64 --- /dev/null +++ b/conformance/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "typing-conformance" +version = "0.1.0" +requires-python = "==3.12.*" +dependencies = [ + "mypy", + "pycroscope", + "pyrefly", + "pyright", + "tomli", + "tomlkit", + "zuban", + "ty", +] + +[tool.uv] +package = false diff --git a/conformance/results/mypy/aliases_explicit.toml b/conformance/results/mypy/aliases_explicit.toml new file mode 100644 index 000000000..ee057629f --- /dev/null +++ b/conformance/results/mypy/aliases_explicit.toml @@ -0,0 +1,34 @@ +conformant = "Partial" +notes = """ +Does not reject specialization of type alias that has already been implicitly specialized. +""" +output = """ +aliases_explicit.py:67: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg] +aliases_explicit.py:68: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg] +aliases_explicit.py:69: error: Bad number of arguments for type alias, expected 1, given 2 [type-arg] +aliases_explicit.py:70: error: Bad number of arguments for type alias, expected 1, given 2 [type-arg] +aliases_explicit.py:71: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [valid-type] +aliases_explicit.py:79: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:80: error: Bracketed expression "[...]" is not valid as a type [valid-type] +aliases_explicit.py:81: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:82: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:83: error: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict [misc] +aliases_explicit.py:83: error: Name "b" is not defined [name-defined] +aliases_explicit.py:84: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:85: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:86: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:87: error: Variable "aliases_explicit.var1" is not valid as a type [valid-type] +aliases_explicit.py:87: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_explicit.py:88: error: Invalid type: try using Literal[True] instead? [valid-type] +aliases_explicit.py:89: error: Invalid type: try using Literal[1] instead? [valid-type] +aliases_explicit.py:90: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:90: error: Function "list" could always be true in boolean context [truthy-function] +aliases_explicit.py:91: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:101: error: "UnionType[list[Any], set[Any]]" not callable [operator] +aliases_explicit.py:102: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 100: Expected 1 errors +""" +ignore_errors = ["Function \"list\" could always be true in boolean context"] diff --git a/conformance/results/mypy/aliases_implicit.toml b/conformance/results/mypy/aliases_implicit.toml new file mode 100644 index 000000000..98f363428 --- /dev/null +++ b/conformance/results/mypy/aliases_implicit.toml @@ -0,0 +1,44 @@ +conformant = "Pass" +output = """ +aliases_implicit.py:76: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg] +aliases_implicit.py:77: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg] +aliases_implicit.py:78: error: Bad number of arguments for type alias, expected 1, given 2 [type-arg] +aliases_implicit.py:79: error: Bad number of arguments for type alias, expected 1, given 2 [type-arg] +aliases_implicit.py:80: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [valid-type] +aliases_implicit.py:81: error: Type argument "str" of "GoodTypeAlias12" must be a subtype of "float" [type-var] +aliases_implicit.py:100: error: Function "list" could always be true in boolean context [truthy-function] +aliases_implicit.py:106: error: Variable "aliases_implicit.BadTypeAlias1" is not valid as a type [valid-type] +aliases_implicit.py:106: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:107: error: Variable "aliases_implicit.BadTypeAlias2" is not valid as a type [valid-type] +aliases_implicit.py:107: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:108: error: Variable "aliases_implicit.BadTypeAlias3" is not valid as a type [valid-type] +aliases_implicit.py:108: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:109: error: Variable "aliases_implicit.BadTypeAlias4" is not valid as a type [valid-type] +aliases_implicit.py:109: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:110: error: Variable "aliases_implicit.BadTypeAlias5" is not valid as a type [valid-type] +aliases_implicit.py:110: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:111: error: Variable "aliases_implicit.BadTypeAlias6" is not valid as a type [valid-type] +aliases_implicit.py:111: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:112: error: Variable "aliases_implicit.BadTypeAlias7" is not valid as a type [valid-type] +aliases_implicit.py:112: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:113: error: Variable "aliases_implicit.BadTypeAlias8" is not valid as a type [valid-type] +aliases_implicit.py:113: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:114: error: Variable "aliases_implicit.BadTypeAlias9" is not valid as a type [valid-type] +aliases_implicit.py:114: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:115: error: Variable "aliases_implicit.BadTypeAlias10" is not valid as a type [valid-type] +aliases_implicit.py:115: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:116: error: Variable "aliases_implicit.BadTypeAlias11" is not valid as a type [valid-type] +aliases_implicit.py:116: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:117: error: Variable "aliases_implicit.BadTypeAlias12" is not valid as a type [valid-type] +aliases_implicit.py:117: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:118: error: Variable "aliases_implicit.BadTypeAlias13" is not valid as a type [valid-type] +aliases_implicit.py:118: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:119: error: Variable "aliases_implicit.BadTypeAlias14" is not valid as a type [valid-type] +aliases_implicit.py:119: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:133: error: "UnionType[list[Any], set[Any]]" not callable [operator] +aliases_implicit.py:135: error: Bad number of arguments for type alias, expected 0, given 1 [type-arg] +""" +conformance_automated = "Pass" +errors_diff = """ +""" +ignore_errors = ["Function \"list\" could always be true in boolean context"] diff --git a/conformance/results/mypy/aliases_newtype.toml b/conformance/results/mypy/aliases_newtype.toml new file mode 100644 index 000000000..f4c4bc856 --- /dev/null +++ b/conformance/results/mypy/aliases_newtype.toml @@ -0,0 +1,25 @@ +conformant = "Partial" +notes = """ +`NewType`s are incorrectly considered to be classes. +""" +output = """ +aliases_newtype.py:11: error: Argument 1 to "UserId" has incompatible type "str"; expected "int" [arg-type] +aliases_newtype.py:12: error: Incompatible types in assignment (expression has type "int", variable has type "UserId") [assignment] +aliases_newtype.py:23: error: Cannot use isinstance() with NewType type [misc] +aliases_newtype.py:26: error: Cannot subclass "NewType" [misc] +aliases_newtype.py:35: error: String argument 1 "BadName" to NewType(...) does not match variable name "GoodName" [misc] +aliases_newtype.py:41: error: "GoodNewType1" expects no type arguments, but 1 given [type-arg] +aliases_newtype.py:47: error: Argument 2 to NewType(...) must be subclassable (got "int | str") [valid-newtype] +aliases_newtype.py:50: error: Type variable "aliases_newtype.T" is unbound [valid-type] +aliases_newtype.py:50: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +aliases_newtype.py:50: note: (Hint: Use "T" in function signature to bind "T" inside a function) +aliases_newtype.py:52: error: NewType cannot be used with protocol classes [misc] +aliases_newtype.py:54: error: Argument 2 to NewType(...) must be subclassable (got "Literal[7]") [valid-newtype] +aliases_newtype.py:61: error: Argument 2 to NewType(...) must be subclassable (got "TD1") [valid-newtype] +aliases_newtype.py:63: error: NewType(...) expects exactly two positional arguments [misc] +aliases_newtype.py:65: error: Argument 2 to NewType(...) must be subclassable (got "Any") [valid-newtype] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 18: Expected 1 errors +""" diff --git a/conformance/results/mypy/aliases_recursive.toml b/conformance/results/mypy/aliases_recursive.toml new file mode 100644 index 000000000..d8c763cad --- /dev/null +++ b/conformance/results/mypy/aliases_recursive.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +output = """ +aliases_recursive.py:19: error: Dict entry 1 has incompatible type "str": "complex"; expected "str": "int | str | float | list[Json] | dict[str, Json] | None" [dict-item] +aliases_recursive.py:20: error: List item 1 has incompatible type "complex"; expected "int | str | float | list[Json] | dict[str, Json] | None" [list-item] +aliases_recursive.py:38: error: Incompatible types in assignment (expression has type "tuple[int, tuple[str, int], tuple[int, tuple[int, list[int]]]]", variable has type "RecursiveTuple") [assignment] +aliases_recursive.py:39: error: Incompatible types in assignment (expression has type "tuple[int, list[int]]", variable has type "RecursiveTuple") [assignment] +aliases_recursive.py:50: error: Dict entry 0 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, RecursiveMapping]" [dict-item] +aliases_recursive.py:51: error: Dict entry 2 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, RecursiveMapping]" [dict-item] +aliases_recursive.py:52: error: Dict entry 2 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, RecursiveMapping]" [dict-item] +aliases_recursive.py:63: error: List item 0 has incompatible type "float"; expected "GenericTypeAlias1[str] | str" [list-item] +aliases_recursive.py:69: error: List item 0 has incompatible type "float"; expected "GenericTypeAlias2[str, int] | str | int" [list-item] +aliases_recursive.py:72: error: Invalid recursive alias: a union item of itself [misc] +aliases_recursive.py:75: error: Invalid recursive alias: a union item of itself [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/aliases_type_statement.toml b/conformance/results/mypy/aliases_type_statement.toml new file mode 100644 index 000000000..b69d735a7 --- /dev/null +++ b/conformance/results/mypy/aliases_type_statement.toml @@ -0,0 +1,36 @@ +conformant = "Pass" +output = """ +aliases_type_statement.py:17: error: "TypeAliasType" has no attribute "bit_count" [attr-defined] +aliases_type_statement.py:19: error: "TypeAliasType" not callable [operator] +aliases_type_statement.py:23: error: "TypeAliasType" has no attribute "other_attrib" [attr-defined] +aliases_type_statement.py:26: error: Type alias defined using "type" statement not valid as base class [misc] +aliases_type_statement.py:31: error: Parameterized generics cannot be used with class or instance checks [misc] +aliases_type_statement.py:31: error: Argument 2 to "isinstance" has incompatible type "TypeAliasType"; expected "_ClassInfo" [arg-type] +aliases_type_statement.py:37: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:38: error: Bracketed expression "[...]" is not valid as a type [valid-type] +aliases_type_statement.py:39: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:40: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:41: error: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict [misc] +aliases_type_statement.py:41: error: Name "b" is not defined [name-defined] +aliases_type_statement.py:42: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:43: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:44: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:45: error: Variable "aliases_type_statement.var1" is not valid as a type [valid-type] +aliases_type_statement.py:45: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_type_statement.py:46: error: Invalid type: try using Literal[True] instead? [valid-type] +aliases_type_statement.py:47: error: Invalid type: try using Literal[1] instead? [valid-type] +aliases_type_statement.py:48: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:49: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:53: error: All type parameters should be declared ("V" not declared) [valid-type] +aliases_type_statement.py:58: error: All type parameters should be declared ("T1" not declared) [valid-type] +aliases_type_statement.py:68: error: Type argument "str" of "RecursiveTypeAlias2" must be a subtype of "int" [type-var] +aliases_type_statement.py:70: error: Type argument "int" of "RecursiveTypeAlias2" must be a subtype of "str" [type-var] +aliases_type_statement.py:73: error: Cannot resolve name "RecursiveTypeAlias3" (possible cyclic definition) [misc] +aliases_type_statement.py:75: error: Invalid recursive alias: a union item of itself [misc] +aliases_type_statement.py:79: error: Cannot resolve name "RecursiveTypeAlias6" (possible cyclic definition) [misc] +aliases_type_statement.py:79: error: Cannot resolve name "RecursiveTypeAlias7" (possible cyclic definition) [misc] +aliases_type_statement.py:80: error: Cannot resolve name "RecursiveTypeAlias7" (possible cyclic definition) [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/aliases_typealiastype.toml b/conformance/results/mypy/aliases_typealiastype.toml new file mode 100644 index 000000000..1a034e810 --- /dev/null +++ b/conformance/results/mypy/aliases_typealiastype.toml @@ -0,0 +1,43 @@ +conformant = "Partial" +notes = """ +Incorrectly rejects some recursive type aliases using TypeAliasType. +Incorrectly rejects the use of a class-scoped TypeVar in a TypeAliasType definition. +""" +output = """ +aliases_typealiastype.py:20: error: Cannot resolve name "GoodAlias5" (possible cyclic definition) [misc] +aliases_typealiastype.py:22: error: Cannot resolve name "GoodAlias5" (possible cyclic definition) [misc] +aliases_typealiastype.py:27: error: Type variable "T" is not included in type_params [valid-type] +aliases_typealiastype.py:32: error: "TypeAliasType" has no attribute "other_attrib" [attr-defined] +aliases_typealiastype.py:40: error: Type argument "int" of "GoodAlias5" must be a subtype of "str" [type-var] +aliases_typealiastype.py:43: error: Type variable "S" is not included in type_params [valid-type] +aliases_typealiastype.py:44: error: Type variable "S" is not included in type_params [valid-type] +aliases_typealiastype.py:45: error: Tuple literal expected as the type_params argument to TypeAliasType [misc] +aliases_typealiastype.py:46: error: Cannot resolve name "BadAlias4" (possible cyclic definition) [misc] +aliases_typealiastype.py:47: error: Invalid recursive alias: a union item of itself [misc] +aliases_typealiastype.py:48: error: Cannot resolve name "BadAlias6" (possible cyclic definition) [misc] +aliases_typealiastype.py:48: error: Cannot resolve name "BadAlias7" (possible cyclic definition) [misc] +aliases_typealiastype.py:49: error: Cannot resolve name "BadAlias7" (possible cyclic definition) [misc] +aliases_typealiastype.py:52: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:53: error: Bracketed expression "[...]" is not valid as a type [valid-type] +aliases_typealiastype.py:54: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:55: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:56: error: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict [misc] +aliases_typealiastype.py:56: error: Name "b" is not defined [name-defined] +aliases_typealiastype.py:57: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:58: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:59: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:60: error: Variable "aliases_typealiastype.var1" is not valid as a type [valid-type] +aliases_typealiastype.py:60: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_typealiastype.py:61: error: Invalid type: try using Literal[True] instead? [valid-type] +aliases_typealiastype.py:62: error: Invalid type: try using Literal[1] instead? [valid-type] +aliases_typealiastype.py:63: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:63: error: Function "list" could always be true in boolean context [truthy-function] +aliases_typealiastype.py:64: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:66: error: Cannot resolve name "BadAlias21" (possible cyclic definition) [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 20: Unexpected errors ['aliases_typealiastype.py:20: error: Cannot resolve name "GoodAlias5" (possible cyclic definition) [misc]'] +Line 22: Unexpected errors ['aliases_typealiastype.py:22: error: Cannot resolve name "GoodAlias5" (possible cyclic definition) [misc]'] +Line 27: Unexpected errors ['aliases_typealiastype.py:27: error: Type variable "T" is not included in type_params [valid-type]'] +""" diff --git a/conformance/results/mypy/aliases_variance.toml b/conformance/results/mypy/aliases_variance.toml new file mode 100644 index 000000000..7f830350c --- /dev/null +++ b/conformance/results/mypy/aliases_variance.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +aliases_variance.py:24: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +aliases_variance.py:28: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +aliases_variance.py:32: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +aliases_variance.py:44: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/annotations_coroutines.toml b/conformance/results/mypy/annotations_coroutines.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/mypy/annotations_coroutines.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/annotations_forward_refs.toml b/conformance/results/mypy/annotations_forward_refs.toml new file mode 100644 index 000000000..fd9c48687 --- /dev/null +++ b/conformance/results/mypy/annotations_forward_refs.toml @@ -0,0 +1,38 @@ +conformant = "Partial" +notes = """ +Does not report error for a forward reference that is not enclosed in quotes. +Does not report error for use of quoted type with "|" operator (runtime error). +Incorrectly generates error for quoted type defined in class scope. +""" +output = """ +annotations_forward_refs.py:41: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:42: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:43: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:44: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:45: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:46: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:47: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:48: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:49: error: Variable "annotations_forward_refs.var1" is not valid as a type [valid-type] +annotations_forward_refs.py:49: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +annotations_forward_refs.py:50: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:51: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:52: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:53: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:54: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:55: error: Module "types" is not valid as a type [valid-type] +annotations_forward_refs.py:55: note: Perhaps you meant to use a protocol matching the module structure? +annotations_forward_refs.py:80: error: Name "ClassF" is not defined; did you mean "ClassA", "ClassB", or "ClassC"? [name-defined] +annotations_forward_refs.py:87: error: Function "annotations_forward_refs.ClassD.int" is not valid as a type [valid-type] +annotations_forward_refs.py:87: note: Perhaps you need "Callable[...]" or a callback protocol? +annotations_forward_refs.py:89: error: Function "annotations_forward_refs.ClassD.int" is not valid as a type [valid-type] +annotations_forward_refs.py:89: note: Perhaps you need "Callable[...]" or a callback protocol? +annotations_forward_refs.py:96: error: Expression is of type int?, not "int" [assert-type] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 24: Expected 1 errors +Line 25: Expected 1 errors +Line 87: Unexpected errors ['annotations_forward_refs.py:87: error: Function "annotations_forward_refs.ClassD.int" is not valid as a type [valid-type]'] +Line 96: Unexpected errors ['annotations_forward_refs.py:96: error: Expression is of type int?, not "int" [assert-type]'] +""" diff --git a/conformance/results/mypy/annotations_generators.toml b/conformance/results/mypy/annotations_generators.toml new file mode 100644 index 000000000..e14ede973 --- /dev/null +++ b/conformance/results/mypy/annotations_generators.toml @@ -0,0 +1,20 @@ +conformant = "Partial" +notes = """ +Does not report incompatible Generator type in `yield from` statement. +""" +output = """ +annotations_generators.py:51: error: Missing return statement [return] +annotations_generators.py:54: error: Incompatible return value type (got "bool", expected "C") [return-value] +annotations_generators.py:57: error: Incompatible types in "yield" (actual type "int", expected type "A") [misc] +annotations_generators.py:66: error: Incompatible types in "yield" (actual type "int", expected type "A") [misc] +annotations_generators.py:71: error: No return value expected [return-value] +annotations_generators.py:75: error: Incompatible types in "yield" (actual type "B", expected type "A") [misc] +annotations_generators.py:86: error: The return type of a generator function should be "Generator" or one of its supertypes [misc] +annotations_generators.py:91: error: The return type of an async generator function should be "AsyncGenerator" or one of its supertypes [misc] +annotations_generators.py:118: error: Incompatible types in "yield from" (actual type "A", expected type "B") [misc] +annotations_generators.py:119: error: Incompatible types in "yield from" (actual type "int", expected type "B") [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 135: Expected 1 errors +""" diff --git a/conformance/results/mypy/annotations_methods.toml b/conformance/results/mypy/annotations_methods.toml new file mode 100644 index 000000000..c6879001a --- /dev/null +++ b/conformance/results/mypy/annotations_methods.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +notes = """ +Type evaluation differs from other type checkers because of ambiguity in the spec related to method bindings. +""" +output = """ +annotations_methods.py:42: error: Expression is of type "B", not "A" [assert-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/annotations_typeexpr.toml b/conformance/results/mypy/annotations_typeexpr.toml new file mode 100644 index 000000000..b24c4bff1 --- /dev/null +++ b/conformance/results/mypy/annotations_typeexpr.toml @@ -0,0 +1,25 @@ +conformant = "Pass" +output = """ +annotations_typeexpr.py:88: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:88: note: Suggestion: use eval[...] instead of eval(...) +annotations_typeexpr.py:89: error: Bracketed expression "[...]" is not valid as a type [valid-type] +annotations_typeexpr.py:90: error: Syntax error in type annotation [syntax] +annotations_typeexpr.py:90: note: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) +annotations_typeexpr.py:91: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:92: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:93: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:94: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:95: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:96: error: Variable "annotations_typeexpr.var1" is not valid as a type [valid-type] +annotations_typeexpr.py:96: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +annotations_typeexpr.py:97: error: Invalid type: try using Literal[True] instead? [valid-type] +annotations_typeexpr.py:98: error: Invalid type: try using Literal[1] instead? [valid-type] +annotations_typeexpr.py:99: error: Invalid type: try using Literal[-1] instead? [valid-type] +annotations_typeexpr.py:100: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:101: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:102: error: Module "types" is not valid as a type [valid-type] +annotations_typeexpr.py:102: note: Perhaps you meant to use a protocol matching the module structure? +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/callables_annotation.toml b/conformance/results/mypy/callables_annotation.toml new file mode 100644 index 000000000..11daf6205 --- /dev/null +++ b/conformance/results/mypy/callables_annotation.toml @@ -0,0 +1,36 @@ +conformant = "Partial" +notes = """ +Incorrectly treats "*args: T, **kwargs: T" as "..." when T is specialized to Any. +Does not treat "*args: Any, **kargs: Any" as "..." when separated by keyword parameter. +""" +output = """ +callables_annotation.py:25: error: Too few arguments [call-arg] +callables_annotation.py:26: error: Argument 2 has incompatible type "int"; expected "str" [arg-type] +callables_annotation.py:27: error: Too many arguments [call-arg] +callables_annotation.py:29: error: Unexpected keyword argument "a" [call-arg] +callables_annotation.py:29: error: Unexpected keyword argument "b" [call-arg] +callables_annotation.py:35: error: Too many arguments [call-arg] +callables_annotation.py:55: error: Please use "Callable[[], ]" or "Callable" [misc] +callables_annotation.py:56: error: The first argument to Callable must be a list of types, parameter specification, or "..." [valid-type] +callables_annotation.py:56: note: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas +callables_annotation.py:57: error: Bracketed expression "[...]" is not valid as a type [valid-type] +callables_annotation.py:57: note: Did you mean "List[...]"? +callables_annotation.py:58: error: Please use "Callable[[], ]" or "Callable" [misc] +callables_annotation.py:59: error: Unexpected "..." [misc] +callables_annotation.py:91: error: Incompatible types in assignment (expression has type "Callable[[], str]", variable has type "def (int, /, *Any, **Any) -> str") [assignment] +callables_annotation.py:93: error: Incompatible types in assignment (expression has type "def test_cb4(*, a: int) -> str", variable has type "def (int, /, *Any, **Any) -> str") [assignment] +callables_annotation.py:157: error: Incompatible types in assignment (expression has type "Proto7", variable has type "Proto6") [assignment] +callables_annotation.py:157: note: Following member(s) of "Proto7" have conflicts: +callables_annotation.py:157: note: Expected: +callables_annotation.py:157: note: def __call__(self, int, /, *args: Any, k: str, **kwargs: Any) -> None +callables_annotation.py:157: note: Got: +callables_annotation.py:157: note: def __call__(self, float, /, b: int, *, k: str, m: str) -> None +callables_annotation.py:172: error: Incompatible types in assignment (expression has type "Callable[[], str]", variable has type "def (int, /, *Any, **Any) -> str") [assignment] +callables_annotation.py:187: error: Incompatible types in assignment (expression has type "Callable[[int, str], str]", variable has type "def (str, /, *Any, **Any) -> str") [assignment] +callables_annotation.py:189: error: Incompatible types in assignment (expression has type "Callable[[int, str], str]", variable has type "def (str, /, *Any, **Any) -> str") [assignment] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 159: Expected 1 errors +Line 157: Unexpected errors ['callables_annotation.py:157: error: Incompatible types in assignment (expression has type "Proto7", variable has type "Proto6") [assignment]'] +""" diff --git a/conformance/results/mypy/callables_kwargs.toml b/conformance/results/mypy/callables_kwargs.toml new file mode 100644 index 000000000..571931b13 --- /dev/null +++ b/conformance/results/mypy/callables_kwargs.toml @@ -0,0 +1,29 @@ +conformant = "Partial" +notes = """ +Allows callable without kwargs to be assigned to callable with unpacked kwargs +""" +output = """ +callables_kwargs.py:46: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:46: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:51: error: Unexpected keyword argument "v4" for "func1" [call-arg] +callables_kwargs.py:52: error: Too many positional arguments for "func1" [misc] +callables_kwargs.py:58: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] +callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "int" [arg-type] +callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, object]"; expected "str" [arg-type] +callables_kwargs.py:63: error: "func1" gets multiple values for keyword argument "v1" [misc] +callables_kwargs.py:64: error: "func2" gets multiple values for keyword argument "v3" [misc] +callables_kwargs.py:64: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] +callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v1" [misc] +callables_kwargs.py:101: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol3") [assignment] +callables_kwargs.py:101: note: "TDProtocol3.__call__" has type "def __call__(self, *, v1: int, v2: int, v3: str) -> None" +callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol4") [assignment] +callables_kwargs.py:102: note: "TDProtocol4.__call__" has type "def __call__(self, *, v1: int) -> None" +callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: **TD2) -> None", variable has type "TDProtocol5") [assignment] +callables_kwargs.py:103: note: "TDProtocol5.__call__" has type "def __call__(self, v1: int, v3: str) -> None" +callables_kwargs.py:111: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] +callables_kwargs.py:122: error: Unpack item in ** parameter must be a TypedDict [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 134: Expected 1 errors +""" diff --git a/conformance/results/mypy/callables_protocol.toml b/conformance/results/mypy/callables_protocol.toml new file mode 100644 index 000000000..8fc8dcea4 --- /dev/null +++ b/conformance/results/mypy/callables_protocol.toml @@ -0,0 +1,37 @@ +conformant = "Pass" +output = """ +callables_protocol.py:35: error: Incompatible types in assignment (expression has type "def cb1_bad1(*vals: bytes, max_items: int | None) -> list[bytes]", variable has type "Proto1") [assignment] +callables_protocol.py:35: note: "Proto1.__call__" has type "def __call__(self, *vals: bytes, max_len: int | None = ...) -> list[bytes]" +callables_protocol.py:36: error: Incompatible types in assignment (expression has type "def cb1_bad2(*vals: bytes) -> list[bytes]", variable has type "Proto1") [assignment] +callables_protocol.py:36: note: "Proto1.__call__" has type "def __call__(self, *vals: bytes, max_len: int | None = ...) -> list[bytes]" +callables_protocol.py:37: error: Incompatible types in assignment (expression has type "def cb1_bad3(*vals: bytes, max_len: str | None) -> list[bytes]", variable has type "Proto1") [assignment] +callables_protocol.py:37: note: "Proto1.__call__" has type "def __call__(self, *vals: bytes, max_len: int | None = ...) -> list[bytes]" +callables_protocol.py:67: error: Incompatible types in assignment (expression has type "def cb2_bad1(*a: bytes) -> Any", variable has type "Proto2") [assignment] +callables_protocol.py:67: note: "Proto2.__call__" has type "def __call__(self, *vals: bytes, **kwargs: str) -> None" +callables_protocol.py:68: error: Incompatible types in assignment (expression has type "def cb2_bad2(*a: str, **b: str) -> Any", variable has type "Proto2") [assignment] +callables_protocol.py:68: note: "Proto2.__call__" has type "def __call__(self, *vals: bytes, **kwargs: str) -> None" +callables_protocol.py:69: error: Incompatible types in assignment (expression has type "def cb2_bad3(*a: bytes, **b: bytes) -> Any", variable has type "Proto2") [assignment] +callables_protocol.py:69: note: "Proto2.__call__" has type "def __call__(self, *vals: bytes, **kwargs: str) -> None" +callables_protocol.py:70: error: Incompatible types in assignment (expression has type "def cb2_bad4(**b: str) -> Any", variable has type "Proto2") [assignment] +callables_protocol.py:70: note: "Proto2.__call__" has type "def __call__(self, *vals: bytes, **kwargs: str) -> None" +callables_protocol.py:97: error: Incompatible types in assignment (expression has type "Callable[[int], None]", variable has type "Proto4") [assignment] +callables_protocol.py:97: note: "function" is missing following "Proto4" protocol member: +callables_protocol.py:97: note: other_attribute +callables_protocol.py:121: error: Incompatible types in assignment (expression has type "def cb6_bad1(*vals: bytes, max_len: int | None = ...) -> list[bytes]", variable has type "NotProto6") [assignment] +callables_protocol.py:169: error: Incompatible types in assignment (expression has type "Callable[[int], Any]", variable has type "Proto8") [assignment] +callables_protocol.py:169: note: "Proto8.__call__" has type overloaded function +callables_protocol.py:186: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +callables_protocol.py:187: error: "Proto9[P, R]" has no attribute "xxx" [attr-defined] +callables_protocol.py:197: error: "Proto9[[int], str]" has no attribute "other_attribute2"; maybe "other_attribute"? [attr-defined] +callables_protocol.py:238: error: Incompatible types in assignment (expression has type "Callable[[int, str], Any]", variable has type "Proto11") [assignment] +callables_protocol.py:238: note: "Proto11.__call__" has type "def __call__(self, int, /, y: str) -> Any" +callables_protocol.py:260: error: Incompatible types in assignment (expression has type "def cb12_bad1(*args: Any, kwarg0: Any) -> None", variable has type "Proto12") [assignment] +callables_protocol.py:260: note: "Proto12.__call__" has type "def __call__(self, *args: Any, kwarg0: Any, kwarg1: Any) -> None" +callables_protocol.py:284: error: Incompatible types in assignment (expression has type "Callable[[str], str]", variable has type "Proto13_Default") [assignment] +callables_protocol.py:284: note: "Proto13_Default.__call__" has type "def __call__(self, path: str = ...) -> str" +callables_protocol.py:311: error: Incompatible types in assignment (expression has type "def cb14_no_default(*, path: str) -> str", variable has type "Proto14_Default") [assignment] +callables_protocol.py:311: note: "Proto14_Default.__call__" has type "def __call__(self, *, path: str = ...) -> str" +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/callables_subtyping.toml b/conformance/results/mypy/callables_subtyping.toml new file mode 100644 index 000000000..3dfcb382f --- /dev/null +++ b/conformance/results/mypy/callables_subtyping.toml @@ -0,0 +1,179 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +callables_subtyping.py:26: error: Incompatible types in assignment (expression has type "Callable[[int], int]", variable has type "Callable[[float], float]") [assignment] +callables_subtyping.py:29: error: Incompatible types in assignment (expression has type "Callable[[float], float]", variable has type "Callable[[int], int]") [assignment] +callables_subtyping.py:51: error: Incompatible types in assignment (expression has type "PosOnly2", variable has type "Standard2") [assignment] +callables_subtyping.py:52: error: Incompatible types in assignment (expression has type "KwOnly2", variable has type "Standard2") [assignment] +callables_subtyping.py:52: note: Following member(s) of "KwOnly2" have conflicts: +callables_subtyping.py:52: note: Expected: +callables_subtyping.py:52: note: def __call__(self, a: int, b: int) -> None +callables_subtyping.py:52: note: Got: +callables_subtyping.py:52: note: def __call__(self, *, b: int, a: int) -> None +callables_subtyping.py:55: error: Incompatible types in assignment (expression has type "KwOnly2", variable has type "PosOnly2") [assignment] +callables_subtyping.py:55: note: Following member(s) of "KwOnly2" have conflicts: +callables_subtyping.py:55: note: Expected: +callables_subtyping.py:55: note: def __call__(self, int, int, /) -> None +callables_subtyping.py:55: note: Got: +callables_subtyping.py:55: note: def __call__(self, *, b: int, a: int) -> None +callables_subtyping.py:58: error: Incompatible types in assignment (expression has type "PosOnly2", variable has type "KwOnly2") [assignment] +callables_subtyping.py:58: note: Following member(s) of "PosOnly2" have conflicts: +callables_subtyping.py:58: note: Expected: +callables_subtyping.py:58: note: def __call__(self, *, b: int, a: int) -> None +callables_subtyping.py:58: note: Got: +callables_subtyping.py:58: note: def __call__(self, int, int, /) -> None +callables_subtyping.py:82: error: Incompatible types in assignment (expression has type "NoArgs3", variable has type "IntArgs3") [assignment] +callables_subtyping.py:82: note: Following member(s) of "NoArgs3" have conflicts: +callables_subtyping.py:82: note: Expected: +callables_subtyping.py:82: note: def __call__(self, *args: int) -> None +callables_subtyping.py:82: note: Got: +callables_subtyping.py:82: note: def __call__(self) -> None +callables_subtyping.py:85: error: Incompatible types in assignment (expression has type "NoArgs3", variable has type "FloatArgs3") [assignment] +callables_subtyping.py:85: note: Following member(s) of "NoArgs3" have conflicts: +callables_subtyping.py:85: note: Expected: +callables_subtyping.py:85: note: def __call__(self, *args: float) -> None +callables_subtyping.py:85: note: Got: +callables_subtyping.py:85: note: def __call__(self) -> None +callables_subtyping.py:86: error: Incompatible types in assignment (expression has type "IntArgs3", variable has type "FloatArgs3") [assignment] +callables_subtyping.py:86: note: Following member(s) of "IntArgs3" have conflicts: +callables_subtyping.py:86: note: Expected: +callables_subtyping.py:86: note: def __call__(self, *args: float) -> None +callables_subtyping.py:86: note: Got: +callables_subtyping.py:86: note: def __call__(self, *args: int) -> None +callables_subtyping.py:116: error: Incompatible types in assignment (expression has type "IntArgs4", variable has type "PosOnly4") [assignment] +callables_subtyping.py:116: note: Following member(s) of "IntArgs4" have conflicts: +callables_subtyping.py:116: note: Expected: +callables_subtyping.py:116: note: def __call__(self, int, str, /) -> None +callables_subtyping.py:116: note: Got: +callables_subtyping.py:116: note: def __call__(self, *args: int) -> None +callables_subtyping.py:119: error: Incompatible types in assignment (expression has type "StrArgs4", variable has type "IntStrArgs4") [assignment] +callables_subtyping.py:119: note: Following member(s) of "StrArgs4" have conflicts: +callables_subtyping.py:119: note: Expected: +callables_subtyping.py:119: note: def __call__(self, *args: int | str) -> None +callables_subtyping.py:119: note: Got: +callables_subtyping.py:119: note: def __call__(self, int, /, *args: str) -> None +callables_subtyping.py:120: error: Incompatible types in assignment (expression has type "IntArgs4", variable has type "IntStrArgs4") [assignment] +callables_subtyping.py:120: note: Following member(s) of "IntArgs4" have conflicts: +callables_subtyping.py:120: note: Expected: +callables_subtyping.py:120: note: def __call__(self, *args: int | str) -> None +callables_subtyping.py:120: note: Got: +callables_subtyping.py:120: note: def __call__(self, *args: int) -> None +callables_subtyping.py:122: error: Incompatible types in assignment (expression has type "IntArgs4", variable has type "StrArgs4") [assignment] +callables_subtyping.py:122: note: Following member(s) of "IntArgs4" have conflicts: +callables_subtyping.py:122: note: Expected: +callables_subtyping.py:122: note: def __call__(self, int, /, *args: str) -> None +callables_subtyping.py:122: note: Got: +callables_subtyping.py:122: note: def __call__(self, *args: int) -> None +callables_subtyping.py:124: error: Incompatible types in assignment (expression has type "StrArgs4", variable has type "IntArgs4") [assignment] +callables_subtyping.py:124: note: Following member(s) of "StrArgs4" have conflicts: +callables_subtyping.py:124: note: Expected: +callables_subtyping.py:124: note: def __call__(self, *args: int) -> None +callables_subtyping.py:124: note: Got: +callables_subtyping.py:124: note: def __call__(self, int, /, *args: str) -> None +callables_subtyping.py:125: error: Incompatible types in assignment (expression has type "IntStrArgs4", variable has type "Standard4") [assignment] +callables_subtyping.py:126: error: Incompatible types in assignment (expression has type "StrArgs4", variable has type "Standard4") [assignment] +callables_subtyping.py:151: error: Incompatible types in assignment (expression has type "NoKwargs5", variable has type "IntKwargs5") [assignment] +callables_subtyping.py:151: note: Following member(s) of "NoKwargs5" have conflicts: +callables_subtyping.py:151: note: Expected: +callables_subtyping.py:151: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:151: note: Got: +callables_subtyping.py:151: note: def __call__(self) -> None +callables_subtyping.py:154: error: Incompatible types in assignment (expression has type "NoKwargs5", variable has type "FloatKwargs5") [assignment] +callables_subtyping.py:154: note: Following member(s) of "NoKwargs5" have conflicts: +callables_subtyping.py:154: note: Expected: +callables_subtyping.py:154: note: def __call__(self, **kwargs: float) -> None +callables_subtyping.py:154: note: Got: +callables_subtyping.py:154: note: def __call__(self) -> None +callables_subtyping.py:155: error: Incompatible types in assignment (expression has type "IntKwargs5", variable has type "FloatKwargs5") [assignment] +callables_subtyping.py:155: note: Following member(s) of "IntKwargs5" have conflicts: +callables_subtyping.py:155: note: Expected: +callables_subtyping.py:155: note: def __call__(self, **kwargs: float) -> None +callables_subtyping.py:155: note: Got: +callables_subtyping.py:155: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:187: error: Incompatible types in assignment (expression has type "IntKwargs6", variable has type "KwOnly6") [assignment] +callables_subtyping.py:187: note: Following member(s) of "IntKwargs6" have conflicts: +callables_subtyping.py:187: note: Expected: +callables_subtyping.py:187: note: def __call__(self, *, a: int, b: str) -> None +callables_subtyping.py:187: note: Got: +callables_subtyping.py:187: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:190: error: Incompatible types in assignment (expression has type "StrKwargs6", variable has type "IntStrKwargs6") [assignment] +callables_subtyping.py:190: note: Following member(s) of "StrKwargs6" have conflicts: +callables_subtyping.py:190: note: Expected: +callables_subtyping.py:190: note: def __call__(self, **kwargs: int | str) -> None +callables_subtyping.py:190: note: Got: +callables_subtyping.py:190: note: def __call__(self, *, a: int, **kwargs: str) -> None +callables_subtyping.py:191: error: Incompatible types in assignment (expression has type "IntKwargs6", variable has type "IntStrKwargs6") [assignment] +callables_subtyping.py:191: note: Following member(s) of "IntKwargs6" have conflicts: +callables_subtyping.py:191: note: Expected: +callables_subtyping.py:191: note: def __call__(self, **kwargs: int | str) -> None +callables_subtyping.py:191: note: Got: +callables_subtyping.py:191: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:193: error: Incompatible types in assignment (expression has type "IntKwargs6", variable has type "StrKwargs6") [assignment] +callables_subtyping.py:193: note: Following member(s) of "IntKwargs6" have conflicts: +callables_subtyping.py:193: note: Expected: +callables_subtyping.py:193: note: def __call__(self, *, a: int, **kwargs: str) -> None +callables_subtyping.py:193: note: Got: +callables_subtyping.py:193: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:195: error: Incompatible types in assignment (expression has type "StrKwargs6", variable has type "IntKwargs6") [assignment] +callables_subtyping.py:195: note: Following member(s) of "StrKwargs6" have conflicts: +callables_subtyping.py:195: note: Expected: +callables_subtyping.py:195: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:195: note: Got: +callables_subtyping.py:195: note: def __call__(self, *, a: int, **kwargs: str) -> None +callables_subtyping.py:196: error: Incompatible types in assignment (expression has type "IntStrKwargs6", variable has type "Standard6") [assignment] +callables_subtyping.py:196: note: Following member(s) of "IntStrKwargs6" have conflicts: +callables_subtyping.py:196: note: Expected: +callables_subtyping.py:196: note: def __call__(self, a: int, b: str) -> None +callables_subtyping.py:196: note: Got: +callables_subtyping.py:196: note: def __call__(self, **kwargs: int | str) -> None +callables_subtyping.py:197: error: Incompatible types in assignment (expression has type "StrKwargs6", variable has type "Standard6") [assignment] +callables_subtyping.py:197: note: Following member(s) of "StrKwargs6" have conflicts: +callables_subtyping.py:197: note: Expected: +callables_subtyping.py:197: note: def __call__(self, a: int, b: str) -> None +callables_subtyping.py:197: note: Got: +callables_subtyping.py:197: note: def __call__(self, *, a: int, **kwargs: str) -> None +callables_subtyping.py:236: error: Incompatible types in assignment (expression has type "NoDefaultArg8", variable has type "DefaultArg8") [assignment] +callables_subtyping.py:236: note: Following member(s) of "NoDefaultArg8" have conflicts: +callables_subtyping.py:236: note: Expected: +callables_subtyping.py:236: note: def __call__(self, x: int = ...) -> None +callables_subtyping.py:236: note: Got: +callables_subtyping.py:236: note: def __call__(self, x: int) -> None +callables_subtyping.py:237: error: Incompatible types in assignment (expression has type "NoX8", variable has type "DefaultArg8") [assignment] +callables_subtyping.py:237: note: Following member(s) of "NoX8" have conflicts: +callables_subtyping.py:237: note: Expected: +callables_subtyping.py:237: note: def __call__(self, x: int = ...) -> None +callables_subtyping.py:237: note: Got: +callables_subtyping.py:237: note: def __call__(self) -> None +callables_subtyping.py:240: error: Incompatible types in assignment (expression has type "NoX8", variable has type "NoDefaultArg8") [assignment] +callables_subtyping.py:240: note: Following member(s) of "NoX8" have conflicts: +callables_subtyping.py:240: note: Expected: +callables_subtyping.py:240: note: def __call__(self, x: int) -> None +callables_subtyping.py:240: note: Got: +callables_subtyping.py:240: note: def __call__(self) -> None +callables_subtyping.py:243: error: Incompatible types in assignment (expression has type "NoDefaultArg8", variable has type "NoX8") [assignment] +callables_subtyping.py:243: note: Following member(s) of "NoDefaultArg8" have conflicts: +callables_subtyping.py:243: note: Expected: +callables_subtyping.py:243: note: def __call__(self) -> None +callables_subtyping.py:243: note: Got: +callables_subtyping.py:243: note: def __call__(self, x: int) -> None +callables_subtyping.py:273: error: Incompatible types in assignment (expression has type "Overloaded9", variable has type "FloatArg9") [assignment] +callables_subtyping.py:273: note: Following member(s) of "Overloaded9" have conflicts: +callables_subtyping.py:273: note: Expected: +callables_subtyping.py:273: note: def __call__(self, x: float) -> float +callables_subtyping.py:273: note: Got: +callables_subtyping.py:273: note: @overload +callables_subtyping.py:273: note: def __call__(self, x: int) -> int +callables_subtyping.py:273: note: @overload +callables_subtyping.py:273: note: def __call__(self, x: str) -> str +callables_subtyping.py:297: error: Incompatible types in assignment (expression has type "StrArg10", variable has type "Overloaded10") [assignment] +callables_subtyping.py:297: note: Following member(s) of "StrArg10" have conflicts: +callables_subtyping.py:297: note: Expected: +callables_subtyping.py:297: note: @overload +callables_subtyping.py:297: note: def __call__(self, x: int, y: str) -> float +callables_subtyping.py:297: note: @overload +callables_subtyping.py:297: note: def __call__(self, x: str) -> complex +callables_subtyping.py:297: note: Got: +callables_subtyping.py:297: note: def __call__(self, x: str) -> complex +""" +conformance_automated = "Pass" diff --git a/conformance/results/mypy/classes_classvar.toml b/conformance/results/mypy/classes_classvar.toml new file mode 100644 index 000000000..bddc8f0b6 --- /dev/null +++ b/conformance/results/mypy/classes_classvar.toml @@ -0,0 +1,37 @@ +conformant = "Partial" +notes = """ +Internal error if TypeVarTuple is used in ClassVar. +Does not reject use of ParamSpec in ClassVar. +Rejects ClassVar nested in Annotated. +Does not reject use of ClassVar in TypeAlias definition. +""" +output = """ +classes_classvar.py:38: error: ClassVar[...] must have at most one type argument [valid-type] +classes_classvar.py:39: error: Invalid type: try using Literal[3] instead? [valid-type] +classes_classvar.py:40: error: Name "var" is not defined; did you mean "var1" or "vars"? [name-defined] +classes_classvar.py:52: error: Incompatible types in assignment (expression has type "dict[Never, Never]", variable has type "list[str]") [assignment] +classes_classvar.py:54: error: Variable should not be annotated with both ClassVar and Final [misc] +classes_classvar.py:55: error: Invalid type: ClassVar nested inside other type [valid-type] +classes_classvar.py:67: error: Invalid type: ClassVar nested inside other type [valid-type] +classes_classvar.py:69: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:70: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:71: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:73: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:77: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:78: error: ClassVar[...] can't be used inside a type alias [valid-type] +classes_classvar.py:111: error: Cannot assign to class variable "stats" via instance [misc] +classes_classvar.py:130: error: All protocol members must have explicitly declared types [misc] +classes_classvar.py:140: error: Incompatible types in assignment (expression has type "ProtoAImpl", variable has type "ProtoA") [assignment] +classes_classvar.py:140: note: "ProtoAImpl" is missing following "ProtoA" protocol member: +classes_classvar.py:140: note: z +classes_classvar.py:140: note: Protocol member ProtoA.x expected class variable, got instance variable +classes_classvar.py:140: note: Protocol member ProtoA.y expected class variable, got instance variable +""" +conformance_automated = "Fail" +errors_diff = """ +Line 45: Expected 1 errors +Line 46: Expected 1 errors +Line 47: Expected 1 errors +Line 67: Unexpected errors ['classes_classvar.py:67: error: Invalid type: ClassVar nested inside other type [valid-type]'] +Line 130: Unexpected errors ['classes_classvar.py:130: error: All protocol members must have explicitly declared types [misc]'] +""" diff --git a/conformance/results/mypy/classes_override.toml b/conformance/results/mypy/classes_override.toml new file mode 100644 index 000000000..45cceb4cf --- /dev/null +++ b/conformance/results/mypy/classes_override.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +classes_override.py:53: error: Method "method3" is marked as an override, but no base method was found with this name [misc] +classes_override.py:56: error: Method "method4" is marked as an override, but no base method was found with this name [misc] +classes_override.py:79: error: Method "static_method1" is marked as an override, but no base method was found with this name [misc] +classes_override.py:84: error: Method "class_method1" is marked as an override, but no base method was found with this name [misc] +classes_override.py:89: error: Method "property1" is marked as an override, but no base method was found with this name [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/constructors_call_init.toml b/conformance/results/mypy/constructors_call_init.toml new file mode 100644 index 000000000..d8680ec9d --- /dev/null +++ b/conformance/results/mypy/constructors_call_init.toml @@ -0,0 +1,17 @@ +conformant = "Partial" +notes = """ +Does not report errors during binding to self parameter of __init__ method. +Does not reject use of class-scoped type variables in annotation of self parameter in __init__ method. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 56: Expected 1 errors +Line 107: Expected 1 errors +Line 73: Unexpected errors ['constructors_call_init.py:73: error: Expression is of type "Class5[list[int]]", not "Class5[int]" [assert-type]'] +""" +output = """ +constructors_call_init.py:21: error: Argument 1 to "Class1" has incompatible type "float"; expected "int" [arg-type] +constructors_call_init.py:42: error: Argument 1 to "Class3" has incompatible type "Class2[Never]"; expected "Class3 | None" [arg-type] +constructors_call_init.py:73: error: Expression is of type "Class5[list[int]]", not "Class5[int]" [assert-type] +constructors_call_init.py:130: error: Too many arguments for "Class11" [call-arg] +""" diff --git a/conformance/results/mypy/constructors_call_metaclass.toml b/conformance/results/mypy/constructors_call_metaclass.toml new file mode 100644 index 000000000..fb84e85db --- /dev/null +++ b/conformance/results/mypy/constructors_call_metaclass.toml @@ -0,0 +1,18 @@ +conformant = "Unsupported" +notes = """ +Does not honor metaclass __call__ method when evaluating constructor call. +Does not skip evaluation of __new__ and __init__ if custom metaclass call returns non-class. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 26: Unexpected errors ['constructors_call_metaclass.py:26: error: Expression is of type "Class1", not "Never" [assert-type]', 'constructors_call_metaclass.py:26: error: Missing positional argument "x" in call to "Class1" [call-arg]'] +Line 39: Unexpected errors ['constructors_call_metaclass.py:39: error: Expression is of type "Class2", not "int | Meta2" [assert-type]', 'constructors_call_metaclass.py:39: error: Missing positional argument "x" in call to "Class2" [call-arg]'] +""" +output = """ +constructors_call_metaclass.py:26: error: Expression is of type "Class1", not "Never" [assert-type] +constructors_call_metaclass.py:26: error: Missing positional argument "x" in call to "Class1" [call-arg] +constructors_call_metaclass.py:39: error: Expression is of type "Class2", not "int | Meta2" [assert-type] +constructors_call_metaclass.py:39: error: Missing positional argument "x" in call to "Class2" [call-arg] +constructors_call_metaclass.py:54: error: Missing positional argument "x" in call to "Class3" [call-arg] +constructors_call_metaclass.py:68: error: Missing positional argument "x" in call to "Class4" [call-arg] +""" diff --git a/conformance/results/mypy/constructors_call_new.toml b/conformance/results/mypy/constructors_call_new.toml new file mode 100644 index 000000000..a325d9e54 --- /dev/null +++ b/conformance/results/mypy/constructors_call_new.toml @@ -0,0 +1,31 @@ +conformant = "Partial" +notes = """ +Does not support __new__ return type that is not a subclass of the class being constructed. +Does not skip evaluation of __init__ based on __new__ return type. +Does not report errors during binding to cls parameter of __new__ method. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 148: Expected 1 errors +Line 40: Unexpected errors ['constructors_call_new.py:40: error: Incompatible return type for "__new__" (returns "int", but must return a subtype of "Class3") [misc]'] +Line 49: Unexpected errors ['constructors_call_new.py:49: error: Expression is of type "Class3", not "int" [assert-type]', 'constructors_call_new.py:49: error: Missing positional argument "x" in call to "Class3" [call-arg]'] +Line 57: Unexpected errors ['constructors_call_new.py:57: error: "__new__" must return a class instance (got "Class4 | Any") [misc]'] +Line 64: Unexpected errors ['constructors_call_new.py:64: error: Expression is of type "Class4", not "Class4 | Any" [assert-type]', 'constructors_call_new.py:64: error: Missing positional argument "x" in call to "Class4" [call-arg]'] +Line 76: Unexpected errors ['constructors_call_new.py:76: error: Expression is of type "Class5", not "Never" [assert-type]', 'constructors_call_new.py:76: error: Missing positional argument "x" in call to "Class5" [call-arg]'] +Line 82: Unexpected errors ['constructors_call_new.py:82: error: "__new__" must return a class instance (got "int | Class6") [misc]'] +Line 89: Unexpected errors ['constructors_call_new.py:89: error: Expression is of type "Class6", not "int | Class6" [assert-type]', 'constructors_call_new.py:89: error: Missing positional argument "x" in call to "Class6" [call-arg]'] +""" +output = """ +constructors_call_new.py:21: error: Argument 1 to "Class1" has incompatible type "float"; expected "int" [arg-type] +constructors_call_new.py:40: error: Incompatible return type for "__new__" (returns "int", but must return a subtype of "Class3") [misc] +constructors_call_new.py:49: error: Expression is of type "Class3", not "int" [assert-type] +constructors_call_new.py:49: error: Missing positional argument "x" in call to "Class3" [call-arg] +constructors_call_new.py:57: error: "__new__" must return a class instance (got "Class4 | Any") [misc] +constructors_call_new.py:64: error: Expression is of type "Class4", not "Class4 | Any" [assert-type] +constructors_call_new.py:64: error: Missing positional argument "x" in call to "Class4" [call-arg] +constructors_call_new.py:76: error: Expression is of type "Class5", not "Never" [assert-type] +constructors_call_new.py:76: error: Missing positional argument "x" in call to "Class5" [call-arg] +constructors_call_new.py:82: error: "__new__" must return a class instance (got "int | Class6") [misc] +constructors_call_new.py:89: error: Expression is of type "Class6", not "int | Class6" [assert-type] +constructors_call_new.py:89: error: Missing positional argument "x" in call to "Class6" [call-arg] +""" diff --git a/conformance/results/mypy/constructors_call_type.toml b/conformance/results/mypy/constructors_call_type.toml new file mode 100644 index 000000000..7ba573760 --- /dev/null +++ b/conformance/results/mypy/constructors_call_type.toml @@ -0,0 +1,17 @@ +conformant = "Partial" +notes = """ +Does not validate call to custom metaclass __call__ method through type[T]. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 30: Expected 1 errors +Line 72: Expected 1 errors +""" +output = """ +constructors_call_type.py:40: error: Missing positional arguments "x", "y" in call to "Class2" [call-arg] +constructors_call_type.py:50: error: Missing positional arguments "x", "y" in call to "Class3" [call-arg] +constructors_call_type.py:59: error: Too many arguments for "Class4" [call-arg] +constructors_call_type.py:64: error: Too many arguments for "object" [call-arg] +constructors_call_type.py:81: error: Missing positional argument "y" in call to "Class2" [call-arg] +constructors_call_type.py:82: error: Argument 2 to "Class2" has incompatible type "int"; expected "str" [arg-type] +""" diff --git a/conformance/results/mypy/constructors_callable.toml b/conformance/results/mypy/constructors_callable.toml new file mode 100644 index 000000000..2d788f335 --- /dev/null +++ b/conformance/results/mypy/constructors_callable.toml @@ -0,0 +1,49 @@ +conformant = "Partial" +notes = """ +Does not generate a union type for __new__ and __init__ when converting class to callable. +Does not ignore __init__ based on __new__ return type when converting class to callable. +Does not support __new__ return type that is different from class being constructed. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 129: Expected 1 errors +Line 146: Expected 1 errors +Line 74: Unexpected errors ['constructors_callable.py:74: error: Incompatible return type for "__new__" (returns "int", but must return a subtype of "Class4") [misc]'] +Line 80: Unexpected errors ['constructors_callable.py:80: error: Expression is of type "Class4", not "int" [assert-type]'] +Line 102: Unexpected errors ['constructors_callable.py:102: error: Expression is of type "Class5", not "Never" [assert-type]'] +Line 107: Unexpected errors ['constructors_callable.py:107: error: Expression is of type "Class5", not "Never" [assert-type]'] +Line 118: Unexpected errors ['constructors_callable.py:118: error: Incompatible return type for "__new__" (returns "Class6Proxy", but must return a subtype of "Class6") [misc]'] +Line 128: Unexpected errors ['constructors_callable.py:128: error: Expression is of type "Class6", not "Class6Proxy" [assert-type]', 'constructors_callable.py:128: error: Too few arguments [call-arg]'] +Line 145: Unexpected errors ['constructors_callable.py:145: error: Expression is of type "Class6Any", not "Any" [assert-type]', 'constructors_callable.py:145: error: Too few arguments [call-arg]'] +""" +output = """ +constructors_callable.py:36: note: Revealed type is "def (x: int) -> constructors_callable.Class1" +constructors_callable.py:38: error: Too few arguments [call-arg] +constructors_callable.py:39: error: Unexpected keyword argument "y" [call-arg] +constructors_callable.py:49: note: Revealed type is "def () -> constructors_callable.Class2" +constructors_callable.py:51: error: Too many arguments [call-arg] +constructors_callable.py:64: note: Revealed type is "def (x: int) -> constructors_callable.Class3" +constructors_callable.py:66: error: Too few arguments [call-arg] +constructors_callable.py:67: error: Unexpected keyword argument "y" [call-arg] +constructors_callable.py:68: error: Too many arguments [call-arg] +constructors_callable.py:74: error: Incompatible return type for "__new__" (returns "int", but must return a subtype of "Class4") [misc] +constructors_callable.py:79: note: Revealed type is "def (x: int) -> constructors_callable.Class4" +constructors_callable.py:80: error: Expression is of type "Class4", not "int" [assert-type] +constructors_callable.py:81: error: Too few arguments [call-arg] +constructors_callable.py:82: error: Unexpected keyword argument "y" [call-arg] +constructors_callable.py:99: note: Revealed type is "def (*args: Any, **kwargs: Any) -> constructors_callable.Class5" +constructors_callable.py:102: error: Expression is of type "Class5", not "Never" [assert-type] +constructors_callable.py:107: error: Expression is of type "Class5", not "Never" [assert-type] +constructors_callable.py:118: error: Incompatible return type for "__new__" (returns "Class6Proxy", but must return a subtype of "Class6") [misc] +constructors_callable.py:127: note: Revealed type is "def (x: int) -> constructors_callable.Class6" +constructors_callable.py:128: error: Expression is of type "Class6", not "Class6Proxy" [assert-type] +constructors_callable.py:128: error: Too few arguments [call-arg] +constructors_callable.py:144: note: Revealed type is "def (x: int) -> constructors_callable.Class6Any" +constructors_callable.py:145: error: Expression is of type "Class6Any", not "Any" [assert-type] +constructors_callable.py:145: error: Too few arguments [call-arg] +constructors_callable.py:164: note: Revealed type is "Overload(def (x: int) -> constructors_callable.Class7[int], def (x: str) -> constructors_callable.Class7[str])" +constructors_callable.py:184: note: Revealed type is "def [T] (x: list[T], y: list[T]) -> constructors_callable.Class8[T]" +constructors_callable.py:186: error: Cannot infer function type argument [misc] +constructors_callable.py:195: note: Revealed type is "def [T] (x: list[T], y: list[T]) -> constructors_callable.Class9" +constructors_callable.py:197: error: Cannot infer function type argument [misc] +""" diff --git a/conformance/results/mypy/constructors_consistency.toml b/conformance/results/mypy/constructors_consistency.toml new file mode 100644 index 000000000..654ebaa96 --- /dev/null +++ b/conformance/results/mypy/constructors_consistency.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +notes = """ +Does not report inconsistency between __new__ and __init__ (optional). +""" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/mypy/dataclasses_descriptors.toml b/conformance/results/mypy/dataclasses_descriptors.toml new file mode 100644 index 000000000..e47310293 --- /dev/null +++ b/conformance/results/mypy/dataclasses_descriptors.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +notes = """ +Assumes descriptor behavior only when field is assigned in class body. +Does not correctly evaluate type of descriptor access. +""" +output = """ +dataclasses_descriptors.py:61: error: Cannot access instance-only attribute "x" on class object [misc] +dataclasses_descriptors.py:62: error: Cannot access instance-only attribute "y" on class object [misc] +dataclasses_descriptors.py:66: error: Expression is of type "Desc2[int]", not "int" [assert-type] +dataclasses_descriptors.py:67: error: Expression is of type "Desc2[str]", not "str" [assert-type] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 61: Unexpected errors ['dataclasses_descriptors.py:61: error: Cannot access instance-only attribute "x" on class object [misc]'] +Line 62: Unexpected errors ['dataclasses_descriptors.py:62: error: Cannot access instance-only attribute "y" on class object [misc]'] +Line 66: Unexpected errors ['dataclasses_descriptors.py:66: error: Expression is of type "Desc2[int]", not "int" [assert-type]'] +Line 67: Unexpected errors ['dataclasses_descriptors.py:67: error: Expression is of type "Desc2[str]", not "str" [assert-type]'] +""" diff --git a/conformance/results/mypy/dataclasses_final.toml b/conformance/results/mypy/dataclasses_final.toml new file mode 100644 index 000000000..ddfb29575 --- /dev/null +++ b/conformance/results/mypy/dataclasses_final.toml @@ -0,0 +1,20 @@ +conformant = "Partial" +notes = """ +Wrongly requires a Final dataclass field to be initialized at class level. +Doesn't support Final nested inside ClassVar. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 27: Expected 1 errors +Line 18: Unexpected errors ['dataclasses_final.py:18: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type]'] +Line 24: Unexpected errors ['dataclasses_final.py:24: error: Expression is of type "Any", not "int" [assert-type]'] +""" +output = """ +dataclasses_final.py:18: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type] +dataclasses_final.py:24: error: Expression is of type "Any", not "int" [assert-type] +dataclasses_final.py:35: error: Cannot assign to final attribute "final_no_default" [misc] +dataclasses_final.py:36: error: Cannot assign to final attribute "final_with_default" [misc] +dataclasses_final.py:37: error: Cannot access final instance attribute "final_no_default" on class object [misc] +dataclasses_final.py:37: error: Cannot assign to final attribute "final_no_default" [misc] +dataclasses_final.py:38: error: Cannot assign to final attribute "final_with_default" [misc] +""" diff --git a/conformance/results/mypy/dataclasses_frozen.toml b/conformance/results/mypy/dataclasses_frozen.toml new file mode 100644 index 000000000..465a17f6f --- /dev/null +++ b/conformance/results/mypy/dataclasses_frozen.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +dataclasses_frozen.py:16: error: Property "a" defined in "DC1" is read-only [misc] +dataclasses_frozen.py:17: error: Property "b" defined in "DC1" is read-only [misc] +dataclasses_frozen.py:23: error: Non-frozen dataclass cannot inherit from a frozen dataclass [misc] +dataclasses_frozen.py:33: error: Frozen dataclass cannot inherit from a non-frozen dataclass [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/dataclasses_hash.toml b/conformance/results/mypy/dataclasses_hash.toml new file mode 100644 index 000000000..08809153e --- /dev/null +++ b/conformance/results/mypy/dataclasses_hash.toml @@ -0,0 +1,19 @@ +conformant = "Unsupported" +notes = """ +Does not synthesize `__hash__ = None` as a class attribute for unhashable dataclasses. +Does not report when an unhashable dataclass has `__hash__` called directly on an instance. +Does not report when dataclass is not compatible with Hashable protocol. +""" +output = """ +dataclasses_hash.py:14: error: Expression is of type "Callable[[object], int]", not "None" [assert-type] +dataclasses_hash.py:36: error: Expression is of type "Callable[[object], int]", not "None" [assert-type] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 17: Expected 1 errors +Line 18: Expected 1 errors +Line 39: Expected 1 errors +Line 40: Expected 1 errors +Line 14: Unexpected errors ['dataclasses_hash.py:14: error: Expression is of type "Callable[[object], int]", not "None" [assert-type]'] +Line 36: Unexpected errors ['dataclasses_hash.py:36: error: Expression is of type "Callable[[object], int]", not "None" [assert-type]'] +""" diff --git a/conformance/results/mypy/dataclasses_inheritance.toml b/conformance/results/mypy/dataclasses_inheritance.toml new file mode 100644 index 000000000..c90115220 --- /dev/null +++ b/conformance/results/mypy/dataclasses_inheritance.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +dataclasses_inheritance.py:62: error: Cannot override instance variable (previously declared on base class "DC6") with class variable [misc] +dataclasses_inheritance.py:66: error: Cannot override class variable (previously declared on base class "DC6") with instance variable [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/dataclasses_kwonly.toml b/conformance/results/mypy/dataclasses_kwonly.toml new file mode 100644 index 000000000..9f83cef04 --- /dev/null +++ b/conformance/results/mypy/dataclasses_kwonly.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +output = """ +dataclasses_kwonly.py:23: error: Too many positional arguments for "DC1" [misc] +dataclasses_kwonly.py:38: error: Too many positional arguments for "DC2" [misc] +dataclasses_kwonly.py:53: error: Too many positional arguments for "DC3" [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/dataclasses_match_args.toml b/conformance/results/mypy/dataclasses_match_args.toml new file mode 100644 index 000000000..1309854f5 --- /dev/null +++ b/conformance/results/mypy/dataclasses_match_args.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_match_args.py:42: error: "type[DC4]" has no attribute "__match_args__" [attr-defined] +""" diff --git a/conformance/results/mypy/dataclasses_order.toml b/conformance/results/mypy/dataclasses_order.toml new file mode 100644 index 000000000..f34eef87e --- /dev/null +++ b/conformance/results/mypy/dataclasses_order.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +output = """ +dataclasses_order.py:50: error: Unsupported operand types for < ("DC1" and "DC2") [operator] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/dataclasses_postinit.toml b/conformance/results/mypy/dataclasses_postinit.toml new file mode 100644 index 000000000..351249f0b --- /dev/null +++ b/conformance/results/mypy/dataclasses_postinit.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +output = """ +dataclasses_postinit.py:19: error: Argument 3 of "__post_init__" is incompatible with supertype "dataclass"; supertype defines the argument type as "str" [override] +dataclasses_postinit.py:28: error: "DC1" has no attribute "x" [attr-defined] +dataclasses_postinit.py:29: error: "DC1" has no attribute "y" [attr-defined] +dataclasses_postinit.py:36: error: Signature of "__post_init__" incompatible with supertype "dataclass" [override] +dataclasses_postinit.py:36: note: Superclass: +dataclasses_postinit.py:36: note: def __post_init__(self: DC2, x: int, y: str) -> None +dataclasses_postinit.py:36: note: Subclass: +dataclasses_postinit.py:36: note: def __post_init__(self: DC2, x: int) -> None +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/dataclasses_slots.toml b/conformance/results/mypy/dataclasses_slots.toml new file mode 100644 index 000000000..4a6d76337 --- /dev/null +++ b/conformance/results/mypy/dataclasses_slots.toml @@ -0,0 +1,14 @@ +conformant = "Partial" +notes = """ +Does not reject write to instance variable that is not defined in __slots__. +""" +output = """ +dataclasses_slots.py:11: error: "DC1" both defines "__slots__" and is used with "slots=True" [misc] +dataclasses_slots.py:66: error: "type[DC6]" has no attribute "__slots__" [attr-defined] +dataclasses_slots.py:69: error: "DC6" has no attribute "__slots__" [attr-defined] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 25: Expected 1 errors +Line 38: Expected 1 errors +""" diff --git a/conformance/results/mypy/dataclasses_transform_class.toml b/conformance/results/mypy/dataclasses_transform_class.toml new file mode 100644 index 000000000..df9f2efd4 --- /dev/null +++ b/conformance/results/mypy/dataclasses_transform_class.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +output = """ +dataclasses_transform_class.py:51: error: Non-frozen dataclass cannot inherit from a frozen dataclass [misc] +dataclasses_transform_class.py:63: error: Property "id" defined in "Customer1" is read-only [misc] +dataclasses_transform_class.py:66: error: Too many positional arguments for "Customer1" [misc] +dataclasses_transform_class.py:72: error: Unsupported left operand type for < ("Customer1") [operator] +dataclasses_transform_class.py:82: error: Too many positional arguments for "Customer2" [misc] +dataclasses_transform_class.py:122: error: Property "id" defined in "Customer3" is read-only [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/dataclasses_transform_converter.toml b/conformance/results/mypy/dataclasses_transform_converter.toml new file mode 100644 index 000000000..fc5633e50 --- /dev/null +++ b/conformance/results/mypy/dataclasses_transform_converter.toml @@ -0,0 +1,48 @@ +conformant = "Unsupported" +notes = """ +Converter parameter not yet supported. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 118: Expected 1 errors +Line 112: Unexpected errors ['dataclasses_transform_converter.py:112: error: Argument 1 to "DC2" has incompatible type "str"; expected "int" [arg-type]', 'dataclasses_transform_converter.py:112: error: Argument 2 to "DC2" has incompatible type "str"; expected "int" [arg-type]', 'dataclasses_transform_converter.py:112: error: Argument 3 to "DC2" has incompatible type "str"; expected "int" [arg-type]', 'dataclasses_transform_converter.py:112: error: Argument 4 to "DC2" has incompatible type "bytes"; expected "ConverterClass" [arg-type]', 'dataclasses_transform_converter.py:112: error: Argument 5 to "DC2" has incompatible type "list[Never]"; expected "int" [arg-type]'] +Line 114: Unexpected errors ['dataclasses_transform_converter.py:114: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]'] +Line 115: Unexpected errors ['dataclasses_transform_converter.py:115: error: Incompatible types in assignment (expression has type "str", variable has type "ConverterClass") [assignment]'] +Line 116: Unexpected errors ['dataclasses_transform_converter.py:116: error: Incompatible types in assignment (expression has type "bytes", variable has type "ConverterClass") [assignment]'] +Line 121: Unexpected errors ['dataclasses_transform_converter.py:121: error: Argument 1 to "DC2" has incompatible type "str"; expected "int" [arg-type]', 'dataclasses_transform_converter.py:121: error: Argument 2 to "DC2" has incompatible type "str"; expected "int" [arg-type]', 'dataclasses_transform_converter.py:121: error: Argument 3 to "DC2" has incompatible type "str"; expected "int" [arg-type]', 'dataclasses_transform_converter.py:121: error: Argument 4 to "DC2" has incompatible type "str"; expected "ConverterClass" [arg-type]', 'dataclasses_transform_converter.py:121: error: Argument 5 to "DC2" has incompatible type "str"; expected "int" [arg-type]', 'dataclasses_transform_converter.py:121: error: Argument 6 to "DC2" has incompatible type "tuple[tuple[str, str], tuple[str, str]]"; expected "dict[str, str]" [arg-type]'] +""" +output = """ +dataclasses_transform_converter.py:48: error: Argument "converter" to "model_field" has incompatible type "Callable[[], int]"; expected "Callable[[Never], int]" [arg-type] +dataclasses_transform_converter.py:49: error: Argument "converter" to "model_field" has incompatible type "def bad_converter2(*, x: int) -> int"; expected "Callable[[Never], int]" [arg-type] +dataclasses_transform_converter.py:107: error: Argument 2 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:107: error: Argument 3 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:107: error: Argument 4 to "DC2" has incompatible type "bytes"; expected "ConverterClass" [arg-type] +dataclasses_transform_converter.py:107: error: Argument 5 to "DC2" has incompatible type "list[Never]"; expected "int" [arg-type] +dataclasses_transform_converter.py:108: error: Argument 1 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:108: error: Argument 2 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:108: error: Argument 3 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:108: error: Argument 4 to "DC2" has incompatible type "int"; expected "ConverterClass" [arg-type] +dataclasses_transform_converter.py:108: error: Argument 5 to "DC2" has incompatible type "list[Never]"; expected "int" [arg-type] +dataclasses_transform_converter.py:109: error: Argument 1 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:109: error: Argument 2 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:109: error: Argument 3 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:109: error: Argument 4 to "DC2" has incompatible type "str"; expected "ConverterClass" [arg-type] +dataclasses_transform_converter.py:109: error: Argument 5 to "DC2" has incompatible type "complex"; expected "int" [arg-type] +dataclasses_transform_converter.py:112: error: Argument 1 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:112: error: Argument 2 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:112: error: Argument 3 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:112: error: Argument 4 to "DC2" has incompatible type "bytes"; expected "ConverterClass" [arg-type] +dataclasses_transform_converter.py:112: error: Argument 5 to "DC2" has incompatible type "list[Never]"; expected "int" [arg-type] +dataclasses_transform_converter.py:114: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +dataclasses_transform_converter.py:115: error: Incompatible types in assignment (expression has type "str", variable has type "ConverterClass") [assignment] +dataclasses_transform_converter.py:116: error: Incompatible types in assignment (expression has type "bytes", variable has type "ConverterClass") [assignment] +dataclasses_transform_converter.py:119: error: Incompatible types in assignment (expression has type "int", variable has type "ConverterClass") [assignment] +dataclasses_transform_converter.py:121: error: Argument 1 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:121: error: Argument 2 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:121: error: Argument 3 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:121: error: Argument 4 to "DC2" has incompatible type "str"; expected "ConverterClass" [arg-type] +dataclasses_transform_converter.py:121: error: Argument 5 to "DC2" has incompatible type "str"; expected "int" [arg-type] +dataclasses_transform_converter.py:121: error: Argument 6 to "DC2" has incompatible type "tuple[tuple[str, str], tuple[str, str]]"; expected "dict[str, str]" [arg-type] +dataclasses_transform_converter.py:130: error: Argument "converter" to "model_field" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]" [arg-type] +dataclasses_transform_converter.py:133: error: Cannot infer value of type parameter "S" of "model_field" [misc] +""" diff --git a/conformance/results/mypy/dataclasses_transform_field.toml b/conformance/results/mypy/dataclasses_transform_field.toml new file mode 100644 index 000000000..bcc6b6069 --- /dev/null +++ b/conformance/results/mypy/dataclasses_transform_field.toml @@ -0,0 +1,14 @@ +conformant = "Partial" +notes = """ +Does not properly handle field constructor that has default value for `kw_only` or `init` parameter. +""" +output = """ +dataclasses_transform_field.py:64: error: Unexpected keyword argument "id" for "CustomerModel1" [call-arg] +dataclasses_transform_field.py:75: error: Too many positional arguments for "CustomerModel2" [misc] +dataclasses_transform_field.py:75: error: Missing named argument "name" for "CustomerModel2" [call-arg] +dataclasses_transform_field.py:77: error: Missing named argument "id" for "CustomerModel2" [call-arg] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 77: Unexpected errors ['dataclasses_transform_field.py:77: error: Missing named argument "id" for "CustomerModel2" [call-arg]'] +""" diff --git a/conformance/results/mypy/dataclasses_transform_func.toml b/conformance/results/mypy/dataclasses_transform_func.toml new file mode 100644 index 000000000..5861a2808 --- /dev/null +++ b/conformance/results/mypy/dataclasses_transform_func.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +notes = """ +Does not handle `kw_only=False` override when `kw_only_default=True`. +Does not report error when `order=False` and comparison operators are used. +""" +output = """ +dataclasses_transform_func.py:52: error: Too many positional arguments for "Customer1" [misc] +dataclasses_transform_func.py:56: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment] +dataclasses_transform_func.py:64: error: Unexpected keyword argument "salary" for "Customer1" [call-arg] +dataclasses_transform_func.py:70: error: Too many positional arguments for "Customer2" [misc] +dataclasses_transform_func.py:89: error: Non-frozen dataclass cannot inherit from a frozen dataclass [misc] +dataclasses_transform_func.py:96: error: Property "id" defined in "Customer3" is read-only [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 60: Expected 1 errors +Line 52: Unexpected errors ['dataclasses_transform_func.py:52: error: Too many positional arguments for "Customer1" [misc]'] +""" diff --git a/conformance/results/mypy/dataclasses_transform_meta.toml b/conformance/results/mypy/dataclasses_transform_meta.toml new file mode 100644 index 000000000..89585d64a --- /dev/null +++ b/conformance/results/mypy/dataclasses_transform_meta.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +output = """ +dataclasses_transform_meta.py:51: error: Non-frozen dataclass cannot inherit from a frozen dataclass [misc] +dataclasses_transform_meta.py:63: error: Property "id" defined in "Customer1" is read-only [misc] +dataclasses_transform_meta.py:66: error: Too many positional arguments for "Customer1" [misc] +dataclasses_transform_meta.py:73: error: Unsupported left operand type for < ("Customer1") [operator] +dataclasses_transform_meta.py:83: error: Too many positional arguments for "Customer2" [misc] +dataclasses_transform_meta.py:103: error: Property "id" defined in "Customer3" is read-only [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/dataclasses_usage.toml b/conformance/results/mypy/dataclasses_usage.toml new file mode 100644 index 000000000..25f6f3026 --- /dev/null +++ b/conformance/results/mypy/dataclasses_usage.toml @@ -0,0 +1,23 @@ +conformant = "Pass" +notes = """ +Does not detect unannotated usage of `dataclasses.field()`. +""" +output = """ +dataclasses_usage.py:36: error: Accessing "__init__" on an instance is unsound, since instance.__init__ could be from an incompatible subclass [misc] +dataclasses_usage.py:51: error: Missing positional argument "unit_price" in call to "InventoryItem" [call-arg] +dataclasses_usage.py:52: error: Argument 2 to "InventoryItem" has incompatible type "str"; expected "float" [arg-type] +dataclasses_usage.py:53: error: Too many arguments for "InventoryItem" [call-arg] +dataclasses_usage.py:62: error: Attributes without a default cannot follow attributes with one [misc] +dataclasses_usage.py:68: error: Attributes without a default cannot follow attributes with one [misc] +dataclasses_usage.py:74: error: Attributes without a default cannot follow attributes with one [misc] +dataclasses_usage.py:84: error: Too many arguments for "DC4" [call-arg] +dataclasses_usage.py:89: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +dataclasses_usage.py:128: error: Too many arguments for "DC7" [call-arg] +dataclasses_usage.py:131: error: Missing positional argument "y" in call to "DC8" [call-arg] +dataclasses_usage.py:180: error: Too many arguments for "DC13" [call-arg] +dataclasses_usage.py:246: error: Too many arguments for "DC19" [call-arg] +""" +conformance_automated = "Pass" +errors_diff = """ +""" +ignore_errors = ["Accessing \"__init__\" on an instance is unsound"] diff --git a/conformance/results/mypy/directives_assert_type.toml b/conformance/results/mypy/directives_assert_type.toml new file mode 100644 index 000000000..61e4a8a27 --- /dev/null +++ b/conformance/results/mypy/directives_assert_type.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +output = """ +directives_assert_type.py:27: error: Expression is of type "int | str", not "int" [assert-type] +directives_assert_type.py:28: error: Expression is of type "int | str", not "Any" [assert-type] +directives_assert_type.py:29: error: Expression is of type "Any", not "int" [assert-type] +directives_assert_type.py:30: error: Expression is of type "Literal[4]", not "int" [assert-type] +directives_assert_type.py:32: error: "assert_type" expects 2 arguments [misc] +directives_assert_type.py:32: error: Too few arguments for "assert_type" [call-arg] +directives_assert_type.py:33: error: Expression is of type "Literal['']", not "int" [assert-type] +directives_assert_type.py:34: error: "assert_type" expects 2 arguments [misc] +directives_assert_type.py:34: error: Too many arguments for "assert_type" [call-arg] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/directives_cast.toml b/conformance/results/mypy/directives_cast.toml new file mode 100644 index 000000000..0c407e639 --- /dev/null +++ b/conformance/results/mypy/directives_cast.toml @@ -0,0 +1,19 @@ +conformant = "Pass" +output = """ +directives_cast.py:15: error: "cast" expects 2 arguments [misc] +directives_cast.py:15: error: All overload variants of "cast" require at least one argument [call-overload] +directives_cast.py:15: note: Possible overload variants: +directives_cast.py:15: note: def [_T] cast(typ: type[_T], val: Any) -> _T +directives_cast.py:15: note: def cast(typ: str, val: Any) -> Any +directives_cast.py:15: note: def cast(typ: object, val: Any) -> Any +directives_cast.py:16: error: Invalid type: try using Literal[1] instead? [valid-type] +directives_cast.py:17: error: "cast" expects 2 arguments [misc] +directives_cast.py:17: error: No overload variant of "cast" matches argument types "Any", "str", "str" [call-overload] +directives_cast.py:17: note: Possible overload variants: +directives_cast.py:17: note: def [_T] cast(typ: type[_T], val: Any) -> _T +directives_cast.py:17: note: def cast(typ: str, val: Any) -> Any +directives_cast.py:17: note: def cast(typ: object, val: Any) -> Any +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/directives_deprecated.toml b/conformance/results/mypy/directives_deprecated.toml new file mode 100644 index 000000000..e7bec6ab7 --- /dev/null +++ b/conformance/results/mypy/directives_deprecated.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_deprecated.py:18: error: class _directives_deprecated_library.Ham is deprecated: Use Spam instead [deprecated] +directives_deprecated.py:24: error: function _directives_deprecated_library.norwegian_blue is deprecated: It is pining for the fjords [deprecated] +directives_deprecated.py:25: error: function _directives_deprecated_library.norwegian_blue is deprecated: It is pining for the fjords [deprecated] +directives_deprecated.py:30: error: overload def (x: int) -> str of function _directives_deprecated_library.foo is deprecated: Only str will be allowed [deprecated] +directives_deprecated.py:41: error: function _directives_deprecated_library.Spam.__add__ is deprecated: There is enough spam in the world [deprecated] +directives_deprecated.py:42: error: function _directives_deprecated_library.Spam.__add__ is deprecated: There is enough spam in the world [deprecated] +directives_deprecated.py:44: error: function _directives_deprecated_library.Spam.greasy is deprecated: All spam will be equally greasy [deprecated] +directives_deprecated.py:47: error: function _directives_deprecated_library.Spam.shape is deprecated: Shapes are becoming immutable [deprecated] +directives_deprecated.py:48: error: function _directives_deprecated_library.Spam.shape is deprecated: Shapes are becoming immutable [deprecated] +directives_deprecated.py:58: error: function directives_deprecated.Invocable.__call__ is deprecated: Deprecated [deprecated] +directives_deprecated.py:69: error: function directives_deprecated.lorem is deprecated: Deprecated [deprecated] +directives_deprecated.py:98: error: function directives_deprecated.SupportsFoo1.foo is deprecated: Deprecated [deprecated] +""" diff --git a/conformance/results/mypy/directives_disjoint_base.toml b/conformance/results/mypy/directives_disjoint_base.toml new file mode 100644 index 000000000..77e4345df --- /dev/null +++ b/conformance/results/mypy/directives_disjoint_base.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_disjoint_base.py:69: error: Class "LeftAndRight" has incompatible disjoint bases [misc] +directives_disjoint_base.py:73: error: Class "LeftChildAndRight" has incompatible disjoint bases [misc] +directives_disjoint_base.py:77: error: Class "LeftAndRightViaChild" has incompatible disjoint bases [misc] +directives_disjoint_base.py:81: error: Class "LeftRecord" has incompatible disjoint bases [misc] +directives_disjoint_base.py:105: error: Class "IncompatibleSlots" has incompatible disjoint bases [misc] +directives_disjoint_base.py:113: error: Value of type variable "_TC" of "disjoint_base" cannot be "Callable[[], None]" [type-var] +directives_disjoint_base.py:118: error: @disjoint_base cannot be used with TypedDict [misc] +directives_disjoint_base.py:123: error: @disjoint_base cannot be used with protocol class [misc] +""" diff --git a/conformance/results/mypy/directives_no_type_check.toml b/conformance/results/mypy/directives_no_type_check.toml new file mode 100644 index 000000000..00d047592 --- /dev/null +++ b/conformance/results/mypy/directives_no_type_check.toml @@ -0,0 +1,12 @@ +conformant = "Partial" +notes = """ +Does not honor `@no_type_check` class decorator (allowed). +Does not reject invalid call of `@no_type_check` function. +""" +output = """ +directives_no_type_check.py:15: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 32: Expected 1 errors +""" diff --git a/conformance/results/mypy/directives_reveal_type.toml b/conformance/results/mypy/directives_reveal_type.toml new file mode 100644 index 000000000..41a8c8720 --- /dev/null +++ b/conformance/results/mypy/directives_reveal_type.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +output = """ +directives_reveal_type.py:14: note: Revealed type is "int | str" +directives_reveal_type.py:15: note: Revealed type is "list[int]" +directives_reveal_type.py:16: note: Revealed type is "Any" +directives_reveal_type.py:17: note: Revealed type is "directives_reveal_type.ForwardReference" +directives_reveal_type.py:19: error: "reveal_type" expects 1 argument [misc] +directives_reveal_type.py:19: error: Too few arguments for "reveal_type" [call-arg] +directives_reveal_type.py:20: error: "reveal_type" expects 1 argument [misc] +directives_reveal_type.py:20: error: Too many arguments for "reveal_type" [call-arg] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/directives_type_checking.toml b/conformance/results/mypy/directives_type_checking.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/mypy/directives_type_checking.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/directives_type_ignore.toml b/conformance/results/mypy/directives_type_ignore.toml new file mode 100644 index 000000000..318b2b2a2 --- /dev/null +++ b/conformance/results/mypy/directives_type_ignore.toml @@ -0,0 +1,14 @@ +conformant = "Partial" +notes = """ +Does not honor "# type: ignore" comment if comment includes additional text. +""" +output = """ +directives_type_ignore.py:11: error: Invalid "type: ignore" comment [syntax] +directives_type_ignore.py:11: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +directives_type_ignore.py:16: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +directives_type_ignore.py:16: note: Error code "assignment" not covered by "type: ignore[an-empty-str-is-not-an-int]" comment +""" +conformance_automated = "Fail" +errors_diff = """ +Line 11: Unexpected errors ['directives_type_ignore.py:11: error: Invalid "type: ignore" comment [syntax]', 'directives_type_ignore.py:11: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]'] +""" diff --git a/conformance/results/mypy/directives_type_ignore_file1.toml b/conformance/results/mypy/directives_type_ignore_file1.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/mypy/directives_type_ignore_file1.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/directives_type_ignore_file2.toml b/conformance/results/mypy/directives_type_ignore_file2.toml new file mode 100644 index 000000000..dae3196ca --- /dev/null +++ b/conformance/results/mypy/directives_type_ignore_file2.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +output = """ +directives_type_ignore_file2.py:14: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/directives_version_platform.toml b/conformance/results/mypy/directives_version_platform.toml new file mode 100644 index 000000000..e1fcc5822 --- /dev/null +++ b/conformance/results/mypy/directives_version_platform.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +notes = """ +Does not understand three-element form of sys.version checks. +Does not understand os.name checks. +""" +output = """ +directives_version_platform.py:26: error: Expression is of type "int | str", not "int" [assert-type] +directives_version_platform.py:33: error: Name "val3" is not defined; did you mean "val13"? [name-defined] +directives_version_platform.py:42: error: Expression is of type "int | str", not "int" [assert-type] +directives_version_platform.py:50: error: Name "val6" is not defined [name-defined] +directives_version_platform.py:59: error: Name "val9" is not defined [name-defined] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/enums_behaviors.toml b/conformance/results/mypy/enums_behaviors.toml new file mode 100644 index 000000000..a3193b611 --- /dev/null +++ b/conformance/results/mypy/enums_behaviors.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +enums_behaviors.py:28: error: Expression is of type "Color", not "Literal[Color.RED]" [assert-type] +enums_behaviors.py:32: error: Expression is of type "Color", not "Literal[Color.BLUE]" [assert-type] +enums_behaviors.py:44: error: Cannot extend enum with existing members: "Shape" [misc] +""" +conformance_automated = "Pass" diff --git a/conformance/results/mypy/enums_definition.toml b/conformance/results/mypy/enums_definition.toml new file mode 100644 index 000000000..06368c88e --- /dev/null +++ b/conformance/results/mypy/enums_definition.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +enums_definition.py:92: error: "type[Color12]" has no attribute "BLUE" [attr-defined] +""" +conformance_automated = "Pass" diff --git a/conformance/results/mypy/enums_expansion.toml b/conformance/results/mypy/enums_expansion.toml new file mode 100644 index 000000000..9858edfea --- /dev/null +++ b/conformance/results/mypy/enums_expansion.toml @@ -0,0 +1,12 @@ +conformant = "Partial" +notes = """ +Improperly applies narrowing to Flag subclass. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 53: Expected 1 errors +Line 52: Unexpected errors ['enums_expansion.py:52: error: Expression is of type "Literal[CustomFlags.FLAG3]", not "CustomFlags" [assert-type]'] +""" +output = """ +enums_expansion.py:52: error: Expression is of type "Literal[CustomFlags.FLAG3]", not "CustomFlags" [assert-type] +""" diff --git a/conformance/results/mypy/enums_member_names.toml b/conformance/results/mypy/enums_member_names.toml new file mode 100644 index 000000000..fc838dc78 --- /dev/null +++ b/conformance/results/mypy/enums_member_names.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +notes = """ +Does not support special-cased handling of member name literal types in some cases (optional). +""" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_member_names.py:26: error: Expression is of type "str", not "Literal['RED', 'BLUE']" [assert-type] +enums_member_names.py:30: error: Expression is of type "str", not "Literal['RED', 'BLUE', 'GREEN']" [assert-type] +""" diff --git a/conformance/results/mypy/enums_member_values.toml b/conformance/results/mypy/enums_member_values.toml new file mode 100644 index 000000000..f6ddcffe7 --- /dev/null +++ b/conformance/results/mypy/enums_member_values.toml @@ -0,0 +1,15 @@ +conformant = "Partial" +notes = """ +Does not enforce declared type of `_value_`. +Does not enforce assigned tuple types for enum members (optional). +""" +conformance_automated = "Fail" +errors_diff = """ +Line 78: Expected 1 errors +""" +output = """ +enums_member_values.py:26: error: Expression is of type "Any", not "Literal[1, 3]" [assert-type] +enums_member_values.py:54: error: Expression is of type "tuple[int, float, float]", not "Literal[1]" [assert-type] +enums_member_values.py:68: error: Expression is of type "int", not "Literal[1]" [assert-type] +enums_member_values.py:85: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment] +""" diff --git a/conformance/results/mypy/enums_members.toml b/conformance/results/mypy/enums_members.toml new file mode 100644 index 000000000..e1b2b5349 --- /dev/null +++ b/conformance/results/mypy/enums_members.toml @@ -0,0 +1,31 @@ +conformant = "Partial" +notes = """ +Does not treat attribute with annotation and no assignment as non-member. +Does not treat callables as non-members. +Does not honor `enum.member` as method decorator. +Does not properly handle aliased enum members. +Does not support `_ignore_` mechanism (optional). +Does not treat attributes with private names as non-members. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 86: Expected 1 errors +Line 87: Expected 1 errors +Line 133: Expected 1 errors +Line 104: Unexpected errors ['enums_members.py:104: error: Expression is of type "Literal[TrafficLight.AMBER]", not "Literal[TrafficLight.YELLOW]" [assert-type]'] +Line 121: Unexpected errors ['enums_members.py:121: error: Expression is of type "member[Callable[[Example], None]]", not "Any" [assert-type]', 'enums_members.py:121: error: Parameter 1 of Literal[...] is invalid [valid-type]'] +""" +output = """ +enums_members.py:54: error: Enum members must be left unannotated [misc] +enums_members.py:54: note: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members +enums_members.py:88: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:89: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:104: error: Expression is of type "Literal[TrafficLight.AMBER]", not "Literal[TrafficLight.YELLOW]" [assert-type] +enums_members.py:120: error: Expression is of type "int", not "Literal[Example.b]" [assert-type] +enums_members.py:121: error: Expression is of type "member[Callable[[Example], None]]", not "Any" [assert-type] +enums_members.py:121: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:132: note: Revealed type is "Any" +enums_members.py:132: note: 'reveal_type' always outputs 'Any' in unchecked functions +enums_members.py:150: error: Expression is of type "Literal[Pet5.DOG]", not "int" [assert-type] +enums_members.py:151: error: Expression is of type "Literal[Pet5.FISH]", not "int" [assert-type] +""" diff --git a/conformance/results/mypy/exceptions_context_managers.toml b/conformance/results/mypy/exceptions_context_managers.toml new file mode 100644 index 000000000..513dbe853 --- /dev/null +++ b/conformance/results/mypy/exceptions_context_managers.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +""" +conformance_automated = "Pass" diff --git a/conformance/results/mypy/generics_base_class.toml b/conformance/results/mypy/generics_base_class.toml new file mode 100644 index 000000000..a0c547eeb --- /dev/null +++ b/conformance/results/mypy/generics_base_class.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +notes = """ +Does not detect inconsistent type variable ordering. +""" +output = """ +generics_base_class.py:26: error: Argument 1 to "takes_dict_incorrect" has incompatible type "SymbolTable"; expected "dict[str, list[object]]" [arg-type] +generics_base_class.py:29: error: Variable "typing.Generic" is not valid as a type [valid-type] +generics_base_class.py:29: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_base_class.py:30: error: Variable "typing.Generic" is not valid as a type [valid-type] +generics_base_class.py:30: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_base_class.py:49: error: "LinkedList" expects 1 type argument, but 2 given [type-arg] +generics_base_class.py:61: error: "MyDict" expects 1 type argument, but 2 given [type-arg] +generics_base_class.py:68: error: Duplicate type variables in Generic[...] or Protocol[...] [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 98: Expected 1 errors +""" diff --git a/conformance/results/mypy/generics_basic.toml b/conformance/results/mypy/generics_basic.toml new file mode 100644 index 000000000..3c95445b9 --- /dev/null +++ b/conformance/results/mypy/generics_basic.toml @@ -0,0 +1,22 @@ +conformant = "Pass" +output = """ +generics_basic.py:40: error: Value of type variable "AnyStr" of "concat" cannot be "Sequence[object]" [type-var] +generics_basic.py:41: error: Value of type variable "AnyStr" of "concat" cannot be "Sequence[object]" [type-var] +generics_basic.py:49: error: Type variable must have at least two constrained types [misc] +generics_basic.py:55: error: TypeVar constraint type cannot be parametrized by type variables [misc] +generics_basic.py:69: error: Value of type variable "AnyStr" of "concat" cannot be "Sequence[object]" [type-var] +generics_basic.py:121: error: Duplicate type variables in Generic[...] or Protocol[...] [misc] +generics_basic.py:157: error: Invalid index type "int" for "MyMap1[str, int]"; expected type "str" [index] +generics_basic.py:158: error: Invalid index type "int" for "MyMap2[int, str]"; expected type "str" [index] +generics_basic.py:162: error: Free type variable expected in Generic[...] [misc] +generics_basic.py:163: error: Free type variable expected in Protocol[...] [misc] +generics_basic.py:171: error: If Generic[...] or Protocol[...] is present it should list all type variables [misc] +generics_basic.py:172: error: If Generic[...] or Protocol[...] is present it should list all type variables [misc] +generics_basic.py:208: error: Dynamic metaclass not supported for "GenericMetaInstance" [metaclass] +generics_basic.py:208: error: Type variable "generics_basic.T" is unbound [valid-type] +generics_basic.py:208: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +generics_basic.py:208: note: (Hint: Use "T" in function signature to bind "T" inside a function) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_defaults.toml b/conformance/results/mypy/generics_defaults.toml new file mode 100644 index 000000000..5b18af90b --- /dev/null +++ b/conformance/results/mypy/generics_defaults.toml @@ -0,0 +1,24 @@ +conformant = "Partial" +notes = """ +Does not detect a TypeVar with a default used after a TypeVarTuple. +Does not fully support defaults on TypeVarTuple and ParamSpec. +""" +output = """ +generics_defaults.py:24: error: "T" cannot appear after "DefaultStrT" in type parameter list because it has no default type [misc] +generics_defaults.py:66: error: "AllTheDefaults" expects between 2 and 5 type arguments, but 1 given [type-arg] +generics_defaults.py:139: error: Expression is of type "tuple[*tuple[str, int], ...]", not "tuple[str, int]" [assert-type] +generics_defaults.py:152: error: TypeVar default must be a subtype of the bound type [misc] +generics_defaults.py:159: error: TypeVar default must be one of the constraint types [misc] +generics_defaults.py:177: error: Expression is of type "int", not "Any" [assert-type] +generics_defaults.py:203: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "str" [valid-type] +generics_defaults.py:204: error: Expression is of type "tuple[Any, ...]", not "tuple[int, str]" [assert-type] +generics_defaults.py:205: error: Expression is of type "def (*Any, **Any) -> None", not "Callable[[float, bool], None]" [assert-type] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 188: Expected 1 errors +Line 139: Unexpected errors ['generics_defaults.py:139: error: Expression is of type "tuple[*tuple[str, int], ...]", not "tuple[str, int]" [assert-type]'] +Line 203: Unexpected errors ['generics_defaults.py:203: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "str" [valid-type]'] +Line 204: Unexpected errors ['generics_defaults.py:204: error: Expression is of type "tuple[Any, ...]", not "tuple[int, str]" [assert-type]'] +Line 205: Unexpected errors ['generics_defaults.py:205: error: Expression is of type "def (*Any, **Any) -> None", not "Callable[[float, bool], None]" [assert-type]'] +""" diff --git a/conformance/results/mypy/generics_defaults_referential.toml b/conformance/results/mypy/generics_defaults_referential.toml new file mode 100644 index 000000000..e8484dc7d --- /dev/null +++ b/conformance/results/mypy/generics_defaults_referential.toml @@ -0,0 +1,22 @@ +conformant = "Partial" +notes = """ +Does not correctly handle defaults referencing other TypeVars. +""" +output = """ +generics_defaults_referential.py:23: error: Expression is of type "type[slice[StartT, StopT, StepT]]", not "type[slice[int, int, int | None]]" [assert-type] +generics_defaults_referential.py:38: error: Argument 1 to "Foo" has incompatible type "str"; expected "int" [arg-type] +generics_defaults_referential.py:54: error: Type parameter "Start2T" has a default type that refers to one or more type variables that are out of scope [misc] +generics_defaults_referential.py:61: error: Type variable S1 referenced in the default of S2 is unbound [misc] +generics_defaults_referential.py:75: error: TypeVar default must be one of the constraint types [misc] +generics_defaults_referential.py:78: error: TypeVar default must be one of the constraint types [misc] +generics_defaults_referential.py:79: error: TypeVar default must be one of the constraint types [misc] +generics_defaults_referential.py:104: error: Expression is of type "Bar[int, list[Never]]", not "Bar[int, list[int]]" [assert-type] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 37: Expected 1 errors +Line 69: Expected 1 errors +Line 23: Unexpected errors ['generics_defaults_referential.py:23: error: Expression is of type "type[slice[StartT, StopT, StepT]]", not "type[slice[int, int, int | None]]" [assert-type]'] +Line 78: Unexpected errors ['generics_defaults_referential.py:78: error: TypeVar default must be one of the constraint types [misc]'] +Line 104: Unexpected errors ['generics_defaults_referential.py:104: error: Expression is of type "Bar[int, list[Never]]", not "Bar[int, list[int]]" [assert-type]'] +""" diff --git a/conformance/results/mypy/generics_defaults_specialization.toml b/conformance/results/mypy/generics_defaults_specialization.toml new file mode 100644 index 000000000..1aab9fb8d --- /dev/null +++ b/conformance/results/mypy/generics_defaults_specialization.toml @@ -0,0 +1,12 @@ +conformant = "Partial" +notes = """ +Does not correctly resolve defaults when classes are used directly. +""" +output = """ +generics_defaults_specialization.py:30: error: Bad number of arguments for type alias, expected between 0 and 1, given 2 [type-arg] +generics_defaults_specialization.py:56: error: The type "type[Foo]" is not generic and not indexable [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 46: Expected 1 errors +""" diff --git a/conformance/results/mypy/generics_paramspec_basic.toml b/conformance/results/mypy/generics_paramspec_basic.toml new file mode 100644 index 000000000..538f6de4d --- /dev/null +++ b/conformance/results/mypy/generics_paramspec_basic.toml @@ -0,0 +1,19 @@ +conformant = "Pass" +output = """ +generics_paramspec_basic.py:10: error: String argument 1 "NotIt" to ParamSpec(...) does not match variable name "WrongName" [misc] +generics_paramspec_basic.py:15: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:15: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:23: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:23: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:27: error: Invalid location for Concatenate [valid-type] +generics_paramspec_basic.py:27: note: You can use Concatenate as the first argument to Callable +generics_paramspec_basic.py:31: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:31: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:35: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:35: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:39: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:39: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_paramspec_components.toml b/conformance/results/mypy/generics_paramspec_components.toml new file mode 100644 index 000000000..bdc5f0b0f --- /dev/null +++ b/conformance/results/mypy/generics_paramspec_components.toml @@ -0,0 +1,28 @@ +conformant = "Pass" +output = """ +generics_paramspec_components.py:17: error: Use "P.args" for variadic "*" parameter [valid-type] +generics_paramspec_components.py:17: error: Use "P.kwargs" for variadic "**" parameter [valid-type] +generics_paramspec_components.py:20: error: ParamSpec components are not allowed here [valid-type] +generics_paramspec_components.py:23: error: Use "P.kwargs" for variadic "**" parameter [valid-type] +generics_paramspec_components.py:26: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [valid-type] +generics_paramspec_components.py:30: error: ParamSpec "P" is unbound [valid-type] +generics_paramspec_components.py:35: error: ParamSpec components are not allowed here [valid-type] +generics_paramspec_components.py:36: error: ParamSpec components are not allowed here [valid-type] +generics_paramspec_components.py:38: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [valid-type] +generics_paramspec_components.py:41: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [valid-type] +generics_paramspec_components.py:49: error: Argument 1 has incompatible type "*P.kwargs"; expected "P.args" [arg-type] +generics_paramspec_components.py:49: error: Argument 2 has incompatible type "**P.args"; expected "P.kwargs" [arg-type] +generics_paramspec_components.py:51: error: Argument 1 has incompatible type "int"; expected "P.args" [arg-type] +generics_paramspec_components.py:60: error: Arguments not allowed after ParamSpec.args [valid-type] +generics_paramspec_components.py:70: error: Argument 1 has incompatible type "*P.args"; expected "int" [arg-type] +generics_paramspec_components.py:70: error: Argument 2 has incompatible type "int"; expected "P.args" [arg-type] +generics_paramspec_components.py:72: error: Argument 1 has incompatible type "*P.args"; expected "int" [arg-type] +generics_paramspec_components.py:83: error: "foo" gets multiple values for keyword argument "x" [misc] +generics_paramspec_components.py:83: error: Argument 1 to "foo" has incompatible type "*P.args"; expected "int" [arg-type] +generics_paramspec_components.py:83: error: Argument 3 to "foo" has incompatible type "**P.kwargs"; expected "int" [arg-type] +generics_paramspec_components.py:98: error: Argument 2 to "twice" has incompatible type "str"; expected "int" [arg-type] +generics_paramspec_components.py:98: error: Argument 3 to "twice" has incompatible type "int"; expected "str" [arg-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_paramspec_semantics.toml b/conformance/results/mypy/generics_paramspec_semantics.toml new file mode 100644 index 000000000..7dc429c76 --- /dev/null +++ b/conformance/results/mypy/generics_paramspec_semantics.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +output = """ +generics_paramspec_semantics.py:26: error: Unexpected keyword argument "a" [call-arg] +generics_paramspec_semantics.py:26: error: Unexpected keyword argument "b" [call-arg] +generics_paramspec_semantics.py:27: error: Argument 2 has incompatible type "str"; expected "bool" [arg-type] +generics_paramspec_semantics.py:61: error: Argument 2 to "func1" has incompatible type "def keyword_only_y(*, y: int) -> int"; expected "def (*, x: int) -> int" [arg-type] +generics_paramspec_semantics.py:98: error: Argument 1 has incompatible type "int"; expected "str" [arg-type] +generics_paramspec_semantics.py:108: error: Argument 1 has incompatible type "int"; expected "bool" [arg-type] +generics_paramspec_semantics.py:120: error: Argument 1 has incompatible type "int"; expected "str" [arg-type] +generics_paramspec_semantics.py:127: error: Argument 1 to "expects_int_first" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]" [arg-type] +generics_paramspec_semantics.py:127: note: This is likely because "one" has named arguments: "x". Consider marking them positional-only +generics_paramspec_semantics.py:132: error: Argument 1 to "expects_int_first" has incompatible type "def two(*, x: int) -> int"; expected "def (int, /, *, x: int) -> int" [arg-type] +generics_paramspec_semantics.py:137: error: Argument 1 to "expects_int_first" has incompatible type "def three(**kwargs: int) -> int"; expected "def (int, /, **kwargs: int) -> int" [arg-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_paramspec_specialization.toml b/conformance/results/mypy/generics_paramspec_specialization.toml new file mode 100644 index 000000000..c6d433147 --- /dev/null +++ b/conformance/results/mypy/generics_paramspec_specialization.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +generics_paramspec_specialization.py:44: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [valid-type] +generics_paramspec_specialization.py:54: error: Argument 1 has incompatible type "str"; expected "int" [arg-type] +generics_paramspec_specialization.py:55: error: Argument 3 has incompatible type "str"; expected "bool" [arg-type] +generics_paramspec_specialization.py:60: error: Argument 1 has incompatible type "str"; expected "int" [arg-type] +generics_paramspec_specialization.py:61: error: Argument 3 has incompatible type "str"; expected "bool" [arg-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_scoping.toml b/conformance/results/mypy/generics_scoping.toml new file mode 100644 index 000000000..013baaa7c --- /dev/null +++ b/conformance/results/mypy/generics_scoping.toml @@ -0,0 +1,31 @@ +output = """ +generics_scoping.py:16: error: Expression is of type "int", not "Literal[1]" [assert-type] +generics_scoping.py:20: error: Expression is of type "str", not "Literal['a']" [assert-type] +generics_scoping.py:34: error: Argument 1 to "meth_2" of "MyClass" has incompatible type "str"; expected "int" [arg-type] +generics_scoping.py:50: error: Expression is of type "str", not "Literal['abc']" [assert-type] +generics_scoping.py:54: error: Expression is of type "bytes", not "Literal[b'abc']" [assert-type] +generics_scoping.py:61: error: Type variable "generics_scoping.S" is unbound [valid-type] +generics_scoping.py:61: note: (Hint: Use "Generic[S]" or "Protocol[S]" base class to bind "S" inside a class) +generics_scoping.py:61: note: (Hint: Use "S" in function signature to bind "S" inside a function) +generics_scoping.py:65: error: Type variable "generics_scoping.S" is unbound [valid-type] +generics_scoping.py:65: note: (Hint: Use "Generic[S]" or "Protocol[S]" base class to bind "S" inside a class) +generics_scoping.py:65: note: (Hint: Use "S" in function signature to bind "S" inside a function) +generics_scoping.py:76: error: Free type variable expected in Generic[...] [misc] +generics_scoping.py:86: error: Type variable "T" is bound by an outer class [valid-type] +generics_scoping.py:89: error: Type variable "generics_scoping.T" is unbound [valid-type] +generics_scoping.py:89: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +generics_scoping.py:89: note: (Hint: Use "T" in function signature to bind "T" inside a function) +generics_scoping.py:98: error: Can't use bound type variable "T" to define generic alias [valid-type] +generics_scoping.py:105: error: Type variable "generics_scoping.T" is unbound [valid-type] +generics_scoping.py:105: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +generics_scoping.py:105: note: (Hint: Use "T" in function signature to bind "T" inside a function) +generics_scoping.py:106: error: Type variable "generics_scoping.T" is unbound [valid-type] +generics_scoping.py:106: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +generics_scoping.py:106: note: (Hint: Use "T" in function signature to bind "T" inside a function) +generics_scoping.py:107: error: Type variable "generics_scoping.T" is unbound [valid-type] +generics_scoping.py:107: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +generics_scoping.py:107: note: (Hint: Use "T" in function signature to bind "T" inside a function) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_self_advanced.toml b/conformance/results/mypy/generics_self_advanced.toml new file mode 100644 index 000000000..4581356b3 --- /dev/null +++ b/conformance/results/mypy/generics_self_advanced.toml @@ -0,0 +1,20 @@ +conformant = "Partial" +notes = """ +Does not infer the type of an unannotated `self` parameter to be type `Self`. +Does not retain `Self` when calling method that returns `Self`. +Does not infer the type of an unannotated `cls` parameter to be type `type[Self]`. +Does not retain `Self` when accessing attribute through `type[Self]`. +""" +output = """ +generics_self_advanced.py:35: error: Expression is of type "ChildB", not "Self" [assert-type] +generics_self_advanced.py:41: error: Expression is of type "ChildB", not "Self" [assert-type] +generics_self_advanced.py:45: error: Expression is of type "type[ChildB]", not "type[Self]" [assert-type] +generics_self_advanced.py:51: error: Expression is of type "ChildB", not "Self" [assert-type] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 35: Unexpected errors ['generics_self_advanced.py:35: error: Expression is of type "ChildB", not "Self" [assert-type]'] +Line 41: Unexpected errors ['generics_self_advanced.py:41: error: Expression is of type "ChildB", not "Self" [assert-type]'] +Line 45: Unexpected errors ['generics_self_advanced.py:45: error: Expression is of type "type[ChildB]", not "type[Self]" [assert-type]'] +Line 51: Unexpected errors ['generics_self_advanced.py:51: error: Expression is of type "ChildB", not "Self" [assert-type]'] +""" diff --git a/conformance/results/mypy/generics_self_attributes.toml b/conformance/results/mypy/generics_self_attributes.toml new file mode 100644 index 000000000..46a1ee628 --- /dev/null +++ b/conformance/results/mypy/generics_self_attributes.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +generics_self_attributes.py:26: error: Argument "next" to "OrdinalLinkedList" has incompatible type "LinkedList[int]"; expected "OrdinalLinkedList | None" [arg-type] +generics_self_attributes.py:32: error: Incompatible types in assignment (expression has type "LinkedList[int]", variable has type "OrdinalLinkedList | None") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_self_basic.toml b/conformance/results/mypy/generics_self_basic.toml new file mode 100644 index 000000000..2502d0de1 --- /dev/null +++ b/conformance/results/mypy/generics_self_basic.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +output = """ +generics_self_basic.py:20: error: Incompatible return value type (got "Shape", expected "Self") [return-value] +generics_self_basic.py:33: error: Incompatible return value type (got "Shape", expected "Self") [return-value] +generics_self_basic.py:68: error: Self type cannot have type arguments [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_self_protocols.toml b/conformance/results/mypy/generics_self_protocols.toml new file mode 100644 index 000000000..365da1f02 --- /dev/null +++ b/conformance/results/mypy/generics_self_protocols.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +output = """ +generics_self_protocols.py:61: error: Argument 1 to "accepts_shape" has incompatible type "BadReturnType"; expected "ShapeProtocol" [arg-type] +generics_self_protocols.py:61: note: Following member(s) of "BadReturnType" have conflicts: +generics_self_protocols.py:61: note: Expected: +generics_self_protocols.py:61: note: def set_scale(self, scale: float) -> BadReturnType +generics_self_protocols.py:61: note: Got: +generics_self_protocols.py:61: note: def set_scale(self, scale: float) -> int +generics_self_protocols.py:64: error: Argument 1 to "accepts_shape" has incompatible type "ReturnDifferentClass"; expected "ShapeProtocol" [arg-type] +generics_self_protocols.py:64: note: Following member(s) of "ReturnDifferentClass" have conflicts: +generics_self_protocols.py:64: note: Expected: +generics_self_protocols.py:64: note: def set_scale(self, scale: float) -> ReturnDifferentClass +generics_self_protocols.py:64: note: Got: +generics_self_protocols.py:64: note: def set_scale(self, scale: float) -> ReturnConcreteShape +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_self_usage.toml b/conformance/results/mypy/generics_self_usage.toml new file mode 100644 index 000000000..b13852536 --- /dev/null +++ b/conformance/results/mypy/generics_self_usage.toml @@ -0,0 +1,22 @@ +conformant = "Pass" +output = """ +generics_self_usage.py:73: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:76: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:82: error: Method cannot have explicit self annotation and Self type [misc] +generics_self_usage.py:82: error: A function returning TypeVar should receive at least one argument containing the same TypeVar [type-var] +generics_self_usage.py:82: note: Consider using the upper bound "Foo2" instead +generics_self_usage.py:87: error: Incompatible return value type (got "Foo3", expected "Self") [return-value] +generics_self_usage.py:103: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:105: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:108: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:108: error: Self type cannot be used in type alias target [misc] +generics_self_usage.py:113: error: Static methods cannot use Self type [misc] +generics_self_usage.py:113: error: A function returning TypeVar should receive at least one argument containing the same TypeVar [type-var] +generics_self_usage.py:113: note: Consider using the upper bound "Base" instead +generics_self_usage.py:118: error: Static methods cannot use Self type [misc] +generics_self_usage.py:123: error: Self type cannot be used in a metaclass [misc] +generics_self_usage.py:127: error: Self type cannot be used in a metaclass [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_syntax_compatibility.toml b/conformance/results/mypy/generics_syntax_compatibility.toml new file mode 100644 index 000000000..d40eccb49 --- /dev/null +++ b/conformance/results/mypy/generics_syntax_compatibility.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +generics_syntax_compatibility.py:14: error: All type parameters should be declared ("K" not declared) [valid-type] +generics_syntax_compatibility.py:26: error: All type parameters should be declared ("K" not declared) [valid-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_syntax_declarations.toml b/conformance/results/mypy/generics_syntax_declarations.toml new file mode 100644 index 000000000..66d51c931 --- /dev/null +++ b/conformance/results/mypy/generics_syntax_declarations.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +output = """ +generics_syntax_declarations.py:17: error: Generic[...] base class is redundant [misc] +generics_syntax_declarations.py:25: error: No arguments expected for "Protocol" base class [misc] +generics_syntax_declarations.py:32: error: "T" has no attribute "is_integer" [attr-defined] +generics_syntax_declarations.py:44: error: Name "V" is not defined [name-defined] +generics_syntax_declarations.py:48: error: Bracketed expression "[...]" is not valid as a type [valid-type] +generics_syntax_declarations.py:60: error: Type variable must have at least two constrained types [misc] +generics_syntax_declarations.py:64: error: Type variable must have at least two constrained types [misc] +generics_syntax_declarations.py:71: error: Variable "generics_syntax_declarations.t1" is not valid as a type [valid-type] +generics_syntax_declarations.py:71: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_declarations.py:75: error: Invalid type: try using Literal[3] instead? [valid-type] +generics_syntax_declarations.py:79: error: Name "S" is not defined [name-defined] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_syntax_infer_variance.toml b/conformance/results/mypy/generics_syntax_infer_variance.toml new file mode 100644 index 000000000..f664ea38e --- /dev/null +++ b/conformance/results/mypy/generics_syntax_infer_variance.toml @@ -0,0 +1,146 @@ +conformant = "Unsupported" +notes = """ +Type parameter syntax not yet supported. +""" +output = """ +generics_syntax_infer_variance.py:11: error: Unexpected argument to "TypeVar()": "infer_variance" [misc] +generics_syntax_infer_variance.py:12: error: Unexpected argument to "TypeVar()": "infer_variance" [misc] +generics_syntax_infer_variance.py:13: error: Unexpected argument to "TypeVar()": "infer_variance" [misc] +generics_syntax_infer_variance.py:15: error: Unexpected argument to "TypeVar()": "infer_variance" [misc] +generics_syntax_infer_variance.py:17: error: Unexpected argument to "TypeVar()": "infer_variance" [misc] +generics_syntax_infer_variance.py:20: error: Free type variable expected in Generic[...] [misc] +generics_syntax_infer_variance.py:21: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:21: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:24: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:24: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:28: error: "ShouldBeCovariant1" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:28: error: The type "type[ShouldBeCovariant1]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:29: error: "ShouldBeCovariant1" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:29: error: The type "type[ShouldBeCovariant1]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:32: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:32: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:34: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:34: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:37: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:37: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:39: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:39: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:46: error: "ShouldBeCovariant2" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:46: error: The type "type[ShouldBeCovariant2]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:47: error: "ShouldBeCovariant2" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:47: error: The type "type[ShouldBeCovariant2]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:50: error: Free type variable expected in Generic[...] [misc] +generics_syntax_infer_variance.py:51: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:51: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:51: error: "ShouldBeCovariant2" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:55: error: "ShouldBeCovariant3" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:55: error: The type "type[ShouldBeCovariant3]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:56: error: "ShouldBeCovariant3" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:56: error: The type "type[ShouldBeCovariant3]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:60: error: Free type variable expected in Generic[...] [misc] +generics_syntax_infer_variance.py:61: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:61: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:75: error: Free type variable expected in Generic[...] [misc] +generics_syntax_infer_variance.py:76: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:76: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:80: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:80: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:84: error: "ShouldBeCovariant5" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:84: error: The type "type[ShouldBeCovariant5]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:85: error: "ShouldBeCovariant5" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:85: error: The type "type[ShouldBeCovariant5]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:88: error: Free type variable expected in Generic[...] [misc] +generics_syntax_infer_variance.py:89: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:89: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:91: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:91: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:95: error: "ShouldBeCovariant6" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:95: error: The type "type[ShouldBeCovariant6]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:96: error: "ShouldBeCovariant6" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:96: error: The type "type[ShouldBeCovariant6]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:99: error: Free type variable expected in Generic[...] [misc] +generics_syntax_infer_variance.py:100: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:100: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:104: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:104: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:108: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:108: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:112: error: "ShouldBeInvariant1" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:112: error: The type "type[ShouldBeInvariant1]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:113: error: "ShouldBeInvariant1" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:113: error: The type "type[ShouldBeInvariant1]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:116: error: Free type variable expected in Generic[...] [misc] +generics_syntax_infer_variance.py:117: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:117: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:120: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:120: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:123: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:123: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:127: error: "ShouldBeInvariant2" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:127: error: The type "type[ShouldBeInvariant2]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:128: error: "ShouldBeInvariant2" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:128: error: The type "type[ShouldBeInvariant2]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:131: error: Variable "generics_syntax_infer_variance.K" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:131: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:131: error: Variable "generics_syntax_infer_variance.V" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:135: error: "ShouldBeInvariant3" expects no type arguments, but 2 given [type-arg] +generics_syntax_infer_variance.py:135: error: The type "type[ShouldBeInvariant3]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:136: error: "ShouldBeInvariant3" expects no type arguments, but 2 given [type-arg] +generics_syntax_infer_variance.py:136: error: The type "type[ShouldBeInvariant3]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:137: error: "ShouldBeInvariant3" expects no type arguments, but 2 given [type-arg] +generics_syntax_infer_variance.py:137: error: The type "type[ShouldBeInvariant3]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:138: error: "ShouldBeInvariant3" expects no type arguments, but 2 given [type-arg] +generics_syntax_infer_variance.py:138: error: The type "type[ShouldBeInvariant3]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:146: error: Incompatible types in assignment (expression has type "ShouldBeInvariant4[int]", variable has type "ShouldBeInvariant4[float]") [assignment] +generics_syntax_infer_variance.py:154: error: Incompatible types in assignment (expression has type "ShouldBeInvariant5[int]", variable has type "ShouldBeInvariant5[float]") [assignment] +generics_syntax_infer_variance.py:157: error: Free type variable expected in Generic[...] [misc] +generics_syntax_infer_variance.py:158: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:158: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:161: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type] +generics_syntax_infer_variance.py:161: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_infer_variance.py:165: error: "ShouldBeContravariant1" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:165: error: The type "type[ShouldBeContravariant1]" is not generic and not indexable [misc] +generics_syntax_infer_variance.py:166: error: "ShouldBeContravariant1" expects no type arguments, but 1 given [type-arg] +generics_syntax_infer_variance.py:166: error: The type "type[ShouldBeContravariant1]" is not generic and not indexable [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 11: Unexpected errors ['generics_syntax_infer_variance.py:11: error: Unexpected argument to "TypeVar()": "infer_variance" [misc]'] +Line 12: Unexpected errors ['generics_syntax_infer_variance.py:12: error: Unexpected argument to "TypeVar()": "infer_variance" [misc]'] +Line 13: Unexpected errors ['generics_syntax_infer_variance.py:13: error: Unexpected argument to "TypeVar()": "infer_variance" [misc]'] +Line 20: Unexpected errors ['generics_syntax_infer_variance.py:20: error: Free type variable expected in Generic[...] [misc]'] +Line 21: Unexpected errors ['generics_syntax_infer_variance.py:21: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 24: Unexpected errors ['generics_syntax_infer_variance.py:24: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 28: Unexpected errors ['generics_syntax_infer_variance.py:28: error: "ShouldBeCovariant1" expects no type arguments, but 1 given [type-arg]', 'generics_syntax_infer_variance.py:28: error: The type "type[ShouldBeCovariant1]" is not generic and not indexable [misc]'] +Line 32: Unexpected errors ['generics_syntax_infer_variance.py:32: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 34: Unexpected errors ['generics_syntax_infer_variance.py:34: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 37: Unexpected errors ['generics_syntax_infer_variance.py:37: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 39: Unexpected errors ['generics_syntax_infer_variance.py:39: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 46: Unexpected errors ['generics_syntax_infer_variance.py:46: error: "ShouldBeCovariant2" expects no type arguments, but 1 given [type-arg]', 'generics_syntax_infer_variance.py:46: error: The type "type[ShouldBeCovariant2]" is not generic and not indexable [misc]'] +Line 50: Unexpected errors ['generics_syntax_infer_variance.py:50: error: Free type variable expected in Generic[...] [misc]'] +Line 51: Unexpected errors ['generics_syntax_infer_variance.py:51: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]', 'generics_syntax_infer_variance.py:51: error: "ShouldBeCovariant2" expects no type arguments, but 1 given [type-arg]'] +Line 55: Unexpected errors ['generics_syntax_infer_variance.py:55: error: "ShouldBeCovariant3" expects no type arguments, but 1 given [type-arg]', 'generics_syntax_infer_variance.py:55: error: The type "type[ShouldBeCovariant3]" is not generic and not indexable [misc]'] +Line 60: Unexpected errors ['generics_syntax_infer_variance.py:60: error: Free type variable expected in Generic[...] [misc]'] +Line 61: Unexpected errors ['generics_syntax_infer_variance.py:61: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 75: Unexpected errors ['generics_syntax_infer_variance.py:75: error: Free type variable expected in Generic[...] [misc]'] +Line 76: Unexpected errors ['generics_syntax_infer_variance.py:76: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 80: Unexpected errors ['generics_syntax_infer_variance.py:80: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 84: Unexpected errors ['generics_syntax_infer_variance.py:84: error: "ShouldBeCovariant5" expects no type arguments, but 1 given [type-arg]', 'generics_syntax_infer_variance.py:84: error: The type "type[ShouldBeCovariant5]" is not generic and not indexable [misc]'] +Line 88: Unexpected errors ['generics_syntax_infer_variance.py:88: error: Free type variable expected in Generic[...] [misc]'] +Line 89: Unexpected errors ['generics_syntax_infer_variance.py:89: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 91: Unexpected errors ['generics_syntax_infer_variance.py:91: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 95: Unexpected errors ['generics_syntax_infer_variance.py:95: error: "ShouldBeCovariant6" expects no type arguments, but 1 given [type-arg]', 'generics_syntax_infer_variance.py:95: error: The type "type[ShouldBeCovariant6]" is not generic and not indexable [misc]'] +Line 99: Unexpected errors ['generics_syntax_infer_variance.py:99: error: Free type variable expected in Generic[...] [misc]'] +Line 100: Unexpected errors ['generics_syntax_infer_variance.py:100: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 104: Unexpected errors ['generics_syntax_infer_variance.py:104: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 108: Unexpected errors ['generics_syntax_infer_variance.py:108: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 116: Unexpected errors ['generics_syntax_infer_variance.py:116: error: Free type variable expected in Generic[...] [misc]'] +Line 117: Unexpected errors ['generics_syntax_infer_variance.py:117: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 120: Unexpected errors ['generics_syntax_infer_variance.py:120: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 123: Unexpected errors ['generics_syntax_infer_variance.py:123: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 131: Unexpected errors ['generics_syntax_infer_variance.py:131: error: Variable "generics_syntax_infer_variance.K" is not valid as a type [valid-type]', 'generics_syntax_infer_variance.py:131: error: Variable "generics_syntax_infer_variance.V" is not valid as a type [valid-type]'] +Line 157: Unexpected errors ['generics_syntax_infer_variance.py:157: error: Free type variable expected in Generic[...] [misc]'] +Line 158: Unexpected errors ['generics_syntax_infer_variance.py:158: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 161: Unexpected errors ['generics_syntax_infer_variance.py:161: error: Variable "generics_syntax_infer_variance.T" is not valid as a type [valid-type]'] +Line 166: Unexpected errors ['generics_syntax_infer_variance.py:166: error: "ShouldBeContravariant1" expects no type arguments, but 1 given [type-arg]', 'generics_syntax_infer_variance.py:166: error: The type "type[ShouldBeContravariant1]" is not generic and not indexable [misc]'] +""" diff --git a/conformance/results/mypy/generics_syntax_scoping.toml b/conformance/results/mypy/generics_syntax_scoping.toml new file mode 100644 index 000000000..34cc33a00 --- /dev/null +++ b/conformance/results/mypy/generics_syntax_scoping.toml @@ -0,0 +1,36 @@ +conformant = "Partial" +notes = """ +Does not following runtime scoping rules for type parameters in all cases. +""" +output = """ +generics_syntax_scoping.py:14: error: Name "S" is not defined [name-defined] +generics_syntax_scoping.py:18: error: Variable "generics_syntax_scoping.T" is not valid as a type [valid-type] +generics_syntax_scoping.py:18: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_scoping.py:35: error: Cannot determine type of "T" [has-type] +generics_syntax_scoping.py:35: error: Name "T" is used before definition [used-before-def] +generics_syntax_scoping.py:44: error: Variable "generics_syntax_scoping.T" is not valid as a type [valid-type] +generics_syntax_scoping.py:44: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_scoping.py:62: error: Expression is of type "Any", not "str" [assert-type] +generics_syntax_scoping.py:62: note: "assert_type" expects everything to be "Any" in unchecked functions +generics_syntax_scoping.py:67: error: Expression is of type "Any", not "int" [assert-type] +generics_syntax_scoping.py:67: note: "assert_type" expects everything to be "Any" in unchecked functions +generics_syntax_scoping.py:92: error: "T" already defined as a type parameter [misc] +generics_syntax_scoping.py:95: error: "T" already defined as a type parameter [misc] +generics_syntax_scoping.py:98: error: "T" already defined as a type parameter [misc] +generics_syntax_scoping.py:98: error: Variable "generics_syntax_scoping.ClassE.T" is not valid as a type [valid-type] +generics_syntax_scoping.py:98: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_scoping.py:117: error: Expression is of type "Any", not "TypeVar" [assert-type] +generics_syntax_scoping.py:117: note: "assert_type" expects everything to be "Any" in unchecked functions +generics_syntax_scoping.py:122: error: Expression is of type "Any", not "complex" [assert-type] +generics_syntax_scoping.py:122: note: "assert_type" expects everything to be "Any" in unchecked functions +generics_syntax_scoping.py:125: error: Expression is of type "Any", not "complex" [assert-type] +generics_syntax_scoping.py:125: note: "assert_type" expects everything to be "Any" in unchecked functions +""" +conformance_automated = "Fail" +errors_diff = """ +Line 62: Unexpected errors ['generics_syntax_scoping.py:62: error: Expression is of type "Any", not "str" [assert-type]'] +Line 67: Unexpected errors ['generics_syntax_scoping.py:67: error: Expression is of type "Any", not "int" [assert-type]'] +Line 117: Unexpected errors ['generics_syntax_scoping.py:117: error: Expression is of type "Any", not "TypeVar" [assert-type]'] +Line 122: Unexpected errors ['generics_syntax_scoping.py:122: error: Expression is of type "Any", not "complex" [assert-type]'] +Line 125: Unexpected errors ['generics_syntax_scoping.py:125: error: Expression is of type "Any", not "complex" [assert-type]'] +""" diff --git a/conformance/results/mypy/generics_type_erasure.toml b/conformance/results/mypy/generics_type_erasure.toml new file mode 100644 index 000000000..46c739fee --- /dev/null +++ b/conformance/results/mypy/generics_type_erasure.toml @@ -0,0 +1,21 @@ +conformant = "Partial" +notes = """ +Infers Node[Never] instead of Node[Any] when argument is not provided. +False negative on instance attribute access on type(node). +""" +output = """ +generics_type_erasure.py:19: error: Expression is of type "Node[Never]", not "Node[Any]" [assert-type] +generics_type_erasure.py:22: error: Expression is of type "Never", not "Any" [assert-type] +generics_type_erasure.py:38: error: Argument 1 to "Node" has incompatible type "str"; expected "int | None" [arg-type] +generics_type_erasure.py:40: error: Argument 1 to "Node" has incompatible type "int"; expected "str | None" [arg-type] +generics_type_erasure.py:42: error: Access to generic instance variables via class is ambiguous [misc] +generics_type_erasure.py:43: error: Access to generic instance variables via class is ambiguous [misc] +generics_type_erasure.py:44: error: Access to generic instance variables via class is ambiguous [misc] +generics_type_erasure.py:45: error: Access to generic instance variables via class is ambiguous [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 46: Expected 1 errors +Line 19: Unexpected errors ['generics_type_erasure.py:19: error: Expression is of type "Node[Never]", not "Node[Any]" [assert-type]'] +Line 22: Unexpected errors ['generics_type_erasure.py:22: error: Expression is of type "Never", not "Any" [assert-type]'] +""" diff --git a/conformance/results/mypy/generics_typevartuple_args.toml b/conformance/results/mypy/generics_typevartuple_args.toml new file mode 100644 index 000000000..43d9fa078 --- /dev/null +++ b/conformance/results/mypy/generics_typevartuple_args.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +notes = """ +Does not enforce that tuples captured by TypeVarTuple are of the same length. +""" +output = """ +generics_typevartuple_args.py:33: error: Argument 3 to "exec_le" has incompatible type "str"; expected "Env" [arg-type] +generics_typevartuple_args.py:34: error: Argument 3 to "exec_le" has incompatible type "str"; expected "Env" [arg-type] +generics_typevartuple_args.py:48: error: Argument 2 to "func1" has incompatible type "str"; expected "int" [arg-type] +generics_typevartuple_args.py:57: error: Argument 2 to "func2" has incompatible type "int"; expected "*tuple[*tuple[str, ...], str]" [arg-type] +generics_typevartuple_args.py:58: error: Too few arguments for "func2" [call-arg] +generics_typevartuple_args.py:59: error: Too few arguments for "func2" [call-arg] +generics_typevartuple_args.py:59: error: Argument 1 to "func2" has incompatible type "str"; expected "int" [arg-type] +generics_typevartuple_args.py:67: error: Too few arguments for "func3" [call-arg] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 75: Expected 1 errors +""" diff --git a/conformance/results/mypy/generics_typevartuple_basic.toml b/conformance/results/mypy/generics_typevartuple_basic.toml new file mode 100644 index 000000000..37140c379 --- /dev/null +++ b/conformance/results/mypy/generics_typevartuple_basic.toml @@ -0,0 +1,23 @@ +conformant = "Partial" +notes = """ +Does not enforce that tuples captured by TypeVarTuple are same length. +""" +output = """ +generics_typevartuple_basic.py:42: error: Argument 1 to "Array" has incompatible type "Height"; expected "tuple[Height, Width]" [arg-type] +generics_typevartuple_basic.py:43: error: Argument 1 to "Array" has incompatible type "tuple[Batch, Width]"; expected "tuple[Batch, Height, Width]" [arg-type] +generics_typevartuple_basic.py:45: error: Argument 1 to "Array" has incompatible type "tuple[Time, Batch, Width, Height]"; expected "tuple[Time, Batch, Height, Width]" [arg-type] +generics_typevartuple_basic.py:52: error: Free type variable expected in Generic[...] [misc] +generics_typevartuple_basic.py:53: error: TypeVarTuple "Shape" is only valid with an unpack [valid-type] +generics_typevartuple_basic.py:56: error: TypeVarTuple "Shape" is only valid with an unpack [valid-type] +generics_typevartuple_basic.py:59: error: TypeVarTuple "Shape" is only valid with an unpack [valid-type] +generics_typevartuple_basic.py:65: error: Unexpected keyword argument "covariant" for "TypeVarTuple" [misc] +generics_typevartuple_basic.py:66: error: Too many positional arguments for "TypeVarTuple" [misc] +generics_typevartuple_basic.py:67: error: Unexpected keyword argument "bound" for "TypeVarTuple" [misc] +generics_typevartuple_basic.py:100: error: Cannot infer value of type parameter "Shape" of "multiply" [misc] +generics_typevartuple_basic.py:101: error: Cannot infer value of type parameter "Shape" of "multiply" [misc] +generics_typevartuple_basic.py:107: error: Can only use one type var tuple in a class def [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 91: Expected 1 errors +""" diff --git a/conformance/results/mypy/generics_typevartuple_callable.toml b/conformance/results/mypy/generics_typevartuple_callable.toml new file mode 100644 index 000000000..904ddc9fd --- /dev/null +++ b/conformance/results/mypy/generics_typevartuple_callable.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +output = """ +generics_typevartuple_callable.py:26: error: Argument "target" to "Process" has incompatible type "Callable[[int, str], None]"; expected "Callable[[str, int], None]" [arg-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_typevartuple_concat.toml b/conformance/results/mypy/generics_typevartuple_concat.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/mypy/generics_typevartuple_concat.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_typevartuple_overloads.toml b/conformance/results/mypy/generics_typevartuple_overloads.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/mypy/generics_typevartuple_overloads.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_typevartuple_specialization.toml b/conformance/results/mypy/generics_typevartuple_specialization.toml new file mode 100644 index 000000000..7d23a81f1 --- /dev/null +++ b/conformance/results/mypy/generics_typevartuple_specialization.toml @@ -0,0 +1,22 @@ +conformant = "Partial" +notes = """ +Incorrectly specializes generic alias that includes a TypeVar and TypeVarTuple if no type arguments are provided. +Rejects specialization of generic type alias defined as a tuple containing a TypeVar. +""" +output = """ +generics_typevartuple_specialization.py:85: error: Argument 1 to "takes_float_array_with_specific_shape" has incompatible type "Array2[float, *tuple[Any, ...]]"; expected "Array2[float, Height, Width]" [arg-type] +generics_typevartuple_specialization.py:108: error: Type application is only supported for generic classes [misc] +generics_typevartuple_specialization.py:109: error: Type application is only supported for generic classes [misc] +generics_typevartuple_specialization.py:109: error: Unpack is only valid in a variadic position [valid-type] +generics_typevartuple_specialization.py:110: error: Type application is only supported for generic classes [misc] +generics_typevartuple_specialization.py:110: error: Unpack is only valid in a variadic position [valid-type] +generics_typevartuple_specialization.py:121: error: More than one variadic Unpack in a type is not allowed [misc] +generics_typevartuple_specialization.py:122: error: More than one variadic Unpack in a type is not allowed [misc] +generics_typevartuple_specialization.py:127: error: Bad number of arguments for type alias, expected at least 2, given 1 [type-arg] +generics_typevartuple_specialization.py:163: error: TypeVarTuple cannot be split [type-arg] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 85: Unexpected errors ['generics_typevartuple_specialization.py:85: error: Argument 1 to "takes_float_array_with_specific_shape" has incompatible type "Array2[float, *tuple[Any, ...]]"; expected "Array2[float, Height, Width]" [arg-type]'] +Line 108: Unexpected errors ['generics_typevartuple_specialization.py:108: error: Type application is only supported for generic classes [misc]'] +""" diff --git a/conformance/results/mypy/generics_typevartuple_unpack.toml b/conformance/results/mypy/generics_typevartuple_unpack.toml new file mode 100644 index 000000000..e1cf44c86 --- /dev/null +++ b/conformance/results/mypy/generics_typevartuple_unpack.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +output = """ +generics_typevartuple_unpack.py:30: error: Argument 1 to "process_batch_channels" has incompatible type "Array[Batch]"; expected "Array[Batch, *tuple[Any, ...], Channels]" [arg-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/generics_upper_bound.toml b/conformance/results/mypy/generics_upper_bound.toml new file mode 100644 index 000000000..9aad1c5cf --- /dev/null +++ b/conformance/results/mypy/generics_upper_bound.toml @@ -0,0 +1,13 @@ +conformant = "Partial" +notes = """ +Does not reject use of type variable within an upper bound. +""" +output = """ +generics_upper_bound.py:43: error: Expression is of type "Collection[int]", not "list[int] | set[int]" [assert-type] +generics_upper_bound.py:52: error: Value of type variable "ST" of "longer" cannot be "int" [type-var] +generics_upper_bound.py:57: error: TypeVar cannot have both values and an upper bound [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 24: Expected 1 errors +""" diff --git a/conformance/results/mypy/generics_variance.toml b/conformance/results/mypy/generics_variance.toml new file mode 100644 index 000000000..30810aacf --- /dev/null +++ b/conformance/results/mypy/generics_variance.toml @@ -0,0 +1,22 @@ +conformant = "Partial" +notes = """ +Does not reject use of class-scoped TypeVar used in a base class when variance is incompatible. +""" +output = """ +generics_variance.py:14: error: TypeVar cannot be both covariant and contravariant [misc] +generics_variance.py:77: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:81: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +generics_variance.py:93: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +generics_variance.py:105: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:125: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:131: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 113: Expected 1 errors +Line 163: Expected 1 errors +Line 167: Expected 1 errors +Line 191: Expected 1 errors +Lines 141, 142: Expected error (tag 'CoContra_Child5') +Lines 195, 196: Expected error (tag 'ContraToContraToContra_WithTA') +""" diff --git a/conformance/results/mypy/generics_variance_inference.toml b/conformance/results/mypy/generics_variance_inference.toml new file mode 100644 index 000000000..b1158c7e9 --- /dev/null +++ b/conformance/results/mypy/generics_variance_inference.toml @@ -0,0 +1,29 @@ +conformant = "Pass" +output = """ +generics_variance_inference.py:24: error: Incompatible types in assignment (expression has type "ClassA[float, int, int]", variable has type "ClassA[int, int, int]") [assignment] +generics_variance_inference.py:25: error: Incompatible types in assignment (expression has type "ClassA[float, int, int]", variable has type "ClassA[float, float, int]") [assignment] +generics_variance_inference.py:28: error: Incompatible types in assignment (expression has type "ClassA[int, float, float]", variable has type "ClassA[int, int, int]") [assignment] +generics_variance_inference.py:41: error: Incompatible types in assignment (expression has type "ShouldBeCovariant1[float]", variable has type "ShouldBeCovariant1[int]") [assignment] +generics_variance_inference.py:49: error: Incompatible types in assignment (expression has type "ShouldBeCovariant2[float]", variable has type "ShouldBeCovariant2[int]") [assignment] +generics_variance_inference.py:58: error: Incompatible types in assignment (expression has type "ShouldBeCovariant3[float]", variable has type "ShouldBeCovariant3[int]") [assignment] +generics_variance_inference.py:67: error: Incompatible types in assignment (expression has type "ShouldBeCovariant4[float]", variable has type "ShouldBeCovariant4[int]") [assignment] +generics_variance_inference.py:80: error: Incompatible types in assignment (expression has type "ShouldBeCovariant5[float]", variable has type "ShouldBeCovariant5[int]") [assignment] +generics_variance_inference.py:96: error: Incompatible types in assignment (expression has type "ShouldBeInvariant1[int]", variable has type "ShouldBeInvariant1[float]") [assignment] +generics_variance_inference.py:97: error: Incompatible types in assignment (expression has type "ShouldBeInvariant1[float]", variable has type "ShouldBeInvariant1[int]") [assignment] +generics_variance_inference.py:111: error: Incompatible types in assignment (expression has type "ShouldBeInvariant2[int]", variable has type "ShouldBeInvariant2[float]") [assignment] +generics_variance_inference.py:112: error: Incompatible types in assignment (expression has type "ShouldBeInvariant2[float]", variable has type "ShouldBeInvariant2[int]") [assignment] +generics_variance_inference.py:119: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[int, str]", variable has type "ShouldBeInvariant3[float, str]") [assignment] +generics_variance_inference.py:120: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[float, str]", variable has type "ShouldBeInvariant3[int, str]") [assignment] +generics_variance_inference.py:121: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[str, int]", variable has type "ShouldBeInvariant3[str, float]") [assignment] +generics_variance_inference.py:122: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[str, float]", variable has type "ShouldBeInvariant3[str, int]") [assignment] +generics_variance_inference.py:130: error: Incompatible types in assignment (expression has type "ShouldBeInvariant4[int]", variable has type "ShouldBeInvariant4[float]") [assignment] +generics_variance_inference.py:138: error: Incompatible types in assignment (expression has type "ShouldBeInvariant5[int]", variable has type "ShouldBeInvariant5[float]") [assignment] +generics_variance_inference.py:149: error: Incompatible types in assignment (expression has type "ShouldBeContravariant1[int]", variable has type "ShouldBeContravariant1[float]") [assignment] +generics_variance_inference.py:169: error: Incompatible types in assignment (expression has type "ShouldBeInvariant6[float]", variable has type "ShouldBeInvariant6[int]") [assignment] +generics_variance_inference.py:170: error: Incompatible types in assignment (expression has type "ShouldBeInvariant6[int]", variable has type "ShouldBeInvariant6[float]") [assignment] +generics_variance_inference.py:181: error: Incompatible types in assignment (expression has type "ShouldBeCovariant6[float]", variable has type "ShouldBeCovariant6[int]") [assignment] +generics_variance_inference.py:194: error: Incompatible types in assignment (expression has type "ShouldBeContravariant2[int]", variable has type "ShouldBeContravariant2[float]") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/historical_positional.toml b/conformance/results/mypy/historical_positional.toml new file mode 100644 index 000000000..1338644ef --- /dev/null +++ b/conformance/results/mypy/historical_positional.toml @@ -0,0 +1,19 @@ +conformant = "Partial" +notes = """ +Does not reject positional-only parameter after non-positional-only parameter. +Treats keyword-only parameter as positional-only. +Applies legacy positional-only rules when PEP 570 syntax is used. +""" +output = """ +historical_positional.py:18: error: Unexpected keyword argument "__x" for "f1" [call-arg] +historical_positional.py:48: error: Unexpected keyword argument "__y" for "f3" [call-arg] +historical_positional.py:59: error: Unexpected keyword argument "__x" for "m1" of "A" [call-arg] +historical_positional.py:69: error: Unexpected keyword argument "__y" for "f4" [call-arg] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 26: Expected 1 errors +Line 54: Expected 1 errors +Line 48: Unexpected errors ['historical_positional.py:48: error: Unexpected keyword argument "__y" for "f3" [call-arg]'] +Line 69: Unexpected errors ['historical_positional.py:69: error: Unexpected keyword argument "__y" for "f4" [call-arg]'] +""" diff --git a/conformance/results/mypy/literals_interactions.toml b/conformance/results/mypy/literals_interactions.toml new file mode 100644 index 000000000..a9f03f046 --- /dev/null +++ b/conformance/results/mypy/literals_interactions.toml @@ -0,0 +1,20 @@ +notes = """ +Does not narrow `str` or `LiteralString` types to `Literal` string types via equality or containment checks. +""" +output = """ +literals_interactions.py:14: error: Tuple index out of range [misc] +literals_interactions.py:15: error: Tuple index out of range [misc] +literals_interactions.py:16: error: Tuple index out of range [misc] +literals_interactions.py:17: error: Tuple index out of range [misc] +literals_interactions.py:111: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:113: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:116: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:119: error: Argument 1 to "expects_pending_status" has incompatible type "str"; expected "Literal['PENDING']" [arg-type] +literals_interactions.py:128: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:130: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:133: error: Argument 1 to "expects_bad_status" has incompatible type "str"; expected "Literal['MALFORMED', 'ABORTED']" [arg-type] +literals_interactions.py:136: error: Argument 1 to "expects_pending_status" has incompatible type "str"; expected "Literal['PENDING']" [arg-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/literals_literalstring.toml b/conformance/results/mypy/literals_literalstring.toml new file mode 100644 index 000000000..dc1470fc2 --- /dev/null +++ b/conformance/results/mypy/literals_literalstring.toml @@ -0,0 +1,22 @@ +conformant = "Unsupported" +notes = """ +Support for `LiteralString` is not implemented. +""" +output = """ +literals_literalstring.py:36: error: Parameter 2 of Literal[...] is invalid [valid-type] +literals_literalstring.py:37: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_literalstring.py:43: error: Incompatible types in assignment (expression has type "Literal['two']", variable has type "Literal['']") [assignment] +literals_literalstring.py:73: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment] +literals_literalstring.py:74: error: Incompatible types in assignment (expression has type "bytes", variable has type "str") [assignment] +literals_literalstring.py:156: error: Overloaded function signature 3 will never be matched: signature 2's parameter type(s) are the same or broader [overload-cannot-match] +literals_literalstring.py:167: error: Expression is of type "B", not "A" [assert-type] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 65: Expected 1 errors +Line 119: Expected 1 errors +Line 133: Expected 1 errors +Line 171: Expected 1 errors +Line 156: Unexpected errors ["literals_literalstring.py:156: error: Overloaded function signature 3 will never be matched: signature 2's parameter type(s) are the same or broader [overload-cannot-match]"] +Line 167: Unexpected errors ['literals_literalstring.py:167: error: Expression is of type "B", not "A" [assert-type]'] +""" diff --git a/conformance/results/mypy/literals_parameterizations.toml b/conformance/results/mypy/literals_parameterizations.toml new file mode 100644 index 000000000..730d531db --- /dev/null +++ b/conformance/results/mypy/literals_parameterizations.toml @@ -0,0 +1,26 @@ +conformant = "Partial" +notes = """ +Does not reject tuple within Literal. +""" +output = """ +literals_parameterizations.py:41: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:42: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:43: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:44: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:45: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:47: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:48: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:49: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:50: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:51: error: Parameter 1 of Literal[...] cannot be of type "float" [valid-type] +literals_parameterizations.py:52: error: Parameter 1 of Literal[...] cannot be of type "Any" [valid-type] +literals_parameterizations.py:53: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:56: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:60: error: Literal[...] must have at least one parameter [valid-type] +literals_parameterizations.py:61: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:65: error: Incompatible types in assignment (expression has type "Literal[Color.RED]", variable has type "Literal['Color.RED']") [assignment] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 46: Expected 1 errors +""" diff --git a/conformance/results/mypy/literals_semantics.toml b/conformance/results/mypy/literals_semantics.toml new file mode 100644 index 000000000..a948a4bdc --- /dev/null +++ b/conformance/results/mypy/literals_semantics.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +literals_semantics.py:10: error: Incompatible types in assignment (expression has type "Literal[4]", variable has type "Literal[3]") [assignment] +literals_semantics.py:24: error: Incompatible types in assignment (expression has type "Literal[0]", variable has type "Literal[False]") [assignment] +literals_semantics.py:25: error: Incompatible types in assignment (expression has type "Literal[False]", variable has type "Literal[0]") [assignment] +literals_semantics.py:33: error: Incompatible types in assignment (expression has type "int", variable has type "Literal[3, 4, 5]") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/namedtuples_define_class.toml b/conformance/results/mypy/namedtuples_define_class.toml new file mode 100644 index 000000000..12c4dc552 --- /dev/null +++ b/conformance/results/mypy/namedtuples_define_class.toml @@ -0,0 +1,37 @@ +conformant = "Partial" +notes = """ +Does not reject override of named tuple attribute in child class. +Does not support version-conditional fields. +""" +output = """ +namedtuples_define_class.py:33: error: Tuple index out of range [misc] +namedtuples_define_class.py:34: error: Tuple index out of range [misc] +namedtuples_define_class.py:45: error: Missing positional argument "y" in call to "Point" [call-arg] +namedtuples_define_class.py:46: error: Missing positional argument "y" in call to "Point" [call-arg] +namedtuples_define_class.py:47: error: Argument 2 to "Point" has incompatible type "str"; expected "int" [arg-type] +namedtuples_define_class.py:48: error: Argument "units" to "Point" has incompatible type "int"; expected "str" [arg-type] +namedtuples_define_class.py:49: error: Too many arguments for "Point" [call-arg] +namedtuples_define_class.py:50: error: Unexpected keyword argument "other" for "Point" [call-arg] +namedtuples_define_class.py:60: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc] +namedtuples_define_class.py:66: error: Missing positional argument "units" in call to "Point2" [call-arg] +namedtuples_define_class.py:68: error: Too many values to unpack (2 expected, 3 provided) [misc] +namedtuples_define_class.py:77: error: NamedTuple field name cannot start with an underscore: _y [misc] +namedtuples_define_class.py:87: error: Non-default NamedTuple fields cannot follow default fields [misc] +namedtuples_define_class.py:114: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc] +namedtuples_define_class.py:116: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc] +namedtuples_define_class.py:120: error: Too many arguments for "ConditionalField" [call-arg] +namedtuples_define_class.py:121: error: Too many arguments for "ConditionalField" [call-arg] +namedtuples_define_class.py:140: error: Argument 2 to "Property" has incompatible type "float"; expected "str" [arg-type] +namedtuples_define_class.py:147: error: NamedTuple should be a single base [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 70: Expected 1 errors +Line 107: Expected 1 errors +Line 60: Unexpected errors ['namedtuples_define_class.py:60: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc]'] +Line 66: Unexpected errors ['namedtuples_define_class.py:66: error: Missing positional argument "units" in call to "Point2" [call-arg]'] +Line 68: Unexpected errors ['namedtuples_define_class.py:68: error: Too many values to unpack (2 expected, 3 provided) [misc]'] +Line 114: Unexpected errors ['namedtuples_define_class.py:114: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc]'] +Line 116: Unexpected errors ['namedtuples_define_class.py:116: error: Invalid statement in NamedTuple definition; expected "field_name: field_type [= default]" [misc]'] +Line 120: Unexpected errors ['namedtuples_define_class.py:120: error: Too many arguments for "ConditionalField" [call-arg]'] +""" diff --git a/conformance/results/mypy/namedtuples_define_functional.toml b/conformance/results/mypy/namedtuples_define_functional.toml new file mode 100644 index 000000000..6356dc8c5 --- /dev/null +++ b/conformance/results/mypy/namedtuples_define_functional.toml @@ -0,0 +1,19 @@ +conformant = "Pass" +output = """ +namedtuples_define_functional.py:16: error: Missing positional argument "y" in call to "Point1" [call-arg] +namedtuples_define_functional.py:21: error: Missing positional arguments "x", "y" in call to "Point2" [call-arg] +namedtuples_define_functional.py:26: error: Too many arguments for "Point3" [call-arg] +namedtuples_define_functional.py:31: error: Unexpected keyword argument "z" for "Point4" [call-arg] +namedtuples_define_functional.py:36: error: Argument 2 to "Point5" has incompatible type "str"; expected "int" [arg-type] +namedtuples_define_functional.py:37: error: Too many arguments for "Point5" [call-arg] +namedtuples_define_functional.py:42: error: Argument 2 to "Point6" has incompatible type "str"; expected "int" [arg-type] +namedtuples_define_functional.py:43: error: Argument "x" to "Point6" has incompatible type "float"; expected "int" [arg-type] +namedtuples_define_functional.py:52: error: "namedtuple()" has duplicate field name "a" [misc] +namedtuples_define_functional.py:53: error: "namedtuple()" field name "def" is a keyword [misc] +namedtuples_define_functional.py:54: error: "namedtuple()" field name "def" is a keyword [misc] +namedtuples_define_functional.py:55: error: "namedtuple()" field name "_d" starts with an underscore [misc] +namedtuples_define_functional.py:69: error: Missing positional argument "a" in call to "NT7" [call-arg] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/namedtuples_type_compat.toml b/conformance/results/mypy/namedtuples_type_compat.toml new file mode 100644 index 000000000..f02bbc981 --- /dev/null +++ b/conformance/results/mypy/namedtuples_type_compat.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +namedtuples_type_compat.py:22: error: Incompatible types in assignment (expression has type "Point", variable has type "tuple[int, int]") [assignment] +namedtuples_type_compat.py:23: error: Incompatible types in assignment (expression has type "Point", variable has type "tuple[int, str, str]") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/namedtuples_usage.toml b/conformance/results/mypy/namedtuples_usage.toml new file mode 100644 index 000000000..21676c9c0 --- /dev/null +++ b/conformance/results/mypy/namedtuples_usage.toml @@ -0,0 +1,17 @@ +conformant = "Partial" +notes = """ +Does not reject attempt to delete named tuple field by name. +""" +output = """ +namedtuples_usage.py:34: error: Tuple index out of range [misc] +namedtuples_usage.py:35: error: Tuple index out of range [misc] +namedtuples_usage.py:40: error: Property "x" defined in "Point" is read-only [misc] +namedtuples_usage.py:41: error: Unsupported target for indexed assignment ("Point") [index] +namedtuples_usage.py:43: error: "Point" has no attribute "__delitem__" [attr-defined] +namedtuples_usage.py:52: error: Too many values to unpack (2 expected, 3 provided) [misc] +namedtuples_usage.py:53: error: Need more than 3 values to unpack (4 expected) [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 42: Expected 1 errors +""" diff --git a/conformance/results/mypy/narrowing_typeguard.toml b/conformance/results/mypy/narrowing_typeguard.toml new file mode 100644 index 000000000..4c4f09522 --- /dev/null +++ b/conformance/results/mypy/narrowing_typeguard.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +narrowing_typeguard.py:102: error: TypeGuard functions must have a positional argument [valid-type] +narrowing_typeguard.py:107: error: TypeGuard functions must have a positional argument [valid-type] +narrowing_typeguard.py:128: error: Argument 1 to "takes_callable_str" has incompatible type "Callable[[object], TypeGuard[int]]"; expected "Callable[[object], str]" [arg-type] +narrowing_typeguard.py:148: error: Argument 1 to "takes_callable_str_proto" has incompatible type "Callable[[object], TypeGuard[int]]"; expected "CallableStrProto" [arg-type] +narrowing_typeguard.py:148: note: "CallableStrProto.__call__" has type "def __call__(self, val: object) -> str" +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/narrowing_typeis.toml b/conformance/results/mypy/narrowing_typeis.toml new file mode 100644 index 000000000..cdb53c5eb --- /dev/null +++ b/conformance/results/mypy/narrowing_typeis.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +output = """ +narrowing_typeis.py:110: error: "TypeIs" functions must have a positional argument [valid-type] +narrowing_typeis.py:115: error: "TypeIs" functions must have a positional argument [valid-type] +narrowing_typeis.py:137: error: Argument 1 to "takes_callable_str" has incompatible type "Callable[[object], TypeIs[int]]"; expected "Callable[[object], str]" [arg-type] +narrowing_typeis.py:157: error: Argument 1 to "takes_callable_str_proto" has incompatible type "Callable[[object], TypeIs[int]]"; expected "CallableStrProto" [arg-type] +narrowing_typeis.py:157: note: "CallableStrProto.__call__" has type "def __call__(self, val: object) -> str" +narrowing_typeis.py:174: error: Argument 1 to "takes_typeguard" has incompatible type "Callable[[object], TypeIs[int]]"; expected "Callable[[object], TypeGuard[int]]" [arg-type] +narrowing_typeis.py:175: error: Argument 1 to "takes_typeis" has incompatible type "Callable[[object], TypeGuard[int]]"; expected "Callable[[object], TypeIs[int]]" [arg-type] +narrowing_typeis.py:196: error: Argument 1 to "takes_int_typeis" has incompatible type "Callable[[object], TypeIs[bool]]"; expected "Callable[[object], TypeIs[int]]" [arg-type] +narrowing_typeis.py:200: error: Narrowed type "str" is not a subtype of input type "int" [narrowed-type-not-subtype] +narrowing_typeis.py:204: error: Narrowed type "list[int]" is not a subtype of input type "list[object]" [narrowed-type-not-subtype] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/overloads_basic.toml b/conformance/results/mypy/overloads_basic.toml new file mode 100644 index 000000000..f592ea606 --- /dev/null +++ b/conformance/results/mypy/overloads_basic.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +overloads_basic.py:39: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload] +overloads_basic.py:39: note: Possible overload variants: +overloads_basic.py:39: note: def __getitem__(self, int, /) -> int +overloads_basic.py:39: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/overloads_consistency.toml b/conformance/results/mypy/overloads_consistency.toml new file mode 100644 index 000000000..1010582c5 --- /dev/null +++ b/conformance/results/mypy/overloads_consistency.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:28: error: Overloaded function implementation cannot produce return type of signature 2 [misc] +overloads_consistency.py:44: error: Overloaded function implementation does not accept all possible parameters of signature 2 [misc] +""" diff --git a/conformance/results/mypy/overloads_definitions.toml b/conformance/results/mypy/overloads_definitions.toml new file mode 100644 index 000000000..80b29a62f --- /dev/null +++ b/conformance/results/mypy/overloads_definitions.toml @@ -0,0 +1,19 @@ +conformant = "Partial" +conformance_automated = "Fail" +notes = """ +Allows @override to be on all overloads and implementation, instead of just implementation. +""" +errors_diff = """ +Lines 226, 227, 228, 231, 232: Expected error (tag 'override_impl') +""" +output = """ +overloads_definitions.py:15: error: Single overload definition, multiple required [misc] +overloads_definitions.py:27: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:58: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:71: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_definitions.py:84: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_definitions.py:122: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:137: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:180: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions.py:195: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +""" diff --git a/conformance/results/mypy/overloads_definitions_stub.toml b/conformance/results/mypy/overloads_definitions_stub.toml new file mode 100644 index 000000000..51a1aa393 --- /dev/null +++ b/conformance/results/mypy/overloads_definitions_stub.toml @@ -0,0 +1,17 @@ +conformant = "Partial" +notes = """ +Allows @override to appear in a stub file not on the first overload. +""" +conformance_automated = "Fail" +errors_diff = """ +Lines 143, 146, 147, 149: Expected error (tag 'override_impl') +""" +output = """ +overloads_definitions_stub.pyi:13: error: Single overload definition, multiple required [misc] +overloads_definitions_stub.pyi:32: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_definitions_stub.pyi:39: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_definitions_stub.pyi:71: error: In a stub file @final must be applied only to the first overload [misc] +overloads_definitions_stub.pyi:84: error: In a stub file @final must be applied only to the first overload [misc] +overloads_definitions_stub.pyi:107: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions_stub.pyi:120: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +""" diff --git a/conformance/results/mypy/overloads_evaluation.toml b/conformance/results/mypy/overloads_evaluation.toml new file mode 100644 index 000000000..4ddeaca2d --- /dev/null +++ b/conformance/results/mypy/overloads_evaluation.toml @@ -0,0 +1,53 @@ +conformant = "Partial" +notes = """ +Does not expand boolean arguments to Literal[True] and Literal[False]. +Does not expand enum arguments to literal variants. +Does not expand tuple arguments to possible combinations. +Does not evaluate Any in some cases where overload is ambiguous. +Evaluates Any in some cases where overload is not ambiguous. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 135: Unexpected errors ['overloads_evaluation.py:135: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload]'] +Line 136: Unexpected errors ['overloads_evaluation.py:136: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +Line 161: Unexpected errors ['overloads_evaluation.py:161: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload]'] +Line 162: Unexpected errors ['overloads_evaluation.py:162: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type]'] +Line 205: Unexpected errors ['overloads_evaluation.py:205: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type]'] +Line 206: Unexpected errors ['overloads_evaluation.py:206: error: Expression is of type "int", not "int | str" [assert-type]'] +Line 265: Unexpected errors ['overloads_evaluation.py:265: error: Expression is of type "list[Any]", not "Any" [assert-type]'] +Line 281: Unexpected errors ['overloads_evaluation.py:281: error: Expression is of type "list[Any]", not "Any" [assert-type]'] +Line 303: Unexpected errors ['overloads_evaluation.py:303: error: Expression is of type "Any", not "float" [assert-type]'] +Line 347: Unexpected errors ['overloads_evaluation.py:347: error: Expression is of type "list[Any]", not "Any" [assert-type]'] +""" +output = """ +overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] +overloads_evaluation.py:38: note: Possible overload variants: +overloads_evaluation.py:38: note: def example1_1(x: int, y: str) -> int +overloads_evaluation.py:38: note: def example1_1(x: str) -> str +overloads_evaluation.py:46: error: No overload variant of "example1_1" matches argument types "int", "int" [call-overload] +overloads_evaluation.py:46: note: Possible overload variants: +overloads_evaluation.py:46: note: def example1_1(x: int, y: str) -> int +overloads_evaluation.py:46: note: def example1_1(x: str) -> str +overloads_evaluation.py:51: error: No overload variant of "example1_1" matches argument type "int" [call-overload] +overloads_evaluation.py:51: note: Possible overload variants: +overloads_evaluation.py:51: note: def example1_1(x: int, y: str) -> int +overloads_evaluation.py:51: note: def example1_1(x: str) -> str +overloads_evaluation.py:116: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] +overloads_evaluation.py:116: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] +overloads_evaluation.py:135: error: No overload variant of "expand_bool" matches argument type "bool" [call-overload] +overloads_evaluation.py:135: note: Possible overload variants: +overloads_evaluation.py:135: note: def expand_bool(x: Literal[False]) -> Literal[0] +overloads_evaluation.py:135: note: def expand_bool(x: Literal[True]) -> Literal[1] +overloads_evaluation.py:136: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:161: error: No overload variant of "expand_enum" matches argument type "Color" [call-overload] +overloads_evaluation.py:161: note: Possible overload variants: +overloads_evaluation.py:161: note: def expand_enum(x: Literal[Color.RED]) -> Literal[0] +overloads_evaluation.py:161: note: def expand_enum(x: Literal[Color.BLUE]) -> Literal[1] +overloads_evaluation.py:162: error: Expression is of type "Any", not "Literal[0, 1]" [assert-type] +overloads_evaluation.py:205: error: Argument 1 to "expand_tuple" has incompatible type "tuple[int, int | str]"; expected "tuple[int, int]" [arg-type] +overloads_evaluation.py:206: error: Expression is of type "int", not "int | str" [assert-type] +overloads_evaluation.py:265: error: Expression is of type "list[Any]", not "Any" [assert-type] +overloads_evaluation.py:281: error: Expression is of type "list[Any]", not "Any" [assert-type] +overloads_evaluation.py:303: error: Expression is of type "Any", not "float" [assert-type] +overloads_evaluation.py:347: error: Expression is of type "list[Any]", not "Any" [assert-type] +""" diff --git a/conformance/results/mypy/protocols_class_objects.toml b/conformance/results/mypy/protocols_class_objects.toml new file mode 100644 index 000000000..c2fae510f --- /dev/null +++ b/conformance/results/mypy/protocols_class_objects.toml @@ -0,0 +1,26 @@ +conformant = "Pass" +output = """ +protocols_class_objects.py:29: error: Only concrete class can be given where "type[Proto]" is expected [type-abstract] +protocols_class_objects.py:34: error: Can only assign concrete classes to a variable of type "type[Proto]" [type-abstract] +protocols_class_objects.py:58: error: Incompatible types in assignment (expression has type "type[ConcreteA]", variable has type "ProtoA1") [assignment] +protocols_class_objects.py:58: note: Following member(s) of "ConcreteA" have conflicts: +protocols_class_objects.py:58: note: Expected: +protocols_class_objects.py:58: note: def method1(x: int) -> int +protocols_class_objects.py:58: note: Got: +protocols_class_objects.py:58: note: def method1(self: ConcreteA, x: int) -> int +protocols_class_objects.py:74: error: Incompatible types in assignment (expression has type "type[ConcreteB]", variable has type "ProtoB1") [assignment] +protocols_class_objects.py:74: note: Following member(s) of "ConcreteB" have conflicts: +protocols_class_objects.py:74: note: prop1: expected "int", got "Callable[[ConcreteB], int]" +protocols_class_objects.py:74: note: Only class variables allowed for class object access on protocols, prop1 is an instance variable of "ConcreteB" +protocols_class_objects.py:104: error: Incompatible types in assignment (expression has type "type[ConcreteC1]", variable has type "ProtoC1") [assignment] +protocols_class_objects.py:104: note: ClassVar protocol member ProtoC1.attr1 can never be matched by a class object +protocols_class_objects.py:106: error: Incompatible types in assignment (expression has type "type[ConcreteC2]", variable has type "ProtoC1") [assignment] +protocols_class_objects.py:106: note: Only class variables allowed for class object access on protocols, attr1 is an instance variable of "ConcreteC2" +protocols_class_objects.py:106: note: ClassVar protocol member ProtoC1.attr1 can never be matched by a class object +protocols_class_objects.py:107: error: Incompatible types in assignment (expression has type "type[ConcreteC2]", variable has type "ProtoC2") [assignment] +protocols_class_objects.py:107: note: Only class variables allowed for class object access on protocols, attr1 is an instance variable of "ConcreteC2" +protocols_class_objects.py:108: error: Incompatible types in assignment (expression has type "type[ConcreteC3]", variable has type "ProtoC1") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/protocols_definition.toml b/conformance/results/mypy/protocols_definition.toml new file mode 100644 index 000000000..717b8cad5 --- /dev/null +++ b/conformance/results/mypy/protocols_definition.toml @@ -0,0 +1,60 @@ +conformant = "Partial" +notes = """ +Does not detect protocol mismatch if concrete method is missing annotations. +Does not detect protocol mismatch if concrete method's parameters are position-only. +""" +output = """ +protocols_definition.py:30: error: List item 0 has incompatible type "int"; expected "SupportsClose" [list-item] +protocols_definition.py:67: error: Protocol members cannot be defined via assignment to self [misc] +protocols_definition.py:67: error: "Template" has no attribute "temp" [attr-defined] +protocols_definition.py:114: error: Incompatible types in assignment (expression has type "Concrete2_Bad1", variable has type "Template2") [assignment] +protocols_definition.py:115: error: Incompatible types in assignment (expression has type "Concrete2_Bad2", variable has type "Template2") [assignment] +protocols_definition.py:115: note: Following member(s) of "Concrete2_Bad2" have conflicts: +protocols_definition.py:115: note: val1: expected "Sequence[int]", got "Sequence[float]" +protocols_definition.py:115: note: Protocol member Template2.val1 expected class variable, got instance variable +protocols_definition.py:116: error: Incompatible types in assignment (expression has type "Concrete2_Bad3", variable has type "Template2") [assignment] +protocols_definition.py:116: note: Following member(s) of "Concrete2_Bad3" have conflicts: +protocols_definition.py:116: note: val1: expected "Sequence[int]", got "list[int]" +protocols_definition.py:116: note: Protocol member Template2.val1 expected class variable, got instance variable +protocols_definition.py:117: error: Incompatible types in assignment (expression has type "Concrete2_Bad4", variable has type "Template2") [assignment] +protocols_definition.py:117: note: Protocol member Template2.val1 expected class variable, got instance variable +protocols_definition.py:156: error: Incompatible types in assignment (expression has type "Concrete3_Bad1", variable has type "Template3") [assignment] +protocols_definition.py:157: error: Incompatible types in assignment (expression has type "Concrete3_Bad2", variable has type "Template3") [assignment] +protocols_definition.py:157: note: Protocol member Template3.val1 expected instance variable, got class variable +protocols_definition.py:158: error: Incompatible types in assignment (expression has type "Concrete3_Bad3", variable has type "Template3") [assignment] +protocols_definition.py:158: note: Protocol member Template3.val1 expected settable variable, got read-only attribute +protocols_definition.py:159: error: Incompatible types in assignment (expression has type "Concrete3_Bad4", variable has type "Template3") [assignment] +protocols_definition.py:159: note: Following member(s) of "Concrete3_Bad4" have conflicts: +protocols_definition.py:159: note: val1: expected "Sequence[int]", got "Sequence[float]" +protocols_definition.py:160: error: Incompatible types in assignment (expression has type "Concrete3_Bad5", variable has type "Template3") [assignment] +protocols_definition.py:160: note: Following member(s) of "Concrete3_Bad5" have conflicts: +protocols_definition.py:160: note: val1: expected "Sequence[int]", got "list[int]" +protocols_definition.py:218: error: Incompatible types in assignment (expression has type "Concrete4_Bad1", variable has type "Template4") [assignment] +protocols_definition.py:218: note: Following member(s) of "Concrete4_Bad1" have conflicts: +protocols_definition.py:218: note: val1: expected "Sequence[float]", got "Callable[[], Sequence[int]]" +protocols_definition.py:219: error: Incompatible types in assignment (expression has type "Concrete4_Bad2", variable has type "Template4") [assignment] +protocols_definition.py:287: error: Incompatible types in assignment (expression has type "Concrete5_Bad3", variable has type "Template5") [assignment] +protocols_definition.py:287: note: Following member(s) of "Concrete5_Bad3" have conflicts: +protocols_definition.py:287: note: Expected: +protocols_definition.py:287: note: def method1(self, a: int, b: int) -> float +protocols_definition.py:287: note: Got: +protocols_definition.py:287: note: def method1(self, *, a: int, b: int) -> float +protocols_definition.py:289: error: Incompatible types in assignment (expression has type "Concrete5_Bad5", variable has type "Template5") [assignment] +protocols_definition.py:289: note: Following member(s) of "Concrete5_Bad5" have conflicts: +protocols_definition.py:289: note: Expected: +protocols_definition.py:289: note: def method1(self, a: int, b: int) -> float +protocols_definition.py:289: note: Got: +protocols_definition.py:289: note: def method1(self: Any, a: int, b: int) -> float +protocols_definition.py:339: error: Incompatible types in assignment (expression has type "Concrete6_Bad1", variable has type "Template6") [assignment] +protocols_definition.py:339: note: Protocol member Template6.val1 expected settable variable, got read-only attribute +protocols_definition.py:340: error: Incompatible types in assignment (expression has type "Concrete6_Bad2", variable has type "Template6") [assignment] +protocols_definition.py:340: note: Protocol member Template6.val1 expected settable variable, got read-only attribute +protocols_definition.py:341: error: Incompatible types in assignment (expression has type "Concrete6_Bad3", variable has type "Template6") [assignment] +protocols_definition.py:341: note: Protocol member Template6.val1 expected settable variable, got read-only attribute +""" +conformance_automated = "Fail" +errors_diff = """ +Line 285: Expected 1 errors +Line 286: Expected 1 errors +Line 288: Expected 1 errors +""" diff --git a/conformance/results/mypy/protocols_explicit.toml b/conformance/results/mypy/protocols_explicit.toml new file mode 100644 index 000000000..80ef20393 --- /dev/null +++ b/conformance/results/mypy/protocols_explicit.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +notes = """ +Does not report unimplemented attributes for class that explicitly derives from protocol until it is instantiated. +""" +output = """ +protocols_explicit.py:27: error: Call to abstract method "draw" of "PColor" with trivial body via super() is unsafe [safe-super] +protocols_explicit.py:56: error: Incompatible types in assignment (expression has type "tuple[int, int, str]", base class "RGB" defined the type as "tuple[int, int, int]") [assignment] +protocols_explicit.py:60: error: Cannot instantiate abstract class "Point" with abstract attributes "intensity" and "transparency" [abstract] +protocols_explicit.py:89: error: Cannot instantiate abstract class "Concrete1" with abstract attribute "cm1" [abstract] +protocols_explicit.py:134: error: Cannot instantiate abstract class "Concrete5" with abstract attribute "method1" [abstract] +protocols_explicit.py:164: error: Cannot instantiate abstract class "Concrete7A" with abstract attribute "method1" [abstract] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/protocols_generic.toml b/conformance/results/mypy/protocols_generic.toml new file mode 100644 index 000000000..407eefd55 --- /dev/null +++ b/conformance/results/mypy/protocols_generic.toml @@ -0,0 +1,41 @@ +conformant = "Pass" +output = """ +protocols_generic.py:40: error: Incompatible types in assignment (expression has type "Concrete1", variable has type "Proto1[int, str]") [assignment] +protocols_generic.py:40: note: Following member(s) of "Concrete1" have conflicts: +protocols_generic.py:40: note: Expected: +protocols_generic.py:40: note: def __iter__(self) -> Iterator[str] +protocols_generic.py:40: note: Got: +protocols_generic.py:40: note: def __iter__(self) -> Iterator[int] +protocols_generic.py:40: note: Expected: +protocols_generic.py:40: note: def method1(self, x: int) -> int +protocols_generic.py:40: note: Got: +protocols_generic.py:40: note: def method1(self, x: str) -> str +protocols_generic.py:44: error: Only single Generic[...] or Protocol[...] can be in bases [misc] +protocols_generic.py:44: error: Duplicate type variables in Generic[...] or Protocol[...] [misc] +protocols_generic.py:56: error: Incompatible types in assignment (expression has type "Box[float]", variable has type "Box[int]") [assignment] +protocols_generic.py:66: error: Incompatible types in assignment (expression has type "Sender[int]", variable has type "Sender[float]") [assignment] +protocols_generic.py:74: error: Incompatible types in assignment (expression has type "AttrProto[int]", variable has type "AttrProto[float]") [assignment] +protocols_generic.py:75: error: Incompatible types in assignment (expression has type "AttrProto[float]", variable has type "AttrProto[int]") [assignment] +protocols_generic.py:145: error: Incompatible types in assignment (expression has type "ConcreteHasProperty2", variable has type "HasPropertyProto") [assignment] +protocols_generic.py:145: note: Following member(s) of "ConcreteHasProperty2" have conflicts: +protocols_generic.py:145: note: Expected: +protocols_generic.py:145: note: def [T] m(self, item: T, callback: Callable[[T], str]) -> str +protocols_generic.py:145: note: Got: +protocols_generic.py:145: note: def m(self, item: int, callback: Callable[[int], str]) -> str +protocols_generic.py:146: error: Incompatible types in assignment (expression has type "ConcreteHasProperty3", variable has type "HasPropertyProto") [assignment] +protocols_generic.py:146: note: Following member(s) of "ConcreteHasProperty3" have conflicts: +protocols_generic.py:146: note: f: expected "ConcreteHasProperty3", got "int" +protocols_generic.py:146: note: Expected: +protocols_generic.py:146: note: def [T] m(self, item: T, callback: Callable[[T], str]) -> str +protocols_generic.py:146: note: Got: +protocols_generic.py:146: note: def m(self, item: int, callback: Callable[[int], str]) -> str +protocols_generic.py:147: error: Incompatible types in assignment (expression has type "ConcreteHasProperty4", variable has type "HasPropertyProto") [assignment] +protocols_generic.py:147: note: Following member(s) of "ConcreteHasProperty4" have conflicts: +protocols_generic.py:147: note: Expected: +protocols_generic.py:147: note: def [T] m(self, item: T, callback: Callable[[T], str]) -> str +protocols_generic.py:147: note: Got: +protocols_generic.py:147: note: def m(self, item: str, callback: Callable[[int], str]) -> str +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/protocols_merging.toml b/conformance/results/mypy/protocols_merging.toml new file mode 100644 index 000000000..760e8aa12 --- /dev/null +++ b/conformance/results/mypy/protocols_merging.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +output = """ +protocols_merging.py:52: error: Incompatible types in assignment (expression has type "SCConcrete2", variable has type "SizedAndClosable1") [assignment] +protocols_merging.py:52: note: "SCConcrete2" is missing following "SizedAndClosable1" protocol member: +protocols_merging.py:52: note: __len__ +protocols_merging.py:53: error: Incompatible types in assignment (expression has type "SCConcrete2", variable has type "SizedAndClosable2") [assignment] +protocols_merging.py:53: note: "SCConcrete2" is missing following "SizedAndClosable2" protocol member: +protocols_merging.py:53: note: __len__ +protocols_merging.py:54: error: Incompatible types in assignment (expression has type "SCConcrete2", variable has type "SizedAndClosable3") [assignment] +protocols_merging.py:67: error: All bases of a protocol must be protocols [misc] +protocols_merging.py:82: error: Cannot instantiate abstract class "SizedAndClosable4" with abstract attribute "close" [abstract] +protocols_merging.py:83: error: Incompatible types in assignment (expression has type "SCConcrete1", variable has type "SizedAndClosable4") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/protocols_modules.toml b/conformance/results/mypy/protocols_modules.toml new file mode 100644 index 000000000..85193c61e --- /dev/null +++ b/conformance/results/mypy/protocols_modules.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +output = """ +protocols_modules.py:26: error: Incompatible types in assignment (expression has type Module, variable has type "Options2") [assignment] +protocols_modules.py:26: note: Following member(s) of Module "_protocols_modules1" have conflicts: +protocols_modules.py:26: note: timeout: expected "str", got "int" +protocols_modules.py:48: error: Incompatible types in assignment (expression has type Module, variable has type "Reporter2") [assignment] +protocols_modules.py:48: note: Following member(s) of Module "_protocols_modules2" have conflicts: +protocols_modules.py:48: note: Expected: +protocols_modules.py:48: note: def on_error(x: int) -> int +protocols_modules.py:48: note: Got: +protocols_modules.py:48: note: def on_error(x: int) -> None +protocols_modules.py:49: error: Incompatible types in assignment (expression has type Module, variable has type "Reporter3") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/protocols_recursive.toml b/conformance/results/mypy/protocols_recursive.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/mypy/protocols_recursive.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/protocols_runtime_checkable.toml b/conformance/results/mypy/protocols_runtime_checkable.toml new file mode 100644 index 000000000..63124dc56 --- /dev/null +++ b/conformance/results/mypy/protocols_runtime_checkable.toml @@ -0,0 +1,17 @@ +conformant = "Partial" +notes = """ +Does not report unsafe overlap for runtime_checkable protocol. +""" +output = """ +protocols_runtime_checkable.py:23: error: Only @runtime_checkable protocols can be used with instance and class checks [misc] +protocols_runtime_checkable.py:55: error: Only protocols that don't have non-method members can be used with issubclass() [misc] +protocols_runtime_checkable.py:55: note: Protocol "DataProtocol" has non-method member(s): name +protocols_runtime_checkable.py:61: error: Only protocols that don't have non-method members can be used with issubclass() [misc] +protocols_runtime_checkable.py:61: note: Protocol "DataProtocol" has non-method member(s): name +""" +conformance_automated = "Fail" +errors_diff = """ +Line 88: Expected 1 errors +Line 91: Expected 1 errors +Line 94: Expected 1 errors +""" diff --git a/conformance/results/mypy/protocols_self.toml b/conformance/results/mypy/protocols_self.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/mypy/protocols_self.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/protocols_subtyping.toml b/conformance/results/mypy/protocols_subtyping.toml new file mode 100644 index 000000000..36149cd0e --- /dev/null +++ b/conformance/results/mypy/protocols_subtyping.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +output = """ +protocols_subtyping.py:16: error: Cannot instantiate protocol class "Proto1" [misc] +protocols_subtyping.py:38: error: Incompatible types in assignment (expression has type "Proto2", variable has type "Concrete2") [assignment] +protocols_subtyping.py:55: error: Incompatible types in assignment (expression has type "Proto2", variable has type "Proto3") [assignment] +protocols_subtyping.py:55: note: "Proto2" is missing following "Proto3" protocol member: +protocols_subtyping.py:55: note: method2 +protocols_subtyping.py:79: error: Incompatible types in assignment (expression has type "Proto5[int]", variable has type "Proto4[int, float]") [assignment] +protocols_subtyping.py:80: error: Incompatible types in assignment (expression has type "Proto4[int, int]", variable has type "Proto5[float]") [assignment] +protocols_subtyping.py:102: error: Incompatible types in assignment (expression has type "Proto6[float, float]", variable has type "Proto7[int, float]") [assignment] +protocols_subtyping.py:103: error: Incompatible types in assignment (expression has type "Proto6[float, float]", variable has type "Proto7[float, object]") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/protocols_variance.toml b/conformance/results/mypy/protocols_variance.toml new file mode 100644 index 000000000..e434501cf --- /dev/null +++ b/conformance/results/mypy/protocols_variance.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +output = """ +protocols_variance.py:21: error: Invariant type variable "T1" used in protocol where covariant one is expected [misc] +protocols_variance.py:40: error: Invariant type variable "T3" used in protocol where contravariant one is expected [misc] +protocols_variance.py:56: error: Invariant type variable "T1" used in protocol where contravariant one is expected [misc] +protocols_variance.py:61: error: Covariant type variable "T1_co" used in protocol where contravariant one is expected [misc] +protocols_variance.py:62: error: Cannot use a covariant type variable as a parameter [misc] +protocols_variance.py:66: error: Invariant type variable "T1" used in protocol where covariant one is expected [misc] +protocols_variance.py:71: error: Contravariant type variable "T1_contra" used in protocol where covariant one is expected [misc] +protocols_variance.py:72: error: Cannot use a contravariant type variable as return type [misc] +protocols_variance.py:104: error: Invariant type variable "T1" used in protocol where covariant one is expected [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/qualifiers_annotated.toml b/conformance/results/mypy/qualifiers_annotated.toml new file mode 100644 index 000000000..6fba939f9 --- /dev/null +++ b/conformance/results/mypy/qualifiers_annotated.toml @@ -0,0 +1,39 @@ +conformant = "Partial" +notes = """ +Does not allow ClassVar to be nested within Annotated. +Does not allow Final to be nested within Annotated. +Does not allow Required and NotRequired to be nested within Annotated. +Does not reject type[T] compatibility for type alias defined with Annotated. +Does not reject call of type alias defined with Annotated. +""" +output = """ +qualifiers_annotated.py:38: error: Bracketed expression "[...]" is not valid as a type [valid-type] +qualifiers_annotated.py:39: error: Syntax error in type annotation [syntax] +qualifiers_annotated.py:39: note: Suggestion: Is there a spurious trailing comma? +qualifiers_annotated.py:40: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:41: error: Inline TypedDict is experimental, must be enabled with --enable-incomplete-feature=InlineTypedDict [misc] +qualifiers_annotated.py:41: error: Name "b" is not defined [name-defined] +qualifiers_annotated.py:42: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:43: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:44: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:45: error: Name "var1" is not defined [name-defined] +qualifiers_annotated.py:46: error: Invalid type: try using Literal[True] instead? [valid-type] +qualifiers_annotated.py:47: error: Invalid type: try using Literal[1] instead? [valid-type] +qualifiers_annotated.py:48: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:49: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:59: error: Annotated[...] must have exactly one type argument and at least one annotation [valid-type] +qualifiers_annotated.py:71: error: Incompatible types in assignment (expression has type "", variable has type "type[Any]") [assignment] +qualifiers_annotated.py:79: error: Argument 1 to "func4" has incompatible type ""; expected "type[Never]" [arg-type] +qualifiers_annotated.py:86: error: "" not callable [operator] +qualifiers_annotated.py:87: error: "" not callable [operator] +qualifiers_annotated.py:93: error: Invalid type: ClassVar nested inside other type [valid-type] +qualifiers_annotated.py:95: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 72: Expected 1 errors +Line 80: Expected 1 errors +Line 88: Expected 1 errors +Line 93: Unexpected errors ['qualifiers_annotated.py:93: error: Invalid type: ClassVar nested inside other type [valid-type]'] +Line 95: Unexpected errors ['qualifiers_annotated.py:95: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type]'] +""" diff --git a/conformance/results/mypy/qualifiers_final_annotation.toml b/conformance/results/mypy/qualifiers_final_annotation.toml new file mode 100644 index 000000000..2d0cea337 --- /dev/null +++ b/conformance/results/mypy/qualifiers_final_annotation.toml @@ -0,0 +1,48 @@ +conformant = "Partial" +notes = """ +Does not treat use of Final name as if it was replaced by the literal in NamedTuple definition. +Does not allow conditional assignment of Final instance variable in __init__ method. +Does not allow redefinition of private class variable that is marked Final in parent class. +Does not report modification of local Final variable via "for" statement. +""" +output = """ +qualifiers_final_annotation.py:16: error: Type in Final[...] can only be omitted if there is an initializer [misc] +qualifiers_final_annotation.py:18: error: Final[...] takes at most one type argument [misc] +qualifiers_final_annotation.py:34: error: Type in Final[...] can only be omitted if there is an initializer [misc] +qualifiers_final_annotation.py:38: error: Final name must be initialized with a value [misc] +qualifiers_final_annotation.py:54: error: Cannot assign to final attribute "ID5" [misc] +qualifiers_final_annotation.py:59: error: Cannot assign to final attribute "ID6" [misc] +qualifiers_final_annotation.py:62: error: Can only declare a final attribute in class body or __init__ [misc] +qualifiers_final_annotation.py:63: error: Can only declare a final attribute in class body or __init__ [misc] +qualifiers_final_annotation.py:65: error: Cannot assign to final attribute "ID7" [misc] +qualifiers_final_annotation.py:67: error: Cannot assign to final attribute "ID7" [misc] +qualifiers_final_annotation.py:71: error: Cannot assign to final name "RATE" [misc] +qualifiers_final_annotation.py:81: error: Cannot assign to final attribute "DEFAULT_ID" [misc] +qualifiers_final_annotation.py:94: error: Cannot assign to final name "BORDER_WIDTH" [misc] +qualifiers_final_annotation.py:96: error: Cannot assign to final name "__private" [misc] +qualifiers_final_annotation.py:107: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type] +qualifiers_final_annotation.py:108: error: Variable should not be annotated with both ClassVar and Final [misc] +qualifiers_final_annotation.py:118: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type] +qualifiers_final_annotation.py:121: error: Final can be only used as an outermost qualifier in a variable annotation [valid-type] +qualifiers_final_annotation.py:131: error: Invalid "NamedTuple()" field name [misc] +qualifiers_final_annotation.py:133: error: Unexpected keyword argument "x" for "N" [call-arg] +qualifiers_final_annotation.py:133: error: Unexpected keyword argument "y" for "N" [call-arg] +qualifiers_final_annotation.py:134: error: Unexpected keyword argument "a" for "N" [call-arg] +qualifiers_final_annotation.py:135: error: Unexpected keyword argument "x" for "N" [call-arg] +qualifiers_final_annotation.py:135: error: Unexpected keyword argument "y" for "N" [call-arg] +qualifiers_final_annotation.py:141: error: Cannot assign to final name "ID1" [misc] +qualifiers_final_annotation.py:145: error: Cannot assign to final name "x" [misc] +qualifiers_final_annotation.py:147: error: Cannot assign to final name "x" [misc] +qualifiers_final_annotation.py:152: error: Incompatible types in assignment (expression has type "TextIOWrapper[_WrappedBuffer]", variable has type "int") [assignment] +qualifiers_final_annotation.py:155: error: Cannot assign to final name "x" [misc] +qualifiers_final_annotation.py:166: error: Cannot assign to final name "TEN" [misc] +qualifiers_final_annotation.py:170: error: Cannot assign to final name "PI" [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 149: Expected 1 errors +Line 59: Unexpected errors ['qualifiers_final_annotation.py:59: error: Cannot assign to final attribute "ID6" [misc]'] +Line 96: Unexpected errors ['qualifiers_final_annotation.py:96: error: Cannot assign to final name "__private" [misc]'] +Line 131: Unexpected errors ['qualifiers_final_annotation.py:131: error: Invalid "NamedTuple()" field name [misc]'] +Line 133: Unexpected errors ['qualifiers_final_annotation.py:133: error: Unexpected keyword argument "x" for "N" [call-arg]', 'qualifiers_final_annotation.py:133: error: Unexpected keyword argument "y" for "N" [call-arg]'] +""" diff --git a/conformance/results/mypy/qualifiers_final_decorator.toml b/conformance/results/mypy/qualifiers_final_decorator.toml new file mode 100644 index 000000000..e25fa2c8a --- /dev/null +++ b/conformance/results/mypy/qualifiers_final_decorator.toml @@ -0,0 +1,21 @@ +conformant = "Pass" +output = """ +qualifiers_final_decorator.py:21: error: Cannot inherit from final class "Base1" [misc] +qualifiers_final_decorator.py:56: error: Cannot override final attribute "method1" (previously declared in base class "Base2") [misc] +qualifiers_final_decorator.py:59: error: Cannot override final attribute "method2" (previously declared in base class "Base2") [misc] +qualifiers_final_decorator.py:63: error: Cannot override final attribute "method3" (previously declared in base class "Base2") [misc] +qualifiers_final_decorator.py:67: error: Cannot override final attribute "method4" (previously declared in base class "Base2") [misc] +qualifiers_final_decorator.py:80: error: Cannot override final attribute "method" (previously declared in base class "Base3") [misc] +qualifiers_final_decorator.py:84: error: @final should be applied only to overload implementation [misc] +qualifiers_final_decorator.py:94: error: Cannot override final attribute "method" (previously declared in base class "Base4") [misc] +qualifiers_final_decorator.py:118: error: Cannot override final attribute "method" (previously declared in base class "Base5_2") [misc] +qualifiers_final_decorator.py:118: error: Signature of "method" incompatible with supertype "Base5_2" [override] +qualifiers_final_decorator.py:118: note: Superclass: +qualifiers_final_decorator.py:118: note: def method(self, v: int) -> None +qualifiers_final_decorator.py:118: note: Subclass: +qualifiers_final_decorator.py:118: note: def method(self) -> None +qualifiers_final_decorator.py:125: error: @final cannot be used with non-method functions [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/specialtypes_any.toml b/conformance/results/mypy/specialtypes_any.toml new file mode 100644 index 000000000..42bf4cc39 --- /dev/null +++ b/conformance/results/mypy/specialtypes_any.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +specialtypes_any.py:37: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs [annotation-unchecked] +specialtypes_any.py:38: note: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs [annotation-unchecked] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/specialtypes_never.toml b/conformance/results/mypy/specialtypes_never.toml new file mode 100644 index 000000000..d4640eb57 --- /dev/null +++ b/conformance/results/mypy/specialtypes_never.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +specialtypes_never.py:19: error: Implicit return in function which does not return [misc] +specialtypes_never.py:85: error: Incompatible types in assignment (expression has type "list[Never]", variable has type "list[int]") [assignment] +specialtypes_never.py:85: note: "list" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +specialtypes_never.py:85: note: Consider using "Sequence" instead, which is covariant +specialtypes_never.py:104: error: Incompatible return value type (got "ClassC[Never]", expected "ClassC[U]") [return-value] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/specialtypes_none.toml b/conformance/results/mypy/specialtypes_none.toml new file mode 100644 index 000000000..f08b044a7 --- /dev/null +++ b/conformance/results/mypy/specialtypes_none.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +output = """ +specialtypes_none.py:21: error: Argument 1 to "func1" has incompatible type "type[None]"; expected "None" [arg-type] +specialtypes_none.py:27: error: Incompatible types in assignment (expression has type "None", variable has type "Iterable[Any]") [assignment] +specialtypes_none.py:41: error: Argument 1 to "func2" has incompatible type "None"; expected "type[None]" [arg-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/specialtypes_promotions.toml b/conformance/results/mypy/specialtypes_promotions.toml new file mode 100644 index 000000000..864bc73e2 --- /dev/null +++ b/conformance/results/mypy/specialtypes_promotions.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +output = """ +specialtypes_promotions.py:13: error: "float" has no attribute "numerator" [attr-defined] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/specialtypes_type.toml b/conformance/results/mypy/specialtypes_type.toml new file mode 100644 index 000000000..ea8d59f2a --- /dev/null +++ b/conformance/results/mypy/specialtypes_type.toml @@ -0,0 +1,27 @@ +conformant = "Partial" +notes = """ +Does not treat `type` same as `type[Any]` for assert_type. +Does not allow access to unknown attributes from object of type `type[Any]`. +""" +output = """ +specialtypes_type.py:56: error: Argument 1 to "func4" has incompatible type "type[TeamUser]"; expected "type[BasicUser] | type[ProUser]" [arg-type] +specialtypes_type.py:70: error: Argument 1 to "func5" has incompatible type ""; expected "type[Never]" [arg-type] +specialtypes_type.py:76: error: type[...] must have exactly one type argument [valid-type] +specialtypes_type.py:84: error: Expression is of type "type", not "type[Any]" [assert-type] +specialtypes_type.py:99: error: "type" has no attribute "unknown" [attr-defined] +specialtypes_type.py:100: error: "type" has no attribute "unknown" [attr-defined] +specialtypes_type.py:117: error: "type[object]" has no attribute "unknown" [attr-defined] +specialtypes_type.py:120: error: "type[object]" has no attribute "unknown" [attr-defined] +specialtypes_type.py:139: error: Expression is of type "type", not "type[Any]" [assert-type] +specialtypes_type.py:143: error: "" has no attribute "unknown" [attr-defined] +specialtypes_type.py:144: error: "" has no attribute "unknown" [attr-defined] +specialtypes_type.py:145: error: "type[type]" has no attribute "unknown" [attr-defined] +specialtypes_type.py:146: error: "" has no attribute "unknown" [attr-defined] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 84: Unexpected errors ['specialtypes_type.py:84: error: Expression is of type "type", not "type[Any]" [assert-type]'] +Line 99: Unexpected errors ['specialtypes_type.py:99: error: "type" has no attribute "unknown" [attr-defined]'] +Line 100: Unexpected errors ['specialtypes_type.py:100: error: "type" has no attribute "unknown" [attr-defined]'] +Line 139: Unexpected errors ['specialtypes_type.py:139: error: Expression is of type "type", not "type[Any]" [assert-type]'] +""" diff --git a/conformance/results/mypy/tuples_type_compat.toml b/conformance/results/mypy/tuples_type_compat.toml new file mode 100644 index 000000000..a63d5f961 --- /dev/null +++ b/conformance/results/mypy/tuples_type_compat.toml @@ -0,0 +1,38 @@ +conformant = "Partial" +notes = """ +Does not support tuple narrowing based on `len()` type guard (optional). +Incorrectly marks a match case as unreachable. +""" +output = """ +tuples_type_compat.py:15: error: Incompatible types in assignment (expression has type "tuple[float, complex]", variable has type "tuple[int, int]") [assignment] +tuples_type_compat.py:29: error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[int, *tuple[int, ...]]") [assignment] +tuples_type_compat.py:32: error: Incompatible types in assignment (expression has type "tuple[int, *tuple[int, ...]]", variable has type "tuple[int]") [assignment] +tuples_type_compat.py:33: error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[int]") [assignment] +tuples_type_compat.py:43: error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[int]") [assignment] +tuples_type_compat.py:62: error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[int, int]") [assignment] +tuples_type_compat.py:76: error: Expression is of type "tuple[int]", not "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" [assert-type] +tuples_type_compat.py:81: error: Expression is of type "tuple[str, str] | tuple[int, int]", not "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" [assert-type] +tuples_type_compat.py:86: error: Expression is of type "tuple[int, str, int]", not "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" [assert-type] +tuples_type_compat.py:102: error: Expression is of type "tuple[int]", not "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" [assert-type] +tuples_type_compat.py:107: error: Expression is of type "tuple[str, str] | tuple[int, int]", not "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" [assert-type] +tuples_type_compat.py:112: error: Expression is of type "tuple[int, str, int]", not "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" [assert-type] +tuples_type_compat.py:127: error: Expression is of type "tuple[int | str, str]", not "tuple[int | str, int | str]" [assert-type] +tuples_type_compat.py:130: error: Expression is of type "tuple[int | str, int]", not "tuple[int | str, int | str]" [assert-type] +tuples_type_compat.py:149: error: Expression is of type "Sequence[object]", not "Sequence[complex | list[int]]" [assert-type] +tuples_type_compat.py:152: error: Expression is of type "Sequence[object]", not "Sequence[int | str]" [assert-type] +tuples_type_compat.py:157: error: Incompatible types in assignment (expression has type "tuple[int, str, str]", variable has type "tuple[int, str]") [assignment] +tuples_type_compat.py:162: error: Incompatible types in assignment (expression has type "tuple[int, int, str]", variable has type "tuple[int, *tuple[str, ...]]") [assignment] +tuples_type_compat.py:163: error: Incompatible types in assignment (expression has type "tuple[int, str, int]", variable has type "tuple[int, *tuple[str, ...]]") [assignment] +tuples_type_compat.py:169: error: Incompatible types in assignment (expression has type "tuple[int, str, str]", variable has type "tuple[int, *tuple[str, ...], int]") [assignment] +tuples_type_compat.py:170: error: Incompatible types in assignment (expression has type "tuple[int, str, str, float]", variable has type "tuple[int, *tuple[str, ...], int]") [assignment] +tuples_type_compat.py:175: error: Incompatible types in assignment (expression has type "tuple[int, str, int]", variable has type "tuple[*tuple[str, ...], int]") [assignment] +tuples_type_compat.py:176: error: Incompatible types in assignment (expression has type "tuple[str, str, float]", variable has type "tuple[*tuple[str, ...], int]") [assignment] +tuples_type_compat.py:181: error: Incompatible types in assignment (expression has type "tuple[str, str]", variable has type "tuple[str, str, int]") [assignment] +tuples_type_compat.py:184: error: Incompatible types in assignment (expression has type "tuple[str, str]", variable has type "tuple[str, str, str, *tuple[str, ...]]") [assignment] +tuples_type_compat.py:188: error: Incompatible types in assignment (expression has type "tuple[str, str]", variable has type "tuple[*tuple[str, ...], str, str, str]") [assignment] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 149: Unexpected errors ['tuples_type_compat.py:149: error: Expression is of type "Sequence[object]", not "Sequence[complex | list[int]]" [assert-type]'] +Line 152: Unexpected errors ['tuples_type_compat.py:152: error: Expression is of type "Sequence[object]", not "Sequence[int | str]" [assert-type]'] +""" diff --git a/conformance/results/mypy/tuples_type_form.toml b/conformance/results/mypy/tuples_type_form.toml new file mode 100644 index 000000000..b5892f7bb --- /dev/null +++ b/conformance/results/mypy/tuples_type_form.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +output = """ +tuples_type_form.py:12: error: Incompatible types in assignment (expression has type "tuple[int, int]", variable has type "tuple[int]") [assignment] +tuples_type_form.py:14: error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "tuple[int, int]") [assignment] +tuples_type_form.py:15: error: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "tuple[int, int]") [assignment] +tuples_type_form.py:25: error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "tuple[()]") [assignment] +tuples_type_form.py:36: error: Incompatible types in assignment (expression has type "tuple[int, int, int, str]", variable has type "tuple[int, ...]") [assignment] +tuples_type_form.py:40: error: Unexpected "..." [misc] +tuples_type_form.py:41: error: Unexpected "..." [misc] +tuples_type_form.py:42: error: Unexpected "..." [misc] +tuples_type_form.py:43: error: Unexpected "..." [misc] +tuples_type_form.py:44: error: Unpack is only valid in a variadic position [valid-type] +tuples_type_form.py:45: error: Unpack is only valid in a variadic position [valid-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/tuples_unpacked.toml b/conformance/results/mypy/tuples_unpacked.toml new file mode 100644 index 000000000..12dee95c6 --- /dev/null +++ b/conformance/results/mypy/tuples_unpacked.toml @@ -0,0 +1,14 @@ +conformant = "Partial" +notes = """ +"More than one unpack" error is missing in some cases. +""" +output = """ +tuples_unpacked.py:40: error: More than one variadic Unpack in a type is not allowed [misc] +tuples_unpacked.py:51: error: More than one variadic Unpack in a type is not allowed [misc] +tuples_unpacked.py:59: error: More than one variadic Unpack in a type is not allowed [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 41: Expected 1 errors +Lines 60, 61: Expected error (tag 't14') +""" diff --git a/conformance/results/mypy/typeddicts_alt_syntax.toml b/conformance/results/mypy/typeddicts_alt_syntax.toml new file mode 100644 index 000000000..2d1177efa --- /dev/null +++ b/conformance/results/mypy/typeddicts_alt_syntax.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +notes = """ +Does not support keyword-argument form of alternative syntax (deprecated in 3.11). +""" +output = """ +typeddicts_alt_syntax.py:23: error: TypedDict() expects a dictionary literal as the second argument [misc] +typeddicts_alt_syntax.py:27: error: Invalid TypedDict() field name [misc] +typeddicts_alt_syntax.py:31: error: First argument "WrongName" to TypedDict() does not match variable name "BadTypedDict3" [name-match] +typeddicts_alt_syntax.py:35: error: Too many arguments for TypedDict() [misc] +typeddicts_alt_syntax.py:41: error: Unexpected arguments to TypedDict() [misc] +typeddicts_alt_syntax.py:44: error: Extra keys ("name", "year") for TypedDict "Movie2" [typeddict-unknown-key] +typeddicts_alt_syntax.py:45: error: Extra keys ("name", "year") for TypedDict "Movie2" [typeddict-unknown-key] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_class_syntax.toml b/conformance/results/mypy/typeddicts_class_syntax.toml new file mode 100644 index 000000000..29220811d --- /dev/null +++ b/conformance/results/mypy/typeddicts_class_syntax.toml @@ -0,0 +1,21 @@ +conformant = "Partial" +notes = """ +Does not support version-conditional items in TypedDict definitions. +""" +output = """ +typeddicts_class_syntax.py:30: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc] +typeddicts_class_syntax.py:34: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc] +typeddicts_class_syntax.py:39: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc] +typeddicts_class_syntax.py:45: error: Unexpected keyword argument "metaclass" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_class_syntax.py:50: error: Unexpected keyword argument "other" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_class_syntax.py:58: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc] +typeddicts_class_syntax.py:60: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc] +typeddicts_class_syntax.py:64: error: Extra key "y" for TypedDict "ConditionalField" [typeddict-unknown-key] +typeddicts_class_syntax.py:65: error: Extra keys ("y", "z") for TypedDict "ConditionalField" [typeddict-unknown-key] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 58: Unexpected errors ['typeddicts_class_syntax.py:58: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc]'] +Line 60: Unexpected errors ['typeddicts_class_syntax.py:60: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc]'] +Line 64: Unexpected errors ['typeddicts_class_syntax.py:64: error: Extra key "y" for TypedDict "ConditionalField" [typeddict-unknown-key]'] +""" diff --git a/conformance/results/mypy/typeddicts_extra_items.toml b/conformance/results/mypy/typeddicts_extra_items.toml new file mode 100644 index 000000000..e7c58f8bc --- /dev/null +++ b/conformance/results/mypy/typeddicts_extra_items.toml @@ -0,0 +1,145 @@ +conformant = "Unsupported" +notes = """ +Not supported. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 215: Expected 1 errors +Line 222: Expected 1 errors +Line 242: Expected 1 errors +Line 256: Expected 1 errors +Line 257: Expected 1 errors +Line 268: Expected 1 errors +Lines 91, 92: Expected error (tag 'MovieC') +Lines 94, 95: Expected error (tag 'MovieD') +Lines 184, 185: Expected error (tag 'MovieRequiredYear') +Lines 187, 188: Expected error (tag 'MovieNotRequiredYear') +Lines 196, 197: Expected error (tag 'BookWithPublisher') +Line 11: Unexpected errors ['typeddicts_extra_items.py:11: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 14: Unexpected errors ['typeddicts_extra_items.py:14: error: Extra key "novel_adaptation" for TypedDict "Movie" [typeddict-unknown-key]'] +Line 19: Unexpected errors ['typeddicts_extra_items.py:19: error: Unexpected keyword argument "extra_items" for "TypedDict" [misc]'] +Line 21: Unexpected errors ['typeddicts_extra_items.py:21: error: Extra keys ("name", "novel_adaptation") for TypedDict "MovieFunctional" [typeddict-unknown-key]'] +Line 29: Unexpected errors ['typeddicts_extra_items.py:29: error: Expression is of type "Any", not "bool" [assert-type]', 'typeddicts_extra_items.py:29: error: TypedDict "Movie" has no key "novel_adaptation" [typeddict-item]'] +Line 33: Unexpected errors ['typeddicts_extra_items.py:33: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 40: Unexpected errors ['typeddicts_extra_items.py:40: error: Extra key "other_extra_key" for TypedDict "InheritedMovie" [typeddict-unknown-key]'] +Line 55: Unexpected errors ['typeddicts_extra_items.py:55: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 64: Unexpected errors ['typeddicts_extra_items.py:64: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 70: Unexpected errors ['typeddicts_extra_items.py:70: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 82: Unexpected errors ['typeddicts_extra_items.py:82: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 88: Unexpected errors ['typeddicts_extra_items.py:88: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 100: Unexpected errors ['typeddicts_extra_items.py:100: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 103: Unexpected errors ['typeddicts_extra_items.py:103: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 106: Unexpected errors ['typeddicts_extra_items.py:106: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 124: Unexpected errors ['typeddicts_extra_items.py:124: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 129: Unexpected errors ['typeddicts_extra_items.py:129: error: TypedDict "MovieEI" has no key "year" [typeddict-item]'] +Line 137: Unexpected errors ['typeddicts_extra_items.py:137: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 144: Unexpected errors ['typeddicts_extra_items.py:144: error: Unexpected keyword argument "year" for "unpack_extra" [call-arg]'] +Line 149: Unexpected errors ['typeddicts_extra_items.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 152: Unexpected errors ['typeddicts_extra_items.py:152: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 155: Unexpected errors ['typeddicts_extra_items.py:155: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 165: Unexpected errors ['typeddicts_extra_items.py:165: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 171: Unexpected errors ['typeddicts_extra_items.py:171: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 181: Unexpected errors ['typeddicts_extra_items.py:181: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 193: Unexpected errors ['typeddicts_extra_items.py:193: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 210: Unexpected errors ['typeddicts_extra_items.py:210: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 217: Unexpected errors ['typeddicts_extra_items.py:217: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 228: Unexpected errors ['typeddicts_extra_items.py:228: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 231: Unexpected errors ['typeddicts_extra_items.py:231: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 235: Unexpected errors ['typeddicts_extra_items.py:235: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 248: Unexpected errors ['typeddicts_extra_items.py:248: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 251: Unexpected errors ['typeddicts_extra_items.py:251: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 254: Unexpected errors ['typeddicts_extra_items.py:254: error: Extra key "year" for TypedDict "MovieExtraInt" [typeddict-unknown-key]'] +Line 255: Unexpected errors ['typeddicts_extra_items.py:255: error: Extra key "description" for TypedDict "MovieExtraStr" [typeddict-unknown-key]'] +Line 266: Unexpected errors ['typeddicts_extra_items.py:266: error: Extra key "year" for TypedDict "MovieExtraInt" [typeddict-unknown-key]'] +Line 280: Unexpected errors ['typeddicts_extra_items.py:280: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 284: Unexpected errors ['typeddicts_extra_items.py:284: error: Extra key "year" for TypedDict "ExtraMovie" [typeddict-unknown-key]'] +Line 289: Unexpected errors ['typeddicts_extra_items.py:289: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 299: Unexpected errors ['typeddicts_extra_items.py:299: error: Extra key "summary" for TypedDict "MovieExtraStr" [typeddict-unknown-key]'] +Line 300: Unexpected errors ['typeddicts_extra_items.py:300: error: Incompatible types in assignment (expression has type "MovieExtraStr", variable has type "Mapping[str, str]") [assignment]'] +Line 302: Unexpected errors ['typeddicts_extra_items.py:302: error: Extra key "year" for TypedDict "MovieExtraInt" [typeddict-unknown-key]'] +Line 304: Unexpected errors ['typeddicts_extra_items.py:304: error: Incompatible types in assignment (expression has type "MovieExtraInt", variable has type "Mapping[str, int | str]") [assignment]'] +Line 310: Unexpected errors ['typeddicts_extra_items.py:310: error: Expression is of type "list[tuple[str, object]]", not "list[tuple[str, int | str]]" [assert-type]'] +Line 311: Unexpected errors ['typeddicts_extra_items.py:311: error: Expression is of type "list[object]", not "list[int | str]" [assert-type]'] +Line 319: Unexpected errors ['typeddicts_extra_items.py:319: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg]'] +Line 326: Unexpected errors ['typeddicts_extra_items.py:326: error: Incompatible types in assignment (expression has type "IntDict", variable has type "dict[str, int]") [assignment]'] +Line 329: Unexpected errors ['typeddicts_extra_items.py:329: error: Extra key "bar" for TypedDict "IntDictWithNum" [typeddict-unknown-key]'] +Line 330: Unexpected errors ['typeddicts_extra_items.py:330: error: Incompatible types in assignment (expression has type "IntDictWithNum", variable has type "dict[str, int]") [assignment]'] +Line 337: Unexpected errors ['typeddicts_extra_items.py:337: error: "IntDictWithNum" has no attribute "clear" [attr-defined]'] +Line 339: Unexpected errors ['typeddicts_extra_items.py:339: error: Expression is of type "Any", not "tuple[str, int]" [assert-type]', 'typeddicts_extra_items.py:339: error: "IntDictWithNum" has no attribute "popitem" [attr-defined]'] +Line 342: Unexpected errors ['typeddicts_extra_items.py:342: error: TypedDict key must be a string literal; expected one of ("num") [literal-required]'] +Line 343: Unexpected errors ['typeddicts_extra_items.py:343: error: Expected TypedDict key to be string literal [misc]'] +""" +output = """ +typeddicts_extra_items.py:11: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:14: error: Extra key "novel_adaptation" for TypedDict "Movie" [typeddict-unknown-key] +typeddicts_extra_items.py:15: error: Extra key "year" for TypedDict "Movie" [typeddict-unknown-key] +typeddicts_extra_items.py:19: error: Unexpected keyword argument "extra_items" for "TypedDict" [misc] +typeddicts_extra_items.py:21: error: Extra keys ("name", "novel_adaptation") for TypedDict "MovieFunctional" [typeddict-unknown-key] +typeddicts_extra_items.py:22: error: Extra keys ("name", "year") for TypedDict "MovieFunctional" [typeddict-unknown-key] +typeddicts_extra_items.py:29: error: Expression is of type "Any", not "bool" [assert-type] +typeddicts_extra_items.py:29: error: TypedDict "Movie" has no key "novel_adaptation" [typeddict-item] +typeddicts_extra_items.py:33: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:39: error: Incompatible types (expression has type "None", TypedDict item "year" has type "int") [typeddict-item] +typeddicts_extra_items.py:40: error: Extra key "other_extra_key" for TypedDict "InheritedMovie" [typeddict-unknown-key] +typeddicts_extra_items.py:49: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:55: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:64: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:67: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:70: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:73: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:82: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:88: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:100: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:103: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:106: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:109: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:114: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:117: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:124: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:128: error: Key "name" of TypedDict "MovieEI" cannot be deleted [misc] +typeddicts_extra_items.py:129: error: TypedDict "MovieEI" has no key "year" [typeddict-item] +typeddicts_extra_items.py:137: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:143: error: Unexpected keyword argument "year" for "unpack_no_extra" [call-arg] +typeddicts_extra_items.py:144: error: Unexpected keyword argument "year" for "unpack_extra" [call-arg] +typeddicts_extra_items.py:149: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:152: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:155: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:165: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:171: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:174: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:181: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:193: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:210: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:217: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:228: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:231: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:235: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:248: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:251: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:254: error: Extra key "year" for TypedDict "MovieExtraInt" [typeddict-unknown-key] +typeddicts_extra_items.py:255: error: Extra key "description" for TypedDict "MovieExtraStr" [typeddict-unknown-key] +typeddicts_extra_items.py:266: error: Extra key "year" for TypedDict "MovieExtraInt" [typeddict-unknown-key] +typeddicts_extra_items.py:278: error: Extra key "year" for TypedDict "NonClosedMovie" [typeddict-unknown-key] +typeddicts_extra_items.py:280: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:284: error: Extra key "year" for TypedDict "ExtraMovie" [typeddict-unknown-key] +typeddicts_extra_items.py:285: error: Extra key "language" for TypedDict "ExtraMovie" [typeddict-unknown-key] +typeddicts_extra_items.py:289: error: Unexpected keyword argument "closed" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:293: error: Extra key "year" for TypedDict "ClosedMovie" [typeddict-unknown-key] +typeddicts_extra_items.py:299: error: Extra key "summary" for TypedDict "MovieExtraStr" [typeddict-unknown-key] +typeddicts_extra_items.py:300: error: Incompatible types in assignment (expression has type "MovieExtraStr", variable has type "Mapping[str, str]") [assignment] +typeddicts_extra_items.py:302: error: Extra key "year" for TypedDict "MovieExtraInt" [typeddict-unknown-key] +typeddicts_extra_items.py:303: error: Incompatible types in assignment (expression has type "MovieExtraInt", variable has type "Mapping[str, int]") [assignment] +typeddicts_extra_items.py:304: error: Incompatible types in assignment (expression has type "MovieExtraInt", variable has type "Mapping[str, int | str]") [assignment] +typeddicts_extra_items.py:310: error: Expression is of type "list[tuple[str, object]]", not "list[tuple[str, int | str]]" [assert-type] +typeddicts_extra_items.py:311: error: Expression is of type "list[object]", not "list[int | str]" [assert-type] +typeddicts_extra_items.py:319: error: Unexpected keyword argument "extra_items" for "__init_subclass__" of "TypedDict" [call-arg] +typeddicts_extra_items.py:326: error: Incompatible types in assignment (expression has type "IntDict", variable has type "dict[str, int]") [assignment] +typeddicts_extra_items.py:329: error: Extra key "bar" for TypedDict "IntDictWithNum" [typeddict-unknown-key] +typeddicts_extra_items.py:330: error: Incompatible types in assignment (expression has type "IntDictWithNum", variable has type "dict[str, int]") [assignment] +typeddicts_extra_items.py:337: error: "IntDictWithNum" has no attribute "clear" [attr-defined] +typeddicts_extra_items.py:339: error: Expression is of type "Any", not "tuple[str, int]" [assert-type] +typeddicts_extra_items.py:339: error: "IntDictWithNum" has no attribute "popitem" [attr-defined] +typeddicts_extra_items.py:342: error: TypedDict key must be a string literal; expected one of ("num") [literal-required] +typeddicts_extra_items.py:343: error: Expected TypedDict key to be string literal [misc] +typeddicts_extra_items.py:352: error: Incompatible types in assignment (expression has type "dict[str, int]", variable has type "IntDict") [assignment] +""" diff --git a/conformance/results/mypy/typeddicts_final.toml b/conformance/results/mypy/typeddicts_final.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/mypy/typeddicts_final.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_inheritance.toml b/conformance/results/mypy/typeddicts_inheritance.toml new file mode 100644 index 000000000..6679cb131 --- /dev/null +++ b/conformance/results/mypy/typeddicts_inheritance.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +output = """ +typeddicts_inheritance.py:44: error: All bases of a new TypedDict must be TypedDict types [misc] +typeddicts_inheritance.py:55: error: Overwriting TypedDict field "x" while extending [misc] +typeddicts_inheritance.py:65: error: Overwriting TypedDict field "x" while merging [misc] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_operations.toml b/conformance/results/mypy/typeddicts_operations.toml new file mode 100644 index 000000000..64839b9d6 --- /dev/null +++ b/conformance/results/mypy/typeddicts_operations.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +output = """ +typeddicts_operations.py:22: error: Value of "name" has incompatible type "int"; expected "str" [typeddict-item] +typeddicts_operations.py:23: error: Value of "year" has incompatible type "str"; expected "int" [typeddict-item] +typeddicts_operations.py:24: error: TypedDict "Movie" has no key "other" [typeddict-unknown-key] +typeddicts_operations.py:26: error: TypedDict "Movie" has no key "other" [typeddict-item] +typeddicts_operations.py:28: error: Missing key "year" for TypedDict "Movie" [typeddict-item] +typeddicts_operations.py:29: error: Incompatible types (expression has type "float", TypedDict item "year" has type "int") [typeddict-item] +typeddicts_operations.py:32: error: Extra key "other" for TypedDict "Movie" [typeddict-unknown-key] +typeddicts_operations.py:37: error: Expected TypedDict key to be string literal [misc] +typeddicts_operations.py:47: error: "Movie" has no attribute "clear" [attr-defined] +typeddicts_operations.py:49: error: Key "name" of TypedDict "Movie" cannot be deleted [misc] +typeddicts_operations.py:62: error: "MovieOptional" has no attribute "clear" [attr-defined] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_readonly.toml b/conformance/results/mypy/typeddicts_readonly.toml new file mode 100644 index 000000000..882ee1b87 --- /dev/null +++ b/conformance/results/mypy/typeddicts_readonly.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +output = """ +typeddicts_readonly.py:24: error: ReadOnly TypedDict key "members" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:36: error: ReadOnly TypedDict key "members" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:50: error: ReadOnly TypedDict key "title" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:51: error: ReadOnly TypedDict key "year" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:60: error: ReadOnly TypedDict key "title" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:61: error: ReadOnly TypedDict key "year" TypedDict is mutated [typeddict-readonly-mutated] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_readonly_consistency.toml b/conformance/results/mypy/typeddicts_readonly_consistency.toml new file mode 100644 index 000000000..803f1bbd2 --- /dev/null +++ b/conformance/results/mypy/typeddicts_readonly_consistency.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +output = """ +typeddicts_readonly_consistency.py:37: error: Incompatible types in assignment (expression has type "A1", variable has type "B1") [assignment] +typeddicts_readonly_consistency.py:38: error: Incompatible types in assignment (expression has type "C1", variable has type "B1") [assignment] +typeddicts_readonly_consistency.py:40: error: Incompatible types in assignment (expression has type "A1", variable has type "C1") [assignment] +typeddicts_readonly_consistency.py:81: error: Incompatible types in assignment (expression has type "A2", variable has type "B2") [assignment] +typeddicts_readonly_consistency.py:82: error: Incompatible types in assignment (expression has type "C2", variable has type "B2") [assignment] +typeddicts_readonly_consistency.py:84: error: Incompatible types in assignment (expression has type "A2", variable has type "C2") [assignment] +typeddicts_readonly_consistency.py:85: error: Incompatible types in assignment (expression has type "B2", variable has type "C2") [assignment] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_readonly_inheritance.toml b/conformance/results/mypy/typeddicts_readonly_inheritance.toml new file mode 100644 index 000000000..46bc4ce00 --- /dev/null +++ b/conformance/results/mypy/typeddicts_readonly_inheritance.toml @@ -0,0 +1,39 @@ +conformant = "Partial" +notes = """ +Incorrectly rejects non-ReadOnly override of ReadOnly item. +Incorrectly rejects override of ReadOnly item with another ReadOnly item with narrower type. +Incorrectly rejects override of NotRequired ReadOnly item with a Required ReadOnly item. +""" +output = """ +typeddicts_readonly_inheritance.py:19: error: Overwriting TypedDict field "name" while extending [misc] +typeddicts_readonly_inheritance.py:25: error: ReadOnly TypedDict key "name" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly_inheritance.py:36: error: ReadOnly TypedDict key "name" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly_inheritance.py:49: error: Overwriting TypedDict field "albums" while extending [misc] +typeddicts_readonly_inheritance.py:50: error: Overwriting TypedDict field "alt" while extending [misc] +typeddicts_readonly_inheritance.py:62: error: Overwriting TypedDict field "name" while extending [misc] +typeddicts_readonly_inheritance.py:65: error: Missing key "name" for TypedDict "RequiredName" [typeddict-item] +typeddicts_readonly_inheritance.py:76: error: Overwriting TypedDict field "ident" while extending [misc] +typeddicts_readonly_inheritance.py:81: error: ReadOnly TypedDict key "ident" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly_inheritance.py:82: error: ReadOnly TypedDict key "ident" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly_inheritance.py:82: error: Value of "ident" has incompatible type "int"; expected "str" [typeddict-item] +typeddicts_readonly_inheritance.py:83: error: Incompatible types (expression has type "int", TypedDict item "ident" has type "str") [typeddict-item] +typeddicts_readonly_inheritance.py:84: error: Missing key "ident" for TypedDict "User" [typeddict-item] +typeddicts_readonly_inheritance.py:94: error: Overwriting TypedDict field "a" while extending [misc] +typeddicts_readonly_inheritance.py:98: error: Overwriting TypedDict field "a" while extending [misc] +typeddicts_readonly_inheritance.py:102: error: Overwriting TypedDict field "b" while extending [misc] +typeddicts_readonly_inheritance.py:106: error: Overwriting TypedDict field "c" while extending [misc] +typeddicts_readonly_inheritance.py:119: error: Overwriting TypedDict field "x" while merging [misc] +typeddicts_readonly_inheritance.py:119: error: Overwriting TypedDict field "y" while merging [misc] +typeddicts_readonly_inheritance.py:132: error: Overwriting TypedDict field "x" while merging [misc] +typeddicts_readonly_inheritance.py:132: error: Overwriting TypedDict field "y" while merging [misc] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 19: Unexpected errors ['typeddicts_readonly_inheritance.py:19: error: Overwriting TypedDict field "name" while extending [misc]'] +Line 25: Unexpected errors ['typeddicts_readonly_inheritance.py:25: error: ReadOnly TypedDict key "name" TypedDict is mutated [typeddict-readonly-mutated]'] +Line 49: Unexpected errors ['typeddicts_readonly_inheritance.py:49: error: Overwriting TypedDict field "albums" while extending [misc]'] +Line 62: Unexpected errors ['typeddicts_readonly_inheritance.py:62: error: Overwriting TypedDict field "name" while extending [misc]'] +Line 76: Unexpected errors ['typeddicts_readonly_inheritance.py:76: error: Overwriting TypedDict field "ident" while extending [misc]'] +Line 81: Unexpected errors ['typeddicts_readonly_inheritance.py:81: error: ReadOnly TypedDict key "ident" TypedDict is mutated [typeddict-readonly-mutated]'] +Line 102: Unexpected errors ['typeddicts_readonly_inheritance.py:102: error: Overwriting TypedDict field "b" while extending [misc]'] +""" diff --git a/conformance/results/mypy/typeddicts_readonly_kwargs.toml b/conformance/results/mypy/typeddicts_readonly_kwargs.toml new file mode 100644 index 000000000..a90e015f1 --- /dev/null +++ b/conformance/results/mypy/typeddicts_readonly_kwargs.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +output = """ +typeddicts_readonly_kwargs.py:33: error: ReadOnly TypedDict key "key1" TypedDict is mutated [typeddict-readonly-mutated] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_readonly_update.toml b/conformance/results/mypy/typeddicts_readonly_update.toml new file mode 100644 index 000000000..c53706fc4 --- /dev/null +++ b/conformance/results/mypy/typeddicts_readonly_update.toml @@ -0,0 +1,13 @@ +conformant = "Partial" +notes = """ +Incorrectly allows update of ReadOnly item. +Incorrectly rejects update involving an item with Never type. +""" +output = """ +typeddicts_readonly_update.py:34: error: Argument 1 to "update" of "TypedDict" has incompatible type "B"; expected "TypedDict({'x'?=: int, 'y': int})" [typeddict-item] +""" +conformance_automated = "Fail" +errors_diff = """ +Line 23: Expected 1 errors +Line 34: Unexpected errors ['typeddicts_readonly_update.py:34: error: Argument 1 to "update" of "TypedDict" has incompatible type "B"; expected "TypedDict({\\'x\\'?=: int, \\'y\\': int})" [typeddict-item]'] +""" diff --git a/conformance/results/mypy/typeddicts_required.toml b/conformance/results/mypy/typeddicts_required.toml new file mode 100644 index 000000000..85c143e90 --- /dev/null +++ b/conformance/results/mypy/typeddicts_required.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +typeddicts_required.py:12: error: Required[] can be only used in a TypedDict definition [valid-type] +typeddicts_required.py:16: error: NotRequired[] can be only used in a TypedDict definition [valid-type] +typeddicts_required.py:59: error: "Required[]" type cannot be nested [valid-type] +typeddicts_required.py:60: error: "NotRequired[]" type cannot be nested [valid-type] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_type_consistency.toml b/conformance/results/mypy/typeddicts_type_consistency.toml new file mode 100644 index 000000000..3ddab2c9c --- /dev/null +++ b/conformance/results/mypy/typeddicts_type_consistency.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +output = """ +typeddicts_type_consistency.py:21: error: Incompatible types in assignment (expression has type "B1", variable has type "A1") [assignment] +typeddicts_type_consistency.py:38: error: Incompatible types in assignment (expression has type "B2", variable has type "A2") [assignment] +typeddicts_type_consistency.py:65: error: Incompatible types in assignment (expression has type "A3", variable has type "B3") [assignment] +typeddicts_type_consistency.py:69: error: Extra key "y" for TypedDict "A3" [typeddict-unknown-key] +typeddicts_type_consistency.py:76: error: Incompatible types in assignment (expression has type "B3", variable has type "dict[str, int]") [assignment] +typeddicts_type_consistency.py:77: error: Incompatible types in assignment (expression has type "B3", variable has type "dict[str, object]") [assignment] +typeddicts_type_consistency.py:78: error: Incompatible types in assignment (expression has type "B3", variable has type "dict[Any, Any]") [assignment] +typeddicts_type_consistency.py:82: error: Incompatible types in assignment (expression has type "B3", variable has type "Mapping[str, int]") [assignment] +typeddicts_type_consistency.py:126: error: Incompatible types (expression has type "int", TypedDict item "inner_key" has type "str") [typeddict-item] +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeddicts_usage.toml b/conformance/results/mypy/typeddicts_usage.toml new file mode 100644 index 000000000..ef96f5f0c --- /dev/null +++ b/conformance/results/mypy/typeddicts_usage.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +output = """ +typeddicts_usage.py:23: error: TypedDict "Movie" has no key "director" [typeddict-unknown-key] +typeddicts_usage.py:24: error: Value of "year" has incompatible type "str"; expected "int" [typeddict-item] +typeddicts_usage.py:28: error: Missing key "name" for TypedDict "Movie" [typeddict-item] +typeddicts_usage.py:28: error: Extra key "title" for TypedDict "Movie" [typeddict-unknown-key] +typeddicts_usage.py:35: error: Cannot use isinstance() with TypedDict type [misc] +typeddicts_usage.py:40: error: Variable "typing.TypedDict" is not valid as a type [valid-type] +typeddicts_usage.py:40: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/mypy/typeforms_typeform.toml b/conformance/results/mypy/typeforms_typeform.toml new file mode 100644 index 000000000..483dc9723 --- /dev/null +++ b/conformance/results/mypy/typeforms_typeform.toml @@ -0,0 +1,29 @@ +conformant = "Partial" +notes = """ +Does not support assigning Union and GenericAlias objects to their runtime types. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 46: Unexpected errors ['typeforms_typeform.py:46: error: Incompatible types in assignment (expression has type "UnionType | type[str]", variable has type "UnionType") [assignment]'] +Line 49: Unexpected errors ['typeforms_typeform.py:49: error: Incompatible types in assignment (expression has type "type[list[int]]", variable has type "GenericAlias") [assignment]'] +""" +output = """ +typeforms_typeform.py:23: error: Incompatible types in assignment (expression has type "TypeForm[str | int]", variable has type "TypeForm[str | None]") [assignment] +typeforms_typeform.py:24: error: Incompatible types in assignment (expression has type "TypeForm[list[str | None]]", variable has type "TypeForm[str | None]") [assignment] +typeforms_typeform.py:46: error: Incompatible types in assignment (expression has type "UnionType | type[str]", variable has type "UnionType") [assignment] +typeforms_typeform.py:49: error: Incompatible types in assignment (expression has type "type[list[int]]", variable has type "GenericAlias") [assignment] +typeforms_typeform.py:59: error: Argument 1 to "func1" has incompatible type "str"; expected "TypeForm[Any]" [arg-type] +typeforms_typeform.py:67: error: Incompatible types in assignment (expression has type "tuple[Never, ...]", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:68: error: Incompatible types in assignment (expression has type "tuple[int, int]", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:69: error: Incompatible types in assignment (expression has type "int", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:70: error: Incompatible types in assignment (expression has type "", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:71: error: Incompatible types in assignment (expression has type "", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:72: error: Incompatible types in assignment (expression has type "", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:73: error: Incompatible types in assignment (expression has type "", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:74: error: Incompatible types in assignment (expression has type "", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:75: error: Incompatible types in assignment (expression has type "str", variable has type "TypeForm[Any]") [assignment] +typeforms_typeform.py:86: error: Invalid type comment or annotation [valid-type] +typeforms_typeform.py:88: error: TypeForm argument is not a type [misc] +typeforms_typeform.py:98: error: Incompatible types in assignment (expression has type "TypeForm[int]", variable has type "TypeForm[str]") [assignment] +typeforms_typeform.py:108: error: Incompatible types in assignment (expression has type "type[int]", variable has type "TypeForm[str]") [assignment] +""" diff --git a/conformance/results/mypy/version.toml b/conformance/results/mypy/version.toml new file mode 100644 index 000000000..4d966c35b --- /dev/null +++ b/conformance/results/mypy/version.toml @@ -0,0 +1 @@ +version = "mypy 2.1.0" diff --git a/conformance/results/pycroscope/aliases_explicit.toml b/conformance/results/pycroscope/aliases_explicit.toml new file mode 100644 index 000000000..61a47bec4 --- /dev/null +++ b/conformance/results/pycroscope/aliases_explicit.toml @@ -0,0 +1,26 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./aliases_explicit.py:67:8: Expected 0 type arguments for type alias, got 1 [invalid_specialization] +./aliases_explicit.py:68:8: Expected 0 type arguments for type alias, got 1 [invalid_specialization] +./aliases_explicit.py:69:8: Expected 1 type arguments for type alias, got 2 [invalid_specialization] +./aliases_explicit.py:70:8: Expected 1 type arguments for type alias, got 2 [invalid_specialization] +./aliases_explicit.py:71:8: ParamSpec specialization must use list form, Concatenate[..., P], P, or ... [invalid_annotation] +./aliases_explicit.py:79:20: Invalid type annotation [invalid_annotation] +./aliases_explicit.py:80:20: Unrecognized annotation [invalid_annotation] +./aliases_explicit.py:81:20: Unrecognized annotation tuple[tuple[type 'int', type 'str']] [invalid_annotation] +./aliases_explicit.py:82:20: Invalid type annotation [invalid_annotation] +./aliases_explicit.py:83:20: Unrecognized annotation [invalid_annotation] +./aliases_explicit.py:84:20: Invalid type annotation [invalid_annotation] +./aliases_explicit.py:85:20: Cannot resolve subscripted annotation: [invalid_annotation] +./aliases_explicit.py:86:20: Invalid type annotation [invalid_annotation] +./aliases_explicit.py:87:20: Invalid type annotation 3 [invalid_annotation] +./aliases_explicit.py:88:21: Invalid type annotation True [invalid_annotation] +./aliases_explicit.py:89:21: Invalid type annotation 1 [invalid_annotation] +./aliases_explicit.py:90:21: Invalid type annotation [invalid_annotation] +./aliases_explicit.py:91:21: Invalid type annotation [invalid_annotation] +./aliases_explicit.py:100:4: Expected 0 type arguments for type alias, got 1 [invalid_specialization] +./aliases_explicit.py:101:5: TypeForm[.ListOrSetAlias] (synthetic from Literal[list | set]) is not callable [not_callable] +./aliases_explicit.py:102:4: Expected 0 type arguments for type alias, got 1 [invalid_specialization] +""" diff --git a/conformance/results/pycroscope/aliases_implicit.toml b/conformance/results/pycroscope/aliases_implicit.toml new file mode 100644 index 000000000..51e2e303d --- /dev/null +++ b/conformance/results/pycroscope/aliases_implicit.toml @@ -0,0 +1,42 @@ +conformant = "Partial" +notes = """ +Fails to handle various weird annotations. +Various bugs with resolving generic aliases. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 76: Expected 1 errors +Line 106: Expected 1 errors +Line 111: Expected 1 errors +Line 112: Expected 1 errors +Line 113: Expected 1 errors +Line 117: Expected 1 errors +Line 118: Expected 1 errors +Line 119: Expected 1 errors +Line 68: Unexpected errors ['./aliases_implicit.py:68:16: .GoodTypeAlias9[, None] = (int, /, ****P) -> ~R is not equivalent to (int, str, str, /) -> None'] +Line 70: Unexpected errors ['./aliases_implicit.py:70:16: Any[from_another] is not equivalent to int | str | list[list[int]] | None'] +Line 72: Unexpected errors ['./aliases_implicit.py:72:16: .GoodTypeAlias13 = (**__P: **P) -> None is not equivalent to (...) -> None'] +Line 95: Unexpected errors ["./aliases_implicit.py:95:16: Cannot resolve subscripted annotation: [invalid_annotation]"] +Line 100: Unexpected errors ["./aliases_implicit.py:100:17: Annotated[type 'list', (<+list is_truthy None> == NullConstraint())] is always True because it does not provide __bool__ [type_always_true]"] +""" +output = """ +./aliases_implicit.py:68:16: .GoodTypeAlias9[, None] = (int, /, ****P) -> ~R is not equivalent to (int, str, str, /) -> None +./aliases_implicit.py:70:16: Any[from_another] is not equivalent to int | str | list[list[int]] | None +./aliases_implicit.py:72:16: .GoodTypeAlias13 = (**__P: **P) -> None is not equivalent to (...) -> None +./aliases_implicit.py:77:8: Unrecognized annotation types.GenericAlias [invalid_annotation] +./aliases_implicit.py:78:8: Expected 1 type arguments for type alias, got 2 [invalid_specialization] +./aliases_implicit.py:79:8: Expected 1 type arguments for type alias, got 2 [invalid_specialization] +./aliases_implicit.py:80:8: ParamSpec specialization must use list form, Concatenate[..., P], P, or ... [invalid_annotation] +./aliases_implicit.py:81:8: Type argument str is not compatible with ~TFloat@./aliases_implicit.py.GoodTypeAlias12 [invalid_specialization] +./aliases_implicit.py:95:16: Cannot resolve subscripted annotation: [invalid_annotation] +./aliases_implicit.py:100:17: Annotated[type 'list', (<+list is_truthy None> == NullConstraint())] is always True because it does not provide __bool__ [type_always_true] +./aliases_implicit.py:107:8: Invalid type annotation [, ] [invalid_annotation] +./aliases_implicit.py:108:8: Invalid type annotation ((, ),) [invalid_annotation] +./aliases_implicit.py:109:8: Unrecognized annotation [invalid_annotation] +./aliases_implicit.py:110:8: Invalid type annotation {'a': 'b'} [invalid_annotation] +./aliases_implicit.py:114:8: Invalid type annotation 3 [invalid_annotation] +./aliases_implicit.py:115:9: Invalid type annotation True [invalid_annotation] +./aliases_implicit.py:116:9: Invalid type annotation 1 [invalid_annotation] +./aliases_implicit.py:133:5: Literal[list | set] is not callable [not_callable] +./aliases_implicit.py:135:4: Unrecognized annotation object [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/aliases_newtype.toml b/conformance/results/pycroscope/aliases_newtype.toml new file mode 100644 index 000000000..c0ce676a5 --- /dev/null +++ b/conformance/results/pycroscope/aliases_newtype.toml @@ -0,0 +1,19 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./aliases_newtype.py:11:7: Incompatible argument type for x: expected int but got Literal['user'] [incompatible_argument] +./aliases_newtype.py:12:0: Incompatible assignment: expected NewType('UserId', int), got Literal[42] [incompatible_assignment] +./aliases_newtype.py:18:0: Incompatible assignment: expected type, got Literal[pycroscope.implementation.UserId] [incompatible_assignment] +./aliases_newtype.py:23:15: Second argument to "isinstance" must be a type, union, or tuple of types, not "pycroscope.implementation.UserId" [incompatible_argument] +./aliases_newtype.py:26:20: NewType cannot be used as a base class [invalid_base] +./aliases_newtype.py:35:19: NewType named 'BadName' must be assigned to a variable with the same name [must_have_same_name] +./aliases_newtype.py:41:5: Object Literal[pycroscope.implementation.GoodNewType1] does not support subscripting [unsupported_operation] +./aliases_newtype.py:47:37: NewType base type cannot be generic [incompatible_call] +./aliases_newtype.py:50:37: NewType base type cannot be generic [incompatible_call] +./aliases_newtype.py:52:37: NewType base type cannot be a protocol [incompatible_call] +./aliases_newtype.py:54:37: NewType base type cannot be a literal type [incompatible_call] +./aliases_newtype.py:61:37: NewType base type cannot be a TypedDict [incompatible_call] +./aliases_newtype.py:63:14: In call to typing.NewType: Takes 2 positional arguments but 3 were given [incompatible_call] +./aliases_newtype.py:65:37: NewType base type cannot be Any [incompatible_call] +""" diff --git a/conformance/results/pycroscope/aliases_recursive.toml b/conformance/results/pycroscope/aliases_recursive.toml new file mode 100644 index 000000000..471c9bead --- /dev/null +++ b/conformance/results/pycroscope/aliases_recursive.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./aliases_recursive.py:19:0: Incompatible assignment: expected int | str | float | list[int | str | float | list[.Json = int | str | float | list[int | str | float | list[.Json] | dict[str, .Json] | None] | dict[str, int | str | float | list[.Json] | dict[str, .Json] | None] | None] | dict[str, .Json = int | str | float | list[int | str | float | list[.Json] | dict[str, .Json] | None] | dict[str, int | str | float | list[.Json] | dict[str, .Json] | None] | None] | None] | dict[str, int | str | float | list[.Json = int | str | float | list[int | str | float | list[.Json] | dict[str, .Json] | None] | dict[str, int | str | float | list[.Json] | dict[str, .Json] | None] | None] | dict[str, .Json = int | str | float | list[int | str | float | list[.Json] | dict[str, .Json] | None] | dict[str, int | str | float | list[.Json] | dict[str, .Json] | None] | None] | None] | None, got Literal[{'a': 1, 'b': 3j}] [incompatible_assignment] +./aliases_recursive.py:20:0: Incompatible assignment: expected int | str | float | list[int | str | float | list[.Json = int | str | float | list[int | str | float | list[.Json] | dict[str, .Json] | None] | dict[str, int | str | float | list[.Json] | dict[str, .Json] | None] | None] | dict[str, .Json = int | str | float | list[int | str | float | list[.Json] | dict[str, .Json] | None] | dict[str, int | str | float | list[.Json] | dict[str, .Json] | None] | None] | None] | dict[str, int | str | float | list[.Json = int | str | float | list[int | str | float | list[.Json] | dict[str, .Json] | None] | dict[str, int | str | float | list[.Json] | dict[str, .Json] | None] | None] | dict[str, .Json = int | str | float | list[int | str | float | list[.Json] | dict[str, .Json] | None] | dict[str, int | str | float | list[.Json] | dict[str, .Json] | None] | None] | None] | None, got Literal[[2, 3j]] [incompatible_assignment] +./aliases_recursive.py:38:0: Incompatible assignment: expected str | int | tuple[str | int | tuple[.RecursiveTuple = str | int | tuple[str | int | tuple[.RecursiveTuple, ...], ...], ...], ...], got Literal[(1, ('1', 1), (1, (1, [2])))] [incompatible_assignment] +./aliases_recursive.py:39:0: Incompatible assignment: expected str | int | tuple[str | int | tuple[.RecursiveTuple = str | int | tuple[str | int | tuple[.RecursiveTuple, ...], ...], ...], ...], got Literal[(1, [1])] [incompatible_assignment] +./aliases_recursive.py:50:0: Incompatible assignment: expected str | int | collections.abc.Mapping[str, str | int | collections.abc.Mapping[str, None.RecursiveMapping = str | int | collections.abc.Mapping[str, str | int | collections.abc.Mapping[str, None.RecursiveMapping]]]], got Literal[{'1': [1]}] [incompatible_assignment] +./aliases_recursive.py:51:0: Incompatible assignment: expected str | int | collections.abc.Mapping[str, str | int | collections.abc.Mapping[str, None.RecursiveMapping = str | int | collections.abc.Mapping[str, str | int | collections.abc.Mapping[str, None.RecursiveMapping]]]], got Literal[{'1': '1', '2': 1, '3': [1, 2]}] [incompatible_assignment] +./aliases_recursive.py:52:0: Incompatible assignment: expected str | int | collections.abc.Mapping[str, str | int | collections.abc.Mapping[str, None.RecursiveMapping = str | int | collections.abc.Mapping[str, str | int | collections.abc.Mapping[str, None.RecursiveMapping]]]], got Literal[{'1': '1', '2': 1, '3': {'0': '0', '1': 1, '2': [1, 2, 3]}}] [incompatible_assignment] +./aliases_recursive.py:63:0: Incompatible assignment: expected aliases_recursive.GenericTypeAlias1[str] = list[aliases_recursive.GenericTypeAlias1[~T1] = list[aliases_recursive.GenericTypeAlias1[~T1] | ~T1] | ~T1], got Literal[['hi', [2.4]]] [incompatible_assignment] +./aliases_recursive.py:69:0: Incompatible assignment: expected aliases_recursive.GenericTypeAlias2[str, int] = list[aliases_recursive.GenericTypeAlias2[~T1, ~T2] = list[aliases_recursive.GenericTypeAlias2[~T1, ~T2] | ~T1 | ~T2] | ~T1 | ~T2], got Literal[[[3, ['hi', 3, [3.4]]], 'hi']] [incompatible_assignment] +./aliases_recursive.py:72:0: Type alias RecursiveUnion has a circular definition [invalid_type_alias] +./aliases_recursive.py:75:0: Type alias MutualReference1 has a circular definition [invalid_type_alias] +./aliases_recursive.py:75:62: Type alias MutualReference2 has a circular definition [invalid_type_alias] +""" diff --git a/conformance/results/pycroscope/aliases_type_statement.toml b/conformance/results/pycroscope/aliases_type_statement.toml new file mode 100644 index 000000000..9961849bc --- /dev/null +++ b/conformance/results/pycroscope/aliases_type_statement.toml @@ -0,0 +1,35 @@ +conformant = "Partial" +notes = """ +Fails to reject various weird annotations. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 37: Expected 1 errors +Line 38: Expected 1 errors +Line 39: Expected 1 errors +Line 40: Expected 1 errors +Line 41: Expected 1 errors +Line 42: Expected 1 errors +Line 43: Expected 1 errors +Line 44: Expected 1 errors +Line 45: Expected 1 errors +Line 46: Expected 1 errors +Line 47: Expected 1 errors +Line 49: Expected 1 errors +""" +output = """ +./aliases_type_statement.py:17:0: typing.TypeAliasType has no attribute 'bit_count' [undefined_attribute] +./aliases_type_statement.py:19:0: TypeForm[.GoodAlias1] (synthetic from typing.TypeAliasType) is not callable [not_callable] +./aliases_type_statement.py:23:6: typing.TypeAliasType has no attribute 'other_attrib' [undefined_attribute] +./aliases_type_statement.py:26:17: Type alias cannot be used as a base class [invalid_base] +./aliases_type_statement.py:31:21: Second argument to "isinstance" cannot be a type alias [incompatible_argument] +./aliases_type_statement.py:48:22: Annotated[type 'list', (<+list is_truthy None> == NullConstraint())] is always True because it does not provide __bool__ [type_always_true] +./aliases_type_statement.py:53:22: Type alias cannot combine old-style TypeVar declarations with type statement parameters [invalid_type_alias] +./aliases_type_statement.py:58:16: Type alias must declare type parameters in the type statement [invalid_type_alias] +./aliases_type_statement.py:68:6: Type argument str is not compatible with ~S@./aliases_type_statement.py.RecursiveTypeAlias2 [invalid_specialization] +./aliases_type_statement.py:70:6: Type argument int is not compatible with ~T@./aliases_type_statement.py.RecursiveTypeAlias2 [invalid_specialization] +./aliases_type_statement.py:73:0: Type alias RecursiveTypeAlias3 has a circular definition [invalid_type_alias] +./aliases_type_statement.py:75:0: Type alias RecursiveTypeAlias4 has a circular definition [invalid_type_alias] +./aliases_type_statement.py:79:0: Type alias RecursiveTypeAlias6 has a circular definition [invalid_type_alias] +./aliases_type_statement.py:80:0: Type alias RecursiveTypeAlias7 has a circular definition [invalid_type_alias] +""" diff --git a/conformance/results/pycroscope/aliases_typealiastype.toml b/conformance/results/pycroscope/aliases_typealiastype.toml new file mode 100644 index 000000000..2708a2ee4 --- /dev/null +++ b/conformance/results/pycroscope/aliases_typealiastype.toml @@ -0,0 +1,34 @@ +conformant = "Partial" +notes = """ +Rejects valid ParamSpec specialization. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 39: Unexpected errors ["./aliases_typealiastype.py:39:4: Unrecognized annotation [invalid_annotation]"] +""" +output = """ +./aliases_typealiastype.py:32:6: typing.TypeAliasType has no attribute 'other_attrib' [undefined_attribute] +./aliases_typealiastype.py:39:4: Unrecognized annotation [invalid_annotation] +./aliases_typealiastype.py:40:4: Type argument int is not compatible with ~TStr@./aliases_typealiastype.py.GoodAlias5 [invalid_specialization] +./aliases_typealiastype.py:43:44: Type alias cannot combine old-style TypeVar declarations with type statement parameters [invalid_type_alias] +./aliases_typealiastype.py:44:44: Type alias must declare type parameters in the type statement [invalid_type_alias] +./aliases_typealiastype.py:45:56: type_params argument to TypeAliasType must be a literal tuple [invalid_type_alias] +./aliases_typealiastype.py:46:0: Type alias BadAlias4 has a circular definition [invalid_type_alias] +./aliases_typealiastype.py:47:0: Type alias BadAlias5 has a circular definition [invalid_type_alias] +./aliases_typealiastype.py:48:0: Type alias BadAlias6 has a circular definition [invalid_type_alias] +./aliases_typealiastype.py:49:0: Type alias BadAlias7 has a circular definition [invalid_type_alias] +./aliases_typealiastype.py:52:39: Invalid type annotation [invalid_annotation] +./aliases_typealiastype.py:53:39: Unrecognized annotation [invalid_annotation] +./aliases_typealiastype.py:54:41: Unrecognized annotation tuple[tuple[type 'int', type 'str']] [invalid_annotation] +./aliases_typealiastype.py:55:41: Invalid type annotation [invalid_annotation] +./aliases_typealiastype.py:56:41: Unrecognized annotation [invalid_annotation] +./aliases_typealiastype.py:57:41: Invalid type annotation [invalid_annotation] +./aliases_typealiastype.py:58:41: Cannot resolve subscripted annotation: [invalid_annotation] +./aliases_typealiastype.py:59:41: Invalid type annotation [invalid_annotation] +./aliases_typealiastype.py:60:41: Invalid type annotation 3 [invalid_annotation] +./aliases_typealiastype.py:61:41: Invalid type annotation True [invalid_annotation] +./aliases_typealiastype.py:62:41: Invalid type annotation 1 [invalid_annotation] +./aliases_typealiastype.py:63:41: Invalid type annotation [invalid_annotation] +./aliases_typealiastype.py:64:41: Invalid type annotation [invalid_annotation] +./aliases_typealiastype.py:66:0: Type alias BadAlias21 has a circular definition [invalid_type_alias] +""" diff --git a/conformance/results/pycroscope/aliases_variance.toml b/conformance/results/pycroscope/aliases_variance.toml new file mode 100644 index 000000000..9b84c03ca --- /dev/null +++ b/conformance/results/pycroscope/aliases_variance.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./aliases_variance.py:24:15: T_co has incompatible variance in base class [invalid_base] +./aliases_variance.py:28:15: T_co has incompatible variance in base class [invalid_base] +./aliases_variance.py:32:15: T_co has incompatible variance in base class [invalid_base] +./aliases_variance.py:44:15: T_contra has incompatible variance in base class [invalid_base] +""" diff --git a/conformance/results/pycroscope/annotations_coroutines.toml b/conformance/results/pycroscope/annotations_coroutines.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/annotations_coroutines.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/annotations_forward_refs.toml b/conformance/results/pycroscope/annotations_forward_refs.toml new file mode 100644 index 000000000..11cedda9e --- /dev/null +++ b/conformance/results/pycroscope/annotations_forward_refs.toml @@ -0,0 +1,48 @@ +conformant = "Partial" +notes = """ +Fails to reject "x" | int annotations that fail at runtime. +Rejects some valid quoted annotations. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 24: Expected 1 errors +Line 25: Expected 1 errors +Line 14: Unexpected errors ['./annotations_forward_refs.py:14:8: Undefined name: ClassA [undefined_name]', './annotations_forward_refs.py:14:22: Undefined name: ClassA [undefined_name]', './annotations_forward_refs.py:14:42: Undefined name: ClassA [undefined_name]', './annotations_forward_refs.py:14:62: Undefined name: ClassA [undefined_name]'] +Line 16: Unexpected errors ['./annotations_forward_refs.py:16:16: Any[error] is not equivalent to ./annotations_forward_refs.py.ClassA'] +Line 17: Unexpected errors ['./annotations_forward_refs.py:17:16: list[Any[error]] is not equivalent to list[./annotations_forward_refs.py.ClassA]'] +Line 18: Unexpected errors ['./annotations_forward_refs.py:18:16: list[Any[error]] is not equivalent to list[./annotations_forward_refs.py.ClassA]'] +Line 19: Unexpected errors ['./annotations_forward_refs.py:19:16: list[int | Any[error]] is not equivalent to list[./annotations_forward_refs.py.ClassA | int]'] +Line 87: Unexpected errors ['./annotations_forward_refs.py:87:7: Unrecognized annotation (self: ./annotations_forward_refs.py.ClassD) -> None [invalid_annotation]'] +Line 96: Unexpected errors ['./annotations_forward_refs.py:96:12: Any[error] is not equivalent to int'] +""" +output = """ +./annotations_forward_refs.py:14:8: Undefined name: ClassA [undefined_name] +./annotations_forward_refs.py:14:22: Undefined name: ClassA [undefined_name] +./annotations_forward_refs.py:14:42: Undefined name: ClassA [undefined_name] +./annotations_forward_refs.py:14:62: Undefined name: ClassA [undefined_name] +./annotations_forward_refs.py:16:16: Any[error] is not equivalent to ./annotations_forward_refs.py.ClassA +./annotations_forward_refs.py:17:16: list[Any[error]] is not equivalent to list[./annotations_forward_refs.py.ClassA] +./annotations_forward_refs.py:18:16: list[Any[error]] is not equivalent to list[./annotations_forward_refs.py.ClassA] +./annotations_forward_refs.py:19:16: list[int | Any[error]] is not equivalent to list[./annotations_forward_refs.py.ClassA | int] +./annotations_forward_refs.py:22:6: Undefined name: ClassA [undefined_name] +./annotations_forward_refs.py:23:11: Undefined name: ClassA [undefined_name] +./annotations_forward_refs.py:41:0: Invalid type annotation [invalid_annotation] +./annotations_forward_refs.py:42:8: Unrecognized annotation [invalid_annotation] +./annotations_forward_refs.py:43:8: Unrecognized annotation tuple[type 'int', type 'str'] [invalid_annotation] +./annotations_forward_refs.py:44:0: Invalid type annotation [invalid_annotation] +./annotations_forward_refs.py:45:8: Unrecognized annotation [invalid_annotation] +./annotations_forward_refs.py:46:0: Invalid type annotation [invalid_annotation] +./annotations_forward_refs.py:47:8: Cannot resolve subscripted annotation: [invalid_annotation] +./annotations_forward_refs.py:48:0: Invalid type annotation [invalid_annotation] +./annotations_forward_refs.py:49:8: Invalid type annotation 1 [invalid_annotation] +./annotations_forward_refs.py:50:9: Invalid type annotation True [invalid_annotation] +./annotations_forward_refs.py:51:9: Invalid type annotation 1 [invalid_annotation] +./annotations_forward_refs.py:52:9: Invalid type annotation -1 [invalid_annotation] +./annotations_forward_refs.py:53:0: Invalid type annotation [invalid_annotation] +./annotations_forward_refs.py:54:0: Invalid type annotation [invalid_annotation] +./annotations_forward_refs.py:55:9: Invalid type annotation [invalid_annotation] +./annotations_forward_refs.py:80:12: Undefined name: ClassF [undefined_name] +./annotations_forward_refs.py:87:7: Unrecognized annotation (self: ./annotations_forward_refs.py.ClassD) -> None [invalid_annotation] +./annotations_forward_refs.py:89:7: Unrecognized annotation (self: ./annotations_forward_refs.py.ClassD) -> None [invalid_annotation] +./annotations_forward_refs.py:96:12: Any[error] is not equivalent to int +""" diff --git a/conformance/results/pycroscope/annotations_generators.toml b/conformance/results/pycroscope/annotations_generators.toml new file mode 100644 index 000000000..44a1909d4 --- /dev/null +++ b/conformance/results/pycroscope/annotations_generators.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./annotations_generators.py:51:0: Function may exit without returning a value [missing_return] +./annotations_generators.py:54:8: Incompatible return type: expected annotations_generators.C, got Literal[False] [incompatible_return_value] +./annotations_generators.py:57:8: Cannot assign value of type Literal[3] to yield expression of type annotations_generators.A [incompatible_yield] +./annotations_generators.py:66:8: Cannot assign value of type Literal[3] to yield expression of type annotations_generators.A [incompatible_yield] +./annotations_generators.py:71:4: Incompatible return type: expected None, got Literal[True] [incompatible_return_value] +./annotations_generators.py:75:4: Cannot assign value of type annotations_generators.B to yield expression of type annotations_generators.A [incompatible_yield] +./annotations_generators.py:86:20: Generator function must return an iterable [generator_return] +./annotations_generators.py:91:26: Async generator function must return an async iterable [generator_return] +./annotations_generators.py:118:4: Cannot yield from collections.abc.Iterator[annotations_generators.A] (expected annotations_generators.B) [incompatible_yield] +./annotations_generators.py:119:4: Cannot yield from Literal[[1]] (expected annotations_generators.B) [incompatible_yield] +./annotations_generators.py:135:4: Cannot send int to a generator (expected str) [incompatible_yield] +""" diff --git a/conformance/results/pycroscope/annotations_methods.toml b/conformance/results/pycroscope/annotations_methods.toml new file mode 100644 index 000000000..abfcc72e8 --- /dev/null +++ b/conformance/results/pycroscope/annotations_methods.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./annotations_methods.py:42:12: annotations_methods.B is not equivalent to annotations_methods.A +""" diff --git a/conformance/results/pycroscope/annotations_typeexpr.toml b/conformance/results/pycroscope/annotations_typeexpr.toml new file mode 100644 index 000000000..d323d5697 --- /dev/null +++ b/conformance/results/pycroscope/annotations_typeexpr.toml @@ -0,0 +1,24 @@ +conformant = "Partial" +notes = """ +Fails to reject various weird annotations +""" +conformance_automated = "Fail" +errors_diff = """ +Line 88: Expected 1 errors +Line 93: Expected 1 errors +Line 94: Expected 1 errors +Line 95: Expected 1 errors +Line 100: Expected 1 errors +Line 101: Expected 1 errors +""" +output = """ +./annotations_typeexpr.py:89:8: Invalid type annotation [, ] [invalid_annotation] +./annotations_typeexpr.py:90:8: Invalid type annotation (, ) [invalid_annotation] +./annotations_typeexpr.py:91:8: Unrecognized annotation [invalid_annotation] +./annotations_typeexpr.py:92:8: Invalid type annotation {} [invalid_annotation] +./annotations_typeexpr.py:96:8: Invalid type annotation 3 [invalid_annotation] +./annotations_typeexpr.py:97:9: Invalid type annotation True [invalid_annotation] +./annotations_typeexpr.py:98:9: Invalid type annotation 1 [invalid_annotation] +./annotations_typeexpr.py:99:9: Invalid type annotation -1 [invalid_annotation] +./annotations_typeexpr.py:102:9: Invalid type annotation [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/callables_annotation.toml b/conformance/results/pycroscope/callables_annotation.toml new file mode 100644 index 000000000..2de3e2782 --- /dev/null +++ b/conformance/results/pycroscope/callables_annotation.toml @@ -0,0 +1,21 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./callables_annotation.py:25:4: Missing required positional argument at position 1 [incompatible_call] +./callables_annotation.py:26:10: Incompatible argument type for @1: expected str but got Literal[2] [incompatible_argument] +./callables_annotation.py:27:4: Takes 2 positional arguments but 3 were given [incompatible_call] +./callables_annotation.py:29:4: Missing required positional argument at position 0 [incompatible_call] +./callables_annotation.py:35:4: Takes 0 positional arguments but 1 were given [incompatible_call] +./callables_annotation.py:55:4: Callable[] requires exactly two arguments [invalid_annotation] +./callables_annotation.py:56:4: Invalid arguments to Callable: [invalid_annotation] +./callables_annotation.py:57:4: Invalid type annotation [] [invalid_annotation] +./callables_annotation.py:58:4: Callable[] requires exactly two arguments [invalid_annotation] +./callables_annotation.py:59:4: Ellipsis must be used directly in Callable[..., T], not in Callable[[...], T] [invalid_annotation] +./callables_annotation.py:91:0: Incompatible assignment: expected (int, /, **Any[explicit]) -> str, got () -> str [incompatible_assignment] +./callables_annotation.py:93:0: Incompatible assignment: expected (int, /, **Any[explicit]) -> str, got (*, a: int) -> str [incompatible_assignment] +./callables_annotation.py:159:4: Incompatible assignment: expected ./callables_annotation.py.Proto5[Any[explicit]], got ./callables_annotation.py.Proto8 [incompatible_assignment] +./callables_annotation.py:172:4: Incompatible assignment: expected .Callback2[Any[ellipsis_callable]] = (int, /, ****P) -> str, got () -> str [incompatible_assignment] +./callables_annotation.py:187:4: Incompatible assignment: expected (str, /, **Any[explicit]) -> str, got (int, str, /) -> str [incompatible_assignment] +./callables_annotation.py:189:4: Incompatible assignment: expected .CallbackWithStr[Any[ellipsis_callable]] = (str, /, ****P) -> str, got (int, str, /) -> str [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/callables_kwargs.toml b/conformance/results/pycroscope/callables_kwargs.toml new file mode 100644 index 000000000..492f041f5 --- /dev/null +++ b/conformance/results/pycroscope/callables_kwargs.toml @@ -0,0 +1,19 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./callables_kwargs.py:46:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] +./callables_kwargs.py:51:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] +./callables_kwargs.py:52:4: In call to callables_kwargs.func1: Missing required argument 'v1' [incompatible_call] +./callables_kwargs.py:58:4: Incompatible argument type for v1: expected int but got str [incompatible_argument] +./callables_kwargs.py:61:4: In call to callables_kwargs.func1: Got an unexpected keyword argument 'v4' [incompatible_call] +./callables_kwargs.py:63:4: Multiple values provided for argument 'v1' [incompatible_call] +./callables_kwargs.py:64:4: In call to callables_kwargs.func2: Parameter 'v3' provided as both a positional and a keyword argument [incompatible_call] +./callables_kwargs.py:65:4: Multiple values provided for argument 'v1' [incompatible_call] +./callables_kwargs.py:101:0: Incompatible assignment: expected callables_kwargs.TDProtocol3, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:102:0: Incompatible assignment: expected callables_kwargs.TDProtocol4, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:103:0: Incompatible assignment: expected callables_kwargs.TDProtocol5, got function 'callables_kwargs.func1' [incompatible_assignment] +./callables_kwargs.py:111:21: Parameter v1 overlaps with TypedDict key in **kwargs [invalid_annotation] +./callables_kwargs.py:122:12: Expected TypedDict type inside Unpack[] for **kwargs [invalid_annotation] +./callables_kwargs.py:134:0: Incompatible assignment: expected callables_kwargs.TDProtocol6, got function 'callables_kwargs.func7' [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/callables_protocol.toml b/conformance/results/pycroscope/callables_protocol.toml new file mode 100644 index 000000000..658e045ad --- /dev/null +++ b/conformance/results/pycroscope/callables_protocol.toml @@ -0,0 +1,22 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./callables_protocol.py:35:0: Incompatible assignment: expected ./callables_protocol.py.Proto1, got (*vals: tuple[bytes, ...], max_items: int | None) -> list[bytes] [incompatible_assignment] +./callables_protocol.py:36:0: Incompatible assignment: expected ./callables_protocol.py.Proto1, got (*vals: tuple[bytes, ...]) -> list[bytes] [incompatible_assignment] +./callables_protocol.py:37:0: Incompatible assignment: expected ./callables_protocol.py.Proto1, got (*vals: tuple[bytes, ...], max_len: str | None) -> list[bytes] [incompatible_assignment] +./callables_protocol.py:67:0: Incompatible assignment: expected ./callables_protocol.py.Proto2, got (*a: tuple[bytes, ...]) -> Any[unannotated] [incompatible_assignment] +./callables_protocol.py:68:0: Incompatible assignment: expected ./callables_protocol.py.Proto2, got (*a: tuple[str, ...], **b: dict[str, str]) -> Any[unannotated] [incompatible_assignment] +./callables_protocol.py:69:0: Incompatible assignment: expected ./callables_protocol.py.Proto2, got (*a: tuple[bytes, ...], **b: dict[str, bytes]) -> Any[unannotated] [incompatible_assignment] +./callables_protocol.py:70:0: Incompatible assignment: expected ./callables_protocol.py.Proto2, got (**b: dict[str, str]) -> Any[unannotated] [incompatible_assignment] +./callables_protocol.py:97:0: Incompatible assignment: expected ./callables_protocol.py.Proto4, got (x: int) -> None [incompatible_assignment] +./callables_protocol.py:121:0: Incompatible assignment: expected ./callables_protocol.py.NotProto6, got (*vals: tuple[bytes, ...], max_len: int | None = None) -> list[bytes] [incompatible_assignment] +./callables_protocol.py:169:0: Incompatible assignment: expected ./callables_protocol.py.Proto8, got (x: int) -> Any[explicit] [incompatible_assignment] +./callables_protocol.py:186:4: Incompatible types in assignment to attribute 'other_attribute': expected int, got Literal['str'] [incompatible_assignment] +./callables_protocol.py:187:4: ./callables_protocol.py.Proto9[**P@./callables_protocol.py.decorator1..decorator1, +R@./callables_protocol.py.decorator1..decorator1] has no attribute 'xxx' [undefined_attribute] +./callables_protocol.py:197:6: ./callables_protocol.py.Proto9[(x: int) -> Any[unannotated], str] has no attribute 'other_attribute2' [undefined_attribute] +./callables_protocol.py:238:0: Incompatible assignment: expected ./callables_protocol.py.Proto11, got (x: int, y: str, /) -> Any[explicit] [incompatible_assignment] +./callables_protocol.py:260:0: Incompatible assignment: expected ./callables_protocol.py.Proto12, got (*args: tuple[Any[explicit], ...], kwarg0: Any[explicit]) -> None [incompatible_assignment] +./callables_protocol.py:284:0: Incompatible assignment: expected ./callables_protocol.py.Proto13_Default, got (path: str) -> str [incompatible_assignment] +./callables_protocol.py:311:0: Incompatible assignment: expected ./callables_protocol.py.Proto14_Default, got (*, path: str) -> str [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/callables_subtyping.toml b/conformance/results/pycroscope/callables_subtyping.toml new file mode 100644 index 000000000..548cae50e --- /dev/null +++ b/conformance/results/pycroscope/callables_subtyping.toml @@ -0,0 +1,37 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./callables_subtyping.py:26:4: Incompatible assignment: expected (float | int, /) -> float | int, got (int, /) -> int [incompatible_assignment] +./callables_subtyping.py:29:4: Incompatible assignment: expected (int, /) -> int, got (float | int, /) -> float | int [incompatible_assignment] +./callables_subtyping.py:51:4: Incompatible assignment: expected callables_subtyping.Standard2, got callables_subtyping.PosOnly2 [incompatible_assignment] +./callables_subtyping.py:52:4: Incompatible assignment: expected callables_subtyping.Standard2, got callables_subtyping.KwOnly2 [incompatible_assignment] +./callables_subtyping.py:55:4: Incompatible assignment: expected callables_subtyping.PosOnly2, got callables_subtyping.KwOnly2 [incompatible_assignment] +./callables_subtyping.py:58:4: Incompatible assignment: expected callables_subtyping.KwOnly2, got callables_subtyping.PosOnly2 [incompatible_assignment] +./callables_subtyping.py:82:4: Incompatible assignment: expected callables_subtyping.IntArgs3, got callables_subtyping.NoArgs3 [incompatible_assignment] +./callables_subtyping.py:85:4: Incompatible assignment: expected callables_subtyping.FloatArgs3, got callables_subtyping.NoArgs3 [incompatible_assignment] +./callables_subtyping.py:86:4: Incompatible assignment: expected callables_subtyping.FloatArgs3, got callables_subtyping.IntArgs3 [incompatible_assignment] +./callables_subtyping.py:116:4: Incompatible assignment: expected callables_subtyping.PosOnly4, got callables_subtyping.IntArgs4 [incompatible_assignment] +./callables_subtyping.py:119:4: Incompatible assignment: expected callables_subtyping.IntStrArgs4, got callables_subtyping.StrArgs4 [incompatible_assignment] +./callables_subtyping.py:120:4: Incompatible assignment: expected callables_subtyping.IntStrArgs4, got callables_subtyping.IntArgs4 [incompatible_assignment] +./callables_subtyping.py:122:4: Incompatible assignment: expected callables_subtyping.StrArgs4, got callables_subtyping.IntArgs4 [incompatible_assignment] +./callables_subtyping.py:124:4: Incompatible assignment: expected callables_subtyping.IntArgs4, got callables_subtyping.StrArgs4 [incompatible_assignment] +./callables_subtyping.py:125:4: Incompatible assignment: expected callables_subtyping.Standard4, got callables_subtyping.IntStrArgs4 [incompatible_assignment] +./callables_subtyping.py:126:4: Incompatible assignment: expected callables_subtyping.Standard4, got callables_subtyping.StrArgs4 [incompatible_assignment] +./callables_subtyping.py:151:4: Incompatible assignment: expected callables_subtyping.IntKwargs5, got callables_subtyping.NoKwargs5 [incompatible_assignment] +./callables_subtyping.py:154:4: Incompatible assignment: expected callables_subtyping.FloatKwargs5, got callables_subtyping.NoKwargs5 [incompatible_assignment] +./callables_subtyping.py:155:4: Incompatible assignment: expected callables_subtyping.FloatKwargs5, got callables_subtyping.IntKwargs5 [incompatible_assignment] +./callables_subtyping.py:187:4: Incompatible assignment: expected callables_subtyping.KwOnly6, got callables_subtyping.IntKwargs6 [incompatible_assignment] +./callables_subtyping.py:190:4: Incompatible assignment: expected callables_subtyping.IntStrKwargs6, got callables_subtyping.StrKwargs6 [incompatible_assignment] +./callables_subtyping.py:191:4: Incompatible assignment: expected callables_subtyping.IntStrKwargs6, got callables_subtyping.IntKwargs6 [incompatible_assignment] +./callables_subtyping.py:193:4: Incompatible assignment: expected callables_subtyping.StrKwargs6, got callables_subtyping.IntKwargs6 [incompatible_assignment] +./callables_subtyping.py:195:4: Incompatible assignment: expected callables_subtyping.IntKwargs6, got callables_subtyping.StrKwargs6 [incompatible_assignment] +./callables_subtyping.py:196:4: Incompatible assignment: expected callables_subtyping.Standard6, got callables_subtyping.IntStrKwargs6 [incompatible_assignment] +./callables_subtyping.py:197:4: Incompatible assignment: expected callables_subtyping.Standard6, got callables_subtyping.StrKwargs6 [incompatible_assignment] +./callables_subtyping.py:236:4: Incompatible assignment: expected callables_subtyping.DefaultArg8, got callables_subtyping.NoDefaultArg8 [incompatible_assignment] +./callables_subtyping.py:237:4: Incompatible assignment: expected callables_subtyping.DefaultArg8, got callables_subtyping.NoX8 [incompatible_assignment] +./callables_subtyping.py:240:4: Incompatible assignment: expected callables_subtyping.NoDefaultArg8, got callables_subtyping.NoX8 [incompatible_assignment] +./callables_subtyping.py:243:4: Incompatible assignment: expected callables_subtyping.NoX8, got callables_subtyping.NoDefaultArg8 [incompatible_assignment] +./callables_subtyping.py:273:4: Incompatible assignment: expected callables_subtyping.FloatArg9, got callables_subtyping.Overloaded9 [incompatible_assignment] +./callables_subtyping.py:297:4: Incompatible assignment: expected callables_subtyping.Overloaded10, got callables_subtyping.StrArg10 [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/classes_classvar.toml b/conformance/results/pycroscope/classes_classvar.toml new file mode 100644 index 000000000..a377b105f --- /dev/null +++ b/conformance/results/pycroscope/classes_classvar.toml @@ -0,0 +1,22 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./classes_classvar.py:38:10: Invalid type annotation (, ) [invalid_annotation] +./classes_classvar.py:39:10: Invalid type annotation 3 [invalid_annotation] +./classes_classvar.py:40:13: Undefined name: var [undefined_name] +./classes_classvar.py:45:10: ClassVar type cannot include type parameters [classvar_type_parameters] +./classes_classvar.py:46:10: ClassVar type cannot include type parameters [classvar_type_parameters] +./classes_classvar.py:47:10: ClassVar type cannot include type parameters [classvar_type_parameters] +./classes_classvar.py:52:4: Incompatible assignment: expected list[str], got Literal[{}] [incompatible_assignment] +./classes_classvar.py:54:10: Final cannot be combined with ClassVar [invalid_qualifier] +./classes_classvar.py:55:11: Unrecognized annotation typing.ClassVar[] [invalid_annotation] +./classes_classvar.py:69:25: Unexpected ClassVar annotation [invalid_qualifier] +./classes_classvar.py:70:11: ClassVar can only be used for assignments in class body [invalid_qualifier] +./classes_classvar.py:71:17: ClassVar can only be used for assignments in class body [invalid_qualifier] +./classes_classvar.py:73:25: Unexpected ClassVar annotation [invalid_qualifier] +./classes_classvar.py:77:7: ClassVar can only be used for assignments in class body [invalid_qualifier] +./classes_classvar.py:78:19: ClassVar cannot be used in type aliases [invalid_qualifier] +./classes_classvar.py:111:0: Cannot assign to class variable 'stats' via instance [incompatible_assignment] +./classes_classvar.py:140:0: Incompatible assignment: expected ./classes_classvar.py.ProtoA, got ./classes_classvar.py.ProtoAImpl [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/classes_override.toml b/conformance/results/pycroscope/classes_override.toml new file mode 100644 index 000000000..b73910e7f --- /dev/null +++ b/conformance/results/pycroscope/classes_override.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./classes_override.py:53:4: Method does not override any base method [override_does_not_override] +./classes_override.py:65:4: Method does not override any base method [override_does_not_override] +./classes_override.py:79:4: Method does not override any base method [override_does_not_override] +./classes_override.py:84:4: Method does not override any base method [override_does_not_override] +./classes_override.py:89:4: Method does not override any base method [override_does_not_override] +""" diff --git a/conformance/results/pycroscope/constructors_call_init.toml b/conformance/results/pycroscope/constructors_call_init.toml new file mode 100644 index 000000000..da605980b --- /dev/null +++ b/conformance/results/pycroscope/constructors_call_init.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./constructors_call_init.py:21:12: Incompatible argument type for x: expected int but got Literal[1.0] [incompatible_argument] +./constructors_call_init.py:42:7: Incompatible argument type for x: expected ./constructors_call_init.py.Class3 | None but got ./constructors_call_init.py.Class2 [incompatible_argument] +./constructors_call_init.py:56:0: Missing required positional argument '%self' [incompatible_call] +./constructors_call_init.py:107:17: Class-scoped type variables are not allowed in __init__ self annotation [invalid_self_usage] +./constructors_call_init.py:130:0: Takes 0 positional arguments but 1 were given [incompatible_call] +""" diff --git a/conformance/results/pycroscope/constructors_call_metaclass.toml b/conformance/results/pycroscope/constructors_call_metaclass.toml new file mode 100644 index 000000000..2ec970e16 --- /dev/null +++ b/conformance/results/pycroscope/constructors_call_metaclass.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./constructors_call_metaclass.py:54:0: Missing required argument 'x' [incompatible_call] +./constructors_call_metaclass.py:68:0: Missing required argument 'x' [incompatible_call] +""" diff --git a/conformance/results/pycroscope/constructors_call_new.toml b/conformance/results/pycroscope/constructors_call_new.toml new file mode 100644 index 000000000..b747f222c --- /dev/null +++ b/conformance/results/pycroscope/constructors_call_new.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./constructors_call_new.py:21:12: Incompatible argument type for x: expected int but got Literal[1.0] [incompatible_argument] +./constructors_call_new.py:148:0: Missing required positional argument '%self' [incompatible_call] +""" diff --git a/conformance/results/pycroscope/constructors_call_type.toml b/conformance/results/pycroscope/constructors_call_type.toml new file mode 100644 index 000000000..1391f7471 --- /dev/null +++ b/conformance/results/pycroscope/constructors_call_type.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./constructors_call_type.py:30:4: In call to constructors_call_type.Meta1.__call__: Missing required argument 'x' [incompatible_call] +./constructors_call_type.py:40:4: In call to constructors_call_type.Class2: Missing required argument 'x' [incompatible_call] +./constructors_call_type.py:50:4: In call to constructors_call_type.Class3: Missing required argument 'x' [incompatible_call] +./constructors_call_type.py:59:4: In call to constructors_call_type.Class4: Takes 0 positional arguments but 1 were given [incompatible_call] +./constructors_call_type.py:64:4: Takes 0 positional arguments but 1 were given [incompatible_call] +./constructors_call_type.py:72:4: In call to constructors_call_type.Meta1.__call__: Missing required argument 'x' [incompatible_call] +./constructors_call_type.py:81:4: In call to constructors_call_type.Class2: Missing required argument 'y' [incompatible_call] +./constructors_call_type.py:82:11: Incompatible argument type for y: expected str but got Literal[2] [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/constructors_callable.toml b/conformance/results/pycroscope/constructors_callable.toml new file mode 100644 index 000000000..3c01c1f16 --- /dev/null +++ b/conformance/results/pycroscope/constructors_callable.toml @@ -0,0 +1,27 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./constructors_callable.py:36:12: Revealed type is '(x: int) -> ./constructors_callable.py.Class1' [reveal_type] +./constructors_callable.py:38:0: Missing required argument 'x' [incompatible_call] +./constructors_callable.py:39:0: Missing required argument 'x' [incompatible_call] +./constructors_callable.py:49:12: Revealed type is '() -> ./constructors_callable.py.Class2' [reveal_type] +./constructors_callable.py:51:0: Takes 0 positional arguments but 1 were given [incompatible_call] +./constructors_callable.py:64:12: Revealed type is '(x: int) -> ./constructors_callable.py.Class3' [reveal_type] +./constructors_callable.py:66:0: Missing required argument 'x' [incompatible_call] +./constructors_callable.py:67:0: Missing required argument 'x' [incompatible_call] +./constructors_callable.py:68:0: Takes 1 positional arguments but 2 were given [incompatible_call] +./constructors_callable.py:79:12: Revealed type is '(x: int) -> int' [reveal_type] +./constructors_callable.py:81:0: Missing required argument 'x' [incompatible_call] +./constructors_callable.py:82:0: Missing required argument 'x' [incompatible_call] +./constructors_callable.py:99:12: Revealed type is '(*args: tuple[Any[ellipsis_callable], ...], **kwargs: dict[str, Any[ellipsis_callable]]) -> Never' [reveal_type] +./constructors_callable.py:127:12: Revealed type is '() -> ./constructors_callable.py.Class6Proxy' [reveal_type] +./constructors_callable.py:129:0: Takes 0 positional arguments but 1 were given [incompatible_call] +./constructors_callable.py:144:12: Revealed type is '() -> Any[explicit]' [reveal_type] +./constructors_callable.py:146:0: Takes 0 positional arguments but 1 were given [incompatible_call] +./constructors_callable.py:164:4: Revealed type is '(x: ~_Ctor_Class7_T) -> ./constructors_callable.py.Class7[~_Ctor_Class7_T]' [reveal_type] +./constructors_callable.py:184:12: Revealed type is '(x: list[~T@./constructors_callable.py.Class8], y: list[~T@./constructors_callable.py.Class8]) -> ./constructors_callable.py.Class8[~T@./constructors_callable.py.Class8]' [reveal_type] +./constructors_callable.py:186:0: Cannot resolve type variables [incompatible_call] +./constructors_callable.py:195:12: Revealed type is '(x: list[~T@./constructors_callable.py.Class9.__init__..__init__], y: list[~T@./constructors_callable.py.Class9.__init__..__init__]) -> ./constructors_callable.py.Class9' [reveal_type] +./constructors_callable.py:197:0: Cannot resolve type variables [incompatible_call] +""" diff --git a/conformance/results/pycroscope/constructors_consistency.toml b/conformance/results/pycroscope/constructors_consistency.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/constructors_consistency.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/dataclasses_descriptors.toml b/conformance/results/pycroscope/dataclasses_descriptors.toml new file mode 100644 index 000000000..e5410991c --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_descriptors.toml @@ -0,0 +1,23 @@ +conformant = "Partial" +notes = """ +Conformance suite is questionable; see https://github.com/python/typing/issues/2259 +""" +conformance_automated = "Fail" +errors_diff = """ +Line 61: Unexpected errors ['./dataclasses_descriptors.py:61:12: Any[error] is not equivalent to list[int]', "./dataclasses_descriptors.py:61:12: has no attribute 'x' [undefined_attribute]"] +Line 62: Unexpected errors ['./dataclasses_descriptors.py:62:12: Any[error] is not equivalent to list[str]', "./dataclasses_descriptors.py:62:12: has no attribute 'y' [undefined_attribute]"] +Line 63: Unexpected errors ['./dataclasses_descriptors.py:63:12: list[Any[generic_argument]] is not equivalent to list[str]'] +Line 66: Unexpected errors ['./dataclasses_descriptors.py:66:12: ./dataclasses_descriptors.py.Desc2[int] is not equivalent to int'] +Line 67: Unexpected errors ['./dataclasses_descriptors.py:67:12: ./dataclasses_descriptors.py.Desc2[str] is not equivalent to str'] +Line 68: Unexpected errors ['./dataclasses_descriptors.py:68:12: Any[generic_argument] is not equivalent to str'] +""" +output = """ +./dataclasses_descriptors.py:61:12: Any[error] is not equivalent to list[int] +./dataclasses_descriptors.py:61:12: has no attribute 'x' [undefined_attribute] +./dataclasses_descriptors.py:62:12: Any[error] is not equivalent to list[str] +./dataclasses_descriptors.py:62:12: has no attribute 'y' [undefined_attribute] +./dataclasses_descriptors.py:63:12: list[Any[generic_argument]] is not equivalent to list[str] +./dataclasses_descriptors.py:66:12: ./dataclasses_descriptors.py.Desc2[int] is not equivalent to int +./dataclasses_descriptors.py:67:12: ./dataclasses_descriptors.py.Desc2[str] is not equivalent to str +./dataclasses_descriptors.py:68:12: Any[generic_argument] is not equivalent to str +""" diff --git a/conformance/results/pycroscope/dataclasses_final.toml b/conformance/results/pycroscope/dataclasses_final.toml new file mode 100644 index 000000000..07f248420 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_final.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_final.py:27:0: Cannot assign to final name final_classvar [incompatible_assignment] +./dataclasses_final.py:35:0: Cannot assign to final name final_no_default [incompatible_assignment] +./dataclasses_final.py:36:0: Cannot assign to final name final_with_default [incompatible_assignment] +./dataclasses_final.py:37:0: Cannot assign to instance attribute 'final_no_default' via class object [incompatible_assignment] +./dataclasses_final.py:38:0: Cannot assign to final name final_with_default [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/dataclasses_frozen.toml b/conformance/results/pycroscope/dataclasses_frozen.toml new file mode 100644 index 000000000..c64b3d316 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_frozen.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_frozen.py:16:0: Cannot assign to attribute a of frozen dataclass ./dataclasses_frozen.py.DC1 [incompatible_assignment] +./dataclasses_frozen.py:17:0: Cannot assign to attribute b of frozen dataclass ./dataclasses_frozen.py.DC1 [incompatible_assignment] +./dataclasses_frozen.py:23:0: Non-frozen dataclass cannot inherit from a frozen dataclass [invalid_base] +./dataclasses_frozen.py:33:0: Frozen dataclass cannot inherit from a non-frozen dataclass [invalid_base] +""" diff --git a/conformance/results/pycroscope/dataclasses_hash.toml b/conformance/results/pycroscope/dataclasses_hash.toml new file mode 100644 index 000000000..ebe2bbf79 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_hash.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_hash.py:17:0: None is not callable [not_callable] +./dataclasses_hash.py:18:0: Incompatible assignment: expected pycroscope.relations.HashableProto, got ./dataclasses_hash.py.DC1 [incompatible_assignment] +./dataclasses_hash.py:39:0: None is not callable [not_callable] +./dataclasses_hash.py:40:0: Incompatible assignment: expected pycroscope.relations.HashableProto, got ./dataclasses_hash.py.DC3 [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/dataclasses_inheritance.toml b/conformance/results/pycroscope/dataclasses_inheritance.toml new file mode 100644 index 000000000..5c74e15a1 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_inheritance.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_inheritance.py:62:4: Value of x incompatible with base class ./dataclasses_inheritance.py.DC6 [incompatible_override] +./dataclasses_inheritance.py:66:4: Value of y incompatible with base class ./dataclasses_inheritance.py.DC6 [incompatible_override] +""" diff --git a/conformance/results/pycroscope/dataclasses_kwonly.toml b/conformance/results/pycroscope/dataclasses_kwonly.toml new file mode 100644 index 000000000..55ead395f --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_kwonly.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_kwonly.py:23:0: Takes 1 positional arguments but 2 were given [incompatible_call] +./dataclasses_kwonly.py:38:0: Takes 1 positional arguments but 2 were given [incompatible_call] +./dataclasses_kwonly.py:53:0: Takes 1 positional arguments but 2 were given [incompatible_call] +""" diff --git a/conformance/results/pycroscope/dataclasses_match_args.toml b/conformance/results/pycroscope/dataclasses_match_args.toml new file mode 100644 index 000000000..6b3009b7e --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_match_args.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_match_args.py:42:0: has no attribute '__match_args__' [undefined_attribute] +""" diff --git a/conformance/results/pycroscope/dataclasses_order.toml b/conformance/results/pycroscope/dataclasses_order.toml new file mode 100644 index 000000000..69aaa4a36 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_order.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_order.py:50:3: Unsupported operands for less than: ./dataclasses_order.py.DC1 and ./dataclasses_order.py.DC2 [unsupported_operation] +""" diff --git a/conformance/results/pycroscope/dataclasses_postinit.toml b/conformance/results/pycroscope/dataclasses_postinit.toml new file mode 100644 index 000000000..225afffc6 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_postinit.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_postinit.py:19:4: Dataclass __post_init__ is incompatible with InitVar fields [incompatible_override] +./dataclasses_postinit.py:28:6: ./dataclasses_postinit.py.DC1 has no attribute 'x' [undefined_attribute] +./dataclasses_postinit.py:29:6: ./dataclasses_postinit.py.DC1 has no attribute 'y' [undefined_attribute] +./dataclasses_postinit.py:36:4: Dataclass __post_init__ is incompatible with InitVar fields [incompatible_override] +""" diff --git a/conformance/results/pycroscope/dataclasses_slots.toml b/conformance/results/pycroscope/dataclasses_slots.toml new file mode 100644 index 000000000..d89f94045 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_slots.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_slots.py:11:0: Class cannot define __slots__ when dataclass slots=True [invalid_dataclass] +./dataclasses_slots.py:25:8: Cannot assign to attribute 'y'; it is not in __slots__ for ./dataclasses_slots.py.DC2 [incompatible_assignment] +./dataclasses_slots.py:38:8: Cannot assign to attribute 'y'; it is not in __slots__ for ./dataclasses_slots.py.DC3 [incompatible_assignment] +./dataclasses_slots.py:66:0: has no attribute '__slots__' [undefined_attribute] +./dataclasses_slots.py:69:0: ./dataclasses_slots.py.DC6 has no attribute '__slots__' [undefined_attribute] +""" diff --git a/conformance/results/pycroscope/dataclasses_transform_class.toml b/conformance/results/pycroscope/dataclasses_transform_class.toml new file mode 100644 index 000000000..1eab06434 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_transform_class.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_transform_class.py:51:0: Non-frozen dataclass cannot inherit from a frozen dataclass [invalid_base] +./dataclasses_transform_class.py:63:0: Cannot assign to attribute id of frozen dataclass ./dataclasses_transform_class.py.Customer1 [incompatible_assignment] +./dataclasses_transform_class.py:66:7: Missing required argument 'id' [incompatible_call] +./dataclasses_transform_class.py:72:5: Unsupported operands for less than: ./dataclasses_transform_class.py.Customer1 and ./dataclasses_transform_class.py.Customer1 [unsupported_operation] +./dataclasses_transform_class.py:82:7: Missing required argument 'id' [incompatible_call] +./dataclasses_transform_class.py:122:0: Cannot assign to attribute id of frozen dataclass ./dataclasses_transform_class.py.Customer3 [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/dataclasses_transform_converter.toml b/conformance/results/pycroscope/dataclasses_transform_converter.toml new file mode 100644 index 000000000..7db3d21eb --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_transform_converter.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_transform_converter.py:48:40: Incompatible argument type for converter: expected (~S, /) -> ~T but got () -> int [incompatible_argument] +./dataclasses_transform_converter.py:49:40: Incompatible argument type for converter: expected (~S, /) -> ~T but got (*, x: int) -> int [incompatible_argument] +./dataclasses_transform_converter.py:107:4: Incompatible argument type for field0: expected str but got Literal[1] [incompatible_argument] +./dataclasses_transform_converter.py:108:22: Incompatible argument type for field3: expected str | bytes but got Literal[1] [incompatible_argument] +./dataclasses_transform_converter.py:109:28: Incompatible argument type for field4: expected str | list[str] but got Literal[3j] [incompatible_argument] +./dataclasses_transform_converter.py:118:0: Incompatible types in assignment to attribute 'field0': expected str, got Literal[1] [incompatible_assignment] +./dataclasses_transform_converter.py:119:0: Incompatible types in assignment to attribute 'field3': expected str | bytes, got Literal[1] [incompatible_assignment] +./dataclasses_transform_converter.py:130:18: Cannot resolve type variables [incompatible_call] +./dataclasses_transform_converter.py:133:18: Cannot resolve type variables [incompatible_call] +""" diff --git a/conformance/results/pycroscope/dataclasses_transform_field.toml b/conformance/results/pycroscope/dataclasses_transform_field.toml new file mode 100644 index 000000000..527ade972 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_transform_field.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_transform_field.py:64:0: Got an unexpected keyword argument 'id' [incompatible_call] +./dataclasses_transform_field.py:75:0: Missing required argument 'name' [incompatible_call] +""" diff --git a/conformance/results/pycroscope/dataclasses_transform_func.toml b/conformance/results/pycroscope/dataclasses_transform_func.toml new file mode 100644 index 000000000..42b222af7 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_transform_func.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_transform_func.py:56:0: Incompatible types in assignment to attribute 'name': expected str, got Literal[3] [incompatible_assignment] +./dataclasses_transform_func.py:60:5: Unsupported operands for less than: ./dataclasses_transform_func.py.Customer1 and ./dataclasses_transform_func.py.Customer1 [unsupported_operation] +./dataclasses_transform_func.py:64:7: Got an unexpected keyword argument 'salary' [incompatible_call] +./dataclasses_transform_func.py:70:7: Missing required argument 'id' [incompatible_call] +./dataclasses_transform_func.py:89:0: Non-frozen dataclass cannot inherit from a frozen dataclass [invalid_base] +./dataclasses_transform_func.py:96:0: Cannot assign to attribute id of frozen dataclass ./dataclasses_transform_func.py.Customer3 [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/dataclasses_transform_meta.toml b/conformance/results/pycroscope/dataclasses_transform_meta.toml new file mode 100644 index 000000000..4aff98937 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_transform_meta.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_transform_meta.py:51:0: Non-frozen dataclass cannot inherit from a frozen dataclass [invalid_base] +./dataclasses_transform_meta.py:63:0: Cannot assign to attribute id of frozen dataclass ./dataclasses_transform_meta.py.Customer1 [incompatible_assignment] +./dataclasses_transform_meta.py:66:7: Missing required argument 'id' [incompatible_call] +./dataclasses_transform_meta.py:73:5: Unsupported operands for less than: ./dataclasses_transform_meta.py.Customer1 and ./dataclasses_transform_meta.py.Customer1 [unsupported_operation] +./dataclasses_transform_meta.py:83:7: Missing required argument 'id' [incompatible_call] +./dataclasses_transform_meta.py:103:0: Cannot assign to attribute id of frozen dataclass ./dataclasses_transform_meta.py.Customer3 [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/dataclasses_usage.toml b/conformance/results/pycroscope/dataclasses_usage.toml new file mode 100644 index 000000000..1bd968622 --- /dev/null +++ b/conformance/results/pycroscope/dataclasses_usage.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./dataclasses_usage.py:51:5: Missing required argument 'unit_price' [incompatible_call] +./dataclasses_usage.py:52:27: Incompatible argument type for unit_price: expected float | int but got Literal['price'] [incompatible_argument] +./dataclasses_usage.py:53:5: Takes 3 positional arguments but 4 were given [incompatible_call] +./dataclasses_usage.py:59:1: Dataclass fields without defaults cannot follow fields with defaults [invalid_dataclass] +./dataclasses_usage.py:65:1: Dataclass fields without defaults cannot follow fields with defaults [invalid_dataclass] +./dataclasses_usage.py:71:1: Dataclass fields without defaults cannot follow fields with defaults [invalid_dataclass] +./dataclasses_usage.py:84:5: Takes 1 positional arguments but 2 were given [incompatible_call] +./dataclasses_usage.py:89:4: Dataclass default_factory return type is incompatible with field type int [incompatible_assignment] +./dataclasses_usage.py:128:0: Takes 1 positional arguments but 2 were given [incompatible_call] +./dataclasses_usage.py:131:0: Missing required argument 'y' [incompatible_call] +./dataclasses_usage.py:180:0: Takes 0 positional arguments but 1 were given [incompatible_call] +./dataclasses_usage.py:246:0: Takes 2 positional arguments but 3 were given [incompatible_call] +""" diff --git a/conformance/results/pycroscope/directives_assert_type.toml b/conformance/results/pycroscope/directives_assert_type.toml new file mode 100644 index 000000000..a1506a6ff --- /dev/null +++ b/conformance/results/pycroscope/directives_assert_type.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./directives_assert_type.py:27:16: int | str is not equivalent to int +./directives_assert_type.py:28:16: int | str is not equivalent to Any[explicit] +./directives_assert_type.py:29:16: Any[explicit] is not equivalent to int +./directives_assert_type.py:30:16: Literal[4] is not equivalent to int +./directives_assert_type.py:32:4: In call to typing.assert_type: Missing required positional argument 'val' [incompatible_call] +./directives_assert_type.py:33:16: Literal[''] is not equivalent to int +./directives_assert_type.py:34:4: In call to typing.assert_type: Takes 2 positional arguments but 3 were given [incompatible_call] +""" diff --git a/conformance/results/pycroscope/directives_cast.toml b/conformance/results/pycroscope/directives_cast.toml new file mode 100644 index 000000000..9429a6b9c --- /dev/null +++ b/conformance/results/pycroscope/directives_cast.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./directives_cast.py:15:7: In call to typing.cast: Missing required positional argument 'typ' [incompatible_call] +./directives_cast.py:16:7: Invalid type annotation 1 [invalid_annotation] +./directives_cast.py:17:7: In call to typing.cast: Takes 2 positional arguments but 3 were given [incompatible_call] +""" diff --git a/conformance/results/pycroscope/directives_deprecated.toml b/conformance/results/pycroscope/directives_deprecated.toml new file mode 100644 index 000000000..0aa05c021 --- /dev/null +++ b/conformance/results/pycroscope/directives_deprecated.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./directives_deprecated.py:18:43: type '_directives_deprecated_library.Ham' is deprecated: Use Spam instead [deprecated] +./directives_deprecated.py:24:0: function '_directives_deprecated_library.norwegian_blue' is deprecated: It is pining for the fjords [deprecated] +./directives_deprecated.py:25:4: function '_directives_deprecated_library.norwegian_blue' is deprecated: It is pining for the fjords [deprecated] +./directives_deprecated.py:30:0: Use of deprecated overload (x: int) -> str: Only str will be allowed [deprecated] +./directives_deprecated.py:41:4: function '_directives_deprecated_library.Spam.__add__' with typevars TypeVarMap(typevars={~SelfT@_directives_deprecated_library.Spam=_directives_deprecated_library.Spam}) is deprecated: There is enough spam in the world [deprecated] +./directives_deprecated.py:42:0: function '_directives_deprecated_library.Spam.__add__' with typevars TypeVarMap(typevars={~SelfT@_directives_deprecated_library.Spam=_directives_deprecated_library.Spam}) is deprecated: There is enough spam in the world [deprecated] +./directives_deprecated.py:44:0: Property getter 'greasy' is deprecated: All spam will be equally greasy [deprecated] +./directives_deprecated.py:47:0: Property setter 'shape' is deprecated: Shapes are becoming immutable [deprecated] +./directives_deprecated.py:48:0: Property setter 'shape' is deprecated: Shapes are becoming immutable [deprecated] +./directives_deprecated.py:58:0: Method __call__ is deprecated: Deprecated [deprecated] +./directives_deprecated.py:69:0: () -> None is deprecated: Deprecated [deprecated] +./directives_deprecated.py:98:4: () -> None is deprecated: Deprecated [deprecated] +""" diff --git a/conformance/results/pycroscope/directives_disjoint_base.toml b/conformance/results/pycroscope/directives_disjoint_base.toml new file mode 100644 index 000000000..96a30b849 --- /dev/null +++ b/conformance/results/pycroscope/directives_disjoint_base.toml @@ -0,0 +1,17 @@ +conformant = "Partial" +notes = """ +Does not reject invalid class definitions due to disjoint bases, but uses disjoint base information in type narrowing. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 69: Expected 1 errors +Line 73: Expected 1 errors +Line 77: Expected 1 errors +Line 81: Expected 1 errors +Line 105: Expected 1 errors +Line 118: Expected 1 errors +Line 123: Expected 1 errors +""" +output = """ +./directives_disjoint_base.py:113:1: Incompatible argument type for cls: expected ~_TC but got () -> None [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/directives_no_type_check.toml b/conformance/results/pycroscope/directives_no_type_check.toml new file mode 100644 index 000000000..869c59892 --- /dev/null +++ b/conformance/results/pycroscope/directives_no_type_check.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./directives_no_type_check.py:15:4: Incompatible assignment: expected int, got Literal[''] [incompatible_assignment] +./directives_no_type_check.py:25:8: Unsupported operands for addition: int and str [unsupported_operation] +./directives_no_type_check.py:26:4: Incompatible return type: expected None, got Literal[1] [incompatible_return_value] +./directives_no_type_check.py:29:6: Incompatible argument type for a: expected int but got Literal[b'invalid'] [incompatible_argument] +./directives_no_type_check.py:29:18: Incompatible argument type for b: expected str but got Literal[b'arguments'] [incompatible_argument] +./directives_no_type_check.py:32:0: Missing required argument 'a' [incompatible_call] +""" diff --git a/conformance/results/pycroscope/directives_reveal_type.toml b/conformance/results/pycroscope/directives_reveal_type.toml new file mode 100644 index 000000000..4af147aab --- /dev/null +++ b/conformance/results/pycroscope/directives_reveal_type.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./directives_reveal_type.py:14:16: Revealed type is 'int | str' [reveal_type] +./directives_reveal_type.py:15:16: Revealed type is 'list[int]' [reveal_type] +./directives_reveal_type.py:16:16: Revealed type is 'Any[explicit]' [reveal_type] +./directives_reveal_type.py:17:16: Revealed type is 'directives_reveal_type.ForwardReference' [reveal_type] +./directives_reveal_type.py:19:4: In call to typing.reveal_type: Missing required positional argument 'value' [incompatible_call] +./directives_reveal_type.py:20:4: In call to typing.reveal_type: Takes 1 positional arguments but 2 were given [incompatible_call] +""" diff --git a/conformance/results/pycroscope/directives_type_checking.toml b/conformance/results/pycroscope/directives_type_checking.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/directives_type_checking.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/directives_type_ignore.toml b/conformance/results/pycroscope/directives_type_ignore.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/directives_type_ignore.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/directives_type_ignore_file1.toml b/conformance/results/pycroscope/directives_type_ignore_file1.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/directives_type_ignore_file1.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/directives_type_ignore_file2.toml b/conformance/results/pycroscope/directives_type_ignore_file2.toml new file mode 100644 index 000000000..b1f55aca0 --- /dev/null +++ b/conformance/results/pycroscope/directives_type_ignore_file2.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./directives_type_ignore_file2.py:14:0: Incompatible assignment: expected int, got Literal[''] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/directives_version_platform.toml b/conformance/results/pycroscope/directives_version_platform.toml new file mode 100644 index 000000000..a6ce7215d --- /dev/null +++ b/conformance/results/pycroscope/directives_version_platform.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./directives_version_platform.py:33:18: Undefined name: val3 [undefined_name] +./directives_version_platform.py:50:18: Undefined name: val6 [undefined_name] +./directives_version_platform.py:59:18: Undefined name: val9 [undefined_name] +./directives_version_platform.py:66:18: val10 may be used uninitialized [possibly_undefined_name] +./directives_version_platform.py:67:21: val11 may be used uninitialized [possibly_undefined_name] +./directives_version_platform.py:74:21: val12 may be used uninitialized [possibly_undefined_name] +./directives_version_platform.py:75:18: val13 may be used uninitialized [possibly_undefined_name] +""" diff --git a/conformance/results/pycroscope/enums_behaviors.toml b/conformance/results/pycroscope/enums_behaviors.toml new file mode 100644 index 000000000..abe030442 --- /dev/null +++ b/conformance/results/pycroscope/enums_behaviors.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./enums_behaviors.py:28:12: ./enums_behaviors.py.Color is not equivalent to Literal[] +./enums_behaviors.py:32:12: ./enums_behaviors.py.Color is not equivalent to Literal[] +./enums_behaviors.py:44:20: Cannot inherit from final class [invalid_base] +""" diff --git a/conformance/results/pycroscope/enums_definition.toml b/conformance/results/pycroscope/enums_definition.toml new file mode 100644 index 000000000..896d77db6 --- /dev/null +++ b/conformance/results/pycroscope/enums_definition.toml @@ -0,0 +1,28 @@ +conformant = "Partial" +notes = """ +Does not allow enum members to be conditional on version/platform checks. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 91: Unexpected errors ['./enums_definition.py:91:12: ./enums_definition.py.Color12 is not equivalent to Any[unannotated]'] +""" +output = """ +./enums_definition.py:34:12: enum.Enum has no attribute 'RED' [undefined_attribute] +./enums_definition.py:34:32: enum.Enum has no attribute 'RED' [undefined_attribute] +./enums_definition.py:35:12: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:35:32: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:36:12: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:36:32: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:37:12: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:37:32: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:38:12: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:38:32: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:39:12: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:39:32: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:40:12: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:40:32: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:41:12: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:41:32: type[enum.Enum] has no attribute 'RED' [undefined_attribute] +./enums_definition.py:91:12: ./enums_definition.py.Color12 is not equivalent to Any[unannotated] +./enums_definition.py:92:0: has no attribute 'BLUE' [undefined_attribute] +""" diff --git a/conformance/results/pycroscope/enums_expansion.toml b/conformance/results/pycroscope/enums_expansion.toml new file mode 100644 index 000000000..e12a9c12f --- /dev/null +++ b/conformance/results/pycroscope/enums_expansion.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./enums_expansion.py:53:20: enums_expansion.CustomFlags is not equivalent to Literal[] +""" diff --git a/conformance/results/pycroscope/enums_member_names.toml b/conformance/results/pycroscope/enums_member_names.toml new file mode 100644 index 000000000..3a96dcafc --- /dev/null +++ b/conformance/results/pycroscope/enums_member_names.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./enums_member_names.py:21:12: str is not equivalent to Literal['RED'] +./enums_member_names.py:30:16: Annotated[str, EnumName(enum_cls=)] is not equivalent to Literal['RED', 'BLUE', 'GREEN'] +""" diff --git a/conformance/results/pycroscope/enums_member_values.toml b/conformance/results/pycroscope/enums_member_values.toml new file mode 100644 index 000000000..ca9b47ee4 --- /dev/null +++ b/conformance/results/pycroscope/enums_member_values.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./enums_member_values.py:21:12: Any[explicit] is not equivalent to Literal[1] +./enums_member_values.py:54:12: Literal[(1, 3.303e+23, 2439700.0)] is not equivalent to Literal[1] +./enums_member_values.py:68:12: Literal[] is not equivalent to Literal[1] +./enums_member_values.py:78:4: Enum member value must be assignable to int [invalid_annotation] +./enums_member_values.py:85:8: Enum member value must be assignable to str [invalid_annotation] +./enums_member_values.py:96:12: Literal[Ellipsis] is not equivalent to int +""" diff --git a/conformance/results/pycroscope/enums_members.toml b/conformance/results/pycroscope/enums_members.toml new file mode 100644 index 000000000..a7edb501a --- /dev/null +++ b/conformance/results/pycroscope/enums_members.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./enums_members.py:54:4: Enum members should not be explicitly annotated [invalid_annotation] +./enums_members.py:86:11: Arguments to Literal[] must be literals, not (AnyValue(source=),) [invalid_literal] +./enums_members.py:87:11: Arguments to Literal[] must be literals, not (CallableValue(typ=, literal_only=False, signature=Signature(parameters={'x': SigParameter(name='x', kind=, default=None, annotation=TypedValue(typ=, literal_only=False))}, return_value=TypedValue(typ=, literal_only=False), impl=None, callable=None, is_asynq=False, has_return_annotation=True, allow_call=False, allow_partial_call=False, evaluator=None, deprecated=None)),) [invalid_literal] +./enums_members.py:88:9: Arguments to Literal[] must be literals, not (TypedValue(typ=, literal_only=False),) [invalid_literal] +./enums_members.py:89:7: Arguments to Literal[] must be literals, not (CallableValue(typ=, literal_only=False, signature=Signature(parameters={'self': SigParameter(name='self', kind=, default=None, annotation=TypedValue(typ=ClassOwner(module='./enums_members.py', qualname='Pet4', identity='./enums_members.py.Pet4'), literal_only=False))}, return_value=KnownValue(val=None), impl=None, callable=None, is_asynq=False, has_return_annotation=True, allow_call=False, allow_partial_call=False, evaluator=None, deprecated=None)),) [invalid_literal] +./enums_members.py:120:12: int is not equivalent to Any[unannotated] +./enums_members.py:132:20: Revealed type is 'int' [reveal_type] +./enums_members.py:133:20: int is not equivalent to Any[unannotated] +./enums_members.py:150:12: ./enums_members.py.Pet5 is not equivalent to int +./enums_members.py:151:12: ./enums_members.py.Pet5 is not equivalent to int +""" diff --git a/conformance/results/pycroscope/exceptions_context_managers.toml b/conformance/results/pycroscope/exceptions_context_managers.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/exceptions_context_managers.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/generics_base_class.toml b/conformance/results/pycroscope/generics_base_class.toml new file mode 100644 index 000000000..4f03dbec0 --- /dev/null +++ b/conformance/results/pycroscope/generics_base_class.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_base_class.py:26:25: Incompatible argument type for x: expected dict[str, list[object]] but got ./generics_base_class.py.SymbolTable [incompatible_argument] +./generics_base_class.py:29:10: Generic[...] is valid only as a base class [invalid_annotation] +./generics_base_class.py:30:7: Generic[...] is valid only as a base class [invalid_annotation] +./generics_base_class.py:49:21: Expected 1 type arguments for ./generics_base_class.py.LinkedList [invalid_specialization] +./generics_base_class.py:61:17: Expected 1 type arguments for ./generics_base_class.py.MyDict [invalid_specialization] +./generics_base_class.py:68:24: Type parameter list cannot contain duplicate type variables [invalid_base] +./generics_base_class.py:98:31: Inconsistent type variable order in base classes [invalid_base] +""" diff --git a/conformance/results/pycroscope/generics_basic.toml b/conformance/results/pycroscope/generics_basic.toml new file mode 100644 index 000000000..1876b1010 --- /dev/null +++ b/conformance/results/pycroscope/generics_basic.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_basic.py:40:4: Cannot resolve type variables [incompatible_call] +./generics_basic.py:41:4: Cannot resolve type variables [incompatible_call] +./generics_basic.py:49:17: TypeVar must have at least two constraints [incompatible_call] +./generics_basic.py:55:52: TypeVar bounds and constraints cannot contain type parameters [invalid_annotation] +./generics_basic.py:69:4: Cannot resolve type variables [incompatible_call] +./generics_basic.py:121:20: Type parameter list cannot contain duplicate type variables [invalid_base] +./generics_basic.py:157:7: Incompatible argument type for key: expected str but got Literal[0] [incompatible_argument] +./generics_basic.py:158:7: Incompatible argument type for key: expected str but got Literal[0] [incompatible_argument] +./generics_basic.py:162:19: All arguments to Generic or Protocol must be type variables [invalid_base] +./generics_basic.py:163:20: All arguments to Generic or Protocol must be type variables [invalid_base] +./generics_basic.py:171:27: All type parameters for the class must appear within Generic or Protocol [invalid_base] +./generics_basic.py:172:27: All type parameters for the class must appear within Generic or Protocol [invalid_base] +./generics_basic.py:208:36: Cannot determine metaclass [invalid_metaclass] +""" diff --git a/conformance/results/pycroscope/generics_defaults.toml b/conformance/results/pycroscope/generics_defaults.toml new file mode 100644 index 000000000..93d9e5fb4 --- /dev/null +++ b/conformance/results/pycroscope/generics_defaults.toml @@ -0,0 +1,44 @@ +conformant = "Partial" +notes = """ +Numerous issues; does not support TypeVarTuple and ParamSpec defaults. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 33: Unexpected errors ['./generics_defaults.py:33:16: ~DefaultStrT@./generics_defaults.py.NoNonDefaults is not equivalent to str'] +Line 34: Unexpected errors ['./generics_defaults.py:34:16: ~DefaultIntT@./generics_defaults.py.NoNonDefaults is not equivalent to int'] +Line 73: Unexpected errors ['./generics_defaults.py:73:16: ~T1@./generics_defaults.py.AllTheDefaults is not equivalent to Any[explicit]'] +Line 74: Unexpected errors ['./generics_defaults.py:74:16: ~T2@./generics_defaults.py.AllTheDefaults is not equivalent to Any[explicit]'] +Line 75: Unexpected errors ['./generics_defaults.py:75:16: ~DefaultStrT@./generics_defaults.py.NoNonDefaults is not equivalent to str'] +Line 76: Unexpected errors ['./generics_defaults.py:76:16: ~DefaultIntT@./generics_defaults.py.NoNonDefaults is not equivalent to int'] +Line 77: Unexpected errors ['./generics_defaults.py:77:16: ~DefaultBoolT@./generics_defaults.py.AllTheDefaults is not equivalent to bool'] +Line 122: Unexpected errors ['./generics_defaults.py:122:16: (**__P: **DefaultP@./generics_defaults.py.Class_ParamSpec) -> None is not equivalent to (str, int, /) -> None'] +Line 124: Unexpected errors ['./generics_defaults.py:124:16: ./generics_defaults.py.Class_ParamSpec[AnySig()] is not equivalent to ./generics_defaults.py.Class_ParamSpec[tuple[bool, bool]]'] +Line 144: Unexpected errors ['./generics_defaults.py:144:60: Incompatible argument type for default: expected TypeForm[object] but got Literal[typing.Unpack[DefaultTs]] [incompatible_argument]'] +Line 203: Unexpected errors ["./generics_defaults.py:203:36: Invalid type annotation [] [invalid_annotation]", './generics_defaults.py:203:17: ParamSpec specialization must use list form, Concatenate[..., P], P, or ... [invalid_annotation]'] +Line 204: Unexpected errors ['./generics_defaults.py:204:16: tuple[int] is not equivalent to tuple[int, str]'] +Line 205: Unexpected errors ['./generics_defaults.py:205:16: (...) -> None is not equivalent to (float | int, bool, /) -> None'] +Line 208: Unexpected errors ['./generics_defaults.py:208:16: (...) -> None is not equivalent to (bytes, /) -> None'] +""" +output = """ +./generics_defaults.py:24:0: non-default TypeVars cannot follow ones with defaults [invalid_type_parameter] +./generics_defaults.py:33:16: ~DefaultStrT@./generics_defaults.py.NoNonDefaults is not equivalent to str +./generics_defaults.py:34:16: ~DefaultIntT@./generics_defaults.py.NoNonDefaults is not equivalent to int +./generics_defaults.py:66:7: Expected at least 2 type arguments for ./generics_defaults.py.AllTheDefaults [invalid_specialization] +./generics_defaults.py:73:16: ~T1@./generics_defaults.py.AllTheDefaults is not equivalent to Any[explicit] +./generics_defaults.py:74:16: ~T2@./generics_defaults.py.AllTheDefaults is not equivalent to Any[explicit] +./generics_defaults.py:75:16: ~DefaultStrT@./generics_defaults.py.NoNonDefaults is not equivalent to str +./generics_defaults.py:76:16: ~DefaultIntT@./generics_defaults.py.NoNonDefaults is not equivalent to int +./generics_defaults.py:77:16: ~DefaultBoolT@./generics_defaults.py.AllTheDefaults is not equivalent to bool +./generics_defaults.py:122:16: (**__P: **DefaultP@./generics_defaults.py.Class_ParamSpec) -> None is not equivalent to (str, int, /) -> None +./generics_defaults.py:124:16: ./generics_defaults.py.Class_ParamSpec[AnySig()] is not equivalent to ./generics_defaults.py.Class_ParamSpec[tuple[bool, bool]] +./generics_defaults.py:144:60: Incompatible argument type for default: expected TypeForm[object] but got Literal[typing.Unpack[DefaultTs]] [incompatible_argument] +./generics_defaults.py:152:50: TypeVar default must be assignable to its bound [incompatible_call] +./generics_defaults.py:159:51: TypeVar default must be one of its constraints [incompatible_call] +./generics_defaults.py:176:12: Any[generic_argument] is not equivalent to int +./generics_defaults.py:188:0: TypeVars with defaults cannot follow TypeVarTuples [invalid_type_parameter] +./generics_defaults.py:203:36: Invalid type annotation [] [invalid_annotation] +./generics_defaults.py:203:17: ParamSpec specialization must use list form, Concatenate[..., P], P, or ... [invalid_annotation] +./generics_defaults.py:204:16: tuple[int] is not equivalent to tuple[int, str] +./generics_defaults.py:205:16: (...) -> None is not equivalent to (float | int, bool, /) -> None +./generics_defaults.py:208:16: (...) -> None is not equivalent to (bytes, /) -> None +""" diff --git a/conformance/results/pycroscope/generics_defaults_referential.toml b/conformance/results/pycroscope/generics_defaults_referential.toml new file mode 100644 index 000000000..bd7f61cec --- /dev/null +++ b/conformance/results/pycroscope/generics_defaults_referential.toml @@ -0,0 +1,22 @@ +conformant = "Partial" +notes = """ +Fails to apply default specializations in some cases. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 23: Unexpected errors ["./generics_defaults_referential.py:23:12: type 'generics_defaults_referential.slice' is not equivalent to type[generics_defaults_referential.slice[int, int, int | None]]"] +Line 98: Unexpected errors ['./generics_defaults_referential.py:98:16: ~Z1@generics_defaults_referential.Bar is not equivalent to Any[explicit]'] +Line 99: Unexpected errors ['./generics_defaults_referential.py:99:16: ~ListDefaultT@generics_defaults_referential.Bar is not equivalent to list[Any[explicit]]'] +""" +output = """ +./generics_defaults_referential.py:23:12: type 'generics_defaults_referential.slice' is not equivalent to type[generics_defaults_referential.slice[int, int, int | None]] +./generics_defaults_referential.py:37:16: Incompatible argument type for b: expected int but got str [incompatible_argument] +./generics_defaults_referential.py:38:13: Incompatible argument type for a: expected int but got str [incompatible_argument] +./generics_defaults_referential.py:54:0: Type parameter defaults can reference only earlier type parameters from the same class [invalid_type_parameter] +./generics_defaults_referential.py:61:4: Type parameter defaults can reference only earlier type parameters from the same class [invalid_type_parameter] +./generics_defaults_referential.py:69:39: TypeVar default must be assignable to its bound [incompatible_call] +./generics_defaults_referential.py:75:51: TypeVar default must be one of its constraints [incompatible_call] +./generics_defaults_referential.py:79:62: TypeVar default must be one of its constraints [incompatible_call] +./generics_defaults_referential.py:98:16: ~Z1@generics_defaults_referential.Bar is not equivalent to Any[explicit] +./generics_defaults_referential.py:99:16: ~ListDefaultT@generics_defaults_referential.Bar is not equivalent to list[Any[explicit]] +""" diff --git a/conformance/results/pycroscope/generics_defaults_specialization.toml b/conformance/results/pycroscope/generics_defaults_specialization.toml new file mode 100644 index 000000000..aab3a627d --- /dev/null +++ b/conformance/results/pycroscope/generics_defaults_specialization.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_defaults_specialization.py:30:0: Expected 1 type arguments for type alias, got 2 [invalid_specialization] +./generics_defaults_specialization.py:46:0: Incompatible assignment: expected type[./generics_defaults_specialization.py.Bar[int]], got [incompatible_assignment] +./generics_defaults_specialization.py:56:0: ./generics_defaults_specialization.py.Foo cannot be further subscripted [invalid_specialization] +""" diff --git a/conformance/results/pycroscope/generics_paramspec_basic.toml b/conformance/results/pycroscope/generics_paramspec_basic.toml new file mode 100644 index 000000000..5a5c534f6 --- /dev/null +++ b/conformance/results/pycroscope/generics_paramspec_basic.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_paramspec_basic.py:10:22: ParamSpec named 'NotIt' must be assigned to a variable with the same name [must_have_same_name] +./generics_paramspec_basic.py:15:17: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_basic.py:23:10: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_basic.py:23:19: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_basic.py:27:13: Unrecognized annotation typing.Concatenate[, ~P] [invalid_annotation] +./generics_paramspec_basic.py:31:10: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_basic.py:35:10: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_basic.py:39:11: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_basic.py:39:22: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +""" diff --git a/conformance/results/pycroscope/generics_paramspec_components.toml b/conformance/results/pycroscope/generics_paramspec_components.toml new file mode 100644 index 000000000..80e8cbccf --- /dev/null +++ b/conformance/results/pycroscope/generics_paramspec_components.toml @@ -0,0 +1,23 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_paramspec_components.py:17:18: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_components.py:17:36: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_components.py:20:19: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_components.py:23:37: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_components.py:26:37: ParamSpec.args must be used together with ParamSpec.kwargs [invalid_paramspec_usage] +./generics_paramspec_components.py:30:18: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_components.py:30:34: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_components.py:35:17: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_components.py:36:19: ParamSpec cannot be used in this annotation context [invalid_paramspec_usage] +./generics_paramspec_components.py:38:19: ParamSpec.args must be used together with ParamSpec.kwargs [invalid_paramspec_usage] +./generics_paramspec_components.py:41:22: ParamSpec.kwargs must be used together with ParamSpec.args [invalid_paramspec_usage] +./generics_paramspec_components.py:49:8: tuple[object, ...] is not a mapping [incompatible_call] +./generics_paramspec_components.py:51:8: Takes 0 positional arguments but 1 were given [incompatible_call] +./generics_paramspec_components.py:60:37: ParamSpec.args and ParamSpec.kwargs must be adjacent [invalid_paramspec_usage] +./generics_paramspec_components.py:70:17: Arguments cannot follow ParamSpec.args [incompatible_call] +./generics_paramspec_components.py:72:8: Missing required positional argument at position 0 [incompatible_call] +./generics_paramspec_components.py:83:14: Arguments cannot follow ParamSpec.args [incompatible_call] +./generics_paramspec_components.py:98:0: In call to generics_paramspec_components.twice: Cannot resolve type variables [incompatible_call] +""" diff --git a/conformance/results/pycroscope/generics_paramspec_semantics.toml b/conformance/results/pycroscope/generics_paramspec_semantics.toml new file mode 100644 index 000000000..dfaf3a3c1 --- /dev/null +++ b/conformance/results/pycroscope/generics_paramspec_semantics.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_paramspec_semantics.py:26:0: Missing required positional argument 'a' [incompatible_call] +./generics_paramspec_semantics.py:27:8: Incompatible argument type for b: expected bool but got Literal['A'] [incompatible_argument] +./generics_paramspec_semantics.py:46:5: Cannot resolve type variables [incompatible_call] +./generics_paramspec_semantics.py:61:0: Cannot resolve type variables [incompatible_call] +./generics_paramspec_semantics.py:98:3: Incompatible argument type for @0: expected str but got Literal[1] [incompatible_argument] +./generics_paramspec_semantics.py:108:0: Incompatible argument type for args: expected tuple[bool, ...] but got tuple[Literal[1]] [incompatible_argument] +./generics_paramspec_semantics.py:120:3: Incompatible argument type for @0: expected str but got Literal[1] [incompatible_argument] +./generics_paramspec_semantics.py:127:1: Incompatible argument type for x: expected (int, /, ****P@./generics_paramspec_semantics.py.expects_int_first..expects_int_first) -> int but got (x: str) -> int [incompatible_argument] +./generics_paramspec_semantics.py:132:1: Incompatible argument type for x: expected (int, /, ****P@./generics_paramspec_semantics.py.expects_int_first..expects_int_first) -> int but got (*, x: int) -> int [incompatible_argument] +./generics_paramspec_semantics.py:137:1: Incompatible argument type for x: expected (int, /, ****P@./generics_paramspec_semantics.py.expects_int_first..expects_int_first) -> int but got (**kwargs: dict[str, int]) -> int [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/generics_paramspec_specialization.toml b/conformance/results/pycroscope/generics_paramspec_specialization.toml new file mode 100644 index 000000000..5dd4d8c8c --- /dev/null +++ b/conformance/results/pycroscope/generics_paramspec_specialization.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_paramspec_specialization.py:44:14: ParamSpec specialization must use list form, Concatenate[..., P], P, or ... [invalid_annotation] +./generics_paramspec_specialization.py:54:8: Incompatible argument type for @0: expected int but got Literal[''] [incompatible_argument] +./generics_paramspec_specialization.py:55:15: Incompatible argument type for @2: expected bool but got Literal[''] [incompatible_argument] +./generics_paramspec_specialization.py:60:8: Incompatible argument type for @0: expected int but got Literal[''] [incompatible_argument] +./generics_paramspec_specialization.py:61:15: Incompatible argument type for @2: expected bool but got Literal[''] [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/generics_scoping.toml b/conformance/results/pycroscope/generics_scoping.toml new file mode 100644 index 000000000..3238868ee --- /dev/null +++ b/conformance/results/pycroscope/generics_scoping.toml @@ -0,0 +1,19 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_scoping.py:15:12: Literal[1] is not equivalent to int +./generics_scoping.py:19:12: Literal['a'] is not equivalent to str +./generics_scoping.py:34:9: Incompatible argument type for x: expected int but got Literal['a'] [incompatible_argument] +./generics_scoping.py:49:12: Literal['abc'] is not equivalent to str +./generics_scoping.py:53:12: Literal[b'abc'] is not equivalent to bytes +./generics_scoping.py:61:12: Type parameter is not valid in this annotation context [invalid_annotation] +./generics_scoping.py:65:18: Type parameter is not valid in this annotation context [invalid_annotation] +./generics_scoping.py:76:28: Type parameter is not valid in this annotation context [invalid_annotation] +./generics_scoping.py:86:23: Type parameter is not valid in this annotation context [invalid_annotation] +./generics_scoping.py:89:16: Type parameter is not valid in this annotation context [invalid_annotation] +./generics_scoping.py:98:28: Type parameter is not valid in this annotation context [invalid_annotation] +./generics_scoping.py:105:13: Type parameter is not valid in this annotation context [invalid_annotation] +./generics_scoping.py:106:18: Type parameter is not valid in this annotation context [invalid_annotation] +./generics_scoping.py:107:5: Type parameter is not valid in this annotation context [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/generics_self_advanced.toml b/conformance/results/pycroscope/generics_self_advanced.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/generics_self_advanced.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/generics_self_attributes.toml b/conformance/results/pycroscope/generics_self_attributes.toml new file mode 100644 index 000000000..c49b0428e --- /dev/null +++ b/conformance/results/pycroscope/generics_self_attributes.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_self_attributes.py:26:37: Incompatible argument type for next: expected generics_self_attributes.OrdinalLinkedList | None but got generics_self_attributes.LinkedList[int] [incompatible_argument] +./generics_self_attributes.py:32:4: Incompatible types in assignment to attribute 'next': expected generics_self_attributes.OrdinalLinkedList | None, got generics_self_attributes.LinkedList[int] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/generics_self_basic.toml b/conformance/results/pycroscope/generics_self_basic.toml new file mode 100644 index 000000000..d93d9f37a --- /dev/null +++ b/conformance/results/pycroscope/generics_self_basic.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_self_basic.py:20:8: Incompatible return type: expected ~SelfT@./generics_self_basic.py.Shape, got ./generics_self_basic.py.Shape [incompatible_return_value] +./generics_self_basic.py:33:8: Incompatible return type: expected ~SelfT@./generics_self_basic.py.Shape, got ./generics_self_basic.py.Shape [incompatible_return_value] +./generics_self_basic.py:68:25: Self cannot be further subscripted [invalid_specialization] +""" diff --git a/conformance/results/pycroscope/generics_self_protocols.toml b/conformance/results/pycroscope/generics_self_protocols.toml new file mode 100644 index 000000000..cfcf2f650 --- /dev/null +++ b/conformance/results/pycroscope/generics_self_protocols.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_self_protocols.py:61:18: Incompatible argument type for shape: expected generics_self_protocols.ShapeProtocol but got generics_self_protocols.BadReturnType [incompatible_argument] +./generics_self_protocols.py:64:18: Incompatible argument type for shape: expected generics_self_protocols.ShapeProtocol but got generics_self_protocols.ReturnDifferentClass [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/generics_self_usage.toml b/conformance/results/pycroscope/generics_self_usage.toml new file mode 100644 index 000000000..b3b83f3d6 --- /dev/null +++ b/conformance/results/pycroscope/generics_self_usage.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_self_usage.py:73:13: Self cannot be used outside a class [invalid_self_usage] +./generics_self_usage.py:73:22: Self cannot be used outside a class [invalid_self_usage] +./generics_self_usage.py:76:5: Self can only be used in class attribute annotations [invalid_self_usage] +./generics_self_usage.py:82:4: Self methods using Self must annotate the receiver with Self or leave it unannotated [invalid_self_usage] +./generics_self_usage.py:87:8: Incompatible return type: expected ~SelfT@./generics_self_usage.py.Foo3, got ./generics_self_usage.py.Foo3 [incompatible_return_value] +./generics_self_usage.py:103:10: Self cannot be used in base class expressions [invalid_base] +./generics_self_usage.py:105:11: Self cannot be used in base class expressions [invalid_base] +./generics_self_usage.py:108:23: Self cannot be used in type aliases [invalid_self_usage] +./generics_self_usage.py:113:4: Self cannot be used in staticmethod signatures [invalid_self_usage] +./generics_self_usage.py:118:4: Self cannot be used in staticmethod signatures [invalid_self_usage] +./generics_self_usage.py:123:4: Self cannot be used in metaclass definitions [invalid_self_usage] +./generics_self_usage.py:127:4: Self cannot be used in metaclass definitions [invalid_self_usage] +""" diff --git a/conformance/results/pycroscope/generics_syntax_compatibility.toml b/conformance/results/pycroscope/generics_syntax_compatibility.toml new file mode 100644 index 000000000..d4f3d5172 --- /dev/null +++ b/conformance/results/pycroscope/generics_syntax_compatibility.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_syntax_compatibility.py:14:21: Class definition cannot combine old-style TypeVar declarations with type parameter syntax [invalid_annotation] +./generics_syntax_compatibility.py:26:34: Function definition cannot combine old-style TypeVar declarations with type parameter syntax [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/generics_syntax_declarations.toml b/conformance/results/pycroscope/generics_syntax_declarations.toml new file mode 100644 index 000000000..663c7959f --- /dev/null +++ b/conformance/results/pycroscope/generics_syntax_declarations.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_syntax_declarations.py:17:16: Class definition cannot specialize Generic or Protocol bases when using type parameter syntax [invalid_base] +./generics_syntax_declarations.py:25:19: Class definition cannot specialize Generic or Protocol bases when using type parameter syntax [invalid_base] +./generics_syntax_declarations.py:32:8: str has no attribute 'is_integer' [undefined_attribute] +./generics_syntax_declarations.py:44:20: TypeVar bound cannot be parameterized by type variables [invalid_annotation] +./generics_syntax_declarations.py:48:16: Invalid type annotation [, ] [invalid_annotation] +./generics_syntax_declarations.py:60:16: TypeVar constraints must contain at least two types [invalid_annotation] +./generics_syntax_declarations.py:64:16: TypeVar constraints must contain at least two types [invalid_annotation] +./generics_syntax_declarations.py:71:16: Invalid type annotation (, ) [invalid_annotation] +./generics_syntax_declarations.py:75:17: Invalid type annotation 3 [invalid_annotation] +./generics_syntax_declarations.py:79:22: Undefined name: S [undefined_name] +""" diff --git a/conformance/results/pycroscope/generics_syntax_infer_variance.toml b/conformance/results/pycroscope/generics_syntax_infer_variance.toml new file mode 100644 index 000000000..fdae01c6a --- /dev/null +++ b/conformance/results/pycroscope/generics_syntax_infer_variance.toml @@ -0,0 +1,23 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_syntax_infer_variance.py:15:5: TypeVar cannot combine infer_variance with explicit variance [incompatible_call] +./generics_syntax_infer_variance.py:17:5: TypeVar cannot combine infer_variance with explicit variance [incompatible_call] +./generics_syntax_infer_variance.py:29:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeCovariant1[int], got ./generics_syntax_infer_variance.py.ShouldBeCovariant1[float | int] [incompatible_assignment] +./generics_syntax_infer_variance.py:47:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeCovariant2[int], got ./generics_syntax_infer_variance.py.ShouldBeCovariant2[float | int] [incompatible_assignment] +./generics_syntax_infer_variance.py:56:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeCovariant3[int], got ./generics_syntax_infer_variance.py.ShouldBeCovariant3[float | int] [incompatible_assignment] +./generics_syntax_infer_variance.py:85:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeCovariant5[int], got ./generics_syntax_infer_variance.py.ShouldBeCovariant5[float | int] [incompatible_assignment] +./generics_syntax_infer_variance.py:96:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeCovariant6[int], got ./generics_syntax_infer_variance.py.ShouldBeCovariant6[float | int] [incompatible_assignment] +./generics_syntax_infer_variance.py:112:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant1[float | int], got ./generics_syntax_infer_variance.py.ShouldBeInvariant1[int] [incompatible_assignment] +./generics_syntax_infer_variance.py:113:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant1[int], got ./generics_syntax_infer_variance.py.ShouldBeInvariant1[float | int] [incompatible_assignment] +./generics_syntax_infer_variance.py:127:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant2[float | int], got ./generics_syntax_infer_variance.py.ShouldBeInvariant2[int] [incompatible_assignment] +./generics_syntax_infer_variance.py:128:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant2[int], got ./generics_syntax_infer_variance.py.ShouldBeInvariant2[float | int] [incompatible_assignment] +./generics_syntax_infer_variance.py:135:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant3[float | int, str], got ./generics_syntax_infer_variance.py.ShouldBeInvariant3[int, str] [incompatible_assignment] +./generics_syntax_infer_variance.py:136:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant3[int, str], got ./generics_syntax_infer_variance.py.ShouldBeInvariant3[float | int, str] [incompatible_assignment] +./generics_syntax_infer_variance.py:137:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant3[str, float | int], got ./generics_syntax_infer_variance.py.ShouldBeInvariant3[str, int] [incompatible_assignment] +./generics_syntax_infer_variance.py:138:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant3[str, int], got ./generics_syntax_infer_variance.py.ShouldBeInvariant3[str, float | int] [incompatible_assignment] +./generics_syntax_infer_variance.py:146:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant4[float | int], got ./generics_syntax_infer_variance.py.ShouldBeInvariant4[int] [incompatible_assignment] +./generics_syntax_infer_variance.py:154:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeInvariant5[float | int], got ./generics_syntax_infer_variance.py.ShouldBeInvariant5[int] [incompatible_assignment] +./generics_syntax_infer_variance.py:165:0: Incompatible assignment: expected ./generics_syntax_infer_variance.py.ShouldBeContravariant1[float | int], got ./generics_syntax_infer_variance.py.ShouldBeContravariant1[int] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/generics_syntax_scoping.toml b/conformance/results/pycroscope/generics_syntax_scoping.toml new file mode 100644 index 000000000..3bff89f05 --- /dev/null +++ b/conformance/results/pycroscope/generics_syntax_scoping.toml @@ -0,0 +1,24 @@ +conformant = "Partial" +notes = """ +Misses some details of scoping rules. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 18: Expected 1 errors +Line 92: Expected 1 errors +Line 95: Expected 1 errors +Line 98: Expected 1 errors +Line 74: Unexpected errors ['./generics_syntax_scoping.py:74:19: Undefined name: Private [undefined_name]'] +Line 88: Unexpected errors ['./generics_syntax_scoping.py:88:1: Undefined name: decorator2 [undefined_name]'] +Line 117: Unexpected errors ['./generics_syntax_scoping.py:117:28: ~T@./generics_syntax_scoping.py.f..Outer2 is not equivalent to typing.TypeVar', './generics_syntax_scoping.py:117:28: Type parameter is not valid in this annotation context [invalid_annotation]'] +""" +output = """ +./generics_syntax_scoping.py:14:19: TypeVar bound cannot be parameterized by type variables [invalid_annotation] +./generics_syntax_scoping.py:35:6: Undefined name: T [undefined_name] +./generics_syntax_scoping.py:44:16: Undefined name: T [undefined_name] +./generics_syntax_scoping.py:44:1: Undefined name: decorator1 [undefined_name] +./generics_syntax_scoping.py:74:19: Undefined name: Private [undefined_name] +./generics_syntax_scoping.py:88:1: Undefined name: decorator2 [undefined_name] +./generics_syntax_scoping.py:117:28: ~T@./generics_syntax_scoping.py.f..Outer2 is not equivalent to typing.TypeVar +./generics_syntax_scoping.py:117:28: Type parameter is not valid in this annotation context [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/generics_type_erasure.toml b/conformance/results/pycroscope/generics_type_erasure.toml new file mode 100644 index 000000000..9ffdc5c1e --- /dev/null +++ b/conformance/results/pycroscope/generics_type_erasure.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_type_erasure.py:38:15: Incompatible argument type for label: expected int | None but got Literal[''] [incompatible_argument] +./generics_type_erasure.py:40:15: Incompatible argument type for label: expected str | None but got Literal[0] [incompatible_argument] +./generics_type_erasure.py:42:0: Cannot assign to instance attribute 'label' via class object [incompatible_assignment] +./generics_type_erasure.py:43:0: types.GenericAlias (partial from [type 'int']) has no attribute 'label' [undefined_attribute] +./generics_type_erasure.py:44:0: Cannot assign to instance attribute 'label' via class object [incompatible_assignment] +./generics_type_erasure.py:45:0: has no attribute 'label' [undefined_attribute] +./generics_type_erasure.py:46:0: type[./generics_type_erasure.py.Node[int]] has no attribute 'label' [undefined_attribute] +""" diff --git a/conformance/results/pycroscope/generics_typevartuple_args.toml b/conformance/results/pycroscope/generics_typevartuple_args.toml new file mode 100644 index 000000000..e65813587 --- /dev/null +++ b/conformance/results/pycroscope/generics_typevartuple_args.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_typevartuple_args.py:33:4: Incompatible argument type for args: expected tuple[*tuple[Ts, ...], generics_typevartuple_args.Env] but got tuple[Literal[0], Literal['']] [incompatible_argument] +./generics_typevartuple_args.py:34:4: Incompatible argument type for args: expected tuple[*tuple[Ts, ...], generics_typevartuple_args.Env] but got tuple[Literal[0], Literal['']] [incompatible_argument] +./generics_typevartuple_args.py:48:0: Incompatible argument type for args: expected tuple[int, ...] but got tuple[Literal[1], Literal['2'], Literal[3]] [incompatible_argument] +./generics_typevartuple_args.py:57:0: Incompatible argument type for args: expected tuple[int, *tuple[str, ...], str] but got tuple[Literal[1], Literal[1], Literal['']] [incompatible_argument] +./generics_typevartuple_args.py:58:0: Incompatible argument type for args: expected tuple[int, *tuple[str, ...], str] but got tuple[Literal[1]] [incompatible_argument] +./generics_typevartuple_args.py:59:0: Incompatible argument type for args: expected tuple[int, *tuple[str, ...], str] but got tuple[Literal['']] [incompatible_argument] +./generics_typevartuple_args.py:67:0: In call to generics_typevartuple_args.func3: Missing required positional argument at position 1 [incompatible_call] +./generics_typevartuple_args.py:75:0: In call to generics_typevartuple_args.func4: Cannot resolve type variables [incompatible_call] +""" diff --git a/conformance/results/pycroscope/generics_typevartuple_basic.toml b/conformance/results/pycroscope/generics_typevartuple_basic.toml new file mode 100644 index 000000000..842c7ca46 --- /dev/null +++ b/conformance/results/pycroscope/generics_typevartuple_basic.toml @@ -0,0 +1,19 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_typevartuple_basic.py:42:33: Incompatible argument type for shape: expected tuple[*tuple[Shape, ...]] but got NewType('Height', int) [incompatible_argument] +./generics_typevartuple_basic.py:43:0: Incompatible assignment: expected ./generics_typevartuple_basic.py.Array[NewType('Batch', int), NewType('Height', int), NewType('Width', int)], got ./generics_typevartuple_basic.py.Array~[NewType('Batch', int), NewType('Width', int)] [incompatible_assignment] +./generics_typevartuple_basic.py:44:0: Incompatible assignment: expected ./generics_typevartuple_basic.py.Array[NewType('Time', int), NewType('Batch', int), NewType('Height', int), NewType('Width', int)], got ./generics_typevartuple_basic.py.Array~[NewType('Time', int), NewType('Batch', int), NewType('Width', int), NewType('Height', int)] [incompatible_assignment] +./generics_typevartuple_basic.py:52:21: TypeVarTuple must be unpacked [invalid_base] +./generics_typevartuple_basic.py:53:30: TypeVarTuple must be unpacked [invalid_annotation] +./generics_typevartuple_basic.py:56:27: TypeVarTuple must be unpacked [invalid_annotation] +./generics_typevartuple_basic.py:59:17: TypeVarTuple must be unpacked [invalid_annotation] +./generics_typevartuple_basic.py:65:6: In call to typing.TypeVarTuple: Got an unexpected keyword argument 'covariant' [incompatible_call] +./generics_typevartuple_basic.py:66:6: In call to typing.TypeVarTuple: Takes 1 positional arguments but 3 were given [incompatible_call] +./generics_typevartuple_basic.py:67:6: In call to typing.TypeVarTuple: Got an unexpected keyword argument 'bound' [incompatible_call] +./generics_typevartuple_basic.py:91:0: Cannot resolve type variables [incompatible_call] +./generics_typevartuple_basic.py:100:4: Cannot resolve type variables [incompatible_call] +./generics_typevartuple_basic.py:101:4: Cannot resolve type variables [incompatible_call] +./generics_typevartuple_basic.py:107:21: Only one TypeVarTuple can be used in a type parameter list [invalid_base] +""" diff --git a/conformance/results/pycroscope/generics_typevartuple_callable.toml b/conformance/results/pycroscope/generics_typevartuple_callable.toml new file mode 100644 index 000000000..cf4dd26c6 --- /dev/null +++ b/conformance/results/pycroscope/generics_typevartuple_callable.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_typevartuple_callable.py:26:0: Cannot resolve type variables [incompatible_call] +""" diff --git a/conformance/results/pycroscope/generics_typevartuple_concat.toml b/conformance/results/pycroscope/generics_typevartuple_concat.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/generics_typevartuple_concat.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/generics_typevartuple_overloads.toml b/conformance/results/pycroscope/generics_typevartuple_overloads.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/generics_typevartuple_overloads.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/generics_typevartuple_specialization.toml b/conformance/results/pycroscope/generics_typevartuple_specialization.toml new file mode 100644 index 000000000..9de610e51 --- /dev/null +++ b/conformance/results/pycroscope/generics_typevartuple_specialization.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_typevartuple_specialization.py:109:0: Unpacked TypeVarTuple cannot specialize this type alias [invalid_annotation] +./generics_typevartuple_specialization.py:110:0: Unpacked TypeVarTuple cannot specialize this type alias [invalid_annotation] +./generics_typevartuple_specialization.py:121:6: Only one unbounded tuple can be used inside a tuple type [invalid_annotation] +./generics_typevartuple_specialization.py:122:6: Only one unbounded tuple can be used inside a tuple type [invalid_annotation] +./generics_typevartuple_specialization.py:127:4: Expected 3 type arguments for type alias, got 1 [invalid_specialization] +./generics_typevartuple_specialization.py:163:7: Unpacked TypeVarTuple cannot specialize this type alias [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/generics_typevartuple_unpack.toml b/conformance/results/pycroscope/generics_typevartuple_unpack.toml new file mode 100644 index 000000000..7c7e107eb --- /dev/null +++ b/conformance/results/pycroscope/generics_typevartuple_unpack.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_typevartuple_unpack.py:30:27: Incompatible argument type for x: expected generics_typevartuple_unpack.Array[NewType('Batch', int), *tuple[Any[explicit], ...], NewType('Channels', int)] but got generics_typevartuple_unpack.Array[NewType('Batch', int)] [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/generics_upper_bound.toml b/conformance/results/pycroscope/generics_upper_bound.toml new file mode 100644 index 000000000..4cd6263ee --- /dev/null +++ b/conformance/results/pycroscope/generics_upper_bound.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_upper_bound.py:24:37: TypeVar bounds and constraints cannot contain type parameters [invalid_annotation] +./generics_upper_bound.py:44:16: list[int] | set[int] is not equivalent to collections.abc.Collection[int] +./generics_upper_bound.py:52:7: Incompatible argument type for x: expected ~ST@./generics_upper_bound.py.longer..longer but got Literal[3] [incompatible_argument] +./generics_upper_bound.py:57:9: TypeVar cannot have both bound and constraints [incompatible_call] +""" diff --git a/conformance/results/pycroscope/generics_variance.toml b/conformance/results/pycroscope/generics_variance.toml new file mode 100644 index 000000000..a4eb7dcae --- /dev/null +++ b/conformance/results/pycroscope/generics_variance.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_variance.py:14:5: Bivariant types are not supported [incompatible_call] +./generics_variance.py:77:13: T_co has incompatible variance in base class [invalid_base] +./generics_variance.py:81:13: T_contra has incompatible variance in base class [invalid_base] +./generics_variance.py:93:16: T_contra has incompatible variance in base class [invalid_base] +./generics_variance.py:105:20: T_co has incompatible variance in base class [invalid_base] +./generics_variance.py:113:20: T_co has incompatible variance in base class [invalid_base] +./generics_variance.py:126:4: T_co has incompatible variance in base class [invalid_base] +./generics_variance.py:132:4: T_contra has incompatible variance in base class [invalid_base] +./generics_variance.py:142:4: T_co has incompatible variance in base class [invalid_base] +./generics_variance.py:163:25: T_contra has incompatible variance in base class [invalid_base] +./generics_variance.py:167:29: T_co has incompatible variance in base class [invalid_base] +./generics_variance.py:191:32: T_contra has incompatible variance in base class [invalid_base] +./generics_variance.py:196:4: T_co has incompatible variance in base class [invalid_base] +""" diff --git a/conformance/results/pycroscope/generics_variance_inference.toml b/conformance/results/pycroscope/generics_variance_inference.toml new file mode 100644 index 000000000..56b1fa57b --- /dev/null +++ b/conformance/results/pycroscope/generics_variance_inference.toml @@ -0,0 +1,28 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./generics_variance_inference.py:24:4: Incompatible assignment: expected generics_variance_inference.ClassA[int, int, int], got generics_variance_inference.ClassA[float | int, int, int] [incompatible_assignment] +./generics_variance_inference.py:25:4: Incompatible assignment: expected generics_variance_inference.ClassA[float | int, float | int, int], got generics_variance_inference.ClassA[float | int, int, int] [incompatible_assignment] +./generics_variance_inference.py:28:4: Incompatible assignment: expected generics_variance_inference.ClassA[int, int, int], got generics_variance_inference.ClassA[int, float | int, float | int] [incompatible_assignment] +./generics_variance_inference.py:41:0: Incompatible assignment: expected generics_variance_inference.ShouldBeCovariant1[int], got generics_variance_inference.ShouldBeCovariant1[float | int] [incompatible_assignment] +./generics_variance_inference.py:49:0: Incompatible assignment: expected generics_variance_inference.ShouldBeCovariant2[int], got generics_variance_inference.ShouldBeCovariant2[float | int] [incompatible_assignment] +./generics_variance_inference.py:58:0: Incompatible assignment: expected generics_variance_inference.ShouldBeCovariant3[int], got generics_variance_inference.ShouldBeCovariant3[float | int] [incompatible_assignment] +./generics_variance_inference.py:67:0: Incompatible assignment: expected generics_variance_inference.ShouldBeCovariant4[int], got generics_variance_inference.ShouldBeCovariant4[float | int] [incompatible_assignment] +./generics_variance_inference.py:80:0: Incompatible assignment: expected generics_variance_inference.ShouldBeCovariant5[int], got generics_variance_inference.ShouldBeCovariant5[float | int] [incompatible_assignment] +./generics_variance_inference.py:96:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant1[float | int], got generics_variance_inference.ShouldBeInvariant1[int] [incompatible_assignment] +./generics_variance_inference.py:97:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant1[int], got generics_variance_inference.ShouldBeInvariant1[float | int] [incompatible_assignment] +./generics_variance_inference.py:111:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant2[float | int], got generics_variance_inference.ShouldBeInvariant2[int] [incompatible_assignment] +./generics_variance_inference.py:112:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant2[int], got generics_variance_inference.ShouldBeInvariant2[float | int] [incompatible_assignment] +./generics_variance_inference.py:119:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant3[float | int, str], got generics_variance_inference.ShouldBeInvariant3[int, str] [incompatible_assignment] +./generics_variance_inference.py:120:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant3[int, str], got generics_variance_inference.ShouldBeInvariant3[float | int, str] [incompatible_assignment] +./generics_variance_inference.py:121:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant3[str, float | int], got generics_variance_inference.ShouldBeInvariant3[str, int] [incompatible_assignment] +./generics_variance_inference.py:122:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant3[str, int], got generics_variance_inference.ShouldBeInvariant3[str, float | int] [incompatible_assignment] +./generics_variance_inference.py:130:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant4[float | int], got generics_variance_inference.ShouldBeInvariant4[int] [incompatible_assignment] +./generics_variance_inference.py:138:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant5[float | int], got generics_variance_inference.ShouldBeInvariant5[int] [incompatible_assignment] +./generics_variance_inference.py:149:0: Incompatible assignment: expected generics_variance_inference.ShouldBeContravariant1[float | int], got generics_variance_inference.ShouldBeContravariant1[int] [incompatible_assignment] +./generics_variance_inference.py:169:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant6[int], got generics_variance_inference.ShouldBeInvariant6[float | int] [incompatible_assignment] +./generics_variance_inference.py:170:0: Incompatible assignment: expected generics_variance_inference.ShouldBeInvariant6[float | int], got generics_variance_inference.ShouldBeInvariant6[int] [incompatible_assignment] +./generics_variance_inference.py:181:0: Incompatible assignment: expected generics_variance_inference.ShouldBeCovariant6[int], got generics_variance_inference.ShouldBeCovariant6[float | int] [incompatible_assignment] +./generics_variance_inference.py:194:0: Incompatible assignment: expected generics_variance_inference.ShouldBeContravariant2[float | int], got generics_variance_inference.ShouldBeContravariant2[int] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/historical_positional.toml b/conformance/results/pycroscope/historical_positional.toml new file mode 100644 index 000000000..d8bfa937a --- /dev/null +++ b/conformance/results/pycroscope/historical_positional.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./historical_positional.py:18:0: Missing required positional argument '__x' [incompatible_call] +./historical_positional.py:26:15: Historical positional-only parameter may not follow a positional-or-keyword parameter [invalid_positional_only] +./historical_positional.py:54:25: Historical positional-only parameter may not follow a positional-or-keyword parameter [invalid_positional_only] +./historical_positional.py:59:0: Missing required positional argument '__x' [incompatible_call] +""" diff --git a/conformance/results/pycroscope/literals_interactions.toml b/conformance/results/pycroscope/literals_interactions.toml new file mode 100644 index 000000000..e13017ec5 --- /dev/null +++ b/conformance/results/pycroscope/literals_interactions.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./literals_interactions.py:14:4: Tuple index out of range: Literal[5] [incompatible_call] +./literals_interactions.py:15:4: Tuple index out of range: Literal[-5] [incompatible_call] +./literals_interactions.py:16:4: Tuple index out of range: Literal[4] [incompatible_call] +./literals_interactions.py:17:4: Tuple index out of range: Literal[-4] [incompatible_call] +""" diff --git a/conformance/results/pycroscope/literals_literalstring.toml b/conformance/results/pycroscope/literals_literalstring.toml new file mode 100644 index 000000000..742999f58 --- /dev/null +++ b/conformance/results/pycroscope/literals_literalstring.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./literals_literalstring.py:36:11: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got typing.LiteralString [invalid_literal] +./literals_literalstring.py:37:13: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got typing.LiteralString [invalid_literal] +./literals_literalstring.py:43:4: Incompatible assignment: expected Literal[''], got Literal['two'] [incompatible_assignment] +./literals_literalstring.py:65:4: Incompatible assignment: expected LiteralString, got str [incompatible_assignment] +./literals_literalstring.py:73:4: Incompatible assignment: expected LiteralString, got Literal[3] [incompatible_assignment] +./literals_literalstring.py:74:4: Incompatible assignment: expected LiteralString, got Literal[b'test'] [incompatible_assignment] +./literals_literalstring.py:119:21: Incompatible argument type for s: expected ~TLiteral but got str [incompatible_argument] +./literals_literalstring.py:133:50: Incompatible argument type for value: expected ~T@literals_literalstring.Container but got str [incompatible_argument] +./literals_literalstring.py:171:4: Incompatible assignment: expected list[str], got list[LiteralString] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/literals_parameterizations.toml b/conformance/results/pycroscope/literals_parameterizations.toml new file mode 100644 index 000000000..80a88342d --- /dev/null +++ b/conformance/results/pycroscope/literals_parameterizations.toml @@ -0,0 +1,27 @@ +conformant = "Partial" +notes = """ +Fails to reject various invalid literal parameterizations. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 41: Expected 1 errors +Line 42: Expected 1 errors +Line 44: Expected 1 errors +Line 45: Expected 1 errors +Line 46: Expected 1 errors +Line 49: Expected 1 errors +Line 56: Expected 1 errors +""" +output = """ +./literals_parameterizations.py:43:6: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got (4+3j) [invalid_literal] +./literals_parameterizations.py:47:6: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got {'a': 'b', 'c': 'd'} [invalid_literal] +./literals_parameterizations.py:48:6: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got [invalid_literal] +./literals_parameterizations.py:50:7: Arguments to Literal[] must be literals, not (PartialCallValue(callee=KnownValue(val=), arguments={'name': KnownValue(val='T'), 'constraints': SequenceValue(typ=, literal_only=False, args=(MultiValuedValue(vals=()),), weak=True, members=()), 'bound': KnownValue(val=), 'covariant': KnownValue(val=False), 'contravariant': KnownValue(val=False), 'infer_variance': KnownValue(val=False), 'default': KnownValue(val=)}, runtime_value=TypedValue(typ=, literal_only=False), node=),) [invalid_literal] +./literals_parameterizations.py:50:15: Type parameter is not valid in this annotation context [invalid_annotation] +./literals_parameterizations.py:51:7: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got 3.14 [invalid_literal] +./literals_parameterizations.py:52:7: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got typing.Any [invalid_literal] +./literals_parameterizations.py:53:7: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got Ellipsis [invalid_literal] +./literals_parameterizations.py:60:3: Invalid type annotation typing.Literal [invalid_annotation] +./literals_parameterizations.py:61:3: Arguments to Literal[] must be None, bool, int, str, bytes, or enum members; got [invalid_literal] +./literals_parameterizations.py:65:4: Incompatible assignment: expected Literal['Color.RED'], got Literal[] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/literals_semantics.toml b/conformance/results/pycroscope/literals_semantics.toml new file mode 100644 index 000000000..89d3a423c --- /dev/null +++ b/conformance/results/pycroscope/literals_semantics.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./literals_semantics.py:10:0: Incompatible assignment: expected Literal[3], got Literal[4] [incompatible_assignment] +./literals_semantics.py:24:4: Incompatible assignment: expected Literal[False], got Literal[0] [incompatible_assignment] +./literals_semantics.py:25:4: Incompatible assignment: expected Literal[0], got Literal[False] [incompatible_assignment] +./literals_semantics.py:33:4: Incompatible assignment: expected Literal[3, 4, 5], got Literal[6, 7, 8] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/namedtuples_define_class.toml b/conformance/results/pycroscope/namedtuples_define_class.toml new file mode 100644 index 000000000..8cd3f9ab2 --- /dev/null +++ b/conformance/results/pycroscope/namedtuples_define_class.toml @@ -0,0 +1,28 @@ +conformant = "Partial" +notes = """ +Does not support precise type inference for slices over namedtuples. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 30: Unexpected errors ['./namedtuples_define_class.py:30:12: tuple is not equivalent to tuple[int, int]'] +Line 31: Unexpected errors ['./namedtuples_define_class.py:31:12: tuple is not equivalent to tuple[int, int, str]'] +""" +output = """ +./namedtuples_define_class.py:30:12: tuple is not equivalent to tuple[int, int] +./namedtuples_define_class.py:31:12: tuple is not equivalent to tuple[int, int, str] +./namedtuples_define_class.py:33:6: Tuple index out of range: Literal[3] [incompatible_call] +./namedtuples_define_class.py:34:6: Tuple index out of range: Literal[-4] [incompatible_call] +./namedtuples_define_class.py:45:5: Missing required argument 'y' [incompatible_call] +./namedtuples_define_class.py:46:5: Missing required argument 'y' [incompatible_call] +./namedtuples_define_class.py:47:14: Incompatible argument type for y: expected int but got Literal[''] [incompatible_argument] +./namedtuples_define_class.py:48:23: Incompatible argument type for units: expected str but got Literal[3] [incompatible_argument] +./namedtuples_define_class.py:49:5: Takes 3 positional arguments but 4 were given [incompatible_call] +./namedtuples_define_class.py:50:6: Got an unexpected keyword argument 'other' [incompatible_call] +./namedtuples_define_class.py:70:6: Takes 2 positional arguments but 3 were given [incompatible_call] +./namedtuples_define_class.py:77:4: NamedTuple field names cannot start with an underscore [invalid_namedtuple] +./namedtuples_define_class.py:87:4: NamedTuple fields without defaults cannot follow fields with defaults [invalid_namedtuple] +./namedtuples_define_class.py:107:4: Field 'x' conflicts with base NamedTuple field [incompatible_override] +./namedtuples_define_class.py:121:0: Takes 2 positional arguments but 3 were given [incompatible_call] +./namedtuples_define_class.py:140:18: Incompatible argument type for value: expected str but got Literal[3.1] [incompatible_argument] +./namedtuples_define_class.py:147:23: NamedTuple classes may only inherit from NamedTuple and Generic [invalid_base] +""" diff --git a/conformance/results/pycroscope/namedtuples_define_functional.toml b/conformance/results/pycroscope/namedtuples_define_functional.toml new file mode 100644 index 000000000..eaa0de901 --- /dev/null +++ b/conformance/results/pycroscope/namedtuples_define_functional.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./namedtuples_define_functional.py:16:7: In call to pycroscope.signature.Point1: Missing required argument 'y' [incompatible_call] +./namedtuples_define_functional.py:21:7: In call to pycroscope.signature.Point2: Missing required argument 'x' [incompatible_call] +./namedtuples_define_functional.py:26:7: In call to pycroscope.signature.Point3: Takes 2 positional arguments but 3 were given [incompatible_call] +./namedtuples_define_functional.py:31:7: In call to pycroscope.signature.Point4: Missing required argument 'y' [incompatible_call] +./namedtuples_define_functional.py:36:17: Incompatible argument type for y: expected int but got Literal['1'] [incompatible_argument] +./namedtuples_define_functional.py:37:7: In call to pycroscope.signature.Point5: Takes 2 positional arguments but 3 were given [incompatible_call] +./namedtuples_define_functional.py:42:17: Incompatible argument type for y: expected int but got Literal['1'] [incompatible_argument] +./namedtuples_define_functional.py:43:16: Incompatible argument type for x: expected int but got Literal[1.1] [incompatible_argument] +./namedtuples_define_functional.py:52:6: Error calling (typename: str, field_names: str | collections.abc.Iterable[str], *, rename: bool = Literal[False], module: str | None = None, defaults: collections.abc.Iterable[Any[explicit]] | None = None) -> type[tuple[Any[explicit], ...]]: Encountered duplicate field name: 'a' [incompatible_call] +./namedtuples_define_functional.py:53:6: Error calling (typename: str, field_names: str | collections.abc.Iterable[str], *, rename: bool = Literal[False], module: str | None = None, defaults: collections.abc.Iterable[Any[explicit]] | None = None) -> type[tuple[Any[explicit], ...]]: Type names and field names cannot be a keyword: 'def' [incompatible_call] +./namedtuples_define_functional.py:54:6: Error calling (typename: str, field_names: str | collections.abc.Iterable[str], *, rename: bool = Literal[False], module: str | None = None, defaults: collections.abc.Iterable[Any[explicit]] | None = None) -> type[tuple[Any[explicit], ...]]: Type names and field names cannot be a keyword: 'def' [incompatible_call] +./namedtuples_define_functional.py:55:6: Error calling (typename: str, field_names: str | collections.abc.Iterable[str], *, rename: bool = Literal[False], module: str | None = None, defaults: collections.abc.Iterable[Any[explicit]] | None = None) -> type[tuple[Any[explicit], ...]]: Field names cannot start with an underscore: '_d' [incompatible_call] +./namedtuples_define_functional.py:69:0: In call to pycroscope.signature.NT7: Missing required argument 'a' [incompatible_call] +""" diff --git a/conformance/results/pycroscope/namedtuples_type_compat.toml b/conformance/results/pycroscope/namedtuples_type_compat.toml new file mode 100644 index 000000000..15ccafae4 --- /dev/null +++ b/conformance/results/pycroscope/namedtuples_type_compat.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./namedtuples_type_compat.py:22:0: Incompatible assignment: expected tuple[int, int], got Literal[Point(x=1, y=2, units='inches')] [incompatible_assignment] +./namedtuples_type_compat.py:23:0: Incompatible assignment: expected tuple[int, str, str], got Literal[Point(x=1, y=2, units='inches')] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/namedtuples_usage.toml b/conformance/results/pycroscope/namedtuples_usage.toml new file mode 100644 index 000000000..8c47f786e --- /dev/null +++ b/conformance/results/pycroscope/namedtuples_usage.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./namedtuples_usage.py:34:6: Tuple index out of range: Literal[3] [incompatible_call] +./namedtuples_usage.py:35:6: Tuple index out of range: Literal[-4] [incompatible_call] +./namedtuples_usage.py:40:0: Cannot assign to read-only attribute 'x' [incompatible_assignment] +./namedtuples_usage.py:41:0: Object of type tuple[int, int, str] does not support '__setitem__' [unsupported_operation] +./namedtuples_usage.py:42:4: Cannot delete read-only attribute 'x' [incompatible_assignment] +./namedtuples_usage.py:43:4: Object of type tuple[int, int, str] does not support '__delitem__' [unsupported_operation] +./namedtuples_usage.py:52:0: Cannot unpack ./namedtuples_usage.py.Point [bad_unpack] +./namedtuples_usage.py:53:0: Cannot unpack ./namedtuples_usage.py.Point [bad_unpack] +""" diff --git a/conformance/results/pycroscope/narrowing_typeguard.toml b/conformance/results/pycroscope/narrowing_typeguard.toml new file mode 100644 index 000000000..9b9824070 --- /dev/null +++ b/conformance/results/pycroscope/narrowing_typeguard.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./narrowing_typeguard.py:102:4: TypeGuard must be used on a function taking at least one positional parameter [invalid_typeguard] +./narrowing_typeguard.py:107:4: TypeGuard must be used on a function taking at least one positional parameter [invalid_typeguard] +./narrowing_typeguard.py:128:19: Incompatible argument type for f: expected (object, /) -> str but got function 'narrowing_typeguard.simple_typeguard' [incompatible_argument] +./narrowing_typeguard.py:148:25: Incompatible argument type for f: expected narrowing_typeguard.CallableStrProto but got function 'narrowing_typeguard.simple_typeguard' [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/narrowing_typeis.toml b/conformance/results/pycroscope/narrowing_typeis.toml new file mode 100644 index 000000000..d466f5fc3 --- /dev/null +++ b/conformance/results/pycroscope/narrowing_typeis.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./narrowing_typeis.py:110:4: TypeIs must be used on a function taking at least one positional parameter [invalid_typeguard] +./narrowing_typeis.py:115:4: TypeIs must be used on a function taking at least one positional parameter [invalid_typeguard] +./narrowing_typeis.py:137:19: Incompatible argument type for f: expected (object, /) -> str but got function 'narrowing_typeis.simple_typeguard' [incompatible_argument] +./narrowing_typeis.py:157:25: Incompatible argument type for f: expected narrowing_typeis.CallableStrProto but got function 'narrowing_typeis.simple_typeguard' [incompatible_argument] +./narrowing_typeis.py:174:16: Incompatible argument type for f: expected (object, /) -> Annotated[bool, TypeGuardExtension(guarded_type=TypedValue(typ=, literal_only=False))] but got function 'narrowing_typeis.is_int_typeis' [incompatible_argument] +./narrowing_typeis.py:175:13: Incompatible argument type for f: expected (object, /) -> Annotated[bool, TypeIsExtension(guarded_type=TypedValue(typ=, literal_only=False))] but got function 'narrowing_typeis.is_int_typeguard' [incompatible_argument] +./narrowing_typeis.py:196:17: Incompatible argument type for f: expected (object, /) -> Annotated[bool, TypeIsExtension(guarded_type=TypedValue(typ=, literal_only=False))] but got function 'narrowing_typeis.bool_typeis' [incompatible_argument] +./narrowing_typeis.py:200:26: TypeIs narrowed type str is incompatible with parameter x [typeis_must_be_subtype] +./narrowing_typeis.py:204:44: TypeIs narrowed type list[int] is incompatible with parameter x [typeis_must_be_subtype] +""" diff --git a/conformance/results/pycroscope/overloads_basic.toml b/conformance/results/pycroscope/overloads_basic.toml new file mode 100644 index 000000000..beb836472 --- /dev/null +++ b/conformance/results/pycroscope/overloads_basic.toml @@ -0,0 +1,14 @@ +conformant = "Partial" +notes = """ +Does less literal promotion than the test asks for. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 37: Unexpected errors ['./overloads_basic.py:37:12: Literal[0] is not equivalent to int'] +Line 38: Unexpected errors ["./overloads_basic.py:38:12: Literal[b''] is not equivalent to bytes"] +""" +output = """ +./overloads_basic.py:37:12: Literal[0] is not equivalent to int +./overloads_basic.py:38:12: Literal[b''] is not equivalent to bytes +./overloads_basic.py:39:0: Cannot call overloaded function [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/overloads_consistency.toml b/conformance/results/pycroscope/overloads_consistency.toml new file mode 100644 index 000000000..69bde2f94 --- /dev/null +++ b/conformance/results/pycroscope/overloads_consistency.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./overloads_consistency.py:25:0: Overload for 'return_type' is inconsistent with implementation [inconsistent_overload] +./overloads_consistency.py:41:0: Overload for 'parameter_type' is inconsistent with implementation [inconsistent_overload] +""" diff --git a/conformance/results/pycroscope/overloads_definitions.toml b/conformance/results/pycroscope/overloads_definitions.toml new file mode 100644 index 000000000..bb487e497 --- /dev/null +++ b/conformance/results/pycroscope/overloads_definitions.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./overloads_definitions.py:16:0: At least two overload signatures are required [invalid_overload] +./overloads_definitions.py:28:0: Overloaded function is missing an implementation [invalid_overload] +./overloads_definitions.py:59:4: Overloaded function is missing an implementation [invalid_overload] +./overloads_definitions.py:81:4: Overload implementation has incompatible @staticmethod/@classmethod decorator [invalid_overload] +./overloads_definitions.py:86:4: @staticmethod/@classmethod usage must be consistent across overloads [invalid_overload] +./overloads_definitions.py:124:4: @final should be applied only to the overload implementation [invalid_final] +./overloads_definitions.py:139:4: @final should be applied only to the overload implementation [invalid_final] +./overloads_definitions.py:144:4: @final should be applied only to the overload implementation [invalid_final] +./overloads_definitions.py:186:4: Cannot override final attribute final_method [incompatible_override] +./overloads_definitions.py:203:4: Method does not override any base method [override_does_not_override] +./overloads_definitions.py:228:4: @override decorator in invalid location [invalid_override_decorator] +""" diff --git a/conformance/results/pycroscope/overloads_definitions_stub.toml b/conformance/results/pycroscope/overloads_definitions_stub.toml new file mode 100644 index 000000000..5e2d111cb --- /dev/null +++ b/conformance/results/pycroscope/overloads_definitions_stub.toml @@ -0,0 +1,17 @@ +conformant = "Unsupported" +notes = """ +Does not support checking stubs. +""" +conformance_automated = "Fail" +errors_diff = """ +Lines 13, 14: Expected error (tag 'func1') +Lines 32, 33, 37: Expected error (tag 'func5') +Lines 39, 41, 44: Expected error (tag 'func6') +Lines 67, 69, 71, 72, 73: Expected error (tag 'invalid_final') +Lines 80, 82, 84, 85, 86: Expected error (tag 'invalid_final_2') +Lines 102, 107, 108, 111, 113: Expected error (tag 'override-final') +Lines 120, 121, 122: Expected error (tag 'bad_override') +Lines 143, 146, 147, 149: Expected error (tag 'override_impl') +""" +output = """ +""" diff --git a/conformance/results/pycroscope/overloads_evaluation.toml b/conformance/results/pycroscope/overloads_evaluation.toml new file mode 100644 index 000000000..f04831ec5 --- /dev/null +++ b/conformance/results/pycroscope/overloads_evaluation.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./overloads_evaluation.py:38:0: Cannot call overloaded function [incompatible_call] +./overloads_evaluation.py:46:0: Cannot call overloaded function [incompatible_argument] +./overloads_evaluation.py:51:0: Cannot call overloaded function [incompatible_argument] +./overloads_evaluation.py:116:4: Cannot call overloaded function [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/protocols_class_objects.toml b/conformance/results/pycroscope/protocols_class_objects.toml new file mode 100644 index 000000000..e40edf15f --- /dev/null +++ b/conformance/results/pycroscope/protocols_class_objects.toml @@ -0,0 +1,17 @@ +conformant = "Partial" +notes = """ +Abstract type[Proto] still allows protocol class objects in some paths. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 29: Expected 1 errors +Line 34: Expected 1 errors +Line 107: Expected 1 errors +""" +output = """ +./protocols_class_objects.py:58:0: Incompatible assignment: expected ./protocols_class_objects.py.ProtoA1, got [incompatible_assignment] +./protocols_class_objects.py:74:0: Incompatible assignment: expected ./protocols_class_objects.py.ProtoB1, got [incompatible_assignment] +./protocols_class_objects.py:104:0: Incompatible assignment: expected ./protocols_class_objects.py.ProtoC1, got [incompatible_assignment] +./protocols_class_objects.py:106:0: Incompatible assignment: expected ./protocols_class_objects.py.ProtoC1, got [incompatible_assignment] +./protocols_class_objects.py:108:0: Incompatible assignment: expected ./protocols_class_objects.py.ProtoC1, got [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/protocols_definition.toml b/conformance/results/pycroscope/protocols_definition.toml new file mode 100644 index 000000000..506df6a7a --- /dev/null +++ b/conformance/results/pycroscope/protocols_definition.toml @@ -0,0 +1,26 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./protocols_definition.py:30:10: Incompatible argument type for things: expected collections.abc.Iterable[./protocols_definition.py.SupportsClose] but got Literal[[1]] [incompatible_argument] +./protocols_definition.py:67:8: Protocol members cannot be defined via assignment to self [invalid_protocol] +./protocols_definition.py:114:0: Incompatible assignment: expected ./protocols_definition.py.Template2, got ./protocols_definition.py.Concrete2_Bad1 [incompatible_assignment] +./protocols_definition.py:115:0: Incompatible assignment: expected ./protocols_definition.py.Template2, got ./protocols_definition.py.Concrete2_Bad2 [incompatible_assignment] +./protocols_definition.py:116:0: Incompatible assignment: expected ./protocols_definition.py.Template2, got ./protocols_definition.py.Concrete2_Bad3 [incompatible_assignment] +./protocols_definition.py:117:0: Incompatible assignment: expected ./protocols_definition.py.Template2, got ./protocols_definition.py.Concrete2_Bad4 [incompatible_assignment] +./protocols_definition.py:156:0: Incompatible assignment: expected ./protocols_definition.py.Template3, got ./protocols_definition.py.Concrete3_Bad1 [incompatible_assignment] +./protocols_definition.py:157:0: Incompatible assignment: expected ./protocols_definition.py.Template3, got ./protocols_definition.py.Concrete3_Bad2 [incompatible_assignment] +./protocols_definition.py:158:0: Incompatible assignment: expected ./protocols_definition.py.Template3, got ./protocols_definition.py.Concrete3_Bad3 [incompatible_assignment] +./protocols_definition.py:159:0: Incompatible assignment: expected ./protocols_definition.py.Template3, got ./protocols_definition.py.Concrete3_Bad4 [incompatible_assignment] +./protocols_definition.py:160:0: Incompatible assignment: expected ./protocols_definition.py.Template3, got ./protocols_definition.py.Concrete3_Bad5 [incompatible_assignment] +./protocols_definition.py:218:0: Incompatible assignment: expected ./protocols_definition.py.Template4, got ./protocols_definition.py.Concrete4_Bad1 [incompatible_assignment] +./protocols_definition.py:219:0: Incompatible assignment: expected ./protocols_definition.py.Template4, got ./protocols_definition.py.Concrete4_Bad2 [incompatible_assignment] +./protocols_definition.py:285:0: Incompatible assignment: expected ./protocols_definition.py.Template5, got ./protocols_definition.py.Concrete5_Bad1 [incompatible_assignment] +./protocols_definition.py:286:0: Incompatible assignment: expected ./protocols_definition.py.Template5, got ./protocols_definition.py.Concrete5_Bad2 [incompatible_assignment] +./protocols_definition.py:287:0: Incompatible assignment: expected ./protocols_definition.py.Template5, got ./protocols_definition.py.Concrete5_Bad3 [incompatible_assignment] +./protocols_definition.py:288:0: Incompatible assignment: expected ./protocols_definition.py.Template5, got ./protocols_definition.py.Concrete5_Bad4 [incompatible_assignment] +./protocols_definition.py:289:0: Incompatible assignment: expected ./protocols_definition.py.Template5, got ./protocols_definition.py.Concrete5_Bad5 [incompatible_assignment] +./protocols_definition.py:339:0: Incompatible assignment: expected ./protocols_definition.py.Template6, got ./protocols_definition.py.Concrete6_Bad1 [incompatible_assignment] +./protocols_definition.py:340:0: Incompatible assignment: expected ./protocols_definition.py.Template6, got ./protocols_definition.py.Concrete6_Bad2 [incompatible_assignment] +./protocols_definition.py:341:0: Incompatible assignment: expected ./protocols_definition.py.Template6, got ./protocols_definition.py.Concrete6_Bad3 [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/protocols_explicit.toml b/conformance/results/pycroscope/protocols_explicit.toml new file mode 100644 index 000000000..e0bf74a47 --- /dev/null +++ b/conformance/results/pycroscope/protocols_explicit.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./protocols_explicit.py:27:15: Call to abstract protocol member via super() has no default implementation [bad_super_call] +./protocols_explicit.py:56:8: Incompatible types in assignment to attribute 'rgb': expected tuple[int, int, int], got tuple[int, int, str] [incompatible_assignment] +./protocols_explicit.py:60:4: Cannot instantiate abstract class Point [incompatible_call] +./protocols_explicit.py:89:5: Cannot instantiate abstract class Concrete1 [incompatible_call] +./protocols_explicit.py:134:5: Cannot instantiate abstract class Concrete5 [incompatible_call] +./protocols_explicit.py:164:6: Cannot instantiate abstract class Concrete7A [incompatible_call] +""" diff --git a/conformance/results/pycroscope/protocols_generic.toml b/conformance/results/pycroscope/protocols_generic.toml new file mode 100644 index 000000000..9882a5578 --- /dev/null +++ b/conformance/results/pycroscope/protocols_generic.toml @@ -0,0 +1,21 @@ +conformant = "Partial" +notes = """ +Fails to reject duplicate generic/protocol bases. +Treats global object as a literal. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 44: Expected 1 errors +Line 96: Unexpected errors ['./protocols_generic.py:96:12: Literal[] is not equivalent to protocols_generic.ConcreteHasParent'] +""" +output = """ +./protocols_generic.py:40:0: Incompatible assignment: expected protocols_generic.Proto1[int, str], got protocols_generic.Concrete1 [incompatible_assignment] +./protocols_generic.py:56:4: Incompatible assignment: expected protocols_generic.Box[int], got protocols_generic.Box[float | int] [incompatible_assignment] +./protocols_generic.py:66:4: Incompatible assignment: expected protocols_generic.Sender[float | int], got protocols_generic.Sender[int] [incompatible_assignment] +./protocols_generic.py:74:4: Incompatible assignment: expected protocols_generic.AttrProto[float | int], got protocols_generic.AttrProto[int] [incompatible_assignment] +./protocols_generic.py:75:4: Incompatible assignment: expected protocols_generic.AttrProto[int], got protocols_generic.AttrProto[float | int] [incompatible_assignment] +./protocols_generic.py:96:12: Literal[] is not equivalent to protocols_generic.ConcreteHasParent +./protocols_generic.py:145:0: Incompatible assignment: expected protocols_generic.HasPropertyProto, got protocols_generic.ConcreteHasProperty2 [incompatible_assignment] +./protocols_generic.py:146:0: Incompatible assignment: expected protocols_generic.HasPropertyProto, got protocols_generic.ConcreteHasProperty3 [incompatible_assignment] +./protocols_generic.py:147:0: Incompatible assignment: expected protocols_generic.HasPropertyProto, got protocols_generic.ConcreteHasProperty4 [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/protocols_merging.toml b/conformance/results/pycroscope/protocols_merging.toml new file mode 100644 index 000000000..78edb0b62 --- /dev/null +++ b/conformance/results/pycroscope/protocols_merging.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./protocols_merging.py:52:0: Incompatible assignment: expected ./protocols_merging.py.SizedAndClosable1, got ./protocols_merging.py.SCConcrete2 [incompatible_assignment] +./protocols_merging.py:53:0: Incompatible assignment: expected ./protocols_merging.py.SizedAndClosable2, got ./protocols_merging.py.SCConcrete2 [incompatible_assignment] +./protocols_merging.py:54:0: Incompatible assignment: expected ./protocols_merging.py.SizedAndClosable3, got ./protocols_merging.py.SCConcrete2 [incompatible_assignment] +./protocols_merging.py:67:15: Protocols can only inherit from protocol bases [invalid_base] +./protocols_merging.py:82:4: Cannot instantiate abstract class SizedAndClosable4 [incompatible_call] +./protocols_merging.py:83:0: Incompatible assignment: expected ./protocols_merging.py.SizedAndClosable4, got ./protocols_merging.py.SCConcrete1 [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/protocols_modules.toml b/conformance/results/pycroscope/protocols_modules.toml new file mode 100644 index 000000000..21a190037 --- /dev/null +++ b/conformance/results/pycroscope/protocols_modules.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./protocols_modules.py:26:0: Incompatible assignment: expected protocols_modules.Options2, got module '_protocols_modules1' [incompatible_assignment] +./protocols_modules.py:48:0: Incompatible assignment: expected protocols_modules.Reporter2, got module '_protocols_modules2' [incompatible_assignment] +./protocols_modules.py:49:0: Incompatible assignment: expected protocols_modules.Reporter3, got module '_protocols_modules2' [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/protocols_recursive.toml b/conformance/results/pycroscope/protocols_recursive.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/protocols_recursive.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/protocols_runtime_checkable.toml b/conformance/results/pycroscope/protocols_runtime_checkable.toml new file mode 100644 index 000000000..adc077e7e --- /dev/null +++ b/conformance/results/pycroscope/protocols_runtime_checkable.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./protocols_runtime_checkable.py:23:21: Second argument to "isinstance" cannot be a protocol that is not @runtime_checkable [incompatible_argument] +./protocols_runtime_checkable.py:55:21: Second argument to "issubclass" cannot be a runtime-checkable protocol with non-method members [incompatible_argument] +./protocols_runtime_checkable.py:61:21: Second argument to "issubclass" cannot be a runtime-checkable protocol with non-method members [incompatible_argument] +./protocols_runtime_checkable.py:88:18: First argument to "isinstance" has unsafe overlap between 'Concrete3A' and protocol 'Proto3' [incompatible_argument] +./protocols_runtime_checkable.py:91:18: First argument to "isinstance" has unsafe overlap between 'Concrete3B' and protocol 'Proto3' [incompatible_argument] +./protocols_runtime_checkable.py:94:18: First argument to "issubclass" has unsafe overlap between 'Concrete3A' and protocol 'Proto3' [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/protocols_self.toml b/conformance/results/pycroscope/protocols_self.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/protocols_self.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/protocols_subtyping.toml b/conformance/results/pycroscope/protocols_subtyping.toml new file mode 100644 index 000000000..3e0ba0a35 --- /dev/null +++ b/conformance/results/pycroscope/protocols_subtyping.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./protocols_subtyping.py:16:5: Cannot instantiate protocol class Proto1 [incompatible_call] +./protocols_subtyping.py:38:4: Incompatible assignment: expected ./protocols_subtyping.py.Concrete2, got ./protocols_subtyping.py.Proto2 [incompatible_assignment] +./protocols_subtyping.py:55:4: Incompatible assignment: expected ./protocols_subtyping.py.Proto3, got ./protocols_subtyping.py.Proto2 [incompatible_assignment] +./protocols_subtyping.py:79:4: Incompatible assignment: expected ./protocols_subtyping.py.Proto4[int, float | int], got ./protocols_subtyping.py.Proto5[int] [incompatible_assignment] +./protocols_subtyping.py:80:4: Incompatible assignment: expected ./protocols_subtyping.py.Proto5[float | int], got ./protocols_subtyping.py.Proto4[int, int] [incompatible_assignment] +./protocols_subtyping.py:102:4: Incompatible assignment: expected ./protocols_subtyping.py.Proto7[int, float | int], got ./protocols_subtyping.py.Proto6[float | int, float | int] [incompatible_assignment] +./protocols_subtyping.py:103:4: Incompatible assignment: expected ./protocols_subtyping.py.Proto7[float | int, object], got ./protocols_subtyping.py.Proto6[float | int, float | int] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/protocols_variance.toml b/conformance/results/pycroscope/protocols_variance.toml new file mode 100644 index 000000000..70d6e1c4b --- /dev/null +++ b/conformance/results/pycroscope/protocols_variance.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./protocols_variance.py:21:0: T1 should be covariant [invalid_protocol] +./protocols_variance.py:40:0: T3 should be contravariant [invalid_protocol] +./protocols_variance.py:56:0: T1 should be contravariant [invalid_protocol] +./protocols_variance.py:61:0: T1_co should be contravariant [invalid_protocol] +./protocols_variance.py:66:0: T1 should be covariant [invalid_protocol] +./protocols_variance.py:71:0: T1_contra should be covariant [invalid_protocol] +./protocols_variance.py:104:0: T1 should be covariant [invalid_protocol] +""" diff --git a/conformance/results/pycroscope/qualifiers_annotated.toml b/conformance/results/pycroscope/qualifiers_annotated.toml new file mode 100644 index 000000000..97000ee40 --- /dev/null +++ b/conformance/results/pycroscope/qualifiers_annotated.toml @@ -0,0 +1,32 @@ +conformant = "Partial" +notes = """ +Fails to reject various weird annotations. +False positive on lambda in Annotated. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 42: Expected 1 errors +Line 43: Expected 1 errors +Line 44: Expected 1 errors +Line 49: Expected 1 errors +Line 88: Expected 1 errors +Line 55: Unexpected errors ['./qualifiers_annotated.py:55:46: Type parameter is not valid in this annotation context [invalid_annotation]'] +""" +output = """ +./qualifiers_annotated.py:38:6: Invalid type annotation [, ] [invalid_annotation] +./qualifiers_annotated.py:39:6: Invalid type annotation ((, ),) [invalid_annotation] +./qualifiers_annotated.py:40:6: Unrecognized annotation [invalid_annotation] +./qualifiers_annotated.py:41:6: Invalid type annotation {'a': 'b'} [invalid_annotation] +./qualifiers_annotated.py:45:16: Undefined name: var1 [undefined_name] +./qualifiers_annotated.py:46:6: Invalid type annotation True [invalid_annotation] +./qualifiers_annotated.py:47:7: Invalid type annotation 1 [invalid_annotation] +./qualifiers_annotated.py:48:17: Annotated[type 'list', (<+list is_truthy None> == NullConstraint())] is always True because it does not provide __bool__ [type_always_true] +./qualifiers_annotated.py:55:46: Type parameter is not valid in this annotation context [invalid_annotation] +./qualifiers_annotated.py:59:7: Annotated[] requires at least two arguments [invalid_annotation] +./qualifiers_annotated.py:71:0: Incompatible assignment: expected type, got Literal[typing.Annotated[int, '']] [incompatible_assignment] +./qualifiers_annotated.py:72:0: Incompatible assignment: expected type, got TypeForm[.SmallInt] (synthetic from Literal[typing.Annotated[int, '']]) [incompatible_assignment] +./qualifiers_annotated.py:79:6: Incompatible argument type for x: expected type[~T@./qualifiers_annotated.py.func4..func4] but got Literal[typing.Annotated[str, '']] [incompatible_argument] +./qualifiers_annotated.py:80:6: Incompatible argument type for x: expected type[~T@./qualifiers_annotated.py.func4..func4] but got TypeForm[.SmallInt] (synthetic from Literal[typing.Annotated[int, '']]) [incompatible_argument] +./qualifiers_annotated.py:86:0: Annotated cannot be called [not_callable] +./qualifiers_annotated.py:87:0: Annotated cannot be called [not_callable] +""" diff --git a/conformance/results/pycroscope/qualifiers_final_annotation.toml b/conformance/results/pycroscope/qualifiers_final_annotation.toml new file mode 100644 index 000000000..07fc0d812 --- /dev/null +++ b/conformance/results/pycroscope/qualifiers_final_annotation.toml @@ -0,0 +1,33 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./qualifiers_final_annotation.py:16:6: Final annotation without assignment requires an explicit type [invalid_qualifier] +./qualifiers_final_annotation.py:18:6: Invalid type annotation (, ) [invalid_annotation] +./qualifiers_final_annotation.py:34:9: Final annotation without assignment requires an explicit type [invalid_qualifier] +./qualifiers_final_annotation.py:34:4: Final class attributes without initializers must be assigned in __init__ [invalid_qualifier] +./qualifiers_final_annotation.py:38:4: Final class attributes without initializers must be assigned in __init__ [invalid_qualifier] +./qualifiers_final_annotation.py:54:8: Cannot assign to final name ID5 [incompatible_assignment] +./qualifiers_final_annotation.py:62:18: Final instance attributes may be declared only in __init__ [invalid_qualifier] +./qualifiers_final_annotation.py:63:18: Final instance attributes may be declared only in __init__ [invalid_qualifier] +./qualifiers_final_annotation.py:65:8: Cannot assign to final name ID7 [incompatible_assignment] +./qualifiers_final_annotation.py:67:8: Cannot assign to final name ID7 [incompatible_assignment] +./qualifiers_final_annotation.py:71:0: Cannot assign to final name RATE [incompatible_assignment] +./qualifiers_final_annotation.py:81:0: Cannot assign to final name DEFAULT_ID [incompatible_assignment] +./qualifiers_final_annotation.py:94:4: Cannot override final attribute BORDER_WIDTH [incompatible_override] +./qualifiers_final_annotation.py:107:12: Final cannot be combined with ClassVar [invalid_qualifier] +./qualifiers_final_annotation.py:108:12: Final cannot be combined with ClassVar [invalid_qualifier] +./qualifiers_final_annotation.py:118:3: Unrecognized annotation typing.Final[] [invalid_annotation] +./qualifiers_final_annotation.py:121:13: Unexpected Final annotation [invalid_qualifier] +./qualifiers_final_annotation.py:134:0: In call to pycroscope.signature.N: Missing required argument 'x' [incompatible_call] +./qualifiers_final_annotation.py:135:4: Incompatible argument type for x: expected int but got Literal[''] [incompatible_argument] +./qualifiers_final_annotation.py:135:10: Incompatible argument type for y: expected int but got Literal[''] [incompatible_argument] +./qualifiers_final_annotation.py:141:4: Cannot assign to final name ID1 [incompatible_assignment] +./qualifiers_final_annotation.py:145:4: Cannot assign to final name x [incompatible_assignment] +./qualifiers_final_annotation.py:147:9: Cannot assign to final name x [incompatible_assignment] +./qualifiers_final_annotation.py:149:8: Cannot assign to final name x [incompatible_assignment] +./qualifiers_final_annotation.py:152:29: Cannot assign to final name x [incompatible_assignment] +./qualifiers_final_annotation.py:155:8: Cannot assign to final name x [incompatible_assignment] +./qualifiers_final_annotation.py:166:0: Cannot assign to final name TEN [incompatible_assignment] +./qualifiers_final_annotation.py:170:0: Cannot assign to final name PI [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/qualifiers_final_decorator.toml b/conformance/results/pycroscope/qualifiers_final_decorator.toml new file mode 100644 index 000000000..fc2652a74 --- /dev/null +++ b/conformance/results/pycroscope/qualifiers_final_decorator.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./qualifiers_final_decorator.py:21:15: Cannot inherit from final class [invalid_base] +./qualifiers_final_decorator.py:56:4: Cannot override final attribute method1 [incompatible_override] +./qualifiers_final_decorator.py:60:4: Cannot override final attribute method2 [incompatible_override] +./qualifiers_final_decorator.py:64:4: Cannot override final attribute method3 [incompatible_override] +./qualifiers_final_decorator.py:75:4: Cannot override final attribute method4 [incompatible_override] +./qualifiers_final_decorator.py:86:4: @final should be applied only to the overload implementation [invalid_final] +./qualifiers_final_decorator.py:89:4: Cannot override final attribute method [incompatible_override] +./qualifiers_final_decorator.py:102:4: Cannot override final attribute method [incompatible_override] +./qualifiers_final_decorator.py:118:4: Cannot override final attribute method [incompatible_override] +./qualifiers_final_decorator.py:126:0: @final is not allowed on non-method functions [invalid_final] +""" diff --git a/conformance/results/pycroscope/specialtypes_any.toml b/conformance/results/pycroscope/specialtypes_any.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/specialtypes_any.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/specialtypes_never.toml b/conformance/results/pycroscope/specialtypes_never.toml new file mode 100644 index 000000000..e6f61291a --- /dev/null +++ b/conformance/results/pycroscope/specialtypes_never.toml @@ -0,0 +1,17 @@ +conformant = "Partial" +notes = """ +Does not enforce invariance in some contexts +Does not allow Any to be assigned to Never +""" +conformance_automated = "Fail" +errors_diff = """ +Line 104: Expected 1 errors +Line 51: Unexpected errors ['./specialtypes_never.py:51:4: Incompatible return type: expected list[Never], got Literal[[]] [incompatible_return_value]'] +Line 84: Unexpected errors ['./specialtypes_never.py:84:4: Incompatible assignment: expected Never, got Any[explicit] [incompatible_assignment]'] +""" +output = """ +./specialtypes_never.py:19:0: Function is annotated as NoReturn but may return [no_return_may_return] +./specialtypes_never.py:51:4: Incompatible return type: expected list[Never], got Literal[[]] [incompatible_return_value] +./specialtypes_never.py:84:4: Incompatible assignment: expected Never, got Any[explicit] [incompatible_assignment] +./specialtypes_never.py:85:4: Incompatible assignment: expected list[int], got list[Never] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/specialtypes_none.toml b/conformance/results/pycroscope/specialtypes_none.toml new file mode 100644 index 000000000..f3684f779 --- /dev/null +++ b/conformance/results/pycroscope/specialtypes_none.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./specialtypes_none.py:21:6: Incompatible argument type for val1: expected None but got type 'NoneType' [incompatible_argument] +./specialtypes_none.py:27:0: Incompatible assignment: expected collections.abc.Iterable, got None [incompatible_assignment] +./specialtypes_none.py:41:6: Incompatible argument type for val1: expected type[NoneType] but got None [incompatible_argument] +""" diff --git a/conformance/results/pycroscope/specialtypes_promotions.toml b/conformance/results/pycroscope/specialtypes_promotions.toml new file mode 100644 index 000000000..8e32e247b --- /dev/null +++ b/conformance/results/pycroscope/specialtypes_promotions.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./specialtypes_promotions.py:13:4: float has no attribute 'numerator' [undefined_attribute] +""" diff --git a/conformance/results/pycroscope/specialtypes_type.toml b/conformance/results/pycroscope/specialtypes_type.toml new file mode 100644 index 000000000..3bfe1708c --- /dev/null +++ b/conformance/results/pycroscope/specialtypes_type.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./specialtypes_type.py:56:6: Incompatible argument type for user_class: expected type[./specialtypes_type.py.BasicUser] | type[./specialtypes_type.py.ProUser] but got [incompatible_argument] +./specialtypes_type.py:70:6: Incompatible argument type for x: expected type[~T@./specialtypes_type.py.func5..func5] but got Literal[typing.Callable] [incompatible_argument] +./specialtypes_type.py:76:11: Type[] requires a single argument [invalid_annotation] +./specialtypes_type.py:117:4: type[object] has no attribute 'unknown' [undefined_attribute] +./specialtypes_type.py:120:4: type[object] has no attribute 'unknown' [undefined_attribute] +./specialtypes_type.py:143:0: Literal[typing.Type] has no attribute 'unknown' [undefined_attribute] +./specialtypes_type.py:144:0: Literal[typing.Type[typing.Any]] has no attribute 'unknown' [undefined_attribute] +./specialtypes_type.py:145:0: type 'type' has no attribute 'unknown' [undefined_attribute] +./specialtypes_type.py:146:0: Literal[type[typing.Any]] has no attribute 'unknown' [undefined_attribute] +""" diff --git a/conformance/results/pycroscope/tuples_type_compat.toml b/conformance/results/pycroscope/tuples_type_compat.toml new file mode 100644 index 000000000..cefaf21ac --- /dev/null +++ b/conformance/results/pycroscope/tuples_type_compat.toml @@ -0,0 +1,29 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./tuples_type_compat.py:15:4: Incompatible assignment: expected tuple[int, int], got tuple[float | int, complex | float | int] [incompatible_assignment] +./tuples_type_compat.py:29:4: Incompatible assignment: expected tuple[int, *tuple[int, ...]], got tuple[int, ...] [incompatible_assignment] +./tuples_type_compat.py:32:4: Incompatible assignment: expected tuple[int], got tuple[int, *tuple[int, ...]] [incompatible_assignment] +./tuples_type_compat.py:33:4: Incompatible assignment: expected tuple[int], got tuple[int, ...] [incompatible_assignment] +./tuples_type_compat.py:43:4: Incompatible assignment: expected tuple[int], got tuple[int, ...] [incompatible_assignment] +./tuples_type_compat.py:62:4: Incompatible assignment: expected tuple[int, int], got tuple[int, ...] [incompatible_assignment] +./tuples_type_compat.py:76:20: tuple[int] is not equivalent to .Func5Input = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int] +./tuples_type_compat.py:81:20: tuple[str, str] | tuple[int, int] is not equivalent to .Func5Input = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int] +./tuples_type_compat.py:86:20: tuple[int, str, int] is not equivalent to .Func5Input = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int] +./tuples_type_compat.py:102:24: tuple[int] is not equivalent to .Func6Input = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int] +./tuples_type_compat.py:107:24: tuple[str, str] | tuple[int, int] is not equivalent to .Func6Input = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int] +./tuples_type_compat.py:112:24: tuple[int, str, int] is not equivalent to .Func6Input = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int] +./tuples_type_compat.py:126:24: .Func7Input = tuple[int | str, int | str] is not equivalent to tuple[int | str, str] +./tuples_type_compat.py:129:24: .Func7Input = tuple[int | str, int | str] is not equivalent to tuple[int | str, int] +./tuples_type_compat.py:157:0: Incompatible assignment: expected tuple[int, str], got Literal[(1, '', '')] [incompatible_assignment] +./tuples_type_compat.py:162:0: Incompatible assignment: expected tuple[int, *tuple[str, ...]], got Literal[(1, 1, '')] [incompatible_assignment] +./tuples_type_compat.py:163:0: Incompatible assignment: expected tuple[int, *tuple[str, ...]], got Literal[(1, '', 1)] [incompatible_assignment] +./tuples_type_compat.py:169:0: Incompatible assignment: expected tuple[int, *tuple[str, ...], int], got Literal[(1, '', '')] [incompatible_assignment] +./tuples_type_compat.py:170:0: Incompatible assignment: expected tuple[int, *tuple[str, ...], int], got Literal[(1, '', '', 1.2)] [incompatible_assignment] +./tuples_type_compat.py:175:0: Incompatible assignment: expected tuple[*tuple[str, ...], int], got Literal[(1, '', 1)] [incompatible_assignment] +./tuples_type_compat.py:176:0: Incompatible assignment: expected tuple[*tuple[str, ...], int], got Literal[('', '', 1.2)] [incompatible_assignment] +./tuples_type_compat.py:181:4: Incompatible assignment: expected tuple[str, str, int], got tuple[str, str] [incompatible_assignment] +./tuples_type_compat.py:184:4: Incompatible assignment: expected tuple[str, str, str, *tuple[str, ...]], got tuple[str, str] [incompatible_assignment] +./tuples_type_compat.py:188:4: Incompatible assignment: expected tuple[*tuple[str, ...], str, str, str], got tuple[str, str] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/tuples_type_form.toml b/conformance/results/pycroscope/tuples_type_form.toml new file mode 100644 index 000000000..021d00028 --- /dev/null +++ b/conformance/results/pycroscope/tuples_type_form.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./tuples_type_form.py:12:0: Incompatible assignment: expected tuple[int], got Literal[(1, 2)] [incompatible_assignment] +./tuples_type_form.py:14:0: Incompatible assignment: expected tuple[int, int], got Literal[(1,)] [incompatible_assignment] +./tuples_type_form.py:15:0: Incompatible assignment: expected tuple[int, int], got Literal[(1, '')] [incompatible_assignment] +./tuples_type_form.py:25:0: Incompatible assignment: expected tuple[()], got Literal[(1,)] [incompatible_assignment] +./tuples_type_form.py:36:0: Incompatible assignment: expected tuple[int, ...], got Literal[(1, 2, 3, '')] [incompatible_assignment] +./tuples_type_form.py:40:5: Ellipsis can be used only in tuple[T, ...] [invalid_annotation] +./tuples_type_form.py:41:5: Ellipsis can be used only in tuple[T, ...] [invalid_annotation] +./tuples_type_form.py:42:5: Ellipsis can be used only in tuple[T, ...] [invalid_annotation] +./tuples_type_form.py:43:5: Ellipsis can be used only in tuple[T, ...] [invalid_annotation] +./tuples_type_form.py:44:5: Ellipsis can be used only in tuple[T, ...] [invalid_annotation] +./tuples_type_form.py:45:5: Ellipsis can be used only in tuple[T, ...] [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/tuples_unpacked.toml b/conformance/results/pycroscope/tuples_unpacked.toml new file mode 100644 index 000000000..f7d1ae069 --- /dev/null +++ b/conformance/results/pycroscope/tuples_unpacked.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./tuples_unpacked.py:40:4: Only one unbounded tuple can be used inside a tuple type [invalid_annotation] +./tuples_unpacked.py:41:4: Only one unbounded tuple can be used inside a tuple type [invalid_annotation] +./tuples_unpacked.py:51:8: Only one unbounded tuple can be used inside a tuple type [invalid_annotation] +./tuples_unpacked.py:59:5: Only one unbounded tuple can be used inside a tuple type [invalid_annotation] +./tuples_unpacked.py:60:5: Only one unbounded tuple can be used inside a tuple type [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/typeddicts_alt_syntax.toml b/conformance/results/pycroscope/typeddicts_alt_syntax.toml new file mode 100644 index 000000000..a91070ecd --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_alt_syntax.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_alt_syntax.py:23:43: TypedDict fields argument must be a dictionary literal [incompatible_call] +./typeddicts_alt_syntax.py:27:44: TypedDict field names must be string literals [incompatible_call] +./typeddicts_alt_syntax.py:31:26: TypedDict named 'WrongName' must be assigned to a variable with the same name [must_have_same_name] +./typeddicts_alt_syntax.py:35:71: TypedDict takes either a dict or keyword arguments, but not both [incompatible_call] +./typeddicts_alt_syntax.py:45:0: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got Literal[{'name': 'Blade Runner', 'year': ''}] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/typeddicts_class_syntax.toml b/conformance/results/pycroscope/typeddicts_class_syntax.toml new file mode 100644 index 000000000..9e883248c --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_class_syntax.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_class_syntax.py:30:4: Methods are not allowed in TypedDict definitions [invalid_typeddict] +./typeddicts_class_syntax.py:35:4: Methods are not allowed in TypedDict definitions [invalid_typeddict] +./typeddicts_class_syntax.py:40:4: Methods are not allowed in TypedDict definitions [invalid_typeddict] +./typeddicts_class_syntax.py:45:31: TypedDict definitions cannot specify a metaclass [invalid_typeddict] +./typeddicts_class_syntax.py:50:31: Unexpected keyword argument 'other' in TypedDict definition [invalid_typeddict] +./typeddicts_class_syntax.py:65:0: Got an unexpected keyword argument 'z' [incompatible_call] +""" diff --git a/conformance/results/pycroscope/typeddicts_extra_items.toml b/conformance/results/pycroscope/typeddicts_extra_items.toml new file mode 100644 index 000000000..fa51d53a8 --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_extra_items.toml @@ -0,0 +1,33 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_extra_items.py:15:0: Incompatible assignment: expected TypedDict({"name": str, "__extra_items__": bool}, closed=True), got Literal[{'name': 'Blade Runner', 'year': 1982}] [incompatible_assignment] +./typeddicts_extra_items.py:22:0: Incompatible assignment: expected TypedDict({"name": str, "__extra_items__": bool}, closed=True), got Literal[{'name': 'Blade Runner', 'year': 1982}] [incompatible_assignment] +./typeddicts_extra_items.py:39:0: Incompatible assignment: expected TypedDict({"name": str, "year": int, "__extra_items__": ReadOnly[int | None]}, closed=True), got Literal[{'name': 'Blade Runner', 'year': None}] [incompatible_assignment] +./typeddicts_extra_items.py:49:34: Argument to "closed" must be a literal True or False [invalid_typeddict] +./typeddicts_extra_items.py:67:0: Cannot set 'closed=False' when superclass is closed or has 'extra_items' [invalid_typeddict] +./typeddicts_extra_items.py:73:0: Cannot set 'closed=False' when superclass is closed or has 'extra_items' [invalid_typeddict] +./typeddicts_extra_items.py:92:4: "MovieC" is a closed TypedDict; extra key 'age' not allowed [invalid_typeddict] +./typeddicts_extra_items.py:95:4: "MovieD" is a closed TypedDict; extra key 'age' not allowed [invalid_typeddict] +./typeddicts_extra_items.py:109:0: Cannot set 'closed=True' when superclass has non-read-only 'extra_items' [invalid_typeddict] +./typeddicts_extra_items.py:114:49: 'extra_items' value cannot be 'Required[...]' [invalid_qualifier] +./typeddicts_extra_items.py:117:56: 'extra_items' value cannot be 'NotRequired[...]' [invalid_qualifier] +./typeddicts_extra_items.py:128:14: Cannot delete required TypedDict key Literal['name'] [incompatible_argument] +./typeddicts_extra_items.py:143:0: Got an unexpected keyword argument 'year' [incompatible_call] +./typeddicts_extra_items.py:174:0: Cannot change 'extra_items' type unless it is 'ReadOnly' in the superclass [invalid_typeddict] +./typeddicts_extra_items.py:185:4: Required key 'year' is not known to base TypedDict [invalid_typeddict] +./typeddicts_extra_items.py:188:4: int is not consistent with 'extra_items' type int | None [invalid_typeddict] +./typeddicts_extra_items.py:197:4: str is not assignable to 'extra_items' type int | None [invalid_typeddict] +./typeddicts_extra_items.py:215:0: Incompatible assignment: expected TypedDict({"name": str, "__extra_items__": int | None}, closed=True), got TypedDict({"name": str, "year": NotRequired[int], "__extra_items__": int | None}, closed=True) [incompatible_assignment] +./typeddicts_extra_items.py:222:0: Incompatible assignment: expected TypedDict({"name": str, "__extra_items__": int | None}, closed=True), got TypedDict({"name": str, "year": int | None, "__extra_items__": int | None}, closed=True) [incompatible_assignment] +./typeddicts_extra_items.py:242:0: Incompatible assignment: expected TypedDict({"name": str, "__extra_items__": ReadOnly[str | int]}, closed=True), got TypedDict({"name": str, "actors": list[str], "__extra_items__": int}, closed=True) [incompatible_assignment] +./typeddicts_extra_items.py:256:0: Incompatible assignment: expected TypedDict({"name": str, "__extra_items__": int}, closed=True), got TypedDict({"name": str, "__extra_items__": str}, closed=True) [incompatible_assignment] +./typeddicts_extra_items.py:257:0: Incompatible assignment: expected TypedDict({"name": str, "__extra_items__": str}, closed=True), got TypedDict({"name": str, "__extra_items__": int}, closed=True) [incompatible_assignment] +./typeddicts_extra_items.py:268:0: Incompatible assignment: expected TypedDict({"name": str, "__extra_items__": int}, closed=True), got TypedDict({"name": str}) [incompatible_assignment] +./typeddicts_extra_items.py:278:0: Got an unexpected keyword argument 'year' [incompatible_call] +./typeddicts_extra_items.py:285:0: Incompatible argument type for %kwargs: expected dict[str, int] but got [incompatible_argument] +./typeddicts_extra_items.py:293:0: Incompatible argument type for %kwargs: expected dict[str, Never] but got [incompatible_argument] +./typeddicts_extra_items.py:303:0: Incompatible assignment: expected collections.abc.Mapping[str, int], got TypedDict({"name": str, "__extra_items__": int}, closed=True) [incompatible_assignment] +./typeddicts_extra_items.py:352:4: Incompatible assignment: expected TypedDict({"__extra_items__": int}, closed=True), got dict[str, int] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/typeddicts_final.toml b/conformance/results/pycroscope/typeddicts_final.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_final.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pycroscope/typeddicts_inheritance.toml b/conformance/results/pycroscope/typeddicts_inheritance.toml new file mode 100644 index 000000000..6dace629a --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_inheritance.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_inheritance.py:44:30: TypedDict classes may only inherit from TypedDict types and Generic [invalid_base] +./typeddicts_inheritance.py:55:3: Incompatible TypedDict override for key 'x' [invalid_typeddict] +./typeddicts_inheritance.py:65:0: TypedDict base classes define key 'x' with incompatible types [invalid_base] +""" diff --git a/conformance/results/pycroscope/typeddicts_operations.toml b/conformance/results/pycroscope/typeddicts_operations.toml new file mode 100644 index 000000000..20850460c --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_operations.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_operations.py:22:0: Value for key 'name' must be str, not Literal[1982] [incompatible_argument] +./typeddicts_operations.py:23:0: Value for key 'year' must be int, not Literal[''] [incompatible_argument] +./typeddicts_operations.py:24:6: Key 'other' does not exist in TypedDict({"name": str, "year": int}) [invalid_typeddict_key] +./typeddicts_operations.py:26:12: Unknown TypedDict key 'other' [invalid_typeddict_key] +./typeddicts_operations.py:28:0: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got Literal[{'name': 'Blade Runner'}] [incompatible_assignment] +./typeddicts_operations.py:29:0: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got Literal[{'name': 'Blade Runner', 'year': 1982.1}] [incompatible_assignment] +./typeddicts_operations.py:32:0: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got Literal[{'name': '', 'year': 1900, 'other': 2}] [incompatible_assignment] +./typeddicts_operations.py:37:4: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got [incompatible_assignment] +./typeddicts_operations.py:44:0: Unknown TypedDict key 'other' [invalid_typeddict_key] +./typeddicts_operations.py:47:0: Cannot call clear() on non-closed TypedDict [incompatible_call] +./typeddicts_operations.py:49:10: Cannot delete required TypedDict key Literal['name'] [incompatible_argument] +./typeddicts_operations.py:62:0: Cannot call clear() on non-closed TypedDict [incompatible_call] +""" diff --git a/conformance/results/pycroscope/typeddicts_readonly.toml b/conformance/results/pycroscope/typeddicts_readonly.toml new file mode 100644 index 000000000..51ab83000 --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_readonly.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_readonly.py:24:3: Cannot set readonly key 'members' in TypedDict TypedDict({"name": str, "members": Readonly[list[str]]}) [readonly_typeddict] +./typeddicts_readonly.py:36:3: Cannot set readonly key 'members' in TypedDict TypedDict({"name": str, "members": Readonly[list[str]]}) [readonly_typeddict] +./typeddicts_readonly.py:50:3: Cannot set readonly key 'title' in TypedDict TypedDict({"title": Readonly[str], "year": NotRequired[Readonly[int]]}) [readonly_typeddict] +./typeddicts_readonly.py:51:3: Cannot set readonly key 'year' in TypedDict TypedDict({"title": Readonly[str], "year": NotRequired[Readonly[int]]}) [readonly_typeddict] +./typeddicts_readonly.py:60:3: Cannot set readonly key 'title' in TypedDict TypedDict({"title": Readonly[str], "year": NotRequired[Readonly[int]]}) [readonly_typeddict] +./typeddicts_readonly.py:61:3: Cannot set readonly key 'year' in TypedDict TypedDict({"title": Readonly[str], "year": NotRequired[Readonly[int]]}) [readonly_typeddict] +""" diff --git a/conformance/results/pycroscope/typeddicts_readonly_consistency.toml b/conformance/results/pycroscope/typeddicts_readonly_consistency.toml new file mode 100644 index 000000000..3fd93c951 --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_readonly_consistency.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_readonly_consistency.py:37:4: Incompatible assignment: expected TypedDict({"x": int, "y": NotRequired[str]}), got TypedDict({"x": int}) [incompatible_assignment] +./typeddicts_readonly_consistency.py:38:4: Incompatible assignment: expected TypedDict({"x": int, "y": NotRequired[str]}), got TypedDict({"x": int, "y": NotRequired[Readonly[str]]}) [incompatible_assignment] +./typeddicts_readonly_consistency.py:40:4: Incompatible assignment: expected TypedDict({"x": int, "y": NotRequired[Readonly[str]]}), got TypedDict({"x": int}) [incompatible_assignment] +./typeddicts_readonly_consistency.py:81:4: Incompatible assignment: expected TypedDict({"x": NotRequired[str]}), got TypedDict({"x": NotRequired[Readonly[str]]}) [incompatible_assignment] +./typeddicts_readonly_consistency.py:82:4: Incompatible assignment: expected TypedDict({"x": NotRequired[str]}), got TypedDict({"x": str}) [incompatible_assignment] +./typeddicts_readonly_consistency.py:84:4: Incompatible assignment: expected TypedDict({"x": str}), got TypedDict({"x": NotRequired[Readonly[str]]}) [incompatible_assignment] +./typeddicts_readonly_consistency.py:85:4: Incompatible assignment: expected TypedDict({"x": str}), got TypedDict({"x": NotRequired[str]}) [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/typeddicts_readonly_inheritance.toml b/conformance/results/pycroscope/typeddicts_readonly_inheritance.toml new file mode 100644 index 000000000..f04b52a05 --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_readonly_inheritance.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_readonly_inheritance.py:36:3: Cannot set readonly key 'name' in TypedDict TypedDict({"name": Readonly[str], "year": int}) [readonly_typeddict] +./typeddicts_readonly_inheritance.py:50:4: Incompatible TypedDict override for key 'alt' [invalid_typeddict] +./typeddicts_readonly_inheritance.py:65:0: Incompatible assignment: expected TypedDict({"name": Readonly[str]}), got Literal[{}] [incompatible_assignment] +./typeddicts_readonly_inheritance.py:82:0: Value for key 'ident' must be str, not Literal[3] [incompatible_argument] +./typeddicts_readonly_inheritance.py:83:0: Incompatible assignment: expected TypedDict({"ident": str}), got Literal[{'ident': 3}] [incompatible_assignment] +./typeddicts_readonly_inheritance.py:84:0: Incompatible assignment: expected TypedDict({"ident": str}), got Literal[{}] [incompatible_assignment] +./typeddicts_readonly_inheritance.py:94:4: Incompatible TypedDict override for key 'a' [invalid_typeddict] +./typeddicts_readonly_inheritance.py:98:4: Incompatible TypedDict override for key 'a' [invalid_typeddict] +./typeddicts_readonly_inheritance.py:106:4: Incompatible TypedDict override for key 'c' [invalid_typeddict] +./typeddicts_readonly_inheritance.py:119:0: TypedDict base classes define key 'x' with incompatible types [invalid_base] +./typeddicts_readonly_inheritance.py:132:0: TypedDict base classes define key 'x' with incompatible requiredness [invalid_base] +""" diff --git a/conformance/results/pycroscope/typeddicts_readonly_kwargs.toml b/conformance/results/pycroscope/typeddicts_readonly_kwargs.toml new file mode 100644 index 000000000..af09a4abb --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_readonly_kwargs.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_readonly_kwargs.py:33:11: Cannot set readonly key 'key1' in TypedDict TypedDict({"key1": Readonly[int], "key2": Readonly[str]}) [readonly_typeddict] +""" diff --git a/conformance/results/pycroscope/typeddicts_readonly_update.toml b/conformance/results/pycroscope/typeddicts_readonly_update.toml new file mode 100644 index 000000000..f7e82a4ba --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_readonly_update.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_readonly_update.py:23:0: Cannot set readonly key 'x' in TypedDict TypedDict({"x": Readonly[int], "y": int}) [readonly_typeddict] +""" diff --git a/conformance/results/pycroscope/typeddicts_required.toml b/conformance/results/pycroscope/typeddicts_required.toml new file mode 100644 index 000000000..47bf31ca8 --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_required.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_required.py:12:7: Unexpected Required annotation [invalid_qualifier] +./typeddicts_required.py:16:7: Unexpected NotRequired annotation [invalid_qualifier] +./typeddicts_required.py:59:16: Required[] cannot be nested [invalid_qualifier] +./typeddicts_required.py:60:7: Required[] and NotRequired[] cannot be nested [invalid_qualifier] +""" diff --git a/conformance/results/pycroscope/typeddicts_type_consistency.toml b/conformance/results/pycroscope/typeddicts_type_consistency.toml new file mode 100644 index 000000000..62d6661c8 --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_type_consistency.toml @@ -0,0 +1,18 @@ +conformant = "Partial" +notes = """ +Considers TypedDicts to be assignable to plain dict types. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 76: Expected 1 errors +Line 78: Expected 1 errors +Line 82: Expected 1 errors +""" +output = """ +./typeddicts_type_consistency.py:21:0: Incompatible assignment: expected TypedDict({"x": int | None}), got TypedDict({"x": int}) [incompatible_assignment] +./typeddicts_type_consistency.py:38:0: Incompatible assignment: expected TypedDict({"x": NotRequired[int]}), got TypedDict({"x": int}) [incompatible_assignment] +./typeddicts_type_consistency.py:65:0: Incompatible assignment: expected TypedDict({"x": int, "y": int}), got TypedDict({"x": int}) [incompatible_assignment] +./typeddicts_type_consistency.py:69:0: Incompatible assignment: expected TypedDict({"x": int}), got Literal[{'x': 0, 'y': 0}] [incompatible_assignment] +./typeddicts_type_consistency.py:77:0: Incompatible assignment: expected dict[str, object], got TypedDict({"x": int, "y": int}) [incompatible_assignment] +./typeddicts_type_consistency.py:126:0: Incompatible assignment: expected TypedDict({"outer_key": TypedDict({"inner_key": TypedDict({"inner_key": str})})}), got Literal[{'outer_key': {'inner_key': {'inner_key': 1}}}] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/typeddicts_usage.toml b/conformance/results/pycroscope/typeddicts_usage.toml new file mode 100644 index 000000000..0c836ae99 --- /dev/null +++ b/conformance/results/pycroscope/typeddicts_usage.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +./typeddicts_usage.py:23:6: Key 'director' does not exist in TypedDict({"name": str, "year": int}) [invalid_typeddict_key] +./typeddicts_usage.py:24:0: Value for key 'year' must be int, not Literal['1982'] [incompatible_argument] +./typeddicts_usage.py:28:0: Incompatible assignment: expected TypedDict({"name": str, "year": int}), got Literal[{'title': 'Blade Runner', 'year': 1982}] [incompatible_assignment] +./typeddicts_usage.py:35:21: Second argument to "isinstance" cannot be a TypedDict [incompatible_argument] +./typeddicts_usage.py:40:23: TypedDict cannot be used as a TypeVar bound [invalid_annotation] +""" diff --git a/conformance/results/pycroscope/typeforms_typeform.toml b/conformance/results/pycroscope/typeforms_typeform.toml new file mode 100644 index 000000000..01e46a0a1 --- /dev/null +++ b/conformance/results/pycroscope/typeforms_typeform.toml @@ -0,0 +1,25 @@ +conformant = "Partial" +notes = """ +Fails to reject various weird annotations +""" +conformance_automated = "Fail" +errors_diff = """ +Line 88: Expected 1 errors +""" +output = """ +./typeforms_typeform.py:23:0: Incompatible assignment: expected TypeForm[str | None], got Literal[str | int] [incompatible_assignment] +./typeforms_typeform.py:24:0: Incompatible assignment: expected TypeForm[str | None], got types.GenericAlias (partial from type 'list'[Any[inference] (partial from type 'str' | None)]) [incompatible_assignment] +./typeforms_typeform.py:59:6: Incompatible argument type for x: expected TypeForm[Any[explicit]] but got Literal['not a type'] [incompatible_argument] +./typeforms_typeform.py:67:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal[()] [incompatible_assignment] +./typeforms_typeform.py:68:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal[(1, 2)] [incompatible_assignment] +./typeforms_typeform.py:69:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal[1] [incompatible_assignment] +./typeforms_typeform.py:70:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal[typing.Self] [incompatible_assignment] +./typeforms_typeform.py:71:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal[typing.ClassVar[int]] [incompatible_assignment] +./typeforms_typeform.py:72:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal[typing.Final[int]] [incompatible_assignment] +./typeforms_typeform.py:73:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal[typing.Unpack[Ts]] [incompatible_assignment] +./typeforms_typeform.py:74:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal[typing.Optional] [incompatible_assignment] +./typeforms_typeform.py:75:0: Incompatible assignment: expected TypeForm[Any[explicit]], got Literal['int + str'] [incompatible_assignment] +./typeforms_typeform.py:86:5: Unrecognized annotation type [invalid_annotation] +./typeforms_typeform.py:98:0: Incompatible assignment: expected TypeForm[str], got TypeForm[int] [incompatible_assignment] +./typeforms_typeform.py:108:0: Incompatible assignment: expected TypeForm[str], got type[int] [incompatible_assignment] +""" diff --git a/conformance/results/pycroscope/version.toml b/conformance/results/pycroscope/version.toml new file mode 100644 index 000000000..8060c0669 --- /dev/null +++ b/conformance/results/pycroscope/version.toml @@ -0,0 +1 @@ +version = "pycroscope 0.4.0" diff --git a/conformance/results/pyrefly/aliases_explicit.toml b/conformance/results/pyrefly/aliases_explicit.toml new file mode 100644 index 000000000..9edd1350a --- /dev/null +++ b/conformance/results/pyrefly/aliases_explicit.toml @@ -0,0 +1,29 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR aliases_explicit.py:67:9-28: `TypeAlias[GoodTypeAlias2, type[int | None]]` is not subscriptable [unsupported-operation] +ERROR aliases_explicit.py:68:9-28: `TypeAlias[GoodTypeAlias3, type[list[int | None]]]` is not subscriptable [unsupported-operation] +ERROR aliases_explicit.py:69:9-33: Expected 1 type argument for `GoodTypeAlias4`, got 2 [bad-specialization] +ERROR aliases_explicit.py:70:9-33: Expected 1 type argument for `GoodTypeAlias8`, got 2 [bad-specialization] +ERROR aliases_explicit.py:71:9-33: Expected a valid ParamSpec expression, got `int` [invalid-param-spec] +ERROR aliases_explicit.py:79:21-61: Function call cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:80:21-31: List literal cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:81:21-34: Tuple literal cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:82:21-44: List comprehension cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:83:21-31: Dict literal cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:83:23-24: Could not find name `a` [unknown-name] +ERROR aliases_explicit.py:83:28-29: Could not find name `b` [unknown-name] +ERROR aliases_explicit.py:84:21-36: Function call cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:85:21-29: Invalid subscript expression cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:86:21-42: If expression cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:87:21-25: Expected `BadTypeAlias9` to be a type alias, got `Literal[3]` [invalid-type-alias] +ERROR aliases_explicit.py:88:22-26: Bool literal cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:89:22-23: Number literal cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:90:22-33: Boolean operation cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:91:22-32: F-string cannot be used in annotations [invalid-annotation] +ERROR aliases_explicit.py:100:5-19: `TypeAlias[ListAlias, type[list[Unknown]]]` is not subscriptable [unsupported-operation] +ERROR aliases_explicit.py:101:6-20: Expected a callable, got `TypeAlias[ListOrSetAlias, type[list[Unknown] | set[Unknown]]]` [not-callable] +ERROR aliases_explicit.py:102:5-24: `TypeAlias[ListOrSetAlias, type[list[Unknown] | set[Unknown]]]` is not subscriptable [unsupported-operation] +""" diff --git a/conformance/results/pyrefly/aliases_implicit.toml b/conformance/results/pyrefly/aliases_implicit.toml new file mode 100644 index 000000000..b8dea415a --- /dev/null +++ b/conformance/results/pyrefly/aliases_implicit.toml @@ -0,0 +1,31 @@ +conformant = "Partial" +notes = """ +Does not reject invalid syntax in implicit type aliases. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 106: Expected 1 errors +Line 111: Expected 1 errors +Line 112: Expected 1 errors +Line 113: Expected 1 errors +Line 117: Expected 1 errors +""" +output = """ +ERROR aliases_implicit.py:76:9-28: `type[int | None]` is not subscriptable [unsupported-operation] +ERROR aliases_implicit.py:77:9-28: `type[list[GoodTypeAlias2]]` is not subscriptable [unsupported-operation] +ERROR aliases_implicit.py:78:9-33: Expected 1 type argument for `GoodTypeAlias4`, got 2 [bad-specialization] +ERROR aliases_implicit.py:79:9-33: Expected 1 type argument for `GoodTypeAlias8`, got 2 [bad-specialization] +ERROR aliases_implicit.py:80:9-33: Expected a valid ParamSpec expression, got `int` [invalid-param-spec] +ERROR aliases_implicit.py:81:9-29: `str` is not assignable to upper bound `float` of type variable `TFloat` [bad-specialization] +ERROR aliases_implicit.py:107:9-22: Expected a type form, got instance of `list[type[int] | type[str]]` [not-a-type] +ERROR aliases_implicit.py:108:9-22: Expected a type form, got instance of `tuple[tuple[type[int], type[str]]]` [not-a-type] +ERROR aliases_implicit.py:109:9-22: Expected a type form, got instance of `list[type[int]]` [not-a-type] +ERROR aliases_implicit.py:110:9-22: Expected a type form, got instance of `dict[str, str]` [not-a-type] +ERROR aliases_implicit.py:114:9-22: Expected a type form, got instance of `Literal[3]` [not-a-type] +ERROR aliases_implicit.py:115:10-24: Expected a type form, got instance of `Literal[True]` [not-a-type] +ERROR aliases_implicit.py:116:10-24: Expected a type form, got instance of `Literal[1]` [not-a-type] +ERROR aliases_implicit.py:118:10-24: Expected a type form, got instance of `Literal['int']` [not-a-type] +ERROR aliases_implicit.py:119:10-24: Expected a type form, got instance of `Literal['int | str']` [not-a-type] +ERROR aliases_implicit.py:133:6-20: Expected a callable, got `type[list[Unknown] | set[Unknown]]` [not-callable] +ERROR aliases_implicit.py:135:5-24: `type[list[Unknown] | set[Unknown]]` is not subscriptable [unsupported-operation] +""" diff --git a/conformance/results/pyrefly/aliases_newtype.toml b/conformance/results/pyrefly/aliases_newtype.toml new file mode 100644 index 000000000..27291ca76 --- /dev/null +++ b/conformance/results/pyrefly/aliases_newtype.toml @@ -0,0 +1,20 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR aliases_newtype.py:11:8-14: Argument `Literal['user']` is not assignable to parameter `_x` with type `int` in function `UserId.__new__` [bad-argument-type] +ERROR aliases_newtype.py:12:14-16: `Literal[42]` is not assignable to `UserId` [bad-assignment] +ERROR aliases_newtype.py:18:11-17: `type[UserId]` is not assignable to `type[Any]` [bad-assignment] +ERROR aliases_newtype.py:23:16-22: NewType `UserId` not allowed in isinstance() [invalid-argument] +ERROR aliases_newtype.py:26:21-27: Subclassing a NewType not allowed [invalid-inheritance] +ERROR aliases_newtype.py:35:20-29: Expected string literal "GoodName" [invalid-argument] +ERROR aliases_newtype.py:41:6-23: Expected 0 type arguments for `GoodNewType1`, got 1 [bad-specialization] +ERROR aliases_newtype.py:47:38-47: Second argument to NewType is invalid [invalid-argument] +ERROR aliases_newtype.py:50:38-45: Second argument to NewType cannot be an unbound generic [invalid-argument] +ERROR aliases_newtype.py:52:38-46: Second argument to NewType cannot be a protocol [invalid-argument] +ERROR aliases_newtype.py:54:38-48: Second argument to NewType is invalid [invalid-argument] +ERROR aliases_newtype.py:61:38-41: Second argument to NewType is invalid [invalid-argument] +ERROR aliases_newtype.py:63:43-46: Expected 2 positional arguments, got 3 in function `typing.NewType.__init__` [bad-argument-count] +ERROR aliases_newtype.py:65:38-41: Second argument to NewType is invalid [invalid-argument] +""" diff --git a/conformance/results/pyrefly/aliases_recursive.toml b/conformance/results/pyrefly/aliases_recursive.toml new file mode 100644 index 000000000..e0cb9c870 --- /dev/null +++ b/conformance/results/pyrefly/aliases_recursive.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR aliases_recursive.py:19:12-29: `dict[str, complex | int]` is not assignable to `dict[str, Json] | float | int | list[Json] | str | None` [bad-assignment] +ERROR aliases_recursive.py:20:12-19: `list[complex | int]` is not assignable to `dict[str, Json] | float | int | list[Json] | str | None` [bad-assignment] +ERROR aliases_recursive.py:38:22-50: `tuple[Literal[1], tuple[Literal['1'], Literal[1]], tuple[Literal[1], tuple[Literal[1], list[int]]]]` is not assignable to `int | str | tuple[RecursiveTuple, ...]` [bad-assignment] +ERROR aliases_recursive.py:39:22-30: `tuple[Literal[1], list[int]]` is not assignable to `int | str | tuple[RecursiveTuple, ...]` [bad-assignment] +ERROR aliases_recursive.py:50:24-34: `dict[str, list[int]]` is not assignable to `Mapping[str, RecursiveMapping] | int | str` [bad-assignment] +ERROR aliases_recursive.py:51:24-55: `dict[str, int | list[int] | str]` is not assignable to `Mapping[str, RecursiveMapping] | int | str` [bad-assignment] +ERROR aliases_recursive.py:52:24-83: `dict[str, dict[str, int | list[int] | str] | int | str]` is not assignable to `Mapping[str, RecursiveMapping] | int | str` [bad-assignment] +ERROR aliases_recursive.py:63:30-43: `list[list[float] | str]` is not assignable to `list[str | GenericTypeAlias1[str]]` [bad-assignment] +ERROR aliases_recursive.py:69:35-64: `list[list[int | list[int | list[float] | str]] | str]` is not assignable to `list[int | str | GenericTypeAlias2[str, int]]` [bad-assignment] +ERROR aliases_recursive.py:72:29-57: Found cyclic self-reference in `RecursiveUnion` [invalid-type-alias] +ERROR aliases_recursive.py:75:31-61: Found cyclic self-reference in `MutualReference1` [invalid-type-alias] +ERROR aliases_recursive.py:75:93-123: Found cyclic self-reference in `MutualReference2` [invalid-type-alias] +""" diff --git a/conformance/results/pyrefly/aliases_type_statement.toml b/conformance/results/pyrefly/aliases_type_statement.toml new file mode 100644 index 000000000..2feb71761 --- /dev/null +++ b/conformance/results/pyrefly/aliases_type_statement.toml @@ -0,0 +1,34 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR aliases_type_statement.py:17:1-21: Object of class `TypeAliasType` has no attribute `bit_count` [missing-attribute] +ERROR aliases_type_statement.py:19:1-11: Expected a callable, got `TypeAlias[GoodAlias1, type[int]]` [not-callable] +ERROR aliases_type_statement.py:23:7-30: Object of class `TypeAliasType` has no attribute `other_attrib` [missing-attribute] +ERROR aliases_type_statement.py:26:18-28: Cannot use scoped type alias `GoodAlias1` as a base class. Use a legacy type alias instead: `GoodAlias1: TypeAlias = type[int]` [invalid-inheritance] +ERROR aliases_type_statement.py:31:22-32: Expected class object, got `TypeAliasType` [invalid-argument] +ERROR aliases_type_statement.py:37:22-62: Function call cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:38:22-32: List literal cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:39:22-35: Tuple literal cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:40:22-45: List comprehension cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:41:22-32: Dict literal cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:41:24-25: Could not find name `a` [unknown-name] +ERROR aliases_type_statement.py:41:29-30: Could not find name `b` [unknown-name] +ERROR aliases_type_statement.py:42:22-37: Function call cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:43:22-30: Invalid subscript expression cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:44:22-43: If expression cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:45:22-26: Expected `BadTypeAlias9` to be a type alias, got `Literal[1]` [invalid-type-alias] +ERROR aliases_type_statement.py:46:23-27: Bool literal cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:47:23-24: Number literal cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:48:23-34: Boolean operation cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:49:23-33: F-string cannot be used in annotations [invalid-annotation] +ERROR aliases_type_statement.py:53:15-25: Type parameters used in `TA1` but not declared [invalid-type-var] +ERROR aliases_type_statement.py:58:12-20: Type parameters used in `TA2` but not declared [invalid-type-var] +ERROR aliases_type_statement.py:68:7-41: `str` is not assignable to upper bound `int` of type variable `S` [bad-specialization] +ERROR aliases_type_statement.py:70:7-41: `int` is not assignable to upper bound `str` of type variable `T` [bad-specialization] +ERROR aliases_type_statement.py:73:28-47: Found cyclic self-reference in `RecursiveTypeAlias3` [invalid-type-alias] +ERROR aliases_type_statement.py:75:31-59: Found cyclic self-reference in `RecursiveTypeAlias4` [invalid-type-alias] +ERROR aliases_type_statement.py:79:28-47: Found cyclic self-reference in `RecursiveTypeAlias6` [invalid-type-alias] +ERROR aliases_type_statement.py:80:28-47: Found cyclic self-reference in `RecursiveTypeAlias7` [invalid-type-alias] +""" diff --git a/conformance/results/pyrefly/aliases_typealiastype.toml b/conformance/results/pyrefly/aliases_typealiastype.toml new file mode 100644 index 000000000..d6ec8ac8e --- /dev/null +++ b/conformance/results/pyrefly/aliases_typealiastype.toml @@ -0,0 +1,31 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR aliases_typealiastype.py:32:7-30: Object of class `TypeAliasType` has no attribute `other_attrib` [missing-attribute] +ERROR aliases_typealiastype.py:40:5-30: `int` is not assignable to upper bound `str` of type variable `TStr` [bad-specialization] +ERROR aliases_typealiastype.py:43:13-66: Type variable `S` is out of scope for this `TypeAliasType` [invalid-type-alias] +ERROR aliases_typealiastype.py:44:13-48: Type variable `S` is out of scope for this `TypeAliasType` [invalid-type-alias] +ERROR aliases_typealiastype.py:45:45-65: Value for argument `type_params` must be a tuple literal [invalid-type-alias] +ERROR aliases_typealiastype.py:46:13-52: Found cyclic self-reference in `BadAlias4` [invalid-type-alias] +ERROR aliases_typealiastype.py:47:13-79: Found cyclic self-reference in `BadAlias5` [invalid-type-alias] +ERROR aliases_typealiastype.py:48:13-52: Found cyclic self-reference in `BadAlias6` [invalid-type-alias] +ERROR aliases_typealiastype.py:49:13-50: Found cyclic self-reference in `BadAlias7` [invalid-type-alias] +ERROR aliases_typealiastype.py:52:40-80: Function call cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:53:40-50: List literal cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:54:42-55: Tuple literal cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:55:42-65: List comprehension cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:56:42-52: Dict literal cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:56:44-45: Could not find name `a` [unknown-name] +ERROR aliases_typealiastype.py:56:49-50: Could not find name `b` [unknown-name] +ERROR aliases_typealiastype.py:57:42-57: Function call cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:58:42-50: Invalid subscript expression cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:59:42-63: If expression cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:60:42-46: Expected `BadAlias16` to be a type alias, got `Literal[3]` [invalid-type-alias] +ERROR aliases_typealiastype.py:61:42-46: Bool literal cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:62:42-43: Number literal cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:63:42-53: Boolean operation cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:64:42-52: F-string cannot be used in annotations [invalid-annotation] +ERROR aliases_typealiastype.py:66:14-59: Found cyclic self-reference in `BadAlias21` [invalid-type-alias] +""" diff --git a/conformance/results/pyrefly/aliases_variance.toml b/conformance/results/pyrefly/aliases_variance.toml new file mode 100644 index 000000000..7cfcd6ebc --- /dev/null +++ b/conformance/results/pyrefly/aliases_variance.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR aliases_variance.py:24:16-28: Type variable `T_co` is covariant but is used in invariant position [invalid-variance] +ERROR aliases_variance.py:28:16-31: Type variable `T_co` is covariant but is used in invariant position [invalid-variance] +ERROR aliases_variance.py:32:16-31: Type variable `T_co` is covariant but is used in invariant position [invalid-variance] +ERROR aliases_variance.py:44:16-41: Type variable `T_contra` is contravariant but is used in invariant position [invalid-variance] +""" diff --git a/conformance/results/pyrefly/annotations_coroutines.toml b/conformance/results/pyrefly/annotations_coroutines.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/annotations_coroutines.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/annotations_forward_refs.toml b/conformance/results/pyrefly/annotations_forward_refs.toml new file mode 100644 index 000000000..5f45379af --- /dev/null +++ b/conformance/results/pyrefly/annotations_forward_refs.toml @@ -0,0 +1,33 @@ +conformant = "Partial" +notes = """ +Types in quotes incorrectly refer to shadowing class member. +Does not reject some type forms that require quotes. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 87: Unexpected errors ['Expected a type form, got instance of `(self: Self@ClassD) -> None` [not-a-type]'] +Line 96: Unexpected errors ['assert_type(Unknown, int) failed [assert-type]'] +""" +output = """ +ERROR annotations_forward_refs.py:24:7-21: `|` union syntax does not work with string literals [invalid-annotation] +ERROR annotations_forward_refs.py:25:7-21: `|` union syntax does not work with string literals [invalid-annotation] +ERROR annotations_forward_refs.py:41:10-50: Function call cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:42:10-20: List literal cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:43:10-20: Tuple literal cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:44:10-33: List comprehension cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:45:10-12: Dict literal cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:46:10-26: Function call cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:47:10-18: Invalid subscript expression cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:48:10-31: If expression cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:49:10-14: Expected a type form, got instance of `Literal[1]` [not-a-type] +ERROR annotations_forward_refs.py:50:11-15: Bool literal cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:51:11-12: Number literal cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:52:11-13: Unary operation cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:53:11-21: Boolean operation cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:54:11-17: F-string cannot be used in annotations [invalid-annotation] +ERROR annotations_forward_refs.py:55:11-16: Expected a type form, got instance of `Module[types]` [not-a-type] +ERROR annotations_forward_refs.py:80:14-20: Could not find name `ClassF` [unknown-name] +ERROR annotations_forward_refs.py:87:9-12: Expected a type form, got instance of `(self: Self@ClassD) -> None` [not-a-type] +ERROR annotations_forward_refs.py:89:8-11: Expected a type form, got instance of `(self: Self@ClassD) -> None` [not-a-type] +ERROR annotations_forward_refs.py:96:12-27: assert_type(Unknown, int) failed [assert-type] +""" diff --git a/conformance/results/pyrefly/annotations_generators.toml b/conformance/results/pyrefly/annotations_generators.toml new file mode 100644 index 000000000..15f7ef6c3 --- /dev/null +++ b/conformance/results/pyrefly/annotations_generators.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR annotations_generators.py:51:21-39: Function declared to return `C`, but one or more paths are missing an explicit `return` [bad-return] +ERROR annotations_generators.py:54:16-21: Returned type `Literal[False]` is not assignable to declared return type `C` [bad-return] +ERROR annotations_generators.py:57:15-16: Yielded type `Literal[3]` is not assignable to declared yield type `A` [invalid-yield] +ERROR annotations_generators.py:66:15-16: Yielded type `Literal[3]` is not assignable to declared yield type `A` [invalid-yield] +ERROR annotations_generators.py:71:12-16: Returned type `Literal[True]` is not assignable to declared return type `None` [bad-return] +ERROR annotations_generators.py:75:11-14: Yielded type `B` is not assignable to declared yield type `A` [invalid-yield] +ERROR annotations_generators.py:86:21-24: Generator function should return `Generator` [bad-return] +ERROR annotations_generators.py:91:27-30: Async generator function should return `AsyncGenerator` [bad-return] +ERROR annotations_generators.py:118:5-29: Cannot yield from `Generator[A]`, which is not assignable to declared return type `Generator[B, None, Unknown]` [invalid-yield] +ERROR annotations_generators.py:119:5-19: Cannot yield from `Generator[int]`, which is not assignable to declared return type `Generator[B, None, Unknown]` [invalid-yield] +ERROR annotations_generators.py:135:5-29: Cannot yield from `Generator[None, int]`, which is not assignable to declared return type `Generator[None, str, Unknown]` [invalid-yield] +""" diff --git a/conformance/results/pyrefly/annotations_methods.toml b/conformance/results/pyrefly/annotations_methods.toml new file mode 100644 index 000000000..06e3f82f9 --- /dev/null +++ b/conformance/results/pyrefly/annotations_methods.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR annotations_methods.py:42:12-28: assert_type(B, A) failed [assert-type] +""" diff --git a/conformance/results/pyrefly/annotations_typeexpr.toml b/conformance/results/pyrefly/annotations_typeexpr.toml new file mode 100644 index 000000000..16dd4131a --- /dev/null +++ b/conformance/results/pyrefly/annotations_typeexpr.toml @@ -0,0 +1,21 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR annotations_typeexpr.py:88:9-49: Function call cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:89:9-19: List literal cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:90:9-19: Tuple literal cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:91:9-32: List comprehension cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:92:9-11: Dict literal cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:93:9-24: Function call cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:94:9-17: Invalid subscript expression cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:95:9-30: If expression cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:96:9-13: Expected a type form, got instance of `Literal[3]` [not-a-type] +ERROR annotations_typeexpr.py:97:10-14: Bool literal cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:98:10-11: Number literal cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:99:10-12: Unary operation cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:100:10-20: Boolean operation cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:101:10-16: F-string cannot be used in annotations [invalid-annotation] +ERROR annotations_typeexpr.py:102:10-15: Expected a type form, got instance of `Module[types]` [not-a-type] +""" diff --git a/conformance/results/pyrefly/callables_annotation.toml b/conformance/results/pyrefly/callables_annotation.toml new file mode 100644 index 000000000..efbdf98e8 --- /dev/null +++ b/conformance/results/pyrefly/callables_annotation.toml @@ -0,0 +1,27 @@ +conformant = "Partial" +notes = """ +Parameter names are lost when resolving ParamSpec +""" +conformance_automated = "Fail" +errors_diff = """ +Line 159: Expected 1 errors +""" +output = """ +ERROR callables_annotation.py:25:7-10: Expected 1 more positional argument [bad-argument-count] +ERROR callables_annotation.py:26:11-12: Argument `Literal[2]` is not assignable to parameter with type `str` [bad-argument-type] +ERROR callables_annotation.py:27:15-16: Expected 2 positional arguments, got 3 [bad-argument-count] +ERROR callables_annotation.py:29:8-9: Unexpected keyword argument `a` [unexpected-keyword] +ERROR callables_annotation.py:29:8-11: Expected 2 more positional arguments [bad-argument-count] +ERROR callables_annotation.py:29:13-14: Unexpected keyword argument `b` [unexpected-keyword] +ERROR callables_annotation.py:35:8-9: Expected 0 positional arguments, got 1 [bad-argument-count] +ERROR callables_annotation.py:55:5-18: Expected 2 arguments for `Callable`, got 1 [bad-specialization] +ERROR callables_annotation.py:56:14-17: Callable types can only have `ParamSpec` in this position, got `int` [bad-specialization] +ERROR callables_annotation.py:57:18-23: Expected a type form, got instance of `list[type[int]]` [not-a-type] +ERROR callables_annotation.py:58:5-28: Expected 2 arguments for `Callable`, got 3 [bad-specialization] +ERROR callables_annotation.py:59:15-18: Invalid position for `...` [invalid-argument] +ERROR callables_annotation.py:91:7-15: `() -> str` is not assignable to variable `cb3` with type `(int, ...) -> str` [bad-assignment] +ERROR callables_annotation.py:93:7-15: `(*, a: int) -> str` is not assignable to variable `cb3` with type `(int, ...) -> str` [bad-assignment] +ERROR callables_annotation.py:172:26-29: `() -> str` is not assignable to `(int, ...) -> str` [bad-assignment] +ERROR callables_annotation.py:187:48-50: `(int, str) -> str` is not assignable to `(str, ...) -> str` [bad-assignment] +ERROR callables_annotation.py:189:32-34: `(int, str) -> str` is not assignable to `(str, ...) -> str` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/callables_kwargs.toml b/conformance/results/pyrefly/callables_kwargs.toml new file mode 100644 index 000000000..d37554ce3 --- /dev/null +++ b/conformance/results/pyrefly/callables_kwargs.toml @@ -0,0 +1,23 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR callables_kwargs.py:46:10-12: Missing argument `v1` in function `func1` [missing-argument] +ERROR callables_kwargs.py:46:10-12: Missing argument `v3` in function `func1` [missing-argument] +ERROR callables_kwargs.py:51:32-34: Unexpected keyword argument `v4` in function `func1` [unexpected-keyword] +ERROR callables_kwargs.py:52:11-12: Expected argument `v1` to be passed by name in function `func1` [unexpected-positional-argument] +ERROR callables_kwargs.py:52:11-12: Expected 0 positional arguments, got 3 in function `func1` [bad-argument-count] +ERROR callables_kwargs.py:52:14-16: Expected argument `v3` to be passed by name in function `func1` [unexpected-positional-argument] +ERROR callables_kwargs.py:58:11-20: Unpacked keyword argument `str` is not assignable to parameter `v1` with type `int` in function `func1` [bad-argument-type] +ERROR callables_kwargs.py:63:17-22: Multiple values for argument `v1` in function `func1` [bad-keyword-argument] +ERROR callables_kwargs.py:64:11-12: Argument `Literal[1]` is not assignable to parameter `v3` with type `str` in function `func2` [bad-argument-type] +ERROR callables_kwargs.py:64:14-19: Multiple values for argument `v3` in function `func2` [bad-keyword-argument] +ERROR callables_kwargs.py:65:17-22: Multiple values for argument `v1` in function `func2` [bad-keyword-argument] +ERROR callables_kwargs.py:101:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol3` [bad-assignment] +ERROR callables_kwargs.py:102:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol4` [bad-assignment] +ERROR callables_kwargs.py:103:19-24: `(**kwargs: Unpack[TD2]) -> None` is not assignable to `TDProtocol5` [bad-assignment] +ERROR callables_kwargs.py:111:20-41: TypedDict key 'v1' in **kwargs overlaps with parameter 'v1' [bad-function-definition] +ERROR callables_kwargs.py:122:21-30: `Unpack` in **kwargs annotation must be used only with a `TypedDict` [invalid-annotation] +ERROR callables_kwargs.py:134:19-24: `(*, v1: int, v3: str, v2: str = '') -> None` is not assignable to `TDProtocol6` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/callables_protocol.toml b/conformance/results/pyrefly/callables_protocol.toml new file mode 100644 index 000000000..3bdc990b0 --- /dev/null +++ b/conformance/results/pyrefly/callables_protocol.toml @@ -0,0 +1,23 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR callables_protocol.py:35:7-15: `(*vals: bytes, *, max_items: int | None) -> list[bytes]` is not assignable to variable `cb1` with type `Proto1` [bad-assignment] +ERROR callables_protocol.py:36:7-15: `(*vals: bytes) -> list[bytes]` is not assignable to variable `cb1` with type `Proto1` [bad-assignment] +ERROR callables_protocol.py:37:7-15: `(*vals: bytes, *, max_len: str | None) -> list[bytes]` is not assignable to variable `cb1` with type `Proto1` [bad-assignment] +ERROR callables_protocol.py:67:7-15: `(*a: bytes) -> None` is not assignable to variable `cb2` with type `Proto2` [bad-assignment] +ERROR callables_protocol.py:68:7-15: `(*a: str, **b: str) -> None` is not assignable to variable `cb2` with type `Proto2` [bad-assignment] +ERROR callables_protocol.py:69:7-15: `(*a: bytes, **b: bytes) -> None` is not assignable to variable `cb2` with type `Proto2` [bad-assignment] +ERROR callables_protocol.py:70:7-15: `(**b: str) -> None` is not assignable to variable `cb2` with type `Proto2` [bad-assignment] +ERROR callables_protocol.py:97:16-24: `(x: int) -> None` is not assignable to `Proto4` [bad-assignment] +ERROR callables_protocol.py:121:18-26: `(*vals: bytes, *, max_len: int | None = None) -> list[bytes]` is not assignable to `NotProto6` [bad-assignment] +ERROR callables_protocol.py:169:7-15: `(x: int) -> Any` is not assignable to variable `cb8` with type `Proto8` [bad-assignment] +ERROR callables_protocol.py:186:33-38: `Literal['str']` is not assignable to attribute `other_attribute` with type `int` [bad-assignment] +ERROR callables_protocol.py:187:5-18: Object of class `Proto9` has no attribute `xxx` [missing-attribute] +ERROR callables_protocol.py:197:7-32: Object of class `Proto9` has no attribute `other_attribute2` [missing-attribute] +ERROR callables_protocol.py:238:8-17: `(x: int, y: str, /) -> Any` is not assignable to variable `cb11` with type `Proto11` [bad-assignment] +ERROR callables_protocol.py:260:8-17: `(*args: Any, *, kwarg0: Any) -> None` is not assignable to variable `cb12` with type `Proto12` [bad-assignment] +ERROR callables_protocol.py:284:27-42: `(path: str) -> str` is not assignable to `Proto13_Default` [bad-assignment] +ERROR callables_protocol.py:311:27-42: `(*, path: str) -> str` is not assignable to `Proto14_Default` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/callables_subtyping.toml b/conformance/results/pyrefly/callables_subtyping.toml new file mode 100644 index 000000000..3a332c8c8 --- /dev/null +++ b/conformance/results/pyrefly/callables_subtyping.toml @@ -0,0 +1,38 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR callables_subtyping.py:26:36-39: `(int) -> int` is not assignable to `(float) -> float` [bad-assignment] +ERROR callables_subtyping.py:29:32-35: `(float) -> float` is not assignable to `(int) -> int` [bad-assignment] +ERROR callables_subtyping.py:51:21-29: `PosOnly2` is not assignable to `Standard2` [bad-assignment] +ERROR callables_subtyping.py:52:21-28: `KwOnly2` is not assignable to `Standard2` [bad-assignment] +ERROR callables_subtyping.py:55:20-27: `KwOnly2` is not assignable to `PosOnly2` [bad-assignment] +ERROR callables_subtyping.py:58:19-27: `PosOnly2` is not assignable to `KwOnly2` [bad-assignment] +ERROR callables_subtyping.py:82:20-27: `NoArgs3` is not assignable to `IntArgs3` [bad-assignment] +ERROR callables_subtyping.py:85:22-29: `NoArgs3` is not assignable to `FloatArgs3` [bad-assignment] +ERROR callables_subtyping.py:86:22-30: `IntArgs3` is not assignable to `FloatArgs3` [bad-assignment] +ERROR callables_subtyping.py:116:20-28: `IntArgs4` is not assignable to `PosOnly4` [bad-assignment] +ERROR callables_subtyping.py:119:23-31: `StrArgs4` is not assignable to `IntStrArgs4` [bad-assignment] +ERROR callables_subtyping.py:120:23-31: `IntArgs4` is not assignable to `IntStrArgs4` [bad-assignment] +ERROR callables_subtyping.py:122:20-28: `IntArgs4` is not assignable to `StrArgs4` [bad-assignment] +ERROR callables_subtyping.py:124:20-28: `StrArgs4` is not assignable to `IntArgs4` [bad-assignment] +ERROR callables_subtyping.py:125:22-34: `IntStrArgs4` is not assignable to `Standard4` [bad-assignment] +ERROR callables_subtyping.py:126:22-30: `StrArgs4` is not assignable to `Standard4` [bad-assignment] +ERROR callables_subtyping.py:151:22-31: `NoKwargs5` is not assignable to `IntKwargs5` [bad-assignment] +ERROR callables_subtyping.py:154:24-33: `NoKwargs5` is not assignable to `FloatKwargs5` [bad-assignment] +ERROR callables_subtyping.py:155:24-34: `IntKwargs5` is not assignable to `FloatKwargs5` [bad-assignment] +ERROR callables_subtyping.py:187:19-29: `IntKwargs6` is not assignable to `KwOnly6` [bad-assignment] +ERROR callables_subtyping.py:190:25-35: `StrKwargs6` is not assignable to `IntStrKwargs6` [bad-assignment] +ERROR callables_subtyping.py:191:25-35: `IntKwargs6` is not assignable to `IntStrKwargs6` [bad-assignment] +ERROR callables_subtyping.py:193:22-32: `IntKwargs6` is not assignable to `StrKwargs6` [bad-assignment] +ERROR callables_subtyping.py:195:22-32: `StrKwargs6` is not assignable to `IntKwargs6` [bad-assignment] +ERROR callables_subtyping.py:196:22-36: `IntStrKwargs6` is not assignable to `Standard6` [bad-assignment] +ERROR callables_subtyping.py:197:22-32: `StrKwargs6` is not assignable to `Standard6` [bad-assignment] +ERROR callables_subtyping.py:236:23-37: `NoDefaultArg8` is not assignable to `DefaultArg8` [bad-assignment] +ERROR callables_subtyping.py:237:23-27: `NoX8` is not assignable to `DefaultArg8` [bad-assignment] +ERROR callables_subtyping.py:240:25-29: `NoX8` is not assignable to `NoDefaultArg8` [bad-assignment] +ERROR callables_subtyping.py:243:16-30: `NoDefaultArg8` is not assignable to `NoX8` [bad-assignment] +ERROR callables_subtyping.py:273:21-31: `Overloaded9` is not assignable to `FloatArg9` [bad-assignment] +ERROR callables_subtyping.py:297:24-31: `StrArg10` is not assignable to `Overloaded10` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/classes_classvar.toml b/conformance/results/pyrefly/classes_classvar.toml new file mode 100644 index 000000000..551f730b1 --- /dev/null +++ b/conformance/results/pyrefly/classes_classvar.toml @@ -0,0 +1,27 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR classes_classvar.py:38:11-29: Expected 1 type argument for `ClassVar`, got 2 [invalid-annotation] +ERROR classes_classvar.py:39:14-15: Number literal cannot be used in annotations [invalid-annotation] +ERROR classes_classvar.py:40:14-17: Could not find name `var` [unknown-name] +ERROR classes_classvar.py:45:20-21: `ClassVar` arguments may not contain any type variables [invalid-annotation] +ERROR classes_classvar.py:46:20-27: `ClassVar` arguments may not contain any type variables [invalid-annotation] +ERROR classes_classvar.py:47:20-36: `ClassVar` arguments may not contain any type variables [invalid-annotation] +ERROR classes_classvar.py:52:33-35: `dict[@_, @_]` is not assignable to `list[str]` [bad-assignment] +ERROR classes_classvar.py:54:17-30: `ClassVar` may not be nested inside `Final` [invalid-annotation] +ERROR classes_classvar.py:55:17-30: `ClassVar` is not allowed in this context [invalid-annotation] +ERROR classes_classvar.py:69:26-34: `ClassVar` is only allowed inside a class body [invalid-annotation] +ERROR classes_classvar.py:69:26-39: `ClassVar` is not allowed in this context [invalid-annotation] +ERROR classes_classvar.py:70:12-20: `ClassVar` is only allowed inside a class body [invalid-annotation] +ERROR classes_classvar.py:70:12-25: `ClassVar` is not allowed in this context [invalid-annotation] +ERROR classes_classvar.py:71:9-16: Cannot set field `xx` [read-only] +ERROR classes_classvar.py:73:26-34: `ClassVar` is only allowed inside a class body [invalid-annotation] +ERROR classes_classvar.py:73:26-39: `ClassVar` is not allowed in this context [invalid-annotation] +ERROR classes_classvar.py:77:8-16: `ClassVar` is only allowed inside a class body [invalid-annotation] +ERROR classes_classvar.py:77:8-21: `ClassVar` is not allowed in this context [invalid-annotation] +ERROR classes_classvar.py:78:20-33: `ClassVar` is not allowed in this context [invalid-annotation] +ERROR classes_classvar.py:111:1-19: Cannot set field `stats` [read-only] +ERROR classes_classvar.py:140:13-25: `ProtoAImpl` is not assignable to `ProtoA` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/classes_override.toml b/conformance/results/pyrefly/classes_override.toml new file mode 100644 index 000000000..cb376ba0b --- /dev/null +++ b/conformance/results/pyrefly/classes_override.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR classes_override.py:53:9-16: Class member `ChildA.method3` is marked as an override, but no parent class has a matching attribute [bad-override] +ERROR classes_override.py:57:9-16: Class member `ChildA.method4` is marked as an override, but no parent class has a matching attribute [bad-override] +ERROR classes_override.py:79:9-23: Class member `ChildA.static_method1` is marked as an override, but no parent class has a matching attribute [bad-override] +ERROR classes_override.py:84:9-22: Class member `ChildA.class_method1` is marked as an override, but no parent class has a matching attribute [bad-override] +ERROR classes_override.py:89:9-18: Class member `ChildA.property1` is marked as an override, but no parent class has a matching attribute [bad-override] +""" diff --git a/conformance/results/pyrefly/constructors_call_init.toml b/conformance/results/pyrefly/constructors_call_init.toml new file mode 100644 index 000000000..05593e0a0 --- /dev/null +++ b/conformance/results/pyrefly/constructors_call_init.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR constructors_call_init.py:21:13-16: Argument `float` is not assignable to parameter `x` with type `int` in function `Class1.__init__` [bad-argument-type] +ERROR constructors_call_init.py:42:8-20: Argument `Class2[@_]` is not assignable to parameter `x` with type `Class3 | None` in function `Class2.__init__` [bad-argument-type] +ERROR constructors_call_init.py:56:12-14: Argument `Class4[str]` is not assignable to parameter `self` with type `Class4[int]` in function `Class4.__init__` [bad-argument-type] +ERROR constructors_call_init.py:107:9-17: `__init__` method self type cannot reference class type parameters `T2`, `T1` [invalid-annotation] +ERROR constructors_call_init.py:130:9-10: Expected 0 positional arguments, got 1 in function `object.__init__` [bad-argument-count] +""" diff --git a/conformance/results/pyrefly/constructors_call_metaclass.toml b/conformance/results/pyrefly/constructors_call_metaclass.toml new file mode 100644 index 000000000..5471a7404 --- /dev/null +++ b/conformance/results/pyrefly/constructors_call_metaclass.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR constructors_call_metaclass.py:54:7-9: Missing argument `x` in function `Class3.__new__` [missing-argument] +ERROR constructors_call_metaclass.py:68:7-9: Missing argument `x` in function `Class4.__new__` [missing-argument] +""" diff --git a/conformance/results/pyrefly/constructors_call_new.toml b/conformance/results/pyrefly/constructors_call_new.toml new file mode 100644 index 000000000..edef6e5e5 --- /dev/null +++ b/conformance/results/pyrefly/constructors_call_new.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR constructors_call_new.py:21:13-16: Argument `float` is not assignable to parameter `x` with type `int` in function `Class1.__new__` [bad-argument-type] +ERROR constructors_call_new.py:148:13-15: Argument `type[Class11[str]]` is not assignable to parameter `cls` with type `type[Class11[int]]` in function `Class11.__new__` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/constructors_call_type.toml b/conformance/results/pyrefly/constructors_call_type.toml new file mode 100644 index 000000000..fd4d14c64 --- /dev/null +++ b/conformance/results/pyrefly/constructors_call_type.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR constructors_call_type.py:30:8-10: Missing argument `x` in function `Meta1.__call__` [missing-argument] +ERROR constructors_call_type.py:30:8-10: Missing argument `y` in function `Meta1.__call__` [missing-argument] +ERROR constructors_call_type.py:40:8-10: Missing argument `x` in function `Class2.__new__` [missing-argument] +ERROR constructors_call_type.py:40:8-10: Missing argument `y` in function `Class2.__new__` [missing-argument] +ERROR constructors_call_type.py:50:8-10: Missing argument `x` in function `Class3.__init__` [missing-argument] +ERROR constructors_call_type.py:50:8-10: Missing argument `y` in function `Class3.__init__` [missing-argument] +ERROR constructors_call_type.py:59:9-10: Expected 0 positional arguments, got 1 in function `object.__init__` [bad-argument-count] +ERROR constructors_call_type.py:64:9-10: Expected 0 positional arguments, got 1 [bad-argument-count] +ERROR constructors_call_type.py:72:8-10: Missing argument `x` in function `Meta1.__call__` [missing-argument] +ERROR constructors_call_type.py:72:8-10: Missing argument `y` in function `Meta1.__call__` [missing-argument] +ERROR constructors_call_type.py:81:8-11: Missing argument `y` in function `Class2.__new__` [missing-argument] +ERROR constructors_call_type.py:82:12-13: Argument `Literal[2]` is not assignable to parameter `y` with type `str` in function `Class2.__new__` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/constructors_callable.toml b/conformance/results/pyrefly/constructors_callable.toml new file mode 100644 index 000000000..33d7b6000 --- /dev/null +++ b/conformance/results/pyrefly/constructors_callable.toml @@ -0,0 +1,26 @@ +conformant = "Partial" +notes = """ +Converting constructor to callable does not preserve class-scoped type params. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 186: Expected 1 errors +Line 185: Unexpected errors ['assert_type(Class8[Unknown], Class8[str]) failed [assert-type]'] +""" +output = """ +ERROR constructors_callable.py:38:3-5: Missing argument `x` [missing-argument] +ERROR constructors_callable.py:39:3-8: Missing argument `x` [missing-argument] +ERROR constructors_callable.py:39:4-5: Unexpected keyword argument `y` [unexpected-keyword] +ERROR constructors_callable.py:51:4-5: Expected 0 positional arguments, got 1 [bad-argument-count] +ERROR constructors_callable.py:66:3-5: Missing argument `x` [missing-argument] +ERROR constructors_callable.py:67:3-8: Missing argument `x` [missing-argument] +ERROR constructors_callable.py:67:4-5: Unexpected keyword argument `y` [unexpected-keyword] +ERROR constructors_callable.py:68:7-8: Expected 1 positional argument, got 2 [bad-argument-count] +ERROR constructors_callable.py:81:3-5: Missing argument `x` [missing-argument] +ERROR constructors_callable.py:82:3-8: Missing argument `x` [missing-argument] +ERROR constructors_callable.py:82:4-5: Unexpected keyword argument `y` [unexpected-keyword] +ERROR constructors_callable.py:129:4-5: Expected 0 positional arguments, got 1 [bad-argument-count] +ERROR constructors_callable.py:146:8-9: Expected 0 positional arguments, got 1 [bad-argument-count] +ERROR constructors_callable.py:185:12-41: assert_type(Class8[Unknown], Class8[str]) failed [assert-type] +ERROR constructors_callable.py:197:9-13: Argument `list[str]` is not assignable to parameter `y` with type `list[int]` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/constructors_consistency.toml b/conformance/results/pyrefly/constructors_consistency.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/constructors_consistency.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/dataclasses_descriptors.toml b/conformance/results/pyrefly/dataclasses_descriptors.toml new file mode 100644 index 000000000..e2ae83bf1 --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_descriptors.toml @@ -0,0 +1,22 @@ +conformant = "Partial" +notes = """ +* Assumes descriptor behavior only when field is assigned in class body +* Doesn't allow non-data descriptors or data descriptors with differing `__get__` and `__set__` types +""" +conformance_automated = "Fail" +errors_diff = """ +Line 32: Unexpected errors ['Cannot set field `y` to data descriptor `Desc1` with inconsistent types [bad-class-definition]'] +Line 58: Unexpected errors ['Cannot set field `z` to non-data descriptor `Desc2` [bad-class-definition]'] +Line 61: Unexpected errors ['assert_type(Desc2[int], list[int]) failed [assert-type]'] +Line 62: Unexpected errors ['assert_type(Desc2[str], list[str]) failed [assert-type]'] +Line 66: Unexpected errors ['assert_type(Desc2[int], int) failed [assert-type]'] +Line 67: Unexpected errors ['assert_type(Desc2[str], str) failed [assert-type]'] +""" +output = """ +ERROR dataclasses_descriptors.py:32:5-6: Cannot set field `y` to data descriptor `Desc1` with inconsistent types [bad-class-definition] +ERROR dataclasses_descriptors.py:58:5-6: Cannot set field `z` to non-data descriptor `Desc2` [bad-class-definition] +ERROR dataclasses_descriptors.py:61:12-30: assert_type(Desc2[int], list[int]) failed [assert-type] +ERROR dataclasses_descriptors.py:62:12-30: assert_type(Desc2[str], list[str]) failed [assert-type] +ERROR dataclasses_descriptors.py:66:12-24: assert_type(Desc2[int], int) failed [assert-type] +ERROR dataclasses_descriptors.py:67:12-24: assert_type(Desc2[str], str) failed [assert-type] +""" diff --git a/conformance/results/pyrefly/dataclasses_final.toml b/conformance/results/pyrefly/dataclasses_final.toml new file mode 100644 index 000000000..144be4b17 --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_final.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_final.py:27:1-17: Cannot set field `final_classvar` [read-only] +ERROR dataclasses_final.py:35:1-19: Cannot set field `final_no_default` [read-only] +ERROR dataclasses_final.py:36:1-21: Cannot set field `final_with_default` [read-only] +ERROR dataclasses_final.py:37:1-19: Cannot set field `final_no_default` [read-only] +ERROR dataclasses_final.py:38:1-21: Cannot set field `final_with_default` [read-only] +""" diff --git a/conformance/results/pyrefly/dataclasses_frozen.toml b/conformance/results/pyrefly/dataclasses_frozen.toml new file mode 100644 index 000000000..4fd80f44f --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_frozen.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_frozen.py:16:1-6: Cannot set field `a` [read-only] +ERROR dataclasses_frozen.py:17:1-6: Cannot set field `b` [read-only] +ERROR dataclasses_frozen.py:23:7-10: Cannot inherit non-frozen dataclass `DC2` from frozen dataclass `DC1` [invalid-inheritance] +ERROR dataclasses_frozen.py:33:7-10: Cannot inherit frozen dataclass `DC4` from non-frozen dataclass `DC3` [invalid-inheritance] +""" diff --git a/conformance/results/pyrefly/dataclasses_hash.toml b/conformance/results/pyrefly/dataclasses_hash.toml new file mode 100644 index 000000000..4c89ed9cf --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_hash.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_hash.py:17:1-16: Expected a callable, got `None` [not-callable] +ERROR dataclasses_hash.py:18:16-22: `DC1` is not assignable to `Hashable` [bad-assignment] +ERROR dataclasses_hash.py:39:1-16: Expected a callable, got `None` [not-callable] +ERROR dataclasses_hash.py:40:16-22: `DC3` is not assignable to `Hashable` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/dataclasses_inheritance.toml b/conformance/results/pyrefly/dataclasses_inheritance.toml new file mode 100644 index 000000000..0b64d63ad --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_inheritance.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_inheritance.py:62:5-6: ClassVar `DC7.x` overrides instance variable of the same name in parent class `DC6` [bad-override] +ERROR dataclasses_inheritance.py:66:5-6: Instance variable `DC7.y` overrides ClassVar of the same name in parent class `DC6` [bad-override] +""" diff --git a/conformance/results/pyrefly/dataclasses_kwonly.toml b/conformance/results/pyrefly/dataclasses_kwonly.toml new file mode 100644 index 000000000..aebe69eb9 --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_kwonly.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_kwonly.py:23:11-12: Expected 1 positional argument, got 2 in function `DC1.__init__` [bad-argument-count] +ERROR dataclasses_kwonly.py:38:11-12: Expected 1 positional argument, got 2 in function `DC2.__init__` [bad-argument-count] +ERROR dataclasses_kwonly.py:53:11-12: Expected 1 positional argument, got 2 in function `DC3.__init__` [bad-argument-count] +""" diff --git a/conformance/results/pyrefly/dataclasses_match_args.toml b/conformance/results/pyrefly/dataclasses_match_args.toml new file mode 100644 index 000000000..16221354f --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_match_args.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_match_args.py:42:1-19: Class `DC4` has no class attribute `__match_args__` [missing-attribute] +""" diff --git a/conformance/results/pyrefly/dataclasses_order.toml b/conformance/results/pyrefly/dataclasses_order.toml new file mode 100644 index 000000000..65b34aec2 --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_order.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_order.py:50:4-17: `<` is not supported between `DC1` and `DC2` [unsupported-operation] +""" diff --git a/conformance/results/pyrefly/dataclasses_postinit.toml b/conformance/results/pyrefly/dataclasses_postinit.toml new file mode 100644 index 000000000..45efdbd19 --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_postinit.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_postinit.py:19:9-22: `__post_init__` type `(self: DC1, x: int, y: int) -> None` is not assignable to expected type `(x: int, y: str) -> object` generated from the dataclass's `InitVar` fields [bad-function-definition] +ERROR dataclasses_postinit.py:28:7-12: Object of class `DC1` has no attribute `x` [missing-attribute] +ERROR dataclasses_postinit.py:29:7-12: Object of class `DC1` has no attribute `y` [missing-attribute] +ERROR dataclasses_postinit.py:36:9-22: `__post_init__` type `(self: DC2, x: int) -> None` is not assignable to expected type `(x: int, y: str) -> object` generated from the dataclass's `InitVar` fields [bad-function-definition] +""" diff --git a/conformance/results/pyrefly/dataclasses_slots.toml b/conformance/results/pyrefly/dataclasses_slots.toml new file mode 100644 index 000000000..33af4406b --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_slots.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_slots.py:11:7-10: Cannot specify both `slots=True` and `__slots__` [bad-class-definition] +ERROR dataclasses_slots.py:25:14-15: Object of class `DC2` has no attribute `y` (not declared in `__slots__`) [missing-attribute] +ERROR dataclasses_slots.py:38:14-15: Object of class `DC3` has no attribute `y` (not declared in `__slots__`) [missing-attribute] +ERROR dataclasses_slots.py:66:1-14: Class `DC6` has no class attribute `__slots__` [missing-attribute] +ERROR dataclasses_slots.py:69:1-17: Object of class `DC6` has no attribute `__slots__` [missing-attribute] +""" diff --git a/conformance/results/pyrefly/dataclasses_transform_class.toml b/conformance/results/pyrefly/dataclasses_transform_class.toml new file mode 100644 index 000000000..31fc9270f --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_transform_class.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_transform_class.py:51:7-24: Cannot inherit non-frozen dataclass `Customer1Subclass` from frozen dataclass `Customer1` [invalid-inheritance] +ERROR dataclasses_transform_class.py:63:1-8: Cannot set field `id` [read-only] +ERROR dataclasses_transform_class.py:66:18-19: Expected argument `id` to be passed by name in function `Customer1.__init__` [unexpected-positional-argument] +ERROR dataclasses_transform_class.py:66:21-26: Expected argument `name` to be passed by name in function `Customer1.__init__` [unexpected-positional-argument] +ERROR dataclasses_transform_class.py:72:6-17: `<` is not supported between `Customer1` and `Customer1` [unsupported-operation] +ERROR dataclasses_transform_class.py:82:18-19: Expected argument `id` to be passed by name in function `Customer2.__init__` [unexpected-positional-argument] +ERROR dataclasses_transform_class.py:82:18-19: Expected 0 positional arguments, got 2 in function `Customer2.__init__` [bad-argument-count] +ERROR dataclasses_transform_class.py:122:1-8: Cannot set field `id` [read-only] +""" diff --git a/conformance/results/pyrefly/dataclasses_transform_converter.toml b/conformance/results/pyrefly/dataclasses_transform_converter.toml new file mode 100644 index 000000000..f49dd73da --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_transform_converter.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_transform_converter.py:48:41-55: Argument `() -> int` is not assignable to parameter `converter` with type `(@_) -> @_` in function `model_field` [bad-argument-type] +ERROR dataclasses_transform_converter.py:49:41-55: Argument `(*, x: int) -> int` is not assignable to parameter `converter` with type `(@_) -> @_` in function `model_field` [bad-argument-type] +ERROR dataclasses_transform_converter.py:107:5-6: Argument `Literal[1]` is not assignable to parameter `field0` with type `str` in function `DC2.__init__` [bad-argument-type] +ERROR dataclasses_transform_converter.py:108:23-24: Argument `Literal[1]` is not assignable to parameter `field3` with type `bytes | str` in function `DC2.__init__` [bad-argument-type] +ERROR dataclasses_transform_converter.py:109:29-31: Argument `complex` is not assignable to parameter `field4` with type `list[str] | str` in function `DC2.__init__` [bad-argument-type] +ERROR dataclasses_transform_converter.py:118:14-15: `Literal[1]` is not assignable to attribute `field0` with type `str` [bad-assignment] +ERROR dataclasses_transform_converter.py:119:14-15: `Literal[1]` is not assignable to attribute `field3` with type `bytes | str` [bad-assignment] +ERROR dataclasses_transform_converter.py:130:67-68: Argument `Literal[1]` is not assignable to parameter `default` with type `str | None` in function `model_field` [bad-argument-type] +ERROR dataclasses_transform_converter.py:133:75-78: Argument `type[int]` is not assignable to parameter `default_factory` with type `(() -> str) | None` in function `model_field` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/dataclasses_transform_field.toml b/conformance/results/pyrefly/dataclasses_transform_field.toml new file mode 100644 index 000000000..df3d09509 --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_transform_field.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_transform_field.py:64:16-18: Unexpected keyword argument `id` in function `CustomerModel1.__init__` [unexpected-keyword] +ERROR dataclasses_transform_field.py:75:16-17: Expected argument `name` to be passed by name in function `CustomerModel2.__init__` [unexpected-positional-argument] +""" diff --git a/conformance/results/pyrefly/dataclasses_transform_func.toml b/conformance/results/pyrefly/dataclasses_transform_func.toml new file mode 100644 index 000000000..1d8c11fc8 --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_transform_func.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_transform_func.py:56:13-14: `Literal[3]` is not assignable to attribute `name` with type `str` [bad-assignment] +ERROR dataclasses_transform_func.py:60:6-17: `<` is not supported between `Customer1` and `Customer1` [unsupported-operation] +ERROR dataclasses_transform_func.py:64:36-42: Unexpected keyword argument `salary` in function `Customer1.__init__` [unexpected-keyword] +ERROR dataclasses_transform_func.py:70:18-19: Expected argument `id` to be passed by name in function `Customer2.__init__` [unexpected-positional-argument] +ERROR dataclasses_transform_func.py:70:21-27: Expected argument `name` to be passed by name in function `Customer2.__init__` [unexpected-positional-argument] +ERROR dataclasses_transform_func.py:89:7-24: Cannot inherit non-frozen dataclass `Customer3Subclass` from frozen dataclass `Customer3` [invalid-inheritance] +ERROR dataclasses_transform_func.py:96:1-8: Cannot set field `id` [read-only] +""" diff --git a/conformance/results/pyrefly/dataclasses_transform_meta.toml b/conformance/results/pyrefly/dataclasses_transform_meta.toml new file mode 100644 index 000000000..8af862f41 --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_transform_meta.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_transform_meta.py:51:7-24: Cannot inherit non-frozen dataclass `Customer1Subclass` from frozen dataclass `Customer1` [invalid-inheritance] +ERROR dataclasses_transform_meta.py:63:1-8: Cannot set field `id` [read-only] +ERROR dataclasses_transform_meta.py:66:18-19: Expected argument `id` to be passed by name in function `Customer1.__init__` [unexpected-positional-argument] +ERROR dataclasses_transform_meta.py:66:21-26: Expected argument `name` to be passed by name in function `Customer1.__init__` [unexpected-positional-argument] +ERROR dataclasses_transform_meta.py:73:6-17: `<` is not supported between `Customer1` and `Customer1` [unsupported-operation] +ERROR dataclasses_transform_meta.py:83:18-19: Expected argument `id` to be passed by name in function `Customer2.__init__` [unexpected-positional-argument] +ERROR dataclasses_transform_meta.py:83:18-19: Expected 0 positional arguments, got 2 in function `Customer2.__init__` [bad-argument-count] +ERROR dataclasses_transform_meta.py:103:1-8: Cannot set field `id` [read-only] +""" diff --git a/conformance/results/pyrefly/dataclasses_usage.toml b/conformance/results/pyrefly/dataclasses_usage.toml new file mode 100644 index 000000000..a289244ab --- /dev/null +++ b/conformance/results/pyrefly/dataclasses_usage.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR dataclasses_usage.py:51:19-27: Missing argument `unit_price` in function `InventoryItem.__init__` [missing-argument] +ERROR dataclasses_usage.py:52:28-35: Argument `Literal['price']` is not assignable to parameter `unit_price` with type `float` in function `InventoryItem.__init__` [bad-argument-type] +ERROR dataclasses_usage.py:53:36-37: Expected 3 positional arguments, got 4 in function `InventoryItem.__init__` [bad-argument-count] +ERROR dataclasses_usage.py:62:5-6: Dataclass field `b` without a default may not follow dataclass field with a default [bad-class-definition] +ERROR dataclasses_usage.py:68:5-6: Dataclass field `b` without a default may not follow dataclass field with a default [bad-class-definition] +ERROR dataclasses_usage.py:74:5-6: Dataclass field `b` without a default may not follow dataclass field with a default [bad-class-definition] +ERROR dataclasses_usage.py:84:13-14: Expected 1 positional argument, got 2 in function `DC4.__init__` [bad-argument-count] +ERROR dataclasses_usage.py:89:14-40: `str` is not assignable to `int` [bad-assignment] +ERROR dataclasses_usage.py:128:8-9: Expected 1 positional argument, got 2 in function `DC7.__init__` [bad-argument-count] +ERROR dataclasses_usage.py:131:4-7: Missing argument `y` in function `DC8.__init__` [missing-argument] +ERROR dataclasses_usage.py:180:6-7: Expected 0 positional arguments, got 1 in function `object.__init__` [bad-argument-count] +ERROR dataclasses_usage.py:246:12-13: Expected 2 positional arguments, got 3 in function `DC19.__init__` [bad-argument-count] +""" diff --git a/conformance/results/pyrefly/directives_assert_type.toml b/conformance/results/pyrefly/directives_assert_type.toml new file mode 100644 index 000000000..bafeb7409 --- /dev/null +++ b/conformance/results/pyrefly/directives_assert_type.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR directives_assert_type.py:27:16-24: assert_type(int | str, int) failed [assert-type] +ERROR directives_assert_type.py:28:16-24: assert_type(int | str, Any) failed [assert-type] +ERROR directives_assert_type.py:29:16-24: assert_type(Any, int) failed [assert-type] +ERROR directives_assert_type.py:30:16-24: assert_type(Literal[4], int) failed [assert-type] +ERROR directives_assert_type.py:32:16-18: assert_type needs 2 positional arguments, got 0 [bad-argument-count] +ERROR directives_assert_type.py:33:16-25: assert_type(Literal[''], int) failed [assert-type] +ERROR directives_assert_type.py:34:16-33: assert_type needs 2 positional arguments, got 3 [bad-argument-count] +""" diff --git a/conformance/results/pyrefly/directives_cast.toml b/conformance/results/pyrefly/directives_cast.toml new file mode 100644 index 000000000..e4e745aef --- /dev/null +++ b/conformance/results/pyrefly/directives_cast.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR directives_cast.py:15:12-14: `typing.cast` missing required argument `typ` [missing-argument] +ERROR directives_cast.py:15:12-14: `typing.cast` missing required argument `val` [missing-argument] +ERROR directives_cast.py:16:12-19: First argument to `typing.cast` must be a type [bad-argument-type] +ERROR directives_cast.py:17:12-25: `typing.cast` expected 2 arguments, got 3 [bad-argument-count] +""" diff --git a/conformance/results/pyrefly/directives_deprecated.toml b/conformance/results/pyrefly/directives_deprecated.toml new file mode 100644 index 000000000..9373c54f4 --- /dev/null +++ b/conformance/results/pyrefly/directives_deprecated.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ + WARN directives_deprecated.py:18:44-47: `Ham` is deprecated [deprecated] + WARN directives_deprecated.py:24:1-23: `_directives_deprecated_library.norwegian_blue` is deprecated [deprecated] + WARN directives_deprecated.py:25:5-27: `_directives_deprecated_library.norwegian_blue` is deprecated [deprecated] + WARN directives_deprecated.py:30:12-15: Call to deprecated overload `_directives_deprecated_library.foo` [deprecated] + WARN directives_deprecated.py:41:5-13: `+` is not supported between `Spam` and `Literal[1]` [deprecated] + WARN directives_deprecated.py:42:1-10: `+=` is not supported between `Spam` and `Literal[1]` [deprecated] + WARN directives_deprecated.py:44:1-12: `_directives_deprecated_library.Spam.greasy` is deprecated [deprecated] + WARN directives_deprecated.py:47:1-11: `_directives_deprecated_library.Spam.shape` is deprecated [deprecated] + WARN directives_deprecated.py:48:1-11: `_directives_deprecated_library.Spam.shape` is deprecated [deprecated] + WARN directives_deprecated.py:58:1-10: `Invocable.__call__` is deprecated [deprecated] + WARN directives_deprecated.py:69:1-6: `lorem` is deprecated [deprecated] + WARN directives_deprecated.py:98:5-10: `SupportsFoo1.foo` is deprecated [deprecated] +""" diff --git a/conformance/results/pyrefly/directives_disjoint_base.toml b/conformance/results/pyrefly/directives_disjoint_base.toml new file mode 100644 index 000000000..d4c242b28 --- /dev/null +++ b/conformance/results/pyrefly/directives_disjoint_base.toml @@ -0,0 +1,19 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +notes = """ +Does not support PEP 800 disjoint-base semantics. +""" +errors_diff = """ +Line 69: Expected 1 errors +Line 73: Expected 1 errors +Line 77: Expected 1 errors +Line 118: Expected 1 errors +Line 123: Expected 1 errors +Line 60: Unexpected errors ['Named tuples do not support multiple inheritance [invalid-inheritance]'] +""" +output = """ +ERROR directives_disjoint_base.py:60:7-18: Named tuples do not support multiple inheritance [invalid-inheritance] +ERROR directives_disjoint_base.py:81:7-17: Named tuples do not support multiple inheritance [invalid-inheritance] +ERROR directives_disjoint_base.py:105:7-24: Class `IncompatibleSlots` has multiple base classes with non-empty `__slots__` (`SlotBase1`, `SlotBase2`), which causes a TypeError at runtime [invalid-inheritance] +ERROR directives_disjoint_base.py:113:1-15: `() -> None` is not assignable to upper bound `type[object]` of type variable `_TC` [bad-specialization] +""" diff --git a/conformance/results/pyrefly/directives_no_type_check.toml b/conformance/results/pyrefly/directives_no_type_check.toml new file mode 100644 index 000000000..52623ac35 --- /dev/null +++ b/conformance/results/pyrefly/directives_no_type_check.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR directives_no_type_check.py:15:14-16: `Literal['']` is not assignable to `int` [bad-assignment] +ERROR directives_no_type_check.py:32:6-8: Missing argument `a` in function `func1` [missing-argument] +ERROR directives_no_type_check.py:32:6-8: Missing argument `b` in function `func1` [missing-argument] +""" diff --git a/conformance/results/pyrefly/directives_reveal_type.toml b/conformance/results/pyrefly/directives_reveal_type.toml new file mode 100644 index 000000000..5cd063da4 --- /dev/null +++ b/conformance/results/pyrefly/directives_reveal_type.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR directives_reveal_type.py:19:16-18: reveal_type needs 1 positional argument, got 0 [bad-argument-count] +ERROR directives_reveal_type.py:20:16-22: reveal_type needs 1 positional argument, got 2 [bad-argument-count] +""" diff --git a/conformance/results/pyrefly/directives_type_checking.toml b/conformance/results/pyrefly/directives_type_checking.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/directives_type_checking.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/directives_type_ignore.toml b/conformance/results/pyrefly/directives_type_ignore.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/directives_type_ignore.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/directives_type_ignore_file1.toml b/conformance/results/pyrefly/directives_type_ignore_file1.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/directives_type_ignore_file1.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/directives_type_ignore_file2.toml b/conformance/results/pyrefly/directives_type_ignore_file2.toml new file mode 100644 index 000000000..ae2a41fdc --- /dev/null +++ b/conformance/results/pyrefly/directives_type_ignore_file2.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR directives_type_ignore_file2.py:14:10-12: `Literal['']` is not assignable to `int` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/directives_version_platform.toml b/conformance/results/pyrefly/directives_version_platform.toml new file mode 100644 index 000000000..562bdae49 --- /dev/null +++ b/conformance/results/pyrefly/directives_version_platform.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR directives_version_platform.py:33:19-23: Could not find name `val3` [unknown-name] +ERROR directives_version_platform.py:50:19-23: Could not find name `val6` [unknown-name] +ERROR directives_version_platform.py:59:19-23: Could not find name `val9` [unknown-name] +ERROR directives_version_platform.py:66:19-24: Could not find name `val10` [unknown-name] +ERROR directives_version_platform.py:75:19-24: Could not find name `val13` [unknown-name] +""" diff --git a/conformance/results/pyrefly/enums_behaviors.toml b/conformance/results/pyrefly/enums_behaviors.toml new file mode 100644 index 000000000..150f59c2c --- /dev/null +++ b/conformance/results/pyrefly/enums_behaviors.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR enums_behaviors.py:27:12-33: assert_type(Literal[Color.RED], Color) failed [assert-type] +ERROR enums_behaviors.py:32:12-43: assert_type(Color, Literal[Color.BLUE]) failed [assert-type] +ERROR enums_behaviors.py:44:21-26: Cannot extend final class `Shape` [invalid-inheritance] +""" diff --git a/conformance/results/pyrefly/enums_definition.toml b/conformance/results/pyrefly/enums_definition.toml new file mode 100644 index 000000000..22ed73f5d --- /dev/null +++ b/conformance/results/pyrefly/enums_definition.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR enums_definition.py:92:1-13: Class `Color12` has no class attribute `BLUE` [missing-attribute] +""" diff --git a/conformance/results/pyrefly/enums_expansion.toml b/conformance/results/pyrefly/enums_expansion.toml new file mode 100644 index 000000000..bbf9ce7ce --- /dev/null +++ b/conformance/results/pyrefly/enums_expansion.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR enums_expansion.py:53:20-51: assert_type(CustomFlags, Literal[CustomFlags.FLAG3]) failed [assert-type] +""" diff --git a/conformance/results/pyrefly/enums_member_names.toml b/conformance/results/pyrefly/enums_member_names.toml new file mode 100644 index 000000000..f0f464fe6 --- /dev/null +++ b/conformance/results/pyrefly/enums_member_names.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR enums_member_names.py:30:16-65: assert_type(str, Literal['BLUE', 'GREEN', 'RED']) failed [assert-type] +""" diff --git a/conformance/results/pyrefly/enums_member_values.toml b/conformance/results/pyrefly/enums_member_values.toml new file mode 100644 index 000000000..5ac05b3dd --- /dev/null +++ b/conformance/results/pyrefly/enums_member_values.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR enums_member_values.py:30:16-51: assert_type(int, Literal[1, 2, 3]) failed [assert-type] +ERROR enums_member_values.py:54:12-46: assert_type(Any, Literal[1]) failed [assert-type] +ERROR enums_member_values.py:68:12-42: assert_type(int, Literal[1]) failed [assert-type] +ERROR enums_member_values.py:78:5-10: Enum member `GREEN` has type `Literal['green']`, must match the `_value_` attribute annotation of `int` [bad-assignment] +ERROR enums_member_values.py:85:24-29: `int` is not assignable to attribute `_value_` with type `str` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/enums_members.toml b/conformance/results/pyrefly/enums_members.toml new file mode 100644 index 000000000..c5352b243 --- /dev/null +++ b/conformance/results/pyrefly/enums_members.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR enums_members.py:54:5-8: Enum member `DOG` may not be annotated directly. Instead, annotate the `_value_` attribute. [invalid-annotation] +ERROR enums_members.py:86:20-34: `Pet4.converter` is not a valid enum member [invalid-literal] +ERROR enums_members.py:87:20-34: `Pet4.transform` is not a valid enum member [invalid-literal] +ERROR enums_members.py:88:18-30: `Pet4.species` is not a valid enum member [invalid-literal] +ERROR enums_members.py:89:16-26: `Pet4.speak` is not a valid enum member [invalid-literal] +ERROR enums_members.py:120:12-43: assert_type(int, Unknown) failed [assert-type] +ERROR enums_members.py:120:32-41: `Example.b` is not a valid enum member [invalid-literal] +ERROR enums_members.py:133:20-57: assert_type(int, Unknown) failed [assert-type] +ERROR enums_members.py:133:43-55: `Example2.__B` is not a valid enum member [invalid-literal] +ERROR enums_members.py:150:12-27: assert_type(Literal[Pet5.DOG], int) failed [assert-type] +ERROR enums_members.py:151:12-28: assert_type(Literal[Pet5.FISH], int) failed [assert-type] +""" diff --git a/conformance/results/pyrefly/exceptions_context_managers.toml b/conformance/results/pyrefly/exceptions_context_managers.toml new file mode 100644 index 000000000..d9b225e74 --- /dev/null +++ b/conformance/results/pyrefly/exceptions_context_managers.toml @@ -0,0 +1,13 @@ +conformant = "Partial" +notes = """ +Some error suppressing context managers are not detected +""" +conformance_automated = "Fail" +errors_diff = """ +Line 50: Unexpected errors ['assert_type(str, int | str) failed [assert-type]'] +Line 57: Unexpected errors ['assert_type(str, int | str) failed [assert-type]'] +""" +output = """ +ERROR exceptions_context_managers.py:50:16-30: assert_type(str, int | str) failed [assert-type] +ERROR exceptions_context_managers.py:57:16-30: assert_type(str, int | str) failed [assert-type] +""" diff --git a/conformance/results/pyrefly/generics_base_class.toml b/conformance/results/pyrefly/generics_base_class.toml new file mode 100644 index 000000000..b1434372f --- /dev/null +++ b/conformance/results/pyrefly/generics_base_class.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_base_class.py:26:26-27: Argument `SymbolTable` is not assignable to parameter `x` with type `dict[str, list[object]]` in function `takes_dict_incorrect` [bad-argument-type] +ERROR generics_base_class.py:29:14-24: `Generic` is not allowed in this context [invalid-annotation] +ERROR generics_base_class.py:30:8-15: Expected a type argument for `Generic` [invalid-annotation] +ERROR generics_base_class.py:49:22-42: Expected 1 type argument for `LinkedList`, got 2 [bad-specialization] +ERROR generics_base_class.py:61:18-34: Expected 1 type argument for `MyDict`, got 2 [bad-specialization] +ERROR generics_base_class.py:68:28-29: Duplicated type parameter declaration `T` [invalid-inheritance] +ERROR generics_base_class.py:98:7-15: Class `BadChild` has inconsistent type arguments for base class `Grandparent`: `Grandparent[T1, T2]` and `Grandparent[T2, T1]` [invalid-inheritance] +""" diff --git a/conformance/results/pyrefly/generics_basic.toml b/conformance/results/pyrefly/generics_basic.toml new file mode 100644 index 000000000..cc8c87d4b --- /dev/null +++ b/conformance/results/pyrefly/generics_basic.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_basic.py:40:15-16: Argument `bytes` is not assignable to parameter `y` with type `str` in function `concat` [bad-argument-type] +ERROR generics_basic.py:41:15-16: Argument `str` is not assignable to parameter `y` with type `bytes` in function `concat` [bad-argument-type] +ERROR generics_basic.py:49:18-48: Expected at least 2 constraints in TypeVar `BadConstraint1`, got 1 [invalid-type-var] +ERROR generics_basic.py:55:53-60: Type variable bounds and constraints must be concrete [invalid-annotation] +ERROR generics_basic.py:69:15-16: Argument `bytes` is not assignable to parameter `y` with type `str` in function `concat` [bad-argument-type] +ERROR generics_basic.py:121:24-25: Duplicated type parameter declaration `T` [invalid-inheritance] +ERROR generics_basic.py:157:8-9: Cannot index into `MyMap1[str, int]` [bad-index] +ERROR generics_basic.py:158:8-9: Cannot index into `MyMap2[int, str]` [bad-index] +ERROR generics_basic.py:162:7-11: Expected a type variable, got `int` [invalid-type-var] +ERROR generics_basic.py:163:7-11: Expected a type variable, got `int` [invalid-type-var] +ERROR generics_basic.py:171:7-11: Class `Bad3` uses type variables not specified in `Generic` or `Protocol` base [invalid-type-var] +ERROR generics_basic.py:172:7-11: Class `Bad4` uses type variables not specified in `Generic` or `Protocol` base [invalid-type-var] +ERROR generics_basic.py:208:7-26: Metaclass may not be an unbound generic [invalid-inheritance] +""" diff --git a/conformance/results/pyrefly/generics_defaults.toml b/conformance/results/pyrefly/generics_defaults.toml new file mode 100644 index 000000000..5570f83ca --- /dev/null +++ b/conformance/results/pyrefly/generics_defaults.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_defaults.py:24:7-31: Type parameter `T` without a default cannot follow type parameter `DefaultStrT` with a default [invalid-type-var] +ERROR generics_defaults.py:66:8-27: Expected 5 type arguments for `AllTheDefaults`, got 1 [bad-specialization] +ERROR generics_defaults.py:152:51-54: Expected default `int` of `Invalid1` to be assignable to the upper bound of `str` [invalid-type-var] +ERROR generics_defaults.py:159:52-55: Expected default `int` of `Invalid2` to be one of the following constraints: `float`, `str` [invalid-type-var] +ERROR generics_defaults.py:177:12-27: assert_type(int, Any) failed [assert-type] +ERROR generics_defaults.py:188:7-11: TypeVar `T5` with a default cannot follow TypeVarTuple `Ts` [invalid-type-var] +""" diff --git a/conformance/results/pyrefly/generics_defaults_referential.toml b/conformance/results/pyrefly/generics_defaults_referential.toml new file mode 100644 index 000000000..138472455 --- /dev/null +++ b/conformance/results/pyrefly/generics_defaults_referential.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_defaults_referential.py:37:17-18: Argument `str` is not assignable to parameter `b` with type `int` in function `Foo.__init__` [bad-argument-type] +ERROR generics_defaults_referential.py:38:14-15: Argument `str` is not assignable to parameter `a` with type `int` in function `Foo.__init__` [bad-argument-type] +ERROR generics_defaults_referential.py:54:7-13: Default of type parameter `Start2T` refers to out-of-scope type parameter `StopT` [invalid-type-var] +ERROR generics_defaults_referential.py:61:11-15: Default of type parameter `S2` refers to out-of-scope type parameter `S1` [invalid-type-var] +ERROR generics_defaults_referential.py:69:40-42: Expected default `TypeVar[X1]` of `Invalid1` to be assignable to the upper bound of `str` [invalid-type-var] +ERROR generics_defaults_referential.py:75:52-54: Expected default `TypeVar[Y1]` of `Invalid2` to be one of the following constraints: `float`, `str` [invalid-type-var] +ERROR generics_defaults_referential.py:79:63-65: Expected default `TypeVar[Y2]` of `AlsoInvalid2` to be one of the following constraints: `bool`, `complex` [invalid-type-var] +""" diff --git a/conformance/results/pyrefly/generics_defaults_specialization.toml b/conformance/results/pyrefly/generics_defaults_specialization.toml new file mode 100644 index 000000000..4b087764e --- /dev/null +++ b/conformance/results/pyrefly/generics_defaults_specialization.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_defaults_specialization.py:30:1-19: Expected 1 type argument for `MyAlias`, got 2 [bad-specialization] +ERROR generics_defaults_specialization.py:46:22-25: `type[Bar]` is not assignable to `type[Bar[int]]` [bad-assignment] +ERROR generics_defaults_specialization.py:56:1-9: Expected 0 type arguments for `Foo`, got 1 [bad-specialization] +""" diff --git a/conformance/results/pyrefly/generics_paramspec_basic.toml b/conformance/results/pyrefly/generics_paramspec_basic.toml new file mode 100644 index 000000000..b446dd177 --- /dev/null +++ b/conformance/results/pyrefly/generics_paramspec_basic.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_paramspec_basic.py:10:13-31: ParamSpec must be assigned to a variable named `NotIt` [invalid-param-spec] +ERROR generics_paramspec_basic.py:15:18-19: `ParamSpec` is not allowed in this context [invalid-annotation] +ERROR generics_paramspec_basic.py:23:14-15: `ParamSpec` is not allowed in this context [invalid-annotation] +ERROR generics_paramspec_basic.py:23:20-21: `ParamSpec` is not allowed in this context [invalid-annotation] +ERROR generics_paramspec_basic.py:27:14-33: `Concatenate[int, P]` is not allowed in this context [invalid-annotation] +ERROR generics_paramspec_basic.py:31:14-21: `ParamSpec` cannot be used for type parameter [invalid-param-spec] +ERROR generics_paramspec_basic.py:35:35-36: `ParamSpec` is not allowed in this context [invalid-annotation] +ERROR generics_paramspec_basic.py:39:18-19: `ParamSpec` is not allowed in this context [invalid-annotation] +ERROR generics_paramspec_basic.py:39:31-32: `ParamSpec` is not allowed in this context [invalid-annotation] +""" diff --git a/conformance/results/pyrefly/generics_paramspec_components.toml b/conformance/results/pyrefly/generics_paramspec_components.toml new file mode 100644 index 000000000..03d98dbde --- /dev/null +++ b/conformance/results/pyrefly/generics_paramspec_components.toml @@ -0,0 +1,25 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_paramspec_components.py:17:25-33: `ParamSpec` **kwargs is only allowed in a **kwargs annotation [invalid-annotation] +ERROR generics_paramspec_components.py:17:45-51: `ParamSpec` *args is only allowed in an *args annotation [invalid-annotation] +ERROR generics_paramspec_components.py:20:23-29: `ParamSpec` *args is only allowed in an *args annotation [invalid-annotation] +ERROR generics_paramspec_components.py:23:5-24:13: `ParamSpec` *args and **kwargs must be used together [invalid-param-spec] +ERROR generics_paramspec_components.py:23:46-52: `ParamSpec` *args is only allowed in an *args annotation [invalid-annotation] +ERROR generics_paramspec_components.py:26:5-27:13: `ParamSpec` *args and **kwargs must be used together [invalid-param-spec] +ERROR generics_paramspec_components.py:30:25-31: Expected a type form, got instance of `ParamSpecArgs` [not-a-type] +ERROR generics_paramspec_components.py:30:43-51: Expected a type form, got instance of `ParamSpecKwargs` [not-a-type] +ERROR generics_paramspec_components.py:35:18-24: `ParamSpec` *args is only allowed in an *args annotation [invalid-annotation] +ERROR generics_paramspec_components.py:36:20-28: `ParamSpec` **kwargs is only allowed in a **kwargs annotation [invalid-annotation] +ERROR generics_paramspec_components.py:38:5-39:13: `ParamSpec` *args and **kwargs must be used together [invalid-param-spec] +ERROR generics_paramspec_components.py:41:5-42:13: `ParamSpec` *args and **kwargs must be used together [invalid-param-spec] +ERROR generics_paramspec_components.py:49:10-27: Expected *-unpacked P.args and **-unpacked P.kwargs [invalid-param-spec] +ERROR generics_paramspec_components.py:51:11-12: Expected 0 positional arguments, got 1 [bad-argument-count] +ERROR generics_paramspec_components.py:60:28-34: Keyword-only parameter `s` may not appear after ParamSpec args parameter [bad-function-definition] +ERROR generics_paramspec_components.py:70:10-30: Expected *-unpacked P.args and **-unpacked P.kwargs [invalid-param-spec] +ERROR generics_paramspec_components.py:72:10-27: Expected 1 more positional argument [bad-argument-count] +ERROR generics_paramspec_components.py:83:13-14: Expected argument `x` to be positional in function `foo` [unexpected-keyword] +ERROR generics_paramspec_components.py:98:20-23: Argument `Literal['A']` is not assignable to parameter `a` with type `int` in function `twice` [bad-argument-type] +ERROR generics_paramspec_components.py:98:25-26: Argument `Literal[1]` is not assignable to parameter `b` with type `str` in function `twice` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/generics_paramspec_semantics.toml b/conformance/results/pyrefly/generics_paramspec_semantics.toml new file mode 100644 index 000000000..a62bb475d --- /dev/null +++ b/conformance/results/pyrefly/generics_paramspec_semantics.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_paramspec_semantics.py:26:4-5: Expected argument `a` to be positional [unexpected-keyword] +ERROR generics_paramspec_semantics.py:26:11-12: Expected argument `b` to be positional [unexpected-keyword] +ERROR generics_paramspec_semantics.py:27:9-12: Argument `Literal['A']` is not assignable to parameter `b` with type `bool` [bad-argument-type] +ERROR generics_paramspec_semantics.py:46:17-20: Argument `(y: int, x: str) -> int` is not assignable to parameter `y` with type `(x: int, y: str) -> int` in function `func1` [bad-argument-type] +ERROR generics_paramspec_semantics.py:61:23-37: Argument `(*, y: int) -> int` is not assignable to parameter `y` with type `(*, x: int) -> int` in function `func1` [bad-argument-type] +ERROR generics_paramspec_semantics.py:98:4-5: Argument `Literal[1]` is not assignable to parameter with type `str` [bad-argument-type] +ERROR generics_paramspec_semantics.py:108:4-5: Argument `Literal[1]` is not assignable to parameter `*args` with type `bool` [bad-argument-type] +ERROR generics_paramspec_semantics.py:120:4-5: Argument `Literal[1]` is not assignable to parameter with type `str` [bad-argument-type] +ERROR generics_paramspec_semantics.py:127:1-19: Argument `(x: str) -> int` is not assignable to parameter `x` with type `(int, ParamSpec(@_)) -> int` in function `expects_int_first` [bad-argument-type] +ERROR generics_paramspec_semantics.py:132:1-19: Argument `(*, x: int) -> int` is not assignable to parameter `x` with type `(int, ParamSpec(@_)) -> int` in function `expects_int_first` [bad-argument-type] +ERROR generics_paramspec_semantics.py:137:1-19: Argument `(**kwargs: int) -> int` is not assignable to parameter `x` with type `(int, ParamSpec(@_)) -> int` in function `expects_int_first` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/generics_paramspec_specialization.toml b/conformance/results/pyrefly/generics_paramspec_specialization.toml new file mode 100644 index 000000000..a835b8d19 --- /dev/null +++ b/conformance/results/pyrefly/generics_paramspec_specialization.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_paramspec_specialization.py:44:15-31: Expected a valid ParamSpec expression, got `int` [invalid-param-spec] +ERROR generics_paramspec_specialization.py:54:9-11: Argument `Literal['']` is not assignable to parameter with type `int` [bad-argument-type] +ERROR generics_paramspec_specialization.py:55:16-18: Argument `Literal['']` is not assignable to parameter with type `bool` [bad-argument-type] +ERROR generics_paramspec_specialization.py:60:9-11: Argument `Literal['']` is not assignable to parameter with type `int` [bad-argument-type] +ERROR generics_paramspec_specialization.py:61:16-18: Argument `Literal['']` is not assignable to parameter with type `bool` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/generics_scoping.toml b/conformance/results/pyrefly/generics_scoping.toml new file mode 100644 index 000000000..236c49f06 --- /dev/null +++ b/conformance/results/pyrefly/generics_scoping.toml @@ -0,0 +1,23 @@ +conformant = "Partial" +notes = """ +Does not implement several scoping checks/restrictions for generics +""" +conformance_automated = "Fail" +errors_diff = """ +Line 86: Expected 1 errors +Line 89: Expected 1 errors +Line 98: Expected 1 errors +Line 107: Expected 1 errors +""" +output = """ +ERROR generics_scoping.py:16:12-34: assert_type(int, Literal[1]) failed [assert-type] +ERROR generics_scoping.py:20:12-38: assert_type(str, Literal['a']) failed [assert-type] +ERROR generics_scoping.py:34:10-13: Argument `Literal['a']` is not assignable to parameter `x` with type `int` in function `MyClass.meth_2` [bad-argument-type] +ERROR generics_scoping.py:50:12-48: assert_type(str, Literal['abc']) failed [assert-type] +ERROR generics_scoping.py:54:12-50: assert_type(bytes, Literal[b'abc']) failed [assert-type] +ERROR generics_scoping.py:61:8-15: Type variable `S` is not in scope [invalid-type-var] +ERROR generics_scoping.py:65:14-21: Type variable `S` is not in scope [invalid-type-var] +ERROR generics_scoping.py:76:11-20: Redundant type parameter declaration [invalid-type-var] +ERROR generics_scoping.py:105:14-15: Type variable `T` is not in scope [invalid-type-var] +ERROR generics_scoping.py:106:14-21: Type variable `T` is not in scope [invalid-type-var] +""" diff --git a/conformance/results/pyrefly/generics_self_advanced.toml b/conformance/results/pyrefly/generics_self_advanced.toml new file mode 100644 index 000000000..fb11f7c63 --- /dev/null +++ b/conformance/results/pyrefly/generics_self_advanced.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +notes = """ +Treats attributes not initialized on the class as instance-only +""" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/generics_self_attributes.toml b/conformance/results/pyrefly/generics_self_attributes.toml new file mode 100644 index 000000000..d1b89fa7c --- /dev/null +++ b/conformance/results/pyrefly/generics_self_attributes.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_self_attributes.py:26:38-62: Argument `LinkedList[int]` is not assignable to parameter `next` with type `OrdinalLinkedList | None` in function `OrdinalLinkedList.__init__` [bad-argument-type] +ERROR generics_self_attributes.py:32:15-50: `LinkedList[int]` is not assignable to attribute `next` with type `OrdinalLinkedList | None` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/generics_self_basic.toml b/conformance/results/pyrefly/generics_self_basic.toml new file mode 100644 index 000000000..07948c473 --- /dev/null +++ b/conformance/results/pyrefly/generics_self_basic.toml @@ -0,0 +1,12 @@ +conformant = "Partial" +notes = """ +Return annotation of Self allows returning the concrete instance of the current class. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 20: Expected 1 errors +Line 33: Expected 1 errors +""" +output = """ +ERROR generics_self_basic.py:68:26-35: `Self` may not be subscripted [invalid-annotation] +""" diff --git a/conformance/results/pyrefly/generics_self_protocols.toml b/conformance/results/pyrefly/generics_self_protocols.toml new file mode 100644 index 000000000..75aead9ff --- /dev/null +++ b/conformance/results/pyrefly/generics_self_protocols.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_self_protocols.py:61:19-34: Argument `BadReturnType` is not assignable to parameter `shape` with type `ShapeProtocol` in function `accepts_shape` [bad-argument-type] +ERROR generics_self_protocols.py:64:19-41: Argument `ReturnDifferentClass` is not assignable to parameter `shape` with type `ShapeProtocol` in function `accepts_shape` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/generics_self_usage.toml b/conformance/results/pyrefly/generics_self_usage.toml new file mode 100644 index 000000000..8d26f2592 --- /dev/null +++ b/conformance/results/pyrefly/generics_self_usage.toml @@ -0,0 +1,22 @@ +conformant = "Partial" +notes = """ +Does not implement some restrictions on where Self can be used +""" +conformance_automated = "Fail" +errors_diff = """ +Line 87: Expected 1 errors +""" +output = """ +ERROR generics_self_usage.py:73:14-18: `Self` must appear within a class [invalid-annotation] +ERROR generics_self_usage.py:73:23-27: `Self` must appear within a class [invalid-annotation] +ERROR generics_self_usage.py:76:6-10: `Self` must appear within a class [invalid-annotation] +ERROR generics_self_usage.py:82:9-37: `Self` cannot be used when `self` has an explicit TypeVar annotation [invalid-annotation] +ERROR generics_self_usage.py:103:15-19: `Self` must appear within a class [invalid-annotation] +ERROR generics_self_usage.py:105:12-16: `Self` must appear within a class [invalid-annotation] +ERROR generics_self_usage.py:105:12-16: Invalid base class: `Self` [invalid-inheritance] +ERROR generics_self_usage.py:108:30-34: `Self` must appear within a class [invalid-annotation] +ERROR generics_self_usage.py:113:9-13: `Self` cannot be used in a static method [invalid-annotation] +ERROR generics_self_usage.py:118:9-25: `Self` cannot be used in a static method [invalid-annotation] +ERROR generics_self_usage.py:123:37-41: `Self` cannot be used in a metaclass [invalid-annotation] +ERROR generics_self_usage.py:127:42-46: `Self` cannot be used in a metaclass [invalid-annotation] +""" diff --git a/conformance/results/pyrefly/generics_syntax_compatibility.toml b/conformance/results/pyrefly/generics_syntax_compatibility.toml new file mode 100644 index 000000000..d6e72a2c5 --- /dev/null +++ b/conformance/results/pyrefly/generics_syntax_compatibility.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_syntax_compatibility.py:14:7-13: Class `ClassA` uses type variables not specified in `Generic` or `Protocol` base [invalid-type-var] +ERROR generics_syntax_compatibility.py:14:22-23: Type parameter K is not included in the type parameter list [invalid-type-var] +ERROR generics_syntax_compatibility.py:26:35-36: Type parameter K is not included in the type parameter list [invalid-type-var] +""" diff --git a/conformance/results/pyrefly/generics_syntax_declarations.toml b/conformance/results/pyrefly/generics_syntax_declarations.toml new file mode 100644 index 000000000..d5b472029 --- /dev/null +++ b/conformance/results/pyrefly/generics_syntax_declarations.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_syntax_declarations.py:17:7-13: Redundant type parameter declaration [invalid-type-var] +ERROR generics_syntax_declarations.py:25:7-13: Redundant type parameter declaration [invalid-type-var] +ERROR generics_syntax_declarations.py:32:9-21: Object of class `str` has no attribute `is_integer` [missing-attribute] +ERROR generics_syntax_declarations.py:44:21-33: Type variable bounds and constraints must be concrete [invalid-annotation] +ERROR generics_syntax_declarations.py:48:17-27: Expected a type form, got instance of `list[type[int] | type[str]]` [not-a-type] +ERROR generics_syntax_declarations.py:60:17-19: Expected at least 2 constraints in TypeVar `T`, got 0 [invalid-type-var] +ERROR generics_syntax_declarations.py:64:17-23: Expected at least 2 constraints in TypeVar `T`, got 1 [invalid-type-var] +ERROR generics_syntax_declarations.py:71:17-19: Expected a type form, got instance of `tuple[type[bytes], type[str]]` [not-a-type] +ERROR generics_syntax_declarations.py:75:18-19: Expected a type form, got instance of `Literal[3]` [not-a-type] +ERROR generics_syntax_declarations.py:79:23-24: Could not find name `S` [unknown-name] +""" diff --git a/conformance/results/pyrefly/generics_syntax_infer_variance.toml b/conformance/results/pyrefly/generics_syntax_infer_variance.toml new file mode 100644 index 000000000..422d6031b --- /dev/null +++ b/conformance/results/pyrefly/generics_syntax_infer_variance.toml @@ -0,0 +1,24 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_syntax_infer_variance.py:15:36-55: Contradictory variance specifications [invalid-type-var] +ERROR generics_syntax_infer_variance.py:17:40-59: Contradictory variance specifications [invalid-type-var] +ERROR generics_syntax_infer_variance.py:29:35-62: `ShouldBeCovariant1[float]` is not assignable to `ShouldBeCovariant1[int]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:47:35-62: `ShouldBeCovariant2[float]` is not assignable to `ShouldBeCovariant2[int]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:56:35-62: `ShouldBeCovariant3[float]` is not assignable to `ShouldBeCovariant3[int]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:85:34-64: `ShouldBeCovariant5[float]` is not assignable to `ShouldBeCovariant5[int]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:96:34-64: `ShouldBeCovariant6[float]` is not assignable to `ShouldBeCovariant6[int]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:112:38-64: `ShouldBeInvariant1[int]` is not assignable to `ShouldBeInvariant1[float]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:113:36-66: `ShouldBeInvariant1[float]` is not assignable to `ShouldBeInvariant1[int]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:127:38-64: `ShouldBeInvariant2[int]` is not assignable to `ShouldBeInvariant2[float]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:128:36-66: `ShouldBeInvariant2[float]` is not assignable to `ShouldBeInvariant2[int]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:135:43-73: `ShouldBeInvariant3[int, str]` is not assignable to `ShouldBeInvariant3[float, str]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:136:41-73: `ShouldBeInvariant3[float, str]` is not assignable to `ShouldBeInvariant3[int, str]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:137:43-73: `ShouldBeInvariant3[str, int]` is not assignable to `ShouldBeInvariant3[str, float]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:138:41-73: `ShouldBeInvariant3[str, float]` is not assignable to `ShouldBeInvariant3[str, int]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:146:38-64: `ShouldBeInvariant4[int]` is not assignable to `ShouldBeInvariant4[float]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:154:38-64: `ShouldBeInvariant5[int]` is not assignable to `ShouldBeInvariant5[float]` [bad-assignment] +ERROR generics_syntax_infer_variance.py:165:45-75: `ShouldBeContravariant1[int]` is not assignable to `ShouldBeContravariant1[float]` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/generics_syntax_scoping.toml b/conformance/results/pyrefly/generics_syntax_scoping.toml new file mode 100644 index 000000000..6d7c35a63 --- /dev/null +++ b/conformance/results/pyrefly/generics_syntax_scoping.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_syntax_scoping.py:14:20-31: Type variable bounds and constraints must be concrete [invalid-annotation] +ERROR generics_syntax_scoping.py:18:26-27: Expected a type form, got instance of `int` [not-a-type] +ERROR generics_syntax_scoping.py:35:7-8: `T` is uninitialized [unbound-name] +ERROR generics_syntax_scoping.py:44:17-18: Expected a type form, got instance of `int` [not-a-type] +ERROR generics_syntax_scoping.py:92:17-18: Type parameter `T` shadows a type parameter of the same name from an enclosing scope [invalid-type-var] +ERROR generics_syntax_scoping.py:95:17-18: Type parameter `T` shadows a type parameter of the same name from an enclosing scope [invalid-type-var] +ERROR generics_syntax_scoping.py:98:17-18: Type parameter `T` shadows a type parameter of the same name from an enclosing scope [invalid-type-var] +""" diff --git a/conformance/results/pyrefly/generics_type_erasure.toml b/conformance/results/pyrefly/generics_type_erasure.toml new file mode 100644 index 000000000..efb957387 --- /dev/null +++ b/conformance/results/pyrefly/generics_type_erasure.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_type_erasure.py:38:16-18: Argument `Literal['']` is not assignable to parameter `label` with type `int | None` in function `Node.__init__` [bad-argument-type] +ERROR generics_type_erasure.py:40:16-17: Argument `Literal[0]` is not assignable to parameter `label` with type `str | None` in function `Node.__init__` [bad-argument-type] +ERROR generics_type_erasure.py:42:1-16: Generic attribute `label` of class `Node` is not visible on the class [no-access] +ERROR generics_type_erasure.py:43:1-16: Generic attribute `label` of class `Node` is not visible on the class [missing-attribute] +ERROR generics_type_erasure.py:44:1-11: Generic attribute `label` of class `Node` is not visible on the class [no-access] +ERROR generics_type_erasure.py:45:1-11: Generic attribute `label` of class `Node` is not visible on the class [missing-attribute] +ERROR generics_type_erasure.py:46:1-15: Generic attribute `label` of class `Node` is not visible on the class [missing-attribute] +""" diff --git a/conformance/results/pyrefly/generics_typevartuple_args.toml b/conformance/results/pyrefly/generics_typevartuple_args.toml new file mode 100644 index 000000000..9db9ebae3 --- /dev/null +++ b/conformance/results/pyrefly/generics_typevartuple_args.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_typevartuple_args.py:33:12-23: Unpacked argument `tuple[Literal[0], Literal['']]` is not assignable to parameter `*args` with type `tuple[*@_, Env]` in function `exec_le` [bad-argument-type] +ERROR generics_typevartuple_args.py:34:12-34: Unpacked argument `tuple[Literal[0], Literal['']]` is not assignable to parameter `*args` with type `tuple[*@_, Env]` in function `exec_le` [bad-argument-type] +ERROR generics_typevartuple_args.py:48:6-17: Unpacked argument `tuple[Literal[1], Literal['2'], Literal[3]]` is not assignable to parameter `*args` with type `tuple[int, ...]` in function `func1` [bad-argument-type] +ERROR generics_typevartuple_args.py:57:6-16: Unpacked argument `tuple[Literal[1], Literal[1], Literal['']]` is not assignable to parameter `*args` with type `tuple[int, *tuple[str, ...], str]` in function `func2` [bad-argument-type] +ERROR generics_typevartuple_args.py:58:6-9: Unpacked argument `tuple[Literal[1]]` is not assignable to parameter `*args` with type `tuple[int, *tuple[str, ...], str]` in function `func2` [bad-argument-type] +ERROR generics_typevartuple_args.py:59:6-10: Unpacked argument `tuple[Literal['']]` is not assignable to parameter `*args` with type `tuple[int, *tuple[str, ...], str]` in function `func2` [bad-argument-type] +ERROR generics_typevartuple_args.py:67:6-9: Expected 1 more positional argument in function `func3` [bad-argument-count] +ERROR generics_typevartuple_args.py:75:13-19: Argument `tuple[Literal[1], Literal[2]]` is not assignable to parameter `*args` with type `tuple[int]` in function `func4` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/generics_typevartuple_basic.toml b/conformance/results/pyrefly/generics_typevartuple_basic.toml new file mode 100644 index 000000000..e4437865d --- /dev/null +++ b/conformance/results/pyrefly/generics_typevartuple_basic.toml @@ -0,0 +1,20 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_typevartuple_basic.py:42:34-43: Argument `Height` is not assignable to parameter `shape` with type `tuple[*@_]` in function `Array.__init__` [bad-argument-type] +ERROR generics_typevartuple_basic.py:43:35-62: `Array[Batch, Width]` is not assignable to `Array[Batch, Height, Width]` [bad-assignment] +ERROR generics_typevartuple_basic.py:44:41-46:2: `Array[Time, Batch, Width, Height]` is not assignable to `Array[Time, Batch, Height, Width]` [bad-assignment] +ERROR generics_typevartuple_basic.py:52:22-27: `TypeVarTuple` must be unpacked [invalid-annotation] +ERROR generics_typevartuple_basic.py:53:37-42: `TypeVarTuple` must be unpacked [invalid-type-var-tuple] +ERROR generics_typevartuple_basic.py:56:34-39: `TypeVarTuple` must be unpacked [invalid-type-var-tuple] +ERROR generics_typevartuple_basic.py:59:24-29: `TypeVarTuple` must be unpacked [invalid-annotation] +ERROR generics_typevartuple_basic.py:65:27-41: Unexpected keyword argument `covariant` to TypeVarTuple [invalid-type-var-tuple] +ERROR generics_typevartuple_basic.py:66:27-30: Unexpected positional argument to TypeVarTuple [invalid-type-var-tuple] +ERROR generics_typevartuple_basic.py:67:27-36: Unexpected keyword argument `bound` to TypeVarTuple [invalid-type-var-tuple] +ERROR generics_typevartuple_basic.py:91:15-19: Argument `tuple[Literal[0]]` is not assignable to parameter `arg2` with type `tuple[int, int]` in function `func2` [bad-argument-type] +ERROR generics_typevartuple_basic.py:100:17-18: Argument `Array[Width]` is not assignable to parameter `y` with type `Array[Height]` in function `multiply` [bad-argument-type] +ERROR generics_typevartuple_basic.py:101:17-18: Argument `Array[Height, Width]` is not assignable to parameter `y` with type `Array[Height]` in function `multiply` [bad-argument-type] +ERROR generics_typevartuple_basic.py:107:7-13: Type parameters for class may not have more than one TypeVarTuple [invalid-type-var-tuple] +""" diff --git a/conformance/results/pyrefly/generics_typevartuple_callable.toml b/conformance/results/pyrefly/generics_typevartuple_callable.toml new file mode 100644 index 000000000..4c3a32c03 --- /dev/null +++ b/conformance/results/pyrefly/generics_typevartuple_callable.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_typevartuple_callable.py:26:28-35: Argument `tuple[Literal[''], Literal[0]]` is not assignable to parameter `args` with type `tuple[int, str]` in function `Process.__init__` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/generics_typevartuple_concat.toml b/conformance/results/pyrefly/generics_typevartuple_concat.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/generics_typevartuple_concat.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/generics_typevartuple_overloads.toml b/conformance/results/pyrefly/generics_typevartuple_overloads.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/generics_typevartuple_overloads.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/generics_typevartuple_specialization.toml b/conformance/results/pyrefly/generics_typevartuple_specialization.toml new file mode 100644 index 000000000..e5dcdaff5 --- /dev/null +++ b/conformance/results/pyrefly/generics_typevartuple_specialization.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_typevartuple_specialization.py:109:1-21: Unpacked argument cannot be used for type parameter T [bad-unpacking] +ERROR generics_typevartuple_specialization.py:110:1-36: Unpacked argument cannot be used for type parameter T [bad-unpacking] +ERROR generics_typevartuple_specialization.py:121:26-29: Only one unbounded type is allowed to be unpacked [bad-unpacking] +ERROR generics_typevartuple_specialization.py:122:26-42: Only one unbounded type is allowed to be unpacked [bad-unpacking] +ERROR generics_typevartuple_specialization.py:127:5-13: Expected 3 type arguments for `TA7`, got 1 [bad-specialization] +ERROR generics_typevartuple_specialization.py:163:8-18: Unpacked argument cannot be used for type parameter T [bad-unpacking] +""" diff --git a/conformance/results/pyrefly/generics_typevartuple_unpack.toml b/conformance/results/pyrefly/generics_typevartuple_unpack.toml new file mode 100644 index 000000000..300385ff4 --- /dev/null +++ b/conformance/results/pyrefly/generics_typevartuple_unpack.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_typevartuple_unpack.py:30:28-29: Argument `Array[Batch]` is not assignable to parameter `x` with type `Array[Batch, *tuple[Any, ...], Channels]` in function `process_batch_channels` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/generics_upper_bound.toml b/conformance/results/pyrefly/generics_upper_bound.toml new file mode 100644 index 000000000..58dd424cd --- /dev/null +++ b/conformance/results/pyrefly/generics_upper_bound.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +conformant = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_upper_bound.py:24:38-45: Type variable bounds and constraints must be concrete [invalid-annotation] +ERROR generics_upper_bound.py:44:16-54: assert_type(list[int] | set[int], Collection[int]) failed [assert-type] +ERROR generics_upper_bound.py:52:7-13: `int` is not assignable to upper bound `Sized` of type variable `ST` [bad-specialization] +ERROR generics_upper_bound.py:57:38-49: TypeVar cannot have both constraints and bound [invalid-type-var] +""" diff --git a/conformance/results/pyrefly/generics_variance.toml b/conformance/results/pyrefly/generics_variance.toml new file mode 100644 index 000000000..ea219502b --- /dev/null +++ b/conformance/results/pyrefly/generics_variance.toml @@ -0,0 +1,19 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_variance.py:14:36-54: Contradictory variance specifications [invalid-type-var] +ERROR generics_variance.py:77:14-23: Type variable `T_co` is covariant but is used in invariant position [invalid-variance] +ERROR generics_variance.py:81:14-27: Type variable `T_contra` is contravariant but is used in invariant position [invalid-variance] +ERROR generics_variance.py:93:17-29: Type variable `T_contra` is contravariant but is used in covariant position [invalid-variance] +ERROR generics_variance.py:105:21-33: Type variable `T_co` is covariant but is used in contravariant position [invalid-variance] +ERROR generics_variance.py:113:21-37: Type variable `T_co` is covariant but is used in contravariant position [invalid-variance] +ERROR generics_variance.py:126:5-25: Type variable `T_co` is covariant but is used in contravariant position [invalid-variance] +ERROR generics_variance.py:132:5-33: Type variable `T_contra` is contravariant but is used in covariant position [invalid-variance] +ERROR generics_variance.py:142:5-33: Type variable `T_co` is covariant but is used in contravariant position [invalid-variance] +ERROR generics_variance.py:163:26-54: Type variable `T_contra` is contravariant but is used in covariant position [invalid-variance] +ERROR generics_variance.py:167:30-58: Type variable `T_co` is covariant but is used in contravariant position [invalid-variance] +ERROR generics_variance.py:191:33-70: Type variable `T_contra` is contravariant but is used in covariant position [invalid-variance] +ERROR generics_variance.py:196:5-42: Type variable `T_co` is covariant but is used in contravariant position [invalid-variance] +""" diff --git a/conformance/results/pyrefly/generics_variance_inference.toml b/conformance/results/pyrefly/generics_variance_inference.toml new file mode 100644 index 000000000..8035068c3 --- /dev/null +++ b/conformance/results/pyrefly/generics_variance_inference.toml @@ -0,0 +1,29 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR generics_variance_inference.py:24:33-35: `ClassA[float, int, int]` is not assignable to `ClassA[int, int, int]` [bad-assignment] +ERROR generics_variance_inference.py:25:37-39: `ClassA[float, int, int]` is not assignable to `ClassA[float, float, int]` [bad-assignment] +ERROR generics_variance_inference.py:28:33-35: `ClassA[int, float, float]` is not assignable to `ClassA[int, int, int]` [bad-assignment] +ERROR generics_variance_inference.py:41:35-62: `ShouldBeCovariant1[float]` is not assignable to `ShouldBeCovariant1[int]` [bad-assignment] +ERROR generics_variance_inference.py:49:35-62: `ShouldBeCovariant2[float]` is not assignable to `ShouldBeCovariant2[int]` [bad-assignment] +ERROR generics_variance_inference.py:58:35-62: `ShouldBeCovariant3[float]` is not assignable to `ShouldBeCovariant3[int]` [bad-assignment] +ERROR generics_variance_inference.py:67:34-62: `ShouldBeCovariant4[float]` is not assignable to `ShouldBeCovariant4[int]` [bad-assignment] +ERROR generics_variance_inference.py:80:34-62: `ShouldBeCovariant5[float]` is not assignable to `ShouldBeCovariant5[int]` [bad-assignment] +ERROR generics_variance_inference.py:96:38-64: `ShouldBeInvariant1[int]` is not assignable to `ShouldBeInvariant1[float]` [bad-assignment] +ERROR generics_variance_inference.py:97:36-66: `ShouldBeInvariant1[float]` is not assignable to `ShouldBeInvariant1[int]` [bad-assignment] +ERROR generics_variance_inference.py:111:38-64: `ShouldBeInvariant2[int]` is not assignable to `ShouldBeInvariant2[float]` [bad-assignment] +ERROR generics_variance_inference.py:112:36-66: `ShouldBeInvariant2[float]` is not assignable to `ShouldBeInvariant2[int]` [bad-assignment] +ERROR generics_variance_inference.py:119:43-73: `ShouldBeInvariant3[int, str]` is not assignable to `ShouldBeInvariant3[float, str]` [bad-assignment] +ERROR generics_variance_inference.py:120:41-73: `ShouldBeInvariant3[float, str]` is not assignable to `ShouldBeInvariant3[int, str]` [bad-assignment] +ERROR generics_variance_inference.py:121:43-73: `ShouldBeInvariant3[str, int]` is not assignable to `ShouldBeInvariant3[str, float]` [bad-assignment] +ERROR generics_variance_inference.py:122:41-73: `ShouldBeInvariant3[str, float]` is not assignable to `ShouldBeInvariant3[str, int]` [bad-assignment] +ERROR generics_variance_inference.py:130:38-64: `ShouldBeInvariant4[int]` is not assignable to `ShouldBeInvariant4[float]` [bad-assignment] +ERROR generics_variance_inference.py:138:38-64: `ShouldBeInvariant5[int]` is not assignable to `ShouldBeInvariant5[float]` [bad-assignment] +ERROR generics_variance_inference.py:149:45-75: `ShouldBeContravariant1[int]` is not assignable to `ShouldBeContravariant1[float]` [bad-assignment] +ERROR generics_variance_inference.py:169:31-58: `ShouldBeInvariant6[float]` is not assignable to `ShouldBeInvariant6[int]` [bad-assignment] +ERROR generics_variance_inference.py:170:33-58: `ShouldBeInvariant6[int]` is not assignable to `ShouldBeInvariant6[float]` [bad-assignment] +ERROR generics_variance_inference.py:181:31-58: `ShouldBeCovariant6[float]` is not assignable to `ShouldBeCovariant6[int]` [bad-assignment] +ERROR generics_variance_inference.py:194:37-66: `ShouldBeContravariant2[int]` is not assignable to `ShouldBeContravariant2[float]` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/historical_positional.toml b/conformance/results/pyrefly/historical_positional.toml new file mode 100644 index 000000000..3242ab5da --- /dev/null +++ b/conformance/results/pyrefly/historical_positional.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR historical_positional.py:18:4-7: Expected argument `__x` to be positional in function `f1` [unexpected-keyword] +ERROR historical_positional.py:26:16-19: Positional-only parameter `__y` cannot appear after keyword parameters [bad-function-definition] +ERROR historical_positional.py:54:26-29: Positional-only parameter `__y` cannot appear after keyword parameters [bad-function-definition] +ERROR historical_positional.py:59:6-9: Expected argument `__x` to be positional in function `A.m1` [unexpected-keyword] +""" diff --git a/conformance/results/pyrefly/literals_interactions.toml b/conformance/results/pyrefly/literals_interactions.toml new file mode 100644 index 000000000..451fa2d4b --- /dev/null +++ b/conformance/results/pyrefly/literals_interactions.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR literals_interactions.py:14:7-8: Index 5 out of range for tuple with 3 elements [bad-index] +ERROR literals_interactions.py:15:7-8: Index -5 out of range for tuple with 3 elements [bad-index] +ERROR literals_interactions.py:16:7-8: Index 4 out of range for tuple with 3 elements [bad-index] +ERROR literals_interactions.py:17:7-9: Index -4 out of range for tuple with 3 elements [bad-index] +""" diff --git a/conformance/results/pyrefly/literals_literalstring.toml b/conformance/results/pyrefly/literals_literalstring.toml new file mode 100644 index 000000000..201849395 --- /dev/null +++ b/conformance/results/pyrefly/literals_literalstring.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR literals_literalstring.py:36:29-42: Invalid type inside literal, `LiteralString` [invalid-literal] +ERROR literals_literalstring.py:37:22-35: Invalid type inside literal, `LiteralString` [invalid-literal] +ERROR literals_literalstring.py:43:23-24: `Literal['two']` is not assignable to `Literal['']` [bad-assignment] +ERROR literals_literalstring.py:65:25-45: `str` is not assignable to `LiteralString` [bad-assignment] +ERROR literals_literalstring.py:73:25-26: `Literal[3]` is not assignable to `LiteralString` [bad-assignment] +ERROR literals_literalstring.py:74:25-32: `Literal[b'test']` is not assignable to `LiteralString` [bad-assignment] +ERROR literals_literalstring.py:119:21-24: `str` is not assignable to upper bound `LiteralString` of type variable `TLiteral` [bad-specialization] +ERROR literals_literalstring.py:133:41-53: `Container[str]` is not assignable to `Container[LiteralString]` [bad-assignment] +ERROR literals_literalstring.py:133:50-53: `str` is not assignable to upper bound `LiteralString` of type variable `T` [bad-specialization] +ERROR literals_literalstring.py:171:21-24: `list[LiteralString]` is not assignable to `list[str]` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/literals_parameterizations.toml b/conformance/results/pyrefly/literals_parameterizations.toml new file mode 100644 index 000000000..e31da9df2 --- /dev/null +++ b/conformance/results/pyrefly/literals_parameterizations.toml @@ -0,0 +1,23 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR literals_parameterizations.py:41:15-20: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:42:15-38: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:43:15-21: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:44:15-17: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:45:15-24: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:46:7-33: `Literal` arguments cannot be parenthesized [invalid-literal] +ERROR literals_parameterizations.py:47:15-35: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:48:15-18: Invalid type inside literal, `int` [invalid-literal] +ERROR literals_parameterizations.py:49:15-23: Expected a type form, got instance of `Literal[3]` [not-a-type] +ERROR literals_parameterizations.py:50:16-17: Invalid type inside literal, `TypeVar[T]` [invalid-literal] +ERROR literals_parameterizations.py:51:16-20: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:52:16-19: Invalid type inside literal, `Any` [invalid-literal] +ERROR literals_parameterizations.py:53:16-19: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:56:28-33: Invalid literal expression [invalid-literal] +ERROR literals_parameterizations.py:60:4-11: Expected a type argument for `Literal` [invalid-annotation] +ERROR literals_parameterizations.py:61:12-23: Expected a type form, got instance of `(x: Unknown) -> int` [not-a-type] +ERROR literals_parameterizations.py:65:32-33: `Literal[Color.RED]` is not assignable to `Literal['Color.RED']` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/literals_semantics.toml b/conformance/results/pyrefly/literals_semantics.toml new file mode 100644 index 000000000..89fc09ba8 --- /dev/null +++ b/conformance/results/pyrefly/literals_semantics.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR literals_semantics.py:10:18-19: `Literal[4]` is not assignable to `Literal[3]` [bad-assignment] +ERROR literals_semantics.py:24:26-27: `Literal[0]` is not assignable to `Literal[False]` [bad-assignment] +ERROR literals_semantics.py:25:22-23: `Literal[False]` is not assignable to `Literal[0]` [bad-assignment] +ERROR literals_semantics.py:33:5-11: Augmented assignment result `int` is not assignable to `Literal[3, 4, 5]` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/namedtuples_define_class.toml b/conformance/results/pyrefly/namedtuples_define_class.toml new file mode 100644 index 000000000..e98b6c716 --- /dev/null +++ b/conformance/results/pyrefly/namedtuples_define_class.toml @@ -0,0 +1,21 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR namedtuples_define_class.py:33:10-11: Index 3 out of range for tuple with 3 elements [bad-index] +ERROR namedtuples_define_class.py:34:10-12: Index -4 out of range for tuple with 3 elements [bad-index] +ERROR namedtuples_define_class.py:45:11-14: Missing argument `y` in function `Point.__new__` [missing-argument] +ERROR namedtuples_define_class.py:46:11-16: Missing argument `y` in function `Point.__new__` [missing-argument] +ERROR namedtuples_define_class.py:47:15-17: Argument `Literal['']` is not assignable to parameter `y` with type `int` in function `Point.__new__` [bad-argument-type] +ERROR namedtuples_define_class.py:48:24-25: Argument `Literal[3]` is not assignable to parameter `units` with type `str` in function `Point.__new__` [bad-argument-type] +ERROR namedtuples_define_class.py:49:22-24: Expected 3 positional arguments, got 4 in function `Point.__new__` [bad-argument-count] +ERROR namedtuples_define_class.py:50:23-28: Unexpected keyword argument `other` in function `Point.__new__` [unexpected-keyword] +ERROR namedtuples_define_class.py:70:20-22: Expected 2 positional arguments, got 3 in function `Point2.__new__` [bad-argument-count] +ERROR namedtuples_define_class.py:77:5-7: NamedTuple field name may not start with an underscore: `_y` [bad-class-definition] +ERROR namedtuples_define_class.py:87:5-13: NamedTuple field 'latitude' without a default may not follow NamedTuple field with a default [bad-class-definition] +ERROR namedtuples_define_class.py:107:5-6: Cannot override named tuple element `x` [bad-override] +ERROR namedtuples_define_class.py:121:24-25: Expected 2 positional arguments, got 3 in function `ConditionalField.__new__` [bad-argument-count] +ERROR namedtuples_define_class.py:140:19-22: Argument `float` is not assignable to parameter `value` with type `str` in function `Property.__new__` [bad-argument-type] +ERROR namedtuples_define_class.py:147:7-11: Named tuples do not support multiple inheritance [invalid-inheritance] +""" diff --git a/conformance/results/pyrefly/namedtuples_define_functional.toml b/conformance/results/pyrefly/namedtuples_define_functional.toml new file mode 100644 index 000000000..40c47f739 --- /dev/null +++ b/conformance/results/pyrefly/namedtuples_define_functional.toml @@ -0,0 +1,21 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR namedtuples_define_functional.py:16:14-19: Missing argument `y` in function `Point1.__new__` [missing-argument] +ERROR namedtuples_define_functional.py:21:14-16: Missing argument `x` in function `Point2.__new__` [missing-argument] +ERROR namedtuples_define_functional.py:21:14-16: Missing argument `y` in function `Point2.__new__` [missing-argument] +ERROR namedtuples_define_functional.py:26:21-22: Expected 2 positional arguments, got 3 in function `Point3.__new__` [bad-argument-count] +ERROR namedtuples_define_functional.py:31:14-22: Missing argument `y` in function `Point4.__new__` [missing-argument] +ERROR namedtuples_define_functional.py:31:18-19: Unexpected keyword argument `z` in function `Point4.__new__` [unexpected-keyword] +ERROR namedtuples_define_functional.py:36:18-21: Argument `Literal['1']` is not assignable to parameter `y` with type `int` in function `Point5.__new__` [bad-argument-type] +ERROR namedtuples_define_functional.py:37:21-22: Expected 2 positional arguments, got 3 in function `Point5.__new__` [bad-argument-count] +ERROR namedtuples_define_functional.py:42:18-21: Argument `Literal['1']` is not assignable to parameter `y` with type `int` in function `Point6.__new__` [bad-argument-type] +ERROR namedtuples_define_functional.py:43:17-20: Argument `float` is not assignable to parameter `x` with type `int` in function `Point6.__new__` [bad-argument-type] +ERROR namedtuples_define_functional.py:52:31-34: Duplicate field `a` [bad-class-definition] +ERROR namedtuples_define_functional.py:53:33-38: `def` is not a valid identifier [bad-class-definition] +ERROR namedtuples_define_functional.py:54:33-38: `def` is not a valid identifier [bad-class-definition] +ERROR namedtuples_define_functional.py:55:33-37: NamedTuple field name may not start with an underscore: `_d` [bad-class-definition] +ERROR namedtuples_define_functional.py:69:4-6: Missing argument `a` in function `NT7.__new__` [missing-argument] +""" diff --git a/conformance/results/pyrefly/namedtuples_type_compat.toml b/conformance/results/pyrefly/namedtuples_type_compat.toml new file mode 100644 index 000000000..83282a96f --- /dev/null +++ b/conformance/results/pyrefly/namedtuples_type_compat.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR namedtuples_type_compat.py:22:23-24: `Point` is not assignable to `tuple[int, int]` [bad-assignment] +ERROR namedtuples_type_compat.py:23:28-29: `Point` is not assignable to `tuple[int, str, str]` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/namedtuples_usage.toml b/conformance/results/pyrefly/namedtuples_usage.toml new file mode 100644 index 000000000..fb3a165ad --- /dev/null +++ b/conformance/results/pyrefly/namedtuples_usage.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR namedtuples_usage.py:34:9-10: Index 3 out of range for tuple with 3 elements [bad-index] +ERROR namedtuples_usage.py:35:9-11: Index -4 out of range for tuple with 3 elements [bad-index] +ERROR namedtuples_usage.py:40:1-4: Cannot set field `x` [read-only] +ERROR namedtuples_usage.py:41:1-5: Cannot set item in `Point` [unsupported-operation] +ERROR namedtuples_usage.py:42:5-8: Cannot delete field `x` [read-only] +ERROR namedtuples_usage.py:43:5-9: Cannot delete item in `Point` [unsupported-operation] +ERROR namedtuples_usage.py:52:1-7: Cannot unpack Point (of size 3) into 2 values [bad-unpacking] +ERROR namedtuples_usage.py:53:1-21: Cannot unpack Point (of size 3) into 4 values [bad-unpacking] +""" diff --git a/conformance/results/pyrefly/narrowing_typeguard.toml b/conformance/results/pyrefly/narrowing_typeguard.toml new file mode 100644 index 000000000..be07b9006 --- /dev/null +++ b/conformance/results/pyrefly/narrowing_typeguard.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR narrowing_typeguard.py:102:9-13: Type guard functions must accept at least one positional argument [bad-function-definition] +ERROR narrowing_typeguard.py:107:9-13: Type guard functions must accept at least one positional argument [bad-function-definition] +ERROR narrowing_typeguard.py:128:20-36: Argument `(val: object) -> TypeGuard[int]` is not assignable to parameter `f` with type `(object) -> str` in function `takes_callable_str` [bad-argument-type] +ERROR narrowing_typeguard.py:148:26-42: Argument `(val: object) -> TypeGuard[int]` is not assignable to parameter `f` with type `CallableStrProto` in function `takes_callable_str_proto` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/narrowing_typeis.toml b/conformance/results/pyrefly/narrowing_typeis.toml new file mode 100644 index 000000000..008c5b417 --- /dev/null +++ b/conformance/results/pyrefly/narrowing_typeis.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR narrowing_typeis.py:110:9-13: Type guard functions must accept at least one positional argument [bad-function-definition] +ERROR narrowing_typeis.py:115:9-13: Type guard functions must accept at least one positional argument [bad-function-definition] +ERROR narrowing_typeis.py:137:20-36: Argument `(val: object) -> TypeIs[int]` is not assignable to parameter `f` with type `(object) -> str` in function `takes_callable_str` [bad-argument-type] +ERROR narrowing_typeis.py:157:26-42: Argument `(val: object) -> TypeIs[int]` is not assignable to parameter `f` with type `CallableStrProto` in function `takes_callable_str_proto` [bad-argument-type] +ERROR narrowing_typeis.py:174:17-30: Argument `(val: object) -> TypeIs[int]` is not assignable to parameter `f` with type `(object) -> TypeGuard[int]` in function `takes_typeguard` [bad-argument-type] +ERROR narrowing_typeis.py:175:14-30: Argument `(val: object) -> TypeGuard[int]` is not assignable to parameter `f` with type `(object) -> TypeIs[int]` in function `takes_typeis` [bad-argument-type] +ERROR narrowing_typeis.py:196:18-29: Argument `(val: object) -> TypeIs[bool]` is not assignable to parameter `f` with type `(object) -> TypeIs[int]` in function `takes_int_typeis` [bad-argument-type] +ERROR narrowing_typeis.py:200:5-15: Return type `str` must be assignable to the first argument type `int` [bad-function-definition] +ERROR narrowing_typeis.py:204:5-24: Return type `list[int]` must be assignable to the first argument type `list[object]` [bad-function-definition] +""" diff --git a/conformance/results/pyrefly/overloads_basic.toml b/conformance/results/pyrefly/overloads_basic.toml new file mode 100644 index 000000000..9dd079a23 --- /dev/null +++ b/conformance/results/pyrefly/overloads_basic.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR overloads_basic.py:39:1-6: Cannot index into `Bytes` [bad-index] +""" diff --git a/conformance/results/pyrefly/overloads_consistency.toml b/conformance/results/pyrefly/overloads_consistency.toml new file mode 100644 index 000000000..da6c71193 --- /dev/null +++ b/conformance/results/pyrefly/overloads_consistency.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR overloads_consistency.py:25:5-16: Overload return type `str` is not assignable to implementation return type `int` [inconsistent-overload] +ERROR overloads_consistency.py:41:5-19: Implementation signature `(x: int) -> int | str` does not accept all arguments that overload signature `(x: str) -> str` accepts [inconsistent-overload] +""" diff --git a/conformance/results/pyrefly/overloads_definitions.toml b/conformance/results/pyrefly/overloads_definitions.toml new file mode 100644 index 000000000..31182ffd5 --- /dev/null +++ b/conformance/results/pyrefly/overloads_definitions.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR overloads_definitions.py:16:5-10: Overloaded function needs at least two @overload declarations [invalid-overload] +ERROR overloads_definitions.py:28:5-10: Overloaded function must have an implementation [invalid-overload] +ERROR overloads_definitions.py:59:9-21: Overloaded function must have an implementation [invalid-overload] +ERROR overloads_definitions.py:81:9-14: If `@staticmethod` is present on any overload or the implementation, it should be on every overload and the implementation. [invalid-overload] +ERROR overloads_definitions.py:90:9-14: If `@classmethod` is present on any overload or the implementation, it should be on every overload and the implementation. [invalid-overload] +ERROR overloads_definitions.py:124:9-22: `@final` should only be applied to the implementation of an overloaded function. [invalid-overload] +ERROR overloads_definitions.py:139:9-24: `@final` should only be applied to the implementation of an overloaded function. [invalid-overload] +ERROR overloads_definitions.py:144:9-24: `@final` should only be applied to the implementation of an overloaded function. [invalid-overload] +ERROR overloads_definitions.py:181:9-21: `final_method` is declared as final in parent class `Base` [bad-override] +ERROR overloads_definitions.py:196:9-21: Class member `Child.bad_override` is marked as an override, but no parent class has a matching attribute [bad-override] +ERROR overloads_definitions.py:228:9-20: `@override` should only be applied to the implementation of an overloaded function. [invalid-overload] +ERROR overloads_definitions.py:232:9-20: `@override` should only be applied to the implementation of an overloaded function. [invalid-overload] +""" diff --git a/conformance/results/pyrefly/overloads_definitions_stub.toml b/conformance/results/pyrefly/overloads_definitions_stub.toml new file mode 100644 index 000000000..983ac42fb --- /dev/null +++ b/conformance/results/pyrefly/overloads_definitions_stub.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR overloads_definitions_stub.pyi:14:5-10: Overloaded function needs at least two @overload declarations [invalid-overload] +ERROR overloads_definitions_stub.pyi:33:9-14: If `@staticmethod` is present on one overload, all overloads must have that decorator. [invalid-overload] +ERROR overloads_definitions_stub.pyi:44:9-14: If `@classmethod` is present on one overload, all overloads must have that decorator. [invalid-overload] +ERROR overloads_definitions_stub.pyi:73:9-22: If an overloaded function has no implementation, `@final` should be applied to the first overload only. [invalid-overload] +ERROR overloads_definitions_stub.pyi:86:9-24: If an overloaded function has no implementation, `@final` should be applied to the first overload only. [invalid-overload] +ERROR overloads_definitions_stub.pyi:108:9-21: `final_method` is declared as final in parent class `Base` [bad-override] +ERROR overloads_definitions_stub.pyi:122:9-21: Class member `Child.bad_override` is marked as an override, but no parent class has a matching attribute [bad-override] +ERROR overloads_definitions_stub.pyi:147:9-20: If an overloaded function has no implementation, `@override` should be applied to the first overload only. [invalid-overload] +""" diff --git a/conformance/results/pyrefly/overloads_evaluation.toml b/conformance/results/pyrefly/overloads_evaluation.toml new file mode 100644 index 000000000..6b60e3f4a --- /dev/null +++ b/conformance/results/pyrefly/overloads_evaluation.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR overloads_evaluation.py:38:11-13: No matching overload found for function `example1_1` called with arguments: () [no-matching-overload] +ERROR overloads_evaluation.py:46:15-16: Argument `Literal[1]` is not assignable to parameter `y` with type `str` in function `example1_1` [bad-argument-type] +ERROR overloads_evaluation.py:51:12-13: Argument `Literal[1]` is not assignable to parameter `x` with type `str` in function `example1_1` [bad-argument-type] +ERROR overloads_evaluation.py:116:13-22: No matching overload found for function `example2` called with arguments: (int | str, int | str, Literal[1]) [no-matching-overload] +""" diff --git a/conformance/results/pyrefly/protocols_class_objects.toml b/conformance/results/pyrefly/protocols_class_objects.toml new file mode 100644 index 000000000..1dc760acf --- /dev/null +++ b/conformance/results/pyrefly/protocols_class_objects.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR protocols_class_objects.py:29:5-10: Argument `type[Proto]` is not assignable to parameter `cls` with type `type[Proto]` in function `fun` [bad-argument-type] +ERROR protocols_class_objects.py:34:7-12: `type[Proto]` is not assignable to variable `var` with type `type[Proto]` [bad-assignment] +ERROR protocols_class_objects.py:58:16-25: `type[ConcreteA]` is not assignable to `ProtoA1` [bad-assignment] +ERROR protocols_class_objects.py:74:16-25: `type[ConcreteB]` is not assignable to `ProtoB1` [bad-assignment] +ERROR protocols_class_objects.py:104:16-26: `type[ConcreteC1]` is not assignable to `ProtoC1` [bad-assignment] +ERROR protocols_class_objects.py:106:16-26: `type[ConcreteC2]` is not assignable to `ProtoC1` [bad-assignment] +ERROR protocols_class_objects.py:107:16-26: `type[ConcreteC2]` is not assignable to `ProtoC2` [bad-assignment] +ERROR protocols_class_objects.py:108:16-26: `type[ConcreteC3]` is not assignable to `ProtoC1` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/protocols_definition.toml b/conformance/results/pyrefly/protocols_definition.toml new file mode 100644 index 000000000..766d78c3f --- /dev/null +++ b/conformance/results/pyrefly/protocols_definition.toml @@ -0,0 +1,27 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR protocols_definition.py:30:11-14: Argument `list[int]` is not assignable to parameter `things` with type `Iterable[SupportsClose]` in function `close_all` [bad-argument-type] +ERROR protocols_definition.py:67:14-18: Protocol variables must be explicitly declared in the class body [protocol-implicitly-defined-attribute] +ERROR protocols_definition.py:114:22-38: `Concrete2_Bad1` is not assignable to `Template2` [bad-assignment] +ERROR protocols_definition.py:115:22-38: `Concrete2_Bad2` is not assignable to `Template2` [bad-assignment] +ERROR protocols_definition.py:116:22-38: `Concrete2_Bad3` is not assignable to `Template2` [bad-assignment] +ERROR protocols_definition.py:117:22-38: `Concrete2_Bad4` is not assignable to `Template2` [bad-assignment] +ERROR protocols_definition.py:156:22-38: `Concrete3_Bad1` is not assignable to `Template3` [bad-assignment] +ERROR protocols_definition.py:157:22-38: `Concrete3_Bad2` is not assignable to `Template3` [bad-assignment] +ERROR protocols_definition.py:158:22-38: `Concrete3_Bad3` is not assignable to `Template3` [bad-assignment] +ERROR protocols_definition.py:159:22-38: `Concrete3_Bad4` is not assignable to `Template3` [bad-assignment] +ERROR protocols_definition.py:160:22-38: `Concrete3_Bad5` is not assignable to `Template3` [bad-assignment] +ERROR protocols_definition.py:218:22-38: `Concrete4_Bad1` is not assignable to `Template4` [bad-assignment] +ERROR protocols_definition.py:219:22-38: `Concrete4_Bad2` is not assignable to `Template4` [bad-assignment] +ERROR protocols_definition.py:285:22-38: `Concrete5_Bad1` is not assignable to `Template5` [bad-assignment] +ERROR protocols_definition.py:286:22-38: `Concrete5_Bad2` is not assignable to `Template5` [bad-assignment] +ERROR protocols_definition.py:287:22-38: `Concrete5_Bad3` is not assignable to `Template5` [bad-assignment] +ERROR protocols_definition.py:288:22-38: `Concrete5_Bad4` is not assignable to `Template5` [bad-assignment] +ERROR protocols_definition.py:289:22-38: `Concrete5_Bad5` is not assignable to `Template5` [bad-assignment] +ERROR protocols_definition.py:339:22-38: `Concrete6_Bad1` is not assignable to `Template6` [bad-assignment] +ERROR protocols_definition.py:340:22-38: `Concrete6_Bad2` is not assignable to `Template6` [bad-assignment] +ERROR protocols_definition.py:341:22-38: `Concrete6_Bad3` is not assignable to `Template6` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/protocols_explicit.toml b/conformance/results/pyrefly/protocols_explicit.toml new file mode 100644 index 000000000..0ce752b0b --- /dev/null +++ b/conformance/results/pyrefly/protocols_explicit.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR protocols_explicit.py:27:16-28: Method `draw` inherited from class `PColor` has no implementation and cannot be accessed via `super()` [missing-attribute] +ERROR protocols_explicit.py:56:20-36: `tuple[int, int, str]` is not assignable to attribute `rgb` with type `tuple[int, int, int]` [bad-assignment] +ERROR protocols_explicit.py:60:10-20: Cannot instantiate `Point` because the following members are abstract: `intensity`, `transparency` [bad-instantiation] +ERROR protocols_explicit.py:89:15-17: Cannot instantiate `Concrete1` because the following members are abstract: `cm1` [bad-instantiation] +ERROR protocols_explicit.py:134:15-17: Cannot instantiate `Concrete5` because the following members are abstract: `method1` [bad-instantiation] +ERROR protocols_explicit.py:164:17-19: Cannot instantiate `Concrete7A` because the following members are abstract: `method1` [bad-instantiation] +""" diff --git a/conformance/results/pyrefly/protocols_generic.toml b/conformance/results/pyrefly/protocols_generic.toml new file mode 100644 index 000000000..634166922 --- /dev/null +++ b/conformance/results/pyrefly/protocols_generic.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR protocols_generic.py:40:24-35: `Concrete1` is not assignable to `Proto1[int, str]` [bad-assignment] +ERROR protocols_generic.py:44:7-13: Class `Proto2` specifies type parameters in both `Generic` and `Protocol` bases [invalid-inheritance] +ERROR protocols_generic.py:56:20-29: `Box[float]` is not assignable to `Box[int]` [bad-assignment] +ERROR protocols_generic.py:66:25-35: `Sender[int]` is not assignable to `Sender[float]` [bad-assignment] +ERROR protocols_generic.py:74:28-36: `AttrProto[int]` is not assignable to `AttrProto[float]` [bad-assignment] +ERROR protocols_generic.py:75:26-36: `AttrProto[float]` is not assignable to `AttrProto[int]` [bad-assignment] +ERROR protocols_generic.py:145:25-47: `ConcreteHasProperty2` is not assignable to `HasPropertyProto` [bad-assignment] +ERROR protocols_generic.py:146:25-47: `ConcreteHasProperty3` is not assignable to `HasPropertyProto` [bad-assignment] +ERROR protocols_generic.py:147:25-47: `ConcreteHasProperty4` is not assignable to `HasPropertyProto` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/protocols_merging.toml b/conformance/results/pyrefly/protocols_merging.toml new file mode 100644 index 000000000..0819adc23 --- /dev/null +++ b/conformance/results/pyrefly/protocols_merging.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR protocols_merging.py:52:25-38: `SCConcrete2` is not assignable to `SizedAndClosable1` [bad-assignment] +ERROR protocols_merging.py:53:25-38: `SCConcrete2` is not assignable to `SizedAndClosable2` [bad-assignment] +ERROR protocols_merging.py:54:25-38: `SCConcrete2` is not assignable to `SizedAndClosable3` [bad-assignment] +ERROR protocols_merging.py:67:16-33: If `Protocol` is included as a base class, all other bases must be protocols [invalid-inheritance] +ERROR protocols_merging.py:82:22-24: Cannot instantiate `SizedAndClosable4` because the following members are abstract: `close` [bad-instantiation] +ERROR protocols_merging.py:83:24-37: `SCConcrete1` is not assignable to `SizedAndClosable4` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/protocols_modules.toml b/conformance/results/pyrefly/protocols_modules.toml new file mode 100644 index 000000000..08e55a351 --- /dev/null +++ b/conformance/results/pyrefly/protocols_modules.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR protocols_modules.py:26:17-36: `Module[_protocols_modules1]` is not assignable to `Options2` [bad-assignment] +ERROR protocols_modules.py:48:18-37: `Module[_protocols_modules2]` is not assignable to `Reporter2` [bad-assignment] +ERROR protocols_modules.py:49:18-37: `Module[_protocols_modules2]` is not assignable to `Reporter3` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/protocols_recursive.toml b/conformance/results/pyrefly/protocols_recursive.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/protocols_recursive.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/protocols_runtime_checkable.toml b/conformance/results/pyrefly/protocols_runtime_checkable.toml new file mode 100644 index 000000000..f794b975d --- /dev/null +++ b/conformance/results/pyrefly/protocols_runtime_checkable.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR protocols_runtime_checkable.py:23:22-28: Protocol `Proto1` is not decorated with @runtime_checkable and cannot be used with isinstance() [invalid-argument] +ERROR protocols_runtime_checkable.py:55:22-34: Protocol `DataProtocol` has non-method members and cannot be used with issubclass() [invalid-argument] +ERROR protocols_runtime_checkable.py:61:22-53: Protocol `DataProtocol` has non-method members and cannot be used with issubclass() [invalid-argument] +ERROR protocols_runtime_checkable.py:88:33-39: Runtime checkable protocol `Proto3` has an unsafe overlap with type `Concrete3A` [unsafe-overlap] +ERROR protocols_runtime_checkable.py:91:33-58: Runtime checkable protocol `Proto3` has an unsafe overlap with type `Concrete3B` [unsafe-overlap] +ERROR protocols_runtime_checkable.py:91:33-58: Runtime checkable protocol `NonDataProtocol` has an unsafe overlap with type `Concrete3B` [unsafe-overlap] +ERROR protocols_runtime_checkable.py:94:31-56: Runtime checkable protocol `Proto3` has an unsafe overlap with type `Concrete3A` [unsafe-overlap] +ERROR protocols_runtime_checkable.py:94:31-56: Runtime checkable protocol `NonDataProtocol` has an unsafe overlap with type `Concrete3A` [unsafe-overlap] +""" diff --git a/conformance/results/pyrefly/protocols_self.toml b/conformance/results/pyrefly/protocols_self.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/protocols_self.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/protocols_subtyping.toml b/conformance/results/pyrefly/protocols_subtyping.toml new file mode 100644 index 000000000..ab7a89280 --- /dev/null +++ b/conformance/results/pyrefly/protocols_subtyping.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR protocols_subtyping.py:16:12-14: Cannot instantiate `Proto1` because it is a protocol [bad-instantiation] +ERROR protocols_subtyping.py:38:21-23: `Proto2` is not assignable to `Concrete2` [bad-assignment] +ERROR protocols_subtyping.py:55:18-20: `Proto2` is not assignable to `Proto3` [bad-assignment] +ERROR protocols_subtyping.py:79:30-36: `Proto5[int]` is not assignable to `Proto4[int, float]` [bad-assignment] +ERROR protocols_subtyping.py:80:25-31: `Proto4[int, int]` is not assignable to `Proto5[float]` [bad-assignment] +ERROR protocols_subtyping.py:102:30-32: `Proto6[float, float]` is not assignable to `Proto7[int, float]` [bad-assignment] +ERROR protocols_subtyping.py:103:33-35: `Proto6[float, float]` is not assignable to `Proto7[float, object]` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/protocols_variance.toml b/conformance/results/pyrefly/protocols_variance.toml new file mode 100644 index 000000000..f0c9d08dc --- /dev/null +++ b/conformance/results/pyrefly/protocols_variance.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ + WARN protocols_variance.py:21:7-17: Type variable `T1` in class `AnotherBox` is declared as invariant, but could be covariant based on its usage [variance-mismatch] + WARN protocols_variance.py:40:7-16: Type variable `T3` in class `Protocol2` is declared as invariant, but could be contravariant based on its usage [variance-mismatch] + WARN protocols_variance.py:56:7-16: Type variable `T1` in class `Protocol4` is declared as invariant, but could be contravariant based on its usage [variance-mismatch] +ERROR protocols_variance.py:62:9-11: Type variable `T1_co` is covariant but is used in contravariant position [invalid-variance] + WARN protocols_variance.py:66:7-16: Type variable `T1` in class `Protocol6` is declared as invariant, but could be covariant based on its usage [variance-mismatch] +ERROR protocols_variance.py:72:9-11: Type variable `T1_contra` is contravariant but is used in covariant position [invalid-variance] + WARN protocols_variance.py:104:7-17: Type variable `T1` in class `Protocol12` is declared as invariant, but could be covariant based on its usage [variance-mismatch] +""" diff --git a/conformance/results/pyrefly/qualifiers_annotated.toml b/conformance/results/pyrefly/qualifiers_annotated.toml new file mode 100644 index 000000000..cf976cd2f --- /dev/null +++ b/conformance/results/pyrefly/qualifiers_annotated.toml @@ -0,0 +1,28 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR qualifiers_annotated.py:38:17-27: List literal cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:39:17-30: Tuple literal cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:40:17-40: List comprehension cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:41:17-27: Dict literal cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:41:19-20: Could not find name `a` [unknown-name] +ERROR qualifiers_annotated.py:41:24-25: Could not find name `b` [unknown-name] +ERROR qualifiers_annotated.py:42:17-32: Function call cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:43:17-25: Invalid subscript expression cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:44:17-38: If expression cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:45:17-21: Could not find name `var1` [unknown-name] +ERROR qualifiers_annotated.py:46:17-21: Bool literal cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:47:18-19: Number literal cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:48:18-29: Boolean operation cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:49:18-28: F-string cannot be used in annotations [invalid-annotation] +ERROR qualifiers_annotated.py:59:8-22: `Annotated` needs at least one piece of metadata in addition to the type [invalid-annotation] +ERROR qualifiers_annotated.py:71:24-42: `Annotated[int]` is not assignable to `type[Any]` [bad-assignment] +ERROR qualifiers_annotated.py:72:24-32: `TypeAlias[SmallInt, Annotated[int]]` is not assignable to `type[Any]` [bad-assignment] +ERROR qualifiers_annotated.py:79:7-25: Argument `Annotated[str]` is not assignable to parameter `x` with type `type[@_]` in function `func4` [bad-argument-type] +ERROR qualifiers_annotated.py:80:7-15: Argument `TypeAlias[SmallInt, Annotated[int]]` is not assignable to parameter `x` with type `type[@_]` in function `func4` [bad-argument-type] +ERROR qualifiers_annotated.py:86:1-10: Expected a callable, got `type[Annotated]` [not-callable] +ERROR qualifiers_annotated.py:87:1-19: Expected a callable, got `Annotated[int]` [not-callable] +ERROR qualifiers_annotated.py:88:1-9: Expected a callable, got `TypeAlias[SmallInt, Annotated[int]]` [not-callable] +""" diff --git a/conformance/results/pyrefly/qualifiers_final_annotation.toml b/conformance/results/pyrefly/qualifiers_final_annotation.toml new file mode 100644 index 000000000..eda91ff00 --- /dev/null +++ b/conformance/results/pyrefly/qualifiers_final_annotation.toml @@ -0,0 +1,35 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR qualifiers_final_annotation.py:16:7-12: Expected a type argument for `Final` [invalid-annotation] +ERROR qualifiers_final_annotation.py:18:7-22: Expected 1 type argument for `Final`, got 2 [invalid-annotation] +ERROR qualifiers_final_annotation.py:34:5-8: Final attribute declared in class body must be initialized with a value or in `__init__` [invalid-annotation] +ERROR qualifiers_final_annotation.py:38:5-8: Final attribute declared in class body must be initialized with a value or in `__init__` [invalid-annotation] +ERROR qualifiers_final_annotation.py:54:9-17: Cannot set field `ID5` [read-only] +ERROR qualifiers_final_annotation.py:62:9-17: Cannot set field `id3` [read-only] +ERROR qualifiers_final_annotation.py:63:9-17: Cannot set field `id4` [read-only] +ERROR qualifiers_final_annotation.py:65:9-17: Cannot set field `ID7` [read-only] +ERROR qualifiers_final_annotation.py:67:9-17: Cannot set field `ID7` [read-only] +ERROR qualifiers_final_annotation.py:71:8-11: Cannot assign to variable `RATE` because it is marked final [bad-assignment] +ERROR qualifiers_final_annotation.py:81:1-18: Cannot set field `DEFAULT_ID` [read-only] +ERROR qualifiers_final_annotation.py:94:5-17: `BORDER_WIDTH` is declared as final in parent class `ClassC` [bad-override] +ERROR qualifiers_final_annotation.py:107:5-11: `Final` may not be nested inside `ClassVar` [invalid-annotation] +ERROR qualifiers_final_annotation.py:108:19-27: `ClassVar` may not be nested inside `Final` [invalid-annotation] +ERROR qualifiers_final_annotation.py:118:9-19: `Final` is not allowed in this context [invalid-annotation] +ERROR qualifiers_final_annotation.py:121:14-19: `Final` is only allowed on a class or local variable annotation [invalid-annotation] +ERROR qualifiers_final_annotation.py:121:14-30: `Final` is not allowed in this context [invalid-annotation] +ERROR qualifiers_final_annotation.py:134:2-7: Missing argument `x` in function `N.__new__` [missing-argument] +ERROR qualifiers_final_annotation.py:134:2-7: Missing argument `y` in function `N.__new__` [missing-argument] +ERROR qualifiers_final_annotation.py:134:3-4: Unexpected keyword argument `a` in function `N.__new__` [unexpected-keyword] +ERROR qualifiers_final_annotation.py:135:5-7: Argument `Literal['']` is not assignable to parameter `x` with type `int` in function `N.__new__` [bad-argument-type] +ERROR qualifiers_final_annotation.py:135:11-13: Argument `Literal['']` is not assignable to parameter `y` with type `int` in function `N.__new__` [bad-argument-type] +ERROR qualifiers_final_annotation.py:141:11-12: Cannot assign to variable `ID1` because it is marked final [bad-assignment] +ERROR qualifiers_final_annotation.py:145:5-11: Cannot assign to variable `x` because it is marked final [bad-assignment] +ERROR qualifiers_final_annotation.py:147:15-16: Cannot assign to variable `x` because it is marked final [bad-assignment] +ERROR qualifiers_final_annotation.py:149:14-23: Cannot assign to variable `x` because it is marked final [bad-assignment] +ERROR qualifiers_final_annotation.py:152:10-26: Cannot assign to variable `x` because it is marked final [bad-assignment] +ERROR qualifiers_final_annotation.py:155:5-11: Cannot assign to variable `x` because it is marked final [bad-assignment] +ERROR qualifiers_final_annotation.py:166:1-4: Cannot assign to `TEN` because it is imported as final [bad-assignment] +ERROR qualifiers_final_annotation.py:170:1-3: Cannot assign to `PI` because it is imported as final [bad-assignment] +""" diff --git a/conformance/results/pyrefly/qualifiers_final_decorator.toml b/conformance/results/pyrefly/qualifiers_final_decorator.toml new file mode 100644 index 000000000..381d74150 --- /dev/null +++ b/conformance/results/pyrefly/qualifiers_final_decorator.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR qualifiers_final_decorator.py:21:16-21: Cannot extend final class `Base1` [invalid-inheritance] +ERROR qualifiers_final_decorator.py:56:9-16: `method1` is declared as final in parent class `Base2` [bad-override] +ERROR qualifiers_final_decorator.py:60:9-16: `method2` is declared as final in parent class `Base2` [bad-override] +ERROR qualifiers_final_decorator.py:64:9-16: `method3` is declared as final in parent class `Base2` [bad-override] +ERROR qualifiers_final_decorator.py:68:9-16: `method4` is declared as final in parent class `Base2` [bad-override] +ERROR qualifiers_final_decorator.py:81:9-15: `method` is declared as final in parent class `Base3` [bad-override] +ERROR qualifiers_final_decorator.py:86:9-15: `@final` should only be applied to the implementation of an overloaded function. [invalid-overload] +ERROR qualifiers_final_decorator.py:95:9-15: `method` is declared as final in parent class `Base4` [bad-override] +ERROR qualifiers_final_decorator.py:118:9-15: `method` is declared as final in parent class `Base5_2` [bad-override] + WARN qualifiers_final_decorator.py:125:1-7: Decorator `@final` can only be used on methods. [invalid-decorator] +""" diff --git a/conformance/results/pyrefly/specialtypes_any.toml b/conformance/results/pyrefly/specialtypes_any.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/specialtypes_any.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/specialtypes_never.toml b/conformance/results/pyrefly/specialtypes_never.toml new file mode 100644 index 000000000..251ce6ae0 --- /dev/null +++ b/conformance/results/pyrefly/specialtypes_never.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR specialtypes_never.py:19:22-30: Function declared to return `NoReturn` but is missing an explicit `return` [bad-return] +ERROR specialtypes_never.py:85:21-22: `list[Never]` is not assignable to `list[int]` [bad-assignment] +ERROR specialtypes_never.py:104:12-27: Returned type `ClassC[Never]` is not assignable to declared return type `ClassC[U]` [bad-return] +""" diff --git a/conformance/results/pyrefly/specialtypes_none.toml b/conformance/results/pyrefly/specialtypes_none.toml new file mode 100644 index 000000000..6d4c240e0 --- /dev/null +++ b/conformance/results/pyrefly/specialtypes_none.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR specialtypes_none.py:21:7-17: Argument `type[NoneType]` is not assignable to parameter `val1` with type `None` in function `func1` [bad-argument-type] +ERROR specialtypes_none.py:27:19-23: `None` is not assignable to `Iterable[Unknown]` [bad-assignment] +ERROR specialtypes_none.py:41:7-11: Argument `None` is not assignable to parameter `val1` with type `type[None]` in function `func2` [bad-argument-type] +""" diff --git a/conformance/results/pyrefly/specialtypes_promotions.toml b/conformance/results/pyrefly/specialtypes_promotions.toml new file mode 100644 index 000000000..6b038e892 --- /dev/null +++ b/conformance/results/pyrefly/specialtypes_promotions.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR specialtypes_promotions.py:13:5-16: Object of class `float` has no attribute `numerator` [missing-attribute] +""" diff --git a/conformance/results/pyrefly/specialtypes_type.toml b/conformance/results/pyrefly/specialtypes_type.toml new file mode 100644 index 000000000..e5bf63e0c --- /dev/null +++ b/conformance/results/pyrefly/specialtypes_type.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR specialtypes_type.py:56:7-15: Argument `type[TeamUser]` is not assignable to parameter `user_class` with type `type[BasicUser | ProUser]` in function `func4` [bad-argument-type] +ERROR specialtypes_type.py:70:7-15: Argument `type[Callable]` is not assignable to parameter `x` with type `type[@_]` in function `func5` [bad-argument-type] +ERROR specialtypes_type.py:76:12-26: Expected 1 type argument for `type`, got 2 [bad-specialization] +ERROR specialtypes_type.py:117:5-14: Class `object` has no class attribute `unknown` [missing-attribute] +ERROR specialtypes_type.py:120:5-14: Class `object` has no class attribute `unknown` [missing-attribute] +ERROR specialtypes_type.py:143:1-12: Class `type` has no class attribute `unknown` [missing-attribute] +ERROR specialtypes_type.py:144:1-12: Class `type` has no class attribute `unknown` [missing-attribute] +ERROR specialtypes_type.py:145:1-12: Class `type` has no class attribute `unknown` [missing-attribute] +ERROR specialtypes_type.py:146:1-12: Class `type` has no class attribute `unknown` [missing-attribute] +""" diff --git a/conformance/results/pyrefly/tuples_type_compat.toml b/conformance/results/pyrefly/tuples_type_compat.toml new file mode 100644 index 000000000..c85896b78 --- /dev/null +++ b/conformance/results/pyrefly/tuples_type_compat.toml @@ -0,0 +1,30 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR tuples_type_compat.py:15:27-29: `tuple[float, complex]` is not assignable to `tuple[int, int]` [bad-assignment] +ERROR tuples_type_compat.py:29:10-12: `tuple[int, ...]` is not assignable to variable `v2` with type `tuple[int, *tuple[int, ...]]` [bad-assignment] +ERROR tuples_type_compat.py:32:10-12: `tuple[int, *tuple[int, ...]]` is not assignable to variable `v3` with type `tuple[int]` [bad-assignment] +ERROR tuples_type_compat.py:33:10-12: `tuple[int, ...]` is not assignable to variable `v3` with type `tuple[int]` [bad-assignment] +ERROR tuples_type_compat.py:43:22-24: `tuple[int, ...]` is not assignable to `tuple[int]` [bad-assignment] +ERROR tuples_type_compat.py:62:26-35: `tuple[int, ...]` is not assignable to `tuple[int, int]` [bad-assignment] +ERROR tuples_type_compat.py:76:20-37: assert_type(tuple[int], tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]) failed [assert-type] +ERROR tuples_type_compat.py:81:20-37: assert_type(tuple[int, int] | tuple[str, str], tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]) failed [assert-type] +ERROR tuples_type_compat.py:86:20-37: assert_type(tuple[int, str, int], tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]) failed [assert-type] +ERROR tuples_type_compat.py:102:24-41: assert_type(tuple[int], tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]) failed [assert-type] +ERROR tuples_type_compat.py:107:24-41: assert_type(tuple[int, int] | tuple[str, str], tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]) failed [assert-type] +ERROR tuples_type_compat.py:112:24-41: assert_type(tuple[int, str, int], tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]) failed [assert-type] +ERROR tuples_type_compat.py:126:24-53: assert_type(tuple[int | str, int | str], tuple[int | str, str]) failed [assert-type] +ERROR tuples_type_compat.py:129:24-53: assert_type(tuple[int | str, int | str], tuple[int | str, int]) failed [assert-type] +ERROR tuples_type_compat.py:157:6-17: `tuple[Literal[1], Literal[''], Literal['']]` is not assignable to variable `t1` with type `tuple[int, str]` [bad-assignment] +ERROR tuples_type_compat.py:162:6-16: `tuple[Literal[1], Literal[1], Literal['']]` is not assignable to variable `t2` with type `tuple[int, *tuple[str, ...]]` [bad-assignment] +ERROR tuples_type_compat.py:163:6-16: `tuple[Literal[1], Literal[''], Literal[1]]` is not assignable to variable `t2` with type `tuple[int, *tuple[str, ...]]` [bad-assignment] +ERROR tuples_type_compat.py:169:6-17: `tuple[Literal[1], Literal[''], Literal['']]` is not assignable to variable `t3` with type `tuple[int, *tuple[str, ...], int]` [bad-assignment] +ERROR tuples_type_compat.py:170:6-22: `tuple[Literal[1], Literal[''], Literal[''], float]` is not assignable to variable `t3` with type `tuple[int, *tuple[str, ...], int]` [bad-assignment] +ERROR tuples_type_compat.py:175:6-16: `tuple[Literal[1], Literal[''], Literal[1]]` is not assignable to variable `t4` with type `tuple[*tuple[str, ...], int]` [bad-assignment] +ERROR tuples_type_compat.py:176:6-19: `tuple[Literal[''], Literal[''], float]` is not assignable to variable `t4` with type `tuple[*tuple[str, ...], int]` [bad-assignment] +ERROR tuples_type_compat.py:181:40-41: `tuple[str, str]` is not assignable to `tuple[str, str, int]` [bad-assignment] +ERROR tuples_type_compat.py:184:50-51: `tuple[str, str]` is not assignable to `tuple[str, str, str, *tuple[str, ...]]` [bad-assignment] +ERROR tuples_type_compat.py:188:50-51: `tuple[str, str]` is not assignable to `tuple[*tuple[str, ...], str, str, str]` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/tuples_type_form.toml b/conformance/results/pyrefly/tuples_type_form.toml new file mode 100644 index 000000000..7a9c92c85 --- /dev/null +++ b/conformance/results/pyrefly/tuples_type_form.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR tuples_type_form.py:12:6-12: `tuple[Literal[1], Literal[2]]` is not assignable to variable `t1` with type `tuple[int]` [bad-assignment] +ERROR tuples_type_form.py:14:6-10: `tuple[Literal[1]]` is not assignable to variable `t2` with type `tuple[int, int]` [bad-assignment] +ERROR tuples_type_form.py:15:6-13: `tuple[Literal[1], Literal['']]` is not assignable to variable `t2` with type `tuple[int, int]` [bad-assignment] +ERROR tuples_type_form.py:25:7-11: `tuple[Literal[1]]` is not assignable to variable `t10` with type `tuple[()]` [bad-assignment] +ERROR tuples_type_form.py:36:7-20: `tuple[Literal[1], Literal[2], Literal[3], Literal['']]` is not assignable to variable `t20` with type `tuple[int, ...]` [bad-assignment] +ERROR tuples_type_form.py:40:22-25: Invalid position for `...` [invalid-argument] +ERROR tuples_type_form.py:41:12-15: Invalid position for `...` [invalid-argument] +ERROR tuples_type_form.py:42:12-15: Invalid position for `...` [invalid-argument] +ERROR tuples_type_form.py:43:17-20: Invalid position for `...` [invalid-argument] +ERROR tuples_type_form.py:44:25-28: `...` cannot be used with an unpacked `TypeVarTuple` or tuple [invalid-argument] +ERROR tuples_type_form.py:45:30-33: Invalid position for `...` [invalid-argument] +""" diff --git a/conformance/results/pyrefly/tuples_unpacked.toml b/conformance/results/pyrefly/tuples_unpacked.toml new file mode 100644 index 000000000..8792b4535 --- /dev/null +++ b/conformance/results/pyrefly/tuples_unpacked.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR tuples_unpacked.py:40:29-45: Only one unbounded type is allowed to be unpacked [bad-unpacking] +ERROR tuples_unpacked.py:41:42-58: Only one unbounded type is allowed to be unpacked [bad-unpacking] +ERROR tuples_unpacked.py:51:33-36: Only one unbounded type is allowed to be unpacked [bad-unpacking] +ERROR tuples_unpacked.py:59:37-60: Only one unbounded type is allowed to be unpacked [bad-unpacking] +ERROR tuples_unpacked.py:61:50-73: Only one unbounded type is allowed to be unpacked [bad-unpacking] +""" diff --git a/conformance/results/pyrefly/typeddicts_alt_syntax.toml b/conformance/results/pyrefly/typeddicts_alt_syntax.toml new file mode 100644 index 000000000..ab8a7e821 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_alt_syntax.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_alt_syntax.py:23:1-14: Expected valid functional typed dictionary definition [invalid-argument] +ERROR typeddicts_alt_syntax.py:27:45-46: Expected first item to be a string literal [invalid-argument] + WARN typeddicts_alt_syntax.py:31:27-38: Expected string literal "BadTypedDict3" [name-mismatch] +ERROR typeddicts_alt_syntax.py:35:72-83: Unrecognized keyword argument `other` in typed dictionary definition [invalid-argument] +ERROR typeddicts_alt_syntax.py:41:1-7: Expected valid functional typed dictionary definition [invalid-argument] +ERROR typeddicts_alt_syntax.py:41:30-38: Unrecognized keyword argument `name` in typed dictionary definition [invalid-argument] +ERROR typeddicts_alt_syntax.py:41:40-48: Unrecognized keyword argument `year` in typed dictionary definition [invalid-argument] +ERROR typeddicts_alt_syntax.py:44:11-17: Key `name` is not defined in TypedDict `Movie2` [bad-typed-dict-key] +ERROR typeddicts_alt_syntax.py:44:35-41: Key `year` is not defined in TypedDict `Movie2` [bad-typed-dict-key] +ERROR typeddicts_alt_syntax.py:45:11-17: Key `name` is not defined in TypedDict `Movie2` [bad-typed-dict-key] +ERROR typeddicts_alt_syntax.py:45:35-41: Key `year` is not defined in TypedDict `Movie2` [bad-typed-dict-key] +""" diff --git a/conformance/results/pyrefly/typeddicts_class_syntax.toml b/conformance/results/pyrefly/typeddicts_class_syntax.toml new file mode 100644 index 000000000..2917003cf --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_class_syntax.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_class_syntax.py:30:9-16: TypedDict members must be declared in the form `field: Annotation` with no assignment [bad-class-definition] +ERROR typeddicts_class_syntax.py:35:9-16: TypedDict members must be declared in the form `field: Annotation` with no assignment [bad-class-definition] +ERROR typeddicts_class_syntax.py:40:9-16: TypedDict members must be declared in the form `field: Annotation` with no assignment [bad-class-definition] +ERROR typeddicts_class_syntax.py:45:7-20: Metaclass of `BadTypedDict2` has type `type[Any]` that is not a simple class type [invalid-inheritance] +ERROR typeddicts_class_syntax.py:50:7-20: TypedDict does not support keyword argument `other` [bad-typed-dict] +ERROR typeddicts_class_syntax.py:65:17-32: No matching overload found for function `ConditionalField.__init__` called with arguments: (x=Literal[1], y=Literal[2], z=Literal[3]) [no-matching-overload] +""" diff --git a/conformance/results/pyrefly/typeddicts_extra_items.toml b/conformance/results/pyrefly/typeddicts_extra_items.toml new file mode 100644 index 000000000..af4c4cec6 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_extra_items.toml @@ -0,0 +1,34 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_extra_items.py:15:45-49: `Literal[1982]` is not assignable to TypedDict key with type `bool` [bad-typed-dict-key] +ERROR typeddicts_extra_items.py:22:55-59: `Literal[1982]` is not assignable to TypedDict key with type `bool` [bad-typed-dict-key] +ERROR typeddicts_extra_items.py:39:54-58: `None` is not assignable to TypedDict key `year` with type `int` [bad-typed-dict-key] +ERROR typeddicts_extra_items.py:49:7-16: Expected literal True or False for keyword `closed`, got instance of `bool` [bad-typed-dict] +ERROR typeddicts_extra_items.py:67:7-20: Non-closed TypedDict cannot inherit from closed TypedDict `ClosedBase` [bad-typed-dict] +ERROR typeddicts_extra_items.py:73:7-20: Non-closed TypedDict cannot inherit from TypedDict `ExtraItemsBase` with extra items [bad-typed-dict] +ERROR typeddicts_extra_items.py:92:5-8: Cannot extend closed TypedDict `MovieA` with extra item `age` [bad-typed-dict-key] +ERROR typeddicts_extra_items.py:95:5-8: Cannot extend closed TypedDict `MovieB` with extra item `age` [bad-typed-dict-key] +ERROR typeddicts_extra_items.py:109:7-30: Closed TypedDict cannot inherit from TypedDict `ExtraItemsBase` with non-read-only extra items [bad-typed-dict] +ERROR typeddicts_extra_items.py:114:50-63: `Required` is not allowed in this context [invalid-annotation] +ERROR typeddicts_extra_items.py:117:57-73: `NotRequired` is not allowed in this context [invalid-annotation] +ERROR typeddicts_extra_items.py:128:15-21: Key `name` in TypedDict `MovieEI` may not be deleted [unsupported-delete] +ERROR typeddicts_extra_items.py:143:48-52: Unexpected keyword argument `year` in function `unpack_no_extra` [unexpected-keyword] +ERROR typeddicts_extra_items.py:174:7-12: Cannot change the non-read-only extra items type of TypedDict `Parent` [bad-typed-dict] +ERROR typeddicts_extra_items.py:185:5-9: Cannot add required field `year` to TypedDict `MovieBase2` with non-read-only `extra_items` [bad-typed-dict-key] +ERROR typeddicts_extra_items.py:188:5-9: `int` is not consistent with `extra_items` type `int | None` of TypedDict `MovieBase2` [bad-typed-dict-key] +ERROR typeddicts_extra_items.py:197:5-14: `str` is not assignable to `extra_items` type `int | None` of TypedDict `BookBase` [bad-typed-dict-key] +ERROR typeddicts_extra_items.py:215:22-30: `MovieDetails` is not assignable to `MovieBase2` [bad-assignment] +ERROR typeddicts_extra_items.py:222:22-30: `MovieWithYear2` is not assignable to `MovieBase2` [bad-assignment] +ERROR typeddicts_extra_items.py:242:19-27: `MovieDetails5` is not assignable to `MovieSI` [bad-assignment] +ERROR typeddicts_extra_items.py:256:13-22: `MovieExtraStr` is not assignable to variable `extra_int` with type `MovieExtraInt` [bad-assignment] +ERROR typeddicts_extra_items.py:257:13-22: `MovieExtraInt` is not assignable to variable `extra_str` with type `MovieExtraStr` [bad-assignment] +ERROR typeddicts_extra_items.py:268:14-24: `MovieNotClosed` is not assignable to variable `extra_int2` with type `MovieExtraInt` [bad-assignment] +ERROR typeddicts_extra_items.py:278:15-57: No matching overload found for function `NonClosedMovie.__init__` called with arguments: (name=Literal['No Country for Old Men'], year=Literal[2007]) [no-matching-overload] +ERROR typeddicts_extra_items.py:285:52-61: Keyword argument `language` with type `Literal['English']` is not assignable to kwargs type `int` in function `ExtraMovie.__init__` [bad-argument-type] +ERROR typeddicts_extra_items.py:293:12-54: No matching overload found for function `ClosedMovie.__init__` called with arguments: (name=Literal['No Country for Old Men'], year=Literal[2007]) [no-matching-overload] +ERROR typeddicts_extra_items.py:303:34-44: `MovieExtraInt` is not assignable to `Mapping[str, int]` [bad-assignment] +ERROR typeddicts_extra_items.py:352:25-52: `dict[str, int]` is not assignable to `IntDict` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/typeddicts_final.toml b/conformance/results/pyrefly/typeddicts_final.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_final.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyrefly/typeddicts_inheritance.toml b/conformance/results/pyrefly/typeddicts_inheritance.toml new file mode 100644 index 000000000..621a115e8 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_inheritance.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_inheritance.py:44:7-19: `NonTypedDict` is not a typed dictionary. Typed dictionary definitions may only extend other typed dictionaries. [invalid-inheritance] +ERROR typeddicts_inheritance.py:55:4-5: Class member `Y1.x` overrides parent class `X1` in an inconsistent manner [bad-override-mutable-attribute] +ERROR typeddicts_inheritance.py:65:7-11: Field `x` has inconsistent types inherited from multiple base classes [inconsistent-inheritance] +""" diff --git a/conformance/results/pyrefly/typeddicts_operations.toml b/conformance/results/pyrefly/typeddicts_operations.toml new file mode 100644 index 000000000..3c3dafe48 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_operations.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_operations.py:22:17-21: `Literal[1982]` is not assignable to TypedDict key `name` with type `str` [bad-typed-dict-key] +ERROR typeddicts_operations.py:23:17-19: `Literal['']` is not assignable to TypedDict key `year` with type `int` [bad-typed-dict-key] +ERROR typeddicts_operations.py:24:7-14: TypedDict `Movie` does not have key `other` [bad-typed-dict-key] +ERROR typeddicts_operations.py:26:13-20: TypedDict `Movie` does not have key `other` [bad-typed-dict-key] +ERROR typeddicts_operations.py:28:9-33: Missing required key `year` for TypedDict `Movie` [bad-typed-dict-key] +ERROR typeddicts_operations.py:29:42-48: `float` is not assignable to TypedDict key `year` with type `int` [bad-typed-dict-key] +ERROR typeddicts_operations.py:32:36-43: Key `other` is not defined in TypedDict `Movie` [bad-typed-dict-key] +ERROR typeddicts_operations.py:37:20-52: Missing required key `name` for TypedDict `Movie` [bad-typed-dict-key] +ERROR typeddicts_operations.py:37:21-33: Expected string literal key, got `str` [bad-typed-dict-key] +ERROR typeddicts_operations.py:47:1-12: Object of class `Movie` has no attribute `clear` [missing-attribute] +ERROR typeddicts_operations.py:49:11-17: Key `name` in TypedDict `Movie` may not be deleted [unsupported-delete] +ERROR typeddicts_operations.py:62:1-21: Object of class `MovieOptional` has no attribute `clear` [missing-attribute] +""" diff --git a/conformance/results/pyrefly/typeddicts_readonly.toml b/conformance/results/pyrefly/typeddicts_readonly.toml new file mode 100644 index 000000000..08517495c --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_readonly.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_readonly.py:24:4-13: Key `members` in TypedDict `Band` is read-only [read-only] +ERROR typeddicts_readonly.py:36:4-13: Key `members` in TypedDict `Band2` is read-only [read-only] +ERROR typeddicts_readonly.py:50:4-11: Key `title` in TypedDict `Movie1` is read-only [read-only] +ERROR typeddicts_readonly.py:51:4-10: Key `year` in TypedDict `Movie1` is read-only [read-only] +ERROR typeddicts_readonly.py:60:4-11: Key `title` in TypedDict `Movie2` is read-only [read-only] +ERROR typeddicts_readonly.py:61:4-10: Key `year` in TypedDict `Movie2` is read-only [read-only] +""" diff --git a/conformance/results/pyrefly/typeddicts_readonly_consistency.toml b/conformance/results/pyrefly/typeddicts_readonly_consistency.toml new file mode 100644 index 000000000..9f8e01d85 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_readonly_consistency.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_readonly_consistency.py:37:14-15: `A1` is not assignable to `B1` [bad-assignment] +ERROR typeddicts_readonly_consistency.py:38:14-15: `C1` is not assignable to `B1` [bad-assignment] +ERROR typeddicts_readonly_consistency.py:40:14-15: `A1` is not assignable to `C1` [bad-assignment] +ERROR typeddicts_readonly_consistency.py:81:14-15: `A2` is not assignable to `B2` [bad-assignment] +ERROR typeddicts_readonly_consistency.py:82:14-15: `C2` is not assignable to `B2` [bad-assignment] +ERROR typeddicts_readonly_consistency.py:84:14-15: `A2` is not assignable to `C2` [bad-assignment] +ERROR typeddicts_readonly_consistency.py:85:14-15: `B2` is not assignable to `C2` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/typeddicts_readonly_inheritance.toml b/conformance/results/pyrefly/typeddicts_readonly_inheritance.toml new file mode 100644 index 000000000..b1c3dc118 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_readonly_inheritance.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_readonly_inheritance.py:36:4-10: Key `name` in TypedDict `Album2` is read-only [read-only] +ERROR typeddicts_readonly_inheritance.py:50:5-8: Class member `RecordShop.alt` overrides parent class `AlbumCollection` in an inconsistent manner [bad-override] +ERROR typeddicts_readonly_inheritance.py:65:19-21: Missing required key `name` for TypedDict `RequiredName` [bad-typed-dict-key] +ERROR typeddicts_readonly_inheritance.py:82:14-15: `Literal[3]` is not assignable to TypedDict key `ident` with type `str` [bad-typed-dict-key] +ERROR typeddicts_readonly_inheritance.py:83:15-16: `Literal[3]` is not assignable to TypedDict key `ident` with type `str` [bad-typed-dict-key] +ERROR typeddicts_readonly_inheritance.py:84:5-7: Missing required key `ident` for TypedDict `User` [bad-typed-dict-key] +ERROR typeddicts_readonly_inheritance.py:94:5-6: TypedDict field `a` in `F3` cannot be marked read-only; parent TypedDict `F1` defines it as mutable [bad-typed-dict-key] +ERROR typeddicts_readonly_inheritance.py:98:5-6: TypedDict field `a` in `F4` must remain required because parent TypedDict `F1` defines it as required [bad-typed-dict-key] +ERROR typeddicts_readonly_inheritance.py:106:5-6: TypedDict field `c` in `F6` cannot be made non-required; parent TypedDict `F1` defines it as required [bad-typed-dict-key] +ERROR typeddicts_readonly_inheritance.py:119:7-11: Field `x` is declared `float` in ancestor `class TD_A2: ... +ERROR typeddicts_readonly_inheritance.py:132:7-11: TypedDict field `x` in `TD_B` cannot be made non-required; parent TypedDict `TD_B2` defines it as required [bad-typed-dict-key] +""" diff --git a/conformance/results/pyrefly/typeddicts_readonly_kwargs.toml b/conformance/results/pyrefly/typeddicts_readonly_kwargs.toml new file mode 100644 index 000000000..0be703f17 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_readonly_kwargs.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_readonly_kwargs.py:33:12-18: Key `key1` in TypedDict `ReadOnlyArgs` is read-only [read-only] +""" diff --git a/conformance/results/pyrefly/typeddicts_readonly_update.toml b/conformance/results/pyrefly/typeddicts_readonly_update.toml new file mode 100644 index 000000000..44bc14874 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_readonly_update.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_readonly_update.py:23:10-14: No matching overload found for function `A.update` called with arguments: (A) [no-matching-overload] +""" diff --git a/conformance/results/pyrefly/typeddicts_required.toml b/conformance/results/pyrefly/typeddicts_required.toml new file mode 100644 index 000000000..8a57a6267 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_required.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_required.py:12:5-6: `Required` may only be used for TypedDict members [invalid-annotation] +ERROR typeddicts_required.py:16:8-19: `NotRequired` is only allowed inside a class body [invalid-annotation] +ERROR typeddicts_required.py:16:8-24: `NotRequired` is not allowed in this context [invalid-annotation] +ERROR typeddicts_required.py:59:8-31: Duplicate qualifier `Required` [invalid-annotation] +ERROR typeddicts_required.py:60:8-34: Cannot combine `Required` and `NotRequired` for a TypedDict field [invalid-annotation] +""" diff --git a/conformance/results/pyrefly/typeddicts_type_consistency.toml b/conformance/results/pyrefly/typeddicts_type_consistency.toml new file mode 100644 index 000000000..3003ae792 --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_type_consistency.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_type_consistency.py:21:10-12: `B1` is not assignable to `A1` [bad-assignment] +ERROR typeddicts_type_consistency.py:38:10-12: `B2` is not assignable to `A2` [bad-assignment] +ERROR typeddicts_type_consistency.py:65:6-8: `A3` is not assignable to variable `b3` with type `B3` [bad-assignment] +ERROR typeddicts_type_consistency.py:69:21-24: Key `y` is not defined in TypedDict `A3` [bad-typed-dict-key] +ERROR typeddicts_type_consistency.py:76:22-24: `B3` is not assignable to `dict[str, int]` [bad-assignment] +ERROR typeddicts_type_consistency.py:77:25-27: `B3` is not assignable to `dict[str, object]` [bad-assignment] +ERROR typeddicts_type_consistency.py:78:22-24: `B3` is not assignable to `dict[Any, Any]` [bad-assignment] +ERROR typeddicts_type_consistency.py:82:25-27: `B3` is not assignable to `Mapping[str, int]` [bad-assignment] +ERROR typeddicts_type_consistency.py:126:56-57: `Literal[1]` is not assignable to TypedDict key `inner_key` with type `str` [bad-typed-dict-key] +""" diff --git a/conformance/results/pyrefly/typeddicts_usage.toml b/conformance/results/pyrefly/typeddicts_usage.toml new file mode 100644 index 000000000..6c43741df --- /dev/null +++ b/conformance/results/pyrefly/typeddicts_usage.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +ERROR typeddicts_usage.py:23:7-17: TypedDict `Movie` does not have key `director` [bad-typed-dict-key] +ERROR typeddicts_usage.py:24:17-23: `Literal['1982']` is not assignable to TypedDict key `year` with type `int` [bad-typed-dict-key] +ERROR typeddicts_usage.py:28:17-56: Missing required key `name` for TypedDict `Movie` [bad-typed-dict-key] +ERROR typeddicts_usage.py:28:18-25: Key `title` is not defined in TypedDict `Movie` [bad-typed-dict-key] +ERROR typeddicts_usage.py:35:22-27: TypedDict `Movie` not allowed as second argument to isinstance() [invalid-argument] +ERROR typeddicts_usage.py:40:24-33: `TypedDict` is not allowed in this context [invalid-annotation] +""" diff --git a/conformance/results/pyrefly/typeforms_typeform.toml b/conformance/results/pyrefly/typeforms_typeform.toml new file mode 100644 index 000000000..c5ef5d8d6 --- /dev/null +++ b/conformance/results/pyrefly/typeforms_typeform.toml @@ -0,0 +1,31 @@ +conformant = "Partial" +notes = """ +Does not allow assigning a TypeForm to types.GenericAlias. +Does not allow passing a forward reference to a function accepting a TypeForm. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 49: Unexpected errors ['`type[list[int]]` is not assignable to `GenericAlias` [bad-assignment]', 'Expected `v2_actual` to be a type alias, got `GenericAlias` [invalid-type-alias]'] +Line 58: Unexpected errors ["Argument `Literal['int']` is not assignable to parameter `x` with type `TypeForm[Any]` in function `func1` [bad-argument-type]"] +""" +output = """ +ERROR typeforms_typeform.py:23:30-39: `type[int | str]` is not assignable to `TypeForm[str | None]` [bad-assignment] +ERROR typeforms_typeform.py:24:30-46: `type[list[str | None]]` is not assignable to `TypeForm[str | None]` [bad-assignment] +ERROR typeforms_typeform.py:49:33-42: `type[list[int]]` is not assignable to `GenericAlias` [bad-assignment] +ERROR typeforms_typeform.py:49:33-42: Expected `v2_actual` to be a type alias, got `GenericAlias` [invalid-type-alias] +ERROR typeforms_typeform.py:58:7-12: Argument `Literal['int']` is not assignable to parameter `x` with type `TypeForm[Any]` in function `func1` [bad-argument-type] +ERROR typeforms_typeform.py:59:7-19: Argument `Literal['not a type']` is not assignable to parameter `x` with type `TypeForm[Any]` in function `func1` [bad-argument-type] +ERROR typeforms_typeform.py:67:18-25: `tuple[@_, ...]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:68:18-24: `tuple[Literal[1], Literal[2]]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:69:18-19: `Literal[1]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:70:18-22: `type[Self]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:71:18-31: `ClassVar` is not allowed in this context [invalid-annotation] +ERROR typeforms_typeform.py:72:18-28: `Final` is not allowed in this context [invalid-annotation] +ERROR typeforms_typeform.py:73:18-28: `type[*TypeVarTuple[Ts]]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:74:18-26: `type[Optional]` is not assignable to `TypeForm[Any]` [bad-assignment] +ERROR typeforms_typeform.py:75:19-28: `+` is not supported between `type[int]` and `type[str]` [unsupported-operation] +ERROR typeforms_typeform.py:86:16-23: Function call cannot be used in annotations [invalid-annotation] +ERROR typeforms_typeform.py:88:15-22: Function call cannot be used in annotations [invalid-annotation] +ERROR typeforms_typeform.py:98:21-36: `TypeForm[int]` is not assignable to `TypeForm[str]` [bad-assignment] +ERROR typeforms_typeform.py:108:21-31: `type[int]` is not assignable to `TypeForm[str]` [bad-assignment] +""" diff --git a/conformance/results/pyrefly/version.toml b/conformance/results/pyrefly/version.toml new file mode 100644 index 000000000..89dcee8a0 --- /dev/null +++ b/conformance/results/pyrefly/version.toml @@ -0,0 +1 @@ +version = "pyrefly 1.0.0" diff --git a/conformance/results/pyright/aliases_explicit.toml b/conformance/results/pyright/aliases_explicit.toml new file mode 100644 index 000000000..5948d8aba --- /dev/null +++ b/conformance/results/pyright/aliases_explicit.toml @@ -0,0 +1,51 @@ +conformant = "Pass" +output = """ +aliases_explicit.py:67:24 - error: Expected no type arguments for class "int" (reportInvalidTypeArguments) +aliases_explicit.py:67:24 - error: Expected no type arguments for class "NoneType" (reportInvalidTypeArguments) +aliases_explicit.py:68:9 - error: Type "list[int | None]" is already specialized (reportInvalidTypeArguments) +aliases_explicit.py:69:29 - error: Too many type arguments provided for "GoodTypeAlias4[T@GoodTypeAlias4]"; expected 1 but received 2 (reportInvalidTypeForm) +aliases_explicit.py:70:29 - error: Too many type arguments provided for "GoodTypeAlias8[T@GoodTypeAlias8]"; expected 1 but received 2 (reportInvalidTypeForm) +aliases_explicit.py:71:24 - error: Expected ParamSpec, ellipsis, or list of types (reportInvalidTypeForm) +aliases_explicit.py:79:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:79:21 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +aliases_explicit.py:80:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:80:21 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_explicit.py:80:21 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) +aliases_explicit.py:81:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:81:21 - error: Tuple expression not allowed in type expression +  Use tuple[T1, ..., Tn] to indicate a tuple type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_explicit.py:82:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:82:21 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_explicit.py:82:21 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +aliases_explicit.py:83:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:83:21 - error: Dictionary expression not allowed in type expression +  Use dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) +aliases_explicit.py:83:21 - error: Expected class but received "dict[str, Unknown]" (reportGeneralTypeIssues) +aliases_explicit.py:83:28 - error: "b" is not defined (reportUndefinedVariable) +aliases_explicit.py:84:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:84:21 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +aliases_explicit.py:85:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:85:21 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_explicit.py:85:21 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +aliases_explicit.py:85:27 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) +aliases_explicit.py:86:21 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:86:21 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) +aliases_explicit.py:87:21 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_explicit.py:88:22 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:88:22 - error: Expected class but received "Literal[True]" (reportGeneralTypeIssues) +aliases_explicit.py:89:22 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:89:22 - error: Expected class but received "Literal[1]" (reportGeneralTypeIssues) +aliases_explicit.py:90:22 - error: Invalid expression form for type alias definition (reportInvalidTypeForm) +aliases_explicit.py:90:22 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +aliases_explicit.py:91:22 - error: Type expressions cannot use format string literals (f-strings) (reportGeneralTypeIssues) +aliases_explicit.py:100:5 - error: Type "list[Unknown]" is already specialized (reportInvalidTypeArguments) +aliases_explicit.py:101:6 - error: Object of type "UnionType" is not callable (reportCallIssue) +aliases_explicit.py:102:5 - error: Type "list[Unknown]" is already specialized (reportInvalidTypeArguments) +aliases_explicit.py:102:5 - error: Type "set[Unknown]" is already specialized (reportInvalidTypeArguments) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/aliases_implicit.toml b/conformance/results/pyright/aliases_implicit.toml new file mode 100644 index 000000000..32f2b7be2 --- /dev/null +++ b/conformance/results/pyright/aliases_implicit.toml @@ -0,0 +1,32 @@ +conformant = "Pass" +output = """ +aliases_implicit.py:76:24 - error: Expected no type arguments for class "int" (reportInvalidTypeArguments) +aliases_implicit.py:76:24 - error: Expected no type arguments for class "NoneType" (reportInvalidTypeArguments) +aliases_implicit.py:77:9 - error: Type "list[int | None]" is already specialized (reportInvalidTypeArguments) +aliases_implicit.py:78:29 - error: Too many type arguments provided for "GoodTypeAlias4[T@GoodTypeAlias4]"; expected 1 but received 2 (reportInvalidTypeForm) +aliases_implicit.py:79:29 - error: Too many type arguments provided for "GoodTypeAlias8[T@GoodTypeAlias8]"; expected 1 but received 2 (reportInvalidTypeForm) +aliases_implicit.py:80:24 - error: Expected ParamSpec, ellipsis, or list of types (reportInvalidTypeForm) +aliases_implicit.py:81:9 - error: Could not specialize type "GoodTypeAlias12[TFloat@GoodTypeAlias12]" +  Type "str" is not assignable to type "float" +    "str" is not assignable to "float" (reportInvalidTypeForm) +aliases_implicit.py:106:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:107:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:108:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:109:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:110:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:111:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:112:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:113:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:114:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:115:10 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:116:10 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:117:10 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:118:10 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:119:10 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_implicit.py:133:6 - error: Object of type "UnionType" is not callable (reportCallIssue) +aliases_implicit.py:135:5 - error: Type "list[Unknown]" is already specialized (reportInvalidTypeArguments) +aliases_implicit.py:135:5 - error: Type "set[Unknown]" is already specialized (reportInvalidTypeArguments) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/aliases_newtype.toml b/conformance/results/pyright/aliases_newtype.toml new file mode 100644 index 000000000..4e70b6d53 --- /dev/null +++ b/conformance/results/pyright/aliases_newtype.toml @@ -0,0 +1,29 @@ +conformant = "Pass" +output = """ +aliases_newtype.py:11:8 - error: Argument of type "Literal['user']" cannot be assigned to parameter "_x" of type "int" in function "__init__" +  "Literal['user']" is not assignable to "int" (reportArgumentType) +aliases_newtype.py:12:14 - error: Type "Literal[42]" is not assignable to declared type "UserId" +  "Literal[42]" is not assignable to "UserId" (reportAssignmentType) +aliases_newtype.py:18:11 - error: Type "type[UserId]" is not assignable to declared type "type" +  "FunctionType" is not assignable to "type" (reportAssignmentType) +aliases_newtype.py:23:16 - error: Argument of type "type[UserId]" cannot be assigned to parameter "class_or_tuple" of type "_ClassInfo" in function "isinstance" +  Type "FunctionType" is not assignable to type "_ClassInfo" +    "FunctionType" is not assignable to "type" +    "FunctionType" is not assignable to "UnionType" +    "FunctionType" is not assignable to "tuple[_ClassInfo, ...]" (reportArgumentType) +aliases_newtype.py:23:16 - error: Second argument to "isinstance" must be a class or tuple of classes +  Type created with NewType cannot be used with instance and class checks (reportArgumentType) +aliases_newtype.py:26:21 - error: Base class "UserId" is marked final and cannot be subclassed (reportGeneralTypeIssues) +aliases_newtype.py:35:1 - error: NewType must be assigned to a variable with the same name (reportGeneralTypeIssues) +aliases_newtype.py:41:19 - error: Expected no type arguments for class "GoodNewType1" (reportInvalidTypeArguments) +aliases_newtype.py:47:38 - error: Expected class as second argument to NewType (reportGeneralTypeIssues) +aliases_newtype.py:50:43 - error: Type variable "T" has no meaning in this context (reportGeneralTypeIssues) +aliases_newtype.py:52:38 - error: NewType cannot be used with structural type (a Protocol or TypedDict class) (reportGeneralTypeIssues) +aliases_newtype.py:54:38 - error: NewType cannot be used with Literal type (reportGeneralTypeIssues) +aliases_newtype.py:61:38 - error: NewType cannot be used with structural type (a Protocol or TypedDict class) (reportGeneralTypeIssues) +aliases_newtype.py:63:15 - error: NewType requires two positional arguments (reportCallIssue) +aliases_newtype.py:65:38 - error: The second argument to NewType must be a known class, not Any or Unknown (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/aliases_recursive.toml b/conformance/results/pyright/aliases_recursive.toml new file mode 100644 index 000000000..dd135ac2b --- /dev/null +++ b/conformance/results/pyright/aliases_recursive.toml @@ -0,0 +1,77 @@ +conformant = "Pass" +output = """ +aliases_recursive.py:19:12 - error: Type "dict[str, int | complex]" is not assignable to declared type "Json" +  Type "dict[str, int | complex]" is not assignable to type "Json" +    "dict[str, int | complex]" is not assignable to "None" +    "dict[str, int | complex]" is not assignable to "int" +    "dict[str, int | complex]" is not assignable to "str" +    "dict[str, int | complex]" is not assignable to "float" +    "dict[str, int | complex]" is not assignable to "list[Json]" +    "dict[str, int | complex]" is not assignable to "dict[str, Json]" +      Type parameter "_VT@dict" is invariant, but "int | complex" is not the same as "Json" + ... (reportAssignmentType) +aliases_recursive.py:20:16 - error: Type "list[int | complex]" is not assignable to declared type "Json" +  Type "complex" is not assignable to type "Json" +    "complex" is not assignable to "None" +    "complex" is not assignable to "int" +    "complex" is not assignable to "str" +    "complex" is not assignable to "float" +    "complex" is not assignable to "list[Json]" +    "complex" is not assignable to "dict[str, Json]" (reportAssignmentType) +aliases_recursive.py:38:22 - error: Type "tuple[Literal[1], tuple[str, int], tuple[int, tuple[int, list[int]]]]" is not assignable to declared type "RecursiveTuple" +  Type "tuple[Literal[1], tuple[str, int], tuple[int, tuple[int, list[int]]]]" is not assignable to type "RecursiveTuple" +    "tuple[Literal[1], tuple[str, int], tuple[int, tuple[int, list[int]]]]" is not assignable to "str" +    "tuple[Literal[1], tuple[str, int], tuple[int, tuple[int, list[int]]]]" is not assignable to "int" +    "tuple[Literal[1], tuple[str, int], tuple[int, tuple[int, list[int]]]]" is not assignable to "tuple[RecursiveTuple, ...]" +      Tuple entry 1 is incorrect type +        Type "tuple[str, int] | tuple[int, tuple[int, list[int]]] | Literal[1]" is not assignable to type "RecursiveTuple" +          Type "tuple[int, tuple[int, list[int]]]" is not assignable to type "RecursiveTuple" (reportAssignmentType) +aliases_recursive.py:39:22 - error: Type "tuple[Literal[1], list[int]]" is not assignable to declared type "RecursiveTuple" +  Type "tuple[Literal[1], list[int]]" is not assignable to type "RecursiveTuple" +    "tuple[Literal[1], list[int]]" is not assignable to "str" +    "tuple[Literal[1], list[int]]" is not assignable to "int" +    "tuple[Literal[1], list[int]]" is not assignable to "tuple[RecursiveTuple, ...]" +      Tuple entry 1 is incorrect type +        Type "list[int] | Literal[1]" is not assignable to type "RecursiveTuple" +          Type "list[int]" is not assignable to type "RecursiveTuple" (reportAssignmentType) +aliases_recursive.py:50:24 - error: Type "dict[str, list[int]]" is not assignable to declared type "RecursiveMapping" +  Type "dict[str, list[int]]" is not assignable to type "RecursiveMapping" +    "dict[str, list[int]]" is not assignable to "str" +    "dict[str, list[int]]" is not assignable to "int" +    "dict[str, list[int]]" is not assignable to "Mapping[str, RecursiveMapping]" +      Type parameter "_VT_co@Mapping" is covariant, but "list[int]" is not a subtype of "RecursiveMapping" +        Type "list[int]" is not assignable to type "RecursiveMapping" +          "list[int]" is not assignable to "str" +          "list[int]" is not assignable to "int" + ... (reportAssignmentType) +aliases_recursive.py:51:24 - error: Type "dict[str, str | int | list[int]]" is not assignable to declared type "RecursiveMapping" +  Type "dict[str, str | int | list[int]]" is not assignable to type "RecursiveMapping" +    "dict[str, str | int | list[int]]" is not assignable to "str" +    "dict[str, str | int | list[int]]" is not assignable to "int" +    "dict[str, str | int | list[int]]" is not assignable to "Mapping[str, RecursiveMapping]" +      Type parameter "_VT_co@Mapping" is covariant, but "str | int | list[int]" is not a subtype of "RecursiveMapping" +        Type "str | int | list[int]" is not assignable to type "RecursiveMapping" +          Type "list[int]" is not assignable to type "RecursiveMapping" (reportAssignmentType) +aliases_recursive.py:52:24 - error: Type "dict[str, str | int | dict[str, str | int | list[int]]]" is not assignable to declared type "RecursiveMapping" +  Type "dict[str, str | int | dict[str, str | int | list[int]]]" is not assignable to type "RecursiveMapping" +    "dict[str, str | int | dict[str, str | int | list[int]]]" is not assignable to "str" +    "dict[str, str | int | dict[str, str | int | list[int]]]" is not assignable to "int" +    "dict[str, str | int | dict[str, str | int | list[int]]]" is not assignable to "Mapping[str, RecursiveMapping]" +      Type parameter "_VT_co@Mapping" is covariant, but "str | int | dict[str, str | int | list[int]]" is not a subtype of "RecursiveMapping" +        Type "str | int | dict[str, str | int | list[int]]" is not assignable to type "RecursiveMapping" +          Type "dict[str, str | int | list[int]]" is not assignable to type "RecursiveMapping" (reportAssignmentType) +aliases_recursive.py:63:38 - error: Type "list[str | list[float]]" is not assignable to declared type "GenericTypeAlias1[str]" +  Type "float" is not assignable to type "GenericTypeAlias1[T1@GenericTypeAlias1] | str" +    "float" is not assignable to "list[GenericTypeAlias1 | str]" +    "float" is not assignable to "str" (reportAssignmentType) +aliases_recursive.py:69:51 - error: Type "list[list[int | list[str | int | list[float]]] | str]" is not assignable to declared type "GenericTypeAlias2[str, int]" +  Type "float" is not assignable to type "GenericTypeAlias2[T1@GenericTypeAlias2, T2@GenericTypeAlias2] | str | int" +    "float" is not assignable to "list[GenericTypeAlias2 | str | int]" +    "float" is not assignable to "str" +    "float" is not assignable to "int" (reportAssignmentType) +aliases_recursive.py:72:29 - error: Type alias "RecursiveUnion" cannot use itself in its definition (reportGeneralTypeIssues) +aliases_recursive.py:75:31 - error: Type alias "MutualReference1" cannot use itself in its definition (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/aliases_type_statement.toml b/conformance/results/pyright/aliases_type_statement.toml new file mode 100644 index 000000000..407dbd082 --- /dev/null +++ b/conformance/results/pyright/aliases_type_statement.toml @@ -0,0 +1,53 @@ +conformant = "Pass" +output = """ +aliases_type_statement.py:17:12 - error: Cannot access attribute "bit_count" for class "TypeAliasType" +  Attribute "bit_count" is unknown (reportAttributeAccessIssue) +aliases_type_statement.py:19:1 - error: Object of type "TypeAliasType" is not callable +  Attribute "__call__" is unknown (reportCallIssue) +aliases_type_statement.py:23:18 - error: Cannot access attribute "other_attrib" for class "TypeAliasType" +  Attribute "other_attrib" is unknown (reportAttributeAccessIssue) +aliases_type_statement.py:26:18 - error: A type alias defined in a "type" statement cannot be used as a base class (reportGeneralTypeIssues) +aliases_type_statement.py:31:22 - error: Argument of type "TypeAliasType" cannot be assigned to parameter "class_or_tuple" of type "_ClassInfo" in function "isinstance" +  Type "TypeAliasType" is not assignable to type "_ClassInfo" +    "TypeAliasType" is not assignable to "type" +    "TypeAliasType" is not assignable to "UnionType" +    "TypeAliasType" is not assignable to "tuple[_ClassInfo, ...]" (reportArgumentType) +aliases_type_statement.py:37:22 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +aliases_type_statement.py:38:22 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_type_statement.py:38:22 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) +aliases_type_statement.py:39:22 - error: Tuple expression not allowed in type expression +  Use tuple[T1, ..., Tn] to indicate a tuple type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_type_statement.py:40:22 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_type_statement.py:40:22 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +aliases_type_statement.py:41:22 - error: Dictionary expression not allowed in type expression +  Use dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) +aliases_type_statement.py:41:22 - error: Expected class but received "dict[str, Unknown]" (reportGeneralTypeIssues) +aliases_type_statement.py:41:29 - error: "b" is not defined (reportUndefinedVariable) +aliases_type_statement.py:42:22 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +aliases_type_statement.py:43:22 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_type_statement.py:43:22 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +aliases_type_statement.py:43:28 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) +aliases_type_statement.py:44:22 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) +aliases_type_statement.py:45:22 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_type_statement.py:46:23 - error: Expected class but received "Literal[True]" (reportGeneralTypeIssues) +aliases_type_statement.py:47:23 - error: Expected class but received "Literal[1]" (reportGeneralTypeIssues) +aliases_type_statement.py:48:23 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +aliases_type_statement.py:49:23 - error: Type expressions cannot use format string literals (f-strings) (reportGeneralTypeIssues) +aliases_type_statement.py:53:23 - error: Type parameter "V" is not included in the type parameter list for "TA1" (reportGeneralTypeIssues) +aliases_type_statement.py:58:17 - error: Type parameter "T1" is not included in the type parameter list for "TA2" (reportGeneralTypeIssues) +aliases_type_statement.py:68:7 - error: Could not specialize type "RecursiveTypeAlias2[S@RecursiveTypeAlias2, T@RecursiveTypeAlias2, P@RecursiveTypeAlias2]" +  Type "str" is not assignable to type "int" +    "str" is not assignable to "int" (reportInvalidTypeForm) +aliases_type_statement.py:70:7 - error: Could not specialize type "RecursiveTypeAlias2[S@RecursiveTypeAlias2, T@RecursiveTypeAlias2, P@RecursiveTypeAlias2]" +  Type "int" is not assignable to type "str" +    "int" is not assignable to "str" (reportInvalidTypeForm) +aliases_type_statement.py:73:28 - error: Type alias "RecursiveTypeAlias3" cannot use itself in its definition (reportGeneralTypeIssues) +aliases_type_statement.py:75:31 - error: Type alias "RecursiveTypeAlias4" cannot use itself in its definition (reportGeneralTypeIssues) +aliases_type_statement.py:79:28 - error: Type alias "RecursiveTypeAlias6" cannot use itself in its definition (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/aliases_typealiastype.toml b/conformance/results/pyright/aliases_typealiastype.toml new file mode 100644 index 000000000..8bd8d47da --- /dev/null +++ b/conformance/results/pyright/aliases_typealiastype.toml @@ -0,0 +1,35 @@ +conformant = "Pass" +output = """ +aliases_typealiastype.py:32:18 - error: Cannot access attribute "other_attrib" for class "TypeAliasType" +  Attribute "other_attrib" is unknown (reportAttributeAccessIssue) +aliases_typealiastype.py:40:5 - error: Could not specialize type "GoodAlias5[S@GoodAlias5, TStr@GoodAlias5, P@GoodAlias5, Ts@GoodAlias5]" +  Type "int" is not assignable to type "str" +    "int" is not assignable to "str" (reportInvalidTypeForm) +aliases_typealiastype.py:43:45 - error: Type variable "S" has no meaning in this context (reportGeneralTypeIssues) +aliases_typealiastype.py:44:45 - error: Type variable "S" has no meaning in this context (reportGeneralTypeIssues) +aliases_typealiastype.py:45:57 - error: Type parameter list must be a tuple containing only TypeVar, TypeVarTuple, or ParamSpec (reportGeneralTypeIssues) +aliases_typealiastype.py:46:40 - error: Type alias "BadAlias4" cannot use itself in its definition (reportGeneralTypeIssues) +aliases_typealiastype.py:47:40 - error: Type alias "BadAlias5" cannot use itself in its definition (reportGeneralTypeIssues) +aliases_typealiastype.py:48:40 - error: Type alias "BadAlias6" cannot use itself in its definition (reportGeneralTypeIssues) +aliases_typealiastype.py:52:40 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +aliases_typealiastype.py:53:40 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) +aliases_typealiastype.py:54:42 - error: Expected class but received "tuple[tuple[type[int], type[str]]]" (reportGeneralTypeIssues) +aliases_typealiastype.py:55:42 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +aliases_typealiastype.py:56:42 - error: Expected class but received "dict[str, Unknown]" (reportGeneralTypeIssues) +aliases_typealiastype.py:56:49 - error: "b" is not defined (reportUndefinedVariable) +aliases_typealiastype.py:57:42 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +aliases_typealiastype.py:58:42 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +aliases_typealiastype.py:58:42 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +aliases_typealiastype.py:58:48 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) +aliases_typealiastype.py:59:42 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) +aliases_typealiastype.py:60:42 - error: Variable not allowed in type expression (reportInvalidTypeForm) +aliases_typealiastype.py:61:42 - error: Expected class but received "Literal[True]" (reportGeneralTypeIssues) +aliases_typealiastype.py:62:42 - error: Expected class but received "Literal[1]" (reportGeneralTypeIssues) +aliases_typealiastype.py:63:42 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +aliases_typealiastype.py:64:42 - error: Type expressions cannot use format string literals (f-strings) (reportGeneralTypeIssues) +aliases_typealiastype.py:66:47 - error: "BadAlias21" is not defined (reportUndefinedVariable) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/aliases_variance.toml b/conformance/results/pyright/aliases_variance.toml new file mode 100644 index 000000000..b653968e9 --- /dev/null +++ b/conformance/results/pyright/aliases_variance.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +output = """ +aliases_variance.py:24:23 - error: Type "T_co@ClassA_1" cannot be assigned to type variable "T@ClassA" +  Variance of type argument "T_co@ClassA_1" is incompatible with base class "ClassA" (reportInvalidTypeArguments) +aliases_variance.py:28:26 - error: Could not specialize type "A_Alias_1[T_co@A_Alias_1]" +  Variance of type argument "T_co@ClassA_2" is incompatible with "T_co@A_Alias_1" (reportInvalidTypeForm) +aliases_variance.py:32:26 - error: Could not specialize type "A_Alias_2[T_co@A_Alias_2]" +  Variance of type argument "T_co@ClassA_3" is incompatible with "T_co@A_Alias_2" (reportInvalidTypeForm) +aliases_variance.py:44:26 - error: Could not specialize type "B_Alias_1[T_co@B_Alias_1, T_contra@B_Alias_1]" +  Variance of type argument "T_contra@ClassB_1" is incompatible with "T_co@B_Alias_1" (reportInvalidTypeForm) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/annotations_coroutines.toml b/conformance/results/pyright/annotations_coroutines.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/annotations_coroutines.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/annotations_forward_refs.toml b/conformance/results/pyright/annotations_forward_refs.toml new file mode 100644 index 000000000..ca35b5bb1 --- /dev/null +++ b/conformance/results/pyright/annotations_forward_refs.toml @@ -0,0 +1,39 @@ +conformant = "Pass" +output = """ +annotations_forward_refs.py:22:7 - error: "ClassA" is not defined (reportUndefinedVariable) +annotations_forward_refs.py:23:12 - error: "ClassA" is not defined (reportUndefinedVariable) +annotations_forward_refs.py:24:7 - error: Union syntax cannot be used with string operand; use quotes around entire expression (reportGeneralTypeIssues) +annotations_forward_refs.py:25:13 - error: Union syntax cannot be used with string operand; use quotes around entire expression (reportGeneralTypeIssues) +annotations_forward_refs.py:41:10 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +annotations_forward_refs.py:42:10 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +annotations_forward_refs.py:42:10 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) +annotations_forward_refs.py:43:10 - error: Tuple expression not allowed in type expression +  Use tuple[T1, ..., Tn] to indicate a tuple type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +annotations_forward_refs.py:44:10 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +annotations_forward_refs.py:44:10 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +annotations_forward_refs.py:45:10 - error: Dictionary expression not allowed in type expression +  Use dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) +annotations_forward_refs.py:45:10 - error: Expected class but received "dict[Unknown, Unknown]" (reportGeneralTypeIssues) +annotations_forward_refs.py:46:10 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +annotations_forward_refs.py:47:10 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +annotations_forward_refs.py:47:10 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +annotations_forward_refs.py:47:16 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) +annotations_forward_refs.py:48:10 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) +annotations_forward_refs.py:49:10 - error: Variable not allowed in type expression (reportInvalidTypeForm) +annotations_forward_refs.py:50:11 - error: Expected class but received "Literal[True]" (reportGeneralTypeIssues) +annotations_forward_refs.py:51:11 - error: Expected class but received "Literal[1]" (reportGeneralTypeIssues) +annotations_forward_refs.py:52:11 - error: Unary operator not allowed in type expression (reportInvalidTypeForm) +annotations_forward_refs.py:53:11 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +annotations_forward_refs.py:54:11 - error: Type expressions cannot use format string literals (f-strings) (reportGeneralTypeIssues) +annotations_forward_refs.py:55:10 - error: Module cannot be used as a type (reportGeneralTypeIssues) +annotations_forward_refs.py:66:26 - error: "ClassB" is not defined (reportUndefinedVariable) +annotations_forward_refs.py:80:14 - error: Type of "ClassF" could not be determined because it refers to itself (reportGeneralTypeIssues) +annotations_forward_refs.py:80:14 - error: Variable not allowed in type expression (reportInvalidTypeForm) +annotations_forward_refs.py:89:8 - error: Expected class but received "(self: Self@ClassD) -> None" (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/annotations_generators.toml b/conformance/results/pyright/annotations_generators.toml new file mode 100644 index 000000000..84e2463d0 --- /dev/null +++ b/conformance/results/pyright/annotations_generators.toml @@ -0,0 +1,42 @@ +conformant = "Pass" +output = """ +annotations_generators.py:51:21 - error: Function with declared return type "C" must return value on all code paths +  "None" is not assignable to "C" (reportReturnType) +annotations_generators.py:54:16 - error: Type "Literal[False]" is not assignable to return type "C" +  "Literal[False]" is not assignable to "C" (reportReturnType) +annotations_generators.py:57:15 - error: Return type of generator function must be compatible with "Generator[Literal[3], Any, Any]" +  "Generator[Literal[3], Unknown, Unknown]" is not assignable to "Generator[A, B, C]" +    Type parameter "_YieldT_co@Generator" is covariant, but "Literal[3]" is not a subtype of "A" +      "Literal[3]" is not assignable to "A" (reportReturnType) +annotations_generators.py:66:15 - error: Return type of generator function must be compatible with "Generator[Literal[3], Any, Any]" +  "Generator[Literal[3], Unknown, Unknown]" is not assignable to "Generator[A, int, Any]" +    Type parameter "_YieldT_co@Generator" is covariant, but "Literal[3]" is not a subtype of "A" +      "Literal[3]" is not assignable to "A" (reportReturnType) +annotations_generators.py:75:11 - error: Return type of generator function must be compatible with "Generator[B, Any, Any]" +  "Generator[B, Unknown, Unknown]" is not assignable to "Iterator[A]" +    Type parameter "_T_co@Iterator" is covariant, but "B" is not a subtype of "A" +      "B" is not assignable to "A" (reportReturnType) +annotations_generators.py:86:21 - error: Return type of generator function must be compatible with "Generator[Any, Any, Any]" +  "Generator[Any, Any, Any]" is not assignable to "int" (reportInvalidTypeForm) +annotations_generators.py:87:11 - error: Return type of generator function must be compatible with "Generator[None, Any, Any]" +  "Generator[None, Unknown, Unknown]" is not assignable to "int" (reportReturnType) +annotations_generators.py:91:27 - error: Return type of async generator function must be compatible with "AsyncGenerator[Any, Any]" +  "AsyncGenerator[Any, Any, Any]" is not assignable to "int" (reportInvalidTypeForm) +annotations_generators.py:92:11 - error: Return type of async generator function must be compatible with "AsyncGenerator[None, Any]" +  "AsyncGenerator[None, Unknown, Unknown]" is not assignable to "int" (reportReturnType) +annotations_generators.py:118:16 - error: Return type of generator function must be compatible with "Generator[A, Any, Any]" +  "Generator[A, Unknown, Unknown]" is not assignable to "Iterator[B]" +    Type parameter "_T_co@Iterator" is covariant, but "A" is not a subtype of "B" +      "A" is not assignable to "B" (reportReturnType) +annotations_generators.py:119:16 - error: Return type of generator function must be compatible with "Generator[int, Any, Any]" +  "Generator[int, Unknown, Unknown]" is not assignable to "Iterator[B]" +    Type parameter "_T_co@Iterator" is covariant, but "int" is not a subtype of "B" +      "int" is not assignable to "B" (reportReturnType) +annotations_generators.py:135:16 - error: Return type of generator function must be compatible with "Generator[None, Any, Any]" +  "Generator[None, int, Unknown]" is not assignable to "Generator[None, str, None]" +    Type parameter "_SendT_contra@Generator" is contravariant, but "int" is not a supertype of "str" +      "str" is not assignable to "int" (reportReturnType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/annotations_methods.toml b/conformance/results/pyright/annotations_methods.toml new file mode 100644 index 000000000..27ff70b96 --- /dev/null +++ b/conformance/results/pyright/annotations_methods.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +notes = """ +Type evaluation differs from other type checkers because of ambiguity in the spec related to method bindings. +""" +output = """ +annotations_methods.py:46:8 - error: Argument of type "A" cannot be assigned to parameter "self" of type "B" in function "copy" +  "A" is not assignable to "B" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/annotations_typeexpr.toml b/conformance/results/pyright/annotations_typeexpr.toml new file mode 100644 index 000000000..e9e3fdca6 --- /dev/null +++ b/conformance/results/pyright/annotations_typeexpr.toml @@ -0,0 +1,31 @@ +conformant = "Pass" +output = """ +annotations_typeexpr.py:88:9 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +annotations_typeexpr.py:89:9 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +annotations_typeexpr.py:89:9 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) +annotations_typeexpr.py:90:9 - error: Tuple expression not allowed in type expression +  Use tuple[T1, ..., Tn] to indicate a tuple type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +annotations_typeexpr.py:91:9 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +annotations_typeexpr.py:91:9 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +annotations_typeexpr.py:92:9 - error: Dictionary expression not allowed in type expression +  Use dict[T1, T2] to indicate a dictionary type (reportInvalidTypeForm) +annotations_typeexpr.py:92:9 - error: Expected class but received "dict[Unknown, Unknown]" (reportGeneralTypeIssues) +annotations_typeexpr.py:93:9 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +annotations_typeexpr.py:94:9 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +annotations_typeexpr.py:94:9 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +annotations_typeexpr.py:94:15 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) +annotations_typeexpr.py:95:9 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) +annotations_typeexpr.py:96:9 - error: Variable not allowed in type expression (reportInvalidTypeForm) +annotations_typeexpr.py:97:10 - error: Expected class but received "Literal[True]" (reportGeneralTypeIssues) +annotations_typeexpr.py:98:10 - error: Expected class but received "Literal[1]" (reportGeneralTypeIssues) +annotations_typeexpr.py:99:10 - error: Unary operator not allowed in type expression (reportInvalidTypeForm) +annotations_typeexpr.py:100:10 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +annotations_typeexpr.py:101:10 - error: Type expressions cannot use format string literals (f-strings) (reportGeneralTypeIssues) +annotations_typeexpr.py:102:10 - error: Module cannot be used as a type (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/callables_annotation.toml b/conformance/results/pyright/callables_annotation.toml new file mode 100644 index 000000000..66fdcb54e --- /dev/null +++ b/conformance/results/pyright/callables_annotation.toml @@ -0,0 +1,41 @@ +conformant = "Pass" +output = """ +callables_annotation.py:25:5 - error: Expected 1 more positional argument (reportCallIssue) +callables_annotation.py:26:11 - error: Argument of type "Literal[2]" cannot be assigned to parameter of type "str" +  "Literal[2]" is not assignable to "str" (reportArgumentType) +callables_annotation.py:27:15 - error: Expected 2 positional arguments (reportCallIssue) +callables_annotation.py:29:10 - error: Expected 2 more positional arguments (reportCallIssue) +callables_annotation.py:35:8 - error: Expected 0 positional arguments (reportCallIssue) +callables_annotation.py:55:14 - error: Expected parameter type list or "..." (reportInvalidTypeForm) +callables_annotation.py:56:14 - error: Expected parameter type list or "..." (reportInvalidTypeForm) +callables_annotation.py:57:18 - error: List expression not allowed for this type argument (reportInvalidTypeForm) +callables_annotation.py:58:14 - error: Expected parameter type list or "..." (reportInvalidTypeForm) +callables_annotation.py:58:24 - error: Expected only two type arguments to "Callable" (reportInvalidTypeForm) +callables_annotation.py:59:15 - error: "..." is not allowed in this context (reportInvalidTypeForm) +callables_annotation.py:91:7 - error: Type "() -> str" is not assignable to declared type "(int, ...) -> str" +  Type "() -> str" is not assignable to type "(int, ...) -> str" +    Function accepts too many positional parameters; expected 0 but received 2 (reportAssignmentType) +callables_annotation.py:93:7 - error: Type "(*, a: int) -> str" is not assignable to declared type "(int, ...) -> str" +  Type "(*, a: int) -> str" is not assignable to type "(int, ...) -> str" +    Function accepts too many positional parameters; expected 0 but received 2 (reportAssignmentType) +callables_annotation.py:159:25 - error: Type "Proto8" is not assignable to declared type "Proto5[Any]" +  "Proto8" is incompatible with protocol "Proto5[Any]" +    "__call__" is an incompatible type +      Type "() -> None" is not assignable to type "(*args: T_contra@Proto5, **kwargs: T_contra@Proto5) -> None" +        Parameter "*args" has no corresponding parameter +        Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) +callables_annotation.py:172:26 - error: Type "() -> str" is not assignable to declared type "Callback2[...]" +  Type "() -> str" is not assignable to type "Callback2[...]" +    Function accepts too many positional parameters; expected 0 but received 2 (reportAssignmentType) +callables_annotation.py:187:48 - error: Type "(int, str) -> str" is not assignable to declared type "(str, ...) -> str" +  Type "(int, str) -> str" is not assignable to type "(str, ...) -> str" +    Parameter 1: type "str" is incompatible with type "int" +      "str" is not assignable to "int" (reportAssignmentType) +callables_annotation.py:189:32 - error: Type "(int, str) -> str" is not assignable to declared type "CallbackWithStr[...]" +  Type "(int, str) -> str" is not assignable to type "CallbackWithStr[...]" +    Parameter 1: type "str" is incompatible with type "int" +      "str" is not assignable to "int" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/callables_kwargs.toml b/conformance/results/pyright/callables_kwargs.toml new file mode 100644 index 000000000..191d97a58 --- /dev/null +++ b/conformance/results/pyright/callables_kwargs.toml @@ -0,0 +1,36 @@ +conformant = "Pass" +output = """ +callables_kwargs.py:28:5 - error: Could not access item in TypedDict +  "v2" is not a required key in "TD2", so access may result in runtime exception (reportTypedDictNotRequiredAccess) +callables_kwargs.py:46:5 - error: Arguments missing for parameters "v1", "v3" (reportCallIssue) +callables_kwargs.py:51:32 - error: No parameter named "v4" (reportCallIssue) +callables_kwargs.py:52:11 - error: Expected 0 positional arguments (reportCallIssue) +callables_kwargs.py:58:13 - error: Argument of type "str" cannot be assigned to parameter "v1" of type "int" in function "func1" +  "str" is not assignable to "int" (reportArgumentType) +callables_kwargs.py:63:19 - error: Unable to match unpacked TypedDict argument to parameters +  Parameter "v1" is already assigned (reportCallIssue) +callables_kwargs.py:64:16 - error: Unable to match unpacked TypedDict argument to parameters +  Parameter "v3" is already assigned (reportCallIssue) +callables_kwargs.py:65:19 - error: Unable to match unpacked TypedDict argument to parameters +  Parameter "v1" is already assigned (reportCallIssue) +callables_kwargs.py:101:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol3" +  Type "(**kwargs: **TD2) -> None" is not assignable to type "(*, v1: int, v2: int, v3: str) -> None" +    Keyword parameter "v2" of type "int" is incompatible with type "str" +      "int" is not assignable to "str" (reportAssignmentType) +callables_kwargs.py:102:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol4" +  Type "(**kwargs: **TD2) -> None" is not assignable to type "(*, v1: int) -> None" +    Extra parameter "v3" (reportAssignmentType) +callables_kwargs.py:103:19 - error: Type "(**kwargs: **TD2) -> None" is not assignable to declared type "TDProtocol5" +  Type "(**kwargs: **TD2) -> None" is not assignable to type "(v1: int, v3: str) -> None" +    Function accepts too many positional parameters; expected 0 but received 2 +      Extra parameter "v1" +      Extra parameter "v3" (reportAssignmentType) +callables_kwargs.py:111:30 - error: Typed dictionary overlaps with keyword parameter: v1 (reportGeneralTypeIssues) +callables_kwargs.py:122:21 - error: Expected TypedDict type argument for Unpack (reportGeneralTypeIssues) +callables_kwargs.py:134:19 - error: Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to declared type "TDProtocol6" +  Type "(*, v1: int, v3: str, v2: str = "") -> None" is not assignable to type "(**kwargs: **TD2) -> None" +    Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/callables_protocol.toml b/conformance/results/pyright/callables_protocol.toml new file mode 100644 index 000000000..82afbb4c6 --- /dev/null +++ b/conformance/results/pyright/callables_protocol.toml @@ -0,0 +1,67 @@ +conformant = "Pass" +output = """ +callables_protocol.py:35:7 - error: Type "(*vals: bytes, max_items: int | None) -> list[bytes]" is not assignable to declared type "Proto1" +  Type "(*vals: bytes, max_items: int | None) -> list[bytes]" is not assignable to type "(*vals: bytes, max_len: int | None = None) -> list[bytes]" +    Extra parameter "max_items" +    Missing keyword parameter "max_len" (reportAssignmentType) +callables_protocol.py:36:7 - error: Type "(*vals: bytes) -> list[bytes]" is not assignable to declared type "Proto1" +  Type "(*vals: bytes) -> list[bytes]" is not assignable to type "(*vals: bytes, max_len: int | None = None) -> list[bytes]" +    Missing keyword parameter "max_len" (reportAssignmentType) +callables_protocol.py:37:7 - error: Type "(*vals: bytes, max_len: str | None) -> list[bytes]" is not assignable to declared type "Proto1" +  Type "(*vals: bytes, max_len: str | None) -> list[bytes]" is not assignable to type "(*vals: bytes, max_len: int | None = None) -> list[bytes]" +    Keyword parameter "max_len" of type "int | None" is incompatible with type "str | None" +      Type "int | None" is not assignable to type "str | None" +        Type "int" is not assignable to type "str | None" +          "int" is not assignable to "str" +          "int" is not assignable to "None" +    Parameter "max_len" is missing default argument (reportAssignmentType) +callables_protocol.py:67:7 - error: Type "(*a: bytes) -> None" is not assignable to declared type "Proto2" +  Type "(*a: bytes) -> None" is not assignable to type "(*vals: bytes, **kwargs: str) -> None" +    Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) +callables_protocol.py:68:7 - error: Type "(*a: str, **b: str) -> None" is not assignable to declared type "Proto2" +  Type "(*a: str, **b: str) -> None" is not assignable to type "(*vals: bytes, **kwargs: str) -> None" +    Parameter 1: type "*tuple[bytes, ...]" is incompatible with type "*tuple[str, ...]" +      "*tuple[bytes, ...]" is not assignable to "*tuple[str, ...]" +        Tuple entry 1 is incorrect type +          "bytes" is not assignable to "str" (reportAssignmentType) +callables_protocol.py:69:7 - error: Type "(*a: bytes, **b: bytes) -> None" is not assignable to declared type "Proto2" +  Type "(*a: bytes, **b: bytes) -> None" is not assignable to type "(*vals: bytes, **kwargs: str) -> None" +    Parameter 2: type "str" is incompatible with type "bytes" +      "str" is not assignable to "bytes" (reportAssignmentType) +callables_protocol.py:70:7 - error: Type "(**b: str) -> None" is not assignable to declared type "Proto2" +  Type "(**b: str) -> None" is not assignable to type "(*vals: bytes, **kwargs: str) -> None" +    Parameter "*vals" has no corresponding parameter (reportAssignmentType) +callables_protocol.py:97:16 - error: Type "(x: int) -> None" is not assignable to declared type "Proto4" +  "FunctionType" is incompatible with protocol "Proto4" +    "other_attribute" is not present (reportAssignmentType) +callables_protocol.py:121:18 - error: Type "(*vals: bytes, max_len: int | None = None) -> list[bytes]" is not assignable to declared type "NotProto6" +  "FunctionType" is not assignable to "NotProto6" (reportAssignmentType) +callables_protocol.py:169:7 - error: Type "(x: int) -> Any" is not assignable to declared type "Proto8" +  One or more overloads of "__call__" is not assignable +    Type "(x: int) -> Any" is not assignable to type "(x: str) -> str" +      Parameter 1: type "str" is incompatible with type "int" +        "str" is not assignable to "int" (reportAssignmentType) +callables_protocol.py:186:33 - error: Cannot assign to attribute "other_attribute" for class "Proto9[P@decorator1, R@decorator1]" +  "Literal['str']" is not assignable to "int" (reportAttributeAccessIssue) +callables_protocol.py:187:15 - error: Cannot assign to attribute "xxx" for class "Proto9[P@decorator1, R@decorator1]" +  Attribute "xxx" is unknown (reportAttributeAccessIssue) +callables_protocol.py:197:16 - error: Cannot access attribute "other_attribute2" for class "Proto9[(x: int), str]" +  Attribute "other_attribute2" is unknown (reportAttributeAccessIssue) +callables_protocol.py:238:8 - error: Type "(x: int, y: str, /) -> Any" is not assignable to declared type "Proto11" +  Type "(x: int, y: str, /) -> Any" is not assignable to type "(x: int, /, y: str) -> Any" +    Missing keyword parameter "y" +      Position-only parameter mismatch; parameter "y" is not position-only +      Position-only parameter mismatch; expected 2 but received 1 (reportAssignmentType) +callables_protocol.py:260:8 - error: Type "(*args: Any, kwarg0: Any) -> None" is not assignable to declared type "Proto12" +  Type "(*args: Any, kwarg0: Any) -> None" is not assignable to type "(*args: Any, kwarg0: Any, kwarg1: Any) -> None" +    Missing keyword parameter "kwarg1" (reportAssignmentType) +callables_protocol.py:284:27 - error: Type "(path: str) -> str" is not assignable to declared type "Proto13_Default" +  Type "(path: str) -> str" is not assignable to type "(path: str = ...) -> str" +    Parameter "path" is missing default argument (reportAssignmentType) +callables_protocol.py:311:27 - error: Type "(*, path: str) -> str" is not assignable to declared type "Proto14_Default" +  Type "(*, path: str) -> str" is not assignable to type "(*, path: str = ...) -> str" +    Parameter "path" is missing default argument (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/callables_subtyping.toml b/conformance/results/pyright/callables_subtyping.toml new file mode 100644 index 000000000..53f09982d --- /dev/null +++ b/conformance/results/pyright/callables_subtyping.toml @@ -0,0 +1,202 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +callables_subtyping.py:26:36 - error: Type "(int) -> int" is not assignable to declared type "(float) -> float" +  Type "(int) -> int" is not assignable to type "(float) -> float" +    Parameter 1: type "float" is incompatible with type "int" +      "float" is not assignable to "int" (reportAssignmentType) +callables_subtyping.py:29:32 - error: Type "(float) -> float" is not assignable to declared type "(int) -> int" +  Type "(float) -> float" is not assignable to type "(int) -> int" +    Function return type "float" is incompatible with type "int" +      "float" is not assignable to "int" (reportAssignmentType) +callables_subtyping.py:51:21 - error: Type "PosOnly2" is not assignable to declared type "Standard2" +  "PosOnly2" is incompatible with protocol "Standard2" +    "__call__" is an incompatible type +      Type "(b: int, a: int, /) -> None" is not assignable to type "(a: int, b: int) -> None" +        Missing keyword parameter "a" +        Missing keyword parameter "b" +          Position-only parameter mismatch; parameter "a" is not position-only +          Position-only parameter mismatch; parameter "b" is not position-only +          Position-only parameter mismatch; expected 2 but received 0 (reportAssignmentType) +callables_subtyping.py:52:21 - error: Type "KwOnly2" is not assignable to declared type "Standard2" +  "KwOnly2" is incompatible with protocol "Standard2" +    "__call__" is an incompatible type +      Type "(*, b: int, a: int) -> None" is not assignable to type "(a: int, b: int) -> None" +        Function accepts too many positional parameters; expected 0 but received 2 +          Extra parameter "b" +          Extra parameter "a" (reportAssignmentType) +callables_subtyping.py:55:20 - error: Type "KwOnly2" is not assignable to declared type "PosOnly2" +  "KwOnly2" is incompatible with protocol "PosOnly2" +    "__call__" is an incompatible type +      Type "(*, b: int, a: int) -> None" is not assignable to type "(b: int, a: int, /) -> None" +        Function accepts too many positional parameters; expected 0 but received 2 +          Extra parameter "b" +          Extra parameter "a" (reportAssignmentType) +callables_subtyping.py:58:19 - error: Type "PosOnly2" is not assignable to declared type "KwOnly2" +  "PosOnly2" is incompatible with protocol "KwOnly2" +    "__call__" is an incompatible type +      Type "(b: int, a: int, /) -> None" is not assignable to type "(*, b: int, a: int) -> None" +        Position-only parameter mismatch; expected 2 but received 0 +        Function accepts too few positional parameters; expected 2 but received 0 +        Missing keyword parameter "b" +        Missing keyword parameter "a" (reportAssignmentType) +callables_subtyping.py:82:20 - error: Type "NoArgs3" is not assignable to declared type "IntArgs3" +  "NoArgs3" is incompatible with protocol "IntArgs3" +    "__call__" is an incompatible type +      Type "() -> None" is not assignable to type "(*args: int) -> None" +        Parameter "*args" has no corresponding parameter (reportAssignmentType) +callables_subtyping.py:85:22 - error: Type "NoArgs3" is not assignable to declared type "FloatArgs3" +  "NoArgs3" is incompatible with protocol "FloatArgs3" +    "__call__" is an incompatible type +      Type "() -> None" is not assignable to type "(*args: float) -> None" +        Parameter "*args" has no corresponding parameter (reportAssignmentType) +callables_subtyping.py:86:22 - error: Type "IntArgs3" is not assignable to declared type "FloatArgs3" +  "IntArgs3" is incompatible with protocol "FloatArgs3" +    "__call__" is an incompatible type +      Type "(*args: int) -> None" is not assignable to type "(*args: float) -> None" +        Parameter 1: type "*tuple[float, ...]" is incompatible with type "*tuple[int, ...]" +          "*tuple[float, ...]" is not assignable to "*tuple[int, ...]" (reportAssignmentType) +callables_subtyping.py:116:20 - error: Type "IntArgs4" is not assignable to declared type "PosOnly4" +  "IntArgs4" is incompatible with protocol "PosOnly4" +    "__call__" is an incompatible type +      Type "(*args: int) -> None" is not assignable to type "(a: int, b: str, /) -> None" +        Parameter 2: type "str" is incompatible with type "int" +          "str" is not assignable to "int" (reportAssignmentType) +callables_subtyping.py:119:23 - error: Type "StrArgs4" is not assignable to declared type "IntStrArgs4" +  "StrArgs4" is incompatible with protocol "IntStrArgs4" +    "__call__" is an incompatible type +      Type "(a: int, /, *args: str) -> None" is not assignable to type "(*args: int | str) -> None" +        Parameter 1: type "int | str" is incompatible with type "int" +          Type "int | str" is not assignable to type "int" +        Parameter 2: type "int | str" is incompatible with type "str" +          Type "int | str" is not assignable to type "str" +        Parameter 1: type "*tuple[int | str, ...]" is incompatible with type "*tuple[str, ...]" + ... (reportAssignmentType) +callables_subtyping.py:120:23 - error: Type "IntArgs4" is not assignable to declared type "IntStrArgs4" +  "IntArgs4" is incompatible with protocol "IntStrArgs4" +    "__call__" is an incompatible type +      Type "(*args: int) -> None" is not assignable to type "(*args: int | str) -> None" +        Parameter 1: type "*tuple[int | str, ...]" is incompatible with type "*tuple[int, ...]" +          "*tuple[int | str, ...]" is not assignable to "*tuple[int, ...]" (reportAssignmentType) +callables_subtyping.py:122:20 - error: Type "IntArgs4" is not assignable to declared type "StrArgs4" +  "IntArgs4" is incompatible with protocol "StrArgs4" +    "__call__" is an incompatible type +      Type "(*args: int) -> None" is not assignable to type "(a: int, /, *args: str) -> None" +        Parameter 2: type "str" is incompatible with type "int" +          "str" is not assignable to "int" +        Parameter 3: type "*tuple[str, ...]" is incompatible with type "*tuple[int, ...]" +          "*tuple[str, ...]" is not assignable to "*tuple[int, ...]" (reportAssignmentType) +callables_subtyping.py:124:20 - error: Type "StrArgs4" is not assignable to declared type "IntArgs4" +  "StrArgs4" is incompatible with protocol "IntArgs4" +    "__call__" is an incompatible type +      Type "(a: int, /, *args: str) -> None" is not assignable to type "(*args: int) -> None" +        Parameter 2: type "int" is incompatible with type "str" +          "int" is not assignable to "str" +        Parameter 1: type "*tuple[int, ...]" is incompatible with type "*tuple[str, ...]" +          "*tuple[int, ...]" is not assignable to "*tuple[str, ...]" (reportAssignmentType) +callables_subtyping.py:125:22 - error: Type "IntStrArgs4" is not assignable to declared type "Standard4" +  "IntStrArgs4" is incompatible with protocol "Standard4" +    "__call__" is an incompatible type +      Type "(*args: int | str) -> None" is not assignable to type "(a: int, b: str) -> None" +        Missing keyword parameter "a" +        Missing keyword parameter "b" (reportAssignmentType) +callables_subtyping.py:126:22 - error: Type "StrArgs4" is not assignable to declared type "Standard4" +  "StrArgs4" is incompatible with protocol "Standard4" +    "__call__" is an incompatible type +      Type "(a: int, /, *args: str) -> None" is not assignable to type "(a: int, b: str) -> None" +        Missing keyword parameter "a" +        Missing keyword parameter "b" +          Position-only parameter mismatch; parameter "a" is not position-only +          Position-only parameter mismatch; expected 1 but received 0 (reportAssignmentType) +callables_subtyping.py:151:22 - error: Type "NoKwargs5" is not assignable to declared type "IntKwargs5" +  "NoKwargs5" is incompatible with protocol "IntKwargs5" +    "__call__" is an incompatible type +      Type "() -> None" is not assignable to type "(**kwargs: int) -> None" +        Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) +callables_subtyping.py:154:24 - error: Type "NoKwargs5" is not assignable to declared type "FloatKwargs5" +  "NoKwargs5" is incompatible with protocol "FloatKwargs5" +    "__call__" is an incompatible type +      Type "() -> None" is not assignable to type "(**kwargs: float) -> None" +        Parameter "**kwargs" has no corresponding parameter (reportAssignmentType) +callables_subtyping.py:155:24 - error: Type "IntKwargs5" is not assignable to declared type "FloatKwargs5" +  "IntKwargs5" is incompatible with protocol "FloatKwargs5" +    "__call__" is an incompatible type +      Type "(**kwargs: int) -> None" is not assignable to type "(**kwargs: float) -> None" +        Parameter 1: type "float" is incompatible with type "int" +          "float" is not assignable to "int" (reportAssignmentType) +callables_subtyping.py:187:19 - error: Type "IntKwargs6" is not assignable to declared type "KwOnly6" +  "IntKwargs6" is incompatible with protocol "KwOnly6" +    "__call__" is an incompatible type +      Type "(**kwargs: int) -> None" is not assignable to type "(*, a: int, b: str) -> None" +        Parameter 3: type "str" is incompatible with type "int" +          "str" is not assignable to "int" (reportAssignmentType) +callables_subtyping.py:190:25 - error: Type "StrKwargs6" is not assignable to declared type "IntStrKwargs6" +  "StrKwargs6" is incompatible with protocol "IntStrKwargs6" +    "__call__" is an incompatible type +      Type "(*, a: int, **kwargs: str) -> None" is not assignable to type "(**kwargs: int | str) -> None" +        Parameter 1: type "int | str" is incompatible with type "int" +          Type "int | str" is not assignable to type "int" +        Parameter 1: type "int | str" is incompatible with type "str" +          Type "int | str" is not assignable to type "str" (reportAssignmentType) +callables_subtyping.py:191:25 - error: Type "IntKwargs6" is not assignable to declared type "IntStrKwargs6" +  "IntKwargs6" is incompatible with protocol "IntStrKwargs6" +    "__call__" is an incompatible type +      Type "(**kwargs: int) -> None" is not assignable to type "(**kwargs: int | str) -> None" +        Parameter 1: type "int | str" is incompatible with type "int" +          Type "int | str" is not assignable to type "int" (reportAssignmentType) +callables_subtyping.py:193:22 - error: Type "IntKwargs6" is not assignable to declared type "StrKwargs6" +  "IntKwargs6" is incompatible with protocol "StrKwargs6" +    "__call__" is an incompatible type +      Type "(**kwargs: int) -> None" is not assignable to type "(*, a: int, **kwargs: str) -> None" +        Parameter 3: type "str" is incompatible with type "int" +          "str" is not assignable to "int" (reportAssignmentType) +callables_subtyping.py:195:22 - error: Type "StrKwargs6" is not assignable to declared type "IntKwargs6" +  "StrKwargs6" is incompatible with protocol "IntKwargs6" +    "__call__" is an incompatible type +      Type "(*, a: int, **kwargs: str) -> None" is not assignable to type "(**kwargs: int) -> None" +        Parameter 1: type "int" is incompatible with type "str" +          "int" is not assignable to "str" (reportAssignmentType) +callables_subtyping.py:196:22 - error: Type "IntStrKwargs6" is not assignable to declared type "Standard6" +  "IntStrKwargs6" is incompatible with protocol "Standard6" +    "__call__" is an incompatible type +      Type "(**kwargs: int | str) -> None" is not assignable to type "(a: int, b: str) -> None" +        Function accepts too many positional parameters; expected 0 but received 2 (reportAssignmentType) +callables_subtyping.py:197:22 - error: Type "StrKwargs6" is not assignable to declared type "Standard6" +  "StrKwargs6" is incompatible with protocol "Standard6" +    "__call__" is an incompatible type +      Type "(*, a: int, **kwargs: str) -> None" is not assignable to type "(a: int, b: str) -> None" +        Function accepts too many positional parameters; expected 0 but received 2 +          Extra parameter "a" (reportAssignmentType) +callables_subtyping.py:236:23 - error: Type "NoDefaultArg8" is not assignable to declared type "DefaultArg8" +  "NoDefaultArg8" is incompatible with protocol "DefaultArg8" +    "__call__" is an incompatible type +      Type "(x: int) -> None" is not assignable to type "(x: int = 0) -> None" +        Parameter "x" is missing default argument (reportAssignmentType) +callables_subtyping.py:237:23 - error: Type "NoX8" is not assignable to declared type "DefaultArg8" +  "NoX8" is incompatible with protocol "DefaultArg8" +    "__call__" is an incompatible type +      Type "() -> None" is not assignable to type "(x: int = 0) -> None" +        Function accepts too many positional parameters; expected 0 but received 1 (reportAssignmentType) +callables_subtyping.py:240:25 - error: Type "NoX8" is not assignable to declared type "NoDefaultArg8" +  "NoX8" is incompatible with protocol "NoDefaultArg8" +    "__call__" is an incompatible type +      Type "() -> None" is not assignable to type "(x: int) -> None" +        Function accepts too many positional parameters; expected 0 but received 1 (reportAssignmentType) +callables_subtyping.py:243:16 - error: Type "NoDefaultArg8" is not assignable to declared type "NoX8" +  "NoDefaultArg8" is incompatible with protocol "NoX8" +    "__call__" is an incompatible type +      Type "(x: int) -> None" is not assignable to type "() -> None" +        Extra parameter "x" (reportAssignmentType) +callables_subtyping.py:273:21 - error: Type "Overloaded9" is not assignable to declared type "FloatArg9" +  "Overloaded9" is incompatible with protocol "FloatArg9" +    "__call__" is an incompatible type +      No overloaded function matches type "(x: float) -> float" (reportAssignmentType) +callables_subtyping.py:297:24 - error: Type "StrArg10" is not assignable to declared type "Overloaded10" +  "StrArg10" is incompatible with protocol "Overloaded10" +    "__call__" is an incompatible type +      One or more overloads of "__call__" is not assignable +        Type "(x: str) -> complex" is not assignable to type "(x: int, y: str) -> float" +          Function accepts too many positional parameters; expected 1 but received 2 (reportAssignmentType) +""" +conformance_automated = "Pass" diff --git a/conformance/results/pyright/classes_classvar.toml b/conformance/results/pyright/classes_classvar.toml new file mode 100644 index 000000000..1b03b11a3 --- /dev/null +++ b/conformance/results/pyright/classes_classvar.toml @@ -0,0 +1,27 @@ +conformant = "Pass" +output = """ +classes_classvar.py:38:25 - error: Expected only one type argument after "ClassVar" (reportInvalidTypeForm) +classes_classvar.py:39:14 - error: Expected class but received "Literal[3]" (reportGeneralTypeIssues) +classes_classvar.py:40:14 - error: "var" is not defined (reportUndefinedVariable) +classes_classvar.py:45:20 - error: "ClassVar" type cannot include type variables (reportGeneralTypeIssues) +classes_classvar.py:46:20 - error: "ClassVar" type cannot include type variables (reportGeneralTypeIssues) +classes_classvar.py:47:20 - error: "ClassVar" type cannot include type variables (reportGeneralTypeIssues) +classes_classvar.py:52:33 - error: Type "dict[Any, Any]" is not assignable to declared type "list[str]" (reportAssignmentType) +classes_classvar.py:54:17 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +classes_classvar.py:55:17 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +classes_classvar.py:69:26 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +classes_classvar.py:70:12 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +classes_classvar.py:71:18 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +classes_classvar.py:73:26 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +classes_classvar.py:77:8 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +classes_classvar.py:78:20 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +classes_classvar.py:111:14 - error: Cannot assign to attribute "stats" for class "Starship" +  Attribute "stats" cannot be assigned through a class instance because it is a ClassVar (reportAttributeAccessIssue) +classes_classvar.py:140:13 - error: Type "ProtoAImpl" is not assignable to declared type "ProtoA" +  "ProtoAImpl" is incompatible with protocol "ProtoA" +    "x" is defined as a ClassVar in protocol +    "y" is defined as a ClassVar in protocol (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/classes_override.toml b/conformance/results/pyright/classes_override.toml new file mode 100644 index 000000000..d27bd43de --- /dev/null +++ b/conformance/results/pyright/classes_override.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +classes_override.py:53:9 - error: Method "method3" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +classes_override.py:65:9 - error: Method "method4" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +classes_override.py:79:9 - error: Method "static_method1" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +classes_override.py:84:9 - error: Method "class_method1" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +classes_override.py:89:9 - error: Method "property1" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/constructors_call_init.toml b/conformance/results/pyright/constructors_call_init.toml new file mode 100644 index 000000000..4d6672588 --- /dev/null +++ b/conformance/results/pyright/constructors_call_init.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_init.py:21:13 - error: Argument of type "float" cannot be assigned to parameter "x" of type "int" in function "__init__" +  "float" is not assignable to "int" (reportArgumentType) +constructors_call_init.py:42:8 - error: Argument of type "Class2[Unknown]" cannot be assigned to parameter "x" of type "Class3 | None" in function "__init__" +  Type "Class2[Unknown]" is not assignable to type "Class3 | None" +    "Class2[Unknown]" is not assignable to "Class3" +    "Class2[Unknown]" is not assignable to "None" (reportArgumentType) +constructors_call_init.py:56:1 - error: +  Could not bind method "__init__" because "Class4[str]" is not assignable to parameter "self" +    "Class4[str]" is not assignable to "Class4[int]" +      Type parameter "T@Class4" is invariant, but "str" is not the same as "int" (reportGeneralTypeIssues) +constructors_call_init.py:107:24 - warning: Type annotation for "self" parameter of "__init__" method cannot contain class-scoped type variables (reportInvalidTypeVarUse) +constructors_call_init.py:130:9 - error: Expected 0 positional arguments (reportCallIssue) +""" diff --git a/conformance/results/pyright/constructors_call_metaclass.toml b/conformance/results/pyright/constructors_call_metaclass.toml new file mode 100644 index 000000000..96498014e --- /dev/null +++ b/conformance/results/pyright/constructors_call_metaclass.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_metaclass.py:54:1 - error: Argument missing for parameter "x" (reportCallIssue) +constructors_call_metaclass.py:68:1 - error: Argument missing for parameter "x" (reportCallIssue) +""" +conformance_automated = "Pass" diff --git a/conformance/results/pyright/constructors_call_new.toml b/conformance/results/pyright/constructors_call_new.toml new file mode 100644 index 000000000..e2387d4fc --- /dev/null +++ b/conformance/results/pyright/constructors_call_new.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_new.py:21:13 - error: Argument of type "float" cannot be assigned to parameter "x" of type "int" in function "__new__" +  "float" is not assignable to "int" (reportArgumentType) +constructors_call_new.py:148:1 - error: +  Could not bind method "__new__" because "type[Class11[str]]" is not assignable to parameter "cls" +    "type[Class11[str]]" is not assignable to "type[Class11[int]]" +    Type "type[Class11[str]]" is not assignable to type "type[Class11[int]]" +      Type parameter "T@Class11" is invariant, but "str" is not the same as "int" (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" diff --git a/conformance/results/pyright/constructors_call_type.toml b/conformance/results/pyright/constructors_call_type.toml new file mode 100644 index 000000000..da804d72e --- /dev/null +++ b/conformance/results/pyright/constructors_call_type.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_type.py:30:5 - error: Arguments missing for parameters "x", "y" (reportCallIssue) +constructors_call_type.py:40:5 - error: Arguments missing for parameters "x", "y" (reportCallIssue) +constructors_call_type.py:50:5 - error: Arguments missing for parameters "x", "y" (reportCallIssue) +constructors_call_type.py:59:9 - error: Expected 0 positional arguments (reportCallIssue) +constructors_call_type.py:64:9 - error: Expected 0 positional arguments (reportCallIssue) +constructors_call_type.py:72:5 - error: Arguments missing for parameters "x", "y" (reportCallIssue) +constructors_call_type.py:81:5 - error: Argument missing for parameter "y" (reportCallIssue) +constructors_call_type.py:82:12 - error: Argument of type "Literal[2]" cannot be assigned to parameter "y" of type "str" in function "__new__" +  "Literal[2]" is not assignable to "str" (reportArgumentType) +""" +conformance_automated = "Pass" diff --git a/conformance/results/pyright/constructors_callable.toml b/conformance/results/pyright/constructors_callable.toml new file mode 100644 index 000000000..4a9c41d59 --- /dev/null +++ b/conformance/results/pyright/constructors_callable.toml @@ -0,0 +1,33 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +constructors_callable.py:36:13 - information: Type of "r1" is "(x: int) -> Class1" +constructors_callable.py:38:1 - error: Argument missing for parameter "x" (reportCallIssue) +constructors_callable.py:39:1 - error: Argument missing for parameter "x" (reportCallIssue) +constructors_callable.py:39:4 - error: No parameter named "y" (reportCallIssue) +constructors_callable.py:49:13 - information: Type of "r2" is "() -> Class2" +constructors_callable.py:51:4 - error: Expected 0 positional arguments (reportCallIssue) +constructors_callable.py:64:13 - information: Type of "r3" is "(x: int) -> Class3" +constructors_callable.py:66:1 - error: Argument missing for parameter "x" (reportCallIssue) +constructors_callable.py:67:1 - error: Argument missing for parameter "x" (reportCallIssue) +constructors_callable.py:67:4 - error: No parameter named "y" (reportCallIssue) +constructors_callable.py:68:7 - error: Expected 1 positional argument (reportCallIssue) +constructors_callable.py:79:13 - information: Type of "r4" is "(x: int) -> int" +constructors_callable.py:81:1 - error: Argument missing for parameter "x" (reportCallIssue) +constructors_callable.py:82:1 - error: Argument missing for parameter "x" (reportCallIssue) +constructors_callable.py:82:4 - error: No parameter named "y" (reportCallIssue) +constructors_callable.py:99:13 - information: Type of "r5" is "(...) -> NoReturn" +constructors_callable.py:127:13 - information: Type of "r6" is "() -> Class6Proxy" +constructors_callable.py:129:4 - error: Expected 0 positional arguments (reportCallIssue) +constructors_callable.py:144:13 - information: Type of "r6_any" is "() -> Any" +constructors_callable.py:146:8 - error: Expected 0 positional arguments (reportCallIssue) +constructors_callable.py:164:5 - information: Type of "r7" is "Overload[(x: int) -> Class7[int], (x: str) -> Class7[str]]" +constructors_callable.py:184:13 - information: Type of "r8" is "(x: list[T@Class8], y: list[T@Class8]) -> Class8[T@Class8]" +constructors_callable.py:186:10 - error: Argument of type "list[str]" cannot be assigned to parameter "y" of type "list[T@Class8]" +  "Literal['']" is not assignable to "int" (reportArgumentType) +constructors_callable.py:195:13 - information: Type of "r9" is "(x: list[T@__init__], y: list[T@__init__]) -> Class9" +constructors_callable.py:197:10 - error: Argument of type "list[str]" cannot be assigned to parameter "y" of type "list[T@__init__]" +  "Literal['']" is not assignable to "int" (reportArgumentType) +""" diff --git a/conformance/results/pyright/constructors_consistency.toml b/conformance/results/pyright/constructors_consistency.toml new file mode 100644 index 000000000..b335fa1ce --- /dev/null +++ b/conformance/results/pyright/constructors_consistency.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +constructors_consistency.py:25:9 - error: Mismatch between signature of __new__ and __init__ in class "Class1" +  Signature of __init__ is "(x: str) -> None" +  Signature of __new__ is "() -> Class1" (reportInconsistentConstructor) +""" +conformance_automated = "Pass" diff --git a/conformance/results/pyright/dataclasses_descriptors.toml b/conformance/results/pyright/dataclasses_descriptors.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/dataclasses_descriptors.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_final.toml b/conformance/results/pyright/dataclasses_final.toml new file mode 100644 index 000000000..9b85ad5ab --- /dev/null +++ b/conformance/results/pyright/dataclasses_final.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_final.py:27:3 - error: Cannot assign to attribute "final_classvar" for class "type[D]" +  "final_classvar" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +dataclasses_final.py:35:3 - error: Cannot assign to attribute "final_no_default" for class "D" +  "final_no_default" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +dataclasses_final.py:36:3 - error: Cannot assign to attribute "final_with_default" for class "D" +  "final_with_default" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +dataclasses_final.py:37:3 - error: Cannot assign to attribute "final_no_default" for class "type[D]" +  "final_no_default" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +dataclasses_final.py:38:3 - error: Cannot assign to attribute "final_with_default" for class "type[D]" +  "final_with_default" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +""" diff --git a/conformance/results/pyright/dataclasses_frozen.toml b/conformance/results/pyright/dataclasses_frozen.toml new file mode 100644 index 000000000..87a046e23 --- /dev/null +++ b/conformance/results/pyright/dataclasses_frozen.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +output = """ +dataclasses_frozen.py:16:5 - error: Cannot assign to attribute "a" for class "DC1" +  Attribute "a" is read-only (reportAttributeAccessIssue) +dataclasses_frozen.py:17:5 - error: Cannot assign to attribute "b" for class "DC1" +  Attribute "b" is read-only (reportAttributeAccessIssue) +dataclasses_frozen.py:22:1 - error: A non-frozen class cannot inherit from a class that is frozen (reportGeneralTypeIssues) +dataclasses_frozen.py:32:12 - error: A frozen class cannot inherit from a class that is not frozen (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_hash.toml b/conformance/results/pyright/dataclasses_hash.toml new file mode 100644 index 000000000..7e05cf570 --- /dev/null +++ b/conformance/results/pyright/dataclasses_hash.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +output = """ +dataclasses_hash.py:17:1 - error: Object of type "None" cannot be called (reportOptionalCall) +dataclasses_hash.py:18:16 - error: Type "DC1" is not assignable to declared type "Hashable" +  "DC1" is incompatible with protocol "Hashable" +    "__hash__" is an incompatible type +      Type "None" is not assignable to type "() -> int" (reportAssignmentType) +dataclasses_hash.py:39:1 - error: Object of type "None" cannot be called (reportOptionalCall) +dataclasses_hash.py:40:16 - error: Type "DC3" is not assignable to declared type "Hashable" +  "DC3" is incompatible with protocol "Hashable" +    "__hash__" is an incompatible type +      Type "None" is not assignable to type "() -> int" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_inheritance.toml b/conformance/results/pyright/dataclasses_inheritance.toml new file mode 100644 index 000000000..8576bb702 --- /dev/null +++ b/conformance/results/pyright/dataclasses_inheritance.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +dataclasses_inheritance.py:62:5 - error: Class variable "x" overrides instance variable of same name in class "DC6" (reportIncompatibleVariableOverride) +dataclasses_inheritance.py:66:5 - error: Instance variable "y" overrides class variable of same name in class "DC6" (reportIncompatibleVariableOverride) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_kwonly.toml b/conformance/results/pyright/dataclasses_kwonly.toml new file mode 100644 index 000000000..3afb280aa --- /dev/null +++ b/conformance/results/pyright/dataclasses_kwonly.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +output = """ +dataclasses_kwonly.py:23:11 - error: Expected 1 positional argument (reportCallIssue) +dataclasses_kwonly.py:38:11 - error: Expected 1 positional argument (reportCallIssue) +dataclasses_kwonly.py:53:11 - error: Expected 1 positional argument (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_match_args.toml b/conformance/results/pyright/dataclasses_match_args.toml new file mode 100644 index 000000000..570ca46c9 --- /dev/null +++ b/conformance/results/pyright/dataclasses_match_args.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_match_args.py:42:5 - error: Cannot access attribute "__match_args__" for class "type[DC4]" +  Attribute "__match_args__" is unknown (reportAttributeAccessIssue) +""" diff --git a/conformance/results/pyright/dataclasses_order.toml b/conformance/results/pyright/dataclasses_order.toml new file mode 100644 index 000000000..b6c83a70f --- /dev/null +++ b/conformance/results/pyright/dataclasses_order.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +output = """ +dataclasses_order.py:50:4 - error: Operator "<" not supported for types "DC1" and "DC2" (reportOperatorIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_postinit.toml b/conformance/results/pyright/dataclasses_postinit.toml new file mode 100644 index 000000000..9ba5550ab --- /dev/null +++ b/conformance/results/pyright/dataclasses_postinit.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +output = """ +dataclasses_postinit.py:19:40 - error: Dataclass __post_init__ method parameter type mismatch for field "y" +  "str" is not assignable to "int" (reportGeneralTypeIssues) +dataclasses_postinit.py:28:11 - error: Cannot access attribute "x" for class "DC1" +  "x" is an init-only field (reportAttributeAccessIssue) +dataclasses_postinit.py:29:11 - error: Cannot access attribute "y" for class "DC1" +  "y" is an init-only field (reportAttributeAccessIssue) +dataclasses_postinit.py:36:9 - error: Dataclass __post_init__ incorrect parameter count; number of InitVar fields is 2 (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_slots.toml b/conformance/results/pyright/dataclasses_slots.toml new file mode 100644 index 000000000..021a84417 --- /dev/null +++ b/conformance/results/pyright/dataclasses_slots.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +output = """ +dataclasses_slots.py:10:12 - error: __slots__ is already defined in class (reportGeneralTypeIssues) +dataclasses_slots.py:25:14 - error: "y" is not specified in __slots__ (reportGeneralTypeIssues) +dataclasses_slots.py:38:14 - error: "y" is not specified in __slots__ (reportGeneralTypeIssues) +dataclasses_slots.py:66:5 - error: Cannot access attribute "__slots__" for class "type[DC6]" +  Attribute "__slots__" is unknown (reportAttributeAccessIssue) +dataclasses_slots.py:69:8 - error: Cannot access attribute "__slots__" for class "DC6" +  Attribute "__slots__" is unknown (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_transform_class.toml b/conformance/results/pyright/dataclasses_transform_class.toml new file mode 100644 index 000000000..25b8ccc83 --- /dev/null +++ b/conformance/results/pyright/dataclasses_transform_class.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +output = """ +dataclasses_transform_class.py:51:7 - error: A non-frozen class cannot inherit from a class that is frozen (reportGeneralTypeIssues) +dataclasses_transform_class.py:63:6 - error: Cannot assign to attribute "id" for class "Customer1" +  Attribute "id" is read-only (reportAttributeAccessIssue) +dataclasses_transform_class.py:66:18 - error: Expected 0 positional arguments (reportCallIssue) +dataclasses_transform_class.py:72:6 - error: Operator "<" not supported for types "Customer1" and "Customer1" (reportOperatorIssue) +dataclasses_transform_class.py:82:18 - error: Expected 0 positional arguments (reportCallIssue) +dataclasses_transform_class.py:122:6 - error: Cannot assign to attribute "id" for class "Customer3" +  Attribute "id" is read-only (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_transform_converter.toml b/conformance/results/pyright/dataclasses_transform_converter.toml new file mode 100644 index 000000000..ec7db74cb --- /dev/null +++ b/conformance/results/pyright/dataclasses_transform_converter.toml @@ -0,0 +1,44 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_converter.py:48:31 - error: Argument of type "() -> int" is not a valid converter for field "field1" of type "int" +  Type "() -> int" is not assignable to type "(__converterInput) -> int" +    Function accepts too many positional parameters; expected 0 but received 1 (reportGeneralTypeIssues) +dataclasses_transform_converter.py:48:41 - error: Argument of type "() -> int" cannot be assigned to parameter "converter" of type "(S@model_field) -> T@model_field" in function "model_field" +  Type "() -> int" is not assignable to type "(S@model_field) -> int" +    Function accepts too many positional parameters; expected 0 but received 1 (reportArgumentType) +dataclasses_transform_converter.py:49:31 - error: Argument of type "(*, x: int) -> int" is not a valid converter for field "field2" of type "int" +  Type "(*, x: int) -> int" is not assignable to type "(__converterInput) -> int" +    Function accepts too many positional parameters; expected 0 but received 1 +      Extra parameter "x" (reportGeneralTypeIssues) +dataclasses_transform_converter.py:49:41 - error: Argument of type "(*, x: int) -> int" cannot be assigned to parameter "converter" of type "(S@model_field) -> T@model_field" in function "model_field" +  Type "(*, x: int) -> int" is not assignable to type "(S@model_field) -> int" +    Function accepts too many positional parameters; expected 0 but received 1 +      Extra parameter "x" (reportArgumentType) +dataclasses_transform_converter.py:107:5 - error: Argument of type "Literal[1]" cannot be assigned to parameter "field0" of type "str" in function "__init__" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +dataclasses_transform_converter.py:108:23 - error: Argument of type "Literal[1]" cannot be assigned to parameter "field3" of type "str | bytes" in function "__init__" +  Type "Literal[1]" is not assignable to type "str | bytes" +    "Literal[1]" is not assignable to "str" +    "Literal[1]" is not assignable to "bytes" (reportArgumentType) +dataclasses_transform_converter.py:109:29 - error: Argument of type "complex" cannot be assigned to parameter "field4" of type "str | list[str]" in function "__init__" +  Type "complex" is not assignable to type "str | list[str]" +    "complex" is not assignable to "str" +    "complex" is not assignable to "list[str]" (reportArgumentType) +dataclasses_transform_converter.py:118:14 - error: Cannot assign to attribute "field0" for class "DC2" +  "Literal[1]" is not assignable to "str" (reportAttributeAccessIssue) +dataclasses_transform_converter.py:119:14 - error: Cannot assign to attribute "field3" for class "DC2" +  Type "Literal[1]" is not assignable to type "str | bytes" +    "Literal[1]" is not assignable to "str" +    "Literal[1]" is not assignable to "bytes" (reportAttributeAccessIssue) +dataclasses_transform_converter.py:130:67 - error: Argument of type "Literal[1]" cannot be assigned to parameter "default" of type "S@model_field | None" in function "model_field" +  Type "Literal[1]" is not assignable to type "str | None" +    "Literal[1]" is not assignable to "str" +    "Literal[1]" is not assignable to "None" (reportArgumentType) +dataclasses_transform_converter.py:133:75 - error: Argument of type "type[int]" cannot be assigned to parameter "default_factory" of type "(() -> S@model_field) | None" in function "model_field" +  Type "type[int]" is not assignable to type "(() -> str) | None" +    No overloaded function matches type "() -> str" +    Type is not assignable to "None" (reportArgumentType) +""" diff --git a/conformance/results/pyright/dataclasses_transform_field.toml b/conformance/results/pyright/dataclasses_transform_field.toml new file mode 100644 index 000000000..777701731 --- /dev/null +++ b/conformance/results/pyright/dataclasses_transform_field.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +dataclasses_transform_field.py:64:16 - error: No parameter named "id" (reportCallIssue) +dataclasses_transform_field.py:75:16 - error: Expected 0 positional arguments (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_transform_func.toml b/conformance/results/pyright/dataclasses_transform_func.toml new file mode 100644 index 000000000..86e12bfcf --- /dev/null +++ b/conformance/results/pyright/dataclasses_transform_func.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +output = """ +dataclasses_transform_func.py:56:13 - error: Cannot assign to attribute "name" for class "Customer1" +  "Literal[3]" is not assignable to "str" (reportAttributeAccessIssue) +dataclasses_transform_func.py:60:6 - error: Operator "<" not supported for types "Customer1" and "Customer1" (reportOperatorIssue) +dataclasses_transform_func.py:64:36 - error: No parameter named "salary" (reportCallIssue) +dataclasses_transform_func.py:70:18 - error: Expected 0 positional arguments (reportCallIssue) +dataclasses_transform_func.py:88:1 - error: A non-frozen class cannot inherit from a class that is frozen (reportGeneralTypeIssues) +dataclasses_transform_func.py:96:6 - error: Cannot assign to attribute "id" for class "Customer3" +  Attribute "id" is read-only (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_transform_meta.toml b/conformance/results/pyright/dataclasses_transform_meta.toml new file mode 100644 index 000000000..392f95a70 --- /dev/null +++ b/conformance/results/pyright/dataclasses_transform_meta.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +output = """ +dataclasses_transform_meta.py:51:36 - error: A non-frozen class cannot inherit from a class that is frozen (reportGeneralTypeIssues) +dataclasses_transform_meta.py:63:6 - error: Cannot assign to attribute "id" for class "Customer1" +  Attribute "id" is read-only (reportAttributeAccessIssue) +dataclasses_transform_meta.py:66:18 - error: Expected 0 positional arguments (reportCallIssue) +dataclasses_transform_meta.py:73:6 - error: Operator "<" not supported for types "Customer1" and "Customer1" (reportOperatorIssue) +dataclasses_transform_meta.py:83:18 - error: Expected 0 positional arguments (reportCallIssue) +dataclasses_transform_meta.py:103:6 - error: Cannot assign to attribute "id" for class "Customer3" +  Attribute "id" is read-only (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/dataclasses_usage.toml b/conformance/results/pyright/dataclasses_usage.toml new file mode 100644 index 000000000..62d5a0cb7 --- /dev/null +++ b/conformance/results/pyright/dataclasses_usage.toml @@ -0,0 +1,21 @@ +conformant = "Pass" +output = """ +dataclasses_usage.py:51:6 - error: Argument missing for parameter "unit_price" (reportCallIssue) +dataclasses_usage.py:52:28 - error: Argument of type "Literal['price']" cannot be assigned to parameter "unit_price" of type "float" in function "__init__" +  "Literal['price']" is not assignable to "float" (reportArgumentType) +dataclasses_usage.py:53:36 - error: Expected 3 positional arguments (reportCallIssue) +dataclasses_usage.py:62:5 - error: Fields without default values cannot appear after fields with default values (reportGeneralTypeIssues) +dataclasses_usage.py:68:5 - error: Fields without default values cannot appear after fields with default values (reportGeneralTypeIssues) +dataclasses_usage.py:74:5 - error: Fields without default values cannot appear after fields with default values (reportGeneralTypeIssues) +dataclasses_usage.py:84:13 - error: Expected 1 positional argument (reportCallIssue) +dataclasses_usage.py:89:14 - error: Type "str" is not assignable to declared type "int" +  "str" is not assignable to "int" (reportAssignmentType) +dataclasses_usage.py:128:8 - error: Expected 1 positional argument (reportCallIssue) +dataclasses_usage.py:131:1 - error: Argument missing for parameter "y" (reportCallIssue) +dataclasses_usage.py:180:6 - error: Expected 0 positional arguments (reportCallIssue) +dataclasses_usage.py:229:9 - error: Dataclass field without type annotation will cause runtime exception (reportGeneralTypeIssues) +dataclasses_usage.py:246:12 - error: Expected 2 positional arguments (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_assert_type.toml b/conformance/results/pyright/directives_assert_type.toml new file mode 100644 index 000000000..cb868983d --- /dev/null +++ b/conformance/results/pyright/directives_assert_type.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +output = """ +directives_assert_type.py:27:17 - error: "assert_type" mismatch: expected "int" but received "int | str" (reportAssertTypeFailure) +directives_assert_type.py:28:17 - error: "assert_type" mismatch: expected "Any" but received "int | str" (reportAssertTypeFailure) +directives_assert_type.py:29:17 - error: "assert_type" mismatch: expected "int" but received "Any" (reportAssertTypeFailure) +directives_assert_type.py:30:17 - error: "assert_type" mismatch: expected "int" but received "Literal[4]" (reportAssertTypeFailure) +directives_assert_type.py:32:5 - error: "assert_type" expects two positional arguments (reportCallIssue) +directives_assert_type.py:33:17 - error: "assert_type" mismatch: expected "int" but received "Literal['']" (reportAssertTypeFailure) +directives_assert_type.py:34:5 - error: "assert_type" expects two positional arguments (reportCallIssue) +directives_assert_type.py:41:17 - error: "assert_type" mismatch: expected "str | Literal['spam']" but received "str" (reportAssertTypeFailure) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_cast.toml b/conformance/results/pyright/directives_cast.toml new file mode 100644 index 000000000..d7dc7e221 --- /dev/null +++ b/conformance/results/pyright/directives_cast.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +directives_cast.py:15:8 - error: No overloads for "cast" match the provided arguments +  Argument types: () (reportCallIssue) +directives_cast.py:16:13 - error: Expected class but received "Literal[1]" (reportGeneralTypeIssues) +directives_cast.py:17:8 - error: No overloads for "cast" match the provided arguments +  Argument types: (type[int], Literal[''], Literal['']) (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_deprecated.toml b/conformance/results/pyright/directives_deprecated.toml new file mode 100644 index 000000000..21cb8172a --- /dev/null +++ b/conformance/results/pyright/directives_deprecated.toml @@ -0,0 +1,32 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_deprecated.py:18:44 - error: The class "Ham" is deprecated +  Use Spam instead (reportDeprecated) +directives_deprecated.py:24:9 - error: The function "norwegian_blue" is deprecated +  It is pining for the fjords (reportDeprecated) +directives_deprecated.py:25:13 - error: The function "norwegian_blue" is deprecated +  It is pining for the fjords (reportDeprecated) +directives_deprecated.py:30:9 - error: The function "foo" is deprecated +  Only str will be allowed (reportDeprecated) +directives_deprecated.py:34:7 - error: The class "Ham" is deprecated +  Use Spam instead (reportDeprecated) +directives_deprecated.py:41:5 - error: The method "__add__" in class "Spam" is deprecated +  There is enough spam in the world (reportDeprecated) +directives_deprecated.py:42:1 - error: The method "__add__" in class "Spam" is deprecated +  There is enough spam in the world (reportDeprecated) +directives_deprecated.py:44:6 - error: The getter for property "greasy" is deprecated +  All spam will be equally greasy (reportDeprecated) +directives_deprecated.py:47:6 - error: The setter for property "shape" is deprecated +  Shapes are becoming immutable (reportDeprecated) +directives_deprecated.py:48:6 - error: The setter for property "shape" is deprecated +  Shapes are becoming immutable (reportDeprecated) +directives_deprecated.py:58:1 - error: The function "invocable" is deprecated +  Deprecated (reportDeprecated) +directives_deprecated.py:69:1 - error: The function "lorem" is deprecated +  Deprecated (reportDeprecated) +directives_deprecated.py:98:7 - error: The method "foo" in class "SupportsFoo1" is deprecated +  Deprecated (reportDeprecated) +""" diff --git a/conformance/results/pyright/directives_disjoint_base.toml b/conformance/results/pyright/directives_disjoint_base.toml new file mode 100644 index 000000000..023812996 --- /dev/null +++ b/conformance/results/pyright/directives_disjoint_base.toml @@ -0,0 +1,21 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +notes = """ +Does not support PEP 800 disjoint-base semantics. +""" +errors_diff = """ +Line 69: Expected 1 errors +Line 73: Expected 1 errors +Line 77: Expected 1 errors +Line 81: Expected 1 errors +Line 105: Expected 1 errors +Line 118: Expected 1 errors +Line 123: Expected 1 errors +""" +output = """ +directives_disjoint_base.py:113:2 - error: Argument of type "() -> None" cannot be assigned to parameter "cls" of type "_TC@disjoint_base" in function "disjoint_base" +  Type "() -> None" is not assignable to type "type" +    "FunctionType" is not assignable to "type" (reportArgumentType) +directives_disjoint_base.py:135:22 - error: Argument of type "" cannot be assigned to parameter "arg" of type "Never" in function "assert_never" +  Type "" is not assignable to type "Never" (reportArgumentType) +""" diff --git a/conformance/results/pyright/directives_no_type_check.toml b/conformance/results/pyright/directives_no_type_check.toml new file mode 100644 index 000000000..858c581eb --- /dev/null +++ b/conformance/results/pyright/directives_no_type_check.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +notes = """ +Does not honor `@no_type_check` class decorator (allowed). +""" +output = """ +directives_no_type_check.py:15:14 - error: Type "Literal['']" is not assignable to declared type "int" +  "Literal['']" is not assignable to "int" (reportAssignmentType) +directives_no_type_check.py:32:1 - error: Arguments missing for parameters "a", "b" (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_reveal_type.toml b/conformance/results/pyright/directives_reveal_type.toml new file mode 100644 index 000000000..8f427c3b3 --- /dev/null +++ b/conformance/results/pyright/directives_reveal_type.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +output = """ +directives_reveal_type.py:14:17 - information: Type of "a" is "int | str" +directives_reveal_type.py:15:17 - information: Type of "b" is "list[int]" +directives_reveal_type.py:16:17 - information: Type of "c" is "Any" +directives_reveal_type.py:17:17 - information: Type of "d" is "ForwardReference" +directives_reveal_type.py:19:5 - error: Expected a single positional argument for "reveal_type" call (reportCallIssue) +directives_reveal_type.py:20:5 - error: Expected a single positional argument for "reveal_type" call (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_type_checking.toml b/conformance/results/pyright/directives_type_checking.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/directives_type_checking.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_type_ignore.toml b/conformance/results/pyright/directives_type_ignore.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/directives_type_ignore.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_type_ignore_file1.toml b/conformance/results/pyright/directives_type_ignore_file1.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/directives_type_ignore_file1.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_type_ignore_file2.toml b/conformance/results/pyright/directives_type_ignore_file2.toml new file mode 100644 index 000000000..667eeb77b --- /dev/null +++ b/conformance/results/pyright/directives_type_ignore_file2.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +directives_type_ignore_file2.py:14:10 - error: Type "Literal['']" is not assignable to declared type "int" +  "Literal['']" is not assignable to "int" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/directives_version_platform.toml b/conformance/results/pyright/directives_version_platform.toml new file mode 100644 index 000000000..e37efb6b3 --- /dev/null +++ b/conformance/results/pyright/directives_version_platform.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +directives_version_platform.py:33:19 - error: "val3" is not defined (reportUndefinedVariable) +directives_version_platform.py:50:19 - error: "val6" is not defined (reportUndefinedVariable) +directives_version_platform.py:59:19 - error: "val9" is not defined (reportUndefinedVariable) +directives_version_platform.py:66:19 - error: "val10" is not defined (reportUndefinedVariable) +directives_version_platform.py:75:19 - error: "val13" is not defined (reportUndefinedVariable) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/enums_behaviors.toml b/conformance/results/pyright/enums_behaviors.toml new file mode 100644 index 000000000..3d5c93daa --- /dev/null +++ b/conformance/results/pyright/enums_behaviors.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +enums_behaviors.py:28:13 - error: "assert_type" mismatch: expected "Literal[Color.RED]" but received "Color" (reportAssertTypeFailure) +enums_behaviors.py:32:13 - error: "assert_type" mismatch: expected "Literal[Color.BLUE]" but received "Color" (reportAssertTypeFailure) +enums_behaviors.py:44:21 - error: Enum class "Shape" is final and cannot be subclassed (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" diff --git a/conformance/results/pyright/enums_definition.toml b/conformance/results/pyright/enums_definition.toml new file mode 100644 index 000000000..6741494f1 --- /dev/null +++ b/conformance/results/pyright/enums_definition.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +enums_definition.py:92:9 - error: Cannot access attribute "BLUE" for class "type[Color12]" +  Attribute "BLUE" is unknown (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" diff --git a/conformance/results/pyright/enums_expansion.toml b/conformance/results/pyright/enums_expansion.toml new file mode 100644 index 000000000..2a48dea06 --- /dev/null +++ b/conformance/results/pyright/enums_expansion.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_expansion.py:53:21 - error: "assert_type" mismatch: expected "Literal[CustomFlags.FLAG3]" but received "CustomFlags" (reportAssertTypeFailure) +""" diff --git a/conformance/results/pyright/enums_member_names.toml b/conformance/results/pyright/enums_member_names.toml new file mode 100644 index 000000000..be211bd5b --- /dev/null +++ b/conformance/results/pyright/enums_member_names.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/pyright/enums_member_values.toml b/conformance/results/pyright/enums_member_values.toml new file mode 100644 index 000000000..2efe3d766 --- /dev/null +++ b/conformance/results/pyright/enums_member_values.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_member_values.py:50:12 - error: Argument missing for parameter "radius" (reportCallIssue) +enums_member_values.py:51:15 - error: Arguments missing for parameters "mass", "radius" (reportCallIssue) +enums_member_values.py:54:13 - error: "assert_type" mismatch: expected "Literal[1]" but received "Any" (reportAssertTypeFailure) +enums_member_values.py:68:13 - error: "assert_type" mismatch: expected "Literal[1]" but received "int" (reportAssertTypeFailure) +enums_member_values.py:78:13 - error: Type "Literal['green']" is not assignable to declared type "int" +  "Literal['green']" is not assignable to "int" (reportAssignmentType) +enums_member_values.py:85:24 - error: Cannot assign to attribute "_value_" for class "Planet2*" +  "int" is not assignable to "str" (reportAttributeAccessIssue) +""" diff --git a/conformance/results/pyright/enums_members.toml b/conformance/results/pyright/enums_members.toml new file mode 100644 index 000000000..563fb2297 --- /dev/null +++ b/conformance/results/pyright/enums_members.toml @@ -0,0 +1,24 @@ +conformant = "Pass" +notes = """ +Does not support `_ignore_` mechanism (optional). +""" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_members.py:54:5 - error: Type annotations are not allowed for enum members (reportGeneralTypeIssues) +enums_members.py:86:20 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +enums_members.py:86:25 - error: Variable not allowed in type expression (reportInvalidTypeForm) +enums_members.py:87:20 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +enums_members.py:87:25 - error: Variable not allowed in type expression (reportInvalidTypeForm) +enums_members.py:88:18 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +enums_members.py:89:16 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +enums_members.py:120:13 - error: "assert_type" mismatch: expected "Unknown" but received "int" (reportAssertTypeFailure) +enums_members.py:120:32 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +enums_members.py:132:21 - information: Type of "Example2.__B" is "Literal[2]" +enums_members.py:133:21 - error: "assert_type" mismatch: expected "Unknown" but received "Literal[2]" (reportAssertTypeFailure) +enums_members.py:133:43 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +enums_members.py:133:52 - error: Variable not allowed in type expression (reportInvalidTypeForm) +enums_members.py:150:13 - error: "assert_type" mismatch: expected "int" but received "Literal[Pet5.DOG]" (reportAssertTypeFailure) +enums_members.py:151:13 - error: "assert_type" mismatch: expected "int" but received "Literal[Pet5.FISH]" (reportAssertTypeFailure) +""" diff --git a/conformance/results/pyright/exceptions_context_managers.toml b/conformance/results/pyright/exceptions_context_managers.toml new file mode 100644 index 000000000..513dbe853 --- /dev/null +++ b/conformance/results/pyright/exceptions_context_managers.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +errors_diff = """ +""" +output = """ +""" +conformance_automated = "Pass" diff --git a/conformance/results/pyright/generics_base_class.toml b/conformance/results/pyright/generics_base_class.toml new file mode 100644 index 000000000..e39db1ee5 --- /dev/null +++ b/conformance/results/pyright/generics_base_class.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +output = """ +generics_base_class.py:26:26 - error: Argument of type "SymbolTable" cannot be assigned to parameter "x" of type "dict[str, list[object]]" in function "takes_dict_incorrect" +  "SymbolTable" is not assignable to "dict[str, list[object]]" +    Type parameter "_VT@dict" is invariant, but "list[Node]" is not the same as "list[object]" +    Consider switching from "dict" to "Mapping" which is covariant in the value type (reportArgumentType) +generics_base_class.py:29:14 - error: "Generic" is not valid in this context (reportGeneralTypeIssues) +generics_base_class.py:30:8 - error: "Generic" requires at least one type argument (reportInvalidTypeForm) +generics_base_class.py:30:8 - error: "Generic" is not valid in this context (reportGeneralTypeIssues) +generics_base_class.py:49:38 - error: Too many type arguments provided for "LinkedList"; expected 1 but received 2 (reportInvalidTypeArguments) +generics_base_class.py:61:30 - error: Too many type arguments provided for "MyDict"; expected 1 but received 2 (reportInvalidTypeArguments) +generics_base_class.py:68:28 - error: Type arguments for "Generic" must be unique (reportInvalidTypeForm) +generics_base_class.py:98:7 - error: Base classes of BadChild are mutually incompatible +  Base class "Grandparent[T2@BadChild, T1@BadChild]" derives from "Grandparent[T2@BadChild, T1@BadChild]" which is incompatible with type "Grandparent[T1@BadChild, T2@BadChild]" (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_basic.toml b/conformance/results/pyright/generics_basic.toml new file mode 100644 index 000000000..8b77d57f4 --- /dev/null +++ b/conformance/results/pyright/generics_basic.toml @@ -0,0 +1,26 @@ +conformant = "Pass" +output = """ +generics_basic.py:40:15 - error: Argument of type "bytes" cannot be assigned to parameter "y" of type "AnyStr@concat" in function "concat" +  "bytes" is not assignable to "str" (reportArgumentType) +generics_basic.py:41:15 - error: Argument of type "str" cannot be assigned to parameter "y" of type "AnyStr@concat" in function "concat" +  "str" is not assignable to "bytes" (reportArgumentType) +generics_basic.py:49:44 - error: TypeVar must have at least two constrained types (reportGeneralTypeIssues) +generics_basic.py:55:53 - error: TypeVar constraint type cannot be generic (reportGeneralTypeIssues) +generics_basic.py:69:15 - error: Argument of type "bytes" cannot be assigned to parameter "y" of type "AnyStr@concat" in function "concat" +  "bytes" is not assignable to "str" (reportArgumentType) +generics_basic.py:121:24 - error: Type arguments for "Generic" must be unique (reportInvalidTypeForm) +generics_basic.py:157:5 - error: Argument of type "Literal[0]" cannot be assigned to parameter "key" of type "str" in function "__getitem__" +  "Literal[0]" is not assignable to "str" (reportArgumentType) +generics_basic.py:158:5 - error: Argument of type "Literal[0]" cannot be assigned to parameter "key" of type "str" in function "__getitem__" +  "Literal[0]" is not assignable to "str" (reportArgumentType) +generics_basic.py:162:20 - error: Type argument for "Generic" must be a type variable (reportInvalidTypeForm) +generics_basic.py:163:21 - error: Type argument for "Protocol" must be a type parameter (reportInvalidTypeForm) +generics_basic.py:171:7 - error: Generic[] or Protocol[] must include all type variables +  Missing type variables: "T_co" (reportGeneralTypeIssues) +generics_basic.py:172:7 - error: Generic[] or Protocol[] must include all type variables +  Missing type variables: "T_co" (reportGeneralTypeIssues) +generics_basic.py:208:37 - error: Metaclass cannot be generic (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_defaults.toml b/conformance/results/pyright/generics_defaults.toml new file mode 100644 index 000000000..d306076f2 --- /dev/null +++ b/conformance/results/pyright/generics_defaults.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +output = """ +generics_defaults.py:24:7 - error: "T" cannot appear after "DefaultStrT" in type parameter list because it has no default type (reportGeneralTypeIssues) +generics_defaults.py:66:23 - error: Too few type arguments provided for "AllTheDefaults"; expected 2 but received 1 (reportInvalidTypeArguments) +generics_defaults.py:152:51 - error: TypeVar default type must be a subtype of the bound type (reportGeneralTypeIssues) +generics_defaults.py:159:52 - error: TypeVar default type must be one of the constrained types (reportGeneralTypeIssues) +generics_defaults.py:177:13 - error: "assert_type" mismatch: expected "Any" but received "int" (reportAssertTypeFailure) +generics_defaults.py:188:7 - error: TypeVar "T5" has a default value and cannot follow TypeVarTuple "Ts" (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" +ignore_errors = ["Access to generic instance variable through class is ambiguous"] diff --git a/conformance/results/pyright/generics_defaults_referential.toml b/conformance/results/pyright/generics_defaults_referential.toml new file mode 100644 index 000000000..f05169557 --- /dev/null +++ b/conformance/results/pyright/generics_defaults_referential.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +output = """ +generics_defaults_referential.py:37:17 - error: Argument of type "str" cannot be assigned to parameter "b" of type "int" in function "__init__" +  "str" is not assignable to "int" (reportArgumentType) +generics_defaults_referential.py:38:14 - error: Argument of type "str" cannot be assigned to parameter "a" of type "int" in function "__init__" +  "str" is not assignable to "int" (reportArgumentType) +generics_defaults_referential.py:54:7 - error: Type parameter "Start2T" has a default type that refers to one or more type variables that are out of scope +  Type variable "StopT" is not in scope (reportGeneralTypeIssues) +generics_defaults_referential.py:61:11 - error: Type parameter "S2" has a default type that refers to one or more type variables that are out of scope +  Type variable "S1" is not in scope (reportGeneralTypeIssues) +generics_defaults_referential.py:69:40 - error: TypeVar default type must be a subtype of the bound type (reportGeneralTypeIssues) +generics_defaults_referential.py:75:52 - error: TypeVar default type must be one of the constrained types (reportGeneralTypeIssues) +generics_defaults_referential.py:79:63 - error: TypeVar default type must be one of the constrained types (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_defaults_specialization.toml b/conformance/results/pyright/generics_defaults_specialization.toml new file mode 100644 index 000000000..28c6cdced --- /dev/null +++ b/conformance/results/pyright/generics_defaults_specialization.toml @@ -0,0 +1,12 @@ +conformant = "Partial" +notes = """ +Allows incorrect assignment to type[]. +""" +output = """ +generics_defaults_specialization.py:30:15 - error: Too many type arguments provided for "MyAlias[DefaultStrT@MyAlias]"; expected 1 but received 2 (reportInvalidTypeForm) +generics_defaults_specialization.py:56:5 - error: Expected no type arguments for class "Foo" (reportInvalidTypeArguments) +""" +conformance_automated = "Fail" +errors_diff = """ +Line 46: Expected 1 errors +""" diff --git a/conformance/results/pyright/generics_paramspec_basic.toml b/conformance/results/pyright/generics_paramspec_basic.toml new file mode 100644 index 000000000..4a4eed99a --- /dev/null +++ b/conformance/results/pyright/generics_paramspec_basic.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +output = """ +generics_paramspec_basic.py:10:1 - error: ParamSpec must be assigned to a variable named "NotIt" (reportGeneralTypeIssues) +generics_paramspec_basic.py:15:18 - error: ParamSpec is not allowed in this context (reportInvalidTypeForm) +generics_paramspec_basic.py:23:14 - error: ParamSpec is not allowed in this context (reportInvalidTypeForm) +generics_paramspec_basic.py:23:20 - error: ParamSpec is not allowed in this context (reportInvalidTypeForm) +generics_paramspec_basic.py:27:14 - error: "Concatenate" is not allowed in this context (reportInvalidTypeForm) +generics_paramspec_basic.py:31:19 - error: Type "P@func3" cannot be assigned to type variable "_T@list" +  ParamSpec is not allowed in this context (reportInvalidTypeArguments) +generics_paramspec_basic.py:35:35 - error: ParamSpec is not allowed in this context (reportInvalidTypeForm) +generics_paramspec_basic.py:39:18 - error: ParamSpec is not allowed in this context (reportInvalidTypeForm) +generics_paramspec_basic.py:39:31 - error: ParamSpec is not allowed in this context (reportInvalidTypeForm) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_paramspec_components.toml b/conformance/results/pyright/generics_paramspec_components.toml new file mode 100644 index 000000000..91d07a572 --- /dev/null +++ b/conformance/results/pyright/generics_paramspec_components.toml @@ -0,0 +1,27 @@ +conformant = "Pass" +output = """ +generics_paramspec_components.py:17:25 - error: "kwargs" attribute of ParamSpec is valid only when used with **kwargs parameter (reportInvalidTypeForm) +generics_paramspec_components.py:17:45 - error: "args" attribute of ParamSpec is valid only when used with *args parameter (reportInvalidTypeForm) +generics_paramspec_components.py:20:23 - error: "args" attribute of ParamSpec is valid only when used with *args parameter (reportInvalidTypeForm) +generics_paramspec_components.py:23:28 - error: "args" and "kwargs" attributes of ParamSpec must both appear within a function signature (reportGeneralTypeIssues) +generics_paramspec_components.py:23:46 - error: "args" attribute of ParamSpec is valid only when used with *args parameter (reportInvalidTypeForm) +generics_paramspec_components.py:26:28 - error: "args" and "kwargs" attributes of ParamSpec must both appear within a function signature (reportGeneralTypeIssues) +generics_paramspec_components.py:30:25 - error: ParamSpec "P" has no meaning in this context (reportGeneralTypeIssues) +generics_paramspec_components.py:35:18 - error: "args" attribute of ParamSpec is valid only when used with *args parameter (reportInvalidTypeForm) +generics_paramspec_components.py:36:20 - error: "kwargs" attribute of ParamSpec is valid only when used with **kwargs parameter (reportInvalidTypeForm) +generics_paramspec_components.py:38:26 - error: "args" and "kwargs" attributes of ParamSpec must both appear within a function signature (reportGeneralTypeIssues) +generics_paramspec_components.py:41:31 - error: "args" and "kwargs" attributes of ParamSpec must both appear within a function signature (reportGeneralTypeIssues) +generics_paramspec_components.py:49:9 - error: Arguments for ParamSpec "P@decorator" are missing (reportCallIssue) +generics_paramspec_components.py:51:11 - error: Expected 0 positional arguments (reportCallIssue) +generics_paramspec_components.py:60:28 - error: Keyword parameter "s" cannot appear in signature after ParamSpec args parameter (reportGeneralTypeIssues) +generics_paramspec_components.py:70:12 - error: Expected 1 positional argument (reportCallIssue) +generics_paramspec_components.py:72:12 - error: Expected 1 positional argument (reportCallIssue) +generics_paramspec_components.py:83:19 - error: Expected 1 positional argument (reportCallIssue) +generics_paramspec_components.py:98:20 - error: Argument of type "Literal['A']" cannot be assigned to parameter "a" of type "int" +  "Literal['A']" is not assignable to "int" (reportArgumentType) +generics_paramspec_components.py:98:25 - error: Argument of type "Literal[1]" cannot be assigned to parameter "b" of type "str" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_paramspec_semantics.toml b/conformance/results/pyright/generics_paramspec_semantics.toml new file mode 100644 index 000000000..e24a0a9c8 --- /dev/null +++ b/conformance/results/pyright/generics_paramspec_semantics.toml @@ -0,0 +1,36 @@ +conformant = "Pass" +notes = """ +Constraint solver doesn't find common type for two signatures captured by a single ParamSpec (allowed). +""" +output = """ +generics_paramspec_semantics.py:26:6 - error: Expected 2 more positional arguments (reportCallIssue) +generics_paramspec_semantics.py:27:9 - error: Argument of type "Literal['A']" cannot be assigned to parameter "b" of type "bool" +  "Literal['A']" is not assignable to "bool" (reportArgumentType) +generics_paramspec_semantics.py:46:17 - error: Argument of type "(y: int, x: str) -> int" cannot be assigned to parameter "y" of type "(**P@func1) -> int" in function "func1" +  Type "(y: int, x: str) -> int" is not assignable to type "(x: int, y: str) -> int" +    Parameter name mismatch: "x" versus "y" +    Parameter name mismatch: "y" versus "x" (reportArgumentType) +generics_paramspec_semantics.py:61:23 - error: Argument of type "(*, y: int) -> int" cannot be assigned to parameter "y" of type "(**P@func1) -> int" in function "func1" +  Type "(*, y: int) -> int" is not assignable to type "(*, x: int) -> int" +    Extra parameter "y" +    Missing keyword parameter "x" (reportArgumentType) +generics_paramspec_semantics.py:98:4 - error: Argument of type "Literal[1]" cannot be assigned to parameter of type "str" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +generics_paramspec_semantics.py:108:4 - error: Argument of type "Literal[1]" cannot be assigned to parameter "args" of type "bool" +  "Literal[1]" is not assignable to "bool" (reportArgumentType) +generics_paramspec_semantics.py:120:4 - error: Argument of type "Literal[1]" cannot be assigned to parameter of type "str" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +generics_paramspec_semantics.py:127:2 - error: Argument of type "(x: str) -> int" cannot be assigned to parameter "x" of type "(int, **P@expects_int_first) -> int" in function "expects_int_first" +  Type "(x: str) -> int" is not assignable to type "(int, **P@expects_int_first) -> int" +    Parameter 1: type "int" is incompatible with type "str" +      "int" is not assignable to "str" (reportArgumentType) +generics_paramspec_semantics.py:132:2 - error: Argument of type "(*, x: int) -> int" cannot be assigned to parameter "x" of type "(int, **P@expects_int_first) -> int" in function "expects_int_first" +  Type "(*, x: int) -> int" is not assignable to type "(int, **P@expects_int_first) -> int" +    Function accepts too many positional parameters; expected 0 but received 1 (reportArgumentType) +generics_paramspec_semantics.py:137:2 - error: Argument of type "(**kwargs: int) -> int" cannot be assigned to parameter "x" of type "(int, **P@expects_int_first) -> int" in function "expects_int_first" +  Type "(**kwargs: int) -> int" is not assignable to type "(int, **P@expects_int_first) -> int" +    Function accepts too many positional parameters; expected 0 but received 1 (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_paramspec_specialization.toml b/conformance/results/pyright/generics_paramspec_specialization.toml new file mode 100644 index 000000000..af1ab6764 --- /dev/null +++ b/conformance/results/pyright/generics_paramspec_specialization.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +output = """ +generics_paramspec_specialization.py:44:27 - error: Type "int" cannot be assigned to type variable "P1@ClassA" +  Type "int" is incompatible with ParamSpec "P1@ClassA" (reportInvalidTypeArguments) +generics_paramspec_specialization.py:54:9 - error: Argument of type "Literal['']" cannot be assigned to parameter of type "int" +  "Literal['']" is not assignable to "int" (reportArgumentType) +generics_paramspec_specialization.py:55:16 - error: Argument of type "Literal['']" cannot be assigned to parameter of type "bool" +  "Literal['']" is not assignable to "bool" (reportArgumentType) +generics_paramspec_specialization.py:60:9 - error: Argument of type "Literal['']" cannot be assigned to parameter of type "int" +  "Literal['']" is not assignable to "int" (reportArgumentType) +generics_paramspec_specialization.py:61:16 - error: Argument of type "Literal['']" cannot be assigned to parameter of type "bool" +  "Literal['']" is not assignable to "bool" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_scoping.toml b/conformance/results/pyright/generics_scoping.toml new file mode 100644 index 000000000..9e73f07dd --- /dev/null +++ b/conformance/results/pyright/generics_scoping.toml @@ -0,0 +1,21 @@ +conformant = "Pass" +output = """ +generics_scoping.py:16:13 - error: "assert_type" mismatch: expected "Literal[1]" but received "int" (reportAssertTypeFailure) +generics_scoping.py:20:13 - error: "assert_type" mismatch: expected "Literal['a']" but received "str" (reportAssertTypeFailure) +generics_scoping.py:34:10 - error: Argument of type "Literal['a']" cannot be assigned to parameter "x" of type "int" in function "meth_2" +  "Literal['a']" is not assignable to "int" (reportArgumentType) +generics_scoping.py:50:13 - error: "assert_type" mismatch: expected "Literal['abc']" but received "str" (reportAssertTypeFailure) +generics_scoping.py:54:13 - error: "assert_type" mismatch: expected "Literal[b"abc"]" but received "bytes" (reportAssertTypeFailure) +generics_scoping.py:61:13 - error: Type variable "S" has no meaning in this context (reportGeneralTypeIssues) +generics_scoping.py:65:19 - error: Type variable "S" has no meaning in this context (reportGeneralTypeIssues) +generics_scoping.py:76:29 - error: TypeVar "T" is already in use by an outer scope (reportGeneralTypeIssues) +generics_scoping.py:86:24 - error: TypeVar "T" is already in use by an outer scope (reportGeneralTypeIssues) +generics_scoping.py:89:17 - error: Type variable "T" has no meaning in this context (reportGeneralTypeIssues) +generics_scoping.py:98:5 - error: Generic type alias within class cannot use bound type variables T (reportInvalidTypeForm) +generics_scoping.py:105:14 - error: Type variable "T" has no meaning in this context (reportGeneralTypeIssues) +generics_scoping.py:106:19 - error: Type variable "T" has no meaning in this context (reportGeneralTypeIssues) +generics_scoping.py:107:6 - error: Type variable "T" has no meaning in this context (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_self_advanced.toml b/conformance/results/pyright/generics_self_advanced.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/generics_self_advanced.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_self_attributes.toml b/conformance/results/pyright/generics_self_attributes.toml new file mode 100644 index 000000000..b5c9fe45c --- /dev/null +++ b/conformance/results/pyright/generics_self_attributes.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +output = """ +generics_self_attributes.py:26:38 - error: Argument of type "LinkedList[int]" cannot be assigned to parameter "next" of type "OrdinalLinkedList | None" in function "__init__" +  Type "LinkedList[int]" is not assignable to type "OrdinalLinkedList | None" +    "LinkedList[int]" is not assignable to "OrdinalLinkedList" +    "LinkedList[int]" is not assignable to "None" (reportArgumentType) +generics_self_attributes.py:32:8 - error: Cannot assign to attribute "next" for class "OrdinalLinkedList" +  Expression of type "LinkedList[int]" cannot be assigned to attribute "next" of class "OrdinalLinkedList" +    Type "LinkedList[int]" is not assignable to type "OrdinalLinkedList | None" +      "LinkedList[int]" is not assignable to "OrdinalLinkedList" +      "LinkedList[int]" is not assignable to "None" (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_self_basic.toml b/conformance/results/pyright/generics_self_basic.toml new file mode 100644 index 000000000..6b1dc6c56 --- /dev/null +++ b/conformance/results/pyright/generics_self_basic.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +generics_self_basic.py:20:16 - error: Type "Shape" is not assignable to return type "Self@Shape" +  Type "Shape" is not assignable to type "Self@Shape" (reportReturnType) +generics_self_basic.py:33:16 - error: Type "Shape" is not assignable to return type "Self@Shape" +  Type "Shape" is not assignable to type "Self@Shape" (reportReturnType) +generics_self_basic.py:68:31 - error: Expected no type arguments for class "Self" (reportInvalidTypeArguments) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_self_protocols.toml b/conformance/results/pyright/generics_self_protocols.toml new file mode 100644 index 000000000..aa6455302 --- /dev/null +++ b/conformance/results/pyright/generics_self_protocols.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +output = """ +generics_self_protocols.py:61:19 - error: Argument of type "BadReturnType" cannot be assigned to parameter "shape" of type "ShapeProtocol" in function "accepts_shape" +  "BadReturnType" is incompatible with protocol "ShapeProtocol" +    "set_scale" is an incompatible type +      Type "(scale: float) -> int" is not assignable to type "(scale: float) -> BadReturnType" +        Function return type "int" is incompatible with type "BadReturnType" +          "int" is not assignable to "BadReturnType" (reportArgumentType) +generics_self_protocols.py:64:19 - error: Argument of type "ReturnDifferentClass" cannot be assigned to parameter "shape" of type "ShapeProtocol" in function "accepts_shape" +  "ReturnDifferentClass" is incompatible with protocol "ShapeProtocol" +    "set_scale" is an incompatible type +      Type "(scale: float) -> ReturnConcreteShape" is not assignable to type "(scale: float) -> ReturnDifferentClass" +        Function return type "ReturnConcreteShape" is incompatible with type "ReturnDifferentClass" +          "ReturnConcreteShape" is not assignable to "ReturnDifferentClass" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_self_usage.toml b/conformance/results/pyright/generics_self_usage.toml new file mode 100644 index 000000000..b3b609619 --- /dev/null +++ b/conformance/results/pyright/generics_self_usage.toml @@ -0,0 +1,22 @@ +conformant = "Pass" +output = """ +generics_self_usage.py:73:14 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:73:23 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:76:6 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:82:44 - warning: TypeVar "TFoo2" appears only once in generic function signature +  Use "Foo2" instead (reportInvalidTypeVarUse) +generics_self_usage.py:82:54 - error: "Self" cannot be used in a function with a `self` or `cls` parameter that has a type annotation other than "Self" (reportGeneralTypeIssues) +generics_self_usage.py:87:16 - error: Type "Foo3" is not assignable to return type "Self@Foo3" +  Type "Foo3" is not assignable to type "Self@Foo3" (reportReturnType) +generics_self_usage.py:103:15 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:105:12 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:108:30 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:113:19 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:118:31 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:118:40 - error: "Self" is not valid in this context (reportGeneralTypeIssues) +generics_self_usage.py:123:37 - error: "Self" cannot be used within a metaclass (a subclass of "type") (reportGeneralTypeIssues) +generics_self_usage.py:127:42 - error: "Self" cannot be used within a metaclass (a subclass of "type") (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_syntax_compatibility.toml b/conformance/results/pyright/generics_syntax_compatibility.toml new file mode 100644 index 000000000..415f0f6ac --- /dev/null +++ b/conformance/results/pyright/generics_syntax_compatibility.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +output = """ +generics_syntax_compatibility.py:14:22 - error: Type parameter "K" is not included in the type parameter list for "ClassA" (reportGeneralTypeIssues) +generics_syntax_compatibility.py:26:35 - error: Type parameter "K" is not included in the type parameter list for "method2" (reportGeneralTypeIssues) +generics_syntax_compatibility.py:26:45 - error: Type parameter "K" is not included in the type parameter list for "method2" (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_syntax_declarations.toml b/conformance/results/pyright/generics_syntax_declarations.toml new file mode 100644 index 000000000..bc076dc93 --- /dev/null +++ b/conformance/results/pyright/generics_syntax_declarations.toml @@ -0,0 +1,19 @@ +conformant = "Pass" +output = """ +generics_syntax_declarations.py:17:17 - error: "Generic" base class cannot be used with type parameter syntax (reportGeneralTypeIssues) +generics_syntax_declarations.py:25:20 - error: Type arguments are not allowed with Protocol class when using type parameter syntax (reportGeneralTypeIssues) +generics_syntax_declarations.py:32:11 - error: Cannot access attribute "is_integer" for class "str*" +  Attribute "is_integer" is unknown (reportAttributeAccessIssue) +generics_syntax_declarations.py:44:21 - error: TypeVar constraint type cannot be generic (reportGeneralTypeIssues) +generics_syntax_declarations.py:48:17 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +generics_syntax_declarations.py:48:17 - error: Expected class but received "list[Unknown]" (reportGeneralTypeIssues) +generics_syntax_declarations.py:60:17 - error: TypeVar must have at least two constrained types (reportGeneralTypeIssues) +generics_syntax_declarations.py:64:17 - error: TypeVar must have at least two constrained types (reportGeneralTypeIssues) +generics_syntax_declarations.py:71:17 - error: Variable not allowed in type expression (reportInvalidTypeForm) +generics_syntax_declarations.py:75:18 - error: Expected class but received "Literal[3]" (reportGeneralTypeIssues) +generics_syntax_declarations.py:79:23 - error: "S" is not defined (reportUndefinedVariable) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_syntax_infer_variance.toml b/conformance/results/pyright/generics_syntax_infer_variance.toml new file mode 100644 index 000000000..a871ffee0 --- /dev/null +++ b/conformance/results/pyright/generics_syntax_infer_variance.toml @@ -0,0 +1,62 @@ +conformant = "Pass" +output = """ +generics_syntax_infer_variance.py:15:51 - error: TypeVar cannot be both covariant and contravariant (reportGeneralTypeIssues) +generics_syntax_infer_variance.py:17:55 - error: TypeVar cannot be both covariant and contravariant (reportGeneralTypeIssues) +generics_syntax_infer_variance.py:29:35 - error: Type "ShouldBeCovariant1[float]" is not assignable to declared type "ShouldBeCovariant1[int]" +  "ShouldBeCovariant1[float]" is not assignable to "ShouldBeCovariant1[int]" +    Type parameter "T@ShouldBeCovariant1" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_syntax_infer_variance.py:47:35 - error: Type "ShouldBeCovariant2[float]" is not assignable to declared type "ShouldBeCovariant2[int]" +  "ShouldBeCovariant2[float]" is not assignable to "ShouldBeCovariant2[int]" +    Type parameter "T@ShouldBeCovariant2" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_syntax_infer_variance.py:56:35 - error: Type "ShouldBeCovariant3[float]" is not assignable to declared type "ShouldBeCovariant3[int]" +  "ShouldBeCovariant3[float]" is not assignable to "ShouldBeCovariant3[int]" +    Type parameter "T@ShouldBeCovariant3" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_syntax_infer_variance.py:85:34 - error: Type "ShouldBeCovariant5[float]" is not assignable to declared type "ShouldBeCovariant5[int]" +  "ShouldBeCovariant5[float]" is not assignable to "ShouldBeCovariant5[int]" +    Type parameter "T@ShouldBeCovariant5" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_syntax_infer_variance.py:96:34 - error: Type "ShouldBeCovariant6[float]" is not assignable to declared type "ShouldBeCovariant6[int]" +  "ShouldBeCovariant6[float]" is not assignable to "ShouldBeCovariant6[int]" +    Type parameter "T@ShouldBeCovariant6" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_syntax_infer_variance.py:112:38 - error: Type "ShouldBeInvariant1[int]" is not assignable to declared type "ShouldBeInvariant1[float]" +  "ShouldBeInvariant1[int]" is not assignable to "ShouldBeInvariant1[float]" +    Type parameter "T@ShouldBeInvariant1" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_syntax_infer_variance.py:113:36 - error: Type "ShouldBeInvariant1[float]" is not assignable to declared type "ShouldBeInvariant1[int]" +  "ShouldBeInvariant1[float]" is not assignable to "ShouldBeInvariant1[int]" +    Type parameter "T@ShouldBeInvariant1" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_syntax_infer_variance.py:127:38 - error: Type "ShouldBeInvariant2[int]" is not assignable to declared type "ShouldBeInvariant2[float]" +  "ShouldBeInvariant2[int]" is not assignable to "ShouldBeInvariant2[float]" +    Type parameter "T@ShouldBeInvariant2" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_syntax_infer_variance.py:128:36 - error: Type "ShouldBeInvariant2[float]" is not assignable to declared type "ShouldBeInvariant2[int]" +  "ShouldBeInvariant2[float]" is not assignable to "ShouldBeInvariant2[int]" +    Type parameter "T@ShouldBeInvariant2" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_syntax_infer_variance.py:135:43 - error: Type "ShouldBeInvariant3[int, str]" is not assignable to declared type "ShouldBeInvariant3[float, str]" +  "ShouldBeInvariant3[int, str]" is not assignable to "ShouldBeInvariant3[float, str]" +    Type parameter "K@ShouldBeInvariant3" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_syntax_infer_variance.py:136:41 - error: Type "ShouldBeInvariant3[float, str]" is not assignable to declared type "ShouldBeInvariant3[int, str]" +  "ShouldBeInvariant3[float, str]" is not assignable to "ShouldBeInvariant3[int, str]" +    Type parameter "K@ShouldBeInvariant3" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_syntax_infer_variance.py:137:43 - error: Type "ShouldBeInvariant3[str, int]" is not assignable to declared type "ShouldBeInvariant3[str, float]" +  "ShouldBeInvariant3[str, int]" is not assignable to "ShouldBeInvariant3[str, float]" +    Type parameter "V@ShouldBeInvariant3" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_syntax_infer_variance.py:138:41 - error: Type "ShouldBeInvariant3[str, float]" is not assignable to declared type "ShouldBeInvariant3[str, int]" +  "ShouldBeInvariant3[str, float]" is not assignable to "ShouldBeInvariant3[str, int]" +    Type parameter "V@ShouldBeInvariant3" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_syntax_infer_variance.py:146:38 - error: Type "ShouldBeInvariant4[int]" is not assignable to declared type "ShouldBeInvariant4[float]" +  "ShouldBeInvariant4[int]" is not assignable to "ShouldBeInvariant4[float]" +    Type parameter "T@ShouldBeInvariant4" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_syntax_infer_variance.py:154:38 - error: Type "ShouldBeInvariant5[int]" is not assignable to declared type "ShouldBeInvariant5[float]" +  "ShouldBeInvariant5[int]" is not assignable to "ShouldBeInvariant5[float]" +    Type parameter "T@ShouldBeInvariant5" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_syntax_infer_variance.py:165:45 - error: Type "ShouldBeContravariant1[int]" is not assignable to declared type "ShouldBeContravariant1[float]" +  "ShouldBeContravariant1[int]" is not assignable to "ShouldBeContravariant1[float]" +    Type parameter "T@ShouldBeContravariant1" is contravariant, but "int" is not a supertype of "float" +      "float" is not assignable to "int" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_syntax_scoping.toml b/conformance/results/pyright/generics_syntax_scoping.toml new file mode 100644 index 000000000..4a8f43c6a --- /dev/null +++ b/conformance/results/pyright/generics_syntax_scoping.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +output = """ +generics_syntax_scoping.py:14:20 - error: TypeVar constraint type cannot be generic (reportGeneralTypeIssues) +generics_syntax_scoping.py:18:17 - error: TypeVar constraint type cannot be generic (reportGeneralTypeIssues) +generics_syntax_scoping.py:35:7 - error: "T" is not defined (reportUndefinedVariable) +generics_syntax_scoping.py:44:17 - error: "T" is not defined (reportUndefinedVariable) +generics_syntax_scoping.py:92:17 - error: TypeVar "T" is already in use by an outer scope (reportGeneralTypeIssues) +generics_syntax_scoping.py:95:17 - error: TypeVar "T" is already in use by an outer scope (reportGeneralTypeIssues) +generics_syntax_scoping.py:98:17 - error: TypeVar "T" is already in use by an outer scope (reportGeneralTypeIssues) +generics_syntax_scoping.py:98:29 - warning: TypeVar "T" appears only once in generic function signature +  Use "object" instead (reportInvalidTypeVarUse) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_type_erasure.toml b/conformance/results/pyright/generics_type_erasure.toml new file mode 100644 index 000000000..cbbc0d1c6 --- /dev/null +++ b/conformance/results/pyright/generics_type_erasure.toml @@ -0,0 +1,19 @@ +conformant = "Pass" +output = """ +generics_type_erasure.py:38:16 - error: Argument of type "Literal['']" cannot be assigned to parameter "label" of type "int | None" in function "__init__" +  Type "Literal['']" is not assignable to type "int | None" +    "Literal['']" is not assignable to "int" +    "Literal['']" is not assignable to "None" (reportArgumentType) +generics_type_erasure.py:40:16 - error: Argument of type "Literal[0]" cannot be assigned to parameter "label" of type "str | None" in function "__init__" +  Type "Literal[0]" is not assignable to type "str | None" +    "Literal[0]" is not assignable to "str" +    "Literal[0]" is not assignable to "None" (reportArgumentType) +generics_type_erasure.py:42:11 - error: Access to generic instance variable through class is ambiguous (reportGeneralTypeIssues) +generics_type_erasure.py:43:11 - error: Access to generic instance variable through class is ambiguous (reportGeneralTypeIssues) +generics_type_erasure.py:44:6 - error: Access to generic instance variable through class is ambiguous (reportGeneralTypeIssues) +generics_type_erasure.py:45:6 - error: Access to generic instance variable through class is ambiguous (reportGeneralTypeIssues) +generics_type_erasure.py:46:10 - error: Access to generic instance variable through class is ambiguous (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_typevartuple_args.toml b/conformance/results/pyright/generics_typevartuple_args.toml new file mode 100644 index 000000000..65c6a1992 --- /dev/null +++ b/conformance/results/pyright/generics_typevartuple_args.toml @@ -0,0 +1,26 @@ +conformant = "Partial" +notes = """ +Does not correctly solve TypeVarTuple with heterogeneous bounds. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 76: Unexpected errors ['generics_typevartuple_args.py:76:14 - error: Argument of type "tuple[Literal[\\'1\\']]" cannot be assigned to parameter "args" of type "tuple[*Ts@func4]" in function "func4"'] +""" +output = """ +generics_typevartuple_args.py:33:20 - error: Argument of type "Literal['']" cannot be assigned to parameter of type "Env" in function "exec_le" +  "Literal['']" is not assignable to "Env" (reportArgumentType) +generics_typevartuple_args.py:34:20 - error: Argument of type "Literal['']" cannot be assigned to parameter of type "Env" in function "exec_le" +  "Literal['']" is not assignable to "Env" (reportArgumentType) +generics_typevartuple_args.py:48:10 - error: Argument of type "Literal['2']" cannot be assigned to parameter of type "int" in function "func1" +  "Literal['2']" is not assignable to "int" (reportArgumentType) +generics_typevartuple_args.py:57:10 - error: Argument of type "Literal[1]" cannot be assigned to parameter of type "str" in function "func2" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +generics_typevartuple_args.py:58:1 - error: Argument missing for parameter "args[2]" (reportCallIssue) +generics_typevartuple_args.py:59:1 - error: Argument missing for parameter "args[2]" (reportCallIssue) +generics_typevartuple_args.py:67:1 - error: Argument missing for parameter "args[1]" (reportCallIssue) +generics_typevartuple_args.py:75:13 - error: Argument of type "tuple[Literal[1], Literal[2]]" cannot be assigned to parameter "args" of type "tuple[*Ts@func4]" in function "func4" +  "tuple[Literal[1], Literal[2]]" is not assignable to "tuple[int]" +    Tuple size mismatch; expected 1 but received 2 (reportArgumentType) +generics_typevartuple_args.py:76:14 - error: Argument of type "tuple[Literal['1']]" cannot be assigned to parameter "args" of type "tuple[*Ts@func4]" in function "func4" +  "Literal['1']" is not assignable to "int" (reportArgumentType) +""" diff --git a/conformance/results/pyright/generics_typevartuple_basic.toml b/conformance/results/pyright/generics_typevartuple_basic.toml new file mode 100644 index 000000000..7e5cfa403 --- /dev/null +++ b/conformance/results/pyright/generics_typevartuple_basic.toml @@ -0,0 +1,39 @@ +conformant = "Partial" +notes = """ +Does not correctly solve TypeVarTuple with heterogeneous bounds. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 90: Unexpected errors ['generics_typevartuple_basic.py:90:14 - error: Argument of type "tuple[Literal[\\'0\\']]" cannot be assigned to parameter "arg2" of type "tuple[*Ts@func2]" in function "func2"'] +""" +output = """ +generics_typevartuple_basic.py:42:34 - error: Argument of type "Height" cannot be assigned to parameter "shape" of type "tuple[*Shape@Array]" in function "__init__" +  "Height" is not assignable to "tuple[*Shape@Array]" (reportArgumentType) +generics_typevartuple_basic.py:43:35 - error: Type "Array[Batch, Width]" is not assignable to declared type "Array[Batch, Height, Width]" +  "Array[Batch, Width]" is not assignable to "Array[Batch, Height, Width]" +    Type parameter "Shape@Array" is invariant, but "*tuple[Batch, Width]" is not the same as "*tuple[Batch, Height, Width]" (reportAssignmentType) +generics_typevartuple_basic.py:44:41 - error: Type "Array[Time, Batch, Width, Height]" is not assignable to declared type "Array[Time, Batch, Height, Width]" +  "Array[Time, Batch, Width, Height]" is not assignable to "Array[Time, Batch, Height, Width]" +    Type parameter "Shape@Array" is invariant, but "*tuple[Time, Batch, Width, Height]" is not the same as "*tuple[Time, Batch, Height, Width]" (reportAssignmentType) +generics_typevartuple_basic.py:52:22 - error: Expected unpacked TypeVarTuple; use Unpack[Shape] or *Shape (reportInvalidTypeForm) +generics_typevartuple_basic.py:53:37 - error: Expected unpacked TypeVarTuple; use Unpack[Shape] or *Shape (reportInvalidTypeForm) +generics_typevartuple_basic.py:56:34 - error: Expected unpacked TypeVarTuple; use Unpack[Shape] or *Shape (reportInvalidTypeForm) +generics_typevartuple_basic.py:59:24 - error: Expected unpacked TypeVarTuple; use Unpack[Shape] or *Shape (reportInvalidTypeForm) +generics_typevartuple_basic.py:65:27 - error: "covariant" is unknown parameter to TypeVarTuple (reportGeneralTypeIssues) +generics_typevartuple_basic.py:66:27 - error: TypeVarTuple cannot have value constraints (reportGeneralTypeIssues) +generics_typevartuple_basic.py:66:32 - error: TypeVarTuple cannot have value constraints (reportGeneralTypeIssues) +generics_typevartuple_basic.py:67:27 - error: "bound" is unknown parameter to TypeVarTuple (reportGeneralTypeIssues) +generics_typevartuple_basic.py:90:14 - error: Argument of type "tuple[Literal['0']]" cannot be assigned to parameter "arg2" of type "tuple[*Ts@func2]" in function "func2" +  "Literal['0']" is not assignable to "int" (reportArgumentType) +generics_typevartuple_basic.py:91:15 - error: Argument of type "tuple[Literal[0]]" cannot be assigned to parameter "arg2" of type "tuple[*Ts@func2]" in function "func2" +  "tuple[Literal[0]]" is not assignable to "tuple[int, int]" +    Tuple size mismatch; expected 2 but received 1 (reportArgumentType) +generics_typevartuple_basic.py:100:17 - error: Argument of type "Array[Width]" cannot be assigned to parameter "y" of type "Array[*Shape@multiply]" in function "multiply" +  "Array[Width]" is not assignable to "Array[Height]" +    Type parameter "Shape@Array" is invariant, but "*tuple[Width]" is not the same as "*tuple[Height]" (reportArgumentType) +generics_typevartuple_basic.py:101:17 - error: Argument of type "Array[Height, Width]" cannot be assigned to parameter "y" of type "Array[*Shape@multiply]" in function "multiply" +  "Array[Height, Width]" is not assignable to "Array[Height]" +    Type parameter "Shape@Array" is invariant, but "*tuple[Height, Width]" is not the same as "*tuple[Height]" (reportArgumentType) +generics_typevartuple_basic.py:107:14 - error: Generic class can have at most one TypeVarTuple type parameter but received multiple ("Ts1", "Ts2") (reportGeneralTypeIssues) +generics_typevartuple_basic.py:107:29 - error: Type argument list can have at most one unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +""" diff --git a/conformance/results/pyright/generics_typevartuple_callable.toml b/conformance/results/pyright/generics_typevartuple_callable.toml new file mode 100644 index 000000000..fb569c88c --- /dev/null +++ b/conformance/results/pyright/generics_typevartuple_callable.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +output = """ +generics_typevartuple_callable.py:26:28 - error: Argument of type "tuple[Literal[''], Literal[0]]" cannot be assigned to parameter "args" of type "tuple[*Ts@__init__]" in function "__init__" +  "Literal['']" is not assignable to "int" +  "Literal[0]" is not assignable to "str" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_typevartuple_concat.toml b/conformance/results/pyright/generics_typevartuple_concat.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/generics_typevartuple_concat.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_typevartuple_overloads.toml b/conformance/results/pyright/generics_typevartuple_overloads.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/generics_typevartuple_overloads.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_typevartuple_specialization.toml b/conformance/results/pyright/generics_typevartuple_specialization.toml new file mode 100644 index 000000000..24066fc5d --- /dev/null +++ b/conformance/results/pyright/generics_typevartuple_specialization.toml @@ -0,0 +1,16 @@ +conformant = "Pass" +output = """ +generics_typevartuple_specialization.py:109:18 - error: Type variable "Ts" has no meaning in this context (reportGeneralTypeIssues) +generics_typevartuple_specialization.py:109:18 - error: Could not specialize type "IntTupleGeneric[T@IntTupleGeneric]" +  Unpacked arguments cannot be used in this context (reportInvalidTypeForm) +generics_typevartuple_specialization.py:110:18 - error: Could not specialize type "IntTupleGeneric[T@IntTupleGeneric]" +  Unpacked arguments cannot be used in this context (reportInvalidTypeForm) +generics_typevartuple_specialization.py:121:27 - error: Type argument list can have at most one unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +generics_typevartuple_specialization.py:122:27 - error: Type argument list can have at most one unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +generics_typevartuple_specialization.py:127:9 - error: Too few type arguments provided for "TA7[*Ts@TA7, T1@TA7, T2@TA7]"; expected 3 but received 2 (reportInvalidTypeForm) +generics_typevartuple_specialization.py:163:14 - error: Could not specialize type "TA11[T@TA11, *Ts1@TA11]" +  Unpacked arguments cannot be used in this context (reportInvalidTypeForm) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_typevartuple_unpack.toml b/conformance/results/pyright/generics_typevartuple_unpack.toml new file mode 100644 index 000000000..a6484a5a2 --- /dev/null +++ b/conformance/results/pyright/generics_typevartuple_unpack.toml @@ -0,0 +1,9 @@ +conformant = "Pass" +output = """ +generics_typevartuple_unpack.py:30:28 - error: Argument of type "Array[Batch]" cannot be assigned to parameter "x" of type "Array[Batch, *tuple[Any, ...], Channels]" in function "process_batch_channels" +  "Array[Batch]" is not assignable to "Array[Batch, *tuple[Any, ...], Channels]" +    Type parameter "Ts@Array" is invariant, but "*tuple[Batch]" is not the same as "*tuple[Batch, *tuple[Any, ...], Channels]" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_upper_bound.toml b/conformance/results/pyright/generics_upper_bound.toml new file mode 100644 index 000000000..1fd34bceb --- /dev/null +++ b/conformance/results/pyright/generics_upper_bound.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +output = """ +generics_upper_bound.py:24:38 - error: TypeVar bound type cannot be generic (reportGeneralTypeIssues) +generics_upper_bound.py:44:17 - error: "assert_type" mismatch: expected "Collection[int]" but received "list[int] | set[int]" (reportAssertTypeFailure) +generics_upper_bound.py:52:8 - error: Argument of type "Literal[3]" cannot be assigned to parameter "x" of type "ST@longer" in function "longer" +  Type "Literal[3]" is not assignable to type "Sized" +    "Literal[3]" is incompatible with protocol "Sized" +      "__len__" is not present (reportArgumentType) +generics_upper_bound.py:52:11 - error: Argument of type "Literal[3]" cannot be assigned to parameter "y" of type "ST@longer" in function "longer" +  Type "Literal[3]" is not assignable to type "Sized" +    "Literal[3]" is incompatible with protocol "Sized" +      "__len__" is not present (reportArgumentType) +generics_upper_bound.py:57:44 - error: TypeVar cannot be both bound and constrained (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_variance.toml b/conformance/results/pyright/generics_variance.toml new file mode 100644 index 000000000..113a44121 --- /dev/null +++ b/conformance/results/pyright/generics_variance.toml @@ -0,0 +1,31 @@ +conformant = "Pass" +output = """ +generics_variance.py:14:50 - error: TypeVar cannot be both covariant and contravariant (reportGeneralTypeIssues) +generics_variance.py:77:18 - error: Type "T_co@Class1" cannot be assigned to type variable "T@Inv" +  Variance of type argument "T_co@Class1" is incompatible with base class "Inv" (reportInvalidTypeArguments) +generics_variance.py:81:18 - error: Type "T_contra@Class2" cannot be assigned to type variable "T@Inv" +  Variance of type argument "T_contra@Class2" is incompatible with base class "Inv" (reportInvalidTypeArguments) +generics_variance.py:93:20 - error: Type "T_contra@Co_Child3" cannot be assigned to type variable "T_co@Co" +  Variance of type argument "T_contra@Co_Child3" is incompatible with base class "Co" (reportInvalidTypeArguments) +generics_variance.py:105:28 - error: Type "T_co@Contra_Child3" cannot be assigned to type variable "T_contra@Contra" +  Variance of type argument "T_co@Contra_Child3" is incompatible with base class "Contra" (reportInvalidTypeArguments) +generics_variance.py:113:28 - error: Type "Co[T_co@Contra_Child5]" cannot be assigned to type variable "T_contra@Contra" +  Variance of type argument "Co[T_co@Contra_Child5]" is incompatible with base class "Contra" (reportInvalidTypeArguments) +generics_variance.py:126:20 - error: Type "T_co@CoContra_Child2" cannot be assigned to type variable "T_contra@CoContra" +  Variance of type argument "T_co@CoContra_Child2" is incompatible with base class "CoContra" (reportInvalidTypeArguments) +generics_variance.py:132:14 - error: Type "T_contra@CoContra_Child3" cannot be assigned to type variable "T_co@CoContra" +  Variance of type argument "T_contra@CoContra_Child3" is incompatible with base class "CoContra" (reportInvalidTypeArguments) +generics_variance.py:142:24 - error: Type "Co[T_co@CoContra_Child5]" cannot be assigned to type variable "T_contra@CoContra" +  Variance of type argument "Co[T_co@CoContra_Child5]" is incompatible with base class "CoContra" (reportInvalidTypeArguments) +generics_variance.py:163:33 - error: Type "Co[Contra[T_contra@CoToContraToContra]]" cannot be assigned to type variable "T_contra@Contra" +  Variance of type argument "Co[Contra[T_contra@CoToContraToContra]]" is incompatible with base class "Contra" (reportInvalidTypeArguments) +generics_variance.py:167:37 - error: Type "Contra[Contra[T_co@ContraToContraToContra]]" cannot be assigned to type variable "T_contra@Contra" +  Variance of type argument "Contra[Contra[T_co@ContraToContraToContra]]" is incompatible with base class "Contra" (reportInvalidTypeArguments) +generics_variance.py:191:43 - error: Could not specialize type "Contra_TA[T_contra@Contra_TA]" +  Variance of type argument "Co_TA[Contra_TA[T_contra@CoToContraToContra_WithTA]]" is incompatible with "T_contra@Contra_TA" (reportInvalidTypeForm) +generics_variance.py:196:15 - error: Could not specialize type "Contra_TA[T_contra@Contra_TA]" +  Variance of type argument "Contra_TA[Contra_TA[T_co@ContraToContraToContra_WithTA]]" is incompatible with "T_contra@Contra_TA" (reportInvalidTypeForm) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/generics_variance_inference.toml b/conformance/results/pyright/generics_variance_inference.toml new file mode 100644 index 000000000..9a378ad6f --- /dev/null +++ b/conformance/results/pyright/generics_variance_inference.toml @@ -0,0 +1,85 @@ +conformant = "Pass" +output = """ +generics_variance_inference.py:24:33 - error: Type "ClassA[float, int, int]" is not assignable to declared type "ClassA[int, int, int]" +  "ClassA[float, int, int]" is not assignable to "ClassA[int, int, int]" +    Type parameter "T1@ClassA" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_variance_inference.py:25:37 - error: Type "ClassA[float, int, int]" is not assignable to declared type "ClassA[float, float, int]" +  "ClassA[float, int, int]" is not assignable to "ClassA[float, float, int]" +    Type parameter "T2@ClassA" is contravariant, but "int" is not a supertype of "float" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:28:33 - error: Type "ClassA[int, float, float]" is not assignable to declared type "ClassA[int, int, int]" +  "ClassA[int, float, float]" is not assignable to "ClassA[int, int, int]" +    Type parameter "T3@ClassA" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:41:35 - error: Type "ShouldBeCovariant1[float]" is not assignable to declared type "ShouldBeCovariant1[int]" +  "ShouldBeCovariant1[float]" is not assignable to "ShouldBeCovariant1[int]" +    Type parameter "T@ShouldBeCovariant1" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:49:35 - error: Type "ShouldBeCovariant2[float]" is not assignable to declared type "ShouldBeCovariant2[int]" +  "ShouldBeCovariant2[float]" is not assignable to "ShouldBeCovariant2[int]" +    Type parameter "T@ShouldBeCovariant2" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:58:35 - error: Type "ShouldBeCovariant3[float]" is not assignable to declared type "ShouldBeCovariant3[int]" +  "ShouldBeCovariant3[float]" is not assignable to "ShouldBeCovariant3[int]" +    Type parameter "T@ShouldBeCovariant3" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:67:34 - error: Type "ShouldBeCovariant4[float]" is not assignable to declared type "ShouldBeCovariant4[int]" +  "ShouldBeCovariant4[float]" is not assignable to "ShouldBeCovariant4[int]" +    Type parameter "T@ShouldBeCovariant4" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:80:34 - error: Type "ShouldBeCovariant5[float]" is not assignable to declared type "ShouldBeCovariant5[int]" +  "ShouldBeCovariant5[float]" is not assignable to "ShouldBeCovariant5[int]" +    Type parameter "T@ShouldBeCovariant5" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:96:38 - error: Type "ShouldBeInvariant1[int]" is not assignable to declared type "ShouldBeInvariant1[float]" +  "ShouldBeInvariant1[int]" is not assignable to "ShouldBeInvariant1[float]" +    Type parameter "T@ShouldBeInvariant1" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_variance_inference.py:97:36 - error: Type "ShouldBeInvariant1[float]" is not assignable to declared type "ShouldBeInvariant1[int]" +  "ShouldBeInvariant1[float]" is not assignable to "ShouldBeInvariant1[int]" +    Type parameter "T@ShouldBeInvariant1" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_variance_inference.py:111:38 - error: Type "ShouldBeInvariant2[int]" is not assignable to declared type "ShouldBeInvariant2[float]" +  "ShouldBeInvariant2[int]" is not assignable to "ShouldBeInvariant2[float]" +    Type parameter "T@ShouldBeInvariant2" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_variance_inference.py:112:36 - error: Type "ShouldBeInvariant2[float]" is not assignable to declared type "ShouldBeInvariant2[int]" +  "ShouldBeInvariant2[float]" is not assignable to "ShouldBeInvariant2[int]" +    Type parameter "T@ShouldBeInvariant2" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_variance_inference.py:119:43 - error: Type "ShouldBeInvariant3[int, str]" is not assignable to declared type "ShouldBeInvariant3[float, str]" +  "ShouldBeInvariant3[int, str]" is not assignable to "ShouldBeInvariant3[float, str]" +    Type parameter "K@ShouldBeInvariant3" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_variance_inference.py:120:41 - error: Type "ShouldBeInvariant3[float, str]" is not assignable to declared type "ShouldBeInvariant3[int, str]" +  "ShouldBeInvariant3[float, str]" is not assignable to "ShouldBeInvariant3[int, str]" +    Type parameter "K@ShouldBeInvariant3" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_variance_inference.py:121:43 - error: Type "ShouldBeInvariant3[str, int]" is not assignable to declared type "ShouldBeInvariant3[str, float]" +  "ShouldBeInvariant3[str, int]" is not assignable to "ShouldBeInvariant3[str, float]" +    Type parameter "V@ShouldBeInvariant3" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_variance_inference.py:122:41 - error: Type "ShouldBeInvariant3[str, float]" is not assignable to declared type "ShouldBeInvariant3[str, int]" +  "ShouldBeInvariant3[str, float]" is not assignable to "ShouldBeInvariant3[str, int]" +    Type parameter "V@ShouldBeInvariant3" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_variance_inference.py:130:38 - error: Type "ShouldBeInvariant4[int]" is not assignable to declared type "ShouldBeInvariant4[float]" +  "ShouldBeInvariant4[int]" is not assignable to "ShouldBeInvariant4[float]" +    Type parameter "T@ShouldBeInvariant4" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_variance_inference.py:138:38 - error: Type "ShouldBeInvariant5[int]" is not assignable to declared type "ShouldBeInvariant5[float]" +  "ShouldBeInvariant5[int]" is not assignable to "ShouldBeInvariant5[float]" +    Type parameter "T@ShouldBeInvariant5" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_variance_inference.py:149:45 - error: Type "ShouldBeContravariant1[int]" is not assignable to declared type "ShouldBeContravariant1[float]" +  "ShouldBeContravariant1[int]" is not assignable to "ShouldBeContravariant1[float]" +    Type parameter "T@ShouldBeContravariant1" is contravariant, but "int" is not a supertype of "float" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:169:31 - error: Type "ShouldBeInvariant6[float]" is not assignable to declared type "ShouldBeInvariant6[int]" +  "ShouldBeInvariant6[float]" is not assignable to "ShouldBeInvariant6[int]" +    Type parameter "T@ShouldBeInvariant6" is invariant, but "float" is not the same as "int" (reportAssignmentType) +generics_variance_inference.py:170:33 - error: Type "ShouldBeInvariant6[int]" is not assignable to declared type "ShouldBeInvariant6[float]" +  "ShouldBeInvariant6[int]" is not assignable to "ShouldBeInvariant6[float]" +    Type parameter "T@ShouldBeInvariant6" is invariant, but "int" is not the same as "float" (reportAssignmentType) +generics_variance_inference.py:181:31 - error: Type "ShouldBeCovariant6[float]" is not assignable to declared type "ShouldBeCovariant6[int]" +  "ShouldBeCovariant6[float]" is not assignable to "ShouldBeCovariant6[int]" +    Type parameter "T@ShouldBeCovariant6" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +generics_variance_inference.py:194:37 - error: Type "ShouldBeContravariant2[int]" is not assignable to declared type "ShouldBeContravariant2[float]" +  "ShouldBeContravariant2[int]" is not assignable to "ShouldBeContravariant2[float]" +    Type parameter "T@ShouldBeContravariant2" is contravariant, but "int" is not a supertype of "float" +      "float" is not assignable to "int" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/historical_positional.toml b/conformance/results/pyright/historical_positional.toml new file mode 100644 index 000000000..ccb120e5d --- /dev/null +++ b/conformance/results/pyright/historical_positional.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +historical_positional.py:18:8 - error: Expected 1 more positional argument (reportCallIssue) +historical_positional.py:26:16 - error: Position-only parameter not allowed after parameter that is not position-only (reportGeneralTypeIssues) +historical_positional.py:54:26 - error: Position-only parameter not allowed after parameter that is not position-only (reportGeneralTypeIssues) +historical_positional.py:59:10 - error: Expected 1 more positional argument (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/literals_interactions.toml b/conformance/results/pyright/literals_interactions.toml new file mode 100644 index 000000000..1ecbc1f04 --- /dev/null +++ b/conformance/results/pyright/literals_interactions.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +literals_interactions.py:14:5 - error: Index 5 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) +literals_interactions.py:15:5 - error: Index -5 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) +literals_interactions.py:16:5 - error: Index 4 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) +literals_interactions.py:17:5 - error: Index -4 is out of range for type tuple[int, str, list[bool]] (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/literals_literalstring.toml b/conformance/results/pyright/literals_literalstring.toml new file mode 100644 index 000000000..c917941ac --- /dev/null +++ b/conformance/results/pyright/literals_literalstring.toml @@ -0,0 +1,26 @@ +conformant = "Pass" +output = """ +literals_literalstring.py:36:29 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_literalstring.py:37:22 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_literalstring.py:43:23 - error: Type "Literal['two']" is not assignable to declared type "Literal['']" +  "Literal['two']" is not assignable to type "Literal['']" (reportAssignmentType) +literals_literalstring.py:65:25 - error: Type "str" is not assignable to declared type "LiteralString" +  "str" is not assignable to "LiteralString" (reportAssignmentType) +literals_literalstring.py:73:25 - error: Type "Literal[3]" is not assignable to declared type "LiteralString" +  "Literal[3]" is not assignable to "LiteralString" (reportAssignmentType) +literals_literalstring.py:74:25 - error: Type "Literal[b"test"]" is not assignable to declared type "LiteralString" +  "Literal[b"test"]" is not assignable to "LiteralString" (reportAssignmentType) +literals_literalstring.py:119:22 - error: Argument of type "str" cannot be assigned to parameter "s" of type "TLiteral@literal_identity" in function "literal_identity" +  Type "str" is not assignable to type "LiteralString" +    "str" is not assignable to "LiteralString" (reportArgumentType) +literals_literalstring.py:133:51 - error: Argument of type "str" cannot be assigned to parameter "value" of type "T@Container" in function "__init__" +  Type "str" is not assignable to type "LiteralString" +    "str" is not assignable to "LiteralString" (reportArgumentType) +literals_literalstring.py:171:21 - error: Type "list[LiteralString]" is not assignable to declared type "list[str]" +  "list[LiteralString]" is not assignable to "list[str]" +    Type parameter "_T@list" is invariant, but "LiteralString" is not the same as "str" +    Consider switching from "list" to "Sequence" which is covariant (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/literals_parameterizations.toml b/conformance/results/pyright/literals_parameterizations.toml new file mode 100644 index 000000000..bad4ec5e3 --- /dev/null +++ b/conformance/results/pyright/literals_parameterizations.toml @@ -0,0 +1,32 @@ +conformant = "Pass" +output = """ +literals_parameterizations.py:41:15 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +literals_parameterizations.py:41:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:42:15 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +literals_parameterizations.py:42:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:43:15 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +literals_parameterizations.py:43:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:44:15 - error: Unary operator not allowed in type expression (reportInvalidTypeForm) +literals_parameterizations.py:44:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:45:15 - error: Unary operator not allowed in type expression (reportInvalidTypeForm) +literals_parameterizations.py:45:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:46:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:47:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:48:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:49:15 - error: Variable not allowed in type expression (reportInvalidTypeForm) +literals_parameterizations.py:49:15 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:50:16 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:51:16 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:52:16 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:53:16 - error: "..." is not allowed in this context (reportInvalidTypeForm) +literals_parameterizations.py:53:16 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:56:28 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +literals_parameterizations.py:56:28 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:60:4 - error: "Literal" cannot be used in this context without a type argument (reportInvalidTypeForm) +literals_parameterizations.py:61:12 - error: Type arguments for "Literal" must be None, a literal value (int, bool, str, or bytes), or an enum value (reportInvalidTypeForm) +literals_parameterizations.py:65:32 - error: Type "Literal[Color.RED]" is not assignable to declared type "Literal['Color.RED']" +  "Literal[Color.RED]" is not assignable to "Literal['Color.RED']" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/literals_semantics.toml b/conformance/results/pyright/literals_semantics.toml new file mode 100644 index 000000000..7d8b8af5f --- /dev/null +++ b/conformance/results/pyright/literals_semantics.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +output = """ +literals_semantics.py:10:18 - error: Type "Literal[4]" is not assignable to declared type "Literal[3]" +  "Literal[4]" is not assignable to type "Literal[3]" (reportAssignmentType) +literals_semantics.py:24:26 - error: Type "Literal[0]" is not assignable to declared type "Literal[False]" +  "Literal[0]" is not assignable to "Literal[False]" (reportAssignmentType) +literals_semantics.py:25:22 - error: Type "Literal[False]" is not assignable to declared type "Literal[0]" +  "Literal[False]" is not assignable to "Literal[0]" (reportAssignmentType) +literals_semantics.py:33:10 - error: Type "Literal[6, 7, 8]" is not assignable to declared type "Literal[3, 4, 5]" +  Type "Literal[6, 7, 8]" is not assignable to type "Literal[3, 4, 5]" +    Type "Literal[6]" is not assignable to type "Literal[3, 4, 5]" +      "Literal[6]" is not assignable to type "Literal[3]" +      "Literal[6]" is not assignable to type "Literal[4]" +      "Literal[6]" is not assignable to type "Literal[5]" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/namedtuples_define_class.toml b/conformance/results/pyright/namedtuples_define_class.toml new file mode 100644 index 000000000..59049fa48 --- /dev/null +++ b/conformance/results/pyright/namedtuples_define_class.toml @@ -0,0 +1,24 @@ +conformant = "Pass" +output = """ +namedtuples_define_class.py:33:7 - error: Index 3 is out of range for type Point (reportGeneralTypeIssues) +namedtuples_define_class.py:34:7 - error: Index -4 is out of range for type Point (reportGeneralTypeIssues) +namedtuples_define_class.py:45:6 - error: Argument missing for parameter "y" (reportCallIssue) +namedtuples_define_class.py:46:6 - error: Argument missing for parameter "y" (reportCallIssue) +namedtuples_define_class.py:47:15 - error: Argument of type "Literal['']" cannot be assigned to parameter "y" of type "int" in function "__new__" +  "Literal['']" is not assignable to "int" (reportArgumentType) +namedtuples_define_class.py:48:24 - error: Argument of type "Literal[3]" cannot be assigned to parameter "units" of type "str" in function "__new__" +  "Literal[3]" is not assignable to "str" (reportArgumentType) +namedtuples_define_class.py:49:22 - error: Expected 3 positional arguments (reportCallIssue) +namedtuples_define_class.py:50:23 - error: No parameter named "other" (reportCallIssue) +namedtuples_define_class.py:70:20 - error: Expected 2 positional arguments (reportCallIssue) +namedtuples_define_class.py:77:5 - error: Named tuple field names cannot start with an underscore (reportGeneralTypeIssues) +namedtuples_define_class.py:87:5 - error: Fields without default values cannot appear after fields with default values (reportGeneralTypeIssues) +namedtuples_define_class.py:107:5 - error: Cannot override "x" because parent class "Point" is a named tuple (reportIncompatibleVariableOverride) +namedtuples_define_class.py:121:24 - error: Expected 2 positional arguments (reportCallIssue) +namedtuples_define_class.py:140:19 - error: Argument of type "float" cannot be assigned to parameter "value" of type "str" in function "__new__" +  "float" is not assignable to "str" (reportArgumentType) +namedtuples_define_class.py:147:7 - error: Multiple inheritance with NamedTuple is not supported (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/namedtuples_define_functional.toml b/conformance/results/pyright/namedtuples_define_functional.toml new file mode 100644 index 000000000..2c9738f7b --- /dev/null +++ b/conformance/results/pyright/namedtuples_define_functional.toml @@ -0,0 +1,22 @@ +output = """ +namedtuples_define_functional.py:16:8 - error: Argument missing for parameter "y" (reportCallIssue) +namedtuples_define_functional.py:21:8 - error: Arguments missing for parameters "x", "y" (reportCallIssue) +namedtuples_define_functional.py:26:21 - error: Expected 2 positional arguments (reportCallIssue) +namedtuples_define_functional.py:31:8 - error: Argument missing for parameter "y" (reportCallIssue) +namedtuples_define_functional.py:31:18 - error: No parameter named "z" (reportCallIssue) +namedtuples_define_functional.py:36:18 - error: Argument of type "Literal['1']" cannot be assigned to parameter "y" of type "int" in function "__new__" +  "Literal['1']" is not assignable to "int" (reportArgumentType) +namedtuples_define_functional.py:37:21 - error: Expected 2 positional arguments (reportCallIssue) +namedtuples_define_functional.py:42:18 - error: Argument of type "Literal['1']" cannot be assigned to parameter "y" of type "int" in function "__new__" +  "Literal['1']" is not assignable to "int" (reportArgumentType) +namedtuples_define_functional.py:43:17 - error: Argument of type "float" cannot be assigned to parameter "x" of type "int" in function "__new__" +  "float" is not assignable to "int" (reportArgumentType) +namedtuples_define_functional.py:52:31 - error: Names within a named tuple must be unique (reportGeneralTypeIssues) +namedtuples_define_functional.py:53:33 - error: Field names cannot be a keyword (reportGeneralTypeIssues) +namedtuples_define_functional.py:54:33 - error: Field names cannot be a keyword (reportGeneralTypeIssues) +namedtuples_define_functional.py:55:33 - error: Named tuple field names cannot start with an underscore (reportGeneralTypeIssues) +namedtuples_define_functional.py:69:1 - error: Argument missing for parameter "a" (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/namedtuples_type_compat.toml b/conformance/results/pyright/namedtuples_type_compat.toml new file mode 100644 index 000000000..c31192fc8 --- /dev/null +++ b/conformance/results/pyright/namedtuples_type_compat.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +output = """ +namedtuples_type_compat.py:22:23 - error: Type "Point" is not assignable to declared type "tuple[int, int]" +  "Point" is not assignable to "tuple[int, int]" +    Tuple size mismatch; expected 2 but received 3 (reportAssignmentType) +namedtuples_type_compat.py:23:28 - error: Type "Point" is not assignable to declared type "tuple[int, str, str]" +  "Point" is not assignable to "tuple[int, str, str]" +    Tuple entry 2 is incorrect type +      "int" is not assignable to "str" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/namedtuples_usage.toml b/conformance/results/pyright/namedtuples_usage.toml new file mode 100644 index 000000000..76f53162e --- /dev/null +++ b/conformance/results/pyright/namedtuples_usage.toml @@ -0,0 +1,20 @@ +conformant = "Pass" +output = """ +namedtuples_usage.py:34:7 - error: Index 3 is out of range for type Point (reportGeneralTypeIssues) +namedtuples_usage.py:35:7 - error: Index -4 is out of range for type Point (reportGeneralTypeIssues) +namedtuples_usage.py:40:3 - error: Cannot assign to attribute "x" for class "Point" +  Attribute "x" is read-only (reportAttributeAccessIssue) +namedtuples_usage.py:41:1 - error: "__setitem__" method not defined on type "Point" (reportIndexIssue) +namedtuples_usage.py:42:7 - error: Cannot delete attribute "x" for class "Point" +  Attribute "x" is read-only (reportAttributeAccessIssue) +namedtuples_usage.py:43:5 - error: "__delitem__" method not defined on type "Point" (reportIndexIssue) +namedtuples_usage.py:52:1 - error: Expression with type "Point" cannot be assigned to target tuple +  Type "Point" is incompatible with target tuple +    Tuple size mismatch; expected 2 but received 3 (reportAssignmentType) +namedtuples_usage.py:53:1 - error: Expression with type "Point" cannot be assigned to target tuple +  Type "Point" is incompatible with target tuple +    Tuple size mismatch; expected 4 but received 3 (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/narrowing_typeguard.toml b/conformance/results/pyright/narrowing_typeguard.toml new file mode 100644 index 000000000..a363ec8f3 --- /dev/null +++ b/conformance/results/pyright/narrowing_typeguard.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +output = """ +narrowing_typeguard.py:102:9 - error: User-defined type guard functions and methods must have at least one input parameter (reportGeneralTypeIssues) +narrowing_typeguard.py:107:9 - error: User-defined type guard functions and methods must have at least one input parameter (reportGeneralTypeIssues) +narrowing_typeguard.py:128:20 - error: Argument of type "(val: object) -> TypeGuard[int]" cannot be assigned to parameter "f" of type "(object) -> str" in function "takes_callable_str" +  Type "(val: object) -> TypeGuard[int]" is not assignable to type "(object) -> str" +    Function return type "TypeGuard[int]" is incompatible with type "str" +      "TypeGuard[int]" is not assignable to "str" +      "bool" is not assignable to "str" (reportArgumentType) +narrowing_typeguard.py:148:26 - error: Argument of type "(val: object) -> TypeGuard[int]" cannot be assigned to parameter "f" of type "CallableStrProto" in function "takes_callable_str_proto" +  Type "(val: object) -> TypeGuard[int]" is not assignable to type "(val: object) -> str" +    Function return type "TypeGuard[int]" is incompatible with type "str" +      "TypeGuard[int]" is not assignable to "str" +      "bool" is not assignable to "str" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/narrowing_typeis.toml b/conformance/results/pyright/narrowing_typeis.toml new file mode 100644 index 000000000..327ec9f19 --- /dev/null +++ b/conformance/results/pyright/narrowing_typeis.toml @@ -0,0 +1,36 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +narrowing_typeis.py:110:9 - error: User-defined type guard functions and methods must have at least one input parameter (reportGeneralTypeIssues) +narrowing_typeis.py:115:9 - error: User-defined type guard functions and methods must have at least one input parameter (reportGeneralTypeIssues) +narrowing_typeis.py:137:20 - error: Argument of type "(val: object) -> TypeIs[int]" cannot be assigned to parameter "f" of type "(object) -> str" in function "takes_callable_str" +  Type "(val: object) -> TypeIs[int]" is not assignable to type "(object) -> str" +    Function return type "TypeIs[int]" is incompatible with type "str" +      "TypeIs[int]" is not assignable to "str" +      "bool" is not assignable to "str" (reportArgumentType) +narrowing_typeis.py:157:26 - error: Argument of type "(val: object) -> TypeIs[int]" cannot be assigned to parameter "f" of type "CallableStrProto" in function "takes_callable_str_proto" +  Type "(val: object) -> TypeIs[int]" is not assignable to type "(val: object) -> str" +    Function return type "TypeIs[int]" is incompatible with type "str" +      "TypeIs[int]" is not assignable to "str" +      "bool" is not assignable to "str" (reportArgumentType) +narrowing_typeis.py:174:17 - error: Argument of type "(val: object) -> TypeIs[int]" cannot be assigned to parameter "f" of type "(object) -> TypeGuard[int]" in function "takes_typeguard" +  Type "(val: object) -> TypeIs[int]" is not assignable to type "(object) -> TypeGuard[int]" +    Function return type "TypeIs[int]" is incompatible with type "TypeGuard[int]" +      "TypeIs[int]" is not assignable to "TypeGuard[int]" +      "bool" is not assignable to "TypeGuard[int]" (reportArgumentType) +narrowing_typeis.py:175:14 - error: Argument of type "(val: object) -> TypeGuard[int]" cannot be assigned to parameter "f" of type "(object) -> TypeIs[int]" in function "takes_typeis" +  Type "(val: object) -> TypeGuard[int]" is not assignable to type "(object) -> TypeIs[int]" +    Function return type "TypeGuard[int]" is incompatible with type "TypeIs[int]" +      "TypeGuard[int]" is not assignable to "TypeIs[int]" +      "bool" is not assignable to "TypeIs[int]" (reportArgumentType) +narrowing_typeis.py:196:18 - error: Argument of type "(val: object) -> TypeIs[bool]" cannot be assigned to parameter "f" of type "(object) -> TypeIs[int]" in function "takes_int_typeis" +  Type "(val: object) -> TypeIs[bool]" is not assignable to type "(object) -> TypeIs[int]" +    Function return type "TypeIs[bool]" is incompatible with type "TypeIs[int]" +      "TypeIs[bool]" is not assignable to "TypeIs[int]" +        Type parameter "T@TypeIs" is invariant, but "bool" is not the same as "int" +      "bool" is not assignable to "TypeIs[int]" (reportArgumentType) +narrowing_typeis.py:200:27 - error: Return type of TypeIs ("str") is not consistent with value parameter type ("int") (reportGeneralTypeIssues) +narrowing_typeis.py:204:45 - error: Return type of TypeIs ("list[int]") is not consistent with value parameter type ("list[object]") (reportGeneralTypeIssues) +""" diff --git a/conformance/results/pyright/overloads_basic.toml b/conformance/results/pyright/overloads_basic.toml new file mode 100644 index 000000000..14bfb58bf --- /dev/null +++ b/conformance/results/pyright/overloads_basic.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +notes = """ +""" +output = """ +overloads_basic.py:39:1 - error: No overloads for "__getitem__" match the provided arguments (reportCallIssue) +overloads_basic.py:39:1 - error: Argument of type "Literal['']" cannot be assigned to parameter "__s" of type "slice[Any, Any, Any]" in function "__getitem__" +  "Literal['']" is not assignable to "slice[Any, Any, Any]" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/overloads_consistency.toml b/conformance/results/pyright/overloads_consistency.toml new file mode 100644 index 000000000..4568f8d82 --- /dev/null +++ b/conformance/results/pyright/overloads_consistency.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:28:5 - error: Overloaded implementation is not consistent with signature of overload 2 +  Function return type "str" is incompatible with type "int" +    "str" is not assignable to "int" (reportInconsistentOverload) +overloads_consistency.py:44:5 - error: Overloaded implementation is not consistent with signature of overload 2 +  Type "(x: int) -> (int | str)" is not assignable to type "(x: str) -> str" +    Parameter 1: type "str" is incompatible with type "int" +      "str" is not assignable to "int" (reportInconsistentOverload) +""" diff --git a/conformance/results/pyright/overloads_definitions.toml b/conformance/results/pyright/overloads_definitions.toml new file mode 100644 index 000000000..e6118e2a3 --- /dev/null +++ b/conformance/results/pyright/overloads_definitions.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_definitions.py:16:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_definitions.py:28:5 - error: "func2" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_definitions.py:59:9 - error: "not_abstract" is marked as overload, but no implementation is provided (reportNoOverloadImplementation) +overloads_definitions.py:73:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_definitions.py:86:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_definitions.py:124:9 - error: @final decorator should be applied only to the implementation (reportInconsistentOverload) +overloads_definitions.py:139:9 - error: @final decorator should be applied only to the implementation (reportInconsistentOverload) +overloads_definitions.py:144:9 - error: @final decorator should be applied only to the implementation (reportInconsistentOverload) +overloads_definitions.py:186:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +overloads_definitions.py:203:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +overloads_definitions.py:228:9 - error: @override decorator should be applied only to the implementation (reportInconsistentOverload) +overloads_definitions.py:232:9 - error: @override decorator should be applied only to the implementation (reportInconsistentOverload) +""" diff --git a/conformance/results/pyright/overloads_definitions_stub.toml b/conformance/results/pyright/overloads_definitions_stub.toml new file mode 100644 index 000000000..a043c6cf1 --- /dev/null +++ b/conformance/results/pyright/overloads_definitions_stub.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_definitions_stub.pyi:14:5 - error: "func1" is marked as overload, but additional overloads are missing (reportInconsistentOverload) +overloads_definitions_stub.pyi:33:9 - error: Overloads for "func5" use @staticmethod inconsistently (reportInconsistentOverload) +overloads_definitions_stub.pyi:41:9 - error: Overloads for "func6" use @classmethod inconsistently (reportInconsistentOverload) +overloads_definitions_stub.pyi:73:9 - error: Only the first overload should be marked @final (reportInconsistentOverload) +overloads_definitions_stub.pyi:86:9 - error: Only the first overload should be marked @final (reportInconsistentOverload) +overloads_definitions_stub.pyi:111:9 - error: Method "final_method" cannot override final method defined in class "Base" (reportIncompatibleMethodOverride) +overloads_definitions_stub.pyi:122:9 - error: Method "bad_override" is marked as override, but no base method of same name is present (reportGeneralTypeIssues) +overloads_definitions_stub.pyi:147:9 - error: Only the first overload should be marked @override (reportInconsistentOverload) +""" diff --git a/conformance/results/pyright/overloads_evaluation.toml b/conformance/results/pyright/overloads_evaluation.toml new file mode 100644 index 000000000..7b41dc4e1 --- /dev/null +++ b/conformance/results/pyright/overloads_evaluation.toml @@ -0,0 +1,24 @@ +conformant = "Partial" +notes = """ +Does not evaluate Any in some cases where overload is ambiguous. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 281: Unexpected errors ['overloads_evaluation.py:281:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure)'] +""" +output = """ +overloads_evaluation.py:38:1 - error: No overloads for "example1_1" match the provided arguments +  Argument types: () (reportCallIssue) +overloads_evaluation.py:46:15 - error: Argument of type "Literal[1]" cannot be assigned to parameter "y" of type "str" in function "example1_1" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +overloads_evaluation.py:51:12 - error: Argument of type "Literal[1]" cannot be assigned to parameter "x" of type "str" in function "example1_1" +  "Literal[1]" is not assignable to "str" (reportArgumentType) +overloads_evaluation.py:116:5 - error: No overloads for "example2" match the provided arguments (reportCallIssue) +overloads_evaluation.py:116:14 - error: Argument of type "int | str" cannot be assigned to parameter "x" of type "int" in function "example2" +  Type "int | str" is not assignable to type "int" +    "str" is not assignable to "int" (reportArgumentType) +overloads_evaluation.py:116:17 - error: Argument of type "int | str" cannot be assigned to parameter "y" of type "int" in function "example2" +  Type "int | str" is not assignable to type "int" +    "str" is not assignable to "int" (reportArgumentType) +overloads_evaluation.py:281:17 - error: "assert_type" mismatch: expected "Any" but received "list[int]" (reportAssertTypeFailure) +""" diff --git a/conformance/results/pyright/protocols_class_objects.toml b/conformance/results/pyright/protocols_class_objects.toml new file mode 100644 index 000000000..f68c71e6d --- /dev/null +++ b/conformance/results/pyright/protocols_class_objects.toml @@ -0,0 +1,30 @@ +conformant = "Pass" +output = """ +protocols_class_objects.py:29:5 - error: Argument of type "type[Proto]" cannot be assigned to parameter "cls" of type "type[Proto]" in function "fun" +  "Proto" is not a concrete class type and cannot be assigned to type "type[Proto]" (reportArgumentType) +protocols_class_objects.py:34:7 - error: Type "type[Proto]" is not assignable to declared type "type[Proto]" +  "Proto" is not a concrete class type and cannot be assigned to type "type[Proto]" (reportAssignmentType) +protocols_class_objects.py:58:16 - error: Type "type[ConcreteA]" is not assignable to declared type "ProtoA1" +  Type "type[ConcreteA]" is not assignable to type "ConcreteA" +  Type "type[ConcreteA]" is not assignable to type "ConcreteA" +  "method1" is an incompatible type +    Type "(self: ConcreteA, x: int) -> int" is not assignable to type "(x: int) -> int" +      Parameter name mismatch: "x" versus "self" +      Parameter 1: type "int" is incompatible with type "ConcreteA" +        "int" is not assignable to "ConcreteA" +      Extra parameter "x" (reportAssignmentType) +protocols_class_objects.py:74:16 - error: Type "type[ConcreteB]" is not assignable to declared type "ProtoB1" +  "prop1" is an incompatible type +    "property" is not assignable to "int" (reportAssignmentType) +protocols_class_objects.py:104:16 - error: Type "type[ConcreteC1]" is not assignable to declared type "ProtoC1" +  "attr1" is defined as a ClassVar in protocol (reportAssignmentType) +protocols_class_objects.py:106:16 - error: Type "type[ConcreteC2]" is not assignable to declared type "ProtoC1" +  "attr1" is defined as a ClassVar in protocol (reportAssignmentType) +protocols_class_objects.py:107:16 - error: Type "type[ConcreteC2]" is not assignable to declared type "ProtoC2" +  "attr1" must be defined as a ClassVar to be compatible with protocol (reportAssignmentType) +protocols_class_objects.py:108:16 - error: Type "type[ConcreteC3]" is not assignable to declared type "ProtoC1" +  "attr1" is defined as a ClassVar in protocol (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_definition.toml b/conformance/results/pyright/protocols_definition.toml new file mode 100644 index 000000000..b8252b8f7 --- /dev/null +++ b/conformance/results/pyright/protocols_definition.toml @@ -0,0 +1,104 @@ +conformant = "Pass" +output = """ +protocols_definition.py:30:12 - error: Argument of type "list[int]" cannot be assigned to parameter "things" of type "Iterable[SupportsClose]" in function "close_all" +  "Literal[1]" is incompatible with protocol "SupportsClose" +    "close" is not present (reportArgumentType) +protocols_definition.py:67:14 - error: Instance or class variables within a Protocol class must be explicitly declared within the class body (reportGeneralTypeIssues) +protocols_definition.py:114:22 - error: Type "Concrete2_Bad1" is not assignable to declared type "Template2" +  "Concrete2_Bad1" is incompatible with protocol "Template2" +    "val1" is not present (reportAssignmentType) +protocols_definition.py:115:22 - error: Type "Concrete2_Bad2" is not assignable to declared type "Template2" +  "Concrete2_Bad2" is incompatible with protocol "Template2" +    "val1" is invariant because it is mutable +    "val1" is an incompatible type +    "val1" is defined as a ClassVar in protocol +      "Sequence[float]" is not assignable to "Sequence[int]" (reportAssignmentType) +protocols_definition.py:116:22 - error: Type "Concrete2_Bad3" is not assignable to declared type "Template2" +  "Concrete2_Bad3" is incompatible with protocol "Template2" +    "val1" is invariant because it is mutable +    "val1" is an incompatible type +    "val1" is defined as a ClassVar in protocol +      "list[int]" is not assignable to "Sequence[int]" (reportAssignmentType) +protocols_definition.py:117:22 - error: Type "Concrete2_Bad4" is not assignable to declared type "Template2" +  "Concrete2_Bad4" is incompatible with protocol "Template2" +    "val1" is defined as a ClassVar in protocol (reportAssignmentType) +protocols_definition.py:156:22 - error: Type "Concrete3_Bad1" is not assignable to declared type "Template3" +  "Concrete3_Bad1" is incompatible with protocol "Template3" +    "val1" is not present (reportAssignmentType) +protocols_definition.py:157:22 - error: Type "Concrete3_Bad2" is not assignable to declared type "Template3" +  "Concrete3_Bad2" is incompatible with protocol "Template3" +    "val1" is not defined as a ClassVar in protocol (reportAssignmentType) +protocols_definition.py:158:22 - error: Type "Concrete3_Bad3" is not assignable to declared type "Template3" +  "Concrete3_Bad3" is incompatible with protocol "Template3" +    "val1" is invariant because it is mutable +    "val1" is an incompatible type +      "property" is not assignable to "Sequence[int]" (reportAssignmentType) +protocols_definition.py:159:22 - error: Type "Concrete3_Bad4" is not assignable to declared type "Template3" +  "Concrete3_Bad4" is incompatible with protocol "Template3" +    "val1" is invariant because it is mutable +    "val1" is an incompatible type +      "Sequence[float]" is not assignable to "Sequence[int]" (reportAssignmentType) +protocols_definition.py:160:22 - error: Type "Concrete3_Bad5" is not assignable to declared type "Template3" +  "Concrete3_Bad5" is incompatible with protocol "Template3" +    "val1" is invariant because it is mutable +    "val1" is an incompatible type +      "list[int]" is not assignable to "Sequence[int]" (reportAssignmentType) +protocols_definition.py:218:22 - error: Type "Concrete4_Bad1" is not assignable to declared type "Template4" +  "Concrete4_Bad1" is incompatible with protocol "Template4" +    "val1" is an incompatible type +      "MethodType" is not assignable to "Sequence[float]" (reportAssignmentType) +protocols_definition.py:219:22 - error: Type "Concrete4_Bad2" is not assignable to declared type "Template4" +  "Concrete4_Bad2" is incompatible with protocol "Template4" +    "val1" is not present (reportAssignmentType) +protocols_definition.py:276:17 - warning: Static methods should not take a "self" or "cls" parameter (reportSelfClsParameterName) +protocols_definition.py:285:22 - error: Type "Concrete5_Bad1" is not assignable to declared type "Template5" +  "Concrete5_Bad1" is incompatible with protocol "Template5" +    "method1" is an incompatible type +      Type "(a: Unknown, c: Unknown) -> int" is not assignable to type "(a: int, b: int) -> float" +        Parameter name mismatch: "b" versus "c" (reportAssignmentType) +protocols_definition.py:286:22 - error: Type "Concrete5_Bad2" is not assignable to declared type "Template5" +  "Concrete5_Bad2" is incompatible with protocol "Template5" +    "method1" is an incompatible type +      Type "(a: int, c: int) -> int" is not assignable to type "(a: int, b: int) -> float" +        Parameter name mismatch: "b" versus "c" (reportAssignmentType) +protocols_definition.py:287:22 - error: Type "Concrete5_Bad3" is not assignable to declared type "Template5" +  "Concrete5_Bad3" is incompatible with protocol "Template5" +    "method1" is an incompatible type +      Type "(*, a: int, b: int) -> float" is not assignable to type "(a: int, b: int) -> float" +        Function accepts too many positional parameters; expected 0 but received 2 +          Extra parameter "a" +          Extra parameter "b" (reportAssignmentType) +protocols_definition.py:288:22 - error: Type "Concrete5_Bad4" is not assignable to declared type "Template5" +  "Concrete5_Bad4" is incompatible with protocol "Template5" +    "method1" is an incompatible type +      Type "(a: int, b: int, /) -> float" is not assignable to type "(a: int, b: int) -> float" +        Missing keyword parameter "a" +        Missing keyword parameter "b" +          Position-only parameter mismatch; parameter "a" is not position-only +          Position-only parameter mismatch; parameter "b" is not position-only +          Position-only parameter mismatch; expected 2 but received 0 (reportAssignmentType) +protocols_definition.py:289:22 - error: Type "Concrete5_Bad5" is not assignable to declared type "Template5" +  "Concrete5_Bad5" is incompatible with protocol "Template5" +    "method1" is an incompatible type +      Type "(self: Unknown, a: int, b: int) -> float" is not assignable to type "(a: int, b: int) -> float" +        Parameter name mismatch: "a" versus "self" +        Parameter name mismatch: "b" versus "a" +        Extra parameter "b" (reportAssignmentType) +protocols_definition.py:339:22 - error: Type "Concrete6_Bad1" is not assignable to declared type "Template6" +  "Concrete6_Bad1" is incompatible with protocol "Template6" +    "val1" is an incompatible type +      Property setter method is missing +        "Template6" is not assignable to "Concrete6_Bad1" (reportAssignmentType) +protocols_definition.py:340:22 - error: Type "Concrete6_Bad2" is not assignable to declared type "Template6" +  "Concrete6_Bad2" is incompatible with protocol "Template6" +    "val1" is writable in protocol +    "val1" is not read-only in protocol (reportAssignmentType) +protocols_definition.py:341:22 - error: Type "Concrete6_Bad3" is not assignable to declared type "Template6" +  "Concrete6_Bad3" is incompatible with protocol "Template6" +    "val1" is writable in protocol +    "val1" is not read-only in protocol (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" +ignore_errors = ["Static methods should not take a \"self\" or \"cls\" parameter"] diff --git a/conformance/results/pyright/protocols_explicit.toml b/conformance/results/pyright/protocols_explicit.toml new file mode 100644 index 000000000..aad78c2f3 --- /dev/null +++ b/conformance/results/pyright/protocols_explicit.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +output = """ +protocols_explicit.py:27:16 - error: Method "draw" cannot be called because it is abstract and unimplemented (reportAbstractUsage) +protocols_explicit.py:56:32 - error: Cannot assign to attribute "rgb" for class "Point*" +  "str" is not assignable to "int" (reportAttributeAccessIssue) +protocols_explicit.py:60:5 - error: Cannot instantiate abstract class "Point" +  "RGB.intensity" is not implemented +  "RGB.transparency" is not implemented (reportAbstractUsage) +protocols_explicit.py:89:6 - error: Cannot instantiate abstract class "Concrete1" +  "Proto1.cm1" is not implemented (reportAbstractUsage) +protocols_explicit.py:134:6 - error: Cannot instantiate abstract class "Concrete5" +  "Proto5.method1" is not implemented (reportAbstractUsage) +protocols_explicit.py:164:7 - error: Cannot instantiate abstract class "Concrete7A" +  "Proto7.method1" is not implemented (reportAbstractUsage) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_generic.toml b/conformance/results/pyright/protocols_generic.toml new file mode 100644 index 000000000..f19f84dae --- /dev/null +++ b/conformance/results/pyright/protocols_generic.toml @@ -0,0 +1,57 @@ +conformant = "Pass" +output = """ +protocols_generic.py:40:24 - error: Type "Concrete1" is not assignable to declared type "Proto1[int, str]" +  "Concrete1" is incompatible with protocol "Proto1[int, str]" +    "method1" is an incompatible type +      Type "(x: str) -> str" is not assignable to type "(x: S@Proto1) -> S@Proto1" +        Parameter 1: type "S@Proto1" is incompatible with type "str" +          "int" is not assignable to "str" +        Function return type "str" is incompatible with type "S@Proto1" +          Type "int | str" is not assignable to type "int" +    "__iter__" is an incompatible type + ... (reportAssignmentType) +protocols_generic.py:44:30 - error: Only one Generic[...] or Protocol[...] base class allowed (reportGeneralTypeIssues) +protocols_generic.py:56:20 - error: Type "Box[float]" is not assignable to declared type "Box[int]" +  "Box[float]" is not assignable to "Box[int]" +    Type parameter "T_co@Box" is covariant, but "float" is not a subtype of "int" +      "float" is not assignable to "int" (reportAssignmentType) +protocols_generic.py:66:25 - error: Type "Sender[int]" is not assignable to declared type "Sender[float]" +  "Sender[int]" is not assignable to "Sender[float]" +    Type parameter "T_contra@Sender" is contravariant, but "int" is not a supertype of "float" +      "float" is not assignable to "int" (reportAssignmentType) +protocols_generic.py:74:28 - error: Type "AttrProto[int]" is not assignable to declared type "AttrProto[float]" +  "AttrProto[int]" is not assignable to "AttrProto[float]" +    Type parameter "T@AttrProto" is invariant, but "int" is not the same as "float" (reportAssignmentType) +protocols_generic.py:75:26 - error: Type "AttrProto[float]" is not assignable to declared type "AttrProto[int]" +  "AttrProto[float]" is not assignable to "AttrProto[int]" +    Type parameter "T@AttrProto" is invariant, but "float" is not the same as "int" (reportAssignmentType) +protocols_generic.py:145:25 - error: Type "ConcreteHasProperty2" is not assignable to declared type "HasPropertyProto" +  "ConcreteHasProperty2" is incompatible with protocol "HasPropertyProto" +    "m" is an incompatible type +      Type "(item: int, callback: (int) -> str) -> str" is not assignable to type "(item: T@m, callback: (T@m) -> str) -> str" +        Parameter 1: type "T@m" is incompatible with type "int" +          "object*" is not assignable to "int" +        Parameter 2: type "(T@m) -> str" is incompatible with type "(int) -> str" +          Type "(T@m) -> str" is not assignable to type "(int) -> str" (reportAssignmentType) +protocols_generic.py:146:25 - error: Type "ConcreteHasProperty3" is not assignable to declared type "HasPropertyProto" +  "ConcreteHasProperty3" is incompatible with protocol "HasPropertyProto" +    "f" is an incompatible type +      Type "() -> int" is not assignable to type "() -> HasPropertyProto" +        Function return type "int" is incompatible with type "HasPropertyProto" +          "int" is incompatible with protocol "HasPropertyProto" +    "m" is an incompatible type +      Type "(item: int, callback: (int) -> str) -> str" is not assignable to type "(item: T@m, callback: (T@m) -> str) -> str" +        Parameter 1: type "T@m" is incompatible with type "int" + ... (reportAssignmentType) +protocols_generic.py:147:25 - error: Type "ConcreteHasProperty4" is not assignable to declared type "HasPropertyProto" +  "ConcreteHasProperty4" is incompatible with protocol "HasPropertyProto" +    "m" is an incompatible type +      Type "(item: str, callback: (int) -> str) -> str" is not assignable to type "(item: T@m, callback: (T@m) -> str) -> str" +        Parameter 1: type "T@m" is incompatible with type "str" +          "object*" is not assignable to "str" +        Parameter 2: type "(T@m) -> str" is incompatible with type "(int) -> str" +          Type "(T@m) -> str" is not assignable to type "(int) -> str" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_merging.toml b/conformance/results/pyright/protocols_merging.toml new file mode 100644 index 000000000..1baa74a77 --- /dev/null +++ b/conformance/results/pyright/protocols_merging.toml @@ -0,0 +1,19 @@ +conformant = "Pass" +output = """ +protocols_merging.py:52:25 - error: Type "SCConcrete2" is not assignable to declared type "SizedAndClosable1" +  "SCConcrete2" is incompatible with protocol "SizedAndClosable1" +    "__len__" is not present (reportAssignmentType) +protocols_merging.py:53:25 - error: Type "SCConcrete2" is not assignable to declared type "SizedAndClosable2" +  "SCConcrete2" is incompatible with protocol "SizedAndClosable2" +    "__len__" is not present (reportAssignmentType) +protocols_merging.py:54:25 - error: Type "SCConcrete2" is not assignable to declared type "SizedAndClosable3" +  "SCConcrete2" is not assignable to "SizedAndClosable3" (reportAssignmentType) +protocols_merging.py:67:16 - error: Protocol class "BadProto" cannot derive from non-Protocol class "SizedAndClosable3" (reportGeneralTypeIssues) +protocols_merging.py:82:5 - error: Cannot instantiate abstract class "SizedAndClosable4" +  "SizedAndClosable4.close" is not implemented (reportAbstractUsage) +protocols_merging.py:83:24 - error: Type "SCConcrete1" is not assignable to declared type "SizedAndClosable4" +  "SCConcrete1" is not assignable to "SizedAndClosable4" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_modules.toml b/conformance/results/pyright/protocols_modules.toml new file mode 100644 index 000000000..570cd977d --- /dev/null +++ b/conformance/results/pyright/protocols_modules.toml @@ -0,0 +1,17 @@ +conformant = "Pass" +output = """ +protocols_modules.py:26:17 - error: Type "Module("_protocols_modules1")" is not assignable to declared type "Options2" +  "timeout" is invariant because it is mutable +  "timeout" is an incompatible type +    "int" is not assignable to "str" (reportAssignmentType) +protocols_modules.py:48:18 - error: Type "Module("_protocols_modules2")" is not assignable to declared type "Reporter2" +  "on_error" is an incompatible type +    Type "(x: int) -> None" is not assignable to type "(x: int) -> int" +      Function return type "None" is incompatible with type "int" +        "None" is not assignable to "int" (reportAssignmentType) +protocols_modules.py:49:18 - error: Type "Module("_protocols_modules2")" is not assignable to declared type "Reporter3" +  "not_implemented" is not present (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_recursive.toml b/conformance/results/pyright/protocols_recursive.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/protocols_recursive.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_runtime_checkable.toml b/conformance/results/pyright/protocols_runtime_checkable.toml new file mode 100644 index 000000000..06d9aa35c --- /dev/null +++ b/conformance/results/pyright/protocols_runtime_checkable.toml @@ -0,0 +1,20 @@ +conformant = "Pass" +output = """ +protocols_runtime_checkable.py:23:22 - error: Second argument to "isinstance" must be a class or tuple of classes +  Protocol class must be @runtime_checkable to be used with instance and class checks (reportArgumentType) +protocols_runtime_checkable.py:55:22 - error: Data protocols (which include non-method attributes) are not allowed in issubclass calls (reportGeneralTypeIssues) +protocols_runtime_checkable.py:61:22 - error: Data protocols (which include non-method attributes) are not allowed in issubclass calls (reportGeneralTypeIssues) +protocols_runtime_checkable.py:88:19 - error: Class overlaps "Proto3" unsafely and could produce a match at runtime +  Attributes of "Concrete3A" have the same names as the protocol (reportGeneralTypeIssues) +protocols_runtime_checkable.py:91:19 - error: Class overlaps "Proto3" unsafely and could produce a match at runtime +  Attributes of "Concrete3B" have the same names as the protocol (reportGeneralTypeIssues) +protocols_runtime_checkable.py:91:19 - error: Class overlaps "NonDataProtocol" unsafely and could produce a match at runtime +  Attributes of "Concrete3B" have the same names as the protocol (reportGeneralTypeIssues) +protocols_runtime_checkable.py:94:19 - error: Class overlaps "Proto3" unsafely and could produce a match at runtime +  Attributes of "Concrete3A" have the same names as the protocol (reportGeneralTypeIssues) +protocols_runtime_checkable.py:94:19 - error: Class overlaps "NonDataProtocol" unsafely and could produce a match at runtime +  Attributes of "Concrete3A" have the same names as the protocol (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_self.toml b/conformance/results/pyright/protocols_self.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/protocols_self.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_subtyping.toml b/conformance/results/pyright/protocols_subtyping.toml new file mode 100644 index 000000000..84a70f745 --- /dev/null +++ b/conformance/results/pyright/protocols_subtyping.toml @@ -0,0 +1,38 @@ +conformant = "Pass" +output = """ +protocols_subtyping.py:16:6 - error: Cannot instantiate Protocol class "Proto1" (reportAbstractUsage) +protocols_subtyping.py:38:21 - error: Type "Proto2" is not assignable to declared type "Concrete2" +  "Proto2" is not assignable to "Concrete2" (reportAssignmentType) +protocols_subtyping.py:55:18 - error: Type "Proto2" is not assignable to declared type "Proto3" +  "Proto2" is incompatible with protocol "Proto3" +    "method2" is not present (reportAssignmentType) +protocols_subtyping.py:79:30 - error: Type "Proto5[int]" is not assignable to declared type "Proto4[int, float]" +  "Proto5[int]" is incompatible with protocol "Proto4[int, float]" +    "method1" is an incompatible type +      Type "(a: int, b: int) -> tuple[int, int]" is not assignable to type "(a: S@Proto4, b: T@Proto4) -> tuple[S@Proto4, T@Proto4]" +        Parameter 2: type "T@Proto4" is incompatible with type "int" +          "float" is not assignable to "int" (reportAssignmentType) +protocols_subtyping.py:80:25 - error: Type "Proto4[int, int]" is not assignable to declared type "Proto5[float]" +  "Proto4[int, int]" is incompatible with protocol "Proto5[float]" +    "method1" is an incompatible type +      Type "(a: int, b: int) -> tuple[int, int]" is not assignable to type "(a: T@Proto5, b: T@Proto5) -> tuple[T@Proto5, T@Proto5]" +        Parameter 1: type "T@Proto5" is incompatible with type "int" +          "float" is not assignable to "int" +        Parameter 2: type "T@Proto5" is incompatible with type "int" +          "float" is not assignable to "int" (reportAssignmentType) +protocols_subtyping.py:102:30 - error: Type "Proto6[float, float]" is not assignable to declared type "Proto7[int, float]" +  "Proto6[float, float]" is incompatible with protocol "Proto7[int, float]" +    "method1" is an incompatible type +      Type "(a: float) -> Sequence[float]" is not assignable to type "(a: T_contra@Proto7) -> Sequence[S_co@Proto7]" +        Function return type "Sequence[float]" is incompatible with type "Sequence[S_co@Proto7]" +          "Sequence[float]" is not assignable to "Sequence[S_co@Proto7]" (reportAssignmentType) +protocols_subtyping.py:103:33 - error: Type "Proto6[float, float]" is not assignable to declared type "Proto7[float, object]" +  "Proto6[float, float]" is incompatible with protocol "Proto7[float, object]" +    "method1" is an incompatible type +      Type "(a: float) -> Sequence[float]" is not assignable to type "(a: T_contra@Proto7) -> Sequence[S_co@Proto7]" +        Parameter 1: type "T_contra@Proto7" is incompatible with type "float" +          "object" is not assignable to "float" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/protocols_variance.toml b/conformance/results/pyright/protocols_variance.toml new file mode 100644 index 000000000..108fd9d99 --- /dev/null +++ b/conformance/results/pyright/protocols_variance.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +output = """ +protocols_variance.py:21:7 - warning: Type variable "T1" used in generic Protocol "AnotherBox" should be covariant (reportInvalidTypeVarUse) +protocols_variance.py:40:7 - warning: Type variable "T3" used in generic Protocol "Protocol2" should be contravariant (reportInvalidTypeVarUse) +protocols_variance.py:56:7 - warning: Type variable "T1" used in generic Protocol "Protocol4" should be contravariant (reportInvalidTypeVarUse) +protocols_variance.py:61:7 - warning: Type variable "T1_co" used in generic Protocol "Protocol5" should be contravariant (reportInvalidTypeVarUse) +protocols_variance.py:62:22 - error: Covariant type variable cannot be used in parameter type (reportGeneralTypeIssues) +protocols_variance.py:66:7 - warning: Type variable "T1" used in generic Protocol "Protocol6" should be covariant (reportInvalidTypeVarUse) +protocols_variance.py:71:7 - warning: Type variable "T1_contra" used in generic Protocol "Protocol7" should be covariant (reportInvalidTypeVarUse) +protocols_variance.py:72:21 - error: Contravariant type variable cannot be used in return type (reportGeneralTypeIssues) +protocols_variance.py:104:7 - warning: Type variable "T1" used in generic Protocol "Protocol12" should be covariant (reportInvalidTypeVarUse) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/qualifiers_annotated.toml b/conformance/results/pyright/qualifiers_annotated.toml new file mode 100644 index 000000000..0e49034de --- /dev/null +++ b/conformance/results/pyright/qualifiers_annotated.toml @@ -0,0 +1,37 @@ +conformant = "Pass" +output = """ +qualifiers_annotated.py:38:17 - error: List expression not allowed for this type argument (reportInvalidTypeForm) +qualifiers_annotated.py:39:17 - error: Expected class but received "tuple[tuple[type[int], type[str]]]" (reportGeneralTypeIssues) +qualifiers_annotated.py:40:17 - error: List expression not allowed for this type argument (reportInvalidTypeForm) +qualifiers_annotated.py:40:18 - error: Expected class but received "Generator[type[int], None, None]" (reportGeneralTypeIssues) +qualifiers_annotated.py:41:17 - error: Expected class but received "dict[str, Unknown]" (reportGeneralTypeIssues) +qualifiers_annotated.py:41:17 - error: Dictionary expression not allowed in type expression (reportInvalidTypeForm) +qualifiers_annotated.py:41:24 - error: "b" is not defined (reportUndefinedVariable) +qualifiers_annotated.py:42:17 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +qualifiers_annotated.py:43:17 - error: List expression not allowed in type expression +  Use list[T] to indicate a list type or T1 | T2 to indicate a union type (reportInvalidTypeForm) +qualifiers_annotated.py:43:17 - error: Expected class but received "list[type[int]]" (reportGeneralTypeIssues) +qualifiers_annotated.py:43:23 - error: Expected class but received "Literal[0]" (reportGeneralTypeIssues) +qualifiers_annotated.py:44:17 - error: Ternary expression not allowed in type expression (reportInvalidTypeForm) +qualifiers_annotated.py:45:17 - error: "var1" is not defined (reportUndefinedVariable) +qualifiers_annotated.py:46:17 - error: Expected class but received "Literal[True]" (reportGeneralTypeIssues) +qualifiers_annotated.py:47:18 - error: Expected class but received "Literal[1]" (reportGeneralTypeIssues) +qualifiers_annotated.py:48:18 - error: Binary operator not allowed in type expression (reportInvalidTypeForm) +qualifiers_annotated.py:49:18 - error: Type expressions cannot use format string literals (f-strings) (reportGeneralTypeIssues) +qualifiers_annotated.py:59:8 - error: Expected one type argument and one or more annotations for "Annotated" (reportInvalidTypeForm) +qualifiers_annotated.py:71:24 - error: Type "Annotated" is not assignable to declared type "type[Any]" +  "Annotated" is not assignable to "type[Any]" (reportAssignmentType) +qualifiers_annotated.py:72:24 - error: Type "SmallInt" is not assignable to declared type "type[Any]" +  "Annotated" is not assignable to "type[Any]" (reportAssignmentType) +qualifiers_annotated.py:79:7 - error: Argument of type "Annotated" cannot be assigned to parameter "x" of type "type[T@func4]" in function "func4" +  Type "Annotated" is not assignable to type "type[T@func4]" (reportArgumentType) +qualifiers_annotated.py:80:7 - error: Argument of type "SmallInt" cannot be assigned to parameter "x" of type "type[T@func4]" in function "func4" +  Type "Annotated" is not assignable to type "type[T@func4]" (reportArgumentType) +qualifiers_annotated.py:86:1 - error: "Annotated" cannot be instantiated (reportCallIssue) +qualifiers_annotated.py:87:1 - error: Object of type "Annotated" is not callable +  Attribute "__call__" is unknown (reportCallIssue) +qualifiers_annotated.py:88:1 - error: Object of type "Annotated" is not callable (reportCallIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/qualifiers_final_annotation.toml b/conformance/results/pyright/qualifiers_final_annotation.toml new file mode 100644 index 000000000..38f55274a --- /dev/null +++ b/conformance/results/pyright/qualifiers_final_annotation.toml @@ -0,0 +1,45 @@ +conformant = "Pass" +output = """ +qualifiers_final_annotation.py:16:1 - error: "BAD1" is declared Final, but value is not assigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:18:7 - error: Expected a single type argument after "Final" (reportInvalidTypeForm) +qualifiers_final_annotation.py:34:5 - error: "ID2" is declared Final, but value is not assigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:38:5 - error: "ID3" is declared Final, but value is not assigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:54:14 - error: Cannot assign to attribute "ID5" for class "ClassA*" +  Attribute "ID5" cannot be assigned through a class instance because it is a ClassVar +  "ID5" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +qualifiers_final_annotation.py:62:19 - error: "Final" is not allowed in this context (reportInvalidTypeForm) +qualifiers_final_annotation.py:63:19 - error: "Final" is not allowed in this context (reportInvalidTypeForm) +qualifiers_final_annotation.py:63:32 - error: Cannot assign to attribute "id4" for class "ClassA*" +  "Literal[1]" is not assignable to "Final" (reportAttributeAccessIssue) +qualifiers_final_annotation.py:65:14 - error: Cannot assign to attribute "ID7" for class "ClassA*" +  Attribute "ID7" cannot be assigned through a class instance because it is a ClassVar +  "ID7" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +qualifiers_final_annotation.py:67:14 - error: Cannot assign to attribute "ID7" for class "ClassA*" +  Attribute "ID7" cannot be assigned through a class instance because it is a ClassVar +  "ID7" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +qualifiers_final_annotation.py:71:1 - error: "RATE" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:81:8 - error: Cannot assign to attribute "DEFAULT_ID" for class "type[ClassB]" +  "DEFAULT_ID" is declared as Final and cannot be reassigned (reportAttributeAccessIssue) +qualifiers_final_annotation.py:94:5 - error: "BORDER_WIDTH" cannot be redeclared because parent class "ClassC" declares it as Final (reportGeneralTypeIssues) +qualifiers_final_annotation.py:107:22 - error: "Final" is not allowed in this context (reportInvalidTypeForm) +qualifiers_final_annotation.py:108:19 - error: "ClassVar" is not allowed in this context (reportInvalidTypeForm) +qualifiers_final_annotation.py:118:9 - error: "Final" is not allowed in this context (reportInvalidTypeForm) +qualifiers_final_annotation.py:121:14 - error: "Final" is not allowed in this context (reportInvalidTypeForm) +qualifiers_final_annotation.py:134:1 - error: Arguments missing for parameters "x", "y" (reportCallIssue) +qualifiers_final_annotation.py:134:3 - error: No parameter named "a" (reportCallIssue) +qualifiers_final_annotation.py:135:5 - error: Argument of type "Literal['']" cannot be assigned to parameter "x" of type "int" in function "__new__" +  "Literal['']" is not assignable to "int" (reportArgumentType) +qualifiers_final_annotation.py:135:11 - error: Argument of type "Literal['']" cannot be assigned to parameter "y" of type "int" in function "__new__" +  "Literal['']" is not assignable to "int" (reportArgumentType) +qualifiers_final_annotation.py:141:5 - error: "ID1" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:145:5 - error: "x" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:147:10 - error: "x" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:149:9 - error: "x" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:152:30 - error: "x" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:155:9 - error: "x" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:166:1 - error: "TEN" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +qualifiers_final_annotation.py:170:1 - error: "PI" is declared as Final and cannot be reassigned (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/qualifiers_final_decorator.toml b/conformance/results/pyright/qualifiers_final_decorator.toml new file mode 100644 index 000000000..94448af44 --- /dev/null +++ b/conformance/results/pyright/qualifiers_final_decorator.toml @@ -0,0 +1,20 @@ +conformant = "Pass" +output = """ +qualifiers_final_decorator.py:21:16 - error: Base class "Base1" is marked final and cannot be subclassed (reportGeneralTypeIssues) +qualifiers_final_decorator.py:56:9 - error: Method "method1" cannot override final method defined in class "Base2" (reportIncompatibleMethodOverride) +qualifiers_final_decorator.py:60:9 - error: Method "method2" cannot override final method defined in class "Base2" (reportIncompatibleMethodOverride) +qualifiers_final_decorator.py:64:9 - error: Method "method3" cannot override final method defined in class "Base2" (reportIncompatibleMethodOverride) +qualifiers_final_decorator.py:75:9 - error: Method "method4" cannot override final method defined in class "Base2" (reportIncompatibleMethodOverride) +qualifiers_final_decorator.py:86:9 - error: @final decorator should be applied only to the implementation (reportInconsistentOverload) +qualifiers_final_decorator.py:89:9 - error: Method "method" cannot override final method defined in class "Base3" (reportIncompatibleMethodOverride) +qualifiers_final_decorator.py:102:9 - error: Method "method" cannot override final method defined in class "Base4" (reportIncompatibleMethodOverride) +qualifiers_final_decorator.py:118:9 - error: Method "method" cannot override final method defined in class "Base5_2" (reportIncompatibleMethodOverride) +qualifiers_final_decorator.py:118:9 - error: Method "method" overrides class "Base5_2" in an incompatible manner +  Positional parameter count mismatch; base method has 2, but override has 1 +  Parameter 2 mismatch: base parameter "v" is keyword parameter, override parameter is position-only (reportIncompatibleMethodOverride) +qualifiers_final_decorator.py:126:5 - error: Function "func1" cannot be marked @final because it is not a method (reportGeneralTypeIssues) +""" +conformance_automated = "Pass" +errors_diff = """ +""" +ignore_errors = ["reportMissingModuleSource"] diff --git a/conformance/results/pyright/specialtypes_any.toml b/conformance/results/pyright/specialtypes_any.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/specialtypes_any.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/specialtypes_never.toml b/conformance/results/pyright/specialtypes_never.toml new file mode 100644 index 000000000..ad3d6a257 --- /dev/null +++ b/conformance/results/pyright/specialtypes_never.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +output = """ +specialtypes_never.py:19:22 - error: Function with declared return type "NoReturn" cannot return "None" (reportReturnType) +specialtypes_never.py:85:21 - error: Type "list[Never]" is not assignable to declared type "list[int]" +  "list[Never]" is not assignable to "list[int]" +    Type parameter "_T@list" is invariant, but "Never" is not the same as "int" +    Consider switching from "list" to "Sequence" which is covariant (reportAssignmentType) +specialtypes_never.py:104:12 - error: Type "ClassC[Never]" is not assignable to return type "ClassC[U@func10]" +  "ClassC[Never]" is not assignable to "ClassC[U@func10]" +    Type parameter "T@ClassC" is invariant, but "Never" is not the same as "U@func10" (reportReturnType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/specialtypes_none.toml b/conformance/results/pyright/specialtypes_none.toml new file mode 100644 index 000000000..5bfb2d37d --- /dev/null +++ b/conformance/results/pyright/specialtypes_none.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +output = """ +specialtypes_none.py:21:7 - error: Argument of type "type[None]" cannot be assigned to parameter "val1" of type "None" in function "func1" +  Type is not assignable to "None" (reportArgumentType) +specialtypes_none.py:27:19 - error: Type "None" is not assignable to declared type "Iterable[Unknown]" +  "None" is incompatible with protocol "Iterable[Unknown]" +    "__iter__" is not present (reportAssignmentType) +specialtypes_none.py:41:7 - error: Argument of type "None" cannot be assigned to parameter "val1" of type "type[None]" in function "func2" +  Type "None" is not assignable to type "type[None]" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/specialtypes_promotions.toml b/conformance/results/pyright/specialtypes_promotions.toml new file mode 100644 index 000000000..20daf3a3e --- /dev/null +++ b/conformance/results/pyright/specialtypes_promotions.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +specialtypes_promotions.py:13:7 - error: Cannot access attribute "numerator" for class "float" +  Attribute "numerator" is unknown (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/specialtypes_type.toml b/conformance/results/pyright/specialtypes_type.toml new file mode 100644 index 000000000..904c541a0 --- /dev/null +++ b/conformance/results/pyright/specialtypes_type.toml @@ -0,0 +1,26 @@ +conformant = "Pass" +output = """ +specialtypes_type.py:56:7 - error: Argument of type "type[TeamUser]" cannot be assigned to parameter "user_class" of type "type[BasicUser] | type[ProUser]" in function "func4" +  Type "type[TeamUser]" is not assignable to type "type[BasicUser] | type[ProUser]" +    "type[TeamUser]" is not assignable to "type[BasicUser]" +    Type "type[TeamUser]" is not assignable to type "type[BasicUser]" +    "type[TeamUser]" is not assignable to "type[ProUser]" +    Type "type[TeamUser]" is not assignable to type "type[ProUser]" (reportArgumentType) +specialtypes_type.py:70:7 - error: Argument of type "type[Callable]" cannot be assigned to parameter "x" of type "type[T@func5]" in function "func5" (reportArgumentType) +specialtypes_type.py:76:22 - error: Too many type arguments provided for "type"; expected 1 but received 2 (reportInvalidTypeForm) +specialtypes_type.py:117:7 - error: Cannot access attribute "unknown" for class "type[object]" +  Attribute "unknown" is unknown (reportAttributeAccessIssue) +specialtypes_type.py:120:7 - error: Cannot access attribute "unknown" for class "type[object]" +  Attribute "unknown" is unknown (reportAttributeAccessIssue) +specialtypes_type.py:143:5 - error: Cannot access attribute "unknown" for class "TA1" +  Attribute "unknown" is unknown (reportAttributeAccessIssue) +specialtypes_type.py:144:5 - error: Cannot access attribute "unknown" for class "TA2" +  Attribute "unknown" is unknown (reportAttributeAccessIssue) +specialtypes_type.py:145:5 - error: Cannot access attribute "unknown" for class "TA3" +  Attribute "unknown" is unknown (reportAttributeAccessIssue) +specialtypes_type.py:146:5 - error: Cannot access attribute "unknown" for class "TA4" +  Attribute "unknown" is unknown (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/tuples_type_compat.toml b/conformance/results/pyright/tuples_type_compat.toml new file mode 100644 index 000000000..72058759b --- /dev/null +++ b/conformance/results/pyright/tuples_type_compat.toml @@ -0,0 +1,57 @@ +conformant = "Pass" +output = """ +tuples_type_compat.py:15:27 - error: Type "tuple[float, complex]" is not assignable to declared type "tuple[int, int]" +  "tuple[float, complex]" is not assignable to "tuple[int, int]" +    Tuple entry 1 is incorrect type +      "float" is not assignable to "int" (reportAssignmentType) +tuples_type_compat.py:29:10 - error: Type "tuple[int, ...]" is not assignable to declared type "tuple[int, *tuple[int, ...]]" +  "tuple[int, ...]" is not assignable to "tuple[int, *tuple[int, ...]]" +    Tuple size mismatch; expected 1 or more but received indeterminate (reportAssignmentType) +tuples_type_compat.py:32:10 - error: Type "tuple[int, *tuple[int, ...]]" is not assignable to declared type "tuple[int]" +  "tuple[int, *tuple[int, ...]]" is not assignable to "tuple[int]" +    Tuple size mismatch; expected 1 but received indeterminate (reportAssignmentType) +tuples_type_compat.py:33:10 - error: Type "tuple[int, ...]" is not assignable to declared type "tuple[int]" +  "tuple[int, ...]" is not assignable to "tuple[int]" +    Tuple size mismatch; expected 1 but received indeterminate (reportAssignmentType) +tuples_type_compat.py:43:22 - error: Type "tuple[int, ...]" is not assignable to declared type "tuple[int]" +  "tuple[int, ...]" is not assignable to "tuple[int]" +    Tuple size mismatch; expected 1 but received indeterminate (reportAssignmentType) +tuples_type_compat.py:62:26 - error: Type "tuple[int, ...]" is not assignable to declared type "tuple[int, int]" +  "tuple[int, ...]" is not assignable to "tuple[int, int]" +    Tuple size mismatch; expected 2 but received indeterminate (reportAssignmentType) +tuples_type_compat.py:76:21 - error: "assert_type" mismatch: expected "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" but received "tuple[int]" (reportAssertTypeFailure) +tuples_type_compat.py:81:21 - error: "assert_type" mismatch: expected "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" but received "tuple[str, str] | tuple[int, int]" (reportAssertTypeFailure) +tuples_type_compat.py:86:21 - error: "assert_type" mismatch: expected "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" but received "tuple[int, str, int]" (reportAssertTypeFailure) +tuples_type_compat.py:102:25 - error: "assert_type" mismatch: expected "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" but received "tuple[int]" (reportAssertTypeFailure) +tuples_type_compat.py:107:25 - error: "assert_type" mismatch: expected "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" but received "tuple[str, str] | tuple[int, int]" (reportAssertTypeFailure) +tuples_type_compat.py:112:25 - error: "assert_type" mismatch: expected "tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]" but received "tuple[int, str, int]" (reportAssertTypeFailure) +tuples_type_compat.py:127:25 - error: "assert_type" mismatch: expected "tuple[int | str, int | str]" but received "tuple[int | str, str]" (reportAssertTypeFailure) +tuples_type_compat.py:130:25 - error: "assert_type" mismatch: expected "tuple[int | str, int | str]" but received "tuple[int | str, int]" (reportAssertTypeFailure) +tuples_type_compat.py:157:6 - error: Type "tuple[Literal[1], Literal[''], Literal['']]" is not assignable to declared type "tuple[int, str]" +  "tuple[Literal[1], Literal[''], Literal['']]" is not assignable to "tuple[int, str]" +    Tuple size mismatch; expected 2 but received 3 (reportAssignmentType) +tuples_type_compat.py:162:10 - error: Type "tuple[Literal[1], Literal[1], Literal['']]" is not assignable to declared type "tuple[int, *tuple[str, ...]]" +  "Literal[1]" is not assignable to "str" (reportAssignmentType) +tuples_type_compat.py:163:14 - error: Type "tuple[Literal[1], Literal[''], Literal[1]]" is not assignable to declared type "tuple[int, *tuple[str, ...]]" +  "Literal[1]" is not assignable to "str" (reportAssignmentType) +tuples_type_compat.py:169:14 - error: Type "tuple[Literal[1], Literal[''], Literal['']]" is not assignable to declared type "tuple[int, *tuple[str, ...], int]" +  "Literal['']" is not assignable to "int" (reportAssignmentType) +tuples_type_compat.py:170:18 - error: Type "tuple[Literal[1], Literal[''], Literal[''], float]" is not assignable to declared type "tuple[int, *tuple[str, ...], int]" +  "float" is not assignable to "int" (reportAssignmentType) +tuples_type_compat.py:175:7 - error: Type "tuple[Literal[1], Literal[''], Literal[1]]" is not assignable to declared type "tuple[*tuple[str, ...], int]" +  "Literal[1]" is not assignable to "str" (reportAssignmentType) +tuples_type_compat.py:176:15 - error: Type "tuple[Literal[''], Literal[''], float]" is not assignable to declared type "tuple[*tuple[str, ...], int]" +  "float" is not assignable to "int" (reportAssignmentType) +tuples_type_compat.py:181:40 - error: Type "tuple[str, str]" is not assignable to declared type "tuple[str, str, int]" +  "tuple[str, str]" is not assignable to "tuple[str, str, int]" +    Tuple size mismatch; expected 3 but received 2 (reportAssignmentType) +tuples_type_compat.py:184:50 - error: Type "tuple[str, str]" is not assignable to declared type "tuple[str, str, str, *tuple[str, ...]]" +  "tuple[str, str]" is not assignable to "tuple[str, str, str, *tuple[str, ...]]" +    Tuple size mismatch; expected 3 or more but received 2 (reportAssignmentType) +tuples_type_compat.py:188:50 - error: Type "tuple[str, str]" is not assignable to declared type "tuple[*tuple[str, ...], str, str, str]" +  "tuple[str, str]" is not assignable to "tuple[*tuple[str, ...], str, str, str]" +    Tuple size mismatch; expected 3 or more but received 2 (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/tuples_type_form.toml b/conformance/results/pyright/tuples_type_form.toml new file mode 100644 index 000000000..ca0d3792f --- /dev/null +++ b/conformance/results/pyright/tuples_type_form.toml @@ -0,0 +1,25 @@ +conformant = "Pass" +output = """ +tuples_type_form.py:12:6 - error: Type "tuple[Literal[1], Literal[2]]" is not assignable to declared type "tuple[int]" +  "tuple[Literal[1], Literal[2]]" is not assignable to "tuple[int]" +    Tuple size mismatch; expected 1 but received 2 (reportAssignmentType) +tuples_type_form.py:14:6 - error: Type "tuple[Literal[1]]" is not assignable to declared type "tuple[int, int]" +  "tuple[Literal[1]]" is not assignable to "tuple[int, int]" +    Tuple size mismatch; expected 2 but received 1 (reportAssignmentType) +tuples_type_form.py:15:10 - error: Type "tuple[Literal[1], Literal['']]" is not assignable to declared type "tuple[int, int]" +  "Literal['']" is not assignable to "int" (reportAssignmentType) +tuples_type_form.py:25:7 - error: Type "tuple[Literal[1]]" is not assignable to declared type "tuple[()]" +  "tuple[Literal[1]]" is not assignable to "tuple[()]" +    Tuple size mismatch; expected 0 but received 1 (reportAssignmentType) +tuples_type_form.py:36:17 - error: Type "tuple[Literal[1], Literal[2], Literal[3], Literal['']]" is not assignable to declared type "tuple[int, ...]" +  "Literal['']" is not assignable to "int" (reportAssignmentType) +tuples_type_form.py:40:22 - error: "..." is allowed only as the second of two arguments (reportInvalidTypeForm) +tuples_type_form.py:41:12 - error: "..." is allowed only as the second of two arguments (reportInvalidTypeForm) +tuples_type_form.py:42:12 - error: "..." is allowed only as the second of two arguments (reportInvalidTypeForm) +tuples_type_form.py:43:17 - error: "..." is allowed only as the second of two arguments (reportInvalidTypeForm) +tuples_type_form.py:44:25 - error: "..." cannot be used with an unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +tuples_type_form.py:45:30 - error: "..." cannot be used with an unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/tuples_unpacked.toml b/conformance/results/pyright/tuples_unpacked.toml new file mode 100644 index 000000000..d0bf64302 --- /dev/null +++ b/conformance/results/pyright/tuples_unpacked.toml @@ -0,0 +1,11 @@ +conformant = "Pass" +output = """ +tuples_unpacked.py:40:30 - error: Type argument list can have at most one unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +tuples_unpacked.py:41:43 - error: Type argument list can have at most one unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +tuples_unpacked.py:51:34 - error: Type argument list can have at most one unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +tuples_unpacked.py:59:37 - error: Type argument list can have at most one unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +tuples_unpacked.py:61:50 - error: Type argument list can have at most one unpacked TypeVarTuple or tuple (reportInvalidTypeForm) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_alt_syntax.toml b/conformance/results/pyright/typeddicts_alt_syntax.toml new file mode 100644 index 000000000..8137309ea --- /dev/null +++ b/conformance/results/pyright/typeddicts_alt_syntax.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +output = """ +typeddicts_alt_syntax.py:23:17 - error: Expected dict or keyword parameter as second parameter (reportArgumentType) +typeddicts_alt_syntax.py:27:45 - error: Expected string literal for dictionary entry name (reportGeneralTypeIssues) +typeddicts_alt_syntax.py:31:1 - error: TypedDict must be assigned to a variable named "WrongName" (reportGeneralTypeIssues) +typeddicts_alt_syntax.py:35:78 - error: Extra TypedDict arguments not supported (reportCallIssue) +typeddicts_alt_syntax.py:45:43 - error: Type "dict[str, str]" is not assignable to declared type "Movie2" +  "Literal['']" is not assignable to "int" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_class_syntax.toml b/conformance/results/pyright/typeddicts_class_syntax.toml new file mode 100644 index 000000000..e77eac4c6 --- /dev/null +++ b/conformance/results/pyright/typeddicts_class_syntax.toml @@ -0,0 +1,20 @@ +conformant = "Partial" +notes = """ +Does not support version-conditional items in TypedDict definitions. +""" +output = """ +typeddicts_class_syntax.py:30:5 - error: TypedDict classes can contain only type annotations (reportGeneralTypeIssues) +typeddicts_class_syntax.py:34:5 - error: TypedDict classes can contain only type annotations (reportGeneralTypeIssues) +typeddicts_class_syntax.py:39:5 - error: TypedDict classes can contain only type annotations (reportGeneralTypeIssues) +typeddicts_class_syntax.py:45:32 - error: TypedDict does not support __init_subclass__ parameter "metaclass" (reportGeneralTypeIssues) +typeddicts_class_syntax.py:50:32 - error: TypedDict does not support __init_subclass__ parameter "other" (reportGeneralTypeIssues) +typeddicts_class_syntax.py:58:5 - error: TypedDict classes can contain only type annotations (reportGeneralTypeIssues) +typeddicts_class_syntax.py:60:5 - error: TypedDict classes can contain only type annotations (reportGeneralTypeIssues) +typeddicts_class_syntax.py:65:1 - error: No overloads for "__init__" match the provided arguments +  Argument types: (Literal[1], Literal[2], Literal[3]) (reportCallIssue) +""" +conformance_automated = "Fail" +errors_diff = """ +Line 58: Unexpected errors ['typeddicts_class_syntax.py:58:5 - error: TypedDict classes can contain only type annotations (reportGeneralTypeIssues)'] +Line 60: Unexpected errors ['typeddicts_class_syntax.py:60:5 - error: TypedDict classes can contain only type annotations (reportGeneralTypeIssues)'] +""" diff --git a/conformance/results/pyright/typeddicts_extra_items.toml b/conformance/results/pyright/typeddicts_extra_items.toml new file mode 100644 index 000000000..0eb4f16ba --- /dev/null +++ b/conformance/results/pyright/typeddicts_extra_items.toml @@ -0,0 +1,66 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_extra_items.py:15:45 - error: Type "dict[str, str | int]" is not assignable to declared type "Movie" +  "Literal[1982]" is not assignable to "bool" (reportAssignmentType) +typeddicts_extra_items.py:22:55 - error: Type "dict[str, str | int]" is not assignable to declared type "MovieFunctional" +  "Literal[1982]" is not assignable to "bool" (reportAssignmentType) +typeddicts_extra_items.py:39:54 - error: Type "dict[str, str | None]" is not assignable to declared type "InheritedMovie" +  "None" is not assignable to "int" (reportAssignmentType) +typeddicts_extra_items.py:49:35 - error: Expected "closed" parameter to have a value of True or False (reportGeneralTypeIssues) +typeddicts_extra_items.py:67:40 - error: Base class "ClosedBase" is not an open TypedDict; closed=False is not allowed (reportGeneralTypeIssues) +typeddicts_extra_items.py:73:44 - error: Base class "ExtraItemsBase" is not an open TypedDict; closed=False is not allowed (reportGeneralTypeIssues) +typeddicts_extra_items.py:91:7 - error: Base class "MovieA" is a TypedDict that limits the type of extra items to type "Never" +  Cannot add item "age" (reportIncompatibleVariableOverride) +typeddicts_extra_items.py:94:7 - error: Base class "MovieB" is a TypedDict that limits the type of extra items to type "Never" +  Cannot add item "age" (reportIncompatibleVariableOverride) +typeddicts_extra_items.py:109:7 - error: Base class "ExtraItemsBase" is a TypedDict that limits the type of extra items to type "int" +  Cannot add item "extra_items" with type "Never" (reportIncompatibleVariableOverride) +typeddicts_extra_items.py:114:50 - error: "Required" is not allowed in this context (reportInvalidTypeForm) +typeddicts_extra_items.py:117:57 - error: "NotRequired" is not allowed in this context (reportInvalidTypeForm) +typeddicts_extra_items.py:128:9 - error: Could not delete item in TypedDict +  "name" is a required key and cannot be deleted (reportGeneralTypeIssues) +typeddicts_extra_items.py:143:48 - error: No parameter named "year" (reportCallIssue) +typeddicts_extra_items.py:174:7 - error: Base class "Parent" is a TypedDict that limits the type of extra items to type "int | None" +  Cannot add item "extra_items" with type "int" (reportIncompatibleVariableOverride) +typeddicts_extra_items.py:184:7 - error: Base class "MovieBase2" is a TypedDict that limits the type of extra items to type "int | None" +  Cannot add item "year" because it must be NotRequired (reportIncompatibleVariableOverride) +typeddicts_extra_items.py:187:7 - error: Base class "MovieBase2" is a TypedDict that limits the type of extra items to type "int | None" +  Cannot add item "year" with type "int" (reportIncompatibleVariableOverride) +typeddicts_extra_items.py:196:7 - error: Base class "BookBase" is a TypedDict that limits the type of extra items to type "int | None" +  Cannot add item "publisher" with type "str" (reportIncompatibleVariableOverride) +typeddicts_extra_items.py:215:22 - error: Type "MovieDetails" is not assignable to declared type "MovieBase2" +  Type of "year" is incompatible with type of "extra_items" in "MovieBase2" +    Type "int" is not assignable to type "int | None" +      "int" is not assignable to "None" (reportAssignmentType) +typeddicts_extra_items.py:222:22 - error: Type "MovieWithYear2" is not assignable to declared type "MovieBase2" +  "year" is not required in "MovieBase2" (reportAssignmentType) +typeddicts_extra_items.py:242:19 - error: Type "MovieDetails5" is not assignable to declared type "MovieSI" +  Type of "actors" is incompatible with type of "extra_items" in "MovieSI" +    Type "list[str]" is not assignable to type "str | int" +      "list[str]" is not assignable to "str" +      "list[str]" is not assignable to "int" (reportAssignmentType) +typeddicts_extra_items.py:256:13 - error: Type "MovieExtraStr" is not assignable to declared type "MovieExtraInt" +  Type of "extra_items" is incompatible with type of "extra_items" in "MovieExtraStr" +    "str" is not assignable to "int" (reportAssignmentType) +typeddicts_extra_items.py:257:13 - error: Type "MovieExtraInt" is not assignable to declared type "MovieExtraStr" +  Type of "extra_items" is incompatible with type of "extra_items" in "MovieExtraInt" +    "int" is not assignable to "str" (reportAssignmentType) +typeddicts_extra_items.py:268:14 - error: Type "MovieNotClosed" is not assignable to declared type "MovieExtraInt" +  Type of "extra_items" is incompatible with type of "extra_items" in "MovieNotClosed" +    "object" is not assignable to "int" (reportAssignmentType) +typeddicts_extra_items.py:278:1 - error: No overloads for "__init__" match the provided arguments +  Argument types: (Literal['No Country for Old Men'], Literal[2007]) (reportCallIssue) +typeddicts_extra_items.py:285:52 - error: Argument of type "Literal['English']" cannot be assigned to parameter "language" of type "int" in function "__init__" +  "Literal['English']" is not assignable to "int" (reportArgumentType) +typeddicts_extra_items.py:293:1 - error: No overloads for "__init__" match the provided arguments +  Argument types: (Literal['No Country for Old Men'], Literal[2007]) (reportCallIssue) +typeddicts_extra_items.py:303:34 - error: Type "MovieExtraInt" is not assignable to declared type "Mapping[str, int]" +  "Mapping[str, str | int]" is not assignable to "Mapping[str, int]" +    Type parameter "_VT_co@Mapping" is covariant, but "str | int" is not a subtype of "int" +      Type "str | int" is not assignable to type "int" +        "str" is not assignable to "int" (reportAssignmentType) +typeddicts_extra_items.py:352:25 - error: Type "dict[str, int]" is not assignable to declared type "IntDict" +  "dict[str, int]" is not assignable to "IntDict" (reportAssignmentType) +""" diff --git a/conformance/results/pyright/typeddicts_final.toml b/conformance/results/pyright/typeddicts_final.toml new file mode 100644 index 000000000..5e485e856 --- /dev/null +++ b/conformance/results/pyright/typeddicts_final.toml @@ -0,0 +1,6 @@ +conformant = "Pass" +output = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_inheritance.toml b/conformance/results/pyright/typeddicts_inheritance.toml new file mode 100644 index 000000000..a827b06ca --- /dev/null +++ b/conformance/results/pyright/typeddicts_inheritance.toml @@ -0,0 +1,12 @@ +conformant = "Pass" +output = """ +typeddicts_inheritance.py:44:7 - error: All base classes for TypedDict classes must also be TypedDict classes +  Class "NonTypedDict" is not a TypedDict (reportGeneralTypeIssues) +typeddicts_inheritance.py:55:4 - error: "x" overrides symbol of same name in class "X1" +  Variable is mutable so its type is invariant +    Override type "int" is not the same as base type "str" (reportIncompatibleVariableOverride) +typeddicts_inheritance.py:65:7 - error: Base classes for class "XYZ2" define variable "x" in incompatible way (reportIncompatibleVariableOverride) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_operations.toml b/conformance/results/pyright/typeddicts_operations.toml new file mode 100644 index 000000000..9eae54519 --- /dev/null +++ b/conformance/results/pyright/typeddicts_operations.toml @@ -0,0 +1,27 @@ +conformant = "Pass" +output = """ +typeddicts_operations.py:22:1 - error: Could not assign item in TypedDict +  "Literal[1982]" is not assignable to "str" (reportGeneralTypeIssues) +typeddicts_operations.py:23:1 - error: Could not assign item in TypedDict +  "Literal['']" is not assignable to "int" (reportGeneralTypeIssues) +typeddicts_operations.py:24:1 - error: Could not assign item in TypedDict +  "other" is not a defined key in "Movie" (reportGeneralTypeIssues) +typeddicts_operations.py:26:7 - error: Could not access item in TypedDict +  "other" is not a defined key in "Movie" (reportGeneralTypeIssues) +typeddicts_operations.py:28:9 - error: Type "dict[str, str]" is not assignable to declared type "Movie" +  "year" is required in "Movie" (reportAssignmentType) +typeddicts_operations.py:29:42 - error: Type "dict[str, str | float]" is not assignable to declared type "Movie" +  "float" is not assignable to "int" (reportAssignmentType) +typeddicts_operations.py:32:36 - error: Type "dict[str, str | int]" is not assignable to declared type "Movie" +  "other" is an undefined item in type "Movie" (reportAssignmentType) +typeddicts_operations.py:37:20 - error: Type "dict[str, str | int]" is not assignable to declared type "Movie" (reportAssignmentType) +typeddicts_operations.py:47:7 - error: Cannot access attribute "clear" for class "Movie" +  Attribute "clear" is unknown (reportAttributeAccessIssue) +typeddicts_operations.py:49:5 - error: Could not delete item in TypedDict +  "name" is a required key and cannot be deleted (reportGeneralTypeIssues) +typeddicts_operations.py:62:16 - error: Cannot access attribute "clear" for class "MovieOptional" +  Attribute "clear" is unknown (reportAttributeAccessIssue) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_readonly.toml b/conformance/results/pyright/typeddicts_readonly.toml new file mode 100644 index 000000000..f35a90e86 --- /dev/null +++ b/conformance/results/pyright/typeddicts_readonly.toml @@ -0,0 +1,18 @@ +conformant = "Pass" +output = """ +typeddicts_readonly.py:24:1 - error: Could not assign item in TypedDict +  "members" is a read-only key in "Band" (reportTypedDictNotRequiredAccess) +typeddicts_readonly.py:36:1 - error: Could not assign item in TypedDict +  "members" is a read-only key in "Band2" (reportTypedDictNotRequiredAccess) +typeddicts_readonly.py:50:1 - error: Could not assign item in TypedDict +  "title" is a read-only key in "Movie1" (reportTypedDictNotRequiredAccess) +typeddicts_readonly.py:51:1 - error: Could not assign item in TypedDict +  "year" is a read-only key in "Movie1" (reportTypedDictNotRequiredAccess) +typeddicts_readonly.py:60:1 - error: Could not assign item in TypedDict +  "title" is a read-only key in "Movie2" (reportTypedDictNotRequiredAccess) +typeddicts_readonly.py:61:1 - error: Could not assign item in TypedDict +  "year" is a read-only key in "Movie2" (reportTypedDictNotRequiredAccess) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_readonly_consistency.toml b/conformance/results/pyright/typeddicts_readonly_consistency.toml new file mode 100644 index 000000000..bb53197cc --- /dev/null +++ b/conformance/results/pyright/typeddicts_readonly_consistency.toml @@ -0,0 +1,22 @@ +conformant = "Pass" +output = """ +typeddicts_readonly_consistency.py:37:14 - error: Type "A1" is not assignable to declared type "B1" +  "y" is missing from "A1" (reportAssignmentType) +typeddicts_readonly_consistency.py:38:14 - error: Type "C1" is not assignable to declared type "B1" +  "y" is not read-only in "B1" (reportAssignmentType) +typeddicts_readonly_consistency.py:40:14 - error: Type "A1" is not assignable to declared type "C1" +  "y" is an incompatible type +    "object" is not assignable to "str" (reportAssignmentType) +typeddicts_readonly_consistency.py:81:14 - error: Type "A2" is not assignable to declared type "B2" +  "x" is not read-only in "B2" (reportAssignmentType) +typeddicts_readonly_consistency.py:82:14 - error: Type "C2" is not assignable to declared type "B2" +  "x" is not required in "B2" (reportAssignmentType) +typeddicts_readonly_consistency.py:84:14 - error: Type "A2" is not assignable to declared type "C2" +  "x" is required in "C2" +  "x" is not read-only in "C2" (reportAssignmentType) +typeddicts_readonly_consistency.py:85:14 - error: Type "B2" is not assignable to declared type "C2" +  "x" is required in "C2" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_readonly_inheritance.toml b/conformance/results/pyright/typeddicts_readonly_inheritance.toml new file mode 100644 index 000000000..d9e432b89 --- /dev/null +++ b/conformance/results/pyright/typeddicts_readonly_inheritance.toml @@ -0,0 +1,25 @@ +conformant = "Pass" +output = """ +typeddicts_readonly_inheritance.py:36:1 - error: Could not assign item in TypedDict +  "name" is a read-only key in "Album2" (reportTypedDictNotRequiredAccess) +typeddicts_readonly_inheritance.py:50:5 - error: "alt" overrides symbol of same name in class "AlbumCollection" +  "list[str]" is not assignable to "list[str | int]" +    Type parameter "_T@list" is invariant, but "str" is not the same as "str | int" +    Consider switching from "list" to "Sequence" which is covariant (reportIncompatibleVariableOverride) +typeddicts_readonly_inheritance.py:65:19 - error: Type "dict[Any, Any]" is not assignable to declared type "RequiredName" +  "name" is required in "RequiredName" (reportAssignmentType) +typeddicts_readonly_inheritance.py:82:1 - error: Could not assign item in TypedDict +  "Literal[3]" is not assignable to "str" (reportGeneralTypeIssues) +typeddicts_readonly_inheritance.py:83:15 - error: Type "dict[str, int]" is not assignable to declared type "User" +  "Literal[3]" is not assignable to "str" (reportAssignmentType) +typeddicts_readonly_inheritance.py:84:5 - error: Type "dict[Any, Any]" is not assignable to declared type "User" +  "ident" is required in "User" (reportAssignmentType) +typeddicts_readonly_inheritance.py:94:5 - error: TypedDict item "a" cannot be redefined as ReadOnly (reportGeneralTypeIssues) +typeddicts_readonly_inheritance.py:98:5 - error: TypedDict item "a" cannot be redefined as NotRequired (reportGeneralTypeIssues) +typeddicts_readonly_inheritance.py:106:5 - error: TypedDict item "c" cannot be redefined as NotRequired (reportGeneralTypeIssues) +typeddicts_readonly_inheritance.py:119:7 - error: Base classes for class "TD_A" define variable "x" in incompatible way (reportIncompatibleVariableOverride) +typeddicts_readonly_inheritance.py:132:7 - error: TypedDict item "x" cannot be redefined as NotRequired (reportIncompatibleVariableOverride) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_readonly_kwargs.toml b/conformance/results/pyright/typeddicts_readonly_kwargs.toml new file mode 100644 index 000000000..4a2e2d220 --- /dev/null +++ b/conformance/results/pyright/typeddicts_readonly_kwargs.toml @@ -0,0 +1,8 @@ +conformant = "Pass" +output = """ +typeddicts_readonly_kwargs.py:33:5 - error: Could not assign item in TypedDict +  "key1" is a read-only key in "ReadOnlyArgs" (reportTypedDictNotRequiredAccess) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_readonly_update.toml b/conformance/results/pyright/typeddicts_readonly_update.toml new file mode 100644 index 000000000..650894094 --- /dev/null +++ b/conformance/results/pyright/typeddicts_readonly_update.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +typeddicts_readonly_update.py:23:1 - error: No overloads for "update" match the provided arguments (reportCallIssue) +typeddicts_readonly_update.py:23:11 - error: Argument of type "A" cannot be assigned to parameter "__m" of type "Partial[A]" in function "update" +  "x" is an incompatible type +    Type "int" is not assignable to type "Never" (reportArgumentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_required.toml b/conformance/results/pyright/typeddicts_required.toml new file mode 100644 index 000000000..b612b7c02 --- /dev/null +++ b/conformance/results/pyright/typeddicts_required.toml @@ -0,0 +1,10 @@ +conformant = "Pass" +output = """ +typeddicts_required.py:12:8 - error: "Required" is not allowed in this context (reportInvalidTypeForm) +typeddicts_required.py:16:8 - error: "NotRequired" is not allowed in this context (reportInvalidTypeForm) +typeddicts_required.py:59:8 - error: "Required" is not allowed in this context (reportInvalidTypeForm) +typeddicts_required.py:60:8 - error: "Required" is not allowed in this context (reportInvalidTypeForm) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_type_consistency.toml b/conformance/results/pyright/typeddicts_type_consistency.toml new file mode 100644 index 000000000..328b8b937 --- /dev/null +++ b/conformance/results/pyright/typeddicts_type_consistency.toml @@ -0,0 +1,28 @@ +conformant = "Pass" +output = """ +typeddicts_type_consistency.py:21:10 - error: Type "B1" is not assignable to declared type "A1" +  "x" is an incompatible type +    Type "int" is not assignable to type "int | None" +      "int" is not assignable to "None" (reportAssignmentType) +typeddicts_type_consistency.py:38:10 - error: Type "B2" is not assignable to declared type "A2" +  "x" is not required in "A2" (reportAssignmentType) +typeddicts_type_consistency.py:65:6 - error: Type "A3" is not assignable to declared type "B3" +  "y" is missing from "A3" (reportAssignmentType) +typeddicts_type_consistency.py:69:21 - error: Type "dict[str, int]" is not assignable to declared type "A3" +  "y" is an undefined item in type "A3" (reportAssignmentType) +typeddicts_type_consistency.py:76:22 - error: Type "B3" is not assignable to declared type "dict[str, int]" +  "B3" is not assignable to "dict[str, int]" (reportAssignmentType) +typeddicts_type_consistency.py:77:25 - error: Type "B3" is not assignable to declared type "dict[str, object]" +  "B3" is not assignable to "dict[str, object]" (reportAssignmentType) +typeddicts_type_consistency.py:78:22 - error: Type "B3" is not assignable to declared type "dict[Any, Any]" +  "B3" is not assignable to "dict[Any, Any]" (reportAssignmentType) +typeddicts_type_consistency.py:82:25 - error: Type "B3" is not assignable to declared type "Mapping[str, int]" +  "B3" is not assignable to "Mapping[str, int]" +    Type parameter "_VT_co@Mapping" is covariant, but "object" is not a subtype of "int" +      "object" is not assignable to "int" (reportAssignmentType) +typeddicts_type_consistency.py:126:56 - error: Type "dict[str, dict[str, dict[str, int]]]" is not assignable to declared type "Outer1" +  "Literal[1]" is not assignable to "str" (reportAssignmentType) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeddicts_usage.toml b/conformance/results/pyright/typeddicts_usage.toml new file mode 100644 index 000000000..ce846f1dd --- /dev/null +++ b/conformance/results/pyright/typeddicts_usage.toml @@ -0,0 +1,15 @@ +conformant = "Pass" +output = """ +typeddicts_usage.py:23:1 - error: Could not assign item in TypedDict +  "director" is not a defined key in "Movie" (reportGeneralTypeIssues) +typeddicts_usage.py:24:1 - error: Could not assign item in TypedDict +  "Literal['1982']" is not assignable to "int" (reportGeneralTypeIssues) +typeddicts_usage.py:28:18 - error: Type "dict[str, str | int]" is not assignable to declared type "Movie" +  "title" is an undefined item in type "Movie" (reportAssignmentType) +typeddicts_usage.py:35:22 - error: Second argument to "isinstance" must be a class or tuple of classes +  TypedDict class not allowed for instance or class checks (reportArgumentType) +typeddicts_usage.py:40:24 - error: "TypedDict" cannot be used in this context (reportInvalidTypeForm) +""" +conformance_automated = "Pass" +errors_diff = """ +""" diff --git a/conformance/results/pyright/typeforms_typeform.toml b/conformance/results/pyright/typeforms_typeform.toml new file mode 100644 index 000000000..4b75ffd63 --- /dev/null +++ b/conformance/results/pyright/typeforms_typeform.toml @@ -0,0 +1,80 @@ +conformant = "Unsupported" +conformance_automated = "Fail" +errors_diff = """ +Line 70: Expected 1 errors +Line 71: Expected 1 errors +Line 72: Expected 1 errors +Line 74: Expected 1 errors +Line 15: Unexpected errors ['typeforms_typeform.py:15:29 - error: Type "UnionType" is not assignable to declared type "TypeForm[str | None]"'] +Line 17: Unexpected errors ['typeforms_typeform.py:17:29 - error: Type "None" is not assignable to declared type "TypeForm[str | None]"'] +Line 19: Unexpected errors ['typeforms_typeform.py:19:29 - error: Type "UnionType" is not assignable to declared type "TypeForm[str | None]"'] +Line 20: Unexpected errors ['typeforms_typeform.py:20:29 - error: Type "Literal[\\'str | None\\']" is not assignable to declared type "TypeForm[str | None]"'] +Line 21: Unexpected errors ['typeforms_typeform.py:21:29 - error: Type "type[Any]" is not assignable to declared type "TypeForm[str | None]"'] +Line 29: Unexpected errors ['typeforms_typeform.py:29:24 - error: Type "UnionType" is not assignable to declared type "TypeForm[Any]"'] +Line 32: Unexpected errors ['typeforms_typeform.py:32:13 - error: "assert_type" mismatch: expected "TypeForm[str]" but received "type[str]" (reportAssertTypeFailure)'] +Line 47: Unexpected errors ['typeforms_typeform.py:47:38 - error: Type "UnionType" is not assignable to declared type "TypeForm[str | None]"'] +Line 49: Unexpected errors ['typeforms_typeform.py:49:33 - error: Type "type[list[int]]" is not assignable to declared type "GenericAlias"'] +Line 52: Unexpected errors ['typeforms_typeform.py:52:27 - error: Type "Annotated" is not assignable to declared type "TypeForm[int | str]"'] +Line 53: Unexpected errors ['typeforms_typeform.py:53:26 - error: Type "Literal[\\'set[str]\\']" is not assignable to declared type "TypeForm[set[str]]"'] +Line 58: Unexpected errors ['typeforms_typeform.py:58:7 - error: Argument of type "Literal[\\'int\\']" cannot be assigned to parameter "x" of type "TypeForm[Unknown]" in function "func1"'] +Line 81: Unexpected errors ['typeforms_typeform.py:81:13 - error: "assert_type" mismatch: expected "TypeForm[str | None]" but received "UnionType" (reportAssertTypeFailure)'] +Line 84: Unexpected errors ['typeforms_typeform.py:84:13 - error: "assert_type" mismatch: expected "TypeForm[list[int]]" but received "type[list[int]]" (reportAssertTypeFailure)'] +""" +output = """ +typeforms_typeform.py:15:29 - error: Type "UnionType" is not assignable to declared type "TypeForm[str | None]" +  "UnionType" is not assignable to "TypeForm[str | None]" (reportAssignmentType) +typeforms_typeform.py:17:29 - error: Type "None" is not assignable to declared type "TypeForm[str | None]" +  "None" is not assignable to "TypeForm[str | None]" (reportAssignmentType) +typeforms_typeform.py:19:29 - error: Type "UnionType" is not assignable to declared type "TypeForm[str | None]" +  "UnionType" is not assignable to "TypeForm[str | None]" (reportAssignmentType) +typeforms_typeform.py:20:29 - error: Type "Literal['str | None']" is not assignable to declared type "TypeForm[str | None]" +  "Literal['str | None']" is not assignable to "TypeForm[str | None]" (reportAssignmentType) +typeforms_typeform.py:21:29 - error: Type "type[Any]" is not assignable to declared type "TypeForm[str | None]" +  Type "Any" is not assignable to type "str | None" +    "Any" is not assignable to "str" +    "Any" is not assignable to "None" (reportAssignmentType) +typeforms_typeform.py:23:30 - error: Type "UnionType" is not assignable to declared type "TypeForm[str | None]" +  "UnionType" is not assignable to "TypeForm[str | None]" (reportAssignmentType) +typeforms_typeform.py:24:30 - error: Type "type[list[str | None]]" is not assignable to declared type "TypeForm[str | None]" +  Type "list[str | None]" is not assignable to type "str | None" +    "list[str | None]" is not assignable to "str" +    "list[str | None]" is not assignable to "None" (reportAssignmentType) +typeforms_typeform.py:29:24 - error: Type "UnionType" is not assignable to declared type "TypeForm[Any]" +  "UnionType" is not assignable to "TypeForm[Any]" (reportAssignmentType) +typeforms_typeform.py:32:13 - error: "assert_type" mismatch: expected "TypeForm[str]" but received "type[str]" (reportAssertTypeFailure) +typeforms_typeform.py:47:38 - error: Type "UnionType" is not assignable to declared type "TypeForm[str | None]" +  "UnionType" is not assignable to "TypeForm[str | None]" (reportAssignmentType) +typeforms_typeform.py:49:33 - error: Type "type[list[int]]" is not assignable to declared type "GenericAlias" +  Type "type[list[int]]" is not assignable to type "GenericAlias" (reportAssignmentType) +typeforms_typeform.py:52:27 - error: Type "Annotated" is not assignable to declared type "TypeForm[int | str]" +  "Annotated" is not assignable to "TypeForm[int | str]" (reportAssignmentType) +typeforms_typeform.py:53:26 - error: Type "Literal['set[str]']" is not assignable to declared type "TypeForm[set[str]]" +  "Literal['set[str]']" is not assignable to "TypeForm[set[str]]" (reportAssignmentType) +typeforms_typeform.py:58:7 - error: Argument of type "Literal['int']" cannot be assigned to parameter "x" of type "TypeForm[Unknown]" in function "func1" +  "Literal['int']" is not assignable to "TypeForm[Unknown]" (reportArgumentType) +typeforms_typeform.py:59:7 - error: Argument of type "Literal['not a type']" cannot be assigned to parameter "x" of type "TypeForm[Unknown]" in function "func1" +  "Literal['not a type']" is not assignable to "TypeForm[Unknown]" (reportArgumentType) +typeforms_typeform.py:67:18 - error: Type "tuple[()]" is not assignable to declared type "TypeForm[Unknown]" +  "tuple[()]" is not assignable to "TypeForm[Unknown]" (reportAssignmentType) +typeforms_typeform.py:68:18 - error: Type "tuple[Literal[1], Literal[2]]" is not assignable to declared type "TypeForm[Unknown]" +  "tuple[Literal[1], Literal[2]]" is not assignable to "TypeForm[Unknown]" (reportAssignmentType) +typeforms_typeform.py:69:18 - error: Type "Literal[1]" is not assignable to declared type "TypeForm[Unknown]" +  "Literal[1]" is not assignable to "TypeForm[Unknown]" (reportAssignmentType) +typeforms_typeform.py:73:25 - error: Type variable "Ts" has no meaning in this context (reportGeneralTypeIssues) +typeforms_typeform.py:75:18 - error: Type "Literal['int + str']" is not assignable to declared type "TypeForm[Unknown]" +  "Literal['int + str']" is not assignable to "TypeForm[Unknown]" (reportAssignmentType) +typeforms_typeform.py:81:13 - error: "assert_type" mismatch: expected "TypeForm[str | None]" but received "UnionType" (reportAssertTypeFailure) +typeforms_typeform.py:84:13 - error: "assert_type" mismatch: expected "TypeForm[list[int]]" but received "type[list[int]]" (reportAssertTypeFailure) +typeforms_typeform.py:86:16 - error: type() call should not be used in type expression +  Use type[T] instead (reportInvalidTypeForm) +typeforms_typeform.py:86:16 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +typeforms_typeform.py:88:15 - error: type() call should not be used in type expression +  Use type[T] instead (reportInvalidTypeForm) +typeforms_typeform.py:88:15 - error: Call expression not allowed in type expression (reportInvalidTypeForm) +typeforms_typeform.py:98:21 - error: Type "TypeForm[int]" is not assignable to declared type "TypeForm[str]" +  "TypeForm[int]" is not assignable to "TypeForm[str]" +    Type parameter "T@TypeForm" is covariant, but "int" is not a subtype of "str" +      "int" is not assignable to "str" (reportAssignmentType) +typeforms_typeform.py:108:21 - error: Type "type[int]" is not assignable to declared type "TypeForm[str]" +  "int" is not assignable to "str" (reportAssignmentType) +""" diff --git a/conformance/results/pyright/version.toml b/conformance/results/pyright/version.toml new file mode 100644 index 000000000..a759b9a37 --- /dev/null +++ b/conformance/results/pyright/version.toml @@ -0,0 +1 @@ +version = "pyright 1.1.409" diff --git a/conformance/results/results.html b/conformance/results/results.html new file mode 100644 index 000000000..0edd52cc8 --- /dev/null +++ b/conformance/results/results.html @@ -0,0 +1,1397 @@ + + + + + + + Type System Test Results + + + + +
+
+

Python Type System Conformance Test Results

+

+ While spec conformance is important for the ecosystem, we don't recommend using it + as the primary basis for choosing a type checker. It is not representative + of many of the things users typically care about. +

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
mypy 2.1.0
+
pyright 1.1.409
+
zuban 0.7.2
+
pyrefly 1.0.0
+
pycroscope 0.4.0
+
ty 0.0.40
+
+Type annotations +
     annotations_coroutinesPassPassPassPassPassPass
     annotations_forward_refs
Partial

Does not report error for a forward reference that is not enclosed in quotes.

Does not report error for use of quoted type with "|" operator (runtime error).

Incorrectly generates error for quoted type defined in class scope.

Pass
Partial

Incorrectly generates error for quoted type defined in class scope.

Partial

Types in quotes incorrectly refer to shadowing class member.

Does not reject some type forms that require quotes.

Partial

Fails to reject "x" | int annotations that fail at runtime.

Rejects some valid quoted annotations.

Partial

Resolves references in type annotations as referring to end-of-scope types (https://discuss.python.org/t/annotation-string-references-in-class-scope-in-conformance-tests/105439, https://github.com/python/typing/pull/2144)

     annotations_generators
Partial

Does not report incompatible Generator type in `yield from` statement.

PassPassPassPassPass
     annotations_methods
Pass*

Type evaluation differs from other type checkers because of ambiguity in the spec related to method bindings.

Pass*

Type evaluation differs from other type checkers because of ambiguity in the spec related to method bindings.

PassPassPassPass
     annotations_typeexprPassPassPassPass
Partial

Fails to reject various weird annotations

Pass
+Type forms +
     typeforms_typeform
Partial

Does not support assigning Union and GenericAlias objects to their runtime types.

UnsupportedPass
Partial

Does not allow assigning a TypeForm to types.GenericAlias.

Does not allow passing a forward reference to a function accepting a TypeForm.

Partial

Fails to reject various weird annotations

Pass
+Special types in annotations +
     specialtypes_anyPassPassPassPassPassPass
     specialtypes_neverPassPassPassPass
Partial

Does not enforce invariance in some contexts

Does not allow Any to be assigned to Never

Pass
     specialtypes_nonePassPassPassPassPassPass
     specialtypes_promotionsPassPassPassPassPassPass
     specialtypes_type
Partial

Does not treat `type` same as `type[Any]` for assert_type.

Does not allow access to unknown attributes from object of type `type[Any]`.

PassPassPassPass
Partial

Allows arbitrary attributes to be accessed on `TA` where `TA = typing.Type[typing.Any]` or `TA = type[typing.Any]`.

Treats `type` equivalently to `type[object]` rather than `type[typing.Any]`.

+Generics +
     generics_base_class
Partial

Does not detect inconsistent type variable ordering.

PassPassPassPassPass
     generics_basicPassPassPassPassPass
Partial

Incorrectly allows constrained type variables to be solved to a union of their constraints.

     generics_defaults
Partial

Does not detect a TypeVar with a default used after a TypeVarTuple.

Does not fully support defaults on TypeVarTuple and ParamSpec.

PassPassPass
Partial

Numerous issues; does not support TypeVarTuple and ParamSpec defaults.

Partial

Does not forbid a `TypeVar` immediately following a `TypeVarTuple` in a parameter list from having a default.

Does not support `TypeVarTuple`.

     generics_defaults_referential
Partial

Does not correctly handle defaults referencing other TypeVars.

PassPassPass
Partial

Fails to apply default specializations in some cases.

Pass
     generics_defaults_specialization
Partial

Does not correctly resolve defaults when classes are used directly.

Partial

Allows incorrect assignment to type[].

PassPassPass
Partial

Does not reject subscription of an already-specialized generic class.

     generics_paramspec_basicPassPassPassPassPassPass
     generics_paramspec_componentsPassPassPassPassPass
Partial

Incorrectly allows using `*args: P.args` and `**kwargs: P.kwargs` when `P` has not been put into scope by any other parameter annotation or enclosing scope.

     generics_paramspec_semanticsPass
Pass*

Constraint solver doesn't find common type for two signatures captured by a single ParamSpec (allowed).

PassPassPassPass
     generics_paramspec_specializationPassPassPassPassPassPass
     generics_scopingPassPassPass
Partial

Does not implement several scoping checks/restrictions for generics

Pass
Partial

Does not reject `list[T]()` in the global scope, where `T` is an unbound type variable.

Does nto reject `alias: TypeAlias = list[T]` in the body scope of a class generic over a type variable `T`.

     generics_self_advanced
Partial

Does not infer the type of an unannotated `self` parameter to be type `Self`.

Does not retain `Self` when calling method that returns `Self`.

Does not infer the type of an unannotated `cls` parameter to be type `type[Self]`.

Does not retain `Self` when accessing attribute through `type[Self]`.

PassPass
Pass*

Treats attributes not initialized on the class as instance-only

PassPass
     generics_self_attributesPassPassPassPassPassPass
     generics_self_basicPassPassPass
Partial

Return annotation of Self allows returning the concrete instance of the current class.

PassPass
     generics_self_protocolsPassPassPassPassPassPass
     generics_self_usagePassPassPass
Partial

Does not implement some restrictions on where Self can be used

Pass
Partial

Does not reject `Self` used in a return annotation when `self` is annotated using another type variable.

     generics_syntax_compatibilityPassPassPassPassPassUnsupported
     generics_syntax_declarationsPassPassPassPassPassPass
     generics_syntax_infer_variance
Unsupported

Type parameter syntax not yet supported.

PassPassPassPassPass
     generics_syntax_scoping
Partial

Does not following runtime scoping rules for type parameters in all cases.

PassPassPass
Partial

Misses some details of scoping rules.

Pass
     generics_type_erasure
Partial

Infers Node[Never] instead of Node[Any] when argument is not provided.

False negative on instance attribute access on type(node).

PassPassPassPass
Partial

Does not reject access of generic instance variable from the class object.

     generics_typevartuple_args
Partial

Does not enforce that tuples captured by TypeVarTuple are of the same length.

Partial

Does not correctly solve TypeVarTuple with heterogeneous bounds.

PassPassPass
Partial

Supports PEP-646 unpacked tuples but not TypeVarTuple.

     generics_typevartuple_basic
Partial

Does not enforce that tuples captured by TypeVarTuple are same length.

Partial

Does not correctly solve TypeVarTuple with heterogeneous bounds.

PassPassPassUnsupported
     generics_typevartuple_callablePassPassPassPassPassUnsupported
     generics_typevartuple_concatPassPassPassPassPassUnsupported
     generics_typevartuple_overloadsPassPassPassPassPassPass
     generics_typevartuple_specialization
Partial

Incorrectly specializes generic alias that includes a TypeVar and TypeVarTuple if no type arguments are provided.

Rejects specialization of generic type alias defined as a tuple containing a TypeVar.

PassPassPassPassUnsupported
     generics_typevartuple_unpackPassPassPassPassPassUnsupported
     generics_upper_bound
Partial

Does not reject use of type variable within an upper bound.

PassPassPassPassPass
     generics_variance
Partial

Does not reject use of class-scoped TypeVar used in a base class when variance is incompatible.

PassPassPassPassPass
     generics_variance_inferencePassPassPassPassPassPass
+Type qualifiers +
     qualifiers_annotated
Partial

Does not allow ClassVar to be nested within Annotated.

Does not allow Final to be nested within Annotated.

Does not allow Required and NotRequired to be nested within Annotated.

Does not reject type[T] compatibility for type alias defined with Annotated.

Does not reject call of type alias defined with Annotated.

PassPassPass
Partial

Fails to reject various weird annotations.

False positive on lambda in Annotated.

Pass
     qualifiers_final_annotation
Partial

Does not treat use of Final name as if it was replaced by the literal in NamedTuple definition.

Does not allow conditional assignment of Final instance variable in __init__ method.

Does not allow redefinition of private class variable that is marked Final in parent class.

Does not report modification of local Final variable via "for" statement.

PassPassPassPassPass
     qualifiers_final_decoratorPassPassPassPassPassPass
+Class type compatibility +
     classes_classvar
Partial

Internal error if TypeVarTuple is used in ClassVar.

Does not reject use of ParamSpec in ClassVar.

Rejects ClassVar nested in Annotated.

Does not reject use of ClassVar in TypeAlias definition.

PassPassPassPassPass
     classes_overridePassPassPassPassPassPass
+Type aliases +
     aliases_explicit
Partial

Does not reject specialization of type alias that has already been implicitly specialized.

PassPassPassPass
Partial

Does not emit a diagnostic if a type alias to a union, in which every element of the union is implicitly specialized with `Unknown`, is invalidly specialized again.

     aliases_implicitPassPassPass
Partial

Does not reject invalid syntax in implicit type aliases.

Partial

Fails to handle various weird annotations.

Various bugs with resolving generic aliases.

Partial

Does not reject variables with `Any` or `Unknown` types when used as implicit type aliases.

Falls short on full syntactic validation of type aliases.

     aliases_newtype
Partial

`NewType`s are incorrectly considered to be classes.

PassPassPassPassPass
     aliases_recursivePassPassPassPassPassUnsupported
     aliases_type_statementPassPassPassPass
Partial

Fails to reject various weird annotations.

Partial

Does not reject circular definitions of type aliases.

Does not support `type` statements generic over `TypeVarTuple`s.

     aliases_typealiastype
Partial

Incorrectly rejects some recursive type aliases using TypeAliasType.

Incorrectly rejects the use of a class-scoped TypeVar in a TypeAliasType definition.

PassPassPass
Partial

Rejects valid ParamSpec specialization.

Partial

Does not reject specializing a type parameter in a generic type alias with a type inconsistent with the parameter's upper bound.

Does not reject declaring a type alias with a type variable that is not in scope.

Does not reject declaring a type alias with a non-literal tuple passed to the `type_params` parameter.

Does not reject cyclically defined type aliases.

     aliases_variancePassPassPassPassPassPass
+Literals +
     literals_interactions
Pass*

Does not narrow `str` or `LiteralString` types to `Literal` string types via equality or containment checks.

PassPassPassPass
Pass*

Deliberately does not allow `str` to be narrowed to literal string types through equality or containment checks due to the possibility of `str` subclasses that could have unexpected equality semantics.

     literals_literalstring
Unsupported

Support for `LiteralString` is not implemented.

PassPassPassPassPass
     literals_parameterizations
Partial

Does not reject tuple within Literal.

PassPassPass
Partial

Fails to reject various invalid literal parameterizations.

Pass
     literals_semanticsPassPassPassPassPassPass
+Protocols +
     protocols_class_objectsPassPassPassPass
Partial

Abstract type[Proto] still allows protocol class objects in some paths.

Unsupported

`type[Proto]` is not yet supported.

`ClassVar` protocol members are not yet supported.

`@property` protocol members are only partially supported.

A class object `C` is only considered to inhabit a protocol type with a method member `f` if `f` exists as an attribute on the metaclass of `C`.

     protocols_definition
Partial

Does not detect protocol mismatch if concrete method is missing annotations.

Does not detect protocol mismatch if concrete method's parameters are position-only.

PassPassPassPass
Partial

Does not reject implicit instance attributes in `Protocol` methods.

Does not support `ClassVar` protocol members.

Incorrectly considers `ClassVar` attributes on concrete classes as satisfying non-`ClassVar` attribute members on protocols.

Only has partial support for `@property` protocol members.

     protocols_explicit
Pass*

Does not report unimplemented attributes for class that explicitly derives from protocol until it is instantiated.

PassPassPassPass
Unsupported

Allows implicitly abstract protocol methods to be called via `super()` on a protocol subclass.

Allows instantiation of abstract subclasses of protocol classes.

     protocols_genericPassPassPassPass
Partial

Fails to reject duplicate generic/protocol bases.

Treats global object as a literal.

Partial

Only partially supports `@property` protocol members.

     protocols_mergingPassPassPassPassPass
Partial

Does not reject attempted instantiation of abstract subclasses of protocols.

     protocols_modulesPassPassPassPassPass
Partial

Never considers a module as satisfying a protocol with a method member due to the fact that the method will never exist on the class `types.ModuleType`, only on a given specific module instance.

     protocols_recursivePassPassPassPassPass
Partial

Fails to solve a type variable involving a recursive generic protocol.

     protocols_runtime_checkable
Partial

Does not report unsafe overlap for runtime_checkable protocol.

PassPassPassPass
Partial

Does not reject `isinstance()` or `issubclass()` calls against runtime-checkable protocols where there is an unsafe overlap between the type of the first argument and the protocol.

     protocols_selfPassPassPassPassPassPass
     protocols_subtypingPassPassPassPassPassPass
     protocols_variancePassPassPassPassPassUnsupported
+Callables +
     callables_annotation
Partial

Incorrectly treats "*args: T, **kwargs: T" as "..." when T is specialized to Any.

Does not treat "*args: Any, **kargs: Any" as "..." when separated by keyword parameter.

PassPass
Partial

Parameter names are lost when resolving ParamSpec

Pass
Partial

Infers a callback protocol as being a gradual type if the callback has signature `__call__[T](self, *args: T, **kwargs: T)` and `T` has been explicitly specialized to `Any`.

     callables_kwargs
Partial

Allows callable without kwargs to be assigned to callable with unpacked kwargs

PassPassPassPassPass
     callables_protocolPassPassPassPassPassPass
     callables_subtypingPassPassPassPassPassPass
+Constructors +
     constructors_call_init
Partial

Does not report errors during binding to self parameter of __init__ method.

Does not reject use of class-scoped type variables in annotation of self parameter in __init__ method.

PassPassPassPass
Partial

Does not reject invalid argument types to an inherited constructor in a specialized subclass of a generic superclass.

Does not reject class-scoped type variables used in the `self` annotation.

     constructors_call_metaclass
Unsupported

Does not honor metaclass __call__ method when evaluating constructor call.

Does not skip evaluation of __new__ and __init__ if custom metaclass call returns non-class.

PassPassPassPassPass
     constructors_call_new
Partial

Does not support __new__ return type that is not a subclass of the class being constructed.

Does not skip evaluation of __init__ based on __new__ return type.

Does not report errors during binding to cls parameter of __new__ method.

PassPassPassPassPass
     constructors_call_type
Partial

Does not validate call to custom metaclass __call__ method through type[T].

PassPassPassPass
Partial

Has overly lenient handling of calls to `type[T]` if `T` is a type variable without an upper bound.

     constructors_callable
Partial

Does not generate a union type for __new__ and __init__ when converting class to callable.

Does not ignore __init__ based on __new__ return type when converting class to callable.

Does not support __new__ return type that is different from class being constructed.

PassPass
Partial

Converting constructor to callable does not preserve class-scoped type params.

Pass
Partial

Does not include `__init__` when `__new__` returns `Self`.

Does not respect `NoReturn` return type on metaclass `__call__`.

Does not ignore `__init__` when `__new__` returns `Any`.

Unions overload return types.

     constructors_consistency
Pass*

Does not report inconsistency between __new__ and __init__ (optional).

PassPassPassPassPass
+Overloads +
     overloads_basicPassPassPassPass
Partial

Does less literal promotion than the test asks for.

Pass
     overloads_consistencyPassPassPassPassPassPass
     overloads_definitions
Partial

Allows @override to be on all overloads and implementation, instead of just implementation.

PassPassPassPassPass
     overloads_definitions_stub
Partial

Allows @override to appear in a stub file not on the first overload.

PassPassPass
Unsupported

Does not support checking stubs.

Pass
     overloads_evaluation
Partial

Does not expand boolean arguments to Literal[True] and Literal[False].

Does not expand enum arguments to literal variants.

Does not expand tuple arguments to possible combinations.

Does not evaluate Any in some cases where overload is ambiguous.

Evaluates Any in some cases where overload is not ambiguous.

Partial

Does not evaluate Any in some cases where overload is ambiguous.

PassPassPassPass
+Exceptions +
     exceptions_context_managersPassPassPass
Partial

Some error suppressing context managers are not detected

PassUnsupported
+Dataclasses +
     dataclasses_descriptors
Partial

Assumes descriptor behavior only when field is assigned in class body.

Does not correctly evaluate type of descriptor access.

PassPass
Partial

* Assumes descriptor behavior only when field is assigned in class body

* Doesn't allow non-data descriptors or data descriptors with differing `__get__` and `__set__` types

Partial

Conformance suite is questionable; see https://github.com/python/typing/issues/2259

Partial

Only infers a descriptor `__get__` method as being called when a descriptor attribute is accessed on an instance if the descriptor attribute is present in the class namespace.

     dataclasses_final
Partial

Wrongly requires a Final dataclass field to be initialized at class level.

Doesn't support Final nested inside ClassVar.

PassPassPassPassPass
     dataclasses_frozenPassPassPassPassPassPass
     dataclasses_hash
Unsupported

Does not synthesize `__hash__ = None` as a class attribute for unhashable dataclasses.

Does not report when an unhashable dataclass has `__hash__` called directly on an instance.

Does not report when dataclass is not compatible with Hashable protocol.

PassPassPassPass
Partial

Understands the `Hashable` protocol as equivalent to `object`.

     dataclasses_inheritancePassPassPassPassPassPass
     dataclasses_kwonlyPassPassPassPassPassPass
     dataclasses_match_argsPassPassPassPassPassPass
     dataclasses_orderPassPassPassPassPassPass
     dataclasses_postinitPassPassPassPassPassPass
     dataclasses_slots
Partial

Does not reject write to instance variable that is not defined in __slots__.

PassPassPassPass
Partial

Synthesizes a `__slots__` attribute but does not validate attribute assignments against `__slots__`.

     dataclasses_transform_classPassPassPassPassPassPass
     dataclasses_transform_converter
Unsupported

Converter parameter not yet supported.

PassPassPassPassPass
     dataclasses_transform_field
Partial

Does not properly handle field constructor that has default value for `kw_only` or `init` parameter.

PassPassPassPassPass
     dataclasses_transform_func
Partial

Does not handle `kw_only=False` override when `kw_only_default=True`.

Does not report error when `order=False` and comparison operators are used.

PassPassPassPassPass
     dataclasses_transform_metaPassPassPassPassPassPass
     dataclasses_usage
Pass*

Does not detect unannotated usage of `dataclasses.field()`.

PassPassPassPassPass
+Typed dictionaries +
     typeddicts_alt_syntax
Pass*

Does not support keyword-argument form of alternative syntax (deprecated in 3.11).

PassPassPassPassPass
     typeddicts_class_syntax
Partial

Does not support version-conditional items in TypedDict definitions.

Partial

Does not support version-conditional items in TypedDict definitions.

PassPassPassPass
     typeddicts_extra_items
Unsupported

Not supported.

PassPassPassPassUnsupported
     typeddicts_finalPassPassPassPassPassPass
     typeddicts_inheritancePassPassPassPassPassPass
     typeddicts_operationsPassPassPassPassPassPass
     typeddicts_readonlyPassPassPassPassPassPass
     typeddicts_readonly_consistencyPassPassPassPassPassPass
     typeddicts_readonly_inheritance
Partial

Incorrectly rejects non-ReadOnly override of ReadOnly item.

Incorrectly rejects override of ReadOnly item with another ReadOnly item with narrower type.

Incorrectly rejects override of NotRequired ReadOnly item with a Required ReadOnly item.

PassPassPassPassPass
     typeddicts_readonly_kwargsPassPassPassPassPassPass
     typeddicts_readonly_update
Partial

Incorrectly allows update of ReadOnly item.

Incorrectly rejects update involving an item with Never type.

PassPassPassPassPass
     typeddicts_requiredPassPassPassPassPassPass
     typeddicts_type_consistencyPassPassPassPass
Partial

Considers TypedDicts to be assignable to plain dict types.

Pass
     typeddicts_usagePassPassPassPassPassPass
+Tuples +
     tuples_type_compat
Partial

Does not support tuple narrowing based on `len()` type guard (optional).

Incorrectly marks a match case as unreachable.

PassPassPassPassPass
     tuples_type_formPassPassPassPassPassPass
     tuples_unpacked
Partial

"More than one unpack" error is missing in some cases.

PassPassPassPassPass
+Named tuples +
     namedtuples_define_class
Partial

Does not reject override of named tuple attribute in child class.

Does not support version-conditional fields.

PassPassPass
Partial

Does not support precise type inference for slices over namedtuples.

Pass
     namedtuples_define_functionalPassPassPassPassPassPass
     namedtuples_type_compatPassPassPassPassPassPass
     namedtuples_usage
Partial

Does not reject attempt to delete named tuple field by name.

PassPassPassPassPass
+Enumerations +
     enums_behaviorsPassPassPassPassPassPass
     enums_definitionPassPassPassPass
Partial

Does not allow enum members to be conditional on version/platform checks.

Pass
     enums_expansion
Partial

Improperly applies narrowing to Flag subclass.

PassPassPassPass
Partial

Does not support `enum.Flag`.

     enums_member_names
Pass*

Does not support special-cased handling of member name literal types in some cases (optional).

PassPassPassPassPass
     enums_member_values
Partial

Does not enforce declared type of `_value_`.

Does not enforce assigned tuple types for enum members (optional).

PassPassPassPassPass
     enums_members
Partial

Does not treat attribute with annotation and no assignment as non-member.

Does not treat callables as non-members.

Does not honor `enum.member` as method decorator.

Does not properly handle aliased enum members.

Does not support `_ignore_` mechanism (optional).

Does not treat attributes with private names as non-members.

Pass*

Does not support `_ignore_` mechanism (optional).

PassPassPassPass
+Type narrowing +
     narrowing_typeguardPassPassPassPassPassPass
     narrowing_typeisPassPassPassPassPass
Partial

Intersects the pre-existing type with the top materialization of the bracketed type rather than the bracketed type itself.

+Type checker directives +
     directives_assert_typePassPassPassPassPassPass
     directives_castPassPassPassPassPassPass
     directives_deprecatedPassPassPassPassPass
Partial

Does not detect calls to deprecated overloads.

Does not detect implicit calls to deprecated dunder methods, for example via operators.

Does not detect accesses of, or attempts to set, deprecated properties.

     directives_disjoint_basePass
Unsupported

Does not support PEP 800 disjoint-base semantics.

Pass
Unsupported

Does not support PEP 800 disjoint-base semantics.

Partial

Does not reject invalid class definitions due to disjoint bases, but uses disjoint base information in type narrowing.

Pass
     directives_no_type_check
Partial

Does not honor `@no_type_check` class decorator (allowed).

Does not reject invalid call of `@no_type_check` function.

Pass*

Does not honor `@no_type_check` class decorator (allowed).

PassPassPassPass
     directives_reveal_typePassPassPassPassPassPass
     directives_type_checkingPassPassPassPassPassPass
     directives_type_ignore
Partial

Does not honor "# type: ignore" comment if comment includes additional text.

PassPassPassPassPass
     directives_type_ignore_file1PassPassPassPassPassPass
     directives_type_ignore_file2PassPassPassPassPassPass
     directives_version_platform
Pass*

Does not understand three-element form of sys.version checks.

Does not understand os.name checks.

PassPassPassPassPass
+Historical and deprecated features +
     historical_positional
Partial

Does not reject positional-only parameter after non-positional-only parameter.

Treats keyword-only parameter as positional-only.

Applies legacy positional-only rules when PEP 570 syntax is used.

PassPassPassPassPass
+ + +
+ + + diff --git a/conformance/results/ty/aliases_explicit.toml b/conformance/results/ty/aliases_explicit.toml new file mode 100644 index 000000000..217de53b7 --- /dev/null +++ b/conformance/results/ty/aliases_explicit.toml @@ -0,0 +1,30 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not emit a diagnostic if a type alias to a union, in which every element of the union is implicitly specialized with `Unknown`, is invalidly specialized again. +""" +errors_diff = """ +Line 100: Expected 1 errors +""" +output = """ +aliases_explicit.py:67:9: error[not-subscriptable] Cannot subscript non-generic type `` +aliases_explicit.py:68:9: error[not-subscriptable] Cannot subscript non-generic type `` +aliases_explicit.py:69:29: error[invalid-type-arguments] Too many type arguments: expected 1, got 2 +aliases_explicit.py:70:29: error[invalid-type-arguments] Too many type arguments: expected 1, got 2 +aliases_explicit.py:71:24: error[invalid-type-arguments] Type argument for `ParamSpec` must be either a list of types, `ParamSpec`, `Concatenate`, or `...` +aliases_explicit.py:79:21: error[invalid-type-form] Function calls are not allowed in type alias values +aliases_explicit.py:80:21: error[invalid-type-form] List literals are not allowed in this context in a type alias value +aliases_explicit.py:81:21: error[invalid-type-form] Tuple literals are not allowed in this context in a type alias value +aliases_explicit.py:82:21: error[invalid-type-form] List comprehensions are not allowed in type alias values +aliases_explicit.py:83:21: error[invalid-type-form] Dict literals are not allowed in type alias values +aliases_explicit.py:84:21: error[invalid-type-form] Function calls are not allowed in type alias values +aliases_explicit.py:85:21: error[invalid-type-form] Only simple names and dotted names can be subscripted in type alias values +aliases_explicit.py:86:21: error[invalid-type-form] `if` expressions are not allowed in type alias values +aliases_explicit.py:87:21: error[invalid-type-form] Variable of type `Literal[3]` is not allowed in a type alias value +aliases_explicit.py:88:22: error[invalid-type-form] Boolean literals are not allowed in this context in a type alias value: Did you mean `typing.Literal[True]`? +aliases_explicit.py:89:22: error[invalid-type-form] Int literals are not allowed in this context in a type alias value: Did you mean `typing.Literal[1]`? +aliases_explicit.py:90:22: error[invalid-type-form] Boolean operations are not allowed in type alias values +aliases_explicit.py:91:22: error[invalid-type-form] F-strings are not allowed in type alias values +aliases_explicit.py:101:6: error[call-non-callable] Object of type `UnionType` is not callable +aliases_explicit.py:102:5: error[not-subscriptable] Cannot subscript non-generic type `` +""" diff --git a/conformance/results/ty/aliases_implicit.toml b/conformance/results/ty/aliases_implicit.toml new file mode 100644 index 000000000..53571d439 --- /dev/null +++ b/conformance/results/ty/aliases_implicit.toml @@ -0,0 +1,32 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject variables with `Any` or `Unknown` types when used as implicit type aliases. +Falls short on full syntactic validation of type aliases. +""" +errors_diff = """ +Line 106: Expected 1 errors +Line 111: Expected 1 errors +Line 112: Expected 1 errors +Line 113: Expected 1 errors +Line 117: Expected 1 errors +""" +output = """ +aliases_implicit.py:76:9: error[not-subscriptable] Cannot subscript non-generic type `` +aliases_implicit.py:77:9: error[not-subscriptable] Cannot subscript non-generic type `` +aliases_implicit.py:78:29: error[invalid-type-arguments] Too many type arguments: expected 1, got 2 +aliases_implicit.py:79:29: error[invalid-type-arguments] Too many type arguments: expected 1, got 2 +aliases_implicit.py:80:24: error[invalid-type-arguments] Type argument for `ParamSpec` must be either a list of types, `ParamSpec`, `Concatenate`, or `...` +aliases_implicit.py:81:25: error[invalid-type-arguments] Type `str` is not assignable to upper bound `int | float` of type variable `TFloat@GoodTypeAlias12` +aliases_implicit.py:107:9: error[invalid-type-form] Variable of type `list[ | ]` is not allowed in a parameter annotation +aliases_implicit.py:108:9: error[invalid-type-form] Variable of type `tuple[tuple[, ]]` is not allowed in a parameter annotation +aliases_implicit.py:109:9: error[invalid-type-form] Variable of type `list[]` is not allowed in a parameter annotation +aliases_implicit.py:110:9: error[invalid-type-form] Variable of type `dict[str, str]` is not allowed in a parameter annotation +aliases_implicit.py:114:9: error[invalid-type-form] Variable of type `Literal[3]` is not allowed in a parameter annotation +aliases_implicit.py:115:10: error[invalid-type-form] Variable of type `Literal[True]` is not allowed in a parameter annotation +aliases_implicit.py:116:10: error[invalid-type-form] Variable of type `Literal[1]` is not allowed in a parameter annotation +aliases_implicit.py:118:10: error[invalid-type-form] Variable of type `Literal["int"]` is not allowed in a parameter annotation +aliases_implicit.py:119:10: error[invalid-type-form] Variable of type `Literal["int | str"]` is not allowed in a parameter annotation +aliases_implicit.py:133:6: error[call-non-callable] Object of type `UnionType` is not callable +aliases_implicit.py:135:5: error[not-subscriptable] Cannot subscript non-generic type `` +""" diff --git a/conformance/results/ty/aliases_newtype.toml b/conformance/results/ty/aliases_newtype.toml new file mode 100644 index 000000000..6b23739e2 --- /dev/null +++ b/conformance/results/ty/aliases_newtype.toml @@ -0,0 +1,19 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_newtype.py:11:8: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["user"]` +aliases_newtype.py:12:14: error[invalid-assignment] Object of type `Literal[42]` is not assignable to `UserId` +aliases_newtype.py:18:11: error[invalid-assignment] Object of type `` is not assignable to `type` +aliases_newtype.py:23:16: error[invalid-argument-type] Argument to function `isinstance` is incorrect: Expected `type | UnionType | tuple[Divergent, ...]`, found `` +aliases_newtype.py:26:21: error[invalid-base] Cannot subclass an instance of NewType +aliases_newtype.py:35:20: error[mismatched-type-name] The name passed to `NewType` must match the variable it is assigned to: Expected "GoodName", got "BadName" +aliases_newtype.py:41:6: error[invalid-type-form] `GoodNewType1` is a `NewType` and cannot be specialized +aliases_newtype.py:47:38: error[invalid-newtype] invalid base for `typing.NewType`: type `int | str` +aliases_newtype.py:50:38: error[invalid-newtype] invalid base for `typing.NewType`: A `NewType` base cannot be generic +aliases_newtype.py:52:38: error[invalid-newtype] invalid base for `typing.NewType`: type `Hashable` +aliases_newtype.py:54:38: error[invalid-newtype] invalid base for `typing.NewType`: type `Literal[7]` +aliases_newtype.py:61:38: error[invalid-newtype] invalid base for `typing.NewType`: type `TD1` +aliases_newtype.py:63:15: error[invalid-newtype] Wrong number of arguments in `NewType` creation: expected 2, found 3 +aliases_newtype.py:65:38: error[invalid-newtype] invalid base for `typing.NewType`: type `Any` +""" diff --git a/conformance/results/ty/aliases_recursive.toml b/conformance/results/ty/aliases_recursive.toml new file mode 100644 index 000000000..b74c97b2a --- /dev/null +++ b/conformance/results/ty/aliases_recursive.toml @@ -0,0 +1,17 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 19: Expected 1 errors +Line 20: Expected 1 errors +Line 38: Expected 1 errors +Line 39: Expected 1 errors +Line 50: Expected 1 errors +Line 51: Expected 1 errors +Line 52: Expected 1 errors +Line 63: Expected 1 errors +Line 69: Expected 1 errors +Line 72: Expected 1 errors +Line 75: Expected 1 errors +""" +output = """ +""" diff --git a/conformance/results/ty/aliases_type_statement.toml b/conformance/results/ty/aliases_type_statement.toml new file mode 100644 index 000000000..d1175af88 --- /dev/null +++ b/conformance/results/ty/aliases_type_statement.toml @@ -0,0 +1,39 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject circular definitions of type aliases. +Does not support `type` statements generic over `TypeVarTuple`s. +""" +errors_diff = """ +Line 75: Expected 1 errors +Line 10: Unexpected errors ['aliases_type_statement.py:10:52: error[invalid-type-arguments] Too many type arguments: expected 2, got 3', 'aliases_type_statement.py:10:52: error[invalid-type-form] `...` is not allowed in this context in a type alias value'] +""" +output = """ +aliases_type_statement.py:10:52: error[invalid-type-arguments] Too many type arguments: expected 2, got 3 +aliases_type_statement.py:10:52: error[invalid-type-form] `...` is not allowed in this context in a type alias value +aliases_type_statement.py:17:1: error[unresolved-attribute] Object of type `TypeAliasType` has no attribute `bit_count` +aliases_type_statement.py:19:1: error[call-non-callable] Object of type `TypeAliasType` is not callable +aliases_type_statement.py:23:7: error[unresolved-attribute] Object of type `TypeAliasType` has no attribute `other_attrib` +aliases_type_statement.py:26:18: error[invalid-base] Invalid class base with type `TypeAliasType` +aliases_type_statement.py:31:22: error[invalid-argument-type] Argument to function `isinstance` is incorrect: Expected `type | UnionType | tuple[Divergent, ...]`, found `TypeAliasType` +aliases_type_statement.py:37:22: error[invalid-type-form] Function calls are not allowed in type alias values +aliases_type_statement.py:38:22: error[invalid-type-form] List literals are not allowed in this context in a type alias value +aliases_type_statement.py:39:22: error[invalid-type-form] Tuple literals are not allowed in this context in a type alias value +aliases_type_statement.py:40:22: error[invalid-type-form] List comprehensions are not allowed in type alias values +aliases_type_statement.py:41:22: error[invalid-type-form] Dict literals are not allowed in type alias values +aliases_type_statement.py:42:22: error[invalid-type-form] Function calls are not allowed in type alias values +aliases_type_statement.py:43:22: error[invalid-type-form] Only simple names and dotted names can be subscripted in type alias values +aliases_type_statement.py:44:22: error[invalid-type-form] `if` expressions are not allowed in type alias values +aliases_type_statement.py:45:22: error[invalid-type-form] Variable of type `Literal[1]` is not allowed in a type alias value +aliases_type_statement.py:46:23: error[invalid-type-form] Boolean literals are not allowed in this context in a type alias value: Did you mean `typing.Literal[True]`? +aliases_type_statement.py:47:23: error[invalid-type-form] Int literals are not allowed in this context in a type alias value: Did you mean `typing.Literal[1]`? +aliases_type_statement.py:48:23: error[invalid-type-form] Boolean operations are not allowed in type alias values +aliases_type_statement.py:49:23: error[invalid-type-form] F-strings are not allowed in type alias values +aliases_type_statement.py:53:23: error[unbound-type-variable] Type variable `V` is not bound to any outer generic context +aliases_type_statement.py:58:17: error[unbound-type-variable] Type variable `T1` is not bound to any outer generic context +aliases_type_statement.py:68:27: error[invalid-type-arguments] Type `str` is not assignable to upper bound `int` of type variable `S@RecursiveTypeAlias2` +aliases_type_statement.py:70:32: error[invalid-type-arguments] Type `int` is not assignable to upper bound `str` of type variable `T@RecursiveTypeAlias2` +aliases_type_statement.py:73:1: error[cyclic-type-alias-definition] Cyclic definition of `RecursiveTypeAlias3` +aliases_type_statement.py:79:1: error[cyclic-type-alias-definition] Cyclic definition of `RecursiveTypeAlias6` +aliases_type_statement.py:80:1: error[cyclic-type-alias-definition] Cyclic definition of `RecursiveTypeAlias7` +""" diff --git a/conformance/results/ty/aliases_typealiastype.toml b/conformance/results/ty/aliases_typealiastype.toml new file mode 100644 index 000000000..181178cff --- /dev/null +++ b/conformance/results/ty/aliases_typealiastype.toml @@ -0,0 +1,34 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject specializing a type parameter in a generic type alias with a type inconsistent with the parameter's upper bound. +Does not reject declaring a type alias with a type variable that is not in scope. +Does not reject declaring a type alias with a non-literal tuple passed to the `type_params` parameter. +Does not reject cyclically defined type aliases. +""" +errors_diff = """ +Line 40: Expected 1 errors +Line 43: Expected 1 errors +Line 44: Expected 1 errors +Line 45: Expected 1 errors +Line 46: Expected 1 errors +Line 47: Expected 1 errors +Line 48: Expected 1 errors +""" +output = """ +aliases_typealiastype.py:32:7: error[unresolved-attribute] Object of type `TypeAliasType` has no attribute `other_attrib` +aliases_typealiastype.py:52:40: error[invalid-type-form] Function calls are not allowed in type expressions +aliases_typealiastype.py:53:40: error[invalid-type-form] List literals are not allowed in this context in a type expression +aliases_typealiastype.py:54:42: error[invalid-type-form] Tuple literals are not allowed in this context in a type expression +aliases_typealiastype.py:55:42: error[invalid-type-form] List comprehensions are not allowed in type expressions +aliases_typealiastype.py:56:42: error[invalid-type-form] Dict literals are not allowed in type expressions +aliases_typealiastype.py:57:42: error[invalid-type-form] Function calls are not allowed in type expressions +aliases_typealiastype.py:58:42: error[invalid-type-form] Only simple names and dotted names can be subscripted in type expressions +aliases_typealiastype.py:59:42: error[invalid-type-form] `if` expressions are not allowed in type expressions +aliases_typealiastype.py:60:42: error[invalid-type-form] Variable of type `Literal[3]` is not allowed in a type expression +aliases_typealiastype.py:61:42: error[invalid-type-form] Boolean literals are not allowed in this context in a type expression: Did you mean `typing.Literal[True]`? +aliases_typealiastype.py:62:42: error[invalid-type-form] Int literals are not allowed in this context in a type expression: Did you mean `typing.Literal[1]`? +aliases_typealiastype.py:63:42: error[invalid-type-form] Boolean operations are not allowed in type expressions +aliases_typealiastype.py:64:42: error[invalid-type-form] F-strings are not allowed in type expressions +aliases_typealiastype.py:66:47: error[unresolved-reference] Name `BadAlias21` used when not defined +""" diff --git a/conformance/results/ty/aliases_variance.toml b/conformance/results/ty/aliases_variance.toml new file mode 100644 index 000000000..9b8afdb53 --- /dev/null +++ b/conformance/results/ty/aliases_variance.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_variance.py:24:16: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `ClassA` +aliases_variance.py:28:16: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `ClassA` +aliases_variance.py:32:16: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `ClassA` +aliases_variance.py:44:16: error[invalid-generic-class] Variance of type variable `T_contra` is incompatible with base class `ClassB` +""" diff --git a/conformance/results/ty/annotations_coroutines.toml b/conformance/results/ty/annotations_coroutines.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/annotations_coroutines.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/annotations_forward_refs.toml b/conformance/results/ty/annotations_forward_refs.toml new file mode 100644 index 000000000..5944683d0 --- /dev/null +++ b/conformance/results/ty/annotations_forward_refs.toml @@ -0,0 +1,37 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Resolves references in type annotations as referring to end-of-scope types (https://discuss.python.org/t/annotation-string-references-in-class-scope-in-conformance-tests/105439, https://github.com/python/typing/pull/2144) +""" +errors_diff = """ +Line 87: Unexpected errors ['annotations_forward_refs.py:87:9: error[invalid-type-form] Function `int` is not valid in a type expression'] +Line 95: Unexpected errors ['annotations_forward_refs.py:95:1: error[type-assertion-failure] Type `Divergent` does not match asserted type `str`'] +Line 96: Unexpected errors ['annotations_forward_refs.py:96:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `int`'] +""" +output = """ +annotations_forward_refs.py:22:7: error[unresolved-reference] Name `ClassA` used when not defined +annotations_forward_refs.py:23:12: error[unresolved-reference] Name `ClassA` used when not defined +annotations_forward_refs.py:24:7: error[unsupported-operator] Operator `|` is unsupported between objects of type `Literal["ClassA"]` and `` +annotations_forward_refs.py:25:7: error[unsupported-operator] Operator `|` is unsupported between objects of type `` and `Literal["ClassA"]` +annotations_forward_refs.py:41:10: error[invalid-type-form] Function calls are not allowed in parameter annotations +annotations_forward_refs.py:42:10: error[invalid-type-form] List literals are not allowed in this context in a parameter annotation +annotations_forward_refs.py:43:10: error[invalid-type-form] Tuple literals are not allowed in this context in a parameter annotation: Did you mean `tuple[int, str]`? +annotations_forward_refs.py:44:10: error[invalid-type-form] List comprehensions are not allowed in parameter annotations +annotations_forward_refs.py:45:10: error[invalid-type-form] Dict literals are not allowed in parameter annotations +annotations_forward_refs.py:46:10: error[invalid-type-form] Function calls are not allowed in parameter annotations +annotations_forward_refs.py:47:10: error[invalid-type-form] Only simple names and dotted names can be subscripted in parameter annotations +annotations_forward_refs.py:48:10: error[invalid-type-form] `if` expressions are not allowed in parameter annotations +annotations_forward_refs.py:49:10: error[invalid-type-form] Variable of type `Literal[1]` is not allowed in a parameter annotation +annotations_forward_refs.py:50:11: error[invalid-type-form] Boolean literals are not allowed in this context in a parameter annotation: Did you mean `typing.Literal[True]`? +annotations_forward_refs.py:51:11: error[invalid-type-form] Int literals are not allowed in this context in a parameter annotation: Did you mean `typing.Literal[1]`? +annotations_forward_refs.py:52:11: error[invalid-type-form] Unary operations are not allowed in parameter annotations +annotations_forward_refs.py:53:11: error[invalid-type-form] Boolean operations are not allowed in parameter annotations +annotations_forward_refs.py:54:11: error[invalid-type-form] F-strings are not allowed in parameter annotations +annotations_forward_refs.py:55:11: error[invalid-type-form] Module `types` is not valid in a parameter annotation +annotations_forward_refs.py:66:26: error[unresolved-reference] Name `ClassB` used when not defined +annotations_forward_refs.py:80:14: error[unresolved-reference] Name `ClassF` used when not defined +annotations_forward_refs.py:87:9: error[invalid-type-form] Function `int` is not valid in a type expression +annotations_forward_refs.py:89:8: error[invalid-type-form] Function `int` is not valid in a type expression +annotations_forward_refs.py:95:1: error[type-assertion-failure] Type `Divergent` does not match asserted type `str` +annotations_forward_refs.py:96:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `int` +""" diff --git a/conformance/results/ty/annotations_generators.toml b/conformance/results/ty/annotations_generators.toml new file mode 100644 index 000000000..1f05f0b0b --- /dev/null +++ b/conformance/results/ty/annotations_generators.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +annotations_generators.py:51:21: error[invalid-return-type] Function can implicitly return `None`, which is not assignable to return type `C` +annotations_generators.py:54:16: error[invalid-return-type] Return type does not match returned value: expected `C`, found `Literal[False]` +annotations_generators.py:57:15: error[invalid-yield] Yield type `Literal[3]` does not match annotated yield type `A` +annotations_generators.py:66:15: error[invalid-yield] Yield type `Literal[3]` does not match annotated yield type `A` +annotations_generators.py:75:11: error[invalid-yield] Yield type `B` does not match annotated yield type `A` +annotations_generators.py:86:21: error[invalid-return-type] Return type does not match returned value: expected `int`, found `types.GeneratorType` +annotations_generators.py:91:27: error[invalid-return-type] Return type does not match returned value: expected `int`, found `types.AsyncGeneratorType` +annotations_generators.py:118:16: error[invalid-yield] Yield type `A` does not match annotated yield type `B` +annotations_generators.py:119:16: error[invalid-yield] Yield type `int` does not match annotated yield type `B` +annotations_generators.py:135:16: error[invalid-yield] Send type `int` does not match annotated send type `str` +""" diff --git a/conformance/results/ty/annotations_methods.toml b/conformance/results/ty/annotations_methods.toml new file mode 100644 index 000000000..5800c037c --- /dev/null +++ b/conformance/results/ty/annotations_methods.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +annotations_methods.py:42:1: error[type-assertion-failure] Type `B` does not match asserted type `A` +""" diff --git a/conformance/results/ty/annotations_typeexpr.toml b/conformance/results/ty/annotations_typeexpr.toml new file mode 100644 index 000000000..96d3d212d --- /dev/null +++ b/conformance/results/ty/annotations_typeexpr.toml @@ -0,0 +1,20 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +annotations_typeexpr.py:88:9: error[invalid-type-form] Function calls are not allowed in parameter annotations +annotations_typeexpr.py:89:9: error[invalid-type-form] List literals are not allowed in this context in a parameter annotation +annotations_typeexpr.py:90:9: error[invalid-type-form] Tuple literals are not allowed in this context in a parameter annotation: Did you mean `tuple[int, str]`? +annotations_typeexpr.py:91:9: error[invalid-type-form] List comprehensions are not allowed in parameter annotations +annotations_typeexpr.py:92:9: error[invalid-type-form] Dict literals are not allowed in parameter annotations +annotations_typeexpr.py:93:9: error[invalid-type-form] Function calls are not allowed in parameter annotations +annotations_typeexpr.py:94:9: error[invalid-type-form] Only simple names and dotted names can be subscripted in parameter annotations +annotations_typeexpr.py:95:9: error[invalid-type-form] `if` expressions are not allowed in parameter annotations +annotations_typeexpr.py:96:9: error[invalid-type-form] Variable of type `Literal[3]` is not allowed in a parameter annotation +annotations_typeexpr.py:97:10: error[invalid-type-form] Boolean literals are not allowed in this context in a parameter annotation: Did you mean `typing.Literal[True]`? +annotations_typeexpr.py:98:10: error[invalid-type-form] Int literals are not allowed in this context in a parameter annotation: Did you mean `typing.Literal[1]`? +annotations_typeexpr.py:99:10: error[invalid-type-form] Unary operations are not allowed in parameter annotations +annotations_typeexpr.py:100:10: error[invalid-type-form] Boolean operations are not allowed in parameter annotations +annotations_typeexpr.py:101:10: error[invalid-type-form] F-strings are not allowed in parameter annotations +annotations_typeexpr.py:102:10: error[invalid-type-form] Module `types` is not valid in a parameter annotation +""" diff --git a/conformance/results/ty/callables_annotation.toml b/conformance/results/ty/callables_annotation.toml new file mode 100644 index 000000000..1be55c1ca --- /dev/null +++ b/conformance/results/ty/callables_annotation.toml @@ -0,0 +1,29 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Infers a callback protocol as being a gradual type if the callback has signature `__call__[T](self, *args: T, **kwargs: T)` and `T` has been explicitly specialized to `Any`. +""" +errors_diff = """ +Line 159: Expected 1 errors +""" +output = """ +callables_annotation.py:25:5: error[missing-argument] No argument provided for required parameter 2 +callables_annotation.py:26:11: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `Literal[2]` +callables_annotation.py:27:15: error[too-many-positional-arguments] Too many positional arguments: expected 2, got 3 +callables_annotation.py:29:5: error[missing-argument] No arguments provided for required parameters 1, 2 +callables_annotation.py:29:8: error[unknown-argument] Argument `a` does not match any known parameter +callables_annotation.py:29:13: error[unknown-argument] Argument `b` does not match any known parameter +callables_annotation.py:35:8: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 1 +callables_annotation.py:55:5: error[invalid-type-form] Special form `typing.Callable` expected exactly two arguments (parameter types and return type) +callables_annotation.py:55:14: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...` +callables_annotation.py:56:14: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...` +callables_annotation.py:57:18: error[invalid-type-form] List literals are not allowed in this context in a type expression: Did you mean `list[int]`? +callables_annotation.py:58:5: error[invalid-type-form] Special form `typing.Callable` expected exactly two arguments (parameter types and return type) +callables_annotation.py:58:14: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...` +callables_annotation.py:59:14: error[invalid-type-form] `[...]` is not a valid parameter list for `Callable`: Did you mean `Callable[..., int]`? +callables_annotation.py:91:7: error[invalid-assignment] Object of type `def test_cb2() -> str` is not assignable to `(int, /, *args: Any, **kwargs: Any) -> str` +callables_annotation.py:93:7: error[invalid-assignment] Object of type `def test_cb4(*, a: int) -> str` is not assignable to `(int, /, *args: Any, **kwargs: Any) -> str` +callables_annotation.py:172:26: error[invalid-assignment] Object of type `() -> str` is not assignable to `(int, /, *args: Any, **kwargs: Any) -> str` +callables_annotation.py:187:48: error[invalid-assignment] Object of type `(int, str, /) -> str` is not assignable to `(str, /, *args: Any, **kwargs: Any) -> str` +callables_annotation.py:189:32: error[invalid-assignment] Object of type `(int, str, /) -> str` is not assignable to `(str, /, *args: Any, **kwargs: Any) -> str` +""" diff --git a/conformance/results/ty/callables_kwargs.toml b/conformance/results/ty/callables_kwargs.toml new file mode 100644 index 000000000..3318d6bb6 --- /dev/null +++ b/conformance/results/ty/callables_kwargs.toml @@ -0,0 +1,20 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +callables_kwargs.py:46:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` +callables_kwargs.py:52:5: error[missing-argument] No arguments provided for required parameters `v1`, `v3` of function `func1` +callables_kwargs.py:52:11: error[too-many-positional-arguments] Too many positional arguments to function `func1`: expected 0, got 3 +callables_kwargs.py:58:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `int`, found `str` +callables_kwargs.py:61:11: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `str`, found `int | str` +callables_kwargs.py:63:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func1` +callables_kwargs.py:64:11: error[invalid-argument-type] Argument to function `func2` is incorrect: Expected `str`, found `Literal[1]` +callables_kwargs.py:64:14: error[parameter-already-assigned] Multiple values provided for parameter `v3` of function `func2` +callables_kwargs.py:65:17: error[parameter-already-assigned] Multiple values provided for parameter `v1` of function `func2` +callables_kwargs.py:101:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol3` +callables_kwargs.py:102:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol4` +callables_kwargs.py:103:19: error[invalid-assignment] Object of type `def func1(*, v1: int, v2: str = ..., v3: str, **kwargs: object) -> None` is not assignable to `TDProtocol5` +callables_kwargs.py:111:20: error[invalid-type-form] Parameter `v1` overlaps with unpacked TypedDict key in `**kwargs` annotation +callables_kwargs.py:122:21: error[invalid-type-form] Unpacked value for `**kwargs` must be a TypedDict, not `T@func6` +callables_kwargs.py:134:19: error[invalid-assignment] Object of type `def func7(*, v1: int, v3: str, v2: str = "") -> None` is not assignable to `TDProtocol6` +""" diff --git a/conformance/results/ty/callables_protocol.toml b/conformance/results/ty/callables_protocol.toml new file mode 100644 index 000000000..23b8e9cf3 --- /dev/null +++ b/conformance/results/ty/callables_protocol.toml @@ -0,0 +1,22 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +callables_protocol.py:35:7: error[invalid-assignment] Object of type `def cb1_bad1(*vals: bytes, *, max_items: int | None) -> list[bytes]` is not assignable to `Proto1` +callables_protocol.py:36:7: error[invalid-assignment] Object of type `def cb1_bad2(*vals: bytes) -> list[bytes]` is not assignable to `Proto1` +callables_protocol.py:37:7: error[invalid-assignment] Object of type `def cb1_bad3(*vals: bytes, *, max_len: str | None) -> list[bytes]` is not assignable to `Proto1` +callables_protocol.py:67:7: error[invalid-assignment] Object of type `def cb2_bad1(*a: bytes) -> Unknown` is not assignable to `Proto2` +callables_protocol.py:68:7: error[invalid-assignment] Object of type `def cb2_bad2(*a: str, **b: str) -> Unknown` is not assignable to `Proto2` +callables_protocol.py:69:7: error[invalid-assignment] Object of type `def cb2_bad3(*a: bytes, **b: bytes) -> Unknown` is not assignable to `Proto2` +callables_protocol.py:70:7: error[invalid-assignment] Object of type `def cb2_bad4(**b: str) -> Unknown` is not assignable to `Proto2` +callables_protocol.py:97:16: error[invalid-assignment] Object of type `def cb4_bad1(x: int) -> None` is not assignable to `Proto4` +callables_protocol.py:121:18: error[invalid-assignment] Object of type `def cb6_bad1(*vals: bytes, *, max_len: int | None = None) -> list[bytes]` is not assignable to `NotProto6` +callables_protocol.py:169:7: error[invalid-assignment] Object of type `def cb8_bad1(x: int) -> Any` is not assignable to `Proto8` +callables_protocol.py:186:5: error[invalid-assignment] Object of type `Literal["str"]` is not assignable to attribute `other_attribute` of type `int` +callables_protocol.py:187:5: error[unresolved-attribute] Unresolved attribute `xxx` on type `Proto9[P@decorator1, R@decorator1]` +callables_protocol.py:197:7: error[unresolved-attribute] Object of type `Proto9[(x: int), str]` has no attribute `other_attribute2` +callables_protocol.py:238:8: error[invalid-assignment] Object of type `def cb11_bad1(x: int, y: str, /) -> Any` is not assignable to `Proto11` +callables_protocol.py:260:8: error[invalid-assignment] Object of type `def cb12_bad1(*args: Any, *, kwarg0: Any) -> None` is not assignable to `Proto12` +callables_protocol.py:284:27: error[invalid-assignment] Object of type `def cb13_no_default(path: str) -> str` is not assignable to `Proto13_Default` +callables_protocol.py:311:27: error[invalid-assignment] Object of type `def cb14_no_default(*, path: str) -> str` is not assignable to `Proto14_Default` +""" diff --git a/conformance/results/ty/callables_subtyping.toml b/conformance/results/ty/callables_subtyping.toml new file mode 100644 index 000000000..d6d67269d --- /dev/null +++ b/conformance/results/ty/callables_subtyping.toml @@ -0,0 +1,37 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +callables_subtyping.py:26:36: error[invalid-assignment] Object of type `(int, /) -> int` is not assignable to `(int | float, /) -> int | float` +callables_subtyping.py:29:32: error[invalid-assignment] Object of type `(int | float, /) -> int | float` is not assignable to `(int, /) -> int` +callables_subtyping.py:51:21: error[invalid-assignment] Object of type `PosOnly2` is not assignable to `Standard2` +callables_subtyping.py:52:21: error[invalid-assignment] Object of type `KwOnly2` is not assignable to `Standard2` +callables_subtyping.py:55:20: error[invalid-assignment] Object of type `KwOnly2` is not assignable to `PosOnly2` +callables_subtyping.py:58:19: error[invalid-assignment] Object of type `PosOnly2` is not assignable to `KwOnly2` +callables_subtyping.py:82:20: error[invalid-assignment] Object of type `NoArgs3` is not assignable to `IntArgs3` +callables_subtyping.py:85:22: error[invalid-assignment] Object of type `NoArgs3` is not assignable to `FloatArgs3` +callables_subtyping.py:86:22: error[invalid-assignment] Object of type `IntArgs3` is not assignable to `FloatArgs3` +callables_subtyping.py:116:20: error[invalid-assignment] Object of type `IntArgs4` is not assignable to `PosOnly4` +callables_subtyping.py:119:23: error[invalid-assignment] Object of type `StrArgs4` is not assignable to `IntStrArgs4` +callables_subtyping.py:120:23: error[invalid-assignment] Object of type `IntArgs4` is not assignable to `IntStrArgs4` +callables_subtyping.py:122:20: error[invalid-assignment] Object of type `IntArgs4` is not assignable to `StrArgs4` +callables_subtyping.py:124:20: error[invalid-assignment] Object of type `StrArgs4` is not assignable to `IntArgs4` +callables_subtyping.py:125:22: error[invalid-assignment] Object of type `IntStrArgs4` is not assignable to `Standard4` +callables_subtyping.py:126:22: error[invalid-assignment] Object of type `StrArgs4` is not assignable to `Standard4` +callables_subtyping.py:151:22: error[invalid-assignment] Object of type `NoKwargs5` is not assignable to `IntKwargs5` +callables_subtyping.py:154:24: error[invalid-assignment] Object of type `NoKwargs5` is not assignable to `FloatKwargs5` +callables_subtyping.py:155:24: error[invalid-assignment] Object of type `IntKwargs5` is not assignable to `FloatKwargs5` +callables_subtyping.py:187:19: error[invalid-assignment] Object of type `IntKwargs6` is not assignable to `KwOnly6` +callables_subtyping.py:190:25: error[invalid-assignment] Object of type `StrKwargs6` is not assignable to `IntStrKwargs6` +callables_subtyping.py:191:25: error[invalid-assignment] Object of type `IntKwargs6` is not assignable to `IntStrKwargs6` +callables_subtyping.py:193:22: error[invalid-assignment] Object of type `IntKwargs6` is not assignable to `StrKwargs6` +callables_subtyping.py:195:22: error[invalid-assignment] Object of type `StrKwargs6` is not assignable to `IntKwargs6` +callables_subtyping.py:196:22: error[invalid-assignment] Object of type `IntStrKwargs6` is not assignable to `Standard6` +callables_subtyping.py:197:22: error[invalid-assignment] Object of type `StrKwargs6` is not assignable to `Standard6` +callables_subtyping.py:236:23: error[invalid-assignment] Object of type `NoDefaultArg8` is not assignable to `DefaultArg8` +callables_subtyping.py:237:23: error[invalid-assignment] Object of type `NoX8` is not assignable to `DefaultArg8` +callables_subtyping.py:240:25: error[invalid-assignment] Object of type `NoX8` is not assignable to `NoDefaultArg8` +callables_subtyping.py:243:16: error[invalid-assignment] Object of type `NoDefaultArg8` is not assignable to `NoX8` +callables_subtyping.py:273:21: error[invalid-assignment] Object of type `Overloaded9` is not assignable to `FloatArg9` +callables_subtyping.py:297:24: error[invalid-assignment] Object of type `StrArg10` is not assignable to `Overloaded10` +""" diff --git a/conformance/results/ty/classes_classvar.toml b/conformance/results/ty/classes_classvar.toml new file mode 100644 index 000000000..1cadf9978 --- /dev/null +++ b/conformance/results/ty/classes_classvar.toml @@ -0,0 +1,22 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +classes_classvar.py:38:11: error[invalid-type-form] Type qualifier `typing.ClassVar` expected exactly 1 argument, got 2 +classes_classvar.py:39:14: error[invalid-type-form] Int literals are not allowed in this context in a type expression: Did you mean `typing.Literal[3]`? +classes_classvar.py:40:14: error[unresolved-reference] Name `var` used when not defined +classes_classvar.py:45:11: error[invalid-type-form] `ClassVar` cannot contain type variables +classes_classvar.py:46:11: error[invalid-type-form] `ClassVar` cannot contain type variables +classes_classvar.py:47:11: error[invalid-type-form] `ClassVar` cannot contain type variables +classes_classvar.py:52:33: error[invalid-assignment] Object of type `dict[Unknown, Unknown]` is not assignable to `list[str]` +classes_classvar.py:54:11: error[redundant-final-classvar] `Combining `ClassVar` and `Final` is redundant +classes_classvar.py:55:17: error[invalid-type-form] Type qualifier `typing.ClassVar` is not allowed in type expressions (only in annotation expressions) +classes_classvar.py:69:26: error[invalid-type-form] Type qualifier `typing.ClassVar` is not allowed in parameter annotations +classes_classvar.py:70:12: error[invalid-type-form] `ClassVar` is only allowed in class bodies +classes_classvar.py:71:18: error[invalid-type-form] `ClassVar` annotations are not allowed for non-name targets +classes_classvar.py:73:26: error[invalid-type-form] Type qualifier `typing.ClassVar` is not allowed in return type annotations +classes_classvar.py:77:8: error[invalid-type-form] `ClassVar` is only allowed in class bodies +classes_classvar.py:78:20: error[invalid-type-form] Type qualifier `typing.ClassVar` is not allowed in type alias values +classes_classvar.py:111:1: error[invalid-attribute-access] Cannot assign to ClassVar `stats` from an instance of type `Starship` +classes_classvar.py:140:13: error[invalid-assignment] Object of type `ProtoAImpl` is not assignable to `ProtoA` +""" diff --git a/conformance/results/ty/classes_override.toml b/conformance/results/ty/classes_override.toml new file mode 100644 index 000000000..52cef47dd --- /dev/null +++ b/conformance/results/ty/classes_override.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +classes_override.py:53:9: error[invalid-explicit-override] Method `method3` is decorated with `@override` but does not override anything +classes_override.py:65:9: error[invalid-explicit-override] Method `method4` is decorated with `@override` but does not override anything +classes_override.py:79:9: error[invalid-explicit-override] Method `static_method1` is decorated with `@override` but does not override anything +classes_override.py:84:9: error[invalid-explicit-override] Method `class_method1` is decorated with `@override` but does not override anything +classes_override.py:89:9: error[invalid-explicit-override] Method `property1` is decorated with `@override` but does not override anything +""" diff --git a/conformance/results/ty/constructors_call_init.toml b/conformance/results/ty/constructors_call_init.toml new file mode 100644 index 000000000..aee79b414 --- /dev/null +++ b/conformance/results/ty/constructors_call_init.toml @@ -0,0 +1,15 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject invalid argument types to an inherited constructor in a specialized subclass of a generic superclass. +Does not reject class-scoped type variables used in the `self` annotation. +""" +errors_diff = """ +Line 42: Expected 1 errors +Line 107: Expected 1 errors +""" +output = """ +constructors_call_init.py:21:13: error[invalid-argument-type] Argument to `Class1.__init__` is incorrect: Expected `int`, found `float` +constructors_call_init.py:56:1: error[invalid-argument-type] Argument to `Class4.__init__` is incorrect: Expected `Class4[int]`, found `Class4[str]` +constructors_call_init.py:130:9: error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2 +""" diff --git a/conformance/results/ty/constructors_call_metaclass.toml b/conformance/results/ty/constructors_call_metaclass.toml new file mode 100644 index 000000000..f7e30d6f0 --- /dev/null +++ b/conformance/results/ty/constructors_call_metaclass.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +conformant = "Pass" +notes = """ +""" +errors_diff = """ +""" +output = """ +constructors_call_metaclass.py:54:1: error[missing-argument] No argument provided for required parameter `x` of constructor `Class3.__new__` +constructors_call_metaclass.py:68:1: error[missing-argument] No argument provided for required parameter `x` of constructor `Class4.__new__` +""" diff --git a/conformance/results/ty/constructors_call_new.toml b/conformance/results/ty/constructors_call_new.toml new file mode 100644 index 000000000..51da8faed --- /dev/null +++ b/conformance/results/ty/constructors_call_new.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_new.py:21:13: error[invalid-argument-type] Argument to constructor `Class1.__new__` is incorrect: Expected `int`, found `float` +constructors_call_new.py:148:1: error[invalid-argument-type] Argument to constructor `Class11.__new__` is incorrect: Expected `type[Class11[int]]`, found `` +""" diff --git a/conformance/results/ty/constructors_call_type.toml b/conformance/results/ty/constructors_call_type.toml new file mode 100644 index 000000000..d30d79c97 --- /dev/null +++ b/conformance/results/ty/constructors_call_type.toml @@ -0,0 +1,18 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Has overly lenient handling of calls to `type[T]` if `T` is a type variable without an upper bound. +""" +errors_diff = """ +Line 64: Expected 1 errors +""" +output = """ +constructors_call_type.py:19:55: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive +constructors_call_type.py:30:5: error[missing-argument] No arguments provided for required parameters `x`, `y` of bound method `Meta1.__call__` +constructors_call_type.py:40:5: error[missing-argument] No arguments provided for required parameters `x`, `y` of constructor `Class2.__new__` +constructors_call_type.py:50:5: error[missing-argument] No arguments provided for required parameters `x`, `y` of `Class3.__init__` +constructors_call_type.py:59:9: error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2 +constructors_call_type.py:72:5: error[missing-argument] No arguments provided for required parameters `x`, `y` of bound method `Meta1.__call__` +constructors_call_type.py:81:5: error[missing-argument] No argument provided for required parameter `y` of constructor `Class2.__new__` +constructors_call_type.py:82:12: error[invalid-argument-type] Argument to constructor `Class2.__new__` is incorrect: Expected `str`, found `Literal[2]` +""" diff --git a/conformance/results/ty/constructors_callable.toml b/conformance/results/ty/constructors_callable.toml new file mode 100644 index 000000000..a0e0c6c35 --- /dev/null +++ b/conformance/results/ty/constructors_callable.toml @@ -0,0 +1,50 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not include `__init__` when `__new__` returns `Self`. +Does not respect `NoReturn` return type on metaclass `__call__`. +Does not ignore `__init__` when `__new__` returns `Any`. +Unions overload return types. +""" +errors_diff = """ +Line 66: Expected 1 errors +Line 67: Expected 1 errors +Line 68: Expected 1 errors +Line 102: Unexpected errors ['constructors_callable.py:102:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `Never`'] +Line 107: Unexpected errors ['constructors_callable.py:107:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `Never`'] +Line 143: Unexpected errors ["constructors_callable.py:143:27: error[invalid-argument-type] Argument to function `accepts_callable` is incorrect: Expected `() -> Any | Class6Any`, found ``"] +Line 145: Unexpected errors ['constructors_callable.py:145:1: error[type-assertion-failure] Type `Any | Class6Any` does not match asserted type `Any`'] +Line 166: Unexpected errors ['constructors_callable.py:166:1: error[type-assertion-failure] Type `Class7[int] | Class7[str]` does not match asserted type `Class7[int]`'] +Line 167: Unexpected errors ['constructors_callable.py:167:1: error[type-assertion-failure] Type `Class7[int] | Class7[str]` does not match asserted type `Class7[str]`'] +""" +output = """ +constructors_callable.py:36:13: info[revealed-type] Revealed type: `(x: int) -> Class1` +constructors_callable.py:38:1: error[missing-argument] No argument provided for required parameter `x` +constructors_callable.py:39:1: error[missing-argument] No argument provided for required parameter `x` +constructors_callable.py:39:4: error[unknown-argument] Argument `y` does not match any known parameter +constructors_callable.py:49:13: info[revealed-type] Revealed type: `() -> Class2` +constructors_callable.py:51:4: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 1 +constructors_callable.py:64:13: info[revealed-type] Revealed type: `(...) -> Class3` +constructors_callable.py:79:13: info[revealed-type] Revealed type: `(x: int) -> int` +constructors_callable.py:81:1: error[missing-argument] No argument provided for required parameter `x` +constructors_callable.py:82:1: error[missing-argument] No argument provided for required parameter `x` +constructors_callable.py:82:4: error[unknown-argument] Argument `y` does not match any known parameter +constructors_callable.py:99:13: info[revealed-type] Revealed type: `(...) -> Unknown` +constructors_callable.py:102:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `Never` +constructors_callable.py:107:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `Never` +constructors_callable.py:127:13: info[revealed-type] Revealed type: `() -> Class6Proxy` +constructors_callable.py:129:4: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 1 +constructors_callable.py:143:27: error[invalid-argument-type] Argument to function `accepts_callable` is incorrect: Expected `() -> Any | Class6Any`, found `` +constructors_callable.py:144:13: info[revealed-type] Revealed type: `() -> Any | Class6Any` +constructors_callable.py:145:1: error[type-assertion-failure] Type `Any | Class6Any` does not match asserted type `Any` +constructors_callable.py:146:8: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 1 +constructors_callable.py:164:5: info[revealed-type] Revealed type: `Overload[[T](x: int) -> Class7[int] | Class7[str], [T](x: str) -> Class7[int] | Class7[str]]` +constructors_callable.py:166:1: error[type-assertion-failure] Type `Class7[int] | Class7[str]` does not match asserted type `Class7[int]` +constructors_callable.py:167:1: error[type-assertion-failure] Type `Class7[int] | Class7[str]` does not match asserted type `Class7[str]` +constructors_callable.py:184:13: info[revealed-type] Revealed type: `[T](x: list[T], y: list[T]) -> Class8[T]` +constructors_callable.py:186:4: error[invalid-argument-type] Argument is incorrect: Expected `list[int | str]`, found `list[int]` +constructors_callable.py:186:9: error[invalid-argument-type] Argument is incorrect: Expected `list[int | str]`, found `list[str]` +constructors_callable.py:195:13: info[revealed-type] Revealed type: `[T](x: list[T], y: list[T]) -> Class9` +constructors_callable.py:197:4: error[invalid-argument-type] Argument is incorrect: Expected `list[int | str]`, found `list[int]` +constructors_callable.py:197:9: error[invalid-argument-type] Argument is incorrect: Expected `list[int | str]`, found `list[str]` +""" diff --git a/conformance/results/ty/constructors_consistency.toml b/conformance/results/ty/constructors_consistency.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/constructors_consistency.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/dataclasses_descriptors.toml b/conformance/results/ty/dataclasses_descriptors.toml new file mode 100644 index 000000000..91ad20b90 --- /dev/null +++ b/conformance/results/ty/dataclasses_descriptors.toml @@ -0,0 +1,13 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Only infers a descriptor `__get__` method as being called when a descriptor attribute is accessed on an instance if the descriptor attribute is present in the class namespace. +""" +errors_diff = """ +Line 66: Unexpected errors ['dataclasses_descriptors.py:66:1: error[type-assertion-failure] Type `int | Desc2[int]` does not match asserted type `int`'] +Line 67: Unexpected errors ['dataclasses_descriptors.py:67:1: error[type-assertion-failure] Type `str | Desc2[str]` does not match asserted type `str`'] +""" +output = """ +dataclasses_descriptors.py:66:1: error[type-assertion-failure] Type `int | Desc2[int]` does not match asserted type `int` +dataclasses_descriptors.py:67:1: error[type-assertion-failure] Type `str | Desc2[str]` does not match asserted type `str` +""" diff --git a/conformance/results/ty/dataclasses_final.toml b/conformance/results/ty/dataclasses_final.toml new file mode 100644 index 000000000..aea48b761 --- /dev/null +++ b/conformance/results/ty/dataclasses_final.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_final.py:27:1: error[invalid-assignment] Cannot assign to final attribute `final_classvar` on type ``: `Final` attributes can only be assigned in the class body, `__init__`, or `__post_init__` on dataclass-like classes +dataclasses_final.py:35:1: error[invalid-assignment] Cannot assign to final attribute `final_no_default` on type `D`: `Final` attributes can only be assigned in the class body, `__init__`, or `__post_init__` on dataclass-like classes +dataclasses_final.py:36:1: error[invalid-assignment] Cannot assign to final attribute `final_with_default` on type `D`: `Final` attributes can only be assigned in the class body, `__init__`, or `__post_init__` on dataclass-like classes +dataclasses_final.py:37:1: error[invalid-assignment] Cannot assign to final attribute `final_no_default` on type ``: `Final` attributes can only be assigned in the class body, `__init__`, or `__post_init__` on dataclass-like classes +dataclasses_final.py:38:1: error[invalid-assignment] Cannot assign to final attribute `final_with_default` on type ``: `Final` attributes can only be assigned in the class body, `__init__`, or `__post_init__` on dataclass-like classes +""" diff --git a/conformance/results/ty/dataclasses_frozen.toml b/conformance/results/ty/dataclasses_frozen.toml new file mode 100644 index 000000000..410316402 --- /dev/null +++ b/conformance/results/ty/dataclasses_frozen.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_frozen.py:16:1: error[invalid-assignment] Property `a` defined in `DC1` is read-only +dataclasses_frozen.py:17:1: error[invalid-assignment] Property `b` defined in `DC1` is read-only +dataclasses_frozen.py:23:7: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `DC2` cannot inherit from frozen dataclass `DC1` +dataclasses_frozen.py:33:7: error[invalid-frozen-dataclass-subclass] Frozen dataclass `DC4` cannot inherit from non-frozen dataclass `DC3` +""" diff --git a/conformance/results/ty/dataclasses_hash.toml b/conformance/results/ty/dataclasses_hash.toml new file mode 100644 index 000000000..8c24b29fa --- /dev/null +++ b/conformance/results/ty/dataclasses_hash.toml @@ -0,0 +1,13 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Understands the `Hashable` protocol as equivalent to `object`. +""" +errors_diff = """ +Line 18: Expected 1 errors +Line 40: Expected 1 errors +""" +output = """ +dataclasses_hash.py:17:1: error[call-non-callable] Object of type `None` is not callable +dataclasses_hash.py:39:1: error[call-non-callable] Object of type `None` is not callable +""" diff --git a/conformance/results/ty/dataclasses_inheritance.toml b/conformance/results/ty/dataclasses_inheritance.toml new file mode 100644 index 000000000..fa6332619 --- /dev/null +++ b/conformance/results/ty/dataclasses_inheritance.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_inheritance.py:62:5: error[invalid-attribute-override] Invalid override of attribute `x`: class variable cannot override instance variable `DC6.x` +dataclasses_inheritance.py:66:5: error[invalid-attribute-override] Invalid override of attribute `y`: instance variable cannot override class variable `DC6.y` +""" diff --git a/conformance/results/ty/dataclasses_kwonly.toml b/conformance/results/ty/dataclasses_kwonly.toml new file mode 100644 index 000000000..902795f6f --- /dev/null +++ b/conformance/results/ty/dataclasses_kwonly.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_kwonly.py:23:11: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2 +dataclasses_kwonly.py:38:11: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2 +dataclasses_kwonly.py:53:11: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2 +""" diff --git a/conformance/results/ty/dataclasses_match_args.toml b/conformance/results/ty/dataclasses_match_args.toml new file mode 100644 index 000000000..2b13c1886 --- /dev/null +++ b/conformance/results/ty/dataclasses_match_args.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_match_args.py:42:1: error[unresolved-attribute] Class `DC4` has no attribute `__match_args__` +""" diff --git a/conformance/results/ty/dataclasses_order.toml b/conformance/results/ty/dataclasses_order.toml new file mode 100644 index 000000000..cd0a0cb47 --- /dev/null +++ b/conformance/results/ty/dataclasses_order.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_order.py:50:4: error[unsupported-operator] Operator `<` is not supported between objects of type `DC1` and `DC2` +""" diff --git a/conformance/results/ty/dataclasses_postinit.toml b/conformance/results/ty/dataclasses_postinit.toml new file mode 100644 index 000000000..83f2f2416 --- /dev/null +++ b/conformance/results/ty/dataclasses_postinit.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_postinit.py:19:9: error[invalid-dataclass] Invalid `__post_init__` signature for dataclass `DC1` +dataclasses_postinit.py:28:7: error[unresolved-attribute] Object of type `DC1` has no attribute `x` +dataclasses_postinit.py:29:7: error[unresolved-attribute] Object of type `DC1` has no attribute `y` +dataclasses_postinit.py:36:9: error[invalid-dataclass] Invalid `__post_init__` signature for dataclass `DC2` +""" diff --git a/conformance/results/ty/dataclasses_slots.toml b/conformance/results/ty/dataclasses_slots.toml new file mode 100644 index 000000000..818e9cd48 --- /dev/null +++ b/conformance/results/ty/dataclasses_slots.toml @@ -0,0 +1,14 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Synthesizes a `__slots__` attribute but does not validate attribute assignments against `__slots__`. +""" +errors_diff = """ +Line 25: Expected 1 errors +Line 38: Expected 1 errors +Lines 10, 11: Expected error (tag 'DC1') +""" +output = """ +dataclasses_slots.py:66:1: error[unresolved-attribute] Class `DC6` has no attribute `__slots__` +dataclasses_slots.py:69:1: error[unresolved-attribute] Object of type `DC6` has no attribute `__slots__` +""" diff --git a/conformance/results/ty/dataclasses_transform_class.toml b/conformance/results/ty/dataclasses_transform_class.toml new file mode 100644 index 000000000..5756e69dc --- /dev/null +++ b/conformance/results/ty/dataclasses_transform_class.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_class.py:51:7: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `Customer1Subclass` cannot inherit from frozen dataclass `Customer1` +dataclasses_transform_class.py:63:1: error[invalid-assignment] Property `id` defined in `Customer1` is read-only +dataclasses_transform_class.py:66:8: error[missing-argument] No arguments provided for required parameters `id`, `name` +dataclasses_transform_class.py:66:18: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 2 +dataclasses_transform_class.py:72:6: error[unsupported-operator] Operator `<` is not supported between two objects of type `Customer1` +dataclasses_transform_class.py:82:8: error[missing-argument] No argument provided for required parameter `id` +dataclasses_transform_class.py:82:18: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 2 +dataclasses_transform_class.py:122:1: error[invalid-assignment] Property `id` defined in `Customer3` is read-only +""" diff --git a/conformance/results/ty/dataclasses_transform_converter.toml b/conformance/results/ty/dataclasses_transform_converter.toml new file mode 100644 index 000000000..e04d01805 --- /dev/null +++ b/conformance/results/ty/dataclasses_transform_converter.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_converter.py:48:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Unknown, /) -> Unknown`, found `def bad_converter1() -> int` +dataclasses_transform_converter.py:49:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(Unknown, /) -> Unknown`, found `def bad_converter2(*, x: int) -> int` +dataclasses_transform_converter.py:107:5: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `Literal[1]` +dataclasses_transform_converter.py:108:23: error[invalid-argument-type] Argument is incorrect: Expected `str | bytes`, found `Literal[1]` +dataclasses_transform_converter.py:109:29: error[invalid-argument-type] Argument is incorrect: Expected `str | list[str]`, found `complex` +dataclasses_transform_converter.py:118:1: error[invalid-assignment] Object of type `Literal[1]` is not assignable to attribute `field0` of type `str` +dataclasses_transform_converter.py:119:1: error[invalid-assignment] Object of type `Literal[1]` is not assignable to attribute `field3` of type `str | bytes` +dataclasses_transform_converter.py:130:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | Literal[1], /) -> int`, found `def converter_simple(s: str) -> int` +dataclasses_transform_converter.py:133:31: error[invalid-argument-type] Argument to function `model_field` is incorrect: Expected `(str | int, /) -> int`, found `def converter_simple(s: str) -> int` +""" diff --git a/conformance/results/ty/dataclasses_transform_field.toml b/conformance/results/ty/dataclasses_transform_field.toml new file mode 100644 index 000000000..62ef62792 --- /dev/null +++ b/conformance/results/ty/dataclasses_transform_field.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_field.py:64:16: error[unknown-argument] Argument `id` does not match any known parameter +dataclasses_transform_field.py:75:1: error[missing-argument] No argument provided for required parameter `name` +dataclasses_transform_field.py:75:16: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 1 +""" diff --git a/conformance/results/ty/dataclasses_transform_func.toml b/conformance/results/ty/dataclasses_transform_func.toml new file mode 100644 index 000000000..2355c3387 --- /dev/null +++ b/conformance/results/ty/dataclasses_transform_func.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_func.py:56:1: error[invalid-assignment] Object of type `Literal[3]` is not assignable to attribute `name` of type `str` +dataclasses_transform_func.py:60:6: error[unsupported-operator] Operator `<` is not supported between two objects of type `Customer1` +dataclasses_transform_func.py:64:36: error[unknown-argument] Argument `salary` does not match any known parameter +dataclasses_transform_func.py:70:8: error[missing-argument] No arguments provided for required parameters `id`, `name` +dataclasses_transform_func.py:70:18: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 2 +dataclasses_transform_func.py:89:7: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `Customer3Subclass` cannot inherit from frozen dataclass `Customer3` +dataclasses_transform_func.py:96:1: error[invalid-assignment] Property `id` defined in `Customer3` is read-only +""" diff --git a/conformance/results/ty/dataclasses_transform_meta.toml b/conformance/results/ty/dataclasses_transform_meta.toml new file mode 100644 index 000000000..a2c5e5028 --- /dev/null +++ b/conformance/results/ty/dataclasses_transform_meta.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_meta.py:51:7: error[invalid-frozen-dataclass-subclass] Non-frozen dataclass `Customer1Subclass` cannot inherit from frozen dataclass `Customer1` +dataclasses_transform_meta.py:63:1: error[invalid-assignment] Property `id` defined in `Customer1` is read-only +dataclasses_transform_meta.py:66:8: error[missing-argument] No arguments provided for required parameters `id`, `name` +dataclasses_transform_meta.py:66:18: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 2 +dataclasses_transform_meta.py:73:6: error[unsupported-operator] Operator `<` is not supported between two objects of type `Customer1` +dataclasses_transform_meta.py:83:8: error[missing-argument] No argument provided for required parameter `id` +dataclasses_transform_meta.py:83:18: error[too-many-positional-arguments] Too many positional arguments: expected 0, got 2 +dataclasses_transform_meta.py:103:1: error[invalid-assignment] Property `id` defined in `Customer3` is read-only +""" diff --git a/conformance/results/ty/dataclasses_usage.toml b/conformance/results/ty/dataclasses_usage.toml new file mode 100644 index 000000000..989dd39fa --- /dev/null +++ b/conformance/results/ty/dataclasses_usage.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_usage.py:51:6: error[missing-argument] No argument provided for required parameter `unit_price` +dataclasses_usage.py:52:28: error[invalid-argument-type] Argument is incorrect: Expected `int | float`, found `Literal["price"]` +dataclasses_usage.py:53:36: error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4 +dataclasses_usage.py:62:5: error[dataclass-field-order] Required field `b` cannot be defined after fields with default values +dataclasses_usage.py:68:5: error[dataclass-field-order] Required field `b` cannot be defined after fields with default values +dataclasses_usage.py:74:5: error[dataclass-field-order] Required field `b` cannot be defined after fields with default values +dataclasses_usage.py:84:13: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2 +dataclasses_usage.py:89:14: error[invalid-assignment] Object of type `dataclasses.Field[str]` is not assignable to `int` +dataclasses_usage.py:128:8: error[too-many-positional-arguments] Too many positional arguments: expected 1, got 2 +dataclasses_usage.py:131:1: error[missing-argument] No argument provided for required parameter `y` of `DC8.__init__` +dataclasses_usage.py:180:6: error[too-many-positional-arguments] Too many positional arguments to `object.__init__`: expected 1, got 2 +dataclasses_usage.py:246:12: error[too-many-positional-arguments] Too many positional arguments: expected 2, got 3 +""" diff --git a/conformance/results/ty/directives_assert_type.toml b/conformance/results/ty/directives_assert_type.toml new file mode 100644 index 000000000..75907e11e --- /dev/null +++ b/conformance/results/ty/directives_assert_type.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_assert_type.py:27:5: error[type-assertion-failure] Type `int | str` does not match asserted type `int` +directives_assert_type.py:28:5: error[type-assertion-failure] Type `int | str` does not match asserted type `Any` +directives_assert_type.py:29:5: error[type-assertion-failure] Type `Any` does not match asserted type `int` +directives_assert_type.py:30:5: error[type-assertion-failure] Type `Literal[4]` does not match asserted type `int` +directives_assert_type.py:32:5: error[missing-argument] No arguments provided for required parameters `value`, `type` of function `assert_type` +directives_assert_type.py:33:5: error[type-assertion-failure] Type `Literal[""]` does not match asserted type `int` +directives_assert_type.py:34:31: error[too-many-positional-arguments] Too many positional arguments to function `assert_type`: expected 2, got 3 +""" diff --git a/conformance/results/ty/directives_cast.toml b/conformance/results/ty/directives_cast.toml new file mode 100644 index 000000000..614f1f93c --- /dev/null +++ b/conformance/results/ty/directives_cast.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_cast.py:15:8: error[missing-argument] No arguments provided for required parameters `typ`, `val` of function `cast` +directives_cast.py:16:13: error[invalid-type-form] Int literals are not allowed in this context in a type expression: Did you mean `typing.Literal[1]`? +directives_cast.py:17:22: error[too-many-positional-arguments] Too many positional arguments to function `cast`: expected 2, got 3 +""" diff --git a/conformance/results/ty/directives_deprecated.toml b/conformance/results/ty/directives_deprecated.toml new file mode 100644 index 000000000..45177b364 --- /dev/null +++ b/conformance/results/ty/directives_deprecated.toml @@ -0,0 +1,24 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not detect calls to deprecated overloads. +Does not detect implicit calls to deprecated dunder methods, for example via operators. +Does not detect accesses of, or attempts to set, deprecated properties. +""" +errors_diff = """ +Line 30: Expected 1 errors +Line 41: Expected 1 errors +Line 42: Expected 1 errors +Line 44: Expected 1 errors +Line 47: Expected 1 errors +Line 48: Expected 1 errors +Line 58: Expected 1 errors +""" +output = """ +directives_deprecated.py:18:44: error[deprecated] The class `Ham` is deprecated: Use Spam instead +directives_deprecated.py:24:9: error[deprecated] The function `norwegian_blue` is deprecated: It is pining for the fjords +directives_deprecated.py:25:13: error[deprecated] The function `norwegian_blue` is deprecated: It is pining for the fjords +directives_deprecated.py:34:7: error[deprecated] The class `Ham` is deprecated: Use Spam instead +directives_deprecated.py:69:1: error[deprecated] The function `lorem` is deprecated: Deprecated +directives_deprecated.py:98:7: error[deprecated] The function `foo` is deprecated: Deprecated +""" diff --git a/conformance/results/ty/directives_disjoint_base.toml b/conformance/results/ty/directives_disjoint_base.toml new file mode 100644 index 000000000..91204fd31 --- /dev/null +++ b/conformance/results/ty/directives_disjoint_base.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +conformant = "Pass" +notes = """ +""" +errors_diff = """ +""" +output = """ +directives_disjoint_base.py:69:7: error[instance-layout-conflict] Class will raise `TypeError` at runtime due to incompatible bases: Bases `Left` and `Right` cannot be combined in multiple inheritance +directives_disjoint_base.py:73:7: error[instance-layout-conflict] Class will raise `TypeError` at runtime due to incompatible bases: Bases `LeftChild` and `Right` cannot be combined in multiple inheritance +directives_disjoint_base.py:77:7: error[instance-layout-conflict] Class will raise `TypeError` at runtime due to incompatible bases: Bases `LeftAndPlain` and `Right` cannot be combined in multiple inheritance +directives_disjoint_base.py:81:7: error[instance-layout-conflict] Class will raise `TypeError` at runtime due to incompatible bases: Bases `Left` and `Record` cannot be combined in multiple inheritance +directives_disjoint_base.py:105:7: error[instance-layout-conflict] Class will raise `TypeError` at runtime due to incompatible bases: Bases `SlotBase1` and `SlotBase2` cannot be combined in multiple inheritance +directives_disjoint_base.py:113:1: error[invalid-argument-type] Argument to function `disjoint_base` is incorrect: Argument type `def func() -> None` does not satisfy upper bound `type` of type variable `_TC` +directives_disjoint_base.py:118:1: error[invalid-typed-dict-header] `@disjoint_base` cannot be used with `TypedDict` class `Movie` +directives_disjoint_base.py:123:1: error[invalid-protocol] `@disjoint_base` cannot be used with protocol class `SupportsClose` +""" diff --git a/conformance/results/ty/directives_no_type_check.toml b/conformance/results/ty/directives_no_type_check.toml new file mode 100644 index 000000000..bbaa658bc --- /dev/null +++ b/conformance/results/ty/directives_no_type_check.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_no_type_check.py:15:14: error[invalid-assignment] Object of type `Literal[""]` is not assignable to `int` +directives_no_type_check.py:29:7: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `int`, found `Literal[b"invalid"]` +directives_no_type_check.py:29:19: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `str`, found `Literal[b"arguments"]` +directives_no_type_check.py:32:1: error[missing-argument] No arguments provided for required parameters `a`, `b` of function `func1` +""" diff --git a/conformance/results/ty/directives_reveal_type.toml b/conformance/results/ty/directives_reveal_type.toml new file mode 100644 index 000000000..aa525cb4c --- /dev/null +++ b/conformance/results/ty/directives_reveal_type.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_reveal_type.py:14:17: info[revealed-type] Revealed type: `int | str` +directives_reveal_type.py:15:17: info[revealed-type] Revealed type: `list[int]` +directives_reveal_type.py:16:17: info[revealed-type] Revealed type: `Any` +directives_reveal_type.py:17:17: info[revealed-type] Revealed type: `ForwardReference` +directives_reveal_type.py:19:5: error[missing-argument] No argument provided for required parameter `obj` of function `reveal_type` +directives_reveal_type.py:20:20: error[too-many-positional-arguments] Too many positional arguments to function `reveal_type`: expected 1, got 2 +""" diff --git a/conformance/results/ty/directives_type_checking.toml b/conformance/results/ty/directives_type_checking.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/directives_type_checking.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/directives_type_ignore.toml b/conformance/results/ty/directives_type_ignore.toml new file mode 100644 index 000000000..d4a44dd67 --- /dev/null +++ b/conformance/results/ty/directives_type_ignore.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_type_ignore.py:16:10: error[invalid-assignment] Object of type `Literal[""]` is not assignable to `int` +""" diff --git a/conformance/results/ty/directives_type_ignore_file1.toml b/conformance/results/ty/directives_type_ignore_file1.toml new file mode 100644 index 000000000..4c7c7e858 --- /dev/null +++ b/conformance/results/ty/directives_type_ignore_file1.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_type_ignore_file1.py:11:7: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive +directives_type_ignore_file1.py:14:17: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive +""" diff --git a/conformance/results/ty/directives_type_ignore_file2.toml b/conformance/results/ty/directives_type_ignore_file2.toml new file mode 100644 index 000000000..76fb1fde2 --- /dev/null +++ b/conformance/results/ty/directives_type_ignore_file2.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_type_ignore_file2.py:7:1: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive +directives_type_ignore_file2.py:9:7: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive +directives_type_ignore_file2.py:12:17: warning[unused-type-ignore-comment] Unused blanket `type: ignore` directive +directives_type_ignore_file2.py:14:10: error[invalid-assignment] Object of type `Literal[""]` is not assignable to `int` +""" diff --git a/conformance/results/ty/directives_version_platform.toml b/conformance/results/ty/directives_version_platform.toml new file mode 100644 index 000000000..c74b90393 --- /dev/null +++ b/conformance/results/ty/directives_version_platform.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_version_platform.py:33:19: error[unresolved-reference] Name `val3` used when not defined +directives_version_platform.py:50:19: error[unresolved-reference] Name `val6` used when not defined +directives_version_platform.py:59:19: error[unresolved-reference] Name `val9` used when not defined +directives_version_platform.py:66:19: error[unresolved-reference] Name `val10` used when not defined +directives_version_platform.py:75:19: error[unresolved-reference] Name `val13` used when not defined +""" diff --git a/conformance/results/ty/enums_behaviors.toml b/conformance/results/ty/enums_behaviors.toml new file mode 100644 index 000000000..2a9f002be --- /dev/null +++ b/conformance/results/ty/enums_behaviors.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_behaviors.py:28:1: error[type-assertion-failure] Type `Color` does not match asserted type `Literal[Color.RED]` +enums_behaviors.py:32:1: error[type-assertion-failure] Type `Color` does not match asserted type `Literal[Color.BLUE]` +enums_behaviors.py:44:21: error[subclass-of-final-class] Class `ExtendedShape` cannot inherit from final class `Shape` +""" diff --git a/conformance/results/ty/enums_definition.toml b/conformance/results/ty/enums_definition.toml new file mode 100644 index 000000000..09271fcdf --- /dev/null +++ b/conformance/results/ty/enums_definition.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_definition.py:25:32: error[too-many-positional-arguments] Too many positional arguments to function `Enum`: expected 2, got 4 +enums_definition.py:34:33: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +enums_definition.py:92:1: error[unresolved-attribute] Class `Color12` has no attribute `BLUE` +""" diff --git a/conformance/results/ty/enums_expansion.toml b/conformance/results/ty/enums_expansion.toml new file mode 100644 index 000000000..5e5e1f0de --- /dev/null +++ b/conformance/results/ty/enums_expansion.toml @@ -0,0 +1,10 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """Does not support `enum.Flag`.""" +errors_diff = """ +Line 53: Expected 1 errors +Line 52: Unexpected errors ['enums_expansion.py:52:9: error[type-assertion-failure] Type `Literal[CustomFlags.FLAG3]` does not match asserted type `CustomFlags`'] +""" +output = """ +enums_expansion.py:52:9: error[type-assertion-failure] Type `Literal[CustomFlags.FLAG3]` does not match asserted type `CustomFlags` +""" diff --git a/conformance/results/ty/enums_member_names.toml b/conformance/results/ty/enums_member_names.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/enums_member_names.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/enums_member_values.toml b/conformance/results/ty/enums_member_values.toml new file mode 100644 index 000000000..2686347f9 --- /dev/null +++ b/conformance/results/ty/enums_member_values.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_member_values.py:50:5: error[invalid-assignment] Enum member `MARS` is incompatible with `__init__` +enums_member_values.py:51:5: error[invalid-assignment] Enum member `JUPITER` is incompatible with `__init__` +enums_member_values.py:54:1: error[type-assertion-failure] Type `Any` does not match asserted type `Literal[1]` +enums_member_values.py:78:5: error[invalid-assignment] Enum member `GREEN` value is not assignable to expected type +enums_member_values.py:85:9: error[invalid-assignment] Object of type `int` is not assignable to attribute `_value_` of type `str` +""" diff --git a/conformance/results/ty/enums_members.toml b/conformance/results/ty/enums_members.toml new file mode 100644 index 000000000..26dff7c35 --- /dev/null +++ b/conformance/results/ty/enums_members.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_members.py:54:10: error[invalid-enum-member-annotation] Type annotation on enum member `DOG` is not allowed +enums_members.py:86:20: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +enums_members.py:87:20: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +enums_members.py:88:18: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +enums_members.py:89:16: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +enums_members.py:120:1: error[type-assertion-failure] Type `int` does not match asserted type `Unknown` +enums_members.py:120:32: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +enums_members.py:132:21: info[revealed-type] Revealed type: `int` +enums_members.py:133:9: error[type-assertion-failure] Type `int` does not match asserted type `Unknown` +enums_members.py:133:43: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +""" diff --git a/conformance/results/ty/exceptions_context_managers.toml b/conformance/results/ty/exceptions_context_managers.toml new file mode 100644 index 000000000..8ba800645 --- /dev/null +++ b/conformance/results/ty/exceptions_context_managers.toml @@ -0,0 +1,10 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 50: Unexpected errors ['exceptions_context_managers.py:50:5: error[type-assertion-failure] Type `str` does not match asserted type `int | str`'] +Line 57: Unexpected errors ['exceptions_context_managers.py:57:5: error[type-assertion-failure] Type `str` does not match asserted type `int | str`'] +""" +output = """ +exceptions_context_managers.py:50:5: error[type-assertion-failure] Type `str` does not match asserted type `int | str` +exceptions_context_managers.py:57:5: error[type-assertion-failure] Type `str` does not match asserted type `int | str` +""" diff --git a/conformance/results/ty/generics_base_class.toml b/conformance/results/ty/generics_base_class.toml new file mode 100644 index 000000000..a76b7a8bf --- /dev/null +++ b/conformance/results/ty/generics_base_class.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_base_class.py:26:26: error[invalid-argument-type] Argument to function `takes_dict_incorrect` is incorrect: Expected `dict[str, list[object]]`, found `SymbolTable` +generics_base_class.py:29:14: error[invalid-type-form] `typing.Generic` is not allowed in parameter annotations +generics_base_class.py:30:8: error[invalid-type-form] `typing.Generic` is not allowed in type expressions +generics_base_class.py:49:38: error[invalid-type-arguments] Too many type arguments to class `LinkedList`: expected 1, got 2 +generics_base_class.py:61:30: error[invalid-type-arguments] Too many type arguments to class `MyDict`: expected 1, got 2 +generics_base_class.py:68:17: error[invalid-generic-class] Type parameter `T` cannot appear multiple times in `Generic` subscription +generics_base_class.py:98:7: error[invalid-generic-class] Inconsistent type arguments: class cannot inherit from both `Grandparent[T2@BadChild, T1@BadChild]` and `Grandparent[T1@BadChild, T2@BadChild]` +""" diff --git a/conformance/results/ty/generics_basic.toml b/conformance/results/ty/generics_basic.toml new file mode 100644 index 000000000..463ed0f7d --- /dev/null +++ b/conformance/results/ty/generics_basic.toml @@ -0,0 +1,22 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Incorrectly allows constrained type variables to be solved to a union of their constraints. +""" +errors_diff = """ +Line 40: Expected 1 errors +Line 41: Expected 1 errors +Line 69: Expected 1 errors +""" +output = """ +generics_basic.py:49:44: error[invalid-legacy-type-variable] A `TypeVar` cannot have exactly one constraint +generics_basic.py:55:53: error[invalid-type-variable-constraints] TypeVar constraint cannot be generic +generics_basic.py:121:13: error[invalid-generic-class] Type parameter `T` cannot appear multiple times in `Generic` subscription +generics_basic.py:157:5: error[invalid-argument-type] Method `__getitem__` of type `bound method MyMap1[str, int].__getitem__(key: str, /) -> int` cannot be called with key of type `Literal[0]` on object of type `MyMap1[str, int]` +generics_basic.py:158:5: error[invalid-argument-type] Method `__getitem__` of type `bound method MyMap2[int, str].__getitem__(key: str, /) -> int` cannot be called with key of type `Literal[0]` on object of type `MyMap2[int, str]` +generics_basic.py:162:12: error[invalid-argument-type] `` is not a valid argument to `Generic` +generics_basic.py:163:12: error[invalid-argument-type] `` is not a valid argument to `Protocol` +generics_basic.py:171:1: error[invalid-generic-class] `Generic` base class must include all type variables used in other base classes +generics_basic.py:172:1: error[invalid-generic-class] `Generic` base class must include all type variables used in other base classes +generics_basic.py:208:27: error[invalid-metaclass] Generic metaclasses are not supported +""" diff --git a/conformance/results/ty/generics_defaults.toml b/conformance/results/ty/generics_defaults.toml new file mode 100644 index 000000000..d8887a2e3 --- /dev/null +++ b/conformance/results/ty/generics_defaults.toml @@ -0,0 +1,30 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not forbid a `TypeVar` immediately following a `TypeVarTuple` in a parameter list from having a default. +Does not support `TypeVarTuple`. +""" +errors_diff = """ +Line 188: Expected 1 errors +Line 139: Unexpected errors ['generics_defaults.py:139:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str, int]`'] +Line 140: Unexpected errors ['generics_defaults.py:140:5: error[type-assertion-failure] Type `Class_TypeVarTuple` does not match asserted type `@Todo`'] +Line 200: Unexpected errors ['generics_defaults.py:200:17: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...`'] +Line 204: Unexpected errors ['generics_defaults.py:204:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `tuple[int, str]`'] +Line 205: Unexpected errors ['generics_defaults.py:205:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `(int | float, bool, /) -> None`'] +Line 207: Unexpected errors ['generics_defaults.py:207:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `tuple[int, str]`'] +Line 208: Unexpected errors ['generics_defaults.py:208:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `(bytes, /) -> None`'] +""" +output = """ +generics_defaults.py:24:40: error[invalid-generic-class] Type parameter `T` without a default cannot follow earlier parameter `DefaultStrT` with a default +generics_defaults.py:66:8: error[invalid-type-arguments] No type argument provided for required type variable `T2` of class `AllTheDefaults` +generics_defaults.py:139:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str, int]` +generics_defaults.py:140:5: error[type-assertion-failure] Type `Class_TypeVarTuple` does not match asserted type `@Todo` +generics_defaults.py:152:51: error[invalid-type-variable-default] TypeVar default is not assignable to the TypeVar's upper bound +generics_defaults.py:159:52: error[invalid-type-variable-default] TypeVar default is inconsistent with the TypeVar's constraints: `int` is not one of the constraints of `Invalid2` +generics_defaults.py:177:1: error[type-assertion-failure] Type `int` does not match asserted type `Any` +generics_defaults.py:200:17: error[invalid-type-form] The first argument to `Callable` must be either a list of types, ParamSpec, Concatenate, or `...` +generics_defaults.py:204:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `tuple[int, str]` +generics_defaults.py:205:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `(int | float, bool, /) -> None` +generics_defaults.py:207:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `tuple[int, str]` +generics_defaults.py:208:5: error[type-assertion-failure] Type `@Todo` does not match asserted type `(bytes, /) -> None` +""" diff --git a/conformance/results/ty/generics_defaults_referential.toml b/conformance/results/ty/generics_defaults_referential.toml new file mode 100644 index 000000000..7df90ae84 --- /dev/null +++ b/conformance/results/ty/generics_defaults_referential.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_defaults_referential.py:37:17: error[invalid-argument-type] Argument to `Foo.__init__` is incorrect: Expected `int`, found `str` +generics_defaults_referential.py:38:14: error[invalid-argument-type] Argument to `Foo.__init__` is incorrect: Expected `int`, found `str` +generics_defaults_referential.py:54:7: error[invalid-generic-class] Default of `Start2T` cannot reference out-of-scope type variable `StopT` +generics_defaults_referential.py:61:11: error[invalid-generic-class] Default of `S2` cannot reference out-of-scope type variable `S1` +generics_defaults_referential.py:69:40: error[invalid-type-variable-default] Default `X1` of TypeVar `Invalid1` is not assignable to upper bound `str` of `Invalid1` because its upper bound `int` is not assignable to `str` +generics_defaults_referential.py:75:52: error[invalid-type-variable-default] TypeVar default is inconsistent with the TypeVar's constraints: Bounded TypeVar cannot be used as the default for a constrained TypeVar +generics_defaults_referential.py:79:63: error[invalid-type-variable-default] Default `Y2` of TypeVar `AlsoInvalid2` is inconsistent with its constraints `AlsoInvalid2` because constraint `int` of `Y2` is not one of the constraints of `AlsoInvalid2` +""" diff --git a/conformance/results/ty/generics_defaults_specialization.toml b/conformance/results/ty/generics_defaults_specialization.toml new file mode 100644 index 000000000..062e89daa --- /dev/null +++ b/conformance/results/ty/generics_defaults_specialization.toml @@ -0,0 +1,12 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject subscription of an already-specialized generic class. +""" +errors_diff = """ +Line 56: Expected 1 errors +""" +output = """ +generics_defaults_specialization.py:30:15: error[invalid-type-arguments] Too many type arguments: expected between 0 and 1, got 2 +generics_defaults_specialization.py:46:22: error[invalid-assignment] Object of type `` is not assignable to `type[Bar[int]]` +""" diff --git a/conformance/results/ty/generics_paramspec_basic.toml b/conformance/results/ty/generics_paramspec_basic.toml new file mode 100644 index 000000000..3c1f8ca1b --- /dev/null +++ b/conformance/results/ty/generics_paramspec_basic.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_paramspec_basic.py:10:23: error[mismatched-type-name] The name passed to `ParamSpec` must match the variable it is assigned to: Expected "WrongName", got "NotIt" +generics_paramspec_basic.py:15:18: error[invalid-type-form] Bare ParamSpec `P` is not valid in this context in a type alias value +generics_paramspec_basic.py:23:14: error[invalid-type-form] Bare ParamSpec `P` is not valid in this context in a parameter annotation +generics_paramspec_basic.py:23:20: error[invalid-type-form] Bare ParamSpec `P` is not valid in this context in a return type annotation +generics_paramspec_basic.py:27:14: error[invalid-type-form] `typing.Concatenate` is not allowed in this context in a parameter annotation +generics_paramspec_basic.py:31:19: error[invalid-type-arguments] ParamSpec `P` cannot be used to specialize type variable `_T` +generics_paramspec_basic.py:35:35: error[invalid-type-form] Bare ParamSpec `P` is not valid in this context in a parameter annotation +generics_paramspec_basic.py:39:18: error[invalid-type-form] Bare ParamSpec `P` is not valid in this context in a parameter annotation +generics_paramspec_basic.py:39:31: error[invalid-type-form] Bare ParamSpec `P` is not valid in this context in a parameter annotation +""" diff --git a/conformance/results/ty/generics_paramspec_components.toml b/conformance/results/ty/generics_paramspec_components.toml new file mode 100644 index 000000000..21ef9bf9b --- /dev/null +++ b/conformance/results/ty/generics_paramspec_components.toml @@ -0,0 +1,31 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Incorrectly allows using `*args: P.args` and `**kwargs: P.kwargs` when `P` has not been put into scope by any other parameter annotation or enclosing scope. +""" +errors_diff = """ +Line 30: Expected 1 errors +""" +output = """ +generics_paramspec_components.py:17:25: error[invalid-type-form] `P.kwargs` is valid only in `**kwargs` annotation: Did you mean `P.args`? +generics_paramspec_components.py:17:45: error[invalid-type-form] `P.args` is valid only in `*args` annotation: Did you mean `P.kwargs`? +generics_paramspec_components.py:20:23: error[invalid-paramspec] `P.args` is only valid for annotating `*args` +generics_paramspec_components.py:23:46: error[invalid-paramspec] `*args: P.args` must be accompanied by `**kwargs: P.kwargs` +generics_paramspec_components.py:23:46: error[invalid-type-form] `P.args` is valid only in `*args` annotation: Did you mean `P.kwargs`? +generics_paramspec_components.py:26:46: error[invalid-paramspec] `*args: P.args` must be accompanied by `**kwargs: P.kwargs` +generics_paramspec_components.py:35:18: error[invalid-paramspec] `P.args` is only valid for annotating `*args` function parameters +generics_paramspec_components.py:36:20: error[invalid-paramspec] `P.kwargs` is only valid for annotating `**kwargs` function parameters +generics_paramspec_components.py:38:26: error[invalid-paramspec] `*args: P.args` must be accompanied by `**kwargs: P.kwargs` +generics_paramspec_components.py:41:31: error[invalid-paramspec] `**kwargs: P.kwargs` must be accompanied by `*args: P.args` +generics_paramspec_components.py:49:11: error[invalid-argument-type] Argument is incorrect: Expected `P@decorator.args`, found `P@decorator.kwargs` +generics_paramspec_components.py:49:20: error[invalid-argument-type] Argument is incorrect: Expected `P@decorator.kwargs`, found `P@decorator.args` +generics_paramspec_components.py:51:11: error[invalid-argument-type] Argument is incorrect: Expected `P@decorator.args`, found `Literal[1]` +generics_paramspec_components.py:60:28: error[invalid-paramspec] No parameters may appear between `*args: P.args` and `**kwargs: P.kwargs` +generics_paramspec_components.py:70:11: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `P@remove.args` +generics_paramspec_components.py:70:18: error[invalid-argument-type] Argument is incorrect: Expected `P@remove.args`, found `Literal[1]` +generics_paramspec_components.py:72:11: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `P@remove.args` +generics_paramspec_components.py:83:18: error[invalid-argument-type] Argument to function `foo` is incorrect: Expected `int`, found `(...)` +generics_paramspec_components.py:83:18: error[parameter-already-assigned] Multiple values provided for parameter 1 (`x`) of function `foo` +generics_paramspec_components.py:98:20: error[invalid-argument-type] Argument to function `twice` is incorrect: Expected `int`, found `Literal["A"]` +generics_paramspec_components.py:98:25: error[invalid-argument-type] Argument to function `twice` is incorrect: Expected `str`, found `Literal[1]` +""" diff --git a/conformance/results/ty/generics_paramspec_semantics.toml b/conformance/results/ty/generics_paramspec_semantics.toml new file mode 100644 index 000000000..2a84cbc37 --- /dev/null +++ b/conformance/results/ty/generics_paramspec_semantics.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_paramspec_semantics.py:26:4: error[positional-only-parameter-as-kwarg] Positional-only parameter 1 (`a`) passed as keyword argument +generics_paramspec_semantics.py:26:11: error[positional-only-parameter-as-kwarg] Positional-only parameter 2 (`b`) passed as keyword argument +generics_paramspec_semantics.py:27:9: error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Literal["A"]` +generics_paramspec_semantics.py:46:17: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `(x: int, y: str) -> int`, found `def y_x(y: int, x: str) -> int` +generics_paramspec_semantics.py:61:23: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `(*, x: int) -> int`, found `def keyword_only_y(*, y: int) -> int` +generics_paramspec_semantics.py:98:4: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `Literal[1]` +generics_paramspec_semantics.py:108:4: error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Literal[1]` +generics_paramspec_semantics.py:120:4: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `Literal[1]` +generics_paramspec_semantics.py:127:1: error[invalid-argument-type] Argument to function `expects_int_first` is incorrect: Expected `(int, /, *args: Unknown, **kwargs: Unknown) -> int`, found `def one(x: str) -> int` +generics_paramspec_semantics.py:132:1: error[invalid-argument-type] Argument to function `expects_int_first` is incorrect: Expected `(int, /, *args: Unknown, **kwargs: Unknown) -> int`, found `def two(*, x: int) -> int` +generics_paramspec_semantics.py:137:1: error[invalid-argument-type] Argument to function `expects_int_first` is incorrect: Expected `(int, /, *args: Unknown, **kwargs: Unknown) -> int`, found `def three(**kwargs: int) -> int` +""" diff --git a/conformance/results/ty/generics_paramspec_specialization.toml b/conformance/results/ty/generics_paramspec_specialization.toml new file mode 100644 index 000000000..b95502032 --- /dev/null +++ b/conformance/results/ty/generics_paramspec_specialization.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_paramspec_specialization.py:44:27: error[invalid-type-arguments] Type argument for `ParamSpec` must be either a list of types, `ParamSpec`, `Concatenate`, or `...` +generics_paramspec_specialization.py:54:9: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]` +generics_paramspec_specialization.py:55:16: error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Literal[""]` +generics_paramspec_specialization.py:60:9: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]` +generics_paramspec_specialization.py:61:16: error[invalid-argument-type] Argument is incorrect: Expected `bool`, found `Literal[""]` +""" diff --git a/conformance/results/ty/generics_scoping.toml b/conformance/results/ty/generics_scoping.toml new file mode 100644 index 000000000..b37df7322 --- /dev/null +++ b/conformance/results/ty/generics_scoping.toml @@ -0,0 +1,25 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject `list[T]()` in the global scope, where `T` is an unbound type variable. +Does nto reject `alias: TypeAlias = list[T]` in the body scope of a class generic over a type variable `T`. +""" +errors_diff = """ +Line 98: Expected 1 errors +Line 107: Expected 1 errors +""" +output = """ +generics_scoping.py:15:1: error[type-assertion-failure] Type `Literal[1]` does not match asserted type `int` +generics_scoping.py:19:1: error[type-assertion-failure] Type `Literal["a"]` does not match asserted type `str` +generics_scoping.py:34:10: error[invalid-argument-type] Argument to bound method `MyClass.meth_2` is incorrect: Expected `int`, found `Literal["a"]` +generics_scoping.py:49:1: error[type-assertion-failure] Type `Literal["abc"]` does not match asserted type `str` +generics_scoping.py:53:1: error[type-assertion-failure] Type `Literal[b"abc"]` does not match asserted type `bytes` +generics_scoping.py:61:13: error[unbound-type-variable] Type variable `S` is not bound to any outer generic context +generics_scoping.py:65:19: error[unbound-type-variable] Type variable `S` is not bound to any outer generic context +generics_scoping.py:76:11: error[shadowed-type-variable] Generic class `MyGeneric` uses type variable `T` already bound by an enclosing scope +generics_scoping.py:76:11: error[shadowed-type-variable] Generic class `MyGeneric` uses type variable `T` already bound by an enclosing scope +generics_scoping.py:86:11: error[shadowed-type-variable] Generic class `Bad` uses type variable `T` already bound by an enclosing scope +generics_scoping.py:89:17: error[unbound-type-variable] Type variable `T` is not bound to any outer generic context +generics_scoping.py:105:14: error[unbound-type-variable] Type variable `T` is not bound to any outer generic context +generics_scoping.py:106:19: error[unbound-type-variable] Type variable `T` is not bound to any outer generic context +""" diff --git a/conformance/results/ty/generics_self_advanced.toml b/conformance/results/ty/generics_self_advanced.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/generics_self_advanced.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/generics_self_attributes.toml b/conformance/results/ty/generics_self_attributes.toml new file mode 100644 index 000000000..cca34273e --- /dev/null +++ b/conformance/results/ty/generics_self_attributes.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_self_attributes.py:26:33: error[invalid-argument-type] Argument is incorrect: Expected `OrdinalLinkedList | None`, found `LinkedList[int]` +generics_self_attributes.py:32:5: error[invalid-assignment] Object of type `LinkedList[int]` is not assignable to attribute `next` of type `OrdinalLinkedList | None` +""" diff --git a/conformance/results/ty/generics_self_basic.toml b/conformance/results/ty/generics_self_basic.toml new file mode 100644 index 000000000..7e56f458c --- /dev/null +++ b/conformance/results/ty/generics_self_basic.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_self_basic.py:20:16: error[invalid-return-type] Return type does not match returned value: expected `Self@method2`, found `Shape` +generics_self_basic.py:33:16: error[invalid-return-type] Return type does not match returned value: expected `Self@cls_method2`, found `Shape` +generics_self_basic.py:68:26: error[invalid-type-form] Special form `typing.Self` expected no type parameter +""" diff --git a/conformance/results/ty/generics_self_protocols.toml b/conformance/results/ty/generics_self_protocols.toml new file mode 100644 index 000000000..5d9cc9a00 --- /dev/null +++ b/conformance/results/ty/generics_self_protocols.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_self_protocols.py:61:19: error[invalid-argument-type] Argument to function `accepts_shape` is incorrect: Expected `ShapeProtocol`, found `BadReturnType` +generics_self_protocols.py:64:19: error[invalid-argument-type] Argument to function `accepts_shape` is incorrect: Expected `ShapeProtocol`, found `ReturnDifferentClass` +""" diff --git a/conformance/results/ty/generics_self_usage.toml b/conformance/results/ty/generics_self_usage.toml new file mode 100644 index 000000000..b60b6f256 --- /dev/null +++ b/conformance/results/ty/generics_self_usage.toml @@ -0,0 +1,22 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject `Self` used in a return annotation when `self` is annotated using another type variable. +""" +errors_diff = """ +Line 82: Expected 1 errors +""" +output = """ +generics_self_usage.py:73:14: error[invalid-type-form] Variable of type `` is not allowed in a parameter annotation +generics_self_usage.py:73:23: error[invalid-type-form] Variable of type `` is not allowed in a return type annotation +generics_self_usage.py:76:6: error[invalid-type-form] Variable of type `` is not allowed in a type expression +generics_self_usage.py:87:16: error[invalid-return-type] Return type does not match returned value: expected `Self@return_concrete_type`, found `Foo3` +generics_self_usage.py:103:15: error[invalid-type-form] Variable of type `` is not allowed in a type expression +generics_self_usage.py:105:12: error[invalid-base] Invalid class base with type `` +generics_self_usage.py:108:30: error[invalid-type-form] Variable of type `` is not allowed in a type alias value +generics_self_usage.py:113:19: error[invalid-type-form] `Self` cannot be used in a static method +generics_self_usage.py:118:31: error[invalid-type-form] `Self` cannot be used in a static method +generics_self_usage.py:118:40: error[invalid-type-form] `Self` cannot be used in a static method +generics_self_usage.py:123:37: error[invalid-type-form] `Self` cannot be used in a metaclass +generics_self_usage.py:127:42: error[invalid-type-form] `Self` cannot be used in a metaclass +""" diff --git a/conformance/results/ty/generics_syntax_compatibility.toml b/conformance/results/ty/generics_syntax_compatibility.toml new file mode 100644 index 000000000..51432fe82 --- /dev/null +++ b/conformance/results/ty/generics_syntax_compatibility.toml @@ -0,0 +1,8 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 14: Expected 1 errors +Line 26: Expected 1 errors +""" +output = """ +""" diff --git a/conformance/results/ty/generics_syntax_declarations.toml b/conformance/results/ty/generics_syntax_declarations.toml new file mode 100644 index 000000000..39031dc2e --- /dev/null +++ b/conformance/results/ty/generics_syntax_declarations.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_syntax_declarations.py:17:1: error[invalid-generic-class] Cannot both inherit from `typing.Generic` and use PEP 695 type variables +generics_syntax_declarations.py:25:20: error[invalid-generic-class] Cannot both inherit from subscripted `Protocol` and use PEP 695 type variables +generics_syntax_declarations.py:32:9: error[unresolved-attribute] Object of type `T@ClassD` has no attribute `is_integer` +generics_syntax_declarations.py:44:21: error[invalid-type-variable-bound] TypeVar upper bound cannot be generic +generics_syntax_declarations.py:48:17: error[invalid-type-form] List literals are not allowed in this context in a type expression +generics_syntax_declarations.py:60:17: error[invalid-type-variable-constraints] TypeVar must have at least two constrained types +generics_syntax_declarations.py:64:17: error[invalid-type-variable-constraints] TypeVar must have at least two constrained types +generics_syntax_declarations.py:71:17: error[invalid-type-form] Variable of type `tuple[, ]` is not allowed in a type expression +generics_syntax_declarations.py:75:18: error[invalid-type-form] Int literals are not allowed in this context in a type expression: Did you mean `typing.Literal[3]`? +generics_syntax_declarations.py:79:23: error[unresolved-reference] Name `S` used when not defined +""" diff --git a/conformance/results/ty/generics_syntax_infer_variance.toml b/conformance/results/ty/generics_syntax_infer_variance.toml new file mode 100644 index 000000000..d1af4ea25 --- /dev/null +++ b/conformance/results/ty/generics_syntax_infer_variance.toml @@ -0,0 +1,23 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_syntax_infer_variance.py:15:6: error[invalid-legacy-type-variable] A `TypeVar` cannot specify variance when `infer_variance=True` +generics_syntax_infer_variance.py:17:6: error[invalid-legacy-type-variable] A `TypeVar` cannot specify variance when `infer_variance=True` +generics_syntax_infer_variance.py:29:35: error[invalid-assignment] Object of type `ShouldBeCovariant1[int | float]` is not assignable to `ShouldBeCovariant1[int]` +generics_syntax_infer_variance.py:47:35: error[invalid-assignment] Object of type `ShouldBeCovariant2[int | float]` is not assignable to `ShouldBeCovariant2[int]` +generics_syntax_infer_variance.py:56:35: error[invalid-assignment] Object of type `ShouldBeCovariant3[int | float]` is not assignable to `ShouldBeCovariant3[int]` +generics_syntax_infer_variance.py:85:34: error[invalid-assignment] Object of type `ShouldBeCovariant5[int | float]` is not assignable to `ShouldBeCovariant5[int]` +generics_syntax_infer_variance.py:96:34: error[invalid-assignment] Object of type `ShouldBeCovariant6[int | float]` is not assignable to `ShouldBeCovariant6[int]` +generics_syntax_infer_variance.py:112:38: error[invalid-assignment] Object of type `ShouldBeInvariant1[int]` is not assignable to `ShouldBeInvariant1[int | float]` +generics_syntax_infer_variance.py:113:36: error[invalid-assignment] Object of type `ShouldBeInvariant1[int | float]` is not assignable to `ShouldBeInvariant1[int]` +generics_syntax_infer_variance.py:127:38: error[invalid-assignment] Object of type `ShouldBeInvariant2[int]` is not assignable to `ShouldBeInvariant2[int | float]` +generics_syntax_infer_variance.py:128:36: error[invalid-assignment] Object of type `ShouldBeInvariant2[int | float]` is not assignable to `ShouldBeInvariant2[int]` +generics_syntax_infer_variance.py:135:43: error[invalid-assignment] Object of type `ShouldBeInvariant3[int, str]` is not assignable to `ShouldBeInvariant3[int | float, str]` +generics_syntax_infer_variance.py:136:41: error[invalid-assignment] Object of type `ShouldBeInvariant3[int | float, str]` is not assignable to `ShouldBeInvariant3[int, str]` +generics_syntax_infer_variance.py:137:43: error[invalid-assignment] Object of type `ShouldBeInvariant3[str, int]` is not assignable to `ShouldBeInvariant3[str, int | float]` +generics_syntax_infer_variance.py:138:41: error[invalid-assignment] Object of type `ShouldBeInvariant3[str, int | float]` is not assignable to `ShouldBeInvariant3[str, int]` +generics_syntax_infer_variance.py:146:38: error[invalid-assignment] Object of type `ShouldBeInvariant4[int]` is not assignable to `ShouldBeInvariant4[int | float]` +generics_syntax_infer_variance.py:154:38: error[invalid-assignment] Object of type `ShouldBeInvariant5[int]` is not assignable to `ShouldBeInvariant5[int | float]` +generics_syntax_infer_variance.py:165:45: error[invalid-assignment] Object of type `ShouldBeContravariant1[int]` is not assignable to `ShouldBeContravariant1[int | float]` +""" diff --git a/conformance/results/ty/generics_syntax_scoping.toml b/conformance/results/ty/generics_syntax_scoping.toml new file mode 100644 index 000000000..839a9eda0 --- /dev/null +++ b/conformance/results/ty/generics_syntax_scoping.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_syntax_scoping.py:14:20: error[invalid-type-variable-bound] TypeVar upper bound cannot be generic +generics_syntax_scoping.py:18:17: error[invalid-type-variable-bound] TypeVar upper bound cannot be generic +generics_syntax_scoping.py:35:7: error[unresolved-reference] Name `T` used when not defined +generics_syntax_scoping.py:44:17: error[unresolved-reference] Name `T` used when not defined +generics_syntax_scoping.py:92:9: error[shadowed-type-variable] Generic function `method1` uses type variable `T` already bound by an enclosing scope +generics_syntax_scoping.py:95:9: error[shadowed-type-variable] Generic function `method2` uses type variable `T` already bound by an enclosing scope +generics_syntax_scoping.py:98:9: error[shadowed-type-variable] Generic function `method3` uses type variable `T` already bound by an enclosing scope +""" diff --git a/conformance/results/ty/generics_type_erasure.toml b/conformance/results/ty/generics_type_erasure.toml new file mode 100644 index 000000000..0d180a391 --- /dev/null +++ b/conformance/results/ty/generics_type_erasure.toml @@ -0,0 +1,16 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject access of generic instance variable from the class object. +""" +errors_diff = """ +Line 42: Expected 1 errors +Line 43: Expected 1 errors +Line 44: Expected 1 errors +Line 45: Expected 1 errors +Line 46: Expected 1 errors +""" +output = """ +generics_type_erasure.py:38:16: error[invalid-argument-type] Argument to `Node.__init__` is incorrect: Expected `int | None`, found `Literal[""]` +generics_type_erasure.py:40:16: error[invalid-argument-type] Argument to `Node.__init__` is incorrect: Expected `str | None`, found `Literal[0]` +""" diff --git a/conformance/results/ty/generics_typevartuple_args.toml b/conformance/results/ty/generics_typevartuple_args.toml new file mode 100644 index 000000000..7de7af540 --- /dev/null +++ b/conformance/results/ty/generics_typevartuple_args.toml @@ -0,0 +1,23 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Supports PEP-646 unpacked tuples but not TypeVarTuple. +""" +errors_diff = """ +Line 33: Expected 1 errors +Line 34: Expected 1 errors +Line 48: Expected 1 errors +Line 57: Expected 1 errors +Line 58: Expected 1 errors +Line 59: Expected 1 errors +Line 67: Expected 1 errors +Line 75: Expected 1 errors +Line 29: Unexpected errors ['generics_typevartuple_args.py:29:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int, str]`'] +Line 31: Unexpected errors ['generics_typevartuple_args.py:31:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[()]`'] +Line 32: Unexpected errors ['generics_typevartuple_args.py:32:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int, str]`'] +""" +output = """ +generics_typevartuple_args.py:29:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int, str]` +generics_typevartuple_args.py:31:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[()]` +generics_typevartuple_args.py:32:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int, str]` +""" diff --git a/conformance/results/ty/generics_typevartuple_basic.toml b/conformance/results/ty/generics_typevartuple_basic.toml new file mode 100644 index 000000000..02dace065 --- /dev/null +++ b/conformance/results/ty/generics_typevartuple_basic.toml @@ -0,0 +1,22 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 43: Expected 1 errors +Line 53: Expected 1 errors +Line 56: Expected 1 errors +Line 59: Expected 1 errors +Line 91: Expected 1 errors +Line 100: Expected 1 errors +Line 101: Expected 1 errors +Line 107: Expected 1 errors +Lines 44, 45: Expected error (tag 'v6') +Line 85: Unexpected errors ['generics_typevartuple_basic.py:85:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int]`'] +""" +output = """ +generics_typevartuple_basic.py:42:34: error[invalid-argument-type] Argument to `Array.__init__` is incorrect: Expected `tuple[@Todo(TypeVarTuple), ...]`, found `Height` +generics_typevartuple_basic.py:52:14: error[invalid-generic-class] `TypeVarTuple` must be unpacked with `*` or `Unpack[]` when used as an argument to `Generic` +generics_typevartuple_basic.py:65:27: error[unknown-argument] Argument `covariant` does not match any known parameter of constructor `TypeVarTuple.__new__` +generics_typevartuple_basic.py:66:27: error[too-many-positional-arguments] Too many positional arguments to constructor `TypeVarTuple.__new__`: expected 2, got 4 +generics_typevartuple_basic.py:67:27: error[unknown-argument] Argument `bound` does not match any known parameter of constructor `TypeVarTuple.__new__` +generics_typevartuple_basic.py:85:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int]` +""" diff --git a/conformance/results/ty/generics_typevartuple_callable.toml b/conformance/results/ty/generics_typevartuple_callable.toml new file mode 100644 index 000000000..223d59b3a --- /dev/null +++ b/conformance/results/ty/generics_typevartuple_callable.toml @@ -0,0 +1,13 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 26: Expected 1 errors +Line 41: Unexpected errors ['generics_typevartuple_callable.py:41:1: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str, int, int | float | complex]`'] +Line 42: Unexpected errors ['generics_typevartuple_callable.py:42:1: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str]`'] +Line 50: Unexpected errors ['generics_typevartuple_callable.py:50:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int | float | complex, str, int | float]`'] +""" +output = """ +generics_typevartuple_callable.py:41:1: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str, int, int | float | complex]` +generics_typevartuple_callable.py:42:1: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str]` +generics_typevartuple_callable.py:50:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int | float | complex, str, int | float]` +""" diff --git a/conformance/results/ty/generics_typevartuple_concat.toml b/conformance/results/ty/generics_typevartuple_concat.toml new file mode 100644 index 000000000..e02443283 --- /dev/null +++ b/conformance/results/ty/generics_typevartuple_concat.toml @@ -0,0 +1,8 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 53: Unexpected errors ['generics_typevartuple_concat.py:53:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int, bool, str]`'] +""" +output = """ +generics_typevartuple_concat.py:53:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int, bool, str]` +""" diff --git a/conformance/results/ty/generics_typevartuple_overloads.toml b/conformance/results/ty/generics_typevartuple_overloads.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/generics_typevartuple_overloads.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/generics_typevartuple_specialization.toml b/conformance/results/ty/generics_typevartuple_specialization.toml new file mode 100644 index 000000000..edae9026f --- /dev/null +++ b/conformance/results/ty/generics_typevartuple_specialization.toml @@ -0,0 +1,42 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 109: Expected 1 errors +Line 110: Expected 1 errors +Line 127: Expected 1 errors +Line 163: Expected 1 errors +Line 45: Unexpected errors ["generics_typevartuple_specialization.py:45:40: error[not-subscriptable] Cannot subscript non-generic type ``"] +Line 46: Unexpected errors ['generics_typevartuple_specialization.py:46:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int, int | float, bool]`'] +Line 47: Unexpected errors ['generics_typevartuple_specialization.py:47:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `tuple[str, @Todo]`'] +Line 51: Unexpected errors ['generics_typevartuple_specialization.py:51:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int]`'] +Line 93: Unexpected errors ['generics_typevartuple_specialization.py:93:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str, int]`'] +Line 94: Unexpected errors ['generics_typevartuple_specialization.py:94:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int | float]`'] +Line 95: Unexpected errors ['generics_typevartuple_specialization.py:95:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[Any, *tuple[Any, ...]]`'] +Line 135: Unexpected errors ['generics_typevartuple_specialization.py:135:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown]` does not match asserted type `tuple[tuple[()], str, bool]`'] +Line 136: Unexpected errors ['generics_typevartuple_specialization.py:136:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown]` does not match asserted type `tuple[tuple[str], bool, int | float]`'] +Line 137: Unexpected errors ['generics_typevartuple_specialization.py:137:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown]` does not match asserted type `tuple[tuple[str, bool], int | float, int]`'] +Line 148: Unexpected errors ['generics_typevartuple_specialization.py:148:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown, Unknown]` does not match asserted type `tuple[tuple[()], str, bool, int | float]`'] +Line 149: Unexpected errors ['generics_typevartuple_specialization.py:149:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown, Unknown]` does not match asserted type `tuple[tuple[bool], str, int | float, int]`'] +Line 157: Unexpected errors ['generics_typevartuple_specialization.py:157:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[*tuple[int, ...], int]`'] +Line 158: Unexpected errors ['generics_typevartuple_specialization.py:158:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[*tuple[int, ...], str]`'] +Line 159: Unexpected errors ['generics_typevartuple_specialization.py:159:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[*tuple[int, ...], str]`'] +""" +output = """ +generics_typevartuple_specialization.py:45:40: error[not-subscriptable] Cannot subscript non-generic type `` +generics_typevartuple_specialization.py:46:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int, int | float, bool]` +generics_typevartuple_specialization.py:47:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `tuple[str, @Todo]` +generics_typevartuple_specialization.py:51:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int]` +generics_typevartuple_specialization.py:93:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[str, int]` +generics_typevartuple_specialization.py:94:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[int | float]` +generics_typevartuple_specialization.py:95:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[Any, *tuple[Any, ...]]` +generics_typevartuple_specialization.py:121:7: error[invalid-type-form] Multiple unpacked variadic tuples are not allowed in a `tuple` specialization +generics_typevartuple_specialization.py:122:7: error[invalid-type-form] Multiple unpacked variadic tuples are not allowed in a `tuple` specialization +generics_typevartuple_specialization.py:135:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown]` does not match asserted type `tuple[tuple[()], str, bool]` +generics_typevartuple_specialization.py:136:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown]` does not match asserted type `tuple[tuple[str], bool, int | float]` +generics_typevartuple_specialization.py:137:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown]` does not match asserted type `tuple[tuple[str, bool], int | float, int]` +generics_typevartuple_specialization.py:148:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown, Unknown]` does not match asserted type `tuple[tuple[()], str, bool, int | float]` +generics_typevartuple_specialization.py:149:5: error[type-assertion-failure] Type `tuple[tuple[@Todo(TypeVarTuple), ...], Unknown, Unknown, Unknown]` does not match asserted type `tuple[tuple[bool], str, int | float, int]` +generics_typevartuple_specialization.py:157:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[*tuple[int, ...], int]` +generics_typevartuple_specialization.py:158:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[*tuple[int, ...], str]` +generics_typevartuple_specialization.py:159:5: error[type-assertion-failure] Type `tuple[@Todo(TypeVarTuple), ...]` does not match asserted type `tuple[*tuple[int, ...], str]` +""" diff --git a/conformance/results/ty/generics_typevartuple_unpack.toml b/conformance/results/ty/generics_typevartuple_unpack.toml new file mode 100644 index 000000000..d22971b51 --- /dev/null +++ b/conformance/results/ty/generics_typevartuple_unpack.toml @@ -0,0 +1,7 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 30: Expected 1 errors +""" +output = """ +""" diff --git a/conformance/results/ty/generics_upper_bound.toml b/conformance/results/ty/generics_upper_bound.toml new file mode 100644 index 000000000..47ea50c59 --- /dev/null +++ b/conformance/results/ty/generics_upper_bound.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_upper_bound.py:24:32: error[invalid-type-variable-bound] TypeVar upper bound cannot be generic +generics_upper_bound.py:44:5: error[type-assertion-failure] Type `list[int] | set[int]` does not match asserted type `Collection[int]` +generics_upper_bound.py:52:8: error[invalid-argument-type] Argument to function `longer` is incorrect: Argument type `Literal[3]` does not satisfy upper bound `Sized` of type variable `ST` +generics_upper_bound.py:52:11: error[invalid-argument-type] Argument to function `longer` is incorrect: Argument type `Literal[3]` does not satisfy upper bound `Sized` of type variable `ST` +generics_upper_bound.py:57:10: error[invalid-legacy-type-variable] A `TypeVar` cannot have both a bound and constraints +""" diff --git a/conformance/results/ty/generics_variance.toml b/conformance/results/ty/generics_variance.toml new file mode 100644 index 000000000..dc88240f5 --- /dev/null +++ b/conformance/results/ty/generics_variance.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_variance.py:14:6: error[invalid-legacy-type-variable] A `TypeVar` cannot be both covariant and contravariant +generics_variance.py:77:14: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `Inv` +generics_variance.py:81:14: error[invalid-generic-class] Variance of type variable `T_contra` is incompatible with base class `Inv` +generics_variance.py:93:17: error[invalid-generic-class] Variance of type variable `T_contra` is incompatible with base class `Co` +generics_variance.py:105:21: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `Contra` +generics_variance.py:113:21: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `Contra` +generics_variance.py:126:5: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `CoContra` +generics_variance.py:132:5: error[invalid-generic-class] Variance of type variable `T_contra` is incompatible with base class `CoContra` +generics_variance.py:142:5: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `CoContra` +generics_variance.py:163:26: error[invalid-generic-class] Variance of type variable `T_contra` is incompatible with base class `Contra` +generics_variance.py:167:30: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `Contra` +generics_variance.py:191:33: error[invalid-generic-class] Variance of type variable `T_contra` is incompatible with base class `Contra` +generics_variance.py:196:5: error[invalid-generic-class] Variance of type variable `T_co` is incompatible with base class `Contra` +""" diff --git a/conformance/results/ty/generics_variance_inference.toml b/conformance/results/ty/generics_variance_inference.toml new file mode 100644 index 000000000..1c637eecf --- /dev/null +++ b/conformance/results/ty/generics_variance_inference.toml @@ -0,0 +1,28 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_variance_inference.py:24:33: error[invalid-assignment] Object of type `ClassA[int | float, int, int]` is not assignable to `ClassA[int, int, int]` +generics_variance_inference.py:25:37: error[invalid-assignment] Object of type `ClassA[int | float, int, int]` is not assignable to `ClassA[int | float, int | float, int]` +generics_variance_inference.py:28:33: error[invalid-assignment] Object of type `ClassA[int, int | float, int | float]` is not assignable to `ClassA[int, int, int]` +generics_variance_inference.py:41:35: error[invalid-assignment] Object of type `ShouldBeCovariant1[int | float]` is not assignable to `ShouldBeCovariant1[int]` +generics_variance_inference.py:49:35: error[invalid-assignment] Object of type `ShouldBeCovariant2[int | float]` is not assignable to `ShouldBeCovariant2[int]` +generics_variance_inference.py:58:35: error[invalid-assignment] Object of type `ShouldBeCovariant3[int | float]` is not assignable to `ShouldBeCovariant3[int]` +generics_variance_inference.py:67:34: error[invalid-assignment] Object of type `ShouldBeCovariant4[int | float]` is not assignable to `ShouldBeCovariant4[int]` +generics_variance_inference.py:80:34: error[invalid-assignment] Object of type `ShouldBeCovariant5[int | float]` is not assignable to `ShouldBeCovariant5[int]` +generics_variance_inference.py:96:38: error[invalid-assignment] Object of type `ShouldBeInvariant1[int]` is not assignable to `ShouldBeInvariant1[int | float]` +generics_variance_inference.py:97:36: error[invalid-assignment] Object of type `ShouldBeInvariant1[int | float]` is not assignable to `ShouldBeInvariant1[int]` +generics_variance_inference.py:111:38: error[invalid-assignment] Object of type `ShouldBeInvariant2[int]` is not assignable to `ShouldBeInvariant2[int | float]` +generics_variance_inference.py:112:36: error[invalid-assignment] Object of type `ShouldBeInvariant2[int | float]` is not assignable to `ShouldBeInvariant2[int]` +generics_variance_inference.py:119:43: error[invalid-assignment] Object of type `ShouldBeInvariant3[int, str]` is not assignable to `ShouldBeInvariant3[int | float, str]` +generics_variance_inference.py:120:41: error[invalid-assignment] Object of type `ShouldBeInvariant3[int | float, str]` is not assignable to `ShouldBeInvariant3[int, str]` +generics_variance_inference.py:121:43: error[invalid-assignment] Object of type `ShouldBeInvariant3[str, int]` is not assignable to `ShouldBeInvariant3[str, int | float]` +generics_variance_inference.py:122:41: error[invalid-assignment] Object of type `ShouldBeInvariant3[str, int | float]` is not assignable to `ShouldBeInvariant3[str, int]` +generics_variance_inference.py:130:38: error[invalid-assignment] Object of type `ShouldBeInvariant4[int]` is not assignable to `ShouldBeInvariant4[int | float]` +generics_variance_inference.py:138:38: error[invalid-assignment] Object of type `ShouldBeInvariant5[int]` is not assignable to `ShouldBeInvariant5[int | float]` +generics_variance_inference.py:149:45: error[invalid-assignment] Object of type `ShouldBeContravariant1[int]` is not assignable to `ShouldBeContravariant1[int | float]` +generics_variance_inference.py:169:31: error[invalid-assignment] Object of type `ShouldBeInvariant6[int | float]` is not assignable to `ShouldBeInvariant6[int]` +generics_variance_inference.py:170:33: error[invalid-assignment] Object of type `ShouldBeInvariant6[int]` is not assignable to `ShouldBeInvariant6[int | float]` +generics_variance_inference.py:181:31: error[invalid-assignment] Object of type `ShouldBeCovariant6[int | float]` is not assignable to `ShouldBeCovariant6[int]` +generics_variance_inference.py:194:37: error[invalid-assignment] Object of type `ShouldBeContravariant2[int]` is not assignable to `ShouldBeContravariant2[int | float]` +""" diff --git a/conformance/results/ty/historical_positional.toml b/conformance/results/ty/historical_positional.toml new file mode 100644 index 000000000..f435999e0 --- /dev/null +++ b/conformance/results/ty/historical_positional.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +historical_positional.py:18:4: error[positional-only-parameter-as-kwarg] Positional-only parameter 1 (`__x`) passed as keyword argument of function `f1` +historical_positional.py:26:16: error[invalid-legacy-positional-parameter] Invalid use of the legacy convention for positional-only parameters: Parameter name begins with `__` but will not be treated as positional-only +historical_positional.py:45:28: error[invalid-legacy-positional-parameter] Invalid use of the legacy convention for positional-only parameters: Parameter name begins with `__` but will not be treated as positional-only +historical_positional.py:54:26: error[invalid-legacy-positional-parameter] Invalid use of the legacy convention for positional-only parameters: Parameter name begins with `__` but will not be treated as positional-only +historical_positional.py:59:6: error[positional-only-parameter-as-kwarg] Positional-only parameter 2 (`__x`) passed as keyword argument of bound method `A.m1` +""" diff --git a/conformance/results/ty/literals_interactions.toml b/conformance/results/ty/literals_interactions.toml new file mode 100644 index 000000000..a5f280b0a --- /dev/null +++ b/conformance/results/ty/literals_interactions.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +notes = """ +Deliberately does not allow `str` to be narrowed to literal string types through equality or containment checks due to the possibility of `str` subclasses that could have unexpected equality semantics. +""" +errors_diff = """ +""" +output = """ +literals_interactions.py:14:5: error[index-out-of-bounds] Index 5 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 +literals_interactions.py:15:5: error[index-out-of-bounds] Index -5 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 +literals_interactions.py:16:5: error[index-out-of-bounds] Index 4 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 +literals_interactions.py:17:5: error[index-out-of-bounds] Index -4 is out of bounds for tuple `tuple[int, str, list[bool]]` with length 3 +literals_interactions.py:128:28: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str` +literals_interactions.py:130:28: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str & ~Literal["MALFORMED"]` +literals_interactions.py:133:28: error[invalid-argument-type] Argument to function `expects_bad_status` is incorrect: Expected `Literal["MALFORMED", "ABORTED"]`, found `str` +literals_interactions.py:136:32: error[invalid-argument-type] Argument to function `expects_pending_status` is incorrect: Expected `Literal["PENDING"]`, found `str` +""" diff --git a/conformance/results/ty/literals_literalstring.toml b/conformance/results/ty/literals_literalstring.toml new file mode 100644 index 000000000..58e4d8719 --- /dev/null +++ b/conformance/results/ty/literals_literalstring.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +literals_literalstring.py:36:29: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_literalstring.py:37:22: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_literalstring.py:43:23: error[invalid-assignment] Object of type `Literal["two"]` is not assignable to `Literal[""]` +literals_literalstring.py:65:25: error[invalid-assignment] Object of type `str` is not assignable to `LiteralString` +literals_literalstring.py:73:25: error[invalid-assignment] Object of type `Literal[3]` is not assignable to `LiteralString` +literals_literalstring.py:74:25: error[invalid-assignment] Object of type `Literal[b"test"]` is not assignable to `LiteralString` +literals_literalstring.py:119:22: error[invalid-argument-type] Argument to function `literal_identity` is incorrect: Argument type `str` does not satisfy upper bound `LiteralString` of type variable `TLiteral` +literals_literalstring.py:133:41: error[invalid-assignment] Object of type `Container[T@Container]` is not assignable to `Container[LiteralString]` +literals_literalstring.py:133:51: error[invalid-argument-type] Argument to `Container.__init__` is incorrect: Argument type `str` does not satisfy upper bound `LiteralString` of type variable `T` +literals_literalstring.py:133:51: error[invalid-argument-type] Argument to `Container.__init__` is incorrect: Expected `LiteralString`, found `str` +literals_literalstring.py:171:21: error[invalid-assignment] Object of type `list[LiteralString]` is not assignable to `list[str]` +""" diff --git a/conformance/results/ty/literals_parameterizations.toml b/conformance/results/ty/literals_parameterizations.toml new file mode 100644 index 000000000..27f64295d --- /dev/null +++ b/conformance/results/ty/literals_parameterizations.toml @@ -0,0 +1,22 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +literals_parameterizations.py:41:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:42:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:43:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:44:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:45:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:46:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:47:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:48:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:49:15: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:50:16: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:51:16: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:52:16: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:53:16: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:56:28: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:60:4: error[invalid-type-form] `typing.Literal` requires at least one argument when used in a type expression +literals_parameterizations.py:61:12: error[invalid-type-form] Type arguments for `Literal` must be `None`, a literal value (int, bool, str, or bytes), or an enum member +literals_parameterizations.py:65:32: error[invalid-assignment] Object of type `Literal[Color.RED]` is not assignable to `Literal["Color.RED"]` +""" diff --git a/conformance/results/ty/literals_semantics.toml b/conformance/results/ty/literals_semantics.toml new file mode 100644 index 000000000..f0a0bb5f6 --- /dev/null +++ b/conformance/results/ty/literals_semantics.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +literals_semantics.py:10:18: error[invalid-assignment] Object of type `Literal[4]` is not assignable to `Literal[3]` +literals_semantics.py:24:26: error[invalid-assignment] Object of type `Literal[0]` is not assignable to `Literal[False]` +literals_semantics.py:25:22: error[invalid-assignment] Object of type `Literal[False]` is not assignable to `Literal[0]` +literals_semantics.py:33:5: error[invalid-assignment] Object of type `Literal[6, 7, 8]` is not assignable to `Literal[3, 4, 5]` +""" diff --git a/conformance/results/ty/namedtuples_define_class.toml b/conformance/results/ty/namedtuples_define_class.toml new file mode 100644 index 000000000..e5e7bc787 --- /dev/null +++ b/conformance/results/ty/namedtuples_define_class.toml @@ -0,0 +1,20 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +namedtuples_define_class.py:33:7: error[index-out-of-bounds] Index 3 is out of bounds for tuple `Point` with length 3 +namedtuples_define_class.py:34:7: error[index-out-of-bounds] Index -4 is out of bounds for tuple `Point` with length 3 +namedtuples_define_class.py:45:6: error[missing-argument] No argument provided for required parameter `y` +namedtuples_define_class.py:46:6: error[missing-argument] No argument provided for required parameter `y` +namedtuples_define_class.py:47:15: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]` +namedtuples_define_class.py:48:18: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `Literal[3]` +namedtuples_define_class.py:49:22: error[too-many-positional-arguments] Too many positional arguments: expected 4, got 5 +namedtuples_define_class.py:50:23: error[unknown-argument] Argument `other` does not match any known parameter +namedtuples_define_class.py:70:20: error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4 +namedtuples_define_class.py:77:5: error[invalid-named-tuple] NamedTuple field `_y` cannot start with an underscore +namedtuples_define_class.py:87:5: error[invalid-named-tuple] NamedTuple field without default value cannot follow field(s) with default value(s): Field `latitude` defined here without a default value +namedtuples_define_class.py:107:5: error[invalid-named-tuple-override] Cannot override NamedTuple field `x` inherited from `Point` +namedtuples_define_class.py:121:24: error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4 +namedtuples_define_class.py:140:19: error[invalid-argument-type] Argument is incorrect: Expected `str`, found `float` +namedtuples_define_class.py:147:24: error[invalid-named-tuple] NamedTuple class `Unit` cannot use multiple inheritance except with `Generic[]` +""" diff --git a/conformance/results/ty/namedtuples_define_functional.toml b/conformance/results/ty/namedtuples_define_functional.toml new file mode 100644 index 000000000..dc85a1e4b --- /dev/null +++ b/conformance/results/ty/namedtuples_define_functional.toml @@ -0,0 +1,19 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +namedtuples_define_functional.py:16:8: error[missing-argument] No argument provided for required parameter `y` +namedtuples_define_functional.py:21:8: error[missing-argument] No arguments provided for required parameters `x`, `y` +namedtuples_define_functional.py:26:21: error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4 +namedtuples_define_functional.py:31:8: error[missing-argument] No argument provided for required parameter `y` +namedtuples_define_functional.py:31:18: error[unknown-argument] Argument `z` does not match any known parameter +namedtuples_define_functional.py:36:18: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["1"]` +namedtuples_define_functional.py:37:21: error[too-many-positional-arguments] Too many positional arguments: expected 3, got 4 +namedtuples_define_functional.py:42:18: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal["1"]` +namedtuples_define_functional.py:43:15: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `float` +namedtuples_define_functional.py:52:25: error[invalid-named-tuple] Duplicate field name `a` in `namedtuple()`: Field `a` already defined; will raise `ValueError` at runtime +namedtuples_define_functional.py:53:25: error[invalid-named-tuple] Field name `def` in `namedtuple()` cannot be a Python keyword: Will raise `ValueError` at runtime +namedtuples_define_functional.py:54:25: error[invalid-named-tuple] Field name `def` in `namedtuple()` cannot be a Python keyword: Will raise `ValueError` at runtime +namedtuples_define_functional.py:55:25: error[invalid-named-tuple] Field name `_d` in `namedtuple()` cannot start with an underscore: Will raise `ValueError` at runtime +namedtuples_define_functional.py:69:1: error[missing-argument] No argument provided for required parameter `a` +""" diff --git a/conformance/results/ty/namedtuples_type_compat.toml b/conformance/results/ty/namedtuples_type_compat.toml new file mode 100644 index 000000000..1003a3256 --- /dev/null +++ b/conformance/results/ty/namedtuples_type_compat.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +namedtuples_type_compat.py:22:23: error[invalid-assignment] Object of type `Point` is not assignable to `tuple[int, int]` +namedtuples_type_compat.py:23:28: error[invalid-assignment] Object of type `Point` is not assignable to `tuple[int, str, str]` +""" diff --git a/conformance/results/ty/namedtuples_usage.toml b/conformance/results/ty/namedtuples_usage.toml new file mode 100644 index 000000000..01cf6f669 --- /dev/null +++ b/conformance/results/ty/namedtuples_usage.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +namedtuples_usage.py:34:7: error[index-out-of-bounds] Index 3 is out of bounds for tuple `Point` with length 3 +namedtuples_usage.py:35:7: error[index-out-of-bounds] Index -4 is out of bounds for tuple `Point` with length 3 +namedtuples_usage.py:40:1: error[invalid-assignment] Cannot assign to read-only property `x` on object of type `Point` +namedtuples_usage.py:41:1: error[invalid-assignment] Cannot assign to a subscript on an object of type `Point` +namedtuples_usage.py:42:5: error[invalid-assignment] Cannot delete read-only property `x` on object of type `Point` +namedtuples_usage.py:43:5: error[not-subscriptable] Cannot delete subscript on object of type `Point` with no `__delitem__` method +namedtuples_usage.py:52:1: error[invalid-assignment] Too many values to unpack: Expected 2 +namedtuples_usage.py:53:1: error[invalid-assignment] Not enough values to unpack: Expected 4 +""" diff --git a/conformance/results/ty/narrowing_typeguard.toml b/conformance/results/ty/narrowing_typeguard.toml new file mode 100644 index 000000000..196a41941 --- /dev/null +++ b/conformance/results/ty/narrowing_typeguard.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +narrowing_typeguard.py:102:23: error[invalid-type-guard-definition] `TypeGuard` function must have a parameter to narrow +narrowing_typeguard.py:107:22: error[invalid-type-guard-definition] `TypeGuard` function must have a parameter to narrow +narrowing_typeguard.py:128:20: error[invalid-argument-type] Argument to function `takes_callable_str` is incorrect: Expected `(object, /) -> str`, found `def simple_typeguard(val: object) -> TypeGuard[int]` +narrowing_typeguard.py:148:26: error[invalid-argument-type] Argument to function `takes_callable_str_proto` is incorrect: Expected `CallableStrProto`, found `def simple_typeguard(val: object) -> TypeGuard[int]` +""" diff --git a/conformance/results/ty/narrowing_typeis.toml b/conformance/results/ty/narrowing_typeis.toml new file mode 100644 index 000000000..7abfa5ba8 --- /dev/null +++ b/conformance/results/ty/narrowing_typeis.toml @@ -0,0 +1,20 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Intersects the pre-existing type with the top materialization of the bracketed type rather than the bracketed type itself. +""" +errors_diff = """ +Line 35: Unexpected errors ['narrowing_typeis.py:35:18: error[invalid-assignment] Object of type `object` is not assignable to `int`'] +""" +output = """ +narrowing_typeis.py:35:18: error[invalid-assignment] Object of type `object` is not assignable to `int` +narrowing_typeis.py:110:23: error[invalid-type-guard-definition] `TypeIs` function must have a parameter to narrow +narrowing_typeis.py:115:22: error[invalid-type-guard-definition] `TypeIs` function must have a parameter to narrow +narrowing_typeis.py:137:20: error[invalid-argument-type] Argument to function `takes_callable_str` is incorrect: Expected `(object, /) -> str`, found `def simple_typeguard(val: object) -> TypeIs[int]` +narrowing_typeis.py:157:26: error[invalid-argument-type] Argument to function `takes_callable_str_proto` is incorrect: Expected `CallableStrProto`, found `def simple_typeguard(val: object) -> TypeIs[int]` +narrowing_typeis.py:174:17: error[invalid-argument-type] Argument to function `takes_typeguard` is incorrect: Expected `(object, /) -> TypeGuard[int]`, found `def is_int_typeis(val: object) -> TypeIs[int]` +narrowing_typeis.py:175:14: error[invalid-argument-type] Argument to function `takes_typeis` is incorrect: Expected `(object, /) -> TypeIs[int]`, found `def is_int_typeguard(val: object) -> TypeGuard[int]` +narrowing_typeis.py:196:18: error[invalid-argument-type] Argument to function `takes_int_typeis` is incorrect: Expected `(object, /) -> TypeIs[int]`, found `def bool_typeis(val: object) -> TypeIs[bool]` +narrowing_typeis.py:200:27: error[invalid-type-guard-definition] Narrowed type `str` is not assignable to the declared parameter type `int` +narrowing_typeis.py:204:45: error[invalid-type-guard-definition] Narrowed type `list[int]` is not assignable to the declared parameter type `list[object]` +""" diff --git a/conformance/results/ty/overloads_basic.toml b/conformance/results/ty/overloads_basic.toml new file mode 100644 index 000000000..eb874c741 --- /dev/null +++ b/conformance/results/ty/overloads_basic.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_basic.py:39:1: error[invalid-argument-type] Method `__getitem__` of type `Overload[(__i: int, /) -> int, (__s: slice[Any, Any, Any], /) -> bytes]` cannot be called with key of type `Literal[""]` on object of type `Bytes` +""" diff --git a/conformance/results/ty/overloads_consistency.toml b/conformance/results/ty/overloads_consistency.toml new file mode 100644 index 000000000..ba10fac4e --- /dev/null +++ b/conformance/results/ty/overloads_consistency.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:25:5: error[invalid-overload] Overload return type is not assignable to implementation return type +overloads_consistency.py:41:5: error[invalid-overload] Implementation does not accept all arguments of this overload +""" diff --git a/conformance/results/ty/overloads_definitions.toml b/conformance/results/ty/overloads_definitions.toml new file mode 100644 index 000000000..090f8fdf0 --- /dev/null +++ b/conformance/results/ty/overloads_definitions.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_definitions.py:16:5: error[invalid-overload] Overloaded function `func1` requires at least two overloads: Only one overload defined here +overloads_definitions.py:28:5: error[invalid-overload] Overloads for function `func2` must be followed by a non-`@overload`-decorated implementation function +overloads_definitions.py:59:9: error[invalid-overload] Overloads for function `not_abstract` must be followed by a non-`@overload`-decorated implementation function +overloads_definitions.py:81:9: error[invalid-overload] Overloaded function `func5` does not use the `@staticmethod` decorator consistently +overloads_definitions.py:94:9: error[invalid-overload] Overloaded function `func6` does not use the `@classmethod` decorator consistently +overloads_definitions.py:124:9: error[invalid-overload] `@final` decorator should be applied only to the overload implementation +overloads_definitions.py:139:9: error[invalid-overload] `@final` decorator should be applied only to the overload implementation +overloads_definitions.py:144:9: error[invalid-overload] `@final` decorator should be applied only to the overload implementation +overloads_definitions.py:186:9: error[override-of-final-method] Cannot override final member `final_method` from superclass `Base` +overloads_definitions.py:203:9: error[invalid-explicit-override] Method `bad_override` is decorated with `@override` but does not override anything +overloads_definitions.py:228:9: error[invalid-overload] `@override` decorator should be applied only to the overload implementation +overloads_definitions.py:232:9: error[invalid-overload] `@override` decorator should be applied only to the overload implementation +""" diff --git a/conformance/results/ty/overloads_definitions_stub.toml b/conformance/results/ty/overloads_definitions_stub.toml new file mode 100644 index 000000000..43ec8feea --- /dev/null +++ b/conformance/results/ty/overloads_definitions_stub.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_definitions_stub.pyi:14:5: error[invalid-overload] Overloaded function `func1` requires at least two overloads: Only one overload defined here +overloads_definitions_stub.pyi:37:9: error[invalid-overload] Overloaded function `func5` does not use the `@staticmethod` decorator consistently +overloads_definitions_stub.pyi:44:9: error[invalid-overload] Overloaded function `func6` does not use the `@classmethod` decorator consistently +overloads_definitions_stub.pyi:73:9: error[invalid-overload] `@final` decorator should be applied only to the first overload +overloads_definitions_stub.pyi:86:9: error[invalid-overload] `@final` decorator should be applied only to the first overload +overloads_definitions_stub.pyi:111:9: error[override-of-final-method] Cannot override final member `final_method` from superclass `Base` +overloads_definitions_stub.pyi:122:9: error[invalid-explicit-override] Method `bad_override` is decorated with `@override` but does not override anything +overloads_definitions_stub.pyi:147:9: error[invalid-overload] `@override` decorator should be applied only to the first overload +""" diff --git a/conformance/results/ty/overloads_evaluation.toml b/conformance/results/ty/overloads_evaluation.toml new file mode 100644 index 000000000..3a9a963c2 --- /dev/null +++ b/conformance/results/ty/overloads_evaluation.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_evaluation.py:38:1: error[no-matching-overload] No overload of function `example1_1` matches arguments +overloads_evaluation.py:46:15: error[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]` +overloads_evaluation.py:51:12: error[invalid-argument-type] Argument to function `example1_1` is incorrect: Expected `str`, found `Literal[1]` +overloads_evaluation.py:116:5: error[no-matching-overload] No overload of function `example2` matches arguments +""" diff --git a/conformance/results/ty/protocols_class_objects.toml b/conformance/results/ty/protocols_class_objects.toml new file mode 100644 index 000000000..fcc3ec8a1 --- /dev/null +++ b/conformance/results/ty/protocols_class_objects.toml @@ -0,0 +1,22 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +notes = """ +`type[Proto]` is not yet supported. +`ClassVar` protocol members are not yet supported. +`@property` protocol members are only partially supported. +A class object `C` is only considered to inhabit a protocol type with a method member `f` if `f` exists as an attribute on the metaclass of `C`. +""" +errors_diff = """ +Line 29: Expected 1 errors +Line 34: Expected 1 errors +Line 74: Expected 1 errors +Line 104: Expected 1 errors +Line 106: Expected 1 errors +Line 107: Expected 1 errors +Line 108: Expected 1 errors +Line 59: Unexpected errors ["protocols_class_objects.py:59:16: error[invalid-assignment] Object of type `` is not assignable to `ProtoA2`"] +""" +output = """ +protocols_class_objects.py:58:16: error[invalid-assignment] Object of type `` is not assignable to `ProtoA1` +protocols_class_objects.py:59:16: error[invalid-assignment] Object of type `` is not assignable to `ProtoA2` +""" diff --git a/conformance/results/ty/protocols_definition.toml b/conformance/results/ty/protocols_definition.toml new file mode 100644 index 000000000..5f8ae7f3c --- /dev/null +++ b/conformance/results/ty/protocols_definition.toml @@ -0,0 +1,33 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject implicit instance attributes in `Protocol` methods. +Does not support `ClassVar` protocol members. +Incorrectly considers `ClassVar` attributes on concrete classes as satisfying non-`ClassVar` attribute members on protocols. +Only has partial support for `@property` protocol members. +""" +errors_diff = """ +Line 67: Expected 1 errors +Line 117: Expected 1 errors +Line 157: Expected 1 errors +Line 158: Expected 1 errors +Line 218: Expected 1 errors +Line 339: Expected 1 errors +Line 340: Expected 1 errors +Line 341: Expected 1 errors +""" +output = """ +protocols_definition.py:30:11: error[invalid-argument-type] Argument to function `close_all` is incorrect: Expected `Iterable[SupportsClose]`, found `list[int]` +protocols_definition.py:114:22: error[invalid-assignment] Object of type `Concrete2_Bad1` is not assignable to `Template2` +protocols_definition.py:115:22: error[invalid-assignment] Object of type `Concrete2_Bad2` is not assignable to `Template2` +protocols_definition.py:116:22: error[invalid-assignment] Object of type `Concrete2_Bad3` is not assignable to `Template2` +protocols_definition.py:156:22: error[invalid-assignment] Object of type `Concrete3_Bad1` is not assignable to `Template3` +protocols_definition.py:159:22: error[invalid-assignment] Object of type `Concrete3_Bad4` is not assignable to `Template3` +protocols_definition.py:160:22: error[invalid-assignment] Object of type `Concrete3_Bad5` is not assignable to `Template3` +protocols_definition.py:219:22: error[invalid-assignment] Object of type `Concrete4_Bad2` is not assignable to `Template4` +protocols_definition.py:285:22: error[invalid-assignment] Object of type `Concrete5_Bad1` is not assignable to `Template5` +protocols_definition.py:286:22: error[invalid-assignment] Object of type `Concrete5_Bad2` is not assignable to `Template5` +protocols_definition.py:287:22: error[invalid-assignment] Object of type `Concrete5_Bad3` is not assignable to `Template5` +protocols_definition.py:288:22: error[invalid-assignment] Object of type `Concrete5_Bad4` is not assignable to `Template5` +protocols_definition.py:289:22: error[invalid-assignment] Object of type `Concrete5_Bad5` is not assignable to `Template5` +""" diff --git a/conformance/results/ty/protocols_explicit.toml b/conformance/results/ty/protocols_explicit.toml new file mode 100644 index 000000000..24f1e8541 --- /dev/null +++ b/conformance/results/ty/protocols_explicit.toml @@ -0,0 +1,16 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +notes = """ +Allows implicitly abstract protocol methods to be called via `super()` on a protocol subclass. +Allows instantiation of abstract subclasses of protocol classes. +""" +errors_diff = """ +Line 27: Expected 1 errors +Line 60: Expected 1 errors +Line 89: Expected 1 errors +Line 134: Expected 1 errors +Line 164: Expected 1 errors +""" +output = """ +protocols_explicit.py:56:9: error[invalid-assignment] Object of type `tuple[int, int, str]` is not assignable to attribute `rgb` of type `tuple[int, int, int]` +""" diff --git a/conformance/results/ty/protocols_generic.toml b/conformance/results/ty/protocols_generic.toml new file mode 100644 index 000000000..96e3dbdca --- /dev/null +++ b/conformance/results/ty/protocols_generic.toml @@ -0,0 +1,18 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Only partially supports `@property` protocol members. +""" +errors_diff = """ +Line 145: Expected 1 errors +Line 146: Expected 1 errors +Line 147: Expected 1 errors +""" +output = """ +protocols_generic.py:40:24: error[invalid-assignment] Object of type `Concrete1` is not assignable to `Proto1[int, str]` +protocols_generic.py:44:14: error[invalid-generic-class] Cannot both inherit from subscripted `Protocol` and subscripted `Generic` +protocols_generic.py:56:20: error[invalid-assignment] Object of type `Box[int | float]` is not assignable to `Box[int]` +protocols_generic.py:66:25: error[invalid-assignment] Object of type `Sender[int]` is not assignable to `Sender[int | float]` +protocols_generic.py:74:28: error[invalid-assignment] Object of type `AttrProto[int]` is not assignable to `AttrProto[int | float]` +protocols_generic.py:75:26: error[invalid-assignment] Object of type `AttrProto[int | float]` is not assignable to `AttrProto[int]` +""" diff --git a/conformance/results/ty/protocols_merging.toml b/conformance/results/ty/protocols_merging.toml new file mode 100644 index 000000000..f3229e3fc --- /dev/null +++ b/conformance/results/ty/protocols_merging.toml @@ -0,0 +1,15 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject attempted instantiation of abstract subclasses of protocols. +""" +errors_diff = """ +Line 82: Expected 1 errors +""" +output = """ +protocols_merging.py:52:25: error[invalid-assignment] Object of type `SCConcrete2` is not assignable to `SizedAndClosable1` +protocols_merging.py:53:25: error[invalid-assignment] Object of type `SCConcrete2` is not assignable to `SizedAndClosable2` +protocols_merging.py:54:25: error[invalid-assignment] Object of type `SCConcrete2` is not assignable to `SizedAndClosable3` +protocols_merging.py:67:16: error[invalid-protocol] Protocol class `BadProto` cannot inherit from non-protocol class `SizedAndClosable3` +protocols_merging.py:83:24: error[invalid-assignment] Object of type `SCConcrete1` is not assignable to `SizedAndClosable4` +""" diff --git a/conformance/results/ty/protocols_modules.toml b/conformance/results/ty/protocols_modules.toml new file mode 100644 index 000000000..ff470754f --- /dev/null +++ b/conformance/results/ty/protocols_modules.toml @@ -0,0 +1,16 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Never considers a module as satisfying a protocol with a method member due to the fact that the method will never exist on the class `types.ModuleType`, only on a given specific module instance. +""" +errors_diff = """ +Line 25: Unexpected errors ["protocols_modules.py:25:17: error[invalid-assignment] Object of type `` is not assignable to `Options1`"] +Line 47: Unexpected errors ["protocols_modules.py:47:18: error[invalid-assignment] Object of type `` is not assignable to `Reporter1`"] +""" +output = """ +protocols_modules.py:25:17: error[invalid-assignment] Object of type `` is not assignable to `Options1` +protocols_modules.py:26:17: error[invalid-assignment] Object of type `` is not assignable to `Options2` +protocols_modules.py:47:18: error[invalid-assignment] Object of type `` is not assignable to `Reporter1` +protocols_modules.py:48:18: error[invalid-assignment] Object of type `` is not assignable to `Reporter2` +protocols_modules.py:49:18: error[invalid-assignment] Object of type `` is not assignable to `Reporter3` +""" diff --git a/conformance/results/ty/protocols_recursive.toml b/conformance/results/ty/protocols_recursive.toml new file mode 100644 index 000000000..f6bfd7871 --- /dev/null +++ b/conformance/results/ty/protocols_recursive.toml @@ -0,0 +1,11 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Fails to solve a type variable involving a recursive generic protocol. +""" +errors_diff = """ +Line 81: Unexpected errors ['protocols_recursive.py:81:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `list[int]`'] +""" +output = """ +protocols_recursive.py:81:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `list[int]` +""" diff --git a/conformance/results/ty/protocols_runtime_checkable.toml b/conformance/results/ty/protocols_runtime_checkable.toml new file mode 100644 index 000000000..8725a7eec --- /dev/null +++ b/conformance/results/ty/protocols_runtime_checkable.toml @@ -0,0 +1,15 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Does not reject `isinstance()` or `issubclass()` calls against runtime-checkable protocols where there is an unsafe overlap between the type of the first argument and the protocol. +""" +errors_diff = """ +Line 88: Expected 1 errors +Line 91: Expected 1 errors +Line 94: Expected 1 errors +""" +output = """ +protocols_runtime_checkable.py:23:8: error[isinstance-against-protocol] Class `Proto1` cannot be used as the second argument to `isinstance`: This call will raise `TypeError` at runtime +protocols_runtime_checkable.py:55:8: error[isinstance-against-protocol] `DataProtocol` cannot be used as the second argument to `issubclass` as it is a protocol with non-method members +protocols_runtime_checkable.py:61:8: error[isinstance-against-protocol] `DataProtocol` cannot be used as the second argument to `issubclass` as it is a protocol with non-method members +""" diff --git a/conformance/results/ty/protocols_self.toml b/conformance/results/ty/protocols_self.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/protocols_self.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/protocols_subtyping.toml b/conformance/results/ty/protocols_subtyping.toml new file mode 100644 index 000000000..b75d7d453 --- /dev/null +++ b/conformance/results/ty/protocols_subtyping.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_subtyping.py:16:6: error[call-non-callable] Cannot instantiate class `Proto1`: This call will raise `TypeError` at runtime +protocols_subtyping.py:38:21: error[invalid-assignment] Object of type `Proto2` is not assignable to `Concrete2` +protocols_subtyping.py:55:18: error[invalid-assignment] Object of type `Proto2` is not assignable to `Proto3` +protocols_subtyping.py:79:30: error[invalid-assignment] Object of type `Proto5[int]` is not assignable to `Proto4[int, int | float]` +protocols_subtyping.py:80:25: error[invalid-assignment] Object of type `Proto4[int, int]` is not assignable to `Proto5[int | float]` +protocols_subtyping.py:102:30: error[invalid-assignment] Object of type `Proto6[int | float, int | float]` is not assignable to `Proto7[int, int | float]` +protocols_subtyping.py:103:33: error[invalid-assignment] Object of type `Proto6[int | float, int | float]` is not assignable to `Proto7[int | float, object]` +""" diff --git a/conformance/results/ty/protocols_variance.toml b/conformance/results/ty/protocols_variance.toml new file mode 100644 index 000000000..303056e98 --- /dev/null +++ b/conformance/results/ty/protocols_variance.toml @@ -0,0 +1,13 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 21: Expected 1 errors +Line 40: Expected 1 errors +Line 56: Expected 1 errors +Line 66: Expected 1 errors +Line 104: Expected 1 errors +Lines 61, 62: Expected error (tag 'covariant_in_input') +Lines 71, 72: Expected error (tag 'contravariant_in_output') +""" +output = """ +""" diff --git a/conformance/results/ty/qualifiers_annotated.toml b/conformance/results/ty/qualifiers_annotated.toml new file mode 100644 index 000000000..cd76ea815 --- /dev/null +++ b/conformance/results/ty/qualifiers_annotated.toml @@ -0,0 +1,25 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +qualifiers_annotated.py:38:17: error[invalid-type-form] List literals are not allowed in this context in a type expression +qualifiers_annotated.py:39:17: error[invalid-type-form] Tuple literals are not allowed in this context in a type expression +qualifiers_annotated.py:40:17: error[invalid-type-form] List comprehensions are not allowed in type expressions +qualifiers_annotated.py:41:17: error[invalid-type-form] Dict literals are not allowed in type expressions +qualifiers_annotated.py:42:17: error[invalid-type-form] Function calls are not allowed in type expressions +qualifiers_annotated.py:43:17: error[invalid-type-form] Only simple names and dotted names can be subscripted in type expressions +qualifiers_annotated.py:44:17: error[invalid-type-form] `if` expressions are not allowed in type expressions +qualifiers_annotated.py:45:17: error[unresolved-reference] Name `var1` used when not defined +qualifiers_annotated.py:46:17: error[invalid-type-form] Boolean literals are not allowed in this context in a type expression: Did you mean `typing.Literal[True]`? +qualifiers_annotated.py:47:18: error[invalid-type-form] Int literals are not allowed in this context in a type expression: Did you mean `typing.Literal[1]`? +qualifiers_annotated.py:48:18: error[invalid-type-form] Boolean operations are not allowed in type expressions +qualifiers_annotated.py:49:18: error[invalid-type-form] F-strings are not allowed in type expressions +qualifiers_annotated.py:59:8: error[invalid-type-form] Special form `typing.Annotated` expected at least 2 arguments (one type and at least one metadata element) +qualifiers_annotated.py:71:24: error[invalid-assignment] Object of type `]'>` is not assignable to `type[Any]` +qualifiers_annotated.py:72:24: error[invalid-assignment] Object of type `]'>` is not assignable to `type[Any]` +qualifiers_annotated.py:79:7: error[invalid-argument-type] Argument to function `func4` is incorrect: Expected `type[Unknown]`, found `]'>` +qualifiers_annotated.py:80:7: error[invalid-argument-type] Argument to function `func4` is incorrect: Expected `type[Unknown]`, found `]'>` +qualifiers_annotated.py:86:1: error[call-non-callable] Object of type `` is not callable +qualifiers_annotated.py:87:1: error[call-non-callable] Object of type `GenericAlias` is not callable +qualifiers_annotated.py:88:1: error[call-non-callable] Object of type `GenericAlias` is not callable +""" diff --git a/conformance/results/ty/qualifiers_final_annotation.toml b/conformance/results/ty/qualifiers_final_annotation.toml new file mode 100644 index 000000000..10678afda --- /dev/null +++ b/conformance/results/ty/qualifiers_final_annotation.toml @@ -0,0 +1,33 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +qualifiers_final_annotation.py:16:1: error[final-without-value] `Final` symbol `BAD1` is not assigned a value +qualifiers_final_annotation.py:18:7: error[invalid-type-form] Type qualifier `typing.Final` expected exactly 1 argument, got 2 +qualifiers_final_annotation.py:34:5: error[final-without-value] `Final` symbol `ID2` is not assigned a value +qualifiers_final_annotation.py:38:5: error[final-without-value] `Final` symbol `ID3` is not assigned a value +qualifiers_final_annotation.py:54:9: error[invalid-assignment] Invalid assignment to final attribute: `ID5` already has a value in the class body +qualifiers_final_annotation.py:62:9: error[invalid-assignment] Cannot assign to final attribute `id3` on type `Self@method1`: `Final` attributes can only be assigned in the class body or `__init__` +qualifiers_final_annotation.py:63:9: error[invalid-assignment] Cannot assign to final attribute `id4` on type `Self@method1`: `Final` attributes can only be assigned in the class body or `__init__` +qualifiers_final_annotation.py:65:9: error[invalid-assignment] Cannot assign to final attribute `ID7` on type `Self@method1`: `Final` attributes can only be assigned in the class body or `__init__` +qualifiers_final_annotation.py:67:9: error[invalid-assignment] Cannot assign to final attribute `ID7` on type `Self@method1`: `Final` attributes can only be assigned in the class body or `__init__` +qualifiers_final_annotation.py:71:1: error[invalid-assignment] Reassignment of `Final` symbol `RATE` is not allowed: Symbol later reassigned here +qualifiers_final_annotation.py:81:1: error[invalid-assignment] Cannot assign to final attribute `DEFAULT_ID` on type ``: `Final` attributes can only be assigned in the class body or `__init__` +qualifiers_final_annotation.py:94:5: error[override-of-final-variable] Cannot override final variable `BORDER_WIDTH` from superclass `ClassC` +qualifiers_final_annotation.py:107:13: error[redundant-final-classvar] `Combining `ClassVar` and `Final` is redundant +qualifiers_final_annotation.py:108:13: error[redundant-final-classvar] `Combining `ClassVar` and `Final` is redundant +qualifiers_final_annotation.py:118:9: error[invalid-type-form] Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions) +qualifiers_final_annotation.py:121:14: error[invalid-type-form] Type qualifier `typing.Final` is not allowed in parameter annotations +qualifiers_final_annotation.py:134:1: error[missing-argument] No arguments provided for required parameters `x`, `y` +qualifiers_final_annotation.py:134:3: error[unknown-argument] Argument `a` does not match any known parameter +qualifiers_final_annotation.py:135:3: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]` +qualifiers_final_annotation.py:135:9: error[invalid-argument-type] Argument is incorrect: Expected `int`, found `Literal[""]` +qualifiers_final_annotation.py:141:5: error[invalid-assignment] Reassignment of `Final` symbol `ID1` is not allowed: Reassignment of `Final` symbol +qualifiers_final_annotation.py:145:5: error[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here +qualifiers_final_annotation.py:147:10: error[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here +qualifiers_final_annotation.py:149:9: error[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here +qualifiers_final_annotation.py:152:30: error[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here +qualifiers_final_annotation.py:155:9: error[invalid-assignment] Reassignment of `Final` symbol `x` is not allowed: Symbol later reassigned here +qualifiers_final_annotation.py:166:1: error[invalid-assignment] Reassignment of `Final` symbol `TEN` is not allowed: Reassignment of `Final` symbol +qualifiers_final_annotation.py:170:1: error[invalid-assignment] Reassignment of `Final` symbol `PI` is not allowed: Reassignment of `Final` symbol +""" diff --git a/conformance/results/ty/qualifiers_final_decorator.toml b/conformance/results/ty/qualifiers_final_decorator.toml new file mode 100644 index 000000000..7236dc2a4 --- /dev/null +++ b/conformance/results/ty/qualifiers_final_decorator.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +qualifiers_final_decorator.py:21:16: error[subclass-of-final-class] Class `Derived1` cannot inherit from final class `Base1` +qualifiers_final_decorator.py:56:9: error[override-of-final-method] Cannot override final member `method1` from superclass `Base2` +qualifiers_final_decorator.py:60:9: error[override-of-final-method] Cannot override final member `method2` from superclass `Base2` +qualifiers_final_decorator.py:64:9: error[override-of-final-method] Cannot override final member `method3` from superclass `Base2` +qualifiers_final_decorator.py:75:9: error[override-of-final-method] Cannot override final member `method4` from superclass `Base2` +qualifiers_final_decorator.py:86:9: error[invalid-overload] `@final` decorator should be applied only to the overload implementation +qualifiers_final_decorator.py:89:9: error[override-of-final-method] Cannot override final member `method` from superclass `Base3` +qualifiers_final_decorator.py:102:9: error[override-of-final-method] Cannot override final member `method` from superclass `Base4` +qualifiers_final_decorator.py:118:9: error[invalid-method-override] Invalid override of method `method`: Definition is incompatible with `Base5_2.method` +qualifiers_final_decorator.py:118:9: error[override-of-final-method] Cannot override final member `method` from superclass `Base5_2` +qualifiers_final_decorator.py:125:1: error[final-on-non-method] `@final` cannot be applied to non-method function `func1` +""" diff --git a/conformance/results/ty/specialtypes_any.toml b/conformance/results/ty/specialtypes_any.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/specialtypes_any.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/specialtypes_never.toml b/conformance/results/ty/specialtypes_never.toml new file mode 100644 index 000000000..9f528bb43 --- /dev/null +++ b/conformance/results/ty/specialtypes_never.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +specialtypes_never.py:19:22: error[invalid-return-type] Function always implicitly returns `None`, which is not assignable to return type `Never` +specialtypes_never.py:85:21: error[invalid-assignment] Object of type `list[Never]` is not assignable to `list[int]` +specialtypes_never.py:104:12: error[invalid-return-type] Return type does not match returned value: expected `ClassC[U@func10]`, found `ClassC[Never]` +""" diff --git a/conformance/results/ty/specialtypes_none.toml b/conformance/results/ty/specialtypes_none.toml new file mode 100644 index 000000000..9801c5cc7 --- /dev/null +++ b/conformance/results/ty/specialtypes_none.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +specialtypes_none.py:21:7: error[invalid-argument-type] Argument to function `func1` is incorrect: Expected `None`, found `` +specialtypes_none.py:27:19: error[invalid-assignment] Object of type `None` is not assignable to `Iterable[Unknown]` +specialtypes_none.py:41:7: error[invalid-argument-type] Argument to function `func2` is incorrect: Expected ``, found `None` +""" diff --git a/conformance/results/ty/specialtypes_promotions.toml b/conformance/results/ty/specialtypes_promotions.toml new file mode 100644 index 000000000..5dea49f83 --- /dev/null +++ b/conformance/results/ty/specialtypes_promotions.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +specialtypes_promotions.py:13:5: error[unresolved-attribute] Attribute `numerator` is not defined on `float` in union `int | float` +""" diff --git a/conformance/results/ty/specialtypes_type.toml b/conformance/results/ty/specialtypes_type.toml new file mode 100644 index 000000000..20b222ad6 --- /dev/null +++ b/conformance/results/ty/specialtypes_type.toml @@ -0,0 +1,37 @@ +conformance_automated = "Fail" +conformant = "Partial" +notes = """ +Allows arbitrary attributes to be accessed on `TA` where `TA = typing.Type[typing.Any]` or `TA = type[typing.Any]`. +Treats `type` equivalently to `type[object]` rather than `type[typing.Any]`. +""" +errors_diff = """ +Line 144: Expected 1 errors +Line 146: Expected 1 errors +Line 84: Unexpected errors ['specialtypes_type.py:84:5: error[type-assertion-failure] Type `type` does not match asserted type `type[Any]`'] +Line 99: Unexpected errors ['specialtypes_type.py:99:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown`'] +Line 100: Unexpected errors ['specialtypes_type.py:100:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown`'] +Line 107: Unexpected errors ['specialtypes_type.py:107:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown`'] +Line 108: Unexpected errors ['specialtypes_type.py:108:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown`'] +Line 137: Unexpected errors ['specialtypes_type.py:137:5: error[type-assertion-failure] Type `type` does not match asserted type `type[Any]`'] +Line 139: Unexpected errors ['specialtypes_type.py:139:5: error[type-assertion-failure] Type `type` does not match asserted type `type[Any]`'] +Line 169: Unexpected errors ['specialtypes_type.py:169:21: error[invalid-assignment] Object of type `type` is not assignable to `type[int]`'] +Line 175: Unexpected errors ['specialtypes_type.py:175:16: error[invalid-return-type] Return type does not match returned value: expected `type[T@ClassA]`, found `type`'] +""" +output = """ +specialtypes_type.py:56:7: error[invalid-argument-type] Argument to function `func4` is incorrect: Expected `type[BasicUser | ProUser]`, found `` +specialtypes_type.py:70:7: error[invalid-argument-type] Argument to function `func5` is incorrect: Expected `type[Unknown]`, found `` +specialtypes_type.py:76:17: error[invalid-type-form] type[...] must have exactly one type argument +specialtypes_type.py:84:5: error[type-assertion-failure] Type `type` does not match asserted type `type[Any]` +specialtypes_type.py:99:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown` +specialtypes_type.py:100:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown` +specialtypes_type.py:107:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown` +specialtypes_type.py:108:17: error[unresolved-attribute] Object of type `type` has no attribute `unknown` +specialtypes_type.py:117:5: error[unresolved-attribute] Object of type `type` has no attribute `unknown` +specialtypes_type.py:120:5: error[unresolved-attribute] Object of type `type` has no attribute `unknown` +specialtypes_type.py:137:5: error[type-assertion-failure] Type `type` does not match asserted type `type[Any]` +specialtypes_type.py:139:5: error[type-assertion-failure] Type `type` does not match asserted type `type[Any]` +specialtypes_type.py:143:1: error[unresolved-attribute] Special form `typing.Type` has no attribute `unknown` +specialtypes_type.py:145:1: error[unresolved-attribute] Class `type` has no attribute `unknown` +specialtypes_type.py:169:21: error[invalid-assignment] Object of type `type` is not assignable to `type[int]` +specialtypes_type.py:175:16: error[invalid-return-type] Return type does not match returned value: expected `type[T@ClassA]`, found `type` +""" diff --git a/conformance/results/ty/tuples_type_compat.toml b/conformance/results/ty/tuples_type_compat.toml new file mode 100644 index 000000000..e908be4b9 --- /dev/null +++ b/conformance/results/ty/tuples_type_compat.toml @@ -0,0 +1,29 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +tuples_type_compat.py:15:27: error[invalid-assignment] Object of type `tuple[int | float, int | float | complex]` is not assignable to `tuple[int, int]` +tuples_type_compat.py:29:10: error[invalid-assignment] Object of type `tuple[int, ...]` is not assignable to `tuple[int, *tuple[int, ...]]` +tuples_type_compat.py:32:10: error[invalid-assignment] Object of type `tuple[int, *tuple[int, ...]]` is not assignable to `tuple[int]` +tuples_type_compat.py:33:10: error[invalid-assignment] Object of type `tuple[int, ...]` is not assignable to `tuple[int]` +tuples_type_compat.py:43:22: error[invalid-assignment] Object of type `tuple[int, ...]` is not assignable to `tuple[int]` +tuples_type_compat.py:62:26: error[invalid-assignment] Object of type `tuple[int, ...]` is not assignable to `tuple[int, int]` +tuples_type_compat.py:75:9: error[type-assertion-failure] Type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]` does not match asserted type `tuple[int]` +tuples_type_compat.py:80:9: error[type-assertion-failure] Type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]` does not match asserted type `tuple[str, str] | tuple[int, int]` +tuples_type_compat.py:85:9: error[type-assertion-failure] Type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]` does not match asserted type `tuple[int, str, int]` +tuples_type_compat.py:101:13: error[type-assertion-failure] Type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]` does not match asserted type `tuple[int]` +tuples_type_compat.py:106:13: error[type-assertion-failure] Type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]` does not match asserted type `tuple[str, str] | tuple[int, int]` +tuples_type_compat.py:111:13: error[type-assertion-failure] Type `tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]` does not match asserted type `tuple[int, str, int]` +tuples_type_compat.py:126:13: error[type-assertion-failure] Type `tuple[int | str, int | str]` does not match asserted type `tuple[int | str, str]` +tuples_type_compat.py:129:13: error[type-assertion-failure] Type `tuple[int | str, int | str]` does not match asserted type `tuple[int | str, int]` +tuples_type_compat.py:157:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[""], Literal[""]]` is not assignable to `tuple[int, str]` +tuples_type_compat.py:162:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[1], Literal[""]]` is not assignable to `tuple[int, *tuple[str, ...]]` +tuples_type_compat.py:163:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[""], Literal[1]]` is not assignable to `tuple[int, *tuple[str, ...]]` +tuples_type_compat.py:169:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[""], Literal[""]]` is not assignable to `tuple[int, *tuple[str, ...], int]` +tuples_type_compat.py:170:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[""], Literal[""], float]` is not assignable to `tuple[int, *tuple[str, ...], int]` +tuples_type_compat.py:175:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[""], Literal[1]]` is not assignable to `tuple[*tuple[str, ...], int]` +tuples_type_compat.py:176:6: error[invalid-assignment] Object of type `tuple[Literal[""], Literal[""], float]` is not assignable to `tuple[*tuple[str, ...], int]` +tuples_type_compat.py:181:40: error[invalid-assignment] Object of type `tuple[str, str]` is not assignable to `tuple[str, str, int]` +tuples_type_compat.py:184:50: error[invalid-assignment] Object of type `tuple[str, str]` is not assignable to `tuple[str, str, str, *tuple[str, ...]]` +tuples_type_compat.py:188:50: error[invalid-assignment] Object of type `tuple[str, str]` is not assignable to `tuple[*tuple[str, ...], str, str, str]` +""" diff --git a/conformance/results/ty/tuples_type_form.toml b/conformance/results/ty/tuples_type_form.toml new file mode 100644 index 000000000..1a50c81a7 --- /dev/null +++ b/conformance/results/ty/tuples_type_form.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +tuples_type_form.py:12:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[2]]` is not assignable to `tuple[int]` +tuples_type_form.py:14:6: error[invalid-assignment] Object of type `tuple[Literal[1]]` is not assignable to `tuple[int, int]` +tuples_type_form.py:15:6: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[""]]` is not assignable to `tuple[int, int]` +tuples_type_form.py:25:7: error[invalid-assignment] Object of type `tuple[Literal[1]]` is not assignable to `tuple[()]` +tuples_type_form.py:36:7: error[invalid-assignment] Object of type `tuple[Literal[1], Literal[2], Literal[3], Literal[""]]` is not assignable to `tuple[int, ...]` +tuples_type_form.py:40:6: error[invalid-type-form] Invalid `tuple` specialization: `...` can only be used as the second element in a two-element `tuple` specialization +tuples_type_form.py:41:6: error[invalid-type-form] Invalid `tuple` specialization: `...` can only be used as the second element in a two-element `tuple` specialization +tuples_type_form.py:42:6: error[invalid-type-form] Invalid `tuple` specialization: `...` can only be used as the second element in a two-element `tuple` specialization +tuples_type_form.py:43:6: error[invalid-type-form] Invalid `tuple` specialization: `...` can only be used as the second element in a two-element `tuple` specialization +tuples_type_form.py:44:6: error[invalid-type-form] Invalid `tuple` specialization: `...` cannot be used after an unpacked element +tuples_type_form.py:45:6: error[invalid-type-form] Invalid `tuple` specialization: `...` cannot be used after an unpacked element +""" diff --git a/conformance/results/ty/tuples_unpacked.toml b/conformance/results/ty/tuples_unpacked.toml new file mode 100644 index 000000000..b45a4fd3a --- /dev/null +++ b/conformance/results/ty/tuples_unpacked.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +tuples_unpacked.py:40:5: error[invalid-type-form] Multiple unpacked variadic tuples are not allowed in a `tuple` specialization +tuples_unpacked.py:41:5: error[invalid-type-form] Multiple unpacked variadic tuples are not allowed in a `tuple` specialization +tuples_unpacked.py:51:9: error[invalid-type-form] Multiple unpacked variadic tuples are not allowed in a `tuple` specialization +tuples_unpacked.py:59:6: error[invalid-type-form] Multiple unpacked variadic tuples are not allowed in a `tuple` specialization +tuples_unpacked.py:60:6: error[invalid-type-form] Multiple unpacked variadic tuples are not allowed in a `tuple` specialization +""" diff --git a/conformance/results/ty/typeddicts_alt_syntax.toml b/conformance/results/ty/typeddicts_alt_syntax.toml new file mode 100644 index 000000000..4a49527d4 --- /dev/null +++ b/conformance/results/ty/typeddicts_alt_syntax.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_alt_syntax.py:23:44: error[invalid-argument-type] Expected a dict literal for parameter `fields` of `TypedDict()` +typeddicts_alt_syntax.py:27:45: error[invalid-argument-type] Expected a string-literal key in the `fields` dict of `TypedDict()`: Found `Literal[1]` +typeddicts_alt_syntax.py:31:27: error[mismatched-type-name] The name passed to `TypedDict` must match the variable it is assigned to: Expected "BadTypedDict3", got "WrongName" +typeddicts_alt_syntax.py:35:72: error[unknown-argument] Argument `other` does not match any known parameter of function `TypedDict` +typeddicts_alt_syntax.py:41:10: error[missing-argument] No argument provided for required parameter `fields` of function `TypedDict` +typeddicts_alt_syntax.py:41:30: error[unknown-argument] Argument `name` does not match any known parameter of function `TypedDict` +typeddicts_alt_syntax.py:41:40: error[unknown-argument] Argument `year` does not match any known parameter of function `TypedDict` +""" diff --git a/conformance/results/ty/typeddicts_class_syntax.toml b/conformance/results/ty/typeddicts_class_syntax.toml new file mode 100644 index 000000000..ccf44582f --- /dev/null +++ b/conformance/results/ty/typeddicts_class_syntax.toml @@ -0,0 +1,14 @@ +conformant = "Pass" +notes = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_class_syntax.py:30:5: error[invalid-typed-dict-statement] TypedDict class cannot have methods +typeddicts_class_syntax.py:34:5: error[invalid-typed-dict-statement] TypedDict class cannot have methods +typeddicts_class_syntax.py:39:5: error[invalid-typed-dict-statement] TypedDict class cannot have methods +typeddicts_class_syntax.py:45:32: error[invalid-typed-dict-header] Custom metaclasses are not supported in `TypedDict` definitions +typeddicts_class_syntax.py:50:32: error[unknown-argument] Unknown keyword argument `other` in `TypedDict` definition +typeddicts_class_syntax.py:65:28: error[invalid-key] Unknown key "z" for TypedDict `ConditionalField` +""" diff --git a/conformance/results/ty/typeddicts_extra_items.toml b/conformance/results/ty/typeddicts_extra_items.toml new file mode 100644 index 000000000..6cf6df4de --- /dev/null +++ b/conformance/results/ty/typeddicts_extra_items.toml @@ -0,0 +1,79 @@ +conformance_automated = "Fail" +conformant = "Unsupported" +errors_diff = """ +Line 49: Expected 1 errors +Line 67: Expected 1 errors +Line 73: Expected 1 errors +Line 109: Expected 1 errors +Line 174: Expected 1 errors +Line 215: Expected 1 errors +Line 222: Expected 1 errors +Line 242: Expected 1 errors +Line 256: Expected 1 errors +Line 257: Expected 1 errors +Line 268: Expected 1 errors +Lines 91, 92: Expected error (tag 'MovieC') +Lines 94, 95: Expected error (tag 'MovieD') +Lines 184, 185: Expected error (tag 'MovieRequiredYear') +Lines 187, 188: Expected error (tag 'MovieNotRequiredYear') +Lines 196, 197: Expected error (tag 'BookWithPublisher') +Line 14: Unexpected errors ['typeddicts_extra_items.py:14:37: error[invalid-key] Unknown key "novel_adaptation" for TypedDict `Movie`'] +Line 21: Unexpected errors ['typeddicts_extra_items.py:21:47: error[invalid-key] Unknown key "novel_adaptation" for TypedDict `MovieFunctional`'] +Line 29: Unexpected errors ['typeddicts_extra_items.py:29:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `bool`', 'typeddicts_extra_items.py:29:23: error[invalid-key] Unknown key "novel_adaptation" for TypedDict `Movie`'] +Line 43: Unexpected errors ['typeddicts_extra_items.py:43:5: error[invalid-key] Unknown key "other_extra_key" for TypedDict `InheritedMovie`'] +Line 129: Unexpected errors ['typeddicts_extra_items.py:129:15: error[invalid-argument-type] Cannot delete unknown key "year" from TypedDict `MovieEI`'] +Line 254: Unexpected errors ['typeddicts_extra_items.py:254:63: error[invalid-key] Unknown key "year" for TypedDict `MovieExtraInt`'] +Line 255: Unexpected errors ['typeddicts_extra_items.py:255:63: error[invalid-key] Unknown key "description" for TypedDict `MovieExtraStr`'] +Line 266: Unexpected errors ['typeddicts_extra_items.py:266:64: error[invalid-key] Unknown key "year" for TypedDict `MovieExtraInt`'] +Line 284: Unexpected errors ['typeddicts_extra_items.py:284:43: error[invalid-key] Unknown key "year" for TypedDict `ExtraMovie`'] +Line 299: Unexpected errors ['typeddicts_extra_items.py:299:54: error[invalid-key] Unknown key "summary" for TypedDict `MovieExtraStr`'] +Line 300: Unexpected errors ['typeddicts_extra_items.py:300:34: error[invalid-assignment] Object of type `MovieExtraStr` is not assignable to `Mapping[str, str]`'] +Line 302: Unexpected errors ['typeddicts_extra_items.py:302:54: error[invalid-key] Unknown key "year" for TypedDict `MovieExtraInt`'] +Line 304: Unexpected errors ['typeddicts_extra_items.py:304:44: error[invalid-assignment] Object of type `MovieExtraInt` is not assignable to `Mapping[str, int | str]`'] +Line 310: Unexpected errors ['typeddicts_extra_items.py:310:5: error[type-assertion-failure] Type `list[tuple[str, object]]` does not match asserted type `list[tuple[str, int | str]]`'] +Line 311: Unexpected errors ['typeddicts_extra_items.py:311:5: error[type-assertion-failure] Type `list[object]` does not match asserted type `list[int | str]`'] +Line 326: Unexpected errors ['typeddicts_extra_items.py:326:25: error[invalid-assignment] Object of type `IntDict` is not assignable to `dict[str, int]`'] +Line 329: Unexpected errors ['typeddicts_extra_items.py:329:52: error[invalid-key] Unknown key "bar" for TypedDict `IntDictWithNum`'] +Line 330: Unexpected errors ['typeddicts_extra_items.py:330:32: error[invalid-assignment] Object of type `IntDictWithNum` is not assignable to `dict[str, int]`'] +Line 337: Unexpected errors ['typeddicts_extra_items.py:337:1: error[unresolved-attribute] Object of type `IntDictWithNum` has no attribute `clear`'] +Line 339: Unexpected errors ['typeddicts_extra_items.py:339:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `tuple[str, int]`', 'typeddicts_extra_items.py:339:13: error[unresolved-attribute] Object of type `IntDictWithNum` has no attribute `popitem`'] +Line 342: Unexpected errors ['typeddicts_extra_items.py:342:27: error[invalid-key] TypedDict `IntDictWithNum` can only be subscripted with a string literal key, got key of type `str`.'] +Line 343: Unexpected errors ['typeddicts_extra_items.py:343:9: error[invalid-argument-type] Method `__delitem__` of type `(key: Literal["num"], /) -> None` cannot be called with key of type `str` on object of type `IntDictWithNum`'] +""" +output = """ +typeddicts_extra_items.py:14:37: error[invalid-key] Unknown key "novel_adaptation" for TypedDict `Movie` +typeddicts_extra_items.py:15:37: error[invalid-key] Unknown key "year" for TypedDict `Movie` +typeddicts_extra_items.py:21:47: error[invalid-key] Unknown key "novel_adaptation" for TypedDict `MovieFunctional` +typeddicts_extra_items.py:22:47: error[invalid-key] Unknown key "year" for TypedDict `MovieFunctional` +typeddicts_extra_items.py:29:5: error[type-assertion-failure] Type `Unknown` does not match asserted type `bool` +typeddicts_extra_items.py:29:23: error[invalid-key] Unknown key "novel_adaptation" for TypedDict `Movie` +typeddicts_extra_items.py:39:54: error[invalid-argument-type] Invalid argument to key "year" with declared type `int` on TypedDict `InheritedMovie`: value of type `None` +typeddicts_extra_items.py:43:5: error[invalid-key] Unknown key "other_extra_key" for TypedDict `InheritedMovie` +typeddicts_extra_items.py:114:50: error[invalid-type-form] Type qualifier `typing.Required` is not valid in a TypedDict `extra_items` argument +typeddicts_extra_items.py:117:57: error[invalid-type-form] Type qualifier `typing.NotRequired` is not valid in a TypedDict `extra_items` argument +typeddicts_extra_items.py:128:15: error[invalid-argument-type] Cannot delete required key "name" from TypedDict `MovieEI` +typeddicts_extra_items.py:129:15: error[invalid-argument-type] Cannot delete unknown key "year" from TypedDict `MovieEI` +typeddicts_extra_items.py:254:63: error[invalid-key] Unknown key "year" for TypedDict `MovieExtraInt` +typeddicts_extra_items.py:255:63: error[invalid-key] Unknown key "description" for TypedDict `MovieExtraStr` +typeddicts_extra_items.py:266:64: error[invalid-key] Unknown key "year" for TypedDict `MovieExtraInt` +typeddicts_extra_items.py:278:47: error[invalid-key] Unknown key "year" for TypedDict `NonClosedMovie` +typeddicts_extra_items.py:284:43: error[invalid-key] Unknown key "year" for TypedDict `ExtraMovie` +typeddicts_extra_items.py:285:43: error[invalid-key] Unknown key "language" for TypedDict `ExtraMovie` +typeddicts_extra_items.py:293:44: error[invalid-key] Unknown key "year" for TypedDict `ClosedMovie` +typeddicts_extra_items.py:299:54: error[invalid-key] Unknown key "summary" for TypedDict `MovieExtraStr` +typeddicts_extra_items.py:300:34: error[invalid-assignment] Object of type `MovieExtraStr` is not assignable to `Mapping[str, str]` +typeddicts_extra_items.py:302:54: error[invalid-key] Unknown key "year" for TypedDict `MovieExtraInt` +typeddicts_extra_items.py:303:34: error[invalid-assignment] Object of type `MovieExtraInt` is not assignable to `Mapping[str, int]` +typeddicts_extra_items.py:304:44: error[invalid-assignment] Object of type `MovieExtraInt` is not assignable to `Mapping[str, int | str]` +typeddicts_extra_items.py:310:5: error[type-assertion-failure] Type `list[tuple[str, object]]` does not match asserted type `list[tuple[str, int | str]]` +typeddicts_extra_items.py:311:5: error[type-assertion-failure] Type `list[object]` does not match asserted type `list[int | str]` +typeddicts_extra_items.py:326:25: error[invalid-assignment] Object of type `IntDict` is not assignable to `dict[str, int]` +typeddicts_extra_items.py:329:52: error[invalid-key] Unknown key "bar" for TypedDict `IntDictWithNum` +typeddicts_extra_items.py:330:32: error[invalid-assignment] Object of type `IntDictWithNum` is not assignable to `dict[str, int]` +typeddicts_extra_items.py:337:1: error[unresolved-attribute] Object of type `IntDictWithNum` has no attribute `clear` +typeddicts_extra_items.py:339:1: error[type-assertion-failure] Type `Unknown` does not match asserted type `tuple[str, int]` +typeddicts_extra_items.py:339:13: error[unresolved-attribute] Object of type `IntDictWithNum` has no attribute `popitem` +typeddicts_extra_items.py:342:27: error[invalid-key] TypedDict `IntDictWithNum` can only be subscripted with a string literal key, got key of type `str`. +typeddicts_extra_items.py:343:9: error[invalid-argument-type] Method `__delitem__` of type `(key: Literal["num"], /) -> None` cannot be called with key of type `str` on object of type `IntDictWithNum` +typeddicts_extra_items.py:352:25: error[invalid-assignment] Object of type `dict[str, int]` is not assignable to `IntDict` +""" diff --git a/conformance/results/ty/typeddicts_final.toml b/conformance/results/ty/typeddicts_final.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/ty/typeddicts_final.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/ty/typeddicts_inheritance.toml b/conformance/results/ty/typeddicts_inheritance.toml new file mode 100644 index 000000000..52361d4b1 --- /dev/null +++ b/conformance/results/ty/typeddicts_inheritance.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_inheritance.py:44:31: error[invalid-typed-dict-header] TypedDict class `BadTypedDict` can only inherit from TypedDict classes: `NonTypedDict` is not a `TypedDict` class +typeddicts_inheritance.py:55:4: error[invalid-typed-dict-field] Cannot overwrite TypedDict field `x`: Inherited mutable field type `str` is incompatible with `int` +typeddicts_inheritance.py:65:7: error[invalid-typed-dict-field] Cannot overwrite TypedDict field `x` while merging base classes: Inherited mutable field type `str` is incompatible with `int` +""" diff --git a/conformance/results/ty/typeddicts_operations.toml b/conformance/results/ty/typeddicts_operations.toml new file mode 100644 index 000000000..8526b4218 --- /dev/null +++ b/conformance/results/ty/typeddicts_operations.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_operations.py:22:17: error[invalid-assignment] Invalid assignment to key "name" with declared type `str` on TypedDict `Movie`: value of type `Literal[1982]` +typeddicts_operations.py:23:17: error[invalid-assignment] Invalid assignment to key "year" with declared type `int` on TypedDict `Movie`: value of type `Literal[""]` +typeddicts_operations.py:24:7: error[invalid-key] Unknown key "other" for TypedDict `Movie` +typeddicts_operations.py:26:13: error[invalid-key] Unknown key "other" for TypedDict `Movie` +typeddicts_operations.py:28:9: error[missing-typed-dict-key] Missing required key 'year' in TypedDict `Movie` constructor +typeddicts_operations.py:29:42: error[invalid-argument-type] Invalid argument to key "year" with declared type `int` on TypedDict `Movie`: value of type `float` +typeddicts_operations.py:32:36: error[invalid-key] Unknown key "other" for TypedDict `Movie` +typeddicts_operations.py:37:20: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor +typeddicts_operations.py:47:1: error[unresolved-attribute] Object of type `Movie` has no attribute `clear` +typeddicts_operations.py:49:11: error[invalid-argument-type] Cannot delete required key "name" from TypedDict `Movie` +typeddicts_operations.py:62:1: error[unresolved-attribute] Object of type `MovieOptional` has no attribute `clear` +""" diff --git a/conformance/results/ty/typeddicts_readonly.toml b/conformance/results/ty/typeddicts_readonly.toml new file mode 100644 index 000000000..cfb8b6c9b --- /dev/null +++ b/conformance/results/ty/typeddicts_readonly.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly.py:24:4: error[invalid-assignment] Cannot assign to key "members" on TypedDict `Band`: key is marked read-only +typeddicts_readonly.py:36:4: error[invalid-assignment] Cannot assign to key "members" on TypedDict `Band2`: key is marked read-only +typeddicts_readonly.py:50:4: error[invalid-assignment] Cannot assign to key "title" on TypedDict `Movie1`: key is marked read-only +typeddicts_readonly.py:51:4: error[invalid-assignment] Cannot assign to key "year" on TypedDict `Movie1`: key is marked read-only +typeddicts_readonly.py:60:4: error[invalid-assignment] Cannot assign to key "title" on TypedDict `Movie2`: key is marked read-only +typeddicts_readonly.py:61:4: error[invalid-assignment] Cannot assign to key "year" on TypedDict `Movie2`: key is marked read-only +""" diff --git a/conformance/results/ty/typeddicts_readonly_consistency.toml b/conformance/results/ty/typeddicts_readonly_consistency.toml new file mode 100644 index 000000000..b289d22e6 --- /dev/null +++ b/conformance/results/ty/typeddicts_readonly_consistency.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly_consistency.py:37:14: error[invalid-assignment] Object of type `A1` is not assignable to `B1` +typeddicts_readonly_consistency.py:38:14: error[invalid-assignment] Object of type `C1` is not assignable to `B1` +typeddicts_readonly_consistency.py:40:14: error[invalid-assignment] Object of type `A1` is not assignable to `C1` +typeddicts_readonly_consistency.py:81:14: error[invalid-assignment] Object of type `A2` is not assignable to `B2` +typeddicts_readonly_consistency.py:82:14: error[invalid-assignment] Object of type `C2` is not assignable to `B2` +typeddicts_readonly_consistency.py:84:14: error[invalid-assignment] Object of type `A2` is not assignable to `C2` +typeddicts_readonly_consistency.py:85:14: error[invalid-assignment] Object of type `B2` is not assignable to `C2` +""" diff --git a/conformance/results/ty/typeddicts_readonly_inheritance.toml b/conformance/results/ty/typeddicts_readonly_inheritance.toml new file mode 100644 index 000000000..bc9847757 --- /dev/null +++ b/conformance/results/ty/typeddicts_readonly_inheritance.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +output = """ +typeddicts_readonly_inheritance.py:36:4: error[invalid-assignment] Cannot assign to key "name" on TypedDict `Album2`: key is marked read-only +typeddicts_readonly_inheritance.py:50:5: error[invalid-typed-dict-field] Cannot overwrite TypedDict field `alt`: Inherited read-only field type `list[str | int]` is not assignable from `list[str]` +typeddicts_readonly_inheritance.py:65:19: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `RequiredName` constructor +typeddicts_readonly_inheritance.py:82:14: error[invalid-assignment] Invalid assignment to key "ident" with declared type `str` on TypedDict `User`: value of type `Literal[3]` +typeddicts_readonly_inheritance.py:83:15: error[invalid-argument-type] Invalid argument to key "ident" with declared type `str` on TypedDict `User`: value of type `Literal[3]` +typeddicts_readonly_inheritance.py:84:5: error[missing-typed-dict-key] Missing required key 'ident' in TypedDict `User` constructor +typeddicts_readonly_inheritance.py:94:5: error[invalid-typed-dict-field] Cannot overwrite TypedDict field `a`: Mutable inherited fields cannot be redeclared as read-only +typeddicts_readonly_inheritance.py:98:5: error[invalid-typed-dict-field] Cannot overwrite TypedDict field `a`: Required inherited fields cannot be redeclared as `NotRequired` +typeddicts_readonly_inheritance.py:106:5: error[invalid-typed-dict-field] Cannot overwrite TypedDict field `c`: Required inherited fields cannot be redeclared as `NotRequired` +typeddicts_readonly_inheritance.py:119:7: error[invalid-typed-dict-field] Cannot overwrite TypedDict field `x` while merging base classes: Inherited mutable field type `int | float` is incompatible with `int` +typeddicts_readonly_inheritance.py:132:7: error[invalid-typed-dict-field] Cannot overwrite TypedDict field `x` while merging base classes: Required inherited fields cannot be redeclared as `NotRequired` +""" diff --git a/conformance/results/ty/typeddicts_readonly_kwargs.toml b/conformance/results/ty/typeddicts_readonly_kwargs.toml new file mode 100644 index 000000000..cbe729539 --- /dev/null +++ b/conformance/results/ty/typeddicts_readonly_kwargs.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly_kwargs.py:33:12: error[invalid-assignment] Cannot assign to key "key1" on TypedDict `ReadOnlyArgs`: key is marked read-only +""" diff --git a/conformance/results/ty/typeddicts_readonly_update.toml b/conformance/results/ty/typeddicts_readonly_update.toml new file mode 100644 index 000000000..97c6f758c --- /dev/null +++ b/conformance/results/ty/typeddicts_readonly_update.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly_update.py:23:11: error[invalid-argument-type] Argument is incorrect: Expected ` | Iterable[tuple[str, object]]`, found `A` +""" diff --git a/conformance/results/ty/typeddicts_required.toml b/conformance/results/ty/typeddicts_required.toml new file mode 100644 index 000000000..7f3331fb0 --- /dev/null +++ b/conformance/results/ty/typeddicts_required.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_required.py:12:8: error[invalid-type-form] `Required` is only allowed in TypedDict fields +typeddicts_required.py:16:8: error[invalid-type-form] Type qualifier `typing.NotRequired` is not allowed in parameter annotations +typeddicts_required.py:59:8: error[invalid-type-form] `typing.Required` cannot be nested inside `Required` or `NotRequired` +typeddicts_required.py:60:8: error[invalid-type-form] `typing.Required` cannot be nested inside `Required` or `NotRequired` +""" diff --git a/conformance/results/ty/typeddicts_type_consistency.toml b/conformance/results/ty/typeddicts_type_consistency.toml new file mode 100644 index 000000000..030df7b1e --- /dev/null +++ b/conformance/results/ty/typeddicts_type_consistency.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_type_consistency.py:21:10: error[invalid-assignment] Object of type `B1` is not assignable to `A1` +typeddicts_type_consistency.py:38:10: error[invalid-assignment] Object of type `B2` is not assignable to `A2` +typeddicts_type_consistency.py:65:6: error[invalid-assignment] Object of type `A3` is not assignable to `B3` +typeddicts_type_consistency.py:69:21: error[invalid-key] Unknown key "y" for TypedDict `A3` +typeddicts_type_consistency.py:76:22: error[invalid-assignment] Object of type `B3` is not assignable to `dict[str, int]` +typeddicts_type_consistency.py:77:25: error[invalid-assignment] Object of type `B3` is not assignable to `dict[str, object]` +typeddicts_type_consistency.py:78:22: error[invalid-assignment] Object of type `B3` is not assignable to `dict[Any, Any]` +typeddicts_type_consistency.py:82:25: error[invalid-assignment] Object of type `B3` is not assignable to `Mapping[str, int]` +typeddicts_type_consistency.py:126:56: error[invalid-argument-type] Invalid argument to key "inner_key" with declared type `str` on TypedDict `Inner1`: value of type `Literal[1]` +""" diff --git a/conformance/results/ty/typeddicts_usage.toml b/conformance/results/ty/typeddicts_usage.toml new file mode 100644 index 000000000..648b7db1d --- /dev/null +++ b/conformance/results/ty/typeddicts_usage.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_usage.py:23:7: error[invalid-key] Unknown key "director" for TypedDict `Movie` +typeddicts_usage.py:24:17: error[invalid-assignment] Invalid assignment to key "year" with declared type `int` on TypedDict `Movie`: value of type `Literal["1982"]` +typeddicts_usage.py:28:17: error[missing-typed-dict-key] Missing required key 'name' in TypedDict `Movie` constructor +typeddicts_usage.py:28:18: error[invalid-key] Unknown key "title" for TypedDict `Movie` +typeddicts_usage.py:35:4: error[isinstance-against-typed-dict] `TypedDict` class `Movie` cannot be used as the second argument to `isinstance`: This call will raise `TypeError` at runtime +typeddicts_usage.py:40:24: error[invalid-type-form] The special form `typing.TypedDict` is not allowed in type expressions +""" diff --git a/conformance/results/ty/typeforms_typeform.toml b/conformance/results/ty/typeforms_typeform.toml new file mode 100644 index 000000000..bf5496935 --- /dev/null +++ b/conformance/results/ty/typeforms_typeform.toml @@ -0,0 +1,21 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeforms_typeform.py:23:30: error[invalid-assignment] Object of type `TypeForm[str | int]` is not assignable to `TypeForm[str | None]` +typeforms_typeform.py:24:30: error[invalid-assignment] Object of type `TypeForm[list[str | None]]` is not assignable to `TypeForm[str | None]` +typeforms_typeform.py:59:7: error[invalid-syntax-in-forward-annotation] Syntax error in forward annotation: Unexpected token at the end of an expression: Did you mean `typing.Literal["not a type"]`? +typeforms_typeform.py:67:18: error[invalid-type-form] Function calls are not allowed in type expressions +typeforms_typeform.py:68:18: error[invalid-type-form] Tuple literals are not allowed in this context in a type expression +typeforms_typeform.py:69:18: error[invalid-type-form] Int literals are not allowed in this context in a type expression: Did you mean `typing.Literal[1]`? +typeforms_typeform.py:70:18: error[invalid-type-form] Variable of type `` is not allowed in a type expression +typeforms_typeform.py:71:18: error[invalid-type-form] Type qualifier `typing.ClassVar` is not allowed in type expressions (only in annotation expressions) +typeforms_typeform.py:72:18: error[invalid-type-form] Type qualifier `typing.Final` is not allowed in type expressions (only in annotation expressions) +typeforms_typeform.py:73:18: error[invalid-type-form] `Unpack` is not allowed in type expressions +typeforms_typeform.py:74:18: error[invalid-type-form] `typing.Optional` requires exactly one argument when used in a type expression +typeforms_typeform.py:75:19: error[invalid-type-form] Invalid binary operator `+` in type annotation +typeforms_typeform.py:86:16: error[invalid-type-form] Function calls are not allowed in type expressions +typeforms_typeform.py:88:15: error[invalid-type-form] Function calls are not allowed in type expressions +typeforms_typeform.py:98:21: error[invalid-assignment] Object of type `TypeForm[int]` is not assignable to `TypeForm[str]` +typeforms_typeform.py:108:21: error[invalid-assignment] Object of type `type[int]` is not assignable to `TypeForm[str]` +""" diff --git a/conformance/results/ty/version.toml b/conformance/results/ty/version.toml new file mode 100644 index 000000000..00cce94ae --- /dev/null +++ b/conformance/results/ty/version.toml @@ -0,0 +1 @@ +version = "ty 0.0.40" diff --git a/conformance/results/zuban/aliases_explicit.toml b/conformance/results/zuban/aliases_explicit.toml new file mode 100644 index 000000000..0f48394fe --- /dev/null +++ b/conformance/results/zuban/aliases_explicit.toml @@ -0,0 +1,29 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_explicit.py:67: error: Bad number of arguments for type alias, expected 0, given 1 [misc] +aliases_explicit.py:68: error: Bad number of arguments for type alias, expected 0, given 1 [misc] +aliases_explicit.py:69: error: Bad number of arguments for type alias, expected 1, given 2 [misc] +aliases_explicit.py:70: error: Bad number of arguments for type alias, expected 1, given 2 [misc] +aliases_explicit.py:71: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [misc] +aliases_explicit.py:79: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:80: error: Bracketed expression "[...]" is not valid as a type [valid-type] +aliases_explicit.py:80: note: Did you mean "List[...]"? +aliases_explicit.py:81: error: Syntax error in type annotation [valid-type] +aliases_explicit.py:81: note: Suggestion: Is there a spurious trailing comma? +aliases_explicit.py:82: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:83: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:84: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:85: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:86: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:87: error: Variable "tests.aliases_explicit.var1" is not valid as a type [valid-type] +aliases_explicit.py:87: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_explicit.py:88: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:89: error: Invalid type: try using Literal[1] instead? [valid-type] +aliases_explicit.py:90: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:91: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_explicit.py:100: error: Bad number of arguments for type alias, expected 0, given 1 [misc] +aliases_explicit.py:101: error: "" not callable [operator] +aliases_explicit.py:102: error: Bad number of arguments for type alias, expected 0, given 1 [misc] +""" diff --git a/conformance/results/zuban/aliases_implicit.toml b/conformance/results/zuban/aliases_implicit.toml new file mode 100644 index 000000000..1ba69a041 --- /dev/null +++ b/conformance/results/zuban/aliases_implicit.toml @@ -0,0 +1,41 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_implicit.py:76: error: Bad number of arguments for type alias, expected 0, given 1 [misc] +aliases_implicit.py:77: error: Bad number of arguments for type alias, expected 0, given 1 [misc] +aliases_implicit.py:78: error: Bad number of arguments for type alias, expected 1, given 2 [misc] +aliases_implicit.py:79: error: Bad number of arguments for type alias, expected 1, given 2 [misc] +aliases_implicit.py:80: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [misc] +aliases_implicit.py:81: error: Type argument "str" of "GoodTypeAlias12" must be a subtype of "float" [type-var] +aliases_implicit.py:106: error: Variable "tests.aliases_implicit.BadTypeAlias1" is not valid as a type [valid-type] +aliases_implicit.py:106: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:107: error: Variable "tests.aliases_implicit.BadTypeAlias2" is not valid as a type [valid-type] +aliases_implicit.py:107: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:108: error: Variable "tests.aliases_implicit.BadTypeAlias3" is not valid as a type [valid-type] +aliases_implicit.py:108: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:109: error: Variable "tests.aliases_implicit.BadTypeAlias4" is not valid as a type [valid-type] +aliases_implicit.py:109: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:110: error: Variable "tests.aliases_implicit.BadTypeAlias5" is not valid as a type [valid-type] +aliases_implicit.py:110: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:111: error: Variable "tests.aliases_implicit.BadTypeAlias6" is not valid as a type [valid-type] +aliases_implicit.py:111: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:112: error: Variable "tests.aliases_implicit.BadTypeAlias7" is not valid as a type [valid-type] +aliases_implicit.py:112: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:113: error: Variable "tests.aliases_implicit.BadTypeAlias8" is not valid as a type [valid-type] +aliases_implicit.py:113: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:114: error: Variable "tests.aliases_implicit.var1" is not valid as a type [valid-type] +aliases_implicit.py:114: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:115: error: Variable "tests.aliases_implicit.BadTypeAlias10" is not valid as a type [valid-type] +aliases_implicit.py:115: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:116: error: Variable "tests.aliases_implicit.BadTypeAlias11" is not valid as a type [valid-type] +aliases_implicit.py:116: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:117: error: Variable "tests.aliases_implicit.BadTypeAlias12" is not valid as a type [valid-type] +aliases_implicit.py:117: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:118: error: Variable "tests.aliases_implicit.BadTypeAlias13" is not valid as a type [valid-type] +aliases_implicit.py:118: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:119: error: Variable "tests.aliases_implicit.BadTypeAlias14" is not valid as a type [valid-type] +aliases_implicit.py:119: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_implicit.py:133: error: "UnionType" not callable [operator] +aliases_implicit.py:135: error: Bad number of arguments for type alias, expected 0, given 1 [misc] +""" diff --git a/conformance/results/zuban/aliases_newtype.toml b/conformance/results/zuban/aliases_newtype.toml new file mode 100644 index 000000000..840d3b002 --- /dev/null +++ b/conformance/results/zuban/aliases_newtype.toml @@ -0,0 +1,21 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_newtype.py:11: error: Argument 1 to "UserId" has incompatible type "str"; expected "int" [arg-type] +aliases_newtype.py:12: error: Incompatible types in assignment (expression has type "int", variable has type "UserId") [assignment] +aliases_newtype.py:18: error: Incompatible types in assignment (expression has type "type[UserId]", variable has type "type[Any]") [assignment] +aliases_newtype.py:23: error: Cannot use isinstance() with NewType type [misc] +aliases_newtype.py:26: error: Cannot subclass "NewType" [misc] +aliases_newtype.py:35: error: String argument 1 "BadName" to NewType(...) does not match variable name "GoodName" [misc] +aliases_newtype.py:41: error: Bad number of arguments for type alias, expected 0, given 1 [misc] +aliases_newtype.py:47: error: Argument 2 to NewType(...) must be subclassable (got "int | str") [valid-newtype] +aliases_newtype.py:50: error: Type variable "tests.aliases_newtype.T" is unbound [misc] +aliases_newtype.py:50: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +aliases_newtype.py:50: note: (Hint: Use "T" in function signature to bind "T" inside a function) +aliases_newtype.py:52: error: NewType cannot be used with protocol classes [misc] +aliases_newtype.py:54: error: Argument 2 to NewType(...) must be subclassable (got "Literal[7]") [valid-newtype] +aliases_newtype.py:61: error: Argument 2 to NewType(...) must be subclassable (got "TD1") [valid-newtype] +aliases_newtype.py:63: error: NewType(...) expects exactly two positional arguments [call-arg] +aliases_newtype.py:65: error: Argument 2 to NewType(...) must be subclassable (got "Any") [valid-newtype] +""" diff --git a/conformance/results/zuban/aliases_recursive.toml b/conformance/results/zuban/aliases_recursive.toml new file mode 100644 index 000000000..e58e7affa --- /dev/null +++ b/conformance/results/zuban/aliases_recursive.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_recursive.py:19: error: Dict entry 1 has incompatible type "str": "complex"; expected "str": "None | int | str | float | list[Json] | dict[str, Json]" [dict-item] +aliases_recursive.py:20: error: List item 1 has incompatible type "complex"; expected "None | int | str | float | list[Json] | dict[str, Json]" [list-item] +aliases_recursive.py:38: error: Incompatible types in assignment (expression has type "tuple[int, tuple[str, int], tuple[int, tuple[int, list[int]]]]", variable has type "str | int | tuple[RecursiveTuple, ...]") [assignment] +aliases_recursive.py:39: error: Incompatible types in assignment (expression has type "tuple[int, list[int]]", variable has type "str | int | tuple[RecursiveTuple, ...]") [assignment] +aliases_recursive.py:50: error: Dict entry 0 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, str | int | Mapping[str, RecursiveMapping]]" [dict-item] +aliases_recursive.py:51: error: Dict entry 2 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, str | int | Mapping[str, RecursiveMapping]]" [dict-item] +aliases_recursive.py:52: error: Dict entry 2 has incompatible type "str": "list[int]"; expected "str": "str | int | Mapping[str, str | int | Mapping[str, RecursiveMapping]]" [dict-item] +aliases_recursive.py:63: error: List item 0 has incompatible type "float"; expected "GenericTypeAlias1[str] | str" [list-item] +aliases_recursive.py:69: error: List item 0 has incompatible type "float"; expected "GenericTypeAlias2[str, int] | str | int" [list-item] +aliases_recursive.py:72: error: Invalid recursive alias: a union item of itself [misc] +aliases_recursive.py:75: error: Invalid recursive alias: a union item of itself [misc] +""" diff --git a/conformance/results/zuban/aliases_type_statement.toml b/conformance/results/zuban/aliases_type_statement.toml new file mode 100644 index 000000000..d844880b7 --- /dev/null +++ b/conformance/results/zuban/aliases_type_statement.toml @@ -0,0 +1,33 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_type_statement.py:17: error: "TypeAliasType" has no attribute "bit_count" [attr-defined] +aliases_type_statement.py:19: error: "TypeAliasType" not callable [operator] +aliases_type_statement.py:23: error: "TypeAliasType" has no attribute "other_attrib" [attr-defined] +aliases_type_statement.py:26: error: Type alias defined using "type" statement not valid as base class [misc] +aliases_type_statement.py:31: error: Argument 2 to "isinstance" has incompatible type "TypeAliasType"; expected "type[Any] | UnionType | tuple[_ClassInfo, ...]" [arg-type] +aliases_type_statement.py:37: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:38: error: Bracketed expression "[...]" is not valid as a type [valid-type] +aliases_type_statement.py:38: note: Did you mean "List[...]"? +aliases_type_statement.py:39: error: Syntax error in type annotation [valid-type] +aliases_type_statement.py:39: note: Suggestion: Is there a spurious trailing comma? +aliases_type_statement.py:40: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:41: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:42: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:43: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:44: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:45: error: Variable "tests.aliases_type_statement.var1" is not valid as a type [valid-type] +aliases_type_statement.py:45: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_type_statement.py:46: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:47: error: Invalid type: try using Literal[1] instead? [valid-type] +aliases_type_statement.py:48: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:49: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_type_statement.py:53: error: All type parameters should be declared ("V" not declared) [misc] +aliases_type_statement.py:58: error: All type parameters should be declared ("T1" not declared) [misc] +aliases_type_statement.py:68: error: Type argument "str" of "RecursiveTypeAlias2" must be a subtype of "int" [type-var] +aliases_type_statement.py:70: error: Type argument "int" of "RecursiveTypeAlias2" must be a subtype of "str" [type-var] +aliases_type_statement.py:73: error: Invalid recursive alias: a union item of itself [misc] +aliases_type_statement.py:75: error: Invalid recursive alias: a union item of itself [misc] +aliases_type_statement.py:79: error: Invalid recursive alias: a union item of itself [misc] +""" diff --git a/conformance/results/zuban/aliases_typealiastype.toml b/conformance/results/zuban/aliases_typealiastype.toml new file mode 100644 index 000000000..5eb157015 --- /dev/null +++ b/conformance/results/zuban/aliases_typealiastype.toml @@ -0,0 +1,30 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_typealiastype.py:32: error: "TypeAliasType" has no attribute "other_attrib" [attr-defined] +aliases_typealiastype.py:40: error: Type argument "int" of "GoodAlias5" must be a subtype of "str" [type-var] +aliases_typealiastype.py:43: error: All type parameters should be declared ("S" not declared) [misc] +aliases_typealiastype.py:44: error: All type parameters should be declared ("S" not declared) [misc] +aliases_typealiastype.py:45: error: Tuple literal expected as the type_params argument to TypeAliasType [misc] +aliases_typealiastype.py:46: error: Invalid recursive alias: a union item of itself [misc] +aliases_typealiastype.py:47: error: Invalid recursive alias: a union item of itself [misc] +aliases_typealiastype.py:48: error: Invalid recursive alias: a union item of itself [misc] +aliases_typealiastype.py:52: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:53: error: Bracketed expression "[...]" is not valid as a type [valid-type] +aliases_typealiastype.py:53: note: Did you mean "List[...]"? +aliases_typealiastype.py:54: error: Syntax error in type annotation [valid-type] +aliases_typealiastype.py:54: note: Suggestion: Is there a spurious trailing comma? +aliases_typealiastype.py:55: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:56: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:57: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:58: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:59: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:60: error: Variable "tests.aliases_typealiastype.var1" is not valid as a type [valid-type] +aliases_typealiastype.py:60: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +aliases_typealiastype.py:61: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:62: error: Invalid type: try using Literal[1] instead? [valid-type] +aliases_typealiastype.py:63: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:64: error: Invalid type alias: expression is not a valid type [valid-type] +aliases_typealiastype.py:66: error: Name "BadAlias21" is used before definition [used-before-def] +""" diff --git a/conformance/results/zuban/aliases_variance.toml b/conformance/results/zuban/aliases_variance.toml new file mode 100644 index 000000000..8d4c8f1f1 --- /dev/null +++ b/conformance/results/zuban/aliases_variance.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +aliases_variance.py:24: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +aliases_variance.py:28: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +aliases_variance.py:32: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +aliases_variance.py:44: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +""" diff --git a/conformance/results/zuban/annotations_coroutines.toml b/conformance/results/zuban/annotations_coroutines.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/annotations_coroutines.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/annotations_forward_refs.toml b/conformance/results/zuban/annotations_forward_refs.toml new file mode 100644 index 000000000..9dce37fac --- /dev/null +++ b/conformance/results/zuban/annotations_forward_refs.toml @@ -0,0 +1,42 @@ +conformant = "Partial" +notes = """ +Incorrectly generates error for quoted type defined in class scope. +""" +conformance_automated = "Fail" +errors_diff = """ +Line 82: Unexpected errors ['annotations_forward_refs.py:82: error: Name "str" is not defined [name-defined]'] +Line 87: Unexpected errors ['annotations_forward_refs.py:87: error: Function "tests.annotations_forward_refs.ClassD.int" is not valid as a type [valid-type]'] +Line 95: Unexpected errors ['annotations_forward_refs.py:95: error: Expression is of type "Any", not "str" [misc]'] +Line 96: Unexpected errors ['annotations_forward_refs.py:96: error: Expression is of type "Any", not "int" [misc]'] +""" +output = """ +annotations_forward_refs.py:24: error: Forward reference unions cause runtime errors, consider wrapping the whole annotation with a string [misc] +annotations_forward_refs.py:25: error: Forward reference unions cause runtime errors, consider wrapping the whole annotation with a string [misc] +annotations_forward_refs.py:41: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:42: error: Bracketed expression "[...]" is not valid as a type [valid-type] +annotations_forward_refs.py:42: note: Did you mean "List[...]"? +annotations_forward_refs.py:43: error: Syntax error in type annotation [valid-type] +annotations_forward_refs.py:43: note: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) +annotations_forward_refs.py:44: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:45: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:46: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:47: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:48: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:49: error: Variable "tests.annotations_forward_refs.var1" is not valid as a type [valid-type] +annotations_forward_refs.py:49: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +annotations_forward_refs.py:50: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:51: error: Invalid type: try using Literal[1] instead? [valid-type] +annotations_forward_refs.py:52: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:53: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:54: error: Invalid type comment or annotation [valid-type] +annotations_forward_refs.py:55: error: Module "types" is not valid as a type [valid-type] +annotations_forward_refs.py:55: note: Perhaps you meant to use a protocol matching the module structure? +annotations_forward_refs.py:80: error: Name "ClassF" is not defined [name-defined] +annotations_forward_refs.py:82: error: Name "str" is not defined [name-defined] +annotations_forward_refs.py:87: error: Function "tests.annotations_forward_refs.ClassD.int" is not valid as a type [valid-type] +annotations_forward_refs.py:87: note: Perhaps you need "Callable[...]" or a callback protocol? +annotations_forward_refs.py:89: error: Function "tests.annotations_forward_refs.ClassD.int" is not valid as a type [valid-type] +annotations_forward_refs.py:89: note: Perhaps you need "Callable[...]" or a callback protocol? +annotations_forward_refs.py:95: error: Expression is of type "Any", not "str" [misc] +annotations_forward_refs.py:96: error: Expression is of type "Any", not "int" [misc] +""" diff --git a/conformance/results/zuban/annotations_generators.toml b/conformance/results/zuban/annotations_generators.toml new file mode 100644 index 000000000..8243e130d --- /dev/null +++ b/conformance/results/zuban/annotations_generators.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +annotations_generators.py:51: error: Incompatible return value type (implicitly returns "None", expected "C") [return-value] +annotations_generators.py:54: error: Incompatible return value type (got "bool", expected "C") [return-value] +annotations_generators.py:57: error: Incompatible types in "yield" (actual type "int", expected type "A") [misc] +annotations_generators.py:66: error: Incompatible types in "yield" (actual type "int", expected type "A") [misc] +annotations_generators.py:71: error: No return value expected [return-value] +annotations_generators.py:75: error: Incompatible types in "yield" (actual type "B", expected type "A") [misc] +annotations_generators.py:86: error: The return type of a generator function should be "Generator" or one of its supertypes [misc] +annotations_generators.py:91: error: The return type of an async generator function should be "AsyncGenerator" or one of its supertypes [misc] +annotations_generators.py:118: error: Incompatible types in "yield from" (actual type "A", expected type "B") [misc] +annotations_generators.py:119: error: Incompatible types in "yield from" (actual type "int", expected type "B") [misc] +annotations_generators.py:135: error: Incompatible send types in yield from (actual type "int", expected type "str") [misc] +""" diff --git a/conformance/results/zuban/annotations_methods.toml b/conformance/results/zuban/annotations_methods.toml new file mode 100644 index 000000000..bd2004812 --- /dev/null +++ b/conformance/results/zuban/annotations_methods.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +annotations_methods.py:42: error: Expression is of type "B", not "A" [misc] +""" diff --git a/conformance/results/zuban/annotations_typeexpr.toml b/conformance/results/zuban/annotations_typeexpr.toml new file mode 100644 index 000000000..67edaac2d --- /dev/null +++ b/conformance/results/zuban/annotations_typeexpr.toml @@ -0,0 +1,24 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +annotations_typeexpr.py:88: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:89: error: Bracketed expression "[...]" is not valid as a type [valid-type] +annotations_typeexpr.py:89: note: Did you mean "List[...]"? +annotations_typeexpr.py:90: error: Syntax error in type annotation [valid-type] +annotations_typeexpr.py:90: note: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) +annotations_typeexpr.py:91: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:92: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:93: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:94: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:95: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:96: error: Variable "tests.annotations_typeexpr.var1" is not valid as a type [valid-type] +annotations_typeexpr.py:96: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +annotations_typeexpr.py:97: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:98: error: Invalid type: try using Literal[1] instead? [valid-type] +annotations_typeexpr.py:99: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:100: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:101: error: Invalid type comment or annotation [valid-type] +annotations_typeexpr.py:102: error: Module "types" is not valid as a type [valid-type] +annotations_typeexpr.py:102: note: Perhaps you meant to use a protocol matching the module structure? +""" diff --git a/conformance/results/zuban/callables_annotation.toml b/conformance/results/zuban/callables_annotation.toml new file mode 100644 index 000000000..e131dee29 --- /dev/null +++ b/conformance/results/zuban/callables_annotation.toml @@ -0,0 +1,29 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +callables_annotation.py:25: error: Too few arguments [call-arg] +callables_annotation.py:26: error: Argument 2 has incompatible type "int"; expected "str" [arg-type] +callables_annotation.py:27: error: Too many arguments [call-arg] +callables_annotation.py:29: error: Unexpected keyword argument "a" [call-arg] +callables_annotation.py:29: error: Unexpected keyword argument "b" [call-arg] +callables_annotation.py:35: error: Too many arguments [call-arg] +callables_annotation.py:55: error: Please use "Callable[[], ]" or "Callable" [misc] +callables_annotation.py:56: error: The first argument to Callable must be a list of types, parameter specification, or "..." [misc] +callables_annotation.py:56: note: See https://mypy.readthedocs.io/en/stable/kinds_of_types.html#callable-types-and-lambdas +callables_annotation.py:57: error: Bracketed expression "[...]" is not valid as a type [valid-type] +callables_annotation.py:57: note: Did you mean "List[...]"? +callables_annotation.py:58: error: Please use "Callable[[], ]" or "Callable" [misc] +callables_annotation.py:59: error: Unexpected "..." [valid-type] +callables_annotation.py:91: error: Incompatible types in assignment (expression has type "Callable[[], str]", variable has type "def (int, /, *Any, **Any) -> str") [assignment] +callables_annotation.py:93: error: Incompatible types in assignment (expression has type "def test_cb4(*, a: int) -> str", variable has type "def (int, /, *Any, **Any) -> str") [assignment] +callables_annotation.py:159: error: Incompatible types in assignment (expression has type "Proto8", variable has type "Proto5[Any]") [assignment] +callables_annotation.py:159: note: Following member(s) of "Proto8" have conflicts: +callables_annotation.py:159: note: Expected: +callables_annotation.py:159: note: def __call__(self, *args: Any, **kwargs: Any) -> None +callables_annotation.py:159: note: Got: +callables_annotation.py:159: note: def __call__(self) -> None +callables_annotation.py:172: error: Incompatible types in assignment (expression has type "Callable[[], str]", variable has type "def (int, /, *Any, **Any) -> str") [assignment] +callables_annotation.py:187: error: Incompatible types in assignment (expression has type "Callable[[int, str], str]", variable has type "def (str, /, *Any, **Any) -> str") [assignment] +callables_annotation.py:189: error: Incompatible types in assignment (expression has type "Callable[[int, str], str]", variable has type "def (str, /, *Any, **Any) -> str") [assignment] +""" diff --git a/conformance/results/zuban/callables_kwargs.toml b/conformance/results/zuban/callables_kwargs.toml new file mode 100644 index 000000000..678f5d318 --- /dev/null +++ b/conformance/results/zuban/callables_kwargs.toml @@ -0,0 +1,25 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +callables_kwargs.py:46: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:46: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:51: error: Unexpected keyword argument "v4" for "func1" [call-arg] +callables_kwargs.py:52: error: Too many positional arguments for "func1" [call-arg] +callables_kwargs.py:52: error: Missing named argument "v1" for "func1" [call-arg] +callables_kwargs.py:52: error: Missing named argument "v3" for "func1" [call-arg] +callables_kwargs.py:58: error: Argument 1 to "func1" has incompatible type "**dict[str, str]"; expected "int" [arg-type] +callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "int" [arg-type] +callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] +callables_kwargs.py:61: error: Argument 1 to "func1" has incompatible type "**dict[str, int | str]"; expected "str" [arg-type] +callables_kwargs.py:63: error: "func1" gets multiple values for keyword argument "v1" [call-arg] +callables_kwargs.py:64: error: Argument 1 to "func2" has incompatible type "int"; expected "str" [arg-type] +callables_kwargs.py:64: error: "func2" gets multiple values for keyword argument "v3" [call-arg] +callables_kwargs.py:65: error: "func2" gets multiple values for keyword argument "v1" [call-arg] +callables_kwargs.py:101: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol3") [assignment] +callables_kwargs.py:102: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol4") [assignment] +callables_kwargs.py:103: error: Incompatible types in assignment (expression has type "def func1(**kwargs: Unpack[TD2]) -> None", variable has type "TDProtocol5") [assignment] +callables_kwargs.py:111: error: Overlap between parameter names and ** TypedDict items: "v1" [misc] +callables_kwargs.py:122: error: Unpack item in ** parameter must be a TypedDict [misc] +callables_kwargs.py:134: error: Incompatible types in assignment (expression has type "def func7(*, v1: int, v3: str, v2: str = ...) -> None", variable has type "TDProtocol6") [assignment] +""" diff --git a/conformance/results/zuban/callables_protocol.toml b/conformance/results/zuban/callables_protocol.toml new file mode 100644 index 000000000..675f92555 --- /dev/null +++ b/conformance/results/zuban/callables_protocol.toml @@ -0,0 +1,24 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +callables_protocol.py:35: error: Incompatible types in assignment (expression has type "def cb1_bad1(*vals: bytes, max_items: int | None) -> list[bytes]", variable has type "Proto1") [assignment] +callables_protocol.py:36: error: Incompatible types in assignment (expression has type "def cb1_bad2(*vals: bytes) -> list[bytes]", variable has type "Proto1") [assignment] +callables_protocol.py:37: error: Incompatible types in assignment (expression has type "def cb1_bad3(*vals: bytes, max_len: str | None) -> list[bytes]", variable has type "Proto1") [assignment] +callables_protocol.py:67: error: Incompatible types in assignment (expression has type "def cb2_bad1(*a: bytes) -> None", variable has type "Proto2") [assignment] +callables_protocol.py:68: error: Incompatible types in assignment (expression has type "def cb2_bad2(*a: str, **b: str) -> None", variable has type "Proto2") [assignment] +callables_protocol.py:69: error: Incompatible types in assignment (expression has type "def cb2_bad3(*a: bytes, **b: bytes) -> None", variable has type "Proto2") [assignment] +callables_protocol.py:70: error: Incompatible types in assignment (expression has type "def cb2_bad4(**b: str) -> None", variable has type "Proto2") [assignment] +callables_protocol.py:97: error: Incompatible types in assignment (expression has type "Callable[[int], None]", variable has type "Proto4") [assignment] +callables_protocol.py:97: note: "Callable[[int], None]" is missing following "Proto4" protocol member: +callables_protocol.py:97: note: other_attribute +callables_protocol.py:121: error: Incompatible types in assignment (expression has type "def cb6_bad1(*vals: bytes, max_len: int | None = ...) -> list[bytes]", variable has type "NotProto6") [assignment] +callables_protocol.py:169: error: Incompatible types in assignment (expression has type "Callable[[int], Any]", variable has type "Proto8") [assignment] +callables_protocol.py:186: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +callables_protocol.py:187: error: "Proto9[P, R]" has no attribute "xxx" [attr-defined] +callables_protocol.py:197: error: "Proto9[[x: int], str]" has no attribute "other_attribute2" [attr-defined] +callables_protocol.py:238: error: Incompatible types in assignment (expression has type "Callable[[int, str], Any]", variable has type "Proto11") [assignment] +callables_protocol.py:260: error: Incompatible types in assignment (expression has type "def cb12_bad1(*args: Any, kwarg0: Any) -> None", variable has type "Proto12") [assignment] +callables_protocol.py:284: error: Incompatible types in assignment (expression has type "Callable[[str], str]", variable has type "Proto13_Default") [assignment] +callables_protocol.py:311: error: Incompatible types in assignment (expression has type "def cb14_no_default(*, path: str) -> str", variable has type "Proto14_Default") [assignment] +""" diff --git a/conformance/results/zuban/callables_subtyping.toml b/conformance/results/zuban/callables_subtyping.toml new file mode 100644 index 000000000..81e71c806 --- /dev/null +++ b/conformance/results/zuban/callables_subtyping.toml @@ -0,0 +1,187 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +callables_subtyping.py:26: error: Incompatible types in assignment (expression has type "Callable[[int], int]", variable has type "Callable[[float], float]") [assignment] +callables_subtyping.py:29: error: Incompatible types in assignment (expression has type "Callable[[float], float]", variable has type "Callable[[int], int]") [assignment] +callables_subtyping.py:51: error: Incompatible types in assignment (expression has type "PosOnly2", variable has type "Standard2") [assignment] +callables_subtyping.py:51: note: Following member(s) of "PosOnly2" have conflicts: +callables_subtyping.py:51: note: Expected: +callables_subtyping.py:51: note: def __call__(self, a: int, b: int) -> None +callables_subtyping.py:51: note: Got: +callables_subtyping.py:51: note: def __call__(self, int, int, /) -> None +callables_subtyping.py:52: error: Incompatible types in assignment (expression has type "KwOnly2", variable has type "Standard2") [assignment] +callables_subtyping.py:52: note: Following member(s) of "KwOnly2" have conflicts: +callables_subtyping.py:52: note: Expected: +callables_subtyping.py:52: note: def __call__(self, a: int, b: int) -> None +callables_subtyping.py:52: note: Got: +callables_subtyping.py:52: note: def __call__(self, *, b: int, a: int) -> None +callables_subtyping.py:55: error: Incompatible types in assignment (expression has type "KwOnly2", variable has type "PosOnly2") [assignment] +callables_subtyping.py:55: note: Following member(s) of "KwOnly2" have conflicts: +callables_subtyping.py:55: note: Expected: +callables_subtyping.py:55: note: def __call__(self, int, int, /) -> None +callables_subtyping.py:55: note: Got: +callables_subtyping.py:55: note: def __call__(self, *, b: int, a: int) -> None +callables_subtyping.py:58: error: Incompatible types in assignment (expression has type "PosOnly2", variable has type "KwOnly2") [assignment] +callables_subtyping.py:58: note: Following member(s) of "PosOnly2" have conflicts: +callables_subtyping.py:58: note: Expected: +callables_subtyping.py:58: note: def __call__(self, *, b: int, a: int) -> None +callables_subtyping.py:58: note: Got: +callables_subtyping.py:58: note: def __call__(self, int, int, /) -> None +callables_subtyping.py:82: error: Incompatible types in assignment (expression has type "NoArgs3", variable has type "IntArgs3") [assignment] +callables_subtyping.py:82: note: Following member(s) of "NoArgs3" have conflicts: +callables_subtyping.py:82: note: Expected: +callables_subtyping.py:82: note: def __call__(self, *args: int) -> None +callables_subtyping.py:82: note: Got: +callables_subtyping.py:82: note: def __call__(self) -> None +callables_subtyping.py:85: error: Incompatible types in assignment (expression has type "NoArgs3", variable has type "FloatArgs3") [assignment] +callables_subtyping.py:85: note: Following member(s) of "NoArgs3" have conflicts: +callables_subtyping.py:85: note: Expected: +callables_subtyping.py:85: note: def __call__(self, *args: float) -> None +callables_subtyping.py:85: note: Got: +callables_subtyping.py:85: note: def __call__(self) -> None +callables_subtyping.py:86: error: Incompatible types in assignment (expression has type "IntArgs3", variable has type "FloatArgs3") [assignment] +callables_subtyping.py:86: note: Following member(s) of "IntArgs3" have conflicts: +callables_subtyping.py:86: note: Expected: +callables_subtyping.py:86: note: def __call__(self, *args: float) -> None +callables_subtyping.py:86: note: Got: +callables_subtyping.py:86: note: def __call__(self, *args: int) -> None +callables_subtyping.py:116: error: Incompatible types in assignment (expression has type "IntArgs4", variable has type "PosOnly4") [assignment] +callables_subtyping.py:116: note: Following member(s) of "IntArgs4" have conflicts: +callables_subtyping.py:116: note: Expected: +callables_subtyping.py:116: note: def __call__(self, int, str, /) -> None +callables_subtyping.py:116: note: Got: +callables_subtyping.py:116: note: def __call__(self, *args: int) -> None +callables_subtyping.py:119: error: Incompatible types in assignment (expression has type "StrArgs4", variable has type "IntStrArgs4") [assignment] +callables_subtyping.py:119: note: Following member(s) of "StrArgs4" have conflicts: +callables_subtyping.py:119: note: Expected: +callables_subtyping.py:119: note: def __call__(self, *args: int | str) -> None +callables_subtyping.py:119: note: Got: +callables_subtyping.py:119: note: def __call__(self, int, /, *args: str) -> None +callables_subtyping.py:120: error: Incompatible types in assignment (expression has type "IntArgs4", variable has type "IntStrArgs4") [assignment] +callables_subtyping.py:120: note: Following member(s) of "IntArgs4" have conflicts: +callables_subtyping.py:120: note: Expected: +callables_subtyping.py:120: note: def __call__(self, *args: int | str) -> None +callables_subtyping.py:120: note: Got: +callables_subtyping.py:120: note: def __call__(self, *args: int) -> None +callables_subtyping.py:122: error: Incompatible types in assignment (expression has type "IntArgs4", variable has type "StrArgs4") [assignment] +callables_subtyping.py:122: note: Following member(s) of "IntArgs4" have conflicts: +callables_subtyping.py:122: note: Expected: +callables_subtyping.py:122: note: def __call__(self, int, /, *args: str) -> None +callables_subtyping.py:122: note: Got: +callables_subtyping.py:122: note: def __call__(self, *args: int) -> None +callables_subtyping.py:124: error: Incompatible types in assignment (expression has type "StrArgs4", variable has type "IntArgs4") [assignment] +callables_subtyping.py:124: note: Following member(s) of "StrArgs4" have conflicts: +callables_subtyping.py:124: note: Expected: +callables_subtyping.py:124: note: def __call__(self, *args: int) -> None +callables_subtyping.py:124: note: Got: +callables_subtyping.py:124: note: def __call__(self, int, /, *args: str) -> None +callables_subtyping.py:125: error: Incompatible types in assignment (expression has type "IntStrArgs4", variable has type "Standard4") [assignment] +callables_subtyping.py:125: note: Following member(s) of "IntStrArgs4" have conflicts: +callables_subtyping.py:125: note: Expected: +callables_subtyping.py:125: note: def __call__(self, a: int, b: str) -> None +callables_subtyping.py:125: note: Got: +callables_subtyping.py:125: note: def __call__(self, *args: int | str) -> None +callables_subtyping.py:126: error: Incompatible types in assignment (expression has type "StrArgs4", variable has type "Standard4") [assignment] +callables_subtyping.py:126: note: Following member(s) of "StrArgs4" have conflicts: +callables_subtyping.py:126: note: Expected: +callables_subtyping.py:126: note: def __call__(self, a: int, b: str) -> None +callables_subtyping.py:126: note: Got: +callables_subtyping.py:126: note: def __call__(self, int, /, *args: str) -> None +callables_subtyping.py:151: error: Incompatible types in assignment (expression has type "NoKwargs5", variable has type "IntKwargs5") [assignment] +callables_subtyping.py:151: note: Following member(s) of "NoKwargs5" have conflicts: +callables_subtyping.py:151: note: Expected: +callables_subtyping.py:151: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:151: note: Got: +callables_subtyping.py:151: note: def __call__(self) -> None +callables_subtyping.py:154: error: Incompatible types in assignment (expression has type "NoKwargs5", variable has type "FloatKwargs5") [assignment] +callables_subtyping.py:154: note: Following member(s) of "NoKwargs5" have conflicts: +callables_subtyping.py:154: note: Expected: +callables_subtyping.py:154: note: def __call__(self, **kwargs: float) -> None +callables_subtyping.py:154: note: Got: +callables_subtyping.py:154: note: def __call__(self) -> None +callables_subtyping.py:155: error: Incompatible types in assignment (expression has type "IntKwargs5", variable has type "FloatKwargs5") [assignment] +callables_subtyping.py:155: note: Following member(s) of "IntKwargs5" have conflicts: +callables_subtyping.py:155: note: Expected: +callables_subtyping.py:155: note: def __call__(self, **kwargs: float) -> None +callables_subtyping.py:155: note: Got: +callables_subtyping.py:155: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:187: error: Incompatible types in assignment (expression has type "IntKwargs6", variable has type "KwOnly6") [assignment] +callables_subtyping.py:187: note: Following member(s) of "IntKwargs6" have conflicts: +callables_subtyping.py:187: note: Expected: +callables_subtyping.py:187: note: def __call__(self, *, a: int, b: str) -> None +callables_subtyping.py:187: note: Got: +callables_subtyping.py:187: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:190: error: Incompatible types in assignment (expression has type "StrKwargs6", variable has type "IntStrKwargs6") [assignment] +callables_subtyping.py:190: note: Following member(s) of "StrKwargs6" have conflicts: +callables_subtyping.py:190: note: Expected: +callables_subtyping.py:190: note: def __call__(self, **kwargs: int | str) -> None +callables_subtyping.py:190: note: Got: +callables_subtyping.py:190: note: def __call__(self, *, a: int, **kwargs: str) -> None +callables_subtyping.py:191: error: Incompatible types in assignment (expression has type "IntKwargs6", variable has type "IntStrKwargs6") [assignment] +callables_subtyping.py:191: note: Following member(s) of "IntKwargs6" have conflicts: +callables_subtyping.py:191: note: Expected: +callables_subtyping.py:191: note: def __call__(self, **kwargs: int | str) -> None +callables_subtyping.py:191: note: Got: +callables_subtyping.py:191: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:193: error: Incompatible types in assignment (expression has type "IntKwargs6", variable has type "StrKwargs6") [assignment] +callables_subtyping.py:193: note: Following member(s) of "IntKwargs6" have conflicts: +callables_subtyping.py:193: note: Expected: +callables_subtyping.py:193: note: def __call__(self, *, a: int, **kwargs: str) -> None +callables_subtyping.py:193: note: Got: +callables_subtyping.py:193: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:195: error: Incompatible types in assignment (expression has type "StrKwargs6", variable has type "IntKwargs6") [assignment] +callables_subtyping.py:195: note: Following member(s) of "StrKwargs6" have conflicts: +callables_subtyping.py:195: note: Expected: +callables_subtyping.py:195: note: def __call__(self, **kwargs: int) -> None +callables_subtyping.py:195: note: Got: +callables_subtyping.py:195: note: def __call__(self, *, a: int, **kwargs: str) -> None +callables_subtyping.py:196: error: Incompatible types in assignment (expression has type "IntStrKwargs6", variable has type "Standard6") [assignment] +callables_subtyping.py:196: note: Following member(s) of "IntStrKwargs6" have conflicts: +callables_subtyping.py:196: note: Expected: +callables_subtyping.py:196: note: def __call__(self, a: int, b: str) -> None +callables_subtyping.py:196: note: Got: +callables_subtyping.py:196: note: def __call__(self, **kwargs: int | str) -> None +callables_subtyping.py:197: error: Incompatible types in assignment (expression has type "StrKwargs6", variable has type "Standard6") [assignment] +callables_subtyping.py:197: note: Following member(s) of "StrKwargs6" have conflicts: +callables_subtyping.py:197: note: Expected: +callables_subtyping.py:197: note: def __call__(self, a: int, b: str) -> None +callables_subtyping.py:197: note: Got: +callables_subtyping.py:197: note: def __call__(self, *, a: int, **kwargs: str) -> None +callables_subtyping.py:236: error: Incompatible types in assignment (expression has type "NoDefaultArg8", variable has type "DefaultArg8") [assignment] +callables_subtyping.py:236: note: Following member(s) of "NoDefaultArg8" have conflicts: +callables_subtyping.py:236: note: Expected: +callables_subtyping.py:236: note: def __call__(self, x: int = ...) -> None +callables_subtyping.py:236: note: Got: +callables_subtyping.py:236: note: def __call__(self, x: int) -> None +callables_subtyping.py:237: error: Incompatible types in assignment (expression has type "NoX8", variable has type "DefaultArg8") [assignment] +callables_subtyping.py:237: note: Following member(s) of "NoX8" have conflicts: +callables_subtyping.py:237: note: Expected: +callables_subtyping.py:237: note: def __call__(self, x: int = ...) -> None +callables_subtyping.py:237: note: Got: +callables_subtyping.py:237: note: def __call__(self) -> None +callables_subtyping.py:240: error: Incompatible types in assignment (expression has type "NoX8", variable has type "NoDefaultArg8") [assignment] +callables_subtyping.py:240: note: Following member(s) of "NoX8" have conflicts: +callables_subtyping.py:240: note: Expected: +callables_subtyping.py:240: note: def __call__(self, x: int) -> None +callables_subtyping.py:240: note: Got: +callables_subtyping.py:240: note: def __call__(self) -> None +callables_subtyping.py:243: error: Incompatible types in assignment (expression has type "NoDefaultArg8", variable has type "NoX8") [assignment] +callables_subtyping.py:243: note: Following member(s) of "NoDefaultArg8" have conflicts: +callables_subtyping.py:243: note: Expected: +callables_subtyping.py:243: note: def __call__(self) -> None +callables_subtyping.py:243: note: Got: +callables_subtyping.py:243: note: def __call__(self, x: int) -> None +callables_subtyping.py:273: error: Incompatible types in assignment (expression has type "Overloaded9", variable has type "FloatArg9") [assignment] +callables_subtyping.py:273: note: Following member(s) of "Overloaded9" have conflicts: +callables_subtyping.py:273: note: Expected: +callables_subtyping.py:273: note: def __call__(self: FloatArg9, x: float) -> float +callables_subtyping.py:273: note: Got: +callables_subtyping.py:273: note: def __call__(*args: Any, **kwds: Any) -> Any +callables_subtyping.py:297: error: Incompatible types in assignment (expression has type "StrArg10", variable has type "Overloaded10") [assignment] +callables_subtyping.py:297: note: Following member(s) of "StrArg10" have conflicts: +callables_subtyping.py:297: note: Expected: +callables_subtyping.py:297: note: def __call__(*args: Any, **kwds: Any) -> Any +callables_subtyping.py:297: note: Got: +callables_subtyping.py:297: note: def __call__(self: StrArg10, x: str) -> complex +""" diff --git a/conformance/results/zuban/classes_classvar.toml b/conformance/results/zuban/classes_classvar.toml new file mode 100644 index 000000000..a451ac506 --- /dev/null +++ b/conformance/results/zuban/classes_classvar.toml @@ -0,0 +1,27 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +classes_classvar.py:38: error: ClassVar[...] must have at most one type argument [misc] +classes_classvar.py:39: error: Invalid type: try using Literal[3] instead? [valid-type] +classes_classvar.py:40: error: Name "var" is not defined [name-defined] +classes_classvar.py:45: error: ClassVar cannot contain type variables [misc] +classes_classvar.py:46: error: ClassVar cannot contain type variables [misc] +classes_classvar.py:47: error: ClassVar cannot contain type variables [misc] +classes_classvar.py:52: error: Incompatible types in assignment (expression has type "dict[Any, Any]", variable has type "list[str]") [assignment] +classes_classvar.py:54: error: Variable should not be annotated with both ClassVar and Final [misc] +classes_classvar.py:55: error: Invalid Type: ClassVar nested inside other type [misc] +classes_classvar.py:66: error: Type in ClassVar[...] can only be omitted if there is an initializer [misc] +classes_classvar.py:69: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:70: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:71: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:73: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:77: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:78: error: ClassVar can only be used for assignments in class body [misc] +classes_classvar.py:111: error: Cannot assign to class variable "stats" via instance [misc] +classes_classvar.py:140: error: Incompatible types in assignment (expression has type "ProtoAImpl", variable has type "ProtoA") [assignment] +classes_classvar.py:140: note: Protocol member ProtoA.x expected class variable, got instance variable +classes_classvar.py:140: note: Protocol member ProtoA.y expected class variable, got instance variable +classes_classvar.py:140: note: "ProtoAImpl" is missing following "ProtoA" protocol member: +classes_classvar.py:140: note: z +""" diff --git a/conformance/results/zuban/classes_override.toml b/conformance/results/zuban/classes_override.toml new file mode 100644 index 000000000..6bc2112ca --- /dev/null +++ b/conformance/results/zuban/classes_override.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +classes_override.py:53: error: Method "method3" is marked as an override, but no base method was found with this name [misc] +classes_override.py:56: error: Method "method4" is marked as an override, but no base method was found with this name [misc] +classes_override.py:79: error: Method "static_method1" is marked as an override, but no base method was found with this name [misc] +classes_override.py:84: error: Method "class_method1" is marked as an override, but no base method was found with this name [misc] +classes_override.py:89: error: Method "property1" is marked as an override, but no base method was found with this name [misc] +""" diff --git a/conformance/results/zuban/constructors_call_init.toml b/conformance/results/zuban/constructors_call_init.toml new file mode 100644 index 000000000..3ebf04786 --- /dev/null +++ b/conformance/results/zuban/constructors_call_init.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_init.py:21: error: Argument 1 to "Class1" has incompatible type "float"; expected "int" [arg-type] +constructors_call_init.py:42: error: Argument 1 to "Class3" has incompatible type "Class2[Any]"; expected "Self | None" [arg-type] +constructors_call_init.py:56: error: Invalid self type in __init__ [call-arg] +constructors_call_init.py:107: error: The type of self "T1" has type vars in non standard positions for class "Class8" [misc] +constructors_call_init.py:107: error: The type of self "T2" has type vars in non standard positions for class "Class8" [misc] +constructors_call_init.py:130: error: Too many arguments for "Class11" [call-arg] +""" diff --git a/conformance/results/zuban/constructors_call_metaclass.toml b/conformance/results/zuban/constructors_call_metaclass.toml new file mode 100644 index 000000000..c9c2218aa --- /dev/null +++ b/conformance/results/zuban/constructors_call_metaclass.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_metaclass.py:54: error: Missing positional argument "x" in call to "Class3" [call-arg] +constructors_call_metaclass.py:68: error: Missing positional argument "x" in call to "Class4" [call-arg] +""" diff --git a/conformance/results/zuban/constructors_call_new.toml b/conformance/results/zuban/constructors_call_new.toml new file mode 100644 index 000000000..c3407c9ad --- /dev/null +++ b/conformance/results/zuban/constructors_call_new.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_new.py:21: error: Argument 1 to "Class1" has incompatible type "float"; expected "int" [arg-type] +constructors_call_new.py:148: error: Invalid self argument "type[Class11[str]]" to attribute function "__new__" with type "type[Class11[int]]" [misc] +""" diff --git a/conformance/results/zuban/constructors_call_type.toml b/conformance/results/zuban/constructors_call_type.toml new file mode 100644 index 000000000..45cc8aeb0 --- /dev/null +++ b/conformance/results/zuban/constructors_call_type.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +constructors_call_type.py:30: error: Missing positional arguments "x", "y" in call to "Class1" [call-arg] +constructors_call_type.py:40: error: Missing positional arguments "x", "y" in call to "Class2" [call-arg] +constructors_call_type.py:50: error: Missing positional arguments "x", "y" in call to "Class3" [call-arg] +constructors_call_type.py:59: error: Too many arguments for "Class4" [call-arg] +constructors_call_type.py:64: error: Too many arguments for "object" [call-arg] +constructors_call_type.py:72: error: Missing positional arguments "x", "y" in call to "Class1" [call-arg] +constructors_call_type.py:81: error: Missing positional argument "y" in call to "Class2" [call-arg] +constructors_call_type.py:82: error: Argument 2 to "Class2" has incompatible type "int"; expected "str" [arg-type] +""" diff --git a/conformance/results/zuban/constructors_callable.toml b/conformance/results/zuban/constructors_callable.toml new file mode 100644 index 000000000..09b2c88ee --- /dev/null +++ b/conformance/results/zuban/constructors_callable.toml @@ -0,0 +1,27 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +constructors_callable.py:36: note: Revealed type is "def (x: builtins.int) -> tests.constructors_callable.Class1" +constructors_callable.py:38: error: Missing positional argument "x" in call [call-arg] +constructors_callable.py:39: error: Unexpected keyword argument "y" [call-arg] +constructors_callable.py:49: note: Revealed type is "def () -> tests.constructors_callable.Class2" +constructors_callable.py:51: error: Too many arguments [call-arg] +constructors_callable.py:64: note: Revealed type is "def (x: builtins.int) -> tests.constructors_callable.Class3" +constructors_callable.py:66: error: Missing positional argument "x" in call [call-arg] +constructors_callable.py:67: error: Unexpected keyword argument "y" [call-arg] +constructors_callable.py:68: error: Too many arguments [call-arg] +constructors_callable.py:79: note: Revealed type is "def (x: builtins.int) -> builtins.int" +constructors_callable.py:81: error: Missing positional argument "x" in call [call-arg] +constructors_callable.py:82: error: Unexpected keyword argument "y" [call-arg] +constructors_callable.py:99: note: Revealed type is "def (*args: Any, **kwargs: Any) -> Never" +constructors_callable.py:127: note: Revealed type is "def () -> tests.constructors_callable.Class6Proxy" +constructors_callable.py:129: error: Too many arguments [call-arg] +constructors_callable.py:144: note: Revealed type is "def () -> Any" +constructors_callable.py:146: error: Too many arguments [call-arg] +constructors_callable.py:164: note: Revealed type is "Overload(def (x: builtins.int) -> tests.constructors_callable.Class7[builtins.int], def (x: builtins.str) -> tests.constructors_callable.Class7[builtins.str])" +constructors_callable.py:184: note: Revealed type is "def [T] (x: builtins.list[T], y: builtins.list[T]) -> tests.constructors_callable.Class8[T]" +constructors_callable.py:186: error: List item 0 has incompatible type "str"; expected "int" [list-item] +constructors_callable.py:195: note: Revealed type is "def [T] (x: builtins.list[T], y: builtins.list[T]) -> tests.constructors_callable.Class9" +constructors_callable.py:197: error: List item 0 has incompatible type "str"; expected "int" [list-item] +""" diff --git a/conformance/results/zuban/constructors_consistency.toml b/conformance/results/zuban/constructors_consistency.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/constructors_consistency.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/dataclasses_descriptors.toml b/conformance/results/zuban/dataclasses_descriptors.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/dataclasses_descriptors.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/dataclasses_final.toml b/conformance/results/zuban/dataclasses_final.toml new file mode 100644 index 000000000..b59d3cac0 --- /dev/null +++ b/conformance/results/zuban/dataclasses_final.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_final.py:27: error: Cannot assign to final attribute "final_classvar" [misc] +dataclasses_final.py:35: error: Cannot assign to final attribute "final_no_default" [misc] +dataclasses_final.py:36: error: Cannot assign to final attribute "final_with_default" [misc] +dataclasses_final.py:37: error: Cannot assign to final attribute "final_no_default" [misc] +dataclasses_final.py:38: error: Cannot assign to final attribute "final_with_default" [misc] +""" diff --git a/conformance/results/zuban/dataclasses_frozen.toml b/conformance/results/zuban/dataclasses_frozen.toml new file mode 100644 index 000000000..81262e632 --- /dev/null +++ b/conformance/results/zuban/dataclasses_frozen.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_frozen.py:16: error: Property "a" defined in "DC1" is read-only [misc] +dataclasses_frozen.py:17: error: Property "b" defined in "DC1" is read-only [misc] +dataclasses_frozen.py:23: error: Non-frozen dataclass cannot inherit from a frozen dataclass [misc] +dataclasses_frozen.py:33: error: Frozen dataclass cannot inherit from a non-frozen dataclass [misc] +""" diff --git a/conformance/results/zuban/dataclasses_hash.toml b/conformance/results/zuban/dataclasses_hash.toml new file mode 100644 index 000000000..a041362c2 --- /dev/null +++ b/conformance/results/zuban/dataclasses_hash.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +conformant = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_hash.py:17: error: "None" not callable [operator] +dataclasses_hash.py:18: error: Incompatible types in assignment (expression has type "DC1", variable has type "Hashable") [assignment] +dataclasses_hash.py:18: note: Following member(s) of "DC1" have conflicts: +dataclasses_hash.py:18: note: __hash__: expected "Callable[[], int]", got "None" +dataclasses_hash.py:39: error: "None" not callable [operator] +dataclasses_hash.py:40: error: Incompatible types in assignment (expression has type "DC3", variable has type "Hashable") [assignment] +dataclasses_hash.py:40: note: Following member(s) of "DC3" have conflicts: +dataclasses_hash.py:40: note: __hash__: expected "Callable[[], int]", got "None" +""" diff --git a/conformance/results/zuban/dataclasses_inheritance.toml b/conformance/results/zuban/dataclasses_inheritance.toml new file mode 100644 index 000000000..3baad3e38 --- /dev/null +++ b/conformance/results/zuban/dataclasses_inheritance.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_inheritance.py:62: error: Cannot override instance variable (previously declared on base class "DC6") with class variable [misc] +dataclasses_inheritance.py:66: error: Cannot override class variable (previously declared on base class "DC6") with instance variable [misc] +""" diff --git a/conformance/results/zuban/dataclasses_kwonly.toml b/conformance/results/zuban/dataclasses_kwonly.toml new file mode 100644 index 000000000..88c32379f --- /dev/null +++ b/conformance/results/zuban/dataclasses_kwonly.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_kwonly.py:23: error: Too many positional arguments for "DC1" [call-arg] +dataclasses_kwonly.py:38: error: Too many positional arguments for "DC2" [call-arg] +dataclasses_kwonly.py:53: error: Too many positional arguments for "DC3" [call-arg] +""" diff --git a/conformance/results/zuban/dataclasses_match_args.toml b/conformance/results/zuban/dataclasses_match_args.toml new file mode 100644 index 000000000..1309854f5 --- /dev/null +++ b/conformance/results/zuban/dataclasses_match_args.toml @@ -0,0 +1,7 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_match_args.py:42: error: "type[DC4]" has no attribute "__match_args__" [attr-defined] +""" diff --git a/conformance/results/zuban/dataclasses_order.toml b/conformance/results/zuban/dataclasses_order.toml new file mode 100644 index 000000000..712ba3e1e --- /dev/null +++ b/conformance/results/zuban/dataclasses_order.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_order.py:50: error: Unsupported operand types for < ("DC1" and "DC2") [operator] +""" diff --git a/conformance/results/zuban/dataclasses_postinit.toml b/conformance/results/zuban/dataclasses_postinit.toml new file mode 100644 index 000000000..cd08f520c --- /dev/null +++ b/conformance/results/zuban/dataclasses_postinit.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_postinit.py:19: error: Argument 2 of "__post_init__" is incompatible with supertype "dataclass"; supertype defines the argument type as "str" [override] +dataclasses_postinit.py:28: error: "DC1" has no attribute "x" [attr-defined] +dataclasses_postinit.py:29: error: "DC1" has no attribute "y" [attr-defined] +dataclasses_postinit.py:36: error: Signature of "__post_init__" incompatible with supertype "dataclass" [override] +dataclasses_postinit.py:36: note: Superclass: +dataclasses_postinit.py:36: note: def __post_init__(self, x: int, y: str) -> None +dataclasses_postinit.py:36: note: Subclass: +dataclasses_postinit.py:36: note: def __post_init__(self, x: int) -> None +""" diff --git a/conformance/results/zuban/dataclasses_slots.toml b/conformance/results/zuban/dataclasses_slots.toml new file mode 100644 index 000000000..3bd82f443 --- /dev/null +++ b/conformance/results/zuban/dataclasses_slots.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_slots.py:11: error: "DC1" both defines "__slots__" and is used with "slots=True" [misc] +dataclasses_slots.py:25: error: Trying to assign name "y" that is not in "__slots__" of type "tests.dataclasses_slots.DC2" [misc] +dataclasses_slots.py:38: error: Trying to assign name "y" that is not in "__slots__" of type "tests.dataclasses_slots.DC3" [misc] +dataclasses_slots.py:66: error: "type[DC6]" has no attribute "__slots__" [attr-defined] +dataclasses_slots.py:69: error: "DC6" has no attribute "__slots__" [attr-defined] +""" diff --git a/conformance/results/zuban/dataclasses_transform_class.toml b/conformance/results/zuban/dataclasses_transform_class.toml new file mode 100644 index 000000000..e007f5906 --- /dev/null +++ b/conformance/results/zuban/dataclasses_transform_class.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_class.py:51: error: Non-frozen dataclass cannot inherit from a frozen dataclass [misc] +dataclasses_transform_class.py:63: error: Property "id" defined in "Customer1" is read-only [misc] +dataclasses_transform_class.py:66: error: Too many positional arguments for "Customer1" [call-arg] +dataclasses_transform_class.py:72: error: Unsupported left operand type for < ("Customer1") [operator] +dataclasses_transform_class.py:82: error: Too many positional arguments for "Customer2" [call-arg] +dataclasses_transform_class.py:122: error: Property "id" defined in "Customer3" is read-only [misc] +""" diff --git a/conformance/results/zuban/dataclasses_transform_converter.toml b/conformance/results/zuban/dataclasses_transform_converter.toml new file mode 100644 index 000000000..5191e0a9b --- /dev/null +++ b/conformance/results/zuban/dataclasses_transform_converter.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_converter.py:48: error: Argument "converter" to "model_field" has incompatible type "Callable[[], int]"; expected "Callable[[Never], int]" [arg-type] +dataclasses_transform_converter.py:49: error: Argument "converter" to "model_field" has incompatible type "def bad_converter2(*, x: int) -> int"; expected "Callable[[Never], int]" [arg-type] +dataclasses_transform_converter.py:107: error: Argument 1 to "DC2" has incompatible type "int"; expected "str" [arg-type] +dataclasses_transform_converter.py:108: error: Argument 4 to "DC2" has incompatible type "int"; expected "str | bytes" [arg-type] +dataclasses_transform_converter.py:109: error: Argument 5 to "DC2" has incompatible type "complex"; expected "str | list[str]" [arg-type] +dataclasses_transform_converter.py:118: error: Incompatible types in assignment of a dataclass converter (expression has type "int", expected type "str") [assignment] +dataclasses_transform_converter.py:119: error: Incompatible types in assignment of a dataclass converter (expression has type "int", expected type "str | bytes") [assignment] +dataclasses_transform_converter.py:130: error: Argument "default" to "model_field" has incompatible type "int"; expected "str | None" [arg-type] +dataclasses_transform_converter.py:133: error: Argument "default_factory" to "model_field" has incompatible type "type[int]"; expected "Callable[[], str] | None" [arg-type] +""" diff --git a/conformance/results/zuban/dataclasses_transform_field.toml b/conformance/results/zuban/dataclasses_transform_field.toml new file mode 100644 index 000000000..90f98c551 --- /dev/null +++ b/conformance/results/zuban/dataclasses_transform_field.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_field.py:64: error: Unexpected keyword argument "id" for "CustomerModel1" [call-arg] +dataclasses_transform_field.py:75: error: Too many positional arguments for "CustomerModel2" [call-arg] +""" diff --git a/conformance/results/zuban/dataclasses_transform_func.toml b/conformance/results/zuban/dataclasses_transform_func.toml new file mode 100644 index 000000000..4ebe35382 --- /dev/null +++ b/conformance/results/zuban/dataclasses_transform_func.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_func.py:56: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment] +dataclasses_transform_func.py:60: error: Unsupported left operand type for < ("Customer1") [operator] +dataclasses_transform_func.py:64: error: Unexpected keyword argument "salary" for "Customer1" [call-arg] +dataclasses_transform_func.py:70: error: Too many positional arguments for "Customer2" [call-arg] +dataclasses_transform_func.py:89: error: Non-frozen dataclass cannot inherit from a frozen dataclass [misc] +dataclasses_transform_func.py:96: error: Property "id" defined in "Customer3" is read-only [misc] +""" diff --git a/conformance/results/zuban/dataclasses_transform_meta.toml b/conformance/results/zuban/dataclasses_transform_meta.toml new file mode 100644 index 000000000..f19fbfd87 --- /dev/null +++ b/conformance/results/zuban/dataclasses_transform_meta.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_transform_meta.py:51: error: Non-frozen dataclass cannot inherit from a frozen dataclass [misc] +dataclasses_transform_meta.py:63: error: Property "id" defined in "Customer1" is read-only [misc] +dataclasses_transform_meta.py:66: error: Too many positional arguments for "Customer1" [call-arg] +dataclasses_transform_meta.py:73: error: Unsupported left operand type for < ("Customer1") [operator] +dataclasses_transform_meta.py:83: error: Too many positional arguments for "Customer2" [call-arg] +dataclasses_transform_meta.py:103: error: Property "id" defined in "Customer3" is read-only [misc] +""" diff --git a/conformance/results/zuban/dataclasses_usage.toml b/conformance/results/zuban/dataclasses_usage.toml new file mode 100644 index 000000000..8e2a7cccd --- /dev/null +++ b/conformance/results/zuban/dataclasses_usage.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +dataclasses_usage.py:51: error: Missing positional argument "unit_price" in call to "InventoryItem" [call-arg] +dataclasses_usage.py:52: error: Argument 2 to "InventoryItem" has incompatible type "str"; expected "float" [arg-type] +dataclasses_usage.py:53: error: Too many arguments for "InventoryItem" [call-arg] +dataclasses_usage.py:62: error: Attributes without a default cannot follow attributes with one [misc] +dataclasses_usage.py:68: error: Attributes without a default cannot follow attributes with one [misc] +dataclasses_usage.py:74: error: Attributes without a default cannot follow attributes with one [misc] +dataclasses_usage.py:84: error: Too many arguments for "DC4" [call-arg] +dataclasses_usage.py:89: error: Argument "default_factory" to "field" has incompatible type "type[str]"; expected "Callable[[], int]" [arg-type] +dataclasses_usage.py:128: error: Too many arguments for "DC7" [call-arg] +dataclasses_usage.py:131: error: Missing positional argument "y" in call to "DC8" [call-arg] +dataclasses_usage.py:180: error: Too many arguments for "DC13" [call-arg] +dataclasses_usage.py:246: error: Too many arguments for "DC19" [call-arg] +""" diff --git a/conformance/results/zuban/directives_assert_type.toml b/conformance/results/zuban/directives_assert_type.toml new file mode 100644 index 000000000..154dfdeda --- /dev/null +++ b/conformance/results/zuban/directives_assert_type.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_assert_type.py:27: error: Expression is of type "int | str", not "int" [misc] +directives_assert_type.py:28: error: Expression is of type "int | str", not "Any" [misc] +directives_assert_type.py:29: error: Expression is of type "Any", not "int" [misc] +directives_assert_type.py:30: error: Expression is of type "Literal[4]", not "int" [misc] +directives_assert_type.py:32: error: "assert_type" expects 2 arguments [call-arg] +directives_assert_type.py:33: error: Expression is of type "Literal['']", not "int" [misc] +directives_assert_type.py:34: error: "assert_type" expects 2 arguments [call-arg] +directives_assert_type.py:41: error: Expression is of type "str", not "str | Literal['spam']" [misc] +""" diff --git a/conformance/results/zuban/directives_cast.toml b/conformance/results/zuban/directives_cast.toml new file mode 100644 index 000000000..5f0624563 --- /dev/null +++ b/conformance/results/zuban/directives_cast.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_cast.py:15: error: "cast" expects 2 arguments [call-arg] +directives_cast.py:16: error: Invalid type: try using Literal[1] instead? [valid-type] +directives_cast.py:17: error: "cast" expects 2 arguments [call-arg] +""" diff --git a/conformance/results/zuban/directives_deprecated.toml b/conformance/results/zuban/directives_deprecated.toml new file mode 100644 index 000000000..a980f030f --- /dev/null +++ b/conformance/results/zuban/directives_deprecated.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_deprecated.py:18: error: class tests._directives_deprecated_library.Ham is deprecated: Use Spam instead [deprecated] +directives_deprecated.py:24: error: function tests._directives_deprecated_library.norwegian_blue is deprecated: It is pining for the fjords [deprecated] +directives_deprecated.py:25: error: function tests._directives_deprecated_library.norwegian_blue is deprecated: It is pining for the fjords [deprecated] +directives_deprecated.py:30: error: overload def (x: builtins.int) -> builtins.str of function tests._directives_deprecated_library.foo is deprecated: Only str will be allowed [deprecated] +directives_deprecated.py:41: error: function tests._directives_deprecated_library.Spam.__add__ is deprecated: There is enough spam in the world [deprecated] +directives_deprecated.py:42: error: function tests._directives_deprecated_library.Spam.__add__ is deprecated: There is enough spam in the world [deprecated] +directives_deprecated.py:44: error: function tests._directives_deprecated_library.Spam.greasy is deprecated: All spam will be equally greasy [deprecated] +directives_deprecated.py:47: error: function tests._directives_deprecated_library.Spam.shape is deprecated: Shapes are becoming immutable [deprecated] +directives_deprecated.py:48: error: function tests._directives_deprecated_library.Spam.shape is deprecated: Shapes are becoming immutable [deprecated] +directives_deprecated.py:58: error: function tests.directives_deprecated.Invocable.__call__ is deprecated: Deprecated [deprecated] +directives_deprecated.py:69: error: function tests.directives_deprecated.lorem is deprecated: Deprecated [deprecated] +directives_deprecated.py:98: error: function tests.directives_deprecated.SupportsFoo1.foo is deprecated: Deprecated [deprecated] +""" diff --git a/conformance/results/zuban/directives_disjoint_base.toml b/conformance/results/zuban/directives_disjoint_base.toml new file mode 100644 index 000000000..8746451e5 --- /dev/null +++ b/conformance/results/zuban/directives_disjoint_base.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_disjoint_base.py:69: error: Class "LeftAndRight" has incompatible disjoint bases [misc] +directives_disjoint_base.py:73: error: Class "LeftChildAndRight" has incompatible disjoint bases [misc] +directives_disjoint_base.py:77: error: Class "LeftAndRightViaChild" has incompatible disjoint bases [misc] +directives_disjoint_base.py:81: error: Class "LeftRecord" has incompatible disjoint bases [misc] +directives_disjoint_base.py:105: error: Class "IncompatibleSlots" has incompatible disjoint bases [misc] +directives_disjoint_base.py:113: error: Value of type variable "_TC" of "disjoint_base" cannot be "Callable[[], None]" [type-var] +directives_disjoint_base.py:118: error: @disjoint_base cannot be used with TypedDict [misc] +directives_disjoint_base.py:123: error: @disjoint_base cannot be used with protocol class [misc] +directives_disjoint_base.py:134: error: Subclass of "Left" and "Right" cannot exist: have distinct disjoint bases [unreachable] +""" diff --git a/conformance/results/zuban/directives_no_type_check.toml b/conformance/results/zuban/directives_no_type_check.toml new file mode 100644 index 000000000..722db58b0 --- /dev/null +++ b/conformance/results/zuban/directives_no_type_check.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_no_type_check.py:15: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +directives_no_type_check.py:32: error: Missing positional arguments "a", "b" in call to "func1" [call-arg] +""" diff --git a/conformance/results/zuban/directives_reveal_type.toml b/conformance/results/zuban/directives_reveal_type.toml new file mode 100644 index 000000000..76a0ed461 --- /dev/null +++ b/conformance/results/zuban/directives_reveal_type.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_reveal_type.py:14: note: Revealed type is "builtins.int | builtins.str" +directives_reveal_type.py:15: note: Revealed type is "builtins.list[builtins.int]" +directives_reveal_type.py:16: note: Revealed type is "Any" +directives_reveal_type.py:17: note: Revealed type is "tests.directives_reveal_type.ForwardReference" +directives_reveal_type.py:19: error: Too few arguments for "reveal_type" [call-arg] +directives_reveal_type.py:20: error: Too many arguments for "reveal_type" [call-arg] +""" diff --git a/conformance/results/zuban/directives_type_checking.toml b/conformance/results/zuban/directives_type_checking.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/directives_type_checking.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/directives_type_ignore.toml b/conformance/results/zuban/directives_type_ignore.toml new file mode 100644 index 000000000..1f6e8b4c6 --- /dev/null +++ b/conformance/results/zuban/directives_type_ignore.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_type_ignore.py:16: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +directives_type_ignore.py:16: note: Error code "assignment" not covered by "type: ignore[an-empty-str-is-not-an-int]" comment +""" diff --git a/conformance/results/zuban/directives_type_ignore_file1.toml b/conformance/results/zuban/directives_type_ignore_file1.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/directives_type_ignore_file1.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/directives_type_ignore_file2.toml b/conformance/results/zuban/directives_type_ignore_file2.toml new file mode 100644 index 000000000..afe027f9b --- /dev/null +++ b/conformance/results/zuban/directives_type_ignore_file2.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_type_ignore_file2.py:14: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment] +""" diff --git a/conformance/results/zuban/directives_version_platform.toml b/conformance/results/zuban/directives_version_platform.toml new file mode 100644 index 000000000..75af449a0 --- /dev/null +++ b/conformance/results/zuban/directives_version_platform.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +directives_version_platform.py:26: error: Expression is of type "int | str", not "int" [misc] +directives_version_platform.py:33: error: Name "val3" is not defined [name-defined] +directives_version_platform.py:42: error: Expression is of type "int | str", not "int" [misc] +directives_version_platform.py:50: error: Name "val6" is not defined [name-defined] +directives_version_platform.py:59: error: Name "val9" is not defined [name-defined] +""" diff --git a/conformance/results/zuban/enums_behaviors.toml b/conformance/results/zuban/enums_behaviors.toml new file mode 100644 index 000000000..cf90125a2 --- /dev/null +++ b/conformance/results/zuban/enums_behaviors.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_behaviors.py:28: error: Expression is of type "Color", not "Literal[Color.RED]" [misc] +enums_behaviors.py:32: error: Expression is of type "Color", not "Literal[Color.BLUE]" [misc] +enums_behaviors.py:44: error: Cannot extend enum with existing members: "Shape" [misc] +""" diff --git a/conformance/results/zuban/enums_definition.toml b/conformance/results/zuban/enums_definition.toml new file mode 100644 index 000000000..300aa75aa --- /dev/null +++ b/conformance/results/zuban/enums_definition.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_definition.py:25: error: Too many arguments for Enum() [call-arg] +enums_definition.py:92: error: "type[Color12]" has no attribute "BLUE" [attr-defined] +""" diff --git a/conformance/results/zuban/enums_expansion.toml b/conformance/results/zuban/enums_expansion.toml new file mode 100644 index 000000000..787ef8ef0 --- /dev/null +++ b/conformance/results/zuban/enums_expansion.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_expansion.py:35: error: Statement is unreachable [unreachable] +enums_expansion.py:53: error: Expression is of type "CustomFlags", not "Literal[CustomFlags.FLAG3]" [misc] +""" diff --git a/conformance/results/zuban/enums_member_names.toml b/conformance/results/zuban/enums_member_names.toml new file mode 100644 index 000000000..f22302d47 --- /dev/null +++ b/conformance/results/zuban/enums_member_names.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_member_names.py:30: error: Expression is of type "str", not "Literal['RED', 'BLUE', 'GREEN']" [misc] +""" diff --git a/conformance/results/zuban/enums_member_values.toml b/conformance/results/zuban/enums_member_values.toml new file mode 100644 index 000000000..9705e064b --- /dev/null +++ b/conformance/results/zuban/enums_member_values.toml @@ -0,0 +1,12 @@ +notes = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_member_values.py:54: error: Expression is of type "tuple[Literal[1], float, float]", not "Literal[1]" [misc] +enums_member_values.py:68: error: Expression is of type "int", not "Literal[1]" [misc] +enums_member_values.py:78: error: Enum value type "str" does not match the type "int" of "_value_" [misc] +enums_member_values.py:85: error: Incompatible types in assignment (expression has type "int", variable has type "str") [assignment] +enums_member_values.py:96: error: Expression is of type "EllipsisType", not "int" [misc] +""" diff --git a/conformance/results/zuban/enums_members.toml b/conformance/results/zuban/enums_members.toml new file mode 100644 index 000000000..5176996e0 --- /dev/null +++ b/conformance/results/zuban/enums_members.toml @@ -0,0 +1,18 @@ +notes = """ +""" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +enums_members.py:54: error: Enum members must be left unannotated [misc] +enums_members.py:54: note: See https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members +enums_members.py:86: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:87: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:88: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:89: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:120: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:132: note: Revealed type is "builtins.int" +enums_members.py:133: error: Parameter 1 of Literal[...] is invalid [valid-type] +enums_members.py:150: error: Expression is of type "Literal[Pet5.DOG]", not "int" [misc] +enums_members.py:151: error: Expression is of type "Literal[Pet5.FISH]", not "int" [misc] +""" diff --git a/conformance/results/zuban/exceptions_context_managers.toml b/conformance/results/zuban/exceptions_context_managers.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/exceptions_context_managers.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/generics_base_class.toml b/conformance/results/zuban/generics_base_class.toml new file mode 100644 index 000000000..c6f007321 --- /dev/null +++ b/conformance/results/zuban/generics_base_class.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_base_class.py:26: error: Argument 1 to "takes_dict_incorrect" has incompatible type "SymbolTable"; expected "dict[str, list[object]]" [arg-type] +generics_base_class.py:29: error: Invalid type [valid-type] +generics_base_class.py:30: error: Invalid type [valid-type] +generics_base_class.py:49: error: "LinkedList" expects 1 type argument, but 2 given [type-arg] +generics_base_class.py:61: error: "MyDict" expects 1 type argument, but 2 given [type-arg] +generics_base_class.py:68: error: Duplicate type variables in Generic[...] or Protocol[...] [misc] +generics_base_class.py:98: error: Cannot determine consistent method resolution order (MRO) for "BadChild" [misc] +""" diff --git a/conformance/results/zuban/generics_basic.toml b/conformance/results/zuban/generics_basic.toml new file mode 100644 index 000000000..1b2260e44 --- /dev/null +++ b/conformance/results/zuban/generics_basic.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_basic.py:40: error: Argument 2 to "concat" has incompatible type "bytes"; expected "str" [arg-type] +generics_basic.py:41: error: Argument 2 to "concat" has incompatible type "str"; expected "bytes" [arg-type] +generics_basic.py:49: error: Type variable must have at least two constrained types [misc] +generics_basic.py:55: error: TypeVar constraint type cannot be parametrized by type variables [misc] +generics_basic.py:69: error: Argument 2 to "concat" has incompatible type "bytes"; expected "str" [arg-type] +generics_basic.py:121: error: Duplicate type variables in Generic[...] or Protocol[...] [misc] +generics_basic.py:157: error: Invalid index type "int" for "MyMap1[str, int]"; expected type "str" [index] +generics_basic.py:158: error: Invalid index type "int" for "MyMap2[int, str]"; expected type "str" [index] +generics_basic.py:162: error: Free type variable expected in Generic[...] [misc] +generics_basic.py:163: error: Free type variable expected in Protocol[...] [misc] +generics_basic.py:171: error: If Generic[...] or Protocol[...] is present it should list all type variables [misc] +generics_basic.py:172: error: If Generic[...] or Protocol[...] is present it should list all type variables [misc] +generics_basic.py:208: error: Invalid metaclass "GenericMeta[T]" [metaclass] +""" diff --git a/conformance/results/zuban/generics_defaults.toml b/conformance/results/zuban/generics_defaults.toml new file mode 100644 index 000000000..fd34b3631 --- /dev/null +++ b/conformance/results/zuban/generics_defaults.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_defaults.py:24: error: "T" cannot appear after "DefaultStrT" in type parameter list because it has no default type [misc] +generics_defaults.py:66: error: "AllTheDefaults" expects between 2 and 5 type arguments, but 1 given [type-arg] +generics_defaults.py:152: error: TypeVar default must be a subtype of the bound type [misc] +generics_defaults.py:159: error: TypeVar default must be one of the constraint types [misc] +generics_defaults.py:177: error: Expression is of type "int", not "Any" [misc] +generics_defaults.py:188: error: TypeVar defaults are ambiguous after a TypeVarTuple [misc] +""" diff --git a/conformance/results/zuban/generics_defaults_referential.toml b/conformance/results/zuban/generics_defaults_referential.toml new file mode 100644 index 000000000..097f66531 --- /dev/null +++ b/conformance/results/zuban/generics_defaults_referential.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_defaults_referential.py:37: error: Argument 2 to "Foo" has incompatible type "str"; expected "int" [arg-type] +generics_defaults_referential.py:38: error: Argument 1 to "Foo" has incompatible type "str"; expected "int" [arg-type] +generics_defaults_referential.py:54: error: Type parameter "Start2T" has a default type that refers to one or more type variables that are out of scope [misc] +generics_defaults_referential.py:61: error: Type parameter "S2" has a default type that refers to one or more type variables that are out of scope [misc] +generics_defaults_referential.py:69: error: TypeVar default must be a subtype of the bound type [misc] +generics_defaults_referential.py:75: error: TypeVar default must be one of the constraint types [misc] +generics_defaults_referential.py:79: error: TypeVar default must be one of the constraint types [misc] +""" diff --git a/conformance/results/zuban/generics_defaults_specialization.toml b/conformance/results/zuban/generics_defaults_specialization.toml new file mode 100644 index 000000000..7592bf2be --- /dev/null +++ b/conformance/results/zuban/generics_defaults_specialization.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_defaults_specialization.py:30: error: Bad number of arguments for type alias, expected between 0 and 1, given 2 [misc] +generics_defaults_specialization.py:46: error: Incompatible types in assignment (expression has type "type[Bar]", variable has type "type[Bar[int]]") [assignment] +generics_defaults_specialization.py:56: error: "Foo" expects no type arguments, but 1 given [type-arg] +""" diff --git a/conformance/results/zuban/generics_paramspec_basic.toml b/conformance/results/zuban/generics_paramspec_basic.toml new file mode 100644 index 000000000..4d3ada23c --- /dev/null +++ b/conformance/results/zuban/generics_paramspec_basic.toml @@ -0,0 +1,22 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_paramspec_basic.py:10: error: String argument 1 "NotIt" to ParamSpec(...) does not match variable name "WrongName" [misc] +generics_paramspec_basic.py:15: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:15: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:23: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:23: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:23: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:23: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:27: error: Invalid location for Concatenate [valid-type] +generics_paramspec_basic.py:27: note: You can use Concatenate as the first argument to Callable +generics_paramspec_basic.py:31: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:31: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:35: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:35: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:39: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:39: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +generics_paramspec_basic.py:39: error: Invalid location for ParamSpec "P" [valid-type] +generics_paramspec_basic.py:39: note: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]" +""" diff --git a/conformance/results/zuban/generics_paramspec_components.toml b/conformance/results/zuban/generics_paramspec_components.toml new file mode 100644 index 000000000..0e1fa0041 --- /dev/null +++ b/conformance/results/zuban/generics_paramspec_components.toml @@ -0,0 +1,27 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_paramspec_components.py:17: error: Use "P.args" for variadic "*" parameter [misc] +generics_paramspec_components.py:17: error: Use "P.kwargs" for variadic "**" parameter [misc] +generics_paramspec_components.py:20: error: ParamSpec components are not allowed here [misc] +generics_paramspec_components.py:23: error: Use "P.kwargs" for variadic "**" parameter [misc] +generics_paramspec_components.py:26: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [misc] +generics_paramspec_components.py:30: error: ParamSpec "P" is unbound [misc] +generics_paramspec_components.py:35: error: ParamSpec components are not allowed here [misc] +generics_paramspec_components.py:36: error: ParamSpec components are not allowed here [misc] +generics_paramspec_components.py:38: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [misc] +generics_paramspec_components.py:41: error: ParamSpec must have "*args" typed as "P.args" and "**kwargs" typed as "P.kwargs" [misc] +generics_paramspec_components.py:49: error: Argument 1 has incompatible type "*P.kwargs"; expected "P.args" [arg-type] +generics_paramspec_components.py:49: error: Argument after ** must be a mapping, not "P.args" [arg-type] +generics_paramspec_components.py:51: error: Argument 1 has incompatible type "int"; expected "P.args" [arg-type] +generics_paramspec_components.py:60: error: Arguments not allowed after ParamSpec.args [misc] +generics_paramspec_components.py:70: error: Too few arguments [call-arg] +generics_paramspec_components.py:70: error: ParamSpec arguments must be of types "*P.args, **P.kwargs" [misc] +generics_paramspec_components.py:70: error: Argument 3 has incompatible type "**P.kwargs"; expected "P.kwargs" [arg-type] +generics_paramspec_components.py:72: error: Too few arguments [call-arg] +generics_paramspec_components.py:83: error: Argument "x" to "foo" has incompatible type "int"; expected "P.args" [arg-type] +generics_paramspec_components.py:83: error: Argument 2 to "foo" has incompatible type "*P.args"; expected "int" [arg-type] +generics_paramspec_components.py:98: error: Argument 2 to "twice" has incompatible type "str"; expected "int" [arg-type] +generics_paramspec_components.py:98: error: Argument 3 to "twice" has incompatible type "int"; expected "str" [arg-type] +""" diff --git a/conformance/results/zuban/generics_paramspec_semantics.toml b/conformance/results/zuban/generics_paramspec_semantics.toml new file mode 100644 index 000000000..7aec788a2 --- /dev/null +++ b/conformance/results/zuban/generics_paramspec_semantics.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_paramspec_semantics.py:26: error: Unexpected keyword argument "a" [call-arg] +generics_paramspec_semantics.py:26: error: Unexpected keyword argument "b" [call-arg] +generics_paramspec_semantics.py:27: error: Argument 2 has incompatible type "str"; expected "bool" [arg-type] +generics_paramspec_semantics.py:61: error: Argument 2 to "func1" has incompatible type "def keyword_only_y(*, y: int) -> int"; expected "Callable[[NamedArg(int, 'x')], int]" [arg-type] +generics_paramspec_semantics.py:98: error: Argument 1 has incompatible type "int"; expected "str" [arg-type] +generics_paramspec_semantics.py:108: error: Argument 1 has incompatible type "int"; expected "bool" [arg-type] +generics_paramspec_semantics.py:120: error: Argument 1 has incompatible type "int"; expected "str" [arg-type] +generics_paramspec_semantics.py:127: error: Argument 1 to "expects_int_first" has incompatible type "Callable[[str], int]"; expected "Callable[[int], int]" [arg-type] +generics_paramspec_semantics.py:132: error: Argument 1 to "expects_int_first" has incompatible type "def two(*, x: int) -> int"; expected "Callable[[int, Never], int]" [arg-type] +generics_paramspec_semantics.py:137: error: Argument 1 to "expects_int_first" has incompatible type "def three(**kwargs: int) -> int"; expected "Callable[[int, Never], int]" [arg-type] +""" diff --git a/conformance/results/zuban/generics_paramspec_specialization.toml b/conformance/results/zuban/generics_paramspec_specialization.toml new file mode 100644 index 000000000..70ea88ccc --- /dev/null +++ b/conformance/results/zuban/generics_paramspec_specialization.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_paramspec_specialization.py:44: error: Can only replace ParamSpec with a parameter types list or another ParamSpec, got "int" [misc] +generics_paramspec_specialization.py:54: error: Argument 1 has incompatible type "str"; expected "int" [arg-type] +generics_paramspec_specialization.py:55: error: Argument 3 has incompatible type "str"; expected "bool" [arg-type] +generics_paramspec_specialization.py:60: error: Argument 1 has incompatible type "str"; expected "int" [arg-type] +generics_paramspec_specialization.py:61: error: Argument 3 has incompatible type "str"; expected "bool" [arg-type] +""" diff --git a/conformance/results/zuban/generics_scoping.toml b/conformance/results/zuban/generics_scoping.toml new file mode 100644 index 000000000..000fd4f34 --- /dev/null +++ b/conformance/results/zuban/generics_scoping.toml @@ -0,0 +1,30 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_scoping.py:15: error: Expression is of type "Literal[1]", not "int" [misc] +generics_scoping.py:19: error: Expression is of type "Literal['a']", not "str" [misc] +generics_scoping.py:34: error: Argument 1 to "meth_2" of "MyClass" has incompatible type "str"; expected "int" [arg-type] +generics_scoping.py:49: error: Expression is of type "Literal['abc']", not "str" [misc] +generics_scoping.py:53: error: Expression is of type "Literal[b'abc']", not "bytes" [misc] +generics_scoping.py:61: error: Type variable "tests.generics_scoping.S" is unbound [misc] +generics_scoping.py:61: note: (Hint: Use "Generic[S]" or "Protocol[S]" base class to bind "S" inside a class) +generics_scoping.py:61: note: (Hint: Use "S" in function signature to bind "S" inside a function) +generics_scoping.py:65: error: Type variable "tests.generics_scoping.S" is unbound [misc] +generics_scoping.py:65: note: (Hint: Use "Generic[S]" or "Protocol[S]" base class to bind "S" inside a class) +generics_scoping.py:65: note: (Hint: Use "S" in function signature to bind "S" inside a function) +generics_scoping.py:76: error: Free type variable expected in Generic[...] [misc] +generics_scoping.py:86: error: Type variable "T" is bound by an outer class [misc] +generics_scoping.py:89: error: Type variable "T" is bound by an outer class [misc] +generics_scoping.py:91: error: Type variable "T" is bound by an outer class [misc] +generics_scoping.py:98: error: Can't use bound type variable "T" to define generic alias [misc] +generics_scoping.py:105: error: Type variable "tests.generics_scoping.T" is unbound [misc] +generics_scoping.py:105: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +generics_scoping.py:105: note: (Hint: Use "T" in function signature to bind "T" inside a function) +generics_scoping.py:106: error: Type variable "tests.generics_scoping.T" is unbound [misc] +generics_scoping.py:106: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +generics_scoping.py:106: note: (Hint: Use "T" in function signature to bind "T" inside a function) +generics_scoping.py:107: error: Type variable "tests.generics_scoping.T" is unbound [misc] +generics_scoping.py:107: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +generics_scoping.py:107: note: (Hint: Use "T" in function signature to bind "T" inside a function) +""" diff --git a/conformance/results/zuban/generics_self_advanced.toml b/conformance/results/zuban/generics_self_advanced.toml new file mode 100644 index 000000000..ff36318bb --- /dev/null +++ b/conformance/results/zuban/generics_self_advanced.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_self_advanced.py:48: error: Access to generic instance variables via class is ambiguous [misc] +""" diff --git a/conformance/results/zuban/generics_self_attributes.toml b/conformance/results/zuban/generics_self_attributes.toml new file mode 100644 index 000000000..449b88a32 --- /dev/null +++ b/conformance/results/zuban/generics_self_attributes.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_self_attributes.py:26: error: Argument "next" to "OrdinalLinkedList" has incompatible type "LinkedList[int]"; expected "OrdinalLinkedList | None" [arg-type] +generics_self_attributes.py:32: error: Incompatible types in assignment (expression has type "LinkedList[int]", variable has type "OrdinalLinkedList | None") [assignment] +""" diff --git a/conformance/results/zuban/generics_self_basic.toml b/conformance/results/zuban/generics_self_basic.toml new file mode 100644 index 000000000..428c5a58a --- /dev/null +++ b/conformance/results/zuban/generics_self_basic.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_self_basic.py:20: error: Incompatible return value type (got "Shape", expected "Self") [return-value] +generics_self_basic.py:33: error: Incompatible return value type (got "Shape", expected "Self") [return-value] +generics_self_basic.py:68: error: Self type cannot have type arguments [valid-type] +""" diff --git a/conformance/results/zuban/generics_self_protocols.toml b/conformance/results/zuban/generics_self_protocols.toml new file mode 100644 index 000000000..7d25923b6 --- /dev/null +++ b/conformance/results/zuban/generics_self_protocols.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_self_protocols.py:61: error: Argument 1 to "accepts_shape" has incompatible type "BadReturnType"; expected "ShapeProtocol" [arg-type] +generics_self_protocols.py:61: note: Following member(s) of "BadReturnType" have conflicts: +generics_self_protocols.py:61: note: Expected: +generics_self_protocols.py:61: note: def [Self: BadReturnType] set_scale(self, scale: float) -> Self +generics_self_protocols.py:61: note: Got: +generics_self_protocols.py:61: note: def set_scale(self, scale: float) -> int +generics_self_protocols.py:64: error: Argument 1 to "accepts_shape" has incompatible type "ReturnDifferentClass"; expected "ShapeProtocol" [arg-type] +generics_self_protocols.py:64: note: Following member(s) of "ReturnDifferentClass" have conflicts: +generics_self_protocols.py:64: note: Expected: +generics_self_protocols.py:64: note: def [Self: ReturnDifferentClass] set_scale(self, scale: float) -> Self +generics_self_protocols.py:64: note: Got: +generics_self_protocols.py:64: note: def set_scale(self, scale: float) -> ReturnConcreteShape +""" diff --git a/conformance/results/zuban/generics_self_usage.toml b/conformance/results/zuban/generics_self_usage.toml new file mode 100644 index 000000000..f2ff29fdf --- /dev/null +++ b/conformance/results/zuban/generics_self_usage.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_self_usage.py:73: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:73: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:76: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:82: error: "self" parameter missing for a non-static method (or an invalid type for self) [misc] +generics_self_usage.py:87: error: Incompatible return value type (got "Foo3", expected "Self") [return-value] +generics_self_usage.py:103: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:105: error: Self type is only allowed in annotations within class definition [misc] +generics_self_usage.py:108: error: Self type cannot be used in type alias target [misc] +generics_self_usage.py:113: error: Static methods cannot use Self type [misc] +generics_self_usage.py:118: error: Static methods cannot use Self type [misc] +generics_self_usage.py:118: error: Static methods cannot use Self type [misc] +generics_self_usage.py:123: error: Self type cannot be used in a metaclass [misc] +generics_self_usage.py:127: error: Self type cannot be used in a metaclass [misc] +""" diff --git a/conformance/results/zuban/generics_syntax_compatibility.toml b/conformance/results/zuban/generics_syntax_compatibility.toml new file mode 100644 index 000000000..9eb19efc3 --- /dev/null +++ b/conformance/results/zuban/generics_syntax_compatibility.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_syntax_compatibility.py:14: error: All type parameters should be declared ("K" not declared) [misc] +generics_syntax_compatibility.py:26: error: All type parameters should be declared ("K" not declared) [misc] +""" diff --git a/conformance/results/zuban/generics_syntax_declarations.toml b/conformance/results/zuban/generics_syntax_declarations.toml new file mode 100644 index 000000000..6da51b54f --- /dev/null +++ b/conformance/results/zuban/generics_syntax_declarations.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_syntax_declarations.py:17: error: Generic[...] base class is redundant [misc] +generics_syntax_declarations.py:25: error: No arguments expected for "Protocol" base class [misc] +generics_syntax_declarations.py:32: error: "T" has no attribute "is_integer" [attr-defined] +generics_syntax_declarations.py:44: error: TypeVar bound must not contain type variables [misc] +generics_syntax_declarations.py:48: error: Bracketed expression "[...]" is not valid as a type [valid-type] +generics_syntax_declarations.py:48: note: Did you mean "List[...]"? +generics_syntax_declarations.py:60: error: Type variable must have at least two constrained types [misc] +generics_syntax_declarations.py:64: error: Type variable must have at least two constrained types [misc] +generics_syntax_declarations.py:71: error: Variable "tests.generics_syntax_declarations.t1" is not valid as a type [valid-type] +generics_syntax_declarations.py:71: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_declarations.py:75: error: Invalid type: try using Literal[3] instead? [valid-type] +generics_syntax_declarations.py:79: error: Name "S" is not defined [name-defined] +""" diff --git a/conformance/results/zuban/generics_syntax_infer_variance.toml b/conformance/results/zuban/generics_syntax_infer_variance.toml new file mode 100644 index 000000000..36d8e5ac7 --- /dev/null +++ b/conformance/results/zuban/generics_syntax_infer_variance.toml @@ -0,0 +1,23 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_syntax_infer_variance.py:15: error: Cannot use covariant with infer_variance [misc] +generics_syntax_infer_variance.py:17: error: Cannot use contravariant with infer_variance [misc] +generics_syntax_infer_variance.py:29: error: Incompatible types in assignment (expression has type "ShouldBeCovariant1[float]", variable has type "ShouldBeCovariant1[int]") [assignment] +generics_syntax_infer_variance.py:47: error: Incompatible types in assignment (expression has type "ShouldBeCovariant2[float]", variable has type "ShouldBeCovariant2[int]") [assignment] +generics_syntax_infer_variance.py:56: error: Incompatible types in assignment (expression has type "ShouldBeCovariant3[float]", variable has type "ShouldBeCovariant3[int]") [assignment] +generics_syntax_infer_variance.py:85: error: Incompatible types in assignment (expression has type "ShouldBeCovariant5[float]", variable has type "ShouldBeCovariant5[int]") [assignment] +generics_syntax_infer_variance.py:96: error: Incompatible types in assignment (expression has type "ShouldBeCovariant6[float]", variable has type "ShouldBeCovariant6[int]") [assignment] +generics_syntax_infer_variance.py:112: error: Incompatible types in assignment (expression has type "ShouldBeInvariant1[int]", variable has type "ShouldBeInvariant1[float]") [assignment] +generics_syntax_infer_variance.py:113: error: Incompatible types in assignment (expression has type "ShouldBeInvariant1[float]", variable has type "ShouldBeInvariant1[int]") [assignment] +generics_syntax_infer_variance.py:127: error: Incompatible types in assignment (expression has type "ShouldBeInvariant2[int]", variable has type "ShouldBeInvariant2[float]") [assignment] +generics_syntax_infer_variance.py:128: error: Incompatible types in assignment (expression has type "ShouldBeInvariant2[float]", variable has type "ShouldBeInvariant2[int]") [assignment] +generics_syntax_infer_variance.py:135: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[int, str]", variable has type "ShouldBeInvariant3[float, str]") [assignment] +generics_syntax_infer_variance.py:136: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[float, str]", variable has type "ShouldBeInvariant3[int, str]") [assignment] +generics_syntax_infer_variance.py:137: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[str, int]", variable has type "ShouldBeInvariant3[str, float]") [assignment] +generics_syntax_infer_variance.py:138: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[str, float]", variable has type "ShouldBeInvariant3[str, int]") [assignment] +generics_syntax_infer_variance.py:146: error: Incompatible types in assignment (expression has type "ShouldBeInvariant4[int]", variable has type "ShouldBeInvariant4[float]") [assignment] +generics_syntax_infer_variance.py:154: error: Incompatible types in assignment (expression has type "ShouldBeInvariant5[int]", variable has type "ShouldBeInvariant5[float]") [assignment] +generics_syntax_infer_variance.py:165: error: Incompatible types in assignment (expression has type "ShouldBeContravariant1[int]", variable has type "ShouldBeContravariant1[float]") [assignment] +""" diff --git a/conformance/results/zuban/generics_syntax_scoping.toml b/conformance/results/zuban/generics_syntax_scoping.toml new file mode 100644 index 000000000..a0444e813 --- /dev/null +++ b/conformance/results/zuban/generics_syntax_scoping.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_syntax_scoping.py:14: error: Variable "tests.generics_syntax_scoping.S" is not valid as a type [valid-type] +generics_syntax_scoping.py:14: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_scoping.py:18: error: Variable "tests.generics_syntax_scoping.T" is not valid as a type [valid-type] +generics_syntax_scoping.py:18: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_scoping.py:35: error: Name "T" is used before definition [used-before-def] +generics_syntax_scoping.py:44: error: Name "T" is used before definition [used-before-def] +generics_syntax_scoping.py:44: error: Variable "tests.generics_syntax_scoping.T" is not valid as a type [valid-type] +generics_syntax_scoping.py:44: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +generics_syntax_scoping.py:92: error: "T" already defined as a type parameter [misc] +generics_syntax_scoping.py:95: error: "T" already defined as a type parameter [misc] +generics_syntax_scoping.py:98: error: "T" already defined as a type parameter [misc] +""" diff --git a/conformance/results/zuban/generics_type_erasure.toml b/conformance/results/zuban/generics_type_erasure.toml new file mode 100644 index 000000000..051f94d22 --- /dev/null +++ b/conformance/results/zuban/generics_type_erasure.toml @@ -0,0 +1,13 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_type_erasure.py:38: error: Argument 1 to "Node" has incompatible type "str"; expected "int | None" [arg-type] +generics_type_erasure.py:40: error: Argument 1 to "Node" has incompatible type "int"; expected "str | None" [arg-type] +generics_type_erasure.py:42: error: Access to generic instance variables via class is ambiguous [misc] +generics_type_erasure.py:43: error: Access to generic instance variables via class is ambiguous [misc] +generics_type_erasure.py:44: error: Access to generic instance variables via class is ambiguous [misc] +generics_type_erasure.py:45: error: Access to generic instance variables via class is ambiguous [misc] +generics_type_erasure.py:46: error: Access to generic instance variables via class is ambiguous [misc] +""" diff --git a/conformance/results/zuban/generics_typevartuple_args.toml b/conformance/results/zuban/generics_typevartuple_args.toml new file mode 100644 index 000000000..29b6e9434 --- /dev/null +++ b/conformance/results/zuban/generics_typevartuple_args.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_typevartuple_args.py:33: error: Argument 3 to "exec_le" has incompatible type "str"; expected "Env" [arg-type] +generics_typevartuple_args.py:34: error: Argument 3 to "exec_le" has incompatible type "str"; expected "Env" [arg-type] +generics_typevartuple_args.py:48: error: Argument 2 to "func1" has incompatible type "str"; expected "int" [arg-type] +generics_typevartuple_args.py:57: error: Argument 2 to "func2" has incompatible type "int"; expected "str" [arg-type] +generics_typevartuple_args.py:58: error: Too few arguments for "func2" [call-arg] +generics_typevartuple_args.py:59: error: Too few arguments for "func2" [call-arg] +generics_typevartuple_args.py:59: error: Argument 1 to "func2" has incompatible type "str"; expected "int" [arg-type] +generics_typevartuple_args.py:67: error: Too few arguments for "func3" [call-arg] +generics_typevartuple_args.py:75: error: Argument 2 to "func4" has incompatible type "tuple[int, int]"; expected "tuple[int]" [arg-type] +""" diff --git a/conformance/results/zuban/generics_typevartuple_basic.toml b/conformance/results/zuban/generics_typevartuple_basic.toml new file mode 100644 index 000000000..ca0cca722 --- /dev/null +++ b/conformance/results/zuban/generics_typevartuple_basic.toml @@ -0,0 +1,19 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_typevartuple_basic.py:42: error: Argument 1 to "Array" has incompatible type "Height"; expected "tuple[Height, Width]" [arg-type] +generics_typevartuple_basic.py:43: error: Argument 1 to "Array" has incompatible type "tuple[Batch, Width]"; expected "tuple[Batch, Height, Width]" [arg-type] +generics_typevartuple_basic.py:45: error: Argument 1 to "Array" has incompatible type "tuple[Time, Batch, Width, Height]"; expected "tuple[Time, Batch, Height, Width]" [arg-type] +generics_typevartuple_basic.py:52: error: Free type variable expected in Generic[...] [misc] +generics_typevartuple_basic.py:53: error: TypeVarTuple "Shape" is only valid with an unpack [valid-type] +generics_typevartuple_basic.py:56: error: TypeVarTuple "Shape" is only valid with an unpack [valid-type] +generics_typevartuple_basic.py:59: error: TypeVarTuple "Shape" is only valid with an unpack [valid-type] +generics_typevartuple_basic.py:65: error: Unexpected keyword argument "covariant" for "TypeVarTuple" [call-arg] +generics_typevartuple_basic.py:66: error: Too many positional arguments for "TypeVarTuple" [call-arg] +generics_typevartuple_basic.py:67: error: Unexpected keyword argument "bound" for "TypeVarTuple" [call-arg] +generics_typevartuple_basic.py:91: error: Argument 2 to "func2" has incompatible type "tuple[int]"; expected "tuple[int, int]" [arg-type] +generics_typevartuple_basic.py:100: error: Argument 2 to "multiply" has incompatible type "Array[Width]"; expected "Array[Height]" [arg-type] +generics_typevartuple_basic.py:101: error: Argument 2 to "multiply" has incompatible type "Array[Height, Width]"; expected "Array[Height]" [arg-type] +generics_typevartuple_basic.py:107: error: Can only use one type var tuple in a class def [misc] +""" diff --git a/conformance/results/zuban/generics_typevartuple_callable.toml b/conformance/results/zuban/generics_typevartuple_callable.toml new file mode 100644 index 000000000..d18ba3c57 --- /dev/null +++ b/conformance/results/zuban/generics_typevartuple_callable.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_typevartuple_callable.py:26: error: Argument "args" to "Process" has incompatible type "tuple[str, int]"; expected "tuple[int, str]" [arg-type] +""" diff --git a/conformance/results/zuban/generics_typevartuple_concat.toml b/conformance/results/zuban/generics_typevartuple_concat.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/generics_typevartuple_concat.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/generics_typevartuple_overloads.toml b/conformance/results/zuban/generics_typevartuple_overloads.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/generics_typevartuple_overloads.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/generics_typevartuple_specialization.toml b/conformance/results/zuban/generics_typevartuple_specialization.toml new file mode 100644 index 000000000..b7b6182df --- /dev/null +++ b/conformance/results/zuban/generics_typevartuple_specialization.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_typevartuple_specialization.py:109: error: Unpack is only valid in a variadic position [misc] +generics_typevartuple_specialization.py:109: error: TypeVarTuple "Ts" is unbound [misc] +generics_typevartuple_specialization.py:110: error: Unpack is only valid in a variadic position [misc] +generics_typevartuple_specialization.py:121: error: More than one variadic Unpack in a type is not allowed [misc] +generics_typevartuple_specialization.py:122: error: More than one variadic Unpack in a type is not allowed [misc] +generics_typevartuple_specialization.py:127: error: Bad number of arguments for type alias, expected at least 2, given 2 [misc] +generics_typevartuple_specialization.py:163: error: TypeVarTuple cannot be split [misc] +""" diff --git a/conformance/results/zuban/generics_typevartuple_unpack.toml b/conformance/results/zuban/generics_typevartuple_unpack.toml new file mode 100644 index 000000000..5f2652dd0 --- /dev/null +++ b/conformance/results/zuban/generics_typevartuple_unpack.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_typevartuple_unpack.py:30: error: Argument 1 to "process_batch_channels" has incompatible type "Array[Batch]"; expected "Array[Batch, Unpack[Tuple[Any, ...]], Channels]" [arg-type] +""" diff --git a/conformance/results/zuban/generics_upper_bound.toml b/conformance/results/zuban/generics_upper_bound.toml new file mode 100644 index 000000000..766c0373a --- /dev/null +++ b/conformance/results/zuban/generics_upper_bound.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_upper_bound.py:24: error: TypeVar bound must not contain type variables [misc] +generics_upper_bound.py:44: error: Expression is of type "list[int] | set[int]", not "Collection[int]" [misc] +generics_upper_bound.py:52: error: Value of type variable "ST" of "longer" cannot be "int" [type-var] +generics_upper_bound.py:52: error: Value of type variable "ST" of "longer" cannot be "int" [type-var] +generics_upper_bound.py:57: error: TypeVar cannot have both values and an upper bound [misc] +""" diff --git a/conformance/results/zuban/generics_variance.toml b/conformance/results/zuban/generics_variance.toml new file mode 100644 index 000000000..f104685a4 --- /dev/null +++ b/conformance/results/zuban/generics_variance.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_variance.py:14: error: TypeVar cannot be both covariant and contravariant [misc] +generics_variance.py:77: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:81: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +generics_variance.py:93: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +generics_variance.py:105: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:113: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:125: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:131: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +generics_variance.py:141: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:163: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +generics_variance.py:167: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +generics_variance.py:191: error: Variance of TypeVar "T_contra" incompatible with variance in parent type [type-var] +generics_variance.py:195: error: Variance of TypeVar "T_co" incompatible with variance in parent type [type-var] +""" diff --git a/conformance/results/zuban/generics_variance_inference.toml b/conformance/results/zuban/generics_variance_inference.toml new file mode 100644 index 000000000..722cb1739 --- /dev/null +++ b/conformance/results/zuban/generics_variance_inference.toml @@ -0,0 +1,28 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +generics_variance_inference.py:24: error: Incompatible types in assignment (expression has type "ClassA[float, int, int]", variable has type "ClassA[int, int, int]") [assignment] +generics_variance_inference.py:25: error: Incompatible types in assignment (expression has type "ClassA[float, int, int]", variable has type "ClassA[float, float, int]") [assignment] +generics_variance_inference.py:28: error: Incompatible types in assignment (expression has type "ClassA[int, float, float]", variable has type "ClassA[int, int, int]") [assignment] +generics_variance_inference.py:41: error: Incompatible types in assignment (expression has type "ShouldBeCovariant1[float]", variable has type "ShouldBeCovariant1[int]") [assignment] +generics_variance_inference.py:49: error: Incompatible types in assignment (expression has type "ShouldBeCovariant2[float]", variable has type "ShouldBeCovariant2[int]") [assignment] +generics_variance_inference.py:58: error: Incompatible types in assignment (expression has type "ShouldBeCovariant3[float]", variable has type "ShouldBeCovariant3[int]") [assignment] +generics_variance_inference.py:67: error: Incompatible types in assignment (expression has type "ShouldBeCovariant4[float]", variable has type "ShouldBeCovariant4[int]") [assignment] +generics_variance_inference.py:80: error: Incompatible types in assignment (expression has type "ShouldBeCovariant5[float]", variable has type "ShouldBeCovariant5[int]") [assignment] +generics_variance_inference.py:96: error: Incompatible types in assignment (expression has type "ShouldBeInvariant1[int]", variable has type "ShouldBeInvariant1[float]") [assignment] +generics_variance_inference.py:97: error: Incompatible types in assignment (expression has type "ShouldBeInvariant1[float]", variable has type "ShouldBeInvariant1[int]") [assignment] +generics_variance_inference.py:111: error: Incompatible types in assignment (expression has type "ShouldBeInvariant2[int]", variable has type "ShouldBeInvariant2[float]") [assignment] +generics_variance_inference.py:112: error: Incompatible types in assignment (expression has type "ShouldBeInvariant2[float]", variable has type "ShouldBeInvariant2[int]") [assignment] +generics_variance_inference.py:119: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[int, str]", variable has type "ShouldBeInvariant3[float, str]") [assignment] +generics_variance_inference.py:120: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[float, str]", variable has type "ShouldBeInvariant3[int, str]") [assignment] +generics_variance_inference.py:121: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[str, int]", variable has type "ShouldBeInvariant3[str, float]") [assignment] +generics_variance_inference.py:122: error: Incompatible types in assignment (expression has type "ShouldBeInvariant3[str, float]", variable has type "ShouldBeInvariant3[str, int]") [assignment] +generics_variance_inference.py:130: error: Incompatible types in assignment (expression has type "ShouldBeInvariant4[int]", variable has type "ShouldBeInvariant4[float]") [assignment] +generics_variance_inference.py:138: error: Incompatible types in assignment (expression has type "ShouldBeInvariant5[int]", variable has type "ShouldBeInvariant5[float]") [assignment] +generics_variance_inference.py:149: error: Incompatible types in assignment (expression has type "ShouldBeContravariant1[int]", variable has type "ShouldBeContravariant1[float]") [assignment] +generics_variance_inference.py:169: error: Incompatible types in assignment (expression has type "ShouldBeInvariant6[float]", variable has type "ShouldBeInvariant6[int]") [assignment] +generics_variance_inference.py:170: error: Incompatible types in assignment (expression has type "ShouldBeInvariant6[int]", variable has type "ShouldBeInvariant6[float]") [assignment] +generics_variance_inference.py:181: error: Incompatible types in assignment (expression has type "ShouldBeCovariant6[float]", variable has type "ShouldBeCovariant6[int]") [assignment] +generics_variance_inference.py:194: error: Incompatible types in assignment (expression has type "ShouldBeContravariant2[int]", variable has type "ShouldBeContravariant2[float]") [assignment] +""" diff --git a/conformance/results/zuban/historical_positional.toml b/conformance/results/zuban/historical_positional.toml new file mode 100644 index 000000000..787972c69 --- /dev/null +++ b/conformance/results/zuban/historical_positional.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +historical_positional.py:18: error: Unexpected keyword argument "__x" for "f1" [call-arg] +historical_positional.py:26: error: A positional only param starting with two underscores is not allowed after a positional or keyword param [misc] +historical_positional.py:54: error: A positional only param starting with two underscores is not allowed after a positional or keyword param [misc] +historical_positional.py:59: error: Unexpected keyword argument "__x" for "m1" of "A" [call-arg] +""" diff --git a/conformance/results/zuban/literals_interactions.toml b/conformance/results/zuban/literals_interactions.toml new file mode 100644 index 000000000..166336175 --- /dev/null +++ b/conformance/results/zuban/literals_interactions.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +literals_interactions.py:14: error: Tuple index out of range [misc] +literals_interactions.py:15: error: Tuple index out of range [misc] +literals_interactions.py:16: error: Tuple index out of range [misc] +literals_interactions.py:17: error: Tuple index out of range [misc] +""" diff --git a/conformance/results/zuban/literals_literalstring.toml b/conformance/results/zuban/literals_literalstring.toml new file mode 100644 index 000000000..6db478d33 --- /dev/null +++ b/conformance/results/zuban/literals_literalstring.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +literals_literalstring.py:36: error: Parameter 2 of Literal[...] is invalid [valid-type] +literals_literalstring.py:37: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_literalstring.py:43: error: Incompatible types in assignment (expression has type "Literal['two']", variable has type "Literal['']") [assignment] +literals_literalstring.py:65: error: Incompatible types in assignment (expression has type "str", variable has type "LiteralString") [assignment] +literals_literalstring.py:73: error: Incompatible types in assignment (expression has type "Literal[3]", variable has type "LiteralString") [assignment] +literals_literalstring.py:74: error: Incompatible types in assignment (expression has type "Literal[b'test']", variable has type "LiteralString") [assignment] +literals_literalstring.py:119: error: Value of type variable "TLiteral" of "literal_identity" cannot be "str" [type-var] +literals_literalstring.py:133: error: Value of type variable "T" of "Container" cannot be "str" [type-var] +literals_literalstring.py:171: error: Incompatible types in assignment (expression has type "list[LiteralString]", variable has type "list[str]") [assignment] +literals_literalstring.py:171: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +literals_literalstring.py:171: note: Consider using "Sequence" instead, which is covariant +""" diff --git a/conformance/results/zuban/literals_parameterizations.toml b/conformance/results/zuban/literals_parameterizations.toml new file mode 100644 index 000000000..0a1ba71fa --- /dev/null +++ b/conformance/results/zuban/literals_parameterizations.toml @@ -0,0 +1,24 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +literals_parameterizations.py:41: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:42: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:43: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:44: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:45: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:46: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:47: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:48: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:49: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:50: error: Type variable "tests.literals_parameterizations.T" is unbound [misc] +literals_parameterizations.py:50: note: (Hint: Use "Generic[T]" or "Protocol[T]" base class to bind "T" inside a class) +literals_parameterizations.py:50: note: (Hint: Use "T" in function signature to bind "T" inside a function) +literals_parameterizations.py:51: error: Parameter 1 of Literal[...] cannot be of type "float" [valid-type] +literals_parameterizations.py:52: error: Parameter 1 of Literal[...] cannot be of type "Any" [valid-type] +literals_parameterizations.py:53: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:56: error: Invalid type: Literal[...] cannot contain arbitrary expressions [valid-type] +literals_parameterizations.py:60: error: Literal[...] must have at least one parameter [valid-type] +literals_parameterizations.py:61: error: Parameter 1 of Literal[...] is invalid [valid-type] +literals_parameterizations.py:65: error: Incompatible types in assignment (expression has type "Literal[Color.RED]", variable has type "Literal['Color.RED']") [assignment] +""" diff --git a/conformance/results/zuban/literals_semantics.toml b/conformance/results/zuban/literals_semantics.toml new file mode 100644 index 000000000..57c26e052 --- /dev/null +++ b/conformance/results/zuban/literals_semantics.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +literals_semantics.py:10: error: Incompatible types in assignment (expression has type "Literal[4]", variable has type "Literal[3]") [assignment] +literals_semantics.py:24: error: Incompatible types in assignment (expression has type "Literal[0]", variable has type "Literal[False]") [assignment] +literals_semantics.py:25: error: Incompatible types in assignment (expression has type "Literal[False]", variable has type "Literal[0]") [assignment] +literals_semantics.py:33: error: Incompatible types in assignment (expression has type "Literal[6, 7, 8]", variable has type "Literal[3, 4, 5]") [assignment] +""" diff --git a/conformance/results/zuban/namedtuples_define_class.toml b/conformance/results/zuban/namedtuples_define_class.toml new file mode 100644 index 000000000..d5c42ff3f --- /dev/null +++ b/conformance/results/zuban/namedtuples_define_class.toml @@ -0,0 +1,20 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +namedtuples_define_class.py:33: error: Tuple index out of range [misc] +namedtuples_define_class.py:34: error: Tuple index out of range [misc] +namedtuples_define_class.py:45: error: Missing positional argument "y" in call to "Point" [call-arg] +namedtuples_define_class.py:46: error: Missing positional argument "y" in call to "Point" [call-arg] +namedtuples_define_class.py:47: error: Argument 2 to "Point" has incompatible type "str"; expected "int" [arg-type] +namedtuples_define_class.py:48: error: Argument "units" to "Point" has incompatible type "int"; expected "str" [arg-type] +namedtuples_define_class.py:49: error: Too many arguments for "Point" [call-arg] +namedtuples_define_class.py:50: error: Unexpected keyword argument "other" for "Point" [call-arg] +namedtuples_define_class.py:70: error: Too many arguments for "Point2" [call-arg] +namedtuples_define_class.py:77: error: NamedTuple field name cannot start with an underscore: _y [misc] +namedtuples_define_class.py:87: error: Non-default NamedTuple fields cannot follow default fields [misc] +namedtuples_define_class.py:107: error: NamedTuple attributes cannot be overridden in subclasses [misc] +namedtuples_define_class.py:121: error: Too many arguments for "ConditionalField" [call-arg] +namedtuples_define_class.py:140: error: Argument 2 to "Property" has incompatible type "float"; expected "str" [arg-type] +namedtuples_define_class.py:147: error: NamedTuple should be a single base [misc] +""" diff --git a/conformance/results/zuban/namedtuples_define_functional.toml b/conformance/results/zuban/namedtuples_define_functional.toml new file mode 100644 index 000000000..98ee2d2fd --- /dev/null +++ b/conformance/results/zuban/namedtuples_define_functional.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +namedtuples_define_functional.py:16: error: Missing positional argument "y" in call to "Point1" [call-arg] +namedtuples_define_functional.py:21: error: Missing positional arguments "x", "y" in call to "Point2" [call-arg] +namedtuples_define_functional.py:26: error: Too many arguments for "Point3" [call-arg] +namedtuples_define_functional.py:31: error: Unexpected keyword argument "z" for "Point4" [call-arg] +namedtuples_define_functional.py:36: error: Argument 2 to "Point5" has incompatible type "str"; expected "int" [arg-type] +namedtuples_define_functional.py:37: error: Too many arguments for "Point5" [call-arg] +namedtuples_define_functional.py:42: error: Argument 2 to "Point6" has incompatible type "str"; expected "int" [arg-type] +namedtuples_define_functional.py:43: error: Argument "x" to "Point6" has incompatible type "float"; expected "int" [arg-type] +namedtuples_define_functional.py:52: error: "namedtuple()" has duplicate field name "a" [misc] +namedtuples_define_functional.py:53: error: "namedtuple()" field name "def" is a keyword [misc] +namedtuples_define_functional.py:54: error: "namedtuple()" field name "def" is a keyword [misc] +namedtuples_define_functional.py:55: error: "namedtuple()" field name "_d" starts with an underscore [misc] +namedtuples_define_functional.py:69: error: Missing positional argument "a" in call to "NT7" [call-arg] +""" diff --git a/conformance/results/zuban/namedtuples_type_compat.toml b/conformance/results/zuban/namedtuples_type_compat.toml new file mode 100644 index 000000000..bddb6fd78 --- /dev/null +++ b/conformance/results/zuban/namedtuples_type_compat.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +namedtuples_type_compat.py:22: error: Incompatible types in assignment (expression has type "Point", variable has type "tuple[int, int]") [assignment] +namedtuples_type_compat.py:23: error: Incompatible types in assignment (expression has type "Point", variable has type "tuple[int, str, str]") [assignment] +""" diff --git a/conformance/results/zuban/namedtuples_usage.toml b/conformance/results/zuban/namedtuples_usage.toml new file mode 100644 index 000000000..75ebf0e03 --- /dev/null +++ b/conformance/results/zuban/namedtuples_usage.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +namedtuples_usage.py:34: error: Tuple index out of range [misc] +namedtuples_usage.py:35: error: Tuple index out of range [misc] +namedtuples_usage.py:40: error: Property "x" defined in "Point" is read-only [misc] +namedtuples_usage.py:41: error: Unsupported target for indexed assignment ("Point") [index] +namedtuples_usage.py:42: error: NamedTuple attributes cannot be deleted [misc] +namedtuples_usage.py:43: error: "Point" has no attribute "__delitem__" [attr-defined] +namedtuples_usage.py:52: error: Too many values to unpack (2 expected, 3 provided) [misc] +namedtuples_usage.py:53: error: Need more than 3 values to unpack (4 expected) [misc] +""" diff --git a/conformance/results/zuban/narrowing_typeguard.toml b/conformance/results/zuban/narrowing_typeguard.toml new file mode 100644 index 000000000..b8892b074 --- /dev/null +++ b/conformance/results/zuban/narrowing_typeguard.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +narrowing_typeguard.py:102: error: TypeGuard functions must have a positional argument [misc] +narrowing_typeguard.py:107: error: TypeGuard functions must have a positional argument [misc] +narrowing_typeguard.py:128: error: Argument 1 to "takes_callable_str" has incompatible type "Callable[[object], TypeGuard[int]]"; expected "Callable[[object], str]" [arg-type] +narrowing_typeguard.py:148: error: Argument 1 to "takes_callable_str_proto" has incompatible type "Callable[[object], TypeGuard[int]]"; expected "CallableStrProto" [arg-type] +""" diff --git a/conformance/results/zuban/narrowing_typeis.toml b/conformance/results/zuban/narrowing_typeis.toml new file mode 100644 index 000000000..8e3c790bf --- /dev/null +++ b/conformance/results/zuban/narrowing_typeis.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +narrowing_typeis.py:110: error: "TypeIs" functions must have a positional argument [misc] +narrowing_typeis.py:115: error: "TypeIs" functions must have a positional argument [misc] +narrowing_typeis.py:137: error: Argument 1 to "takes_callable_str" has incompatible type "Callable[[object], TypeIs[int]]"; expected "Callable[[object], str]" [arg-type] +narrowing_typeis.py:157: error: Argument 1 to "takes_callable_str_proto" has incompatible type "Callable[[object], TypeIs[int]]"; expected "CallableStrProto" [arg-type] +narrowing_typeis.py:174: error: Argument 1 to "takes_typeguard" has incompatible type "Callable[[object], TypeIs[int]]"; expected "Callable[[object], TypeGuard[int]]" [arg-type] +narrowing_typeis.py:175: error: Argument 1 to "takes_typeis" has incompatible type "Callable[[object], TypeGuard[int]]"; expected "Callable[[object], TypeIs[int]]" [arg-type] +narrowing_typeis.py:196: error: Argument 1 to "takes_int_typeis" has incompatible type "Callable[[object], TypeIs[bool]]"; expected "Callable[[object], TypeIs[int]]" [arg-type] +narrowing_typeis.py:200: error: Narrowed type "str" is not a subtype of input type "int" [narrowed-type-not-subtype] +narrowing_typeis.py:204: error: Narrowed type "list[int]" is not a subtype of input type "list[object]" [narrowed-type-not-subtype] +""" diff --git a/conformance/results/zuban/overloads_basic.toml b/conformance/results/zuban/overloads_basic.toml new file mode 100644 index 000000000..6c3455642 --- /dev/null +++ b/conformance/results/zuban/overloads_basic.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_basic.py:39: error: No overload variant of "__getitem__" of "Bytes" matches argument type "str" [call-overload] +overloads_basic.py:39: note: Possible overload variants: +overloads_basic.py:39: note: def __getitem__(self, int, /) -> int +overloads_basic.py:39: note: def __getitem__(self, slice[Any, Any, Any], /) -> bytes +""" diff --git a/conformance/results/zuban/overloads_consistency.toml b/conformance/results/zuban/overloads_consistency.toml new file mode 100644 index 000000000..146fdfb76 --- /dev/null +++ b/conformance/results/zuban/overloads_consistency.toml @@ -0,0 +1,7 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_consistency.py:28: error: Overloaded function implementation cannot produce return type of signature 2 [misc] +overloads_consistency.py:44: error: Overloaded function implementation does not accept all possible parameters of signature 2 [misc] +""" diff --git a/conformance/results/zuban/overloads_definitions.toml b/conformance/results/zuban/overloads_definitions.toml new file mode 100644 index 000000000..b9cab56f9 --- /dev/null +++ b/conformance/results/zuban/overloads_definitions.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_definitions.py:15: error: Single overload definition, multiple required [misc] +overloads_definitions.py:28: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:59: error: An overloaded function outside a stub file must have an implementation [no-overload-impl] +overloads_definitions.py:71: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_definitions.py:84: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_definitions.py:93: error: Overloaded function implementation does not accept all possible parameters of signature 2 [misc] +overloads_definitions.py:123: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:138: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:143: error: @final should be applied only to overload implementation [misc] +overloads_definitions.py:180: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions.py:195: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +overloads_definitions.py:227: error: @override should be applied only to overload implementation [misc] +overloads_definitions.py:231: error: @override should be applied only to overload implementation [misc] +""" diff --git a/conformance/results/zuban/overloads_definitions_stub.toml b/conformance/results/zuban/overloads_definitions_stub.toml new file mode 100644 index 000000000..0dbbf6319 --- /dev/null +++ b/conformance/results/zuban/overloads_definitions_stub.toml @@ -0,0 +1,13 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_definitions_stub.pyi:13: error: Single overload definition, multiple required [misc] +overloads_definitions_stub.pyi:32: error: Overload does not consistently use the "@staticmethod" decorator on all function signatures. [misc] +overloads_definitions_stub.pyi:39: error: Overload does not consistently use the "@classmethod" decorator on all function signatures. [misc] +overloads_definitions_stub.pyi:72: error: In a stub file @final must be applied only to the first overload [misc] +overloads_definitions_stub.pyi:85: error: In a stub file @final must be applied only to the first overload [misc] +overloads_definitions_stub.pyi:107: error: Cannot override final attribute "final_method" (previously declared in base class "Base") [misc] +overloads_definitions_stub.pyi:120: error: Method "bad_override" is marked as an override, but no base method was found with this name [misc] +overloads_definitions_stub.pyi:146: error: In a stub file @override must be applied only to the first overload [misc] +""" diff --git a/conformance/results/zuban/overloads_evaluation.toml b/conformance/results/zuban/overloads_evaluation.toml new file mode 100644 index 000000000..0e3b48cf8 --- /dev/null +++ b/conformance/results/zuban/overloads_evaluation.toml @@ -0,0 +1,19 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +overloads_evaluation.py:38: error: All overload variants of "example1_1" require at least one argument [call-overload] +overloads_evaluation.py:38: note: Possible overload variants: +overloads_evaluation.py:38: note: def example1_1(x: int, y: str) -> int +overloads_evaluation.py:38: note: def example1_1(x: str) -> str +overloads_evaluation.py:46: error: No overload variant of "example1_1" matches argument types "int", "int" [call-overload] +overloads_evaluation.py:46: note: Possible overload variants: +overloads_evaluation.py:46: note: def example1_1(x: int, y: str) -> int +overloads_evaluation.py:46: note: def example1_1(x: str) -> str +overloads_evaluation.py:51: error: No overload variant of "example1_1" matches argument type "int" [call-overload] +overloads_evaluation.py:51: note: Possible overload variants: +overloads_evaluation.py:51: note: def example1_1(x: int, y: str) -> int +overloads_evaluation.py:51: note: def example1_1(x: str) -> str +overloads_evaluation.py:116: error: Argument 1 to "example2" has incompatible type "int | str"; expected "int" [arg-type] +overloads_evaluation.py:116: error: Argument 2 to "example2" has incompatible type "int | str"; expected "str" [arg-type] +""" diff --git a/conformance/results/zuban/protocols_class_objects.toml b/conformance/results/zuban/protocols_class_objects.toml new file mode 100644 index 000000000..04681647c --- /dev/null +++ b/conformance/results/zuban/protocols_class_objects.toml @@ -0,0 +1,25 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_class_objects.py:29: error: Only concrete class can be given where "type[Proto]" is expected [misc] +protocols_class_objects.py:34: error: Can only assign concrete classes to a variable of type "type[Proto]" [misc] +protocols_class_objects.py:58: error: Incompatible types in assignment (expression has type "type[ConcreteA]", variable has type "ProtoA1") [assignment] +protocols_class_objects.py:58: note: Following member(s) of "ConcreteA" have conflicts: +protocols_class_objects.py:58: note: Expected: +protocols_class_objects.py:58: note: def method1(x: int) -> int +protocols_class_objects.py:58: note: Got: +protocols_class_objects.py:58: note: def method1(self: ConcreteA, x: int) -> int +protocols_class_objects.py:74: error: Incompatible types in assignment (expression has type "type[ConcreteB]", variable has type "ProtoB1") [assignment] +protocols_class_objects.py:74: note: Following member(s) of "ConcreteB" have conflicts: +protocols_class_objects.py:74: note: prop1: expected "int", got "property" +protocols_class_objects.py:104: error: Incompatible types in assignment (expression has type "type[ConcreteC1]", variable has type "ProtoC1") [assignment] +protocols_class_objects.py:104: note: ClassVar protocol member ProtoC1.attr1 can never be matched by a class object +protocols_class_objects.py:106: error: Incompatible types in assignment (expression has type "type[ConcreteC2]", variable has type "ProtoC1") [assignment] +protocols_class_objects.py:106: note: Protocol member ProtoC1.attr1 expected class variable, got instance variable +protocols_class_objects.py:106: note: Only class variables allowed for class object access on protocols, attr1 is an instance variable of "ConcreteC2" +protocols_class_objects.py:107: error: Incompatible types in assignment (expression has type "type[ConcreteC2]", variable has type "ProtoC2") [assignment] +protocols_class_objects.py:107: note: Only class variables allowed for class object access on protocols, attr1 is an instance variable of "ConcreteC2" +protocols_class_objects.py:108: error: Incompatible types in assignment (expression has type "type[ConcreteC3]", variable has type "ProtoC1") [assignment] +protocols_class_objects.py:108: note: ClassVar protocol member ProtoC1.attr1 can never be matched by a class object +""" diff --git a/conformance/results/zuban/protocols_definition.toml b/conformance/results/zuban/protocols_definition.toml new file mode 100644 index 000000000..a0316e0ea --- /dev/null +++ b/conformance/results/zuban/protocols_definition.toml @@ -0,0 +1,70 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_definition.py:30: error: List item 0 has incompatible type "int"; expected "SupportsClose" [list-item] +protocols_definition.py:67: error: Protocol members cannot be defined via assignment to self [misc] +protocols_definition.py:67: error: "Template" has no attribute "temp" [attr-defined] +protocols_definition.py:114: error: Incompatible types in assignment (expression has type "Concrete2_Bad1", variable has type "Template2") [assignment] +protocols_definition.py:115: error: Incompatible types in assignment (expression has type "Concrete2_Bad2", variable has type "Template2") [assignment] +protocols_definition.py:115: note: Following member(s) of "Concrete2_Bad2" have conflicts: +protocols_definition.py:115: note: val1: expected "Sequence[int]", got "Sequence[float]" +protocols_definition.py:115: note: Protocol member Template2.val1 expected class variable, got instance variable +protocols_definition.py:116: error: Incompatible types in assignment (expression has type "Concrete2_Bad3", variable has type "Template2") [assignment] +protocols_definition.py:116: note: Following member(s) of "Concrete2_Bad3" have conflicts: +protocols_definition.py:116: note: val1: expected "Sequence[int]", got "list[int]" +protocols_definition.py:116: note: Protocol member Template2.val1 expected class variable, got instance variable +protocols_definition.py:117: error: Incompatible types in assignment (expression has type "Concrete2_Bad4", variable has type "Template2") [assignment] +protocols_definition.py:117: note: Protocol member Template2.val1 expected class variable, got instance variable +protocols_definition.py:156: error: Incompatible types in assignment (expression has type "Concrete3_Bad1", variable has type "Template3") [assignment] +protocols_definition.py:157: error: Incompatible types in assignment (expression has type "Concrete3_Bad2", variable has type "Template3") [assignment] +protocols_definition.py:157: note: Protocol member Template3.val1 expected instance variable, got class variable +protocols_definition.py:158: error: Incompatible types in assignment (expression has type "Concrete3_Bad3", variable has type "Template3") [assignment] +protocols_definition.py:158: note: Protocol member Template3.val1 expected settable variable, got read-only attribute +protocols_definition.py:159: error: Incompatible types in assignment (expression has type "Concrete3_Bad4", variable has type "Template3") [assignment] +protocols_definition.py:159: note: Following member(s) of "Concrete3_Bad4" have conflicts: +protocols_definition.py:159: note: val1: expected "Sequence[int]", got "Sequence[float]" +protocols_definition.py:160: error: Incompatible types in assignment (expression has type "Concrete3_Bad5", variable has type "Template3") [assignment] +protocols_definition.py:160: note: Following member(s) of "Concrete3_Bad5" have conflicts: +protocols_definition.py:160: note: val1: expected "Sequence[int]", got "list[int]" +protocols_definition.py:218: error: Incompatible types in assignment (expression has type "Concrete4_Bad1", variable has type "Template4") [assignment] +protocols_definition.py:218: note: Following member(s) of "Concrete4_Bad1" have conflicts: +protocols_definition.py:218: note: val1: expected "Sequence[float]", got "Callable[[], Sequence[int]]" +protocols_definition.py:219: error: Incompatible types in assignment (expression has type "Concrete4_Bad2", variable has type "Template4") [assignment] +protocols_definition.py:285: error: Incompatible types in assignment (expression has type "Concrete5_Bad1", variable has type "Template5") [assignment] +protocols_definition.py:285: note: Following member(s) of "Concrete5_Bad1" have conflicts: +protocols_definition.py:285: note: Expected: +protocols_definition.py:285: note: def method1(self, a: int, b: int) -> float +protocols_definition.py:285: note: Got: +protocols_definition.py:285: note: def method1(self, a: Any, c: Any) -> int +protocols_definition.py:286: error: Incompatible types in assignment (expression has type "Concrete5_Bad2", variable has type "Template5") [assignment] +protocols_definition.py:286: note: Following member(s) of "Concrete5_Bad2" have conflicts: +protocols_definition.py:286: note: Expected: +protocols_definition.py:286: note: def method1(self, a: int, b: int) -> float +protocols_definition.py:286: note: Got: +protocols_definition.py:286: note: def method1(self, a: int, c: int) -> int +protocols_definition.py:287: error: Incompatible types in assignment (expression has type "Concrete5_Bad3", variable has type "Template5") [assignment] +protocols_definition.py:287: note: Following member(s) of "Concrete5_Bad3" have conflicts: +protocols_definition.py:287: note: Expected: +protocols_definition.py:287: note: def method1(self, a: int, b: int) -> float +protocols_definition.py:287: note: Got: +protocols_definition.py:287: note: def method1(self, *, a: int, b: int) -> float +protocols_definition.py:288: error: Incompatible types in assignment (expression has type "Concrete5_Bad4", variable has type "Template5") [assignment] +protocols_definition.py:288: note: Following member(s) of "Concrete5_Bad4" have conflicts: +protocols_definition.py:288: note: Expected: +protocols_definition.py:288: note: def method1(self, a: int, b: int) -> float +protocols_definition.py:288: note: Got: +protocols_definition.py:288: note: def method1(self, int, int, /) -> float +protocols_definition.py:289: error: Incompatible types in assignment (expression has type "Concrete5_Bad5", variable has type "Template5") [assignment] +protocols_definition.py:289: note: Following member(s) of "Concrete5_Bad5" have conflicts: +protocols_definition.py:289: note: Expected: +protocols_definition.py:289: note: def method1(self: Template5, a: int, b: int) -> float +protocols_definition.py:289: note: Got: +protocols_definition.py:289: note: def method1(self: Any, a: int, b: int) -> float +protocols_definition.py:339: error: Incompatible types in assignment (expression has type "Concrete6_Bad1", variable has type "Template6") [assignment] +protocols_definition.py:339: note: Protocol member Template6.val1 expected settable variable, got read-only attribute +protocols_definition.py:340: error: Incompatible types in assignment (expression has type "Concrete6_Bad2", variable has type "Template6") [assignment] +protocols_definition.py:340: note: Protocol member Template6.val1 expected settable variable, got read-only attribute +protocols_definition.py:341: error: Incompatible types in assignment (expression has type "Concrete6_Bad3", variable has type "Template6") [assignment] +protocols_definition.py:341: note: Protocol member Template6.val1 expected settable variable, got read-only attribute +""" diff --git a/conformance/results/zuban/protocols_explicit.toml b/conformance/results/zuban/protocols_explicit.toml new file mode 100644 index 000000000..259623c10 --- /dev/null +++ b/conformance/results/zuban/protocols_explicit.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_explicit.py:27: error: Call to abstract method "draw" of "PColor" with trivial body via super() is unsafe [safe-super] +protocols_explicit.py:56: error: Incompatible types in assignment (expression has type "tuple[int, int, str]", variable has type "tuple[int, int, int]") [assignment] +protocols_explicit.py:60: error: Cannot instantiate abstract class "Point" with abstract attributes "intensity" and "transparency" [abstract] +protocols_explicit.py:89: error: Cannot instantiate abstract class "Concrete1" with abstract attribute "cm1" [abstract] +protocols_explicit.py:134: error: Cannot instantiate abstract class "Concrete5" with abstract attribute "method1" [abstract] +protocols_explicit.py:164: error: Cannot instantiate abstract class "Concrete7A" with abstract attribute "method1" [abstract] +""" diff --git a/conformance/results/zuban/protocols_generic.toml b/conformance/results/zuban/protocols_generic.toml new file mode 100644 index 000000000..102540dd1 --- /dev/null +++ b/conformance/results/zuban/protocols_generic.toml @@ -0,0 +1,39 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_generic.py:40: error: Incompatible types in assignment (expression has type "Concrete1", variable has type "Proto1[int, str]") [assignment] +protocols_generic.py:40: note: Following member(s) of "Concrete1" have conflicts: +protocols_generic.py:40: note: Expected: +protocols_generic.py:40: note: def method1(self, x: int) -> int +protocols_generic.py:40: note: Got: +protocols_generic.py:40: note: def method1(self, x: str) -> str +protocols_generic.py:40: note: Expected: +protocols_generic.py:40: note: def __iter__(self) -> Iterator[str] +protocols_generic.py:40: note: Got: +protocols_generic.py:40: note: def __iter__(self) -> Iterator[int] +protocols_generic.py:44: error: Only single Generic[...] or Protocol[...] can be in bases [misc] +protocols_generic.py:56: error: Incompatible types in assignment (expression has type "Box[float]", variable has type "Box[int]") [assignment] +protocols_generic.py:66: error: Incompatible types in assignment (expression has type "Sender[int]", variable has type "Sender[float]") [assignment] +protocols_generic.py:74: error: Incompatible types in assignment (expression has type "AttrProto[int]", variable has type "AttrProto[float]") [assignment] +protocols_generic.py:75: error: Incompatible types in assignment (expression has type "AttrProto[float]", variable has type "AttrProto[int]") [assignment] +protocols_generic.py:145: error: Incompatible types in assignment (expression has type "ConcreteHasProperty2", variable has type "HasPropertyProto") [assignment] +protocols_generic.py:145: note: Following member(s) of "ConcreteHasProperty2" have conflicts: +protocols_generic.py:145: note: Expected: +protocols_generic.py:145: note: def [T] m(self, item: T, callback: Callable[[T], str]) -> str +protocols_generic.py:145: note: Got: +protocols_generic.py:145: note: def m(self, item: int, callback: Callable[[int], str]) -> str +protocols_generic.py:146: error: Incompatible types in assignment (expression has type "ConcreteHasProperty3", variable has type "HasPropertyProto") [assignment] +protocols_generic.py:146: note: Following member(s) of "ConcreteHasProperty3" have conflicts: +protocols_generic.py:146: note: f: expected "ConcreteHasProperty3", got "int" +protocols_generic.py:146: note: Expected: +protocols_generic.py:146: note: def [T] m(self, item: T, callback: Callable[[T], str]) -> str +protocols_generic.py:146: note: Got: +protocols_generic.py:146: note: def m(self, item: int, callback: Callable[[int], str]) -> str +protocols_generic.py:147: error: Incompatible types in assignment (expression has type "ConcreteHasProperty4", variable has type "HasPropertyProto") [assignment] +protocols_generic.py:147: note: Following member(s) of "ConcreteHasProperty4" have conflicts: +protocols_generic.py:147: note: Expected: +protocols_generic.py:147: note: def [T] m(self, item: T, callback: Callable[[T], str]) -> str +protocols_generic.py:147: note: Got: +protocols_generic.py:147: note: def m(self, item: str, callback: Callable[[int], str]) -> str +""" diff --git a/conformance/results/zuban/protocols_merging.toml b/conformance/results/zuban/protocols_merging.toml new file mode 100644 index 000000000..96fe626e1 --- /dev/null +++ b/conformance/results/zuban/protocols_merging.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_merging.py:52: error: Incompatible types in assignment (expression has type "SCConcrete2", variable has type "SizedAndClosable1") [assignment] +protocols_merging.py:52: note: "SCConcrete2" is missing following "SizedAndClosable1" protocol member: +protocols_merging.py:52: note: __len__ +protocols_merging.py:53: error: Incompatible types in assignment (expression has type "SCConcrete2", variable has type "SizedAndClosable2") [assignment] +protocols_merging.py:53: note: "SCConcrete2" is missing following "SizedAndClosable2" protocol member: +protocols_merging.py:53: note: __len__ +protocols_merging.py:54: error: Incompatible types in assignment (expression has type "SCConcrete2", variable has type "SizedAndClosable3") [assignment] +protocols_merging.py:67: error: All bases of a protocol must be protocols [misc] +protocols_merging.py:82: error: Cannot instantiate abstract class "SizedAndClosable4" with abstract attribute "close" [abstract] +protocols_merging.py:83: error: Incompatible types in assignment (expression has type "SCConcrete1", variable has type "SizedAndClosable4") [assignment] +""" diff --git a/conformance/results/zuban/protocols_modules.toml b/conformance/results/zuban/protocols_modules.toml new file mode 100644 index 000000000..c478b180b --- /dev/null +++ b/conformance/results/zuban/protocols_modules.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_modules.py:26: error: Incompatible types in assignment (expression has type "ModuleType", variable has type "Options2") [assignment] +protocols_modules.py:26: note: Following member(s) of Module "tests._protocols_modules1" have conflicts: +protocols_modules.py:26: note: timeout: expected "str", got "int" +protocols_modules.py:48: error: Incompatible types in assignment (expression has type "ModuleType", variable has type "Reporter2") [assignment] +protocols_modules.py:48: note: Following member(s) of Module "tests._protocols_modules2" have conflicts: +protocols_modules.py:48: note: Expected: +protocols_modules.py:48: note: def on_error(x: int) -> int +protocols_modules.py:48: note: Got: +protocols_modules.py:48: note: def on_error(x: int) -> None +protocols_modules.py:49: error: Incompatible types in assignment (expression has type "ModuleType", variable has type "Reporter3") [assignment] +""" diff --git a/conformance/results/zuban/protocols_recursive.toml b/conformance/results/zuban/protocols_recursive.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/protocols_recursive.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/protocols_runtime_checkable.toml b/conformance/results/zuban/protocols_runtime_checkable.toml new file mode 100644 index 000000000..e1daddfe8 --- /dev/null +++ b/conformance/results/zuban/protocols_runtime_checkable.toml @@ -0,0 +1,15 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_runtime_checkable.py:23: error: Only @runtime_checkable protocols can be used with instance and class checks [misc] +protocols_runtime_checkable.py:55: error: Only protocols that don't have non-method members can be used with issubclass() [misc] +protocols_runtime_checkable.py:55: note: Protocol "DataProtocol" has non-method member(s): name +protocols_runtime_checkable.py:61: error: Only protocols that don't have non-method members can be used with issubclass() [misc] +protocols_runtime_checkable.py:61: note: Protocol "DataProtocol" has non-method member(s): name +protocols_runtime_checkable.py:88: error: Subclass of "Concrete3A" and "Proto3" cannot exist: would have incompatible method signatures [unreachable] +protocols_runtime_checkable.py:91: error: Subclass of "Concrete3B" and "Proto3" cannot exist: would have incompatible method signatures [unreachable] +protocols_runtime_checkable.py:91: error: Subclass of "Concrete3B" and "NonDataProtocol" cannot exist: would have incompatible method signatures [unreachable] +protocols_runtime_checkable.py:94: error: Subclass of "Concrete3A" and "Proto3" cannot exist: would have incompatible method signatures [unreachable] +protocols_runtime_checkable.py:94: error: Subclass of "Concrete3A" and "NonDataProtocol" cannot exist: would have incompatible method signatures [unreachable] +""" diff --git a/conformance/results/zuban/protocols_self.toml b/conformance/results/zuban/protocols_self.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/protocols_self.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/protocols_subtyping.toml b/conformance/results/zuban/protocols_subtyping.toml new file mode 100644 index 000000000..159857a45 --- /dev/null +++ b/conformance/results/zuban/protocols_subtyping.toml @@ -0,0 +1,34 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_subtyping.py:16: error: Cannot instantiate protocol class "Proto1" [misc] +protocols_subtyping.py:38: error: Incompatible types in assignment (expression has type "Proto2", variable has type "Concrete2") [assignment] +protocols_subtyping.py:55: error: Incompatible types in assignment (expression has type "Proto2", variable has type "Proto3") [assignment] +protocols_subtyping.py:55: note: "Proto2" is missing following "Proto3" protocol member: +protocols_subtyping.py:55: note: method2 +protocols_subtyping.py:79: error: Incompatible types in assignment (expression has type "Proto5[int]", variable has type "Proto4[int, float]") [assignment] +protocols_subtyping.py:79: note: Following member(s) of "Proto5[int]" have conflicts: +protocols_subtyping.py:79: note: Expected: +protocols_subtyping.py:79: note: def method1(self, a: int, b: float) -> tuple[int, float] +protocols_subtyping.py:79: note: Got: +protocols_subtyping.py:79: note: def method1(self, a: int, b: int) -> tuple[int, int] +protocols_subtyping.py:80: error: Incompatible types in assignment (expression has type "Proto4[int, int]", variable has type "Proto5[float]") [assignment] +protocols_subtyping.py:80: note: Following member(s) of "Proto4[int, int]" have conflicts: +protocols_subtyping.py:80: note: Expected: +protocols_subtyping.py:80: note: def method1(self, a: float, b: float) -> tuple[float, float] +protocols_subtyping.py:80: note: Got: +protocols_subtyping.py:80: note: def method1(self, a: int, b: int) -> tuple[int, int] +protocols_subtyping.py:102: error: Incompatible types in assignment (expression has type "Proto6[float, float]", variable has type "Proto7[int, float]") [assignment] +protocols_subtyping.py:102: note: Following member(s) of "Proto6[float, float]" have conflicts: +protocols_subtyping.py:102: note: Expected: +protocols_subtyping.py:102: note: def method1(self, a: float) -> Sequence[int] +protocols_subtyping.py:102: note: Got: +protocols_subtyping.py:102: note: def method1(self, a: float) -> Sequence[float] +protocols_subtyping.py:103: error: Incompatible types in assignment (expression has type "Proto6[float, float]", variable has type "Proto7[float, object]") [assignment] +protocols_subtyping.py:103: note: Following member(s) of "Proto6[float, float]" have conflicts: +protocols_subtyping.py:103: note: Expected: +protocols_subtyping.py:103: note: def method1(self, a: object) -> Sequence[float] +protocols_subtyping.py:103: note: Got: +protocols_subtyping.py:103: note: def method1(self, a: float) -> Sequence[float] +""" diff --git a/conformance/results/zuban/protocols_variance.toml b/conformance/results/zuban/protocols_variance.toml new file mode 100644 index 000000000..ba95f1366 --- /dev/null +++ b/conformance/results/zuban/protocols_variance.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +protocols_variance.py:21: error: Invariant type variable "T1" used in protocol where Covariant one is expected [misc] +protocols_variance.py:40: error: Invariant type variable "T3" used in protocol where Contravariant one is expected [misc] +protocols_variance.py:56: error: Invariant type variable "T1" used in protocol where Contravariant one is expected [misc] +protocols_variance.py:61: error: Covariant type variable "T1_co" used in protocol where Contravariant one is expected [misc] +protocols_variance.py:62: error: Cannot use a covariant type variable as a parameter [type-var] +protocols_variance.py:66: error: Invariant type variable "T1" used in protocol where Covariant one is expected [misc] +protocols_variance.py:71: error: Contravariant type variable "T1_contra" used in protocol where Covariant one is expected [misc] +protocols_variance.py:72: error: Cannot use a contravariant type variable as return type [type-var] +protocols_variance.py:104: error: Invariant type variable "T1" used in protocol where Covariant one is expected [misc] +""" diff --git a/conformance/results/zuban/qualifiers_annotated.toml b/conformance/results/zuban/qualifiers_annotated.toml new file mode 100644 index 000000000..03e563a7a --- /dev/null +++ b/conformance/results/zuban/qualifiers_annotated.toml @@ -0,0 +1,27 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +qualifiers_annotated.py:38: error: Bracketed expression "[...]" is not valid as a type [valid-type] +qualifiers_annotated.py:38: note: Did you mean "List[...]"? +qualifiers_annotated.py:39: error: Syntax error in type annotation [valid-type] +qualifiers_annotated.py:39: note: Suggestion: Is there a spurious trailing comma? +qualifiers_annotated.py:40: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:41: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:42: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:43: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:44: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:45: error: Name "var1" is not defined [name-defined] +qualifiers_annotated.py:46: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:47: error: Invalid type: try using Literal[1] instead? [valid-type] +qualifiers_annotated.py:48: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:49: error: Invalid type comment or annotation [valid-type] +qualifiers_annotated.py:59: error: Annotated[...] must have exactly one type argument and at least one annotation [valid-type] +qualifiers_annotated.py:71: error: Incompatible types in assignment (expression has type "_SpecialForm", variable has type "type[Any]") [assignment] +qualifiers_annotated.py:72: error: Incompatible types in assignment (expression has type "_SpecialForm", variable has type "type[Any]") [assignment] +qualifiers_annotated.py:79: error: Argument 1 to "func4" has incompatible type "_SpecialForm"; expected "type[Never]" [arg-type] +qualifiers_annotated.py:80: error: Argument 1 to "func4" has incompatible type "_SpecialForm"; expected "type[Never]" [arg-type] +qualifiers_annotated.py:86: error: "_SpecialForm" not callable [operator] +qualifiers_annotated.py:87: error: "_SpecialForm" not callable [operator] +qualifiers_annotated.py:88: error: "" not callable [operator] +""" diff --git a/conformance/results/zuban/qualifiers_final_annotation.toml b/conformance/results/zuban/qualifiers_final_annotation.toml new file mode 100644 index 000000000..32a15ea44 --- /dev/null +++ b/conformance/results/zuban/qualifiers_final_annotation.toml @@ -0,0 +1,33 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +qualifiers_final_annotation.py:16: error: Type in Final[...] can only be omitted if there is an initializer [misc] +qualifiers_final_annotation.py:18: error: Final[...] takes at most one type argument [misc] +qualifiers_final_annotation.py:34: error: Type in Final[...] can only be omitted if there is an initializer [misc] +qualifiers_final_annotation.py:38: error: Final name must be initialized with a value [misc] +qualifiers_final_annotation.py:54: error: Cannot assign to final attribute "ID5" [misc] +qualifiers_final_annotation.py:62: error: Can only declare a final attribute in class body or __init__ [misc] +qualifiers_final_annotation.py:63: error: Can only declare a final attribute in class body or __init__ [misc] +qualifiers_final_annotation.py:65: error: Cannot assign to final attribute "ID7" [misc] +qualifiers_final_annotation.py:67: error: Cannot assign to final attribute "ID7" [misc] +qualifiers_final_annotation.py:71: error: Cannot assign to final name "RATE" [misc] +qualifiers_final_annotation.py:81: error: Cannot assign to final attribute "DEFAULT_ID" [misc] +qualifiers_final_annotation.py:94: error: Cannot assign to final name "BORDER_WIDTH" [misc] +qualifiers_final_annotation.py:107: error: Final can be only used as an outermost qualifier in a variable annotation [misc] +qualifiers_final_annotation.py:108: error: Invalid Type: ClassVar nested inside other type [misc] +qualifiers_final_annotation.py:118: error: Final can be only used as an outermost qualifier in a variable annotation [misc] +qualifiers_final_annotation.py:121: error: Final can be only used as an outermost qualifier in a variable annotation [misc] +qualifiers_final_annotation.py:134: error: Unexpected keyword argument "a" for "N" [call-arg] +qualifiers_final_annotation.py:135: error: Argument "x" to "N" has incompatible type "str"; expected "int" [arg-type] +qualifiers_final_annotation.py:135: error: Argument "y" to "N" has incompatible type "str"; expected "int" [arg-type] +qualifiers_final_annotation.py:141: error: Cannot assign to final name "ID1" [misc] +qualifiers_final_annotation.py:145: error: Cannot assign to final name "x" [misc] +qualifiers_final_annotation.py:147: error: Cannot assign to final name "x" [misc] +qualifiers_final_annotation.py:149: error: Cannot assign to final name "x" [misc] +qualifiers_final_annotation.py:152: error: Cannot assign to final name "x" [misc] +qualifiers_final_annotation.py:152: error: Incompatible types in assignment (expression has type "TextIOWrapper[_WrappedBuffer]", variable has type "int") [assignment] +qualifiers_final_annotation.py:155: error: Cannot assign to final name "x" [misc] +qualifiers_final_annotation.py:166: error: Cannot assign to final name "TEN" [misc] +qualifiers_final_annotation.py:170: error: Cannot assign to final name "PI" [misc] +""" diff --git a/conformance/results/zuban/qualifiers_final_decorator.toml b/conformance/results/zuban/qualifiers_final_decorator.toml new file mode 100644 index 000000000..bbedc5e60 --- /dev/null +++ b/conformance/results/zuban/qualifiers_final_decorator.toml @@ -0,0 +1,20 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +qualifiers_final_decorator.py:21: error: Cannot inherit from final class "Base1" [misc] +qualifiers_final_decorator.py:56: error: Cannot override final attribute "method1" (previously declared in base class "Base2") [misc] +qualifiers_final_decorator.py:59: error: Cannot override final attribute "method2" (previously declared in base class "Base2") [misc] +qualifiers_final_decorator.py:63: error: Cannot override final attribute "method3" (previously declared in base class "Base2") [misc] +qualifiers_final_decorator.py:67: error: Cannot override final attribute "method4" (previously declared in base class "Base2") [misc] +qualifiers_final_decorator.py:80: error: Cannot override final attribute "method" (previously declared in base class "Base3") [misc] +qualifiers_final_decorator.py:85: error: @final should be applied only to overload implementation [misc] +qualifiers_final_decorator.py:94: error: Cannot override final attribute "method" (previously declared in base class "Base4") [misc] +qualifiers_final_decorator.py:118: error: Cannot override final attribute "method" (previously declared in base class "Base5_2") [misc] +qualifiers_final_decorator.py:118: error: Signature of "method" incompatible with supertype "Base5_2" [override] +qualifiers_final_decorator.py:118: note: Superclass: +qualifiers_final_decorator.py:118: note: def method(self, v: int) -> None +qualifiers_final_decorator.py:118: note: Subclass: +qualifiers_final_decorator.py:118: note: def method(self) -> None +qualifiers_final_decorator.py:125: error: @final cannot be used with non-method functions [misc] +""" diff --git a/conformance/results/zuban/specialtypes_any.toml b/conformance/results/zuban/specialtypes_any.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/specialtypes_any.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/specialtypes_never.toml b/conformance/results/zuban/specialtypes_never.toml new file mode 100644 index 000000000..d65bc2c97 --- /dev/null +++ b/conformance/results/zuban/specialtypes_never.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +specialtypes_never.py:19: error: Implicit return in function which does not return [misc] +specialtypes_never.py:85: error: Incompatible types in assignment (expression has type "list[Never]", variable has type "list[int]") [assignment] +specialtypes_never.py:85: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance +specialtypes_never.py:85: note: Consider using "Sequence" instead, which is covariant +specialtypes_never.py:104: error: Incompatible return value type (got "ClassC[Never]", expected "ClassC[U]") [return-value] +""" diff --git a/conformance/results/zuban/specialtypes_none.toml b/conformance/results/zuban/specialtypes_none.toml new file mode 100644 index 000000000..c7f0e4b42 --- /dev/null +++ b/conformance/results/zuban/specialtypes_none.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +specialtypes_none.py:21: error: Argument 1 to "func1" has incompatible type "type[None]"; expected "None" [arg-type] +specialtypes_none.py:27: error: Incompatible types in assignment (expression has type "None", variable has type "Iterable[Any]") [assignment] +specialtypes_none.py:41: error: Argument 1 to "func2" has incompatible type "None"; expected "type[None]" [arg-type] +""" diff --git a/conformance/results/zuban/specialtypes_promotions.toml b/conformance/results/zuban/specialtypes_promotions.toml new file mode 100644 index 000000000..a261c18d2 --- /dev/null +++ b/conformance/results/zuban/specialtypes_promotions.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +specialtypes_promotions.py:13: error: "float" has no attribute "numerator" [attr-defined] +""" diff --git a/conformance/results/zuban/specialtypes_type.toml b/conformance/results/zuban/specialtypes_type.toml new file mode 100644 index 000000000..838e866ac --- /dev/null +++ b/conformance/results/zuban/specialtypes_type.toml @@ -0,0 +1,14 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +specialtypes_type.py:56: error: Argument 1 to "func4" has incompatible type "type[TeamUser]"; expected "type[BasicUser | ProUser]" [arg-type] +specialtypes_type.py:70: error: Argument 1 to "func5" has incompatible type "_SpecialForm"; expected "type[Never]" [arg-type] +specialtypes_type.py:76: error: Invalid type comment or annotation [valid-type] +specialtypes_type.py:117: error: "type[object]" has no attribute "unknown" [attr-defined] +specialtypes_type.py:120: error: "type[object]" has no attribute "unknown" [attr-defined] +specialtypes_type.py:143: error: "_SpecialForm" has no attribute "unknown" [attr-defined] +specialtypes_type.py:144: error: "_SpecialForm" has no attribute "unknown" [attr-defined] +specialtypes_type.py:145: error: "_SpecialForm" has no attribute "unknown" [attr-defined] +specialtypes_type.py:146: error: "_SpecialForm" has no attribute "unknown" [attr-defined] +""" diff --git a/conformance/results/zuban/tuples_type_compat.toml b/conformance/results/zuban/tuples_type_compat.toml new file mode 100644 index 000000000..a32b872d5 --- /dev/null +++ b/conformance/results/zuban/tuples_type_compat.toml @@ -0,0 +1,29 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +tuples_type_compat.py:15: error: Incompatible types in assignment (expression has type "tuple[float, complex]", variable has type "tuple[int, int]") [assignment] +tuples_type_compat.py:29: error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[int, Unpack[Tuple[int, ...]]]") [assignment] +tuples_type_compat.py:32: error: Incompatible types in assignment (expression has type "tuple[int, Unpack[Tuple[int, ...]]]", variable has type "tuple[int]") [assignment] +tuples_type_compat.py:33: error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[int]") [assignment] +tuples_type_compat.py:43: error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[int]") [assignment] +tuples_type_compat.py:62: error: Incompatible types in assignment (expression has type "tuple[int, ...]", variable has type "tuple[int, int]") [assignment] +tuples_type_compat.py:76: error: Expression is of type "tuple[int]", not "tuple[int] | tuple[str, str] | tuple[int, Unpack[Tuple[str, ...]], int]" [misc] +tuples_type_compat.py:81: error: Expression is of type "tuple[str, str] | tuple[int, int]", not "tuple[int] | tuple[str, str] | tuple[int, Unpack[Tuple[str, ...]], int]" [misc] +tuples_type_compat.py:86: error: Expression is of type "tuple[int, str, int]", not "tuple[int] | tuple[str, str] | tuple[int, Unpack[Tuple[str, ...]], int]" [misc] +tuples_type_compat.py:102: error: Expression is of type "tuple[int]", not "tuple[int] | tuple[str, str] | tuple[int, Unpack[Tuple[str, ...]], int]" [misc] +tuples_type_compat.py:107: error: Expression is of type "tuple[str, str] | tuple[int, int]", not "tuple[int] | tuple[str, str] | tuple[int, Unpack[Tuple[str, ...]], int]" [misc] +tuples_type_compat.py:112: error: Expression is of type "tuple[int, str, int]", not "tuple[int] | tuple[str, str] | tuple[int, Unpack[Tuple[str, ...]], int]" [misc] +tuples_type_compat.py:127: error: Expression is of type "tuple[int | str, str]", not "tuple[int | str, int | str]" [misc] +tuples_type_compat.py:129: error: Expression is of type "tuple[int | str, int | str]", not "tuple[int | str, int]" [misc] +tuples_type_compat.py:157: error: Incompatible types in assignment (expression has type "tuple[int, str, str]", variable has type "tuple[int, str]") [assignment] +tuples_type_compat.py:162: error: Incompatible types in assignment (expression has type "tuple[int, int, str]", variable has type "tuple[int, Unpack[Tuple[str, ...]]]") [assignment] +tuples_type_compat.py:163: error: Incompatible types in assignment (expression has type "tuple[int, str, int]", variable has type "tuple[int, Unpack[Tuple[str, ...]]]") [assignment] +tuples_type_compat.py:169: error: Incompatible types in assignment (expression has type "tuple[int, str, str]", variable has type "tuple[int, Unpack[Tuple[str, ...]], int]") [assignment] +tuples_type_compat.py:170: error: Incompatible types in assignment (expression has type "tuple[int, str, str, float]", variable has type "tuple[int, Unpack[Tuple[str, ...]], int]") [assignment] +tuples_type_compat.py:175: error: Incompatible types in assignment (expression has type "tuple[int, str, int]", variable has type "tuple[Unpack[Tuple[str, ...]], int]") [assignment] +tuples_type_compat.py:176: error: Incompatible types in assignment (expression has type "tuple[str, str, float]", variable has type "tuple[Unpack[Tuple[str, ...]], int]") [assignment] +tuples_type_compat.py:181: error: Incompatible types in assignment (expression has type "tuple[str, str]", variable has type "tuple[str, str, int]") [assignment] +tuples_type_compat.py:184: error: Incompatible types in assignment (expression has type "tuple[str, str]", variable has type "tuple[str, str, str, Unpack[Tuple[str, ...]]]") [assignment] +tuples_type_compat.py:188: error: Incompatible types in assignment (expression has type "tuple[str, str]", variable has type "tuple[Unpack[Tuple[str, ...]], str, str, str]") [assignment] +""" diff --git a/conformance/results/zuban/tuples_type_form.toml b/conformance/results/zuban/tuples_type_form.toml new file mode 100644 index 000000000..c363b015b --- /dev/null +++ b/conformance/results/zuban/tuples_type_form.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +tuples_type_form.py:12: error: Incompatible types in assignment (expression has type "tuple[int, int]", variable has type "tuple[int]") [assignment] +tuples_type_form.py:14: error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "tuple[int, int]") [assignment] +tuples_type_form.py:15: error: Incompatible types in assignment (expression has type "tuple[int, str]", variable has type "tuple[int, int]") [assignment] +tuples_type_form.py:25: error: Incompatible types in assignment (expression has type "tuple[int]", variable has type "tuple[()]") [assignment] +tuples_type_form.py:36: error: Incompatible types in assignment (expression has type "tuple[int, int, int, str]", variable has type "tuple[int, ...]") [assignment] +tuples_type_form.py:40: error: Unexpected "..." [valid-type] +tuples_type_form.py:41: error: Unexpected "..." [valid-type] +tuples_type_form.py:42: error: Unexpected "..." [valid-type] +tuples_type_form.py:43: error: Unexpected "..." [valid-type] +tuples_type_form.py:44: error: Unpack is only valid in a variadic position [misc] +tuples_type_form.py:45: error: Unpack is only valid in a variadic position [misc] +""" diff --git a/conformance/results/zuban/tuples_unpacked.toml b/conformance/results/zuban/tuples_unpacked.toml new file mode 100644 index 000000000..f550fe371 --- /dev/null +++ b/conformance/results/zuban/tuples_unpacked.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +tuples_unpacked.py:40: error: More than one variadic Unpack in a type is not allowed [misc] +tuples_unpacked.py:41: error: More than one variadic Unpack in a type is not allowed [misc] +tuples_unpacked.py:51: error: More than one variadic Unpack in a type is not allowed [misc] +tuples_unpacked.py:59: error: More than one variadic Unpack in a type is not allowed [misc] +tuples_unpacked.py:61: error: More than one variadic Unpack in a type is not allowed [misc] +""" diff --git a/conformance/results/zuban/typeddicts_alt_syntax.toml b/conformance/results/zuban/typeddicts_alt_syntax.toml new file mode 100644 index 000000000..a9440e734 --- /dev/null +++ b/conformance/results/zuban/typeddicts_alt_syntax.toml @@ -0,0 +1,10 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_alt_syntax.py:23: error: TypedDict() expects a dictionary literal as the second argument [misc] +typeddicts_alt_syntax.py:27: error: Invalid TypedDict() field name [misc] +typeddicts_alt_syntax.py:31: error: First argument "WrongName" to TypedDict() does not match variable name "BadTypedDict3" [name-match] +typeddicts_alt_syntax.py:35: error: Unexpected keyword argument "other" for "TypedDict" [call-arg] +typeddicts_alt_syntax.py:41: error: TypedDict() expects a dictionary literal as the second argument [misc] +""" diff --git a/conformance/results/zuban/typeddicts_class_syntax.toml b/conformance/results/zuban/typeddicts_class_syntax.toml new file mode 100644 index 000000000..8c8f09a9e --- /dev/null +++ b/conformance/results/zuban/typeddicts_class_syntax.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_class_syntax.py:30: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc] +typeddicts_class_syntax.py:34: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc] +typeddicts_class_syntax.py:39: error: Invalid statement in TypedDict definition; expected "field_name: field_type" [misc] +typeddicts_class_syntax.py:45: error: Unexpected keyword argument "metaclass" for "TypedDict" [call-arg] +typeddicts_class_syntax.py:50: error: Unexpected keyword argument "other" for "TypedDict" [call-arg] +typeddicts_class_syntax.py:65: error: Extra key "z" for TypedDict "ConditionalField" [typeddict-unknown-key] +""" diff --git a/conformance/results/zuban/typeddicts_extra_items.toml b/conformance/results/zuban/typeddicts_extra_items.toml new file mode 100644 index 000000000..82f60b5fd --- /dev/null +++ b/conformance/results/zuban/typeddicts_extra_items.toml @@ -0,0 +1,33 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_extra_items.py:15: error: Incompatible types (expression has type "int", TypedDict item "year" has type "bool") [typeddict-item] +typeddicts_extra_items.py:22: error: Incompatible types (expression has type "int", TypedDict item "year" has type "bool") [typeddict-item] +typeddicts_extra_items.py:39: error: Incompatible types (expression has type "None", TypedDict item "year" has type "int") [typeddict-item] +typeddicts_extra_items.py:49: error: "closed" argument must be a True or False literal [misc] +typeddicts_extra_items.py:67: error: Cannot set "closed=False" when superclass is "closed=True" [misc] +typeddicts_extra_items.py:73: error: Cannot set "closed=False" when superclass has "extra_items" [misc] +typeddicts_extra_items.py:92: error: TypedDict member "age" is required, but the extra_items of the super class are not [misc] +typeddicts_extra_items.py:95: error: TypedDict member "age" is required, but the extra_items of the super class are not [misc] +typeddicts_extra_items.py:109: error: Cannot set "closed=True" when superclass has non-read-only "extra_items" [misc] +typeddicts_extra_items.py:114: error: "extra_items" value cannot be "Required[...]" [misc] +typeddicts_extra_items.py:117: error: "extra_items" value cannot be "NotRequired[...]" [misc] +typeddicts_extra_items.py:128: error: Key "name" of TypedDict "MovieEI" cannot be deleted [misc] +typeddicts_extra_items.py:143: error: Unexpected keyword argument "year" for "unpack_no_extra" [call-arg] +typeddicts_extra_items.py:174: error: Cannot change "extra_items" type unless it is "ReadOnly" in the superclass [misc] +typeddicts_extra_items.py:185: error: TypedDict member "year" is required, but the extra_items of the super class are not [misc] +typeddicts_extra_items.py:188: error: TypedDict member "year" type "int" is not assignable, but the extra_items of the super class are of type "int | None" [misc] +typeddicts_extra_items.py:197: error: TypedDict member "publisher" type "str" is not assignable, but the extra_items of the super class are of type "int | None" [misc] +typeddicts_extra_items.py:215: error: Incompatible types in assignment (expression has type "MovieDetails", variable has type "MovieBase2") [assignment] +typeddicts_extra_items.py:222: error: Incompatible types in assignment (expression has type "MovieWithYear2", variable has type "MovieBase2") [assignment] +typeddicts_extra_items.py:242: error: Incompatible types in assignment (expression has type "MovieDetails5", variable has type "MovieSI") [assignment] +typeddicts_extra_items.py:256: error: Incompatible types in assignment (expression has type "MovieExtraStr", variable has type "MovieExtraInt") [assignment] +typeddicts_extra_items.py:257: error: Incompatible types in assignment (expression has type "MovieExtraInt", variable has type "MovieExtraStr") [assignment] +typeddicts_extra_items.py:268: error: Incompatible types in assignment (expression has type "MovieNotClosed", variable has type "MovieExtraInt") [assignment] +typeddicts_extra_items.py:278: error: Extra key "year" for TypedDict "NonClosedMovie" [typeddict-unknown-key] +typeddicts_extra_items.py:285: error: Incompatible types (expression has type "str", TypedDict item "language" has type "int") [typeddict-item] +typeddicts_extra_items.py:293: error: Incompatible types (expression has type "int", TypedDict item "year" has type "Never") [typeddict-item] +typeddicts_extra_items.py:303: error: Incompatible types in assignment (expression has type "MovieExtraInt", variable has type "Mapping[str, int]") [assignment] +typeddicts_extra_items.py:352: error: Incompatible types in assignment (expression has type "dict[str, int]", variable has type "IntDict") [assignment] +""" diff --git a/conformance/results/zuban/typeddicts_final.toml b/conformance/results/zuban/typeddicts_final.toml new file mode 100644 index 000000000..cdd4d0cd9 --- /dev/null +++ b/conformance/results/zuban/typeddicts_final.toml @@ -0,0 +1,5 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +""" diff --git a/conformance/results/zuban/typeddicts_inheritance.toml b/conformance/results/zuban/typeddicts_inheritance.toml new file mode 100644 index 000000000..51cd4730d --- /dev/null +++ b/conformance/results/zuban/typeddicts_inheritance.toml @@ -0,0 +1,8 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_inheritance.py:44: error: All bases of a new TypedDict must be TypedDict types [misc] +typeddicts_inheritance.py:55: error: Overwriting TypedDict field "x" while extending [misc] +typeddicts_inheritance.py:65: error: Overwriting TypedDict field "x" while merging [misc] +""" diff --git a/conformance/results/zuban/typeddicts_operations.toml b/conformance/results/zuban/typeddicts_operations.toml new file mode 100644 index 000000000..7a38bd857 --- /dev/null +++ b/conformance/results/zuban/typeddicts_operations.toml @@ -0,0 +1,16 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_operations.py:22: error: Value of "name" has incompatible type "int"; expected "str" [typeddict-item] +typeddicts_operations.py:23: error: Value of "year" has incompatible type "str"; expected "int" [typeddict-item] +typeddicts_operations.py:24: error: TypedDict "Movie" has no key "other" [typeddict-unknown-key] +typeddicts_operations.py:26: error: TypedDict "Movie" has no key "other" [typeddict-item] +typeddicts_operations.py:28: error: Missing key "year" for TypedDict "Movie" [typeddict-item] +typeddicts_operations.py:29: error: Incompatible types (expression has type "float", TypedDict item "year" has type "int") [typeddict-item] +typeddicts_operations.py:32: error: Extra key "other" for TypedDict "Movie" [typeddict-unknown-key] +typeddicts_operations.py:37: error: Expected TypedDict key to be string literal [literal-required] +typeddicts_operations.py:47: error: "Movie" has no attribute "clear" [attr-defined] +typeddicts_operations.py:49: error: Key "name" of TypedDict "Movie" cannot be deleted [misc] +typeddicts_operations.py:62: error: "MovieOptional" has no attribute "clear" [attr-defined] +""" diff --git a/conformance/results/zuban/typeddicts_readonly.toml b/conformance/results/zuban/typeddicts_readonly.toml new file mode 100644 index 000000000..a9a626030 --- /dev/null +++ b/conformance/results/zuban/typeddicts_readonly.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly.py:24: error: ReadOnly TypedDict key "members" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:36: error: ReadOnly TypedDict key "members" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:50: error: ReadOnly TypedDict key "title" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:51: error: ReadOnly TypedDict key "year" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:60: error: ReadOnly TypedDict key "title" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly.py:61: error: ReadOnly TypedDict key "year" TypedDict is mutated [typeddict-readonly-mutated] +""" diff --git a/conformance/results/zuban/typeddicts_readonly_consistency.toml b/conformance/results/zuban/typeddicts_readonly_consistency.toml new file mode 100644 index 000000000..183704fe6 --- /dev/null +++ b/conformance/results/zuban/typeddicts_readonly_consistency.toml @@ -0,0 +1,12 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly_consistency.py:37: error: Incompatible types in assignment (expression has type "A1", variable has type "B1") [assignment] +typeddicts_readonly_consistency.py:38: error: Incompatible types in assignment (expression has type "C1", variable has type "B1") [assignment] +typeddicts_readonly_consistency.py:40: error: Incompatible types in assignment (expression has type "A1", variable has type "C1") [assignment] +typeddicts_readonly_consistency.py:81: error: Incompatible types in assignment (expression has type "A2", variable has type "B2") [assignment] +typeddicts_readonly_consistency.py:82: error: Incompatible types in assignment (expression has type "C2", variable has type "B2") [assignment] +typeddicts_readonly_consistency.py:84: error: Incompatible types in assignment (expression has type "A2", variable has type "C2") [assignment] +typeddicts_readonly_consistency.py:85: error: Incompatible types in assignment (expression has type "B2", variable has type "C2") [assignment] +""" diff --git a/conformance/results/zuban/typeddicts_readonly_inheritance.toml b/conformance/results/zuban/typeddicts_readonly_inheritance.toml new file mode 100644 index 000000000..d40209993 --- /dev/null +++ b/conformance/results/zuban/typeddicts_readonly_inheritance.toml @@ -0,0 +1,18 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly_inheritance.py:36: error: ReadOnly TypedDict key "name" TypedDict is mutated [typeddict-readonly-mutated] +typeddicts_readonly_inheritance.py:50: error: Overwriting TypedDict field "alt" while extending [misc] +typeddicts_readonly_inheritance.py:65: error: Missing key "name" for TypedDict "RequiredName" [typeddict-item] +typeddicts_readonly_inheritance.py:82: error: Value of "ident" has incompatible type "int"; expected "str" [typeddict-item] +typeddicts_readonly_inheritance.py:83: error: Incompatible types (expression has type "int", TypedDict item "ident" has type "str") [typeddict-item] +typeddicts_readonly_inheritance.py:84: error: Missing key "ident" for TypedDict "User" [typeddict-item] +typeddicts_readonly_inheritance.py:94: error: Overwriting TypedDict field "a" while extending [misc] +typeddicts_readonly_inheritance.py:98: error: Overwriting TypedDict field "a" while extending [misc] +typeddicts_readonly_inheritance.py:106: error: Overwriting TypedDict field "c" while extending [misc] +typeddicts_readonly_inheritance.py:119: error: Overwriting TypedDict field "x" while merging [misc] +typeddicts_readonly_inheritance.py:119: error: Overwriting TypedDict field "y" while merging [misc] +typeddicts_readonly_inheritance.py:132: error: Overwriting TypedDict field "x" while merging [misc] +typeddicts_readonly_inheritance.py:132: error: Overwriting TypedDict field "y" while merging [misc] +""" diff --git a/conformance/results/zuban/typeddicts_readonly_kwargs.toml b/conformance/results/zuban/typeddicts_readonly_kwargs.toml new file mode 100644 index 000000000..48f979f9b --- /dev/null +++ b/conformance/results/zuban/typeddicts_readonly_kwargs.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly_kwargs.py:33: error: ReadOnly TypedDict key "key1" TypedDict is mutated [typeddict-readonly-mutated] +""" diff --git a/conformance/results/zuban/typeddicts_readonly_update.toml b/conformance/results/zuban/typeddicts_readonly_update.toml new file mode 100644 index 000000000..c5069c3d5 --- /dev/null +++ b/conformance/results/zuban/typeddicts_readonly_update.toml @@ -0,0 +1,6 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_readonly_update.py:23: error: TypedDict member "x" is read-only [misc] +""" diff --git a/conformance/results/zuban/typeddicts_required.toml b/conformance/results/zuban/typeddicts_required.toml new file mode 100644 index 000000000..c83d715f6 --- /dev/null +++ b/conformance/results/zuban/typeddicts_required.toml @@ -0,0 +1,9 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_required.py:12: error: Required[] can be only used in a TypedDict definition [valid-type] +typeddicts_required.py:16: error: NotRequired[] can be only used in a TypedDict definition [valid-type] +typeddicts_required.py:59: error: "Required[]" type cannot be nested [misc] +typeddicts_required.py:60: error: "NotRequired[]" type cannot be nested [misc] +""" diff --git a/conformance/results/zuban/typeddicts_type_consistency.toml b/conformance/results/zuban/typeddicts_type_consistency.toml new file mode 100644 index 000000000..dc892f04c --- /dev/null +++ b/conformance/results/zuban/typeddicts_type_consistency.toml @@ -0,0 +1,17 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_type_consistency.py:21: error: Incompatible types in assignment (expression has type "B1", variable has type "A1") [assignment] +typeddicts_type_consistency.py:38: error: Incompatible types in assignment (expression has type "B2", variable has type "A2") [assignment] +typeddicts_type_consistency.py:65: error: Incompatible types in assignment (expression has type "A3", variable has type "B3") [assignment] +typeddicts_type_consistency.py:69: error: Extra key "y" for TypedDict "A3" [typeddict-unknown-key] +typeddicts_type_consistency.py:76: error: Incompatible types in assignment (expression has type "B3", variable has type "dict[str, int]") [assignment] +typeddicts_type_consistency.py:76: note: TypedDicts can only be assigned to a dict if the types match, there's extra_items with the same type and all keys are NotRequired +typeddicts_type_consistency.py:77: error: Incompatible types in assignment (expression has type "B3", variable has type "dict[str, object]") [assignment] +typeddicts_type_consistency.py:77: note: TypedDicts can only be assigned to a dict if the types match, there's extra_items with the same type and all keys are NotRequired +typeddicts_type_consistency.py:78: error: Incompatible types in assignment (expression has type "B3", variable has type "dict[Any, Any]") [assignment] +typeddicts_type_consistency.py:78: note: TypedDicts can only be assigned to a dict if the types match, there's extra_items with the same type and all keys are NotRequired +typeddicts_type_consistency.py:82: error: Incompatible types in assignment (expression has type "B3", variable has type "Mapping[str, int]") [assignment] +typeddicts_type_consistency.py:126: error: Incompatible types (expression has type "int", TypedDict item "inner_key" has type "str") [typeddict-item] +""" diff --git a/conformance/results/zuban/typeddicts_usage.toml b/conformance/results/zuban/typeddicts_usage.toml new file mode 100644 index 000000000..b77f80f23 --- /dev/null +++ b/conformance/results/zuban/typeddicts_usage.toml @@ -0,0 +1,11 @@ +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeddicts_usage.py:23: error: TypedDict "Movie" has no key "director" [typeddict-unknown-key] +typeddicts_usage.py:24: error: Value of "year" has incompatible type "str"; expected "int" [typeddict-item] +typeddicts_usage.py:28: error: Extra key "title" for TypedDict "Movie" [typeddict-unknown-key] +typeddicts_usage.py:28: error: Missing key "name" for TypedDict "Movie" [typeddict-item] +typeddicts_usage.py:35: error: Cannot use isinstance() with TypedDict type [misc] +typeddicts_usage.py:40: error: Invalid type [valid-type] +""" diff --git a/conformance/results/zuban/typeforms_typeform.toml b/conformance/results/zuban/typeforms_typeform.toml new file mode 100644 index 000000000..f2ef075dd --- /dev/null +++ b/conformance/results/zuban/typeforms_typeform.toml @@ -0,0 +1,23 @@ +conformant = "Pass" +conformance_automated = "Pass" +errors_diff = """ +""" +output = """ +typeforms_typeform.py:23: error: Incompatible types in assignment (expression has type "TypeForm[str | int]", variable has type "TypeForm[str | None]") [assignment] +typeforms_typeform.py:24: error: Incompatible types in assignment (expression has type "type[list[str | None]]", variable has type "TypeForm[str | None]") [assignment] +typeforms_typeform.py:59: error: Invalid syntax [syntax] +typeforms_typeform.py:67: error: Invalid type comment or annotation [valid-type] +typeforms_typeform.py:68: error: Syntax error in type annotation [valid-type] +typeforms_typeform.py:68: note: Suggestion: Use Tuple[T1, ..., Tn] instead of (T1, ..., Tn) +typeforms_typeform.py:69: error: Invalid type: try using Literal[1] instead? [valid-type] +typeforms_typeform.py:70: error: Self type is only allowed in annotations within class definition [misc] +typeforms_typeform.py:71: error: ClassVar can only be used for assignments in class body [misc] +typeforms_typeform.py:72: error: Final can be only used as an outermost qualifier in a variable annotation [misc] +typeforms_typeform.py:73: error: TypeVarTuple "Ts" is unbound [misc] +typeforms_typeform.py:74: error: Optional[...] must have exactly one type argument [misc] +typeforms_typeform.py:75: error: Invalid type comment or annotation [valid-type] +typeforms_typeform.py:86: error: Invalid type comment or annotation [valid-type] +typeforms_typeform.py:88: error: Invalid type comment or annotation [valid-type] +typeforms_typeform.py:98: error: Incompatible types in assignment (expression has type "TypeForm[int]", variable has type "TypeForm[str]") [assignment] +typeforms_typeform.py:108: error: Incompatible types in assignment (expression has type "type[int]", variable has type "TypeForm[str]") [assignment] +""" diff --git a/conformance/results/zuban/version.toml b/conformance/results/zuban/version.toml new file mode 100644 index 000000000..ac31d7d9b --- /dev/null +++ b/conformance/results/zuban/version.toml @@ -0,0 +1 @@ +version = "zuban 0.7.2" diff --git a/conformance/scripts/bump_type_checkers.py b/conformance/scripts/bump_type_checkers.py new file mode 100755 index 000000000..195b19663 --- /dev/null +++ b/conformance/scripts/bump_type_checkers.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +""" +Update the conformance checker versions in uv.lock to the latest releases. +""" + +from pathlib import Path +from subprocess import CalledProcessError, run + + +TYPE_CHECKERS = ("mypy", "pyright", "zuban", "pyrefly", "ty", "pycroscope") + + +def main() -> int: + root_dir = Path(__file__).resolve().parents[1] + lock_command = ["uv", "lock", "--python", "3.12"] + for checker in TYPE_CHECKERS: + lock_command.extend(["--upgrade-package", checker]) + + try: + print("+", " ".join(lock_command)) + run(lock_command, cwd=root_dir, check=True) + except CalledProcessError as exc: + print(f"Failed with exit code {exc.returncode}") + return exc.returncode + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/conformance/src/__init__.py b/conformance/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/conformance/src/main.py b/conformance/src/main.py new file mode 100644 index 000000000..005b3bd98 --- /dev/null +++ b/conformance/src/main.py @@ -0,0 +1,294 @@ +""" +Type system conformance test for static type checkers. +""" + +import os +from pathlib import Path +import re +import sys +from time import time +from typing import Sequence + +import tomli +import tomlkit + +from options import parse_options +from reporting import generate_summary +from test_groups import get_test_cases, get_test_groups +from type_checker import TYPE_CHECKERS, TypeChecker + +FULL_OUTPUT_KEY = "__full_output__" + + +def run_tests( + root_dir: Path, + type_checker: TypeChecker, + test_cases: Sequence[Path], + *, + verbose: bool = False, +): + print(f"Running tests for {type_checker.name}") + + test_start_time = time() + tests_output = type_checker.run_tests([file.name for file in test_cases]) + test_duration = time() - test_start_time + + print(f"Completed tests for {type_checker.name} in {test_duration:.2f} seconds") + if verbose: + print(f"Verbose output for {type_checker.name}:") + full_output = tests_output.get(FULL_OUTPUT_KEY) + if full_output: + print("===== raw =====") + print(full_output, end="" if full_output.endswith("\n") else "\n") + else: + for test_name in sorted(tests_output): + print(f"===== {test_name} =====") + output = tests_output[test_name] + if output: + print(output, end="" if output.endswith("\n") else "\n") + print("") + + for test_name, output in tests_output.items(): + if test_name.startswith("__"): + continue + type_checker.parse_errors(output.splitlines()) + + results_dir = root_dir / "results" / type_checker.name + + for test_case in test_cases: + update_output_for_test( + type_checker, results_dir, test_case, tests_output.get(test_case.name, "") + ) + + update_type_checker_info(type_checker, root_dir) + + +def get_expected_errors(test_case: Path) -> tuple[ + dict[int, tuple[int, int]], + dict[str, tuple[list[int], bool]], +]: + """Return the line numbers where type checkers are expected to produce an error. + + The return value is a tuple of two dictionaries: + - The format of the first is {line number: (number of required errors, number of optional errors)}. + - The format of the second is {error tag: ([lines where the error may appear], allow multiple}. + If allow multiple is True, the error may appear on multiple lines; otherwise, it must + appear exactly once. + + For example, the following test case: + + x: int = "x" # E + y: int = "y" # E? + @final # E[final] + def f(): pass # E[final] + + will return: + + ( + {1: (1, 0), 2: (0, 1)}, + {"final": ([3, 4], False)} + ) + """ + with open(test_case, "r", encoding="utf-8") as f: + lines = f.readlines() + output: dict[int, tuple[int, int]] = {} + groups: dict[str, tuple[list[int], bool]] = {} + for i, line in enumerate(lines, start=1): + line_without_comment, *_ = line.split("#") + # Ignore lines with no non-comment content. This allows commenting out test cases. + if not line_without_comment.strip(): + continue + required = 0 + optional = 0 + for match in re.finditer(r"# E\??(?=:|$| )", line): + if match.group() == "# E": + required += 1 + else: + optional += 1 + if required or optional: + output[i] = (required, optional) + for match in re.finditer(r"# E\[([^\]]+)\]", line): + tag = match.group(1) + if tag.endswith("+"): + allow_multiple = True + tag = tag[:-1] + else: + allow_multiple = False + if tag not in groups: + groups[tag] = ([i], allow_multiple) + else: + if groups[tag][1] != allow_multiple: + raise ValueError(f"Error group {tag} has inconsistent allow_multiple value in {test_case}") + groups[tag][0].append(i) + for group, linenos in groups.items(): + if len(linenos) == 1: + raise ValueError(f"Error group {group} only appears on a single line in {test_case}") + return output, groups + + +def diff_expected_errors( + type_checker: TypeChecker, + test_case: Path, + output: str, + ignored_errors: Sequence[str], +) -> str: + """Return a list of errors that were expected but not produced by the type checker.""" + expected_errors, error_groups = get_expected_errors(test_case) + errors = type_checker.parse_errors(output.splitlines()) + if ignored_errors: + errors = { + lineno: [ + error + for error in errors_list + if not any(ignored in error for ignored in ignored_errors)] + for lineno, errors_list in errors.items() + } + errors = {lineno: errors_list for lineno, errors_list in errors.items() if errors_list} + + differences: list[str] = [] + for expected_lineno, (expected_count, _) in expected_errors.items(): + if expected_lineno not in errors and expected_count > 0: + differences.append(f"Line {expected_lineno}: Expected {expected_count} errors") + # We don't report an issue if the count differs, because type checkers may produce + # multiple error messages for a single line. + linenos_used_by_groups: set[int] = set() + for group, (linenos, allow_multiple) in error_groups.items(): + num_errors = sum(1 for lineno in linenos if lineno in errors) + if num_errors == 0: + differences.append(f"Lines {', '.join(map(str, linenos))}: Expected error (tag {group!r})") + elif num_errors == 1 or allow_multiple: + linenos_used_by_groups.update(linenos) + else: + differences.append(f"Lines {', '.join(map(str, linenos))}: Expected exactly one error (tag {group!r})") + for actual_lineno, actual_errors in errors.items(): + if actual_lineno not in expected_errors and actual_lineno not in linenos_used_by_groups: + differences.append(f"Line {actual_lineno}: Unexpected errors {actual_errors}") + return "".join(f"{diff}\n" for diff in differences) + + +def update_output_for_test( + type_checker: TypeChecker, + results_dir: Path, + test_case: Path, + output: str, +): + test_name = test_case.stem + output = f"\n{output}" + + results_file = results_dir / f"{test_name}.toml" + results_file.parent.mkdir(parents=True, exist_ok=True) + should_write = False + + # Read the existing results file if present. + try: + with open(results_file, "rb") as f: + existing_results = tomli.load(f) + except FileNotFoundError: + should_write = True + existing_results = {} + except tomli.TOMLDecodeError: + print(f"Error decoding {results_file}") + existing_results = {} + + ignored_errors = existing_results.get("ignore_errors", []) + errors_diff = "\n" + diff_expected_errors(type_checker, test_case, output, ignored_errors) + old_errors_diff = "\n" + existing_results.get("errors_diff", "") + + if errors_diff != old_errors_diff: + should_write = True + print(f"Result changed for {test_name} when running {type_checker.name}") + print(f"Old output: {old_errors_diff}") + print(f"New output: {errors_diff}") + print("") + + conformance_automated = "Fail" if errors_diff.strip() else "Pass" + if existing_results.get("conformance_automated") != conformance_automated: + should_write = True + existing_results["conformance_automated"] = conformance_automated + + old_output = existing_results.get("output", "") + old_output = f"\n{old_output}" + + # Did the type checker output change since last time the + # test was run? + if old_output != output: + should_write = True + print(f"Output changed for {test_name} when running {type_checker.name}") + print(f"Old output: {old_output}") + print(f"New output: {output}") + print("") + + # Use multiline formatting for any strings that contain newlines. + for key, value in existing_results.items(): + if isinstance(value, str) and "\n" in value: + existing_results[key] = tomlkit.string(f"\n{value}", multiline=True) + + if should_write: + # Always reapply tomlkit.string, or it will turn into a single line. + existing_results["errors_diff"] = tomlkit.string(errors_diff, multiline=True) + existing_results["output"] = tomlkit.string(output, multiline=True) + if "notes" in existing_results: + notes = existing_results["notes"] + if not notes.startswith("\n"): + notes = "\n" + notes + existing_results["notes"] = tomlkit.string(notes, multiline=True) + results_file.parent.mkdir(parents=True, exist_ok=True) + with open(results_file, "w", encoding="utf-8") as f: + tomlkit.dump(existing_results, f) + + +def update_type_checker_info(type_checker: TypeChecker, root_dir: Path): + # Record the version of the type checker used for the latest run. + version_file = root_dir / "results" / type_checker.name / "version.toml" + + # Read the existing version file if present. + try: + with open(version_file, "rb") as f: + existing_info = tomli.load(f) + except FileNotFoundError: + existing_info = {} + except tomli.TOMLDecodeError: + print(f"Error decoding {version_file}") + existing_info = {} + + existing_info["version"] = type_checker.get_version() + + version_file.parent.mkdir(parents=True, exist_ok=True) + with open(version_file, "w") as f: + tomlkit.dump(existing_info, f) + + +def main(): + # Some tests cover features that are available only in the + # latest version of Python (3.12), so we need this version. + assert sys.version_info >= (3, 12) + + options = parse_options(sys.argv[1:]) + + root_dir = Path(__file__).resolve().parent.parent + + if not options.report_only: + tests_dir = root_dir / "tests" + assert tests_dir.is_dir() + + test_groups = get_test_groups(root_dir) + test_cases = get_test_cases(test_groups, tests_dir) + + # Switch to the tests directory. + os.chdir(tests_dir) + + # Run each test case with each type checker. + for type_checker in TYPE_CHECKERS: + if options.only_run and options.only_run != type_checker.name: + continue + if not type_checker.install(): + print(f"Skipping tests for {type_checker.name}") + else: + run_tests(root_dir, type_checker, test_cases, verbose=options.verbose) + + # Generate a summary report. + generate_summary(root_dir) + + +if __name__ == "__main__": + main() diff --git a/conformance/src/options.py b/conformance/src/options.py new file mode 100644 index 000000000..854672776 --- /dev/null +++ b/conformance/src/options.py @@ -0,0 +1,36 @@ +""" +Command-line options for the test tool. +""" + +import argparse +from dataclasses import dataclass + +from type_checker import TYPE_CHECKERS + +@dataclass +class _Options: + report_only: bool + only_run: str | None + verbose: bool + + +def parse_options(argv: list[str]) -> _Options: + parser = argparse.ArgumentParser() + reporting_group = parser.add_argument_group("reporting") + reporting_group.add_argument( + "--report-only", + action="store_true", + help="regenerates the test suite report from past results", + ) + reporting_group.add_argument( + "--only-run", + help="Only runs the type checker", + choices=[tc.name for tc in TYPE_CHECKERS], + ) + parser.add_argument( + "--verbose", + action="store_true", + help="print full output from the type checker", + ) + ret = _Options(**vars(parser.parse_args(argv))) + return ret diff --git a/conformance/src/reporting.py b/conformance/src/reporting.py new file mode 100644 index 000000000..cff2dc465 --- /dev/null +++ b/conformance/src/reporting.py @@ -0,0 +1,122 @@ +""" +Generates a summary of the type checker conformant tests. +""" + +from pathlib import Path + +import tomli + +from test_groups import get_test_cases, get_test_groups +from type_checker import TYPE_CHECKERS + + +def generate_summary(root_dir: Path): + print("Generating summary report") + template_file = root_dir / "src" / "results_template.html" + with open(template_file, "r") as f: + template = f.read() + + summary = template.replace("{{summary}}", generate_summary_html(root_dir)) + + results_file = root_dir / "results" / "results.html" + + with open(results_file, "w") as f: + f.write(summary) + + +def generate_summary_html(root_dir: Path) -> str: + column_count = len(TYPE_CHECKERS) + 1 + test_groups = get_test_groups(root_dir) + test_cases = get_test_cases(test_groups, root_dir / "tests") + + summary_html = ['
'] + summary_html.append('') + + for type_checker in TYPE_CHECKERS: + # Load the version file for the type checker. + version_file = root_dir / "results" / type_checker.name / "version.toml" + + try: + with open(version_file, "rb") as f: + existing_info = tomli.load(f) + except FileNotFoundError: + existing_info = {} + except tomli.TOMLDecodeError: + print(f"Error decoding {version_file}") + existing_info = {} + + version = existing_info["version"] or "Unknown version" + + summary_html.append(f"") + + summary_html.append("") + + for test_group_name, test_group in test_groups.items(): + tests_in_group = [ + case for case in test_cases if case.name.startswith(f"{test_group_name}_") + ] + + tests_in_group.sort(key=lambda x: x.name) + + # Are there any test cases in this group? + if len(tests_in_group) > 0: + summary_html.append(f'") + + for test_case in tests_in_group: + test_case_name = test_case.stem + + summary_html.append(f'') + + for type_checker in TYPE_CHECKERS: + try: + results_file = ( + root_dir + / "results" + / type_checker.name + / f"{test_case_name}.toml" + ) + with open(results_file, "rb") as f: + results = tomli.load(f) + except FileNotFoundError: + results = {} + + raw_notes = results.get("notes", "").strip() + conformance = results.get("conformant", "Unknown") + if conformance == "Unknown": + # Try to look up the automated test results and use + # that if the test passes + automated = results.get("conformance_automated") + if automated == "Pass": + conformance = "Pass" + notes = "".join( + [f"

{note}

" for note in raw_notes.split("\n")] + ) + + conformance_class = ( + "conformant" + if conformance == "Pass" + else "partially-conformant" + if conformance == "Partial" + else "not-conformant" + ) + + # Add an asterisk if there are notes to display for a "Pass". + if raw_notes != "" and conformance == "Pass": + conformance = "Pass*" + + conformance_cell = f"{conformance}" + if raw_notes != "": + conformance_cell = f'
{conformance_cell}{notes}
' + + summary_html.append(f'') + + summary_html.append("") + + summary_html.append("
 
{version}
") + summary_html.append("
') + summary_html.append( + f'{test_group.name}' + ) + summary_html.append("
     {test_case_name}{conformance_cell}
\n") + + return "\n".join(summary_html) diff --git a/conformance/src/results_template.html b/conformance/src/results_template.html new file mode 100644 index 000000000..214f778ed --- /dev/null +++ b/conformance/src/results_template.html @@ -0,0 +1,190 @@ + + + + + + + Type System Test Results + + + + +
+
+

Python Type System Conformance Test Results

+

+ While spec conformance is important for the ecosystem, we don't recommend using it + as the primary basis for choosing a type checker. It is not representative + of many of the things users typically care about. +

+
+ {{summary}} + +
+ + + diff --git a/conformance/src/test_groups.py b/conformance/src/test_groups.py new file mode 100644 index 000000000..b974d4016 --- /dev/null +++ b/conformance/src/test_groups.py @@ -0,0 +1,47 @@ +""" +Reads a template file that describes groups of tests in the +conformance test suite. +""" + +from dataclasses import dataclass +from itertools import chain +from pathlib import Path +from typing import Mapping, Sequence + +import tomli + + +@dataclass +class TestGroup: + name: str + href: str + + +def get_test_groups(root_dir: Path) -> Mapping[str, TestGroup]: + # Read the TOML file that defines the test groups. Each test + # group has a name that associated test cases must start with. + test_group_file = root_dir / "src" / "test_groups.toml" + with open(test_group_file, "rb") as f: + test_groups = tomli.load(f) + + return { + k: TestGroup(v.get("name", "unknown"), v.get("href", "")) + for k, v in test_groups.items() + } + + +def get_test_cases( + test_groups: Mapping[str, TestGroup], tests_dir: Path +) -> Sequence[Path]: + test_group_names = test_groups.keys() + + # Filter test cases based on test group names. Files that do + # not begin with a known test group name are assumed to be + # files that support one or more tests. + test_cases = [ + p + for p in chain(tests_dir.glob("*.py"), tests_dir.glob("*.pyi")) + if p.name.split("_")[0] in test_group_names + ] + + return test_cases diff --git a/conformance/src/test_groups.toml b/conformance/src/test_groups.toml new file mode 100644 index 000000000..adf2b355c --- /dev/null +++ b/conformance/src/test_groups.toml @@ -0,0 +1,92 @@ + +[concepts] +name = "Type system concepts" +href = "https://typing.readthedocs.io/en/latest/spec/concepts.html" + +[annotations] +name = "Type annotations" +href = "https://typing.readthedocs.io/en/latest/spec/annotations.html" + +[typeforms] +name = "Type forms" +href = "https://typing.readthedocs.io/en/latest/spec/type-forms.html" + +[specialtypes] +name = "Special types in annotations" +href = "https://typing.readthedocs.io/en/latest/spec/special-types.html" + +[generics] +name = "Generics" +href = "https://typing.readthedocs.io/en/latest/spec/generics.html" + +[qualifiers] +name = "Type qualifiers" +href = "https://typing.readthedocs.io/en/latest/spec/qualifiers.html" + +[classes] +name = "Class type compatibility" +href = "https://typing.readthedocs.io/en/latest/spec/class-compat.html" + +[aliases] +name = "Type aliases" +href = "https://typing.readthedocs.io/en/latest/spec/aliases.html" + +[literals] +name = "Literals" +href = "https://typing.readthedocs.io/en/latest/spec/literal.html" + +[protocols] +name = "Protocols" +href = "https://typing.readthedocs.io/en/latest/spec/protocol.html" + +[callables] +name = "Callables" +href = "https://typing.readthedocs.io/en/latest/spec/callables.html" + +[constructors] +name = "Constructors" +href = "https://typing.readthedocs.io/en/latest/spec/constructors.html" + +[overloads] +name = "Overloads" +href = "https://typing.readthedocs.io/en/latest/spec/overload.html" + +[exceptions] +name = "Exceptions" +href = "https://typing.readthedocs.io/en/latest/spec/exceptions.html" + +[dataclasses] +name = "Dataclasses" +href = "https://typing.readthedocs.io/en/latest/spec/dataclasses.html" + +[typeddicts] +name = "Typed dictionaries" +href = "https://typing.readthedocs.io/en/latest/spec/typeddict.html" + +[tuples] +name = "Tuples" +href = "https://typing.readthedocs.io/en/latest/spec/tuples.html" + +[namedtuples] +name = "Named tuples" +href = "https://typing.readthedocs.io/en/latest/spec/namedtuples.html" + +[enums] +name = "Enumerations" +href = "https://typing.readthedocs.io/en/latest/spec/enums.html" + +[narrowing] +name = "Type narrowing" +href = "https://typing.readthedocs.io/en/latest/spec/narrowing.html" + +[directives] +name = "Type checker directives" +href = "https://typing.readthedocs.io/en/latest/spec/directives.html" + +[distribution] +name = "Distributing type information" +href = "https://typing.readthedocs.io/en/latest/spec/distributing.html" + +[historical] +name = "Historical and deprecated features" +href = "https://typing.readthedocs.io/en/latest/spec/historical.html" diff --git a/conformance/src/type_checker.py b/conformance/src/type_checker.py new file mode 100644 index 000000000..c7f28b5c7 --- /dev/null +++ b/conformance/src/type_checker.py @@ -0,0 +1,505 @@ +""" +Classes that abstract differences between type checkers. +""" + +import json +import os +from pathlib import Path +import re +import shutil +import sys +import sysconfig +from abc import ABC, abstractmethod +from subprocess import PIPE, CalledProcessError, run +from typing import Sequence + +CONFORMANCE_ROOT = Path(__file__).resolve().parent.parent + + +class TypeChecker(ABC): + @property + @abstractmethod + def name(self) -> str: + """ + Returns the name of the type checker. + """ + raise NotImplementedError + + @abstractmethod + def install(self) -> bool: + """ + Ensures that the type checker is available in the current environment. + Returns False if it cannot be executed. + """ + raise NotImplementedError + + @abstractmethod + def get_version(self) -> str: + """ + Returns the current version string for the type checker. + """ + raise NotImplementedError + + @abstractmethod + def run_tests(self, test_files: Sequence[str]) -> dict[str, str]: + """ + Runs the type checker on the specified test file and + returns the output. + """ + raise NotImplementedError + + @abstractmethod + def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: + """ + Parses type checker output to summarize the lines on which errors occurred. + """ + raise NotImplementedError + + +class MypyTypeChecker(TypeChecker): + @property + def name(self) -> str: + return "mypy" + + def install(self) -> bool: + try: + # Delete the cache for consistent timings. + shutil.rmtree(".mypy_cache") + except (shutil.Error, OSError): + # Ignore any errors here. + pass + + try: + # Run "mypy --version" to ensure that it's available and to work + # around timing issues caused by malware scanners on some systems. + self.get_version() + return True + except (CalledProcessError, FileNotFoundError): + print( + "Unable to run mypy. Install conformance dependencies with " + "'uv sync --frozen' from the conformance directory." + ) + return False + + def get_version(self) -> str: + proc = run( + [sys.executable, "-m", "mypy", "--version"], + check=True, + stdout=PIPE, + text=True, + ) + version = proc.stdout.strip() + + # Remove the " (compiled)" if it's present. + version = version.split(" (")[0] + return version + + def run_tests(self, test_files: Sequence[str]) -> dict[str, str]: + command = [ + sys.executable, + "-m", + "mypy", + ".", + "--enable-error-code", + "deprecated", + "--enable-incomplete-feature=TypeForm", + ] + proc = run(command, stdout=PIPE, text=True, encoding="utf-8") + lines = proc.stdout.split("\n") + + # Add results to a dictionary keyed by the file name. + results_dict: dict[str, str] = {} + for line in lines: + file_name = line.split(":")[0].strip() + results_dict[file_name] = results_dict.get(file_name, "") + line + "\n" + + return results_dict + + def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: + # narrowing_typeguard.py:102: error: TypeGuard functions must have a positional argument [valid-type] + line_to_errors: dict[int, list[str]] = {} + for line in output: + if line.count(":") < 3: + continue + _, lineno, kind, _ = line.split(":", maxsplit=3) + kind = kind.strip() + if kind != "error": + continue + line_to_errors.setdefault(int(lineno), []).append(line) + return line_to_errors + + +class PyrightTypeChecker(TypeChecker): + @property + def name(self) -> str: + return "pyright" + + def install(self) -> bool: + try: + # Force the Python wrapper to install node if needed + # and use the locked version of pyright. + self.get_version() + return True + except (CalledProcessError, FileNotFoundError): + print( + "Unable to run pyright. Install conformance dependencies with " + "'uv sync --frozen' from the conformance directory." + ) + return False + + def get_version(self) -> str: + proc = run( + [sys.executable, "-m", "pyright", "--version"], + check=True, + stdout=PIPE, + text=True, + ) + return self._parse_version(proc.stdout) + + @staticmethod + def _parse_version(output: str) -> str: + # pyright --version can print an update message ("there is a new pyright version available"), + # make sure we extract only the actual version + for line in output.splitlines(): + if line.startswith("pyright "): + return line + return output.strip() + + def run_tests(self, test_files: Sequence[str]) -> dict[str, str]: + command = [sys.executable, "-m", "pyright", ".", "--outputjson"] + proc = run(command, stdout=PIPE, text=True, encoding="utf-8") + output_json = json.loads(proc.stdout) + diagnostics = output_json["generalDiagnostics"] + + # Add results to a dictionary keyed by the file name. + results_dict: dict[str, str] = {} + for diagnostic in diagnostics: + file_path = Path(diagnostic.get("file", "")) + file_name = file_path.name + line_number = diagnostic["range"]["start"]["line"] + 1 + col_number = diagnostic["range"]["start"]["character"] + 1 + severity = diagnostic["severity"] + message = diagnostic["message"] + rule = f" ({diagnostic['rule']})" if "rule" in diagnostic else "" + + line_text = f"{file_name}:{line_number}:{col_number} - {severity}: {message}{rule}\n" + results_dict[file_name] = results_dict.get(file_name, "") + line_text + + return results_dict + + def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: + # narrowing_typeguard.py:102:9 - error: User-defined type guard functions and methods must have at least one input parameter (reportGeneralTypeIssues) + line_to_errors: dict[int, list[str]] = {} + for line in output: + # Ignore indented notes + if not line or line[0].isspace(): + continue + assert line.count(":") >= 3, f"Failed to parse line: {line!r}" + _, lineno, kind, _ = line.split(":", maxsplit=3) + kind = kind.split()[-1] + if kind not in ("error", "warning"): + continue + line_to_errors.setdefault(int(lineno), []).append(line) + return line_to_errors + + +class TyTypeChecker(TypeChecker): + @property + def name(self) -> str: + return "ty" + + def install(self) -> bool: + try: + self.get_version() + return True + except (CalledProcessError, FileNotFoundError): + print( + "Unable to run ty. Install conformance dependencies with " + "'uv sync --frozen' from the conformance directory." + ) + return False + + def get_version(self) -> str: + proc = run([sys.executable, "-m", "ty", "--version"], stdout=PIPE, text=True) + return proc.stdout.split("(")[0].strip() + + def run_tests(self, test_files: Sequence[str]) -> dict[str, str]: + command = [ + sys.executable, + "-m", + "ty", + "check", + ".", + "--output-format=concise", + "--color=never", + "--config-file=./ty.toml", + ] + proc = run(command, stdout=PIPE, text=True, encoding="utf-8") + results_dict = {} + for line in proc.stdout.splitlines(): + if not line.strip(): + continue + file_name = line.split(":")[0].strip() + results_dict[file_name] = results_dict.get(file_name, "") + line + "\n" + return results_dict + + def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: + # narrowing_typeguard.py:102:23: error[invalid-type-guard-definition] `TypeGuard` function must have a parameter to narrow + line_to_errors: dict[int, list[str]] = {} + for line in output: + line = line.strip() + if ( + not line + or line == "All checks passed!" + or re.fullmatch(r"Found \d+ diagnostics?", line) + ): + continue + assert line.count(":") >= 3, f"Failed to parse line: {line!r}" + _, lineno, _, rest = line.split(":", maxsplit=3) + kind = rest.split("[")[0].strip() + if kind != "error": + continue + line_to_errors.setdefault(int(lineno), []).append(line) + return line_to_errors + + +class ZubanLSTypeChecker(MypyTypeChecker): + @property + def name(self) -> str: + return "zuban" + + def install(self) -> bool: + try: + self.get_version() + return True + except (CalledProcessError, FileNotFoundError): + print( + "Unable to run zuban. Install conformance dependencies with " + "'uv sync --frozen' from the conformance directory." + ) + return False + + def get_version(self) -> str: + proc = run(["zuban", "--version"], check=True, stdout=PIPE, text=True) + return proc.stdout.strip() + + def run_tests(self, test_files: Sequence[str]) -> dict[str, str]: + command = [ + "zuban", + "check", + ".", + "--enable-error-code", + "deprecated", + ] + proc = run(command, stdout=PIPE, text=True, encoding="utf-8") + lines = proc.stdout.split("\n") + + # Add results to a dictionary keyed by the file name. + results_dict: dict[str, str] = {} + for line in lines: + file_name = line.split(":")[0].strip() + results_dict[file_name] = results_dict.get(file_name, "") + line + "\n" + + return results_dict + + def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: + # narrowing_typeguard.py:102: error: TypeGuard functions must have a positional argument [valid-type] + line_to_errors: dict[int, list[str]] = {} + for line in output: + if line.count(":") < 3: + continue + _, lineno, kind, _ = line.split(":", maxsplit=3) + kind = kind.strip() + if kind != "error": + continue + line_to_errors.setdefault(int(lineno), []).append(line) + return line_to_errors + + +class PyreflyTypeChecker(TypeChecker): + @property + def name(self) -> str: + return "pyrefly" + + def install(self) -> bool: + try: + self.get_version() + return True + except (CalledProcessError, FileNotFoundError): + print( + "Unable to run pyrefly. Install conformance dependencies with " + "'uv sync --frozen' from the conformance directory." + ) + return False + + def get_version(self) -> str: + proc = run(["pyrefly", "--version"], check=True, stdout=PIPE, text=True) + version = proc.stdout.strip() + return version + + def run_tests(self, test_files: Sequence[str]) -> dict[str, str]: + proc = run( + ["pyrefly", "check", "--output-format", "min-text", "--summary=none", "--min-severity=warn"], + stdout=PIPE, + text=True, + encoding="utf-8", + ) + lines = proc.stdout.split("\n") + + # Add results to a dictionary keyed by the file name. + results_dict: dict[str, str] = {} + for line in lines: + if not line.strip(): + continue + if line.startswith(" INFO "): + continue + # Extract the absolute path reported by pyrefly and convert it to a + # stable relative path (filename only) so results are consistent. + # Example input line: + # "ERROR /abs/.../conformance/tests/foo.py:12:3-5: message [code]" + # We replace the absolute path with just "foo.py". + try: + abs_path = line.split(":", 1)[0].strip().split(" ", 1)[1].strip() + except IndexError: + # If parsing fails, fall back to original line and grouping. + abs_path = "" + file_name = Path(abs_path).name if abs_path else line.split(":")[0] + + # Replace only the first occurrence to avoid touching the message text. + display_line = line.replace(abs_path, file_name, 1) if abs_path else line + results_dict[file_name] = ( + results_dict.get(file_name, "") + display_line + "\n" + ) + return results_dict + + def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: + line_to_errors: dict[int, list[str]] = {} + for line in output: + # Ignore multi-line errors + if ".py:" not in line and ".pyi:" not in line: + continue + # Ignore reveal_type errors + if "revealed type: " in line: + continue + assert line.count(":") >= 3, f"Failed to parse line: {line!r}" + _, lineno, _, error_msg = line.split(":", maxsplit=3) + line_to_errors.setdefault(int(lineno), []).append(error_msg.strip()) + return line_to_errors + + +class PycroscopeTypeChecker(TypeChecker): + @property + def name(self) -> str: + return "pycroscope" + + def install(self) -> bool: + try: + self.get_version() + return True + except (CalledProcessError, FileNotFoundError): + print( + "Unable to run pycroscope. Install conformance dependencies with " + "'uv sync --frozen' from the conformance directory." + ) + return False + + def get_version(self) -> str: + proc = run([self._command(), "--version"], stdout=PIPE, text=True, check=True) + return proc.stdout.strip() + + @staticmethod + def _command() -> str: + executable = "pycroscope.exe" if sys.platform == "win32" else "pycroscope" + return str(Path(sysconfig.get_path("scripts")) / executable) + + @staticmethod + def _normalize_output_line(line: str) -> str: + line = line.replace(str(CONFORMANCE_ROOT), "...") + line = re.sub(r"", r"", line) + # Pycroscope can include object reprs with process-specific addresses + # (e.g. "... at 0x10abc1234>"). Normalize these for stable snapshots. + return re.sub(r"0x[0-9a-fA-F]+", "0x...", line) + + def run_tests(self, test_files: Sequence[str]) -> dict[str, str]: + command = [ + self._command(), + ".", + "--output-format", + "concise", + "--disable", + "import_failed", + "--disable", + "unused_variable", + "--disable", + "unused_assignment", + "--disable", + "must_use", + "--enable", + "invalid_literal", + "--enable", + "incompatible_override", + "--enable", + "classvar_type_parameters", + ] + proc = run( + command, + stdout=PIPE, + stderr=PIPE, + text=True, + encoding="utf-8", + env={**os.environ, "PYTHONPATH": "."}, + ) + lines = proc.stderr.splitlines() + full_output_lines: list[str] = [] + + # Collect results per file and sort for deterministic output. + sortable_results: dict[str, list[tuple[int, str, str]]] = {} + for line in lines: + if not line.strip(): + continue + line = self._normalize_output_line(line) + full_output_lines.append(line) + # Concise output line format: + # file.py:12:3: Message text [error_code] + match = re.match(r"^(.+?):(\d+)(?::\d+)?:\s(.*)$", line) + if not match: + continue + file_name = Path(match.group(1)).name + lineno = int(match.group(2)) + message = match.group(3) + sortable_results.setdefault(file_name, []).append((lineno, message, line)) + + results_dict: dict[str, str] = {} + for file_name, entries in sortable_results.items(): + entries.sort(key=lambda item: (item[0], item[1])) + results_dict[file_name] = "".join(f"{line}\n" for _, _, line in entries) + if full_output_lines: + results_dict["__full_output__"] = "".join( + f"{line}\n" for line in full_output_lines + ) + return results_dict + + def parse_errors(self, output: Sequence[str]) -> dict[int, list[str]]: + line_to_errors: dict[int, list[str]] = {} + for line in output: + if not line.strip(): + continue + line = self._normalize_output_line(line) + # reveal_type diagnostics are informational for conformance purposes. + if "[reveal_type]" in line or "Revealed type is " in line: + continue + match = re.match(r"^.+?:(\d+)(?::\d+)?:\s", line) + if not match: + continue + line_to_errors.setdefault(int(match.group(1)), []).append(line) + return line_to_errors + + +TYPE_CHECKERS: Sequence[TypeChecker] = ( + MypyTypeChecker(), + PyrightTypeChecker(), + ZubanLSTypeChecker(), + PyreflyTypeChecker(), + PycroscopeTypeChecker(), + TyTypeChecker(), +) diff --git a/conformance/src/unexpected_fails.py b/conformance/src/unexpected_fails.py new file mode 100644 index 000000000..0e8453b19 --- /dev/null +++ b/conformance/src/unexpected_fails.py @@ -0,0 +1,32 @@ +""" + +Helper script to find test cases where the automated and manual +conformance results differ. + +""" + +from pathlib import Path +import tomllib + +results_dir = Path(__file__).resolve().parent.parent / "results" + +for type_checker_dir in sorted(results_dir.iterdir()): + if type_checker_dir.is_dir(): + for file in sorted(type_checker_dir.iterdir()): + if file.name == "version.toml": + continue + with file.open("rb") as f: + try: + info = tomllib.load(f) + except Exception as e: + raise Exception(f"Error decoding {file}") from e + try: + new_pass = info["conformance_automated"] == "Pass" + if new_pass and "conformant" not in info: + previous_pass = True + else: + previous_pass = info["conformant"] == "Pass" + except KeyError as e: + raise Exception(f"Missing key in {file}") from e + if previous_pass != new_pass: + print(f"{file.relative_to(results_dir)}: {info['conformant']} vs. {info['conformance_automated']}") diff --git a/conformance/src/validate_results.py b/conformance/src/validate_results.py new file mode 100644 index 000000000..5db7bff27 --- /dev/null +++ b/conformance/src/validate_results.py @@ -0,0 +1,105 @@ +""" +Validate invariants for conformance result files. +""" + +from pathlib import Path +import sys +import tomllib +from typing import Any + +ALLOWED_RESULT_KEYS = frozenset( + { + "conformance_automated", + "conformant", + "errors_diff", + "ignore_errors", + "notes", + "output", + } +) + + +def main() -> int: + results_dir = Path(__file__).resolve().parent.parent / "results" + issues: list[str] = [] + checked = 0 + + for type_checker_dir in sorted(results_dir.iterdir()): + if not type_checker_dir.is_dir(): + continue + for file in sorted(type_checker_dir.iterdir()): + if file.name == "version.toml": + continue + checked += 1 + try: + with file.open("rb") as f: + info = tomllib.load(f) + except Exception as e: + issues.append(f"{file.relative_to(results_dir)}: failed to parse TOML ({e})") + continue + + issues.extend(_validate_result(file, results_dir, info)) + + if issues: + print(f"Found {len(issues)} invariant violation(s) across {checked} file(s):") + for issue in issues: + print(f"- {issue}") + return 1 + + print(f"Validated {checked} conformance result file(s); no invariant violations found.") + return 0 + + +def _validate_result(file: Path, results_dir: Path, info: dict[str, Any]) -> list[str]: + issues: list[str] = [] + rel_path = file.relative_to(results_dir) + + unknown_keys = sorted(set(info) - ALLOWED_RESULT_KEYS) + if unknown_keys: + issues.append( + f"{rel_path}: unrecognized key(s): {', '.join(repr(key) for key in unknown_keys)}" + ) + + automated = info.get("conformance_automated") + if automated not in {"Pass", "Fail"}: + issues.append( + f"{rel_path}: conformance_automated must be 'Pass' or 'Fail' (got {automated!r})" + ) + return issues + automated_is_pass = automated == "Pass" + + conformant = info.get("conformant") + if conformant is None: + if automated_is_pass: + conformant_is_pass = True + else: + issues.append( + f"{rel_path}: conformant is required when conformance_automated is 'Fail'" + ) + return issues + elif isinstance(conformant, str): + if conformant not in ("Pass", "Partial", "Unsupported"): + issues.append(f"{rel_path}: invalid conformance status {conformant!r}") + conformant_is_pass = conformant == "Pass" + else: + issues.append(f"{rel_path}: conformant must be a string when present") + return issues + + if conformant_is_pass != automated_is_pass: + issues.append( + f"{rel_path}: conformant={conformant!r} does not match " + f"conformance_automated={automated!r}" + ) + + if conformant == "Partial": + notes = info.get("notes", "") + if not isinstance(notes, str) or not notes.strip(): + issues.append( + f"{rel_path}: notes must be present when checker is not fully conformant" + ) + + return issues + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/conformance/tests/_directives_deprecated_library.py b/conformance/tests/_directives_deprecated_library.py new file mode 100644 index 000000000..454f5d69e --- /dev/null +++ b/conformance/tests/_directives_deprecated_library.py @@ -0,0 +1,51 @@ +""" +Support module for directive_deprecated. +""" + +from typing import Self, overload +from typing_extensions import deprecated + + +@deprecated("Use Spam instead") +class Ham: + ... + + +@deprecated("It is pining for the fjords") +def norwegian_blue(x: int) -> int: + ... + + +@overload +@deprecated("Only str will be allowed") +def foo(x: int) -> str: + ... + + +@overload +def foo(x: str) -> str: + ... + + +def foo(x: int | str) -> str: + ... + + +class Spam: + @deprecated("There is enough spam in the world") + def __add__(self, other: object) -> Self: + ... + + @property + @deprecated("All spam will be equally greasy") + def greasy(self) -> float: + ... + + @property + def shape(self) -> str: + ... + + @shape.setter + @deprecated("Shapes are becoming immutable") + def shape(self, value: str) -> None: + ... diff --git a/conformance/tests/_enums_member_values.py b/conformance/tests/_enums_member_values.py new file mode 100644 index 000000000..c51259fe9 --- /dev/null +++ b/conformance/tests/_enums_member_values.py @@ -0,0 +1,3 @@ +""" +Dummy implementation _enums_member_values stub. +""" diff --git a/conformance/tests/_enums_member_values.pyi b/conformance/tests/_enums_member_values.pyi new file mode 100644 index 000000000..4778f266f --- /dev/null +++ b/conformance/tests/_enums_member_values.pyi @@ -0,0 +1,15 @@ +""" +Support stub file for enums_member_values test. +""" + +from enum import Enum + +# > If the literal values for enum members are not supplied, as they sometimes +# > are not within a type stub file, a type checker can use the type of the +# > _value_ attribute. + +class ColumnType(Enum): + _value_: int + DORIC = ... + IONIC = ... + CORINTHIAN = ... diff --git a/conformance/tests/_enums_members.py b/conformance/tests/_enums_members.py new file mode 100644 index 000000000..3a6d43227 --- /dev/null +++ b/conformance/tests/_enums_members.py @@ -0,0 +1,3 @@ +""" +Dummy implementation for stub file for enums_members test. +""" diff --git a/conformance/tests/_enums_members.pyi b/conformance/tests/_enums_members.pyi new file mode 100644 index 000000000..73f3cdc56 --- /dev/null +++ b/conformance/tests/_enums_members.pyi @@ -0,0 +1,15 @@ +""" +Support stub file for enums_members test. +""" + +from enum import Enum + +# > Within a type stub, members can be defined using the actual runtime values, +# > or a placeholder of ... can be used + +class Pet2(Enum): + genus: str # Non-member attribute + species: str # Non-member attribute + + CAT = ... # Member attribute + DOG = ... # Member attribute diff --git a/conformance/tests/_protocols_modules1.py b/conformance/tests/_protocols_modules1.py new file mode 100644 index 000000000..14e4bdd57 --- /dev/null +++ b/conformance/tests/_protocols_modules1.py @@ -0,0 +1,7 @@ +""" +Support file for protocols_modules.py test. +""" + +timeout = 100 +one_flag = True +other_flag = False diff --git a/conformance/tests/_protocols_modules2.py b/conformance/tests/_protocols_modules2.py new file mode 100644 index 000000000..edba162f5 --- /dev/null +++ b/conformance/tests/_protocols_modules2.py @@ -0,0 +1,11 @@ +""" +Support file for protocols_modules.py test. +""" + + +def on_error(x: int) -> None: + ... + + +def on_success() -> None: + ... diff --git a/conformance/tests/_qualifiers_final_annotation_1.py b/conformance/tests/_qualifiers_final_annotation_1.py new file mode 100644 index 000000000..b2521b19c --- /dev/null +++ b/conformance/tests/_qualifiers_final_annotation_1.py @@ -0,0 +1,7 @@ +""" +Used as part of the test for the typing.Final special form. +""" + +from typing import Final + +TEN: Final[int] = 10 diff --git a/conformance/tests/_qualifiers_final_annotation_2.py b/conformance/tests/_qualifiers_final_annotation_2.py new file mode 100644 index 000000000..603d8bcb6 --- /dev/null +++ b/conformance/tests/_qualifiers_final_annotation_2.py @@ -0,0 +1,7 @@ +""" +Used as part of the test for the typing.Final special form. +""" + +from typing import Final + +PI: Final = 3.14 diff --git a/conformance/tests/_qualifiers_final_decorator.pyi b/conformance/tests/_qualifiers_final_decorator.pyi new file mode 100644 index 000000000..63577adb8 --- /dev/null +++ b/conformance/tests/_qualifiers_final_decorator.pyi @@ -0,0 +1,29 @@ +""" +Support stub file for @final tests. +""" + +from typing import final, overload + + +class Base3: + # > For overloaded methods, @final should be placed on the implementation + # > (or on the first overload, for stubs): + @final + @overload + def method(self, x: int) -> int: + ... + + @overload + def method(self, x: str) -> str: + ... + +class Base4: + # (Swap the order of overload and final decorators.) + @overload + @final + def method(self, x: int) -> int: + ... + + @overload + def method(self, x: str) -> str: + ... diff --git a/conformance/tests/aliases_explicit.py b/conformance/tests/aliases_explicit.py new file mode 100644 index 000000000..a9b3fca03 --- /dev/null +++ b/conformance/tests/aliases_explicit.py @@ -0,0 +1,102 @@ +""" +Tests explicit type aliases defined with `TypeAlias`. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/aliases.html#typealias + +from typing import Any, Callable, Concatenate, Literal, ParamSpec, TypeVar, Union, assert_type +from typing import TypeAlias as TA + +S = TypeVar("S") +T = TypeVar("T") +P = ParamSpec("P") +R = TypeVar("R") + +GoodTypeAlias1: TA = Union[int, str] +GoodTypeAlias2: TA = int | None +GoodTypeAlias3: TA = list[GoodTypeAlias2] +GoodTypeAlias4: TA = list[T] +GoodTypeAlias5: TA = tuple[T, ...] | list[T] +GoodTypeAlias6: TA = tuple[int, int, S, T] +GoodTypeAlias7: TA = Callable[..., int] +GoodTypeAlias8: TA = Callable[[int, T], T] +GoodTypeAlias9: TA = Callable[Concatenate[int, P], R] +GoodTypeAlias10: TA = Any +GoodTypeAlias11: TA = GoodTypeAlias1 | GoodTypeAlias2 | list[GoodTypeAlias4[int]] +GoodTypeAlias12: TA = Callable[P, None] +GoodTypeAlias13: TA = "int | str" +GoodTypeAlias14: TA = list["int | str"] +GoodTypeAlias15: TA = Literal[3, 4, 5, None] + + +def good_type_aliases( + p1: GoodTypeAlias1, + p2: GoodTypeAlias2, + p3: GoodTypeAlias3, + p4: GoodTypeAlias4[int], + p5: GoodTypeAlias5[str], + p6: GoodTypeAlias6[int, str], + p7: GoodTypeAlias7, + p8: GoodTypeAlias8[str], + p9: GoodTypeAlias9[[str, str], None], + p10: GoodTypeAlias10, + p11: GoodTypeAlias11, + p12: GoodTypeAlias12, + p13: GoodTypeAlias13, + p14: GoodTypeAlias14, + p15: GoodTypeAlias15, +): + assert_type(p1, int | str) + assert_type(p2, int | None) + assert_type(p3, list[int | None]) + assert_type(p4, list[int]) + assert_type(p5, tuple[str, ...] | list[str]) + assert_type(p6, tuple[int, int, int, str]) + assert_type(p7, Callable[..., int]) + assert_type(p8, Callable[[int, str], str]) + assert_type(p9, Callable[[int, str, str], None]) + assert_type(p10, Any) + assert_type(p11, int | str | None | list[list[int]]) + assert_type(p12, Callable[..., None]) + assert_type(p13, int | str) + assert_type(p14, list[int | str]) + assert_type(p15, Literal[3, 4, 5, None]) + + +def good_type_aliases_used_badly( + p1: GoodTypeAlias2[int], # E: type alias is not generic + p2: GoodTypeAlias3[int], # E: type alias is already specialized + p3: GoodTypeAlias4[int, int], # E: too many type arguments + p4: GoodTypeAlias8[int, int], # E: too many type arguments + p5: GoodTypeAlias9[int, int], # E: bad type argument for ParamSpec +): + pass + + +var1 = 3 + +# The following should not be allowed as type aliases. +BadTypeAlias1: TA = eval("".join(map(chr, [105, 110, 116]))) # E +BadTypeAlias2: TA = [int, str] # E +BadTypeAlias3: TA = ((int, str),) # E +BadTypeAlias4: TA = [int for i in range(1)] # E +BadTypeAlias5: TA = {"a": "b"} # E +BadTypeAlias6: TA = (lambda: int)() # E +BadTypeAlias7: TA = [int][0] # E +BadTypeAlias8: TA = int if 1 < 3 else str # E +BadTypeAlias9: TA = var1 # E +BadTypeAlias10: TA = True # E +BadTypeAlias11: TA = 1 # E +BadTypeAlias12: TA = list or set # E +BadTypeAlias13: TA = f"{'int'}" # E + + +ListAlias: TA = list +ListOrSetAlias: TA = list | set + +x1: list[str] = ListAlias() # OK +assert_type(x1, list[str]) + +x2: ListAlias[int] # E: already specialized +x3 = ListOrSetAlias() # E: cannot instantiate union +x4: ListOrSetAlias[int] # E: already specialized diff --git a/conformance/tests/aliases_implicit.py b/conformance/tests/aliases_implicit.py new file mode 100644 index 000000000..736c19847 --- /dev/null +++ b/conformance/tests/aliases_implicit.py @@ -0,0 +1,135 @@ +""" +Tests traditional implicit type aliases. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/aliases.html + +from collections.abc import Iterable +from typing import Any, Callable, Concatenate, ParamSpec, TypeVar, Union, assert_type + +TFloat = TypeVar("TFloat", bound=float) +Vector = Iterable[tuple[TFloat, TFloat]] + + +def in_product(v: Vector[TFloat]) -> Iterable[TFloat]: + return [x for x, _ in v] + + +def dilate(v: Vector[float], scale: float) -> Vector[float]: + return ((x * scale, y * scale) for x, y in v) + + +# > Type aliases may be as complex as type hints in annotations – anything +# > that is acceptable as a type hint is acceptable in a type alias. + +S = TypeVar("S") +T = TypeVar("T") +P = ParamSpec("P") +R = TypeVar("R") + +GoodTypeAlias1 = Union[int, str] +GoodTypeAlias2 = int | None +GoodTypeAlias3 = list[GoodTypeAlias2] +GoodTypeAlias4 = list[T] +GoodTypeAlias5 = tuple[T, ...] | list[T] +GoodTypeAlias6 = tuple[int, int, S, T] +GoodTypeAlias7 = Callable[..., int] +GoodTypeAlias8 = Callable[[int, T], T] +GoodTypeAlias9 = Callable[Concatenate[int, P], R] +GoodTypeAlias10 = Any +GoodTypeAlias11 = GoodTypeAlias1 | GoodTypeAlias2 | list[GoodTypeAlias4[int]] +GoodTypeAlias12 = list[TFloat] +GoodTypeAlias13 = Callable[P, None] + + +def good_type_aliases( + p1: GoodTypeAlias1, + p2: GoodTypeAlias2, + p3: GoodTypeAlias3, + p4: GoodTypeAlias4[int], + p5: GoodTypeAlias5[str], + p6: GoodTypeAlias6[int, str], + p7: GoodTypeAlias7, + p8: GoodTypeAlias8[str], + p9: GoodTypeAlias9[[str, str], None], + p10: GoodTypeAlias10, + p11: GoodTypeAlias11, + p12: GoodTypeAlias12[bool], + p13: GoodTypeAlias13 +): + assert_type(p1, int | str) + assert_type(p2, int | None) + assert_type(p3, list[int | None]) + assert_type(p4, list[int]) + assert_type(p5, tuple[str, ...] | list[str]) + assert_type(p6, tuple[int, int, int, str]) + assert_type(p7, Callable[..., int]) + assert_type(p8, Callable[[int, str], str]) + assert_type(p9, Callable[[int, str, str], None]) + assert_type(p10, Any) + assert_type(p11, int | str | None | list[list[int]]) + assert_type(p12, list[bool]) + assert_type(p13, Callable[..., None]) + + +def good_type_aliases_used_badly( + p1: GoodTypeAlias2[int], # E: type alias is not generic + p2: GoodTypeAlias3[int], # E: type alias is already specialized + p3: GoodTypeAlias4[int, int], # E: too many type arguments + p4: GoodTypeAlias8[int, int], # E: too many type arguments + p5: GoodTypeAlias9[int, int], # E: bad type argument for ParamSpec + p6: GoodTypeAlias12[str], # E: type argument doesn't match bound +): + pass + + +var1 = 3 + +# The following should not be considered type aliases. +BadTypeAlias1 = eval("".join(map(chr, [105, 110, 116]))) +BadTypeAlias2 = [int, str] +BadTypeAlias3 = ((int, str),) +BadTypeAlias4 = [int for i in range(1)] +BadTypeAlias5 = {"a": "b"} +BadTypeAlias6 = (lambda: int)() +BadTypeAlias7 = [int][0] +BadTypeAlias8 = int if 1 < 3 else str +BadTypeAlias9 = var1 +BadTypeAlias10 = True +BadTypeAlias11 = 1 +BadTypeAlias12 = list or set +BadTypeAlias13 = f"int" +BadTypeAlias14 = "int | str" + + +def bad_type_aliases( + p1: BadTypeAlias1, # E: Invalid type annotation + p2: BadTypeAlias2, # E: Invalid type annotation + p3: BadTypeAlias3, # E: Invalid type annotation + p4: BadTypeAlias4, # E: Invalid type annotation + p5: BadTypeAlias5, # E: Invalid type annotation + p6: BadTypeAlias6, # E: Invalid type annotation + p7: BadTypeAlias7, # E: Invalid type annotation + p8: BadTypeAlias8, # E: Invalid type annotation + p9: BadTypeAlias9, # E: Invalid type annotation + p10: BadTypeAlias10, # E: Invalid type annotation + p11: BadTypeAlias11, # E: Invalid type annotation + p12: BadTypeAlias12, # E: Invalid type annotation + p13: BadTypeAlias13, # E: Invalid type annotation + p14: BadTypeAlias14, # E: Invalid type annotation +): + pass + + +ListAlias = list +ListOrSetAlias = list | set + +x1: list[str] = ListAlias() # OK +assert_type(x1, list[str]) + +x2 = ListAlias[int]() # OK +assert_type(x2, list[int]) + +x3 = ListOrSetAlias() # E: cannot instantiate union + +x4: ListOrSetAlias[int] # E: already specialized diff --git a/conformance/tests/aliases_newtype.py b/conformance/tests/aliases_newtype.py new file mode 100644 index 000000000..58baeb8af --- /dev/null +++ b/conformance/tests/aliases_newtype.py @@ -0,0 +1,65 @@ +""" +Tests the `typing.NewType` type constructor. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/aliases.html#newtype + +from typing import Any, Hashable, Literal, NewType, TypeVar, TypedDict, assert_type + +UserId = NewType("UserId", int) + +UserId("user") # E: incorrect type +u1: UserId = 42 # E: incorrect type +u2: UserId = UserId(42) # OK + +assert_type(UserId(5) + 1, int) + +# > NewType('Derived', Base) returns a dummy object +_: type = UserId # E: `NewType()` does not return an instance of `type` + +# > Both ``isinstance`` and ``issubclass``, as well as subclassing will fail +# > for ``NewType('Derived', Base)``, since the object returned by a call to +# > ``NewType`` is not a class. +isinstance(u2, UserId) # E: not allowed in isinstance call + + +class UserIdDerived(UserId): # E: subclassing not allowed + pass + + +# > NewType accepts exactly two arguments: a name for the new unique type, +# > and a base class. The latter should be a proper class (i.e., not a type +# > construct like Union, etc.), or another unique type created by +# > calling NewType. + +GoodName = NewType("BadName", int) # E: assigned name does not match + +GoodNewType1 = NewType("GoodNewType1", list) # OK + +GoodNewType2 = NewType("GoodNewType2", GoodNewType1) # OK + +nt1: GoodNewType1[int] # E: NewType cannot be generic + +TypeAlias1 = dict[str, str] +GoodNewType3 = NewType("GoodNewType3", TypeAlias1) + + +BadNewType1 = NewType("BadNewType1", int | str) # E: cannot be generic + +T = TypeVar("T") +BadNewType2 = NewType("BadNewType2", list[T]) # E: cannot be generic + +BadNewType3 = NewType("BadNewType3", Hashable) # E: cannot be protocol + +BadNewType4 = NewType("BadNewType4", Literal[7]) # E: literal not allowed + + +class TD1(TypedDict): + a: int + + +BadNewType5 = NewType("BadNewType5", TD1) # E: cannot be TypedDict + +BadNewType6 = NewType("BadNewType6", int, int) # E: too many arguments + +BadNewType7 = NewType("BadNewType7", Any) # E: cannot be Any diff --git a/conformance/tests/aliases_recursive.py b/conformance/tests/aliases_recursive.py new file mode 100644 index 000000000..b36eedf5e --- /dev/null +++ b/conformance/tests/aliases_recursive.py @@ -0,0 +1,75 @@ +""" +Tests recursive (self-referential) type aliases. +""" + +# The typing specification doesn't mandate support for recursive +# (self-referential) type aliases prior to PEP 695, but it also +# doesn't indicates that they shouldn't work. +# Most type checkers now support them, and many libraries and code +# bases have started to rely on them. +# PEP 695 formally mandates that recursive type aliases work. + +from typing import Mapping, TypeAlias, TypeVar, Union + +Json = Union[None, int, str, float, list["Json"], dict[str, "Json"]] + +j1: Json = [1, {"a": 1}] # OK +j2: Json = 3.4 # OK +j3: Json = [1.2, None, [1.2, [""]]] # OK +j4: Json = {"a": 1, "b": 3j} # E: incompatible type +j5: Json = [2, 3j] # E: incompatible type + + +# This type alias should be equivalent to Json. +Json2 = Union[None, int, str, float, list["Json2"], dict[str, "Json2"]] + +def func1(j1: Json) -> Json2: + return j1 + + +RecursiveTuple = str | int | tuple["RecursiveTuple", ...] + + +t1: RecursiveTuple = (1, 1) # OK +t2: RecursiveTuple = (1, "1") # OK +t3: RecursiveTuple = (1, "1", 1, "2") # OK +t4: RecursiveTuple = (1, ("1", 1), "2") # OK +t5: RecursiveTuple = (1, ("1", 1), (1, (1, 2))) # OK +t6: RecursiveTuple = (1, ("1", 1), (1, (1, [2]))) # E +t7: RecursiveTuple = (1, [1]) # E + + +RecursiveMapping = str | int | Mapping[str, "RecursiveMapping"] + +m1: RecursiveMapping = 1 # OK +m2: RecursiveMapping = "1" # OK +m3: RecursiveMapping = {"1": "1"} # OK +m4: RecursiveMapping = {"1": "1", "2": 1} # OK +m5: RecursiveMapping = {"1": "1", "2": 1, "3": {}} # OK +m6: RecursiveMapping = {"1": "1", "2": 1, "3": {"0": "0", "1": "2", "2": {}}} # OK +m7: RecursiveMapping = {"1": [1]} # E +m8: RecursiveMapping = {"1": "1", "2": 1, "3": [1, 2]} # E +m9: RecursiveMapping = {"1": "1", "2": 1, "3": {"0": "0", "1": 1, "2": [1, 2, 3]}} # E + + +T1 = TypeVar("T1", str, int) +T2 = TypeVar("T2") + +GenericTypeAlias1 = list["GenericTypeAlias1[T1]" | T1] +SpecializedTypeAlias1 = GenericTypeAlias1[str] + +g1: SpecializedTypeAlias1 = ["hi", ["hi", "hi"]] # OK +g2: GenericTypeAlias1[str] = ["hi", "bye", [""], [["hi"]]] # OK +g3: GenericTypeAlias1[str] = ["hi", [2.4]] # E + +GenericTypeAlias2 = list["GenericTypeAlias2[T1, T2]" | T1 | T2] + +g4: GenericTypeAlias2[str, int] = [[3, ["hi"]], "hi"] # OK +g5: GenericTypeAlias2[str, float] = [[3, ["hi", 3.4, [3.4]]], "hi"] # OK +g6: GenericTypeAlias2[str, int] = [[3, ["hi", 3, [3.4]]], "hi"] # E + + +RecursiveUnion: TypeAlias = Union["RecursiveUnion", int] # E: cyclical reference + +# On one line because different type checkers report the error on different lines +MutualReference1: TypeAlias = Union["MutualReference2", int]; MutualReference2: TypeAlias = Union["MutualReference1", str] # E: cyclical reference diff --git a/conformance/tests/aliases_type_statement.py b/conformance/tests/aliases_type_statement.py new file mode 100644 index 000000000..2cca5bfce --- /dev/null +++ b/conformance/tests/aliases_type_statement.py @@ -0,0 +1,80 @@ +""" +Tests the "type" statement introduced in Python 3.12. +""" + +from typing import Callable, TypeVar + + +type GoodAlias1 = int +type GoodAlias2[S1, *S2, **S3] = Callable[S3, S1] | tuple[*S2] +type GoodAlias3 = GoodAlias2[int, tuple[int, str], ...] + + +class ClassA: + type GoodAlias4 = int | None + + +GoodAlias1.bit_count # E: cannot access attribute + +GoodAlias1() # E: cannot call alias + +print(GoodAlias1.__value__) # OK +print(GoodAlias1.__type_params__) # OK +print(GoodAlias1.other_attrib) # E: unknown attribute + + +class DerivedInt(GoodAlias1): # E: cannot use alias as base class + pass + + +def func2(x: object): + if isinstance(x, GoodAlias1): # E: cannot use alias in isinstance + pass + +var1 = 1 + +# The following should not be allowed as type aliases. +type BadTypeAlias1 = eval("".join(map(chr, [105, 110, 116]))) # E +type BadTypeAlias2 = [int, str] # E +type BadTypeAlias3 = ((int, str),) # E +type BadTypeAlias4 = [int for i in range(1)] # E +type BadTypeAlias5 = {"a": "b"} # E +type BadTypeAlias6 = (lambda: int)() # E +type BadTypeAlias7 = [int][0] # E +type BadTypeAlias8 = int if 1 < 3 else str # E +type BadTypeAlias9 = var1 # E +type BadTypeAlias10 = True # E +type BadTypeAlias11 = 1 # E +type BadTypeAlias12 = list or set # E +type BadTypeAlias13 = f"{'int'}" # E + +V = TypeVar("V") + +type TA1[K] = dict[K, V] # E: combines old and new TypeVars + + +T1 = TypeVar("T1") + +type TA2 = list[T1] # E: uses old TypeVar + + +type RecursiveTypeAlias1[T] = T | list[RecursiveTypeAlias1[T]] + +r1_1: RecursiveTypeAlias1[int] = 1 +r1_2: RecursiveTypeAlias1[int] = [1, [1, 2, 3]] + +type RecursiveTypeAlias2[S: int, T: str, **P] = Callable[P, T] | list[S] | list[RecursiveTypeAlias2[S, T, P]] + +r2_1: RecursiveTypeAlias2[str, str, ...] # E: not compatible with S bound +r2_2: RecursiveTypeAlias2[int, str, ...] +r2_3: RecursiveTypeAlias2[int, int, ...] # E: not compatible with T bound +r2_4: RecursiveTypeAlias2[int, str, [int, str]] + +type RecursiveTypeAlias3 = RecursiveTypeAlias3 # E: circular definition + +type RecursiveTypeAlias4[T] = T | RecursiveTypeAlias4[str] # E: circular definition + +type RecursiveTypeAlias5[T] = T | list[RecursiveTypeAlias5[T]] + +type RecursiveTypeAlias6 = RecursiveTypeAlias7 # E[RTA6+]: circular definition +type RecursiveTypeAlias7 = RecursiveTypeAlias6 # E[RTA6+]: circular definition diff --git a/conformance/tests/aliases_typealiastype.py b/conformance/tests/aliases_typealiastype.py new file mode 100644 index 000000000..d696bfdec --- /dev/null +++ b/conformance/tests/aliases_typealiastype.py @@ -0,0 +1,66 @@ +""" +Tests the TypeAliasType call introduced in Python 3.12. +""" + +from typing import Callable, Generic, ParamSpec, TypeAliasType, TypeVar, TypeVarTuple + +S = TypeVar("S") +T = TypeVar("T") +TStr = TypeVar("TStr", bound=str) +P = ParamSpec("P") +Ts = TypeVarTuple("Ts") + +my_tuple = (S, T) +var1 = 3 + +GoodAlias1 = TypeAliasType("GoodAlias1", int) +GoodAlias2 = TypeAliasType("GoodAlias2", list[T], type_params=(T,)) +GoodAlias3 = TypeAliasType("GoodAlias3", list[T] | list[S], type_params=(S, T)) +GoodAlias4 = TypeAliasType("GoodAlias4", T | "list[GoodAlias4[T]]", type_params=(T,)) +GoodAlias5 = TypeAliasType( + "GoodAlias5", + Callable[P, TStr] | list[S] | list["GoodAlias5[S, TStr, P]"] | tuple[*Ts], + type_params=(S, TStr, P, Ts), +) + +class ClassA(Generic[T]): + GoodAlias6 = TypeAliasType("GoodAlias6", list[T]) + + +print(GoodAlias1.__value__) # OK +print(GoodAlias1.__type_params__) # OK +print(GoodAlias1.other_attrib) # E: unknown attribute + + +x1: GoodAlias4[int] = 1 # OK +x2: GoodAlias4[int] = [1] # OK +x3: GoodAlias5[str, str, ..., int, str] # OK +x4: GoodAlias5[int, str, ..., int, str] # OK +x5: GoodAlias5[int, str, [int, str], *tuple[int, str, int]] # OK +x6: GoodAlias5[int, int, ...] # E: incorrect type arguments + + +BadAlias1 = TypeAliasType("BadAlias1", list[S], type_params=(T,)) # E: S not in scope +BadAlias2 = TypeAliasType("BadAlias2", list[S]) # E: S not in scope +BadAlias3 = TypeAliasType("BadAlias3", int, type_params=my_tuple) # E: not literal tuple +BadAlias4 = TypeAliasType("BadAlias4", "BadAlias4") # E: circular dependency +BadAlias5 = TypeAliasType("BadAlias5", T | "BadAlias5[str]", type_params=(T,)) # E: circular dependency +BadAlias6 = TypeAliasType("BadAlias6", "BadAlias7") # E: circular dependency +BadAlias7 = TypeAliasType("BadAlias7", BadAlias6) # E?: circular dependency + +# The following are invalid type expressions for a type alias. +BadAlias8 = TypeAliasType("BadAlias8", eval("".join(map(chr, [105, 110, 116])))) # E +BadAlias9 = TypeAliasType("BadAlias9", [int, str]) # E +BadAlias10 = TypeAliasType("BadAlias10", ((int, str),)) # E +BadAlias11 = TypeAliasType("BadAlias11", [int for i in range(1)]) # E +BadAlias12 = TypeAliasType("BadAlias12", {"a": "b"}) # E +BadAlias13 = TypeAliasType("BadAlias13", (lambda: int)()) # E +BadAlias14 = TypeAliasType("BadAlias14", [int][0]) # E +BadAlias15 = TypeAliasType("BadAlias15", int if 1 < 3 else str) # E +BadAlias16 = TypeAliasType("BadAlias16", var1) # E +BadAlias17 = TypeAliasType("BadAlias17", True) # E +BadAlias18 = TypeAliasType("BadAlias18", 1) # E +BadAlias19 = TypeAliasType("BadAlias19", list or set) # E +BadAlias20 = TypeAliasType("BadAlias20", f"{'int'}") # E + +BadAlias21 = TypeAliasType("BadAlias21", list[BadAlias21]) # E diff --git a/conformance/tests/aliases_variance.py b/conformance/tests/aliases_variance.py new file mode 100644 index 000000000..280bc6c13 --- /dev/null +++ b/conformance/tests/aliases_variance.py @@ -0,0 +1,45 @@ +""" +Tests generic type aliases used in class declarations for +variance incompatibility. +""" + +from typing import Generic, TypeVar, TypeAlias + +T = TypeVar("T") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class ClassA(Generic[T]): + pass + + +A_Alias_1: TypeAlias = ClassA[T_co] +A_Alias_2: TypeAlias = A_Alias_1[T_co] + +# Specialized type aliases used within a class declaration should +# result in the same variance incompatibility errors as their +# non-aliased counterparts. + +class ClassA_1(ClassA[T_co]): # E: incompatible variance + ... + + +class ClassA_2(A_Alias_1[T_co]): # E: incompatible variance + ... + + +class ClassA_3(A_Alias_2[T_co]): # E: incompatible variance + ... + + + +class ClassB(Generic[T, T_co]): + pass + + +B_Alias_1 = ClassB[T_co, T_contra] + + +class ClassB_1(B_Alias_1[T_contra, T_co]): # E: incompatible variance + ... diff --git a/conformance/tests/annotations_coroutines.py b/conformance/tests/annotations_coroutines.py new file mode 100644 index 000000000..d5b145f45 --- /dev/null +++ b/conformance/tests/annotations_coroutines.py @@ -0,0 +1,27 @@ +""" +Tests for annotating coroutines. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/annotations.html#annotating-generator-functions-and-coroutines + +# > Coroutines introduced in PEP 492 are annotated with the same syntax as +# > ordinary functions. However, the return type annotation corresponds to +# > the type of await expression, not to the coroutine type. + + +from typing import Any, Callable, Coroutine, assert_type + + +async def func1(ignored: int, /) -> str: + return "spam" + + +# Don't use assert_type here because some type checkers infer +# the narrower type types.CoroutineType rather than typing.Coroutine +# in this case. +v1: Callable[[int], Coroutine[Any, Any, str]] = func1 + + +async def func2() -> None: + x = await func1(42) + assert_type(x, str) diff --git a/conformance/tests/annotations_forward_refs.py b/conformance/tests/annotations_forward_refs.py new file mode 100644 index 000000000..70c644700 --- /dev/null +++ b/conformance/tests/annotations_forward_refs.py @@ -0,0 +1,107 @@ +""" +Tests the handling of forward references in type annotations. +""" + +# > When a type hint contains names that have not been defined yet, that +# > definition may be expressed as a string literal, to be resolved later. + + +import types +from typing import assert_type + + +def func1( + p1: "ClassA", p2: "list[ClassA]", p3: list["ClassA"], p4: list["int | ClassA"] +) -> None: + assert_type(p1, ClassA) + assert_type(p2, list[ClassA]) + assert_type(p3, list[ClassA]) + assert_type(p4, list[ClassA | int]) + + +bad1: ClassA # E?: Runtime error prior to 3.14: requires quotes +bad2: list[ClassA] # E?: Runtime error prior to 3.14: requires quotes +bad3: "ClassA" | int # E: Runtime error +bad4: int | "ClassA" # E: Runtime error + + +class ClassA: + ... + + +# > The string literal should contain a valid Python expression +# > should be a valid code object). + +var1 = 1 + + +# The following should all generate errors because they are not legal type +# expressions, despite being enclosed in quotes. +def invalid_annotations( + p1: "eval(''.join(map(chr, [105, 110, 116])))", # E + p2: "[int, str]", # E + p3: "(int, str)", # E + p4: "[int for i in range(1)]", # E + p5: "{}", # E + p6: "(lambda : int)()", # E + p7: "[int][0]", # E + p8: "int if 1 < 3 else str", # E + p9: "var1", # E + p10: "True", # E + p11: "1", # E + p12: "-1", # E + p13: "int or str", # E + p14: 'f"int"', # E + p15: "types", # E +): + pass + + +# > It should evaluate without errors once the module has been fully loaded. +# > The local and global namespace in which it is evaluated should be the same +# > namespaces in which default arguments to the same function would be evaluated. + + +class ClassB: + def method1(self) -> ClassB: # E?: Runtime error prior to 3.14 + return ClassB() + + def method2(self) -> "ClassB": # OK + return ClassB() + + +class ClassC: + ... + + +class ClassD: + ClassC: "ClassC" # OK + + ClassF: "ClassF" # E: circular reference + + str: "str" = "" # OK + + def int(self) -> None: # OK + ... + + x: "int" = 0 # OK + + y: int = 0 # E: Refers to local int, which isn't a legal type expression + + def __init__(self) -> None: + self.ClassC = ClassC() + + +assert_type(ClassD.str, str) +assert_type(ClassD.x, int) + + +# > If a triple quote is used, the string should be parsed as though it is implicitly +# > surrounded by parentheses. This allows newline characters to be +# > used within the string literal. + +value: """ + int | + str | + list[int] +""" diff --git a/conformance/tests/annotations_generators.py b/conformance/tests/annotations_generators.py new file mode 100644 index 000000000..396b51978 --- /dev/null +++ b/conformance/tests/annotations_generators.py @@ -0,0 +1,195 @@ +""" +Tests for annotating generators. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/annotations.html#annotating-generator-functions-and-coroutines + +# The return type of generator functions can be annotated by the generic type +# Generator[yield_type, send_type, return_type] provided by typing.py module. + +import asyncio +from typing import ( + Any, + AsyncGenerator, + AsyncIterable, + AsyncIterator, + Awaitable, + Callable, + Coroutine, + Generator, + Iterable, + Iterator, + Protocol, + TypeVar, + assert_type, +) + +T = TypeVar("T") + + +class A: + pass + + +class B: + def should_continue(self) -> bool: + return True + + +class C: + pass + + +def generator1() -> Generator[A, B, C]: + cont = B() + while cont.should_continue(): + yield A() + + return C() + + +def generator2() -> Generator[A, B, C]: # E: missing return + cont = B() + if cont.should_continue(): + return False # E: incompatible return type + + while cont.should_continue(): + yield 3 # E: incompatible yield type + + +def generator3() -> Generator[A, int, Any]: + cont = B() + if cont.should_continue(): + return 3 + + while cont.should_continue(): + yield 3 # E: Incompatible yield type + + +def generator4() -> Iterable[A]: + yield A() + return True # E?: No return value expected + + +def generator5() -> Iterator[A]: + yield B() # E: incompatible yield type + + +def generator6() -> Generator[None, None, None]: + yield + + +def generator7() -> Iterator[dict[str, int]]: + yield {"": 0} # OK + + +def generator8() -> int: # E: incompatible return type + yield None # E? + return 0 + + +async def generator9() -> int: # E: incompatible return type + yield None # E? + + +class IntIterator(Protocol): + def __next__(self, /) -> int: + ... + + +def generator15() -> IntIterator: # OK + yield 0 + + +class AsyncIntIterator(Protocol): + def __anext__(self, /) -> Awaitable[int]: + ... + + +async def generator16() -> AsyncIntIterator: # OK + yield 0 + + +def generator17() -> Iterator[A]: # OK + yield from generator17() + + +def generator18() -> Iterator[B]: + yield from generator17() # E: incompatible generator type + yield from [1] # E: incompatible generator type + + +def generator19() -> Generator[None, float, None]: # OK + x: float = yield + + +def generator20() -> Generator[None, int, None]: # OK + yield from generator19() + + +def generator21() -> Generator[None, int, None]: + x: float = yield + + +def generator22() -> Generator[None, str, None]: + yield from generator21() # E: incompatible send type + + +def generator23() -> Iterable[str]: # OK + return + yield "" # Unreachable + + +async def generator24() -> AsyncIterable[str]: # OK + return + yield "" # Unreachable + + +def generator25(ints1: list[int], ints2: list[int]) -> Generator[int, None, None]: # OK + yield from ints1 + yield from ints2 + + +async def get_data() -> list[int]: + await asyncio.sleep(1) + return [1, 2, 3] + + +async def generator26(nums: list[int]) -> AsyncGenerator[str, None]: + for n in nums: + await asyncio.sleep(1) + yield f"The number is {n}" + + +async def generator27() -> AsyncGenerator[str, None]: + data = await get_data() + v1 = generator26(data) + assert_type(v1, AsyncGenerator[str, None]) + return v1 + + +async def generator28() -> AsyncIterator[str]: + data = await get_data() + v1 = generator26(data) + assert_type(v1, AsyncGenerator[str, None]) + return v1 + + +async def generator29() -> AsyncIterator[int]: + raise NotImplementedError + + +# Don't use assert_type here because some type checkers infer +# the narrower type types.CoroutineType rather than typing.Coroutine +# in this case. +v1: Callable[[], Coroutine[Any, Any, AsyncIterator[int]]] = generator29 + + +async def generator30() -> AsyncIterator[int]: + raise NotImplementedError + yield 1 + + +async def uses_generator30() -> None: + async for x in generator30(): + assert_type(x, int) diff --git a/conformance/tests/annotations_methods.py b/conformance/tests/annotations_methods.py new file mode 100644 index 000000000..2e4a316f4 --- /dev/null +++ b/conformance/tests/annotations_methods.py @@ -0,0 +1,51 @@ +""" +Tests for annotating instance and class methods. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/annotations.html#annotating-instance-and-class-methods + +from typing import TypeVar, assert_type + + +T = TypeVar("T", bound="A") + + +class A: + def copy(self: T) -> T: + return self + + @classmethod + def factory(cls: type[T]) -> T: + return cls() + + @staticmethod + def static_method(val: type[T]) -> T: + return val() + + +class B(A): + ... + + +assert_type(A().copy(), A) +assert_type(A.factory(), A) +assert_type(A.copy(A()), A) +assert_type(B.copy(B()), B) + +assert_type(B().copy(), B) +assert_type(B.factory(), B) + +# This case is ambiguous in the spec, which does not indicate when +# type binding should be performed. Currently, pyright evaluates +# A here, but mypy evaluates B. Since the spec is not clear, both +# of these are currently acceptable answers. +assert_type(A.copy(B()), A) # E? + +# Similarly, this case is ambiguous in the spec. Pyright currently +# generates a type error here, but mypy accepts this. +B.copy(A()) # E? + +assert_type(A.static_method(A), A) +assert_type(A.static_method(B), B) +assert_type(B.static_method(B), B) +assert_type(B.static_method(A), A) diff --git a/conformance/tests/annotations_typeexpr.py b/conformance/tests/annotations_typeexpr.py new file mode 100644 index 000000000..18ea4e142 --- /dev/null +++ b/conformance/tests/annotations_typeexpr.py @@ -0,0 +1,114 @@ +""" +Test for type expressions used in annotations. +""" + +import abc +import abc +import types +import types +from typing import Any, Callable, Tuple, Union, assert_type + +# https://typing.readthedocs.io/en/latest/spec/annotations.html#valid-type-expression-forms + + +def greeting(name: str) -> str: + return "Hello " + name + + +assert_type(greeting("Monty"), str) + + +# > Expressions whose type is a subtype of a specific argument type are also accepted for that argument. +class StrSub(str): + ... + + +assert_type(greeting(StrSub("Monty")), str) + + +# > Type hints may be built-in classes (including those defined in standard library or third-party +# > extension modules), abstract base classes, types available in the types module, and user-defined +# > classes (including those defined in the standard library or third-party modules). + + +class UserDefinedClass: + ... + + +class AbstractBaseClass(abc.ABC): + @abc.abstractmethod + def abstract_method(self): + ... + + +# The following parameter annotations should all be considered +# valid and not generate errors. +def valid_annotations( + p1: int, + p2: str, + p3: bytes, + p4: bytearray, + p5: memoryview, + p6: complex, + p7: float, + p8: bool, + p9: object, + p10: type, + p11: types.ModuleType, + p12: types.FunctionType, + p13: types.BuiltinFunctionType, + p14: UserDefinedClass, + p15: AbstractBaseClass, + p16: int, + p17: Union[int, str], + p18: None, + p19: list, + p20: list[int], + p21: tuple, + p22: Tuple[int, ...], + p23: Tuple[int, int, str], + p24: Callable[..., int], + p25: Callable[[int, str], None], + p26: Any, +): + assert_type(p17, int | str) + assert_type(p19, list[Any]) + assert_type(p20, list[int]) + assert_type(p21, tuple[Any, ...]) + + +# > Annotations should be kept simple or static analysis tools may not be able to interpret the values. + +var1 = 3 + + +# The following parameter annotations should all be considered +# invalid and generate errors. +def invalid_annotations( + p1: eval("".join(map(chr, [105, 110, 116]))), # E + p2: [int, str], # E + p3: (int, str), # E + p4: [int for i in range(1)], # E + p5: {}, # E + p6: (lambda: int)(), # E + p7: [int][0], # E + p8: int if 1 < 3 else str, # E + p9: var1, # E + p10: True, # E + p11: 1, # E + p12: -1, # E + p13: int or str, # E + p14: f"int", # E + p15: types, # E +): + pass + + +# > When used in a type hint, the expression None is considered equivalent to type(None). + + +def takes_None(x: None) -> None: + ... + + +assert_type(takes_None(None), None) diff --git a/conformance/tests/callables_annotation.py b/conformance/tests/callables_annotation.py new file mode 100644 index 000000000..35b98f5ee --- /dev/null +++ b/conformance/tests/callables_annotation.py @@ -0,0 +1,189 @@ +""" +Tests Callable annotation and parameter annotations for "def" statements. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#callable + +from typing import ( + Any, + Callable, + Concatenate, + ParamSpec, + Protocol, + TypeAlias, + TypeVar, + assert_type, +) + +T_contra = TypeVar("T_contra", contravariant=True) +P = ParamSpec("P") + + +def func1(cb: Callable[[int, str], list[str]]) -> None: + assert_type(cb(1, ""), list[str]) + + cb(1) # E + cb(1, 2) # E + cb(1, "", 1) # E + # Mypy reports two errors, one for each kwarg. + cb(a=1, b="") # E: bad kwarg 'a' + + +def func2(cb: Callable[[], dict[str, str]]) -> None: + assert_type(cb(), dict[str, str]) + + cb(1) # E + + +# https://typing.readthedocs.io/en/latest/spec/callables.html#meaning-of-in-callable + + +# > The Callable special form supports the use of ... in place of the list of +# > parameter types. This indicates that the type is consistent with any input +# > signature. +def func3(cb: Callable[..., list[str]]): + assert_type(cb(), list[str]) + assert_type(cb(""), list[str]) + assert_type(cb(1, ""), list[str]) + + +def func4(*args: int, **kwargs: int) -> None: + assert_type(args, tuple[int, ...]) + assert_type(kwargs, dict[str, int]) + + +v1: Callable[int] # E +v2: Callable[int, int] # E +v3: Callable[[], [int]] # E +v4: Callable[int, int, int] # E +v5: Callable[[...], int] # E + + +def test_cb1(x: int) -> str: + return "" + + +def test_cb2() -> str: + return "" + + +cb1: Callable[..., str] +cb1 = test_cb1 # OK +cb1 = test_cb2 # OK + +cb2: Callable[[], str] = cb1 # OK + +# > A ... can also be used with Concatenate. In this case, the parameters prior +# > to the ... are required to be present in the input signature and be +# > compatible in kind and type, but any additional parameters are permitted. + + +def test_cb3(a: int, b: int, c: int) -> str: + return "" + + +def test_cb4(*, a: int) -> str: + return "" + + +cb3: Callable[Concatenate[int, ...], str] +cb3 = test_cb1 # OK +cb3 = test_cb2 # E +cb3 = test_cb3 # OK +cb3 = test_cb4 # E + +# > If the input signature in a function definition includes both a *args and +# > **kwargs parameter and both are typed as Any (explicitly or implicitly +# > because it has no annotation), a type checker should treat this as the +# > equivalent of `...`. Any other parameters in the signature are unaffected +# > and are retained as part of the signature. + + +class Proto1(Protocol): + def __call__(self, *args: Any, **kwargs: Any) -> None: ... + + +class Proto2(Protocol): + def __call__(self, a: int, /, *args, **kwargs) -> None: ... + + +class Proto3(Protocol): + def __call__(self, a: int, *args: Any, **kwargs: Any) -> None: ... + + +class Proto4(Protocol[P]): + def __call__(self, a: int, *args: P.args, **kwargs: P.kwargs) -> None: ... + + +class Proto5(Protocol[T_contra]): + def __call__(self, *args: T_contra, **kwargs: T_contra) -> None: ... + + +class Proto6(Protocol): + def __call__(self, a: int, /, *args: Any, k: str, **kwargs: Any) -> None: + pass + + +class Proto7(Protocol): + def __call__(self, a: float, /, b: int, *, k: str, m: str) -> None: + pass + + +class Proto8(Protocol): + def __call__(self) -> None: ... + + +def func5( + p1: Proto1, + p2: Proto2, + p3: Proto3, + p4: Proto4[...], + p5: Proto5[Any], + p7: Proto7, + p8: Proto8, + c1: Callable[..., None], + c2: Callable[Concatenate[int, ...], None], +): + ok1: Callable[..., None] = p1 # OK + ok2: Proto1 = c1 # OK + ok3: Callable[..., None] = p5 # OK + ok4: Proto5[Any] = c1 # OK + ok5: Callable[Concatenate[int, ...], None] = p2 # OK + ok6: Proto2 = c2 # OK + ok7: Callable[..., None] = p3 # OK + ok8: Proto3 = c1 # OK + ok9: Proto4[...] = p3 # OK + ok10: Proto3 = p4 # OK + ok11: Proto6 = p7 # OK + + err1: Proto5[Any] = p8 # E + + +# > The ... syntax can also be used to provide a specialized value for a +# > ParamSpec in a generic class or type alias. + + +Callback1: TypeAlias = Callable[P, str] +Callback2: TypeAlias = Callable[Concatenate[int, P], str] + + +def func6(cb1: Callable[[], str], cb2: Callable[[int], str]) -> None: + f1: Callback1[...] = cb1 # OK + f2: Callback2[...] = cb1 # E + + f3: Callback1[...] = cb2 # OK + f4: Callback2[...] = cb2 # OK + + +# > If ... is used with signature concatenation, the ... portion continues +# > to mean “any conceivable set of parameters that could be compatible”. + +CallbackWithInt: TypeAlias = Callable[Concatenate[int, P], str] +CallbackWithStr: TypeAlias = Callable[Concatenate[str, P], str] + + +def func7(cb: Callable[[int, str], str]) -> None: + f1: Callable[Concatenate[int, ...], str] = cb # OK + f2: Callable[Concatenate[str, ...], str] = cb # E + f3: CallbackWithInt[...] = cb # OK + f4: CallbackWithStr[...] = cb # E diff --git a/conformance/tests/callables_kwargs.py b/conformance/tests/callables_kwargs.py new file mode 100644 index 000000000..a4c431dbd --- /dev/null +++ b/conformance/tests/callables_kwargs.py @@ -0,0 +1,134 @@ +""" +Tests the use of an unpacked TypedDict for annotating **kwargs. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#unpack-for-keyword-arguments + +# This sample tests the handling of Unpack[TypedDict] when used with +# a **kwargs parameter in a function signature. + +from typing import Protocol, TypeVar, TypedDict, NotRequired, Required, Unpack, assert_type + + +class TD1(TypedDict): + v1: Required[int] + v2: NotRequired[str] + + +class TD2(TD1): + v3: Required[str] + + +def func1(**kwargs: Unpack[TD2]) -> None: + v1 = kwargs["v1"] + assert_type(v1, int) + + # > Type checkers may allow reading an item using ``d['x']`` even if + # > the key ``'x'`` is not required + kwargs["v2"] # E?: v2 may not be present + + if "v2" in kwargs: + v2 = kwargs["v2"] + assert_type(v2, str) + + v3 = kwargs["v3"] + assert_type(v3, str) + + +def func2(v3: str, **kwargs: Unpack[TD1]) -> None: + # > When Unpack is used, type checkers treat kwargs inside the function + # > body as a TypedDict. + assert_type(kwargs, TD1) + + +def func3() -> None: + # Mypy reports multiple errors here. + func1() # E: missing required keyword args + func1(v1=1, v2="", v3="5") # OK + + td2 = TD2(v1=2, v3="4") + func1(**td2) # OK + func1(v1=1, v2="", v3="5", v4=5) # E?: v4 is not in TD2, but could be in a subtype + func1(1, "", "5") # E: args not passed by position + + # > Passing a dictionary of type dict[str, object] as a **kwargs argument + # > to a function that has **kwargs annotated with Unpack must generate a + # > type checker error. + my_dict: dict[str, str] = {} + func1(**my_dict) # E: untyped dict + + d1 = {"v1": 2, "v3": "4", "v4": 4} + func1(**d1) # E?: OK or Type error (spec allows either) + func2(**td2) # OK + func1(v1=2, **td2) # E: v1 is already specified + func2(1, **td2) # E: v1 is already specified + func2(v1=1, **td2) # E: v1 is already specified + + +class TDProtocol1(Protocol): + def __call__(self, *, v1: int, v3: str) -> None: + ... + + +class TDProtocol2(Protocol): + def __call__(self, *, v1: int, v3: str, v2: str = "") -> None: + ... + + +class TDProtocol3(Protocol): + def __call__(self, *, v1: int, v2: int, v3: str) -> None: + ... + + +class TDProtocol4(Protocol): + def __call__(self, *, v1: int) -> None: + ... + + +class TDProtocol5(Protocol): + def __call__(self, v1: int, v3: str) -> None: + ... + + +class TDProtocol6(Protocol): + def __call__(self, **kwargs: Unpack[TD2]) -> None: + ... + +# Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#assignment + +v1: TDProtocol1 = func1 # OK +v2: TDProtocol2 = func1 # OK +v3: TDProtocol3 = func1 # E: v2 is wrong type +v4: TDProtocol4 = func1 # E: v3 is missing +v5: TDProtocol5 = func1 # E: params are positional +v6: TDProtocol6 = func1 # OK + + +def func4(v1: int, /, **kwargs: Unpack[TD2]) -> None: + ... + + +def func5(v1: int, **kwargs: Unpack[TD2]) -> None: # E: parameter v1 overlaps with the TypedDict. + ... + + +T = TypeVar("T", bound=TD2) + +# > TypedDict is the only permitted heterogeneous type for typing **kwargs. +# > Therefore, in the context of typing **kwargs, using Unpack with types other +# > than TypedDict should not be allowed and type checkers should generate +# > errors in such cases. + +def func6(**kwargs: Unpack[T]) -> None: # E: unpacked value must be a TypedDict, not a TypeVar bound to TypedDict. + ... + +# > The situation where the destination callable contains **kwargs: Unpack[TypedDict] and +# > the source callable doesn’t contain **kwargs should be disallowed. This is because, +# > we cannot be sure that additional keyword arguments are not being passed in when an instance of a subclass +# > had been assigned to a variable with a base class type and then unpacked in the destination callable invocation + +def func7(*, v1: int, v3: str, v2: str = "") -> None: + ... + + +v7: TDProtocol6 = func7 # E: source does not have kwargs diff --git a/conformance/tests/callables_protocol.py b/conformance/tests/callables_protocol.py new file mode 100644 index 000000000..0ee1b5d34 --- /dev/null +++ b/conformance/tests/callables_protocol.py @@ -0,0 +1,313 @@ +""" +Tests handling of callback protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#callback-protocols + +from typing import Any, Callable, ParamSpec, Protocol, TypeVar, cast, overload + +InputT = TypeVar("InputT", contravariant=True) +OutputT = TypeVar("OutputT", covariant=True) + + +class Proto1(Protocol): + def __call__(self, *vals: bytes, max_len: int | None = None) -> list[bytes]: + ... + + +def cb1_good1(*vals: bytes, max_len: int | None = None) -> list[bytes]: + return [] + + +def cb1_bad1(*vals: bytes, max_items: int | None) -> list[bytes]: + return [] + + +def cb1_bad2(*vals: bytes) -> list[bytes]: + return [] + + +def cb1_bad3(*vals: bytes, max_len: str | None) -> list[bytes]: + return [] + + +cb1: Proto1 = cb1_good1 # OK +cb1 = cb1_bad1 # E: different names +cb1 = cb1_bad2 # E: parameter types +cb1 = cb1_bad3 # E: default argument + + +class Proto2(Protocol): + def __call__(self, *vals: bytes, **kwargs: str) -> None: + pass + + +def cb2_good1(*a: bytes, **b: str): + pass + + +def cb2_bad1(*a: bytes): + pass + + +def cb2_bad2(*a: str, **b: str): + pass + + +def cb2_bad3(*a: bytes, **b: bytes): + pass + + +def cb2_bad4(**b: str): + pass + + +cb2: Proto2 = cb2_good1 # OK + +cb2 = cb2_bad1 # E: missing **kwargs +cb2 = cb2_bad2 # E: parameter type +cb2 = cb2_bad3 # E: parameter type +cb2 = cb2_bad4 # E: missing parameter + + +class Proto3(Protocol): + def __call__(self) -> None: + pass + + +cb3: Proto3 = cb2_good1 # OK +cb3 = cb2_bad1 # OK +cb3 = cb2_bad2 # OK +cb3 = cb2_bad3 # OK +cb3 = cb2_bad4 # OK + + +# A callback protocol with other attributes. +class Proto4(Protocol): + other_attribute: int + + def __call__(self, x: int) -> None: + pass + + +def cb4_bad1(x: int) -> None: + pass + + +var4: Proto4 = cb4_bad1 # E: missing attribute + + +class Proto5(Protocol): + def __call__(self, *, a: int, b: str) -> int: + ... + + +def cb5_good1(a: int, b: str) -> int: + return 0 + + +cb5: Proto5 = cb5_good1 # OK + + +class NotProto6: + def __call__(self, *vals: bytes, maxlen: int | None = None) -> list[bytes]: + return [] + + +def cb6_bad1(*vals: bytes, max_len: int | None = None) -> list[bytes]: + return [] + + +cb6: NotProto6 = cb6_bad1 # E: NotProto6 isn't a protocol class + + +class Proto7(Protocol[InputT, OutputT]): + def __call__(self, inputs: InputT) -> OutputT: + ... + + +class Class7_1: + # Test for unannotated parameter. + def __call__(self, inputs) -> int: + return 5 + + +cb7_1: Proto7[int, int] = Class7_1() # OK + + +class Class7_2: + # Test for parameter with type Any. + def __call__(self, inputs: Any) -> int: + return 5 + + +cb7_2: Proto7[int, int] = Class7_2() # OK + + +class Proto8(Protocol): + @overload + def __call__(self, x: int) -> int: + ... + + @overload + def __call__(self, x: str) -> str: + ... + + def __call__(self, x: Any) -> Any: + ... + + +def cb8_good1(x: Any) -> Any: + return x + + +def cb8_bad1(x: int) -> Any: + return x + + +cb8: Proto8 = cb8_good1 # OK +cb8 = cb8_bad1 # E: parameter type + + +P = ParamSpec("P") +R = TypeVar("R", covariant=True) + + +class Proto9(Protocol[P, R]): + other_attribute: int + + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R: + ... + + +def decorator1(f: Callable[P, R]) -> Proto9[P, R]: + converted = cast(Proto9[P, R], f) + converted.other_attribute = 1 + converted.other_attribute = "str" # E: incompatible type + converted.xxx = 3 # E: unknown attribute + return converted + + +@decorator1 +def cb9_good(x: int) -> str: + return "" + + +print(cb9_good.other_attribute) # OK +print(cb9_good.other_attribute2) # E: unknown attribute + +cb9_good(x=3) + + +class Proto10(Protocol): + __name__: str + __module__: str + __qualname__: str + __annotations__: dict[str, Any] + + def __call__(self) -> None: + ... + + +def cb10_good() -> None: + pass + + +cb10: Proto10 = cb10_good # OK + + +class Proto11(Protocol): + def __call__(self, x: int, /, y: str) -> Any: + ... + + +def cb11_good1(x: int, /, y: str, z: None = None) -> Any: + raise NotImplementedError + + +def cb11_good2(x: int, y: str, z: None = None) -> Any: + raise NotImplementedError + + +def cb11_bad1(x: int, y: str, /) -> Any: + raise NotImplementedError + + +cb11: Proto11 = cb11_good1 # OK +cb11 = cb11_good2 # OK +cb11 = cb11_bad1 # E: y is position-only + + +class Proto12(Protocol): + def __call__(self, *args: Any, kwarg0: Any, kwarg1: Any) -> None: + ... + + +def cb12_good1(*args: Any, kwarg0: Any, kwarg1: Any) -> None: + pass + + +def cb12_good2(*args: Any, **kwargs: Any) -> None: + pass + + +def cb12_bad1(*args: Any, kwarg0: Any) -> None: + pass + + +cb12: Proto12 = cb12_good1 # OK +cb12 = cb12_good2 # OK +cb12 = cb12_bad1 # E: missing kwarg1 + + +class Proto13_Default(Protocol): + # Callback with positional parameter with default arg value + def __call__(self, path: str = ...) -> str: + ... + + +class Proto13_NoDefault(Protocol): + # Callback with positional parameter but no default arg value + def __call__(self, path: str) -> str: + ... + + +def cb13_default(path: str = "") -> str: + return "" + + +def cb13_no_default(path: str) -> str: + return "" + + +cb13_1: Proto13_Default = cb13_default # OK +cb13_2: Proto13_Default = cb13_no_default # E: no default + +cb13_3: Proto13_NoDefault = cb13_default # OK +cb13_4: Proto13_NoDefault = cb13_no_default # OK + + +class Proto14_Default(Protocol): + # Callback with keyword parameter with default arg value + def __call__(self, *, path: str = ...) -> str: + ... + + +class Proto14_NoDefault(Protocol): + # Callback with keyword parameter with no default arg value + def __call__(self, *, path: str) -> str: + ... + + +def cb14_default(*, path: str = "") -> str: + return "" + + +def cb14_no_default(*, path: str) -> str: + return "" + + +cb14_1: Proto14_Default = cb14_default # OK +cb14_2: Proto14_Default = cb14_no_default # E: no default +cb14_3: Proto14_NoDefault = cb14_default # OK +cb14_4: Proto14_NoDefault = cb14_no_default # OK diff --git a/conformance/tests/callables_subtyping.py b/conformance/tests/callables_subtyping.py new file mode 100644 index 000000000..0451d6289 --- /dev/null +++ b/conformance/tests/callables_subtyping.py @@ -0,0 +1,297 @@ +""" +Tests subtyping rules for callables. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/callables.html#subtyping-rules-for-callables + +from typing import Callable, ParamSpec, Protocol, TypeAlias, overload + +P = ParamSpec("P") + +# > Callable types are covariant with respect to their return types but +# > contravariant with respect to their parameter types. + + +def func1( + cb1: Callable[[float], int], + cb2: Callable[[float], float], + cb3: Callable[[int], int], +) -> None: + f1: Callable[[int], float] = cb1 # OK + f2: Callable[[int], float] = cb2 # OK + f3: Callable[[int], float] = cb3 # OK + + f4: Callable[[float], float] = cb1 # OK + f5: Callable[[float], float] = cb2 # OK + f6: Callable[[float], float] = cb3 # E + + f7: Callable[[int], int] = cb1 # OK + f8: Callable[[int], int] = cb2 # E + f9: Callable[[int], int] = cb3 # OK + + +# > Callable A is a subtype of callable B if all keyword-only parameters in B +# > are present in A as either keyword-only parameters or standard (positional +# > or keyword) parameters. + + +class PosOnly2(Protocol): + def __call__(self, b: int, a: int, /) -> None: ... + + +class KwOnly2(Protocol): + def __call__(self, *, b: int, a: int) -> None: ... + + +class Standard2(Protocol): + def __call__(self, a: int, b: int) -> None: ... + + +def func2(standard: Standard2, pos_only: PosOnly2, kw_only: KwOnly2): + f1: Standard2 = pos_only # E + f2: Standard2 = kw_only # E + + f3: PosOnly2 = standard # OK + f4: PosOnly2 = kw_only # E + + f5: KwOnly2 = standard # OK + f6: KwOnly2 = pos_only # E + + +# > If a callable B has a signature with a *args parameter, callable A +# > must also have a *args parameter to be a subtype of B, and the type of +# > B’s *args parameter must be a subtype of A’s *args parameter. + + +class NoArgs3(Protocol): + def __call__(self) -> None: ... + + +class IntArgs3(Protocol): + def __call__(self, *args: int) -> None: ... + + +class FloatArgs3(Protocol): + def __call__(self, *args: float) -> None: ... + + +def func3(no_args: NoArgs3, int_args: IntArgs3, float_args: FloatArgs3): + f1: NoArgs3 = int_args # OK + f2: NoArgs3 = float_args # OK + + f3: IntArgs3 = no_args # E: missing *args parameter + f4: IntArgs3 = float_args # OK + + f5: FloatArgs3 = no_args # E: missing *args parameter + f6: FloatArgs3 = int_args # E: float is not subtype of int + + +# > If a callable B has a signature with one or more positional-only parameters, +# > a callable A is a subtype of B if A has an *args parameter whose type is a +# > supertype of the types of any otherwise-unmatched positional-only parameters +# > in B. + + +class PosOnly4(Protocol): + def __call__(self, a: int, b: str, /) -> None: ... + + +class IntArgs4(Protocol): + def __call__(self, *args: int) -> None: ... + + +class IntStrArgs4(Protocol): + def __call__(self, *args: int | str) -> None: ... + + +class StrArgs4(Protocol): + def __call__(self, a: int, /, *args: str) -> None: ... + + +class Standard4(Protocol): + def __call__(self, a: int, b: str) -> None: ... + + +def func4(int_args: IntArgs4, int_str_args: IntStrArgs4, str_args: StrArgs4): + f1: PosOnly4 = int_args # E: str is not subtype of int + f2: PosOnly4 = int_str_args # OK + f3: PosOnly4 = str_args # OK + f4: IntStrArgs4 = str_args # E: int | str is not subtype of str + f5: IntStrArgs4 = int_args # E: int | str is not subtype of int + f6: StrArgs4 = int_str_args # OK + f7: StrArgs4 = int_args # E: str is not subtype of int + f8: IntArgs4 = int_str_args # OK + f9: IntArgs4 = str_args # E: int is not subtype of str + f10: Standard4 = int_str_args # E: keyword parameters a and b missing + f11: Standard4 = str_args # E: keyword parameter b missing + + +# > If a callable B has a signature with a **kwargs parameter (without an +# > unpacked TypedDict type annotation), callable A must also have a **kwargs +# > parameter to be a subtype of B, and the type of B’s **kwargs parameter +# > must be a subtype of A’s **kwargs parameter. + + +class NoKwargs5(Protocol): + def __call__(self) -> None: ... + + +class IntKwargs5(Protocol): + def __call__(self, **kwargs: int) -> None: ... + + +class FloatKwargs5(Protocol): + def __call__(self, **kwargs: float) -> None: ... + + +def func5(no_kwargs: NoKwargs5, int_kwargs: IntKwargs5, float_kwargs: FloatKwargs5): + f1: NoKwargs5 = int_kwargs # OK + f2: NoKwargs5 = float_kwargs # OK + + f3: IntKwargs5 = no_kwargs # E: missing **kwargs parameter + f4: IntKwargs5 = float_kwargs # OK + + f5: FloatKwargs5 = no_kwargs # E: missing **kwargs parameter + f6: FloatKwargs5 = int_kwargs # E: float is not subtype of int + + +# > If a callable B has a signature with one or more keyword-only parameters, +# > a callable A is a subtype of B if A has a **kwargs parameter whose type +# > is a supertype of the types of any otherwise-unmatched keyword-only +# > parameters in B. + + +class KwOnly6(Protocol): + def __call__(self, *, a: int, b: str) -> None: ... + + +class IntKwargs6(Protocol): + def __call__(self, **kwargs: int) -> None: ... + + +class IntStrKwargs6(Protocol): + def __call__(self, **kwargs: int | str) -> None: ... + + +class StrKwargs6(Protocol): + def __call__(self, *, a: int, **kwargs: str) -> None: ... + + +class Standard6(Protocol): + def __call__(self, a: int, b: str) -> None: ... + + +def func6( + int_kwargs: IntKwargs6, int_str_kwargs: IntStrKwargs6, str_kwargs: StrKwargs6 +): + f1: KwOnly6 = int_kwargs # E: str is not subtype of int + f2: KwOnly6 = int_str_kwargs # OK + f3: KwOnly6 = str_kwargs # OK + f4: IntStrKwargs6 = str_kwargs # E: int | str is not subtype of str + f5: IntStrKwargs6 = int_kwargs # E: int | str is not subtype of int + f6: StrKwargs6 = int_str_kwargs # OK + f7: StrKwargs6 = int_kwargs # E: str is not subtype of int + f8: IntKwargs6 = int_str_kwargs # OK + f9: IntKwargs6 = str_kwargs # E: int is not subtype of str + f10: Standard6 = int_str_kwargs # E: Does not accept positional arguments + f11: Standard6 = str_kwargs # E: Does not accept positional arguments + + +# > A signature that includes *args: P.args, **kwargs: P.kwargs is equivalent +# > to a Callable parameterized by P. + + +class ProtocolWithP(Protocol[P]): + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> None: ... + + +TypeAliasWithP: TypeAlias = Callable[P, None] + + +def func7(proto: ProtocolWithP[P], ta: TypeAliasWithP[P]): + # These two types are equivalent + f1: TypeAliasWithP[P] = proto # OK + f2: ProtocolWithP[P] = ta # OK + + +# > If a callable A has a parameter x with a default argument value and B is +# > the same as A except that x has no default argument, then A is a subtype +# > of B. A is also a subtype of C if C is the same as A with parameter x +# > removed. + + +class DefaultArg8(Protocol): + def __call__(self, x: int = 0) -> None: ... + + +class NoDefaultArg8(Protocol): + def __call__(self, x: int) -> None: ... + + +class NoX8(Protocol): + def __call__(self) -> None: ... + + +def func8(default_arg: DefaultArg8, no_default_arg: NoDefaultArg8, no_x: NoX8): + f1: DefaultArg8 = no_default_arg # E + f2: DefaultArg8 = no_x # E + + f3: NoDefaultArg8 = default_arg # OK + f4: NoDefaultArg8 = no_x # E + + f5: NoX8 = default_arg # OK + f6: NoX8 = no_default_arg # E + + +# > If a callable A is overloaded with two or more signatures, it is a subtype +# > of callable B if at least one of the overloaded signatures in A is a +# > subtype of B. + + +class Overloaded9(Protocol): + @overload + def __call__(self, x: int) -> int: ... + @overload + def __call__(self, x: str) -> str: ... + + +class IntArg9(Protocol): + def __call__(self, x: int) -> int: ... + + +class StrArg9(Protocol): + def __call__(self, x: str) -> str: ... + + +class FloatArg9(Protocol): + def __call__(self, x: float) -> float: ... + + +def func9(overloaded: Overloaded9): + f1: IntArg9 = overloaded # OK + f2: StrArg9 = overloaded # OK + f3: FloatArg9 = overloaded # E + + +# > If a callable B is overloaded with two or more signatures, callable A is +# > a subtype of B if A is a subtype of all of the signatures in B. + + +class Overloaded10(Protocol): + @overload + def __call__(self, x: int, y: str) -> float: ... + @overload + def __call__(self, x: str) -> complex: ... + + +class StrArg10(Protocol): + def __call__(self, x: str) -> complex: ... + + +class IntStrArg10(Protocol): + def __call__(self, x: int | str, y: str = "") -> int: ... + + +def func10(int_str_arg: IntStrArg10, str_arg: StrArg10): + f1: Overloaded10 = int_str_arg # OK + f2: Overloaded10 = str_arg # E diff --git a/conformance/tests/classes_classvar.py b/conformance/tests/classes_classvar.py new file mode 100644 index 000000000..3f94fe5a6 --- /dev/null +++ b/conformance/tests/classes_classvar.py @@ -0,0 +1,167 @@ +""" +Tests the typing.ClassVar special form. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/class-compat.html#classvar + +from typing import ( + Annotated, + Any, + Callable, + ClassVar, + Final, + Generic, + ParamSpec, + Protocol, + TypeAlias, + TypeVar, + TypeVarTuple, + assert_type, + cast, +) +from typing import ClassVar as CV + + +var1 = 3 + +P = ParamSpec("P") +T = TypeVar("T") +Ts = TypeVarTuple("Ts") + +# This is currently commented out because it causes mypy to crash. +# class ClassA(Generic[T, P, Unpack[Ts]]): + + +class ClassA(Generic[T, P]): + # > ClassVar accepts only a single argument that should be a valid type + + bad1: ClassVar[int, str] = cast(Any, 0) # E: too many arguments + bad2: CV[3] = cast(Any, 0) # E: invalid type + bad3: CV[var] = cast(Any, 0) # E: invalid type + + # > Note that a ClassVar parameter cannot include any type variables, + # > regardless of the level of nesting. + + bad4: ClassVar[T] = cast(Any, 0) # E: cannot use TypeVar + bad5: ClassVar[list[T]] = cast(Any, 0) # E: cannot use TypeVar + bad6: ClassVar[Callable[P, Any]] = cast(Any, 0) # E: cannot use ParamSpec + + # This is currently commented out because it causes mypy to crash. + # bad7: ClassVar[tuple[*Ts]] # E: cannot use TypeVarTuple + + bad8: ClassVar[list[str]] = {} # E: type violation in initialization + + bad9: Final[ClassVar[int]] = 3 # E: ClassVar cannot be nested with Final + bad10: list[ClassVar[int]] = [] # E: ClassVar cannot be nested + + good1: CV[int] = 1 + good2: ClassVar[list[str]] = [] + good3: ClassVar[Any] = 1 + # > If an assigned value is available, the type should be inferred as some type + # > to which this value is assignable. + # Here, type checkers could infer good4 as `float` or `Any`, for example. + good4: ClassVar = 3.1 + # > If the `ClassVar` qualifier is used without any assigned value, the type + # > should be inferred as `Any`: + good5: ClassVar # E?: Type checkers may error on uninitialized ClassVar + good6: Annotated[ClassVar[list[int]], ""] = [] + + def method1(self, a: ClassVar[int]): # E: ClassVar not allowed here + x: ClassVar[str] = "" # E: ClassVar not allowed here + self.xx: ClassVar[str] = "" # E: ClassVar not allowed here + + def method2(self) -> ClassVar[int]: # E: ClassVar not allowed here + return 3 + + +bad11: ClassVar[int] = 3 # E: ClassVar not allowed here +bad12: TypeAlias = ClassVar[str] # E: ClassVar not allowed here + + +assert_type(ClassA.good1, int) +assert_type(ClassA.good2, list[str]) +assert_type(ClassA.good3, Any) +assert_type(ClassA.good5, Any) + + +class BasicStarship: + captain: str = "Picard" # Instance variable with default + damage: int # Instance variable without default + stats: ClassVar[dict[str, int]] = {} # Class variable + + def __init__(self, damage: int) -> None: + self.damage = damage + + +class Starship: + captain: str = "Picard" + damage: int + stats: ClassVar[dict[str, int]] = {} + + def __init__(self, damage: int, captain: str | None = None): + self.damage = damage + if captain: + self.captain = captain + + def hit(self): + Starship.stats["hits"] = Starship.stats.get("hits", 0) + 1 + + +enterprise_d = Starship(3000) +enterprise_d.stats = {} # E: cannot access via instance +Starship.stats = {} # OK + + +# > As a matter of convenience (and convention), instance variables can be +# > annotated in __init__ or other methods, rather than in the class. + + +class Box(Generic[T]): + def __init__(self, content) -> None: + self.content: T = content + + +# Tests for ClassVar in Protocol + + +class ProtoA(Protocol): + x: ClassVar[str] + y: ClassVar[str] + z: CV = [""] + + +class ProtoAImpl: + x = "" + + def __init__(self) -> None: + self.y = "" + + +a: ProtoA = ProtoAImpl() # E: y is not a ClassVar + + +class ProtoB(Protocol): + x: int = 3 + y: int + z: ClassVar[int] + + @classmethod + def method1(cls) -> None: + return None + + @staticmethod + def method2() -> None: + return None + + +class ProtoBImpl(ProtoB): + y = 0 + z = 0 + + +b_x: int = ProtoBImpl.x +b_y: int = ProtoBImpl.y +b_z: int = ProtoBImpl.z + +b_m1 = ProtoBImpl.method1 +b_m2 = ProtoBImpl.method2 diff --git a/conformance/tests/classes_override.py b/conformance/tests/classes_override.py new file mode 100644 index 000000000..831e37d05 --- /dev/null +++ b/conformance/tests/classes_override.py @@ -0,0 +1,102 @@ +""" +Tests the typing.override decorator. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/class-compat.html#override + +from typing import Any, Callable, overload, override + + +def wrapper(func: Callable[..., Any], /) -> Any: + def wrapped(*args: Any, **kwargs: Any) -> Any: + raise NotImplementedError + + return wrapped + + +class ParentA: + def method1(self) -> int: + return 1 + + @overload + def method2(self, x: int) -> int: + ... + + @overload + def method2(self, x: str) -> str: + ... + + def method2(self, x: int | str) -> int | str: + return 0 + + def method5(self): + pass + + +class ChildA(ParentA): + @override + def method1(self) -> int: # OK + return 2 + + @overload + def method2(self, x: int) -> int: + ... + + @overload + def method2(self, x: str) -> str: + ... + + def method2(self, x: int | str) -> int | str: # OK + return 0 + + @override # E[method3] + def method3(self) -> int: # E[method3]: no matching signature in ancestor + return 1 + + @overload # E[method4] + def method4(self, x: int) -> int: # E[method4] + ... + + @overload + def method4(self, x: str) -> str: + ... + + @override # E[method4] + def method4(self, x: int | str) -> int | str: # E[method4]: no matching signature in ancestor + return 0 + + @override + @wrapper + def method5(self): # OK + pass + + # > The @override decorator should be permitted anywhere a type checker + # > considers a method to be a valid override, which typically includes not + # > only normal methods but also @property, @staticmethod, and @classmethod. + + @staticmethod + @override # E[static_method1] + def static_method1() -> int: # E[static_method1]: no matching signature in ancestor + return 1 + + @classmethod + @override # E[class_method1] + def class_method1(cls) -> int: # E[class_method1]: no matching signature in ancestor + return 1 + + @property + @override # E[property1] + def property1(self) -> int: # E[property1]: no matching signature in ancestor + return 1 + + +# Test the case where the parent derives from Any + +class ParentB(Any): + pass + + +class ChildB(ParentB): + @override + def method1(self) -> None: # OK + pass diff --git a/conformance/tests/constructors_call_init.py b/conformance/tests/constructors_call_init.py new file mode 100644 index 000000000..c3749634a --- /dev/null +++ b/conformance/tests/constructors_call_init.py @@ -0,0 +1,130 @@ +""" +Tests the evaluation of calls to constructors when there is a __init__ +method defined. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/constructors.html#init-method + +from typing import Any, Generic, Self, TypeVar, assert_type, overload + +T = TypeVar("T") + + +class Class1(Generic[T]): + def __init__(self, x: T) -> None: + pass + + +# Constructor calls for specialized classes +assert_type(Class1[int](1), Class1[int]) +assert_type(Class1[float](1), Class1[float]) +Class1[int](1.0) # E + +# Constructor calls for non-specialized classes +assert_type(Class1(1), Class1[int]) +assert_type(Class1(1.0), Class1[float]) + + +# > If the self parameter within the __init__ method is not annotated, type +# > checkers should infer a type of Self. + + +class Class2(Generic[T]): + def __init__(self, x: Self | None) -> None: + pass + + +class Class3(Class2[int]): + pass + + +Class3(Class3(None)) # OK +Class3(Class2(None)) # E + + +# > Regardless of whether the self parameter type is explicit or inferred, a +# > type checker should bind the class being constructed to this parameter and +# > report any type errors that arise during binding. + + +class Class4(Generic[T]): + def __init__(self: "Class4[int]") -> None: ... + + +Class4() # OK +Class4[int]() # OK +Class4[str]() # E + + +class Class5(Generic[T]): + @overload + def __init__(self: "Class5[list[int]]", value: int) -> None: ... + @overload + def __init__(self: "Class5[set[str]]", value: str) -> None: ... + @overload + def __init__(self, value: T) -> None: + pass + + def __init__(self, value: Any) -> None: + pass + + +assert_type(Class5(0), Class5[list[int]]) +assert_type(Class5[int](3), Class5[int]) +assert_type(Class5(""), Class5[set[str]]) +assert_type(Class5(3.0), Class5[float]) + +# > Function-scoped type variables can also be used in the self annotation +# > of an __init__ method to influence the return type of the constructor call. + +T1 = TypeVar("T1") +T2 = TypeVar("T2") + +V1 = TypeVar("V1") +V2 = TypeVar("V2") + + +class Class6(Generic[T1, T2]): + def __init__(self: "Class6[V1, V2]", value1: V1, value2: V2) -> None: ... + + +assert_type(Class6(0, ""), Class6[int, str]) +assert_type(Class6[int, str](0, ""), Class6[int, str]) + + +class Class7(Generic[T1, T2]): + def __init__(self: "Class7[V2, V1]", value1: V1, value2: V2) -> None: ... + + +assert_type(Class7(0, ""), Class7[str, int]) +assert_type(Class7[str, int](0, ""), Class7[str, int]) + + +# > Class-scoped type variables should not be used in the self annotation. + + +class Class8(Generic[T1, T2]): + def __init__(self: "Class8[T2, T1]") -> None: # E + pass + + +# > If a class does not define a __new__ method or __init__ method and does +# > not inherit either of these methods from a base class other than object, +# > a type checker should evaluate the argument list using the __new__ and +# > __init__ methods from the object class. + + +class Class9: + pass + + +class Class10: + pass + + +class Class11(Class9, Class10): + pass + + +assert_type(Class11(), Class11) +Class11(1) # E diff --git a/conformance/tests/constructors_call_metaclass.py b/conformance/tests/constructors_call_metaclass.py new file mode 100644 index 000000000..0a3a9df7a --- /dev/null +++ b/conformance/tests/constructors_call_metaclass.py @@ -0,0 +1,69 @@ +""" +Tests the evaluation of calls to constructors when there is a custom +metaclass with a __call__ method. +""" + +from typing import NoReturn, Self, TypeVar, assert_type + +# Specification: https://typing.readthedocs.io/en/latest/spec/constructors.html#constructor-calls + +# Metaclass __call__ method: https://typing.readthedocs.io/en/latest/spec/constructors.html#metaclass-call-method + + +class Meta1(type): + def __call__(cls, *args, **kwargs) -> NoReturn: + raise TypeError("Cannot instantiate class") + + +class Class1(metaclass=Meta1): + def __new__(cls, x: int) -> Self: + return super().__new__(cls) + + +# This needs to be in a separate scope, because some type checkers might mark +# the statements after it as unreachable. +if bool(): + assert_type(Class1(), NoReturn) + + +class Meta2(type): + def __call__(cls, *args, **kwargs) -> "int | Meta2": + return 1 + + +class Class2(metaclass=Meta2): + def __new__(cls, x: int) -> Self: + return super().__new__(cls) + + +assert_type(Class2(), int | Meta2) + +T = TypeVar("T") + + +class Meta3(type): + def __call__(cls: type[T], *args, **kwargs) -> T: + return type.__call__(cls, *args, **kwargs) + + +class Class3(metaclass=Meta3): + def __new__(cls, x: int) -> Self: + return super().__new__(cls) + + +Class3() # E: Missing argument for 'x' parameter in __new__ +assert_type(Class3(1), Class3) + + +class Meta4(type): + def __call__(cls, *args, **kwargs): + return type.__call__(cls, *args, **kwargs) + + +class Class4(metaclass=Meta4): + def __new__(cls, x: int) -> Self: + return super().__new__(cls) + + +Class4() # E: Missing argument for 'x' parameter in __new__ +assert_type(Class4(1), Class4) diff --git a/conformance/tests/constructors_call_new.py b/conformance/tests/constructors_call_new.py new file mode 100644 index 000000000..48275c331 --- /dev/null +++ b/conformance/tests/constructors_call_new.py @@ -0,0 +1,148 @@ +""" +Tests the evaluation of calls to constructors when there is a __new__ +method defined. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/constructors.html#new-method + + +from typing import Any, Generic, NoReturn, Self, TypeVar, assert_type + +T = TypeVar("T") + + +class Class1(Generic[T]): + def __new__(cls, x: T) -> Self: + return super().__new__(cls) + + +assert_type(Class1[int](1), Class1[int]) +assert_type(Class1[float](1), Class1[float]) +Class1[int](1.0) # E + +assert_type(Class1(1), Class1[int]) +assert_type(Class1(1.0), Class1[float]) + + +class Class2(Generic[T]): + def __new__(cls, *args, **kwargs) -> Self: + return super().__new__(cls) + + def __init__(self, x: T) -> None: + pass + + +assert_type(Class2(1), Class2[int]) +assert_type(Class2(""), Class2[str]) + + +class Class3: + def __new__(cls) -> int: + return 0 + + # In this case, the __init__ method should not be considered + # by the type checker when evaluating a constructor call. + def __init__(self, x: int): + pass + + +assert_type(Class3(), int) + +# > For purposes of this test, an explicit return type of Any (or a union +# > containing Any) should be treated as a type that is not an instance of +# > the class being constructed. + + +class Class4: + def __new__(cls) -> "Class4 | Any": + return 0 + + def __init__(self, x: int): + pass + + +assert_type(Class4(), Class4 | Any) + + +class Class5: + def __new__(cls) -> NoReturn: + raise NotImplementedError + + def __init__(self, x: int): + pass + + +try: + assert_type(Class5(), NoReturn) +except: + pass + + +class Class6: + def __new__(cls) -> "int | Class6": + return 0 + + def __init__(self, x: int): + pass + + +assert_type(Class6(), int | Class6) + +# > If the return type of __new__ is not annotated, a type checker may assume +# > that the return type is Self and proceed with the assumption that the +# > __init__ method will be called. + + +class Class7: + def __new__(cls, *args, **kwargs): + return super().__new__(cls, *args, **kwargs) + + def __init__(self, x: int): + pass + + +assert_type(Class7(1), Class7) + + +# > If the class is generic, it is possible for a __new__ method to override +# > the specialized class type and return a class instance that is specialized +# > with different type arguments. + + +class Class8(Generic[T]): + def __new__(cls, *args, **kwargs) -> "Class8[list[T]]": + raise NotImplementedError + + +assert_type(Class8[int](), Class8[list[int]]) +assert_type(Class8[str](), Class8[list[str]]) + + +# > If the cls parameter within the __new__ method is not annotated, +# > type checkers should infer a type of type[Self]. + + +class Class9(Generic[T]): + def __new__(cls, *args, **kwargs) -> Self: + raise NotImplementedError + + +class Class10(Class9[int]): + pass + + +c10: Class9[int] = Class10() + +# > Regardless of whether the type of the cls parameter is explicit or +# > inferred, the type checker should bind the class being constructed to +# > the cls parameter and report any type errors that arise during binding. + + +class Class11(Generic[T]): + def __new__(cls: "type[Class11[int]]") -> "Class11[int]": + raise NotImplementedError + + +Class11() # OK +Class11[int]() # OK +Class11[str]() # E diff --git a/conformance/tests/constructors_call_type.py b/conformance/tests/constructors_call_type.py new file mode 100644 index 000000000..d795a5802 --- /dev/null +++ b/conformance/tests/constructors_call_type.py @@ -0,0 +1,82 @@ +""" +Tests the evaluation of calls to constructors when the type is type[T]. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/constructors.html#constructor-calls-for-type-t + +# > When a value of type type[T] (where T is a concrete class or a type +# > variable) is called, a type checker should evaluate the constructor +# > call as if it is being made on the class T. + +from typing import Self, TypeVar + + +T = TypeVar("T") + + +class Meta1(type): + # Ignore possible errors related to incompatible override + def __call__(cls: type[T], x: int, y: str) -> T: # type: ignore + return type.__call__(cls) + + +class Class1(metaclass=Meta1): + def __new__(cls, *args, **kwargs) -> Self: + return super().__new__(*args, **kwargs) + + +def func1(cls: type[Class1]): + cls(x=1, y="") # OK + cls() # E + + +class Class2: + def __new__(cls, x: int, y: str) -> Self: + return super().__new__(cls) + + +def func2(cls: type[Class2]): + cls(x=1, y="") # OK + cls() # E + + +class Class3: + def __init__(self, x: int, y: str) -> None: + pass + + +def func3(cls: type[Class3]): + cls(x=1, y="") # OK + cls() # E + + +class Class4: + pass + + +def func4(cls: type[Class4]): + cls() # OK + cls(1) # E + + +def func5(cls: type[T]): + cls() # OK + cls(1) # E + + +T1 = TypeVar("T1", bound=Class1) + + +def func6(cls: type[T1]): + cls(x=1, y="") # OK + cls() # E + + +T2 = TypeVar("T2", bound=Class2) + + +def func7(cls: type[T2]): + cls(1, "") # OK + cls(x=1, y="") # OK + cls(1) # E + cls(1, 2) # E diff --git a/conformance/tests/constructors_callable.py b/conformance/tests/constructors_callable.py new file mode 100644 index 000000000..c05fee3fb --- /dev/null +++ b/conformance/tests/constructors_callable.py @@ -0,0 +1,197 @@ +""" +Tests the conversion of constructors into Callable types. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/constructors.html#converting-a-constructor-to-callable + + +from typing import ( + Any, + Callable, + Generic, + NoReturn, + ParamSpec, + Self, + TypeVar, + assert_type, + overload, + reveal_type, +) + +P = ParamSpec("P") +R = TypeVar("R") +T = TypeVar("T") + + +def accepts_callable(cb: Callable[P, R]) -> Callable[P, R]: + return cb + + +class Class1: + def __init__(self, x: int) -> None: + pass + + +r1 = accepts_callable(Class1) +reveal_type(r1) # `def (x: int) -> Class1` +assert_type(r1(1), Class1) +r1() # E +r1(y=1) # E + + +class Class2: + """No __new__ or __init__""" + + pass + + +r2 = accepts_callable(Class2) +reveal_type(r2) # `def () -> Class2` +assert_type(r2(), Class2) +r2(1) # E + + +class Class3: + """__new__ and __init__""" + + def __new__(cls, *args, **kwargs) -> Self: + raise NotImplementedError + + def __init__(self, x: int) -> None: ... + + +r3 = accepts_callable(Class3) +reveal_type(r3) # `def (x: int) -> Class3` +assert_type(r3(3), Class3) +r3() # E +r3(y=1) # E +r3(1, 2) # E + + +class Class4: + """__new__ but no __init__""" + + def __new__(cls, x: int) -> int: + raise NotImplementedError + + +r4 = accepts_callable(Class4) +reveal_type(r4) # `def (x: int) -> int` +assert_type(r4(1), int) +r4() # E +r4(y=1) # E + + +class Meta1(type): + def __call__(cls, *args: Any, **kwargs: Any) -> NoReturn: + raise NotImplementedError("Class not constructable") + + +class Class5(metaclass=Meta1): + """Custom metaclass that overrides type.__call__""" + + def __new__(cls, *args: Any, **kwargs: Any) -> Self: + """This __new__ is ignored for purposes of conversion""" + return super().__new__(cls) + + +r5 = accepts_callable(Class5) +reveal_type(r5) # `def (*args: Any, **kwargs: Any) -> NoReturn` + +try: + assert_type(r5(), NoReturn) +except: + pass + +try: + assert_type(r5(1, x=1), NoReturn) +except: + pass + + +class Class6Proxy: ... + + +class Class6: + """__new__ that causes __init__ to be ignored""" + + def __new__(cls) -> Class6Proxy: + return Class6Proxy() + + def __init__(self, x: int) -> None: + """This __init__ is ignored for purposes of conversion""" + pass + + +r6 = accepts_callable(Class6) +reveal_type(r6) # `def () -> Class6Proxy` +assert_type(r6(), Class6Proxy) +r6(1) # E + + +class Class6Any: + """__new__ that causes __init__ to be ignored via Any""" + + def __new__(cls) -> Any: + return super().__new__(cls) + + def __init__(self, x: int) -> None: + """This __init__ is ignored for purposes of conversion""" + pass + + +r6_any = accepts_callable(Class6Any) +reveal_type(r6_any) # `def () -> Any` +assert_type(r6_any(), Any) +r6_any(1) # E + +# > If the __init__ or __new__ method is overloaded, the callable type should +# > be synthesized from the overloads. The resulting callable type itself will +# > be overloaded. + + +class Class7(Generic[T]): + @overload + def __init__(self: "Class7[int]", x: int) -> None: ... + @overload + def __init__(self: "Class7[str]", x: str) -> None: ... + def __init__(self, x: int | str) -> None: + pass + + +r7 = accepts_callable(Class7) +reveal_type( + r7 +) # overload of `def (x: int) -> Class7[int]` and `def (x: str) -> Class7[str]` +assert_type(r7(0), Class7[int]) +assert_type(r7(""), Class7[str]) + + +# > If the class is generic, the synthesized callable should include any +# > class-scoped type parameters that appear within the signature, but these +# > type parameters should be converted to function-scoped type parameters +# > for the callable. Any function-scoped type parameters in the __init__ +# > or __new__ method should also be included as function-scoped type parameters +# > in the synthesized callable. + + +class Class8(Generic[T]): + def __new__(cls, x: list[T], y: list[T]) -> Self: + return super().__new__(cls) + + +r8 = accepts_callable(Class8) +reveal_type(r8) # `def [T] (x: list[T], y: list[T]) -> Class8[T]` +assert_type(r8([""], [""]), Class8[str]) +r8([1], [""]) # E + + +class Class9: + def __init__(self, x: list[T], y: list[T]) -> None: + pass + + +r9 = accepts_callable(Class9) +reveal_type(r9) # `def [T] (x: list[T], y: list[T]) -> Class9` +assert_type(r9([""], [""]), Class9) +r9([1], [""]) # E diff --git a/conformance/tests/constructors_consistency.py b/conformance/tests/constructors_consistency.py new file mode 100644 index 000000000..14c37ebc3 --- /dev/null +++ b/conformance/tests/constructors_consistency.py @@ -0,0 +1,26 @@ +""" +Tests consistency checks between __new__ and __init__ methods. +""" + +# pyright: reportInconsistentConstructor=true + +# Specification: https://typing.readthedocs.io/en/latest/spec/constructors.html#consistency-of-new-and-init + +# Note: This functionality is optional in the typing spec, and conformant +# type checkers are not required to implement it. + + +# > Type checkers may optionally validate that the __new__ and __init__ +# > methods for a class have consistent signatures. + + +from typing import Self + + +class Class1: + def __new__(cls) -> Self: + return super().__new__(cls) + + # Type error: __new__ and __init__ have inconsistent signatures + def __init__(self, x: str) -> None: # E? + pass diff --git a/conformance/tests/dataclasses_descriptors.py b/conformance/tests/dataclasses_descriptors.py new file mode 100644 index 000000000..f2f30a623 --- /dev/null +++ b/conformance/tests/dataclasses_descriptors.py @@ -0,0 +1,68 @@ +""" +Tests the handling of descriptors within a dataclass. +""" + +# This portion of the dataclass spec is under-specified in the documentation, +# but its behavior can be determined from the runtime implementation. + +from dataclasses import dataclass +from typing import Any, Generic, TypeVar, assert_type, overload + +T = TypeVar("T") + + +class Desc1: + @overload + def __get__(self, __obj: None, __owner: Any) -> "Desc1": + ... + + @overload + def __get__(self, __obj: object, __owner: Any) -> int: + ... + + def __get__(self, __obj: object | None, __owner: Any) -> "int | Desc1": + raise NotImplementedError + + def __set__(self, __obj: object, __value: int) -> None: + ... + + +@dataclass +class DC1: + y: Desc1 = Desc1() + + +dc1 = DC1(3) + +assert_type(dc1.y, int) +assert_type(DC1.y, Desc1) + + +class Desc2(Generic[T]): + @overload + def __get__(self, instance: None, owner: Any) -> list[T]: + ... + + @overload + def __get__(self, instance: object, owner: Any) -> T: + ... + + def __get__(self, instance: object | None, owner: Any) -> list[T] | T: + raise NotImplementedError + + +@dataclass +class DC2: + x: Desc2[int] + y: Desc2[str] + z: Desc2[str] = Desc2() + + +assert_type(DC2.x, list[int]) +assert_type(DC2.y, list[str]) +assert_type(DC2.z, list[str]) + +dc2 = DC2(Desc2(), Desc2(), Desc2()) +assert_type(dc2.x, int) +assert_type(dc2.y, str) +assert_type(dc2.z, str) diff --git a/conformance/tests/dataclasses_final.py b/conformance/tests/dataclasses_final.py new file mode 100644 index 000000000..ed44f8924 --- /dev/null +++ b/conformance/tests/dataclasses_final.py @@ -0,0 +1,38 @@ +""" +Tests the handling of ClassVar and Final in dataclasses. +""" + +from dataclasses import dataclass +from typing import assert_type, ClassVar, Final + +# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html#dataclass-semantics + +# > A final class variable on a dataclass must be explicitly annotated as +# e.g. x: ClassVar[Final[int]] = 3. + + +@dataclass +class D: + final_no_default: Final[int] + final_with_default: Final[str] = "foo" + final_classvar: ClassVar[Final[int]] = 4 + # we don't require support for Final[ClassVar[...]] because the dataclasses + # runtime implementation won't recognize it as a ClassVar either + + +# An explicitly marked ClassVar can be accessed on the class: +assert_type(D.final_classvar, int) + +# ...but not assigned to, because it's Final: +D.final_classvar = 10 # E: can't assign to final attribute + +# A non-ClassVar attribute (with or without default) is a dataclass field: +d = D(final_no_default=1, final_with_default="bar") +assert_type(d.final_no_default, int) +assert_type(d.final_with_default, str) + +# ... but can't be assigned to (on the class or on an instance): +d.final_no_default = 10 # E: can't assign to final attribute +d.final_with_default = "baz" # E: can't assign to final attribute +D.final_no_default = 10 # E: can't assign to final attribute +D.final_with_default = "baz" # E: can't assign to final attribute diff --git a/conformance/tests/dataclasses_frozen.py b/conformance/tests/dataclasses_frozen.py new file mode 100644 index 000000000..c11de7e46 --- /dev/null +++ b/conformance/tests/dataclasses_frozen.py @@ -0,0 +1,41 @@ +""" +Tests validation of frozen dataclass instances. +""" + +# Specification: https://peps.python.org/pep-0557/#frozen-instances + +from dataclasses import dataclass + +@dataclass(frozen=True) +class DC1: + a: float + b: str + +dc1 = DC1(1, "") + +dc1.a = 1 # E: dataclass is frozen +dc1.b = "" # E: dataclass is frozen + + +# This should generate an error because a non-frozen dataclass +# cannot inherit from a frozen dataclass. +@dataclass # E[DC2] +class DC2(DC1): # E[DC2] + pass + +@dataclass +class DC3: + a: int + +# This should generate an error because a frozen dataclass +# cannot inherit from a non-frozen dataclass. +@dataclass(frozen=True) # E[DC4] +class DC4(DC3): # E[DC4] + pass + + +@dataclass(frozen=True) +class DC1Child(DC1): + # This should be allowed because attributes within a frozen + # dataclass are covariant rather than invariant. + a: int diff --git a/conformance/tests/dataclasses_hash.py b/conformance/tests/dataclasses_hash.py new file mode 100644 index 000000000..f2d441d68 --- /dev/null +++ b/conformance/tests/dataclasses_hash.py @@ -0,0 +1,86 @@ +""" +Tests the synthesis of the __hash__ method in a dataclass. +""" + +from dataclasses import dataclass +from typing import Callable, Hashable, assert_type + + +@dataclass +class DC1: + a: int + + +assert_type(DC1.__hash__, None) + +# These should generate errors because DC1 isn't hashable. +DC1(0).__hash__() # E +v1: Hashable = DC1(0) # E + + +@dataclass(eq=True, frozen=True) +class DC2: + a: int + + +dc2_hash: Callable[..., int] = DC2.__hash__ # OK +DC2(0).__hash__() # OK +v2: Hashable = DC2(0) # OK + + +@dataclass(eq=True) +class DC3: + a: int + + +assert_type(DC3.__hash__, None) + +# These should generate errors because DC3 isn't hashable. +DC3(0).__hash__() # E +v3: Hashable = DC3(0) # E + + +@dataclass(frozen=True) +class DC4: + a: int + + +dc4_hash: Callable[..., int] = DC4.__hash__ # OK +DC4(0).__hash__() # OK +v4: Hashable = DC4(0) # OK + + +@dataclass(eq=True, unsafe_hash=True) +class DC5: + a: int + + +dc5_hash: Callable[..., int] = DC5.__hash__ # OK +DC5(0).__hash__() # OK +v5: Hashable = DC5(0) # OK + + +@dataclass(eq=True) +class DC6: + a: int + + def __hash__(self) -> int: + return 0 + + +dc6_hash: Callable[..., int] = DC6.__hash__ # OK +DC6(0).__hash__() # OK +v6: Hashable = DC6(0) # OK + + +@dataclass(frozen=True) +class DC7: + a: int + + def __eq__(self, other) -> bool: + return self.a == other.a + + +dc7_hash: Callable[..., int] = DC7.__hash__ # OK +DC7(0).__hash__() # OK +v7: Hashable = DC7(0) # OK diff --git a/conformance/tests/dataclasses_inheritance.py b/conformance/tests/dataclasses_inheritance.py new file mode 100644 index 000000000..3ed0b88db --- /dev/null +++ b/conformance/tests/dataclasses_inheritance.py @@ -0,0 +1,66 @@ +""" +Tests inheritance rules for dataclasses. +""" + +# Specification: https://peps.python.org/pep-0557/#inheritance + +from dataclasses import dataclass +from typing import Any, ClassVar + + +@dataclass +class DC1: + a: int + b: str = "" + + +@dataclass +class DC2(DC1): + b: str = "" + a: int = 1 + + +dc2_1 = DC2(1, "") + +dc2_2 = DC2() + + +@dataclass +class DC3: + x: float = 15.0 + y: str = "" + + +@dataclass +class DC4(DC3): + z: tuple[int] = (10,) + x: float = 15 + + +dc4_1 = DC4(0.0, "", (1,)) + + +@dataclass +class DC5: + # While this generates an error at runtime for the stdlib dataclass, + # other libraries that use dataclass_transform don't have similar + # restrictions. It is therefore not required that a type checker + # report an error here. + x: list[int] = [] # E? + + +@dataclass +class DC6: + x: int + y: ClassVar[int] = 1 + + +@dataclass +class DC7(DC6): + # This should generate an error because a ClassVar cannot override + # an instance variable of the same name. + x: ClassVar[int] # E + + # This should generate an error because an instance variable cannot + # override a class variable of the same name. + y: int # E diff --git a/conformance/tests/dataclasses_kwonly.py b/conformance/tests/dataclasses_kwonly.py new file mode 100644 index 000000000..674d85fa9 --- /dev/null +++ b/conformance/tests/dataclasses_kwonly.py @@ -0,0 +1,62 @@ +""" +Tests the keyword-only feature of dataclass added in Python 3.10. +""" + +# Specification: https://docs.python.org/3/library/dataclasses.html#module-contents + +from dataclasses import dataclass, KW_ONLY, field + + +@dataclass +class DC1: + a: str + _: KW_ONLY + b: int = 0 + + +DC1("hi") +DC1(a="hi") +DC1(a="hi", b=1) +DC1("hi", b=1) + +# This should generate an error because "b" is keyword-only. +DC1("hi", 1) # E + + +@dataclass +class DC2: + b: int = field(kw_only=True, default=3) + a: str + + +DC2("hi") +DC2(a="hi") +DC2(a="hi", b=1) +DC2("hi", b=1) + +# This should generate an error because "b" is keyword-only. +DC2("hi", 1) # E + + +@dataclass(kw_only=True) +class DC3: + a: str = field(kw_only=False) + b: int = 0 + + +DC3("hi") +DC3(a="hi") +DC3(a="hi", b=1) +DC3("hi", b=1) + +# This should generate an error because "b" is keyword-only. +DC3("hi", 1) # E + + +@dataclass +class DC4(DC3): + c: float + + +DC4("", 0.2, b=3) +DC4(a="", b=3, c=0.2) diff --git a/conformance/tests/dataclasses_match_args.py b/conformance/tests/dataclasses_match_args.py new file mode 100644 index 000000000..6593fa205 --- /dev/null +++ b/conformance/tests/dataclasses_match_args.py @@ -0,0 +1,49 @@ +""" +Tests the match_args feature of dataclass added in Python 3.10. +""" + +# Specification: https://docs.python.org/3/library/dataclasses.html#module-contents + +from dataclasses import dataclass, KW_ONLY +from typing import assert_type, Literal + +# If true, the __match_args__ tuple will be created from the list of non keyword-only parameters to the generated __init__() method + +@dataclass(match_args=True) +class DC1: + x: int + _: KW_ONLY + y: str + +assert_type(DC1.__match_args__, tuple[Literal['x']]) + +# The match_args default is True + +@dataclass +class DC2: + x: int + +assert_type(DC2.__match_args__, tuple[Literal['x']]) + +# __match_args__ is created even if __init__() is not generated + +@dataclass(match_args=True, init=False) +class DC3: + x: int = 0 + +assert_type(DC3.__match_args__, tuple[Literal['x']]) + +# If false, or if __match_args__ is already defined in the class, then __match_args__ will not be generated. + +@dataclass(match_args=False) +class DC4: + x: int + +DC4.__match_args__ # E + +@dataclass(match_args=True) +class DC5: + __match_args__ = () + x: int + +assert_type(DC5.__match_args__, tuple[()]) diff --git a/conformance/tests/dataclasses_order.py b/conformance/tests/dataclasses_order.py new file mode 100644 index 000000000..27415d3a6 --- /dev/null +++ b/conformance/tests/dataclasses_order.py @@ -0,0 +1,54 @@ +""" +Tests the synthesized comparison methods for dataclasses. +""" + +from dataclasses import dataclass + +@dataclass(order=True) +class DC1: + a: str + b: int + + +@dataclass(order=True) +class DC2: + a: str + b: int + + +dc1_1 = DC1("", 0) +dc1_2 = DC1("", 0) + +if dc1_1 < dc1_2: + pass + +if dc1_1 <= dc1_2: + pass + +if dc1_1 > dc1_2: + pass + +if dc1_1 >= dc1_2: + pass + +if dc1_1 == dc1_2: + pass + +if dc1_1 != dc1_2: + pass + +if dc1_1 == None: + pass + +if dc1_1 != None: + pass + +dc2_1 = DC2("hi", 2) + +# This should generate an error because the types are +# incompatible. +if dc1_1 < dc2_1: # E: + pass + +if dc1_1 != dc2_1: + pass diff --git a/conformance/tests/dataclasses_postinit.py b/conformance/tests/dataclasses_postinit.py new file mode 100644 index 000000000..ca881e8e9 --- /dev/null +++ b/conformance/tests/dataclasses_postinit.py @@ -0,0 +1,55 @@ +""" +Tests type checking of the __post_init__ method in a dataclass. +""" + +# Specification: https://peps.python.org/pep-0557/#post-init-processing + +from dataclasses import InitVar, dataclass, field, replace +from typing import assert_type + + +@dataclass +class DC1: + a: int + b: int + x: InitVar[int] + c: int + y: InitVar[str] + + def __post_init__(self, x: int, y: int) -> None: # E: wrong type for y + pass + + +dc1 = DC1(1, 2, 3, 4, "") + +assert_type(dc1.a, int) +assert_type(dc1.b, int) +assert_type(dc1.c, int) +print(dc1.x) # E: cannot access InitVar +print(dc1.y) # E: cannot access InitVar + +@dataclass +class DC2: + x: InitVar[int] + y: InitVar[str] + + def __post_init__(self, x: int) -> None: # E: missing y + pass + + +@dataclass +class DC3: + _name: InitVar[str] = field() + name: str = field(init=False) + + def __post_init__(self, _name: str): + ... + + +@dataclass +class DC4(DC3): + _age: InitVar[int] = field() + age: int = field(init=False) + + def __post_init__(self, _name: str, _age: int): + ... diff --git a/conformance/tests/dataclasses_slots.py b/conformance/tests/dataclasses_slots.py new file mode 100644 index 000000000..52ee1f6b0 --- /dev/null +++ b/conformance/tests/dataclasses_slots.py @@ -0,0 +1,69 @@ +""" +Tests the slots functionality of dataclass added in Python 3.10. +""" + +# Specification: https://docs.python.org/3/library/dataclasses.html#module-contents + +from dataclasses import dataclass + +# This should generate an error because __slots__ is already defined. +@dataclass(slots=True) # E[DC1] +class DC1: # E[DC1] + x: int + + __slots__ = () + + +@dataclass(slots=True) +class DC2: + x: int + + def __init__(self): + self.x = 3 + + # This should generate an error because "y" is not in slots. + self.y = 3 # E + + +@dataclass(slots=False) +class DC3: + x: int + + __slots__ = ("x",) + + def __init__(self): + self.x = 3 + + # This should generate an error because "y" is not in slots. + self.y = 3 # E + + +@dataclass +class DC4: + __slots__ = ("y", "x") + x: int + y: str + + +DC4(1, "bar") + + +@dataclass(slots=True) +class DC5: + a: int + + +DC5.__slots__ +DC5(1).__slots__ + + +@dataclass +class DC6: + a: int + + +# This should generate an error because __slots__ is not defined. +DC6.__slots__ # E + +# This should generate an error because __slots__ is not defined. +DC6(1).__slots__ # E diff --git a/conformance/tests/dataclasses_transform_class.py b/conformance/tests/dataclasses_transform_class.py new file mode 100644 index 000000000..da7ab82d4 --- /dev/null +++ b/conformance/tests/dataclasses_transform_class.py @@ -0,0 +1,122 @@ +""" +Tests the dataclass_transform mechanism when it is applied to a base class. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html#the-dataclass-transform-decorator + +from typing import Any, Generic, TypeVar, dataclass_transform + +T = TypeVar("T") + + +class ModelField: + def __init__(self, *, init: bool = True, default: Any | None = None) -> None: + ... + + +def model_field( + *, init: bool = True, default: Any | None = None, alias: str | None = None +) -> Any: + raise NotImplementedError + + +@dataclass_transform( + kw_only_default=True, + field_specifiers=(ModelField, model_field), +) +class ModelBase: + not_a_field: str + + def __init_subclass__( + cls, + *, + frozen: bool = False, + kw_only: bool = True, + order: bool = True, + ) -> None: + ... + + def __init__(self, not_a_field: str) -> None: + self.not_a_field = not_a_field + + +class Customer1(ModelBase, frozen=True): + id: int = model_field() + name: str = model_field() + name2: str = model_field(alias="other_name", default="None") + + +# This should generate an error because a non-frozen dataclass cannot +# derive from a frozen one. +class Customer1Subclass(Customer1): # E + salary: float = model_field() + + +class Customer2(ModelBase, order=True): + id: int + name: str = model_field(default="None") + + +c1_1 = Customer1(id=3, name="Sue", other_name="Susan") + +# This should generate an error because the class is frozen. +c1_1.id = 4 # E + +# This should generate an error because the class is kw_only. +c1_2 = Customer1(3, "Sue") # E + +c1_3 = Customer1(id=3, name="John") + +# This should generate an error because comparison methods are +# not synthesized. +v1 = c1_1 < c1_2 # E + +c2_1 = Customer2(id=0, name="John") + +c2_2 = Customer2(id=1) + +v2 = c2_1 < c2_2 + +# This should generate an error because Customer2 supports +# keyword-only parameters for its constructor. +c2_3 = Customer2(0, "John") # E + + +@dataclass_transform( + kw_only_default=True, + field_specifiers=(ModelField, model_field), +) +class GenericModelBase(Generic[T]): + not_a_field: T + + def __init_subclass__( + cls, + *, + frozen: bool = False, + kw_only: bool = True, + order: bool = True, + ) -> None: + ... + + +class GenericCustomer(GenericModelBase[int]): + id: int = model_field() + + +gc_1 = GenericCustomer(id=3) + + +@dataclass_transform(frozen_default=True) +class ModelBaseFrozen: + not_a_field: str + + +class Customer3(ModelBaseFrozen): + id: int + name: str + + +c3_1 = Customer3(id=2, name="hi") + +# This should generate an error because Customer3 is frozen. +c3_1.id = 4 # E diff --git a/conformance/tests/dataclasses_transform_converter.py b/conformance/tests/dataclasses_transform_converter.py new file mode 100644 index 000000000..0a54584ea --- /dev/null +++ b/conformance/tests/dataclasses_transform_converter.py @@ -0,0 +1,133 @@ +""" +Tests the dataclass_transform mechanism supports the "converter" parameter +in a field specifier class. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html#converters + +from typing import ( + Any, + Callable, + TypeVar, + dataclass_transform, + overload, +) + +T = TypeVar("T") +S = TypeVar("S") + + +def model_field( + *, + converter: Callable[[S], T], + default: S | None = None, + default_factory: Callable[[], S] | None = None, +) -> T: + raise NotImplementedError + + +@dataclass_transform(field_specifiers=(model_field,)) +class ModelBase: + ... + + +# > The converter must be a callable that must accept a single positional +# > argument (but may accept other optional arguments, which are ignored for +# > typing purposes). + + +def bad_converter1() -> int: + return 0 + + +def bad_converter2(*, x: int) -> int: + return 0 + + +class DC1(ModelBase): + field1: int = model_field(converter=bad_converter1) # E + field2: int = model_field(converter=bad_converter2) # E + + +# > The type of the first positional parameter provides the type of the +# > synthesized __init__ parameter for the field. + +# > The return type of the callable must be assignable to the field’s +# > declared type. + + +def converter_simple(s: str) -> int: + return int(s) + + +def converter_with_param_before_args(s: str, *args: int, **kwargs: int) -> int: + return int(s) + + +def converter_with_args(*args: str) -> int: + return int(args[0]) + + +@overload +def overloaded_converter(s: str) -> int: + ... + + +@overload +def overloaded_converter(s: list[str]) -> int: + ... + + +def overloaded_converter(s: str | list[str], *args: str) -> int | str: + return 0 + + +class ConverterClass: + @overload + def __init__(self, val: str) -> None: + ... + + @overload + def __init__(self, val: bytes) -> None: + ... + + def __init__(self, val: str | bytes) -> None: + pass + + +class DC2(ModelBase): + field0: int = model_field(converter=converter_simple) + field1: int = model_field(converter=converter_with_param_before_args) + field2: int = model_field(converter=converter_with_args) + field3: ConverterClass = model_field(converter=ConverterClass) + field4: int = model_field(converter=overloaded_converter) + field5: dict[str, str] = model_field(converter=dict, default=()) + + +DC2(1, "f1", "f2", b"f3", []) # E +DC2("f0", "f1", "f2", 1, []) # E +DC2("f0", "f1", "f2", "f3", 3j) # E + + +dc1 = DC2("f0", "f1", "f2", b"f6", []) + +dc1.field0 = "f1" +dc1.field3 = "f6" +dc1.field3 = b"f6" + +dc1.field0 = 1 # E +dc1.field3 = 1 # E + +dc2 = DC2("f0", "f1", "f2", "f6", "1", (("a", "1"), ("b", "2"))) + + +# > If default or default_factory are provided, the type of the default value +# > should be assignable to the first positional parameter of the converter. + + +class DC3(ModelBase): + field0: int = model_field(converter=converter_simple, default="") + field1: int = model_field(converter=converter_simple, default=1) # E + + field2: int = model_field(converter=converter_simple, default_factory=str) + field3: int = model_field(converter=converter_simple, default_factory=int) # E diff --git a/conformance/tests/dataclasses_transform_field.py b/conformance/tests/dataclasses_transform_field.py new file mode 100644 index 000000000..14df5677e --- /dev/null +++ b/conformance/tests/dataclasses_transform_field.py @@ -0,0 +1,77 @@ +""" +Tests the dataclass_transform mechanism honors implicit default values +in field parameters. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html#field-specifier-parameters + +from typing import Any, Callable, Literal, TypeVar, dataclass_transform, overload + +T = TypeVar("T") + +# > Field specifier functions can use overloads that implicitly specify the +# > value of init using a literal bool value type (Literal[False] or Literal[True]). + +@overload +def field1( + *, + default: str | None = None, + resolver: Callable[[], Any], + init: Literal[False] = False, +) -> Any: + ... + + +@overload +def field1( + *, + default: str | None = None, + resolver: None = None, + init: Literal[True] = True, +) -> Any: + ... + + +def field1( + *, + default: str | None = None, + resolver: Callable[[], Any] | None = None, + init: bool = True, +) -> Any: + ... + + +def field2(*, init: bool = False, kw_only: bool = True) -> Any: + raise NotImplementedError + + +@dataclass_transform(kw_only_default=True, field_specifiers=(field1, field2)) +def create_model(*, init: bool = True) -> Callable[[type[T]], type[T]]: + raise NotImplementedError + + +@create_model() +class CustomerModel1: + id: int = field1(resolver=lambda: 0) + name: str = field1(default="Voldemort") + + +CustomerModel1() +CustomerModel1(name="hi") + +# This should generate an error because "id" is not +# supposed to be part of the init function. +CustomerModel1(id=1, name="hi") # E + + +@create_model() +class CustomerModel2: + id: int = field2() + name: str = field2(init=True) + + +# This should generate an error because kw_only is True +# by default for field2. +CustomerModel2(1) # E + +CustomerModel2(name="Fred") diff --git a/conformance/tests/dataclasses_transform_func.py b/conformance/tests/dataclasses_transform_func.py new file mode 100644 index 000000000..bdff17c82 --- /dev/null +++ b/conformance/tests/dataclasses_transform_func.py @@ -0,0 +1,96 @@ +""" +Tests the dataclass_transform mechanism when it is applied to a decorator function. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html#the-dataclass-transform-decorator + +from typing import Any, Callable, TypeVar, dataclass_transform, overload + +T = TypeVar("T") + + +@overload +@dataclass_transform(kw_only_default=True, order_default=True) +def create_model(cls: T) -> T: + ... + + +@overload +def create_model( + *, + frozen: bool = False, + kw_only: bool = True, + order: bool = True, +) -> Callable[[T], T]: + ... + + +def create_model(*args: Any, **kwargs: Any) -> Any: + raise NotImplementedError + + +@create_model(kw_only=False, order=False) +class Customer1: + id: int + name: str + + +@create_model(frozen=True) +class Customer2: + id: int + name: str + + +@create_model(frozen=True) +class Customer2Subclass(Customer2): + salary: float + + +c1_1 = Customer1(id=3, name="Sue") +c1_1.id = 4 + +c1_2 = Customer1(3, "Sue") +c1_2.name = "Susan" + +# This should generate an error because of a type mismatch. +c1_2.name = 3 # E + +# This should generate an error because comparison methods are +# not synthesized. +v1 = c1_1 < c1_2 # E + +# This should generate an error because salary is not +# a defined field. +c1_3 = Customer1(id=3, name="Sue", salary=40000) # E + +c2_1 = Customer2(id=0, name="John") + +# This should generate an error because Customer2 supports +# keyword-only parameters for its constructor. +c2_2 = Customer2(0, "John") # E + +v2 = c2_1 < c2_2 + + +@dataclass_transform(kw_only_default=True, order_default=True, frozen_default=True) +def create_model_frozen(cls: T) -> T: + raise NotImplementedError + + +@create_model_frozen +class Customer3: + id: int + name: str + + +# This should generate an error because a non-frozen class +# cannot inherit from a frozen class. +@create_model # E[Customer3Subclass] +class Customer3Subclass(Customer3): # E[Customer3Subclass] + age: int + + +c3_1 = Customer3(id=2, name="hi") + +# This should generate an error because Customer3 is frozen. +c3_1.id = 4 # E diff --git a/conformance/tests/dataclasses_transform_meta.py b/conformance/tests/dataclasses_transform_meta.py new file mode 100644 index 000000000..cf33df87d --- /dev/null +++ b/conformance/tests/dataclasses_transform_meta.py @@ -0,0 +1,103 @@ +""" +Tests the dataclass_transform mechanism when it is applied to a metaclass. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html#the-dataclass-transform-decorator + + +from typing import Any, dataclass_transform + +class ModelField: + def __init__(self, *, init: bool = True, default: Any | None = None) -> None: + ... + + +def model_field( + *, init: bool = True, default: Any | None = None, alias: str | None = None +) -> Any: + raise NotImplementedError + + +@dataclass_transform( + kw_only_default=True, + field_specifiers=(ModelField, model_field), +) +class ModelMeta(type): + not_a_field: str + + def __init__(self, *args, **kwargs) -> None: + self.not_a_field: str = "not a field" + + +class ModelBase(metaclass=ModelMeta): + def __init_subclass__( + cls, + *, + frozen: bool = False, + kw_only: bool = True, + order: bool = True, + ) -> None: + ... + + +class Customer1(ModelBase, frozen=True): + id: int = model_field() + name: str = model_field() + name2: str = model_field(alias="other_name", default="None") + + +# This should generate an error because a non-frozen class cannot +# derive from a frozen one. +class Customer1Subclass(Customer1, frozen=False): # E + salary: float = model_field() + + +class Customer2(ModelBase, order=True): + id: int + name: str = model_field(default="None") + + +c1_1 = Customer1(id=3, name="Sue", other_name="Susan") + +# This should generate an error because the class is frozen. +c1_1.id = 4 # E + +# This should generate an error because the class is kw_only. +c1_2 = Customer1(3, "Sue") # E + +# OK (other_name is optional). +c1_3 = Customer1(id=3, name="John") + +# This should generate an error because comparison methods are +# not synthesized. +v1 = c1_1 < c1_2 # E + +c2_1 = Customer2(id=0, name="John") + +c2_2 = Customer2(id=1) + +v2 = c2_1 < c2_2 + +# This should generate an error because Customer2 supports +# keyword-only parameters for its constructor. +c2_3 = Customer2(0, "John") # E + + +@dataclass_transform(frozen_default=True) +class ModelMetaFrozen(type): + pass + + +class ModelBaseFrozen(metaclass=ModelMetaFrozen): + ... + + +class Customer3(ModelBaseFrozen): + id: int + name: str + + +c3_1 = Customer3(id=2, name="hi") + +# This should generate an error because Customer3 is frozen. +c3_1.id = 4 # E diff --git a/conformance/tests/dataclasses_usage.py b/conformance/tests/dataclasses_usage.py new file mode 100644 index 000000000..6e9c0e6bd --- /dev/null +++ b/conformance/tests/dataclasses_usage.py @@ -0,0 +1,246 @@ +""" +Tests basic handling of the dataclass factory. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/dataclasses.html +# Also, see https://peps.python.org/pep-0557/ + +from dataclasses import InitVar, dataclass, field +from typing import Any, Callable, ClassVar, Generic, Protocol, TypeVar, assert_type +import sys + +T = TypeVar("T") + + +@dataclass(order=True) +class InventoryItem: + x = 0 + name: str + unit_price: float + quantity_on_hand: int = 0 + + def total_cost(self) -> float: + return self.unit_price * self.quantity_on_hand + + +v1 = InventoryItem("soap", 2.3) + + +class InventoryItemInitProto(Protocol): + def __call__( + self, name: str, unit_price: float, quantity_on_hand: int = ... + ) -> None: ... + + +# Validate the type of the synthesized __init__ method. +x1: InventoryItemInitProto = v1.__init__ + +# Make sure the following additional methods were synthesized. +print(v1.__repr__) +print(v1.__eq__) +print(v1.__ne__) +print(v1.__lt__) +print(v1.__le__) +print(v1.__gt__) +print(v1.__ge__) + +assert_type(v1.name, str) +assert_type(v1.unit_price, float) +assert_type(v1.quantity_on_hand, int) + +v2 = InventoryItem("name") # E: missing unit_price +v3 = InventoryItem("name", "price") # E: incorrect type for unit_price +v4 = InventoryItem("name", 3.1, 3, 4) # E: too many arguments + + +# > TypeError will be raised if a field without a default value follows a +# > field with a default value. This is true either when this occurs in a +# > single class, or as a result of class inheritance. +@dataclass # E[DC1] +class DC1: + a: int = 0 # E[DC1]: field with default should follow a field with no default. + b: int # E[DC1]: field with no default cannot follow field with default. + + +@dataclass # E[DC2] +class DC2: + a: int = field(default=1) # E[DC2]: field with default should follow a field with no default. + b: int # E[DC2]: field with no default cannot follow field with default. + + +@dataclass # E[DC3] +class DC3: + a: InitVar[int] = 0 # E[DC3]: field with default should follow a field with no default. + b: int # E[DC3]: field with no default cannot follow field with default. + + +@dataclass +class DC4: + a: int = field(init=False) + b: int + + +v5 = DC4(0) +v6 = DC4(0, 1) # E: too many parameters + + +@dataclass +class DC5: + a: int = field(default_factory=str) # E: type mismatch + + +def f(s: str) -> int: + return int(s) + + +@dataclass +class DC6: + a: ClassVar[int] = 0 + b: str + c: Callable[[str], int] = f + + +dc6 = DC6("") +assert_type(dc6.a, int) +assert_type(DC6.a, int) +assert_type(dc6.b, str) +assert_type(dc6.c, Callable[[str], int]) + + +@dataclass +class DC7: + x: int + + +@dataclass(init=False) +class DC8(DC7): + y: int + + def __init__(self, a: DC7, y: int): + self.__dict__ = a.__dict__ + self.y = y + + +a = DC7(3) +b = DC8(a, 5) + +# This should generate an error because there is an extra parameter +DC7(3, 4) # E + +# This should generate an error because there is one too few parameters +DC8(a) # E + + +@dataclass +class DC9: + a: str = field(init=False, default="s") + b: bool = field() + + +@dataclass +class DC10(DC9): + a: str = field() + b: bool = field() + + +@dataclass(init=False) +class DC11: + x: int + x_squared: int + + def __init__(self, x: int): + self.x = x + self.x_squared = x * x + + +DC11(3) + + +@dataclass(init=True) +class DC12: + x: int + x_squared: int + + def __init__(self, x: int): + self.x = x + self.x_squared = x * x + + +DC12(3) + + +@dataclass(init=False) +class DC13: + x: int + x_squared: int + + +# This should generate an error because there is no matching +# override __init__ method and no synthesized __init__. +DC13(3) # E + + +@dataclass +class DC14: + prop_1: str = field(init=False) + prop_2: str = field(default="hello") + prop_3: str = field(default_factory=lambda: "hello") + + +@dataclass +class DC15(DC14): + prop_2: str = "" + + +dc15 = DC15(prop_2="test") + +assert_type(dc15.prop_1, str) +assert_type(dc15.prop_2, str) +assert_type(dc15.prop_3, str) + + +class DataclassProto(Protocol): + __dataclass_fields__: ClassVar[dict[str, Any]] + + +v7: DataclassProto = dc15 + + +@dataclass +class DC16(Generic[T]): + value: T + + +assert_type(DC16(1), DC16[int]) + + +class DC17(DC16[str]): + pass + + +assert_type(DC17(""), DC17) + + +@dataclass +class DC18: + x: int = field() + # This may generate a type checker error because an unannotated field + # will result in a runtime exception. + y = field() # E? + + +# > Dataclass fields may be conditional, via checks of the same +# > statically-known conditions that a type-checker understands elsewhere, +# > such as Python version: + +@dataclass +class DC19: + x: int + if sys.version_info >= (3, 12): + y: int + if sys.version_info >= (4, 0): + z: int + +# The conformance suite runs type checkers configured to Python 3.12 or later: +DC19(1, 2) +DC19(1, 2, 3) # E diff --git a/conformance/tests/directives_assert_type.py b/conformance/tests/directives_assert_type.py new file mode 100644 index 000000000..900529b12 --- /dev/null +++ b/conformance/tests/directives_assert_type.py @@ -0,0 +1,45 @@ +""" +Tests the typing.assert_type function. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#assert-type + +from typing import Annotated, Any, Literal, assert_type + + +# > When a type checker encounters a call to assert_type(), it should +# > emit an error if the value is not of the specified type. + + +def func1( + a: int | str, + b: list[int], + c: Any, + d: "ForwardReference", + e: Annotated[Literal[4], ""], +): + assert_type(a, int | str) # OK + assert_type(b, list[int]) # OK + assert_type(c, Any) # OK + assert_type(d, "ForwardReference") # OK + assert_type(e, Literal[4]) # OK + + assert_type(a, int) # E: Type mismatch + assert_type(a, Any) # E: Type mismatch + assert_type(c, int) # E: Type mismatch + assert_type(e, int) # E: Type mismatch + + assert_type() # E: not enough arguments + assert_type("", int) # E: wrong argument type + assert_type(a, int | str, a) # E: too many arguments + + +# > If the two types are :term:`equivalent` but syntactically different, +# > the type checker may reject the ``assert_type()`` call:: + +def func2(name: str): + assert_type(name, str | Literal["spam"]) # E?: Equivalent but not identical + + +class ForwardReference: + pass diff --git a/conformance/tests/directives_cast.py b/conformance/tests/directives_cast.py new file mode 100644 index 000000000..91a35a19a --- /dev/null +++ b/conformance/tests/directives_cast.py @@ -0,0 +1,17 @@ +""" +Tests the typing.cast call. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#cast + +from typing import cast + +def find_first_str(a: list[object]) -> str: + index = next(i for i, x in enumerate(a) if isinstance(x, str)) + return cast(str, a[index]) + +x: int = cast(int, "not an int") # No type error + +bad1 = cast() # E: Too few arguments +bad2 = cast(1, "") # E: Bad first argument type +bad3 = cast(int, "", "") # E: Too many arguments diff --git a/conformance/tests/directives_deprecated.py b/conformance/tests/directives_deprecated.py new file mode 100644 index 000000000..e56b470c4 --- /dev/null +++ b/conformance/tests/directives_deprecated.py @@ -0,0 +1,120 @@ +""" +Tests the warnings.deprecated function. +""" + +# pyright: reportDeprecated=true + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#deprecated +# See also https://peps.python.org/pep-0702/ + +from typing import Protocol, override +from typing_extensions import deprecated + +# > Type checkers should produce a diagnostic whenever they encounter a usage of an object +# > marked as deprecated. [...] For deprecated classes and functions, this includes: + +# > * `from` imports + +from _directives_deprecated_library import Ham # E: Use of deprecated class Ham +import _directives_deprecated_library as library + + +# > * References through module, class, or instance attributes + +library.norwegian_blue(1) # E: Use of deprecated function norwegian_blue +map(library.norwegian_blue, [1, 2, 3]) # E: Use of deprecated function norwegian_blue + + +# > For deprecated overloads, this includes all calls that resolve to the deprecated overload. + +library.foo(1) # E: Use of deprecated overload for foo +library.foo("x") # OK + + +ham = Ham() # E?: OK (already reported above) + + +# > * Any syntax that indirectly triggers a call to the function. + +spam = library.Spam() + +_ = spam + 1 # E: Use of deprecated method Spam.__add__ +spam += 1 # E: Use of deprecated method Spam.__add__ + +spam.greasy # E: Use of deprecated property Spam.greasy +spam.shape # OK + +spam.shape = "cube" # E: Use of deprecated property setter Spam.shape +spam.shape += "cube" # E: Use of deprecated property setter Spam.shape + + +class Invocable: + @deprecated("Deprecated") + def __call__(self) -> None: + ... + + +invocable = Invocable() +invocable() # E: Use of deprecated method __call__ + + +# > * Any usage of deprecated objects in their defining module + + +@deprecated("Deprecated") +def lorem() -> None: + ... + + +lorem() # E: Use of deprecated function lorem + + +# > There are additional scenarios where deprecations could come into play. +# > For example, an object may implement a `typing.Protocol`, +# > but one of the methods required for protocol compliance is deprecated. +# > As scenarios such as this one appear complex and relatively unlikely to come up in practice, +# > this PEP does not mandate that type checkers detect them. + + +class SupportsFoo1(Protocol): + @deprecated("Deprecated") + def foo(self) -> None: + ... + + def bar(self) -> None: + ... + + +class FooConcrete1(SupportsFoo1): + @override + def foo(self) -> None: # E?: Implementation of deprecated method foo + ... + + def bar(self) -> None: + ... + + +def foo_it(f: SupportsFoo1) -> None: + f.foo() # E: Use of deprecated method foo + f.bar() + + +class SupportsFoo2(Protocol): + def foo(self) -> None: + ... + + +class FooConcrete2: + @deprecated("Deprecated") + def foo(self) -> None: + ... + + +def takes_foo(f: SupportsFoo2) -> None: + ... + + +def caller(c: FooConcrete2) -> None: + takes_foo( + c + ) # E?: FooConcrete2 is a SupportsFoo2, but only because of a deprecated method diff --git a/conformance/tests/directives_disjoint_base.py b/conformance/tests/directives_disjoint_base.py new file mode 100644 index 000000000..49ce5d218 --- /dev/null +++ b/conformance/tests/directives_disjoint_base.py @@ -0,0 +1,135 @@ +""" +Tests the typing.disjoint_base decorator introduced in PEP 800. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#disjoint-base +# See also https://peps.python.org/pep-0800/ + +from typing import NamedTuple, Protocol, TypedDict, assert_never +from typing_extensions import disjoint_base + + +# > It may only be used on nominal classes, including ``NamedTuple`` +# > definitions + + +@disjoint_base +class Left: + pass + + +@disjoint_base +class Right: + pass + + +@disjoint_base +class LeftChild(Left): + pass + + +@disjoint_base +class Record(NamedTuple): + value: int + + +class Plain: + pass + + +# > If the candidate set contains a single disjoint base, that is the +# > class's disjoint base. + + +class OtherLeftChild(Left): + pass + + +# > If there are multiple candidates, but one of them is a subclass of +# > all other candidates, that class is the disjoint base. + + +class LeftAndPlain(Left, Plain): + pass + + +class LeftChildAndLeft(LeftChild, Left): + pass + + +class PlainRecord(Plain, Record): + pass + + +# > Type checkers must check for a valid disjoint base when checking class definitions, +# > and emit a diagnostic if they encounter a class +# > definition that lacks a valid disjoint base. + + +class LeftAndRight(Left, Right): # E: incompatible disjoint bases + pass + + +class LeftChildAndRight(LeftChild, Right): # E: incompatible disjoint bases + pass + + +class LeftAndRightViaChild(LeftAndPlain, Right): # E: incompatible disjoint bases + pass + + +class LeftRecord(Left, Record): # E: incompatible disjoint bases + pass + + +# > A nominal class is a disjoint base if it [...] contains a non-empty +# > `__slots__` definition. + + +class SlotBase1: + __slots__ = ("x",) + + +class SlotBase2: + __slots__ = ("y",) + + +class EmptySlots: + __slots__ = () + + +class SlotAndEmptySlots(SlotBase1, EmptySlots): + pass + + +class IncompatibleSlots(SlotBase1, SlotBase2): # E: incompatible disjoint bases + pass + + +# > it is a type checker error to use the decorator on a function, +# > ``TypedDict`` definition, or ``Protocol`` definition. + + +@disjoint_base # E: disjoint_base cannot be applied to a function +def func() -> None: + pass + + +@disjoint_base # E: disjoint_base cannot be applied to a TypedDict +class Movie(TypedDict): + name: str + + +@disjoint_base # E: disjoint_base cannot be applied to a Protocol +class SupportsClose(Protocol): + def close(self) -> None: + ... + + +# > Type checkers may use disjoint bases to determine that two classes cannot +# > have a common subclass. + + +def narrow(obj: Left) -> None: + if isinstance(obj, Right): # E?: may be treated as unreachable + assert_never(obj) # E?: may not be narrowed to Never diff --git a/conformance/tests/directives_no_type_check.py b/conformance/tests/directives_no_type_check.py new file mode 100644 index 000000000..ddf278c74 --- /dev/null +++ b/conformance/tests/directives_no_type_check.py @@ -0,0 +1,32 @@ +""" +Tests the typing.no_type_check decorator. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#no-type-check +# "E?" is used below because support is optional. + +from typing import no_type_check + +# > The behavior for the ``no_type_check`` decorator when applied to a class is +# > left undefined by the typing spec at this time. + +@no_type_check +class ClassA: + x: int = "" # E?: No error should be reported + + +# > If a type checker supports the ``no_type_check`` decorator for functions, it +# > should suppress all type errors for the ``def`` statement and its body including +# > any nested functions or classes. It should also ignore all parameter +# > and return type annotations and treat the function as if it were unannotated. + +@no_type_check +def func1(a: int, b: str) -> None: + c = a + b # E?: No error should be reported + return 1 # E?: No error should be reported + + +func1(b"invalid", b"arguments") # E?: No error should be reported +# This should still be an error because type checkers should ignore +# annotations, but still check the argument count. +func1() # E: incorrect arguments for parameters diff --git a/conformance/tests/directives_reveal_type.py b/conformance/tests/directives_reveal_type.py new file mode 100644 index 000000000..e90844c34 --- /dev/null +++ b/conformance/tests/directives_reveal_type.py @@ -0,0 +1,24 @@ +""" +Tests the typing.reveal_type function. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#reveal-type + +from typing import Any, reveal_type + +# > When a static type checker encounters a call to this function, it should +# > emit a diagnostic with the type of the argument. + + +def func1(a: int | str, b: list[int], c: Any, d: "ForwardReference"): + reveal_type(a) # Revealed type is "int | str" + reveal_type(b) # Revealed type is "list[int]" + reveal_type(c) # Revealed type is "Any" + reveal_type(d) # Revealed type is "ForwardReference" + + reveal_type() # E: not enough arguments + reveal_type(a, a) # E: Too many arguments + + +class ForwardReference: + pass diff --git a/conformance/tests/directives_type_checking.py b/conformance/tests/directives_type_checking.py new file mode 100644 index 000000000..55a2d52df --- /dev/null +++ b/conformance/tests/directives_type_checking.py @@ -0,0 +1,18 @@ +""" +Tests the typing.TYPE_CHECKING constant. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#type-checking + +from typing import TYPE_CHECKING, assert_type + + +if not TYPE_CHECKING: + a: int = "" # This should not generate an error + +if TYPE_CHECKING: + b: list[int] = [1, 2, 3] +else: + b: list[str] = ["a", "b", "c"] + +assert_type(b, list[int]) diff --git a/conformance/tests/directives_type_ignore.py b/conformance/tests/directives_type_ignore.py new file mode 100644 index 000000000..126f3e54d --- /dev/null +++ b/conformance/tests/directives_type_ignore.py @@ -0,0 +1,22 @@ +""" +Tests "# type: ignore" comments. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#type-ignore-comments + +# The following type violation should be suppressed. +x: int = "" # type: ignore + +# The following type violation should be suppressed. +y: int = "" # type: ignore - additional stuff + +# The following type violation may be suppressed, if the type checker +# actually uses the code `an-empty-str-is-not-an-int` (unlikely!), or if +# it treats unknown codes as blanket ignores. +z: int = "" # type: ignore[an-empty-str-is-not-an-int] # E? + +# > In some cases, linting tools or other comments may be needed on the same +# > line as a type comment. In these cases, the type comment should be before +# > other comments and linting markers. + +a: int = "" # type: ignore # other comment diff --git a/conformance/tests/directives_type_ignore_file1.py b/conformance/tests/directives_type_ignore_file1.py new file mode 100644 index 000000000..e89e97102 --- /dev/null +++ b/conformance/tests/directives_type_ignore_file1.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python + +# type: ignore + +""" +Tests a file-level type ignore comment. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#type-ignore-comments + +# > A # type: ignore comment on a line by itself at the top of a file, before any +# > docstrings, imports, or other executable code, silences all errors in the file. +# > Blank lines and other comments, such as shebang lines and coding cookies, may +# > precede the # type: ignore comment. + +x: int = "" # No error should be reported diff --git a/conformance/tests/directives_type_ignore_file2.py b/conformance/tests/directives_type_ignore_file2.py new file mode 100644 index 000000000..932cb3a84 --- /dev/null +++ b/conformance/tests/directives_type_ignore_file2.py @@ -0,0 +1,14 @@ +""" +Tests a file-level type ignore comment. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#type-ignore-comments + +# type: ignore + +# > A # type: ignore comment on a line by itself at the top of a file, before any +# > docstrings, imports, or other executable code, silences all errors in the file. +# > Blank lines and other comments, such as shebang lines and coding cookies, may +# > precede the # type: ignore comment. + +x: int = "" # E: should still error because comment is not at top of file. diff --git a/conformance/tests/directives_version_platform.py b/conformance/tests/directives_version_platform.py new file mode 100644 index 000000000..463c0e4d6 --- /dev/null +++ b/conformance/tests/directives_version_platform.py @@ -0,0 +1,75 @@ +""" +Tests checking the version or platform. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/directives.html#version-and-platform-checking + +import os +import sys +from typing import assert_type + +def test(a: int, b: str): + val1: int | str + if sys.version_info >= (3, 8): + val1 = a + else: + val1 = b + + assert_type(val1, int) + + val2: int | str + if sys.version_info >= (3, 8, 0): + val2 = a + else: + val2 = b + + assert_type(val2, int) # E?: May not generate an error (support for three-element sys.version is optional) + + if sys.version_info < (3, 8): + val3 = "" + else: + val4 = "" + + this_raises = val3 # E: `val3` is undefined + does_not_raise = val4 # should not error + + val5: int | str + if sys.version_info < (3, 100, 0): + val5 = a + else: + val5 = b + + assert_type(val5, int) # E?: May not generate an error (support for three-element sys.version is optional) + + + if sys.platform == "bogus_platform": + val6 = "" + else: + val7 = "" + + this_raises = val6 # E: `val6` is undefined + does_not_raise = val7 # should not error + + if sys.platform != "bogus_platform": + val8 = "" + else: + val9 = "" + + does_not_raise = val8 # should not error + this_raises = val9 # E: `val9` is undefined + + if os.name == "bogus_os": + val10 = "" + else: + val11 = "" + + this_raises = val10 # E?: `val10` is undefined, but support for `os.name` is optional + does_not_raise = val11 # E? should not error if `os.name` control flow is supported, but might be flagged as possibly undefined otherwise + + if os.name != "bogus_os": + val12 = "" + else: + val13 = "" + + does_not_raise = val12 # E?: should not error if `os.name` control flow is supported, but might be flagged as possibly undefined otherwise + this_raises = val13 # E?: `val13` is undefined, but support for `os.name` is optional diff --git a/conformance/tests/enums_behaviors.py b/conformance/tests/enums_behaviors.py new file mode 100644 index 000000000..eeb74ddfd --- /dev/null +++ b/conformance/tests/enums_behaviors.py @@ -0,0 +1,45 @@ +""" +Tests basic behaviors of of Enum classes. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/enums.html#enum-definition + +from enum import Enum +from typing import assert_type, Literal + +# > Enum classes are iterable and indexable, and they can be called with a +# > value to look up the enum member with that value. Type checkers should +# > support these behaviors + +class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + +for color in Color: + assert_type(color, Color) + +# > Unlike most Python classes, Calling an enum class does not invoke its +# > constructor. Instead, the call performs a value-based lookup of an +# > enum member. + +# 'Literal[Color.RED]' and 'Color' are both acceptable +assert_type(Color["RED"], Color) # E[red] +assert_type(Color["RED"], Literal[Color.RED]) # E[red] + +# 'Literal[Color.BLUE]' and 'Color' are both acceptable +assert_type(Color(3), Color) # E[blue] +assert_type(Color(3), Literal[Color.BLUE]) # E[blue] + + +# > An Enum class with one or more defined members cannot be subclassed. + +class EnumWithNoMembers(Enum): + pass + +class Shape(EnumWithNoMembers): # OK (because no members are defined) + SQUARE = 1 + CIRCLE = 2 + +class ExtendedShape(Shape): # E: Shape is implicitly final + TRIANGLE = 3 diff --git a/conformance/tests/enums_definition.py b/conformance/tests/enums_definition.py new file mode 100644 index 000000000..480a06510 --- /dev/null +++ b/conformance/tests/enums_definition.py @@ -0,0 +1,93 @@ +""" +Tests handling of Enum class definitions using the class syntax. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/enums.html#enum-definition + +from enum import Enum, EnumType +from typing import Literal, assert_type +import sys + +# > Type checkers should support the class syntax + + +class Color1(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + +assert_type(Color1.RED, Literal[Color1.RED]) + + +# > The function syntax (in its various forms) is optional + +Color2 = Enum("Color2", "RED", "GREEN", "BLUE") # E? +Color3 = Enum("Color3", ["RED", "GREEN", "BLUE"]) # E? +Color4 = Enum("Color4", ("RED", "GREEN", "BLUE")) # E? +Color5 = Enum("Color5", "RED, GREEN, BLUE") # E? +Color6 = Enum("Color6", "RED GREEN BLUE") # E? +Color7 = Enum("Color7", [("RED", 1), ("GREEN", 2), ("BLUE", 3)]) # E? +Color8 = Enum("Color8", (("RED", 1), ("GREEN", 2), ("BLUE", 3))) # E? +Color9 = Enum("Color9", {"RED": 1, "GREEN": 2, "BLUE": 3}) # E? + +assert_type(Color2.RED, Literal[Color2.RED]) # E? +assert_type(Color3.RED, Literal[Color3.RED]) # E? +assert_type(Color4.RED, Literal[Color4.RED]) # E? +assert_type(Color5.RED, Literal[Color5.RED]) # E? +assert_type(Color6.RED, Literal[Color6.RED]) # E? +assert_type(Color7.RED, Literal[Color7.RED]) # E? +assert_type(Color8.RED, Literal[Color8.RED]) # E? +assert_type(Color9.RED, Literal[Color9.RED]) # E? + + +# > Enum classes can also be defined using a subclass of enum.Enum or any class +# > that uses enum.EnumType (or a subclass thereof) as a metaclass. +# > Type checkers should treat such classes as enums + + +class CustomEnum1(Enum): + pass + + +class Color10(CustomEnum1): + RED = 1 + GREEN = 2 + BLUE = 3 + + +assert_type(Color10.RED, Literal[Color10.RED]) + + +class CustomEnumType(EnumType): + pass + + +class CustomEnum2(metaclass=CustomEnumType): + pass + + +class Color11(CustomEnum2): + RED = 1 + GREEN = 2 + BLUE = 3 + + +assert_type(Color11.RED, Literal[Color11.RED]) + +# > Enum members may be conditional, via checks of the same +# > statically-known conditions that a type-checker understands elsewhere, +# > such as Python version: + +class Color12(Enum): + RED = 1 + if sys.version_info >= (3, 12): + GREEN = 2 + if sys.version_info >= (4, 0): + BLUE = 3 + +# The conformance suite runs type checkers configured to Python 3.12 or later: +assert_type(Color12.RED, Literal[Color12.RED]) +assert_type(Color12.GREEN, Literal[Color12.GREEN]) +Color12.BLUE # E + diff --git a/conformance/tests/enums_expansion.py b/conformance/tests/enums_expansion.py new file mode 100644 index 000000000..57f701f10 --- /dev/null +++ b/conformance/tests/enums_expansion.py @@ -0,0 +1,78 @@ +""" +Tests that the type checker handles literal expansion of enum classes. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/enums.html#enum-literal-expansion + +from enum import Enum, Flag +from typing import Literal, Never, assert_type + +# > From the perspective of the type system, most enum classes are equivalent +# > to the union of the literal members within that enum. Type checkers may +# > therefore expand an enum type + + +class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + +def print_color1(c: Color): + if c is Color.RED or c is Color.BLUE: + print("red or blue") + else: + assert_type(c, Literal[Color.GREEN]) # E? + + +def print_color2(c: Color): + match c: + case Color.RED | Color.BLUE: + print("red or blue") + case Color.GREEN: + print("green") + case _: + assert_type(c, Never) # E? + + +# > This rule does not apply to classes that derive from enum. Flag because +# > these enums allow flags to be combined in arbitrary ways. + + +class CustomFlags(Flag): + FLAG1 = 1 + FLAG2 = 2 + FLAG3 = 4 + + +def test1(f: CustomFlags): + if f is CustomFlags.FLAG1 or f is CustomFlags.FLAG2: + print("flag1 and flag2") + else: + assert_type(f, CustomFlags) + assert_type(f, Literal[CustomFlags.FLAG3]) # E + + +def test2(f: CustomFlags): + match f: + case CustomFlags.FLAG1 | CustomFlags.FLAG2: + pass + case CustomFlags.FLAG3: + pass + case _: + assert_type(f, CustomFlags) + + +# > A type checker should treat a complete union of all literal members as +# > compatible with the enum type. + + +class Answer(Enum): + Yes = 1 + No = 2 + + +def test3(val: object) -> list[Answer]: + assert val is Answer.Yes or val is Answer.No + x = [val] + return x diff --git a/conformance/tests/enums_member_names.py b/conformance/tests/enums_member_names.py new file mode 100644 index 000000000..dc9ee0aae --- /dev/null +++ b/conformance/tests/enums_member_names.py @@ -0,0 +1,30 @@ +""" +Tests that the type checker handles the `_name_` and `name` attributes correctly. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/enums.html#member-names + +from enum import Enum +from typing import Literal, assert_type + +# > All enum member objects have an attribute _name_ that contains the member’s +# > name. They also have a property name that returns the same name. Type +# > checkers may infer a literal type for the name of a member + + +class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + +assert_type(Color.RED._name_, Literal["RED"]) # E? +assert_type(Color.RED.name, Literal["RED"]) # E? + + +def func1(red_or_blue: Literal[Color.RED, Color.BLUE]): + assert_type(red_or_blue.name, Literal["RED", "BLUE"]) # E? + + +def func2(any_color: Color): + assert_type(any_color.name, Literal["RED", "BLUE", "GREEN"]) # E? diff --git a/conformance/tests/enums_member_values.py b/conformance/tests/enums_member_values.py new file mode 100644 index 000000000..741099596 --- /dev/null +++ b/conformance/tests/enums_member_values.py @@ -0,0 +1,96 @@ +""" +Tests that the type checker handles the `_value_` and `value` attributes correctly. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/enums.html#member-values + +# > All enum member objects have an attribute _value_ that contains the +# > member’s value. They also have a property value that returns the same value. +# > Type checkers may infer the type of a member’s value. + +from enum import Enum, auto +from typing import Literal, assert_type + + +class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + +assert_type(Color.RED._value_, Literal[1]) # E? +assert_type(Color.RED.value, Literal[1]) # E? + + +def func1(red_or_blue: Literal[Color.RED, Color.BLUE]): + assert_type(red_or_blue.value, Literal[1, 3]) # E? + + +def func2(any_color: Color): + assert_type(any_color.value, Literal[1, 2, 3]) # E? + + +# > The value of _value_ can be assigned in a constructor method. This +# > technique is sometimes used to initialize both the member value and +# > non-member attributes. If the value assigned in the class body is a tuple, +# > the unpacked tuple value is passed to the constructor. Type checkers may +# > validate consistency between assigned tuple values and the constructor +# > signature. + + +class Planet(Enum): + def __init__(self, value: int, mass: float, radius: float): + self._value_ = value + self.mass = mass + self.radius = radius + + MERCURY = (1, 3.303e23, 2.4397e6) + VENUS = (2, 4.869e24, 6.0518e6) + EARTH = (3, 5.976e24, 6.37814e6) + MARS = (6.421e23, 3.3972e6) # E?: Type checker error (optional) + JUPITER = 5 # E?: Type checker error (optional) + + +assert_type(Planet.MERCURY.value, Literal[1]) # E? + + +# > The class enum.auto and method _generate_next_value_ can be used within +# > an enum class to automatically generate values for enum members. +# > Type checkers may support these to infer literal types for member values. + + +class Color2(Enum): + RED = auto() + GREEN = auto() + BLUE = auto() + + +assert_type(Color2.RED.value, Literal[1]) # E? + +# > If an enum class provides an explicit type annotation for _value_, type +# > checkers should enforce this declared type when values are assigned to +# > _value_. + + +class Color3(Enum): + _value_: int + RED = 1 # OK + GREEN = "green" # E + + +class Planet2(Enum): + _value_: str + + def __init__(self, value: int, mass: float, radius: float): + self._value_ = value # E + + MERCURY = (1, 3.303e23, 2.4397e6) + + +from _enums_member_values import ColumnType + +# > If the literal values for enum members are not supplied, as they sometimes +# > are not within a type stub file, a type checker can use the type of the +# > _value_ attribute. + +assert_type(ColumnType.DORIC.value, int) # E? diff --git a/conformance/tests/enums_members.py b/conformance/tests/enums_members.py new file mode 100644 index 000000000..a746334d0 --- /dev/null +++ b/conformance/tests/enums_members.py @@ -0,0 +1,151 @@ +""" +Tests that the type checker can distinguish enum members from non-members. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/enums.html#defining-members + +from enum import Enum, member, nonmember +from typing import Literal, assert_type, reveal_type + +# > If an attribute is defined in the class body with a type annotation but +# > with no assigned value, a type checker should assume this is a non-member +# > attribute + + +class Pet(Enum): + genus: str # Non-member attribute + species: str # Non-member attribute + + CAT = "felis", "catus" # Member attribute + DOG = "canis", "lupus" # Member attribute + + def __init__(self, genus: str, species: str) -> None: + self.genus = genus + self.species = species + + +def func1(pet: Pet) -> None: + assert_type(pet.genus, str) + assert_type(pet.species, str) + +assert_type(Pet.CAT, Literal[Pet.CAT]) +assert_type(Pet.DOG, Literal[Pet.DOG]) + + +from _enums_members import Pet2 + +def func2(pet: Pet2) -> None: + assert_type(pet.genus, str) + assert_type(pet.species, str) + +assert_type(Pet2.CAT, Literal[Pet2.CAT]) +assert_type(Pet2.DOG, Literal[Pet2.DOG]) + + +# > Members defined within an enum class should not include explicit type +# > annotations. Type checkers should infer a literal type for all members. +# > A type checker should report an error if a type annotation is used for +# > an enum member because this type will be incorrect and misleading to +# > readers of the code + + +class Pet3(Enum): + CAT = 1 + DOG: int = 2 # E + + +# > Methods, callables, descriptors (including properties), and nested classes +# > that are defined in the class are not treated as enum members by the +# > EnumType metaclass and should likewise not be treated as enum members by a +# > type checker + + +def identity(x: int) -> int: + return x + + +class Pet4(Enum): + CAT = 1 # Member attribute + DOG = 2 # Member attribute + + converter = lambda x: str(x) # Non-member attribute + transform = staticmethod(identity) # Non-member attribute + + @property + def species(self) -> str: # Non-member property + return "mammal" + + def speak(self) -> None: # Non-member method + print("meow" if self is Pet4.CAT else "woof") + + class Nested: ... # Non-member nested class + + +assert_type(Pet4.CAT, Literal[Pet4.CAT]) +assert_type(Pet4.DOG, Literal[Pet4.DOG]) +converter: Literal[Pet4.converter] # E +transform: Literal[Pet4.transform] # E +species: Literal[Pet4.species] # E +speak: Literal[Pet4.speak] # E + + +# > An attribute that is assigned the value of another member of the same +# > enum is not a member itself. Instead, it is an alias for the first member + + +class TrafficLight(Enum): + RED = 1 + GREEN = 2 + YELLOW = 3 + + AMBER = YELLOW # Alias for YELLOW + + +assert_type(TrafficLight.AMBER, Literal[TrafficLight.YELLOW]) + +# > If using Python 3.11 or newer, the enum.member and enum.nonmember classes +# > can be used to unambiguously distinguish members from non-members. + + +class Example(Enum): + a = member(1) # Member attribute + b = nonmember(2) # Non-member attribute + + @member + def c(self) -> None: # Member method + pass + + +assert_type(Example.a, Literal[Example.a]) +assert_type(Example.b, Literal[Example.b]) # E +assert_type(Example.c, Literal[Example.c]) + + +# > An attribute with a private name (beginning with, but not ending in, +# > a double underscore) is treated as a non-member. + + +class Example2(Enum): + __B = 2 # Non-member attribute + + def method(self): + reveal_type(Example2.__B) + assert_type(Example2.__B, Literal[Example2.__B]) # E + + +# > An enum class can define a class symbol named _ignore_. This can be +# > a list of names or a string containing a space-delimited list of names +# > that are deleted from the enum class at runtime. Type checkers may +# > support this mechanism + + +class Pet5(Enum): + _ignore_ = "DOG FISH" + CAT = 1 # Member attribute + DOG = 2 # temporary variable, will be removed from the final enum class + FISH = 3 # temporary variable, will be removed from the final enum class + + +assert_type(Pet5.CAT, Literal[Pet5.CAT]) +assert_type(Pet5.DOG, int) # E?: Literal[2] is also acceptable +assert_type(Pet5.FISH, int) # E?: Literal[3] is also acceptable diff --git a/conformance/tests/exceptions_context_managers.py b/conformance/tests/exceptions_context_managers.py new file mode 100644 index 000000000..774497220 --- /dev/null +++ b/conformance/tests/exceptions_context_managers.py @@ -0,0 +1,85 @@ +""" +Tests the handling of __exit__ return types for context managers. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/exceptions.html + + +from typing import Any, Literal, assert_type + + +class CMBase: + def __enter__(self) -> None: + pass + + +class Suppress1(CMBase): + def __exit__(self, exc_type, exc_value, traceback) -> bool: + return True + + +class Suppress2(CMBase): + def __exit__(self, exc_type, exc_value, traceback) -> Literal[True]: + return True + + +class NoSuppress1(CMBase): + def __exit__(self, exc_type, exc_value, traceback) -> None: + return None + + +class NoSuppress2(CMBase): + def __exit__(self, exc_type, exc_value, traceback) -> Literal[False]: + return False + + +class NoSuppress3(CMBase): + def __exit__(self, exc_type, exc_value, traceback) -> Any: + return False + + +class NoSuppress4(CMBase): + def __exit__(self, exc_type, exc_value, traceback) -> None | bool: + return None + + +def suppress1(x: int | str) -> None: + if isinstance(x, int): + with Suppress1(): + raise ValueError + assert_type(x, int | str) + + +def suppress2(x: int | str) -> None: + if isinstance(x, int): + with Suppress2(): + raise ValueError + assert_type(x, int | str) + + +def no_suppress1(x: int | str) -> None: + if isinstance(x, int): + with NoSuppress1(): + raise ValueError + assert_type(x, str) + + +def no_suppress2(x: int | str) -> None: + if isinstance(x, int): + with NoSuppress2(): + raise ValueError + assert_type(x, str) + + +def no_suppress3(x: int | str) -> None: + if isinstance(x, int): + with NoSuppress3(): + raise ValueError + assert_type(x, str) + + +def no_suppress4(x: int | str) -> None: + if isinstance(x, int): + with NoSuppress4(): + raise ValueError + assert_type(x, str) diff --git a/conformance/tests/generics_base_class.py b/conformance/tests/generics_base_class.py new file mode 100644 index 000000000..f58162108 --- /dev/null +++ b/conformance/tests/generics_base_class.py @@ -0,0 +1,98 @@ +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#arbitrary-generic-types-as-base-classes + +from typing import Generic, TypeVar, assert_type + +T = TypeVar("T") + +# > Generic[T] is only valid as a base class – it’s not a proper type. However, +# > user-defined generic types [...] and built-in generic types and ABCs such as +# > list[T] and Iterable[T] are valid both as types and as base classes. + + +class Node: ... + + +class SymbolTable(dict[str, list[Node]]): ... + + +def takes_dict(x: dict): ... +def takes_dict_typed(x: dict[str, list[Node]]): ... +def takes_dict_incorrect(x: dict[str, list[object]]): ... + + +def test_symbol_table(s: SymbolTable): + takes_dict(s) # OK + takes_dict_typed(s) # OK + takes_dict_incorrect(s) # E + + +def func1(y: Generic[T]): # E + x: Generic # E + + +# > If a generic base class has a type variable as a type argument, this makes +# > the defined class generic. + +# Note that there is overlap in the spec and tests in generics_basic.py + +from collections.abc import Iterable, Container, Iterator + + +class LinkedList(Iterable[T], Container[T]): ... + + +def test_linked_list(l: LinkedList[int]): + assert_type(iter(l), Iterator[int]) + assert_type(l.__contains__(1), bool) + + +linked_list_invalid: LinkedList[int, int] # E + +from collections.abc import Mapping + + +class MyDict(Mapping[str, T]): ... + + +def test_my_dict(d: MyDict[int]): + assert_type(d["a"], int) + + +my_dict_invalid: MyDict[int, int] # E + +# > Note that we can use T multiple times in the base class list, +# > as long as we don’t use the same type variable T multiple times +# > within Generic[...]. + + +class BadClass1(Generic[T, T]): # E + pass + + +class GoodClass1(dict[T, T]): # OK + pass + +# > Type variables are applied to the defined class in the order in which +# > they first appear in any generic base classes. + +T1 = TypeVar("T1") +T2 = TypeVar("T2") +T3 = TypeVar("T3") + +class Parent1(Generic[T1, T2]): ... +class Parent2(Generic[T1, T2]): ... +class Child(Parent1[T1, T3], Parent2[T2, T3]): ... + +def takes_parent1(x: Parent1[int, bytes]): ... +def takes_parent2(x: Parent2[str, bytes]): ... + +child: Child[int, bytes, str] = Child() +takes_parent1(child) # OK +takes_parent2(child) # OK + +# > A type checker should report an error when the type variable order is +# > inconsistent. + +class Grandparent(Generic[T1, T2]): ... +class Parent(Grandparent[T1, T2]): ... +class BadChild(Parent[T1, T2], Grandparent[T2, T1]): ... # E diff --git a/conformance/tests/generics_basic.py b/conformance/tests/generics_basic.py new file mode 100644 index 000000000..179d84758 --- /dev/null +++ b/conformance/tests/generics_basic.py @@ -0,0 +1,209 @@ +""" +Tests for basic usage of generics. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#introduction + +from __future__ import annotations + +from collections.abc import Sequence +from typing import Any, Generic, Protocol, TypeVar, assert_type + +T = TypeVar("T") + +# > Generics can be parameterized by using a factory available in +# > ``typing`` called ``TypeVar``. + + +def first(l: Sequence[T]) -> T: + return l[0] + + +def test_first(seq_int: Sequence[int], seq_str: Sequence[str]) -> None: + assert_type(first(seq_int), int) + assert_type(first(seq_str), str) + + +# > ``TypeVar`` supports constraining parametric types to a fixed set of +# > possible types + +AnyStr = TypeVar("AnyStr", str, bytes) + + +def concat(x: AnyStr, y: AnyStr) -> AnyStr: + return x + y + + +def test_concat(s: str, b: bytes, a: Any) -> None: + concat(s, s) # OK + concat(b, b) # OK + concat(s, b) # E + concat(b, s) # E + + concat(s, a) # OK + concat(a, b) # OK + + +# > Specifying a single constraint is disallowed. + +BadConstraint1 = TypeVar("BadConstraint1", str) # E + +# > Note: those types cannot be parameterized by type variables + + +class Test(Generic[T]): + BadConstraint2 = TypeVar("BadConstraint2", str, list[T]) # E + + +# > Subtypes of types constrained by a type variable should be treated +# > as their respective explicitly listed base types in the context of the +# > type variable. + + +class MyStr(str): ... + + +def test_concat_subtype(s: str, b: bytes, a: Any, m: MyStr) -> None: + assert_type(concat(m, m), str) + assert_type(concat(m, s), str) + concat(m, b) # E + + # TODO: should these be str or Any? + # reveal_type(concat(m, a)) + # reveal_type(concat(a, m)) + + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#user-defined-generic-classes + +# > You can include a ``Generic`` base class to define a user-defined class +# > as generic. + +from logging import Logger +from collections.abc import Iterable + + +class LoggedVar(Generic[T]): + def __init__(self, value: T, name: str, logger: Logger) -> None: + self.name = name + self.logger = logger + self.value = value + + def set(self, new: T) -> None: + self.log("Set " + repr(self.value)) + self.value = new + + def get(self) -> T: + self.log("Get " + repr(self.value)) + return self.value + + def log(self, message: str) -> None: + self.logger.info("{}: {}".format(self.name, message)) + + +def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None: + for var in vars: + var.set(0) + assert_type(var.get(), int) + + +# > A generic type can have any number of type variables, and type variables +# > may be constrained. + +S = TypeVar("S") + + +class Pair1(Generic[T, S]): ... + + +# > Each type variable argument to ``Generic`` must be distinct. + + +class Pair2(Generic[T, T]): # E + ... + + +# > The ``Generic[T]`` base class is redundant in simple cases where you +# > subclass some other generic class and specify type variables for its +# > parameters. + +from collections.abc import Iterator, Mapping + + +class MyIter1(Iterator[T]): ... + + +class MyIter2(Iterator[T], Generic[T]): ... + + +def test_my_iter(m1: MyIter1[int], m2: MyIter2[int]): + assert_type(next(m1), int) + assert_type(next(m2), int) + + +K = TypeVar("K") +V = TypeVar("V") + + +class MyMap1(Mapping[K, V], Generic[K, V]): ... + + +class MyMap2(Mapping[K, V], Generic[V, K]): ... + + +def test_my_map(m1: MyMap1[str, int], m2: MyMap2[int, str]): + assert_type(m1["key"], int) + assert_type(m2["key"], int) + + m1[0] # E + m2[0] # E + +# > All arguments to ``Generic`` or ``Protocol`` must be type variables. + +class Bad1(Generic[int]): ... # E +class Bad2(Protocol[int]): ... # E + +# > All type parameters for the class must appear within the ``Generic`` or +# > ``Protocol`` type argument list. + +T_co = TypeVar("T_co", covariant=True) +S_co = TypeVar("S_co", covariant=True) + +class Bad3(Iterable[T_co], Generic[S_co]): ... # E +class Bad4(Iterable[T_co], Protocol[S_co]): ... # E + +# > The above rule does not apply to a bare ``Protocol`` base class. + +class MyIterator(Iterator[T_co], Protocol): ... # OK + +# > You can use multiple inheritance with ``Generic`` + +from collections.abc import Sized, Container + + +class LinkedList(Sized, Generic[T]): ... + + +class MyMapping(Iterable[tuple[K, V]], Container[tuple[K, V]], Generic[K, V]): ... + + +# > Subclassing a generic class without specifying type parameters assumes +# > ``Any`` for each position. In the following example, ``MyIterable`` +# > is not generic but implicitly inherits from ``Iterable[Any]``:: + + +class MyIterableAny(Iterable): # Same as Iterable[Any] + ... + + +def test_my_iterable_any(m: MyIterableAny): + assert_type(iter(m), Iterator[Any]) + + +# > Generic metaclasses are not supported + + +class GenericMeta(type, Generic[T]): ... + + +class GenericMetaInstance(metaclass=GenericMeta[T]): # E + ... diff --git a/conformance/tests/generics_defaults.py b/conformance/tests/generics_defaults.py new file mode 100644 index 000000000..14eafa2bf --- /dev/null +++ b/conformance/tests/generics_defaults.py @@ -0,0 +1,224 @@ +""" +Tests for basic usage of default values for TypeVar-like's. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#defaults-for-type-parameters. + +from typing import Any, Callable, Generic, Self, Unpack, assert_type +from typing_extensions import TypeVar, ParamSpec, TypeVarTuple + + +DefaultStrT = TypeVar("DefaultStrT", default=str) +DefaultIntT = TypeVar("DefaultIntT", default=int) +DefaultBoolT = TypeVar("DefaultBoolT", default=bool) +T = TypeVar("T") +T1 = TypeVar("T1") +T2 = TypeVar("T2") + +# > The order for defaults should follow the standard function parameter +# > rules, so a type parameter with no ``default`` cannot follow one with +# > a ``default`` value. Doing so may raise a ``TypeError`` at runtime, +# > and a type checker should flag this as an error. + + +class NonDefaultFollowsDefault(Generic[DefaultStrT, T]): ... # E: non-default TypeVars cannot follow ones with defaults + + +class NoNonDefaults(Generic[DefaultStrT, DefaultIntT]): + x: DefaultStrT + y: DefaultIntT + + +def test_no_non_defaults(a: NoNonDefaults, b: NoNonDefaults[str], c: NoNonDefaults[str, int]): + assert_type(a.x, str) + assert_type(a.y, int) + + assert_type(b.x, str) + assert_type(b.y, int) + + assert_type(c.x, str) + assert_type(c.y, int) + + +class OneDefault(Generic[T, DefaultBoolT]): + x: T + y: DefaultBoolT + + +def test_one_default(a: OneDefault[float], b: OneDefault[float, bool]): + assert_type(a.x, float) + assert_type(a.y, bool) + + assert_type(b.x, float) + assert_type(b.y, bool) + + +class AllTheDefaults(Generic[T1, T2, DefaultStrT, DefaultIntT, DefaultBoolT]): + x1: T1 + x2: T2 + x3: DefaultStrT + x4: DefaultIntT + x5: DefaultBoolT + + +def test_all_the_defaults( + a: AllTheDefaults, + b: AllTheDefaults[int], # E: expected 2 arguments to AllTheDefaults + c: AllTheDefaults[int, complex], + d: AllTheDefaults[int, complex, str, int, bool], + e: AllTheDefaults[int, complex, str], + f: AllTheDefaults[int, complex, str, int], + g: AllTheDefaults[int, complex, str, int, bool], +): + assert_type(a.x1, Any) + assert_type(a.x2, Any) + assert_type(a.x3, str) + assert_type(a.x4, int) + assert_type(a.x5, bool) + + assert_type(c.x1, int) + assert_type(c.x2, complex) + assert_type(c.x3, str) + assert_type(c.x4, int) + assert_type(c.x5, bool) + + assert_type(d.x1, int) + assert_type(d.x2, complex) + assert_type(d.x3, str) + assert_type(d.x4, int) + assert_type(d.x5, bool) + + assert_type(e.x1, int) + assert_type(e.x2, complex) + assert_type(e.x3, str) + assert_type(e.x4, int) + assert_type(e.x5, bool) + + assert_type(f.x1, int) + assert_type(f.x2, complex) + assert_type(f.x3, str) + assert_type(f.x4, int) + assert_type(f.x5, bool) + + assert_type(g.x1, int) + assert_type(g.x2, complex) + assert_type(g.x3, str) + assert_type(g.x4, int) + assert_type(g.x5, bool) + + +# > ``ParamSpec`` defaults are defined using the same syntax as +# > ``TypeVar`` \ s but use a ``list`` of types or an ellipsis +# > literal "``...``" or another in-scope ``ParamSpec``. + +DefaultP = ParamSpec("DefaultP", default=[str, int]) + + +class Class_ParamSpec(Generic[DefaultP]): + x: Callable[DefaultP, None] + + +def test_param_spec_defaults(a: Class_ParamSpec): + assert_type(a.x, Callable[[str, int], None]) + assert_type(Class_ParamSpec(), Class_ParamSpec[str, int]) + assert_type(Class_ParamSpec[[bool, bool]](), Class_ParamSpec[bool, bool]) + + +# > ``TypeVarTuple`` defaults are defined using the same syntax as +# > ``TypeVar`` \ s, but instead of a single type, they use an unpacked tuple +# > of types or an unpacked, in-scope ``TypeVarTuple`` (see `Scoping Rules`_). + +DefaultTs = TypeVarTuple("DefaultTs", default=Unpack[tuple[str, int]]) + + +class Class_TypeVarTuple(Generic[*DefaultTs]): + x: tuple[*DefaultTs] + + +def test_type_var_tuple_defaults(a: Class_TypeVarTuple): + assert_type(a.x, tuple[str, int]) + assert_type(Class_TypeVarTuple(), Class_TypeVarTuple[str, int]) + assert_type(Class_TypeVarTuple[int, bool](), Class_TypeVarTuple[int, bool]) + + +AnotherDefaultTs = TypeVarTuple("AnotherDefaultTs", default=Unpack[DefaultTs]) + + +# > If both ``bound`` and ``default`` are passed, ``default`` must be a +# > subtype of ``bound``. If not, the type checker should generate an +# > error. + +Ok1 = TypeVar("Ok1", bound=float, default=int) # OK +Invalid1 = TypeVar("Invalid1", bound=str, default=int) # E: the bound and default are incompatible + +# > For constrained ``TypeVar``\ s, the default needs to be one of the +# > constraints. A type checker should generate an error even if it is a +# > subtype of one of the constraints. + +Ok2 = TypeVar("Ok2", float, str, default=float) # OK +Invalid2 = TypeVar("Invalid2", float, str, default=int) # E: expected one of float or str got int + + +# > In generic functions, type checkers may use a type parameter's default when the +# > type parameter cannot be solved to anything. We leave the semantics of this +# > usage unspecified, as ensuring the ``default`` is returned in every code path +# > where the type parameter can go unsolved may be too hard to implement. Type +# > checkers are free to either disallow this case or experiment with implementing +# > support. + +T4 = TypeVar("T4", default=int) + + +def func1(x: int | set[T4]) -> T4: + raise NotImplementedError + + +assert_type(func1(0), int) # E[optional-default-use] +assert_type(func1(0), Any) # E[optional-default-use] + + +# > A ``TypeVar`` that immediately follows a ``TypeVarTuple`` is not allowed +# > to have a default, because it would be ambiguous whether a type argument +# > should be bound to the ``TypeVarTuple`` or the defaulted ``TypeVar``. + +Ts = TypeVarTuple("Ts") +T5 = TypeVar("T5", default=bool) + + +class Foo5(Generic[*Ts, T5]): ... # E + + +# > It is allowed to have a ``ParamSpec`` with a default following a +# > ``TypeVarTuple`` with a default, as there can be no ambiguity between a +# > type argument for the ``ParamSpec`` and one for the ``TypeVarTuple``. + +P = ParamSpec("P", default=[float, bool]) + + +class Foo6(Generic[*Ts, P]): + x: tuple[*Ts] + y: Callable[P, None] + + +def test_foo6(a: Foo6[int, str], b: Foo6[int, str, [bytes]]): + assert_type(a.x, tuple[int, str]) + assert_type(a.y, Callable[[float, bool], None]) + + assert_type(b.x, tuple[int, str]) + assert_type(b.y, Callable[[bytes], None]) + + +# > Type parameter defaults should be bound by attribute access +# > (including call and subscript). + + +class Foo7(Generic[DefaultIntT]): + def meth(self, /) -> Self: + return self + + attr: DefaultIntT + + +foo7 = Foo7() +assert_type(Foo7.meth(foo7), Foo7[int]) +assert_type(Foo7().attr, int) diff --git a/conformance/tests/generics_defaults_referential.py b/conformance/tests/generics_defaults_referential.py new file mode 100644 index 000000000..30c0281f6 --- /dev/null +++ b/conformance/tests/generics_defaults_referential.py @@ -0,0 +1,106 @@ +""" +Tests for type parameter default values that reference other type parameters. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#defaults-for-type-parameters. + +# > To use another type parameter as a default the ``default`` and the +# > type parameter must be the same type (a ``TypeVar``'s default must be +# > a ``TypeVar``, etc.). + +from typing import Any, Generic, assert_type +from typing_extensions import TypeVar + +DefaultStrT = TypeVar("DefaultStrT", default=str) +StartT = TypeVar("StartT", default=int) +StopT = TypeVar("StopT", default=StartT) +StepT = TypeVar("StepT", default=int | None) + + +class slice(Generic[StartT, StopT, StepT]): ... + + +assert_type(slice, type[slice[int, int, int | None]]) +assert_type(slice(), slice[int, int, int | None]) +assert_type(slice[str](), slice[str, str, int | None]) +assert_type(slice[str, bool, complex](), slice[str, bool, complex]) + +T2 = TypeVar("T2", default=DefaultStrT) + + +class Foo(Generic[DefaultStrT, T2]): + def __init__(self, a: DefaultStrT, b: T2) -> None: ... + + +def func1(i: int, s: str) -> None: + assert_type(Foo(i, s), Foo[int, str]) + Foo[int](i, s) # E: Foo[int, str] cannot be assigned to self: Foo[int, int] in Foo.__init__ + Foo[int](s, i) # E: Foo[str, int] cannot be assigned to self: Foo[int, int] in Foo.__init__ + + +# > ``T1`` must be used before ``T2`` in the parameter list of the generic. + +S1 = TypeVar("S1") +S2 = TypeVar("S2", default=S1) + + +class Foo2(Generic[S1, S2]): ... # OK + + +Start2T = TypeVar("Start2T", default="StopT") +Stop2T = TypeVar("Stop2T", default=int) + + +class slice2(Generic[Start2T, Stop2T, StepT]): ... # E: bad ordering + + +# > Using a type parameter from an outer scope as a default is not supported. + + +class Foo3(Generic[S1]): + class Bar2(Generic[S2]): ... # E + + +# > ``T1``'s bound must be a subtype of ``T2``'s bound. + +X1 = TypeVar("X1", bound=int) +Ok1 = TypeVar("Ok1", default=X1, bound=float) # OK +AlsoOk1 = TypeVar("AlsoOk1", default=X1, bound=int) # OK +Invalid1 = TypeVar("Invalid1", default=X1, bound=str) # E: int is not a subtype of str + + +# > The constraints of ``T2`` must be a superset of the constraints of ``T1``. + +Y1 = TypeVar("Y1", bound=int) +Invalid2 = TypeVar("Invalid2", float, str, default=Y1) # E: upper bound int is incompatible with constraints float or str + +Y2 = TypeVar("Y2", int, str) +AlsoOk2 = TypeVar("AlsoOk2", int, str, bool, default=Y2) # OK +AlsoInvalid2 = TypeVar("AlsoInvalid2", bool, complex, default=Y2) # E: {bool, complex} is not a superset of {int, str} + + +# > Type parameters are valid as parameters to generics inside of a +# > ``default`` when the first parameter is in scope as determined by the +# > `previous section `_. + + +Z1 = TypeVar("Z1") +ListDefaultT = TypeVar("ListDefaultT", default=list[Z1]) # OK + + +class Bar(Generic[Z1, ListDefaultT]): # OK + x: Z1 + y: ListDefaultT + def __init__(self, x: Z1, y: ListDefaultT): ... + + +def f(b1: Bar, b2: Bar[int]): + assert_type(b1.x, Any) + assert_type(b1.y, list[Any]) + assert_type(b2.x, int) + assert_type(b2.y, list[int]) + + +assert_type(Bar[int](0, []), Bar[int, list[int]]) +assert_type(Bar[int, list[str]](0, []), Bar[int, list[str]]) +assert_type(Bar[int, str](0, ""), Bar[int, str]) diff --git a/conformance/tests/generics_defaults_specialization.py b/conformance/tests/generics_defaults_specialization.py new file mode 100644 index 000000000..91db2579b --- /dev/null +++ b/conformance/tests/generics_defaults_specialization.py @@ -0,0 +1,66 @@ +""" +Tests for specialization rules associated with type parameters with +default values. +""" + +# > A generic type alias can be further subscripted following normal subscription +# > rules. If a type parameter has a default that hasn't been overridden, it should +# > be treated like it was substituted into the type alias. + +from typing import Generic, TypeAlias, assert_type +from typing_extensions import TypeVar + +T1 = TypeVar("T1") +T2 = TypeVar("T2") +DefaultIntT = TypeVar("DefaultIntT", default=int) +DefaultStrT = TypeVar("DefaultStrT", default=str) + + +class SomethingWithNoDefaults(Generic[T1, T2]): ... + + +MyAlias: TypeAlias = SomethingWithNoDefaults[int, DefaultStrT] # OK + + +def func1(p1: MyAlias, p2: MyAlias[bool]): + assert_type(p1, SomethingWithNoDefaults[int, str]) + assert_type(p2, SomethingWithNoDefaults[int, bool]) + + +MyAlias[bool, int] # E: too many arguments passed to MyAlias + + +# > Generic classes with type parameters that have defaults behave similarly +# > generic type aliases. That is, subclasses can be further subscripted following +# > normal subscription rules, non-overridden defaults should be substituted. + + +class SubclassMe(Generic[T1, DefaultStrT]): + x: DefaultStrT + + +class Bar(SubclassMe[int, DefaultStrT]): ... + + +x1: type[Bar[str]] = Bar # ok +x2: type[Bar[int]] = Bar # E +assert_type(Bar(), Bar[str]) +assert_type(Bar[bool](), Bar[bool]) + + +class Foo(SubclassMe[float]): ... + + +assert_type(Foo().x, str) + +Foo[str] # E: Foo cannot be further subscripted + + +class Baz(Generic[DefaultIntT, DefaultStrT]): ... + + +class Spam(Baz): ... + + +# Spam is +v1: Baz[int, str] = Spam() diff --git a/conformance/tests/generics_paramspec_basic.py b/conformance/tests/generics_paramspec_basic.py new file mode 100644 index 000000000..e2c622583 --- /dev/null +++ b/conformance/tests/generics_paramspec_basic.py @@ -0,0 +1,40 @@ +""" +Tests basic usage of ParamSpec. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#paramspec-variables + +from typing import Any, Callable, Concatenate, ParamSpec, TypeAlias + +P = ParamSpec("P") # OK +WrongName = ParamSpec("NotIt") # E: name inconsistency + + +# > Valid use locations + +TA1: TypeAlias = P # E + +TA2: TypeAlias = Callable[P, None] # OK +TA3: TypeAlias = Callable[Concatenate[int, P], None] # OK +TA4: TypeAlias = Callable[..., None] # OK +TA5: TypeAlias = Callable[..., None] # OK + + +def func1(x: P) -> P: # E + raise NotImplementedError + + +def func2(x: Concatenate[int, P]) -> int: # E + raise NotImplementedError + + +def func3(x: list[P]) -> None: # E + ... + + +def func4(x: Callable[[int, str], P]) -> None: # E + ... + + +def func5(*args: P, **kwargs: P) -> None: # E + ... diff --git a/conformance/tests/generics_paramspec_components.py b/conformance/tests/generics_paramspec_components.py new file mode 100644 index 000000000..4f1929d13 --- /dev/null +++ b/conformance/tests/generics_paramspec_components.py @@ -0,0 +1,98 @@ +""" +Tests the usage of a ParamSpec and its components (P.args, P.kwargs). +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#the-components-of-a-paramspec + + +from typing import Any, Callable, Concatenate, ParamSpec, assert_type + +P = ParamSpec("P") + + +def puts_p_into_scope1(f: Callable[P, int]) -> None: + def inner(*args: P.args, **kwargs: P.kwargs) -> None: # OK + pass + + def mixed_up(*args: P.kwargs, **kwargs: P.args) -> None: # E + pass + + def misplaced1(x: P.args) -> None: # E + pass + + def bad_kwargs1(*args: P.args, **kwargs: P.args) -> None: # E + pass + + def bad_kwargs2(*args: P.args, **kwargs: Any) -> None: # E + pass + + +def out_of_scope(*args: P.args, **kwargs: P.kwargs) -> None: # E + pass + + +def puts_p_into_scope2(f: Callable[P, int]) -> None: + stored_args: P.args # E + stored_kwargs: P.kwargs # E + + def just_args(*args: P.args) -> None: # E + pass + + def just_kwargs(**kwargs: P.kwargs) -> None: # E + pass + + +def decorator(f: Callable[P, int]) -> Callable[P, None]: + def foo(*args: P.args, **kwargs: P.kwargs) -> None: + assert_type(f(*args, **kwargs), int) # OK + + f(*kwargs, **args) # E + + f(1, *args, **kwargs) # E + + return foo # OK + + +def add(f: Callable[P, int]) -> Callable[Concatenate[str, P], None]: + def foo(s: str, *args: P.args, **kwargs: P.kwargs) -> None: # OK + pass + + def bar(*args: P.args, s: str, **kwargs: P.kwargs) -> None: # E + pass + + return foo # OK + + +def remove(f: Callable[Concatenate[int, P], int]) -> Callable[P, None]: + def foo(*args: P.args, **kwargs: P.kwargs) -> None: + f(1, *args, **kwargs) # OK + + f(*args, 1, **kwargs) # E + + f(*args, **kwargs) # E + + return foo # OK + + +def outer(f: Callable[P, None]) -> Callable[P, None]: + def foo(x: int, *args: P.args, **kwargs: P.kwargs) -> None: + f(*args, **kwargs) + + def bar(*args: P.args, **kwargs: P.kwargs) -> None: + foo(1, *args, **kwargs) # OK + foo(x=1, *args, **kwargs) # E + + return bar + + +def twice(f: Callable[P, int], *args: P.args, **kwargs: P.kwargs) -> int: + return f(*args, **kwargs) + f(*args, **kwargs) + + +def a_int_b_str(a: int, b: str) -> int: + return 0 + + +twice(a_int_b_str, 1, "A") # OK +twice(a_int_b_str, b="A", a=1) # OK +twice(a_int_b_str, "A", 1) # E diff --git a/conformance/tests/generics_paramspec_semantics.py b/conformance/tests/generics_paramspec_semantics.py new file mode 100644 index 000000000..836d461d0 --- /dev/null +++ b/conformance/tests/generics_paramspec_semantics.py @@ -0,0 +1,144 @@ +""" +Tests semantics of ParamSpec. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#semantics + + +from typing import Callable, Concatenate, Generic, ParamSpec, TypeVar, assert_type + +P = ParamSpec("P") + + +def changes_return_type_to_str(x: Callable[P, int]) -> Callable[P, str]: + raise NotImplementedError + + +def returns_int(a: str, b: bool, /) -> int: + raise NotImplementedError + + +f1 = changes_return_type_to_str(returns_int) +assert_type(f1, Callable[[str, bool], str]) + +v1 = f1("A", True) # OK +assert_type(v1, str) +f1(a="A", b=True) # E: positional-only +f1("A", "A") # E: wrong type + + +def func1(x: Callable[P, int], y: Callable[P, int]) -> Callable[P, bool]: + raise NotImplementedError + + +def x_y(x: int, y: str) -> int: + raise NotImplementedError + + +def y_x(y: int, x: str) -> int: + raise NotImplementedError + + +f2 = func1(x_y, x_y) +assert_type(f2(1, ""), bool) +assert_type(f2(y="", x=1), bool) + +f3 = func1(x_y, y_x) # E?: Could return (a: int, b: str, /) -> bool +# (a callable with two positional-only parameters) +# This works because both callables have types that are +# behavioral subtypes of Callable[[int, str], int] +# Also OK for type checkers to reject this. + + +def keyword_only_x(*, x: int) -> int: + raise NotImplementedError + + +def keyword_only_y(*, y: int) -> int: + raise NotImplementedError + + +func1(keyword_only_x, keyword_only_y) # E + + +U = TypeVar("U") + + +class Y(Generic[U, P]): + f: Callable[P, str] + prop: U + + def __init__(self, f: Callable[P, str], prop: U) -> None: + self.f = f + self.prop = prop + + +def callback_a(q: int, /) -> str: + raise NotImplementedError + + +def func(x: int) -> None: + y1 = Y(callback_a, x) + assert_type(y1, Y[int, [int]]) + y2 = y1.f + assert_type(y2, Callable[[int], str]) + + +def bar(x: int, *args: bool) -> int: + raise NotImplementedError + + +def add(x: Callable[P, int]) -> Callable[Concatenate[str, P], bool]: + raise NotImplementedError + + +a1 = add(bar) # Should return (a: str, /, x: int, *args: bool) -> bool +assert_type(a1("", 1, False, True), bool) +assert_type(a1("", x=1), bool) +a1(1, x=1) # E + + +def remove(x: Callable[Concatenate[int, P], int]) -> Callable[P, bool]: + raise NotImplementedError + + +r1 = remove(bar) # Should return (*args: bool) -> bool +assert_type(r1(False, True, True), bool) +assert_type(r1(), bool) +r1(1) # E + + +def transform( + x: Callable[Concatenate[int, P], int] +) -> Callable[Concatenate[str, P], bool]: + raise NotImplementedError + + +t1 = transform(bar) # Should return (a: str, /, *args: bool) -> bool +assert_type(t1("", True, False, True), bool) +assert_type(t1(""), bool) +t1(1) # E + + +def expects_int_first(x: Callable[Concatenate[int, P], int]) -> None: + ... + + +@expects_int_first # E +def one(x: str) -> int: + raise NotImplementedError + + +@expects_int_first # E +def two(*, x: int) -> int: + raise NotImplementedError + + +@expects_int_first # E +def three(**kwargs: int) -> int: + raise NotImplementedError + + +@expects_int_first # OK +def four(*args: int) -> int: + raise NotImplementedError diff --git a/conformance/tests/generics_paramspec_specialization.py b/conformance/tests/generics_paramspec_specialization.py new file mode 100644 index 000000000..34ed734fd --- /dev/null +++ b/conformance/tests/generics_paramspec_specialization.py @@ -0,0 +1,61 @@ +""" +Tests the handling of a ParamSpec specialization. +""" + +from typing import Any, Callable, Concatenate, Generic, ParamSpec, TypeVar, cast + + +T = TypeVar("T") +P1 = ParamSpec("P1") +P2 = ParamSpec("P2") + + +class ClassA(Generic[T, P1]): + f: Callable[P1, int] = cast(Any, "") + x: T = cast(Any, "") + + +class ClassB(ClassA[T, P1], Generic[T, P1, P2]): + f1: Callable[P1, int] = cast(Any, "") + f2: Callable[P2, int] = cast(Any, "") + x: T = cast(Any, "") + + +def func20(x: ClassA[int, P2]) -> str: # OK + return "" + + +def func21(x: ClassA[int, Concatenate[int, P2]]) -> str: # OK + return "" + + +def func22(x: ClassB[int, [int, bool], ...]) -> str: # OK + return "" + + +def func23(x: ClassA[int, ...]) -> str: # OK + return "" + + +def func24(x: ClassB[int, [], []]) -> str: # OK + return "" + + +def func25(x: ClassA[int, int]) -> str: # E + return "" + + +class ClassC(Generic[P1]): + f: Callable[P1, int] = cast(Any, "") + + +def func30(x: ClassC[[int, str, bool]]) -> None: # OK + x.f(0, "", True) # OK + x.f("", "", True) # E + x.f(0, "", "") # E + + +def func31(x: ClassC[int, str, bool]) -> None: # OK + x.f(0, "", True) # OK + x.f("", "", True) # E + x.f(0, "", "") # E diff --git a/conformance/tests/generics_scoping.py b/conformance/tests/generics_scoping.py new file mode 100644 index 000000000..56a8795ad --- /dev/null +++ b/conformance/tests/generics_scoping.py @@ -0,0 +1,107 @@ +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#scoping-rules-for-type-variables + +from typing import TypeVar, Generic, Iterable, TypeAlias, assert_type, Literal + +# > A type variable used in a generic function could be inferred to represent +# > different types in the same code block. +T = TypeVar('T') + +def fun_1(x: T) -> T: # T here + return x +def fun_2(x: T) -> T: # and here could be different + return x + +# One of these two should pass; either is acceptable: +assert_type(fun_1(1), int) # E[fun1] +assert_type(fun_1(1), Literal[1]) # E[fun1] + +# One of these two should pass; either is acceptable: +assert_type(fun_2("a"), str) # E[fun2] +assert_type(fun_2("a"), Literal["a"]) # E[fun2] + +# > A type variable used in a method of a generic class that coincides +# > with one of the variables that parameterize this class is always bound +# > to that variable. + +class MyClass(Generic[T]): + def meth_1(self, x: T) -> T: # T here + return x + def meth_2(self, x: T) -> T: # and here are always the same + return x + +a: MyClass[int] = MyClass() +a.meth_1(1) # OK +a.meth_2('a') # E + +# > A type variable used in a method that does not match any of the variables +# > that parameterize the class makes this method a generic function in that +# > variable. + +S = TypeVar("S") + +class Foo(Generic[T]): + def method(self, x: T, y: S) -> S: + return y + +x: Foo[int] = Foo() + +# Either of these is acceptable; one of the two should pass: +assert_type(x.method(0, "abc"), str) # E[method-str] +assert_type(x.method(0, "abc"), Literal["abc"]) # E[method-str] + +# Either of these is acceptable; one of the two should pass: +assert_type(x.method(0, b"abc"), bytes) # E[method-bytes] +assert_type(x.method(0, b"abc"), Literal[b"abc"]) # E[method-bytes] + +# > Unbound type variables should not appear in the bodies of generic functions, +# > or in the class bodies apart from method definitions. + +def fun_3(x: T) -> list[T]: + y: list[T] = [] # OK + z: list[S] = [] # E + return y + +class Bar(Generic[T]): + an_attr: list[S] = [] # E + + def do_something(self, x: S) -> S: # OK + return x + +# A generic class definition that appears inside a generic function +# should not use type variables that parameterize the generic function. + +def fun_4(x: T) -> list[T]: + a_list: list[T] = [] # OK + + class MyGeneric(Generic[T]): # E + ... + + return a_list + +# > A generic class nested in another generic class cannot use the same type +# > variables. The scope of the type variables of the outer class +# > doesn't cover the inner one + +class Outer(Generic[T]): + class Bad(Iterable[T]): # E + ... + class AlsoBad: + x: list[T] # E + + def __init__(self, x: list[T]) -> None: # E? + self.x = x + + class Inner(Iterable[S]): # OK + ... + attr: Inner[T] # OK + + alias: TypeAlias = list[T] # E + + def __init__(self, attr: Inner[T]) -> None: + self.attr = attr + + +# Test unbound type variables at global scope +global_var1: T # E +global_var2: list[T] = [] # E +list[T]() # E diff --git a/conformance/tests/generics_self_advanced.py b/conformance/tests/generics_self_advanced.py new file mode 100644 index 000000000..acf680c25 --- /dev/null +++ b/conformance/tests/generics_self_advanced.py @@ -0,0 +1,51 @@ +""" +Tests for advanced or special-case usage of the typing.Self type. +""" + +from typing import assert_type, Self + + +class ParentA: + # Test for property that returns Self. + @property + def prop1(self) -> Self: + raise NotImplementedError + +class ChildA(ParentA): + ... + + +assert_type(ParentA().prop1, ParentA) +assert_type(ChildA().prop1, ChildA) + + +# Test for a child that accesses an attribute within a parent +# whose type is annotated using Self. +class ParentB: + a: list[Self] + + @classmethod + def method1(cls) -> Self: + raise NotImplementedError + +class ChildB(ParentB): + b: int = 0 + + def method2(self) -> None: + assert_type(self, Self) + # Allow type checkers to error here, since Self definitions on + # non-final classes are unsound. + a = self.a # E? + assert_type(a, list[Self]) + assert_type(a[0], Self) + assert_type(self.method1(), Self) + + @classmethod + def method3(cls) -> None: + assert_type(cls, type[Self]) + # Allow type checkers to error here, since Self definitions on + # non-final classes are unsound. + a = cls.a # E? + assert_type(a, list[Self]) + assert_type(a[0], Self) + assert_type(cls.method1(), Self) diff --git a/conformance/tests/generics_self_attributes.py b/conformance/tests/generics_self_attributes.py new file mode 100644 index 000000000..56acd7df9 --- /dev/null +++ b/conformance/tests/generics_self_attributes.py @@ -0,0 +1,32 @@ +""" +Tests for usage of the typing.Self type with attributes. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#use-in-attribute-annotations + +from typing import TypeVar, Generic, Self +from dataclasses import dataclass + + +T = TypeVar("T") + +@dataclass +class LinkedList(Generic[T]): + value: T + next: Self | None = None + + +@dataclass +class OrdinalLinkedList(LinkedList[int]): + def ordinal_value(self) -> str: + return str(self.value) + + +# This should result in a type error. +xs = OrdinalLinkedList(value=1, next=LinkedList[int](value=2)) # E + +if xs.next is not None: + xs.next = OrdinalLinkedList(value=3, next=None) # OK + + # This should result in a type error. + xs.next = LinkedList[int](value=3, next=None) # E diff --git a/conformance/tests/generics_self_basic.py b/conformance/tests/generics_self_basic.py new file mode 100644 index 000000000..d5712383e --- /dev/null +++ b/conformance/tests/generics_self_basic.py @@ -0,0 +1,85 @@ +""" +Tests for basic usage of the typing.Self type. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#self. + +from typing import Callable, Generic, Self, TypeVar, assert_type + +T = TypeVar("T") + + +class Shape: + def set_scale(self, scale: float) -> Self: + assert_type(self, Self) + self.scale = scale + return self + + def method2(self) -> Self: + # This should result in a type error. + return Shape() # E + + def method3(self) -> "Shape": + return self + + @classmethod + def from_config(cls, config: dict[str, float]) -> Self: + assert_type(cls, type[Self]) + return cls() + + @classmethod + def cls_method2(cls) -> Self: + # This should result in a type error. + return Shape() # E + + @classmethod + def cls_method3(cls) -> "Shape": + return cls() + + def difference(self, other: Self) -> float: + assert_type(other, Self) + return 0.0 + + def apply(self, f: Callable[[Self], None]) -> None: + return f(self) + + +class Circle(Shape): + pass + + +assert_type(Shape().set_scale(1.0), Shape) +assert_type(Circle().set_scale(1.0), Circle) + +assert_type(Shape.from_config({}), Shape) +assert_type(Circle.from_config({}), Circle) + + +class Container(Generic[T]): + value: T + + def __init__(self, value: T) -> None: + self.value = value + + def set_value(self, value: T) -> Self: + raise NotImplementedError + + # This should generate an error because Self isn't subscriptable. + def foo(self, other: Self[int]) -> None: # E + pass + + +def object_with_concrete_type( + int_container: Container[int], str_container: Container[str] +) -> None: + assert_type(int_container.set_value(42), Container[int]) + assert_type(str_container.set_value("hello"), Container[str]) + + +def object_with_generic_type( + container: Container[T], + value: T, +) -> Container[T]: + val = container.set_value(value) + assert_type(val, Container[T]) + return val diff --git a/conformance/tests/generics_self_protocols.py b/conformance/tests/generics_self_protocols.py new file mode 100644 index 000000000..1930bebe6 --- /dev/null +++ b/conformance/tests/generics_self_protocols.py @@ -0,0 +1,64 @@ +""" +Tests for usage of the typing.Self type with protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#use-in-protocols + +from typing import Protocol, Self, assert_type + + +class ShapeProtocol(Protocol): + def set_scale(self, scale: float) -> Self: + ... + + +class ReturnSelf: + scale: float = 1.0 + + def set_scale(self, scale: float) -> Self: + self.scale = scale + return self + + +class ReturnConcreteShape: + scale: float = 1.0 + + def set_scale(self, scale: float) -> "ReturnConcreteShape": + self.scale = scale + return self + + +class BadReturnType: + scale: float = 1.0 + + def set_scale(self, scale: float) -> int: + self.scale = scale + return 42 + + +class ReturnDifferentClass: + scale: float = 1.0 + + def set_scale(self, scale: float) -> ReturnConcreteShape: + return ReturnConcreteShape() + + +def accepts_shape(shape: ShapeProtocol) -> None: + y = shape.set_scale(0.5) + assert_type(y, ShapeProtocol) + + +def main( + return_self_shape: ReturnSelf, + return_concrete_shape: ReturnConcreteShape, + bad_return_type: BadReturnType, + return_different_class: ReturnDifferentClass, +) -> None: + accepts_shape(return_self_shape) # OK + accepts_shape(return_concrete_shape) # OK + + # This should generate a type error. + accepts_shape(bad_return_type) # E + + # Not OK because it returns a non-subclass. + accepts_shape(return_different_class) # E diff --git a/conformance/tests/generics_self_usage.py b/conformance/tests/generics_self_usage.py new file mode 100644 index 000000000..99f36a811 --- /dev/null +++ b/conformance/tests/generics_self_usage.py @@ -0,0 +1,128 @@ +""" +Tests for valid and invalid usage of the typing.Self type. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#valid-locations-for-self + +from typing import Any, Callable, Generic, Self, TypeAlias, TypeVar + +class ReturnsSelf: + def foo(self) -> Self: # Accepted + return self + + @classmethod + def bar(cls) -> Self: # Accepted + return cls(1) + + def __new__(cls, value: int) -> Self: # Accepted + return cls(1) + + def explicitly_use_self(self: Self) -> Self: # Accepted + return self + + # Accepted (Self can be nested within other types) + def returns_list(self) -> list[Self]: + return [] + + # Accepted (Self can be nested within other types) + @classmethod + def return_cls(cls) -> type[Self]: + return cls + +class Child(ReturnsSelf): + # Accepted (we can override a method that uses Self annotations) + def foo(self) -> Self: + return self + +class TakesSelf: + def foo(self, other: Self) -> bool: # Accepted + return True + +class Recursive: + # Accepted (treated as an @property returning ``Self | None``) + next: Self | None + +class CallableAttribute: + def foo(self) -> int: + return 0 + + # Accepted (treated as an @property returning the Callable type) + bar: Callable[[Self], int] = foo + +class HasNestedFunction: + x: int = 42 + + def foo(self) -> None: + + # Accepted (Self is bound to HasNestedFunction). + def nested(z: int, inner_self: Self) -> Self: + print(z) + print(inner_self.x) + return inner_self + + nested(42, self) # OK + + +class Outer: + class Inner: + def foo(self) -> Self: # Accepted (Self is bound to Inner) + return self + + +# This should generate an error. +def foo(bar: Self) -> Self: ... # E: not within a class + +# This should generate an error. +bar: Self # E: not within a class + +TFoo2 = TypeVar("TFoo2", bound="Foo2") + +class Foo2: + # Rejected (Self is treated as unknown). + def has_existing_self_annotation(self: TFoo2) -> Self: # E + raise NotImplementedError + +class Foo3: + def return_concrete_type(self) -> Self: + return Foo3() # E (see FooChild below for rationale) + +class Foo3Child(Foo3): + child_value: int = 42 + + def child_method(self) -> None: + y = self.return_concrete_type() + y.child_value + +T = TypeVar("T") + +class Bar(Generic[T]): + def bar(self) -> T: + raise NotImplementedError + +# This should generate an error. +class Baz(Bar[Self]): ... # E + +class Baz2(Self): ... # E + +# This should generate an error. +TupleSelf: TypeAlias = tuple[Self] # E + +class Base: + @staticmethod + # This should generate an error. + def make() -> Self: # E + raise NotImplementedError + + @staticmethod + # This should generate an error. + def return_parameter(foo: Self) -> Self: # E + raise NotImplementedError + +class MyMetaclass(type): + # This should generate an error. + def __new__(cls, *args: Any) -> Self: # E + raise NotImplementedError + + # This should generate an error. + def __mul__(cls, count: int) -> list[Self]: # E + raise NotImplementedError diff --git a/conformance/tests/generics_syntax_compatibility.py b/conformance/tests/generics_syntax_compatibility.py new file mode 100644 index 000000000..144cbb78c --- /dev/null +++ b/conformance/tests/generics_syntax_compatibility.py @@ -0,0 +1,27 @@ +""" +Tests the compatibility rules between type parameter syntax (introduced in PEP 695) +and traditional TypeVars. +""" + +# Specification: https://peps.python.org/pep-0695/#compatibility-with-traditional-typevars + +from typing import TypeVar + + +K = TypeVar("K") + + +class ClassA[V](dict[K, V]): # E: traditional TypeVar not allowed here + ... + + +class ClassB[K, V](dict[K, V]): # OK + ... + + +class ClassC[V]: + def method1(self, a: V, b: K) -> V | K: # OK + raise NotImplementedError + + def method2[M](self, a: M, b: K) -> M | K: # E + raise NotImplementedError diff --git a/conformance/tests/generics_syntax_declarations.py b/conformance/tests/generics_syntax_declarations.py new file mode 100644 index 000000000..62d2d1019 --- /dev/null +++ b/conformance/tests/generics_syntax_declarations.py @@ -0,0 +1,83 @@ +""" +Validates the type parameter syntax introduced in PEP 695. +""" + +# Specification: https://peps.python.org/pep-0695/#type-parameter-declarations + + +# This generic class is parameterized by a TypeVar T, a +# TypeVarTuple Ts, and a ParamSpec P. +from typing import Generic, Protocol + + +class ChildClass[T, *Ts, **P]: + pass + + +class ClassA[T](Generic[T]): # E: Runtime error + ... + + +class ClassB[S, T](Protocol): # OK + ... + + +class ClassC[S, T](Protocol[S, T]): # E + ... + + +class ClassD[T: str]: + def method1(self, x: T): + x.capitalize() # OK + x.is_integer() # E + + +class ClassE[T: dict[str, int]]: # OK + pass + + +class ClassF[S: ForwardReference[int], T: "ForwardReference[str]"]: # OK + ... + + +class ClassG[V]: + class ClassD[T: dict[str, V]]: # E: generic type not allowed + ... + + +class ClassH[T: [str, int]]: # E: illegal expression form + ... + + +class ClassI[AnyStr: (str, bytes)]: # OK + ... + + +class ClassJ[T: (ForwardReference[int], "ForwardReference[str]", bytes)]: # OK + ... + + +class ClassK[T: ()]: # E: two or more types required + ... + + +class ClassL[T: (str,)]: # E: two or more types required + ... + + +t1 = (bytes, str) + + +class ClassM[T: t1]: # E: literal tuple expression required + ... + + +class ClassN[T: (3, bytes)]: # E: invalid expression form + ... + + +class ClassO[T: (list[S], str)]: # E: generic type + ... + + +class ForwardReference[T]: ... diff --git a/conformance/tests/generics_syntax_infer_variance.py b/conformance/tests/generics_syntax_infer_variance.py new file mode 100644 index 000000000..e0f50a3b7 --- /dev/null +++ b/conformance/tests/generics_syntax_infer_variance.py @@ -0,0 +1,166 @@ +""" +Tests the handling of "infer_variance" parameter for TypeVar. +""" + +# Specification: https://peps.python.org/pep-0695/#auto-variance-for-typevar + +from typing import Final, Generic, Iterator, overload, Sequence, TypeVar +from dataclasses import dataclass + + +T = TypeVar("T", infer_variance=True) +K = TypeVar("K", infer_variance=True) +V = TypeVar("V", infer_variance=True) + +S1 = TypeVar("S1", covariant=True, infer_variance=True) # E: cannot use covariant with infer_variance + +S2 = TypeVar("S2", contravariant=True, infer_variance=True) # E: cannot use contravariant with infer_variance + + +class ShouldBeCovariant1(Generic[T]): + def __getitem__(self, index: int) -> T: + raise NotImplementedError + + def __iter__(self) -> Iterator[T]: + raise NotImplementedError + + +vco1_1: ShouldBeCovariant1[float] = ShouldBeCovariant1[int]() # OK +vco1_2: ShouldBeCovariant1[int] = ShouldBeCovariant1[float]() # E + + +class ShouldBeCovariant2(Sequence[T]): + @overload + def __getitem__(self, index: int) -> T: + ... + @overload + def __getitem__(self, index: slice) -> Sequence[T]: + ... + def __getitem__(self, index: int | slice) -> T | Sequence[T]: + raise NotImplementedError + + def __len__(self) -> int: + raise NotImplementedError + + +vco2_1: ShouldBeCovariant2[float] = ShouldBeCovariant2[int]() # OK +vco2_2: ShouldBeCovariant2[int] = ShouldBeCovariant2[float]() # E + + +class ShouldBeCovariant3(Generic[T]): + def method1(self) -> "ShouldBeCovariant2[T]": + raise NotImplementedError + + +vco3_1: ShouldBeCovariant3[float] = ShouldBeCovariant3[int]() # OK +vco3_2: ShouldBeCovariant3[int] = ShouldBeCovariant3[float]() # E + + +@dataclass(frozen=True) +class ShouldBeCovariant4(Generic[T]): + x: T + + +# This test is problematic as of Python 3.13 because of the +# newly synthesized "__replace__" method, which causes the type +# variable to be inferred as invariant rather than covariant. +# See https://github.com/python/mypy/issues/17623#issuecomment-2266312738 +# for details. Until we sort this out, we'll leave this test commented +# out. + +# vo4_1: ShouldBeCovariant4[float] = ShouldBeCovariant4[int](1) # OK +# vo4_4: ShouldBeCovariant4[int] = ShouldBeCovariant4[float](1.0) # E + + +class ShouldBeCovariant5(Generic[T]): + def __init__(self, x: T) -> None: + self._x = x + + @property + def x(self) -> T: + return self._x + + +vo5_1: ShouldBeCovariant5[float] = ShouldBeCovariant5[int](1) # OK +vo5_2: ShouldBeCovariant5[int] = ShouldBeCovariant5[float](1.0) # E + + +class ShouldBeCovariant6(Generic[T]): + x: Final[T] + + def __init__(self, value: T): + self.x = value + + +vo6_1: ShouldBeCovariant6[float] = ShouldBeCovariant6[int](1) # OK +vo6_2: ShouldBeCovariant6[int] = ShouldBeCovariant6[float](1.0) # E + + +class ShouldBeInvariant1(Generic[T]): + def __init__(self, value: T) -> None: + self._value = value + + @property + def value(self) -> T: + return self._value + + @value.setter + def value(self, value: T): + self._value = value + + +vinv1_1: ShouldBeInvariant1[float] = ShouldBeInvariant1[int](1) # E +vinv1_2: ShouldBeInvariant1[int] = ShouldBeInvariant1[float](1.1) # E + + +class ShouldBeInvariant2(Generic[T]): + def __init__(self, value: T) -> None: + self._value = value + + def get_value(self) -> T: + return self._value + + def set_value(self, value: T): + self._value = value + + +vinv2_1: ShouldBeInvariant2[float] = ShouldBeInvariant2[int](1) # E +vinv2_2: ShouldBeInvariant2[int] = ShouldBeInvariant2[float](1.1) # E + + +class ShouldBeInvariant3(dict[K, V]): + pass + + +vinv3_1: ShouldBeInvariant3[float, str] = ShouldBeInvariant3[int, str]() # E +vinv3_2: ShouldBeInvariant3[int, str] = ShouldBeInvariant3[float, str]() # E +vinv3_3: ShouldBeInvariant3[str, float] = ShouldBeInvariant3[str, int]() # E +vinv3_4: ShouldBeInvariant3[str, int] = ShouldBeInvariant3[str, float]() # E + + +@dataclass +class ShouldBeInvariant4[T]: + x: T + + +vinv4_1: ShouldBeInvariant4[float] = ShouldBeInvariant4[int](1) # E + + +class ShouldBeInvariant5[T]: + def __init__(self, x: T) -> None: + self.x = x + + +vinv5_1: ShouldBeInvariant5[float] = ShouldBeInvariant5[int](1) # E + + +class ShouldBeContravariant1(Generic[T]): + def __init__(self, value: T) -> None: + pass + + def set_value(self, value: T): + pass + + +vcontra1_1: ShouldBeContravariant1[float] = ShouldBeContravariant1[int](1) # E +vcontra1_2: ShouldBeContravariant1[int] = ShouldBeContravariant1[float](1.2) # OK diff --git a/conformance/tests/generics_syntax_scoping.py b/conformance/tests/generics_syntax_scoping.py new file mode 100644 index 000000000..768d8fda7 --- /dev/null +++ b/conformance/tests/generics_syntax_scoping.py @@ -0,0 +1,125 @@ +""" +Tests the scoping rules for type parameter syntax introduced in PEP 695. +""" + +# Specification: https://peps.python.org/pep-0695/#type-parameter-scopes + +from typing import Callable, Mapping, Sequence, TypeVar, assert_type + +# > A compiler error or runtime exception is generated if the definition +# > of an earlier type parameter references a later type parameter even +# > if the name is defined in an outer scope. + + +class ClassA[S, T: Sequence[S]]: # E + ... + + +class ClassB[S: Sequence[T], T]: # E + ... + + +class Foo[T]: + ... + + +class BaseClassC[T]: + def __init_subclass__(cls, param: type[Foo[T]]) -> None: + ... + + +class ClassC[T](BaseClassC[T], param=Foo[T]): # OK + ... + + +print(T) # E: Runtime error: 'T' is not defined + + +def decorator1[ + T, **P, R +](x: type[Foo[T]]) -> Callable[[Callable[P, R]], Callable[P, R]]: + raise NotImplementedError + + +@decorator1(Foo[T]) # E: Runtime error: 'T' is not defined +class ClassD[T]: + ... + + +type Alias1[K, V] = Mapping[K, V] | Sequence[K] + + +S: int = int(0) + + +def outer1[S](x: str): + S: str = x + T: int = 1 + + def outer2[T](): + def inner1(): + nonlocal S # OK + assert_type(S, str) + # nonlocal T # Syntax error + + def inner2(): + global S # OK + assert_type(S, int) + + +class Outer1: + class Private: + pass + + class Inner[T](Private, Sequence[T]): # OK + pass + + def method1[T](self, a: Inner[T]) -> Inner[T]: # OK + return a + + +def decorator2[**P, R](x: int) -> Callable[[Callable[P, R]], Callable[P, R]]: + raise NotImplementedError + + +T = int(0) + + +@decorator2(T) # OK +class ClassE[T](Sequence[T]): + T = int(1) + + def method1[T](self): # E + ... + + def method2[T](self, x=T): # E + ... + + def method3[T](self, x: T): # E + ... + + +T = int(0) + + +def f(a: int, b: str, c: complex): + class Outer2[T]: + T = a + + assert_type(T, int) + + class Inner1: + T = b + + assert_type(T, str) + + def inner_method(self): + assert_type(T, TypeVar) + + def outer_method(self): + T = c + + assert_type(T, complex) + + def inner_func(): + assert_type(T, complex) diff --git a/conformance/tests/generics_type_erasure.py b/conformance/tests/generics_type_erasure.py new file mode 100644 index 000000000..c7462259a --- /dev/null +++ b/conformance/tests/generics_type_erasure.py @@ -0,0 +1,56 @@ +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#instantiating-generic-classes-and-type-erasure + +from typing import Any, TypeVar, Generic, assert_type + +T = TypeVar("T") + +# > If the constructor (__init__ or __new__) uses T in its signature, and a +# > corresponding argument value is passed, the type of the corresponding +# > argument(s) is substituted. Otherwise, Any is assumed. + +class Node(Generic[T]): + label: T + def __init__(self, label: T | None = None) -> None: + if label is not None: + self.label = label + +assert_type(Node(''), Node[str]) +assert_type(Node(0), Node[int]) +assert_type(Node(), Node[Any]) + +assert_type(Node(0).label, int) +assert_type(Node().label, Any) + +# > In case the inferred type uses [Any] but the intended type is more specific, +# > you can use an annotation to force the type of the variable, e.g.: + +n1: Node[int] = Node() +assert_type(n1, Node[int]) +n2: Node[str] = Node() +assert_type(n2, Node[str]) + +n3 = Node[int]() +assert_type(n3, Node[int]) +n4 = Node[str]() +assert_type(n4, Node[str]) + +n5 = Node[int](0) # OK +n6 = Node[int]("") # E +n7 = Node[str]("") # OK +n8 = Node[str](0) # E + +Node[int].label = 1 # E +Node[int].label # E +Node.label = 1 # E +Node.label # E +type(n1).label # E +assert_type(n1.label, int) +assert_type(Node[int]().label, int) +n1.label = 1 # OK + +# > [...] generic versions of concrete collections can be instantiated: + +from typing import DefaultDict + +data = DefaultDict[int, bytes]() +assert_type(data[0], bytes) diff --git a/conformance/tests/generics_typevartuple_args.py b/conformance/tests/generics_typevartuple_args.py new file mode 100644 index 000000000..6d692d942 --- /dev/null +++ b/conformance/tests/generics_typevartuple_args.py @@ -0,0 +1,81 @@ +""" +Tests for the use of TypeVarTuple with "*args" parameter. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#args-as-a-type-variable-tuple + +# > If *args is annotated as a type variable tuple, the types of the individual +# > arguments become the types in the type variable tuple. + +from typing import TypeVarTuple, assert_type + + +Ts = TypeVarTuple("Ts") + + +def args_to_tuple(*args: *Ts) -> tuple[*Ts]: + raise NotImplementedError + + +class Env: + ... + + +def exec_le(path: str, *args: *tuple[*Ts, Env], env: Env | None = None) -> tuple[*Ts]: + raise NotImplementedError + + +def has_int_and_str(x: int, y: str): + assert_type(args_to_tuple(x, y), tuple[int, str]) + + assert_type(exec_le("", Env()), tuple[()]) # OK + assert_type(exec_le(y, x, y, Env()), tuple[int, str]) # OK + exec_le("", 0, "") # E + exec_le("", 0, "", env=Env()) # E + + +# > Using an unpacked unbounded tuple is equivalent to the +# > PEP 484#arbitrary-argument-lists-and-default-argument-values behavior of +# > *args: int, which accepts zero or more values of type int. + + +def func1(*args: *tuple[int, ...]) -> None: + ... + + +func1() # OK +func1(1, 2, 3, 4, 5) # OK +func1(1, "2", 3) # E + + +def func2(*args: *tuple[int, *tuple[str, ...], str]) -> None: + ... + + +func2(1, "") # OK +func2(1, "", "", "", "") # OK +func2(1, 1, "") # E +func2(1) # E +func2("") # E + + +def func3(*args: *tuple[int, str]) -> None: + ... + + +func3(1, "hello") # OK +func3(1) # E + + +def func4(*args: tuple[*Ts]): + ... + + +func4((0,), (1,)) # OK +func4((0,), (1, 2)) # E (length mismatch) +func4((0,), ("1",)) # OK + + +# This is a syntax error, so leave it commented out. +# def func5(**kwargs: *Ts): # E +# ... diff --git a/conformance/tests/generics_typevartuple_basic.py b/conformance/tests/generics_typevartuple_basic.py new file mode 100644 index 000000000..47b493ff0 --- /dev/null +++ b/conformance/tests/generics_typevartuple_basic.py @@ -0,0 +1,108 @@ +""" +Tests basic usage of TypeVarTuple. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#typevartuple + +from typing import Generic, NewType, TypeVarTuple, assert_type + +Ts = TypeVarTuple("Ts") + + +class Array1(Generic[*Ts]): + ... + + +def func1(*args: *Ts) -> tuple[*Ts]: + raise NotImplementedError + + +Shape = TypeVarTuple("Shape") + + +class Array(Generic[*Shape]): + def __init__(self, shape: tuple[*Shape]): + self._shape: tuple[*Shape] = shape + + def get_shape(self) -> tuple[*Shape]: + return self._shape + + +Height = NewType("Height", int) +Width = NewType("Width", int) +Time = NewType("Time", int) +Batch = NewType("Batch", int) + +v1: Array[Height, Width] = Array((Height(1), Width(2))) # OK +v2: Array[Batch, Height, Width] = Array((Batch(1), Height(1), Width(1))) # OK +v3: Array[Time, Batch, Height, Width] = Array( + (Time(1), Batch(1), Height(1), Width(1)) +) # OK + +v4: Array[Height, Width] = Array(Height(1)) # E +v5: Array[Batch, Height, Width] = Array((Batch(1), Width(1))) # E +v6: Array[Time, Batch, Height, Width] = Array( # E[v6] + (Time(1), Batch(1), Width(1), Height(1)) # E[v6] +) + + +# > Type Variable Tuples Must Always be Unpacked + + +class ClassA(Generic[Shape]): # E: not unpacked + def __init__(self, shape: tuple[Shape]): # E: not unpacked + self._shape: tuple[*Shape] = shape + + def get_shape(self) -> tuple[Shape]: # E: not unpacked + raise NotImplementedError + + def method1(*args: Shape) -> None: # E: not unpacked + ... + + +# > TypeVarTuple does not yet support specification of variance, bounds, constraints. + +Ts1 = TypeVarTuple("Ts1", covariant=True) # E +Ts2 = TypeVarTuple("Ts2", int, float) # E +Ts3 = TypeVarTuple("Ts3", bound=int) # E + + +# > If the same TypeVarTuple instance is used in multiple places in a signature +# > or class, a valid type inference might be to bind the TypeVarTuple to a +# > tuple of a union of types. + + +def func2(arg1: tuple[*Ts], arg2: tuple[*Ts]) -> tuple[*Ts]: + raise NotImplementedError + + +# > We do not allow this; type unions may not appear within the tuple. +# > If a type variable tuple appears in multiple places in a signature, +# > the types must match exactly (the list of type parameters must be the +# > same length, and the type parameters themselves must be identical) + +def check_func2(x: int): + assert_type(func2((x,), (1,)), tuple[int]) # OK +func2((0,), (0.0,)) # OK +func2((0.0,), (0,)) # OK +func2((0,), (1,)) # OK + +func2((0,), ("0",)) # OK +func2((0, 0), (0,)) # E + + +def multiply(x: Array[*Shape], y: Array[*Shape]) -> Array[*Shape]: + raise NotImplementedError + + +def func3(x: Array[Height], y: Array[Width], z: Array[Height, Width]): + multiply(x, x) # OK + multiply(x, y) # E + multiply(x, z) # E + + +# > Only a single type variable tuple may appear in a type parameter list. + + +class Array3(Generic[*Ts1, *Ts2]): # E + ... diff --git a/conformance/tests/generics_typevartuple_callable.py b/conformance/tests/generics_typevartuple_callable.py new file mode 100644 index 000000000..b8a9f577e --- /dev/null +++ b/conformance/tests/generics_typevartuple_callable.py @@ -0,0 +1,50 @@ +""" +Tests the use of TypeVarTuple within a Callable. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#type-variable-tuples-with-callable + +# > Type variable tuples can also be used in the arguments section of a Callable. + + +from typing import Callable, TypeVar, TypeVarTuple, assert_type + +Ts = TypeVarTuple("Ts") +T = TypeVar("T") + + +class Process: + def __init__(self, target: Callable[[*Ts], None], args: tuple[*Ts]) -> None: + ... + + +def func1(arg1: int, arg2: str) -> None: + ... + + +Process(target=func1, args=(0, "")) # OK +Process(target=func1, args=("", 0)) # E + + +def func2(f: Callable[[int, *Ts, T], tuple[T, *Ts]]) -> tuple[*Ts, T]: + raise NotImplementedError + + +def callback1(a: int, b: str, c: int, d: complex) -> tuple[complex, str, int]: + raise NotImplementedError + + +def callback2(a: int, d: str) -> tuple[str]: + raise NotImplementedError + + +assert_type(func2(callback1), tuple[str, int, complex]) +assert_type(func2(callback2), tuple[str]) + + +def func3(*args: *tuple[int, *Ts, T]) -> tuple[T, *Ts]: + raise NotImplementedError + + +def has_int_and_str(a: int, b: str, c: float, d: complex): + assert_type(func3(a, b, c, d), tuple[complex, str, float]) diff --git a/conformance/tests/generics_typevartuple_concat.py b/conformance/tests/generics_typevartuple_concat.py new file mode 100644 index 000000000..1fb23f6d9 --- /dev/null +++ b/conformance/tests/generics_typevartuple_concat.py @@ -0,0 +1,57 @@ +""" +Tests type concatenation using TypeVarTuples. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#type-concatenation + +# > Type variable tuples don’t have to be alone; normal types can be prefixed and/or suffixed. + +from typing import Generic, NewType, TypeVar, TypeVarTuple, assert_type + + +Height = NewType("Height", int) +Width = NewType("Width", int) +Batch = NewType("Batch", int) +Channels = NewType("Channels", int) + +Shape = TypeVarTuple("Shape") +Ts = TypeVarTuple("Ts") +T = TypeVar("T") + + +class Array(Generic[*Ts]): + ... + + +def add_batch_axis(x: Array[*Shape]) -> Array[Batch, *Shape]: + raise NotImplementedError + + +def del_batch_axis(x: Array[Batch, *Shape]) -> Array[*Shape]: + raise NotImplementedError + + +def add_batch_channels(x: Array[*Shape]) -> Array[Batch, *Shape, Channels]: + raise NotImplementedError + + +def func1(a: Array[Height, Width]): + b = add_batch_axis(a) # OK + assert_type(b, Array[Batch, Height, Width]) + c = del_batch_axis(b) # OK + assert_type(c, Array[Height, Width]) + d = add_batch_channels(a) # OK + assert_type(d, Array[Batch, Height, Width, Channels]) + + +def prefix_tuple(x: T, y: tuple[*Ts]) -> tuple[T, *Ts]: + raise NotImplementedError + + +def call_prefix_tuple(x: int, y: bool, z: str): + result = prefix_tuple(x=x, y=(y, z)) + assert_type(result, tuple[int, bool, str]) + + +def move_first_element_to_last(tup: tuple[T, *Ts]) -> tuple[*Ts, T]: + return (*tup[1:], tup[0]) diff --git a/conformance/tests/generics_typevartuple_overloads.py b/conformance/tests/generics_typevartuple_overloads.py new file mode 100644 index 000000000..7f6fa0301 --- /dev/null +++ b/conformance/tests/generics_typevartuple_overloads.py @@ -0,0 +1,31 @@ +""" +Tests the use of TypeVarTuple in function overloads. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#overloads-for-accessing-individual-types + +from typing import Any, Generic, TypeVar, TypeVarTuple, assert_type, overload + + +Shape = TypeVarTuple("Shape") +Axis1 = TypeVar("Axis1") +Axis2 = TypeVar("Axis2") +Axis3 = TypeVar("Axis3") + + +class Array(Generic[*Shape]): + @overload + def transpose(self: "Array[Axis1, Axis2]") -> "Array[Axis2, Axis1]": + ... + + @overload + def transpose(self: "Array[Axis1, Axis2, Axis3]") -> "Array[Axis3, Axis2, Axis1]": + ... + + def transpose(self) -> Any: + raise NotImplementedError + + +def func1(a: Array[Axis1, Axis2], b: Array[Axis1, Axis2, Axis3]): + assert_type(a.transpose(), Array[Axis2, Axis1]) + assert_type(b.transpose(), Array[Axis3, Axis2, Axis1]) diff --git a/conformance/tests/generics_typevartuple_specialization.py b/conformance/tests/generics_typevartuple_specialization.py new file mode 100644 index 000000000..3277e40b8 --- /dev/null +++ b/conformance/tests/generics_typevartuple_specialization.py @@ -0,0 +1,163 @@ +""" +Tests the handling of a TypeVarTuple specialization. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#behaviour-when-type-parameters-are-not-specified + +from typing import Any, Generic, NewType, TypeVar, TypeVarTuple, assert_type + + +Ts = TypeVarTuple("Ts") +Height = NewType("Height", int) +Width = NewType("Width", int) +Time = NewType("Time", int) + + +class Array(Generic[*Ts]): + ... + + +def takes_any_array1(arr: Array): + ... + + +def takes_any_array2(arr: Array[*tuple[Any, ...]]): + ... + + +def func1(x: Array[Height, Width]): + takes_any_array1(x) # OK + takes_any_array2(x) # OK + + +def func2(y: Array[Time, Height, Width]): + takes_any_array1(y) # OK + takes_any_array2(y) # OK + + +# > Generic aliases can be created using a type variable tuple in a similar +# > way to regular type variables. + +IntTuple = tuple[int, *Ts] +NamedArray = tuple[str, Array[*Ts]] + + +def func3(a: IntTuple[float, bool], b: NamedArray[Height]): + assert_type(a, tuple[int, float, bool]) + assert_type(b, tuple[str, Array[Height]]) + + +def func4(a: IntTuple[()], b: NamedArray[()]): + assert_type(a, tuple[int]) + assert_type(b, tuple[str, Array[()]]) + + +Shape = TypeVarTuple("Shape") +DType = TypeVar("DType") + + +class Array2(Generic[DType, *Shape]): + ... + + +FloatArray = Array2[float, *Shape] +Array1D = Array2[DType, Any] + + +def func5_0(a: Array1D, b: Array1D[int]): + assert_type(a, Array2[Any, Any]) + assert_type(b, Array2[int, Any]) + + +def takes_float_array_of_any_shape(x: FloatArray): + ... + + +def func5_1(x: FloatArray[Height, Width]): + takes_float_array_of_any_shape(x) # OK + + +def takes_float_array_with_specific_shape(y: FloatArray[Height, Width]): + ... + + +def func5_2(x: FloatArray): + takes_float_array_with_specific_shape(x) # OK + + +T = TypeVar("T") +VariadicTuple = tuple[T, *Ts] + + +def func6(a: VariadicTuple[str, int], b: VariadicTuple[float], c: VariadicTuple): + assert_type(a, tuple[str, int]) + assert_type(b, tuple[float]) + assert_type(c, tuple[Any, *tuple[Any, ...]]) + + +Ts1 = TypeVarTuple("Ts1") +Ts2 = TypeVarTuple("Ts2") + +IntTupleVar = tuple[int, *Ts1] # OK +IntFloatTupleVar = IntTupleVar[float, *Ts2] # OK +IntFloatsTupleVar = IntTupleVar[*tuple[float, ...]] # OK + + +IntTupleGeneric = tuple[int, T] + +IntTupleGeneric[str] # OK +IntTupleGeneric[*Ts] # E +IntTupleGeneric[*tuple[float, ...]] # E + + +T1 = TypeVar("T1") +T2 = TypeVar("T2") +T3 = TypeVar("T3") + +TA1 = tuple[*Ts, T1, T2] # OK +TA2 = tuple[T1, T2, *Ts] # OK +TA3 = tuple[T1, *Ts, T2, T3] # OK +TA4 = tuple[T1, T2, *tuple[int, ...]] # OK +TA5 = tuple[T1, *Ts, T2, *Ts] # E +TA6 = tuple[T1, *Ts, T2, *tuple[int, ...]] # E + + +TA7 = tuple[*Ts, T1, T2] + +v1: TA7[int] # E: requires at least two type arguments + + +def func7(a: TA7[*Ts, T1, T2]) -> tuple[tuple[*Ts], T1, T2]: + raise NotImplementedError + + +def func8(a: TA7[str, bool], b: TA7[str, bool, float], c: TA7[str, bool, float, int]): + assert_type(func7(a), tuple[tuple[()], str, bool]) + assert_type(func7(b), tuple[tuple[str], bool, float]) + assert_type(func7(c), tuple[tuple[str, bool], float, int]) + + +TA8 = tuple[T1, *Ts, T2, T3] + + +def func9(a: TA8[T1, *Ts, T2, T3]) -> tuple[tuple[*Ts], T1, T2, T3]: + raise NotImplementedError + + +def func10(a: TA8[str, bool, float], b: TA8[str, bool, float, int]): + assert_type(func9(a), tuple[tuple[()], str, bool, float]) + assert_type(func9(b), tuple[tuple[bool], str, float, int]) + + +TA9 = tuple[*Ts, T1] +TA10 = TA9[*tuple[int, ...]] # OK + + +def func11(a: TA10, b: TA9[*tuple[int, ...], str], c: TA9[*tuple[int, ...], str]): + assert_type(a, tuple[*tuple[int, ...], int]) + assert_type(b, tuple[*tuple[int, ...], str]) + assert_type(c, tuple[*tuple[int, ...], str]) + + +TA11 = tuple[T, *Ts1] +TA12 = TA11[*Ts2] # E diff --git a/conformance/tests/generics_typevartuple_unpack.py b/conformance/tests/generics_typevartuple_unpack.py new file mode 100644 index 000000000..7b58715ab --- /dev/null +++ b/conformance/tests/generics_typevartuple_unpack.py @@ -0,0 +1,46 @@ +""" +Tests unpack operations for TypeVarTuple. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#unpacking-tuple-types + +from typing import Any, Generic, NewType, TypeVarTuple + +Height = NewType("Height", int) +Width = NewType("Width", int) +Batch = NewType("Batch", int) +Channels = NewType("Channels", int) + +Ts = TypeVarTuple("Ts") + + +class Array(Generic[*Ts]): + ... + + +def process_batch_channels(x: Array[Batch, *tuple[Any, ...], Channels]) -> None: + ... + + +def func3( + x: Array[Batch, Height, Width, Channels], y: Array[Batch, Channels], z: Array[Batch] +): + process_batch_channels(x) # OK + process_batch_channels(y) # OK + process_batch_channels(z) # E + + +Shape = TypeVarTuple("Shape") + + +def expect_variadic_array(x: Array[Batch, *Shape]) -> None: + ... + + +def expect_precise_array(x: Array[Batch, Height, Width, Channels]) -> None: + ... + + +def func4(y: Array[*tuple[Any, ...]]): + expect_variadic_array(y) # OK + expect_precise_array(y) # OK diff --git a/conformance/tests/generics_upper_bound.py b/conformance/tests/generics_upper_bound.py new file mode 100644 index 000000000..c2208c6ef --- /dev/null +++ b/conformance/tests/generics_upper_bound.py @@ -0,0 +1,57 @@ +""" +Tests TypeVars with upper bounds. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#type-variables-with-an-upper-bound + +from typing import Collection, Generic, Sized, TypeVar, assert_type + +# > A type variable may specify an upper bound using bound= + +T_Good1 = TypeVar("T_Good1", bound=int) # OK +T_Good2 = TypeVar("T_Good2", bound="ForwardRef | str") # OK + + +class ForwardRef: ... + + +# > itself cannot be parameterized by type variables. + +T = TypeVar("T") + + +class Test(Generic[T]): + T_Bad1 = TypeVar("T_Bad1", bound=list[T]) # E + + +ST = TypeVar("ST", bound=Sized) + + +def longer(x: ST, y: ST) -> ST: + if len(x) > len(y): + return x + else: + return y + + +def f(list1: list[int], list2: list[int], set1: set[int], set2: set[int]): + assert_type(longer(list1, list2), list[int]) + assert_type(longer(set1, set2), set[int]) + + # Either answer here is conformant with the spec; + # exactly one should pass: + assert_type(longer(list1, set1), list[int] | set[int]) # E[mixed-collections] + assert_type(longer(list1, set1), Collection[int]) # E[mixed-collections] + + +def requires_collection(c: Collection[int]) -> None: ... + + +requires_collection(longer([1], [1, 2])) # OK + +longer(3, 3) # E + + +# > An upper bound cannot be combined with type constraints + +T_Bad2 = TypeVar("T_Bad2", str, int, bound="int") # E diff --git a/conformance/tests/generics_variance.py b/conformance/tests/generics_variance.py new file mode 100644 index 000000000..9f2f143ea --- /dev/null +++ b/conformance/tests/generics_variance.py @@ -0,0 +1,198 @@ +""" +Tests the handling and enforcement of TypeVar variance. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/generics.html#variance + +from typing import Sequence, TypeVar, Generic +from collections.abc import Iterable, Iterator + +# > To facilitate the declaration of container types where covariant or +# > contravariant type checking is acceptable, type variables accept +# > keyword arguments covariant=True or contravariant=True. At most one of +# > these may be passed. +X1 = TypeVar("X1", covariant=True, contravariant=True) # E + + +T = TypeVar("T") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class ImmutableList(Generic[T_co]): + def __init__(self, items: Iterable[T_co]) -> None: + ... + + def __iter__(self) -> Iterator[T_co]: + raise NotImplementedError + + +class Employee: + ... + + +class Manager(Employee): + ... + + +managers: ImmutableList[Manager] = ImmutableList([Manager()]) +employees: ImmutableList[Employee] = managers # OK + + +E = TypeVar("E", bound=Employee) + + +def dump_employee(e: E) -> E: + return e + + +dump_employee(Manager()) # OK + + +B_co = TypeVar("B_co", covariant=True) + + +# > Variance has no meaning, and should therefore be ignored by type checkers, +# > if a type variable is bound to a generic function or type alias. +def func(x: list[B_co]) -> B_co: # OK + raise NotImplementedError + + +class Co(Generic[T_co]): + ... + + +class Contra(Generic[T_contra]): + ... + + +class Inv(Generic[T]): + ... + + +class CoContra(Generic[T_co, T_contra]): + ... + + +class Class1(Inv[T_co]): # E: Inv requires invariant TypeVar + pass + + +class Class2(Inv[T_contra]): # E: Inv requires invariant TypeVar + pass + + +class Co_Child1(Co[T_co]): # OK + ... + + +class Co_Child2(Co[T]): # OK + ... + + +class Co_Child3(Co[T_contra]): # E: Co requires covariant + ... + + +class Contra_Child1(Contra[T_contra]): # OK + ... + + +class Contra_Child2(Contra[T]): # OK + ... + + +class Contra_Child3(Contra[T_co]): # E: Contra requires contravariant + ... + + +class Contra_Child4(Contra[Co[T_contra]]): # OK + ... + + +class Contra_Child5(Contra[Co[T_co]]): # E: Contra requires contravariant + ... + + +class Contra_Child6(Contra[Co[T]]): # OK + ... + + +class CoContra_Child1(CoContra[T_co, T_contra]): # OK + ... + + +class CoContra_Child2( # E[CoContra_Child2]: Second type arg must be contravariant + CoContra[T_co, T_co] # E[CoContra_Child2]: Second type arg must be contravariant +): + ... + + +class CoContra_Child3( # E[CoContra_Child3]: First type arg must be covariant + CoContra[T_contra, T_contra] # E[CoContra_Child3]: First type arg must be covariant +): + ... + + +class CoContra_Child4(CoContra[T, T]): # OK + ... + + +class CoContra_Child5( # E[CoContra_Child5]: Second type arg must be contravariant + CoContra[Co[T_co], Co[T_co]] # E[CoContra_Child5]: Second type arg must be contravariant +): + ... + + +class CoToContra(Contra[Co[T_contra]]): # OK + ... + + +class ContraToContra(Contra[Contra[T_co]]): # OK + ... + + +class CoToCo(Co[Co[T_co]]): # OK + ... + + +class ContraToCo(Co[Contra[T_contra]]): # OK + ... + + +class CoToContraToContra(Contra[Co[Contra[T_contra]]]): # E + ... + + +class ContraToContraToContra(Contra[Contra[Contra[T_co]]]): # E + ... + + +Co_TA = Co[T_co] +Contra_TA = Contra[T_contra] + + +class CoToContra_WithTA(Contra_TA[Co_TA[T_contra]]): # OK + ... + + +class ContraToContra_WithTA(Contra_TA[Contra_TA[T_co]]): # OK + ... + + +class CoToCo_WithTA(Co_TA[Co_TA[T_co]]): # OK + ... + + +class ContraToCo_WithTA(Co_TA[Contra_TA[T_contra]]): # OK + ... + + +class CoToContraToContra_WithTA(Contra_TA[Co_TA[Contra_TA[T_contra]]]): # E + ... + + +class ContraToContraToContra_WithTA( # E[ContraToContraToContra_WithTA] + Contra_TA[Contra_TA[Contra_TA[T_co]]] # E[ContraToContraToContra_WithTA] +): + ... diff --git a/conformance/tests/generics_variance_inference.py b/conformance/tests/generics_variance_inference.py new file mode 100644 index 000000000..0a1cd41cd --- /dev/null +++ b/conformance/tests/generics_variance_inference.py @@ -0,0 +1,194 @@ +""" +Tests variance inference for type parameters. +""" + +# Specification: https://peps.python.org/pep-0695/#variance-inference + +from dataclasses import dataclass + +# T1 should be invariant +# T2 should be contravariant +# T3 should be covariant +from typing import Generic, Iterator, Sequence, TypeVar + + +class ClassA[T1, T2, T3](list[T1]): + def method1(self, a: T2) -> None: + ... + + def method2(self) -> T3: + raise NotImplementedError + + +def func_a(p1: ClassA[float, int, int], p2: ClassA[int, float, float]): + v1: ClassA[int, int, int] = p1 # E + v2: ClassA[float, float, int] = p1 # E + v3: ClassA[float, int, float] = p1 # OK + + v4: ClassA[int, int, int] = p2 # E + v5: ClassA[int, int, float] = p2 # OK + + +class ShouldBeCovariant1[T]: + def __getitem__(self, index: int) -> T: + raise NotImplementedError + + def __iter__(self) -> Iterator[T]: + raise NotImplementedError + + +vco1_1: ShouldBeCovariant1[float] = ShouldBeCovariant1[int]() # OK +vco1_2: ShouldBeCovariant1[int] = ShouldBeCovariant1[float]() # E + + +class ShouldBeCovariant2[T](ShouldBeCovariant1[T]): + pass + + +vco2_1: ShouldBeCovariant2[float] = ShouldBeCovariant2[int]() # OK +vco2_2: ShouldBeCovariant2[int] = ShouldBeCovariant2[float]() # E + + +class ShouldBeCovariant3[T]: + def method1(self) -> "ShouldBeCovariant2[T]": + raise NotImplementedError + + +vco3_1: ShouldBeCovariant3[float] = ShouldBeCovariant3[int]() # OK +vco3_2: ShouldBeCovariant3[int] = ShouldBeCovariant3[float]() # E + + +@dataclass(frozen=True) +class ShouldBeCovariant4[T]: + x: T + + +vo4_1: ShouldBeCovariant4[float] = ShouldBeCovariant4[int](1) # OK +vo4_2: ShouldBeCovariant4[int] = ShouldBeCovariant4[float](1) # E + + +class ShouldBeCovariant5[T]: + def __init__(self, x: T) -> None: + self._x = x + + @property + def x(self) -> T: + return self._x + + +vo5_1: ShouldBeCovariant5[float] = ShouldBeCovariant5[int](1) # OK +vo5_2: ShouldBeCovariant5[int] = ShouldBeCovariant5[float](1) # E + + +class ShouldBeInvariant1[T]: + def __init__(self, value: T) -> None: + self._value = value + + @property + def value(self) -> T: + return self._value + + @value.setter + def value(self, value: T): + self._value = value + + +vinv1_1: ShouldBeInvariant1[float] = ShouldBeInvariant1[int](1) # E +vinv1_2: ShouldBeInvariant1[int] = ShouldBeInvariant1[float](1.1) # E + + +class ShouldBeInvariant2[T]: + def __init__(self, value: T) -> None: + self._value = value + + def get_value(self) -> T: + return self._value + + def set_value(self, value: T): + self._value = value + + +vinv2_1: ShouldBeInvariant2[float] = ShouldBeInvariant2[int](1) # E +vinv2_2: ShouldBeInvariant2[int] = ShouldBeInvariant2[float](1.1) # E + + +class ShouldBeInvariant3[K, V](dict[K, V]): + pass + + +vinv3_1: ShouldBeInvariant3[float, str] = ShouldBeInvariant3[int, str]() # E +vinv3_2: ShouldBeInvariant3[int, str] = ShouldBeInvariant3[float, str]() # E +vinv3_3: ShouldBeInvariant3[str, float] = ShouldBeInvariant3[str, int]() # E +vinv3_4: ShouldBeInvariant3[str, int] = ShouldBeInvariant3[str, float]() # E + + +@dataclass +class ShouldBeInvariant4[T]: + x: T + + +vinv4_1: ShouldBeInvariant4[float] = ShouldBeInvariant4[int](1) # E + + +class ShouldBeInvariant5[T]: + def __init__(self, x: T) -> None: + self.x = x + + +vinv5_1: ShouldBeInvariant5[float] = ShouldBeInvariant5[int](1) # E + + +class ShouldBeContravariant1[T]: + def __init__(self, value: T) -> None: + pass + + def set_value(self, value: T) -> None: + pass + + +vcontra1_1: ShouldBeContravariant1[float] = ShouldBeContravariant1[int](1) # E +vcontra1_2: ShouldBeContravariant1[int] = ShouldBeContravariant1[float](1.2) # OK + + +# Test the case where a class with inferred variance derives from +# a traditional class that doesn't use inferred variance. + +T = TypeVar("T") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class Parent_Invariant(Generic[T]): + pass + + +class ShouldBeInvariant6[T](Parent_Invariant[T]): + pass + + +a1: ShouldBeInvariant6[int] = ShouldBeInvariant6[float]() # E +a2: ShouldBeInvariant6[float] = ShouldBeInvariant6[int]() # E + + +class Parent_Covariant(Generic[T_co]): + pass + + +class ShouldBeCovariant6[T](Parent_Covariant[T]): + pass + + +b1: ShouldBeCovariant6[int] = ShouldBeCovariant6[float]() # E +b2: ShouldBeCovariant6[float] = ShouldBeCovariant6[int]() # OK + + +class Parent_Contravariant(Generic[T_contra]): + pass + + +class ShouldBeContravariant2[T](Parent_Contravariant[T]): + pass + + +c1: ShouldBeContravariant2[int] = ShouldBeContravariant2[float]() # OK +c2: ShouldBeContravariant2[float] = ShouldBeContravariant2[int]() # E diff --git a/conformance/tests/historical_positional.py b/conformance/tests/historical_positional.py new file mode 100644 index 000000000..915a6770b --- /dev/null +++ b/conformance/tests/historical_positional.py @@ -0,0 +1,69 @@ +""" +Tests the historical (pre-3.8) mechanism for specifying positional-only +parameters in function signatures. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/historical.html#positional-only-parameters + + +# > Type checkers should support the following special case: all parameters with +# > names that begin but don’t end with __ are assumed to be positional-only: + + +def f1(__x: int, __y__: int = 0) -> None: ... + + +f1(3, __y__=1) # OK + +f1(__x=3) # E + + +# > Consistent with PEP 570 syntax, positional-only parameters cannot appear +# > after parameters that accept keyword arguments. Type checkers should +# > enforce this requirement: + + +def f2(x: int, __y: int) -> None: ... # E + + +# `x` is a positional-or-keyword parameter, so, +# according to a literal reading of the above rule, `__y` +# should be flagged as an error since it uses the historical +# convention for positional-only parameters but appears after +# a positional-or-keyword parameter that does not. We therefore +# permit type checkers to emit an error on this definition. +# +# Note that `x` can only be passed with a keyword argument if +# no arguments are passed positionally: +# +# ```pycon +# >>> def f3(x: int, *args: int, __y: int) -> None: ... +# ... +# >>> f3(x=1, __y=2) +# >>> +# ``` +def f3(x: int, *args: int, __y: int) -> None: ... # E? + + +f3(3, __y=3) # OK + + +class A: + def m1(self, __x: int, __y__: int = 0) -> None: ... + + def m2(self, x: int, __y: int) -> None: ... # E + + +a = A() +a.m1(3, __y__=1) # OK +a.m1(__x=3) # E + + +# The historical mechanism should not apply when new-style (PEP 570) +# syntax is used. + + +def f4(x: int, /, __y: int) -> None: ... # OK + + +f4(3, __y=4) # OK diff --git a/conformance/tests/literals_interactions.py b/conformance/tests/literals_interactions.py new file mode 100644 index 000000000..474126768 --- /dev/null +++ b/conformance/tests/literals_interactions.py @@ -0,0 +1,143 @@ +""" +Tests interactions between Literal types and other typing features. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/literal.html#interactions-with-other-types-and-features +from enum import Enum +from typing import IO, Any, Final, Generic, Literal, TypeVar, LiteralString, assert_type, overload + + +def func1(v: tuple[int, str, list[bool]], a: Literal[0], b: Literal[5], c: Literal[-5]): + assert_type(v[a], int) + assert_type(v[2], list[bool]) + + v[b] # E: index out of range + v[c] # E: index out of range + v[4] # E: index out of range + v[-4] # E: index out of range + + +_PathType = str | bytes | int + + +@overload +def open( + path: _PathType, + mode: Literal["r", "w", "a", "x", "r+", "w+", "a+", "x+"], +) -> IO[str]: + ... + + +@overload +def open( + path: _PathType, + mode: Literal["rb", "wb", "ab", "xb", "r+b", "w+b", "a+b", "x+b"], +) -> IO[bytes]: + ... + + +@overload +def open(path: _PathType, mode: str) -> IO[Any]: + ... + + +def open(path: _PathType, mode: Any) -> Any: + raise NotImplementedError + + +assert_type(open("path", "r"), IO[str]) +assert_type(open("path", "wb"), IO[bytes]) +assert_type(open("path", "other"), IO[Any]) + + +A = TypeVar("A", bound=int) +B = TypeVar("B", bound=int) +C = TypeVar("C", bound=int) + + +class Matrix(Generic[A, B]): + def __add__(self, other: "Matrix[A, B]") -> "Matrix[A, B]": + raise NotImplementedError + + def __matmul__(self, other: "Matrix[B, C]") -> "Matrix[A, C]": + raise NotImplementedError + + def transpose(self) -> "Matrix[B, A]": + raise NotImplementedError + + +def func2(a: Matrix[Literal[2], Literal[3]], b: Matrix[Literal[3], Literal[7]]): + c = a @ b + assert_type(c, Matrix[Literal[2], Literal[7]]) + + +T = TypeVar("T", Literal["a"], Literal["b"], Literal["c"]) +S = TypeVar("S", bound=Literal["foo"]) + + +class Status(Enum): + SUCCESS = 0 + INVALID_DATA = 1 + FATAL_ERROR = 2 + + +def parse_status1(s: str | Status) -> None: + if s is Status.SUCCESS: + assert_type(s, Literal[Status.SUCCESS]) + elif s is Status.INVALID_DATA: + assert_type(s, Literal[Status.INVALID_DATA]) + elif s is Status.FATAL_ERROR: + assert_type(s, Literal[Status.FATAL_ERROR]) + else: + assert_type(s, str) + + +# > Type checkers may optionally perform additional analysis for both +# > enum and non-enum Literal types beyond what is described in the section above. +# +# > For example, it may be useful to perform narrowing based on things +# > like containment or equality checks: + +def expects_bad_status(status: Literal["MALFORMED", "ABORTED"]): + ... + + +def expects_pending_status(status: Literal["PENDING"]): + ... + + +def parse_status2(status: LiteralString) -> None: + if status == "MALFORMED": + expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec + elif status == "ABORTED": + expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec + + if status in ("MALFORMED", "ABORTED"): + expects_bad_status(status) # E? narrowing the type here is sound, but optional per the spec + + if status == "PENDING": + expects_pending_status(status) # E? narrowing the type here is sound, but optional per the spec + + +# Narrowing `str` to `Literal` strings is unsound given the possiblity of +# user-defined `str` subclasses that could have custom equality semantics, +# but is explicitly listed by the spec as optional analysis that type checkers +# may perform. +def parse_status3(status: str) -> None: + if status == "MALFORMED": + expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec + elif status == "ABORTED": + expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec + + if status in ("MALFORMED", "ABORTED"): + expects_bad_status(status) # E? narrowing the type here is unsound, but allowed per the spec + + if status == "PENDING": + expects_pending_status(status) # E? narrowing the type here is unsound, but allowed per the spec + + +final_val1: Final = 3 +assert_type(final_val1, Literal[3]) + +final_val2: Final = True +assert_type(final_val2, Literal[True]) diff --git a/conformance/tests/literals_literalstring.py b/conformance/tests/literals_literalstring.py new file mode 100644 index 000000000..798c6c7a6 --- /dev/null +++ b/conformance/tests/literals_literalstring.py @@ -0,0 +1,175 @@ +""" +Tests handling of the LiteralString special form. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/literal.html#literalstring + + +from typing import ( + Any, + Generic, + Literal, + LiteralString, + Sequence, + TypeVar, + assert_type, + overload, +) + + +variable_annotation: LiteralString + + +def my_function(literal_string: LiteralString) -> LiteralString: + raise NotImplementedError + + +class Foo: + my_attribute: LiteralString = "" + + +type_argument: list[LiteralString] + +T = TypeVar("T", bound=LiteralString) + + +bad_union: Literal["hello", LiteralString] # E +bad_nesting: Literal[LiteralString] # E + + +def func1(a: Literal["one"], b: Literal["two"]): + x1: LiteralString = a + + x2: Literal[""] = b # E + + +def func2(a: LiteralString, b: LiteralString, non_literal: str): + # > Addition: x + y is of type LiteralString if both x and y are compatible with LiteralString. + assert_type(a + b, LiteralString) + + # > Joining: sep.join(xs) is of type LiteralString if sep’s type is + # > compatible with LiteralString and xs’s type is compatible with Iterable[LiteralString]. + assert_type(",".join((a, b)), LiteralString) + assert_type(",".join((a, non_literal)), str) + + # > In-place addition: If s has type LiteralString and x has type compatible with + # > LiteralString, then s += x preserves s’s type as LiteralString. + a += "hello" + b += a + + # > String formatting: An f-string has type LiteralString if and only if its constituent + # > expressions are literal strings. s.format(...) has type LiteralString if and only if + # > s and the arguments have types compatible with LiteralString. + assert_type(f"{a} {b}", LiteralString) + + x1: LiteralString = f"{a} {non_literal}" # E + + assert_type(a + non_literal, str) + + # > LiteralString is compatible with the type str + x2: str = a + + # > Other literal types, such as literal integers, are not compatible with LiteralString. + x3: LiteralString = 3 # E + x4: LiteralString = b"test" # E + + +# > Conditional statements and expressions work as expected. +def condition1() -> bool: + raise NotImplementedError + + +def return_literal_string() -> LiteralString: + return "foo" if condition1() else "bar" # OK + + +def return_literal_str2(literal_string: LiteralString) -> LiteralString: + return "foo" if condition1() else literal_string # OK + + +def return_literal_str3() -> LiteralString: + result: LiteralString + if condition1(): + result = "foo" + else: + result = "bar" + + return result # OK + + +# > TypeVars can be bound to LiteralString. +TLiteral = TypeVar("TLiteral", bound=LiteralString) + + +def literal_identity(s: TLiteral) -> TLiteral: + return s + + +def func3(s: Literal["hello"]): + y = literal_identity(s) + assert_type(y, Literal["hello"]) + + +def func4(s: LiteralString): + y2 = literal_identity(s) + assert_type(y2, LiteralString) + + +def func5(s: str): + literal_identity(s) # E + + +# > LiteralString can be used as a type argument for generic classes. +class Container(Generic[T]): + def __init__(self, value: T) -> None: + self.value = value + + +def func6(s: LiteralString): + x: Container[LiteralString] = Container(s) # OK + + +def func7(s: str): + x_error: Container[LiteralString] = Container(s) # E + + +# > Standard containers like List work as expected. +xs: list[LiteralString] = ["foo", "bar", "baz"] + + +class A: pass +class B(A): pass +class C(B): pass + + +@overload +def func8(x: Literal["foo"]) -> C: + ... + + +@overload +def func8(x: LiteralString) -> B: + ... + + +@overload +def func8(x: str) -> A: + ... + + +def func8(x: Any) -> Any: + raise NotImplementedError + + +def call_func8(x: str) -> None: + assert_type(func8("foo"), C) # First overload + assert_type(func8("bar"), B) # Second overload + assert_type(func8(x), A) # Third overload + + +def func9(val: list[LiteralString]): + x1: list[str] = val # E + + +def func10(val: Sequence[LiteralString]): + x1: Sequence[str] = val # OK diff --git a/conformance/tests/literals_parameterizations.py b/conformance/tests/literals_parameterizations.py new file mode 100644 index 000000000..97feacf61 --- /dev/null +++ b/conformance/tests/literals_parameterizations.py @@ -0,0 +1,67 @@ +""" +Tests legal and illegal parameterizations of Literal. +""" + +# > Literal must be parameterized with at least one type. + +from typing import Any, Literal, TypeVar +from enum import Enum + + +class Color(Enum): + RED = 0 + GREEN = 1 + BLUE = 2 + + +good1: Literal[26] +good2: Literal[0x1A] +good3: Literal[-4] +good4: Literal[+5] +good5: Literal["hello world"] +good6: Literal[b"hello world"] +good7: Literal["hello world"] +good8: Literal[True] +good9: Literal[Color.RED] +good10: Literal[None] + +ReadOnlyMode = Literal["r", "r+"] +WriteAndTruncateMode = Literal["w", "w+", "wt", "w+t"] +WriteNoTruncateMode = Literal["r+", "r+t"] +AppendMode = Literal["a", "a+", "at", "a+t"] + +AllModes = Literal[ReadOnlyMode, WriteAndTruncateMode, WriteNoTruncateMode, AppendMode] + +good11: Literal[Literal[Literal[1, 2, 3], "foo"], 5, None] + +variable = 3 +T = TypeVar("T") + +# > Arbitrary expressions [are illegal] +bad1: Literal[3 + 4] # E +bad2: Literal["foo".replace("o", "b")] # E +bad3: Literal[4 + 3j] # E +bad4: Literal[~5] # E +bad5: Literal[not False] # E +bad6: Literal[(1, "foo", "bar")] # E +bad7: Literal[{"a": "b", "c": "d"}] # E +bad8: Literal[int] # E +bad9: Literal[variable] # E +bad10: Literal[T] # E +bad11: Literal[3.14] # E +bad12: Literal[Any] # E +bad13: Literal[...] # E + + +def my_function(x: Literal[1 + 2]) -> int: # E + return x * 3 + + +x: Literal # E +y: Literal[my_function] = my_function # E + + +def func2(a: Literal[Color.RED]): + x1: Literal["Color.RED"] = a # E + + x2: "Literal[Color.RED]" = a # OK diff --git a/conformance/tests/literals_semantics.py b/conformance/tests/literals_semantics.py new file mode 100644 index 000000000..d1a4d15b7 --- /dev/null +++ b/conformance/tests/literals_semantics.py @@ -0,0 +1,40 @@ +""" +Tests the semantics of the Literal special form. +""" + +from typing import Literal +from typing import Literal as L + + +v1: Literal[3] = 3 +v2: Literal[3] = 4 # E + +v3: L[-3] = -3 + + +# > Literal[20] and Literal[0x14] are equivalent +def func1(a: Literal[20], b: Literal[0x14], c: Literal[0b10100]): + x1: Literal[0x14] = a + x2: Literal[0x14] = b + x3: Literal[0x14] = c + + +# > Literal[0] and Literal[False] are not equivalent +def func2(a: Literal[0], b: Literal[False]): + x1: Literal[False] = a # E + x2: Literal[0] = b # E + + +# > Given some value v that is a member of type T, the type Literal[v] shall +# > be treated as a subtype of T. For example, Literal[3] is a subtype of int. +def func3(a: L[3, 4, 5]): + b = a.__add__(3) + c = a + 3 + a += 3 # E + + +# > When a Literal is parameterized with more than one value, it’s treated +# > as exactly equivalent to the union of those types. +def func4(a: L[None, 3] | L[3, "foo", b"bar", True]): + x1: Literal[3, b"bar", True, "foo", None] = a + a = x1 diff --git a/conformance/tests/namedtuples_define_class.py b/conformance/tests/namedtuples_define_class.py new file mode 100644 index 000000000..03a631bfc --- /dev/null +++ b/conformance/tests/namedtuples_define_class.py @@ -0,0 +1,148 @@ +""" +Tests NamedTuple definitions using the class syntax. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/namedtuples.html#defining-named-tuples + +# > Type checkers should support the class syntax + +from typing import Generic, NamedTuple, TypeVar, assert_type +import sys + + +class Point(NamedTuple): + x: int + y: int + units: str = "meters" + + +# > Type checkers should synthesize a ``__new__`` method based on +# > the named tuple fields. + +p1 = Point(1, 2) +assert_type(p1, Point) +assert_type(p1[0], int) +assert_type(p1[1], int) +assert_type(p1[2], str) +assert_type(p1[-1], str) +assert_type(p1[-2], int) +assert_type(p1[-3], int) +assert_type(p1[0:2], tuple[int, int]) +assert_type(p1[0:], tuple[int, int, str]) + +print(p1[3]) # E +print(p1[-4]) # E + +p2 = Point(1, 2, "") +assert_type(p2, Point) + +p3 = Point(x=1, y=2) +assert_type(p3, Point) + +p4 = Point(x=1, y=2, units="") +assert_type(p4, Point) + +p5 = Point(1) # E +p6 = Point(x=1) # E +p7 = Point(1, "") # E +p8 = Point(1, 2, units=3) # E +p9 = Point(1, 2, "", "") # E +p10 = Point(1, 2, "", other="") # E + + +# > Fields must be annotated attributes - methods and un-annotated attributes are not +# > considered fields. + + +class Point2(NamedTuple): + x: int + y: int + units = "meters" # Not a field + + def is_origin(self) -> int: # Not a field + return self.x == 0 and self.y == 0 + + +p11 = Point2(1, 2) +assert_type(p11, Point2) +x, y = p11 + +p12 = Point2(1, 2, "") # E + + +# > Field names may not start with an underscore. + +class Point3(NamedTuple): + x: int + _y: int # E: illegal field name + + +# > The runtime implementation of ``NamedTuple`` enforces that fields with default +# > values must come after fields without default values. Type checkers should +# > likewise enforce this restriction:: + + +class Location(NamedTuple): + altitude: float = 0.0 + latitude: float # E: previous field has a default value + + +# > A named tuple class can be subclassed, but any fields added by the subclass +# > are not considered part of the named tuple type. Type checkers should enforce +# > that these newly-added fields do not conflict with the named tuple fields +# > in the base class. + + +class PointWithName(Point): + name: str = "" # OK + + +pn1 = PointWithName(1, 2, "") +pnt1: tuple[int, int, str] = pn1 +assert_type(pn1.name, str) + + +class BadPointWithName(Point): + name: str = "" # OK + x: int = 0 # E + +# > Namedtuple fields may be conditional, via checks of the same statically-known +# > conditions that a type-checker understands elsewhere, such as Python version:: + +class ConditionalField(NamedTuple): + x: int + if sys.version_info >= (3, 12): + y: int + if sys.version_info >= (4, 0): + z: int + +# The conformance suite runs type checkers configured to Python 3.12 or later: +ConditionalField(1, 2) +ConditionalField(1, 2, 3) # E + + +# > In Python 3.11 and newer, the class syntax supports generic named tuple classes. +# > Type checkers should support this. + +T = TypeVar("T") + + +class Property(NamedTuple, Generic[T]): + name: str + value: T + + +pr1 = Property("", 3.4) +assert_type(pr1, Property[float]) +assert_type(pr1[1], float) +assert_type(pr1.value, float) + +Property[str]("", 3.1) # E + + +# > ``NamedTuple`` does not support multiple inheritance. Type checkers should +# > enforce this limitation. + + +class Unit(NamedTuple, object): # E + name: str diff --git a/conformance/tests/namedtuples_define_functional.py b/conformance/tests/namedtuples_define_functional.py new file mode 100644 index 000000000..49f7c1b0c --- /dev/null +++ b/conformance/tests/namedtuples_define_functional.py @@ -0,0 +1,69 @@ +""" +Tests NamedTuple definitions using the functional syntax. +""" + +from collections import namedtuple +from typing import NamedTuple + +# Specification: https://typing.readthedocs.io/en/latest/spec/namedtuples.html#defining-named-tuples + +# > A type checker may support the function syntax in its various forms:: + + +Point1 = namedtuple("Point1", ["x", "y"]) +p1_1 = Point1(x=1, y=1) +p1_2 = Point1(2.3, "") +p1_3 = Point1(2.3) # E + +Point2 = namedtuple("Point2", ("x", "y")) +p2_1 = Point2(x=1, y=1) +p2_2 = Point2(2.3, "") +p2_3 = Point2() # E + +Point3 = namedtuple("Point3", "x y") +p3_1 = Point3(x=1, y=1) +p3_2 = Point3(2.3, "") +p3_3 = Point3(1, 2, 3) # E + +Point4 = namedtuple("Point4", "x, y") +p4_1 = Point4(x=1, y=1) +p4_2 = Point4(2.3, "") +p4_3 = Point4(1, z=3) # E + +Point5 = NamedTuple("Point5", [("x", int), ("y", int)]) +p5_1 = Point5(x=1, y=1) +p5_2 = Point5(2, 1) +p5_3 = Point5(2, "1") # E +p5_4 = Point5(1, 2, 3) # E + +Point6 = NamedTuple("Point6", (("x", int), ("y", int))) +p6_1 = Point6(x=1, y=1) +p6_2 = Point6(2, 1) +p6_3 = Point6(2, "1") # E +p6_4 = Point6(x=1.1, y=2) # E + + +# > At runtime, the ``namedtuple`` function disallows field names that begin with +# > an underscore or are illegal Python identifiers, and either raises an exception +# > or replaces these fields with a parameter name of the form ``_N``. The behavior +# > depends on the value of the ``rename`` argument. Type checkers may replicate +# > this behavior statically. + +NT1 = namedtuple("NT1", ["a", "a"]) # E?: duplicate field name +NT2 = namedtuple("NT2", ["abc", "def"]) # E?: illegal field name +NT3 = namedtuple("NT3", ["abc", "def"], rename=False) # E?: illegal field name +NT4 = namedtuple("NT4", ["abc", "_d"], rename=False) # E?: illegal field name + +NT5 = namedtuple("NT5", ["abc", "def"], rename=True) # OK +NT5(abc="", _1="") # OK +NT6 = namedtuple("NT6", ["abc", "_d"], rename=True) # OK +NT6(abc="", _1="") # OK + + +# > The ``namedtuple`` function also supports a ``defaults`` keyword argument that +# > specifies default values for the fields. Type checkers may support this. + +NT7 = namedtuple("NT7", "a b c", defaults=(1, 2)) +NT7(1) # OK +NT7(1, 2, 3) # OK +NT7() # E: too few arguments diff --git a/conformance/tests/namedtuples_type_compat.py b/conformance/tests/namedtuples_type_compat.py new file mode 100644 index 000000000..2eb656e5c --- /dev/null +++ b/conformance/tests/namedtuples_type_compat.py @@ -0,0 +1,27 @@ +""" +Tests NamedTuple type compatibility rules. +""" + +from typing import Any, NamedTuple + +# Specification: https://typing.readthedocs.io/en/latest/spec/namedtuples.html#type-compatibility-rules + + +class Point(NamedTuple): + x: int + y: int + units: str = "meters" + + +# > A named tuple is a subtype of a ``tuple`` with a known length and parameterized +# > by types corresponding to the named tuple's individual field types. + +p = Point(x=1, y=2, units="inches") +v1: tuple[int, int, str] = p # OK +v2: tuple[Any, ...] = p # OK +v3: tuple[int, int] = p # E: too few elements +v4: tuple[int, str, str] = p # E: incompatible element type + +# > As with normal tuples, named tuples are covariant in their type parameters. + +v5: tuple[float, float, str] = p # OK diff --git a/conformance/tests/namedtuples_usage.py b/conformance/tests/namedtuples_usage.py new file mode 100644 index 000000000..591237955 --- /dev/null +++ b/conformance/tests/namedtuples_usage.py @@ -0,0 +1,61 @@ +""" +Tests NamedTuple usage. +""" + +from typing import NamedTuple, assert_type + +# Specification: https://typing.readthedocs.io/en/latest/spec/namedtuples.html#named-tuple-usage + + +class Point(NamedTuple): + x: int + y: int + units: str = "meters" + + +# > The fields within a named tuple instance can be accessed by name using an +# > attribute access (``.``) operator. Type checkers should support this. + +p = Point(1, 2) +assert_type(p.x, int) +assert_type(p.units, str) + + +# > Like normal tuples, elements of a named tuple can also be accessed by index, +# > and type checkers should support this. + +assert_type(p[0], int) +assert_type(p[1], int) +assert_type(p[2], str) +assert_type(p[-1], str) +assert_type(p[-2], int) +assert_type(p[-3], int) + +print(p[3]) # E +print(p[-4]) # E + +# > Type checkers should enforce that named tuple fields cannot be overwritten +# > or deleted. + +p.x = 3 # E +p[0] = 3 # E +del p.x # E +del p[0] # E + +# > Like regular tuples, named tuples can be unpacked. Type checkers should understand +# > this. + +x1, y1, units1 = p +assert_type(x1, int) +assert_type(units1, str) + +x2, y2 = p # E: too few values to unpack +x3, y3, unit3, other = p # E: too many values to unpack + + +class PointWithName(Point): + name: str = "" # OK + + +pn = PointWithName(1, 1) +x4, y4, units4 = pn diff --git a/conformance/tests/narrowing_typeguard.py b/conformance/tests/narrowing_typeguard.py new file mode 100644 index 000000000..193f9b936 --- /dev/null +++ b/conformance/tests/narrowing_typeguard.py @@ -0,0 +1,167 @@ +""" +Tests TypeGuard functionality. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/narrowing.html#typeguard + +from typing import Any, Callable, Protocol, Self, TypeGuard, TypeVar, assert_type + + +T = TypeVar("T") + +def is_two_element_tuple(val: tuple[T, ...]) -> TypeGuard[tuple[T, T]]: + return len(val) == 2 + +def func1(names: tuple[str, ...]): + if is_two_element_tuple(names): + assert_type(names, tuple[str, str]) + else: + assert_type(names, tuple[str, ...]) + + +def is_str_list(val: list[object], allow_empty: bool) -> TypeGuard[list[str]]: + if len(val) == 0: + return allow_empty + return all(isinstance(x, str) for x in val) + +def is_set_of(val: set[Any], type: type[T]) -> TypeGuard[set[T]]: + return all(isinstance(x, type) for x in val) + +def func2(val: set[object]): + if is_set_of(val, int): + assert_type(val, set[int]) + else: + assert_type(val, set[object]) + + +T_A = TypeVar("T_A", bound="A") + +class A: + def tg_1(self, val: object) -> TypeGuard[int]: + return isinstance(val, int) + + @classmethod + def tg_2(cls, val: object) -> TypeGuard[int]: + return isinstance(val, int) + + @staticmethod + def tg_3(val: object) -> TypeGuard[int]: + return isinstance(val, int) + + def tg4(self, val: object) -> TypeGuard[Self]: + return isinstance(val, type(self)) + + def tg5(self: T_A, val: object) -> TypeGuard[T_A]: + return isinstance(val, type(self)) + +class B(A): + pass + +# > Type checkers should assume that type narrowing should be applied to +# > the expression that is passed as the first positional argument to a +# > user-defined type guard. If the type guard function accepts more than +# > one argument, no type narrowing is applied to those additional argument +# > expressions. + +def func3() -> None: + val1 = object() + if A().tg_1(val1): + assert_type(val1, int) + + val2 = object() + if A().tg_2(val2): + assert_type(val2, int) + + val3 = object() + if A.tg_2(val3): + assert_type(val3, int) + + val4 = object() + if A().tg_3(val4): + assert_type(val4, int) + + val5 = object() + if A.tg_3(val5): + assert_type(val5, int) + + val6 = object() + if B().tg4(val6): + assert_type(val6, B) + + val7 = object() + if B().tg4(val7): + assert_type(val7, B) + + +# > If a type guard function is implemented as an instance method or class +# > method, the first positional argument maps to the second parameter +# > (after “self” or “cls”). + +class C: + # Type checker should emit error here. + def tg_1(self) -> TypeGuard[int]: # E + return False + + @classmethod + # Type checker should emit error here. + def tg_2(cls) -> TypeGuard[int]: # E + return False + +# > ``TypeGuard`` is also valid as the return type of a ``Callable`` type. In that +# > context, it is treated as a subtype of bool. For example, ``Callable[..., TypeGuard[int]]`` +# > is assignable to ``Callable[..., bool]``. + + +def takes_callable_bool(f: Callable[[object], bool]) -> None: + pass + + +def takes_callable_str(f: Callable[[object], str]) -> None: + pass + + +def simple_typeguard(val: object) -> TypeGuard[int]: + return isinstance(val, int) + + +takes_callable_bool(simple_typeguard) # OK +takes_callable_str(simple_typeguard) # E + + +class CallableBoolProto(Protocol): + def __call__(self, val: object) -> bool: ... + + +class CallableStrProto(Protocol): + def __call__(self, val: object) -> str: ... + + +def takes_callable_bool_proto(f: CallableBoolProto) -> None: + pass + + +def takes_callable_str_proto(f: CallableStrProto) -> None: + pass + + +takes_callable_bool_proto(simple_typeguard) # OK +takes_callable_str_proto(simple_typeguard) # E + +# > Unlike ``TypeGuard``, ``TypeIs`` is invariant in its argument type: +# > ``TypeIs[B]`` is not a subtype of ``TypeIs[A]``, +# > even if ``B`` is a subtype of ``A``. + +def takes_int_typeguard(f: Callable[[object], TypeGuard[int]]) -> None: + pass + + +def int_typeguard(val: object) -> TypeGuard[int]: + return isinstance(val, int) + + +def bool_typeguard(val: object) -> TypeGuard[bool]: + return isinstance(val, bool) + + +takes_int_typeguard(int_typeguard) # OK +takes_int_typeguard(bool_typeguard) # OK diff --git a/conformance/tests/narrowing_typeis.py b/conformance/tests/narrowing_typeis.py new file mode 100644 index 000000000..9a2e62cde --- /dev/null +++ b/conformance/tests/narrowing_typeis.py @@ -0,0 +1,205 @@ +""" +Tests TypeIs functionality. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/narrowing.html#typeis + +from collections.abc import Awaitable +from typing import Any, Callable, Protocol, Self, TypeGuard, TypeVar, assert_type +from typing_extensions import TypeIs + + +T = TypeVar("T") + +def is_two_element_tuple(val: tuple[T, ...]) -> TypeIs[tuple[T, T]]: + return len(val) == 2 + +def func1(names: tuple[str, str] | tuple[str, str, str]): + if is_two_element_tuple(names): + assert_type(names, tuple[str, str]) + else: + assert_type(names, tuple[str, str, str]) + + +# > The final narrowed type may be narrower than **R**, due to the constraints of the +# > argument's previously-known type + +def is_awaitable(val: object) -> TypeIs[Awaitable[Any]]: + return isinstance(val, Awaitable) + +async def func2(val: int | Awaitable[int]): + if is_awaitable(val): + # Not `assert_type(val, Awaitable[int])` because a conformant + # implementation could allow the possibility that instead it is + # `int & Awaitable[Any]`, an awaitable subclass of int. + x: int = await val + return x + else: + # We can't say much here. The strictly correct answer is + # (int | Awaitable[int]) & ~Awaitable[Any], but conformant implementations + # may simplify this. + # But it should definitely remain assignable to `int | Awaitable[int]`. + y: int | Awaitable[int] = val + return y + + +T_A = TypeVar("T_A", bound="A") + +class A: + def tg_1(self, val: object) -> TypeIs[int]: + return isinstance(val, int) + + @classmethod + def tg_2(cls, val: object) -> TypeIs[int]: + return isinstance(val, int) + + @staticmethod + def tg_3(val: object) -> TypeIs[int]: + return isinstance(val, int) + + def tg4(self, val: object) -> TypeIs[Self]: + return isinstance(val, type(self)) + + def tg5(self: T_A, val: object) -> TypeIs[T_A]: + return isinstance(val, type(self)) + +class B(A): + pass + +# > The type narrowing behavior is applied to the first positional argument +# > passed to the function. The function may accept additional arguments, +# > but they are not affected by type narrowing. + + +def func3() -> None: + val1 = object() + if A().tg_1(val1): + assert_type(val1, int) + + val2 = object() + if A().tg_2(val2): + assert_type(val2, int) + + val3 = object() + if A.tg_2(val3): + assert_type(val3, int) + + val4 = object() + if A().tg_3(val4): + assert_type(val4, int) + + val5 = object() + if A.tg_3(val5): + assert_type(val5, int) + + val6 = object() + if B().tg4(val6): + assert_type(val6, B) + + val7 = object() + if B().tg4(val7): + assert_type(val7, B) + + +# > If a type narrowing function +# > is implemented as an instance method or class method, the first positional +# > argument maps to the second parameter (after self or cls). + +class C: + # Type checker should emit error here. + def tg_1(self) -> TypeIs[int]: # E + return False + + @classmethod + # Type checker should emit error here. + def tg_2(cls) -> TypeIs[int]: # E + return False + +# > ``TypeIs`` is also valid as the return type of a callable, for example +# > in callback protocols and in the ``Callable`` special form. In these +# > contexts, it is treated as a subtype of bool. For example, ``Callable[..., TypeIs[int]]`` +# > is assignable to ``Callable[..., bool]``. + + +def takes_callable_bool(f: Callable[[object], bool]) -> None: + pass + + +def takes_callable_str(f: Callable[[object], str]) -> None: + pass + + +def simple_typeguard(val: object) -> TypeIs[int]: + return isinstance(val, int) + + +takes_callable_bool(simple_typeguard) # OK +takes_callable_str(simple_typeguard) # E + + +class CallableBoolProto(Protocol): + def __call__(self, val: object) -> bool: ... + + +class CallableStrProto(Protocol): + def __call__(self, val: object) -> str: ... + + +def takes_callable_bool_proto(f: CallableBoolProto) -> None: + pass + + +def takes_callable_str_proto(f: CallableStrProto) -> None: + pass + + +takes_callable_bool_proto(simple_typeguard) # OK +takes_callable_str_proto(simple_typeguard) # E + +# TypeIs and TypeGuard are not compatible with each other. + +def takes_typeguard(f: Callable[[object], TypeGuard[int]]) -> None: + pass + +def takes_typeis(f: Callable[[object], TypeIs[int]]) -> None: + pass + +def is_int_typeis(val: object) -> TypeIs[int]: + return isinstance(val, int) + +def is_int_typeguard(val: object) -> TypeGuard[int]: + return isinstance(val, int) + +takes_typeguard(is_int_typeguard) # OK +takes_typeguard(is_int_typeis) # E +takes_typeis(is_int_typeguard) # E +takes_typeis(is_int_typeis) # OK + + +# > Unlike ``TypeGuard``, ``TypeIs`` is invariant in its argument type: +# > ``TypeIs[B]`` is not a subtype of ``TypeIs[A]``, +# > even if ``B`` is a subtype of ``A``. + +def takes_int_typeis(f: Callable[[object], TypeIs[int]]) -> None: + pass + + +def int_typeis(val: object) -> TypeIs[int]: + return isinstance(val, int) + + +def bool_typeis(val: object) -> TypeIs[bool]: + return isinstance(val, bool) + + +takes_int_typeis(int_typeis) # OK +takes_int_typeis(bool_typeis) # E + +# > It is an error to narrow to a type that is not consistent with the input type + +def bad_typeis(x: int) -> TypeIs[str]: # E + return isinstance(x, str) + + +def bad_typeis_variance(x: list[object]) -> TypeIs[list[int]]: # E + return all(isinstance(x, int) for x in x) diff --git a/conformance/tests/overloads_basic.py b/conformance/tests/overloads_basic.py new file mode 100644 index 000000000..7aee51d87 --- /dev/null +++ b/conformance/tests/overloads_basic.py @@ -0,0 +1,61 @@ +""" +Tests the behavior of typing.overload. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/overload.html#overload + +from typing import ( + Any, + Callable, + Iterable, + Iterator, + TypeVar, + assert_type, + overload, +) + + +class Bytes: + ... + + @overload + def __getitem__(self, __i: int) -> int: + ... + + @overload + def __getitem__(self, __s: slice) -> bytes: + ... + + def __getitem__(self, __i_or_s: int | slice) -> int | bytes: + if isinstance(__i_or_s, int): + return 0 + else: + return b"" + + +b = Bytes() +assert_type(b[0], int) +assert_type(b[0:1], bytes) +b[""] # E: no matching overload + + +T1 = TypeVar("T1") +T2 = TypeVar("T2") +S = TypeVar("S") + + +@overload +def map(func: Callable[[T1], S], iter1: Iterable[T1]) -> Iterator[S]: + ... + + +@overload +def map( + func: Callable[[T1, T2], S], iter1: Iterable[T1], iter2: Iterable[T2] +) -> Iterator[S]: + ... + + +def map(func: Any, iter1: Any, iter2: Any = ...) -> Any: + raise NotImplementedError + diff --git a/conformance/tests/overloads_consistency.py b/conformance/tests/overloads_consistency.py new file mode 100644 index 000000000..58533c125 --- /dev/null +++ b/conformance/tests/overloads_consistency.py @@ -0,0 +1,118 @@ +""" +Tests consistency of overloads with implementation. +""" + +from typing import Callable, Coroutine, overload +from types import CoroutineType + +# > If an overload implementation is defined, type checkers should validate +# > that it is consistent with all of its associated overload signatures. +# > The implementation should accept all potential sets of arguments +# > that are accepted by the overloads and should produce all potential return +# > types produced by the overloads. In typing terms, this means the input +# > signature of the implementation should be :term:`assignable` to the input +# > signatures of all overloads, and the return type of all overloads should be +# > assignable to the return type of the implementation. + +# Return type of all overloads must be assignable to return type of +# implementation: + +@overload +def return_type(x: int) -> int: + ... + +@overload +def return_type(x: str) -> str: # E[return_type] + ... + +def return_type(x: int | str) -> int: # E[return_type] an overload returns `str`, not assignable to `int` + return 1 + + +# Input signature of implementation must be assignable to signature of each +# overload. We don't attempt a thorough testing of input signature +# assignability here; see `callables_subtyping.py` for that: + +@overload +def parameter_type(x: int) -> int: + ... + +@overload +def parameter_type(x: str) -> str: # E[parameter_type] + ... + +def parameter_type(x: int) -> int | str: # E[parameter_type] impl type of `x` must be assignable from overload types of `x` + return 1 + + +# > Overloads are allowed to use a mixture of ``async def`` and ``def`` statements +# > within the same overload definition. Type checkers should convert +# > ``async def`` statements to a non-async signature (wrapping the return +# > type in a ``Coroutine``) before testing for implementation consistency +# > and overlapping overloads (described below). + +# ...and also... + +# > When a type checker checks the implementation for consistency with overloads, +# > it should first apply any transforms that change the effective type of the +# > implementation including the presence of a ``yield`` statement in the +# > implementation body, the use of ``async def``, and the presence of additional +# > decorators. + +# An overload can explicitly return `Coroutine`, while the implementation is an +# `async def`: + +@overload +def returns_coroutine(x: int) -> CoroutineType[None, None, int]: + ... + +@overload +async def returns_coroutine(x: str) -> str: + ... + +async def returns_coroutine(x: int | str) -> int | str: + return 1 + + +# The implementation can explicitly return `Coroutine`, while overloads are +# `async def`: + +@overload +async def returns_coroutine_2(x: int) -> int: + ... + +@overload +async def returns_coroutine_2(x: str) -> str: + ... + +def returns_coroutine_2(x: int | str) -> Coroutine[None, None, int | str]: + return _wrapped(x) + +async def _wrapped(x: int | str) -> int | str: + return 2 + +# Decorator transforms are applied before checking overload consistency: + +def _deco_1(f: Callable) -> Callable[[int], int]: + def wrapped(_x: int, /) -> int: + return 1 + return wrapped + +def _deco_2(f: Callable) -> Callable[[int | str], int | str]: + def wrapped(_x: int | str, /) -> int | str: + return 1 + return wrapped + +@overload +@_deco_1 +def decorated() -> None: + ... + +@overload +def decorated(x: str, /) -> str: + ... + +@_deco_2 +def decorated(y: bytes, z: bytes) -> bytes: + return b"" + diff --git a/conformance/tests/overloads_definitions.py b/conformance/tests/overloads_definitions.py new file mode 100644 index 000000000..67a62e61b --- /dev/null +++ b/conformance/tests/overloads_definitions.py @@ -0,0 +1,236 @@ +""" +Tests valid/invalid definition of overloaded functions. +""" + +from abc import ABC, abstractmethod +from typing import ( + final, + Protocol, + overload, + override, +) + + +# > At least two @overload-decorated definitions must be present. +@overload # E[func1] +def func1() -> None: # E[func1]: At least two overloads must be present + ... + + +def func1() -> None: + pass + + +# > The ``@overload``-decorated definitions must be followed by an overload +# > implementation, which does not include an ``@overload`` decorator. Type +# > checkers should report an error or warning if an implementation is missing. +@overload # E[func2] +def func2(x: int) -> int: # E[func2]: no implementation + ... + + +@overload +def func2(x: str) -> str: ... + + +# > Overload definitions within stub files, protocols, and on abstract methods +# > within abstract base classes are exempt from this check. +class MyProto(Protocol): + @overload + def func3(self, x: int) -> int: ... + + @overload + def func3(self, x: str) -> str: ... + + +class MyAbstractBase(ABC): + @overload + @abstractmethod + def func4(self, x: int) -> int: ... + + @overload + @abstractmethod + def func4(self, x: str) -> str: ... + + # A non-abstract method in an abstract base class still requires an + # implementation: + + @overload # E[not_abstract] + def not_abstract(self, x: int) -> int: # E[not_abstract] no implementation + ... + + @overload + def not_abstract(self, x: str) -> str: ... + + +# > If one overload signature is decorated with ``@staticmethod`` or +# > ``@classmethod``, all overload signatures must be similarly decorated. The +# > implementation, if present, must also have a consistent decorator. Type +# > checkers should report an error if these conditions are not met. +class C: + @overload # E[func5] + @staticmethod + def func5(x: int, /) -> int: # E[func5] + ... + + @overload + @staticmethod + def func5(x: str, /) -> str: # E[func5] + ... + + def func5(*args: object) -> int | str: # E[func5] + return 1 + + @overload # E[func6+] + @classmethod + def func6(cls, x: int, /) -> int: # E[func6+] + ... + + @overload + def func6(self, x: str, /) -> str: # E[func6+] + ... + + @classmethod # E[func6+] + def func6(cls, *args: int | str) -> int | str: # E[func6+] + return 1 + + +# > If a ``@final`` or ``@override`` decorator is supplied for a function with +# > overloads, the decorator should be applied only to the overload +# > implementation if it is present. If an overload implementation isn't present +# > (for example, in a stub file), the ``@final`` or ``@override`` decorator +# > should be applied only to the first overload. Type checkers should enforce +# > these rules and generate an error when they are violated. If a ``@final`` or +# > ``@override`` decorator follows these rules, a type checker should treat the +# > decorator as if it is present on all overloads. +class Base: + # This is a good definition of an overloaded final method (@final decorator + # on implementation only): + + @overload + def final_method(self, x: int) -> int: ... + + @overload + def final_method(self, x: str) -> str: ... + + @final + def final_method(self, x: int | str) -> int | str: + raise NotImplementedError + + # The @final decorator should not be on one of the overloads: + + @overload # E[invalid_final] @final should be on implementation only + @final # E[invalid_final] + def invalid_final(self, x: int) -> int: # E[invalid_final] + ... + + @overload + def invalid_final(self, x: str) -> str: # E[invalid_final] + ... + + def invalid_final(self, x: int | str) -> int | str: + raise NotImplementedError + + # The @final decorator should not be on multiple overloads and + # implementation: + + @overload # E[invalid_final_2+]: @final should be on implementation only + @final # E[invalid_final_2+] + def invalid_final_2(self, x: int) -> int: # E[invalid_final_2+] + ... + + @overload + @final # E[invalid_final_2+] + def invalid_final_2(self, x: str) -> str: # E[invalid_final_2+] + ... + + @final + def invalid_final_2(self, x: int | str) -> int | str: + raise NotImplementedError + + # These methods are just here for the @override test below. We use an + # overload because mypy doesn't like overriding a non-overloaded method + # with an overloaded one, even if LSP isn't violated. That could be its own + # specification question, but it's not what we're trying to test here: + + @overload + def good_override(self, x: int) -> int: ... + + @overload + def good_override(self, x: str) -> str: ... + + def good_override(self, x: int | str) -> int | str: + raise NotImplementedError + + @overload + def to_override(self, x: int) -> int: ... + + @overload + def to_override(self, x: str) -> str: ... + + def to_override(self, x: int | str) -> int | str: + raise NotImplementedError + + +class Child(Base): # E[override-final] + # The correctly-decorated @final method `Base.final_method` should cause an + # error if overridden in a child class (we use an overload here to avoid + # questions of override LSP compatibility and focus only on the override): + + @overload # E[override-final] + def final_method(self, x: int) -> int: ... # E[override-final] + + @overload + def final_method(self, x: str) -> str: ... + + def final_method( # E[override-final] can't override final method + self, x: int | str + ) -> int | str: # E[override-final] can't override final method + raise NotImplementedError + + # This is the right way to mark an overload as @override (decorate + # implementation only), so the use of @override should cause an error + # (because there's no `Base.bad_override` method): + + @overload # E[bad_override] marked as override but doesn't exist in base + def bad_override(self, x: int) -> int: # E[bad_override] + ... + + @overload + def bad_override(self, x: str) -> str: ... + + @override # E[bad_override] + def bad_override(self, x: int | str) -> int | str: # E[bad_override] + raise NotImplementedError + + # This is also a correctly-decorated overloaded @override, which is + # overriding a method that does exist in the base, so there should be no + # error. We need both this test and the previous one, because in the + # previous test, an incorrect error about the use of @override decorator + # could appear on the same line as the expected error about overriding a + # method that doesn't exist in base: + + @overload + def good_override(self, x: int) -> int: ... + + @overload + def good_override(self, x: str) -> str: ... + + @override + def good_override(self, x: int | str) -> int | str: + raise NotImplementedError + + # This is the wrong way to use @override with an overloaded method, and + # should emit an error: + + @overload # E[override_impl+]: @override should appear only on implementation + @override # E[override_impl+] + def to_override(self, x: int) -> int: ... # E[override_impl+] + + @overload + @override # E[override_impl+] + def to_override(self, x: str) -> str: ... # E[override_impl+] + + @override + def to_override(self, x: int | str) -> int | str: + raise NotImplementedError diff --git a/conformance/tests/overloads_definitions_stub.pyi b/conformance/tests/overloads_definitions_stub.pyi new file mode 100644 index 000000000..b9bd7eb61 --- /dev/null +++ b/conformance/tests/overloads_definitions_stub.pyi @@ -0,0 +1,150 @@ +""" +Tests valid/invalid definition of overloaded functions in stub files, where the +rules differ from non-stubs, since an implementation is not required. +""" + +from typing import ( + final, + overload, + override, +) + +# > At least two @overload-decorated definitions must be present. +@overload # E[func1] +def func1() -> None: # E[func1]: At least two overloads must be present + ... + +# > The ``@overload``-decorated definitions must be followed by an overload +# > implementation, which does not include an ``@overload`` decorator. Type +# > checkers should report an error or warning if an implementation is missing. +# > Overload definitions within stub files, protocols, and on abstract methods +# > within abstract base classes are exempt from this check. +@overload +def func2(x: int) -> int: ... +@overload +def func2(x: str) -> str: ... + +# > If one overload signature is decorated with ``@staticmethod`` or +# > ``@classmethod``, all overload signatures must be similarly decorated. The +# > implementation, if present, must also have a consistent decorator. Type +# > checkers should report an error if these conditions are not met. +class C: + @overload # E[func5] + def func5(self, x: int, /) -> int: # E[func5] + ... + @overload + @staticmethod + def func5(x: str, /) -> str: # E[func5] + ... + @overload # E[func6] + @classmethod + def func6(cls, x: int, /) -> int: # E[func6] + ... + @overload + def func6(self, *args: str) -> str: # E[func6] + ... + +# > If a ``@final`` or ``@override`` decorator is supplied for a function with +# > overloads, the decorator should be applied only to the overload +# > implementation if it is present. If an overload implementation isn't present +# > (for example, in a stub file), the ``@final`` or ``@override`` decorator +# > should be applied only to the first overload. Type checkers should enforce +# > these rules and generate an error when they are violated. If a ``@final`` or +# > ``@override`` decorator follows these rules, a type checker should treat the +# > decorator as if it is present on all overloads. +class Base: + # This is a good definition of an overloaded final method in a stub (@final + # decorator on first overload only): + + @overload + @final + def final_method(self, x: int) -> int: ... + @overload + def final_method(self, x: str) -> str: ... + + # The @final decorator should not be on multiple overloads: + + @overload # E[invalid_final] @final should be on first overload + @final + def invalid_final(self, x: int) -> int: # E[invalid_final] + ... + @overload # E[invalid_final] + @final # E[invalid_final] + def invalid_final(self, x: str) -> str: # E[invalid_final] + ... + @overload + def invalid_final(self, x: bytes) -> bytes: ... + + # The @final decorator should not be on all overloads: + + @overload # E[invalid_final_2] @final should be on first overload + @final + def invalid_final_2(self, x: int) -> int: # E[invalid_final_2] + ... + @overload # E[invalid_final_2] + @final # E[invalid_final_2] + def invalid_final_2(self, x: str) -> str: ... # E[invalid_final_2] + + # These methods are just here for the @override test below. We use an + # overload because mypy doesn't like overriding a non-overloaded method + # with an overloaded one, even if LSP isn't violated. That could be its own + # specification question, but it's not what we're trying to test here: + + @overload + def good_override(self, x: int) -> int: ... + @overload + def good_override(self, x: str) -> str: ... + @overload + def to_override(self, x: int) -> int: ... + @overload + def to_override(self, x: str) -> str: ... + +class Child(Base): # E[override-final] + # The correctly-decorated @final method `Base.final_method` should cause an + # error if overridden in a child class (we use an overload here to avoid + # questions of override LSP compatibility and focus only on the override): + + @overload # E[override-final] + def final_method(self, x: int) -> int: # E[override-final] + ... + @overload + def final_method( # E[override-final] can't override final method + self, x: str + ) -> str: # E[override-final] can't override final method + ... + + # This is the right way to mark an overload as @override (decorate first + # overload only), so the use of @override should cause an error (because + # there's no `Base.bad_override` method): + + @overload # E[bad_override] marked as override but doesn't exist in base + @override # E[bad_override] + def bad_override(self, x: int) -> int: # E[bad_override] + ... + @overload + def bad_override(self, x: str) -> str: ... + + # This is also a correctly-decorated overloaded @override, which is + # overriding a method that does exist in the base, so there should be no + # error. We need both this test and the previous one, because in the + # previous test, an incorrect error about the use of @override decorator + # could appear on the same line as the expected error about overriding a + # method that doesn't exist in base: + + @overload + @override + def good_override(self, x: int) -> int: ... + @overload + def good_override(self, x: str) -> str: ... + + # This is the wrong way to use @override with an overloaded method, and + # should emit an error: + + @overload # E[override_impl]: @override should appear only on first overload + def to_override(self, x: int) -> int: ... + @overload + @override # E[override_impl]: @override should appear only on first overload + def to_override( # E[override_impl]: @override should appear only on first overload + self, x: str + ) -> str: # E[override_impl]: @override should appear only on first overload + ... diff --git a/conformance/tests/overloads_evaluation.py b/conformance/tests/overloads_evaluation.py new file mode 100644 index 000000000..c89f9d0ba --- /dev/null +++ b/conformance/tests/overloads_evaluation.py @@ -0,0 +1,347 @@ +""" +Tests for evaluation of calls to overloaded functions. +""" + +from enum import Enum +from typing import Any, assert_type, Literal, overload, TypeVar + +# mypy: disable-error-code=overload-overlap + + +T = TypeVar("T") + +# > Step 1: Examine the argument list to determine the number of +# > positional and keyword arguments. Use this information to eliminate any +# > overload candidates that are not plausible based on their +# > input signatures. + +# (There is no way to observe via conformance tests whether an implementation +# performs this step separately from the argument-type-testing step 2 below, so +# the separation of step 1 from step 2 is purely a presentation choice for the +# algorithm, not a conformance requirement.) + + +@overload +def example1_1(x: int, y: str) -> int: ... + + +@overload +def example1_1(x: str) -> str: ... + + +def example1_1(x: int | str, y: str = "") -> int | str: + return 1 + + +# > - If no candidate overloads remain, generate an error and stop. + +example1_1() # E: no matching overload + +# > - If only one candidate overload remains, it is the winning match. Evaluate +# > it as if it were a non-overloaded function call and stop. + +ret1 = example1_1(1, "") +assert_type(ret1, int) + +example1_1(1, 1) # E: Literal[1] not assignable to str + +ret3 = example1_1("") +assert_type(ret3, str) + +example1_1(1) # E: Literal[1] not assignable to str + + +@overload +def example1_2(b: Literal[True] = ...) -> int: ... + + +@overload +def example1_2(b: bool) -> float: ... + + +def example1_2(b: bool = True) -> float: + raise NotImplementedError + + +def check_example1_2() -> None: + assert_type(example1_2(), int) + + +# > Step 2: Evaluate each remaining overload as a regular (non-overloaded) +# > call to determine whether it is compatible with the supplied +# > argument list. Unlike step 1, this step considers the types of the parameters +# > and arguments. During this step, do not generate any user-visible errors. +# > Simply record which of the overloads result in evaluation errors. + + +@overload +def example2(x: int, y: str, z: int) -> str: ... + + +@overload +def example2(x: int, y: int, z: int) -> int: ... + + +def example2(x: int, y: int | str, z: int) -> int | str: + return 1 + + +# > - If only one overload evaluates without error, it is the winning match. +# > Evaluate it as if it were a non-overloaded function call and stop. + +ret5 = example2(1, 2, 3) +assert_type(ret5, int) + +# > Step 3: If step 2 produces errors for all overloads, perform +# > "argument type expansion". Union types can be expanded +# > into their constituent subtypes. For example, the type ``int | str`` can +# > be expanded into ``int`` and ``str``. + +# > - If all argument lists evaluate successfully, combine their +# > respective return types by union to determine the final return type +# > for the call, and stop. + + +def check_expand_union(v: int | str) -> None: + ret1 = example2(1, v, 1) + assert_type(ret1, int | str) + + +# > - If argument expansion has been applied to all arguments and one or +# > more of the expanded argument lists cannot be evaluated successfully, +# > generate an error and stop. + + +def check_expand_union_2(v: int | str) -> None: + example2(v, v, 1) # E: no overload matches (str, ..., ...) + + +# > 2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``. + + +@overload +def expand_bool(x: Literal[False]) -> Literal[0]: ... + + +@overload +def expand_bool(x: Literal[True]) -> Literal[1]: ... + + +def expand_bool(x: bool) -> int: + return int(x) + + +def check_expand_bool(v: bool) -> None: + ret1 = expand_bool(v) + assert_type(ret1, Literal[0, 1]) + + +# > 3. ``Enum`` types (other than those that derive from ``enum.Flag``) should +# > be expanded into their literal members. + + +class Color(Enum): + RED = 1 + BLUE = 2 + + +@overload +def expand_enum(x: Literal[Color.RED]) -> Literal[0]: ... + + +@overload +def expand_enum(x: Literal[Color.BLUE]) -> Literal[1]: ... + + +def expand_enum(x: Color) -> int: + return x.value + + +def check_expand_enum(v: Color) -> None: + ret1 = expand_enum(v) + assert_type(ret1, Literal[0, 1]) + + +# > 4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``. + + +@overload +def expand_type_union(x: type[int]) -> int: ... + + +@overload +def expand_type_union(x: type[str]) -> str: ... + + +def expand_type_union(x: type[int] | type[str]) -> int | str: + return 1 + + +def check_expand_type_union(v: type[int | str]) -> None: + ret1 = expand_type_union(v) + assert_type(ret1, int | str) + + +# > 5. Tuples of known length that contain expandable types should be expanded +# > into all possible combinations of their element types. For example, the type +# > ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, +# > ``(int, Literal[False])``, ``(str, Literal[True])``, and +# > ``(str, Literal[False])``. + + +@overload +def expand_tuple(x: tuple[int, int]) -> int: ... + + +@overload +def expand_tuple(x: tuple[int, str]) -> str: ... + + +def expand_tuple(x: tuple[int, int | str]) -> int | str: + return 1 + + +def check_expand_tuple(v: int | str) -> None: + ret1 = expand_tuple((1, v)) + assert_type(ret1, int | str) + + +# > Step 4: If the argument list is compatible with two or more overloads, +# > determine whether one or more of the overloads has a variadic parameter +# > (either ``*args`` or ``**kwargs``) that maps to a corresponding argument +# > that supplies an indeterminate number of positional or keyword arguments. +# > If so, eliminate overloads that do not have a variadic parameter. + + +@overload +def variadic(x: int, /) -> str: ... + + +@overload +def variadic(x: int, y: int, /, *args: int) -> int: ... + + +def variadic(*args: int) -> int | str: + return 1 + + +# > - If this results in only one remaining candidate overload, it is +# > the winning match. Evaluate it as if it were a non-overloaded function +# > call and stop. + + +def check_variadic(v: list[int]) -> None: + ret1 = variadic(*v) + assert_type(ret1, int) + + +# > Step 5: For all arguments, determine whether all possible +# > :term:`materializations ` of the argument's type are assignable to +# > the corresponding parameter type for each of the remaining overloads. If so, +# > eliminate all of the subsequent remaining overloads. + + +@overload +def example4(x: list[int], y: int) -> list[int]: ... + + +@overload +def example4(x: list[str], y: str) -> list[int]: ... + + +@overload +def example4(x: int, y: int) -> list[str]: ... + + +def example4(x: list[int] | list[str] | int, y: int | str) -> list[int] | list[str]: + return [] + + +def check_example4(v1: list[Any], v2: Any) -> None: + ret1 = example4(v1, v2) + assert_type(ret1, list[int]) + + ret2 = example4(v2, 1) + assert_type(ret2, Any) + + +@overload +def example5(obj: list[int]) -> list[int]: ... + + +@overload +def example5(obj: list[str]) -> list[str]: ... + + +def example5(obj: Any) -> list[Any]: + return [] + + +def check_example5(b: list[Any]) -> None: + assert_type(example5(b), Any) + + +@overload +def example6(a: int, b: Any) -> float: ... + + +@overload +def example6(a: float, b: T) -> T: ... + + +def example6(a: float, b: T) -> T: + raise NotImplementedError + + +def check_example6(a: list[Any], b: Any, c: str) -> None: + m: list[int] = [] + + # All possible materializations of list[Any] are + # assignable to Any, so this matches the first overload + # and eliminates all subsequent overloads. + v1 = example6(1, a) + assert_type(v1, float) + + # All possible materializations of Any are + # assignable to Any, so this matches the first overload + # and eliminates all subsequent overloads. + v2 = example6(1, b) + assert_type(v2, float) + + # All possible materializations of list[int] are + # assignable to Any, so this matches the first overload + # and eliminates all subsequent overloads. + v3 = example6(1, m) + assert_type(v3, float) + + v4 = example6(1.0, c) + assert_type(v4, str) + + v5 = example6(1.0, b) + assert_type(v5, Any) + + v6 = example6(1.0, m) + assert_type(v6, list[int]) + + +@overload +def example7(x: list[Any], y: int) -> list[int]: ... + + +@overload +def example7(x: list[Any], y: str) -> list[str]: ... + + +def example7(x: list[Any], y: int | str) -> list[int] | list[str]: + return [] + + +def check_example7(v1: list[Any], v2: Any) -> None: + ret1 = example7(v1, 1) + assert_type(ret1, list[int]) + + ret2 = example7(v1, "") + assert_type(ret2, list[str]) + + ret3 = example7(v1, v2) + assert_type(ret3, Any) diff --git a/conformance/tests/protocols_class_objects.py b/conformance/tests/protocols_class_objects.py new file mode 100644 index 000000000..79de51204 --- /dev/null +++ b/conformance/tests/protocols_class_objects.py @@ -0,0 +1,109 @@ +""" +Tests the handling of class objects as implementations of a protocol. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#type-and-class-objects-vs-protocols + +# > Variables and parameters annotated with Type[Proto] accept only concrete +# (non-protocol) subtypes of Proto. + +from abc import abstractmethod +from typing import Any, ClassVar, Protocol + + +class Proto(Protocol): + @abstractmethod + def meth(self) -> int: + ... + + +class Concrete: + def meth(self) -> int: + return 42 + + +def fun(cls: type[Proto]) -> int: + return cls().meth() # OK + + +fun(Proto) # E +fun(Concrete) # OK + + +var: type[Proto] +var = Proto # E +var = Concrete # OK +var().meth() # OK + + +# > A class object is considered an implementation of a protocol if accessing +# > all members on it results in types compatible with the protocol members. + + +class ProtoA1(Protocol): + def method1(self, x: int) -> int: + ... + + +class ProtoA2(Protocol): + def method1(_self, self: Any, x: int) -> int: + ... + + +class ConcreteA: + def method1(self, x: int) -> int: + return 0 + + +pa1: ProtoA1 = ConcreteA # E: signatures don't match +pa2: ProtoA2 = ConcreteA # OK + + +class ProtoB1(Protocol): + @property + def prop1(self) -> int: + ... + + +class ConcreteB: + @property + def prop1(self) -> int: + return 0 + + +pb1: ProtoB1 = ConcreteB # E + + +class ProtoC1(Protocol): + attr1: ClassVar[int] + + +class ProtoC2(Protocol): + attr1: int + + +class ConcreteC1: + attr1: ClassVar[int] = 1 + + +class ConcreteC2: + attr1: int = 1 + + +class CMeta(type): + attr1: int + + def __init__(self, *args, **kwargs) -> None: + self.attr1: int = 1 + + +class ConcreteC3(metaclass=CMeta): + pass + + +pc1: ProtoC1 = ConcreteC1 # E +pc2: ProtoC2 = ConcreteC1 # OK +pc3: ProtoC1 = ConcreteC2 # E +pc4: ProtoC2 = ConcreteC2 # E +pc5: ProtoC1 = ConcreteC3 # E +pc6: ProtoC2 = ConcreteC3 # OK diff --git a/conformance/tests/protocols_definition.py b/conformance/tests/protocols_definition.py new file mode 100644 index 000000000..bf046ee8c --- /dev/null +++ b/conformance/tests/protocols_definition.py @@ -0,0 +1,341 @@ +""" +Tests the basic definition rules for protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#defining-a-protocol + +from abc import abstractmethod +from typing import Any, ClassVar, Iterable, NamedTuple, Protocol, Sequence +from dataclasses import dataclass + + +class SupportsClose(Protocol): + def close(self) -> None: + ... + + +class Resource: + def close(self) -> None: + pass + + +def close_all(things: Iterable[SupportsClose]) -> None: + for t in things: + t.close() + + +f = open("foo.txt") +r = Resource() +close_all([f, r]) # OK +close_all([1]) # E: 'int' has no 'close' method + + +class Example(Protocol): + def first(self) -> int: # This is a protocol member + return 42 + + @abstractmethod + def second(self) -> int: # Method without a default implementation + raise NotImplementedError + + # > Static methods, class methods, and properties are equally allowed in protocols. + + @staticmethod + def third() -> int: + ... + + @classmethod + def fourth(cls) -> int: + ... + + @property + def fifth(self) -> int: + ... + + +# > To define a protocol variable, one can use PEP 526 variable annotations +# > in the class body. Additional attributes only defined in the body of a +# > method by assignment via self are not allowed. + + +class Template(Protocol): + name: str # This is a protocol member + value: int = 0 # This one too (with default) + + def method(self) -> None: + self.name = "name" # OK + self.temp: list[int] = [] # E: use of self variables not allowed + + +class Concrete: + def __init__(self, name: str, value: int) -> None: + self.name = name + self.value = value + + def method(self) -> None: + return + + +var: Template = Concrete("value", 42) # OK + + +# > To distinguish between protocol class variables and protocol instance +# > variables, the special ClassVar annotation should be used as specified +# > by PEP 526. By default, protocol variables as defined above are considered +# > readable and writable. To define a read-only protocol variable, one can +# > use an (abstract) property. + + +class Template2(Protocol): + val1: ClassVar[Sequence[int]] + + +class Concrete2_Good1: + val1: ClassVar[Sequence[int]] = [2] + + +class Concrete2_Bad1: + ... + + +class Concrete2_Bad2: + val1: Sequence[float] = [2] + + +class Concrete2_Bad3: + val1: list[int] = [2] + + +class Concrete2_Bad4: + val1: Sequence[int] = [2] + + +v2_good1: Template2 = Concrete2_Good1() # OK +v2_bad1: Template2 = Concrete2_Bad1() # E +v2_bad2: Template2 = Concrete2_Bad2() # E +v2_bad3: Template2 = Concrete2_Bad3() # E +v2_bad4: Template2 = Concrete2_Bad4() # E + + +class Template3(Protocol): + val1: Sequence[int] + + +class Concrete3_Good1: + val1: Sequence[int] = [0] + + +class Concrete3_Good2: + def __init__(self) -> None: + self.val1: Sequence[int] = [0] + + +class Concrete3_Bad1: + ... + + +class Concrete3_Bad2: + val1: ClassVar[Sequence[int]] = [0] + + +class Concrete3_Bad3: + @property + def val1(self) -> Sequence[int]: + return [0] + + +class Concrete3_Bad4: + val1: Sequence[float] = [0] + + +class Concrete3_Bad5: + val1: list[int] = [0] + + +v3_good1: Template3 = Concrete3_Good1() # OK +v3_bad1: Template3 = Concrete3_Bad1() # E +v3_bad2: Template3 = Concrete3_Bad2() # E +v3_bad3: Template3 = Concrete3_Bad3() # E +v3_bad4: Template3 = Concrete3_Bad4() # E +v3_bad5: Template3 = Concrete3_Bad5() # E + + +class Template4(Protocol): + @property + def val1(self) -> Sequence[float]: + ... + + +class Concrete4_Good1: + @property + def val1(self) -> Sequence[float]: + return [0] + + +class Concrete4_Good2: + @property + def val1(self) -> Sequence[int]: + return [0] + + +class Concrete4_Good3: + val1: Sequence[float] = [0] + + +class Concrete4_Good4: + val1: Sequence[int] = [0] + + +class Concrete4_Good5: + val1: list[float] = [0] + + +class Concrete4_Good6(NamedTuple): + val1: Sequence[float] = [0] + + +@dataclass(frozen=False) +class Concrete4_Good7: + val1: Sequence[float] = (0,) + + +class Concrete4_Bad1: + def val1(self) -> Sequence[int]: # Not a property + return [0] + + +class Concrete4_Bad2: + ... + + +v4_good1: Template4 = Concrete4_Good1() # OK +v4_good2: Template4 = Concrete4_Good2() # OK +v4_good3: Template4 = Concrete4_Good3() # OK +v4_good4: Template4 = Concrete4_Good4() # OK +v4_good5: Template4 = Concrete4_Good5() # OK +v4_good6: Template4 = Concrete4_Good6() # OK +v4_good7: Template4 = Concrete4_Good7() # OK +v4_bad1: Template4 = Concrete4_Bad1() # E +v4_bad2: Template4 = Concrete4_Bad2() # E + + +class Template5(Protocol): + def method1(self, a: int, b: int) -> float: + ... + + +class Concrete5_Good1: + def method1(self, a: float, b: float) -> int: + return 0 + + +class Concrete5_Good2: + def method1(self, *args: float, **kwargs: float) -> Any: + return 0.0 + + +class Concrete5_Good3: + def method1(self, a, b) -> Any: + return 0.0 + + +class Concrete5_Good4: + @classmethod + def method1(cls, a: int, b: int) -> float: + return 0 + + +class Concrete5_Good5: + @staticmethod + def method1(a: int, b: int) -> float: + return 0 + + +class Concrete5_Bad1: + def method1(self, a, c) -> int: + return 0 + + +class Concrete5_Bad2: + def method1(self, a: int, c: int) -> int: + return 0 + + +class Concrete5_Bad3: + def method1(self, *, a: int, b: int) -> float: + return 0 + + +class Concrete5_Bad4: + def method1(self, a: int, b: int, /) -> float: + return 0 + + +class Concrete5_Bad5: + @staticmethod + def method1(self, a: int, b: int) -> float: + return 0 + + +v5_good1: Template5 = Concrete5_Good1() # OK +v5_good2: Template5 = Concrete5_Good2() # OK +v5_good3: Template5 = Concrete5_Good3() # OK +v5_good4: Template5 = Concrete5_Good4() # OK +v5_good5: Template5 = Concrete5_Good5() # OK +v5_bad1: Template5 = Concrete5_Bad1() # E +v5_bad2: Template5 = Concrete5_Bad2() # E +v5_bad3: Template5 = Concrete5_Bad3() # E +v5_bad4: Template5 = Concrete5_Bad4() # E +v5_bad5: Template5 = Concrete5_Bad5() # E + + +class Template6(Protocol): + @property + def val1(self) -> Sequence[float]: + ... + + @val1.setter + def val1(self, val: Sequence[float]) -> None: + ... + + +class Concrete6_Good1: + @property + def val1(self) -> Sequence[float]: + return [0] + + @val1.setter + def val1(self, val: Sequence[float]) -> None: + pass + + +class Concrete6_Good2: + val1: Sequence[float] = [0] + + +@dataclass(frozen=False) +class Concrete6_Good3: + val1: Sequence[float] = (0,) + + +class Concrete6_Bad1: + @property + def val1(self) -> Sequence[float]: + return [0] + + +class Concrete6_Bad2(NamedTuple): + val1: Sequence[float] = [0] + + +@dataclass(frozen=True) +class Concrete6_Bad3: + val1: Sequence[float] = (0,) + + +v6_good1: Template6 = Concrete6_Good1() # OK +v6_good2: Template6 = Concrete6_Good2() # OK +v6_good3: Template6 = Concrete6_Good3() # OK +v6_bad1: Template6 = Concrete6_Bad1() # E +v6_bad2: Template6 = Concrete6_Bad2() # E: named tuple is immutable +v6_bad3: Template6 = Concrete6_Bad3() # E: dataclass is frozen diff --git a/conformance/tests/protocols_explicit.py b/conformance/tests/protocols_explicit.py new file mode 100644 index 000000000..70fd56445 --- /dev/null +++ b/conformance/tests/protocols_explicit.py @@ -0,0 +1,171 @@ +""" +Tests the handling of explicit protocol classes. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#explicitly-declaring-implementation + +from abc import ABC, abstractmethod +from typing import ClassVar, Protocol + + +class PColor(Protocol): + @abstractmethod + def draw(self) -> str: + ... + + def complex_method(self) -> int: + return 1 + + +class NiceColor(PColor): + def draw(self) -> str: + return "deep blue" + + +class BadColor(PColor): + def draw(self) -> str: + return super().draw() # E: no default implementation + + +class ImplicitColor: # Note no 'PColor' base here + def draw(self) -> str: + return "probably gray" + + def complex_method(self) -> int: + return 0 + + +v1: PColor = NiceColor() # OK +v2: PColor = ImplicitColor() # OK + + +class RGB(Protocol): + rgb: tuple[int, int, int] + other: int + + @abstractmethod + def intensity(self) -> int: + return 1 + + def transparency(self) -> int: + ... + + +class Point(RGB): + def __init__(self, red: int, green: int, blue: str) -> None: + self.rgb = red, green, blue # E: 'blue' must be 'int' + self.other = 0 + + +p = Point(0, 0, "") # E: Cannot instantiate abstract class + + +class Proto1(Protocol): + cm1: ClassVar[int] + cm2: ClassVar[int] = 0 + + im1: int + im2: int = 2 + im3: int + + def __init__(self): + self.im3 = 3 + + +class Proto2(Protocol): + cm10: int + + +class Proto3(Proto2, Protocol): + cm11: int + + +class Concrete1(Proto1): + def __init__(self): + self.im1 = 1 + self.im3 = 3 + + +c1 = Concrete1() # E: cannot instantiate abstract class + + +class Concrete2(Proto1): + cm1 = 3 + im1 = 0 + + +c2 = Concrete2() + + +class Concrete3(Proto1, Proto3): + cm1 = 3 + + def __init__(self): + self.im1 = 0 + self.cm10 = 10 + self.cm11 = 11 + + +c3 = Concrete3() + + +class Concrete4(Proto1, Proto3): + cm1 = 3 + cm10 = 3 + + def __init__(self): + self.im1 = 3 + self.im10 = 10 + self.cm11 = 3 + + +c4 = Concrete4() + + +class Proto5(Protocol): + def method1(self) -> int: + ... + + +class Concrete5(Proto5): + pass + + +c5 = Concrete5() # E: cannot instantiate abstract class + + +class Proto6(Protocol): + x: int + + +class Mixin: + x = 3 + + +class Concrete6(Mixin, Proto6): + pass + + +class Proto7(Protocol): + @abstractmethod + def method1(self): + ... + + +class Mixin7(Proto7, ABC): + def method1(self) -> None: + pass + + +class Concrete7A(Proto7): + pass + + +c7a = Concrete7A() # E: cannot instantiate abstract class + + +class Concrete7B(Mixin7, Proto7): + pass + + +c7b = Concrete7B() diff --git a/conformance/tests/protocols_generic.py b/conformance/tests/protocols_generic.py new file mode 100644 index 000000000..b417e00f6 --- /dev/null +++ b/conformance/tests/protocols_generic.py @@ -0,0 +1,147 @@ +""" +Tests the handling of generic protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#generic-protocols + + +from typing import Callable, Generic, Iterator, Protocol, Self, TypeVar, assert_type + +S = TypeVar("S") +T = TypeVar("T") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class Iterable(Protocol[T_co]): + def __iter__(self) -> Iterator[T_co]: + ... + + +# > Protocol[T, S, ...] is allowed as a shorthand for Protocol, Generic[T, S, ...]. +# In particular, the implicit order of type parameters is dictated by +# the order in which they appear in the `Protocol` subscript. + + +class Proto1(Iterable[T_co], Protocol[S, T_co]): + def method1(self, x: S) -> S: + ... + + +class Concrete1: + def __iter__(self) -> Iterator[int]: + return (x for x in [1, 2, 3]) + + def method1(self, x: str) -> str: + return "" + + +p1: Proto1[str, int] = Concrete1() # OK +p2: Proto1[int, str] = Concrete1() # E: incompatible type + + +# > It is an error to combine the shorthand with Generic[T, S, ...] +class Proto2(Protocol[T_co], Generic[T_co]): # E + ... + + +# > User-defined generic protocols support explicitly declared variance. +class Box(Protocol[T_co]): + def content(self) -> T_co: + ... + + +def func1(box_int: Box[int], box_float: Box[float]): + v1: Box[float] = box_int # OK + v2: Box[int] = box_float # E + + +class Sender(Protocol[T_contra]): + def send(self, data: T_contra) -> int: + return 0 + + +def func2(sender_int: Sender[int], sender_float: Sender[float]): + v1: Sender[int] = sender_float # OK + v2: Sender[float] = sender_int # E + + +class AttrProto(Protocol[T]): + attr: T + + +def func3(attr_int: AttrProto[int], attr_float: AttrProto[float]): + v1: AttrProto[float] = attr_int # E + v2: AttrProto[int] = attr_float # E + + +class HasParent(Protocol): + def get_parent(self: T) -> T: + ... + + +GenericHasParent = TypeVar("GenericHasParent", bound=HasParent) + + +def generic_get_parent(n: GenericHasParent) -> GenericHasParent: + return n.get_parent() + + +class ConcreteHasParent: + def get_parent(self) -> Self: + return self + + +parent = generic_get_parent(ConcreteHasParent()) # OK +assert_type(parent, ConcreteHasParent) + + +class HasPropertyProto(Protocol): + @property + def f(self: T) -> T: + ... + + def m(self, item: T, callback: Callable[[T], str]) -> str: + ... + + +class ConcreteHasProperty1: + @property + def f(self: T) -> T: + return self + + def m(self, item: T, callback: Callable[[T], str]) -> str: + return "" + + +class ConcreteHasProperty2: + @property + def f(self) -> Self: + return self + + def m(self, item: int, callback: Callable[[int], str]) -> str: + return "" + + +class ConcreteHasProperty3: + @property + def f(self) -> int: + return 0 + + def m(self, item: int, callback: Callable[[int], str]) -> str: + return "" + + +class ConcreteHasProperty4: + @property + def f(self) -> Self: + return self + + def m(self, item: str, callback: Callable[[int], str]) -> str: + return "" + + +hp1: HasPropertyProto = ConcreteHasProperty1() # OK +hp2: HasPropertyProto = ConcreteHasProperty2() # E +hp3: HasPropertyProto = ConcreteHasProperty3() # E +hp4: HasPropertyProto = ConcreteHasProperty4() # E diff --git a/conformance/tests/protocols_merging.py b/conformance/tests/protocols_merging.py new file mode 100644 index 000000000..e989e4ef8 --- /dev/null +++ b/conformance/tests/protocols_merging.py @@ -0,0 +1,83 @@ +""" +Tests merging and extending of protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#merging-and-extending-protocols + +from abc import abstractmethod +from typing import Protocol +from collections.abc import Sized + + +class SizedAndClosable1(Sized, Protocol): + def close(self) -> None: + ... + + +class SizedAndClosable2(Protocol): + def __len__(self) -> int: + ... + + def close(self) -> None: + ... + + +class SizedAndClosable3(SizedAndClosable1): # Note: not a protocol + def __len__(self) -> int: + return 0 + + def close(self) -> None: + pass + + +class SCConcrete1: + def __len__(self) -> int: + return 0 + + def close(self) -> None: + pass + + +class SCConcrete2: + def close(self) -> None: + pass + + +s1: SizedAndClosable1 = SCConcrete1() # OK +s2: SizedAndClosable2 = SCConcrete1() # OK +s3: SizedAndClosable1 = SizedAndClosable3() # OK +s4: SizedAndClosable2 = SizedAndClosable3() # OK +s5: Sized = SCConcrete1() # OK + +s6: SizedAndClosable1 = SCConcrete2() # E: doesn't implement `__len__` +s7: SizedAndClosable2 = SCConcrete2() # E: doesn't implement `__len__` +s8: SizedAndClosable3 = SCConcrete2() # E: SizedAndClosable3 is not a protocol + + +def func1(s1: SizedAndClosable1, s2: SizedAndClosable2): + # > The two definitions of SizedAndClosable are equivalent. + v1: SizedAndClosable2 = s1 + v2: SizedAndClosable1 = s2 + + +# > If Protocol is included in the base class list, all the other base classes +# > must be protocols. + + +class BadProto(SizedAndClosable3, Protocol): # E: SizedAndClosable3 is not a protocol + ... + + +# > Without this base, the class is “downgraded” to a regular ABC that +# > cannot be used with structural subtyping. +class SizedAndClosable4(SizedAndClosable1): + def __len__(self) -> int: + return 0 + + @abstractmethod + def close(self) -> None: + raise NotImplementedError + + +x = SizedAndClosable4() # E: cannot instantiate abstract class +y: SizedAndClosable4 = SCConcrete1() # E diff --git a/conformance/tests/protocols_modules.py b/conformance/tests/protocols_modules.py new file mode 100644 index 000000000..d417b797a --- /dev/null +++ b/conformance/tests/protocols_modules.py @@ -0,0 +1,49 @@ +""" +Tests the handling of modules as the implementation of a protocol. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#modules-as-implementations-of-protocols + +# > A module object is accepted where a protocol is expected if the public +# > interface of the given module is compatible with the expected protocol. + +import _protocols_modules1 +import _protocols_modules2 +from typing import Protocol + + +class Options1(Protocol): + timeout: int + one_flag: bool + other_flag: bool + + +class Options2(Protocol): + timeout: str + + +op1: Options1 = _protocols_modules1 # OK +op2: Options2 = _protocols_modules1 # E + + +class Reporter1(Protocol): + def on_error(self, x: int) -> None: + ... + + def on_success(self) -> None: + ... + + +class Reporter2(Protocol): + def on_error(self, x: int) -> int: + ... + + +class Reporter3(Protocol): + def not_implemented(self, x: int) -> int: + ... + + +rp1: Reporter1 = _protocols_modules2 # OK +rp2: Reporter2 = _protocols_modules2 # E +rp3: Reporter3 = _protocols_modules2 # E diff --git a/conformance/tests/protocols_recursive.py b/conformance/tests/protocols_recursive.py new file mode 100644 index 000000000..9461f4a60 --- /dev/null +++ b/conformance/tests/protocols_recursive.py @@ -0,0 +1,81 @@ +""" +Tests the handling of recursive protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#recursive-protocols + + +from typing import Generic, Iterable, Never, Protocol, Self, TypeVar, assert_type + +T = TypeVar("T") +T_co = TypeVar("T_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class Traversable(Protocol): + def leaves(self) -> Iterable["Traversable"]: + ... + + +class SimpleTree: + def leaves(self) -> list["SimpleTree"]: + return [] + + +root: Traversable = SimpleTree() # OK + + +class Tree(Generic[T]): + def leaves(self) -> list["Tree[T]"]: + return [] + + +def walk(graph: Traversable) -> None: + pass + + +tree: Tree[float] = Tree() +walk(tree) # OK + + +class ProtoA(Protocol[T_co, T_contra]): + def method1(self) -> "ProtoA[T_co, T_contra]": + ... + + @classmethod + def method2(cls, value: T_contra) -> None: + ... + + +class ProtoB(Protocol[T_co, T_contra]): + def method3(self) -> ProtoA[T_co, T_contra]: + ... + + +class ImplA: + def method1(self) -> Self: + return self + + @classmethod + def method2(cls, value: int) -> None: + pass + + +class ImplB: + def method3(self) -> ImplA: + return ImplA() + + def method1(self) -> Self: + return self + + @classmethod + def method2(cls: type[ProtoB[object, T]], value: list[T]) -> None: + pass + + +def func1(x: ProtoA[Never, T]) -> T: + raise NotImplementedError + + +v1 = func1(ImplB()) +assert_type(v1, list[int]) diff --git a/conformance/tests/protocols_runtime_checkable.py b/conformance/tests/protocols_runtime_checkable.py new file mode 100644 index 000000000..8f5782f87 --- /dev/null +++ b/conformance/tests/protocols_runtime_checkable.py @@ -0,0 +1,95 @@ +""" +Tests the handling of the @runtime_checkable decorator for protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#runtime-checkable-decorator-and-narrowing-types-by-isinstance + +# > A protocol can be used as a second argument in isinstance() and +# > issubclass() only if it is explicitly opt-in by @runtime_checkable decorator. + +from typing import Any, Protocol, runtime_checkable + + +class Proto1(Protocol): + name: str + + +@runtime_checkable +class Proto2(Protocol): + name: str + + +def func1(a: Any): + if isinstance(a, Proto1): # E: not runtime_checkable + return + + if isinstance(a, Proto2): # OK + return + + +# > isinstance() can be used with both data and non-data protocols, while +# > issubclass() can be used only with non-data protocols. + + +@runtime_checkable +class DataProtocol(Protocol): + name: str + + def method1(self) -> int: + ... + + +@runtime_checkable +class NonDataProtocol(Protocol): + def method1(self) -> int: + ... + + +def func2(a: Any): + if isinstance(a, DataProtocol): # OK + return + + if isinstance(a, NonDataProtocol): # OK + return + + if issubclass(a, DataProtocol): # E + return + + if issubclass(a, NonDataProtocol): # OK + return + + if issubclass(a, (NonDataProtocol, DataProtocol)): # E + return + + +# > Type checkers should reject an isinstance() or issubclass() call if there +# > is an unsafe overlap between the type of the first argument and the protocol. + + +@runtime_checkable +class Proto3(Protocol): + def method1(self, a: int) -> int: + ... + + +class Concrete3A: + def method1(self, a: str) -> None: + pass + + +class Concrete3B: + method1: int = 1 + + +def func3(): + if isinstance(Concrete3A(), Proto2): # OK + pass + + if isinstance(Concrete3A(), Proto3): # E: unsafe overlap + pass + + if isinstance(Concrete3B(), (Proto3, NonDataProtocol)): # E: unsafe overlap + pass + + if issubclass(Concrete3A, (Proto3, NonDataProtocol)): # E: unsafe overlap + pass diff --git a/conformance/tests/protocols_self.py b/conformance/tests/protocols_self.py new file mode 100644 index 000000000..225d4614b --- /dev/null +++ b/conformance/tests/protocols_self.py @@ -0,0 +1,73 @@ +""" +Tests the handling of annotated "self" parameters in a protocol. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#self-types-in-protocols + +from typing import Generic, Protocol, Self, TypeVar + + +C = TypeVar("C", bound="Copyable") + + +class Copyable(Protocol): + def copy(self: C) -> C: + return self + + +class One: + def copy(self) -> "One": + return One() + + +T = TypeVar("T", bound="Other") + + +class Other: + def copy(self: T) -> T: + return self + + +c: Copyable +c = One() # OK +c = Other() # OK + + +T1_co = TypeVar("T1_co", covariant=True) +T2_co = TypeVar("T2_co", covariant=True) + + +class P1Parent(Protocol[T2_co]): + def f0(self, /) -> Self: + ... + + +class P1Child(P1Parent[T2_co], Protocol[T2_co]): + ... + + +class C1(Generic[T1_co]): + def f0(self, /) -> Self: + return self + + +a1: P1Parent[str] = C1[str]() +b1: P1Child[str] = C1[str]() + + +class P2Parent(Protocol[T1_co]): + def f0(self, right: Self, /) -> "P2Parent[T1_co]": + return right + + +class P2Child(P2Parent[T1_co], Protocol[T1_co]): + ... + + +class C2(Generic[T2_co]): + def f0(self, other: Self) -> "C2[T2_co]": + return other + + +a2: P2Parent[str] = C2[str]() # OK +b2: P2Child[str] = C2[str]() # OK diff --git a/conformance/tests/protocols_subtyping.py b/conformance/tests/protocols_subtyping.py new file mode 100644 index 000000000..67f695d67 --- /dev/null +++ b/conformance/tests/protocols_subtyping.py @@ -0,0 +1,142 @@ +""" +Tests subtyping rules for protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#subtyping-relationships-with-other-types + +# > Protocols cannot be instantiated. + +from typing import Hashable, Iterable, Protocol, Sequence, TypeVar + + +class Proto1(Protocol): + pass + + +p1 = Proto1() # E: protocol cannot be instantiated + + +# > A protocol is never a subtype of a concrete type. + +# > A concrete type X is a subtype of protocol P if and only if X implements +# > all protocol members of P with compatible types. In other words, subtyping +# > with respect to a protocol is always structural. + + +class Proto2(Protocol): + def method1(self) -> None: + ... + + +class Concrete2: + def method1(self) -> None: + pass + + +def func1(p2: Proto2, c2: Concrete2): + v1: Proto2 = c2 # OK + v2: Concrete2 = p2 # E + + +# > A protocol P1 is a subtype of another protocol P2 if P1 defines all +# > protocol members of P2 with compatible types. + + +class Proto3(Protocol): + def method1(self) -> None: + ... + + def method2(self) -> None: + ... + + +def func2(p2: Proto2, p3: Proto3): + v1: Proto2 = p3 # OK + v2: Proto3 = p2 # E + + +# > Generic protocols follow the rules for generic abstract classes, except +# > for using structural compatibility instead of compatibility defined by +# > inheritance relationships. + +S = TypeVar("S") +T = TypeVar("T") + + +class Proto4(Protocol[S, T]): + def method1(self, a: S, b: T) -> tuple[S, T]: + ... + + +class Proto5(Protocol[T]): + def method1(self, a: T, b: T) -> tuple[T, T]: + ... + + +def func3(p4_int: Proto4[int, int], p5_int: Proto5[int]): + v1: Proto4[int, int] = p5_int # OK + v2: Proto5[int] = p4_int # OK + v3: Proto4[int, float] = p5_int # E + v4: Proto5[float] = p4_int # E + + +S_co = TypeVar("S_co", covariant=True) +T_contra = TypeVar("T_contra", contravariant=True) + + +class Proto6(Protocol[S_co, T_contra]): + def method1(self, a: T_contra) -> Sequence[S_co]: + ... + + +class Proto7(Protocol[S_co, T_contra]): + def method1(self, a: T_contra) -> Sequence[S_co]: + ... + + +def func4(p6: Proto6[float, float]): + v1: Proto7[object, int] = p6 # OK + v2: Proto7[float, float] = p6 # OK + v3: Proto7[complex, int] = p6 # OK + + v4: Proto7[int, float] = p6 # E + v5: Proto7[float, object] = p6 # E + + +# > Unions of protocol classes behaves the same way as for non-protocol classes. + + +class SupportsExit(Protocol): + def exit(self) -> int: + ... + + +class SupportsQuit(Protocol): + def quit(self) -> int | None: + ... + + +def finish(task: SupportsExit | SupportsQuit) -> int: + return 0 + + +class DefaultJob: + def quit(self) -> int: + return 0 + + +finish(DefaultJob()) # OK + + +# > One can use multiple inheritance to define an intersection of protocols. + + +class HashableFloats(Iterable[float], Hashable, Protocol): + pass + + +def cached_func(args: HashableFloats) -> float: + return 0.0 + + +cached_func((1, 2, 3)) # OK, tuple is both hashable and iterable diff --git a/conformance/tests/protocols_variance.py b/conformance/tests/protocols_variance.py new file mode 100644 index 000000000..069ab8ecd --- /dev/null +++ b/conformance/tests/protocols_variance.py @@ -0,0 +1,121 @@ +""" +Tests type variable variance inference for generic protocols. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/protocol.html#generic-protocols + +from typing import ParamSpec, Protocol, TypeVar + +T1 = TypeVar("T1") +T2 = TypeVar("T2", bound=int) +T3 = TypeVar("T3", bytes, str) +T1_co = TypeVar("T1_co", covariant=True) +T1_contra = TypeVar("T1_contra", contravariant=True) +P = ParamSpec("P") +R = TypeVar("R", covariant=True) + +# > Type checkers will warn if the inferred variance is different from the +# > declared variance. + + +class AnotherBox(Protocol[T1]): # E: T should be covariant + def content(self) -> T1: + ... + + +class Protocol1(Protocol[T1, T2, T3]): # OK + def m1(self, p0: T1, p1: T2, p2: T3) -> T1 | T2: + ... + + def m2(self) -> T1: + ... + + def m3(self) -> T2: + ... + + def m4(self) -> T3: + ... + + +class Protocol2(Protocol[T1, T2, T3]): # E: T3 should be contravariant + def m1(self, p0: T1, p1: T2, p2: T3) -> T1: + ... + + def m2(self) -> T1: + ... + + def m3(self) -> T2: + ... + + +class Protocol3(Protocol[T1_co]): + def m1(self) -> None: + pass + + +class Protocol4(Protocol[T1]): # E: T1 should be contravariant + def m1(self, p0: T1) -> None: + ... + + +class Protocol5(Protocol[T1_co]): # E[covariant_in_input+] + def m1(self, p0: T1_co) -> None: # E[covariant_in_input+] + ... + + +class Protocol6(Protocol[T1]): # E: T1 should be covariant + def m1(self) -> T1: + ... + + +class Protocol7(Protocol[T1_contra]): # E[contravariant_in_output+] + def m1(self) -> T1_contra: # E[contravariant_in_output+] + ... + + +class Protocol8(Protocol[T1]): # OK + def m1(self) -> T1: + ... + + def m2(self, p1: T1) -> None: + pass + + +class Callback(Protocol[P, R]): # OK + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R: + ... + + +class Protocol9(Protocol[T1_co]): # OK + @property + def prop1(self) -> T1_co: + ... + + +class Protocol10(Protocol[T1_co]): # OK + def m1(self) -> type[T1_co]: + ... + + +class Protocol11(Protocol[T1]): # OK + x: T1 | None + + +class Protocol12(Protocol[T1]): # E: T1 should be covariant + # __init__ method is exempt from variance calculations. + def __init__(self, x: T1) -> None: + ... + + +class Protocol13(Protocol[T1_contra]): # OK + def m1(self: "Protocol13[T1_contra]", x: T1_contra) -> None: + ... + + @classmethod + def m2(cls: "type[Protocol13[T1_contra]]") -> None: + ... + + +class Protocol14(Protocol[T1]): # OK + def m1(self) -> list[T1]: + ... diff --git a/conformance/tests/pyrefly.toml b/conformance/tests/pyrefly.toml new file mode 100644 index 000000000..c9c44e591 --- /dev/null +++ b/conformance/tests/pyrefly.toml @@ -0,0 +1 @@ +# Empty configuration file to make sure pyrefly treats this directory as the project root. diff --git a/conformance/tests/qualifiers_annotated.py b/conformance/tests/qualifiers_annotated.py new file mode 100644 index 000000000..1b2b61a73 --- /dev/null +++ b/conformance/tests/qualifiers_annotated.py @@ -0,0 +1,113 @@ +""" +Tests the typing.Annotated special form. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/qualifiers.html#annotated + +# > The first argument to Annotated must be a valid type + +from dataclasses import InitVar, dataclass +from typing import ( + Annotated, + Any, + Callable, + ClassVar, + Final, + Literal, + NotRequired, + Required, + TypeAlias, + TypeVar, + TypedDict, + Union, +) + +T = TypeVar("T") + + +Good1: Annotated[Union[int, str], ""] +Good2: Annotated[int | None, ""] +Good3: Annotated[list[int], ""] +Good4: Annotated[Any, ""] +Good5: Annotated[tuple[int, ...] | list[int], ""] +Good6: Annotated[Callable[..., int], ""] +Good7: Annotated["int | str", ""] +Good8: Annotated[list["int | str"], ""] + + +Bad1: Annotated[[int, str], ""] # E: invalid type expression +Bad2: Annotated[((int, str),), ""] # E: invalid type expression +Bad3: Annotated[[int for i in range(1)], ""] # E: invalid type expression +Bad4: Annotated[{"a": "b"}, ""] # E: invalid type expression +Bad5: Annotated[(lambda: int)(), ""] # E: invalid type expression +Bad6: Annotated[[int][0], ""] # E: invalid type expression +Bad7: Annotated[int if 1 < 3 else str, ""] # E: invalid type expression +Bad8: Annotated[var1, ""] # E: invalid type expression +Bad9: Annotated[True, ""] # E: invalid type expression +Bad10: Annotated[1, ""] # E: invalid type expression +Bad11: Annotated[list or set, ""] # E: invalid type expression +Bad12: Annotated[f"{'int'}", ""] # E: invalid type expression + + +# > Multiple type annotations are supported (Annotated supports variadic arguments): + +Multi1: Annotated[int | str, 3, ""] +Multi2: Annotated[int | str, 3, "", lambda x: x, max(1, 2)] + +# > Annotated must be called with at least two arguments ( Annotated[int] is not valid) + +Bad13: Annotated[int] # E: requires at least two arguments + + +# > Annotated types can be nested + +Nested1: list[Annotated[dict[str, Annotated[str, ""]], ""]] +Nested2: Annotated[list[Annotated[dict[str, Annotated[Literal[1, 2, 3], ""]], ""]], ""] + + +# > Annotated is not type compatible with type or type[T] +SmallInt: TypeAlias = Annotated[int, ""] + +not_type1: type[Any] = Annotated[int, ""] # E +not_type2: type[Any] = SmallInt # E + + +def func4(x: type[T]) -> T: + return x() + + +func4(Annotated[str, ""]) # E +func4(SmallInt) # E + + +# > An attempt to call Annotated (whether parameterized or not) should be +# > treated as a type error by type checkers. + +Annotated() # E +Annotated[int, ""]() # E +SmallInt(1) # E + + +class ClassA: + a: ClassVar[Annotated[int, ""]] = 1 + b: Annotated[ClassVar[int], ""] = 1 + c: Final[Annotated[int, ""]] = 1 + d: Annotated[Final[int], ""] = 1 + + +@dataclass +class ClassB: + a: InitVar[Annotated[int, ""]] + b: Annotated[InitVar[int], ""] + + +class ClassC(TypedDict): + a: Annotated[Required[int], ""] + b: Required[Annotated[int, ""]] + c: Annotated[NotRequired[int], ""] + d: NotRequired[Annotated[int, ""]] + + +TA1: TypeAlias = Annotated[int | str, ""] +TA2 = Annotated[Literal[1, 2], ""] +TA3 = Annotated[T, ""] diff --git a/conformance/tests/qualifiers_final_annotation.py b/conformance/tests/qualifiers_final_annotation.py new file mode 100644 index 000000000..822ea564a --- /dev/null +++ b/conformance/tests/qualifiers_final_annotation.py @@ -0,0 +1,170 @@ +""" +Tests the typing.Final special form. +""" + +from typing import ClassVar, Final, Literal, NamedTuple, assert_type + +# Specification: https://typing.readthedocs.io/en/latest/spec/qualifiers.html#id1 + +ID1: Final[int] = 1 + +ID2: Final = 1 +assert_type(ID2, Literal[1]) + +# > If the right hand side is omitted, there must be an explicit type argument to Final. + +BAD1: Final # E: missing assignment + +BAD2: Final[str, int] = "" # E: only one type argument allowed + +# > There can be at most one final declaration per module or class for a given +# > attribute. + +# > As self.id: Final = 1 (also optionally with a type in square brackets). +# > This is allowed only in __init__ methods, so that the final instance +# > attribute is assigned only once when an instance is created. + + +class ClassA: + ID1: Final = 1 + + # > In class bodies and stub files you can omit the right hand side and just + # > write ID: Final[float]. If the right hand side is omitted, there must be + # > an explicit type argument to Final. + ID2: Final # E: missing initialization + + # > A final attribute declared in a class body without an initializer must + # > be initialized in the __init__ method (except in stub files): + ID3: Final[int] # E: missing initialization + + ID4: Final[int] # OK because initialized in __init__ + + ID5: Final[int] = 0 + + ID6: Final[int] + + ID7: Final[int] = 0 + + def __init__(self, cond: bool) -> None: + self.id1: Final = 1 + self.id2: Final[int] = 1 + + self.ID4 = 1 + + self.ID5 = 0 # E: Already initialized + + if cond: + self.ID6 = 1 + else: + self.ID6 = 2 + + def method1(self) -> None: + self.id3: Final = 1 # E: not allowed outside of __init__ method. + self.id4: Final[int] = 1 # E: not allowed outside of __init__ method. + + self.ID7 = 0 # E: cannot modify Final value + + self.ID7 += 1 # E: cannot modify Final value + + +RATE: Final = 3000 +RATE = 300 # E: Cannot redefine Final value + +# > There can’t be separate class-level and instance-level constants +# > with the same name. + + +class ClassB: + DEFAULT_ID: Final = 0 + + +ClassB.DEFAULT_ID = 0 # E: Cannot redefined value + + +# > A type checker should prevent final attributes from being overridden in a subclass: + + +class ClassC: + BORDER_WIDTH: Final = 2.5 + + __private: Final = 0 + + +class ClassCChild(ClassC): + BORDER_WIDTH = 2.5 # E: Cannot override Final value + + __private = 0 # OK + + +# > Type checkers should infer a final attribute that is initialized in a class +# > body as being a class variable. Variables should not be annotated with both +# > ClassVar and Final. (Except in a dataclass; see dataclasses_final.py.) + + +class ClassD: + VALUE1: Final = 1 + + VALUE2: ClassVar[Final] = 1 # E: Final cannot be used with ClassVar + VALUE3: Final[ClassVar] = 1 # E: Final cannot be used with ClassVar + + +ClassD.VALUE1 # OK + + +# > Final may only be used as the outermost type in assignments or variable +# > annotations. Using it in any other position is an error. In particular, +# > Final can’t be used in annotations for function arguments. + +x: list[Final[int]] = [] # E + + +def func1(x: Final[list[int]]) -> None: # E + ... + + +# > Type checkers should treat uses of a final name that was initialized with +# > a literal as if it was replaced by the literal. For example, the following +# > should be allowed: + +X: Final = "x" +Y: Final = "y" +N = NamedTuple("N", [(X, int), (Y, int)]) + +N(x=3, y=4) # OK +N(a=1) # E +N(x="", y="") # E + + +def func2() -> None: + global ID1 + + ID1 = 2 # E: cannot modify Final value + + x: Final = 3 + + x += 1 # E: cannot modify Final value + + a = (x := 4) # E: cannot modify Final value + + for x in [1, 2, 3]: # E: cannot modify Final value + pass + + with open("FileName") as x: # E: cannot modify Final value + pass + + (a, x) = (1, 2) # E: cannot modify Final value + + +# > If a module declares a ``Final`` variable and another module imports that +# > variable in an import statement by name or wildcard, the imported symbol +# > inherits the ``Final`` type qualifier. Any attempt to assign a different value +# > to this symbol should be flagged as an error by a type checker. + + +from _qualifiers_final_annotation_1 import TEN + +TEN = 9 # E: Cannot redefine Final value + +from _qualifiers_final_annotation_2 import * + +PI = 3.14159 # E: Cannot redefine Final value diff --git a/conformance/tests/qualifiers_final_decorator.py b/conformance/tests/qualifiers_final_decorator.py new file mode 100644 index 000000000..5f131f462 --- /dev/null +++ b/conformance/tests/qualifiers_final_decorator.py @@ -0,0 +1,127 @@ +""" +Tests the @final decorator. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/qualifiers.html#final + +from typing import final, overload +from _qualifiers_final_decorator import Base3, Base4 + +# > A type checker should prohibit any class decorated with @final from being +# > subclassed and any method decorated with @final from being overridden in a +# > subclass. The method decorator version may be used with all of instance +# > methods, class methods, static methods, and properties. + + +@final +class Base1: + ... + + +class Derived1(Base1): # E: Cannot inherit from final class "Base" + ... + + +class Base2: + @final + def method1(self) -> None: + pass + + @final + @classmethod + def method2(cls) -> None: + pass + + @final + @staticmethod + def method3() -> None: + pass + + # > For overloaded methods, @final should be placed on the implementation. + + @overload + def method4(self, x: int) -> int: + ... + + @overload + def method4(self, x: str) -> str: + ... + + @final + def method4(self, x: int | str) -> int | str: + return 0 + + +class Derived2(Base2): + def method1(self) -> None: # E + pass + + @classmethod # E[method2] + def method2(cls) -> None: # E[method2] + pass + + @staticmethod # E[method3] + def method3() -> None: # E[method3] + pass + + @overload # E[method4] + def method4(self, x: int) -> int: # E[method4] + ... + + @overload + def method4(self, x: str) -> str: + ... + + def method4(self, x: int | str) -> int | str: # E[method4] + return 0 + + +class Derived3(Base3): + @overload # E[Derived3] + def method(self, x: int) -> int: # E[Derived3] + ... + + @overload # E[Derived3-2] + @final # E[Derived3-2]: should be applied only to implementation + def method(self, x: str) -> str: # E[Derived3-2] + ... + + def method(self, x: int | str) -> int | str: # E[Derived3] + return 0 + + +class Derived4(Base4): + @overload # E[Derived4] + def method(self, x: int) -> int: # E[Derived4] + ... + + @overload + def method(self, x: str) -> str: + ... + + def method(self, x: int | str) -> int | str: # E[Derived4] + return 0 + + +class Base5_1: + ... + + +class Base5_2: + @final + def method(self, v: int) -> None: + ... + + +# Test multiple inheritance case. +class Derived5(Base5_1, Base5_2): + def method(self) -> None: # E + ... + + +# > It is an error to use @final on a non-method function. + + +@final # E[func]: not allowed on non-method function. +def func1() -> int: # E[func] + return 0 diff --git a/conformance/tests/specialtypes_any.py b/conformance/tests/specialtypes_any.py new file mode 100644 index 000000000..8264c8c4f --- /dev/null +++ b/conformance/tests/specialtypes_any.py @@ -0,0 +1,88 @@ +""" +Tests the typing.Any special type. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/special-types.html#any + +# > Every type is consistent with Any. + +from typing import Any, Callable, assert_type + + +val1: Any +val1 = "" # OK +val1 = 1 # OK + +val2: list[Any] +val2 = [""] # OK +val2 = [1] # OK + + +def func1(val1: Any, val2: list[Any]) -> int: + t1: int = val1 # OK + t2: str = val1 # OK + + t3: list[str] = val2 # OK + t4: list[int] = val2 # OK + + return val1 # OK + + +# > A function parameter without an annotation is assumed to be annotated with Any. + + +def func2(val): + assert_type(val, Any) + + t1: int = val # OK + t2: str = val # OK + + return 1 # OK + + +# > If a generic type is used without specifying type parameters, they are +# > assumed to be Any. + + +def func3(val1: list, val2: dict) -> None: + assert_type(val1, list[Any]) + assert_type(val2, dict[Any, Any]) + + t1: list[str] = val1 # OK + t2: list[int] = val1 # OK + + t3: dict[str, str] = val2 # OK + + +# > This rule also applies to tuple, in annotation context it is equivalent +# > to tuple[Any, ...]. + + +def func4(val1: tuple) -> None: + assert_type(val1, tuple[Any, ...]) + + t1: tuple[str, ...] = val1 # OK + t2: tuple[int, ...] = val1 # OK + + +# > As well, a bare Callable in an annotation is equivalent to Callable[..., Any]. + + +def func5(val1: Callable): + assert_type(val1, Callable[..., Any]) + + t1: Callable[[], Any] = val1 # OK + t2: Callable[[int, str], None] = val1 # OK + + +# > Any can also be used as a base class. This can be useful for avoiding type +# > checker errors with classes that can duck type anywhere or are highly dynamic. + +class ClassA(Any): + def method1(self) -> int: + return 1 + +a = ClassA() +assert_type(a.method1(), int) +assert_type(a.method2(), Any) +assert_type(ClassA.method3(), Any) diff --git a/conformance/tests/specialtypes_never.py b/conformance/tests/specialtypes_never.py new file mode 100644 index 000000000..a3ffb4cc1 --- /dev/null +++ b/conformance/tests/specialtypes_never.py @@ -0,0 +1,104 @@ +""" +Tests the handling of typing.Never and typing.NoReturn. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/special-types.html#noreturn + +import sys +from typing import Any, Generic, Never, NoReturn, TypeVar + +T = TypeVar("T") +T_co = TypeVar("T_co", covariant=True) +U = TypeVar("U") + + +def stop() -> NoReturn: + raise RuntimeError("no way") + + +def func1(x: int) -> NoReturn: # E: implicitly returns None + if x != 0: + sys.exit(1) + + +# > The checkers will also recognize that the code after calls to such functions +# > is unreachable and will behave accordingly. + +# No error on implicit None return after stop() +def func2(x: int) -> int: + if x > 0: + return x + stop() + + +# The spec previously said that NoReturn is only valid in a function return type, +# but this was removed and it should now be accepted in all of these contexts: + + +def func3( + a: NoReturn, b: list[NoReturn] +) -> None: + c: NoReturn = a + + +def func4( + a: list[NoReturn], +) -> None: + c: list[NoReturn] = a + + +def func5() -> list[NoReturn]: + return [] + + +class ClassA: + x: NoReturn + y: list[NoReturn] + + def __init__(self, x: NoReturn, y: list[NoReturn]) -> None: + self.x = x + self.y = y + + +# Never is compatible with all types. + + +def func6(a: Never): + v1: int = a # OK + v2: str = a # OK + v3: list[str] = a # OK + + +# Never is a synonym for NoReturn. + + +def func7(x: int) -> Never: + sys.exit(1) + + +# Other types are not compatible with Never except for Never (and Any). + + +def func8(a: Never, b: Any, c: list[Never]): + v1: Never = a # OK + v2: Never = b # OK + v3: list[int] = c # E + v4: Never = stop() # OK + + +class ClassB(Generic[T_co]): + pass + + +def func9(x: U) -> ClassB[U]: + # Never is a bottom type and therefore compatible with a covariant type variable. + return ClassB[Never]() # OK + + +class ClassC(Generic[T]): + pass + + +def func10(x: U) -> ClassC[U]: + # Never is not compatible in an invariant context. + return ClassC[Never]() # E diff --git a/conformance/tests/specialtypes_none.py b/conformance/tests/specialtypes_none.py new file mode 100644 index 000000000..ac4dbffa5 --- /dev/null +++ b/conformance/tests/specialtypes_none.py @@ -0,0 +1,41 @@ +""" +Tests the handling of builtins.None in a type annotation. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/special-types.html#none + +from types import NoneType +from typing import Hashable, Iterable, assert_type + + +# > When used in a type hint, the expression None is considered equivalent to type(None). + + +def func1(val1: None) -> None: + assert_type(val1, None) + t1: None = None + return None # OK + + +func1(None) # OK +func1(type(None)) # E + +# None is hashable +none1: Hashable = None # OK + +# None is not iterable +none2: Iterable = None # E: not iterable + + +None.__class__ # OK +None.__doc__ # OK +None.__eq__(0) # OK + + +def func2(val1: type[None]): + assert_type(val1, type[None]) + + +func2(None.__class__) # OK +func2(type(None)) # OK +func2(None) # E: not compatible diff --git a/conformance/tests/specialtypes_promotions.py b/conformance/tests/specialtypes_promotions.py new file mode 100644 index 000000000..fc51e1331 --- /dev/null +++ b/conformance/tests/specialtypes_promotions.py @@ -0,0 +1,16 @@ +""" +Tests "type promotions" for float and complex when they appear in annotations. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/special-types.html#special-cases-for-float-and-complex + +v1: float = 1 +v2: complex = 1.2 +v2 = 1 + + +def func1(f: float): + f.numerator # E + + if not isinstance(f, float): + f.numerator # OK diff --git a/conformance/tests/specialtypes_type.py b/conformance/tests/specialtypes_type.py new file mode 100644 index 000000000..35ef389c9 --- /dev/null +++ b/conformance/tests/specialtypes_type.py @@ -0,0 +1,175 @@ +""" +Tests the handling of builtins.type[T]. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/special-types.html#type + + +from typing import Any, Callable, Generic, Type, TypeAlias, TypeVar, assert_type + + +class User: + ... + + +class BasicUser(User): + ... + + +class ProUser(User): + ... + + +class TeamUser(User): + ... + + +def func1(user_class: type) -> User: + return user_class() + + +def func2(user_class: type[User]) -> User: + return user_class() + + +U = TypeVar("U", bound=User) + + +def func3(user_class: type[U]) -> U: + return user_class() + + +assert_type(func1(TeamUser), User) +assert_type(func2(TeamUser), User) +assert_type(func3(TeamUser), TeamUser) + + +# > Note that it is legal to use a union of classes as the parameter for type[]. + + +def func4(user_class: type[BasicUser | ProUser]) -> User: + return user_class() + + +assert_type(func4(BasicUser), User) +assert_type(func4(ProUser), User) +func4(TeamUser) # E + + +# > Any other special constructs like Callable are not allowed as an argument to type. + +T = TypeVar("T") + + +def func5(x: type[T]) -> None: + pass + + +func5(User) # OK +func5(tuple) # OK +func5(Callable) # E + + +# > When type is parameterized it requires exactly one parameter. Plain type +# > without brackets, the root of Python’s metaclass hierarchy, is equivalent to type[Any]. + +bad_type1: type[int, str] # E + + +class MyMetaA(type): + ... + + +def func6(a: type, b: type[Any]): + assert_type(a, type[Any]) + a = b # OK + b = a # OK + a = type # OK + b = type # OK + + +# > Regarding the behavior of type[Any] (or type), accessing attributes of a +# > variable with this type only provides attributes and methods defined by +# > type (for example, __repr__() and __mro__). Such a variable can be called +# > with arbitrary arguments, and the return type is Any. + + +def func7(a: type, b: type[Any], c: Type, d: Type[Any]): + assert_type(a.__mro__, tuple[type, ...]) + assert_type(a.unknown, Any) + assert_type(a.unknown(), Any) + + assert_type(b.__mro__, tuple[type, ...]) + assert_type(b.unknown, Any) + assert_type(b.unknown(), Any) + + assert_type(c.__mro__, tuple[type, ...]) + assert_type(c.unknown, Any) + assert_type(c.unknown(), Any) + + assert_type(d.__mro__, tuple[type, ...]) + assert_type(d.unknown, Any) + assert_type(d.unknown(), Any) + + +def func8(a: type[object], b: Type[object]): + assert_type(a.__name__, str) + a.unknown # E + + assert_type(b.__name__, str) + b.unknown # E + + +# > type is covariant in its parameter, because type[Derived] is a subtype of type[Base]: + + +def func9(pro_user_class: type[ProUser]): + assert_type(func3(pro_user_class), ProUser) + + +TA1: TypeAlias = Type +TA2: TypeAlias = Type[Any] +TA3: TypeAlias = type +TA4: TypeAlias = type[Any] + + +def func10(a: TA1, b: TA2, c: TA3, d: TA4): + assert_type(a, type[Any]) + assert_type(b, type[Any]) + assert_type(c, type[Any]) + assert_type(d, type[Any]) + + +TA1.unknown # E +TA2.unknown # E +TA3.unknown # E +TA4.unknown # E + +TA7: TypeAlias = type[T] +TA8: TypeAlias = Type[T] + + +def func11(t1: TA7[T]) -> T: + return t1() + + +def func12(t1: TA8[T]) -> T: + return t1() + + +assert_type(func11(int), int) +assert_type(func12(int), int) + + +def func13(v: type): + x1: Callable[..., Any] = v # OK + x2: Callable[[int, int], int] = v # OK + x3: object = v # OK + x4: type = v # OK + x5: type[int] = v # OK + x6: type[Any] = v # OK + + +class ClassA(Generic[T]): + def method1(self, v: type) -> type[T]: + return v # OK diff --git a/conformance/tests/tuples_type_compat.py b/conformance/tests/tuples_type_compat.py new file mode 100644 index 000000000..27b76921f --- /dev/null +++ b/conformance/tests/tuples_type_compat.py @@ -0,0 +1,188 @@ +""" +Tests type compatibility rules for tuples. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/tuples.html#type-compatibility-rules + +# > Because tuple contents are immutable, the element types of a tuple are covariant. + + +from typing import Any, Iterable, Never, Sequence, TypeAlias, TypeVar, assert_type + + +def func1(t1: tuple[float, complex], t2: tuple[int, int]): + v1: tuple[float, complex] = t2 # OK + v2: tuple[int, int] = t1 # E + + +# > A homogeneous tuple of arbitrary length is equivalent +# > to a union of tuples of different lengths. + + +def func2(t1: tuple[int], t2: tuple[int, *tuple[int, ...]], t3: tuple[int, ...]): + v1: tuple[int, ...] + v1 = t1 # OK + v1 = t2 # OK + + v2: tuple[int, *tuple[int, ...]] + v2 = t1 # OK + v2 = t3 # E + + v3: tuple[int] + v3 = t2 # E + v3 = t3 # E + + +# > The type ``tuple[Any, ...]`` is bidirectionally compatible with any tuple:: + + +def func3(t1: tuple[int], t2: tuple[int, ...], t3: tuple[Any, ...]): + v1: tuple[int, ...] = t1 # OK + v2: tuple[Any, ...] = t1 # OK + + v3: tuple[int] = t2 # E + v4: tuple[Any, ...] = t2 # OK + + v5: tuple[float, float] = t3 # OK + v6: tuple[int, *tuple[str, ...]] = t3 # OK + + +from missing_module import SomeType # type: ignore + + +def func4( + untyped_iter: Iterable[Any], + some_type_tuple: tuple[SomeType, ...], + any_tuple: tuple[Any, ...], + int_tuple: tuple[int, ...], +): + a: tuple[int, int] = tuple(untyped_iter) # OK + b: tuple[int, int] = some_type_tuple # OK + c: tuple[int, int] = any_tuple # OK + d: tuple[int, int] = int_tuple # E + + +# > The length of a tuple at runtime is immutable, so it is safe for type +# > checkers to use length checks to narrow the type of a tuple + +# NOTE: This type narrowing functionality is optional, not mandated. + +Func5Input: TypeAlias = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int] + +def func5(val: Func5Input): + if len(val) == 1: + # Type can be narrowed to tuple[int]. + assert_type(val, tuple[int]) # E[func5_1] + assert_type(val, Func5Input) # E[func5_1] + + if len(val) == 2: + # Type can be narrowed to tuple[str, str] | tuple[int, int]. + assert_type(val, tuple[str, str] | tuple[int, int]) # E[func5_2] + assert_type(val, Func5Input) # E[func5_2] + + if len(val) == 3: + # Type can be narrowed to tuple[int, str, int]. + assert_type(val, tuple[int, str, int]) # E[func5_3] + assert_type(val, Func5Input) # E[func5_3] + + +# > This property may also be used to safely narrow tuple types within a match +# > statement that uses sequence patterns. + +# NOTE: This type narrowing functionality is optional, not mandated. + +Func6Input: TypeAlias = tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int] + + +def func6(val: Func6Input): + match val: + case (x,): + # Type may be narrowed to tuple[int]. + assert_type(val, tuple[int]) # E[func6_1] + assert_type(val, Func6Input) # E[func6_1] + + case (x, y): + # Type may be narrowed to tuple[str, str] | tuple[int, int]. + assert_type(val, tuple[str, str] | tuple[int, int]) # E[func6_2] + assert_type(val, Func6Input) # E[func6_2] + + case (x, y, z): + # Type may be narrowed to tuple[int, str, int]. + assert_type(val, tuple[int, str, int]) # E[func6_3] + assert_type(val, Func6Input) # E[func6_3] + + +# > Type checkers may safely use this equivalency rule (tuple expansion) +# > when narrowing tuple types + +# NOTE: This type narrowing functionality is optional, not mandated. + +Func7Input: TypeAlias = tuple[int | str, int | str] + + +def func7(subj: Func7Input): + match subj: + case x, str(): + assert_type(subj, tuple[int | str, str]) # E[func7_1] + assert_type(subj, Func7Input) # E[func7_1] + case y: + assert_type(subj, tuple[int | str, int]) # E[func7_2] + assert_type(subj, Func7Input) # E[func7_2] + + +# > The tuple class derives from Sequence[T_co] where ``T_co`` is a covariant +# (non-variadic) type variable. The specialized type of ``T_co`` should be +# computed by a type checker as a a supertype of all element types. + +# > A zero-length tuple (``tuple[()]``) is a subtype of ``Sequence[Never]``. + +T = TypeVar("T") + + +def test_seq(x: Sequence[T]) -> Sequence[T]: + return x + + +def func8( + t1: tuple[complex, list[int]], t2: tuple[int, *tuple[str, ...]], t3: tuple[()] +): + assert_type( + test_seq(t1), Sequence[complex | list[int]] + ) # Could be Sequence[object] + assert_type(test_seq(t2), Sequence[int | str]) # Could be Sequence[object] + assert_type(test_seq(t3), Sequence[Never]) + + +t1: tuple[int, *tuple[str]] = (1, "") # OK +t1 = (1, "", "") # E + +t2: tuple[int, *tuple[str, ...]] = (1,) # OK +t2 = (1, "") # OK +t2 = (1, "", "") # OK +t2 = (1, 1, "") # E +t2 = (1, "", 1) # E + + +t3: tuple[int, *tuple[str, ...], int] = (1, 2) # OK +t3 = (1, "", 2) # OK +t3 = (1, "", "", 2) # OK +t3 = (1, "", "") # E +t3 = (1, "", "", 1.2) # E + +t4: tuple[*tuple[str, ...], int] = (1,) # OK +t4 = ("", 1) # OK +t4 = ("", "", 1) # OK +t4 = (1, "", 1) # E +t4 = ("", "", 1.2) # E + + +def func9(a: tuple[str, str]): + t1: tuple[str, str, *tuple[int, ...]] = a # OK + t2: tuple[str, str, *tuple[int]] = a # E + t3: tuple[str, *tuple[str, ...]] = a # OK + t4: tuple[str, str, *tuple[str, ...]] = a # OK + t5: tuple[str, str, str, *tuple[str, ...]] = a # E + t6: tuple[str, *tuple[int, ...], str] = a # OK + t7: tuple[*tuple[str, ...], str] = a # OK + t8: tuple[*tuple[str, ...], str] = a # OK + t9: tuple[*tuple[str, ...], str, str, str] = a # E diff --git a/conformance/tests/tuples_type_form.py b/conformance/tests/tuples_type_form.py new file mode 100644 index 000000000..eb83de022 --- /dev/null +++ b/conformance/tests/tuples_type_form.py @@ -0,0 +1,45 @@ +""" +Tests the handling of builtins.tuple. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/tuples.html#tuple-type-form + +# > The type of a tuple can be expressed by listing the element types. +from typing import Literal + + +t1: tuple[int] = (1,) # OK +t1 = (1, 2) # E +t2: tuple[int, int] = (1, 2) # OK +t2 = (1,) # E +t2 = (1, "") # E + + +def func1() -> tuple[Literal[1], Literal[2]]: + return (1, 2) + + +# > The empty tuple can be typed as tuple[()]. + +t10: tuple[()] = () # OK +t10 = (1,) # E + + +def func2() -> list[tuple[()]]: + return [(), (), ()] + + +# > Arbitrary-length homogeneous tuples can be expressed using one type and ellipsis. +t20: tuple[int, ...] = () # OK +t20 = (1,) # OK +t20 = (1, 2, 3, 4) # OK +t20 = (1, 2, 3, "") # E + + +t30: tuple[int, ...] # OK +t31: tuple[int, int, ...] # E +t32: tuple[...] # E +t33: tuple[..., int] # E +t34: tuple[int, ..., int] # E +t35: tuple[*tuple[str], ...] # E +t36: tuple[*tuple[str, ...], ...] # E diff --git a/conformance/tests/tuples_unpacked.py b/conformance/tests/tuples_unpacked.py new file mode 100644 index 000000000..706b58f83 --- /dev/null +++ b/conformance/tests/tuples_unpacked.py @@ -0,0 +1,62 @@ +""" +Tests the handling of unpacked tuples embedded within other tuples. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/tuples.html#unpacked-tuple-form + +# > An unpacked form of ``tuple`` (using an unpack operator ``*``) +# > can be used within a tuple type argument list + +# > Unpacking a concrete tuple type is analogous to unpacking a tuple of values at runtime. + + +from typing import TypeVarTuple, Unpack, assert_type + + +def func1(x: tuple[int, *tuple[bool, bool], str]): + assert_type(x, tuple[int, bool, bool, str]) + assert_type(x, tuple[*tuple[int, bool], bool, str]) + assert_type(x, tuple[int, bool, *tuple[bool, str]]) + + +# > Unpacking an unbounded tuple preserves the unbounded tuple as it is. + + +def func2(x: tuple[int, *tuple[bool, ...], str]): + assert_type(x, tuple[int, *tuple[bool, ...], str]) + + +# > For example, tuple[int, *tuple[str]] is equivalent to tuple[int, str]. + +u1: tuple[*tuple[int], *tuple[int]] = (int(1), int(1)) # OK +assert_type(u1, tuple[int, int]) +u2: tuple[*tuple[int, ...], *tuple[int]] # OK + + +# > Only one unbounded tuple can be used within another tuple: + +t1: tuple[*tuple[str], *tuple[str]] # OK +t2: tuple[*tuple[str, *tuple[str, ...]]] # OK +t3: tuple[*tuple[str, ...], *tuple[int, ...]] # E +t4: tuple[*tuple[str, *tuple[str, ...]], *tuple[int, ...]] # E + + +# > An unpacked TypeVarTuple counts as an unbounded tuple in the context of this rule + +Ts = TypeVarTuple("Ts") + + +def func3(t: tuple[*Ts]): + t5: tuple[*tuple[str], *Ts] # OK + t6: tuple[*tuple[str, ...], *Ts] # E + + +# > The ``*`` syntax requires Python 3.11 or newer. For older versions of Python, +# > the ``typing.Unpack`` special form can be used: + +t11: tuple[Unpack[tuple[str]], Unpack[tuple[str]]] # OK +t12: tuple[Unpack[tuple[str, Unpack[tuple[str, ...]]]]] # OK +t13: tuple[Unpack[tuple[str, ...]], Unpack[tuple[int, ...]]] # E +t14: tuple[ # E[t14] + Unpack[tuple[str, Unpack[tuple[str, ...]]]], Unpack[tuple[int, ...]] # E[t14] +] diff --git a/conformance/tests/ty.toml b/conformance/tests/ty.toml new file mode 100644 index 000000000..986f5e429 --- /dev/null +++ b/conformance/tests/ty.toml @@ -0,0 +1,11 @@ +[environment] +python-version = "3.12" + +[rules] +deprecated = "error" +invalid-legacy-positional-parameter = "error" +redundant-final-classvar = "error" +assert-type-unspellable-subtype = "ignore" +invalid-enum-member-annotation = "error" +mismatched-type-name = "error" +invalid-named-tuple-override = "error" diff --git a/conformance/tests/typeddicts_alt_syntax.py b/conformance/tests/typeddicts_alt_syntax.py new file mode 100644 index 000000000..93d1b4334 --- /dev/null +++ b/conformance/tests/typeddicts_alt_syntax.py @@ -0,0 +1,45 @@ +""" +Tests the "alternative" (non-class) syntax for defining a TypedDict. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#alternative-syntax + +from typing import TypedDict + + +Movie = TypedDict("Movie", {"name": str, "year": int, "illegal key name": bool}) + +movie: Movie = {"name": "Blade Runner", "year": 1982, "illegal key name": True} + +MovieOptional = TypedDict("MovieOptional", {"name": str, "year": int}, total=False) + +movie_opt1: MovieOptional = {} +movie_opt2: MovieOptional = {"year": 1982} + +# > A type checker is only expected to accept a dictionary display expression as +# > the second argument to TypedDict. In particular, a variable that refers to a +# > dictionary object does not need to be supported, to simplify implementation. +my_dict = {"name": str} +BadTypedDict1 = TypedDict("BadTypedDict1", my_dict) # E + + +# This should generate an error because it uses a non-str key. +BadTypedDict2 = TypedDict("BadTypedDict2", {1: str}) # E + + +# This should generate an error because it uses a non-matching name. +BadTypedDict3 = TypedDict("WrongName", {"name": str}) # E + + +# This should generate an error because of the additional parameter. +BadTypedDict4 = TypedDict("BadTypedDict4", {"name": str}, total=False, other=False) # E + + +# > The keyword-argument syntax is deprecated in 3.11 and will be removed in 3.13. +# > It may also be unsupported by static type checkers. + +Movie2 = TypedDict("Movie2", name=str, year=int) # E? + +movie2: Movie2 +movie2 = {"name": "Blade Runner", "year": 1982} # E?: May generate errors if keyword-argument syntax is unsupported +movie2 = {"name": "Blade Runner", "year": ""} # E?: Incorrect type for year diff --git a/conformance/tests/typeddicts_class_syntax.py b/conformance/tests/typeddicts_class_syntax.py new file mode 100644 index 000000000..923bf1552 --- /dev/null +++ b/conformance/tests/typeddicts_class_syntax.py @@ -0,0 +1,92 @@ +""" +Tests the class syntax for defining a TypedDict. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#typeddict + +from typing import Generic, TypeVar, TypedDict +import sys + + +class Movie(TypedDict): + name: str + year: int + + # > String literal forward references are valid in the value types. + director: "Person" + + +class Person(TypedDict): + name: str + age: int + + +# > Methods are not allowed, since the runtime type of a TypedDict object +# > will always be just dict (it is never a subclass of dict). +class BadTypedDict1(TypedDict): + name: str + + # Methods are not allowed, so this should generate an error. + def method1(self): # E + pass + + # Methods are not allowed, so this should generate an error. + @classmethod # E[method2] + def method2(cls): # E[method2] + pass + + # Methods are not allowed, so this should generate an error. + @staticmethod # E[method3] + def method3(): # E[method3] + pass + + +# > Specifying a metaclass is not allowed. +class BadTypedDict2(TypedDict, metaclass=type): # E + name: str + + +# This should generate an error because "other" is not an allowed keyword argument. +class BadTypedDict3(TypedDict, other=True): # E + name: str + +# > `if` conditions that the type checker is able to +# > statically evaluate are also permitted (e.g. `if sys.version_info > (3, 14)`), +# > in order to specify items that exist only under the given conditions. +class ConditionalField(TypedDict): + x: int + if sys.version_info >= (3, 12): + y: int + if sys.version_info >= (4, 0): + z: int + +# The conformance suite runs type checkers configured to Python 3.12 or later: +ConditionalField(x=1, y=2) +ConditionalField(x=1, y=2, z=3) # E + + +# > TypedDicts may be made generic by adding Generic[T] among the bases. +T = TypeVar("T") + + +class GenericTypedDict(TypedDict, Generic[T]): + name: str + value: T + + +# > An empty TypedDict can be created by only including pass in the +# > body (if there is a docstring, pass can be omitted): +class EmptyDict1(TypedDict): + pass + + +class EmptyDict2(TypedDict): + """Docstring""" + + +class MovieTotal(TypedDict, total=True): + name: str + + +class MovieOptional(TypedDict, total=False): + name: str diff --git a/conformance/tests/typeddicts_extra_items.py b/conformance/tests/typeddicts_extra_items.py new file mode 100644 index 000000000..20354bb8a --- /dev/null +++ b/conformance/tests/typeddicts_extra_items.py @@ -0,0 +1,356 @@ +# pyright: enableExperimentalFeatures=true +from collections.abc import Mapping +from typing_extensions import ReadOnly, TypedDict, Unpack +from typing import assert_type, Required, NotRequired, Never + + +# > For a TypedDict type that specifies +# > ``extra_items``, during construction, the value type of each unknown item +# is expected to be non-required and assignable to the ``extra_items`` argument. + +class Movie(TypedDict, extra_items=bool): + name: str + +a: Movie = {"name": "Blade Runner", "novel_adaptation": True} # OK +b: Movie = {"name": "Blade Runner", "year": 1982} # E: 'int' is not assignable to 'bool' + +# > The alternative inline syntax is also supported:: + +MovieFunctional = TypedDict("MovieFunctional", {"name": str}, extra_items=bool) + +c: MovieFunctional = {"name": "Blade Runner", "novel_adaptation": True} # OK +d: MovieFunctional = {"name": "Blade Runner", "year": 1982} # E: 'int' is not assignable to 'bool' + +# > Accessing extra items is allowed. Type checkers must infer their value type from +# > the ``extra_items`` argument + +def movie_keys(movie: Movie) -> None: + assert_type(movie["name"], str) + assert_type(movie["novel_adaptation"], bool) + +# > ``extra_items`` is inherited through subclassing + +class MovieBase(TypedDict, extra_items=ReadOnly[int | None]): + name: str + +class InheritedMovie(MovieBase): + year: int + +e: InheritedMovie = {"name": "Blade Runner", "year": None} # E: 'None' is incompatible with 'int' +f: InheritedMovie = { + "name": "Blade Runner", + "year": 1982, + "other_extra_key": None, +} # OK + +# > Similar to ``total``, only a literal ``True`` or ``False`` is supported as the +# > value of the ``closed`` argument. Type checkers should reject any non-literal value. + +class IllegalTD(TypedDict, closed=42 == 42): # E: Argument to "closed" must be a literal True or False + name: str + +# > Passing ``closed=False`` explicitly requests the default TypedDict behavior, +# > where arbitrary other keys may be present and subclasses may add arbitrary items. + +class BaseTD(TypedDict, closed=False): + name: str + +class ChildTD(BaseTD): # OK + age: int + +# > It is a type checker error to pass ``closed=False`` if a superclass has +# > ``closed=True`` or sets ``extra_items``. + +class ClosedBase(TypedDict, closed=True): + name: str + +class IllegalChild1(ClosedBase, closed=False): # E: Cannot set 'closed=False' when superclass is 'closed=True' + pass + +class ExtraItemsBase(TypedDict, extra_items=int): + name: str + +class IllegalChild2(ExtraItemsBase, closed=False): # E: Cannot set 'closed=False' when superclass has 'extra_items' + pass + +# > If ``closed`` is not provided, the behavior is inherited from the superclass. +# > If the superclass is TypedDict itself or the superclass does not have ``closed=True`` +# > or the ``extra_items`` parameter, the previous TypedDict behavior is preserved: +# > arbitrary extra items are allowed. If the superclass has ``closed=True``, the +# > child class is also closed. + +class BaseMovie(TypedDict, closed=True): + name: str + +class MovieA(BaseMovie): # OK, still closed + pass + +class MovieB(BaseMovie, closed=True): # OK, but redundant + pass + +class MovieC(MovieA): # E[MovieC] + age: int # E[MovieC]: "MovieC" is a closed TypedDict; extra key "age" not allowed + +class MovieD(MovieB): # E[MovieD] + age: int # E[MovieD]: "MovieD" is a closed TypedDict; extra key "age" not allowed + +# > It is possible to use ``closed=True`` when subclassing if the ``extra_items`` +# > argument is a read-only type. + +class MovieES(TypedDict, extra_items=ReadOnly[str]): + pass + +class MovieClosed(MovieES, closed=True): # OK + pass + +class MovieNever(MovieES, extra_items=Never): # OK, but 'closed=True' is preferred + pass + +class IllegalCloseNonReadOnly(ExtraItemsBase, closed=True): # E: Cannot set 'closed=True' when superclass has non-read-only 'extra_items' + pass + +# > It is an error to use ``Required[]`` or ``NotRequired[]`` with ``extra_items``. + +class IllegalExtraItemsTD(TypedDict, extra_items=Required[int]): # E: 'extra_items' value cannot be 'Required[...]' + name: str + +class AnotherIllegalExtraItemsTD(TypedDict, extra_items=NotRequired[int]): # E: 'extra_items' value cannot be 'NotRequired[...]' + name: str + +# > The extra items are non-required, regardless of the totality of the +# > TypedDict. Operations that are available to ``NotRequired`` items should also be available to the +# > extra items. + +class MovieEI(TypedDict, extra_items=int): + name: str + +def del_items(movie: MovieEI) -> None: + del movie["name"] # E: The value type of 'name' is 'Required[str]' + del movie["year"] # OK: The value type of 'year' is 'NotRequired[int]' + +# > For type checking purposes, ``Unpack[SomeTypedDict]`` with extra items should be +# > treated as its equivalent in regular parameters. + +class MovieNoExtra(TypedDict): + name: str + +class MovieExtra(TypedDict, extra_items=int): + name: str + +def unpack_no_extra(**kwargs: Unpack[MovieNoExtra]) -> None: ... +def unpack_extra(**kwargs: Unpack[MovieExtra]) -> None: ... + +unpack_no_extra(name="No Country for Old Men", year=2007) # E?: Unrecognized item +unpack_extra(name="No Country for Old Men", year=2007) # OK + +# > Notably, if the TypedDict type specifies ``extra_items`` to be read-only, +# > subclasses of the TypedDict type may redeclare ``extra_items``. + +class ReadOnlyBase(TypedDict, extra_items=ReadOnly[int]): + pass + +class ReadOnlyChild(ReadOnlyBase, extra_items=ReadOnly[bool]): # OK + pass + +class MutableChild(ReadOnlyBase, extra_items=int): # OK + pass + +# > Because a non-closed TypedDict type implicitly allows non-required extra items +# > of value type ``ReadOnly[object]``, its subclass can override the +# > ``extra_items`` argument with more specific types. + +class NonClosedBase(TypedDict): + name: str + +class SpecificExtraItems(NonClosedBase, extra_items=bytes): # OK + year: int + +# > First, it is not allowed to change the value of ``extra_items`` in a subclass +# > unless it is declared to be ``ReadOnly`` in the superclass. + +class Parent(TypedDict, extra_items=int | None): + pass + +class Child(Parent, extra_items=int): # E: Cannot change 'extra_items' type unless it is 'ReadOnly' in the superclass + pass + +# > Second, ``extra_items=T`` effectively defines the value type of any unnamed +# > items accepted to the TypedDict and marks them as non-required. Thus, the above +# > restriction applies to any additional items defined in a subclass. + +class MovieBase2(TypedDict, extra_items=int | None): + name: str + +class MovieRequiredYear(MovieBase2): # E[MovieRequiredYear]: Required key 'year' is not known to 'MovieBase2' + year: int | None # E[MovieRequiredYear] + +class MovieNotRequiredYear(MovieBase2): # E[MovieNotRequiredYear]: 'int | None' is not consistent with 'int' + year: NotRequired[int] # E[MovieNotRequiredYear] + +class MovieWithYear(MovieBase2): # OK + year: NotRequired[int | None] + +class BookBase(TypedDict, extra_items=ReadOnly[int | None]): + name: str + +class BookWithPublisher(BookBase): # E[BookWithPublisher]: 'str' is not assignable to 'int | None' + publisher: str # E[BookWithPublisher] + +# > Let ``S`` be the set of keys of the explicitly defined items on a TypedDict +# > type. If it specifies ``extra_items=T``, the TypedDict type is considered to +# > have an infinite set of items that all satisfy the following conditions. +# > - If ``extra_items`` is read-only: +# > - The key's value type is :term:`assignable` to ``T``. +# > - The key is not in ``S``. +# > - If ``extra_items`` is not read-only: +# > - The key is non-required. +# > - The key's value type is :term:`consistent` with ``T``. +# > - The key is not in ``S``. + +class MovieDetails(TypedDict, extra_items=int | None): + name: str + year: NotRequired[int] + +details2: MovieDetails = {"name": "Kill Bill Vol. 1", "year": 2003} +movie2: MovieBase2 = details2 # E: While 'int' is not consistent with 'int | None' + +class MovieWithYear2(TypedDict, extra_items=int | None): + name: str + year: int | None + +details3: MovieWithYear2 = {"name": "Kill Bill Vol. 1", "year": 2003} +movie3: MovieBase2 = details3 # E: 'year' is not required in 'MovieBase2', but it is required in 'MovieWithYear2' + +# > When ``extra_items`` is specified to be read-only on a TypedDict type, it is +# > possible for an item to have a :term:`narrower ` type than the +# > ``extra_items`` argument. + +class MovieSI(TypedDict, extra_items=ReadOnly[str | int]): + name: str + +class MovieDetails4(TypedDict, extra_items=int): + name: str + year: NotRequired[int] + +class MovieDetails5(TypedDict, extra_items=int): + name: str + actors: list[str] + +details4: MovieDetails4 = {"name": "Kill Bill Vol. 2", "year": 2004} +details5: MovieDetails5 = {"name": "Kill Bill Vol. 2", "actors": ["Uma Thurman"]} +movie4: MovieSI = details4 # OK. 'int' is assignable to 'str | int'. +movie5: MovieSI = details5 # E: 'list[str]' is not assignable to 'str | int'. + +# > ``extra_items`` as a pseudo-item follows the same rules that other items have, +# > so when both TypedDicts types specify ``extra_items``, this check is naturally +# > enforced. + +class MovieExtraInt(TypedDict, extra_items=int): + name: str + +class MovieExtraStr(TypedDict, extra_items=str): + name: str + +extra_int: MovieExtraInt = {"name": "No Country for Old Men", "year": 2007} +extra_str: MovieExtraStr = {"name": "No Country for Old Men", "description": ""} +extra_int = extra_str # E: 'str' is not assignable to extra items type 'int' +extra_str = extra_int # E: 'int' is not assignable to extra items type 'str' + +# > A non-closed TypedDict type implicitly allows non-required extra keys of value +# > type ``ReadOnly[object]``. Applying the assignability rules between this type +# > and a closed TypedDict type is allowed. + +class MovieNotClosed(TypedDict): + name: str + +extra_int2: MovieExtraInt = {"name": "No Country for Old Men", "year": 2007} +not_closed: MovieNotClosed = {"name": "No Country for Old Men"} +extra_int2 = not_closed # E: 'extra_items=ReadOnly[object]' implicitly on 'MovieNotClosed' is not assignable to with 'extra_items=int' +not_closed = extra_int2 # OK + +# > TypedDicts that allow extra items of type ``T`` also allow arbitrary keyword +# > arguments of this type when constructed by calling the class object. + +class NonClosedMovie(TypedDict): + name: str + +NonClosedMovie(name="No Country for Old Men") # OK +NonClosedMovie(name="No Country for Old Men", year=2007) # E: Unrecognized item + +class ExtraMovie(TypedDict, extra_items=int): + name: str + +ExtraMovie(name="No Country for Old Men") # OK +ExtraMovie(name="No Country for Old Men", year=2007) # OK +ExtraMovie(name="No Country for Old Men", language="English") # E: Wrong type for extra item 'language' + +# This implies 'extra_items=Never', +# so extra keyword arguments would produce an error +class ClosedMovie(TypedDict, closed=True): + name: str + +ClosedMovie(name="No Country for Old Men") # OK +ClosedMovie(name="No Country for Old Men", year=2007) # E: Extra items not allowed + +# > A TypedDict type is :term:`assignable` to a type of the form ``Mapping[str, VT]`` +# > when all value types of the items in the TypedDict +# > are assignable to ``VT``. + +extra_str3: MovieExtraStr = {"name": "Blade Runner", "summary": ""} +str_mapping: Mapping[str, str] = extra_str3 # OK + +extra_int3: MovieExtraInt = {"name": "Blade Runner", "year": 1982} +int_mapping: Mapping[str, int] = extra_int3 # E: 'int | str' is not assignable with 'int' +int_str_mapping: Mapping[str, int | str] = extra_int3 # OK + +# > Type checkers should infer the precise signatures of ``values()`` and ``items()`` +# > on such TypedDict types. + +def foo(movie: MovieExtraInt) -> None: + assert_type(list(movie.items()), list[tuple[str, int | str]]) + assert_type(list(movie.values()), list[int | str]) + +# > The TypedDict type is :term:`assignable` to ``dict[str, VT]`` if all +# > items on the TypedDict type satisfy the following conditions: +# > - The value type of the item is :term:`consistent` with ``VT``. +# > - The item is not read-only. +# > - The item is not required. + +class IntDict(TypedDict, extra_items=int): + pass + +class IntDictWithNum(IntDict): + num: NotRequired[int] + +def clear_intdict(x: IntDict) -> None: + v: dict[str, int] = x # OK + v.clear() # OK + +not_required_num_dict: IntDictWithNum = {"num": 1, "bar": 2} +regular_dict: dict[str, int] = not_required_num_dict # OK +clear_intdict(not_required_num_dict) # OK + +# > In this case, methods that are previously unavailable on a TypedDict are allowed, +# > with signatures matching ``dict[str, VT]`` +# > (e.g.: ``__setitem__(self, key: str, value: VT) -> None``). + +not_required_num_dict.clear() # OK + +assert_type(not_required_num_dict.popitem(), tuple[str, int]) + +def nrnd(not_required_num_dict: IntDictWithNum, key: str): + not_required_num_dict[key] = 42 # OK + del not_required_num_dict[key] # OK + +# > ``dict[str, VT]`` is not assignable to a TypedDict type, +# > because such dict can be a subtype of dict. + +class CustomDict(dict[str, int]): + pass + +def might_not_be(might_not_be_a_builtin_dict: dict[str, int]): + int_dict: IntDict = might_not_be_a_builtin_dict # E + print(int_dict) + +not_a_builtin_dict = CustomDict({"num": 1}) +might_not_be(not_a_builtin_dict) diff --git a/conformance/tests/typeddicts_final.py b/conformance/tests/typeddicts_final.py new file mode 100644 index 000000000..1cd08f2f9 --- /dev/null +++ b/conformance/tests/typeddicts_final.py @@ -0,0 +1,26 @@ +""" +Tests the use of Final values when used with TypedDicts. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#use-of-final-values-and-literal-types + +from typing import Final, Literal, TypedDict + + +class Movie(TypedDict): + name: str + year: int + + +# > Type checkers should allow final names (PEP 591) with string values to be +# > used instead of string literals in operations on TypedDict objects. +YEAR: Final = "year" + +m: Movie = {"name": "Alien", "year": 1979} +years_since_epoch = m[YEAR] - 1970 + + +# > An expression with a suitable literal type (PEP 586) can be used instead of +# > a literal value. +def get_value(movie: Movie, key: Literal["year", "name"]) -> int | str: + return movie[key] diff --git a/conformance/tests/typeddicts_inheritance.py b/conformance/tests/typeddicts_inheritance.py new file mode 100644 index 000000000..da5e54666 --- /dev/null +++ b/conformance/tests/typeddicts_inheritance.py @@ -0,0 +1,66 @@ +""" +Tests inheritance rules for TypedDict classes. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#typeddict + +from typing import TypedDict + + +class Movie(TypedDict): + name: str + year: int + +class BookBasedMovie(Movie): + based_on: str + +class BookBasedMovieAlso(TypedDict): + name: str + year: int + based_on: str + +b1: BookBasedMovie = {"name": "Little Women", "year": 2019, "based_on": "Little Women"} + +b2: BookBasedMovieAlso = b1 + + +class X(TypedDict): + x: int + +class Y(TypedDict): + y: str + +class XYZ(X, Y): + z: bool + +x1 = XYZ(x=1, y="", z=True) + +# > A TypedDict cannot inherit from both a TypedDict type and a +# > non-TypedDict base class other than Generic. + +class NonTypedDict: + pass + +class BadTypedDict(TypedDict, NonTypedDict): # E + pass + + +# > Changing a field type of a parent TypedDict class in a subclass is +# > not allowed. + +class X1(TypedDict): + x: str + +class Y1(X1): # E[Y1] + x: int # E[Y1]: cannot overwrite TypedDict field "x" + + +# > Multiple inheritance does not allow conflict types for the same name field: +class X2(TypedDict): + x: int + +class Y2(TypedDict): + x: str + +class XYZ2(X2, Y2): # E: cannot overwrite TypedDict field "x" while merging + xyz: bool diff --git a/conformance/tests/typeddicts_operations.py b/conformance/tests/typeddicts_operations.py new file mode 100644 index 000000000..f334f9568 --- /dev/null +++ b/conformance/tests/typeddicts_operations.py @@ -0,0 +1,64 @@ +""" +Tests operations provided by a TypedDict object. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#supported-and-unsupported-operations + + +from typing import TypedDict, assert_type + + +class Movie(TypedDict): + name: str + year: int + + +movie: Movie + +movie = {"name": "Blade Runner", "year": 1982} +movie["name"] = "Other" +movie["year"] = 1981 + +movie["name"] = 1982 # E: wrong type +movie["year"] = "" # E: wrong type +movie["other"] = "" # E: unknown key added + +print(movie["other"]) # E: unknown key referenced + +movie = {"name": "Blade Runner"} # E: year is missing +movie = {"name": "Blade Runner", "year": 1982.1} # E: year is wrong type + +# > The use of a key that is not known to exist should be reported as an error. +movie = {"name": "", "year": 1900, "other": 2} # E: extra key + + +def func1(variable_key: str): + # > A key that is not a literal should generally be rejected. + movie: Movie = {variable_key: "", "year": 1900} # E: variable key + + +# It's not clear from the spec what type this should be. +movie.get("name") + +# It's not clear from the spec what type this should be. +movie.get("other") # E? + + +movie.clear() # E: clear not allowed + +del movie["name"] # E: del not allowed for required key + + + +class MovieOptional(TypedDict, total=False): + name: str + year: int + + +movie_optional: MovieOptional = {} + +assert_type(movie_optional.get("name"), str | None) + +movie_optional.clear() # E: clear not allowed + +del movie_optional["name"] diff --git a/conformance/tests/typeddicts_readonly.py b/conformance/tests/typeddicts_readonly.py new file mode 100644 index 000000000..d7e0e84c7 --- /dev/null +++ b/conformance/tests/typeddicts_readonly.py @@ -0,0 +1,61 @@ +""" +Tests typing.ReadOnly qualifier introduced in PEP 705. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#read-only-items + +from typing import Annotated, NotRequired, Required, TypedDict +from typing_extensions import ReadOnly + + +# > The ``typing.ReadOnly`` type qualifier is used to indicate that an item +# > declared in a ``TypedDict`` definition may not be mutated (added, modified, +# > or removed). + + +class Band(TypedDict): + name: str + members: ReadOnly[list[str]] + + +b1: Band = {"name": "blur", "members": []} + +b1["name"] = "Blur" # OK +b1["members"] = ["Damon Albarn"] # E +b1["members"].append("Damon Albarn") # OK + + +# > The :pep:`alternative functional syntax <589#alternative-syntax>` for +# > TypedDict also supports the new type qualifier. + +Band2 = TypedDict("Band2", {"name": str, "members": ReadOnly[list[str]]}) + +b2: Band2 = {"name": "blur", "members": []} + +b2["name"] = "Blur" # OK +b2["members"] = ["Damon Albarn"] # E +b2["members"].append("Damon Albarn") # OK + + +# > ``ReadOnly[]`` can be used with ``Required[]``, ``NotRequired[]`` and +# > ``Annotated[]``, in any nesting order. + + +class Movie1(TypedDict): + title: ReadOnly[Required[str]] # OK + year: ReadOnly[NotRequired[Annotated[int, ""]]] # OK + + +m1: Movie1 = {"title": "", "year": 1991} +m1["title"] = "" # E +m1["year"] = 1992 # E + + +class Movie2(TypedDict): + title: Required[ReadOnly[str]] # OK + year: Annotated[NotRequired[ReadOnly[int]], ""] # OK + + +m2: Movie2 = {"title": "", "year": 1991} +m2["title"] = "" # E +m2["year"] = 1992 # E diff --git a/conformance/tests/typeddicts_readonly_consistency.py b/conformance/tests/typeddicts_readonly_consistency.py new file mode 100644 index 000000000..070b5b31a --- /dev/null +++ b/conformance/tests/typeddicts_readonly_consistency.py @@ -0,0 +1,85 @@ +""" +Tests type consistency rules for TypedDict with ReadOnly items. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#read-only-items + +from typing import Any, Collection, NotRequired, Required, TypedDict +from typing_extensions import ReadOnly + +# > A TypedDict type ``A`` is consistent with TypedDict ``B`` if ``A`` is +# > structurally compatible with ``B``. This is true if and only if all of +# > the following are satisfied: + +# > For each item in ``B``, ``A`` has the corresponding key, unless the item +# > in ``B`` is read-only, not required, and of top value type +# > (``ReadOnly[NotRequired[object]]``). + + +class A1(TypedDict): + x: Required[int] + + +class B1(TypedDict): + x: Required[int] + y: NotRequired[str] + + +class C1(TypedDict): + x: Required[int] + y: ReadOnly[NotRequired[str]] + + +def func1(a: A1, b: B1, c: C1): + v1: A1 = b # OK + v2: A1 = c # OK + + v3: B1 = a # E + v4: B1 = c # E + + v5: C1 = a # E + v6: C1 = b # OK + + +# > For each item in ``B``, if ``A`` has the corresponding key, the +# > corresponding value type in ``A`` is consistent with the value type in ``B``. + +# (This is the same rule that applies to TypedDicts without read-only items, +# so we will skip this redundant test.) + +# > For each non-read-only item in ``B``, its value type is consistent with +# > the corresponding value type in ``A``. + +# (This is the same rule that applies to TypedDicts without read-only items, +# so we will skip this redundant test.) + +# > For each required key in ``B``, the corresponding key is required in ``A``. + +# (This is the same rule that applies to TypedDicts without read-only items, +# so we will skip this redundant test.) + +# > For each non-required key in ``B``, if the item is not read-only in ``B``, +# > the corresponding key is not required in ``A``. + + +class A2(TypedDict): + x: NotRequired[ReadOnly[str]] + + +class B2(TypedDict): + x: NotRequired[str] + + +class C2(TypedDict): + x: Required[str] + + +def func2(a: A2, b: B2, c: C2): + v1: A2 = b # OK + v2: A2 = c # OK + + v3: B2 = a # E + v4: B2 = c # E + + v5: C2 = a # E + v6: C2 = b # E diff --git a/conformance/tests/typeddicts_readonly_inheritance.py b/conformance/tests/typeddicts_readonly_inheritance.py new file mode 100644 index 000000000..4ba2e4389 --- /dev/null +++ b/conformance/tests/typeddicts_readonly_inheritance.py @@ -0,0 +1,132 @@ +""" +Tests inheritance rules involving typing.ReadOnly. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#read-only-items + +from typing import Collection, NotRequired, Required, TypedDict +from typing_extensions import ReadOnly + +# > Subclasses can redeclare read-only items as non-read-only, allowing them +# > to be mutated. + + +class NamedDict(TypedDict): + name: ReadOnly[str] + + +class Album1(NamedDict): + name: str + year: int + + +a1: Album1 = {"name": "Flood", "year": 1990} +a1["year"] = 1973 # OK +a1["name"] = "Dark Side Of The Moon" # OK + + +# > If a read-only item is not redeclared, it remains read-only. + + +class Album2(NamedDict): + year: int + + +a2: Album2 = {"name": "Flood", "year": 1990} +a2["name"] = "Dark Side Of The Moon" # E + + +# > Subclasses can narrow value types of read-only items. + + +class AlbumCollection(TypedDict): + albums: ReadOnly[Collection[Album1]] + alt: ReadOnly[list[str | int]] + + +class RecordShop(AlbumCollection): + name: str + albums: ReadOnly[list[Album1]] # OK + alt: ReadOnly[list[str]] # E + + +# > Subclasses can require items that are read-only but not required in the +# > superclass. + + +class OptionalName(TypedDict): + name: ReadOnly[NotRequired[str]] + + +class RequiredName(OptionalName): + name: ReadOnly[Required[str]] # OK + + +d: RequiredName = {} # E + + +# > Subclasses can combine these rules. + + +class OptionalIdent(TypedDict): + ident: ReadOnly[NotRequired[str | int]] + + +class User(OptionalIdent): + ident: str # Required, mutable, and not an int + + +u: User +u = {"ident": ""} # OK +u["ident"] = "" # OK +u["ident"] = 3 # E +u = {"ident": 3} # E +u = {} # E + + +class F1(TypedDict): + a: Required[int] + b: ReadOnly[NotRequired[int]] + c: ReadOnly[Required[int]] + + +class F3(F1): + a: ReadOnly[int] # E + + +class F4(F1): + a: NotRequired[int] # E + + +class F5(F1): + b: ReadOnly[Required[int]] # OK + + +class F6(F1): + c: ReadOnly[NotRequired[int]] # E + + +class TD_A1(TypedDict): + x: int + y: ReadOnly[int] + + +class TD_A2(TypedDict): + x: float + y: ReadOnly[float] + + +class TD_A(TD_A1, TD_A2): ... # E: x is incompatible + + +class TD_B1(TypedDict): + x: ReadOnly[NotRequired[int]] + y: ReadOnly[Required[int]] + + +class TD_B2(TypedDict): + x: ReadOnly[Required[int]] + y: ReadOnly[NotRequired[int]] + + +class TD_B(TD_B1, TD_B2): ... # E: x is incompatible diff --git a/conformance/tests/typeddicts_readonly_kwargs.py b/conformance/tests/typeddicts_readonly_kwargs.py new file mode 100644 index 000000000..e2727ba4d --- /dev/null +++ b/conformance/tests/typeddicts_readonly_kwargs.py @@ -0,0 +1,36 @@ +""" +Tests the use of unpacked TypedDicts with read-only items when used to annotate +a **kwargs parameter. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#read-only-items + +from typing import Protocol, TypedDict, Unpack +from typing_extensions import ReadOnly + +# > :pep:`692` introduced ``Unpack`` to annotate ``**kwargs`` with a +# > ``TypedDict``. Marking one or more of the items of a ``TypedDict`` used +# > in this way as read-only will have no effect on the type signature of +# > the method. However, it *will* prevent the item from being modified in +# > the body of the function. + + +class Args(TypedDict): + key1: int + key2: str + + +class ReadOnlyArgs(TypedDict): + key1: ReadOnly[int] + key2: ReadOnly[str] + + +class Function(Protocol): + def __call__(self, **kwargs: Unpack[Args]) -> None: ... + + +def impl(**kwargs: Unpack[ReadOnlyArgs]) -> None: + kwargs["key1"] = 3 # E + + +fn: Function = impl # OK diff --git a/conformance/tests/typeddicts_readonly_update.py b/conformance/tests/typeddicts_readonly_update.py new file mode 100644 index 000000000..c5f2b6400 --- /dev/null +++ b/conformance/tests/typeddicts_readonly_update.py @@ -0,0 +1,34 @@ +""" +Tests inheritance rules involving the update method for TypedDicts +that contain read-only items. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#read-only-items + +from typing import Never, NotRequired, TypedDict +from typing_extensions import ReadOnly + +# > In addition to existing type checking rules, type checkers should error if +# > a TypedDict with a read-only item is updated with another TypedDict that +# > declares that key. + + +class A(TypedDict): + x: ReadOnly[int] + y: int + + +a1: A = {"x": 1, "y": 2} +a2: A = {"x": 3, "y": 4} +a1.update(a2) # E + +# > Unless the declared value is of bottom type (:data:`~typing.Never`). + + +class B(TypedDict): + x: NotRequired[Never] + y: ReadOnly[int] + + +def update_a(a: A, b: B) -> None: + a.update(b) # OK diff --git a/conformance/tests/typeddicts_required.py b/conformance/tests/typeddicts_required.py new file mode 100644 index 000000000..d4cef0142 --- /dev/null +++ b/conformance/tests/typeddicts_required.py @@ -0,0 +1,74 @@ +""" +Tests the Required and NotRequired special forms. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#required-and-notrequired + +from typing import Annotated, NotRequired, Required, TypedDict + + +# Required and NotRequired are valid only within a TypedDict. +class NotTypedDict: + x: Required[int] # E: Required not allowed in this context + + +def func1( + x: NotRequired[int], # E: NotRequired not allowed in this context +) -> None: + pass + + +class TD1(TypedDict, total=False): + a: int + + +class TD2(TD1, total=True): + b: int + + +class TD3(TypedDict): + a: NotRequired[int] + b: Required[int] + + +class TD4(TypedDict, total=False): + a: int + b: Required[int] + + +class TD5(TypedDict, total=True): + a: NotRequired[int] + b: int + + +td3: TD3 = {"b": 0} +td4: TD4 = {"b": 0} +td5: TD5 = {"b": 0} + +# These are all equivalent types, so they should be +# bidirectionally type compatible. +td3 = td4 +td3 = td5 +td4 = td3 +td4 = td5 +td5 = td3 +td5 = td4 + + +class TD6(TypedDict): + a: Required[Required[int]] # E: Nesting not allowed + b: Required[NotRequired[int]] # E: Nesting not allowed + + +class TD7(TypedDict): + # > Required[] and NotRequired[] can be used with Annotated[], in any nesting order. + x: Annotated[Required[int], ""] + y: Required[Annotated[int, ""]] + z: Annotated[Required[Annotated[int, ""]], ""] + + +RecursiveMovie = TypedDict( + "RecursiveMovie", {"title": Required[str], "predecessor": NotRequired["RecursiveMovie"]} +) + +movie: RecursiveMovie = {"title": "Beethoven 3", "predecessor": {"title": "Beethoven 2"}} diff --git a/conformance/tests/typeddicts_type_consistency.py b/conformance/tests/typeddicts_type_consistency.py new file mode 100644 index 000000000..8c2210873 --- /dev/null +++ b/conformance/tests/typeddicts_type_consistency.py @@ -0,0 +1,152 @@ +""" +Tests the type consistency rules for TypedDict objects. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/typeddict.html#type-consistency + +from typing import Any, Literal, Mapping, TypedDict + + +class A1(TypedDict): + x: int | None + + +class B1(TypedDict): + x: int + + +b1: B1 = {"x": 0} + +# > Value types behave invariantly. +a1: A1 = b1 # E: 'B1' not compatible with 'A1' + +# > any TypedDict type is consistent with Mapping[str, object] +v1: Mapping[str, object] = b1 + +# > A TypedDict type with a required key is not consistent with a TypedDict +# > type where the same key is a non-required key. + +class A2(TypedDict, total=False): + x: int + + +class B2(TypedDict): + x: int + + +b2: B2 = {"x": 0} +a2: A2 = b2 # E: 'B2' not compatible with 'A2' + + +# > A TypedDict type A is consistent with TypedDict B if A is structurally +# > compatible with B. This is true if and only if both of these conditions +# > are satisfied: +# > 1. For each key in B, A has the corresponding key and the corresponding +# > value type in A is consistent with the value type in B. For each key in B, +# > the value type in B is also consistent with the corresponding value type +# > in A. +# > 2. For each required key in B, the corresponding key is required in A. For +# > each non-required key in B, the corresponding key is not required in A. + + +class A3(TypedDict): + x: int + + +class B3(TypedDict): + x: int + y: int + + +b3: B3 = {"x": 0, "y": 0} +a3: A3 = b3 + +a3 = {"x": 0} +b3 = a3 # E + + +# This should generate an error because it's a literal assignment. +a3_1: A3 = {"x": 0, "y": 0} # E + +# This should not generate an error. +a3_2 = b3 + +# > A TypedDict isn’t consistent with any Dict[...] type. + +d1: dict[str, int] = b3 # E +d2: dict[str, object] = b3 # E +d3: dict[Any, Any] = b3 # E + +# > A TypedDict with all int values is not consistent with Mapping[str, int]. + +m1: Mapping[str, int] = b3 # E +m2: Mapping[str, object] = b3 # OK +m3: Mapping[str, Any] = b3 # OK + + +# Test "get" method. +UserType1 = TypedDict("UserType1", {"name": str, "age": int}, total=False) +user1: UserType1 = {"name": "Bob", "age": 40} + +name1: str = user1.get("name", "n/a") +age1: int = user1.get("age", 42) + +UserType2 = TypedDict("UserType2", {"name": str, "age": int}) +user2: UserType2 = {"name": "Bob", "age": 40} + +name2: str | None = user2.get("name") + +# The spec does not say whether type checkers should adjust the return type of `.get()` +# to exclude `None` if it is known that the key exists. Either option is acceptable. +name3: str = user2.get("name") # E? + +age2: int = user2.get("age", 42) + +age3: int | str = user2.get("age", "42") + +age4: int = user2.get("age", "42") # E? + +# Test nested TypedDicts. +class Inner1(TypedDict): + inner_key: str + + +class Inner2(TypedDict): + inner_key: Inner1 + + +class Outer1(TypedDict): + outer_key: Inner2 + + +o1: Outer1 = {"outer_key": {"inner_key": {"inner_key": "hi"}}} + +# This should generate an error because the inner-most value +# should be a string. +o2: Outer1 = {"outer_key": {"inner_key": {"inner_key": 1}}} # E + + +class Inner3(TypedDict): + x: int + + +class Inner4(TypedDict): + x: int + + +class Outer2(TypedDict): + y: str + z: Literal[""] | Inner3 + + +class Outer3(TypedDict): + y: str + z: Literal[""] | Inner4 + + +def func1(td: Outer3): + ... + + +o3: Outer2 = {"y": "", "z": {"x": 0}} +o4: Outer3 = o3 diff --git a/conformance/tests/typeddicts_usage.py b/conformance/tests/typeddicts_usage.py new file mode 100644 index 000000000..ac16daaf7 --- /dev/null +++ b/conformance/tests/typeddicts_usage.py @@ -0,0 +1,40 @@ +""" +Tests for basic usage of TypedDict. +""" + +from typing import TypeVar, TypedDict + + +class Movie(TypedDict): + name: str + year: int + + +movie: Movie = {"name": "Blade Runner", "year": 1982} + + +def record_movie(movie: Movie) -> None: + ... + + +record_movie({"name": "Blade Runner", "year": 1982}) + + +movie["director"] = "Ridley Scott" # E: invalid key 'director' +movie["year"] = "1982" # E: invalid value type ("int" expected) + +# The code below should be rejected, since 'title' is not a valid key, +# and the 'name' key is missing: +movie2: Movie = {"title": "Blade Runner", "year": 1982} # E + +m = Movie(name='Blade Runner', year=1982) + + +# > TypedDict type objects cannot be used in isinstance() tests such as +# > isinstance(d, Movie). +if isinstance(movie, Movie): # E + pass + + +# TypedDict should not be allowed as a bound for a TypeVar. +T = TypeVar("T", bound=TypedDict) # E diff --git a/conformance/tests/typeforms_typeform.py b/conformance/tests/typeforms_typeform.py new file mode 100644 index 000000000..51b55ca31 --- /dev/null +++ b/conformance/tests/typeforms_typeform.py @@ -0,0 +1,108 @@ +""" +Tests TypeForm functionality. +""" + +# Specification: https://typing.readthedocs.io/en/latest/spec/type-forms.html#typeform + +import types +from typing import Annotated, Any, ClassVar, Final, Literal, Optional, Self, TypeVarTuple, Unpack, assert_type +from typing_extensions import TypeForm + + +# > ``TypeForm[T]`` describes the set of all type form objects that represent +# > the type ``T`` or types that are assignable to ``T``. + +ok1: TypeForm[str | None] = str | None # OK +ok2: TypeForm[str | None] = str # OK +ok3: TypeForm[str | None] = None # OK +ok4: TypeForm[str | None] = Literal[None] # OK +ok5: TypeForm[str | None] = Optional[str] # OK +ok6: TypeForm[str | None] = "str | None" # OK +ok7: TypeForm[str | None] = Any # OK + +err1: TypeForm[str | None] = str | int # E +err2: TypeForm[str | None] = list[str | None] # E + + +# > ``TypeForm[Any]`` is assignable both to and from any other ``TypeForm`` type. + +v_any: TypeForm[Any] = int | str +v_str: TypeForm[str] = str +assert_type(v_any, TypeForm[Any]) +assert_type(v_str, TypeForm[str]) + +v_any = v_str # OK +v_str = v_any # OK + +# > The type expression ``TypeForm``, with no type argument provided, is +# > equivalent to ``TypeForm[Any]``. + +def func1(x: TypeForm) -> None: + assert_type(x, TypeForm[Any]) + + +# > Valid type expressions are assignable to ``TypeForm`` through implicit TypeForm evaluation. + +v1_actual: types.UnionType = str | None # OK +v1_type_form: TypeForm[str | None] = str | None # OK + +v2_actual: types.GenericAlias = list[int] # OK +v2_type_form: TypeForm = list[int] # OK + +v3: TypeForm[int | str] = Annotated[int | str, "metadata"] # OK +v4: TypeForm[set[str]] = "set[str]" # OK + +func1(v3) # OK +func1(v4) # OK +func1(int) # OK +func1("int") # OK +func1("not a type") # E + + +# > Expressions that are not valid type expressions should not evaluate to a ``TypeForm`` type. + +Ts = TypeVarTuple("Ts") +var = 1 + +bad1: TypeForm = tuple() # E +bad2: TypeForm = (1, 2) # E +bad3: TypeForm = 1 # E +bad4: TypeForm = Self # E +bad5: TypeForm = ClassVar[int] # E +bad6: TypeForm = Final[int] # E +bad7: TypeForm = Unpack[Ts] # E +bad8: TypeForm = Optional # E +bad9: TypeForm = "int + str" # E + + +# > ``TypeForm`` acts as a function that can be called with a single valid type expression. + +x1 = TypeForm(str | None) +assert_type(x1, TypeForm[str | None]) + +x2 = TypeForm("list[int]") +assert_type(x2, TypeForm[list[int]]) + +x3 = TypeForm("type(1)") # E +x4 = type(1) # OK +x5 = TypeForm(type(1)) # E + + +# > ``TypeForm`` is covariant in its single type parameter. + +def get_type_form() -> TypeForm[int]: + return int + + +t1: TypeForm[int | str] = get_type_form() # OK +t2: TypeForm[str] = get_type_form() # E + + +# > ``type[T]`` is a subtype of ``TypeForm[T]``. + +def get_type() -> type[int]: + return int + + +t3: TypeForm[int | str] = get_type() # OK +t4: TypeForm[str] = get_type() # E diff --git a/conformance/uv.lock b/conformance/uv.lock new file mode 100644 index 000000000..718dadee2 --- /dev/null +++ b/conformance/uv.lock @@ -0,0 +1,270 @@ +version = 1 +revision = 3 +requires-python = "==3.12.*" + +[[package]] +name = "ast-serialize" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/9d/09e27731bd5864a9ce04e3244074e674bb8936bf62b45e0357248717adac/ast_serialize-0.5.0.tar.gz", hash = "sha256:5880091bfe6f4f986f22866375c2e884843e7a0b6343ae41aeea659613d879b6", size = 61157, upload-time = "2026-05-17T17:48:29.429Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e0/9e/dc2530acb3a60dc6e46d65abf27d1d9f86721694757906a148d90a6860de/ast_serialize-0.5.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:0668aa9459cfa8c9c49ddd2163ebcf43088ba045ef7492af6fe22e0098303101", size = 1191380, upload-time = "2026-05-17T17:48:03.738Z" }, + { url = "https://files.pythonhosted.org/packages/26/0a/bd3d18a582f273d6c843d16bb9e22e9e16365ff7991e92f18f798e9f1224/ast_serialize-0.5.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:bf683d6363edf2b39eed6b6d4fe22d34b6203867a67e27134d9e2a2680c4bc4a", size = 1183879, upload-time = "2026-05-17T17:48:05.463Z" }, + { url = "https://files.pythonhosted.org/packages/40/ae/1f919100f8620887af58fcc381c61a1f218cdf89c6e155f87b213e61010a/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc22cf0c9be65e71cf88fda130af60d61eb4a79370ad4cfe7900d48a4aa2211", size = 1244529, upload-time = "2026-05-17T17:48:07.008Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ca/6376559dcce707cdbc1d0d9a13c8d3baaaa501e949ce0ebdc4230cd881aa/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f66173891548c9f2726bf27957b41cabce12fa679dc6da505ddbde4d4b3b31cf", size = 1240560, upload-time = "2026-05-17T17:48:08.46Z" }, + { url = "https://files.pythonhosted.org/packages/35/b2/a620e206b5aeb7efbf2710336df57d457cffbb3991076bbcc1147ef9abd4/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e42d729ef2be96a14efbad355093284739e3670ece3e534f82cc8832790911d9", size = 1451172, upload-time = "2026-05-17T17:48:09.922Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e0/4ad5c04c24a40481b2935ce9a0ccdb6023dc8b667167d06ae530cc3512f2/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b725026bafa801dbd7310eb13a75f0a2e370e7e51b2cb225f9d21fcfadf919ee", size = 1265072, upload-time = "2026-05-17T17:48:11.469Z" }, + { url = "https://files.pythonhosted.org/packages/b2/71/4d1d479aa56d0101c40e17720c3d6ac2af7269ea0487a80b18e7bfd1a5b7/ast_serialize-0.5.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b54f60c1d78767a53b67eaa663f0dfac3afe606aa07f1301572f588b73d64809", size = 1270488, upload-time = "2026-05-17T17:48:13.575Z" }, + { url = "https://files.pythonhosted.org/packages/6d/4f/0de1bbe06f6edef9fde4ed12ca8e7b3ec7e6e2bd4e672c5af487f7957665/ast_serialize-0.5.0-cp39-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:27d51654fc240a1e87e742d353d98eb45b75f62f129086b3596ab53df2ac2a43", size = 1260702, upload-time = "2026-05-17T17:48:15.141Z" }, + { url = "https://files.pythonhosted.org/packages/75/61/e00872439cfdddcc3c1b6cdaa6e5d904ba8e26a18807c67c4e14409d0ca8/ast_serialize-0.5.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c36237c46dd1674542f2109740ea5ea485a169bf1431939ada0434e17934", size = 1311182, upload-time = "2026-05-17T17:48:16.779Z" }, + { url = "https://files.pythonhosted.org/packages/76/8e/699a5b955f7926956c95e9e1d74132acad73c2fe7a426f94da89123c20aa/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1943db345233cc7194a470f13afa9c59772c0b123dea0c9414c4d4ca54369759", size = 1421410, upload-time = "2026-05-17T17:48:18.527Z" }, + { url = "https://files.pythonhosted.org/packages/a9/ae/d5b7626874478997adc7a29ab28accf21e596fb590c944290401dfd0b29e/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:df1c00022cbbcb064bfaa505aa9c9295362443ce5dacb459d1331d3da353f887", size = 1516587, upload-time = "2026-05-17T17:48:20.133Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ce/b59e02a82d9c4244d64cde502e0b00e83e38816abe19155ceb5437402c7f/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cae65289fc456fde04af979a2be09302ef5d8ab92ef23e596d6746dc267ada27", size = 1515171, upload-time = "2026-05-17T17:48:21.921Z" }, + { url = "https://files.pythonhosted.org/packages/8b/38/d8d90042747d05aa08d4efcf1c99035a5f670a6bf4c214d31644392afbca/ast_serialize-0.5.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:239a4c354e8d676e9d94631d1d4a64edc6b266f86ff3a5a80aedd344f342c01d", size = 1464668, upload-time = "2026-05-17T17:48:23.544Z" }, + { url = "https://files.pythonhosted.org/packages/dd/51/5b840c4df7334104cecffa28f23904fe81ca89ca223d2450e288de39fd3c/ast_serialize-0.5.0-cp39-abi3-win32.whl", hash = "sha256:143a4ef63285a075871908fda3672dc21864b83a8ec3ee12304aa3e4c5387b9a", size = 1068311, upload-time = "2026-05-17T17:48:25.027Z" }, + { url = "https://files.pythonhosted.org/packages/41/11/ca5672c7d491825bc4cd6702dea106a6b60d928707712ec257c7833ae476/ast_serialize-0.5.0-cp39-abi3-win_amd64.whl", hash = "sha256:cf25572c526add400f26a4750dc6ce0c3bb93fc1f75e7ae0cad4ce4f2cd5c590", size = 1108931, upload-time = "2026-05-17T17:48:26.591Z" }, + { url = "https://files.pythonhosted.org/packages/45/19/cc8bd127d28a43da249aa955cfd164cf8fd534e79e42cea96c4854d72fd0/ast_serialize-0.5.0-cp39-abi3-win_arm64.whl", hash = "sha256:92a31c9c20d25a076edaeec76b128a3535d74a24f340b9a8a7e96c9b86dc9642", size = 1081181, upload-time = "2026-05-17T17:48:28.122Z" }, +] + +[[package]] +name = "importlib-resources" +version = "7.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e4/06/b56dfa750b44e86157093bc8fca0ab81dccbf5260510de4eaf1cb69b5b99/importlib_resources-7.1.0.tar.gz", hash = "sha256:0722d4c6212489c530f2a145a34c0a7a3b4721bc96a15fada5930e2a0b760708", size = 44985, upload-time = "2026-04-12T16:36:09.232Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/db/55a262f3606bebcae07cc14095338471ad7c0bbcaa37707e6f0ee49725b7/importlib_resources-7.1.0-py3-none-any.whl", hash = "sha256:1bd7b48b4088eddb2cd16382150bb515af0bd2c70128194392725f82ad2c96a1", size = 37232, upload-time = "2026-04-12T16:36:08.219Z" }, +] + +[[package]] +name = "librt" +version = "0.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" }, + { url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" }, + { url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" }, + { url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" }, + { url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" }, + { url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" }, + { url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" }, + { url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" }, + { url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" }, + { url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" }, + { url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" }, + { url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" }, +] + +[[package]] +name = "mypy" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ast-serialize" }, + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" }, + { url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" }, + { url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" }, + { url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" }, + { url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" }, + { url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" }, + { url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "pycroscope" +version = "0.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typeshed-client" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7c/8e/284fdc4027bf9a7c92f8256ff07006a50456ccd2ce5506d040e6ed2f2f1b/pycroscope-0.4.0.tar.gz", hash = "sha256:14b40e36f6186dd2eff712c8da91d02eb89a263a257472b49f4c59153ff380fb", size = 662901, upload-time = "2026-05-02T14:00:43.775Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/5f/c172c08b69e29d4094e1466b63260d8daf61a3ed3c689de7936f952f1663/pycroscope-0.4.0-py3-none-any.whl", hash = "sha256:cb7440b44ed16ed927995c33867cab22503f4bff7efe504964b81fb967183223", size = 714256, upload-time = "2026-05-02T14:00:41.891Z" }, +] + +[[package]] +name = "pyrefly" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/3a/9045b0097ac58979c7c30a4fa0e673db942d4adbc7b6d439bd54ae58c441/pyrefly-1.0.0.tar.gz", hash = "sha256:5c2b810ffcebd84be71de5df1223651edee951653a66935c6f091e957c452455", size = 5677995, upload-time = "2026-05-12T20:12:46.812Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/c6/90788819bac9c61dd7bacba53b79f3c12d47ccbe5e51b3d6d89f2387e1d2/pyrefly-1.0.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e355a0908555348ed4b9585ef25c76ff566673e345c866c325f1633f44d890b6", size = 13122950, upload-time = "2026-05-12T20:12:20.711Z" }, + { url = "https://files.pythonhosted.org/packages/82/91/a3cf2a1e87d336eaa804a1e6fc93266faf6dc2a97eecdbc7eae289628022/pyrefly-1.0.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a7038efc3a40f8294edee339895633cf22db268c0d434cdbcbefc34f78a9ecc3", size = 12599494, upload-time = "2026-05-12T20:12:23.495Z" }, + { url = "https://files.pythonhosted.org/packages/cd/ab/74d1e11e737e99b1c003ecc5d7d2e846c4ea1f328966bfdbbd0ac63fad0a/pyrefly-1.0.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da331ca515ed1c08791da2b5f664cf9c1294c48fd802133262e7d5d51e0f4416", size = 12995507, upload-time = "2026-05-12T20:12:25.951Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ac/2df0899f8464c97e5d995f994c97c5cb5b0f58610432aa90d26d924e1db5/pyrefly-1.0.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c74219d8f3e63cdaa5501a0b21d1c9d37011820f9606728d0ed06f09ae86a878", size = 13947693, upload-time = "2026-05-12T20:12:29.188Z" }, + { url = "https://files.pythonhosted.org/packages/6b/3e/b247c24321e36f04b7d51f9ccf3df93e5009e4b29939524b36ec2e17dc2a/pyrefly-1.0.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c0d05543b1bb6ee6d64149eb5d6b2fb15aa72d3962d6a97abca0afaca8b0c131", size = 13925803, upload-time = "2026-05-12T20:12:31.904Z" }, + { url = "https://files.pythonhosted.org/packages/61/16/cfa2d61a4aa1e1f7bca48bb37acd01c6a09db4864b16a54f9587092765ff/pyrefly-1.0.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1382d5b1fcdb49a4de9f34d112d2bddf290a78ff93ee8149492ad5f1077ddffc", size = 13470398, upload-time = "2026-05-12T20:12:35.302Z" }, + { url = "https://files.pythonhosted.org/packages/cb/2b/6372c7dddb326223e24a46b17efd0d4bd7b4fe22c821e523157577eed2d2/pyrefly-1.0.0-py3-none-win32.whl", hash = "sha256:aa8b5d0e47080e3202a2547b39f7a5a61d2c781c712b3b67884f745ca2c759d2", size = 12222643, upload-time = "2026-05-12T20:12:38.618Z" }, + { url = "https://files.pythonhosted.org/packages/be/ad/1d23be700b6b2ddaeb362360c7145917a8edbbf7240ae428d40541772fce/pyrefly-1.0.0-py3-none-win_amd64.whl", hash = "sha256:c8abcb0f2082e83c890375128f9cff4aa4d3f210b85eea7b3046c1ae764e77f5", size = 13146369, upload-time = "2026-05-12T20:12:41.423Z" }, + { url = "https://files.pythonhosted.org/packages/8c/38/16589134f3012fd097a10dcc85771555f1a5fb76e04b682597180743af30/pyrefly-1.0.0-py3-none-win_arm64.whl", hash = "sha256:d150fa9e40e8392832be81c3bcfc0497c146674ce4d0f8e04e1ec29e775ffb8c", size = 12538326, upload-time = "2026-05-12T20:12:43.996Z" }, +] + +[[package]] +name = "pyright" +version = "1.1.409" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/4e/3aa27f74211522dba7e9cbc3e74de779c6d4b654c54e50a4840623be8014/pyright-1.1.409.tar.gz", hash = "sha256:986ee05beca9e077c165758ad123667c679e050059a2546aa02473930394bc93", size = 4430434, upload-time = "2026-04-23T11:02:03.799Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/16/6b/330d8ebae582b30c2959a1ef4c3bc344ebde48c2ff0c3f113c4710735e11/pyright-1.1.409-py3-none-any.whl", hash = "sha256:aa3ea228cab90c845c7a60d28db7a844c04315356392aa09fafcee98c8c22fb3", size = 6438161, upload-time = "2026-04-23T11:02:01.309Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + +[[package]] +name = "tomlkit" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/af/14b24e41977adb296d6bd1fb59402cf7d60ce364f90c890bd2ec65c43b5a/tomlkit-0.14.0.tar.gz", hash = "sha256:cf00efca415dbd57575befb1f6634c4f42d2d87dbba376128adb42c121b87064", size = 187167, upload-time = "2026-01-13T01:14:53.304Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/11/87d6d29fb5d237229d67973a6c9e06e048f01cf4994dee194ab0ea841814/tomlkit-0.14.0-py3-none-any.whl", hash = "sha256:592064ed85b40fa213469f81ac584f67a4f2992509a7c3ea2d632208623a3680", size = 39310, upload-time = "2026-01-13T01:14:51.965Z" }, +] + +[[package]] +name = "ty" +version = "0.0.40" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/f8/a754c96967b71de8723f88be17df8738216bd382ffed229cd500b7a24d13/ty-0.0.40.tar.gz", hash = "sha256:883b53dd98f6e5b33ab1c8e1a3cd94b0f29c762ef22cdf1e86aaffb4fd711c67", size = 5726484, upload-time = "2026-05-27T17:55:43.615Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/42/d029a72165ad39f95228b67355927fbd35c821dc8e3e475d49f47c2eeb1e/ty-0.0.40-py3-none-linux_armv6l.whl", hash = "sha256:9defb4742450e569a6a09de286a04008d6c2e815112da4362c88b6eaa2f52a36", size = 11406372, upload-time = "2026-05-27T17:55:49.633Z" }, + { url = "https://files.pythonhosted.org/packages/23/99/7f8ea09b7e49afbf795cb3341a3217f30f228db7e62a2268ed8cbbf813d6/ty-0.0.40-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:868258a3330db88b683fcafe2c4e936d6226a6312799bf15b585d93557b2d38c", size = 11159782, upload-time = "2026-05-27T17:55:47.405Z" }, + { url = "https://files.pythonhosted.org/packages/04/d8/1ea745ee97a98b26ae9564d19a430a76a35297cd450e84dcaad22e1f7ee8/ty-0.0.40-py3-none-macosx_11_0_arm64.whl", hash = "sha256:589c81060cf1e7a9ffa2f45bfa35ffd9b9fbd214104e3f13959f113627efcd91", size = 10594139, upload-time = "2026-05-27T17:55:37.206Z" }, + { url = "https://files.pythonhosted.org/packages/39/1a/fbef21273c6617ff4715b4827ee1c0b6550aa7d1df4b8c43b325545c1cf4/ty-0.0.40-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b06108990cb338d941c315ae6e9ba2fff8f518bc15d3f33e5619ff6a6c9beab", size = 11114156, upload-time = "2026-05-27T17:55:56.11Z" }, + { url = "https://files.pythonhosted.org/packages/3c/f9/389fc4976d7ec016a7473cf1274bf9c4f491bb54c66649bd022bff9f2b6a/ty-0.0.40-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3913ef37336bec4f96bd2512f8c3a543ca34c259b7170f7eb5adf75b3ed7f04c", size = 11189050, upload-time = "2026-05-27T17:55:54.099Z" }, + { url = "https://files.pythonhosted.org/packages/fa/a9/4ecabbf4bdda7df0d99d8d3892c6edac0efc8c4cae756a5109178a3d0e86/ty-0.0.40-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8fd1486bd5fe48779a8aa857137f3642a0a9161f5cf57d4380f4a0ecea01c8f3", size = 11664266, upload-time = "2026-05-27T17:55:28.17Z" }, + { url = "https://files.pythonhosted.org/packages/45/02/0aa78730116507c265afb1d6d5961c583b49d4c2e368c4a49fd81bcae6dc/ty-0.0.40-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1668364d5254a734329917ee66c2c5fdd5665389d41043f6fce0f22ddb32b749", size = 12187743, upload-time = "2026-05-27T17:56:04.337Z" }, + { url = "https://files.pythonhosted.org/packages/e6/68/ccabf2d173523598271a385c1d3f864dbda23e5ebdc67f5969b9e830ea05/ty-0.0.40-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f77a73edb91e5dfa2ab9af7c4cac64614f8cc121f38a8875f22e830d3aba6a", size = 11862999, upload-time = "2026-05-27T17:55:58.087Z" }, + { url = "https://files.pythonhosted.org/packages/03/8d/6d7ec22771bb23d534797cdb446eb644bccfe7a62b729bb99e7235a02fc3/ty-0.0.40-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1274ce0212ecbfed01bda7c3659c46e8bd0068e32d00c46c790466a95274c3df", size = 11743896, upload-time = "2026-05-27T17:56:00.017Z" }, + { url = "https://files.pythonhosted.org/packages/cd/a4/f9fa076b010c91cb249b1fcc3476569b7b8462cb4b688da2d04c23a0622f/ty-0.0.40-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5ee1261dbc363e5cc1a0c5bb0c8612c192bfe53491214df8bc85a540835685f9", size = 11883581, upload-time = "2026-05-27T17:56:02.319Z" }, + { url = "https://files.pythonhosted.org/packages/fd/0f/5b776a2328c756d574dd4d6afbd30fc24e1ab4b76935c7c3c23f27ebbcb9/ty-0.0.40-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:6220e2cd5cdc4683dd87fb150d195bbd9f1a021395e04cb08bd3c66ea6da6ef8", size = 11093946, upload-time = "2026-05-27T17:55:33.284Z" }, + { url = "https://files.pythonhosted.org/packages/64/c4/eb23154bae83ad7c2935e9e5916660fb3e31598a92ee232aebd79410480c/ty-0.0.40-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:46b9ed69d01d98ef046afac9983c68336f572605ea2a27b90fbe6f80bfc8d6b7", size = 11210737, upload-time = "2026-05-27T17:55:45.523Z" }, + { url = "https://files.pythonhosted.org/packages/ff/19/1fb2529703f708cacfd13a89f98613cae2907dfa941b26976467e6119803/ty-0.0.40-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ddbca9fab4406260f141674ab5efcfe7b02bd468e6985e4cdde0a21626e69ffe", size = 11332563, upload-time = "2026-05-27T17:55:41.674Z" }, + { url = "https://files.pythonhosted.org/packages/87/69/b3f5a8ef26c31204e0391147b3adcdb0674eda3e7d99868478ef168a41c6/ty-0.0.40-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b1fcc082a749e6dc11b68fe9aab0420238bbf2a2374c2c7aa3c22e8c1618b136", size = 11843216, upload-time = "2026-05-27T17:55:35.367Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e8/20193069d32787f3e1a6ec8940aaa3759d3de8f48f9281bcc0c5cb0939da/ty-0.0.40-py3-none-win32.whl", hash = "sha256:75feb115b3587824c5bdf8f8305e9547b0d1e398e3077b0addc7a1988ea9bb50", size = 10670731, upload-time = "2026-05-27T17:55:31.316Z" }, + { url = "https://files.pythonhosted.org/packages/a3/f9/8b2aa4da61db81322d4a2f9db227afeb48110ca15ae31d380f64c64ceb63/ty-0.0.40-py3-none-win_amd64.whl", hash = "sha256:b0f905edaad788bd61f779a85801b60a267a25ed57fca05aaddd168d9d8896be", size = 11766211, upload-time = "2026-05-27T17:55:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/04/87/369056ed46f1b235130ec0595393262f9cd2061ca3dab276d490980f9343/ty-0.0.40-py3-none-win_arm64.whl", hash = "sha256:07da2b09d9130e2c9a257d2a29beb53105835b0256ee5fdb288fe1aab83fee47", size = 11117369, upload-time = "2026-05-27T17:55:39.329Z" }, +] + +[[package]] +name = "typeshed-client" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-resources" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/85/7d/62fbae352d5fb7ce5ef4d9ca73bf7a9b02b790d2524ab6ef1e0e799a5d1b/typeshed_client-2.11.0.tar.gz", hash = "sha256:0b8f2ab88f611f5e97b70d2a8123942d3d7d5c74cee8ae694db83422f32f9481", size = 522774, upload-time = "2026-05-01T14:51:52.38Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/22/fa16b462157bd869dfad528f5637506b9430ca63d48fb536ecf4cc78481a/typeshed_client-2.11.0-py3-none-any.whl", hash = "sha256:5745e0990b80b29a286b22d68f81779c5c7adf1cac8969eeafba44b73b486c36", size = 787609, upload-time = "2026-05-01T14:51:51.005Z" }, +] + +[[package]] +name = "typing-conformance" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "mypy" }, + { name = "pycroscope" }, + { name = "pyrefly" }, + { name = "pyright" }, + { name = "tomli" }, + { name = "tomlkit" }, + { name = "ty" }, + { name = "zuban" }, +] + +[package.metadata] +requires-dist = [ + { name = "mypy" }, + { name = "pycroscope" }, + { name = "pyrefly" }, + { name = "pyright" }, + { name = "tomli" }, + { name = "tomlkit" }, + { name = "ty" }, + { name = "zuban" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "zuban" +version = "0.7.2" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/52/7f/5d5e205eccb20d650c04187b82dbc0f8d1595ed12c0ea650d633643c7c9b/zuban-0.7.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:82e9ad81c84cb39c7082d6f8ff39fe7a3e67a1765b63ae215dcf469745c67c63", size = 11374640, upload-time = "2026-05-03T15:36:03.467Z" }, + { url = "https://files.pythonhosted.org/packages/0e/dd/bc94b699e9ddc857143806b8aa24d064f50dd5e89c9de0013c90d5bdcbea/zuban-0.7.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a3cf4c85c993f375d411a589195983818cd675f7c396835e2141b5b2b7c2fb74", size = 11095967, upload-time = "2026-05-03T15:36:06.699Z" }, + { url = "https://files.pythonhosted.org/packages/98/8e/2cf71f02be4de5398787b551fe8dc3056f99109196a7a0c9c30fef034baf/zuban-0.7.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2c5e6015e4a8b27d7fb0cf38629449b8bc0599aa144d8ee4bec0fc79e065b66", size = 28055795, upload-time = "2026-05-03T15:36:10.448Z" }, + { url = "https://files.pythonhosted.org/packages/59/00/f2b2423e464e54beb0eb1282204770de8c77ad6870368e488a3fb39f253e/zuban-0.7.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7498188561743aa108dca6496c69694c6b4e5eec4a207e2a5b2831ad6e68251e", size = 28253342, upload-time = "2026-05-03T15:36:13.659Z" }, + { url = "https://files.pythonhosted.org/packages/e1/06/0afe190f33cdc3762a1eeb64e6032e3ba96fd7ea26b22447431aff242dab/zuban-0.7.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bba9c566449dfa7c36d155c7d45b0379b66d94ea15dfb66aa2d7bcb903d60308", size = 29481863, upload-time = "2026-05-03T15:36:17.737Z" }, + { url = "https://files.pythonhosted.org/packages/81/19/a65982d180bdb2b8f2aa4be99e14d09a95b34f217eda76bf2b2951025f81/zuban-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada57d1e876c903bf202bd3188904229b774abc7db227ccfa20e9938f418c5c1", size = 30458158, upload-time = "2026-05-03T15:36:21.162Z" }, + { url = "https://files.pythonhosted.org/packages/de/ce/0c73285da471d8c529ac263f3e65855d917a0fcef1f996611551d49f52c9/zuban-0.7.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:b736e8945efa84cd225c304f38fd5b366b566518b4d43d12c256d192ba36fa15", size = 28195120, upload-time = "2026-05-03T15:36:25.044Z" }, + { url = "https://files.pythonhosted.org/packages/76/bf/dc4966d684c3feddf9d0efd13f00ce6dc903e8527459835bd2aac296f5b1/zuban-0.7.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:fe2dd81d8d887a13dc5d8421d0b175ef012575b2ff1d5ae451a941f0e991cbf6", size = 28663808, upload-time = "2026-05-03T15:36:28.778Z" }, + { url = "https://files.pythonhosted.org/packages/92/52/37eb76f764c5f2738983595abd722b269dd5f9a54725c4979659bec59cd9/zuban-0.7.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77a5858a4f84824c06ee9f3afacd0f4cdb309936bdb842a88df490da876a3404", size = 29364843, upload-time = "2026-05-03T15:36:32.591Z" }, + { url = "https://files.pythonhosted.org/packages/54/63/4a054802ca0d97e550b718349ddc0cacdb7813997f26e90cd12dedd073fb/zuban-0.7.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:61d91b7a1b1da72e2cfa42a3c2fe7258b5bde9603ca5f76675aca79989515ea2", size = 28668800, upload-time = "2026-05-03T15:36:36.812Z" }, + { url = "https://files.pythonhosted.org/packages/3b/f9/91971026d8547c13ddfa6fe6dc6b5fac673d2a428974ea43fbd1b9a484c4/zuban-0.7.2-py3-none-win32.whl", hash = "sha256:7bd36c1874897301267742cd8605d0ccbac6a381697073df81fe9020f5c9953c", size = 10016604, upload-time = "2026-05-03T15:36:39.704Z" }, + { url = "https://files.pythonhosted.org/packages/dc/20/6b030577b144f47ecd543a333b7611039acb0faf43c829521dfc1d4fdd31/zuban-0.7.2-py3-none-win_amd64.whl", hash = "sha256:72ce37001958ca923dd8be4317e11ea7d9b65b74b6a8c3bcd759c7078678e1e0", size = 10701959, upload-time = "2026-05-03T15:36:42.509Z" }, +] diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 000000000..ba87fe401 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,48 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SOURCEDIR = . +SOURCES = +BUILDDIR = _build +PYTHON = python3 +VENVDIR = ./venv +SPHINXBUILD = PATH=$(VENVDIR)/bin:$$PATH sphinx-build + +ALLSPHINXOPTS = -W -b $(BUILDER) -d build/doctrees $(SPHINXOPTS) . build/$(BUILDER) $(SOURCES) + +.PHONY: help clean build html text venv Makefile + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +clean: + -rm -rf build/* $(VENVDIR)/* + +build: + -mkdir -p build + $(SPHINXBUILD) $(ALLSPHINXOPTS) + @echo + +html: BUILDER = html +html: build + @echo "Build finished. The HTML pages are in build/html." + @echo "file://$(shell pwd)/build/html/index.html" + @echo + +text: BUILDER = text +text: build + @echo "Build finished; the text files are in build/text." + +venv: + $(PYTHON) -m venv $(VENVDIR) + $(VENVDIR)/bin/python3 -m pip install -U pip setuptools + $(VENVDIR)/bin/python3 -m pip install -r requirements.txt + @echo "The venv has been created in the $(VENVDIR) directory" + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/README.rst b/docs/README.rst new file mode 100644 index 000000000..c2f4cdf08 --- /dev/null +++ b/docs/README.rst @@ -0,0 +1,50 @@ +Python Typing Documentation +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Reading the docs +================= + +The live documentation for Python's static typing can be found at +`typing.python.org `_. + +Building the docs +================= + +The documentation is built with tools which are not included in this +tree but are maintained separately and are available from +`PyPI `_. + +* `Sphinx `_ +* `python-docs-theme `_ + +The easiest way to install these tools is to create a virtual environment and +install the tools into there. + +Using make +---------- + +To get started on UNIX, you can create a virtual environment with the command :: + + make venv + +That will install all the tools necessary to build the documentation. Assuming +the virtual environment was created in the ``venv`` directory (the default; +configurable with the VENVDIR variable), you can run the following command to +build the HTML output files:: + + make html + +By default, if the virtual environment is not created, the Makefile will +look for instances of sphinxbuild and blurb installed on your process PATH +(configurable with the SPHINXBUILD and BLURB variables). + +Available make targets are: + +* "clean", which removes all build files. + +* "venv", which creates a virtual environment with all necessary tools + installed. + +* "html", which builds standalone HTML files for offline viewing. + +* "text", which builds a plain text file for each source file. diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 000000000..f16401bae --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,55 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + + +# -- Project information ----------------------------------------------------- + +project = 'typing' +copyright = '2021, The Python Typing Team' +author = 'The Python Typing Team' + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'venv', 'README.rst'] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'python_docs_theme' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + +extensions = ['sphinx.ext.intersphinx'] +intersphinx_mapping = {'python': ('https://docs.python.org/3', None)} diff --git a/docs/guides/index.rst b/docs/guides/index.rst new file mode 100644 index 000000000..b13e03486 --- /dev/null +++ b/docs/guides/index.rst @@ -0,0 +1,19 @@ +:orphan: + +****************** +Type System Guides +****************** + +.. + Keep in sync with docs/index.rst. + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + libraries + writing_stubs + modernizing + unreachable + type_narrowing + typing_anti_pitch diff --git a/docs/guides/libraries.rst b/docs/guides/libraries.rst new file mode 100644 index 000000000..8e62af9c5 --- /dev/null +++ b/docs/guides/libraries.rst @@ -0,0 +1,689 @@ +.. _libraries: + +*********************** +Typing Python Libraries +*********************** + +Much of Python's popularity can be attributed to the rich collection of +Python libraries available to developers. Authors of these libraries +play an important role in improving the experience for Python +developers. This document provides some recommendations and guidance for +Python library authors. + +Why provide type annotations? +============================= + +Providing type annotations has the following benefits: + +1. Type annotations help provide users of libraries a better coding + experience by enabling fast and accurate completion suggestions, class and + function documentation, signature help, hover text, auto-imports, etc. +2. Users of libraries are able to use static type checkers to detect issues + with their use of libraries. +3. Type annotations allow library authors to specify an interface contract that + is enforced by tools. This lets the library implementation evolve with less + fear that users are depending on implementation details. In the event of + changes to the library interface, type checkers are able to warn users when + their code is affected. +4. Library authors are able to use static type checking themselves to help + produce high-quality, bug-free implementations. + +.. _providing-type-annotations: + +How to provide type annotations? +================================ + +:pep:`561` documents several ways type information can be provided for a +library: + +- inline type annotations (preferred) +- type stub files included in the package +- a separate companion type stub package +- type stubs in the typeshed repository + +Inline type annotations simply refers to the use of annotations within your +``.py`` files. In contrast, with type stub files, type information lives in +separate ``.pyi`` files; see :ref:`stub-files` and :ref:`writing_stubs` for more +details. + +We recommend using the inline type annotations approach, since it has the +following benefits: + +- Typically requires the least effort to add and maintain +- Users don't have to download additional packages +- Always remains consistent with the implementation +- Allows library authors to type check their own code +- Allows language servers to show users relevant details about the + implementation, such as docstrings and default parameter values + +However, there are cases where inlined type annotations are not possible — most +notably when a library's functionality is implemented in a language +other than Python. + +If you are not interested in providing type annotations for your library, you +could suggest users to contribute type stubs to the +`typeshed `__ project. + +Marking a package as providing type information +----------------------------------------------- + +As specified in :pep:`561`, tools will not treat your package as providing type +information unless it includes a special ``py.typed`` marker file. + +.. note:: + Before marking a package as providing type information, it is best to ensure + that the library's interface is fully annotated. See :ref:`type_completeness` + for more details. + +Inline type annotations +^^^^^^^^^^^^^^^^^^^^^^^ + +A typical directory structure would look like: + +.. code-block:: text + + pyproject.toml + my_great_package/ + __init__.py + stuff.py + py.typed + +Note the py.typed should be located inside the package, along with ``__init__.py``. + +Type stub files included in the package +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It's possible to include a mix of type stub files (``.pyi``) and inline type +annotations (``.py``). One use case for including type stub files in your +package is to provide types for extension modules in your library. A typical +directory structure would look like: + +.. code-block:: text + + pyproject.toml + my_great_package/ + __init__.py + stuff.py + stuff.pyi + py.typed + +The presence of ``.pyi`` files does not affect the Python interpreter at runtime +in any way. However, static type checkers will only look at the ``.pyi`` file and +ignore the corresponding ``.py`` file. + +Companion type stub package +^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +These are often referred to as "stub-only" packages. The name of the stub package +should be the name of the runtime package suffixed with ``-stubs``. The ``py.typed`` +marker file is not necessary for stub-only packages. This approach can be useful +to develop type stubs independently from your library. + +For example: + +.. code-block:: text + + pyproject.toml + my_great_package-stubs/ + __init__.pyi + stuff.pyi + +.. code-block:: toml + + [project] + name = "my-great-package-stubs" + version = "0.1.0" + requires-python = ">=3.10" + + [build-system] + requires = ["uv_build>=0.9.18,<0.10.0"] + build-backend = "uv_build" + +Users are then able to install the stubs-only package separately to provide +types for the original library. + +.. _type_completeness: + +How much of my library needs types? +=================================== + +A "py.typed" library should aim to be type complete so that type +checking and inspection can work to their full extent. Here we say that a +library is “type complete” if all of the symbols +that comprise its :ref:`interface ` have type annotations +that refer to types that are fully known. Private symbols are exempt. + +Type Completeness +----------------- + +The following are best practice recommendations for how to define “type complete”: + +Classes: + +- All class variables, instance variables, and methods that are + “visible” (not overridden) are annotated and refer to known types +- If a class is a subclass of a generic class, type arguments are + provided for each generic type parameter, and these type arguments + are known types + +Functions and Methods: + +- All input parameters have type annotations that refer to known types +- The return parameter is annotated and refers to a known type +- The result of applying one or more decorators results in a known type + +Type Aliases: + +- All of the types referenced by the type alias are known + +Variables: + +- All variables have type annotations that refer to known types + +Type annotations can be omitted in a few specific cases where the type +is obvious from the context: + +- Constants that are assigned simple literal values + (e.g. ``RED = '#F00'`` or ``MAX_TIMEOUT = 50`` or + ``room_temperature: Final = 20``). A constant is a symbol that is + assigned only once and is either annotated with ``Final`` or is named + in all-caps. A constant that is not assigned a simple literal value + requires explicit annotations, preferably with a ``Final`` annotation + (e.g. ``WOODWINDS: Final[list[str]] = ['Oboe', 'Bassoon']``). +- Enum values within an Enum class do not require annotations because + they take on the type of the Enum class. +- Type aliases do not require annotations. A type alias is a symbol + that is defined at a module level with a single assignment where the + assigned value is an instantiable type, as opposed to a class + instance + (e.g. ``Foo = Callable[[Literal["a", "b"]], int | str]`` or + ``Bar = MyGenericClass[int] | None``). +- The “self” parameter in an instance method and the “cls” parameter in + a class method do not require an explicit annotation. +- The return type for an ``__init__`` method does not need to be + specified, since it is always ``None``. +- The following module-level symbols do not require type annotations: + ``__all__``,\ ``__author__``, ``__copyright__``, ``__email__``, + ``__license__``, ``__title__``, ``__uri__``, ``__version__``. +- The following class-level symbols do not require type annotations: + ``__class__``, ``__dict__``, ``__doc__``, ``__module__``, + ``__slots__``. + +Examples of known and unknown types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. code:: python + + + # Variable with unknown type + a = [3, 4, 5] + + # Variable with known type + a: list[int] = [3, 4, 5] + + # Type alias with partially unknown type (because type + # arguments are missing for list and dict) + DictOrList = list | dict + + # Type alias with known type + DictOrList = list[Any] | dict[str, Any] + + # Generic type alias with known type + _T = TypeVar("_T") + DictOrList = list[_T] | dict[str, _T] + + # Function with known type + def func(a: int | None, b: dict[str, float] = {}) -> None: + pass + + # Function with partially unknown type (because type annotations + # are missing for input parameters and return type) + def func(a, b): + pass + + # Function with partially unknown type (because of missing + # type args on Dict) + def func(a: int, b: Dict) -> None: + pass + + # Function with partially unknown type (because return type + # annotation is missing) + def func(a: int, b: dict[str, float]): + pass + + # Decorator with partially unknown type (because type annotations + # are missing for input parameters and return type) + def my_decorator(func): + return func + + # Function with partially unknown type (because type is obscured + # by untyped decorator) + @my_decorator + def func(a: int) -> str: + pass + + + # Class with known type + class MyClass: + height: float = 2.0 + + def __init__(self, name: str, age: int): + self.age: int = age + + @property + def name(self) -> str: + ... + + # Class with partially unknown type + class MyClass: + # Missing type annotation for class variable + height = 2.0 + + # Missing input parameter annotations + def __init__(self, name, age): + # Missing type annotation for instance variable + self.age = age + + # Missing return type annotation + @property + def name(self): + ... + + # Class with partially unknown type + class BaseClass: + # Missing type annotation + height = 2.0 + + # Missing type annotation + def get_stuff(self): + ... + + # Class with known type (because it overrides all symbols + # exposed by BaseClass that have incomplete types) + class DerivedClass(BaseClass): + height: float + + def get_stuff(self) -> str: + ... + + # Class with partially unknown type because base class + # (dict) is generic, and type arguments are not specified. + class DictSubclass(dict): + pass + +.. + TODO: consider moving best practices to their own page? + +Best Practices for Inlined Types +================================ + +Wide vs. Narrow Types +--------------------- + +In type theory, when comparing two types that are related to each other, +the “wider” type is the one that is more general, and the “narrower” +type is more specific. For example, ``Sequence[str]`` is a wider type +than ``list[str]`` because all ``list`` objects are also ``Sequence`` +objects, but the converse is not true. A subclass is narrower than a +class it derives from. A union of types is wider than the individual +types that comprise the union. + +In general, a function input parameter should be annotated with the +widest possible type supported by the implementation. For example, if +the implementation requires the caller to provide an iterable collection +of strings, the parameter should be annotated as ``Iterable[str]``, not +as ``list[str]``. The latter type is narrower than necessary, so if a +user attempts to pass a tuple of strings (which is supported by the +implementation), a type checker will complain about a type +incompatibility. + +As a specific application of the “use the widest type possible” rule, +libraries should generally use immutable forms of container types +instead of mutable forms (unless the function needs to modify the +container). Use ``Sequence`` rather than ``list``, ``Mapping`` rather +than ``dict``, etc. Immutable containers allow for more flexibility +because their type parameters are covariant rather than invariant. A +parameter that is typed as ``Sequence[str | int]`` can accept a +``list[int]``, ``Sequence[str]``, and a ``Sequence[int]``. But a +parameter typed as ``list[str | int]`` is much more restrictive +and accepts only a ``list[str | int]``. + +Overloads +--------- + +If a function or method can return multiple different types and those +types can be determined based on the presence or types of certain +parameters, use the ``@overload`` mechanism described in the +:py:data:`typing.overload` documentation. When overloads +are used within a “.py” file, they must appear prior to the function +implementation, which should not have an ``@overload`` decorator. + +Keyword-only Parameters +----------------------- + +If a function or method is intended to take parameters that are +specified only by name, use the keyword-only separator (``*``). + +.. code:: python + + def create_user(age: int, *, dob: date | None = None): + ... + +.. _annotating-decorators: + +Annotating Decorators +--------------------- + +Decorators modify the behavior of a class or a function. Providing +annotations for decorators is straightforward if the decorator retains +the original signature of the decorated function. + +.. code:: python + + _F = TypeVar("_F", bound=Callable[..., Any]) + + def simple_decorator(_func: _F) -> _F: + """ + Simple decorators are invoked without parentheses like this: + @simple_decorator + def my_function(): ... + """ + ... + + def complex_decorator(*, mode: str) -> Callable[[_F], _F]: + """ + Complex decorators are invoked with arguments like this: + @complex_decorator(mode="easy") + def my_function(): ... + """ + ... + +Decorators that mutate the signature of the decorated function present +challenges for type annotations. The ``ParamSpec`` and ``Concatenate`` +mechanisms described in :pep:`612` provide some help +here, but these are available only in Python 3.10 and newer. More +complex signature mutations may require type annotations that erase the +original signature, thus blinding type checkers and other tools that +provide signature assistance. As such, library authors are discouraged +from creating decorators that mutate function signatures in this manner. + +.. _aliasing-decorators: + +Aliasing Decorators +------------------- + +When writing a library with a couple of decorator factories +(i.e. functions returning decorators, like ``complex_decorator`` from the +:ref:`annotating-decorators` section) it may be tempting to create a shortcut +for a decorator. + +Different type checkers handle :data:`TypeAlias ` involving +:class:`Callable ` in a +different manner, so the most portable and easy way to create a shortcut +is to define a callable :class:`Protocol ` as described in the +:ref:`callback-protocols` section of the Typing Specification. + +There is already a :class:`Protocol ` called +``IdentityFunction`` defined in +`_typeshed `_: + +.. code:: python + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from _typeshed import IdentityFunction + + def decorator_factory(*, mode: str) -> "IdentityFunction": + """ + Decorator factory is invoked with arguments like this: + @decorator_factory(mode="easy") + def my_function(): ... + """ + ... + +For non-trivial decorators with custom logic, it is still possible +to define a custom protocol using :class:`ParamSpec ` +and :data:`Concatenate ` mechanisms: + +.. code:: python + + class Client: ... + + P = ParamSpec("P") + R = TypeVar("R") + + class PClientInjector(Protocol): + def __call__(self, _: Callable[Concatenate[Client, P], R], /) -> Callable[P, R]: + ... + + def inject_client(service: str) -> PClientInjector: + """ + Decorator factory is invoked with arguments like this: + @inject_client("testing") + def my_function(client: Client, value: int): ... + + my_function then takes only value + """ + + +Generic Classes and Functions +----------------------------- + +Classes and functions that can operate in a generic manner on various +types should declare themselves as generic using standard typing +mechanisms such as ``TypeVar``. +This includes the use of ``TypeVar`` symbols. Typically, a ``TypeVar`` +should be private to the file that declares it, and should therefore +begin with an underscore. + +Type Aliases +------------ + +Type aliases are symbols that refer to other types. Generic type aliases +(those that refer to unspecialized generic classes) are supported by +most type checkers. + +:pep:`613` provides a way +to explicitly designate a symbol as a type alias using the new TypeAlias +annotation. + +.. code:: python + + # Simple type alias + FamilyPet = Cat | Dog | GoldFish + + # Generic type alias + ListOrTuple = list[_T] | tuple[_T, ...] + + # Recursive type alias + TreeNode = LeafNode | list["TreeNode"] + + # Explicit type alias using TypeAlias + StrOrInt: TypeAlias = str | int + +Abstract Classes and Methods +---------------------------- + +Classes that must be subclassed should derive from ``ABC``, and methods +or properties that must be overridden should be decorated with the +``@abstractmethod`` decorator. This allows type checkers to validate +that the required methods have been overridden and provide developers +with useful error messages when they are not. It is customary to +implement an abstract method by raising a ``NotImplementedError`` +exception. + +.. code:: python + + from abc import ABC, abstractmethod + + class Hashable(ABC): + @property + @abstractmethod + def hash_value(self) -> int: + """Subclasses must override""" + raise NotImplementedError() + + @abstractmethod + def print(self) -> str: + """Subclasses must override""" + raise NotImplementedError() + +Final Classes and Methods +------------------------- + +Classes that are not intended to be subclassed should be decorated as +``@final`` as described in :pep:`591`. The same decorator +can also be used to specify methods that cannot be overridden by +subclasses. + +Literals +-------- + +Type annotations should make use of the Literal type where appropriate, +as described in :pep:`586`. +Literals allow for more type specificity than their non-literal +counterparts. + +Constants +--------- + +Constant values (those that are read-only) can be specified using the +Final annotation as described in :pep:`591`. + +Type checkers will also typically treat variables that are named using +all upper-case characters as constants. + +In both cases, it is OK to omit the declared type of a constant if it is +assigned a literal str, int, float, bool or None value. In such cases, +the type inference rules are clear and unambiguous, and adding a literal +type annotation would be redundant. + +.. code:: python + + # All-caps constant with inferred type + COLOR_FORMAT_RGB = "rgb" + + # All-caps constant with explicit type + COLOR_FORMAT_RGB: Literal["rgb"] = "rgb" + LATEST_VERSION: tuple[int, int] = (4, 5) + + # Final variable with inferred type + ColorFormatRgb: Final = "rgb" + + # Final variable with explicit type + ColorFormatRgb: Final[Literal["rgb"]] = "rgb" + LATEST_VERSION: Final[tuple[int, int]] = (4, 5) + +Typed Dictionaries, Data Classes, and Named Tuples +-------------------------------------------------- + +If your library runs only on newer versions of Python, you are +encouraged to use some of the new type-friendly classes. + +NamedTuple (described in :pep:`484`) is preferred over +namedtuple. + +Data classes (described in :pep:`557`) are preferred over +untyped dictionaries. + +TypedDict (described in :pep:`589`) is preferred over +untyped dictionaries. + +Compatibility with Older Python Versions +======================================== + +Each new version of Python from 3.5 onward has introduced new typing +constructs. This presents a challenge for library authors who want to +maintain runtime compatibility with older versions of Python. This +section documents several techniques that can be used to add types while +maintaining backward compatibility. + +Quoted Annotations +------------------ + +Type annotations for variables, parameters, and return types can be +placed in quotes. The Python interpreter will then ignore them, whereas +a type checker will interpret them as type annotations. + +.. code:: python + + # Older versions of Python do not support subscripting + # for the OrderedDict type, so the annotation must be + # enclosed in quotes. + def get_config(self) -> "OrderedDict[str, str]": + return self._config + +Type Comment Annotations +------------------------ + +Python 3.0 introduced syntax for parameter and return type annotations, +as specified in :pep:`484`. +Python 3.6 introduced support for variable type annotations, as +specified in :pep:`526`. + +If you need to support older versions of Python, type annotations can +still be provided as “type comments”. These comments take the form +``# type:``. + +.. code:: python + + class Foo: + # Variable type comments go at the end of the line + # where the variable is assigned. + timeout = None # type: Optional[int] + + # Function type comments can be specified on the + # line after the function signature. + def send_message(self, name, length): + # type: (str, int) -> None + ... + + # Function type comments can also specify the type + # of each parameter on its own line. + def receive_message( + self, + name, # type: str + length # type: int + ): + # type: () -> Message + ... + +typing_extensions +----------------- + +New type features that require runtime support are typically included in +the stdlib ``typing`` module. Where possible, these new features are +back-ported to a runtime library called ``typing_extensions`` that works +with older Python runtimes. + +TYPE_CHECKING +------------- + +The ``typing`` module exposes a variable called ``TYPE_CHECKING`` which +has a value of False within the Python runtime but a value of True when +the type checker is performing its analysis. This allows type checking +statements to be conditionalized. + +Care should be taken when using ``TYPE_CHECKING`` because behavioral +changes between type checking and runtime could mask problems that the +type checker would otherwise catch. + +Non-Standard Type Behaviors +=========================== + +Type annotations provide a way to annotate typical type behaviors, but +some classes implement specialized, non-standard behaviors that cannot +be described using standard type annotations. For now, such types need +to be annotated as Any, which is unfortunate because the benefits of +static typing are lost. + +Docstrings +========== + +Docstrings should be provided for all classes, functions, and methods in +the interface. They should be formatted according to :pep:`257`. + +There is currently no single agreed-upon standard for function and +method docstrings, but several common variants have emerged. We +recommend using one of these variants. diff --git a/docs/guides/modernizing.rst b/docs/guides/modernizing.rst new file mode 100644 index 000000000..05035b7bf --- /dev/null +++ b/docs/guides/modernizing.rst @@ -0,0 +1,377 @@ +.. role:: python(code) + :language: python + +.. role:: t-ext(class) + +.. _modernizing: + +************************************** +Modernizing Superseded Typing Features +************************************** + +Introduction +============ + +This guide helps to modernize your code by replacing older typing features +with their modern equivalents. Not all features described here are obsolete, +but they are superseded by more modern alternatives, which are recommended to use. + +These newer features are not available in all Python versions, although +some features are available as backports from the +`typing-extensions `_ +package, or require quoting or using :python:`from __future__ import annotations` +when running on Python 3.13 or below. +Each section states the minimum Python version required to use the +feature, whether it is available in typing-extensions, and whether it is +available using quoting. + +.. tip:: + + Tools such as `pyupgrade `__, + `ruff `__ and/or + `com2ann `__ can automatically perform + some of these refactorings for you. + +.. note:: + + The latest version of typing-extensions is available for all Python + versions that have not reached their end of life, but not necessarily for + older versions. + +.. note:: + + :python:`from __future__ import annotations` is available since Python 3.7 + and became unnecessary starting with Python 3.14. + This only has an effect inside type annotations, while quoting is still + required outside. For example:: + + from __future__ import annotations + from typing_extensions import TypeAlias + + def f(x: Foo) -> Foo: ... # the future import is sufficient + Alias: TypeAlias = "Foo" # this forward reference requires quoting + + class Foo: pass + +.. _modernizing-type-comments: + +Type Comments +============= + +*Alternative available since:* Python 3.0, 3.6 + +Type comments were originally introduced to support type annotations in +Python 2 and variable annotations before Python 3.6. While most type checkers +still support them, they are considered obsolete, and type checkers are +not required to support them. + +For example, replace:: + + x = 3 # type: int + def f(x, y): # type: (int, int) -> int + return x + y + +with:: + + x: int = 3 + def f(x: int, y: int) -> int: + return x + y + +When using forward references or types only available during type checking, +it's necessary to either use :python:`from __future__ import annotations` +(available since Python 3.7) or to quote the type:: + + def f(x: "Parrot") -> int: ... + + class Parrot: ... + +When using Python 3.14 and up, quoting forward references is no longer +necessary inside type annotations. + +.. _modernizing-typing-text: + +``typing.Text`` +=============== + +*Alternative available since:* Python 3.0 + +:class:`typing.Text` was a type alias intended for Python 2 compatibility. +It is equivalent to :class:`str` and should be replaced with it. +For example, replace:: + + from typing import Text + + def f(x: Text) -> Text: ... + +with:: + + def f(x: str) -> str: ... + +.. _modernizing-typed-dict: + +``typing.TypedDict`` Legacy Forms +================================= + +*Alternative available since:* Python 3.6 + +:class:`TypedDict ` supports two legacy forms for +supporting Python versions that don't support variable annotations. +Replace these two variants:: + + from typing import TypedDict + + FlyingSaucer = TypedDict("FlyingSaucer", {"x": int, "y": str}) + FlyingSaucer = TypedDict("FlyingSaucer", x=int, y=str) + +with:: + + class FlyingSaucer(TypedDict): + x: int + y: str + +But the dictionary form is still necessary if the keys are not valid Python +identifiers:: + + Airspeeds = TypedDict("Airspeeds", {"unladen-swallow": int}) + +.. _modernizing-generics: + +Generics in the ``typing`` Module +================================= + +*Alternative available since:* Python 3.0 (quoted), Python 3.9 (unquoted) + +Originally, the :mod:`typing` module provided aliases for built-in types that +accepted type parameters. Since Python 3.9, these aliases are no longer +necessary, and can be replaced with the built-in types. For example, +replace:: + + from typing import Dict, List + + def f(x: List[int]) -> Dict[str, int]: ... + +with:: + + def f(x: list[int]) -> dict[str, int]: ... + +This affects the following types: + +* :class:`typing.Dict` (→ :class:`dict`) +* :class:`typing.FrozenSet` (→ :class:`frozenset`) +* :class:`typing.List` (→ :class:`list`) +* :class:`typing.Set` (→ :class:`set`) +* :data:`typing.Tuple` (→ :class:`tuple`) + +The :mod:`typing` module also provided aliases for certain standard library +types that accepted type parameters. Since Python 3.9, these aliases are no +longer necessary, and can be replaced with the proper types. For example, +replace:: + + from typing import DefaultDict, Pattern + + def f(x: Pattern[str]) -> DefaultDict[str, int]: ... + +with:: + + from collections import defaultdict + from re import Pattern + + def f(x: Pattern[str]) -> defaultdict[str, int]: ... + +This affects the following types: + +* :class:`typing.Deque` (→ :class:`collections.deque`) +* :class:`typing.DefaultDict` (→ :class:`collections.defaultdict`) +* :class:`typing.OrderedDict` (→ :class:`collections.OrderedDict`) +* :class:`typing.Counter` (→ :class:`collections.Counter`) +* :class:`typing.ChainMap` (→ :class:`collections.ChainMap`) +* :class:`typing.Awaitable` (→ :class:`collections.abc.Awaitable`) +* :class:`typing.Coroutine` (→ :class:`collections.abc.Coroutine`) +* :class:`typing.AsyncIterable` (→ :class:`collections.abc.AsyncIterable`) +* :class:`typing.AsyncIterator` (→ :class:`collections.abc.AsyncIterator`) +* :class:`typing.AsyncGenerator` (→ :class:`collections.abc.AsyncGenerator`) +* :class:`typing.Iterable` (→ :class:`collections.abc.Iterable`) +* :class:`typing.Iterator` (→ :class:`collections.abc.Iterator`) +* :class:`typing.Generator` (→ :class:`collections.abc.Generator`) +* :class:`typing.Reversible` (→ :class:`collections.abc.Reversible`) +* :class:`typing.Container` (→ :class:`collections.abc.Container`) +* :class:`typing.Collection` (→ :class:`collections.abc.Collection`) +* :data:`typing.Callable` (→ :class:`collections.abc.Callable`) +* :class:`typing.AbstractSet` (→ :class:`collections.abc.Set`), note the change in name +* :class:`typing.MutableSet` (→ :class:`collections.abc.MutableSet`) +* :class:`typing.Mapping` (→ :class:`collections.abc.Mapping`) +* :class:`typing.MutableMapping` (→ :class:`collections.abc.MutableMapping`) +* :class:`typing.Sequence` (→ :class:`collections.abc.Sequence`) +* :class:`typing.MutableSequence` (→ :class:`collections.abc.MutableSequence`) +* :class:`typing.ByteString` (→ :class:`collections.abc.ByteString`), but see :ref:`modernizing-byte-string` +* :class:`typing.MappingView` (→ :class:`collections.abc.MappingView`) +* :class:`typing.KeysView` (→ :class:`collections.abc.KeysView`) +* :class:`typing.ItemsView` (→ :class:`collections.abc.ItemsView`) +* :class:`typing.ValuesView` (→ :class:`collections.abc.ValuesView`) +* :class:`typing.ContextManager` (→ :class:`contextlib.AbstractContextManager`), note the change in name +* :class:`typing.AsyncContextManager` (→ :class:`contextlib.AbstractAsyncContextManager`), note the change in name +* :class:`typing.Pattern` (→ :class:`re.Pattern`) +* :class:`typing.Match` (→ :class:`re.Match`) + +.. _modernizing-union: + +``typing.Union`` and ``typing.Optional`` +======================================== + +*Alternative available since:* Python 3.0 (quoted), Python 3.10 (unquoted) + +While :data:`Union ` and :data:`Optional ` are +not considered obsolete, using the ``|`` (pipe) operator is often more +readable. :python:`Union[X, Y]` is equivalent to :python:`X | Y`, while +:python:`Optional[X]` is equivalent to :python:`X | None`. + +For example, replace:: + + from typing import Optional, Union + + def f(x: Optional[int]) -> Union[int, str]: ... + +with:: + + def f(x: int | None) -> int | str: ... + +.. _modernizing-no-return: + +``typing.NoReturn`` +=================== + +*Alternative available since:* Python 3.11, typing-extensions + +Python 3.11 introduced :data:`typing.Never` as an alias to +:data:`typing.NoReturn` for use in annotations that are not +return types. For example, replace:: + + from typing import NoReturn + + def f(x: int, y: NoReturn) -> None: ... + +with:: + + from typing import Never # or typing_extensions.Never + + def f(x: int, y: Never) -> None: ... + +But keep ``NoReturn`` for return types:: + + from typing import NoReturn + + def f(x: int) -> NoReturn: ... + +.. _modernizing-type-aliases: + +Type Aliases +============ + +*Alternative available since:* Python 3.12 (keyword); Python 3.10, typing-extensions + +Originally, type aliases were defined using a simple assignment:: + + IntList = list[int] + +Python 3.12 introduced the :keyword:`type` keyword to define type aliases:: + + type IntList = list[int] + +Code supporting older Python versions should use +:data:`TypeAlias `, introduced in Python 3.10, but also +available in typing-extensions, instead:: + + from typing import TypeAlias # or typing_extensions.TypeAlias + + IntList: TypeAlias = list[int] + +.. _modernizing-user-generics: + +User Defined Generics +===================== + +*Alternative available since:* Python 3.12 + +Python 3.12 introduced new syntax for defining generic classes. Previously, +generic classes had to derive from :class:`typing.Generic` (or another +generic class) and defined the type variable using :class:`typing.TypeVar`. +For example:: + + from typing import Generic, TypeVar + + T = TypeVar("T") + + class Brian(Generic[T]): ... + class Reg(int, Generic[T]): ... + +Starting with Python 3.12, the type variable doesn't need to be declared +using ``TypeVar``, and instead of deriving the class from ``Generic``, the +following syntax can be used:: + + class Brian[T]: ... + class Reg[T](int): ... + +.. _modernizing-byte-string: + +``typing.ByteString`` +===================== + +*Alternative available since:* Python 3.0; Python 3.12, typing-extensions + +:class:`ByteString ` was originally intended to be a type +alias for "byte-like" types, i.e. :class:`bytes`, :class:`bytearray`, and +:class:`memoryview`. In practice, this +is seldom exactly what is needed. Use one of these alternatives instead: + +* Just :class:`bytes` is often sufficient, especially when not declaring + a public API. +* For items that accept any type that supports the + :ref:`buffer protocol `, use :class:`collections.abc.Buffer` + (available since Python 3.12) or :t-ext:`typing_extensions.Buffer`. +* Otherwise, use a union of :class:`bytes`, :class:`bytearray`, + :class:`memoryview`, and/or any other types that are accepted. + +``typing.Hashable`` and ``typing.Sized`` +======================================== + +*Alternative available since:* Python 3.12, typing-extensions + +The following abstract base classes from :mod:`typing` were added to +:mod:`collections.abc` in Python 3.12: + +* :class:`typing.Hashable` (→ :class:`collections.abc.Hashable`) +* :class:`typing.Sized` (→ :class:`collections.abc.Sized`) + +Update your imports to use the new locations:: + + from collections.abc import Hashable, Sized + + def f(x: Hashable) -> Sized: ... + +``typing.TypeGuard`` +==================== + +*Available since:* Python 3.13, typing-extensions + +:data:`TypeIs ` is an alternative to +:data:`TypeGuard ` that usually has more intuitive +behavior, but has other restrictions. See the documentation for +:data:`TypeIs ` for more information. + +Review existing uses of :data:`TypeGuard ` to see if they +should be replaced with :data:`TypeIs `. + +``from __future__ import annotations`` +====================================== + +*Available since:* Python 3.14 + +Starting with Python 3.14, behavior similar to using +``from __future__ import annotations`` became the default. When running on +Python 3.14 and up, it's no longer necessary to use the future import to defer +evaluation of annotations. However, quoting is still necessary for types used +outside of annotations and :keyword:`type` statements, for example when using +:data:`TypeAlias `. + +Remove unnecessary uses of ``from __future__ import annotations`` for code +only supporting Python 3.14 and up. diff --git a/docs/guides/type_narrowing.rst b/docs/guides/type_narrowing.rst new file mode 100644 index 000000000..c359d9314 --- /dev/null +++ b/docs/guides/type_narrowing.rst @@ -0,0 +1,342 @@ +************** +Type Narrowing +************** + +Python programs often contain symbols that take on multiple types within a +single given scope and that are distinguished by a conditional check at +runtime. For example, here the variable *name* can be either a ``str`` or +``None``, and the ``if name is not None`` narrows it down to just ``str``:: + + def maybe_greet(name: str | None) -> None: + if name is not None: + print("Hello, " + name) + +This technique is called *type narrowing*. +To avoid false positives on such code, type checkers understand +various kinds of conditional checks that are used to narrow types in Python code. +The exact set of type narrowing constructs that a type checker understands +is not specified and varies across type checkers. Commonly understood +patterns include: + +* ``if x is not None`` +* ``if x`` +* ``if isinstance(x, SomeType)`` +* ``if callable(x)`` + +In addition to narrowing local variables, type checkers usually also support +narrowing instance attributes and sequence members, such as +``if x.some_attribute is not None`` or ``if x[0] is not None``, though the exact +conditions for this behavior differ between type checkers. + +Consult your type checker's documentation for more information on the type +narrowing constructs it supports. + +The type system also includes two ways to create *user-defined* type narrowing +functions: :py:data:`typing.TypeIs` and :py:data:`typing.TypeGuard`. These +are useful if you want to reuse a more complicated check in multiple places, or +you use a check that the type checker doesn't understand. In these cases, you +can define a ``TypeIs`` or ``TypeGuard`` function to perform the check and allow type checkers +to use it to narrow the type of a variable. Between the two, ``TypeIs`` usually +has the more intuitive behavior, so we'll talk about it more; see +:ref:`below ` for a comparison. + +How to use ``TypeIs`` and ``TypeGuard`` +--------------------------------------- + +A ``TypeIs`` function takes a single argument and is annotated as returning +``TypeIs[T]``, where ``T`` is the type that you want to narrow to. The function +must return ``True`` if the argument is of type ``T``, and ``False`` otherwise. +The function can then be used in ``if`` checks, just like you would use ``isinstance()``. +For example:: + + from typing import Literal, TypeIs + + type Direction = Literal["N", "E", "S", "W"] + + def is_direction(x: str) -> TypeIs[Direction]: + return x in {"N", "E", "S", "W"} + + def maybe_direction(x: str) -> None: + if is_direction(x): + print(f"{x} is a cardinal direction") + else: + print(f"{x} is not a cardinal direction") + +A ``TypeGuard`` function looks similar and is used in the same way, but the +type narrowing behavior is different, as discussed in :ref:`the section below `. + +Depending on the version of Python you are running, you will be able to +import ``TypeIs`` and ``TypeGuard`` either from the standard library :py:mod:`typing` +module or from the third-party ``typing_extensions`` module: + +* ``TypeIs`` is in ``typing`` starting from Python 3.13 and in ``typing_extensions`` + starting from version 4.10.0. +* ``TypeGuard`` is in ``typing`` starting from Python 3.10 and in ``typing_extensions`` + starting from version 3.10.0.0. + + +Writing a correct ``TypeIs`` function +------------------------------------- + +A ``TypeIs`` function allows you to override your type checker's type narrowing +behavior. This is a powerful tool, but it can be dangerous because an incorrectly +written ``TypeIs`` function can lead to unsound type checking, and type checkers +cannot detect such errors. + +For a function returning ``TypeIs[T]`` to be correct, it must return ``True`` if and only if +the argument is of type ``T``, and ``False`` otherwise. If this condition is +not met, the type checker may infer incorrect types. + +Below are some examples of correct and incorrect ``TypeIs`` functions:: + + from typing import TypeIs + + # Correct + def is_int(x: object) -> TypeIs[int]: + return isinstance(x, int) + + # Incorrect: does not return True for all ints + def is_positive_int(x: object) -> TypeIs[int]: + return isinstance(x, int) and x > 0 + + # Incorrect: returns True for some non-ints + def is_real_number(x: object) -> TypeIs[int]: + return isinstance(x, (int, float)) + +This function demonstrates some errors that can occur when using a poorly written +``TypeIs`` function. These errors are not detected by type checkers:: + + def caller(x: int | str, y: int | float) -> None: + if is_positive_int(x): # narrowed to int + print(x + 1) + else: # narrowed to str (incorrectly) + print("Hello " + x) # runtime error if x is a negative int + + if is_real_number(y): # narrowed to int + # Because of the incorrect TypeIs, this branch is taken at runtime if + # y is a float. + print(y.bit_count()) # runtime error: this method exists only on int, not float + else: # narrowed to float (though never executed at runtime) + pass + +Here is an example of a correct ``TypeIs`` function for a more complicated type:: + + from typing import TypedDict, TypeIs + + class Point(TypedDict): + x: int + y: int + + def is_point(obj: object) -> TypeIs[Point]: + return ( + isinstance(obj, dict) + and all(isinstance(key, str) for key in obj) + and isinstance(obj.get("x"), int) + and isinstance(obj.get("y"), int) + ) + +.. _`guide-type-narrowing-typeis-typeguard`: + +``TypeIs`` and ``TypeGuard`` +---------------------------- + +:py:data:`typing.TypeIs` and :py:data:`typing.TypeGuard` are both tools for narrowing the type of a variable +based on a user-defined function. Both can be used to annotate functions that take an +argument and return a boolean depending on whether the input argument is compatible with +the narrowed type. These functions can then be used in ``if`` checks to narrow the type +of a variable. + +``TypeIs`` usually has the more intuitive behavior, but it +introduces more restrictions. ``TypeGuard`` is the right tool to use if: + +* You want to narrow to a type that is not :term:`assignable` to the input type, for example + from ``list[object]`` to ``list[int]``. ``TypeIs`` only allows narrowing between + compatible types. +* Your function does not return ``True`` for all input values that are members of + the narrowed type. For example, you could have a ``TypeGuard[int]`` that returns ``True`` + only for positive integers. + +``TypeIs`` and ``TypeGuard`` differ in the following ways: + +* ``TypeIs`` requires the narrowed type to be :term:`assignable` to the input type, while + ``TypeGuard`` does not. +* When a ``TypeGuard`` function returns ``True``, type checkers narrow the type of the + variable to exactly the ``TypeGuard`` type. When a ``TypeIs`` function returns ``True``, + type checkers can infer a more precise type combining the previously known type of the + variable with the ``TypeIs`` type. (This is known as an "intersection type".) +* When a ``TypeGuard`` function returns ``False``, type checkers cannot narrow the type of + the variable at all. When a ``TypeIs`` function returns ``False``, type checkers can narrow + the type of the variable to exclude the ``TypeIs`` type. + +This behavior can be seen in the following example:: + + from typing import TypeGuard, TypeIs, reveal_type, final + + class Base: ... + class Child(Base): ... + @final + class Unrelated: ... + + def is_base_typeguard(x: object) -> TypeGuard[Base]: + return isinstance(x, Base) + + def is_base_typeis(x: object) -> TypeIs[Base]: + return isinstance(x, Base) + + def use_typeguard(x: Child | Unrelated) -> None: + if is_base_typeguard(x): + reveal_type(x) # Base + else: + reveal_type(x) # Child | Unrelated + + def use_typeis(x: Child | Unrelated) -> None: + if is_base_typeis(x): + reveal_type(x) # Child + else: + reveal_type(x) # Unrelated + + +Safety and soundness +-------------------- + +While type narrowing is important for typing real-world Python code, many +forms of type narrowing are unsafe in the presence of mutability. Type checkers +attempt to limit type narrowing in a way that minimizes unsafety while remaining +useful, but not all safety violations can be detected. + +``isinstance()`` and ``issubclass()`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While the exact behavior is not standardized, type checkers usually support +narrowing terms based on calls to ``isinstance()`` and ``issubclass()``. However, +these functions have complex runtime behavior that type checkers cannot fully +capture: they call the :py:meth:`__instancecheck__` and :py:meth:`__subclasscheck__` +special methods, which may include arbitrarily complex logic. + +This affects some parts of the standard library that rely on these methods. +:py:class:`abc.ABC` allows registration of subclasses using the ``.register()`` method, +but type checkers usually will not recognize this method. :ref:`Runtime-checkable +protocols ` support runtime ``isinstance()`` checks, but their +behavior does not exactly match the type system (for example, the types of method +parameters are not checked). + +Incorrect ``TypeIs`` and ``TypeGuard`` functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Both ``TypeIs`` and ``TypeGuard`` rely on the user writing a function that +returns whether an object is of a particular type. However, the type checker +does not validate whether the function actually behaves as expected. If it +does not, the type checker's narrowing behavior will not match what happens +at runtime.:: + + from typing import TypeIs + + def is_str(x: object) -> TypeIs[str]: + return True + + def takes_str_or_int(x: str | int) -> None: + if is_str(x): + print(x + " is a string") # runtime error + +To avoid this problem, every ``TypeIs`` and ``TypeGuard`` function should be +carefully reviewed and tested. + +Unsound ``TypeGuard`` narrowing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Unlike ``TypeIs``, ``TypeGuard`` can narrow to a type that is not a subtype of the +original type. This allows for unsafe behavior with invariant data structures:: + + from typing import Any, TypeGuard + + def is_int_list(x: list[Any]) -> TypeGuard[list[int]]: + return all(isinstance(i, int) for i in x) + + def maybe_mutate_list(x: list[Any]) -> None: + if is_int_list(x): + x.append(0) # OK, x is narrowed to list[int] + + def takes_bool_list(x: list[bool]) -> None: + maybe_mutate_list(x) + reveal_type(x) # list[bool] + assert all(isinstance(i, bool) for i in x) # fails at runtime + + takes_bool_list([True, False]) + +To avoid this problem, use ``TypeIs`` instead of ``TypeGuard`` where possible. +If you must use ``TypeGuard``, avoid narrowing across incompatible types. +Prefer using covariant, immutable types in parameter annotations (e.g., +``Sequence`` or ``Iterable`` instead of ``list``). If you do this, it is more likely +that you'll be able to use ``TypeIs`` to implement your type narrowing functions. + +Invalidated assumptions +~~~~~~~~~~~~~~~~~~~~~~~ + +One category of safety issues relates to the fact that type narrowing relies +on a condition that was established at one point in the code and is then relied +on later: we first check ``if x is not None``, then rely on ``x`` not being ``None``. +However, in the meantime other code may have run (for example, in another thread, +another coroutine, or simply some code that was invoked by a function call) and +invalidated the earlier condition. + +Such problems are most likely when narrowing is performed on elements of mutable +objects, but it is possible to construct unsafe examples even using only narrowing +of local variables:: + + def maybe_greet(name: str | None) -> None: + def set_it_to_none(): + nonlocal name + name = None + + if name is not None: + set_it_to_none() + # fails at runtime, no error in current type checkers + print("Hello " + name) + + maybe_greet("Guido") + +A more realistic example might involve multiple coroutines mutating a list:: + + import asyncio + from typing import Sequence, TypeIs + + def is_int_sequence(x: Sequence[object]) -> TypeIs[Sequence[int]]: + return all(isinstance(i, int) for i in x) + + async def takes_seq(x: Sequence[int | None]): + if is_int_sequence(x): + await asyncio.sleep(2) + print("The total is", sum(x)) # fails at runtime + + async def takes_list(x: list[int | None]): + t = asyncio.create_task(takes_seq(x)) + await asyncio.sleep(1) + x.append(None) + await t + + if __name__ == "__main__": + lst: list[int | None] = [1, 2, 3] + asyncio.run(takes_list(lst)) + +These issues unfortunately cannot be fully detected by the current +Python type system. (An example of a different programming language that +does solve this problem is Rust, which uses a system called +`ownership `__.) +To avoid such issues, avoid using type narrowing on objects that are mutated +from other parts of the code. + + +See also +-------- + +* Type checker documentation on type narrowing + + * `Mypy `__ + * `Pyright `__ + +* PEPs related to type narrowing. These contain additional discussion + and motivation for current type checker behaviors. + + * :pep:`647` (introduced ``TypeGuard``) + * (*withdrawn*) :pep:`724` (proposed change to ``TypeGuard`` behavior) + * :pep:`742` (introduced ``TypeIs``) diff --git a/docs/guides/typing_anti_pitch.rst b/docs/guides/typing_anti_pitch.rst new file mode 100644 index 000000000..fbd56767c --- /dev/null +++ b/docs/guides/typing_anti_pitch.rst @@ -0,0 +1,92 @@ +.. _typing-anti-pitch: + +Reasons to avoid static type checking +===================================== + +In the words of :pep:`484`: + + It should also be emphasized that Python will remain a dynamically typed language, and + the authors have no desire to ever make type hints mandatory, even by convention. + +The idea that dynamism in Python is a strength of the language is reflected in the fact that +Python's type system is gradual. See :pep:`483` for details, but the long and short of this is +that you can add static types to your codebase only to the extent that you want to, and static +type checkers and other tools should be able to put up with this. + +It's also worth noting that "static type checking" encompasses a spectrum of possible degrees of +strictness. On the one hand, you can set yourself up so that your type checker does almost nothing. +On the other -- well, I love type checking, but I would quit Python if I had to enable all +possible strictness checks that type checkers offer. + +Anyway, with all that said, here's a list of possible reasons to not use static type checking +in Python: + +* You simply don't want to. Python is a tool that is meant to serve you. Python is a big tent, + multi-paradigm language that generally allows you to do things in the way that best suits your + needs, as best determined by you. + +* Type annotations can both help and hurt readability. While type annotations can serve both + humans and machines, particularly complex annotations or changes to idioms serve machines more + than they do humans. Readability counts. + +* The cost-benefit ratio isn't good enough. Pleasing static type checkers requires a non-zero amount + of busy work. If this isn't worth the extra confidence you get, you shouldn't add static type + checking. + +* Your codebase fits in your developers' heads. Opinions vary, but people tend to agree that at + some number of developers and lines of code, static type checking confers significantly more + benefit. You don't feel like you're there yet. + +* If you maintain high test coverage, that might provide sufficient quality assurance for your + needs (acknowledging that static type checking and tests enforce different things; static type + checking usually cannot validate logic, tests can often not prove invariants of your code to + hold). + +* Your codebase is old, large and has been working fine without static type checking for years. + While Python's type system is designed to + `allow gradual adoption of static type checking `_, + the total cost of adding type annotations to a large extant codebase can be prohibitive. + +* Your application uses a particularly dynamic framework or your library does enough dynamic things + that type checking would be unlikely to help your developers and users. Migrating application + frameworks could be costly. Either a) redesigning your library in ways that static type checkers + could better understand or b) figuring out clever type annotations to twist the arms of type + checkers would take a lot of effort. + +* Your codebase has suffered at the hands of `Hyrum's Law `_ + and all possible observable behaviour is depended on. In order to avoid false positives for your + users, all your types end up being either a) complicated ``Protocol``\s that are hard to maintain, + or b) ``Any`` in which case there's not much point. (On the other hand, static type checking could + be a good solution for communicating to users what behaviour they should be allowed to rely on) + +* You're not opposed to type checking in theory, but you dislike Python type checkers in practice. + Maybe they don't understand enough of the idioms you use, maybe you'd like them to infer more + instead of relying on explicit annotations, maybe they're too slow, maybe they don't integrate + well with your editor, maybe they're too hard to configure. Whatever the reason -- it just doesn't + work for your project. + +* Type checking in Python isn't actually strict enough, powerful enough or expressive enough for + you. Python type checkers end up making various decisions out of pragmatism, or due to limited + resources, and these decisions might not be the ones for you. This might mean that typed Python + simply isn't the right language for you, or you need to find other methods to enforce the + properties you desire. + +Advice for maintainers of untyped libraries +******************************************* + +You've made the decision that adding static types isn't the right choice for your library. But +perhaps you'd still like to help your users who do use static type checking -- and maybe you have +some enthusiastic would-be contributors willing to help with this. + +One option is encourage such contributors to publish a :pep:`561` stub-only package that is +maintained separately from your main project. They could also contribute these stubs to the +`typeshed `_ project. + +Note that if you're willing to maintain the stubs, but you don't wish to have them inline and don't +want to statically type check your code, you can accomplish this by distributing type stubs inside +your package. See :ref:`libraries` for more information. See :ref:`writing_stubs` for advice on +how to help maintain type stubs. + +If more users pester you about adding static types, feel free to link them to this document. And if +you ever change your mind, make sure to check out some of the other guides in this documentation, +and ask any questions you have over at `Python's typing discussions `_. diff --git a/docs/guides/unreachable.rst b/docs/guides/unreachable.rst new file mode 100644 index 000000000..e72fe2f6b --- /dev/null +++ b/docs/guides/unreachable.rst @@ -0,0 +1,161 @@ +.. _unreachable: + +******************************************** +Unreachable Code and Exhaustiveness Checking +******************************************** + +Sometimes it is necessary to write code that should never execute, and +sometimes we write code that we expect to execute, but that is actually +unreachable. The type checker can help in both cases. + +In this guide, we'll cover: + +- ``Never``, the primitive type used for unreachable code +- ``assert_never()``, a helper for exhaustiveness checking +- Directly marking code as unreachable +- Detecting unexpectedly unreachable code + +``Never`` and ``NoReturn`` +========================== + +Type theory has a concept of a +`bottom type `__, +a type that has no values. Concretely, this can be used to represent +the return type of a function that never returns, or the argument type +of a function that may never be called. You can also think of the +bottom type as a union with no members. + +The Python type system has long provided a type called ``NoReturn``. +While it was originally meant only for functions that never return, +this concept is naturally extended to the bottom type in general, and all +type checkers treat ``NoReturn`` as a general bottom type. + +To make the meaning of this type more explicit, Python 3.11 and +typing-extensions 4.1 add a new primitive, ``Never``. To type checkers, +it has the same meaning as ``NoReturn``. + +In this guide, we'll use ``Never`` for the bottom type, but if you cannot +use it yet, you can always use ``typing.NoReturn`` instead. + +``assert_never()`` and Exhaustiveness Checking +============================================== + +The ``Never`` type can be leveraged to perform static exhaustiveness checking, +where we use the type checker to make sure that we covered all possible +cases. For example, this can come up when code performs a separate action +for each member of an enum, or for each type in a union. + +To have the type checker do exhaustiveness checking for us, we call a +function with a parameter typed as ``Never``. The type checker will allow +this call only if it can prove that the code is not reachable. + +As an example, consider this simple calculator: + +.. code:: python + + import enum + from typing import Never + + def assert_never(arg: Never) -> Never: + raise AssertionError("Expected code to be unreachable") + + class Op(enum.Enum): + ADD = 1 + SUBTRACT = 2 + + def calculate(left: int, op: Op, right: int) -> int: + match op: + case Op.ADD: + return left + right + case Op.SUBTRACT: + return left - right + case _: + assert_never(op) + +.. note:: + + To use this feature on Python versions earlier than 3.11, you will need to + import ``Never`` from ``typing_extensions`` (version 4.1 or newer). + +The ``match`` statement covers all members of the ``Op`` enum, +so the ``assert_never()`` call is unreachable and the type checker +will accept this code. However, if you add another member to the +enum (say, ``MULTIPLY``) but don't update the ``match`` statement, +the type checker will give an error saying that you are not handling +the ``MULTIPLY`` case. + +Because the ``assert_never()`` helper function is frequently useful, +it is provided by the standard library as ``typing.assert_never`` +starting in Python 3.11, +and is also present in ``typing_extensions`` starting at version 4.1. +However, it is also possible to define a similar function in your own +code, for example if you want to customize the runtime error message. + +This use of match statements for comprehensive matching is common +enough that some typecheckers have direct support for checking all match +statements for exhaustiveness, regardless of whether or not they have an +``assert_never``-style function in their default arm. For instance, in pyright +this can be enabled by enabling the diagnostic code ``reportMatchNotExhaustive`` +(on by default in strict mode), and in mypy it can be enabled by enabling +the error code ``exhaustive-match``. + +You can also use ``assert_never()`` with a sequence of ``if`` statements: + +.. code:: python + + def calculate(left: int, op: Op, right: int) -> int: + if op is Op.ADD: + return left + right + elif op is Op.SUBTRACT: + return left - right + else: + assert_never(op) + +Marking Code as Unreachable +=========================== + +Sometimes a piece of code is unreachable, but the type system is not +powerful enough to recognize that. For example, consider a function that +finds the lowest unused street number in a street: + +.. code:: python + + import itertools + + def is_used(street: str, number: int) -> bool: + ... + + def lowest_unused(street: str) -> int: + for i in itertools.count(1): + if not is_used(street, i): + return i + assert False, "unreachable" + +Because ``itertools.count()`` is an infinite iterator, this function +will never reach the ``assert False`` statement. However, there is +no way for the type checker to know that, so without the ``assert False``, +the type checker will complain that the function is missing a return +statement. + +Note how this is different from ``assert_never()``: + +- If we used ``assert_never()`` in the ``lowest_unused()`` function, + the type checker would produce an error, because the type checker + cannot prove that the line is unreachable. +- If we used ``assert False`` instead of ``assert_never()`` in the + ``calculate()`` example above, we would not get the benefits of + exhaustiveness checking. If the code is actually reachable, + the type checker will not warn us and we could hit the assertion + at runtime. + +While ``assert False`` is the most idiomatic way to express this pattern, +any statement that ends execution will do. For example, you could raise +an exception or call a function that returns ``Never``. + +Detecting Unexpectedly Unreachable Code +======================================= + +Another possible problem is code that is supposed to execute, but that +can actually be statically determined to be unreachable. +Some type checkers have an option that enables warnings for code +detected as unreachable (e.g., ``--warn-unreachable`` in mypy). diff --git a/docs/guides/writing_stubs.rst b/docs/guides/writing_stubs.rst new file mode 100644 index 000000000..9f46a94d7 --- /dev/null +++ b/docs/guides/writing_stubs.rst @@ -0,0 +1,884 @@ +.. _writing_stubs: + +********************************** +Writing and Maintaining Stub Files +********************************** + +Stub files are a means of providing type information for Python modules. +For a quick introduction, see :ref:`external_libraries`. For a full reference, +refer to :ref:`distributing-type`. + +Maintaining stubs can be a little cumbersome because they are separated from the +implementation. This page lists some tools that make writing and maintaining +stubs less painful, as well as some best practices on stub contents and style. + +Tools for generating stubs +========================== + +stubgen +------- + +stubgen is a tool bundled with `mypy `__ +that can be used to generate basic stubs. These stubs serve as a +basic starting point; most types will default to ``Any``. + +.. code-block:: console + + stubgen -p my_great_package + +For more details, see `stubgen docs `__. + +pyright +------- + +pyright contains a tool that generates basic stubs. Like stubgen, these generated +stubs serve more as a starting point. + +.. code-block:: console + + pyright --createstub my_great_package + +For more details, see `pyright docs `__. + +monkeytype +---------- + +monkeytype takes a slightly different approach — you run your code (perhaps via +your tests) and monkeytype collects the types it observes at runtime to generate +stubs. + +.. code-block:: console + + monkeytype run script.py + monkeytype stub my_great_package + +For more details, see `monkeytype docs `__. + +Tools for maintaining stubs +=========================== + +stubtest +-------- + +stubtest is a tool bundled with `mypy `__. + +stubtest finds inconsistencies between stub files and the implementation. It +does this by comparing stub definitions to what it finds from importing your +code and using runtime introspection (via the ``inspect`` module). + +.. code-block:: console + + stubtest my_great_package + +For more details, see `stubtest docs `__. + +flake8-pyi +---------- + +flake8-pyi is a `flake8 `__ plugin that +lints common issues in stub files. + +.. code-block:: console + + flake8 my_great_package + +For more details, see `flake8-pyi docs `__. + +Running a type checker on the stubs +----------------------------------- + +Simply running a type checker on the stubs can catch several issues, from simple +things like detecting missing annotations to more complex things like ensuring +Liskov substitutability or detecting problematic overloads. + +It may be instructive to examine `typeshed `__'s +`setup for testing stubs `__. + +To suppress type errors in stubs, use ``# type: ignore`` comments. Refer to the :ref:`type-checker-error-suppression` section of the style guide for +error suppression formats specific to individual typecheckers. + +.. + TODO: consider adding examples and configurations for specific type checkers + +Type checking usage of your package +----------------------------------- + +If you have access to a codebase that uses your package — perhaps tests for your +package — running a type checker against it can help you detect issues, +particularly with false positives. + +If your package has some particularly complex aspects, you could even consider +writing dedicated typing tests for tricky definitions. For more details, see +:ref:`testing`. + +Stub Content +============ + +This section documents best practices on what elements to include or +leave out of stub files. + +Public Interface +---------------- + +Stubs should include the complete public interface (classes, functions, +constants, etc.) of the module they cover, but it is not always +clear exactly what is part of the interface. + +The following should always be included: + +* All objects listed in the module's documentation. +* All objects included in ``__all__`` (if present). + +Other objects may be included if they are not prefixed with an underscore +or if they are being used in practice. + +Modules excluded from stubs +--------------------------- + +The following should not be included in stubs: + +1. Implementation details, with `multiprocessing/popen_spawn_win32.py `_ as a notable example +2. Modules that are not supposed to be imported, such as ``__main__.py`` +3. Protected modules that start with a single ``_`` char. However, when needed protected modules can still be added (see :ref:`undocumented-objects` section below) +4. Tests + +.. _undocumented-objects: + +Undocumented Objects +-------------------- + +Undocumented objects may be included as long as they are marked with a comment +of the form ``# undocumented``. + +Example:: + + def list2cmdline(seq: Sequence[str]) -> str: ... # undocumented + +Such undocumented objects are allowed because omitting objects can confuse +users. Users who see an error like "module X has no attribute Y" will +not know whether the error appeared because their code had a bug or +because the stub is wrong. Although it may also be helpful for a type +checker to point out usage of private objects, false negatives (no errors for +wrong code) are preferable over false positives (type errors +for correct code). In addition, even for private objects a type checker +can be helpful in pointing out that an incorrect type was used. + +``__all__`` +------------ + +A stub file should contain an ``__all__`` variable if and only if it is also +present at runtime. In that case, the contents of ``__all__`` should be +identical in the stub and at runtime. If the runtime dynamically adds +or removes elements (for example if certain functions are only available on +some system configurations), include all possible elements in the stubs. + +Stub-Only Objects +----------------- + +Definitions that do not exist at runtime may be included in stubs to aid in +expressing types. Unless intentionally exposed to users (see below), such +definitions should be marked as private by prefixing their names with an +underscore. + +Yes:: + + _T = TypeVar("_T") + _DictList: TypeAlias = dict[str, list[int | None]] + +No:: + + T = TypeVar("T") + DictList: TypeAlias = dict[str, list[int | None]] + +Sometimes, it is desirable to make a stub-only class available +to a stub's users — for example, to allow them to type the return value of a +public method for which a library does not provide a usable runtime type. Use +the ``typing.type_check_only`` decorator to mark such objects:: + + from typing import Protocol, type_check_only + + @type_check_only + class Readable(Protocol): + def read(self) -> str: ... + + def get_reader() -> Readable: ... + +Structural Types +---------------- + +As seen in the example with ``Readable`` in the previous section, a common use +of stub-only objects is to model types that are best described by their +structure. These objects are called protocols (:pep:`544`), and it is encouraged +to use them freely to describe simple structural types. + +Incomplete Stubs +---------------- + +When writing new stubs, it is not necessary to fully annotate all arguments, +return types, and fields. Some items may be left unannotated or +annotated with ``_typeshed.Incomplete`` (`documentation `_):: + + from _typeshed import Incomplete + + field: Incomplete # unannotated + + def foo(x): ... # unannotated argument and return type + +``_typeshed.Incomplete`` can also be used for partially known types:: + + def foo(x: Incomplete | None = None) -> list[Incomplete]: ... + +Partial stubs can be useful, especially for larger packages, but they should +follow the following guidelines: + +* Included functions and methods should list all arguments, but the arguments + can be left unannotated. +* Do not use ``Any`` to mark unannotated or partially annotated values. Leave + function parameters and return values unannotated. In all other cases, use + ``_typeshed.Incomplete``. +* Partial classes should include a ``__getattr__()`` method marked with + ``_typeshed.Incomplete`` (see example below). +* Partial modules (i.e. modules that are missing some or all classes, + functions, or attributes) should include a top-level ``__getattr__()`` + function marked with ``_typeshed.Incomplete`` (see example below). +* Partial packages (i.e. packages that are missing one or more sub-modules) + should have a ``__init__.pyi`` stub that is marked as incomplete (see above). + A better alternative is to create empty stubs for all sub-modules and + mark them as incomplete individually. + +Example of a partial module with a partial class ``Foo`` and a partially +annotated function ``bar()``:: + + from _typeshed import Incomplete + + def __getattr__(name: str) -> Incomplete: ... + + class Foo: + def __getattr__(self, name: str) -> Incomplete: ... + x: int + y: str + + def bar(x: str, y, *, z=...): ... + +``Any`` vs. ``Incomplete`` +-------------------------- + +While ``Incomplete`` is a type alias of ``Any``, they serve different purposes: +``Incomplete`` is a placeholder where a proper type might be substituted. +It's a "to do" item and should be replaced if possible. + +``Any`` is used when it's not possible to accurately type an item using the current +type system. It should be used sparingly, as described in the :ref:`using-any` +section of the style guide. + +Attribute Access +---------------- + +Python has several methods for customizing attribute access: ``__getattr__``, +``__getattribute__``, ``__setattr__``, and ``__delattr__``. Of these, +``__getattr__`` and ``__setattr__`` should sometimes be included in stubs. + +In addition to marking incomplete definitions, ``__getattr__`` should be +included when a class or module allows any name to be accessed. For example, consider +the following class:: + + class Foo: + def __getattribute__(self, name): + return self.__dict__.setdefault(name) + +An appropriate stub definition is:: + + from typing import Any + + class Foo: + def __getattr__(self, name: str) -> Any | None: ... + +Note that only ``__getattr__``, not ``__getattribute__``, is guaranteed to be +supported in stubs. + +On the other hand, ``__getattr__`` should be omitted even if the source code +includes it, if only limited names are allowed. For example, consider this class:: + + class ComplexNumber: + def __init__(self, n): + self._n = n + def __getattr__(self, name): + if name in ("real", "imag"): + return getattr(self._n, name) + raise AttributeError(name) + +In this case, the stub should list the attributes individually:: + + class ComplexNumber: + @property + def real(self) -> float: ... + @property + def imag(self) -> float: ... + def __init__(self, n: complex) -> None: ... + +``__setattr__`` should be included when a class allows any name to be set and +restricts the type. For example:: + + class IntHolder: + def __setattr__(self, name, value): + if isinstance(value, int): + return super().__setattr__(name, value) + raise ValueError(value) + +A good stub definition would be:: + + class IntHolder: + def __setattr__(self, name: str, value: int) -> None: ... + +``__delattr__`` should not be included in stubs. + +Finally, even in the presence of ``__getattr__`` and ``__setattr__``, it is +still recommended to separately define known attributes. + +Constants +--------- + +When the value of a constant is important, mark it as ``Final`` and assign it +to its value. + +Yes:: + + TEL_LANDLINE: Final = "landline" + TEL_MOBILE: Final = "mobile" + DAY_FLAG: Final = 0x01 + NIGHT_FLAG: Final = 0x02 + +No:: + + TEL_LANDLINE: str + TEL_MOBILE: str + DAY_FLAG: int + NIGHT_FLAG: int + +Overloads +--------- + +All variants of overloaded functions and methods must have an ``@overload`` +decorator. Do not include the implementation's final non-`@overload`-decorated +definition. + +Yes:: + + @overload + def foo(x: str) -> str: ... + @overload + def foo(x: float) -> int: ... + +No:: + + @overload + def foo(x: str) -> str: ... + @overload + def foo(x: float) -> int: ... + def foo(x: str | float) -> Any: ... + +Decorators +---------- + +Include only the decorators listed :ref:`here `, whose effects +are understood by all of the major type checkers. The behavior of other +decorators should instead be incorporated into the types. For example, for the +following function:: + + import contextlib + @contextlib.contextmanager + def f(): + yield 42 + +the stub definition should be:: + + from contextlib import AbstractContextManager + def f() -> AbstractContextManager[int]: ... + +Documentation or Implementation +------------------------------- + +Sometimes a library's documented types will differ from the actual types in the +code. In such cases, stub authors should use their best judgment. Consider these +two examples:: + + def print_elements(x): + """Print every element of list x.""" + for y in x: + print(y) + + def maybe_raise(x): + """Raise an error if x (a boolean) is true.""" + if x: + raise ValueError() + +The implementation of ``print_elements`` takes any iterable, despite the +documented type of ``list``. In this case, annotate the argument as +``Iterable[object]``, to follow the :ref:`best practice` +of preferring abstract types for arguments. + +For ``maybe_raise``, on the other hand, it is better to annotate the argument as +``bool`` even though the implementation accepts any object. This guards against +common mistakes like unintentionally passing in ``None``. + +If in doubt, consider asking the library maintainers about their intent. + +Common Patterns +=============== + +.. _stub-patterns: + +This section documents common patterns that are useful in stub files. + +Overloads and Flags +------------------- + +.. _overloads-and-flags: + +Sometimes a function or method has a flag argument that changes the return type +or other accepted argument types. For example, take the following function:: + + def open(name: str, mode: Literal["r", "w"] = "r") -> Reader | Writer: + ... + +We can express this case easily with two overloads:: + + @overload + def open(name: str, mode: Literal["r"] = "r") -> Reader: ... + @overload + def open(name: str, mode: Literal["w"]) -> Writer: ... + +The first overload is picked when the mode is ``"r"`` or not given, and the +second overload is picked when the mode is ``"w"``. But what if the first +argument is optional? + +:: + + def open(name: str | None = None, mode: Literal["r", "w"] = "r") -> Reader | Writer: + ... + +Ideally we would be able to use the following overloads:: + + @overload + def open(name: str | None = None, mode: Literal["r"] = "r") -> Reader: ... + @overload + def open(name: str | None = None, mode: Literal["w"]) -> Writer: ... + +And while the first overload is fine, the second is a syntax error in Python, +because non-default arguments cannot follow default arguments. To work around +this, we need an extra overload:: + + @overload + def open(name: str | None = None, mode: Literal["r"] = "r") -> Reader: ... + @overload + def open(name: str | None, mode: Literal["w"]) -> Writer: ... + @overload + def open(*, mode: Literal["w"]) -> Writer: ... + +As before, the first overload is picked when the mode is ``"r"`` or not given. +Otherwise, the second overload is used when ``open`` is called with an explicit +``name``, e.g. ``open("file.txt", "w")`` or ``open(None, "w")``. The third +overload is used when ``open`` is called without a name, e.g. +``open(mode="w")``. + +Style Guide +=========== + +The recommendations in this section are aimed at stub authors who wish to +provide a consistent style for stubs. Type checkers should not reject stubs that +do not follow these recommendations, but linters can warn about them. + +Stub files should generally follow the Style Guide for Python Code (:pep:`8`) +and the :ref:`best-practices`. There are a few exceptions, outlined below, that take the +different structure of stub files into account and aim to create +more concise files. + +Syntax Example +-------------- + +The below is an excerpt from the types for the ``datetime`` module:: + + MAXYEAR: int + MINYEAR: int + + class date: + def __new__(cls, year: SupportsIndex, month: SupportsIndex, day: SupportsIndex) -> Self: ... + @classmethod + def fromtimestamp(cls, timestamp: float, /) -> Self: ... + @classmethod + def today(cls) -> Self: ... + @classmethod + def fromordinal(cls, n: int, /) -> Self: ... + @property + def year(self) -> int: ... + def replace(self, year: SupportsIndex = ..., month: SupportsIndex = ..., day: SupportsIndex = ...) -> Self: ... + def ctime(self) -> str: ... + def weekday(self) -> int: ... + +Maximum Line Length +------------------- + +Stub files should be limited to 130 characters per line. + +Blank Lines +----------- + +Do not use empty lines between functions, methods, and fields, except to +group them with one empty line. Use one empty line around classes with non-empty +bodies. Do not use empty lines between body-less classes, except for grouping. + +Yes:: + + def time_func() -> None: ... + def date_func() -> None: ... + + def ip_func() -> None: ... + + class Foo: + x: int + y: int + def __init__(self) -> None: ... + + class MyError(Exception): ... + class AnotherError(Exception): ... + +No:: + + def time_func() -> None: ... + + def date_func() -> None: ... # do not leave unnecessary empty lines + + def ip_func() -> None: ... + + + class Foo: # leave only one empty line above + x: int + class MyError(Exception): ... # leave an empty line between the classes + +Module Level Attributes +----------------------- + +Do not unnecessarily use an assignment for module-level attributes. + +Yes:: + + CONST: Literal["const"] + x: int + y: Final = 0 # this assignment conveys additional type information + +No:: + + CONST = "const" + x: int = 0 + y: float = ... + z = 0 # type: int + a = ... # type: int + +.. _stub-style-classes: + +Classes +------- + +Classes without bodies should use the ellipsis literal ``...`` in place +of the body on the same line as the class definition. + +Yes:: + + class MyError(Exception): ... + +No:: + + class MyError(Exception): + ... + class AnotherError(Exception): pass + +Instance attributes and class variables follow the same recommendations as +module level attributes: + +Yes:: + + class Foo: + c: ClassVar[str] + x: int + + class Color(Enum): + # An assignment with no type annotation is a convention used to indicate + # an enum member. + RED = 1 + +No:: + + class Foo: + c: ClassVar[str] = "" + d: ClassVar[int] = ... + x = 4 + y: int = ... + +Functions and Methods +--------------------- + +For keyword-only and positional-or-keyword arguments, use the same +argument names as in the implementation, because otherwise using +keyword arguments will fail. + +For default values, use the literal values of "simple" default values (``None``, +bools, ints, bytes, strings, and floats). Use the ellipsis literal ``...`` in +place of more complex default values. Use an explicit ``X | None`` annotation +when the default is ``None``. + +Yes:: + + def foo(x: int = 0) -> None: ... + def bar(y: str | None = None) -> None: ... + +No:: + + def foo(x: X = X()) -> None: ... + def bar(y: str = None) -> None: ... + +Do not annotate ``self`` and ``cls`` in method definitions, except when +referencing a type variable. + +Yes:: + + _T = TypeVar("_T") + + class Foo: + def bar(self) -> None: ... + @classmethod + def create(cls: type[_T]) -> _T: ... + +No:: + + class Foo: + def bar(self: Foo) -> None: ... + @classmethod + def baz(cls: type[Foo]) -> int: ... + +The bodies of functions and methods should consist of only the ellipsis +literal ``...`` on the same line as the closing parenthesis and colon. + +Yes:: + + def to_int1(x: str) -> int: ... + def to_int2( + x: str, + ) -> int: ... + +No:: + + def to_int1(x: str) -> int: + return int(x) + def to_int2(x: str) -> int: + ... + def to_int3(x: str) -> int: pass + +Avoid invariant collection types (``list``, ``dict``) for function parameters, +in favor of covariant types like ``Mapping`` or ``Sequence``. + +Avoid union return types. See https://github.com/python/mypy/issues/1693 + +Use ``float`` instead of ``int | float`` for parameter annotations. See :pep:`484` for more details. + +Language Features +----------------- + +Use the latest language features available, even for stubs targeting older +Python versions. For example, Python 3.7 added the ``async`` keyword (see +:pep:`492`). Stubs should use it to mark coroutines, even if the implementation +still uses the ``@coroutine`` decorator. On the other hand, the ``type`` soft +keyword from :pep:`695`, introduced in Python 3.12, should not be used in stubs +until Python 3.11 reaches end-of-life in October 2027. + +Do not use quotes around forward references and do not use ``__future__`` +imports. See :ref:`stub-file-syntax` for more information. + +Yes:: + + class Py35Class: + x: int + forward_reference: OtherClass + + class OtherClass: ... + +No:: + + class Py35Class: + x = 0 # type: int + forward_reference: 'OtherClass' + + class OtherClass: ... + +Use variable annotations instead of type comments, even for stubs that target +older versions of Python. + +Platform-dependent APIs +----------------------- + +Use :ref:`platform checks` like ``if sys.platform == 'win32'`` to denote platform-dependent APIs. + +NamedTuple and TypedDict +------------------------ + +Use the class-based syntax for ``typing.NamedTuple`` and +``typing.TypedDict``, following the :ref:`stub-style-classes` section of this style guide. + +Yes:: + + from typing import NamedTuple, TypedDict + + class Point(NamedTuple): + x: float + y: float + + class Thing(TypedDict): + stuff: str + index: int + +No:: + + from typing import NamedTuple, TypedDict + Point = NamedTuple("Point", [('x', float), ('y', float)]) + Thing = TypedDict("Thing", {'stuff': str, 'index': int}) + +Built-in Generics +----------------- + +:pep:`585` built-in generics (such as ``list``, ``dict``, ``tuple``, ``set``) are supported and should be used instead +of the corresponding types from ``typing``:: + + from collections import defaultdict + + def foo(t: type[MyClass]) -> list[int]: ... + x: defaultdict[int] + +Using imports from ``collections.abc`` instead of ``typing`` is +generally possible and recommended:: + + from collections.abc import Iterable + + def foo(iter: Iterable[int]) -> None: ... + +Unions +------ + +Declaring unions with the shorthand ``|`` syntax is recommended and supported by +all type checkers:: + + def foo(x: int | str) -> int | None: ... # recommended + def foo(x: Union[int, str]) -> Optional[int]: ... # ok + +.. _using-any: + +Using ``Any`` and ``object`` +---------------------------- + +When adding type hints, avoid using the :ref:`Any` type when possible. Reserve +the use of :ref:`Any` for when: + +* the correct type cannot be expressed in the current type system; and +* to avoid union returns (see above). + +Note that :ref:`Any` is not the correct type to use if you want to indicate +that some function can accept literally anything: in those cases use +:class:`object` instead. + +When using :ref:`Any`, document the reason for using it in a comment, unless the +reason is obvious. Ideally, document what types could be used. Obvious +reasons can include: + +* Using :ref:`Any` as a type argument for a generic with invariant type variables + to say "any object of this type is allowed", e.g. ``Future[Any]``. +* Using ``dict[str, Any]`` or ``Mapping[str, Any]`` when the value types + depend on the keys. But consider using :ref:`TypedDict` or + ``dict[str, Incomplete]`` (temporarily) when the keys of the dictionary are + fixed. + +The ``Any`` Trick +----------------- + +In cases where a function or method can return ``None``, but where forcing the +user to explicitly check for ``None`` can be detrimental, use +``_typeshed.MaybeNone`` (an alias to ``Any``), instead of ``None``. + +Consider the following (simplified) signature of ``re.Match[str].group``:: + + class Match: + def group(self, group: str | int, /) -> str | MaybeNone: ... + +This avoids forcing the user to check for ``None``:: + + match = re.fullmatch(r"\d+_(.*)", some_string) + assert match is not None + name_group = match.group(1) # The user knows that this will never be None + name_group.uper() # This typo will be flagged by the type checker + +In this case, the user of ``match.group()`` must be prepared to handle a ``str``, +but type checkers are happy with ``if name_group is None`` checks, because we're +saying it can also be something other than a ``str``. + +This is sometimes called "the Any trick". + +Context Managers +---------------- + +When adding type annotations for context manager classes, annotate +the return type of ``__exit__`` as bool only if the context manager +sometimes suppresses exceptions -- if it sometimes returns ``True`` +at runtime. If the context manager never suppresses exceptions, +have the return type be either ``None`` or ``bool | None``. If you +are not sure whether exceptions are suppressed or not or if the +context manager is meant to be subclassed, pick ``bool | None``. +See https://github.com/python/mypy/issues/7214 for more details. + +``__enter__`` methods and other methods that return ``self`` or ``cls(...)`` +should be annotated with ``typing.Self`` +(`example `_). + +Naming +------ + +Type variables and aliases you introduce purely for legibility reasons +should be prefixed with an underscore to make it obvious to the reader +they are not part of the stubbed API. + +A few guidelines for protocol names below. In cases that don't fall +into any of those categories, use your best judgement. + +* Use plain names for protocols that represent a clear concept + (e.g. ``Iterator``, ``Container``). +* Use ``SupportsX`` for protocols that provide callable methods (e.g. + ``SupportsInt``, ``SupportsRead``, ``SupportsReadSeek``). +* Use ``HasX`` for protocols that have readable and/or writable attributes + or getter/setter methods (e.g. ``HasItems``, ``HasFileno``). + +.. _type-checker-error-suppression: + +Type Checker Error Suppression Formats +-------------------------------------- + +* Use mypy error codes for mypy-specific ``# type: ignore`` annotations, e.g. ``# type: ignore[override]`` for Liskov Substitution Principle violations. +* Use pyright error codes for pyright-specific suppressions, e.g. ``# pyright: ignore[reportGeneralTypeIssues]``. +* If you need both on the same line, mypy's annotation needs to go first, e.g. ``# type: ignore[override] # pyright: ignore[reportGeneralTypeIssues]``. + + +``@deprecated`` +--------------- + +The ``@typing_extensions.deprecated`` decorator (``@warnings.deprecated`` +since Python 3.13) can be used to mark deprecated functionality; see +:pep:`702`. + +Keep the deprecation message concise, but try to mention the projected +version when the functionality is to be removed, and a suggested +replacement. + +Docstrings +---------- + +There are several tradeoffs around including docstrings in type stubs. Consider the intended purpose +of your stubs when deciding whether to include docstrings in your project's stubs. + +* They do not affect type checking results and will be ignored by type checkers. +* Docstrings can improve certain IDE functionality, such as hover info. +* Duplicating docstrings between source code and stubs requires extra work to keep them in sync. diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 000000000..49bab4714 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,131 @@ +************************* +Static Typing with Python +************************* + +Tutorials +========= + +.. + Keep in sync with tutorials/index.rst. + +.. toctree:: + :maxdepth: 1 + + tutorials/external_libraries + +Guides +====== + +.. + Keep in sync with docs/guides/index.rst. + +.. toctree:: + :maxdepth: 1 + + guides/libraries + guides/writing_stubs + guides/modernizing + guides/unreachable + guides/type_narrowing + guides/typing_anti_pitch + +Reference +========= + +.. + Keep in sync with docs/reference/index.rst. + +.. toctree:: + :maxdepth: 1 + + reference/generics + reference/protocols + reference/best_practices + reference/quality + typing Module Documentation + +.. seealso:: + + The documentation at https://mypy.readthedocs.io/ is relatively accessible + and complete. + +Specification +============= + +.. toctree:: + :maxdepth: 2 + + spec/index + + +Typing PEPs +----------- + +Significant changes to the Python Type System Specification are proposed and +discussed in Python Enhancement Proposals (PEPs). See +https://peps.python.org/topic/typing for a list of all current and historical +typing-related PEPs. + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`search` + +.. _contact: + +Discussions and Support +======================= + +* `User help forum `_ +* `User chat on Gitter `_ +* `Developer forum `_ +* `Developer mailing list (archived) `_ + +Typing-related Tools +==================== + +Type Checkers +------------- + +* `mypy `_ +* `pyrefly `_ +* `pyright `_ +* `ty `_ +* `Zuban `_ + +The extent to which these adhere to the specification is monitored by a +`conformance test suite `_ +(`results `_). + +Development Environments +------------------------ + +* `PyCharm `_, an IDE that supports + type stubs both for type checking and code completion. +* `Visual Studio Code `_, a code editor that + supports type checking using mypy, pyright, or the + `Pylance `_ + extension. + +Linters and Formatters +---------------------- + +* `black `_, a code formatter with support for + type stub files. +* `flake8-pyi `_, a plugin for the + `flake8 `_ linter that adds support for type + stubs. +* `ruff `_, a linter built for speed, with support for + most of the ``flake8-pyi`` rules. + +Type-Hint and Stub Integration +------------------------------ + +* `autotyping `_, a tool which + infers simple types from their context and inserts them as inline type-hints. +* `merge-pyi + `_, + a thin wrapper around ``ApplyTypeAnnotationsVisitor`` from + `libCST `_ that integrates .pyi + signatures as inline type-hints in Python source code. diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 000000000..11c18e5e6 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -W -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/reference/best_practices.rst b/docs/reference/best_practices.rst new file mode 100644 index 000000000..ebf58dfc9 --- /dev/null +++ b/docs/reference/best_practices.rst @@ -0,0 +1,147 @@ +.. _best-practices: + +********************* +Typing Best Practices +********************* + +Introduction +============ + +Over time, some best practices have proven themselves as useful when working +with type hints in Python. Not all practices are applicable in all situations +and some practices come down to personal style and preference, but they +are a good default set of recommendations to fall back to, unless there is +a specific reason to deviate. + +These best practices are constantly evolving, especially as the typing +capabilities and ecosystem grow. So expect new best practices to be added +and existing best practices to be modified or even removed as better practices +evolve. That is why we would love to hear from your experiences with typing. +Please see :ref:`contact` on how to join the discussion. + +Typing Features +=============== + +Type Aliases +------------ + +Use ``TypeAlias`` for type aliases (but not for regular aliases). + +Yes:: + + _IntList: TypeAlias = list[int] + g = os.stat + Path = pathlib.Path + ERROR = errno.EEXIST + +No:: + + _IntList = list[int] + g: TypeAlias = os.stat + Path: TypeAlias = pathlib.Path + ERROR: TypeAlias = errno.EEXIST + +Ergonomic Practices +=================== + +Using ``Any`` and ``object`` +---------------------------- + +Generally, use ``Any`` when a type cannot be expressed appropriately +with the current type system or using the correct type is unergonomic. + +If a function accepts every possible object as an argument, for example +because it's only passed to ``str()``, use ``object`` instead of ``Any`` as +type annotation:: + + def print_formatted(o: object) -> None: + if isinstance(o, int): + o = f"{o:02}" + print(o) + +Similarly, if the return value of a callback is ignored, +annotate it with ``object``, not ``Any`` or ``None``:: + + def call_cb(cb: Callable[[int], object]) -> None: + cb(42) + +.. _argument-return-practices: + +Arguments and Return Types +-------------------------- + +For arguments, prefer protocols and abstract types (``Mapping``, +``Sequence``, ``Iterable``, etc.). If an argument accepts literally any value, +use ``object`` instead of ``Any``. + +For return values, prefer concrete types (``list``, ``dict``, etc.) for +concrete implementations. The return values of protocols +and abstract base classes must be judged on a case-by-case basis. + +Yes:: + + def map_it(input: Iterable[str]) -> list[int]: ... + def create_map() -> dict[str, int]: ... + def to_string(o: object) -> str: ... # accepts any object + +No:: + + def map_it(input: list[str]) -> list[int]: ... + def create_map() -> MutableMapping[str, int]: ... + def to_string(o: Any) -> str: ... + +Maybe:: + + class MyProto(Protocol): + def foo(self) -> list[int]: ... + def bar(self) -> Mapping[str, str]: ... + +Avoid union return types, since they require ``isinstance()`` checks. +Use ``Any`` or ``X | Any`` if necessary. + +Stylistic Practices +=================== + +Shorthand Syntax +---------------- + +Where possible, use shorthand syntax for unions instead of +``Union`` or ``Optional``. ``None`` should be the last +element of an union. + +Yes:: + + def foo(x: str | int) -> None: ... + def bar(x: str | None) -> int | None: ... + +No:: + + def foo(x: Union[str, int]) -> None: ... + def bar(x: Optional[str]) -> Optional[int]: ... + def baz(x: None | str) -> None: ... + +Types +----- + +Use ``float`` instead of ``int | float``. +Use ``None`` instead of ``Literal[None]``. + +Built-in Generics +----------------- + +Use built-in generics instead of the aliases from ``typing``, +where possible. + +Yes:: + + from collections.abc import Iterable + + def foo(x: type[MyClass]) -> list[str]: ... + def bar(x: Iterable[str]) -> None: ... + +No:: + + from typing import Iterable, List, Type + + def foo(x: Type[MyClass]) -> List[str]: ... + def bar(x: Iterable[str]) -> None: ... diff --git a/docs/reference/generics.rst b/docs/reference/generics.rst new file mode 100644 index 000000000..c39d2bc11 --- /dev/null +++ b/docs/reference/generics.rst @@ -0,0 +1,985 @@ +Generics +======== + +You may have seen type hints like ``list[str]`` or ``dict[str, int]`` in Python +code. These types are interesting in that they are parametrised by other types! +A ``list[str]`` isn't just a list, it's a list of strings. Types with type +parameters like this are called *generic types*. + +You can define your own generic classes that take type parameters, similar to +built-in types such as ``list[X]``. Note that such user-defined generics are a +moderately advanced feature and you can get far without ever using them. + +.. _generic-classes: + +Defining generic classes +************************ + +Here is a very simple generic class that represents a stack: + +.. code-block:: python + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class Stack(Generic[T]): + def __init__(self) -> None: + # Create an empty list with items of type T + self.items: list[T] = [] + + def push(self, item: T) -> None: + self.items.append(item) + + def pop(self) -> T: + return self.items.pop() + + def empty(self) -> bool: + return not self.items + +The ``Stack`` class can be used to represent a stack of any type: +``Stack[int]``, ``Stack[tuple[int, str]]``, etc. + +Using ``Stack`` is similar to built-in container types, like ``list``: + +.. code-block:: python + + # Construct an empty Stack[int] instance + stack = Stack[int]() + stack.push(2) + stack.pop() + 1 + stack.push('x') # error: Argument 1 to "push" of "Stack" has incompatible type "str"; expected "int" + +When creating instances of generic classes, the type argument can usually be +inferred. In cases where you explicitly specify the type argument, the +construction of the instance will be type checked correspondingly. + +.. code-block:: python + + class Box(Generic[T]): + def __init__(self, content: T) -> None: + self.content = content + + Box(1) # OK, inferred type is Box[int] + Box[int](1) # Also OK + Box[int]('some string') # error: Argument 1 to "Box" has incompatible type "str"; expected "int" + +.. _generic-subclasses: + +Defining subclasses of generic classes +************************************** + +User-defined generic classes and generic classes defined in :py:mod:`typing` +can be used as a base class for another class (generic or non-generic). For example: + +.. code-block:: python + + from typing import Generic, TypeVar, Mapping, Iterator + + KT = TypeVar('KT') + VT = TypeVar('VT') + + # This is a generic subclass of Mapping + class MyMap(Mapping[KT, VT]): + def __getitem__(self, k: KT) -> VT: ... + def __iter__(self) -> Iterator[KT]: ... + def __len__(self) -> int: ... + + items: MyMap[str, int] # OK + + # This is a non-generic subclass of dict + class StrDict(dict[str, str]): + def __str__(self) -> str: + return f'StrDict({super().__str__()})' + + + data: StrDict[int, int] # error: "StrDict" expects no type arguments, but 2 given + data2: StrDict # OK + + # This is a user-defined generic class + class Receiver(Generic[T]): + def accept(self, value: T) -> None: ... + + # This is a generic subclass of Receiver + class AdvancedReceiver(Receiver[T]): ... + +.. note:: + + Note that you have to explicitly inherit from :py:class:`~typing.Mapping` + and :py:class:`~typing.Sequence` for your class to be considered a mapping + or sequence. This is because these classes are nominally typed, unlike + protocols like :py:class:`~typing.Iterable`, which use + :ref:`structural subtyping `. + +:py:class:`Generic ` can be omitted from bases if there are +other base classes that include type variables, such as ``Mapping[KT, VT]`` +in the above example. If you include ``Generic[...]`` in bases, then +it should list all type variables present in other bases (or more, +if needed). The order of type variables is defined by the following +rules: + +* If ``Generic[...]`` is present, then the order of variables is + always determined by their order in ``Generic[...]``. +* If there are no ``Generic[...]`` in bases, then all type variables + are collected in the lexicographic order (i.e. by first appearance). + +For example: + +.. code-block:: python + + from typing import Generic, TypeVar, Any + + T = TypeVar('T') + S = TypeVar('S') + U = TypeVar('U') + + class One(Generic[T]): ... + class Another(Generic[T]): ... + + class First(One[T], Another[S]): ... + class Second(One[T], Another[S], Generic[S, U, T]): ... + + x: First[int, str] # Here T is bound to int, S is bound to str + y: Second[int, str, Any] # Here T is Any, S is int, and U is str + +.. _generic-functions: + +Generic functions +***************** + +Type variables can be used to define generic functions. These are functions +where the types of the arguments or return value have some relationship: + +.. code-block:: python + + from typing import TypeVar, Sequence + + T = TypeVar('T') + + # A generic function! + def first(seq: Sequence[T]) -> T: + return seq[0] + +As with generic classes, the type variable can be replaced with any +type. That means ``first`` can be used with any sequence type, and the +return type is derived from the sequence item type. For example: + +.. code-block:: python + + reveal_type(first([1, 2, 3])) # Revealed type is "builtins.int" + reveal_type(first(['a', 'b'])) # Revealed type is "builtins.str" + +Since type variables are about describing the relationship between +two or more types, it's usually not useful to have a type variable +only appear once in a function signature. + +Note that for convenience, a single type variable symbol (such as ``T`` above) +can be used in multiple generic functions or classes, even though the logical +scope is different in each generic function or class. In the following example +we reuse the same type variable symbol in two generic functions; these two +functions do not share any typing relationship to each other: + +.. code-block:: python + + from typing import TypeVar, Sequence + + T = TypeVar('T') + + def first(seq: Sequence[T]) -> T: + return seq[0] + + def last(seq: Sequence[T]) -> T: + return seq[-1] + +Variables should not have a type variable in their type unless the type variable +is bound by a containing generic class, generic function or generic alias. + +.. _generic-methods-and-generic-self: + +Generic methods and generic self +******************************** + +You can also define generic methods — just use a type variable in the +method signature that is different from the type variable(s) bound in +the class definition. + +.. code-block:: python + + # T is the type variable bound by this class + class PairedBox(Generic[T]): + def __init__(self, content: T) -> None: + self.content = content + + # S is a type variable bound only in this method + def first(self, x: list[S]) -> S: + return x[0] + + def pair_with_first(self, x: list[S]) -> tuple[S, T]: + return (x[0], self.content) + + box = PairedBox("asdf") + reveal_type(box.first([1, 2, 3])) # Revealed type is "builtins.int" + reveal_type(box.pair_with_first([1, 2, 3])) # Revealed type is "tuple[builtins.int, builtins.str]" + +In particular, the ``self`` argument may also be generic, allowing a +method to return the most precise type known at the point of access. +In this way, for example, you can type check a chain of setter +methods: + +.. code-block:: python + + from typing import TypeVar + + T = TypeVar('T', bound='Shape') + + class Shape: + def set_scale(self: T, scale: float) -> T: + self.scale = scale + return self + + class Circle(Shape): + def set_radius(self, r: float) -> 'Circle': + self.radius = r + return self + + class Square(Shape): + def set_width(self, w: float) -> 'Square': + self.width = w + return self + + circle: Circle = Circle().set_scale(0.5).set_radius(2.7) + square: Square = Square().set_scale(0.5).set_width(3.2) + +Without using generic ``self``, the last two lines could not be type +checked properly, since the return type of ``set_scale`` would be +``Shape``, which doesn't define ``set_radius`` or ``set_width``. + +Other uses are factory methods, such as copy and deserialization. +For class methods, you can also define generic ``cls``, using :py:class:`type`: + +.. code-block:: python + + from typing import Optional, TypeVar, Type + + T = TypeVar('T', bound='Friend') + + class Friend: + other: Optional["Friend"] = None + + @classmethod + def make_pair(cls: type[T]) -> tuple[T, T]: + a, b = cls(), cls() + a.other = b + b.other = a + return a, b + + class SuperFriend(Friend): + pass + + a, b = SuperFriend.make_pair() + +Note that when overriding a method with generic ``self``, you must either +return a generic ``self`` too, or return an instance of the current class. +In the latter case, you must implement this method in all future subclasses. + +Note also that the type checker may not always verify that the implementation of a copy +or a deserialization method returns the actual type of self. Therefore +you may need to silence the type checker inside these methods (but not at the call site), +possibly by making use of the ``Any`` type or a ``# type: ignore`` comment. + +Automatic self types using typing.Self +************************************** + +Since the patterns described above are quite common, a simpler syntax +was introduced in :pep:`673`. + +Instead of defining a type variable and using an explicit annotation +for ``self``, you can use the special type ``typing.Self``. This is +automatically transformed into a type variable with the current class +as the upper bound, and you don't need an annotation for ``self`` (or +``cls`` in class methods). + +Here's what the example from the previous section looks like +when using ``typing.Self``: + +.. code-block:: python + + from typing import Self + + class Friend: + other: Self | None = None + + @classmethod + def make_pair(cls) -> tuple[Self, Self]: + a, b = cls(), cls() + a.other = b + b.other = a + return a, b + + class SuperFriend(Friend): + pass + + a, b = SuperFriend.make_pair() + +This is more compact than using explicit type variables. Also, you can +use ``Self`` in attribute annotations in addition to methods. + +.. note:: + + To use this feature on Python versions earlier than 3.11, you will need to + import ``Self`` from ``typing_extensions`` (version 4.0 or newer). + +.. _variance-of-generics: + +Variance of generic types +************************* + +There are three main kinds of generic types with respect to subtype +relations between them: invariant, covariant, and contravariant. +Assuming that we have a pair of types ``Animal`` and ``Bear``, and +``Bear`` is a subtype of ``Animal``, these are defined as follows: + +* A generic class ``MyCovGen[T]`` is called covariant in type parameter + ``T`` if ``MyCovGen[Bear]`` is a subtype of ``MyCovGen[Animal]``. + This is the most intuitive form of variance. +* A generic class ``MyContraGen[T]`` is called contravariant in type + parameter ``T`` if ``MyContraGen[Animal]`` is a subtype of + ``MyContraGen[Bear]``. +* A generic class ``MyInvGen[T]`` is called invariant in ``T`` if neither + of the above is true. + +Let us illustrate this by few simple examples: + +.. code-block:: python + + # We'll use these classes in the examples below + class Shape: ... + class Triangle(Shape): ... + class Square(Shape): ... + +* Most immutable containers, such as :py:class:`~typing.Sequence` and + :py:class:`~typing.FrozenSet` are covariant. :py:data:`~typing.Union` is + also covariant in all variables: ``Union[Triangle, int]`` is + a subtype of ``Union[Shape, int]``. + + .. code-block:: python + + def count_lines(shapes: Sequence[Shape]) -> int: + return sum(shape.num_sides for shape in shapes) + + triangles: Sequence[Triangle] + count_lines(triangles) # OK + + def foo(triangle: Triangle, num: int): + shape_or_number: Union[Shape, int] + # a Triangle is a Shape, and a Shape is a valid Union[Shape, int] + shape_or_number = triangle + + Covariance should feel relatively intuitive, but contravariance and invariance + can be harder to reason about. + +* :py:data:`~typing.Callable` is an example of type that behaves contravariantly + in types of arguments. That is, ``Callable[[Shape], int]`` is a subtype of + ``Callable[[Triangle], int]``, despite ``Shape`` being a supertype of + ``Triangle``. To understand this, consider: + + .. code-block:: python + + def cost_of_paint_required( + triangle: Triangle, + area_calculator: Callable[[Triangle], float] + ) -> float: + return area_calculator(triangle) * DOLLAR_PER_SQ_FT + + # This straightforwardly works + def area_of_triangle(triangle: Triangle) -> float: ... + cost_of_paint_required(triangle, area_of_triangle) # OK + + # But this works as well! + def area_of_any_shape(shape: Shape) -> float: ... + cost_of_paint_required(triangle, area_of_any_shape) # OK + + ``cost_of_paint_required`` needs a callable that can calculate the area of a + triangle. If we give it a callable that can calculate the area of an + arbitrary shape (not just triangles), everything still works. + +* :py:class:`~typing.List` is an invariant generic type. Naively, one would think + that it is covariant, like :py:class:`~typing.Sequence` above, but consider this code: + + .. code-block:: python + + class Circle(Shape): + # The rotate method is only defined on Circle, not on Shape + def rotate(self): ... + + def add_one(things: list[Shape]) -> None: + things.append(Shape()) + + 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 + + Another example of an invariant type is :py:class:`~typing.Dict`. Most mutable containers + are invariant. + +By default, all user-defined generics are invariant. +To declare a given generic class as covariant or contravariant use +type variables defined with special keyword arguments ``covariant`` or +``contravariant``. For example: + +.. code-block:: python + + from typing import Generic, TypeVar + + T_co = TypeVar('T_co', covariant=True) + + class Box(Generic[T_co]): # this type is declared covariant + def __init__(self, content: T_co) -> None: + self._content = content + + def get_content(self) -> T_co: + return self._content + + def look_into(box: Box[Animal]): ... + + my_box = Box(Cat()) + look_into(my_box) # OK, but would be an error if Box was invariant in T + +.. _type-variable-upper-bound: + +Type variables with upper bounds +******************************** + +By default, a type variable can be replaced with any type. This means that +you can't do very much with an object of type ``T`` safely -- you don't +know anything about it! + +It's therefore often useful to be able to limit the types that a type +variable can take on, for instance, by restricting it to values that are +subtypes of a specific type. + +Such a type is called the upper bound of the type variable, and is specified +with the ``bound=...`` keyword argument to :py:class:`~typing.TypeVar`. + +.. code-block:: python + + from typing import TypeVar, SupportsAbs + + T = TypeVar('T', bound=SupportsAbs[float]) + +In the definition of a generic function that uses such a type variable +``T``, the type represented by ``T`` is assumed to be a subtype of +its upper bound, so the function can use methods of the upper bound on +values of type ``T``. + +.. code-block:: python + + def largest_in_absolute_value(*xs: T) -> T: + return max(xs, key=abs) # Okay, because T is a subtype of SupportsAbs[float]. + +In a call to such a function, the type ``T`` must be replaced by a +type that is a subtype of its upper bound. Continuing the example +above: + +.. code-block:: python + + largest_in_absolute_value(-3.5, 2) # OK, has type float + largest_in_absolute_value(5+6j, 7) # OK, has type complex + largest_in_absolute_value('a', 'b') # error: error: Value of type variable "T" of "largest_in_absolute_value" cannot be "str" + +Type parameters of generic classes may also have upper bounds, which +restrict the valid values for the type parameter in the same way. + +.. _type-variable-value-restriction: + +Type variables with constraints +******************************* + +In some cases, it can be useful to restrict the values that a type variable can take to +exactly a specific set of types. This feature is a little complex and should +be avoided if an upper bound can be made to work instead, as above. + +An example is a type variable that can only have values ``str`` and ``bytes``: + +.. code-block:: python + + from typing import TypeVar + + AnyStr = TypeVar('AnyStr', str, bytes) + +This is actually such a common type variable that :py:data:`~typing.AnyStr` is +defined in :py:mod:`typing`. + +We can use :py:data:`~typing.AnyStr` to define a function that can concatenate +two strings or bytes objects, but it can't be called with other +argument types: + +.. code-block:: python + + from typing import AnyStr + + def concat(x: AnyStr, y: AnyStr) -> AnyStr: + return x + y + + concat('a', 'b') # Okay + concat(b'a', b'b') # Okay + concat(1, 2) # Error! + +Importantly, this is different from a union type, since combinations +of ``str`` and ``bytes`` are not accepted: + +.. code-block:: python + + concat('string', b'bytes') # Error! + +In this case, this is exactly what we want, since it's not possible +to concatenate a string and a bytes object! If we tried to use +``Union``, the type checker would complain about this possibility: + +.. code-block:: python + + def union_concat(x: Union[str, bytes], y: Union[str, bytes]) -> Union[str, bytes]: + return x + y # Error: can't concatenate str and bytes + +Another interesting special case is calling ``concat()`` with a +subtype of ``str``: + +.. code-block:: python + + class S(str): pass + + ss = concat(S('foo'), S('bar')) + reveal_type(ss) # Revealed type is "builtins.str" + +You may expect that the type of ``ss`` is ``S``, but the type is +actually ``str``: a subtype gets promoted to one of the valid values +for the type variable, which in this case is ``str``. + +This is thus subtly different from *bounded quantification* in languages such as +Java, where the return type would be ``S``. The way type checkers implement this +actually does exactly what we want for ``concat``, since ``concat`` returns an +instance of exactly ``str`` in the above example: + +.. code-block:: python + + >>> print(type(ss)) + + +You can also use a :py:class:`~typing.TypeVar` with a restricted set of possible +values when defining a generic class. For example, you can use the type +:py:class:`Pattern[AnyStr] ` for the return value of :py:func:`re.compile`, +since regular expressions can be based on a string or a bytes pattern. + +A type variable may not have both a value restriction (see +:ref:`type-variable-upper-bound`) and an upper bound. + +.. _declaring-decorators: + +Declaring decorators +******************** + +Decorators are typically functions that take a function as an argument and +return another function. Describing this behaviour in terms of types can +be a little tricky; we'll show how you can use ``TypeVar`` and a special +kind of type variable called a *parameter specification* to do so. + +Suppose we have the following decorator, not type annotated yet, +that preserves the original function's signature and merely prints the decorated function's name: + +.. code-block:: python + + def printing_decorator(func): + def wrapper(*args, **kwds): + print("Calling", func) + return func(*args, **kwds) + return wrapper + +and we use it to decorate function ``add_forty_two``: + +.. code-block:: python + + # A decorated function. + @printing_decorator + def add_forty_two(value: int) -> int: + return value + 42 + + a = add_forty_two(3) + +Since ``printing_decorator`` is not type-annotated, the following won't get type checked: + +.. code-block:: python + + reveal_type(a) # Revealed type is "Any" + add_forty_two('foo') # No type checker error :( + +This is a sorry state of affairs! + +Here's how one could annotate the decorator: + +.. code-block:: python + + from typing import Any, Callable, TypeVar, cast + + F = TypeVar('F', bound=Callable[..., Any]) + + # A decorator that preserves the signature. + def printing_decorator(func: F) -> F: + def wrapper(*args, **kwds): + print("Calling", func) + return func(*args, **kwds) + return cast(F, wrapper) + + @printing_decorator + def add_forty_two(value: int) -> int: + return value + 42 + + a = add_forty_two(3) + reveal_type(a) # Revealed type is "builtins.int" + add_forty_two('x') # Argument 1 to "add_forty_two" has incompatible type "str"; expected "int" + +This still has some shortcomings. First, we need to use the unsafe +:py:func:`~typing.cast` to convince type checkers that ``wrapper()`` has the same +signature as ``func``. + +Second, the ``wrapper()`` function is not tightly type checked, although +wrapper functions are typically small enough that this is not a big +problem. This is also the reason for the :py:func:`~typing.cast` call in the +``return`` statement in ``printing_decorator()``. + +However, we can use a parameter specification (:py:class:`~typing.ParamSpec`), +for a more faithful type annotation: + +.. code-block:: python + + from typing import Callable, ParamSpec, TypeVar + + P = ParamSpec('P') + T = TypeVar('T') + + def printing_decorator(func: Callable[P, T]) -> Callable[P, T]: + def wrapper(*args: P.args, **kwds: P.kwargs) -> T: + print("Calling", func) + return func(*args, **kwds) + return wrapper + +.. note:: + + To use this feature on Python versions earlier than 3.10, you will need to + import ``ParamSpec`` and ``Concatenate`` from ``typing_extensions``. + +Parameter specifications also allow you to describe decorators that +alter the signature of the input function: + +.. code-block:: python + + from typing import Callable, ParamSpec, TypeVar + + P = ParamSpec('P') + T = TypeVar('T') + + # We reuse 'P' in the return type, but replace 'T' with 'str' + def stringify(func: Callable[P, T]) -> Callable[P, str]: + def wrapper(*args: P.args, **kwds: P.kwargs) -> str: + return str(func(*args, **kwds)) + return wrapper + + @stringify + def add_forty_two(value: int) -> int: + return value + 42 + + a = add_forty_two(3) + reveal_type(a) # Revealed type is "builtins.str" + add_forty_two('x') # error: Argument 1 to "add_forty_two" has incompatible type "str"; expected "int" + +Or insert an argument: + +.. code-block:: python + + from typing import Callable, Concatenate, ParamSpec, TypeVar + + P = ParamSpec('P') + T = TypeVar('T') + + def printing_decorator(func: Callable[P, T]) -> Callable[Concatenate[str, P], T]: + def wrapper(msg: str, /, *args: P.args, **kwds: P.kwargs) -> T: + print("Calling", func, "with", msg) + return func(*args, **kwds) + return wrapper + + @printing_decorator + def add_forty_two(value: int) -> int: + return value + 42 + + a = add_forty_two('three', 3) + +.. _decorator-factories: + +Decorator factories +------------------- + +Functions that take arguments and return a decorator (also called second-order decorators), are +similarly supported via generics: + +.. code-block:: python + + from typing import Any, Callable, TypeVar + + F = TypeVar('F', bound=Callable[..., Any]) + + def route(url: str) -> Callable[[F], F]: + ... + + @route(url='/') + def index(request: Any) -> str: + return 'Hello world' + +Sometimes the same decorator supports both bare calls and calls with arguments. This can be +achieved by combining with :py:func:`@overload `: + +.. code-block:: python + + from typing import Any, Callable, Optional, TypeVar, overload + + F = TypeVar('F', bound=Callable[..., Any]) + + # Bare decorator usage + @overload + def atomic(__func: F) -> F: ... + # Decorator with arguments + @overload + def atomic(*, savepoint: bool = True) -> Callable[[F], F]: ... + + # Implementation + def atomic(__func: Optional[Callable[..., Any]] = None, *, savepoint: bool = True): + def decorator(func: Callable[..., Any]): + ... # Code goes here + if __func is not None: + return decorator(__func) + else: + return decorator + + # Usage + @atomic + def func1() -> None: ... + + @atomic(savepoint=False) + def func2() -> None: ... + +Generic protocols +***************** + +Protocols can also be generic (see also :ref:`protocol-types`). Several +:ref:`predefined protocols ` are generic, such as +:py:class:`Iterable[T] `, and you can define additional generic +protocols. Generic protocols mostly follow the normal rules for generic classes. +Example: + +.. code-block:: python + + from typing import Protocol, TypeVar + + T = TypeVar('T') + + class Box(Protocol[T]): + content: T + + def do_stuff(one: Box[str], other: Box[bytes]) -> None: + ... + + class StringWrapper: + def __init__(self, content: str) -> None: + self.content = content + + class BytesWrapper: + def __init__(self, content: bytes) -> None: + self.content = content + + do_stuff(StringWrapper('one'), BytesWrapper(b'other')) # OK + + x: Box[float] = ... + y: Box[int] = ... + x = y # Error -- Box is invariant + +Note that ``class ClassName(Protocol[T])`` is allowed as a shorthand for +``class ClassName(Protocol, Generic[T])``, as per :pep:`PEP 544: Generic protocols <544#generic-protocols>`, + +The main difference between generic protocols and ordinary generic classes is +that the declared variances of generic type variables in a protocol are checked +against how they are used in the protocol definition. The protocol in this +example is rejected, since the type variable ``T`` is used covariantly as a +return type, but the type variable is invariant: + +.. code-block:: python + + from typing import Protocol, TypeVar + + T = TypeVar('T') + + class ReadOnlyBox(Protocol[T]): # error: Invariant type variable "T" used in protocol where covariant one is expected + def content(self) -> T: ... + +This example correctly uses a covariant type variable: + +.. code-block:: python + + from typing import Protocol, TypeVar + + T_co = TypeVar('T_co', covariant=True) + + class ReadOnlyBox(Protocol[T_co]): # OK + def content(self) -> T_co: ... + + ax: ReadOnlyBox[float] = ... + ay: ReadOnlyBox[int] = ... + ax = ay # OK -- ReadOnlyBox is covariant + +See :ref:`variance-of-generics` for more about variance. + +Generic protocols can also be recursive. Example: + +.. code-block:: python + + T = TypeVar('T') + + class Linked(Protocol[T]): + val: T + def next(self) -> 'Linked[T]': ... + + class L: + val: int + def next(self) -> 'L': ... + + def last(seq: Linked[T]) -> T: ... + + result = last(L()) + reveal_type(result) # Revealed type is "builtins.int" + +.. _generic-type-aliases: + +Generic type aliases +******************** + +Type aliases can be generic. In this case they can be used in two ways: +Subscripted aliases are equivalent to original types with substituted type +variables, so the number of type arguments must match the number of free type variables +in the generic type alias. Unsubscripted aliases are treated as original types with free +variables replaced with ``Any``. Examples (following :pep:`PEP 484: Type aliases +<484#type-aliases>`): + +.. code-block:: python + + from typing import TypeVar, Iterable, Union, Callable + + S = TypeVar('S') + + TInt = tuple[int, S] + UInt = Union[S, int] + CBack = Callable[..., S] + + def response(query: str) -> UInt[str]: # Same as Union[str, int] + ... + def activate(cb: CBack[S]) -> S: # Same as Callable[..., S] + ... + table_entry: TInt # Same as tuple[int, Any] + + T = TypeVar('T', int, float, complex) + + Vec = Iterable[tuple[T, T]] + + def inproduct(v: Vec[T]) -> T: + return sum(x*y for x, y in v) + + def dilate(v: Vec[T], scale: T) -> Vec[T]: + return ((x * scale, y * scale) for x, y in v) + + v1: Vec[int] = [] # Same as Iterable[tuple[int, int]] + v2: Vec = [] # Same as Iterable[tuple[Any, Any]] + v3: Vec[int, int] = [] # Error: Invalid alias, too many type arguments! + +Type aliases can be imported from modules just like other names. An +alias can also target another alias, although building complex chains +of aliases is not recommended -- this impedes code readability, thus +defeating the purpose of using aliases. Example: + +.. code-block:: python + + from typing import TypeVar, Generic, Optional + from example1 import AliasType + from example2 import Vec + + # AliasType and Vec are type aliases (Vec as defined above) + + def fun() -> AliasType: + ... + + T = TypeVar('T') + + class NewVec(Vec[T]): + ... + + for i, j in NewVec[int](): + ... + + OIntVec = Optional[Vec[int]] + +Using type variable bounds or values in generic aliases has the same effect +as in generic classes/functions. + + +Generic class internals +*********************** + +You may wonder what happens at runtime when you index a generic class. +Indexing returns a *generic alias* to the original class that returns instances +of the original class on instantiation: + +.. code-block:: python + + >>> from typing import TypeVar, Generic + >>> T = TypeVar('T') + >>> class Stack(Generic[T]): ... + >>> Stack + __main__.Stack + >>> Stack[int] + __main__.Stack[int] + >>> instance = Stack[int]() + >>> instance.__class__ + __main__.Stack + +Generic aliases can be instantiated or subclassed, similar to real +classes, but the above examples illustrate that type variables are +erased at runtime. Generic ``Stack`` instances are just ordinary +Python objects, and they have no extra runtime overhead or magic due +to being generic, other than overloading the indexing operation. + +Note that in Python 3.8 and lower, the built-in types +:py:class:`list`, :py:class:`dict` and others do not support indexing. +This is why we have the aliases :py:class:`~typing.List`, +:py:class:`~typing.Dict` and so on in the :py:mod:`typing` +module. Indexing these aliases gives you a generic alias that +resembles generic aliases constructed by directly indexing the target +class in more recent versions of Python: + +.. code-block:: python + + >>> # Only relevant for Python 3.8 and below + >>> # For Python 3.9 onwards, prefer `list[int]` syntax + >>> from typing import List + >>> List[int] + typing.List[int] + +Note that the generic aliases in ``typing`` don't support constructing +instances: + +.. code-block:: python + + >>> from typing import List + >>> List[int]() + Traceback (most recent call last): + ... + TypeError: Type List cannot be instantiated; use list() instead + +Credits +******* + +This document is based on the `mypy documentation `_ diff --git a/docs/reference/index.rst b/docs/reference/index.rst new file mode 100644 index 000000000..bab77a268 --- /dev/null +++ b/docs/reference/index.rst @@ -0,0 +1,18 @@ +:orphan: + +********************* +Type System Reference +********************* + +.. + Keep in sync with docs/index.rst. + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + generics + protocols + best_practices + quality + typing Module Documentation diff --git a/docs/reference/protocols.rst b/docs/reference/protocols.rst new file mode 100644 index 000000000..72d1a3e80 --- /dev/null +++ b/docs/reference/protocols.rst @@ -0,0 +1,572 @@ +.. _protocol-types: + +Protocols and structural subtyping +================================== + +The Python type system supports two ways of deciding whether two objects are +compatible as types: nominal subtyping and structural subtyping. + +*Nominal* subtyping is strictly based on the class hierarchy. If class ``Dog`` +inherits class ``Animal``, it's a subtype of ``Animal``. Instances of ``Dog`` +can be used when ``Animal`` instances are expected. This form of subtyping +is what Python's type system predominantly uses: it's easy to +understand and produces clear and concise error messages, and matches how the +native :py:func:`isinstance ` check works -- based on class +hierarchy. + +*Structural* subtyping is based on the operations that can be performed with an +object. Class ``Dog`` is a structural subtype of class ``Animal`` if the former +has all attributes and methods of the latter, and with compatible types. + +Structural subtyping can be seen as a static equivalent of duck typing, which is +well known to Python programmers. See :pep:`544` for the detailed specification +of protocols and structural subtyping in Python. + +.. _predefined_protocols: + +Predefined protocols +******************** + +The :py:mod:`typing` module defines various protocol classes that correspond to +places where duck typing is commonly used in Python, such as +:py:class:`Iterable[T] `. + +If a class defines a suitable :py:meth:`__iter__ ` method, type +checkers understand that it implements the :py:term:`iterable` protocol and is compatible +with :py:class:`Iterable[T] `. For example, ``IntList`` below +is iterable, over ``int`` values: + +.. code-block:: python + + from typing import Iterator, Iterable, Optional + + class IntList: + def __init__(self, value: int, next_node: Optional['IntList']) -> None: + self.value = value + self.next_node = next_node + + def __iter__(self) -> Iterator[int]: + current = self + while current: + yield current.value + current = current.next_node + + def print_numbered(items: Iterable[int]) -> None: + for n, x in enumerate(items): + print(n + 1, x) + + x = IntList(3, IntList(5, None)) + print_numbered(x) # OK + print_numbered([4, 5]) # Also OK + +:ref:`predefined_protocols_reference` lists all protocols defined in +:py:mod:`typing` and the signatures of the corresponding methods you need to define +to implement each protocol. + +Simple user-defined protocols +***************************** + +You can define your own protocol class by inheriting from the special +``Protocol`` class: + +.. code-block:: python + + from typing import Iterable, Protocol + + class SupportsClose(Protocol): + # Empty method body (explicit '...') + def close(self) -> None: ... + + class Resource: # No SupportsClose base class! + + def close(self) -> None: + self.resource.release() + + # ... other methods ... + + def close_all(items: Iterable[SupportsClose]) -> None: + for item in items: + item.close() + + close_all([Resource(), open('some/file')]) # OK + +``Resource`` is a subtype of the ``SupportsClose`` protocol since it defines +a compatible ``close`` method. Regular file objects returned by :py:func:`open` are +similarly compatible with the protocol, as they support ``close()``. + +Defining subprotocols and subclassing protocols +*********************************************** + +You can also define subprotocols. Existing protocols can be extended +and merged using multiple inheritance. Example: + +.. code-block:: python + + # ... continuing from the previous example + + class SupportsRead(Protocol): + def read(self, amount: int) -> bytes: ... + + class TaggedReadableResource(SupportsClose, SupportsRead, Protocol): + label: str + + class AdvancedResource(Resource): + def __init__(self, label: str) -> None: + self.label = label + + def read(self, amount: int) -> bytes: + # some implementation + ... + + resource: TaggedReadableResource + resource = AdvancedResource('handle with care') # OK + +Note that inheriting from an existing protocol does not automatically +turn the subclass into a protocol -- it just creates a regular +(non-protocol) class or ABC that implements the given protocol (or +protocols). The ``Protocol`` base class must always be explicitly +present if you are defining a protocol: + +.. code-block:: python + + class NotAProtocol(SupportsClose): # This is NOT a protocol + new_attr: int + + class Concrete: + new_attr: int = 0 + + def close(self) -> None: + ... + + # Error: nominal subtyping used by default + x: NotAProtocol = Concrete() # Error! + +You can also include default implementations of methods in +protocols. If you explicitly subclass these protocols, you inherit +these default implementations. + +Explicitly including a protocol as a +base class is also a way of documenting that your class implements a +particular protocol, and it forces the type checker to verify that your class +implementation is actually compatible with the protocol. In particular, +omitting a value for an attribute or a method body will make it implicitly +abstract: + +.. code-block:: python + + class SomeProto(Protocol): + attr: int # Note, no right hand side + # If the body is literally just "...", explicit subclasses will + # be treated as abstract classes if they don't implement the method. + def method(self) -> str: ... + + class ExplicitSubclass(SomeProto): + pass + + ExplicitSubclass() # error: Cannot instantiate abstract class 'ExplicitSubclass' + # with abstract attributes 'attr' and 'method' + +Similarly, explicitly assigning to a protocol instance can be a way to ask the +type checker to verify that your class implements a protocol: + +.. code-block:: python + + _proto: SomeProto = cast(ExplicitSubclass, None) + +Invariance of protocol attributes +********************************* + +A common issue with protocols is that protocol attributes are invariant. +For example: + +.. code-block:: python + + class Box(Protocol): + content: object + + class IntBox: + content: int + + def takes_box(box: Box) -> None: ... + + takes_box(IntBox()) # error: Argument 1 to "takes_box" has incompatible type "IntBox"; expected "Box" + # note: Following member(s) of "IntBox" have conflicts: + # note: content: expected "object", got "int" + +This is because ``Box`` defines ``content`` as a mutable attribute. +Here's why this is problematic: + +.. code-block:: python + + def takes_box_evil(box: Box) -> None: + box.content = "asdf" # This is bad, since box.content is supposed to be an object + + my_int_box = IntBox() + takes_box_evil(my_int_box) + my_int_box.content + 1 # Oops, TypeError! + +This can be fixed by declaring ``content`` to be read-only in the ``Box`` +protocol using ``@property``: + +.. code-block:: python + + class Box(Protocol): + @property + def content(self) -> object: ... + + class IntBox: + content: int + + def takes_box(box: Box) -> None: ... + + takes_box(IntBox(42)) # OK + +Recursive protocols +******************* + +Protocols can be recursive (self-referential) and mutually +recursive. This is useful for declaring abstract recursive collections +such as trees and linked lists: + +.. code-block:: python + + from typing import TypeVar, Optional, Protocol + + class TreeLike(Protocol): + value: int + + @property + def left(self) -> Optional['TreeLike']: ... + + @property + def right(self) -> Optional['TreeLike']: ... + + class SimpleTree: + def __init__(self, value: int) -> None: + self.value = value + self.left: Optional['SimpleTree'] = None + self.right: Optional['SimpleTree'] = None + + root: TreeLike = SimpleTree(0) # OK + +Using isinstance() with protocols +********************************* + +You can use a protocol class with :py:func:`isinstance` if you decorate it +with the ``@runtime_checkable`` class decorator. The decorator adds +rudimentary support for runtime structural checks: + +.. code-block:: python + + from typing import Protocol, runtime_checkable + + @runtime_checkable + class Portable(Protocol): + handles: int + + class Mug: + def __init__(self) -> None: + self.handles = 1 + + def use(handles: int) -> None: ... + + mug = Mug() + if isinstance(mug, Portable): # Works at runtime! + use(mug.handles) + +:py:func:`isinstance` also works with the :ref:`predefined protocols ` +in :py:mod:`typing` such as :py:class:`~typing.Iterable`. + +.. warning:: + :py:func:`isinstance` with protocols is not completely safe at runtime. + For example, signatures of methods are not checked. The runtime + implementation only checks that all protocol members exist, + not that they have the correct type. :py:func:`issubclass` with protocols + will only check for the existence of methods. + +.. note:: + :py:func:`isinstance` with protocols can also be surprisingly slow. + In many cases, you're better served by using :py:func:`hasattr` to + check for the presence of attributes. + +.. _callback_protocols: + +Callback protocols +****************** + +Protocols can be used to define flexible callback types that are hard +(or even impossible) to express using the :py:data:`Callable[...] ` syntax, such as variadic, +overloaded, and complex generic callbacks. They are defined with a special :py:meth:`__call__ ` +member: + +.. code-block:: python + + from typing import Optional, Iterable, Protocol + + class Combiner(Protocol): + def __call__(self, *vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: ... + + def batch_proc(data: Iterable[bytes], cb_results: Combiner) -> bytes: + for item in data: + ... + + def good_cb(*vals: bytes, maxlen: Optional[int] = None) -> list[bytes]: + ... + def bad_cb(*vals: bytes, maxitems: Optional[int]) -> list[bytes]: + ... + + batch_proc([], good_cb) # OK + batch_proc([], bad_cb) # Error! Argument 2 has incompatible type because of + # different name and kind in the callback + +Callback protocols and :py:data:`~typing.Callable` types can be used mostly interchangeably. +Argument names in :py:meth:`__call__ ` methods must be identical, unless +a double underscore prefix is used. For example: + +.. code-block:: python + + from typing import Callable, TypeVar, Protocol + + T = TypeVar('T') + + class Copy(Protocol): + def __call__(self, __origin: T) -> T: ... + + copy_a: Callable[[T], T] + copy_b: Copy + + copy_a = copy_b # OK + copy_b = copy_a # Also OK + +.. _predefined_protocols_reference: + +Predefined protocol reference +***************************** + +Iteration protocols +................... + +The iteration protocols allow you to type things that can be iterated +over in ``for`` loops or things that can be passed to :py:func:`next`. + +Iterable[T] +----------- + +The :ref:`example above ` has a simple implementation of an +:py:meth:`__iter__ ` method. + +.. code-block:: python + + def __iter__(self) -> Iterator[T] + +See also :py:class:`~typing.Iterable`. + +Iterator[T] +----------- + +.. code-block:: python + + def __next__(self) -> T + def __iter__(self) -> Iterator[T] + +See also :py:class:`~typing.Iterator`. + +Collection protocols +.................... + +Many of these are implemented by built-in container types such as +:py:class:`list` and :py:class:`dict`, and these are also useful for user-defined +collection objects. + +Sized +----- + +This is a type for objects that support :py:func:`len(x) `. + +.. code-block:: python + + def __len__(self) -> int + +See also :py:class:`~typing.Sized`. + +Container[T] +------------ + +This is a type for objects that support the ``in`` operator. + +.. code-block:: python + + def __contains__(self, x: object) -> bool + +See also :py:class:`~typing.Container`. + +Collection[T] +------------- + +.. code-block:: python + + def __len__(self) -> int + def __iter__(self) -> Iterator[T] + def __contains__(self, x: object) -> bool + +See also :py:class:`~typing.Collection`. + +One-off protocols +................. + +These protocols are typically only useful with a single standard +library function or class. + +Reversible[T] +------------- + +This is a type for objects that support :py:func:`reversed(x) `. + +.. code-block:: python + + def __reversed__(self) -> Iterator[T] + +See also :py:class:`~typing.Reversible`. + +SupportsAbs[T] +-------------- + +This is a type for objects that support :py:func:`abs(x) `. ``T`` is the type of +value returned by :py:func:`abs(x) `. + +.. code-block:: python + + def __abs__(self) -> T + +See also :py:class:`~typing.SupportsAbs`. + +SupportsBytes +------------- + +This is a type for objects that support :py:class:`bytes(x) `. + +.. code-block:: python + + def __bytes__(self) -> bytes + +See also :py:class:`~typing.SupportsBytes`. + +.. _supports-int-etc: + +SupportsComplex +--------------- + +This is a type for objects that support :py:class:`complex(x) `. Note that no arithmetic operations +are supported. + +.. code-block:: python + + def __complex__(self) -> complex + +See also :py:class:`~typing.SupportsComplex`. + +SupportsFloat +------------- + +This is a type for objects that support :py:class:`float(x) `. Note that no arithmetic operations +are supported. + +.. code-block:: python + + def __float__(self) -> float + +See also :py:class:`~typing.SupportsFloat`. + +SupportsInt +----------- + +This is a type for objects that support :py:class:`int(x) `. Note that no arithmetic operations +are supported. + +.. code-block:: python + + def __int__(self) -> int + +See also :py:class:`~typing.SupportsInt`. + +SupportsRound[T] +---------------- + +This is a type for objects that support :py:func:`round(x) `. + +.. code-block:: python + + def __round__(self) -> T + +See also :py:class:`~typing.SupportsRound`. + +Async protocols +............... + +These protocols can be useful in async code. + +Awaitable[T] +------------ + +.. code-block:: python + + def __await__(self) -> Generator[Any, None, T] + +See also :py:class:`~typing.Awaitable`. + +AsyncIterable[T] +---------------- + +.. code-block:: python + + def __aiter__(self) -> AsyncIterator[T] + +See also :py:class:`~typing.AsyncIterable`. + +AsyncIterator[T] +---------------- + +.. code-block:: python + + def __anext__(self) -> Awaitable[T] + def __aiter__(self) -> AsyncIterator[T] + +See also :py:class:`~typing.AsyncIterator`. + +Context manager protocols +......................... + +There are two protocols for context managers -- one for regular context +managers and one for async ones. These allow defining objects that can +be used in ``with`` and ``async with`` statements. + +ContextManager[T] +----------------- + +.. code-block:: python + + def __enter__(self) -> T + def __exit__(self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType]) -> Optional[bool] + +See also :py:class:`~typing.ContextManager`. + +AsyncContextManager[T] +---------------------- + +.. code-block:: python + + def __aenter__(self) -> Awaitable[T] + def __aexit__(self, + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType]) -> Awaitable[Optional[bool]] + +See also :py:class:`~typing.AsyncContextManager`. + +Credits +******* + +This document is based on the `mypy documentation `_ diff --git a/docs/reference/quality.rst b/docs/reference/quality.rst new file mode 100644 index 000000000..c8fed8332 --- /dev/null +++ b/docs/reference/quality.rst @@ -0,0 +1,223 @@ +.. _testing: + +******************************************** +Testing and Ensuring Type Annotation Quality +******************************************** + +Testing Annotation Accuracy +=========================== + +When creating a package with type annotations, authors may want to validate +that the annotations they publish meet their expectations. +This is especially important for library authors, for whom the published +annotations are part of the public interface to their package. + +There are several approaches to this problem, and this document will show +a few of them. + +.. note:: + + For simplicity, we will assume that type-checking is done with ``mypy``. + Many of these strategies can be applied to other type-checkers as well. + +Testing Using ``assert_type`` and ``--warn-unused-ignores`` +----------------------------------------------------------- + +The idea is to write normal Python files, set aside in a dedicated directory like ``typing_tests/``, which assert certain properties +of the type annotations. + +``assert_type`` (``mypy`` 0.950 and above) can ensure that the type annotation produces the expected type. + +If the following file is under test: + +.. code-block:: python + + # foo.py + def bar(x: int) -> str: + return str(x) + +then the following file tests ``foo.py``: + +.. code-block:: python + + from typing import assert_type + + assert_type(bar(42), str) + +.. note:: + + To use this feature on Python versions earlier than 3.11, you will need to + import ``assert_type`` from ``typing_extensions`` (version 4.2 or newer). + +Clever use of ``mypy --warn-unused-ignores`` can be used to check that certain +expressions are or are not well-typed. The idea is to have valid expressions along +with invalid expressions annotated with ``type: ignore`` comments. When +``mypy --warn-unused-ignores`` is run on these files, it should pass. + +This strategy does not offer strong guarantees about the types under test, but +it requires no additional tooling. + +If the following file is under test: + +.. code-block:: python + + # foo.py + def bar(x: int) -> str: + return str(x) + +then the following file tests ``foo.py``: + +.. code-block:: python + + bar(42) + bar("42") # type: ignore [arg-type] + bar(y=42) # type: ignore [call-arg] + r1: str = bar(42) + r2: int = bar(42) # type: ignore [assignment] + +Checking ``reveal_type`` output from ``mypy.api.run`` +----------------------------------------------------- + +``mypy`` provides a subpackage named ``api`` for invoking ``mypy`` from a +python process. In combination with ``reveal_type``, this can be used to write +a function which gets the ``reveal_type`` output from an expression. Once +that's obtained, tests can assert strings and regular expression matches +against it. + +This approach requires writing a set of helpers to provide a good testing +experience, and it runs mypy once per test case (which can be slow). +However, it builds only on ``mypy`` and the test framework of your choice. + +The following example could be integrated into a testsuite written in +any framework: + +.. code-block:: python + + import re + from mypy import api + + def get_reveal_type_output(filename): + result = api.run([filename]) + stdout = result[0] + match = re.search(r'note: Revealed type is "([^"]+)"', stdout) + assert match is not None + return match.group(1) + + +For example, we can use the above to provide a ``run_reveal_type`` pytest +fixture which generates a temporary file and uses it as the input to +``get_reveal_type_output``: + +.. code-block:: python + + import os + import pytest + + @pytest.fixture + def _in_tmp_path(tmp_path): + cur = os.getcwd() + try: + os.chdir(tmp_path) + yield + finally: + os.chdir(cur) + + @pytest.fixture + def run_reveal_type(tmp_path, _in_tmp_path): + content_path = tmp_path / "reveal_type_test.py" + + def func(code_snippet, *, preamble = ""): + content_path.write_text(preamble + f"reveal_type({code_snippet})") + return get_reveal_type_output("reveal_type_test.py") + + return func + + +For more details, see `the documentation on mypy.api +`_. + +pytest-mypy-plugins +------------------- + +`pytest-mypy-plugins `_ is +a plugin for ``pytest`` which defines typing test cases as YAML data. +The test cases are run through ``mypy`` and the output of ``reveal_type`` can +be asserted. + +This project supports complex typing arrangements like ``pytest`` parametrized +tests and per-test ``mypy`` configuration. It requires that you are using +``pytest`` to run your tests, and runs ``mypy`` in a subprocess per test case. + +This is an example of a parametrized test with ``pytest-mypy-plugins``: + +.. code-block:: yaml + + - case: with_params + parametrized: + - val: 1 + rt: builtins.int + - val: 1.0 + rt: builtins.float + main: | + reveal_type({[ val }}) # N: Revealed type is '{{ rt }}' + +Improving Type Completeness +=========================== + +One of the goals of many libraries is to ensure that they are "fully type +annotated", meaning that they provide complete and accurate type annotations +for all functions, classes, and objects. Having full annotations is referred to +as "type completeness" or "type coverage". + +Here are some tips for increasing the type completeness score for your +library: + +- Make type completeness an output of your testing process. Several type + checkers have options for generating useful output, warnings, or even + reports. +- If your package includes tests or sample code, consider removing them + from the distribution. If there is good reason to include them, + consider placing them in a directory that begins with an underscore + so they are not considered part of your library’s interface. +- If your package includes submodules that are meant to be + implementation details, rename those files to begin with an + underscore. +- If a symbol is not intended to be part of the library’s interface and + is considered an implementation detail, rename it such that it begins + with an underscore. It will then be considered private and excluded + from the type completeness check. +- If your package exposes types from other libraries, work with the + maintainers of these other libraries to achieve type completeness. + +.. warning:: + + The ways in which different type checkers evaluate and help you achieve + better type coverage may differ. Some of the above recommendations may or + may not be helpful to you, depending on which type checking tools you use. + +Mypy disallow options +------------------------- + +Mypy offers several options which can detect untyped code. +More details can be found in `the mypy documentation on these options +`_. + +Some basic usages which make ``mypy`` error on untyped data are:: + + mypy --disallow-untyped-defs + mypy --disallow-incomplete-defs + +Pyright type verification +----------------------------- + +Pyright has a special command-line flag, ``--verifytypes``, for verifying +type completeness. You can learn more about it from +`the pyright documentation on verifying type completeness +`_. + +Mypy reports +---------------- + +Mypy offers several options for generating reports on its analysis. +See `the mypy documentation on report generation +`_ for details. diff --git a/docs/reference/stubs.rst b/docs/reference/stubs.rst new file mode 100644 index 000000000..b0ce56711 --- /dev/null +++ b/docs/reference/stubs.rst @@ -0,0 +1,8 @@ +:orphan: + +********** +Type Stubs +********** + +The contents of this document have been moved to :ref:`stub-files` and +:ref:`writing_stubs`. diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..e1598b5e2 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,10 @@ +# Requirements to build the Python documentation + +# Sphinx version is pinned so that new versions that introduce new warnings +# won't suddenly cause build failures. Updating the version is fine as long +# as no warnings are raised by doing so. +sphinx==7.2.6 + +# The theme used by the documentation is stored separately, so we need +# to install that as well. +python-docs-theme diff --git a/docs/spec/aliases.rst b/docs/spec/aliases.rst new file mode 100644 index 000000000..cc5e70b55 --- /dev/null +++ b/docs/spec/aliases.rst @@ -0,0 +1,195 @@ +.. _`type-aliases`: + +Type aliases +============ + +(See :pep:`613` for the introduction of ``TypeAlias``, and +:pep:`695` for the ``type`` statement.) + +Type aliases may be defined by simple variable assignments:: + + Url = str + + def retry(url: Url, retry_count: int) -> None: ... + +Or by using ``typing.TypeAlias``:: + + from typing import TypeAlias + + Url: TypeAlias = str + + def retry(url: Url, retry_count: int) -> None: ... + +Or by using the ``type`` statement (Python 3.12 and higher):: + + type Url = str + + def retry(url: Url, retry_count: int) -> None: ... + +Note that we recommend capitalizing alias names, since they represent +user-defined types, which (like user-defined classes) are typically +spelled that way. + +Type aliases may be as complex as type hints in annotations -- +anything that is acceptable as a type hint is acceptable in a type +alias:: + + from collections.abc import Iterable + + type Vector[T: float] = Iterable[tuple[T, T]] + + def inproduct[T: float](v: Vector[T]) -> T: + return sum(x*y for x, y in v) + def dilate[T: float](v: Vector[T], scale: T) -> Vector[T]: + return ((x * scale, y * scale) for x, y in v) + vec: Vector[float] = [] + + +This is equivalent to:: + + from collections.abc import Iterable + + def inproduct[T: float](v: Iterable[tuple[T, T]]) -> T: + return sum(x*y for x, y in v) + def dilate[T: float](v: Iterable[tuple[T, T]], scale: T) -> Iterable[tuple[T, T]]: + return ((x * scale, y * scale) for x, y in v) + vec: Iterable[tuple[float, float]] = [] + +.. _`typealias`: + +``TypeAlias`` +------------- + +The explicit alias declaration syntax with ``TypeAlias`` clearly differentiates between the three +possible kinds of assignments: typed global expressions, untyped global +expressions, and type aliases. This avoids the existence of assignments that +break type checking when an annotation is added, and avoids classifying the +nature of the assignment based on the type of the value. + +Implicit syntax (pre-existing): + +:: + + x = 1 # untyped global expression + x: int = 1 # typed global expression + + x = int # type alias + x: type[int] = int # typed global expression + + +Explicit syntax: + +:: + + x = 1 # untyped global expression + x: int = 1 # typed global expression + + x = int # untyped global expression (see note below) + x: type[int] = int # typed global expression + + x: TypeAlias = int # type alias + x: TypeAlias = "MyClass" # type alias + + +Note: The examples above illustrate implicit and explicit alias declarations in +isolation. For the sake of backwards compatibility, type checkers should support +both simultaneously, meaning an untyped global expression ``x = int`` will +still be considered a valid type alias. + +.. _`type-statement`: + +``type`` statement +------------------ + +Type aliases may also be defined using the ``type`` statement (Python 3.12 and +higher). + +The ``type`` statement allows the creation of explicitly generic +type aliases:: + + type ListOrSet[T] = list[T] | set[T] + +Type parameters declared as part of a generic type alias are valid only +when evaluating the right-hand side of the type alias. + +As with ``typing.TypeAlias``, type checkers should restrict the right-hand +expression to expression forms that are allowed within type annotations. +The use of more complex expression forms (call expressions, ternary operators, +arithmetic operators, comparison operators, etc.) should be flagged as an +error. + +Type alias expressions are not allowed to use traditional type variables (i.e. +those allocated with an explicit ``TypeVar`` constructor call). Type checkers +should generate an error in this case. + +:: + + T = TypeVar("T") + type MyList = list[T] # Type checker error: traditional type variable usage + +.. _`newtype`: + +``NewType`` +----------- + +There are also situations where a programmer might want to avoid logical +errors by creating simple classes. For example:: + + class UserId(int): + pass + + def get_by_user_id(user_id: UserId): + ... + +However, this approach introduces a runtime overhead. To avoid this, +``typing.py`` provides a helper function ``NewType`` that creates +simple unique types with almost zero runtime overhead. For a static type +checker ``Derived = NewType('Derived', Base)`` is roughly equivalent +to a definition:: + + class Derived(Base): + def __init__(self, _x: Base) -> None: + ... + +While at runtime, ``NewType('Derived', Base)`` returns a dummy object +that simply returns its argument when called. Type checkers require explicit +casts from ``int`` where ``UserId`` is expected, while implicitly casting +from ``UserId`` where ``int`` is expected. Examples:: + + UserId = NewType('UserId', int) + + def name_by_id(user_id: UserId) -> str: + ... + + UserId('user') # Fails type check + + name_by_id(42) # Fails type check + name_by_id(UserId(42)) # OK + + num = UserId(5) + 1 # type: int + +``NewType`` accepts exactly two arguments: a name for the new unique type, +and a base class. The latter should be a proper class (i.e., +not a type construct like ``Union``, etc.), or another unique type created +by calling ``NewType``. The callable returned by ``NewType`` +accepts only one argument; this is equivalent to supporting only one +constructor accepting an instance of the base class (see above). Example:: + + class PacketId: + def __init__(self, major: int, minor: int) -> None: + self._major = major + self._minor = minor + + TcpPacketId = NewType('TcpPacketId', PacketId) + + packet = PacketId(100, 100) + tcp_packet = TcpPacketId(packet) # OK + + tcp_packet = TcpPacketId(127, 0) # Fails in type checker and at runtime + +Both ``isinstance`` and ``issubclass``, as well as subclassing will fail +for ``NewType('Derived', Base)``, since the object returned by a call to +``NewType`` is not a class. + +See also :ref:`protocol-newtype-aliases` for a discussion of how +``NewType`` interacts with protocol definitions. diff --git a/docs/spec/annotations.rst b/docs/spec/annotations.rst new file mode 100644 index 000000000..085d9277b --- /dev/null +++ b/docs/spec/annotations.rst @@ -0,0 +1,395 @@ +.. _`type-annotations`: + +Type annotations +================ + +The meaning of annotations +-------------------------- + +The type system leverages :pep:`3107`-style annotations with a number of +extensions described in sections below. In its basic form, type +hinting is used by filling function annotation slots with classes:: + + def greeting(name: str) -> str: + return 'Hello ' + name + +This states that the expected type of the ``name`` argument is +``str``. Analogically, the expected return type is ``str``. + +Expressions whose type is :term:`assignable` to a specific argument type are +also accepted for that argument. Similarly, an expression whose type is +assignable to the annotated return type can be returned from the function. + +.. _`missing-annotations`: + +Any function without annotations can be treated as having :ref:`Any` +annotations on all arguments and the return type. Type checkers may also +optionally infer more precise types for missing annotations. + +Type checkers may choose to entirely ignore (not type check) the bodies of +functions with no annotations, but this behavior is not required. + +It is recommended but not required that checked functions have +annotations for all arguments and the return type. For a checked +function, the default annotation for arguments and for the return type +is ``Any``. An exception to the above is the first argument of +instance and class methods (conventionally named ``self`` or ``cls``), +which type checkers should assume to have an appropriate type, as per +:ref:`annotating-methods`. + +(Note that the return type of ``__init__`` ought to be annotated with +``-> None``. The reason for this is subtle. If ``__init__`` assumed +a return annotation of ``-> None``, would that mean that an +argument-less, un-annotated ``__init__`` method should still be +type-checked? Rather than leaving this ambiguous or introducing an +exception to the exception, we simply say that ``__init__`` ought to +have a return annotation; the default behavior is thus the same as for +other methods.) + +A type checker is expected to check the body of a checked function for +consistency with the given annotations. The annotations may also be +used to check correctness of calls appearing in other checked functions. + +Type checkers are expected to attempt to infer as much information as +necessary. The minimum requirement is to handle the builtin +decorators ``@property``, ``@staticmethod`` and ``@classmethod``. + +.. _valid-types: + +Type and annotation expressions +------------------------------- + +The terms *type expression* and *annotation expression* denote specific +subsets of Python expressions that are used in the type system. All +type expressions are also annotation expressions, but not all annotation +expressions are type expressions. + +.. _`type-expression`: + +A *type expression* is any expression that validly expresses a type. Type +expressions are always acceptable in annotations and also in various other +places. Specifically, type expressions are used in the following locations: + +* In a type annotation (always as part of an annotation expression) +* The first argument to :ref:`cast() ` +* The second argument to :ref:`assert_type() ` +* The bounds and constraints of a ``TypeVar`` (whether created through the + old syntax or the native syntax in Python 3.12) +* The default of a ``TypeVar``, ``ParamSpec``, or ``TypeVarTuple`` (whether + created through the old syntax or the native syntax in Python 3.12) +* The definition of a type alias (whether created through the ``type`` statement, + the old assignment syntax, or the ``TypeAliasType`` constructor) +* The type arguments of a generic class (which may appear in a base class + or in a constructor call) +* The definitions of fields in the functional forms for creating + :ref:`TypedDict ` and :ref:`NamedTuple ` types +* The value passed to the ``extra_items`` argument in the :ref:`TypedDict ` + constructor +* The base type in the definition of a :ref:`NewType ` + +.. _`annotation-expression`: + +An *annotation expression* is an expression that is acceptable to use in +an annotation context (a function parameter annotation, function return +annotation, or variable annotation). Generally, an annotation expression +is a type expression, optionally surrounded by one or more :term:`type qualifiers ` +or by `Annotated`. Each type qualifier is valid only in some contexts. Note +that while annotation expressions are the only expressions valid as type +annotations in the type system, the Python language itself makes no such +restriction: any expression is allowed. + +Annotations must be valid expressions that evaluate without raising +exceptions at the time the function is defined (but see :ref:`forward-references`). + +.. _`expression-grammar`: + +The following grammar describes the allowed elements of type and annotation expressions: + +.. productionlist:: expression-grammar + annotation_expression: '[' `annotation_expression` ']' + : | '[' `annotation_expression` ']' + : | '[' `annotation_expression`']' + : | ('[' `annotation_expression`']')? + : | ('[' `annotation_expression`']')? + : | '[' `annotation_expression` ']' + : | '[' `annotation_expression` ',' + : expression (',' expression)* ']' + : | + : (valid only in variable annotations) + : | `unpacked` + : (valid only for *args annotations) + : | '[' name ']' + : (where name refers to an in-scope TypedDict; + : valid only in **kwargs annotations) + : | `string_annotation` + : (must evaluate to a valid `annotation_expression`) + : | name '.' 'args' + : (where name must be an in-scope ParamSpec; + : valid only in *args annotations) + : | name '.' 'kwargs' + : (where name must be an in-scope ParamSpec; + : valid only in **kwargs annotations) + : | `type_expression` + type_expression: + : | + : (valid only in some contexts) + : | + : | + : | + : | + : | name + : (where name must refer to a valid in-scope class, + : type alias, or TypeVar) + : | name '[' (`maybe_unpacked` | `type_expression_list`) + : (',' (`maybe_unpacked` | `type_expression_list`))* ']' + : (the `type_expression_list` form is valid only when + : specializing a ParamSpec) + : | name '[' '(' ')' ']' + : (denoting specialization with an empty TypeVarTuple) + : | '[' expression (',' expression) ']' + : (see documentation for Literal for restrictions) + : | `type_expression` '|' `type_expression` + : | '[' `type_expression` ']' + : | '[' `type_expression` (',' `type_expression`)* ']' + : | '[' ']' + : | '[' name ']' + : (where name must refer to a valid in-scope class + : or TypeVar) + : | '[' '...' ',' `type_expression` ']' + : | '[' name ',' `type_expression` ']' + : (where name must be a valid in-scope ParamSpec) + : | '[' '[' (`type_expression` ',')+ + : (name | '...') ']' ',' `type_expression` ']' + : (where name must be a valid in-scope ParamSpec) + : | '[' '[' `maybe_unpacked` (',' `maybe_unpacked`)* + : ']' ',' `type_expression` ']' + : | `tuple_type_expression` + : | '[' `type_expression` ',' + : expression (',' expression)* ']' + : | '[' `type_expression` ']' + : (valid only in some contexts) + : | '[' `type_expression` ']' + : (valid only in some contexts) + : | ('[' `type_expression` ']')? + : | `string_annotation` + : (must evaluate to a valid `type_expression`) + maybe_unpacked: `type_expression` | `unpacked` + unpacked: '*' `unpackable` + : | '[' `unpackable` ']' + unpackable: `tuple_type_expression`` + : | name + : (where name must refer to an in-scope TypeVarTuple) + tuple_type_expression: '[' '(' ')' ']' + : (representing an empty tuple) + : | '[' `type_expression` ',' '...' ']' + : (representing an arbitrary-length tuple) + : | '[' `maybe_unpacked` (',' `maybe_unpacked`)* ']' + string_annotation: string + : (must be a string literal that is parsable + : as Python code; see "String annotations") + type_expression_list: '[' `type_expression` (',' `type_expression`)* ']' + : | '[' ']' + +Notes: + +* The grammar assumes the code has already been parsed as Python code, and + loosely follows the structure of the AST. Syntactic details like comments + and whitespace are ignored. + +* ```` refers to a :term:`special form`. Most special forms must be imported + from :py:mod:`typing` or ``typing_extensions``, except for ``None``, ``InitVar``, + ``type``, and ``tuple``. The latter two have aliases in :py:mod:`typing`: :py:class:`typing.Type` + and :py:class:`typing.Tuple`. ``InitVar`` must be imported from :py:mod:`dataclasses`. + ``Callable`` may be imported from either :py:mod:`typing` or :py:mod:`collections.abc`. + Special forms may be aliased + (e.g., ``from typing import Literal as L``), and they may be referred to by a + qualified name (e.g., ``typing.Literal``). There are other special forms that are not + acceptable in any annotation or type expression, including ``Generic``, ``Protocol``, + and ``TypedDict``. + +* Any leaf denoted as ``name`` may also be a qualified name (i.e., ``module '.' name`` + or ``package '.' module '.' name``, with any level of nesting). + +* Comments in parentheses denote additional restrictions not expressed in the + grammar, or brief descriptions of the meaning of a construct. + +.. _ `string-annotations`: + +.. _`forward-references`: + +String annotations +------------------ + +When a type hint cannot be evaluated at runtime, that +definition may be expressed as a string literal, to be resolved later. + +A situation where this occurs commonly is the definition of a +container class, where the class being defined occurs in the signature +of some of the methods. For example, the following code (the start of +a simple binary tree implementation) does not work:: + + class Tree: + def __init__(self, left: Tree, right: Tree): + self.left = left + self.right = right + +To address this, we write:: + + class Tree: + def __init__(self, left: 'Tree', right: 'Tree'): + self.left = left + self.right = right + +The string literal should contain a valid Python expression (i.e., +``compile(lit, '', 'eval')`` should be a valid code object) and it +should evaluate without errors once the module has been fully loaded. +The local and global namespace in which it is evaluated should be the +same namespaces in which default arguments to the same function would +be evaluated. + +Moreover, the expression should be parseable as a valid type hint, i.e., +it is constrained by the rules from :ref:`the expression grammar `. + +If a triple quote is used, the string should be parsed as though it is +implicitly surrounded by parentheses. This allows newline characters to be +used within the string literal:: + + value: """ + int | + str | + list[Any] + """ + +It is allowable to use string literals as *part* of a type hint, for +example:: + + class Tree: + ... + def leaves(self) -> list['Tree']: + ... + +A common use for forward references is when e.g. Django models are +needed in the signatures. Typically, each model is in a separate +file, and has methods taking arguments whose type involves other models. +Because of the way circular imports work in Python, it is often not +possible to import all the needed models directly:: + + # File models/a.py + from models.b import B + class A(Model): + def foo(self, b: B): ... + + # File models/b.py + from models.a import A + class B(Model): + def bar(self, a: A): ... + + # File main.py + from models.a import A + from models.b import B + +Assuming main is imported first, this will fail with an ImportError at +the line ``from models.a import A`` in models/b.py, which is being +imported from models/a.py before a has defined class A. The solution +is to switch to module-only imports and reference the models by their +_module_._class_ name:: + + # File models/a.py + from models import b + class A(Model): + def foo(self, b: 'b.B'): ... + + # File models/b.py + from models import a + class B(Model): + def bar(self, a: 'a.A'): ... + + # File main.py + from models.a import A + from models.b import B + +Annotating generator functions and coroutines +--------------------------------------------- + +The return type of generator functions can be annotated by +the generic type ``Generator[yield_type, send_type, +return_type]`` provided by ``typing.py`` module:: + + def echo_round() -> Generator[int, float, str]: + res = yield 0 + while res: + res = yield round(res) + return 'OK' + +Coroutines introduced in :pep:`492` are annotated with the same syntax as +ordinary functions. However, the return type annotation corresponds to the +type of ``await`` expression, not to the coroutine type:: + + async def spam(ignored: int) -> str: + return 'spam' + + async def foo() -> None: + bar = await spam(42) # type is str + +The generic ABC ``collections.abc.Coroutine`` can be used +to specify awaitables that also support +``send()`` and ``throw()`` methods. The variance and order of type variables +correspond to those of ``Generator``, namely ``Coroutine[T_co, T_contra, V_co]``, +for example:: + + from collections.abc import Coroutine + c: Coroutine[list[str], str, int] + ... + x = c.send('hi') # type is list[str] + async def bar() -> None: + x = await c # type is int + +The generic ABCs ``Awaitable``, +``AsyncIterable``, and ``AsyncIterator`` can be used for situations where more precise +types cannot be specified:: + + def op() -> collections.abc.Awaitable[str]: + if cond: + return spam(42) + else: + return asyncio.Future(...) + +.. _`annotating-methods`: + +Annotating instance and class methods +------------------------------------- + +In most cases the first argument of instance and class methods +(conventionally named ``self`` or ``cls``) does not need to be annotated. + +If the argument is not annotated, then for instance methods it may be +inferred to have either the type of the containing class, or the type :ref:`Self +`. For class methods it may be inferred to have either the type object +type corresponding to the containing class object, or ``type[Self]``. + +In addition, the first argument in an instance method can be annotated +with a type variable. In this case the return type may use the same +type variable, thus making that method a generic function. For example:: + + class Copyable: + def copy[T: Copyable](self: T) -> T: + # return a copy of self + + class C(Copyable): ... + c = C() + c2 = c.copy() # type here should be C + +The same applies to class methods using ``type[]`` in an annotation +of the first argument:: + + class C: + @classmethod + def factory[T: C](cls: type[T]) -> T: + # make a new instance of cls + + class D(C): ... + d = D.factory() # type here should be D + +Note that some type checkers may apply restrictions on this use, such as +requiring an appropriate upper bound for the type variable used +(see examples). diff --git a/docs/spec/callables.rst b/docs/spec/callables.rst new file mode 100644 index 000000000..a4028aae8 --- /dev/null +++ b/docs/spec/callables.rst @@ -0,0 +1,844 @@ +.. _`callables`: + +Callables +========= + +Terminology +----------- + +In this section, and throughout this specification, the term "parameter" +refers to a named symbol associated with a function that receives the value of +an argument (or multiple arguments) passed to the function. The term +"argument" refers to a value passed to a function when it is called. + +Python supports five kinds of parameters: positional-only, keyword-only, +standard (positional or keyword), variadic positional (``*args``), and +variadic keyword (``**kwargs``). Positional-only parameters can accept only +positional arguments, and keyword-only parameters can accept only keyword +arguments. Standard parameters can accept either positional or keyword +arguments. Parameters of the form ``*args`` and ``**kwargs`` are variadic +and accept zero or more positional or keyword arguments, respectively. + +In the example below, ``a`` is a positional-only parameter, ``b`` is +a standard (positional or keyword) parameter, ``c`` is a keyword-only parameter, +``args`` is a variadic parameter that accepts additional positional arguments, +and ``kwargs`` is a variadic parameter that accepts additional keyword +arguments:: + + def func(a: str, /, b, *args, c=0, **kwargs) -> None: + ... + +A function's "signature" refers to its list of parameters (including +the name, kind, optional declared type, and whether it has a default +argument value) plus its return type. The signature of the function above is +``(a: str, /, b, *args, c=..., **kwargs) -> None``. Note that the default +argument value for parameter ``c`` is denoted as ``...`` here because the +presence of a default value is considered part of the signature, but the +specific value is not. + +The term "input signature" is used to refer to only the parameters of a function. +In the example above, the input signature is ``(a: str, /, b, *args, c=..., **kwargs)``. + +Positional-only parameters +-------------------------- + +Within a function signature, positional-only parameters are separated from +non-positional-only parameters by a single forward slash ('/'). This +forward slash does not represent a parameter, but rather a delimiter. In this +example, ``a`` is a positional-only parameter and ``b`` is a standard +(positional or keyword) parameter:: + + def func(a: int, /, b: int) -> None: + ... + + func(1, 2) # OK + func(1, b=2) # OK + func(a=1, b=2) # Error + +Support for the ``/`` delimiter was introduced in Python 3.8 (:pep:`570`). +For compatibility with earlier versions of Python, the type system also +supports specifying positional-only parameters using a :ref:`double leading +underscore `. + +Default argument values +----------------------- + +In certain cases, it may be desirable to omit the default argument value for +a parameter. Examples include function definitions in stub files or methods +within a protocol or abstract base class. In such cases, the default value +may be given as an ellipsis. For example:: + + def func(x: AnyStr, y: AnyStr = ...) -> AnyStr: ... + +If a non-ellipsis default value is present and its type can be statically +evaluated, a type checker should verify that this type is :term:`assignable` to +the declared parameter's type:: + + def func(x: int = 0): ... # OK + def func(x: int | None = None): ... # OK + def func(x: int = 0.0): ... # Error + def func(x: int = None): ... # Error + +.. _`annotating-args-kwargs`: + +Annotating ``*args`` and ``**kwargs`` +------------------------------------- + +At runtime, the type of a variadic positional parameter (``*args``) is a +``tuple``, and the type of a variadic keyword parameter (``**kwargs``) is a +``dict``. However, when annotating these parameters, the type annotation +refers to the type of items within the ``tuple`` or ``dict`` (unless +``Unpack`` is used). + +Therefore, the definition:: + + def func(*args: str, **kwargs: int): ... + +means that the function accepts an arbitrary number of positional arguments +of type ``str`` and an arbitrary number of keyword arguments of type ``int``. +For example, all of the following represent function calls with valid +arguments:: + + func('a', 'b', 'c') + func(x=1, y=2) + func('', z=0) + +In the body of function ``func``, the type of parameter ``args`` is +``tuple[str, ...]``, and the type of parameter ``kwargs`` is ``dict[str, int]``. + +.. _unpack-kwargs: + +``Unpack`` for keyword arguments +-------------------------------- + +``typing.Unpack`` has two use cases in the type system: + +* As introduced by :pep:`646`, a backward-compatible form for certain operations + involving variadic generics. See the section on ``TypeVarTuple`` for details. +* As introduced by :pep:`692`, a way to annotate the ``**kwargs`` of a function. + +This second usage is described in this section. The following example:: + + from typing import TypedDict, Unpack + + class Movie(TypedDict): + name: str + year: int + + def foo(**kwargs: Unpack[Movie]) -> None: ... + +means that the ``**kwargs`` comprise two keyword arguments specified by +``Movie`` (i.e. a ``name`` keyword of type ``str`` and a ``year`` keyword of +type ``int``). This indicates that the function should be called as follows:: + + kwargs: Movie = {"name": "Life of Brian", "year": 1979} + + foo(**kwargs) # OK! + foo(name="The Meaning of Life", year=1983) # OK! + +When ``Unpack`` is used, type checkers treat ``kwargs`` inside the +function body as a ``TypedDict``:: + + def foo(**kwargs: Unpack[Movie]) -> None: + assert_type(kwargs, Movie) # OK! + + +Function calls with standard dictionaries +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Passing a dictionary of type ``dict[str, object]`` as a ``**kwargs`` argument +to a function that has ``**kwargs`` annotated with ``Unpack`` must generate a +type checker error. On the other hand, the behavior for functions using +standard, untyped dictionaries can depend on the type checker. For example:: + + def func(**kwargs: Unpack[Movie]) -> None: ... + + movie: dict[str, object] = {"name": "Life of Brian", "year": 1979} + func(**movie) # WRONG! Movie is of type dict[str, object] + + typed_movie: Movie = {"name": "The Meaning of Life", "year": 1983} + func(**typed_movie) # OK! + + another_movie = {"name": "Life of Brian", "year": 1979} + func(**another_movie) # Depends on the type checker. + +Keyword collisions +^^^^^^^^^^^^^^^^^^ + +A ``TypedDict`` that is used to type ``**kwargs`` could potentially contain +keys that are already defined in the function's signature. If the duplicate +name is a standard parameter, an error should be reported by type checkers. +If the duplicate name is a positional-only parameter, no errors should be +generated. For example:: + + def foo(name, **kwargs: Unpack[Movie]) -> None: ... # WRONG! "name" will + # always bind to the + # first parameter. + + def foo(name, /, **kwargs: Unpack[Movie]) -> None: ... # OK! "name" is a + # positional-only parameter, + # so **kwargs can contain + # a "name" keyword. + +Required and non-required items +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Items in a TypedDict may be either :term:`required` or :term:`non-required`. +When using a ``TypedDict`` to type ``**kwargs`` all of the required and +non-required keys should correspond to required and non-required function +keyword parameters. Therefore, if a required key is not provided by the +caller, then an error must be reported by type checkers. + +Read-only items +^^^^^^^^^^^^^^^ + +TypedDict items may also be :term:`read-only`. Marking one or more of the items of a TypedDict +used to type ``**kwargs`` as read-only will have no effect on the type signature of the method. +However, it *will* prevent the item from being modified in the body of the function:: + + class Args(TypedDict): + key1: int + key2: str + + class ReadOnlyArgs(TypedDict): + key1: ReadOnly[int] + key2: ReadOnly[str] + + class Function(Protocol): + def __call__(self, **kwargs: Unpack[Args]) -> None: ... + + def impl(**kwargs: Unpack[ReadOnlyArgs]) -> None: + kwargs["key1"] = 3 # Type check error: key1 is readonly + + fn: Function = impl # Accepted by type checker: function signatures are identical + +Extra items +^^^^^^^^^^^ + +If the TypedDict used for annotating ``**kwargs`` is defined to allow +:term:`extra items`, arbitrary additional keyword arguments of the right +type may be passed to the function:: + + class MovieNoExtra(TypedDict): + name: str + + class MovieExtra(TypedDict, extra_items=int): + name: str + + def f(**kwargs: Unpack[MovieNoExtra]) -> None: ... + def g(**kwargs: Unpack[MovieExtra]) -> None: ... + + # Should be equivalent to: + def f(*, name: str) -> None: ... + def g(*, name: str, **kwargs: int) -> None: ... + + f(name="No Country for Old Men", year=2007) # Not OK. Unrecognized item + g(name="No Country for Old Men", year=2007) # OK + +Assignment +^^^^^^^^^^ + +Assignments of a function typed with ``**kwargs: Unpack[Movie]`` and another +callable type should pass type checking only for the scenarios described below. + +Source and destination contain ``**kwargs`` +""""""""""""""""""""""""""""""""""""""""""" + +Both destination and source functions have a ``**kwargs: Unpack[TypedDict]`` +parameter and the destination function's ``TypedDict`` is :term:`assignable` to +the source function's ``TypedDict`` and the rest of the parameters are +assignable:: + + class Animal(TypedDict): + name: str + + class Dog(Animal): + breed: str + + def accept_animal(**kwargs: Unpack[Animal]): ... + def accept_dog(**kwargs: Unpack[Dog]): ... + + accept_dog = accept_animal # OK! Expression of type Dog can be + # assigned to a variable of type Animal. + + accept_animal = accept_dog # WRONG! Expression of type Animal + # cannot be assigned to a variable of type Dog. + +.. _PEP 692 assignment dest no kwargs: + +Source contains ``**kwargs`` and destination doesn't +"""""""""""""""""""""""""""""""""""""""""""""""""""" + +The destination callable doesn't contain ``**kwargs``, the source callable +contains ``**kwargs: Unpack[TypedDict]`` and the destination function's keyword +arguments are :term:`assignable` to the corresponding keys in source function's +``TypedDict``. Moreover, not required keys should correspond to optional +function arguments, whereas required keys should correspond to required +function arguments. Again, the rest of the parameters have to be assignable. +Continuing the previous example:: + + class Example(TypedDict): + animal: Animal + string: str + number: NotRequired[int] + + def src(**kwargs: Unpack[Example]): ... + def dest(*, animal: Dog, string: str, number: int = ...): ... + + dest = src # OK! + +It is worth pointing out that the destination function's parameters that are to +be assignable to the keys and values from the ``TypedDict`` must be keyword +only:: + + def dest(dog: Dog, string: str, number: int = ...): ... + + dog: Dog = {"name": "Daisy", "breed": "labrador"} + + dest(dog, "some string") # OK! + + dest = src # Type checker error! + dest(dog, "some string") # The same call fails at + # runtime now because 'src' expects + # keyword arguments. + +The reverse situation where the destination callable contains +``**kwargs: Unpack[TypedDict]`` and the source callable doesn't contain +``**kwargs`` should be disallowed. This is because, we cannot be sure that +additional keyword arguments are not being passed in when an instance of a +subclass had been assigned to a variable with a base class type and then +unpacked in the destination callable invocation:: + + def dest(**kwargs: Unpack[Animal]): ... + def src(name: str): ... + + dog: Dog = {"name": "Daisy", "breed": "Labrador"} + animal: Animal = dog + + dest = src # WRONG! + dest(**animal) # Fails at runtime. + +A similar situation can happen even without inheritance as :term:`assignability +` between ``TypedDict``\s is :term:`structural`. + +Source contains untyped ``**kwargs`` +"""""""""""""""""""""""""""""""""""" + +The destination callable contains ``**kwargs: Unpack[TypedDict]`` and the +source callable contains untyped ``**kwargs``:: + + def src(**kwargs): ... + def dest(**kwargs: Unpack[Movie]): ... + + dest = src # OK! + +Source contains traditionally typed ``**kwargs: T`` +""""""""""""""""""""""""""""""""""""""""""""""""""" + +The destination callable contains ``**kwargs: Unpack[TypedDict]``, the source +callable contains traditionally typed ``**kwargs: T`` and each of the +destination function ``TypedDict``'s fields is :term:`assignable` to a variable +of type ``T``:: + + class Vehicle: + ... + + class Car(Vehicle): + ... + + class Motorcycle(Vehicle): + ... + + class Vehicles(TypedDict): + car: Car + moto: Motorcycle + + def dest(**kwargs: Unpack[Vehicles]): ... + def src(**kwargs: Vehicle): ... + + dest = src # OK! + +On the other hand, if the destination callable contains either untyped or +traditionally typed ``**kwargs: T`` and the source callable is typed using +``**kwargs: Unpack[TypedDict]`` then an error should be generated, because +traditionally typed ``**kwargs`` aren't checked for keyword names. + +To summarize, function parameters should behave contravariantly and function +return types should behave covariantly. + +Passing kwargs inside a function to another function +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:ref:`A previous point ` +mentions the problem of possibly passing additional keyword arguments by +assigning a subclass instance to a variable that has a base class type. Let's +consider the following example:: + + class Animal(TypedDict): + name: str + + class Dog(Animal): + breed: str + + def takes_name(name: str): ... + + dog: Dog = {"name": "Daisy", "breed": "Labrador"} + animal: Animal = dog + + def foo(**kwargs: Unpack[Animal]): + print(kwargs["name"].capitalize()) + + def bar(**kwargs: Unpack[Animal]): + takes_name(**kwargs) + + def baz(animal: Animal): + takes_name(**animal) + + def spam(**kwargs: Unpack[Animal]): + baz(kwargs) + + foo(**animal) # OK! foo only expects and uses keywords of 'Animal'. + + bar(**animal) # WRONG! This will fail at runtime because 'breed' keyword + # will be passed to 'takes_name' as well. + + spam(**animal) # WRONG! Again, 'breed' keyword will be eventually passed + # to 'takes_name'. + +In the example above, the call to ``foo`` will not cause any issues at +runtime. Even though ``foo`` expects ``kwargs`` of type ``Animal`` it doesn't +matter if it receives additional arguments because it only reads and uses what +it needs completely ignoring any additional values. + +The calls to ``bar`` and ``spam`` will fail because an unexpected keyword +argument will be passed to the ``takes_name`` function. + +Therefore, ``kwargs`` hinted with an unpacked ``TypedDict`` can only be passed +to another function if the function to which unpacked kwargs are being passed +to has ``**kwargs`` in its signature as well, because then additional keywords +would not cause errors at runtime during function invocation. Otherwise, the +type checker should generate an error. + +In cases similar to the ``bar`` function above the problem could be worked +around by explicitly dereferencing desired fields and using them as arguments +to perform the function call:: + + def bar(**kwargs: Unpack[Animal]): + name = kwargs["name"] + takes_name(name) + +Using ``Unpack`` with types other than ``TypedDict`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``TypedDict`` is the only permitted heterogeneous type for typing ``**kwargs``. +Therefore, in the context of typing ``**kwargs``, using ``Unpack`` with types +other than ``TypedDict`` should not be allowed and type checkers should +generate errors in such cases. + +.. _`callable`: + +Callable +-------- + +The ``Callable`` special form can be used to specify the signature of +a function within a type expression. The syntax is +``Callable[[Param1Type, Param2Type], ReturnType]``. For example:: + + from collections.abc import Callable + + def func(cb: Callable[[int], str]) -> None: + ... + + x: Callable[[], str] + +Parameters specified using ``Callable`` are assumed to be positional-only. +The ``Callable`` form provides no way to specify keyword-only parameters, +or default argument values. For these use cases, see the section on +`Callback protocols`_. + +Meaning of ``...`` in ``Callable`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``Callable`` special form supports the use of ``...`` in place of the list +of parameter types. This is a :term:`gradual form` indicating that the type is +:term:`consistent` with any input signature:: + + cb1: Callable[..., str] + cb1 = lambda x: str(x) # OK + cb1 = lambda : "" # OK + + cb2: Callable[[], str] = cb1 # OK + +A ``...`` can also be used with ``Concatenate``. In this case, the parameters +prior to the ``...`` are required to be present in the input signature and +be assignable, but any additional parameters are permitted:: + + cb3: Callable[Concatenate[int, ...], str] + cb3 = lambda x: str(x) # OK + cb3 = lambda a, b, c: str(a) # OK + cb3 = lambda : "" # Error + cb3 = lambda *, a: str(a) # Error + + +If the input signature in a function definition includes both a ``*args`` and +``**kwargs`` parameter and both are typed as ``Any`` (explicitly or implicitly +because it has no annotation), a type checker should treat this as the +equivalent of ``...``. Any other parameters in the signature are unaffected +and are retained as part of the signature:: + + class Proto1(Protocol): + def __call__(self, *args: Any, **kwargs: Any) -> None: ... + + class Proto2(Protocol): + def __call__(self, a: int, /, *args, **kwargs) -> None: ... + + class Proto3(Protocol): + def __call__(self, a: int, *args: Any, **kwargs: Any) -> None: ... + + class Proto4[**P](Protocol): + def __call__(self, a: int, *args: P.args, **kwargs: P.kwargs) -> None: ... + + def func(p1: Proto1, p2: Proto2, p3: Proto3): + assert_type(p1, Callable[..., None]) # OK + assert_type(p2, Callable[Concatenate[int, ...], None]) # OK + assert_type(p3, Callable[..., None]) # Error + assert_type(p3, Proto4[...]) # OK + + class A: + def method(self, a: int, /, *args: Any, k: str, **kwargs: Any) -> None: + pass + + class B(A): + # This override is OK because it is assignable to the parent's method. + def method(self, a: float, /, b: int, *, k: str, m: str) -> None: + pass + + +The ``...`` syntax can also be used to provide a :ref:`specialized value for a +ParamSpec ` in a generic class or type alias. +For example:: + + type Callback[**P] = Callable[P, str] + + def func(cb: Callable[[], str]) -> None: + f: Callback[...] = cb # OK + +If ``...`` is used with signature concatenation, the ``...`` portion continues +to be :term:`consistent` with any input parameters:: + + type CallbackWithInt[**P] = Callable[Concatenate[int, P], str] + type CallbackWithStr[**P] = Callable[Concatenate[str, P], str] + + def func(cb: Callable[[int, str], str]) -> None: + f1: Callable[Concatenate[int, ...], str] = cb # OK + f2: Callable[Concatenate[str, ...], str] = cb # Error + f3: CallbackWithInt[...] = cb # OK + f4: CallbackWithStr[...] = cb # Error + + +Variadic positional parameter types can be defined with an unpacked tuple +of the form ``*tuple[int, ...]`` in the parameter list. The first argument +specifies the type of the variadic parameters. +For example, the following defines a callable that accepts any number of integer +arguments:: + + type VarCallback = Callable[[*tuple[int, ...]], None] + +.. _`callback-protocols`: + +Callback protocols +------------------ + +Protocols can be used to define flexible callback types that are impossible to +express using the ``Callable`` special form as specified :ref:`above `. +This includes keyword parameters, variadic parameters, default argument values, +and overloads. They can be defined as protocols with a ``__call__`` member:: + + from typing import Protocol + + class Combiner(Protocol): + def __call__(self, *args: bytes, max_len: int | None = None) -> list[bytes]: ... + + def good_cb(*args: bytes, max_len: int | None = None) -> list[bytes]: + ... + def bad_cb(*args: bytes, max_items: int | None) -> list[bytes]: + ... + + comb: Combiner = good_cb # OK + comb = bad_cb # Error! Argument 2 is not assignable because of + # different parameter name and kind in the callback + +Callback protocols and ``Callable[...]`` types can generally be used +interchangeably. + + +Assignability rules for callables +--------------------------------- + +A callable type ``B`` is :term:`assignable` to a callable type ``A`` if the +return type of ``B`` is assignable to the return type of ``A`` and the input +signature of ``B`` accepts all possible combinations of arguments that the +input signature of ``A`` accepts. All of the specific assignability rules +described below derive from this general rule. + + +Parameter types +^^^^^^^^^^^^^^^ + +Callable types are covariant with respect to their return types but +contravariant with respect to their parameter types. This means a callable +``B`` is :term:`assignable` to callable ``A`` if the types of the parameters of +``A`` are assignable to the parameters of ``B``. For example, ``(x: float) -> +int`` is assignable to ``(x: int) -> float``:: + + def func(cb: Callable[[float], int]): + f1: Callable[[int], float] = cb # OK + + +Parameter kinds +^^^^^^^^^^^^^^^ + +Callable ``B`` is :term:`assignable` to callable ``A`` only if all keyword-only +parameters in ``A`` are present in ``B`` as either keyword-only parameters or +standard (positional or keyword) parameters. For example, ``(a: int) -> None`` +is assignable to ``(*, a: int) -> None``, but the converse is not true. The +order of keyword-only parameters is ignored for purposes of assignability:: + + class KwOnly(Protocol): + def __call__(self, *, b: int, a: int) -> None: ... + + class Standard(Protocol): + def __call__(self, a: int, b: int) -> None: ... + + def func(standard: Standard, kw_only: KwOnly): + f1: KwOnly = standard # OK + f2: Standard = kw_only # Error + +Likewise, callable ``B`` is assignable to callable ``A`` only if all +positional-only parameters in ``A`` are present in ``B`` as either +positional-only parameters or standard (positional or keyword) parameters. The +names of positional-only parameters are ignored for purposes of assignability:: + + class PosOnly(Protocol): + def __call__(self, not_a: int, /) -> None: ... + + class Standard(Protocol): + def __call__(self, a: int) -> None: ... + + def func(standard: Standard, pos_only: PosOnly): + f1: PosOnly = standard # OK + f2: Standard = pos_only # Error + + +``*args`` parameters +^^^^^^^^^^^^^^^^^^^^ + +If a callable ``A`` has a signature with a ``*args`` parameter, callable ``B`` +must also have a ``*args`` parameter to be :term:`assignable` to ``A``, and the +type of ``A``'s ``*args`` parameter must be assignable to ``B``'s ``*args`` +parameter:: + + class NoArgs(Protocol): + def __call__(self) -> None: ... + + class IntArgs(Protocol): + def __call__(self, *args: int) -> None: ... + + class FloatArgs(Protocol): + def __call__(self, *args: float) -> None: ... + + def func(no_args: NoArgs, int_args: IntArgs, float_args: FloatArgs): + f1: NoArgs = int_args # OK + f2: NoArgs = float_args # OK + + f3: IntArgs = no_args # Error: missing *args parameter + f4: IntArgs = float_args # OK + + f5: FloatArgs = no_args # Error: missing *args parameter + f6: FloatArgs = int_args # Error: float is not assignable to int + +If a callable ``A`` has a signature with one or more positional-only +parameters, a callable ``B`` is assignable to ``A`` only if ``B`` has an +``*args`` parameter whose type is assignable from the types of any +otherwise-unmatched positional-only parameters in ``A``:: + + class PosOnly(Protocol): + def __call__(self, a: int, b: str, /) -> None: ... + + class IntArgs(Protocol): + def __call__(self, *args: int) -> None: ... + + class IntStrArgs(Protocol): + def __call__(self, *args: int | str) -> None: ... + + class StrArgs(Protocol): + def __call__(self, a: int, /, *args: str) -> None: ... + + class Standard(Protocol): + def __call__(self, a: int, b: str) -> None: ... + + def func(int_args: IntArgs, int_str_args: IntStrArgs, str_args: StrArgs): + f1: PosOnly = int_args # Error: str is not assignable to int + f2: PosOnly = int_str_args # OK + f3: PosOnly = str_args # OK + f4: IntStrArgs = str_args # Error: int | str is not assignable to str + f5: IntStrArgs = int_args # Error: int | str is not assignable to int + f6: StrArgs = int_str_args # OK + f7: StrArgs = int_args # Error: str is not assignable to int + f8: IntArgs = int_str_args # OK + f9: IntArgs = str_args # Error: int is not assignable to str + f10: Standard = int_str_args # Error: keyword parameters a and b missing + f11: Standard = str_args # Error: keyword parameter b missing + + +``**kwargs`` parameters +^^^^^^^^^^^^^^^^^^^^^^^ + +If a callable ``A`` has a signature with a ``**kwargs`` parameter (without an +unpacked ``TypedDict`` type annotation), callable ``B`` must also have a +``**kwargs`` parameter to be :term:`assignable` to ``A``, and the type of +``A``'s ``**kwargs`` parameter must be assignable to ``B``'s ``**kwargs`` +parameter:: + + class NoKwargs(Protocol): + def __call__(self) -> None: ... + + class IntKwargs(Protocol): + def __call__(self, **kwargs: int) -> None: ... + + class FloatKwargs(Protocol): + def __call__(self, **kwargs: float) -> None: ... + + def func(no_kwargs: NoKwargs, int_kwargs: IntKwargs, float_kwargs: FloatKwargs): + f1: NoKwargs = int_kwargs # OK + f2: NoKwargs = float_kwargs # OK + + f3: IntKwargs = no_kwargs # Error: missing **kwargs parameter + f4: IntKwargs = float_kwargs # OK + + f5: FloatKwargs = no_kwargs # Error: missing **kwargs parameter + f6: FloatKwargs = int_kwargs # Error: float is not assignable to int + +If a callable ``A`` has a signature with one or more keyword-only parameters, +a callable ``B`` is assignable to ``A`` if ``B`` has a ``**kwargs`` parameter +whose type is assignable from the types of any otherwise-unmatched keyword-only +parameters in ``A``:: + + class KwOnly(Protocol): + def __call__(self, *, a: int, b: str) -> None: ... + + class IntKwargs(Protocol): + def __call__(self, **kwargs: int) -> None: ... + + class IntStrKwargs(Protocol): + def __call__(self, **kwargs: int | str) -> None: ... + + class StrKwargs(Protocol): + def __call__(self, *, a: int, **kwargs: str) -> None: ... + + class Standard(Protocol): + def __call__(self, a: int, b: str) -> None: ... + + def func(int_kwargs: IntKwargs, int_str_kwargs: IntStrKwargs, str_kwargs: StrKwargs): + f1: KwOnly = int_kwargs # Error: str is not assignable to int + f2: KwOnly = int_str_kwargs # OK + f3: KwOnly = str_kwargs # OK + f4: IntStrKwargs = str_kwargs # Error: int | str is not assignable to str + f5: IntStrKwargs = int_kwargs # Error: int | str is not assignable to int + f6: StrKwargs = int_str_kwargs # OK + f7: StrKwargs = int_kwargs # Error: str is not assignable to int + f8: IntKwargs = int_str_kwargs # OK + f9: IntKwargs = str_kwargs # Error: int is not assignable to str + f10: Standard = int_str_kwargs # Error: Does not accept positional arguments + f11: Standard = str_kwargs # Error: Does not accept positional arguments + +Assignability rules for callable signatures that contain a ``**kwargs`` with an +unpacked ``TypedDict`` are described in the section :ref:`above +`. + + +Signatures with ParamSpecs +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A signature that includes ``*args: P.args, **kwargs: P.kwargs`` is equivalent +to a ``Callable`` parameterized by ``P``:: + + class ProtocolWithP[**P](Protocol): + def __call__(self, *args: P.args, **kwargs: P.kwargs) -> None: ... + + type TypeAliasWithP[**P] = Callable[P, None] + + def func[**P](proto: ProtocolWithP[P], ta: TypeAliasWithP[P]): + # These two types are equivalent + f1: TypeAliasWithP[P] = proto # OK + f2: ProtocolWithP[P] = ta # OK + + +Default argument values +^^^^^^^^^^^^^^^^^^^^^^^ + +If a callable ``C`` has a parameter ``x`` with a default argument value and +``A`` is the same as ``C`` except that ``x`` has no default argument, then +``C`` is :term:`assignable` to ``A``. ``C`` is also assignable to ``A`` if +``A`` is the same as ``C`` with parameter ``x`` removed:: + + class DefaultArg(Protocol): + def __call__(self, x: int = 0) -> None: ... + + class NoDefaultArg(Protocol): + def __call__(self, x: int) -> None: ... + + class NoX(Protocol): + def __call__(self) -> None: ... + + def func(default_arg: DefaultArg): + f1: NoDefaultArg = default_arg # OK + f2: NoX = default_arg # OK + + +Overloads +^^^^^^^^^ + +If a callable ``B`` is overloaded with two or more signatures, it is +:term:`assignable` to callable ``A`` if *at least one* of the overloaded +signatures in ``B`` is assignable to ``A``:: + + class Overloaded(Protocol): + @overload + def __call__(self, x: int) -> int: ... + @overload + def __call__(self, x: str) -> str: ... + + class IntArg(Protocol): + def __call__(self, x: int) -> int: ... + + class StrArg(Protocol): + def __call__(self, x: str) -> str: ... + + class FloatArg(Protocol): + def __call__(self, x: float) -> float: ... + + def func(overloaded: Overloaded): + f1: IntArg = overloaded # OK + f2: StrArg = overloaded # OK + f3: FloatArg = overloaded # Error + +If a callable ``A`` is overloaded with two or more signatures, callable ``B`` +is assignable to ``A`` if ``B`` is assignable to *all* of the signatures in +``A``:: + + class Overloaded(Protocol): + @overload + def __call__(self, x: int, y: str) -> float: ... + @overload + def __call__(self, x: str) -> complex: ... + + class StrArg(Protocol): + def __call__(self, x: str) -> complex: ... + + class IntStrArg(Protocol): + def __call__(self, x: int | str, y: str = "") -> int: ... + + def func(int_str_arg: IntStrArg, str_arg: StrArg): + f1: Overloaded = int_str_arg # OK + f2: Overloaded = str_arg # Error diff --git a/docs/spec/class-compat.rst b/docs/spec/class-compat.rst new file mode 100644 index 000000000..fe7df968e --- /dev/null +++ b/docs/spec/class-compat.rst @@ -0,0 +1,157 @@ +.. _`class-compat`: + +Class type assignability +======================== + +.. _`classvar`: + +``ClassVar`` +------------ + +(Originally specified in :pep:`526`.) + +The :py:data:`typing.ClassVar` :term:`type qualifier` is used to annotate +class variables that should not be set on class instances. This restriction +is enforced by static checkers, but not at runtime. + +:py:data:`~typing.ClassVar` may be used in one of several forms: + +* With an explicit type, using the syntax ``ClassVar[]``. Example:: + + class C: + x: ClassVar[float] = 1 + +* With no type annotation. Example:: + + class C: + y: ClassVar = 2 + z: ClassVar + + If an assigned value is available (e.g. with ``y``), the type should be + inferred as some type to which this value is :term:`assignable` (for example, + ``int``, ``Literal[2]``, or ``Any``). + + If the bare ``ClassVar`` qualifier is used without any assigned value, the type + should be inferred as :ref:`Any `. Type checkers may error if no assigned + value is present. + +Type annotations can be used to annotate class and instance variables +in class bodies and methods. In particular, the value-less notation ``a: int`` +allows one to annotate instance variables that should be initialized +in ``__init__`` or ``__new__``. The syntax is as follows:: + + class BasicStarship: + captain: str = 'Picard' # instance variable with default + damage: int # instance variable without default + stats: ClassVar[dict[str, int]] = {} # class variable + +Here ``ClassVar`` is a :term:`special form` defined by the :py:mod:`typing` module that +indicates to the static type checker that this variable should not be +set on instances. + +Note that a ``ClassVar`` parameter cannot include any type variables, regardless +of the level of nesting: ``ClassVar[T]`` and ``ClassVar[list[set[T]]]`` are +both invalid if ``T`` is a type variable. + +This could be illustrated with a more detailed example. In this class:: + + class Starship: + captain = 'Picard' + stats = {} + + def __init__(self, damage, captain=None): + self.damage = damage + if captain: + self.captain = captain # Else keep the default + + def hit(self): + Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1 + +``stats`` is intended to be a class variable (keeping track of many different +per-game statistics), while ``captain`` is an instance variable with a default +value set in the class. This difference might not be seen by a type +checker: both get initialized in the class, but ``captain`` serves only +as a convenient default value for the instance variable, while ``stats`` +is truly a class variable -- it is intended to be shared by all instances. + +Since both variables happen to be initialized at the class level, it is +useful to distinguish them by marking class variables as annotated with +types wrapped in ``ClassVar[...]``. In this way a type checker may flag +accidental assignments to attributes with the same name on instances. + +For example, annotating the discussed class:: + + class Starship: + captain: str = 'Picard' + damage: int + stats: ClassVar[dict[str, int]] = {} + + def __init__(self, damage: int, captain: str = None): + self.damage = damage + if captain: + self.captain = captain # Else keep the default + + def hit(self): + Starship.stats['hits'] = Starship.stats.get('hits', 0) + 1 + + enterprise_d = Starship(3000) + enterprise_d.stats = {} # Flagged as error by a type checker + Starship.stats = {} # This is OK + +As a matter of convenience (and convention), instance variables can be +annotated in ``__init__`` or other methods, rather than in the class:: + + class Box[T]: + def __init__(self, content: T): + self.content: T = content + +``ClassVar`` cannot be used as a qualifier for a :ref:`TypedDict ` +item or a :ref:`NamedTuple ` field. Such usage also generates +an error at runtime. + +.. _`override`: + +``@override`` +------------- + +(Originally specified by :pep:`698`.) + +When type checkers encounter a method decorated with ``@typing.override`` they +should treat it as a type error unless that method is overriding a method or +attribute in some ancestor class, and the type of the overriding method is +:term:`assignable` to the type of the overridden method. + + +.. code-block:: python + + from typing import override + + class Parent: + def foo(self) -> int: + return 1 + + def bar(self, x: str) -> str: + return x + + class Child(Parent): + @override + def foo(self) -> int: + return 2 + + @override + def baz(self) -> int: # Type check error: no matching signature in ancestor + return 1 + + +The ``@override`` decorator should be permitted anywhere a type checker +considers a method to be a valid override, which typically includes not only +normal methods but also ``@property``, ``@staticmethod``, and ``@classmethod``. + + +Strict Enforcement Per-Project +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +We believe that ``@override`` is most useful if checkers also allow developers +to opt into a strict mode where methods that override a parent class are +required to use the decorator. Strict enforcement should be opt-in for backward +compatibility. diff --git a/docs/spec/concepts.rst b/docs/spec/concepts.rst new file mode 100644 index 000000000..81fe481fe --- /dev/null +++ b/docs/spec/concepts.rst @@ -0,0 +1,468 @@ +.. _`type-system-concepts`: + +Type system concepts +==================== + +Static, dynamic, and gradual typing +----------------------------------- + +A **statically typed** programming language runs a type checker before running +a program. The program is required to be well typed according to the language's +type system. The type system assigns a type to all expressions in the language +and verifies that their uses obey the typing rules. Normally, a program that is +not well typed (i.e., one that contains a type error) will not run. Java and +C++ are examples of statically typed object-oriented languages. + +A **dynamically typed** programming language does not run a type checker before +running a program. Instead, it checks the types of values before performing +operations on them at runtime. This is not to say that the language is +"untyped". Values at runtime have a type and their uses obey typing rules. Not +every operation will be checked, but certain primitive operations in the +language such as attribute access or arithmetic are. Python is a +dynamically typed language. + +**Gradual typing** is a way to combine static and dynamic typing. +Type-annotated Python allows opting in to static type checking at a fine level +of granularity, so that some type errors can be caught statically, without +running the program. Variables, parameters, and returns can optionally be given +static type annotations. Even within the type of a single data structure, +static type checking is optional and granular. For example, a dictionary can be +annotated to enable static checking of the key type but only have dynamic +runtime checking of the value type. + +A **gradual** type system is one in which a special "unknown" or "dynamic" type +is used to describe names or expressions whose types are not known statically. +In Python, this type is spelled :ref:`Any`. Because :ref:`!Any` indicates a +statically unknown type, the static type checker can't check type correctness +of operations on expressions typed as :ref:`!Any`. These operations are still +dynamically checked, via the Python runtime's usual dynamic checking. + +The Python type system also uses ``...`` within :ref:`Callable` types and +within ``tuple[Any, ...]`` (see :ref:`tuples`) to indicate a statically unknown +component of a type. The detailed rules for these usages are discussed in their +respective sections of the specification. Collectively, along with :ref:`Any`, +these are :term:`gradual forms `. + +This specification describes a gradual type system for Python. + +Fully static and gradual types +------------------------------ + +We will refer to types that do not contain a :term:`gradual form` as a sub-part +as **fully static types**. + +A **gradual type** can be a fully static type, :ref:`Any` itself, or a type +that contains a gradual form as a sub-part. All Python types are gradual types; +fully static types are a subset. + +Fully static types +~~~~~~~~~~~~~~~~~~ + +A fully static type denotes a set of potential runtime values. For instance, +the fully static type ``object`` is the set of all Python objects. The fully +static type ``bool`` is the set of values ``{ True, False }``. The fully static +type ``str`` is the set of all Python strings; more precisely, the set of all +Python objects whose runtime type (``__class__`` attribute) is either ``str`` +or a class that inherits directly or indirectly from ``str``. A :ref:`Protocol +` denotes the set of all objects which share a certain set of +attributes and/or methods. + +If an object ``v`` is a member of the set of objects denoted by a fully static +type ``T``, we can say that ``v`` is a "member of" the type ``T``, or ``v`` +":term:`inhabits `" ``T``. + +Gradual types +~~~~~~~~~~~~~ + +:ref:`Any` represents an unknown static type. It denotes some unknown set of +runtime values. + +This may appear similar to the fully static type ``object``, which represents +the set of all Python objects, but it is quite different. + +If an expression has the type ``object``, a static type checker should ensure +that operations on the expression are valid for all Python objects, or else +emit a static type error. This allows very few operations! For example, if +``x`` is typed as ``object``, ``x.foo`` should be a static type error because +not all Python objects have an attribute ``foo``. + +An expression typed as :ref:`Any`, on the other hand, should be assumed to have +_some_ specific static type, but _which_ static type is not known. A static +type checker should not emit static type errors on an expression or statement +if :ref:`!Any` might represent a static type which would avoid the error. (This +is defined more precisely below, in terms of materialization and +assignability.) + +Similarly, a type such as ``tuple[int, Any]`` (see :ref:`tuples`) or ``int | +Any`` (see :ref:`union-types`) does not represent a single set of Python +objects; rather, it represents a (bounded) range of possible sets of values. + +In the same way that :ref:`Any` does not represent "the set of all Python +objects" but rather "an unknown set of objects", ``tuple[int, Any]`` does not +represent "the set of all length-two tuples whose first element is an integer". +That is a fully static type, spelled ``tuple[int, object]``. By contrast, +``tuple[int, Any]`` represents some unknown set of tuple values; it might be +the set of all tuples of two integers, or the set of all tuples of an integer +and a string, or some other set of tuple values. + +In practice, this difference is seen (for example) in the fact that we can +assign an expression of type ``tuple[int, Any]`` to a target typed as +``tuple[int, int]``, whereas assigning ``tuple[int, object]`` to ``tuple[int, +int]`` is a static type error. (Again, we formalize this distinction in the +below definitions of materialization and assignability.) + +In the same way that the fully static type ``object`` is the upper bound for +the possible sets of values represented by :ref:`Any`, the fully static type +``tuple[int, object]`` is the upper bound for the possible sets of values +represented by ``tuple[int, Any]``. + +The gradual guarantee +~~~~~~~~~~~~~~~~~~~~~ + +:ref:`Any` allows gradually adding static types to a dynamically typed program. +In a fully dynamically typed program, a static checker assigns the type +:ref:`!Any` to all expressions, and should emit no errors. Inferring static +types or adding type annotations to the program (making the program more +statically typed) may result in static type errors, if the program is not +correct or if the static types aren't able to fully represent the runtime +types. Removing type annotations (making the program more dynamic) should not +result in additional static type errors. This is often referred to as the +**gradual guarantee**. + +In Python's type system, we don't take the gradual guarantee as a strict +requirement, but it's a useful guideline. + +Subtype, supertype, and type equivalence +---------------------------------------- + +A fully static type ``B`` is a **subtype** of another fully static type ``A`` +if and only if the set of values represented by ``B`` is a subset of the set of +values represented by ``A``. Because the subset relation on sets is transitive +and reflexive, the subtype relation is also transitive (if ``C`` is a subtype +of ``B`` and ``B`` is a subtype of ``A``, then ``C`` is a subtype of ``A``) and +reflexive (``A`` is always a subtype of ``A``). + +The **supertype** relation is the inverse of subtype: ``A`` is a supertype of +``B`` if and only if ``B`` is a subtype of ``A``; or equivalently, if and only +if the set of values represented by ``A`` is a superset of the values +represented by ``B``. The supertype relation is also transitive and reflexive. + +We also define an **equivalence** relation on fully static types: the types +``A`` and ``B`` are equivalent (or "the same type") if and only if ``A`` is a +subtype of ``B`` and ``B`` is a subtype of ``A``. This means that the set of +values represented by ``A`` is both a superset and a subset of the values +represented by ``B``, meaning ``A`` and ``B`` must represent the same set of +values. + +We may describe a type ``B`` as "narrower" than a type ``A`` (or as a "proper +subtype" of ``A``) if ``B`` is a subtype of ``A`` and ``B`` is not equivalent +to ``A``. In the same scenario we can describe the type ``A`` as "wider" than +``B``, or a "proper supertype" of ``B``. + +Nominal and structural types +---------------------------- + +For a type such as ``str`` (or any other class), which describes the set of +values whose ``__class__`` is ``str`` or a direct or indirect subclass of it, +subtyping corresponds directly to subclassing. A subclass ``MyStr`` of ``str`` +is a subtype of ``str``, because ``MyStr`` represents a subset of the values +represented by ``str``. Such types can be called "nominal types" and this is +"nominal subtyping." + +Other types (e.g. :ref:`Protocols` and :ref:`TypedDict`) instead describe a set +of values by the types of their attributes and methods, or the types of their +dictionary keys and values. These are called "structural types". A structural +type may be a subtype of another type without any inheritance or subclassing +relationship, simply because it meets all the requirements of the supertype, +and perhaps adds more, thus representing a subset of the possible values of the +supertype. This is "structural subtyping". + +Although the means of specifying the set of values represented by the types +differs, the fundamental concepts are the same for both nominal and structural +types: a type represents a set of possible values and a subtype represents a +subset of those values. + +Materialization +--------------- + +Since :ref:`Any` represents an unknown static type, it does not represent any +known single set of values (it represents an unknown set of values). Thus it is +not in the domain of the subtype, supertype, or equivalence relations on static +types described above. + +To relate gradual types more generally, we define a **materialization** +relation. Materialization transforms a "more dynamic" type to a "more static" +type. Given a gradual type ``A``, if we replace zero or more occurrences of +``Any`` in ``A`` with some type (which can be different for each occurrence of +``Any``), the resulting gradual type ``B`` is a materialization of ``A``. (We +can also materialize a :ref:`Callable` type by replacing ``...`` with any type +signature, and materialize ``tuple[Any, ...]`` by replacing it with a +determinate-length tuple type.) + +For instance, ``tuple[int, str]`` (a fully static type) and ``tuple[Any, str]`` +(a gradual type) are both materializations of ``tuple[Any, Any]``. ``tuple[int, +str]`` is also a materialization of ``tuple[Any, str]``. + +If ``B`` is a materialization of ``A``, we can say that ``B`` is a "more +static" type than ``A``, and ``A`` is a "more dynamic" type than ``B``. + +The materialization relation is both transitive and reflexive, so it defines a +preorder on gradual types. + +.. _`consistent`: + +Consistency +----------- + +We define a **consistency** relation on gradual types, based on +materialization. + +A fully static type ``A`` is consistent with another fully static type ``B`` if +and only if they are the same type (``A`` is equivalent to ``B``). + +A gradual type ``A`` is consistent with a gradual type ``B``, and ``B`` is +consistent with ``A``, if and only if there exists some fully static type ``C`` +which is a materialization of both ``A`` and ``B``. + +:ref:`Any` is consistent with every type, and every type is consistent with +:ref:`!Any`. (This follows from the definitions of materialization and +consistency but is worth stating explicitly.) + +The consistency relation is not transitive. ``tuple[int, int]`` is consistent +with ``tuple[Any, int]``, and ``tuple[Any, int]`` is consistent with +``tuple[str, int]``, but ``tuple[int, int]`` is not consistent with +``tuple[str, int]``. + +The consistency relation is symmetric. If ``A`` is consistent with ``B``, ``B`` +is also consistent with ``A``. It is also reflexive: ``A`` is always consistent +with ``A``. + +.. _`assignable`: + +The assignable-to (or consistent subtyping) relation +---------------------------------------------------- + +Given the materialization relation and the subtyping relation, we can define +the **consistent subtype** relation over all types. A type ``B`` is a +consistent subtype of a type ``A`` if there exists a materialization ``A'`` of +``A`` and a materialization ``B'`` of ``B``, where ``A'`` and ``B'`` are both +fully static types, and ``B'`` is a subtype of ``A'``. + +Consistent subtyping defines "assignability" for Python. An expression can be +assigned to a variable (including passed as an argument or returned from a +function) if its type is a consistent subtype of the variable's type annotation +(respectively, parameter's type annotation or return type annotation). + +We can say that a type ``B`` is "assignable to" a type ``A`` if ``B`` is a +consistent subtype of ``A``. In this case we can also say that ``A`` is +"assignable from" ``B``. + +In the remainder of this specification, we will usually prefer the term +**assignable to** over "consistent subtype of". The two are synonymous, but +"assignable to" is shorter, and may communicate a clearer intuition to many +readers. + +For example, ``Any`` is :term:`assignable` to ``int``, because ``int`` is a +materialization of ``Any``, and ``int`` is a subtype of ``int``. The same +materialization also shows that ``int`` is assignable to ``Any``. + +The assignable-to relation is not generally symmetric, however. If ``B`` is a +subtype of ``A``, then ``tuple[Any, B]`` is assignable to ``tuple[int, A]``, +because ``tuple[Any, B]`` can materialize to ``tuple[int, B]``, which is a +subtype of ``tuple[int, A]``. But ``tuple[int, A]`` is not assignable to +``tuple[Any, B]``. + +For a gradual structural type, consistency and assignability are also +structural. For example, the structural type "all objects with an attribute +``x`` of type ``Any``" is consistent with (and assignable to) the structural +type "all objects with an attribute ``x`` of type ``int``". + +Summary of type relations +------------------------- + +The subtype, supertype, and equivalence relations establish a partial order on +fully static types. The analogous relations on gradual types (via +materialization) are "assignable-to" (or "consistent subtype"), +"assignable-from" (or "consistent supertype"), and "consistent with". We can +visualize this analogy in the following table: + +.. list-table:: + :header-rows: 1 + + * - Fully static types + - Gradual types + * - ``B`` is a :term:`subtype` of ``A`` + - ``B`` is :term:`assignable` to (or a consistent subtype of) ``A`` + * - ``A`` is a :term:`supertype` of ``B`` + - ``A`` is assignable from (or a consistent supertype of) ``B`` + * - ``B`` is :term:`equivalent` to ``A`` + - ``B`` is :term:`consistent` with ``A`` + +We can also define an **equivalence** relation on gradual types: the gradual +types ``A`` and ``B`` are equivalent (that is, the same gradual type, not +merely consistent with one another) if and only if all materializations of +``A`` are also materializations of ``B``, and all materializations of ``B`` +are also materializations of ``A``. + +Attributes and methods +---------------------- + +In Python, we can do more with objects at runtime than just assign them to +names, pass them to functions, or return them from functions. We can also +get/set attributes and call methods. + +In the Python data model, the operations that can be performed on a value all +desugar to method calls. For example, ``a + b`` is (roughly, eliding some +details) syntactic sugar for either ``type(a).__add__(a, b)`` or +``type(b).__radd__(b, a)``. + +For a static type checker, accessing ``a.foo`` is a type error unless all +possible objects in the set represented by the type of ``a`` have the ``foo`` +attribute. (We consider an implementation of ``__getattr__`` to be a getter for +all attribute names, and similarly for ``__setattr__`` and ``__delattr__``. +There are more `complexities +`_; +a full specification of attribute access belongs in its own chapter.) + +If all objects in the set represented by the fully static type ``A`` have a +``foo`` attribute, we can say that the type ``A`` has the ``foo`` attribute. + +If the type ``A`` of ``a`` in ``a.foo`` is a gradual type, it may not represent +a single set of objects. In this case, ``a.foo`` is a type error if and only if +there does not exist any materialization of ``A`` which has the ``foo`` +attribute. + +Equivalently, ``a.foo`` is a type error unless the type of ``a`` is assignable +to a type that has the ``foo`` attribute. + + +.. _`union-types`: + +Union types +----------- + +Since accepting a small, limited set of expected types for a single +argument is common, the type system supports union types, created with the +``|`` operator. +Example:: + + def handle_employees(e: Employee | Sequence[Employee]) -> None: + if isinstance(e, Employee): + e = [e] + ... + +A fully static union type ``T1 | T2``, where ``T1`` and ``T2`` are fully static +types, represents the set of values formed by the union of the sets of values +represented by ``T1`` and ``T2``, respectively. Thus, by the definition of the +supertype relation, the union ``T1 | T2`` is a supertype of both ``T1`` and +``T2``, and ``T1`` and ``T2`` are both subtypes of ``T1 | T2``. + +A gradual union type ``S1 | S2``, where ``S1`` and ``S2`` are gradual types, +represents all possible sets of values that could be formed by union of the +possible sets of values represented by materializations of ``S1`` and ``S2``, +respectively. + +For any materialization of ``S1`` to ``T1`` and ``S2`` to ``T2``, ``S1 | S2`` +can likewise be materialized to ``T1 | T2``. Thus, the gradual types ``S1`` and +``S2`` are both assignable to the gradual union type ``S1 | S2``. + +If ``B`` is a subtype of ``A``, ``B | A`` is equivalent to ``A``. + +This rule applies only to subtypes, not assignable-to. The union ``T | Any`` is +not reducible to a simpler form. It represents an unknown static type with +lower bound ``T``. That is, it represents an unknown set of objects which may +be as large as ``object``, or as small as ``T``, but no smaller. + +Equivalent gradual types can, however, be simplified from unions; e.g. +``list[Any] | list[Any]`` is equivalent to ``list[Any]``. Similarly, the union +``Any | Any`` can be simplified to ``Any``: the union of two unknown sets of +objects is an unknown set of objects. + +Union with None +~~~~~~~~~~~~~~~ + +One common case of union types are *optional* types, which are unions with +``None``. Example:: + + def handle_employee(e: Employee | None) -> None: ... + +Either the type ``Employee`` or the type of ``None`` are assignable to the +union ``Employee | None``. + +A past version of this specification allowed type checkers to assume an optional +type when the default value is ``None``, as in this code:: + + def handle_employee(e: Employee = None): ... + +This would have been treated as equivalent to:: + + def handle_employee(e: Employee | None = None) -> None: ... + +This is no longer the recommended behavior. Type checkers should move +towards requiring the optional type to be made explicit. + +Support for singleton types in unions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A singleton instance is frequently used to mark some special condition, +in particular in situations where ``None`` is also a valid value +for a variable. Example:: + + _empty = object() + + def func(x=_empty): + if x is _empty: # default argument value + return 0 + elif x is None: # argument was provided and it's None + return 1 + else: + return x * 2 + +To allow precise typing in such situations, the user should use +a union type in conjunction with the ``enum.Enum`` class provided +by the standard library, so that type errors can be caught statically:: + + from enum import Enum + + class Empty(Enum): + token = 0 + _empty = Empty.token + + def func(x: int | None | Empty = _empty) -> int: + + boom = x * 42 # This fails type check + + if x is _empty: + return 0 + elif x is None: + return 1 + else: # At this point typechecker knows that x can only have type int + return x * 2 + +Since the subclasses of ``Enum`` cannot be further subclassed, +the type of variable ``x`` can be statically inferred in all branches +of the above example. The same approach is applicable if more than one +singleton object is needed: one can use an enumeration that has more than +one value:: + + class Reason(Enum): + timeout = 1 + error = 2 + + def process(response: str | Reason = '') -> str: + if response is Reason.timeout: + return 'TIMEOUT' + elif response is Reason.error: + return 'ERROR' + else: + # response can be only str, all other possible values exhausted + return 'PROCESSED: ' + response + +References +---------- + +The concepts presented here are derived from the research literature in gradual +typing. See e.g.: + +* `Giuseppe Castagna, Victor Lanvin, Tommaso Petrucciani, and Jeremy G. Siek. 2019. Gradual Typing: A New Perspective. `_ Proc. ACM Program. Lang. 3, POPL, Article 16 (January 2019), 112 pages +* `Victor Lanvin. A semantic foundation for gradual set-theoretic types. `_ Computer science. Université Paris Cité, 2021. English. NNT : 2021UNIP7159. tel-03853222 diff --git a/docs/spec/constructors.rst b/docs/spec/constructors.rst new file mode 100644 index 000000000..c95ecc488 --- /dev/null +++ b/docs/spec/constructors.rst @@ -0,0 +1,490 @@ +Constructors +============ + +Calls to constructors require special handling within type checkers. + +Constructor Calls +----------------- + +At runtime, a call to a class' constructor typically results in the invocation of +three methods in the following order: + +#. The ``__call__`` method of the metaclass (which is typically supplied by the + ``type`` class but can be overridden by a custom metaclass and which is + responsible for calling the next two methods) +#. The ``__new__`` static method of the class +#. The ``__init__`` instance method of the class + +Type checkers should mirror this runtime behavior when analyzing a constructor +call. + +Metaclass ``__call__`` Method +----------------------------- + +When evaluating a constructor call, a type checker should first check if the +class has a custom metaclass (a subclass of ``type``) that defines a ``__call__`` +method. If so, it should evaluate the call of this method using the supplied +arguments. If the metaclass is ``type``, this step can be skipped. + +If the evaluated return type of the ``__call__`` method indicates something +other than an instance of the class being constructed, a type checker should +assume that the metaclass ``__call__`` method is overriding ``type.__call__`` +in some special manner, and it should not attempt to evaluate the ``__new__`` +or ``__init__`` methods on the class. For example, some metaclass ``__call__`` +methods are annotated to return ``NoReturn`` to indicate that constructor +calls are not supported for that class. + + :: + + class Meta(type): + def __call__(cls, *args, **kwargs) -> NoReturn: + raise TypeError("Cannot instantiate class") + + class MyClass(metaclass=Meta): + def __new__(cls, *args, **kwargs) -> Self: + return super().__new__(cls, *args, **kwargs) + + assert_type(MyClass(), Never) + +If no return type annotation is provided for ``__call__``, a type checker may +assume that it does not override ``type.__call__`` in a special manner and +proceed as though the return type is an instance of the type specified by +the ``cls`` parameter. + + +``__new__`` Method +------------------ + +After the metaclass ``__call__`` method has been evaluated, a type checker +should evaluate the ``__new__`` method of the class (if applicable) using +the supplied arguments. This step should be skipped if the class does not +define a ``__new__`` method and does not inherit a ``__new__`` method from +a base class other than ``object``. + +If the class is generic and explicitly specialized, the type checker should +partially specialize the ``__new__`` method using the supplied type arguments. +If the class is not explicitly specialized, class-scoped type variables should +be solved using the supplied arguments passed to the constructor call. + + :: + + class MyClass[T]: + def __new__(cls, x: T) -> Self: + return super().__new__(cls) + + # Constructor calls for specialized classes + assert_type(MyClass[int](1), MyClass[int]) + assert_type(MyClass[float](1), MyClass[float]) + MyClass[int](1.0) # Type error + + # Constructor calls for non-specialized classes + assert_type(MyClass(1), MyClass[int]) + assert_type(MyClass(1.0), MyClass[float]) + +If any class-scoped type variables are not solved when evaluating the ``__new__`` +method call using the supplied arguments, these type variables should be left +unsolved, allowing the ``__init__`` method (if applicable) to be used to solve +them. + + :: + + class MyClass[T]: + def __new__(cls, *args, **kwargs) -> Self: + return super().__new__(cls) + + def __init__(self, x: T) -> None: + pass + + assert_type(MyClass(1), MyClass[int]) + assert_type(MyClass(""), MyClass[str]) + +For most classes, the return type for the ``__new__`` method is typically +``Self``, but other types are also allowed. For example, the ``__new__`` +method may return an instance of a subclass or an instance of some completely +unrelated class. + +If the evaluated return type of ``__new__`` is not the class being constructed +(or a subclass thereof), a type checker should assume that the ``__init__`` +method will not be called. This is consistent with the runtime behavior of the +``type.__call__`` method. If the ``__new__`` method return type is a union with +one or more members that are not the class being constructed (or a subclass +thereof), a type checker should likewise assume that the ``__init__`` method +will not be called. + + :: + + class MyClass: + def __new__(cls) -> int: + return 0 + + # In this case, the __init__ method should not be considered + # by the type checker when evaluating a constructor call. + def __init__(self, x: int): + pass + + assert_type(MyClass(), int) + +For purposes of this test, an explicit return type of ``Any`` (or a +union containing ``Any``) should be treated as a type that is not an instance +of the class being constructed. + + :: + + class MyClass: + def __new__(cls) -> Any: + return 0 + + # The __init__ method will not be called in this case, so + # it should not be evaluated. + def __init__(self, x: int): + pass + + assert_type(MyClass(), Any) + +If the return type of ``__new__`` is not annotated, a type checker may assume +that the return type is ``Self`` and proceed with the assumption that the +``__init__`` method will be called. + +If the class is generic, it is possible for a ``__new__`` method to override +the specialized class type and return a class instance that is specialized +with different type arguments. + + :: + + class MyClass[T]: + def __new__(cls, *args, **kwargs) -> "MyClass[list[T]]": + ... + + assert_type(MyClass[int](), MyClass[list[int]]) + +If the ``cls`` parameter within the ``__new__`` method is not annotated, type +checkers should infer a type of ``type[Self]``. Regardless of whether the +type of the ``cls`` parameter is explicit or inferred, the type checker should +bind the class being constructed to the ``cls`` parameter and report any type +errors that arise during binding. + + :: + + class MyClass[T]: + def __new__(cls: "type[MyClass[int]]") -> "MyClass[int]": ... + + MyClass() # OK + MyClass[int]() # OK + MyClass[str]() # Type Error + + +``__init__`` Method +------------------- + +After evaluating the ``__new__`` method, a type checker should evaluate the +``__init__`` method (if applicable) using the supplied arguments. If the class +is generic and explicitly specialized (or specialized via the ``__new__`` method +return type), the type checker should partially specialize the ``__init__`` +method using the supplied type arguments. If the class is not explicitly +specialized, class-scoped type variables should be solved using the supplied +arguments passed to the constructor call. + +This step should be skipped if the class does not define an ``__init__`` method +and does not inherit an ``__init__`` method from a base class other than +``object``. + + :: + + class MyClass[T]: + def __init__(self, x: T) -> None: + ... + + # Constructor calls for specialized classes + assert_type(MyClass[int](1), MyClass[int]) + assert_type(MyClass[float](1), MyClass[float]) + MyClass[int](1.0) # Type error + + # Constructor calls for non-specialized classes + assert_type(MyClass(1), MyClass[int]) + assert_type(MyClass(1.0), MyClass[float]) + +If the ``self`` parameter within the ``__init__`` method is not annotated, type +checkers should infer a type of ``Self``. Regardless of whether the ``self`` +parameter type is explicit or inferred, a type checker should bind the class +being constructed to this parameter and report any type errors that arise +during binding. + + :: + + class MyClass[T]: + def __init__(self: "MyClass[int]") -> None: ... + + MyClass() # OK + MyClass[int]() # OK + MyClass[str]() # Type Error + +The return type for ``__init__`` is always ``None``, which means the +method cannot influence the return type of the constructor call by specifying +a return type. There are cases where it is desirable for the ``__init__`` method +to influence the return type, especially when the ``__init__`` method is +overloaded. To enable this, type checkers should allow the ``self`` parameter +to be annotated with a type that influences the resulting type of the +constructor call. + + :: + + class MyClass1[T]: + @overload + def __init__(self: "MyClass1[list[int]]", value: int) -> None: ... + @overload + def __init__(self: "MyClass1[set[str]]", value: str) -> None: ... + @overload + def __init__(self, value: T) -> None: ... + + + assert_type(MyClass1(0), MyClass1[list[int]]) + assert_type(MyClass1[int](3), MyClass1[int]) + assert_type(MyClass1(""), MyClass1[set[str]]) + assert_type(MyClass1(3.0), MyClass1[float]) + + +Function-scoped type variables can also be used in the ``self`` +annotation of an ``__init__`` method to influence the return type of the +constructor call. + + :: + + class MyClass2[T1, T2]: + def __init__[V1, V2](self: "MyClass2[V1, V2]", value1: V1, value2: V2) -> None: ... + + assert_type(MyClass2(0, ""), MyClass2[int, str]) + assert_type(MyClass2[int, str](0, ""), MyClass2[int, str]) + + class MyClass3[T1, T2]: + def __init__[V1, V2](self: "MyClass3[V2, V1]", value1: V1, value2: V2) -> None: ... + + assert_type(MyClass3(0, ""), MyClass3[str, int]) + assert_type(MyClass3[str, int](0, ""), MyClass3[str, int]) + + +Class-scoped type variables should not be used in the ``self`` annotation +because such use can lead to ambiguous or nonsensical type evaluation results. +Type checkers should report an error if a class-scoped type variable is used +within a type annotation for the ``self`` parameter in an ``__init__`` method. + + :: + + class MyClass4[T1, T2]: + # The ``self`` annotation should result in a type error + def __init__(self: "MyClass4[T2, T1]") -> None: ... + + +Classes Without ``__new__`` and ``__init__`` Methods +---------------------------------------------------- + +If a class does not define a ``__new__`` method or ``__init__`` method and +does not inherit either of these methods from a base class other than +``object``, a type checker should evaluate the argument list using the +``__new__`` and ``__init__`` methods from the ``object`` class. + + :: + + class MyClass5: + pass + + MyClass5() # OK + MyClass5(1) # Type error + + +Constructor Calls for type[T] +----------------------------- + +When a value of type ``type[T]`` (where ``T`` is a concrete class or a type +variable) is called, a type checker should evaluate the constructor call as if +it is being made on the class ``T`` (or the class that represents the upper bound +of type variable ``T``). This means the type checker should use the ``__call__`` +method of ``T``'s metaclass and the ``__new__`` and ``__init__`` methods of ``T`` +to evaluate the constructor call. + +It should be noted that such code could be unsafe because the type ``type[T]`` +may represent subclasses of ``T``, and those subclasses could redefine the +``__new__`` and ``__init__`` methods in a way that is incompatible with the +base class. Likewise, the metaclass of ``T`` could redefine the ``__call__`` +method in a way that is incompatible with the base metaclass. + + +Specialization During Construction +---------------------------------- + +As discussed above, if a class is generic and not explicitly specialized, its +type variables should be solved using the arguments passed to the ``__new__`` +and ``__init__`` methods. If one or more type variables are not solved during +these method evaluations, they should take on their default values. + + :: + + from typing import Any, Self, assert_type + + class MyClass1[T1, T2]: + def __new__(cls, x: T1) -> Self: ... + + assert_type(MyClass1(1), MyClass1[int, Any]) + + class MyClass2[T1, T3 = str]: + def __new__(cls, x: T1) -> Self: ... + + assert_type(MyClass2(1), MyClass2[int, str]) + + +Consistency of ``__new__`` and ``__init__`` +------------------------------------------- + +Type checkers may optionally validate that the ``__new__`` and ``__init__`` +methods for a class have :term:`consistent` signatures. + + :: + + class MyClass: + def __new__(cls) -> Self: + return super().__new__(cls) + + # Type error: __new__ and __init__ have inconsistent signatures + def __init__(self, x: str) -> None: + pass + + +Converting a Constructor to Callable +------------------------------------ + +Class objects are callable, which means the type of a class object can be +:term:`assignable` to a callable type. + + :: + + def accepts_callable[**P, R](cb: Callable[P, R]) -> Callable[P, R]: + return cb + + class MyClass: + def __init__(self, x: int) -> None: + pass + + reveal_type(accepts_callable(MyClass)) # ``def (x: int) -> MyClass`` + +When converting a class to a callable type, a type checker should use the +following rules, which reflect the same rules specified above for evaluating +constructor calls: + +1. If the class has a custom metaclass that defines a ``__call__`` method + that is annotated with a return type other than a subclass of the + class being constructed (or a union that contains such a type), a type + checker should assume that the metaclass ``__call__`` method is overriding + ``type.__call__`` in some special manner. In this case, the callable should + be synthesized from the parameters and return type of the metaclass + ``__call__`` method after it is bound to the class, and the ``__new__`` or + ``__init__`` methods (if present) should be ignored. This is an uncommon + case. In the more typical case where there is no custom metaclass that + overrides ``type.__call__`` in a special manner, the metaclass ``__call__`` + signature should be ignored for purposes of converting to a callable type. + If a custom metaclass ``__call__`` method is present but does not have an + annotated return type, type checkers may assume that the method acts like + ``type.__call__`` and proceed to the next step. + +2. If the class defines a ``__new__`` method or inherits a ``__new__`` method + from a base class other than ``object``, a type checker should synthesize a + callable from the parameters and return type of that method after it is bound + to the class. + +3. If the return type of the method in step 2 evaluates to a type that is not a + subclass of the class being constructed (or a union that includes such a + class), the final callable type is based on the result of step 2, and the + conversion process is complete. The ``__init__`` method is ignored in this + case. This is consistent with the runtime behavior of the ``type.__call__`` + method. + +4. If the class defines an ``__init__`` method or inherits an ``__init__`` method + from a base class other than ``object``, a callable type should be synthesized + from the parameters of the ``__init__`` method after it is bound to the class + instance resulting from step 2. The return type of this synthesized callable + should be the concrete value of ``Self``. + +5. If step 2 and 4 both produce no result because the class does not define or + inherit a ``__new__`` or ``__init__`` method from a class other than ``object``, + the type checker should synthesize callable types from the ``__new__`` and + ``__init__`` methods for the ``object`` class. + +6. Steps 2, 4 and 5 will produce either one or two callable types. The final + result of the conversion process is the union of these types. This will + reflect the callable signatures of the applicable ``__new__`` and + ``__init__`` methods. + + :: + + class A: + """ No __new__ or __init__ """ + pass + + class B: + """ __new__ and __init__ """ + def __new__(cls, *args, **kwargs) -> Self: + ... + + def __init__(self, x: int) -> None: + ... + + class C: + """ __new__ but no __init__ """ + def __new__(cls, x: int) -> int: + ... + + class CustomMeta(type): + def __call__(cls) -> NoReturn: + raise NotImplementedError("Class not constructable") + + class D(metaclass=CustomMeta): + """ Custom metaclass that overrides type.__call__ """ + def __new__(cls, *args, **kwargs) -> Self: + """ This __new__ is ignored for purposes of conversion """ + pass + + + class E: + """ __new__ that causes __init__ to be ignored """ + + def __new__(cls) -> A: + return A.__new__(cls) + + def __init__(self, x: int) -> None: + """ This __init__ is ignored for purposes of conversion """ + ... + + + reveal_type(accepts_callable(A)) # ``def () -> A`` + reveal_type(accepts_callable(B)) # ``def (*args, **kwargs) -> B | def (x: int) -> B`` + reveal_type(accepts_callable(C)) # ``def (x: int) -> int`` + reveal_type(accepts_callable(D)) # ``def () -> NoReturn`` + reveal_type(accepts_callable(E)) # ``def () -> A`` + + +If the ``__init__`` or ``__new__`` method is overloaded, the callable +type should be synthesized from the overloads. The resulting callable type +itself will be overloaded. + + :: + + class MyClass: + @overload + def __init__(self, x: int) -> None: ... + @overload + def __init__(self, x: str) -> None: ... + + reveal_type(accepts_callable(MyClass)) # overload of ``def (x: int) -> MyClass`` and ``def (x: str) -> MyClass`` + + +If the class is generic, the synthesized callable should include any class-scoped +type parameters that appear within the signature, but these type parameters should +be converted to function-scoped type parameters for the callable. +Any function-scoped type parameters in the ``__init__`` or ``__new__`` +method should also be included as function-scoped type parameters in the synthesized +callable. + + :: + + class MyClass[T]: + def __init__[V](self, x: T, y: list[V], z: V) -> None: ... + + reveal_type(accepts_callable(MyClass)) # ``def [T, V] (x: T, y: list[V], z: V) -> MyClass[T]`` diff --git a/docs/spec/dataclasses.rst b/docs/spec/dataclasses.rst new file mode 100644 index 000000000..ef08344b4 --- /dev/null +++ b/docs/spec/dataclasses.rst @@ -0,0 +1,592 @@ +.. _`dataclasses`: + +Dataclasses +=========== + +Type checkers should support dataclasses created through +the :py:mod:`dataclasses` module. In addition, the type system +contains a mechanism to make third-party classes behave like +standard dataclasses. + +.. _`dataclass-transform`: + +The ``dataclass_transform`` decorator +------------------------------------- + +(Originally specified in :pep:`681`.) + +Specification +^^^^^^^^^^^^^ + +This specification describes a decorator function in +the :py:mod:`typing` module named :py:func:`~typing.dataclass_transform`. This decorator +can be applied to either a function that is itself a decorator, +a class, or a metaclass. The presence of +``dataclass_transform`` tells a static type checker that the decorated +function, class, or metaclass performs runtime "magic" that transforms +a class, endowing it with dataclass-like behaviors. + +If ``dataclass_transform`` is applied to a function, using the decorated +function as a decorator is assumed to apply dataclass-like semantics. +If the function has overloads, the ``dataclass_transform`` decorator can +be applied to the implementation of the function or any one, but not more +than one, of the overloads. When applied to an overload, the +``dataclass_transform`` decorator still impacts all usage of the +function. + +If ``dataclass_transform`` is applied to a class, dataclass-like +semantics will be assumed for any class that directly or indirectly +derives from the decorated class or uses the decorated class as a +metaclass. Attributes on the decorated class and its base classes +are not considered to be fields. + +Examples of each approach are shown in the following sections. Each +example creates a ``CustomerModel`` class with dataclass-like semantics. +The implementation of the decorated objects is omitted for brevity, +but we assume that they modify classes in the following ways: + +* They synthesize an ``__init__`` method using data fields declared + within the class and its parent classes. +* They synthesize ``__eq__`` and ``__ne__`` methods. + +Type checkers will recognize that the +``CustomerModel`` class can be instantiated using the synthesized +``__init__`` method: + +.. code-block:: python + + # Using positional arguments + c1 = CustomerModel(327, "John Smith") + + # Using keyword arguments + c2 = CustomerModel(id=327, name="John Smith") + + # These calls will generate runtime errors and should be flagged as + # errors by a static type checker. + c3 = CustomerModel() + c4 = CustomerModel(327, first_name="John") + c5 = CustomerModel(327, "John Smith", 0) + +Decorator function example +"""""""""""""""""""""""""" + +.. code-block:: python + + # The ``create_model`` decorator is defined by a library. + # This could be in a type stub or inline. + @typing.dataclass_transform() + def create_model[T](cls: type[T]) -> type[T]: + cls.__init__ = ... + cls.__eq__ = ... + cls.__ne__ = ... + return cls + + # The ``create_model`` decorator can now be used to create new model + # classes, like this: + @create_model + class CustomerModel: + id: int + name: str + +Class example +""""""""""""" + +.. code-block:: python + + # The ``ModelBase`` class is defined by a library. This could be in + # a type stub or inline. + @typing.dataclass_transform() + class ModelBase: ... + + # The ``ModelBase`` class can now be used to create new model + # subclasses, like this: + class CustomerModel(ModelBase): + id: int + name: str + +Metaclass example +""""""""""""""""" + +.. code-block:: python + + # The ``ModelMeta`` metaclass and ``ModelBase`` class are defined by + # a library. This could be in a type stub or inline. + @typing.dataclass_transform() + class ModelMeta(type): ... + + class ModelBase(metaclass=ModelMeta): ... + + # The ``ModelBase`` class can now be used to create new model + # subclasses, like this: + class CustomerModel(ModelBase): + id: int + name: str + +Conditional fields +"""""""""""""""""" + +Dataclass fields may be conditional, via checks of the same +:ref:`statically-known conditions` +that a type-checker understands elsewhere, such as Python version:: + + @dataclass + class Person: + name: str + if sys.version_info >= (3, 14): + age: int + +Decorator function and class/metaclass parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A decorator function, class, or metaclass that provides dataclass-like +functionality may accept parameters that modify certain behaviors. +This specification defines the following parameters that static type +checkers must honor if they are used by a dataclass transform. Each of +these parameters accepts a bool argument, and it must be possible for +the bool value (``True`` or ``False``) to be statically evaluated. + +* ``eq``, ``order``, ``frozen``, ``init`` and ``unsafe_hash`` are parameters + supported in the stdlib dataclass, with meanings defined in + :pep:`PEP 557 <557#id7>`. +* ``kw_only``, ``match_args`` and ``slots`` are parameters supported + in the stdlib dataclass, first introduced in Python 3.10. + +``dataclass_transform`` parameters +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Parameters to ``dataclass_transform`` allow for some basic +customization of default behaviors: + +.. code-block:: python + + class _IdentityCallable(Protocol): + def __call__[T](self, arg: T, /) -> T: + ... + + def dataclass_transform( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + frozen_default: bool = False, + field_specifiers: tuple[type | Callable[..., Any], ...] = (), + **kwargs: Any, + ) -> _IdentityCallable: ... + +* ``eq_default`` indicates whether the ``eq`` parameter is assumed to + be True or False if it is omitted by the caller. If not specified, + ``eq_default`` will default to True (the default assumption for + dataclass). +* ``order_default`` indicates whether the ``order`` parameter is + assumed to be True or False if it is omitted by the caller. If not + specified, ``order_default`` will default to False (the default + assumption for dataclass). +* ``kw_only_default`` indicates whether the ``kw_only`` parameter is + assumed to be True or False if it is omitted by the caller. If not + specified, ``kw_only_default`` will default to False (the default + assumption for dataclass). +* ``frozen_default`` indicates whether the ``frozen`` parameter is + assumed to be True or False if it is omitted by the caller. If not + specified, ``frozen_default`` will default to False (the default + assumption for dataclass). +* ``field_specifiers`` specifies a static list of supported classes + that describe fields. Some libraries also supply functions to + allocate instances of field specifiers, and those functions may + also be specified in this tuple. If not specified, + ``field_specifiers`` will default to an empty tuple (no field + specifiers supported). The standard dataclass behavior supports + only one type of field specifier called ``Field`` plus a helper + function (``field``) that instantiates this class, so if we were + describing the stdlib dataclass behavior, we would provide the + tuple argument ``(dataclasses.Field, dataclasses.field)``. +* ``kwargs`` allows arbitrary additional keyword args to be passed to + ``dataclass_transform``. This gives type checkers the freedom to + support experimental parameters without needing to wait for changes + in ``typing.py``. Type checkers should report errors for any + unrecognized parameters. + +The following sections provide additional examples showing how these +parameters are used. + +Decorator function example +"""""""""""""""""""""""""" + +.. code-block:: python + + # Indicate that the ``create_model`` function assumes keyword-only + # parameters for the synthesized ``__init__`` method unless it is + # invoked with ``kw_only=False``. It always synthesizes order-related + # methods and provides no way to override this behavior. + @typing.dataclass_transform(kw_only_default=True, order_default=True) + def create_model( + *, + frozen: bool = False, + kw_only: bool = True, + ) -> Callable[[Type[_T]], Type[_T]]: ... + + # Example of how this decorator would be used by code that imports + # from this library: + @create_model(frozen=True, kw_only=False) + class CustomerModel: + id: int + name: str + +Class example +""""""""""""" + +.. code-block:: python + + # Indicate that classes that derive from this class default to + # synthesizing comparison methods. + @typing.dataclass_transform(eq_default=True, order_default=True) + class ModelBase: + def __init_subclass__( + cls, + *, + init: bool = True, + frozen: bool = False, + eq: bool = True, + order: bool = True, + ): + ... + + # Example of how this class would be used by code that imports + # from this library: + class CustomerModel( + ModelBase, + init=False, + frozen=True, + eq=False, + order=False, + ): + id: int + name: str + +Metaclass example +""""""""""""""""" + +.. code-block:: python + + # Indicate that classes that use this metaclass default to + # synthesizing comparison methods. + @typing.dataclass_transform(eq_default=True, order_default=True) + class ModelMeta(type): + def __new__( + cls, + name, + bases, + namespace, + *, + init: bool = True, + frozen: bool = False, + eq: bool = True, + order: bool = True, + ): + ... + + class ModelBase(metaclass=ModelMeta): + ... + + # Example of how this class would be used by code that imports + # from this library: + class CustomerModel( + ModelBase, + init=False, + frozen=True, + eq=False, + order=False, + ): + id: int + name: str + + +Field specifiers +^^^^^^^^^^^^^^^^^ + +Most libraries that support dataclass-like semantics provide one or +more "field specifier" types that allow a class definition to provide +additional metadata about each field in the class. This metadata can +describe, for example, default values, or indicate whether the field +should be included in the synthesized ``__init__`` method. + +Field specifiers can be omitted in cases where additional metadata is +not required: + +.. code-block:: python + + @dataclass + class Employee: + # Field with no specifier + name: str + + # Field that uses field specifier class instance + age: Optional[int] = field(default=None, init=False) + + # Field with type annotation and simple initializer to + # describe default value + is_paid_hourly: bool = True + + # Not a field (but rather a class variable) because type + # annotation is not provided. + office_number = "unassigned" + + +Field specifier parameters +"""""""""""""""""""""""""" + +Libraries that support dataclass-like semantics and support field +specifier classes typically use common parameter names to construct +these field specifiers. This specification formalizes the names and +meanings of the parameters that must be understood for static type +checkers. These standardized parameters must be keyword-only. + +These parameters are a superset of those supported by +:py:func:`dataclasses.field`, excluding those that do not have an impact on +type checking such as ``compare`` and ``hash``. + +Field specifier classes are allowed to use other +parameters in their constructors, and those parameters can be +positional and may use other names. + +* ``init`` is an optional bool parameter that indicates whether the + field should be included in the synthesized ``__init__`` method. If + unspecified, ``init`` defaults to True. Field specifier functions + can use overloads that implicitly specify the value of ``init`` + using a literal bool value type + (``Literal[False]`` or ``Literal[True]``). +* ``default`` is an optional parameter that provides the default value + for the field. +* ``default_factory`` is an optional parameter that provides a runtime + callback that returns the default value for the field. If neither + ``default`` nor ``default_factory`` are specified, the field is + assumed to have no default value and must be provided a value when + the class is instantiated. +* ``factory`` is an alias for ``default_factory``. Stdlib dataclasses + use the name ``default_factory``, but attrs uses the name ``factory`` + in many scenarios, so this alias is necessary for supporting attrs. +* ``kw_only`` is an optional bool parameter that indicates whether the + field should be marked as keyword-only. If true, the field will be + keyword-only. If false, it will not be keyword-only. If unspecified, + the value of the ``kw_only`` parameter on the object decorated with + ``dataclass_transform`` will be used, or if that is unspecified, the + value of ``kw_only_default`` on ``dataclass_transform`` will be used. +* ``alias`` is an optional str parameter that provides an alternative + name for the field. This alternative name is used in the synthesized + ``__init__`` method. +* ``converter`` is an optional parameter that specifies a + callable used to convert values when assigning to the field. + +It is an error to specify more than one of ``default``, +``default_factory`` and ``factory``. + +This example demonstrates the above: + +.. code-block:: python + + # Library code (within type stub or inline) + # In this library, passing a resolver means that init must be False, + # and the overload with Literal[False] enforces that. + @overload + def model_field( + *, + default: Optional[Any] = ..., + resolver: Callable[[], Any], + init: Literal[False] = False, + ) -> Any: ... + + @overload + def model_field( + *, + default: Optional[Any] = ..., + resolver: None = None, + init: bool = True, + ) -> Any: ... + + @typing.dataclass_transform( + kw_only_default=True, + field_specifiers=(model_field, )) + def create_model( + *, + init: bool = True, + ) -> Callable[[Type[_T]], Type[_T]]: ... + + # Code that imports this library: + @create_model(init=False) + class CustomerModel: + id: int = model_field(resolver=lambda : 0) + name: str + + +Runtime behavior +^^^^^^^^^^^^^^^^ + +At runtime, the ``dataclass_transform`` decorator's only effect is to +set an attribute named ``__dataclass_transform__`` on the decorated +function or class to support introspection. The value of the attribute +should be a dict mapping the names of the ``dataclass_transform`` +parameters to their values. + +For example: + +.. code-block:: python + + { + "eq_default": True, + "order_default": False, + "kw_only_default": False, + "field_specifiers": (), + "kwargs": {} + } + + +Dataclass semantics +^^^^^^^^^^^^^^^^^^^ + +Except where stated otherwise, classes impacted by +``dataclass_transform``, either by inheriting from a class that is +decorated with ``dataclass_transform`` or by being decorated with +a function decorated with ``dataclass_transform``, are assumed to +behave like stdlib :func:`~dataclasses.dataclass`. + +This includes, but is not limited to, the following semantics: + +* Frozen dataclasses cannot inherit from non-frozen dataclasses. A + class that has been decorated with ``dataclass_transform`` is + considered neither frozen nor non-frozen, thus allowing frozen + classes to inherit from it. Similarly, a class that directly + specifies a metaclass that is decorated with ``dataclass_transform`` + is considered neither frozen nor non-frozen. + + Consider these class examples: + + .. code-block:: python + + # ModelBase is not considered either "frozen" or "non-frozen" + # because it is decorated with ``dataclass_transform`` + @typing.dataclass_transform() + class ModelBase(): ... + + # Vehicle is considered non-frozen because it does not specify + # "frozen=True". + class Vehicle(ModelBase): + name: str + + # Car is a frozen class that derives from Vehicle, which is a + # non-frozen class. This is an error. + class Car(Vehicle, frozen=True): + wheel_count: int + + And these similar metaclass examples: + + .. code-block:: python + + @typing.dataclass_transform() + class ModelMeta(type): ... + + # ModelBase is not considered either "frozen" or "non-frozen" + # because it directly specifies ModelMeta as its metaclass. + class ModelBase(metaclass=ModelMeta): ... + + # Vehicle is considered non-frozen because it does not specify + # "frozen=True". + class Vehicle(ModelBase): + name: str + + # Car is a frozen class that derives from Vehicle, which is a + # non-frozen class. This is an error. + class Car(Vehicle, frozen=True): + wheel_count: int + +* Field ordering and inheritance is assumed to follow the rules + specified in `the Python docs `__. This includes the effects of + overrides (redefining a field in a child class that has already been + defined in a parent class). + +* :pep:`PEP 557 indicates <557#post-init-parameters>` that + all fields without default values must appear before + fields with default values. Although not explicitly + stated in PEP 557, this rule is ignored when ``init=False``, and + this specification likewise ignores this requirement in that + situation. Likewise, there is no need to enforce this ordering when + keyword-only parameters are used for ``__init__``, so the rule is + not enforced if ``kw_only`` semantics are in effect. + +* As with ``dataclass``, method synthesis is skipped if it would + overwrite a method that is explicitly declared within the class. + Method declarations on base classes do not cause method synthesis to + be skipped. + + For example, if a class declares an ``__init__`` method explicitly, + an ``__init__`` method will not be synthesized for that class. + +* KW_ONLY sentinel values are supported as described in `the Python + docs `_ + and `bpo-43532 `_. + +* ClassVar attributes are not considered dataclass fields and are + `ignored by dataclass mechanisms `_. + +* A dataclass field may be annotated with ``Final[...]``. For example, ``x: + Final[int]`` in a dataclass body specifies a dataclass field ``x``, which + will be initialized in the generated ``__init__`` and cannot be assigned to + thereafter. A ``Final`` dataclass field initialized in the class body is not + a class attribute unless explicitly annotated with ``ClassVar``. For example, + ``x: Final[int] = 3`` is a dataclass field named ``x`` with a default value + of ``3`` in the generated ``__init__`` method. A final class variable on a + dataclass must be explicitly annotated as e.g. ``x: ClassVar[Final[int]] = + 3``. + +Converters +^^^^^^^^^^ + +The ``converter`` parameter can be specified in a field definition to provide a callable +used to convert values when assigning to the associated attribute. This feature allows for automatic type +conversion and validation during attribute assignment. + +Converter behavior: + +* The converter is used for all attribute assignment, including assignment + of default values, assignment in synthesized ``__init__`` methods + and direct attribute setting (e.g., ``obj.attr = value``). +* The converter is not used when reading attributes, as the attributes should already have been converted. + +Typing rules for converters: + +* The ``converter`` must be a callable that must accept a single positional argument + (but may accept other optional arguments, which are ignored for typing purposes). +* The type of the first positional parameter provides the type of the synthesized ``__init__`` parameter + for the field. +* The return type of the callable must be assignable to the field's declared type. +* If ``default`` or ``default_factory`` are provided, the type of the default value should be + assignable to the first positional parameter of the ``converter``. + +Example usage: + +.. code-block:: python + + def str_or_none(x: Any) -> str | None: + return str(x) if x is not None else None + + @custom_dataclass + class Example: + int_field: int = custom_field(converter=int) + str_field: str | None = custom_field(converter=str_or_none) + path_field: pathlib.Path = custom_field( + converter=pathlib.Path, + default="default/path.txt" + ) + + # Usage + example = Example("123", None, "some/path") + # example.int_field == 123 + # example.str_field == None + # example.path_field == pathlib.Path("some/path") + + +Undefined behavior +^^^^^^^^^^^^^^^^^^ + +If multiple ``dataclass_transform`` decorators are found, either on a +single function (including its overloads), a single class, or within a +class hierarchy, the resulting behavior is undefined. Library authors +should avoid these scenarios. diff --git a/docs/spec/directives.rst b/docs/spec/directives.rst new file mode 100644 index 000000000..488d71a26 --- /dev/null +++ b/docs/spec/directives.rst @@ -0,0 +1,379 @@ +.. _directives: + +Type checker directives +======================= + +.. _`assert-type`: + +``assert_type()`` +----------------- + +The function ``typing.assert_type(val, typ)`` allows users to +ask a static type checker to confirm that the inferred type of *val* +is :term:`equivalent` to *typ*. + +When a type checker encounters a call to ``assert_type()``, it +should emit an error if the value is not of the specified type:: + + def greet(name: str) -> None: + assert_type(name, str) # OK, inferred type of `name` is `str` + assert_type(name, int) # type checker error + +If the two types are :term:`equivalent` but syntactically different, +the type checker may reject the ``assert_type()`` call:: + + from typing import assert_type, Literal + + def greet(name: str) -> None: + assert_type(name, str | Literal["spam"]) # type checker may error + +Type checkers should aim to minimize cases where they reject +``assert_type()`` calls that use equivalent types. + +The second argument must be a valid :term:`type expression`. + +.. _`reveal-type`: + +``reveal_type()`` +----------------- + +The function ``reveal_type(obj)`` makes type checkers +reveal the inferred static type of an expression. + +When a static type checker encounters a call to this function, +it should emit a diagnostic with the type of the argument. For example:: + + x: int = 1 + reveal_type(x) # Revealed type is "builtins.int" + +.. _`type-ignore`: + +``# type: ignore`` comments +--------------------------- + +The special comment ``# type: ignore`` is used to silence type checker +errors. + +The ``# type: ignore`` comment should be put on the line that the +error refers to:: + + import http.client + errors = { + 'not_found': http.client.NOT_FOUND # type: ignore + } + +A ``# type: ignore`` comment on a line by itself at the top of a file, +before any docstrings, imports, or other executable code, silences all +errors in the file. Blank lines and other comments, such as shebang +lines and coding cookies, may precede the ``# type: ignore`` comment. + +In some cases, linting tools or other comments may be needed on the same +line as a type comment. In these cases, the type comment should be before +other comments and linting markers: + + # type: ignore # + +The form ``# type: ignore[...]`` may be used to suppress only type errors with a +given error code, though support for this is optional and may vary by type checker: + +- Given ``# type: ignore[my_code1]``, a type checker may ignore the error code + ``my_code1`` and choose to treat this form as equivalent to ``# type: ignore``. +- Alternatively, given ``# type: ignore[my_code1]`` a type checker may suppress the + error only if the error cause matches the error code ``my_code1``. +- A bare ``# type: ignore`` must always suppress all type errors. + +.. _`cast`: + +``cast()`` +---------- + +Occasionally the type checker may need a different kind of hint: the +programmer may know that an expression is of a more constrained type +than a type checker may be able to infer. For example:: + + from typing import cast + + def find_first_str(a: list[object]) -> str: + index = next(i for i, x in enumerate(a) if isinstance(x, str)) + # We only get here if there's at least one string in a + return cast(str, a[index]) + +Some type checkers may not be able to infer that the type of +``a[index]`` is ``str`` and only infer ``object`` or ``Any``, but we +know that (if the code gets to that point) it must be a string. The +``cast(t, x)`` call tells the type checker that we are confident that +the type of ``x`` is ``t``. ``t`` must be a valid :term:`type expression`. +At runtime a cast always returns the +expression unchanged -- it does not check the type, and it does not +convert or coerce the value. + +.. _`if-type-checking`: + +``TYPE_CHECKING`` +----------------- + +Sometimes there's code that must be seen by a type checker (or other +static analysis tools) but should not be executed. For such +situations the ``typing`` module defines a constant, +``TYPE_CHECKING``, that is considered ``True`` during type checking +(or other static analysis) but ``False`` at runtime. Example:: + + import typing + + if typing.TYPE_CHECKING: + import expensive_mod + + def a_func(arg: 'expensive_mod.SomeClass') -> None: + a_var: expensive_mod.SomeClass = arg + ... + +(Note that the type annotation must be enclosed in quotes, making it a +"forward reference", to hide the ``expensive_mod`` reference from the +interpreter runtime. In the variable annotation no quotes are needed.) + +This approach may also be useful to handle import cycles. + +.. _`no-type-check`: + +``@no_type_check`` +------------------ + +The ``@typing.no_type_check`` decorator may be supported by type checkers +for functions and classes. + +If a type checker supports the ``no_type_check`` decorator for functions, it +should suppress all type errors for the ``def`` statement and its body including +any nested functions or classes. It should also ignore all parameter +and return type annotations and treat the function as if it were unannotated. + +The behavior for the ``no_type_check`` decorator when applied to a class is +left undefined by the typing spec at this time. + +.. _`version-and-platform-checks`: + +Version and platform checking +----------------------------- + +Type checkers are expected to understand simple version and platform +checks, e.g.:: + + import sys + + if sys.version_info >= (3, 12): + # Python 3.12+ + else: + # Python 3.11 and lower + + if sys.platform == 'win32': + # Windows specific definitions + else: + # Posix specific definitions + +Don't expect a checker to understand obfuscations like +``"".join(reversed(sys.platform)) == "xunil"``. + +.. _`deprecated`: + +``@deprecated`` +--------------- + +(Originally specified in :pep:`702`.) + +The :py:func:`warnings.deprecated` +decorator can be used on a class, function or method to mark it as deprecated. +This includes :class:`typing.TypedDict` and :class:`typing.NamedTuple` definitions. +With overloaded functions, the decorator may be applied to individual overloads, +indicating that the particular overload is deprecated. The decorator may also be +applied to the overload implementation function, indicating that the entire function +is deprecated. + +The decorator takes the following arguments: + +* A required positional-only argument representing the deprecation message. +* Two keyword-only arguments, ``category`` and ``stacklevel``, controlling + runtime behavior. These are not relevant to type checker behavior so they are + not further described in this specification. + +The positional-only argument is of type ``str`` and contains a message that should +be shown by the type checker when it encounters a usage of the decorated object. +Tools may clean up the deprecation message for display, for example +by using :func:`inspect.cleandoc` or equivalent logic. +The message must be a string literal. +The content of deprecation messages is up to the user, but it may include the version +in which the deprecated object is to be removed, and information about suggested +replacement APIs. + +Type checkers should produce a diagnostic whenever they encounter a usage of an +object marked as deprecated. For deprecated overloads, this includes all calls +that resolve to the deprecated overload. +For deprecated classes and functions, this includes: + +* References through module, class, or instance attributes (``module.deprecated_object``, + ``module.SomeClass.deprecated_method``, ``module.SomeClass().deprecated_method``) +* Any usage of deprecated objects in their defining module + (``x = deprecated_object()`` in ``module.py``) +* If ``import *`` is used, usage of deprecated objects from the + module (``from module import *; x = deprecated_object()``) +* ``from`` imports (``from module import deprecated_object``) +* Any syntax that indirectly triggers a call to the function. For example, + if the ``__add__`` method of a class ``C`` is deprecated, then + the code ``C() + C()`` should trigger a diagnostic. Similarly, if the + setter of a property is marked deprecated, attempts to set the property + should trigger a diagnostic. + +If a method is marked with the :ref:`@override decorator ` +and the base class method it overrides is deprecated, the type checker should +produce a diagnostic. + +There are additional scenarios where deprecations could come into play. +For example, an object may implement a :class:`typing.Protocol`, but one +of the methods required for protocol compliance is deprecated. +As scenarios such as this one appear complex and relatively unlikely to come up in practice, +type checkers are not mandated to detect them. + +Example +^^^^^^^ + +As an example, consider this library stub named ``library.pyi``: + +.. code-block:: python + + from warnings import deprecated + + @deprecated("Use Spam instead") + class Ham: ... + + @deprecated("It is pining for the fiords") + def norwegian_blue(x: int) -> int: ... + + @overload + @deprecated("Only str will be allowed") + def foo(x: int) -> str: ... + @overload + def foo(x: str) -> str: ... + + class Spam: + @deprecated("There is enough spam in the world") + def __add__(self, other: object) -> object: ... + + @property + @deprecated("All spam will be equally greasy") + def greasy(self) -> float: ... + + @property + def shape(self) -> str: ... + @shape.setter + @deprecated("Shapes are becoming immutable") + def shape(self, value: str) -> None: ... + +Here is how type checkers should handle usage of this library: + +.. code-block:: python + + from library import Ham # error: Use of deprecated class Ham. Use Spam instead. + + import library + + library.norwegian_blue(1) # error: Use of deprecated function norwegian_blue. It is pining for the fiords. + map(library.norwegian_blue, [1, 2, 3]) # error: Use of deprecated function norwegian_blue. It is pining for the fiords. + + library.foo(1) # error: Use of deprecated overload for foo. Only str will be allowed. + library.foo("x") # no error + + ham = Ham() # no error (already reported above) + + spam = library.Spam() + spam + 1 # error: Use of deprecated method Spam.__add__. There is enough spam in the world. + spam.greasy # error: Use of deprecated property Spam.greasy. All spam will be equally greasy. + spam.shape # no error + spam.shape = "cube" # error: Use of deprecated property setter Spam.shape. Shapes are becoming immutable. + +The exact wording of the diagnostics is up to the type checker and is not part +of the specification. + +Type checker behavior +^^^^^^^^^^^^^^^^^^^^^ + +It is unspecified exactly how type checkers should present deprecation +diagnostics to their users. However, some users (e.g., application developers +targeting only a specific version of Python) may not care about deprecations, +while others (e.g., library developers who want their library to remain +compatible with future versions of Python) would want to catch any use of +deprecated functionality in their CI pipeline. Therefore, it is recommended +that type checkers provide configuration options that cover both use cases. +As with any other type checker error, it is also possible to ignore deprecations +using ``# type: ignore`` comments. + +.. _`disjoint-base`: + +``@disjoint_base`` +------------------ + +(Originally specified in :pep:`800`.) + +The ``@typing.disjoint_base`` decorator may be used to mark a nominal class as +a disjoint base. It may only be used on nominal classes, including ``NamedTuple`` +definitions; it is a type checker error to use the decorator on a function, +``TypedDict`` definition, or ``Protocol`` definition. + +We define two properties on (nominal) classes: a class may or may not *be* a +disjoint base, and every class must *have* a valid disjoint base. + +A nominal class is a disjoint base if it is decorated with ``@typing.disjoint_base``, +or if it contains a non-empty ``__slots__`` definition. +This includes classes that have ``__slots__`` because of the ``@dataclass(slots=True)`` decorator or +because of the use of the ``dataclass_transform`` mechanism to add slots. +The universal base class, ``object``, is also a disjoint base. + +To determine a class's disjoint base, we look at all of its base classes to +determine a set of candidate disjoint bases. For each base +that is itself a disjoint base, the candidate is the base itself; otherwise, +it is the base's disjoint base. If the candidate set contains +a single disjoint base, that is the class's disjoint base. If there are multiple +candidates, but one of them is a subclass of all other candidates, +that class is the disjoint base. If no such candidate exists, the class does not +have a valid disjoint base, and therefore cannot exist. + +Type checkers must check for a valid disjoint base when checking class definitions, +and emit a diagnostic if they encounter a class +definition that lacks a valid disjoint base. Type checkers may also use the disjoint +base mechanism to determine whether types are disjoint, +for example when checking whether a type narrowing construct like ``isinstance()`` +results in an unreachable branch. + +Example:: + + from typing import disjoint_base, assert_never + + @disjoint_base + class Disjoint1: + pass + + @disjoint_base + class Disjoint2: + pass + + @disjoint_base + class DisjointChild(Disjoint1): + pass + + class C1: # disjoint base is `object` + pass + + # OK: candidate disjoint bases are `Disjoint1` and `object`, and `Disjoint1` is a subclass of `object`. + class C2(Disjoint1, C1): # disjoint base is `Disjoint1` + pass + + # OK: candidate disjoint bases are `DisjointChild` and `Disjoint1`, and `DisjointChild` is a subclass of `Disjoint1`. + class C3(DisjointChild, Disjoint1): # disjoint base is `DisjointChild` + pass + + # error: candidate disjoint bases are `Disjoint1` and `Disjoint2`, but neither is a subclass of the other + class C4(Disjoint1, Disjoint2): + pass + + def narrower(obj: Disjoint1) -> None: + if isinstance(obj, Disjoint2): + assert_never(obj) # OK: child class of `Disjoint1` and `Disjoint2` cannot exist + if isinstance(obj, C1): + reveal_type(obj) # Shows a non-empty type, e.g. `Disjoint1 & C1` diff --git a/docs/spec/distributing.rst b/docs/spec/distributing.rst new file mode 100644 index 000000000..13cfff81a --- /dev/null +++ b/docs/spec/distributing.rst @@ -0,0 +1,417 @@ +.. _distributing-type: + +Distributing type information +============================= + +.. _stub-files: + +Stub files +---------- + +(Originally specified in :pep:`484`.) + +*Stub files*, also called *type stubs*, provide type information for untyped +Python packages and modules. Stub files serve multiple purposes: + +* They are the only way to add type information to extension modules. +* They can provide type information for packages that do not wish to + add them inline. +* They can be distributed separately from the package or module that they + provide types for. The latter is referred to as the *implementation*. + This allows stubs to be developed at a different pace or by different + authors, which is especially useful when adding type annotations to + existing packages. +* They can act as documentation, succinctly explaining the external + API of a package, without including implementation details or private + members. + +Stub files use a subset of the constructs used in Python source files, as +described in :ref:`stub-file-supported-constructs` below. Type checkers should +parse a stub that uses only such constructs without error and not interpret any +construct in a manner contradictory to this specification. However, type +checkers are not required to implement checks for all of these constructs and +can elect to ignore unsupported ones. Additionally, type checkers can support +constructs not described here. + +If a stub file is found for a module, the type checker should not read the +corresponding "real" module. See :ref:`mro` for more information. + +.. _stub-file-syntax: + +Syntax +^^^^^^ + +Stub files are syntactically valid Python files with a ``.pyi`` suffix. They +should be parseable (e.g., with :py:func:`ast.parse`) in all Python versions +that are supported by the implementation and that are still supported +by the CPython project. For example, defining a type alias using the +``type`` keyword is only accepted by the Python parser in Python 3.12 and later, +so stubs supporting Python 3.11 or earlier versions should not use this syntax. +This allows type checkers implemented in Python to parse stub files using +functionality from the standard library. +Type checkers may choose to support syntactic features from newer Python versions +in stub files, but stubs that rely on such features may not be portable to all +type checkers. Type checkers may also choose to support Python versions that +are no longer supported by CPython; if so, they cannot rely on standard library +functionality to parse stub files. + +Type checkers should evaluate all :term:`annotation expressions ` as if they are quoted. +Consequently, forward references do not need to be quoted, and type system +features that do not depend on Python syntax changes are supported in stubs regardless +of the Python version supported. For example, the use of the ``|`` operator +to create unions (``X | Y``) was introduced in Python 3.10, but may be used +even in stubs that support Python 3.9 and older versions. + +.. _stub-file-supported-constructs: + +Supported Constructs +^^^^^^^^^^^^^^^^^^^^ + +Type checkers should fully support these constructs: + +* All features from the ``typing`` module of the latest released Python version + that use :ref:`supported syntax ` +* Comments, including type declaration (``# type: X``) and error suppression + (``# type: ignore``) comments +* Import statements, including the standard :ref:`import-conventions` and cyclic + imports +* Aliases, including type aliases, at both the module and class level +* :ref:`Simple version and platform checks ` + +The constructs in the following subsections may be supported in a more limited +fashion, as described below. + +Value Expressions +""""""""""""""""" + +In locations where value expressions can appear, such as the right-hand side of +assignment statements and function parameter defaults, type checkers should +support the following expressions: + +* The ellipsis literal, ``...``, which can stand in for any value +* Any value that is a + :ref:`legal parameter for typing.Literal ` +* Floating point literals, such as ``3.14`` +* Complex literals, such as ``1 + 2j`` + +Module Level Attributes +""""""""""""""""""""""" + +Type checkers should support module-level variable annotations, with and without +assignments:: + + x: int + x: int = 0 + x = 0 # type: int + x = ... # type: int + +The :ref:`Literal shortcut using Final ` should be +supported:: + + x: Final = 0 # type is Literal[0] + +When the type of a variable is omitted or disagrees from the assigned value, +type checker behavior is undefined:: + + x = 0 # behavior undefined + x: Final = ... # behavior undefined + x: int = "" # behavior undefined + +Classes +""""""" + +Class definition syntax follows general Python syntax, but type checkers +are expected to understand only the following constructs in class bodies: + +* The ellipsis literal ``...`` is used for empty class bodies. Using ``pass`` in + class bodies is undefined. +* Instance attributes follow the same rules as module level attributes + (see above). +* Method definitions (see below) and properties. +* Aliases. +* Inner class definitions. + +Yes:: + + class Simple: ... + + class Complex(Base): + read_write: int + @property + def read_only(self) -> int: ... + def do_stuff(self, y: str) -> None: ... + doStuff = do_stuff + IntList: TypeAlias = list[int] + class Inner: ... + +Functions and Methods +""""""""""""""""""""" + +Function and method definition follows general Python syntax. Using a function +or method body other than the ellipsis literal is undefined:: + + def foo(): ... # compatible + def bar(): pass # behavior undefined + +.. _stub-decorators: + +Decorators +"""""""""" + +Type checkers are expected to understand the effects of all decorators defined +in the ``typing`` module, plus these additional ones: + + * ``classmethod`` + * ``staticmethod`` + * ``property`` (including ``.setter`` and ``.deleter``) + * ``abc.abstractmethod`` + * ``dataclasses.dataclass`` + * ``warnings.deprecated`` + * functions decorated with ``@typing.dataclass_transform`` + +The Typeshed Project +^^^^^^^^^^^^^^^^^^^^ + +The `typeshed project `_ contains type +stubs for the standard library (vendored or handled specially by type checkers) +and type stubs for third-party libraries that don't ship their own type information +(typically distributed via PyPI). Policies regarding the +stubs collected there are decided separately and described in the project's +documentation. + +.. _packaging-typed-libraries: + +Type information in libraries +----------------------------- + +(Originally specified in :pep:`561`.) + +There are several motivations and methods of supporting typing in a package. +This specification recognizes three types of packages that users of typing wish to +create: + +1. The package maintainer would like to add type information inline. + +2. The package maintainer would like to add type information via stubs. + +3. A third party or package maintainer would like to share stub files for + a package, but the maintainer does not want to include them in the source + of the package. + +This specification aims to support all three scenarios and make them simple to add to +packaging and deployment. + +The two major parts of this specification are the packaging specifications +and the resolution order for resolving module type information. + + +Packaging Type Information +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +In order to make packaging and distributing type information as simple and +easy as possible, packaging and distribution is done through existing +frameworks. + +Package maintainers who wish to support type checking of their code MUST add +a marker file named ``py.typed`` to their package supporting typing. This marker applies +recursively: if a top-level package includes it, all its sub-packages MUST support +type checking as well. + +To have this file including with the package, maintainers can use existing packaging +options such as ``package_data`` in ``setuptools``. For more details, see +:ref:`the guide to providing type annotations `. + +For namespace packages (see :pep:`420`), the ``py.typed`` file should be in the +submodules of the namespace, to avoid conflicts and for clarity. + +This specification does not support distributing typing information as part of +module-only distributions or single-file modules within namespace packages. + +The single-file module should be refactored into a package +and indicate that the package supports typing as described +above. + +Stub-only Packages +"""""""""""""""""" + +For package maintainers wishing to ship stub files containing all of their +type information, it is preferred that the ``*.pyi`` stubs are alongside the +corresponding ``*.py`` files. However, the stubs can also be put in a separate +package and distributed separately. Third parties can also find this method +useful if they wish to distribute stub files. The name of the stub package +MUST follow the scheme ``foopkg-stubs`` for type stubs for the package named +``foopkg``. + +Note the name of the distribution (i.e. the project name on PyPI) containing +the package MAY be different than the mandated ``*-stubs`` package name. +The name of the distribution SHOULD NOT be ``types-*``, since this is +conventionally used for stub-only packages provided by typeshed. + +For stub-only packages adding a ``py.typed`` marker is not +needed since the name ``*-stubs`` is enough to indicate it is a source of typing +information. + +Third parties seeking to distribute stub files are encouraged to contact the +maintainer of the package about distribution alongside the package. If the +maintainer does not wish to maintain or package stub files or type information +:term:`inline`, then a third party stub-only package can be created. + +In addition, stub-only distributions MAY indicate which version(s) +of the runtime package are targeted by indicating the runtime distribution's +version(s) through normal dependency data. For example, the +stub package ``flyingcircus-stubs`` can indicate the versions of the +runtime ``flyingcircus`` distribution it supports through ``dependencies`` +field in ``pyproject.toml``. + +For namespace packages (see :pep:`420`), stub-only packages should +use the ``-stubs`` suffix on only the root namespace package. +All stub-only namespace packages should omit ``__init__.pyi`` files. ``py.typed`` +marker files are not necessary for stub-only packages, but similarly +to packages with inline types, if used, they should be in submodules of the namespace to +avoid conflicts and for clarity. + +For example, if the ``pentagon`` and ``hexagon`` are separate distributions +installing within the namespace package ``shapes.polygons`` +The corresponding types-only distributions should produce packages +laid out as follows:: + + shapes-stubs + └── polygons + └── pentagon + └── __init__.pyi + + shapes-stubs + └── polygons + └── hexagon +    └── __init__.pyi + +Partial Stub Packages +""""""""""""""""""""" + +Many stub packages will only have part of the type interface for libraries +completed, especially initially. For the benefit of type checking and code +editors, packages can be "partial". This means modules not found in the stub +package SHOULD be searched for in parts five and six of the module resolution +order below, namely :term:`inline` packages and any third-party stubs the type +checker chooses to vendor. + +Type checkers should merge the stub package and runtime package +directories. This can be thought of as the functional equivalent of copying the +stub package into the same directory as the corresponding runtime package +and type checking the combined directory structure. Thus type +checkers MUST maintain the normal resolution order of checking ``*.pyi`` before +``*.py`` files. + +If a stub package distribution is partial it MUST include ``partial\n`` in a +``py.typed`` file. For stub-packages distributing within a namespace +package (see :pep:`420`), the ``py.typed`` file should be in the +submodules of the namespace. + +Type checkers should treat namespace packages within stub-packages as +incomplete since multiple distributions may populate them. +Regular packages within namespace packages in stub-package distributions +are considered complete unless a ``py.typed`` with ``partial\n`` is included. + +.. _mro: + +Import resolution ordering +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The following is the order in which type checkers supporting this specification SHOULD +resolve modules containing type information: + + +1. :term:`Stubs ` or Python source manually put in the beginning of the path. Type + checkers SHOULD provide this to allow the user complete control of which + stubs to use, and to patch broken stubs or :term:`inline` types from packages. + In mypy the ``$MYPYPATH`` environment variable can be used for this. + +2. User code - the files the type checker is running on. + +3. Typeshed stubs for the standard library. These will usually be vendored by + type checkers, but type checkers SHOULD provide an option for users to + provide a path to a directory containing a custom or modified version of + typeshed; if this option is provided, type checkers SHOULD use this as the + canonical source for standard-library types in this step. + +4. :term:`Stub ` packages - these packages SHOULD supersede any installed inline + package. They can be found in directories named ``foopkg-stubs`` for + package ``foopkg``. + +5. Packages with a ``py.typed`` marker file - if there is nothing overriding + the installed package, *and* it opts into type checking, the types + bundled with the package SHOULD be used (be they in ``.pyi`` type + stub files or inline in ``.py`` files). + +6. If the type checker chooses to additionally vendor any third-party stubs + (from typeshed or elsewhere), these SHOULD come last in the module + resolution order. + +If typecheckers identify a stub-only namespace package without the desired module +in step 4, they should continue to step 5/6. Typecheckers should identify namespace packages +by the absence of ``__init__.pyi``. This allows different subpackages to +independently opt for inline vs stub-only. + +Type checkers that check a different Python version than the version they run +on MUST find the type information in the ``site-packages``/``dist-packages`` +of that Python version. This can be queried e.g. +``pythonX.Y -c 'import site; print(site.getsitepackages())'``. It is also recommended +that the type checker allow for the user to point to a particular Python +binary, in case it is not in the path. + +.. _library-interface: + +Library interface (public and private symbols) +---------------------------------------------- + +If a ``py.typed`` module is present, a type checker will treat all modules +within that package (i.e. all files that end in ``.py`` or ``.pyi``) as +importable unless the file name begins with an underscore. These modules +comprise the supported interface for the library. + +Each module exposes a set of symbols. Some of these symbols are +considered "private” — implementation details that are not part of the +library’s interface. Type checkers can use the following rules +to determine which symbols are visible outside of the package. + +- Symbols whose names begin with an underscore (but are not dunder + names) are considered private. +- Imported symbols are considered private by default. A fixed set of + :ref:`import forms ` re-export imported symbols. +- A module can expose an ``__all__`` symbol at the module level that + provides a list of names that are considered part of the interface. + This overrides all other rules above, allowing imported symbols or + symbols whose names begin with an underscore to be included in the + interface. +- Local variables within a function (including nested functions) are + always considered private. + +The following idioms are supported for defining the values contained +within ``__all__``. These restrictions allow type checkers to statically +determine the value of ``__all__``. + +- ``__all__ = ('a', 'b')`` +- ``__all__ = ['a', 'b']`` +- ``__all__ += ['a', 'b']`` +- ``__all__ += submodule.__all__`` +- ``__all__.extend(['a', 'b'])`` +- ``__all__.extend(submodule.__all__)`` +- ``__all__.append('a')`` +- ``__all__.remove('a')`` + +.. _import-conventions: + +Import Conventions +------------------ + +By convention, certain import forms indicate to type checkers that an imported +symbol is re-exported and should be considered part of the importing module's +public interface. All other imported symbols are considered private by default. + +The following import forms re-export symbols: + +* ``import X as X`` (a redundant module alias): re-exports ``X``. +* ``from Y import X as X`` (a redundant symbol alias): re-exports ``X``. +* ``from Y import *``: if ``Y`` defines a module-level ``__all__`` list, + re-exports all names in ``__all__``; otherwise, re-exports all public symbols + in ``Y``'s global scope. diff --git a/docs/spec/enums.rst b/docs/spec/enums.rst new file mode 100644 index 000000000..ecb283d55 --- /dev/null +++ b/docs/spec/enums.rst @@ -0,0 +1,373 @@ +Enumerations +============ + +The ``enum.Enum`` class behaves differently from other Python classes in several +ways that require special-case handling in type checkers. This section discusses +the Enum behaviors that should be supported by type checkers and others which +may be supported optionally. It is recommended that library and type stub +authors avoid using optional behaviors because these may not be supported +by some type checkers. + + +Enum Definition +--------------- + +Enum classes can be defined using a "class syntax" or a "function syntax". +The function syntax offers several ways to specify enum members: names passed +as individual arguments, a list or tuple of names, a string of +comma-delimited or space-delimited names, a list or tuple of tuples that contain +name/value pairs, and a dictionary of name/value items. + +Type checkers should support the class syntax, but the function syntax (in +its various forms) is optional:: + + class Color1(Enum): # Supported + RED = 1 + GREEN = 2 + BLUE = 3 + + Color2 = Enum('Color2', 'RED', 'GREEN', 'BLUE') # Optional + Color3 = Enum('Color3', ['RED', 'GREEN', 'BLUE']) # Optional + Color4 = Enum('Color4', ('RED', 'GREEN', 'BLUE')) # Optional + Color5 = Enum('Color5', 'RED, GREEN, BLUE') # Optional + Color6 = Enum('Color6', 'RED GREEN BLUE') # Optional + Color7 = Enum('Color7', [('RED', 1), ('GREEN', 2), ('BLUE', 3)]) # Optional + Color8 = Enum('Color8', (('RED', 1), ('GREEN', 2), ('BLUE', 3))) # Optional + Color9 = Enum('Color9', {'RED': 1, 'GREEN': 2, 'BLUE': 3}) # Optional + +Enum classes can also be defined using a subclass of ``enum.Enum`` or any class +that uses ``enum.EnumType`` (or a subclass thereof) as a metaclass. Note that +``enum.EnumType`` was named ``enum.EnumMeta`` prior to Python 3.11. Type +checkers should treat such classes as enums:: + + class CustomEnum1(Enum): + pass + + class Color7(CustomEnum1): # Supported + RED = 1 + GREEN = 2 + BLUE = 3 + + class CustomEnumType(EnumType): + pass + + class CustomEnum2(metaclass=CustomEnumType): + pass + + class Color8(CustomEnum2): # Supported + RED = 1 + GREEN = 2 + BLUE = 3 + +Enum members may be conditional, via checks of the same +:ref:`statically-known conditions` +that a type-checker understands elsewhere, such as Python version:: + + class Color(Enum): + RED = 1 + if sys.version_info >= (3, 14): + BLUE = 2 + + +Enum Behaviors +-------------- + +Enum classes are iterable and indexable, and they can be called with a value +to look up the enum member with that value. Type checkers should support these +behaviors:: + + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + for color in Color: + reveal_type(color) # Revealed type is 'Color' + + reveal_type(Color["RED"]) # Revealed type is 'Literal[Color.RED]' (or 'Color') + reveal_type(Color(3)) # Revealed type is 'Literal[Color.BLUE]' (or 'Color') + +Unlike most Python classes, calling an enum class does not invoke its constructor. +Instead, the call performs a value-based lookup of an enum member. + +An Enum class with one or more defined members cannot be subclassed. They are +implicitly "final". Type checkers should enforce this:: + + class EnumWithNoMembers(Enum): + pass + + class Shape(EnumWithNoMembers): # OK (because no members are defined) + SQUARE = 1 + CIRCLE = 2 + + class ExtendedShape(Shape): # Type checker error: Shape is implicitly final + TRIANGLE = 3 + + +Defining Members +---------------- + +When using the "class syntax", enum classes can define both members and +other (non-member) attributes. The ``EnumType`` metaclass applies a set +of rules to distinguish between members and non-members. Type checkers +should honor the most common of these rules. The lesser-used rules are +optional. Some of these rules may be impossible to evaluate and enforce +statically in cases where dynamic values are used. + +* If an attribute is defined in the class body with a type annotation but + with no assigned value, a type checker should assume this is a non-member + attribute:: + + class Pet(Enum): + genus: str # Non-member attribute + species: str # Non-member attribute + + CAT = 1 # Member attribute + DOG = 2 # Member attribute + + Within a type stub, members can be defined using the actual runtime values, + or a placeholder of ``...`` can be used:: + + class Pet(Enum): + genus: str # Non-member attribute + species: str # Non-member attribute + + CAT = 1 # Member attribute with known value and type + DOG = cast(int, ...) # Member attribute with unknown value and known type + BIRD = ... # Member attribute with unknown value and type + +* Members defined within an enum class should not include explicit type + annotations. Type checkers should infer a literal type for all members. + A type checker should report an error if a type annotation is used + for an enum member because this type will be incorrect and misleading + to readers of the code:: + + class Pet(Enum): + CAT = 1 # OK + DOG: int = 2 # Type checker error + +* Methods, callables, descriptors (including properties), and nested classes + that are defined in the class are not treated as enum members by the + ``EnumType`` metaclass and should likewise not be treated as enum members by + a type checker:: + + def identity(x): return x + + class Pet(Enum): + CAT = 1 # Member attribute + DOG = 2 # Member attribute + + converter = lambda x: str(x) # Non-member attribute + transform = identity # Non-member attribute + + @property + def species(self) -> str: # Non-member property + return "mammal" + + def speak(self) -> None: # Non-member method + print("meow" if self is Pet.CAT else "woof") + + class Nested: ... # Non-member nested class + +* An attribute that is assigned the value of another member of the same enum + is not a member itself. Instead, it is an alias for the first member:: + + class TrafficLight(Enum): + RED = 1 + GREEN = 2 + YELLOW = 3 + + AMBER = YELLOW # Alias for YELLOW + + reveal_type(TrafficLight.AMBER) # Revealed type is Literal[TrafficLight.YELLOW] + +* If using Python 3.11 or newer, the ``enum.member`` and ``enum.nonmember`` + classes can be used to unambiguously distinguish members from non-members. + Type checkers should support these classes:: + + class Example(Enum): + a = member(1) # Member attribute + b = nonmember(2) # Non-member attribute + + @member + def c(self) -> None: # Member method + pass + + reveal_type(Example.a) # Revealed type is Literal[Example.a] + reveal_type(Example.b) # Revealed type is int or Literal[2] + reveal_type(Example.c) # Revealed type is Literal[Example.c] + +* An attribute with a private name (beginning with, but not ending in, a double + underscore) is treated as a non-member:: + + class Example(Enum): + A = 1 # Member attribute + __B = 2 # Non-member attribute + + reveal_type(Example.A) # Revealed type is Literal[Example.A] + reveal_type(Example.__B) # Type Error: Private name is mangled + +* An enum class can define a class symbol named ``_ignore_``. This can be a list + of names or a string containing a space-delimited list of names that are + deleted from the enum class at runtime. Type checkers may support this + mechanism:: + + class Pet(Enum): + _ignore_ = "DOG FISH" + CAT = 1 # Member attribute + DOG = 2 # temporary variable, will be removed from the final enum class + FISH = 3 # temporary variable, will be removed from the final enum class + + +Member Names +------------ + +All enum member objects have an attribute ``_name_`` that contains the member's +name. They also have a property ``name`` that returns the same name. Type +checkers may infer a literal type for the name of a member:: + + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + reveal_type(Color.RED._name_) # Revealed type is Literal["RED"] (or str) + reveal_type(Color.RED.name) # Revealed type is Literal["RED"] (or str) + + def func1(red_or_blue: Literal[Color.RED, Color.BLUE]): + reveal_type(red_or_blue.name) # Revealed type is Literal["RED", "BLUE"] (or str) + + def func2(any_color: Color): + reveal_type(any_color.name) # Revealed type is Literal["RED", "BLUE", "GREEN"] (or str) + + +Member Values +------------- + +All enum member objects have an attribute ``_value_`` that contains the member's +value. They also have a property ``value`` that returns the same value. Type +checkers may infer the type of a member's value:: + + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + reveal_type(Color.RED._value_) # Revealed type is Literal[1] (or int or object or Any) + reveal_type(Color.RED.value) # Revealed type is Literal[1] (or int or object or Any) + + def func1(red_or_blue: Literal[Color.RED, Color.BLUE]): + reveal_type(red_or_blue.value) # Revealed type is Literal[1, 2] (or int or object or Any) + + def func2(any_color: Color): + reveal_type(any_color.value) # Revealed type is Literal[1, 2, 3] (or int or object or Any) + + +The value of ``_value_`` can be assigned in a constructor method. This technique +is sometimes used to initialize both the member value and non-member attributes. +If the value assigned in the class body is a tuple, the unpacked tuple value is +passed to the constructor. Type checkers may validate consistency between assigned +tuple values and the constructor signature:: + + class Planet(Enum): + def __init__(self, value: int, mass: float, radius: float): + self._value_ = value + self.mass = mass + self.radius = radius + + MERCURY = (1, 3.301e+23, 2.4397e6) + VENUS = (2, 4.867e+24, 6.0518e6) + EARTH = (3, 5.972e+24, 6.37814e6) + MARS = (6.417e+23, 3.3962e6) # Type checker error (optional) + JUPITER = 5 # Type checker error (optional) + + reveal_type(Planet.MERCURY.value) # Revealed type is Literal[1] (or int or object or Any) + + +The class ``enum.auto`` and method ``_generate_next_value_`` can be used within +an enum class to automatically generate values for enum members. Type checkers +may support these to infer literal types for member values:: + + class Color(Enum): + RED = auto() + GREEN = auto() + BLUE = auto() + + reveal_type(Color.RED.value) # Revealed type is Literal[1] (or int or object or Any) + + +If an enum class provides an explicit type annotation for ``_value_``, type +checkers should enforce this declared type when values are assigned to +``_value_``:: + + class Color(Enum): + _value_: int + RED = 1 # OK + GREEN = "green" # Type error + + class Planet(Enum): + _value_: str + + def __init__(self, value: int, mass: float, radius: float): + self._value_ = value # Type error + + MERCURY = (1, 3.301e+23, 2.4397e6) + +If the literal values for enum members are not supplied, as they sometimes +are not within a type stub file, a type checker can use the type of the +``_value_`` attribute:: + + class ColumnType(Enum): + _value_: int + DORIC = ... + IONIC = ... + CORINTHIAN = ... + + reveal_type(ColumnType.DORIC.value) # Revealed type is int (or object or Any) + + +Enum Literal Expansion +---------------------- + +From the perspective of the type system, most enum classes are equivalent +to the union of the literal members within that enum. (This rule +does not apply to classes that derive from ``enum.Flag`` because these enums +allow flags to be combined in arbitrary ways.) Because of the equivalency +between an enum class and the union of literal members within that enum, the +two types may be used interchangeably. Type checkers may therefore expand +an enum type (that does not derive from ``enum.Flag``) into a union of +literal values during type narrowing and exhaustion detection:: + + class Color(Enum): + RED = 1 + GREEN = 2 + BLUE = 3 + + def print_color1(c: Color): + if c is Color.RED or c is Color.BLUE: + print("red or blue") + else: + reveal_type(c) # Revealed type is Literal[Color.GREEN] + + def print_color2(c: Color): + match c: + case Color.RED | Color.BLUE: + print("red or blue") + case Color.GREEN: + print("green") + case _: + reveal_type(c) # Revealed type is Never + + +Likewise, a type checker should treat a complete union of all literal members +as :term:`equivalent` to the enum type:: + + class Answer(Enum): + Yes = 1 + No = 2 + + def func(val: object) -> Answer: + if val is not Answer.Yes and val is not Answer.No: + raise ValueError("Invalid value") + reveal_type(val) # Revealed type is Answer (or Literal[Answer.Yes, Answer.No]) + return val # OK diff --git a/docs/spec/exceptions.rst b/docs/spec/exceptions.rst new file mode 100644 index 000000000..aae3fe2c1 --- /dev/null +++ b/docs/spec/exceptions.rst @@ -0,0 +1,63 @@ +Exceptions +========== + +Some type checking behaviors, such as type narrowing and reachability analysis, +require a type checker to understand code flow. Code flow normally proceeds +from one statement to the next, but some statements such as ``for``, ``while`` +and ``return`` can change the code flow. Similarly, ``try``/``except``/``finally`` +statements affect code flow and therefore can affect type evaluation. For example:: + + x = None + try: + some_function() + x = 1 + except NotImplementedError: + pass + + # The type of `x` at this point could be None if `some_function` raises + # an exception or `Literal[1]` if it doesn't, so a type checker may + # choose to narrow its type based on this analysis. + reveal_type(x) # Literal[1] | None + + +Context Managers +---------------- + +A context manager may optionally "suppress" exceptions by returning ``True`` +(or some other truthy value) from its ``__exit__`` method. When such a context +manager is used, any exceptions that are raised and otherwise uncaught within +the ``with`` block are caught by the context manager, and control continues +immediately after the ``with`` block. If a context manager does not suppress +exceptions (as is typically the case), any exceptions that are raised and +otherwise uncaught within the ``with`` block propagate beyond the ``with`` +block. + +Type checkers that employ code flow analysis must be able to distinguish +between these two cases. This is done by examining the return type +annotation of the ``__exit__`` method of the context manager. + +If the return type of the ``__exit__`` method is specifically ``bool`` or +``Literal[True]``, a type checker should assume that exceptions *can be* +suppressed. For any other return type, a type checker should assume that +exceptions *are not* suppressed. Examples include: ``Any``, ``Literal[False]``, +``None``, and ``bool | None``. + +This convention was chosen because most context managers do not suppress +exceptions, and it is common for their ``__exit__`` method to be annotated as +returning ``bool | None``. Context managers that suppress exceptions are +relatively rare, so they are considered a special case. + +For example, the following context manager suppresses exceptions:: + + class Suppress: + def __enter__(self) -> None: + pass + + def __exit__(self, exc_type, exc_value, traceback) -> bool: + return True + + with Suppress(): + raise ValueError("This exception is suppressed") + + # The exception is suppressed, so this line is reachable. + print("Code is reachable") diff --git a/docs/spec/generics.rst b/docs/spec/generics.rst new file mode 100644 index 000000000..c1fa05325 --- /dev/null +++ b/docs/spec/generics.rst @@ -0,0 +1,2841 @@ +.. _`generics`: + +Generics +======== + +Introduction +------------ + +Since type information about objects kept in containers cannot be +statically inferred in a generic way, abstract base classes have been +extended to support subscription to denote expected types for container +elements. Example:: + + from collections.abc import Mapping + + def notify_by_email(employees: set[Employee], overrides: Mapping[str, str]) -> None: ... + +Generics can be parameterized by using a factory available in +``typing`` called ``TypeVar``. Example:: + + from collections.abc import Sequence + from typing import TypeVar + + T = TypeVar('T') # Declare type variable + + def first(l: Sequence[T]) -> T: # Generic function + return l[0] + +Or, since Python 3.12 (:pep:`695`), by using the new syntax for +generic functions:: + + from collections.abc import Sequence + + def first[T](l: Sequence[T]) -> T: # Generic function + return l[0] + +The two syntaxes are equivalent. +In either case the contract is that the returned value is consistent with +the elements held by the collection. + +A ``TypeVar()`` expression must always directly be assigned to a +variable (it should not be used as part of a larger expression). The +argument to ``TypeVar()`` must be a string equal to the variable name +to which it is assigned. Type variables must not be redefined. + +``TypeVar`` supports constraining parametric types to a fixed set of possible +types (note: those types cannot be parameterized by type variables). For +example, we can define a type variable that ranges over just ``str`` and +``bytes``. By default, a type variable ranges over all possible types. +Example of constraining a type variable:: + + from typing import TypeVar + + AnyStr = TypeVar('AnyStr', str, bytes) + + def concat(x: AnyStr, y: AnyStr) -> AnyStr: + return x + y + +Or using the built-in syntax (3.12 and higher):: + + def concat[AnyStr: (str, bytes)](x: AnyStr, y: AnyStr) -> AnyStr: + return x + y + +The function ``concat`` can be called with either two ``str`` arguments +or two ``bytes`` arguments, but not with a mix of ``str`` and ``bytes`` +arguments. + +There should be at least two constraints, if any; specifying a single +constraint is disallowed. + +Subtypes of types constrained by a type variable should be treated +as their respective explicitly listed base types in the context of the +type variable. Consider this example:: + + class MyStr(str): ... + + x = concat(MyStr('apple'), MyStr('pie')) + +The call is valid but the type variable ``AnyStr`` will be set to +``str`` and not ``MyStr``. In effect, the inferred type of the return +value assigned to ``x`` will also be ``str``. + +Additionally, ``Any`` is a valid value for every type variable. +Consider the following:: + + def count_truthy(elements: list[Any]) -> int: + return sum(1 for elem in elements if elem) + +This is equivalent to omitting the generic notation and just saying +``elements: list``. + + +User-defined generic types +-------------------------- + +There are several ways to define a user-defined class as generic: + +* Include a ``Generic`` base class. +* Use the new generic class syntax in Python 3.12 and higher. +* Include a ``Protocol`` base class parameterized with type variables. This + approach also marks the class as a protocol - see + :ref:`generic protocols` for more information. +* Include a generic base class parameterized with type variables. + +Example using ``Generic``:: + + from typing import TypeVar, Generic + from logging import Logger + + T = TypeVar('T') + + class LoggedVar(Generic[T]): + def __init__(self, value: T, name: str, logger: Logger) -> None: + self.name = name + self.logger = logger + self.value = value + + def set(self, new: T) -> None: + self.log('Set ' + repr(self.value)) + self.value = new + + def get(self) -> T: + self.log('Get ' + repr(self.value)) + return self.value + + def log(self, message: str) -> None: + self.logger.info('{}: {}'.format(self.name, message)) + +Or, using the new generic class syntax:: + + class LoggedVar[T]: + # methods as in previous example + +This implicitly adds ``Generic[T]`` as a base class, and type checkers +should treat the two definitions of ``LoggedVar`` largely equivalently (except +for variance, see below). + +``Generic[T]`` as a base class defines that the class ``LoggedVar`` +takes a single type parameter ``T``. This also makes ``T`` valid as +a type within the class body. + +The ``Generic`` base class uses a metaclass that defines ``__getitem__`` +so that ``LoggedVar[t]`` is valid as a type:: + + from collections.abc import Iterable + + def zero_all_vars(vars: Iterable[LoggedVar[int]]) -> None: + for var in vars: + var.set(0) + +A generic type can have any number of type variables, and type variables +may be constrained. This is valid:: + + from typing import TypeVar, Generic + + T = TypeVar('T') + S = TypeVar('S') + + class Pair(Generic[T, S]): + ... + +Each type variable argument to ``Generic`` must be distinct. This is +thus invalid:: + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class Pair(Generic[T, T]): # INVALID + ... + +All arguments to ``Generic`` or ``Protocol`` must be type variables:: + + from typing import Generic, Protocol + + class Bad1(Generic[int]): # INVALID + ... + class Bad2(Protocol[int]): # INVALID + ... + +When a ``Generic`` or parameterized ``Protocol`` base class is present, all type +parameters for the class must appear within the ``Generic`` or +``Protocol`` type argument list, respectively. A type checker should report an +error if a type variable that is not included in the type argument list appears +elsewhere in the base class list:: + + from typing import Generic, Protocol, TypeVar + from collections.abc import Iterable + + T = TypeVar('T') + S = TypeVar('S') + + class Bad1(Iterable[T], Generic[S]): # INVALID + ... + class Bad2(Iterable[T], Protocol[S]): # INVALID + ... + +Note that the above rule does not apply to a bare ``Protocol`` base class. This +is valid (see below):: + + from typing import Protocol, TypeVar + from collections.abc import Iterator + + T = TypeVar('T') + + class MyIterator(Iterator[T], Protocol): ... + +When no ``Generic`` or parameterized ``Protocol`` base class is present, a +defined class is generic if you subclass one or more other generic classes and +specify type variables for their parameters. See :ref:`generic-base-classes` +for details. + +You can use multiple inheritance with ``Generic``:: + + from typing import TypeVar, Generic + from collections.abc import Sized, Iterable, Container + + T = TypeVar('T') + + class LinkedList(Sized, Generic[T]): + ... + + K = TypeVar('K') + V = TypeVar('V') + + class MyMapping(Iterable[tuple[K, V]], + Container[tuple[K, V]], + Generic[K, V]): + ... + +Subclassing a generic class without specifying type parameters assumes +``Any`` for each position unless the type parameter has a default value. +In the following example, ``MyIterable`` is not generic but implicitly inherits +from ``Iterable[Any]``:: + + from collections.abc import Iterable + + class MyIterable(Iterable): # Same as Iterable[Any] + ... + +Generic metaclasses are not supported. + +.. _`typevar-scoping`: + +Scoping rules for type variables +-------------------------------- + +When using the generic class syntax introduced in Python 3.12, the location of +its declaration defines its scope. When using the older syntax, the scoping +rules are more subtle and complex: + +* A type variable used in a generic function could be inferred to represent + different types in the same code block. Example:: + + from typing import TypeVar, Generic + + T = TypeVar('T') + + def fun_1(x: T) -> T: ... # T here + def fun_2(x: T) -> T: ... # and here could be different + + fun_1(1) # This is OK, T is inferred to be int + fun_2('a') # This is also OK, now T is str + +* A type variable used in a method of a generic class that coincides + with one of the variables that parameterize this class is always bound + to that variable. Example:: + + from typing import TypeVar, Generic + + T = TypeVar('T') + + class MyClass(Generic[T]): + def meth_1(self, x: T) -> T: ... # T here + def meth_2(self, x: T) -> T: ... # and here are always the same + + a: MyClass[int] = MyClass() + a.meth_1(1) # OK + a.meth_2('a') # This is an error! + +* A type variable used in a method that does not match any of the variables + that parameterize the class makes this method a generic function in that + variable:: + + T = TypeVar('T') + S = TypeVar('S') + class Foo(Generic[T]): + def method(self, x: T, y: S) -> S: + ... + + x: Foo[int] = Foo() + y = x.method(0, "abc") # inferred type of y is str + +* Unbound type variables should not appear in the bodies of generic functions, + or in the class bodies apart from method definitions:: + + T = TypeVar('T') + S = TypeVar('S') + + def a_fun(x: T) -> None: + # this is OK + y: list[T] = [] + # but below is an error! + y: list[S] = [] + + class Bar(Generic[T]): + # this is also an error + an_attr: list[S] = [] + + def do_something(self, x: S) -> S: # this is OK though + ... + +* A generic class definition that appears inside a generic function + should not use type variables that parameterize the generic function:: + + def a_fun(x: T) -> None: + + # This is OK + a_list: list[T] = [] + ... + + # This is however illegal + class MyGeneric(Generic[T]): + ... + +* A generic class nested in another generic class cannot use the same type + variables. The scope of the type variables of the outer class + doesn't cover the inner one:: + + T = TypeVar('T') + S = TypeVar('S') + + class Outer(Generic[T]): + class Bad(Iterable[T]): # Error + ... + class AlsoBad: + x: list[T] # Also an error + + class Inner(Iterable[S]): # OK + ... + attr: Inner[T] # Also OK + + +Instantiating generic classes and type erasure +---------------------------------------------- + +User-defined generic classes can be instantiated. Suppose we write +a ``Node`` class:: + + class Node[T]: + ... + +To create ``Node`` instances you call ``Node()`` just as for a regular +class. At runtime the type (class) of the instance will be ``Node``. +But what type does it have to the type checker? The answer depends on +how much information is available in the call. If the constructor +(``__init__`` or ``__new__``) uses ``T`` in its signature, and a +corresponding argument value is passed, the type of the corresponding +argument(s) is substituted. Otherwise, the default value for the type +parameter (or ``Any``, if no default is provided) is assumed. Example:: + + class Node[T]: + x: T # Instance attribute (see below) + def __init__(self, label: T | None = None) -> None: + ... + + x = Node('') # Inferred type is Node[str] + y = Node(0) # Inferred type is Node[int] + z = Node() # Inferred type is Node[Any] + +In case the inferred type uses ``[Any]`` but the intended type is more +specific, you can use an annotation (see below) to force the type of +the variable, e.g.:: + + # (continued from previous example) + a: Node[int] = Node() + b: Node[str] = Node() + +Alternatively, you can instantiate a specific concrete type, e.g.:: + + # (continued from previous example) + p = Node[int]() + q = Node[str]() + r = Node[int]('') # Error + s = Node[str](0) # Error + +Note that the runtime type (class) of ``p`` and ``q`` is still just ``Node`` +-- ``Node[int]`` and ``Node[str]`` are distinguishable class objects, but +the runtime class of the objects created by instantiating them doesn't +record the distinction. This behavior is called "type erasure"; it is +common practice in languages with generics (e.g. Java, TypeScript). + +Using generic classes (parameterized or not) to access attributes will result +in type check failure. Outside the class definition body, a class attribute +cannot be assigned, and can only be looked up by accessing it through a +class instance that does not have an instance attribute with the same name:: + + # (continued from previous example) + Node[int].x = 1 # Error + Node[int].x # Error + Node.x = 1 # Error + Node.x # Error + type(p).x # Error + p.x # Ok (evaluates to int) + Node[int]().x # Ok (evaluates to int) + p.x = 1 # Ok, but assigning to instance attribute + +Generic versions of abstract collections like ``Mapping`` or ``Sequence`` +and generic versions of built-in classes -- ``List``, ``Dict``, ``Set``, +and ``FrozenSet`` -- cannot be instantiated. However, concrete user-defined +subclasses thereof and generic versions of concrete collections can be +instantiated:: + + data = DefaultDict[int, bytes]() + +Note that one should not confuse static types and runtime classes. +The type is still erased in this case and the above expression is +just a shorthand for:: + + data: DefaultDict[int, bytes] = collections.defaultdict() + +It is not recommended to use the subscripted class (e.g. ``Node[int]``) +directly in an expression -- using a type alias (e.g. ``IntNode = Node[int]``) +instead is preferred. (First, creating the subscripted class, +e.g. ``Node[int]``, has a runtime cost. Second, using a type alias +is more readable.) + +.. _`generic-base-classes`: + +Arbitrary generic types as base classes +--------------------------------------- + +``Generic[T]`` is only valid as a base class -- it's not a proper type. +However, user-defined generic types such as ``LinkedList[T]`` from the +above example and built-in generic types and ABCs such as ``list[T]`` +and ``Iterable[T]`` are valid both as types and as base classes. For +example, we can define a subclass of ``dict`` that specializes type +arguments:: + + class Node: + ... + + class SymbolTable(dict[str, list[Node]]): + def push(self, name: str, node: Node) -> None: + self.setdefault(name, []).append(node) + + def pop(self, name: str) -> Node: + return self[name].pop() + + def lookup(self, name: str) -> Node | None: + nodes = self.get(name) + if nodes: + return nodes[-1] + return None + +``SymbolTable`` is a subclass of ``dict`` and a subtype of ``dict[str, +list[Node]]``. + +If a generic base class has a type variable as a type argument, this +makes the defined class generic. For example, we can define a generic +``LinkedList`` class that is iterable and a container:: + + from typing import TypeVar + from collections.abc import Iterable, Container + + T = TypeVar('T') + + class LinkedList(Iterable[T], Container[T]): + ... + +Now ``LinkedList[int]`` is a valid type. Note that we can use ``T`` +multiple times in the base class list, as long as we don't use the +same type variable ``T`` multiple times within ``Generic[...]``. + +Also consider the following example:: + + from typing import TypeVar + from collections.abc import Mapping + + T = TypeVar('T') + + class MyDict(Mapping[str, T]): + ... + +In this case ``MyDict`` has a single type parameter, ``T``. + +Type variables are applied to the defined class in the order in which +they first appear in any generic base classes:: + + from typing import Generic, TypeVar + + T1 = TypeVar('T1') + T2 = TypeVar('T2') + T3 = TypeVar('T3') + + class Parent1(Generic[T1, T2]): + ... + class Parent2(Generic[T1, T2]): + ... + class Child(Parent1[T1, T3], Parent2[T2, T3]): + ... + +That ``Child`` definition is equivalent to:: + + class Child(Parent1[T1, T3], Parent2[T2, T3], Generic[T1, T3, T2]): + ... + +A type checker should report an error when the type variable order is +inconsistent:: + + from typing import Generic, TypeVar + + T1 = TypeVar('T1') + T2 = TypeVar('T2') + T3 = TypeVar('T3') + + class Grandparent(Generic[T1, T2]): + ... + class Parent(Grandparent[T1, T2]): + ... + class Child(Parent[T1, T2], Grandparent[T2, T1]): # INVALID + ... + +Abstract generic types +---------------------- + +The metaclass used by ``Generic`` is a subclass of ``abc.ABCMeta``. +A generic class can be an ABC by including abstract methods +or properties, and generic classes can also have ABCs as base +classes without a metaclass conflict. + +.. _`typevar-bound`: + +Type variables with an upper bound +---------------------------------- + +A type variable may specify an upper bound using ``bound=`` (when using +the ``TypeVar`` constructor) or using ``: `` (when using the native +syntax for generics). The bound itself cannot be parameterized by type +variables. This means that an actual type substituted (explicitly or +implicitly) for the type variable must be :term:`assignable` to the bound. +Example:: + + from typing import TypeVar + from collections.abc import Sized + + ST = TypeVar('ST', bound=Sized) + + def longer(x: ST, y: ST) -> ST: + if len(x) > len(y): + return x + else: + return y + + longer([1], [1, 2]) # ok, return type list[int] + longer({1}, {1, 2}) # ok, return type set[int] + longer([1], {1, 2}) # ok, return type a supertype of list[int] and set[int] + +An upper bound cannot be combined with type constraints (as used in ``AnyStr``, +see the example earlier); type constraints cause the inferred type to be +*exactly* one of the constraint types, while an upper bound just requires that +the actual type is :term:`assignable` to the bound. + +.. _`variance`: + +Variance +-------- + +Consider a class ``Employee`` with a subclass ``Manager``. Now +suppose we have a function with an argument annotated with +``list[Employee]``. Should we be allowed to call this function with a +variable of type ``list[Manager]`` as its argument? Many people would +answer "yes, of course" without even considering the consequences. +But unless we know more about the function, a type checker should +reject such a call: the function might append an ``Employee`` instance +to the list, which would violate the variable's type in the caller. + +It turns out such an argument acts *contravariantly*, whereas the +intuitive answer (which is correct in case the function doesn't mutate +its argument!) requires the argument to act *covariantly*. A longer +introduction to these concepts can be found on `Wikipedia +`_ and in :pep:`483`; here we just show how to control +a type checker's behavior. + +By default generic types declared using the old ``TypeVar`` syntax are +considered *invariant* in all type variables, which means that e.g. +``list[Manager]`` is neither a supertype nor a subtype of ``list[Employee]``. + +See below for the behavior when using the built-in generic syntax in Python +3.12 and higher. + +To facilitate the declaration of container types where covariant or +contravariant type checking is acceptable, type variables accept keyword +arguments ``covariant=True`` or ``contravariant=True``. At most one of these +may be passed. Generic types defined with such variables are considered +covariant or contravariant in the corresponding variable. By convention, +it is recommended to use names ending in ``_co`` for type variables +defined with ``covariant=True`` and names ending in ``_contra`` for that +defined with ``contravariant=True``. + +A typical example involves defining an immutable (or read-only) +container class:: + + from typing import TypeVar, Generic + from collections.abc import Iterable, Iterator + + T_co = TypeVar('T_co', covariant=True) + + class ImmutableList(Generic[T_co]): + def __init__(self, items: Iterable[T_co]) -> None: ... + def __iter__(self) -> Iterator[T_co]: ... + ... + + class Employee: ... + + class Manager(Employee): ... + + def dump_employees(emps: ImmutableList[Employee]) -> None: + for emp in emps: + ... + + mgrs: ImmutableList[Manager] = ImmutableList([Manager()]) + dump_employees(mgrs) # OK + +The read-only collection classes in ``typing`` are all declared +covariant in their type variable (e.g. ``Mapping`` and ``Sequence``). The +mutable collection classes (e.g. ``MutableMapping`` and +``MutableSequence``) are declared invariant. The one example of +a contravariant type is the ``Generator`` type, which is contravariant +in the ``send()`` argument type (see below). + +Variance is meaningful only when a type variable is bound to a generic class. +If a type variable declared as covariant or contravariant is bound to a generic +function or type alias, type checkers may warn users about this. However, any +subsequent type analysis involving such functions or aliases should ignore the +declared variance:: + + T = TypeVar('T', covariant=True) + + class A(Generic[T]): # T is covariant in this context + ... + + def f(x: T) -> None: # Variance of T is meaningless in this context + ... + + Alias = list[T] | set[T] # Variance of T is meaningless in this context + +.. _`paramspec`: + +ParamSpec +--------- + +(Originally specified by :pep:`612`.) + +``ParamSpec`` Variables +^^^^^^^^^^^^^^^^^^^^^^^ + +Declaration +"""""""""""" + +In Python 3.12 and newer, a parameter specification variable can be introduced +inline by prefixing its name with ``**`` inside a type parameter list +(for a function, class, or type alias). + +.. code-block:: + + def decorator[**P](func: Callable[P, int]) -> Callable[P, str]: ... + + class CallbackWrapper[T, **P]: + callback: Callable[P, T] + +Prior to 3.12, the ``ParamSpec`` constructor can be used. + +.. code-block:: + + from typing import ParamSpec + P = ParamSpec("P") # Accepted + P = ParamSpec("WrongName") # Rejected because P =/= WrongName + +The runtime should accept ``bound``\ s and ``covariant`` and ``contravariant`` +arguments in the declaration just as ``typing.TypeVar`` does, but for now we +will defer the standardization of the semantics of those options to a later PEP. + +.. _`paramspec_valid_use_locations`: + +Valid use locations +""""""""""""""""""" + +Previously only a list of parameter arguments (``[A, B, C]``) or an ellipsis +(signifying "undefined parameters") were acceptable as the first "argument" to +``typing.Callable`` . We now augment that with two new options: a parameter +specification variable (``Callable[P, int]``\ ) or a concatenation on a +parameter specification variable (``Callable[Concatenate[int, P], int]``\ ). + +.. code-block:: + + callable ::= Callable "[" parameters_expression, type_expression "]" + + parameters_expression ::= + | "..." + | "[" [ type_expression ("," type_expression)* ] "]" + | parameter_specification_variable + | concatenate "[" + type_expression ("," type_expression)* "," + parameter_specification_variable + "]" + +where ``parameter_specification_variable`` is introduced either inline (using +``**`` in a type parameter list) or via ``typing.ParamSpec`` as shown above, +and ``concatenate`` is ``typing.Concatenate``. + +As before, ``parameters_expression``\ s by themselves are not acceptable in +places where a type is expected + +.. code-block:: + + def foo[**P](x: P) -> P: ... # Rejected + def foo[**P](x: Concatenate[int, P]) -> int: ... # Rejected + def foo[**P](x: list[P]) -> None: ... # Rejected + def foo[**P](x: Callable[[int, str], P]) -> None: ... # Rejected + + +User-Defined Generic Classes +"""""""""""""""""""""""""""" + +Just as defining a class with ``class C[T]: ...`` makes that class generic over +the type parameter ``T``, adding ``**P`` makes it generic over a parameter +specification. + +.. code-block:: + + from collections.abc import Callable + from typing import Concatenate + + class X[T, **P]: + f: Callable[P, int] + x: T + + def accept_params[**P](x: X[int, P]) -> str: ... # Accepted + def accept_concatenate[**P](x: X[int, Concatenate[int, P]]) -> str: ... # Accepted + def accept_concrete(x: X[int, [int, bool]]) -> str: ... # Accepted + def accept_unspecified(x: X[int, ...]) -> str: ... # Accepted + def reject_bad(x: X[int, int]) -> str: ... # Rejected + +By the rules defined above, spelling a concrete instance of a class generic +with respect to only a single ``ParamSpec`` would require unsightly double +brackets. For aesthetic purposes we allow these to be omitted. + +.. code-block:: + + class Z[**P]: + f: Callable[P, int] + + def accept_list(x: Z[[int, str, bool]]) -> str: ... # Accepted + def accept_flat(x: Z[int, str, bool]) -> str: ... # Equivalent + + # Both Z[[int, str, bool]] and Z[int, str, bool] express this: + class Z_instantiated: + f: Callable[[int, str, bool], int] + +Semantics +""""""""" + +The inference rules for the return type of a function invocation whose signature +contains a ``ParamSpec`` variable are analogous to those around +evaluating ones with ``TypeVar``\ s. + +.. code-block:: + + def changes_return_type_to_str[**P](x: Callable[P, int]) -> Callable[P, str]: ... + + def returns_int(a: str, b: bool) -> int: ... + + f = changes_return_type_to_str(returns_int) # f should have the type: + # (a: str, b: bool) -> str + + f("A", True) # Accepted + f(a="A", b=True) # Accepted + f("A", "A") # Rejected + + expects_str(f("A", True)) # Accepted + expects_int(f("A", True)) # Rejected + +Just as with traditional ``TypeVars``\ , a user may include the same +``ParamSpec`` multiple times in the arguments of the same function, +to indicate a dependency between multiple arguments. In these cases a type +checker may choose to solve to a common behavioral supertype (i.e. a set of +parameters for which all of the valid calls are valid in both of the subtypes), +but is not obligated to do so. + +.. code-block:: + + def foo[**P](x: Callable[P, int], y: Callable[P, int]) -> Callable[P, bool]: ... + + def x_y(x: int, y: str) -> int: ... + def y_x(y: int, x: str) -> int: ... + + foo(x_y, x_y) # Should return (x: int, y: str) -> bool + # (a callable with two positional-or-keyword parameters) + + foo(x_y, y_x) # Could return (a: int, b: str, /) -> bool + # (a callable with two positional-only parameters) + # This works because both callables have types that are + # behavioral subtypes of Callable[[int, str], int] + + + def keyword_only_x(*, x: int) -> int: ... + def keyword_only_y(*, y: int) -> int: ... + foo(keyword_only_x, keyword_only_y) # Rejected + +The constructors of user-defined classes generic on ``ParamSpec``\ s should be +evaluated in the same way. + +.. code-block:: + + class Y[U, **P]: + f: Callable[P, str] + prop: U + + def __init__(self, f: Callable[P, str], prop: U) -> None: + self.f = f + self.prop = prop + + def a(q: int) -> str: ... + + Y(a, 1) # Should resolve to Y[int, (q: int)] + Y(a, 1).f # Should resolve to (q: int) -> str + +The semantics of ``Concatenate[X, Y, P]`` are that it represents the parameters +represented by ``P`` with two positional-only parameters prepended. This means +that we can use it to represent higher order functions that add, remove or +transform a finite number of parameters of a callable. + +.. code-block:: + + def bar(x: int, *args: bool) -> int: ... + + def add[**P](x: Callable[P, int]) -> Callable[Concatenate[str, P], bool]: ... + + add(bar) # Should return (a: str, /, x: int, *args: bool) -> bool + + def remove[**P](x: Callable[Concatenate[int, P], int]) -> Callable[P, bool]: ... + + remove(bar) # Should return (*args: bool) -> bool + + def transform[**P]( + x: Callable[Concatenate[int, P], int] + ) -> Callable[Concatenate[str, P], bool]: ... + + transform(bar) # Should return (a: str, /, *args: bool) -> bool + +This also means that while any function that returns an ``R`` can satisfy +``typing.Callable[P, R]``, only functions that can be called positionally in +their first position with a ``X`` can satisfy +``typing.Callable[Concatenate[X, P], R]``. + +.. code-block:: + + def expects_int_first[**P](x: Callable[Concatenate[int, P], int]) -> None: ... + + @expects_int_first # Rejected + def one(x: str) -> int: ... + + @expects_int_first # Rejected + def two(*, x: int) -> int: ... + + @expects_int_first # Rejected + def three(**kwargs: int) -> int: ... + + @expects_int_first # Accepted + def four(*args: int) -> int: ... + +There are still some classes of decorators still not supported with these +features: + +* those that add/remove/change a **variable** number of parameters (for + example, ``functools.partial`` remains untypable even using ``ParamSpec``) +* those that add/remove/change keyword-only parameters. + +The components of a ``ParamSpec`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A ``ParamSpec`` captures both positional and keyword accessible +parameters, but there unfortunately is no object in the runtime that captures +both of these together. Instead, we are forced to separate them into ``*args`` +and ``**kwargs``\ , respectively. This means we need to be able to split apart +a single ``ParamSpec`` into these two components, and then bring +them back together into a call. To do this, we introduce ``P.args`` to +represent the tuple of positional arguments in a given call and +``P.kwargs`` to represent the corresponding ``Mapping`` of keywords to +values. + +Valid use locations +""""""""""""""""""" + +These "properties" can only be used as the annotated types for +``*args`` and ``**kwargs``\ , accessed from a ParamSpec already in scope. + +.. code-block:: + + def p_in_scope[**P](f: Callable[P, int]) -> None: + + def inner(*args: P.args, **kwargs: P.kwargs) -> None: # Accepted + pass + + def mixed_up(*args: P.kwargs, **kwargs: P.args) -> None: # Rejected + pass + + def misplaced(x: P.args) -> None: # Rejected + pass + + def p_not_in_scope(*args: P.args, **kwargs: P.kwargs) -> None: # Rejected + pass + + +Furthermore, because the default kind of parameter in Python (\ ``(x: int)``\ ) +may be addressed both positionally and through its name, two valid invocations +of a ``(*args: P.args, **kwargs: P.kwargs)`` function may give different +partitions of the same set of parameters. Therefore, we need to make sure that +these special types are only brought into the world together, and are used +together, so that our usage is valid for all possible partitions. + +.. code-block:: + + def p_in_scope[**P](f: Callable[P, int]) -> None: + + stored_args: P.args # Rejected + + stored_kwargs: P.kwargs # Rejected + + def just_args(*args: P.args) -> None: # Rejected + pass + + def just_kwargs(**kwargs: P.kwargs) -> None: # Rejected + pass + + +Semantics +""""""""" + +With those requirements met, we can now take advantage of the unique properties +afforded to us by this set up: + + +* Inside the function, ``args`` has the type ``P.args``\ , not + ``tuple[P.args, ...]`` as would be with a normal annotation + (and likewise with the ``**kwargs``\ ) + + * This special case is necessary to encapsulate the heterogeneous contents + of the ``args``/``kwargs`` of a given call, which cannot be expressed + by an indefinite tuple/dictionary type. + +* A function of type ``Callable[P, R]`` can be called with ``(*args, **kwargs)`` + if and only if ``args`` has the type ``P.args`` and ``kwargs`` has the type + ``P.kwargs``\ , and that those types both originated from the same function + declaration. +* A function declared as ``def inner(*args: P.args, **kwargs: P.kwargs) -> X`` + has type ``Callable[P, X]``. + +With these three properties, we now have the ability to fully type check +parameter preserving decorators. + +.. code-block:: + + def decorator[**P](f: Callable[P, int]) -> Callable[P, None]: + + def foo(*args: P.args, **kwargs: P.kwargs) -> None: + + f(*args, **kwargs) # Accepted, should resolve to int + + f(*kwargs, **args) # Rejected + + f(1, *args, **kwargs) # Rejected + + return foo # Accepted + +To extend this to include ``Concatenate``, we declare the following properties: + +* A function of type ``Callable[Concatenate[A, B, P], R]`` can only be + called with ``(a, b, *args, **kwargs)`` when ``args`` and ``kwargs`` are the + respective components of ``P``, ``a`` is of type ``A`` and ``b`` is of + type ``B``. +* A function declared as + ``def inner(a: A, b: B, *args: P.args, **kwargs: P.kwargs) -> R`` + has type ``Callable[Concatenate[A, B, P], R]``. Placing keyword-only + parameters between the ``*args`` and ``**kwargs`` is forbidden. + +.. code-block:: + + def add[**P](f: Callable[P, int]) -> Callable[Concatenate[str, P], None]: + + def foo(s: str, *args: P.args, **kwargs: P.kwargs) -> None: # Accepted + pass + + def bar(*args: P.args, s: str, **kwargs: P.kwargs) -> None: # Rejected + pass + + return foo # Accepted + + + def remove[**P](f: Callable[Concatenate[int, P], int]) -> Callable[P, None]: + + def foo(*args: P.args, **kwargs: P.kwargs) -> None: + f(1, *args, **kwargs) # Accepted + + f(*args, 1, **kwargs) # Rejected + + f(*args, **kwargs) # Rejected + + return foo + +Note that the names of the parameters preceding the ``ParamSpec`` +components are not mentioned in the resulting ``Concatenate``. This means that +these parameters can not be addressed via a named argument: + +.. code-block:: + + def outer[**P](f: Callable[P, None]) -> Callable[P, None]: + def foo(x: int, *args: P.args, **kwargs: P.kwargs) -> None: + f(*args, **kwargs) + + def bar(*args: P.args, **kwargs: P.kwargs) -> None: + foo(1, *args, **kwargs) # Accepted + foo(x=1, *args, **kwargs) # Rejected + + return bar + +.. _above: + +This is not an implementation convenience, but a soundness requirement. If we +were to allow that second calling style, then the following snippet would be +problematic. + +.. code-block:: + + @outer + def problem(*, x: object) -> None: + pass + + problem(x="uh-oh") + +Inside of ``bar``, we would get +``TypeError: foo() got multiple values for argument 'x'``. Requiring these +concatenated arguments to be addressed positionally avoids this kind of problem, +and simplifies the syntax for spelling these types. Note that this also why we +have to reject signatures of the form +``(*args: P.args, s: str, **kwargs: P.kwargs)``. + +If one of these prepended positional parameters contains a free ``ParamSpec``\ , +we consider that variable in scope for the purposes of extracting the components +of that ``ParamSpec``. That allows us to spell things like this: + +.. code-block:: + + def twice[**P](f: Callable[P, int], *args: P.args, **kwargs: P.kwargs) -> int: + return f(*args, **kwargs) + f(*args, **kwargs) + +The type of ``twice`` in the above example is +``Callable[Concatenate[Callable[P, int], P], int]``, where ``P`` is bound by the +outer ``Callable``. This has the following semantics: + +.. code-block:: + + def a_int_b_str(a: int, b: str) -> int: + return a + + twice(a_int_b_str, 1, "A") # Accepted + + twice(a_int_b_str, b="A", a=1) # Accepted + + twice(a_int_b_str, "A", 1) # Rejected + +.. _`typevartuple`: + +TypeVarTuple +------------ + +(Originally specified in :pep:`646`.) + +A ``TypeVarTuple`` serves as a placeholder not for a single type +but for a *tuple* of types. + +In addition, we introduce a new use for the star operator: to 'unpack' +``TypeVarTuple`` instances and tuple types such as ``tuple[int, +str]``. Unpacking a ``TypeVarTuple`` or tuple type is the typing +equivalent of unpacking a variable or a tuple of values. + +Type Variable Tuples +^^^^^^^^^^^^^^^^^^^^ + +In the same way that a normal type variable is a stand-in for a single +type such as ``int``, a type variable *tuple* is a stand-in for a *tuple* type such as +``tuple[int, str]``. + +In Python 3.12 and newer, type variable tuples can be introduced inline by prefixing +their name with ``*`` inside a type parameter list. + +:: + + class Array[*Ts]: + ... + + def pack[*Ts](*values: *Ts) -> tuple[*Ts]: + ... + +Prior to 3.12, the ``TypeVarTuple`` constructor can be used. + +:: + + from typing import TypeVarTuple + + Ts = TypeVarTuple('Ts') + + class Array(Generic[*Ts]): + ... + +Using Type Variable Tuples in Generic Classes +""""""""""""""""""""""""""""""""""""""""""""" + +Type variable tuples behave like a number of individual type variables packed in a +``tuple``. To understand this, consider the following example: + +:: + + from typing import NewType + + class Array[*Shape]: ... + + Height = NewType('Height', int) + Width = NewType('Width', int) + x: Array[Height, Width] = Array() + +The ``Shape`` type variable tuple here behaves like ``tuple[T1, T2]``, +where ``T1`` and ``T2`` are type variables. To use these type variables +as type parameters of ``Array``, we must *unpack* the type variable tuple using +the star operator: ``*Shape``. The signature of ``Array`` then behaves +as if we had simply written ``class Array[T1, T2]: ...``. + +In contrast to ``class Array[T1, T2]``, however, ``class Array[*Shape]`` allows +us to parameterize the class with an *arbitrary* number of type parameters. +That is, in addition to being able to define rank-2 arrays such as +``Array[Height, Width]``, we could also define rank-3 arrays, rank-4 arrays, +and so on: + +:: + + Time = NewType('Time', int) + Batch = NewType('Batch', int) + y: Array[Batch, Height, Width] = Array() + z: Array[Time, Batch, Height, Width] = Array() + +Using Type Variable Tuples in Functions +""""""""""""""""""""""""""""""""""""""" + +Type variable tuples can be used anywhere a normal ``TypeVar`` can. +This includes class definitions, as shown above, as well as function +signatures and variable annotations: + +:: + + class Array[*Shape]: + + def __init__(self, shape: tuple[*Shape]): + self._shape: tuple[*Shape] = shape + + def get_shape(self) -> tuple[*Shape]: + return self._shape + + shape = (Height(480), Width(640)) + x: Array[Height, Width] = Array(shape) + y = abs(x) # Inferred type is Array[Height, Width] + z = x + x # ... is Array[Height, Width] + +Type Variable Tuples Must Always be Unpacked +"""""""""""""""""""""""""""""""""""""""""""" + +Note that in the previous example, the ``shape`` argument to ``__init__`` +was annotated as ``tuple[*Shape]``. Why is this necessary - if ``Shape`` +behaves like ``tuple[T1, T2, ...]``, couldn't we have annotated the ``shape`` +argument as ``Shape`` directly? + +This is, in fact, deliberately not possible: type variable tuples must +*always* be used unpacked (that is, prefixed by the star operator). This is +for two reasons: + +* To avoid potential confusion about whether to use a type variable tuple + in a packed or unpacked form ("Hmm, should I write '``-> Shape``', + or '``-> tuple[Shape]``', or '``-> tuple[*Shape]``'...?") +* To improve readability: the star also functions as an explicit visual + indicator that the type variable tuple is not a normal type variable. + +Variance, Type Constraints and Type Bounds: Not Supported +""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +``TypeVarTuple`` does not currently support specification of: + +* Variance (e.g. ``TypeVar('T', covariant=True)``) +* Type constraints (``TypeVar('T', int, float)``) +* Type bounds (``TypeVar('T', bound=ParentClass)``) + +Type Variable Tuple Equality +"""""""""""""""""""""""""""" + +If the same ``TypeVarTuple`` instance is used in multiple places in a signature +or class, type checkers may use the same rules for solving the variables as they +would for normal ``TypeVar``\ s. The exact inference behavior is not specified. + +:: + + def foo(arg1: tuple[*Ts], arg2: tuple[*Ts]) -> tuple[*Ts]: ... + + a = (0,) + b = ('0',) + reveal_type(foo(a, b)) # May be e.g. tuple[object], tuple[int | str], tuple[Literal[0, "0"]] + +All usages of the ``TypeVarTuple`` must match in length, however:: + + foo((1,), (2, 3)) # Error: Expected a tuple of length 1 for arg2, but got a tuple of length 2. + + +Multiple Type Variable Tuples: Not Allowed +"""""""""""""""""""""""""""""""""""""""""" + +Only a single type variable tuple may appear in a type parameter list: + +:: + + class Array[*Ts1, *Ts2]: ... # Error + +The reason is that multiple type variable tuples make it ambiguous +which parameters get bound to which type variable tuple: :: + + x: Array[int, str, bool] # Ts1 = ???, Ts2 = ??? + +Type Concatenation +^^^^^^^^^^^^^^^^^^ + +Type variable tuples don't have to be alone; normal types can be +prefixed and/or suffixed: + +:: + + Batch = NewType('Batch', int) + Channels = NewType('Channels', int) + + def add_batch_axis[*Shape](x: Array[*Shape]) -> Array[Batch, *Shape]: ... + def del_batch_axis[*Shape](x: Array[Batch, *Shape]) -> Array[*Shape]: ... + def add_batch_channels[*Shape]( + x: Array[*Shape] + ) -> Array[Batch, *Shape, Channels]: ... + + a: Array[Height, Width] + b = add_batch_axis(a) # Inferred type is Array[Batch, Height, Width] + c = del_batch_axis(b) # Array[Height, Width] + d = add_batch_channels(a) # Array[Batch, Height, Width, Channels] + + +Normal ``TypeVar`` instances can also be prefixed and/or suffixed: + +:: + + def prefix_tuple[T, *Ts]( + x: T, + y: tuple[*Ts] + ) -> tuple[T, *Ts]: ... + + z = prefix_tuple(x=0, y=(True, 'a')) + # Inferred type of z is tuple[int, bool, str] + +Unpacking Tuple Types +^^^^^^^^^^^^^^^^^^^^^ + +We mentioned that a ``TypeVarTuple`` stands for a tuple of types. +Since we can unpack a ``TypeVarTuple``, for consistency, we also +allow unpacking a tuple type. As we shall see, this also enables a +number of interesting features. + + +Unpacking Unbounded Tuple Types +""""""""""""""""""""""""""""""" + +Unpacking unbounded tuples is useful in function signatures where +we don't care about the exact elements and don't want to define an +unnecessary ``TypeVarTuple``: + +:: + + def process_batch_channels( + x: Array[Batch, *tuple[Any, ...], Channels] + ) -> None: + ... + + + x: Array[Batch, Height, Width, Channels] + process_batch_channels(x) # OK + y: Array[Batch, Channels] + process_batch_channels(y) # OK + z: Array[Batch] + process_batch_channels(z) # Error: Expected Channels. + + +We can also pass a ``*tuple[Any, ...]`` wherever a ``*Ts`` is +expected. This is useful when we have particularly dynamic code and +cannot state the precise number of dimensions or the precise types for +each of the dimensions. In those cases, we can smoothly fall back to +an unbounded tuple: + +:: + + y: Array[*tuple[Any, ...]] = read_from_file() + + def expect_variadic_array( + x: Array[Batch, *Shape] + ) -> None: ... + + expect_variadic_array(y) # OK + + def expect_precise_array( + x: Array[Batch, Height, Width, Channels] + ) -> None: ... + + expect_precise_array(y) # OK + +``Array[*tuple[Any, ...]]`` stands for an array with an arbitrary +number of dimensions of type ``Any``. This means that, in the call to +``expect_variadic_array``, ``Batch`` is bound to ``Any`` and ``Shape`` +is bound to ``tuple[Any, ...]``. In the call to +``expect_precise_array``, the variables ``Batch``, ``Height``, +``Width``, and ``Channels`` are all bound to ``Any``. + +This allows users to handle dynamic code gracefully while still +explicitly marking the code as unsafe (by using ``y: Array[*tuple[Any, +...]]``). Otherwise, users would face noisy errors from the type +checker every time they tried to use the variable ``y``, which would +hinder them when migrating a legacy code base to use ``TypeVarTuple``. + +.. _args_as_typevartuple: + +``*args`` as a Type Variable Tuple +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:ref:`this specification ` states that when a +type annotation is provided for ``*args``, every argument +must be of the type annotated. That is, if we specify ``*args`` to be type ``int``, +then *all* arguments must be of type ``int``. This limits our ability to specify +the type signatures of functions that take heterogeneous argument types. + +If ``*args`` is annotated as a type variable tuple, however, the types of the +individual arguments become the types in the type variable tuple: + +:: + + Ts = TypeVarTuple('Ts') + + def args_to_tuple(*args: *Ts) -> tuple[*Ts]: ... + + args_to_tuple(1, 'a') # Inferred type is tuple[int, str] + +In the above example, ``Ts`` is bound to ``tuple[int, str]``. If no +arguments are passed, the type variable tuple behaves like an empty +tuple, ``tuple[()]``. + +As usual, we can unpack any tuple types. For example, by using a type +variable tuple inside a tuple of other types, we can refer to prefixes +or suffixes of the variadic argument list. For example: + +:: + + # os.execle takes arguments 'path, arg0, arg1, ..., env' + def execle(path: str, *args: *tuple[*Ts, Env]) -> None: ... + +Note that this is different to + +:: + + def execle(path: str, *args: *Ts, env: Env) -> None: ... + +as this would make ``env`` a keyword-only argument. + +Using an unpacked unbounded tuple is equivalent to the +:ref:`behavior ` +of ``*args: int``, which accepts zero or +more values of type ``int``: + +:: + + def foo(*args: *tuple[int, ...]) -> None: ... + + # equivalent to: + def foo(*args: int) -> None: ... + +Unpacking tuple types also allows more precise types for heterogeneous +``*args``. The following function expects an ``int`` at the beginning, +zero or more ``str`` values, and a ``str`` at the end: + +:: + + def foo(*args: *tuple[int, *tuple[str, ...], str]) -> None: ... + +For completeness, we mention that unpacking a concrete tuple allows us +to specify ``*args`` of a fixed number of heterogeneous types: + +:: + + def foo(*args: *tuple[int, str]) -> None: ... + + foo(1, "hello") # OK + +Note that, in keeping with the rule that type variable tuples must always +be used unpacked, annotating ``*args`` as being a plain type variable tuple +instance is *not* allowed: + +:: + + def foo(*args: Ts): ... # NOT valid + +``*args`` is the only case where an argument can be annotated as ``*Ts`` directly; +other arguments should use ``*Ts`` to parameterize something else, e.g. ``tuple[*Ts]``. +If ``*args`` itself is annotated as ``tuple[*Ts]``, the old behavior still applies: +all arguments must be a ``tuple`` parameterized with the same types. + +:: + + def foo(*args: tuple[*Ts]): ... + + foo((0,), (1,)) # Valid + foo((0,), (1, 2)) # Error + foo((0,), ('1',)) # Error + +Finally, note that a type variable tuple may *not* be used as the type of +``**kwargs``. (We do not yet know of a use case for this feature, so we prefer +to leave the ground fresh for a potential future PEP.) + +:: + + # NOT valid + def foo(**kwargs: *Ts): ... + +Type Variable Tuples with ``Callable`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Type variable tuples can also be used in the arguments section of a +``Callable``: + +:: + + class Process: + def __init__( + self, + target: Callable[[*Ts], None], + args: tuple[*Ts], + ) -> None: ... + + def func(arg1: int, arg2: str) -> None: ... + + Process(target=func, args=(0, 'foo')) # Valid + Process(target=func, args=('foo', 0)) # Error + +Other types and normal type variables can also be prefixed/suffixed +to the type variable tuple: + +:: + + T = TypeVar('T') + + def foo(f: Callable[[int, *Ts, T], tuple[T, *Ts]]): ... + +The behavior of a Callable containing an unpacked item, whether the +item is a ``TypeVarTuple`` or a tuple type, is to treat the elements +as if they were the type for ``*args``. So, ``Callable[[*Ts], None]`` +is treated as the type of the function: + +:: + + def foo(*args: *Ts) -> None: ... + +``Callable[[int, *Ts, T], tuple[T, *Ts]]`` is treated as the type of +the function: + +:: + + def foo(*args: *tuple[int, *Ts, T]) -> tuple[T, *Ts]: ... + +Behavior when Type Parameters are not Specified +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When a generic class parameterized by a type variable tuple is used without +any type parameters and the TypeVarTuple has no default value, it behaves as +if the type variable tuple was substituted with ``tuple[Any, ...]``: + +:: + + def takes_any_array(arr: Array): ... + + # equivalent to: + def takes_any_array(arr: Array[*tuple[Any, ...]]): ... + + x: Array[Height, Width] + takes_any_array(x) # Valid + y: Array[Time, Height, Width] + takes_any_array(y) # Also valid + +This enables gradual typing: existing functions accepting, for example, +a plain TensorFlow ``Tensor`` will still be valid even if ``Tensor`` is made +generic and calling code passes a ``Tensor[Height, Width]``. + +This also works in the opposite direction: + +:: + + def takes_specific_array(arr: Array[Height, Width]): ... + + z: Array + # equivalent to Array[*tuple[Any, ...]] + + takes_specific_array(z) + +(For details, see the section on `Unpacking Unbounded Tuple Types`_.) + +This way, even if libraries are updated to use types like ``Array[Height, Width]``, +users of those libraries won't be forced to also apply type annotations to +all of their code; users still have a choice about what parts of their code +to type and which parts to not. + +Aliases +^^^^^^^ + +Generic aliases can be created using a type variable tuple in +a similar way to regular type variables: + +:: + + type IntTuple[*Ts] = tuple[int, *Ts] + type NamedArray[*Ts] = tuple[str, Array[*Ts]] + + IntTuple[float, bool] # Equivalent to tuple[int, float, bool] + NamedArray[Height] # Equivalent to tuple[str, Array[Height]] + +As this example shows, all type parameters passed to the alias are +bound to the type variable tuple. + +This allows us to define convenience aliases for arrays of a fixed shape +or datatype: + +:: + + class Array[DType, *Shape]: + ... + + # E.g. Float32Array[Height, Width, Channels] + type Float32Array[*Shape] = Array[np.float32, *Shape] + + # E.g. Array1D[np.uint8] + type Array1D[DType] = Array[DType, Any] + +If an explicitly empty type parameter list is given, the type variable +tuple in the alias is set empty: + +:: + + IntTuple[()] # Equivalent to tuple[int] + NamedArray[()] # Equivalent to tuple[str, Array[()]] + +If the type parameter list is omitted entirely, the unspecified type +variable tuples are treated as ``tuple[Any, ...]`` (similar to +`Behavior when Type Parameters are not Specified`_): + +:: + + def takes_float_array_of_any_shape(x: Float32Array): ... + x: Float32Array[Height, Width] = Array() + takes_float_array_of_any_shape(x) # Valid + + def takes_float_array_with_specific_shape( + y: Float32Array[Height, Width] + ): ... + y: Float32Array = Array() + takes_float_array_with_specific_shape(y) # Valid + +Normal ``TypeVar`` instances can also be used in such aliases: + +:: + + type Foo[T, *Ts] = tuple[T, *Ts] + + # T bound to str, Ts to tuple[int] + Foo[str, int] + # T bound to float, Ts to tuple[()] + Foo[float] + # T bound to Any, Ts to an tuple[Any, ...] + Foo + + +Substitution in Aliases +^^^^^^^^^^^^^^^^^^^^^^^ + +In the previous section, we only discussed simple usage of generic aliases +in which the type arguments were just simple types. However, a number of +more exotic constructions are also possible. + + +Type Arguments can be Variadic +"""""""""""""""""""""""""""""" + +First, type arguments to generic aliases can be variadic. For example, a +``TypeVarTuple`` can be used as a type argument: + +:: + + type IntTuple[*Ts1] = tuple[int, *Ts1] + type IntFloatTuple[*Ts2] = IntTuple[float, *Ts2] # Valid + +Here, ``*Ts1`` in the ``IntTuple`` alias is bound to ``tuple[float, *Ts2]``, +resulting in an alias ``IntFloatTuple`` equivalent to +``tuple[int, float, *Ts2]``. + +Unpacked arbitrary-length tuples can also be used as type arguments, with +similar effects: + +:: + + IntFloatsTuple = IntTuple[*tuple[float, ...]] # Valid + +Here, ``*Ts1`` is bound to ``*tuple[float, ...]``, resulting in +``IntFloatsTuple`` being equivalent to ``tuple[int, *tuple[float, ...]]``: a tuple +consisting of an ``int`` then zero or more ``float``\s. + + +Variadic Arguments Require Variadic Aliases +""""""""""""""""""""""""""""""""""""""""""" + +Variadic type arguments can only be used with generic aliases that are +themselves variadic. For example: + +:: + + type IntTuple[T] = tuple[int, T] + + IntTuple[str] # Valid + IntTuple[*Ts] # NOT valid + IntTuple[*tuple[float, ...]] # NOT valid + +Here, ``IntTuple`` is a *non*-variadic generic alias that takes exactly one +type argument. Hence, it cannot accept ``*Ts`` or ``*tuple[float, ...]`` as type +arguments, because they represent an arbitrary number of types. + + +Aliases with Both TypeVars and TypeVarTuples +"""""""""""""""""""""""""""""""""""""""""""" + +In `Aliases`_, we briefly mentioned that aliases can be generic in both +``TypeVar``\s and ``TypeVarTuple``\s: + +:: + + T = TypeVar('T') + Foo = tuple[T, *Ts] + + Foo[str, int] # T bound to str, Ts to tuple[int] + Foo[str, int, float] # T bound to str, Ts to tuple[int, float] + +In accordance with `Multiple Type Variable Tuples: Not Allowed`_, at most one +``TypeVarTuple`` may appear in the type parameters to an alias. However, a +``TypeVarTuple`` can be combined with an arbitrary number of ``TypeVar``\s, +both before and after: + +:: + + T1 = TypeVar('T1') + T2 = TypeVar('T2') + T3 = TypeVar('T3') + + tuple[*Ts, T1, T2] # Valid + tuple[T1, T2, *Ts] # Valid + tuple[T1, *Ts, T2, T3] # Valid + +In order to substitute these type variables with supplied type arguments, +any type variables at the beginning or end of the type parameter list first +consume type arguments, and then any remaining type arguments are bound +to the ``TypeVarTuple``: + +:: + + Shrubbery = tuple[*Ts, T1, T2] + + Shrubbery[str, bool] # T2=bool, T1=str, Ts=tuple[()] + Shrubbery[str, bool, float] # T2=float, T1=bool, Ts=tuple[str] + Shrubbery[str, bool, float, int] # T2=int, T1=float, Ts=tuple[str, bool] + + Ptang = tuple[T1, *Ts, T2, T3] + + Ptang[str, bool, float] # T1=str, T3=float, T2=bool, Ts=tuple[()] + Ptang[str, bool, float, int] # T1=str, T3=int, T2=float, Ts=tuple[bool] + +Note that the minimum number of type arguments in such cases is set by +the number of ``TypeVar``\s: + +:: + + Shrubbery[int] # Not valid; Shrubbery needs at least two type arguments + + +Splitting Arbitrary-Length Tuples +""""""""""""""""""""""""""""""""" + +A final complication occurs when an unpacked arbitrary-length tuple is used +as a type argument to an alias consisting of both ``TypeVar``\s and a +``TypeVarTuple``: + +:: + + Elderberries = tuple[*Ts, T1] + Hamster = Elderberries[*tuple[int, ...]] # valid + +In such cases, the arbitrary-length tuple is split between the ``TypeVar``\s +and the ``TypeVarTuple``. We assume the arbitrary-length tuple contains +at least as many items as there are ``TypeVar``\s, such that individual +instances of the inner type - here ``int`` - are bound to any ``TypeVar``\s +present. The 'rest' of the arbitrary-length tuple - here ``*tuple[int, ...]``, +since a tuple of arbitrary length minus two items is still arbitrary-length - +is bound to the ``TypeVarTuple``. + +Here, therefore, ``Hamster`` is equivalent to ``tuple[*tuple[int, ...], int]``: +a tuple consisting of zero or more ``int``\s, then a final ``int``. + +Of course, such splitting only occurs if necessary. For example, if we instead +did: + +:: + + Elderberries[*tuple[int, ...], str] + +Then splitting would not occur; ``T1`` would be bound to ``str``, and +``Ts`` to ``*tuple[int, ...]``. + +In particularly awkward cases, a ``TypeVarTuple`` may consume both a type +*and* a part of an arbitrary-length tuple type: + +:: + + Elderberries[str, *tuple[int, ...]] + +Here, ``T1`` is bound to ``int``, and ``Ts`` is bound to +``tuple[str, *tuple[int, ...]]``. This expression is therefore equivalent to +``tuple[str, *tuple[int, ...], int]``: a tuple consisting of a ``str``, then +zero or more ``int``\s, ending with an ``int``. + + +TypeVarTuples Cannot be Split +""""""""""""""""""""""""""""" + +Finally, although any arbitrary-length tuples in the type argument list can be +split between the type variables and the type variable tuple, the same is not +true of ``TypeVarTuple``\s in the argument list: + +:: + + type Camelot[T, *Ts] = tuple[T, *Ts] + Camelot[int, *tuple[str, ...]] # Valid + Camelot[*tuple[str, ...]] # NOT valid + +This is not possible because, unlike in the case of an unpacked arbitrary-length +tuple, there is no way to 'peer inside' the ``TypeVarTuple`` to see what its +individual types are. + + +Overloads for Accessing Individual Types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +For situations where we require access to each individual type in the type variable tuple, +overloads can be used with individual ``TypeVar`` instances in place of the type variable tuple: + +:: + + Axis1 = TypeVar('Axis1') + Axis2 = TypeVar('Axis2') + Axis3 = TypeVar('Axis3') + + class Array[*Shape]: + + @overload + def transpose( + self: Array[Axis1, Axis2] + ) -> Array[Axis2, Axis1]: ... + + @overload + def transpose( + self: Array[Axis1, Axis2, Axis3] + ) -> Array[Axis3, Axis2, Axis1]: ... + +(For array shape operations in particular, having to specify +overloads for each possible rank is, of course, a rather cumbersome +solution. However, it's the best we can do without additional type +manipulation mechanisms.) + +.. _`type_parameter_defaults`: + +Defaults for Type Parameters +---------------------------- + +(Originally specified in :pep:`696`.) + +Default values can be provided for a TypeVar, ParamSpec, or TypeVarTuple. + +Default Ordering and Subscription Rules +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The order for defaults should follow the standard function parameter +rules, so a type parameter with no ``default`` cannot follow one with +a ``default`` value. Doing so may raise a ``TypeError`` at runtime, +and a type checker should flag this as an error. + +:: + + DefaultStrT = TypeVar("DefaultStrT", default=str) + DefaultIntT = TypeVar("DefaultIntT", default=int) + DefaultBoolT = TypeVar("DefaultBoolT", default=bool) + T = TypeVar("T") + T2 = TypeVar("T2") + + class NonDefaultFollowsDefault(Generic[DefaultStrT, T]): ... # Invalid: non-default TypeVars cannot follow ones with defaults + + + class NoNonDefaults(Generic[DefaultStrT, DefaultIntT]): ... + + ( + NoNonDefaults == + NoNonDefaults[str] == + NoNonDefaults[str, int] + ) # All valid + + + class OneDefault(Generic[T, DefaultBoolT]): ... + + OneDefault[float] == OneDefault[float, bool] # Valid + reveal_type(OneDefault) # type is type[OneDefault[T, DefaultBoolT = bool]] + reveal_type(OneDefault[float]()) # type is OneDefault[float, bool] + + + class AllTheDefaults(Generic[T1, T2, DefaultStrT, DefaultIntT, DefaultBoolT]): ... + + reveal_type(AllTheDefaults) # type is type[AllTheDefaults[T1, T2, DefaultStrT = str, DefaultIntT = int, DefaultBoolT = bool]] + reveal_type(AllTheDefaults[int, complex]()) # type is AllTheDefaults[int, complex, str, int, bool] + AllTheDefaults[int] # Invalid: expected 2 arguments to AllTheDefaults + ( + AllTheDefaults[int, complex] == + AllTheDefaults[int, complex, str] == + AllTheDefaults[int, complex, str, int] == + AllTheDefaults[int, complex, str, int, bool] + ) # All valid + +With the new Python 3.12 syntax for generics (introduced by :pep:`695`), this can +be enforced at compile time:: + + type Alias[DefaultT = int, T] = tuple[DefaultT, T] # SyntaxError: non-default TypeVars cannot follow ones with defaults + + def generic_func[DefaultT = int, T](x: DefaultT, y: T) -> None: ... # SyntaxError: non-default TypeVars cannot follow ones with defaults + + class GenericClass[DefaultT = int, T]: ... # SyntaxError: non-default TypeVars cannot follow ones with defaults + +``ParamSpec`` Defaults +^^^^^^^^^^^^^^^^^^^^^^ + +``ParamSpec`` defaults are defined using the same syntax as +``TypeVar`` \ s but use a ``list`` of types or an ellipsis +literal "``...``" or another in-scope ``ParamSpec`` (see `Scoping Rules`_). + +:: + + class Foo[**P = [str, int]]: ... + + reveal_type(Foo) # type is type[Foo[str, int]] + reveal_type(Foo()) # type is Foo[str, int] + reveal_type(Foo[[bool, bool]]()) # type is Foo[bool, bool] + +``TypeVarTuple`` Defaults +^^^^^^^^^^^^^^^^^^^^^^^^^ + +``TypeVarTuple`` defaults are defined using the same syntax as +``TypeVar`` \ s, but instead of a single type, they use an unpacked tuple of +types or an unpacked, in-scope ``TypeVarTuple`` (see `Scoping Rules`_). + +:: + + class Foo[*Ts = *tuple[str, int]]: ... + + reveal_type(Foo) # type is type[Foo[str, int]] + reveal_type(Foo()) # type is Foo[str, int] + reveal_type(Foo[int, bool]()) # type is Foo[int, bool] + +Using Another Type Parameter as ``default`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This allows for a value to be used again when the type parameter to a +generic is missing but another type parameter is specified. + +To use another type parameter as a default the ``default`` and the +type parameter must be the same type (a ``TypeVar``'s default must be +a ``TypeVar``, etc.). + +:: + + StartT = TypeVar("StartT", default=int) + StopT = TypeVar("StopT", default=StartT) + StepT = TypeVar("StepT", default=int | None) + + class slice(Generic[StartT, StopT, StepT]): ... + + reveal_type(slice) # type is type[slice[StartT = int, StopT = StartT, StepT = int | None]] + reveal_type(slice()) # type is slice[int, int, int | None] + reveal_type(slice[str]()) # type is slice[str, str, int | None] + reveal_type(slice[str, bool, timedelta]()) # type is slice[str, bool, timedelta] + + T2 = TypeVar("T2", default=DefaultStrT) + + class Foo(Generic[DefaultStrT, T2]): + def __init__(self, a: DefaultStrT, b: T2) -> None: ... + + reveal_type(Foo(1, "")) # type is Foo[int, str] + Foo[int](1, "") # Invalid: Foo[int, str] cannot be assigned to self: Foo[int, int] in Foo.__init__ + Foo[int]("", 1) # Invalid: Foo[str, int] cannot be assigned to self: Foo[int, int] in Foo.__init__ + +When using a type parameter as the default to another type parameter, the +following rules apply, where ``T1`` is the default for ``T2``. + +Scoping Rules +^^^^^^^^^^^^^ + +``T1`` must be used before ``T2`` in the parameter list of the generic. + +:: + + T2 = TypeVar("T2", default=T1) + + class Foo(Generic[T1, T2]): ... # Valid + + StartT = TypeVar("StartT", default="StopT") # Swapped defaults around from previous example + StopT = TypeVar("StopT", default=int) + class slice(Generic[StartT, StopT, StepT]): ... + # ^^^^^^ Invalid: ordering does not allow StopT to be bound + +Using a type parameter from an outer scope as a default is not supported. + +:: + + class Foo(Generic[T1]): + class Bar(Generic[T2]): ... # Type Error + +Bound Rules +^^^^^^^^^^^ + +``T1``'s bound must be :term:`assignable` to ``T2``'s bound. + +:: + + T1 = TypeVar("T1", bound=int) + TypeVar("Ok", default=T1, bound=float) # Valid + TypeVar("AlsoOk", default=T1, bound=int) # Valid + TypeVar("Invalid", default=T1, bound=str) # Invalid: int is not assignable to str + +Constraint Rules +^^^^^^^^^^^^^^^^ + +The constraints of ``T2`` must be a superset of the constraints of ``T1``. + +:: + + T1 = TypeVar("T1", bound=int) + TypeVar("Invalid", float, str, default=T1) # Invalid: upper bound int is incompatible with constraints float or str + + T1 = TypeVar("T1", int, str) + TypeVar("AlsoOk", int, str, bool, default=T1) # Valid + TypeVar("AlsoInvalid", bool, complex, default=T1) # Invalid: {bool, complex} is not a superset of {int, str} + + +Type Parameters as Parameters to Generics +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Type parameters are valid as parameters to generics inside of a +``default`` when the first parameter is in scope as determined by the +`previous section `_. + +:: + + T = TypeVar("T") + ListDefaultT = TypeVar("ListDefaultT", default=list[T]) + + class Bar(Generic[T, ListDefaultT]): + def __init__(self, x: T, y: ListDefaultT): ... + + reveal_type(Bar) # type is type[Bar[T, ListDefaultT = list[T]]] + reveal_type(Bar[int]) # type is type[Bar[int, list[int]]] + reveal_type(Bar[int](0, [])) # type is Bar[int, list[int]] + reveal_type(Bar[int, list[str]](0, [])) # type is Bar[int, list[str]] + reveal_type(Bar[int, str](0, "")) # type is Bar[int, str] + +Specialization Rules +^^^^^^^^^^^^^^^^^^^^ + +Generic Type Aliases +"""""""""""""""""""" + +A generic type alias can be further subscripted following normal subscription +rules. If a type parameter has a default that hasn't been overridden, it should +be treated like it was substituted into the type alias. + +:: + + class SomethingWithNoDefaults(Generic[T, T2]): ... + + MyAlias: TypeAlias = SomethingWithNoDefaults[int, DefaultStrT] # Valid + reveal_type(MyAlias) # type is type[SomethingWithNoDefaults[int, DefaultStrT]] + reveal_type(MyAlias[bool]()) # type is SomethingWithNoDefaults[int, bool] + + MyAlias[bool, int] # Invalid: too many arguments passed to MyAlias + +Subclassing +""""""""""" + +Generic classes with type parameters that have defaults behave similarly +to generic type aliases. That is, subclasses can be further subscripted following +normal subscription rules, non-overridden defaults should be substituted. + +:: + + class SubclassMe(Generic[T, DefaultStrT]): + x: DefaultStrT + + class Bar(SubclassMe[int, DefaultStrT]): ... + reveal_type(Bar) # type is type[Bar[DefaultStrT = str]] + reveal_type(Bar()) # type is Bar[str] + reveal_type(Bar[bool]()) # type is Bar[bool] + + class Foo(SubclassMe[float]): ... + + reveal_type(Foo().x) # type is str + + Foo[str] # Invalid: Foo cannot be further subscripted + + class Baz(Generic[DefaultIntT, DefaultStrT]): ... + + class Spam(Baz): ... + reveal_type(Spam()) # type is + +Using ``bound`` and ``default`` +""""""""""""""""""""""""""""""" + +If both ``bound`` and ``default`` are passed, ``default`` must be +:term:`assignable` to ``bound``. If not, the type checker should generate an +error. + +:: + + TypeVar("Ok", bound=float, default=int) # Valid + TypeVar("Invalid", bound=str, default=int) # Invalid: the bound and default are incompatible + +Constraints +""""""""""" + +For constrained ``TypeVar``\ s, the default needs to be one of the +constraints. A type checker should generate an error even if it is a +subtype of one of the constraints. + +:: + + TypeVar("Ok", float, str, default=float) # Valid + TypeVar("Invalid", float, str, default=int) # Invalid: expected one of float or str got int + +Function Defaults +""""""""""""""""" + +In generic functions, type checkers may use a type parameter's default when the +type parameter cannot be solved to anything. We leave the semantics of this +usage unspecified, as ensuring the ``default`` is returned in every code path +where the type parameter can go unsolved may be too hard to implement. Type +checkers are free to either disallow this case or experiment with implementing +support. + +:: + + T = TypeVar('T', default=int) + def func(x: int | set[T]) -> T: ... + reveal_type(func(0)) # a type checker may reveal T's default of int here + +Defaults following ``TypeVarTuple`` +""""""""""""""""""""""""""""""""""" + +A ``TypeVar`` that immediately follows a ``TypeVarTuple`` is not allowed +to have a default, because it would be ambiguous whether a type argument +should be bound to the ``TypeVarTuple`` or the defaulted ``TypeVar``. + +:: + + class Foo[*Ts, T = bool]: ... # Type checker error + + # Could be reasonably interpreted as either Ts = (int, str, float), T = bool + # or Ts = (int, str), T = float + Foo[int, str, float] + +It is allowed to have a ``ParamSpec`` with a default following a +``TypeVarTuple`` with a default, as there can be no ambiguity between a type argument +for the ``ParamSpec`` and one for the ``TypeVarTuple``. + +:: + + class Foo[*Ts = *tuple[int, str], **P = [float, bool]]: ... # Valid + + Foo[int, str] # Ts = (int, str), P = [float, bool] + Foo[int, str, [bytes]] # Ts = (int, str), P = [bytes] + +Binding rules +""""""""""""" + +Type parameter defaults should be bound by attribute access +(including call and subscript). + +:: + + class Foo[T = int]: + def meth(self) -> Self: + return self + + reveal_type(Foo.meth) # type is (self: Foo[int]) -> Foo[int] + + +.. _`self`: + +``Self`` +-------- + +(Originally specified in :pep:`673`.) + +Use in Method Signatures +^^^^^^^^^^^^^^^^^^^^^^^^ + +``Self`` used in the signature of a method is treated as if it were a +``TypeVar`` bound to the class. + +:: + + from typing import Self + + class Shape: + def set_scale(self, scale: float) -> Self: + self.scale = scale + return self + +is treated equivalently to: + +:: + + from typing import TypeVar + + SelfShape = TypeVar("SelfShape", bound="Shape") + + class Shape: + def set_scale(self: SelfShape, scale: float) -> SelfShape: + self.scale = scale + return self + +This works the same for a subclass too: + +:: + + class Circle(Shape): + def set_radius(self, radius: float) -> Self: + self.radius = radius + return self + +which is treated equivalently to: + +:: + + SelfCircle = TypeVar("SelfCircle", bound="Circle") + + class Circle(Shape): + def set_radius(self: SelfCircle, radius: float) -> SelfCircle: + self.radius = radius + return self + +One implementation strategy is to simply desugar the former to the latter in a +preprocessing step. If a method uses ``Self`` in its signature, the type of +``self`` within a method will be ``Self``. In other cases, the type of +``self`` will remain the enclosing class. + + +Use in Classmethod Signatures +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``Self`` type annotation is also useful for classmethods that return +an instance of the class that they operate on. For example, ``from_config`` in +the following snippet builds a ``Shape`` object from a given ``config``. + +:: + + class Shape: + def __init__(self, scale: float) -> None: ... + + @classmethod + def from_config(cls, config: dict[str, float]) -> Shape: + return cls(config["scale"]) + + +However, this means that ``Circle.from_config(...)`` is inferred to return a +value of type ``Shape``, when in fact it should be ``Circle``: + +:: + + class Circle(Shape): + def circumference(self) -> float: ... + + shape = Shape.from_config({"scale": 7.0}) + # => Shape + + circle = Circle.from_config({"scale": 7.0}) + # => *Shape*, not Circle + + circle.circumference() + # Error: `Shape` has no attribute `circumference` + + +The current workaround for this is unintuitive and error-prone: + +:: + + Self = TypeVar("Self", bound="Shape") + + class Shape: + @classmethod + def from_config( + cls: type[Self], config: dict[str, float] + ) -> Self: + return cls(config["scale"]) + +Instead, ``Self`` can be used directly: + +:: + + from typing import Self + + class Shape: + @classmethod + def from_config(cls, config: dict[str, float]) -> Self: + return cls(config["scale"]) + +This avoids the complicated ``cls: type[Self]`` annotation and the ``TypeVar`` +declaration with a ``bound``. Once again, the latter code behaves equivalently +to the former code. + +Use in Parameter Types +^^^^^^^^^^^^^^^^^^^^^^ + +Another use for ``Self`` is to annotate parameters that expect instances of +the current class: + +:: + + Self = TypeVar("Self", bound="Shape") + + class Shape: + def difference(self: Self, other: Self) -> float: ... + + def apply(self: Self, f: Callable[[Self], None]) -> None: ... + +``Self`` can be used directly to achieve the same behavior: + +:: + + from typing import Self + + class Shape: + def difference(self, other: Self) -> float: ... + + def apply(self, f: Callable[[Self], None]) -> None: ... + +Note that specifying ``self: Self`` is harmless, so some users may find it +more readable to write the above as: + +:: + + class Shape: + def difference(self: Self, other: Self) -> float: ... + +Use in Attribute Annotations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Another use for ``Self`` is to annotate attributes. One example is where we +have a ``LinkedList`` whose elements must be :term:`assignable` to the current +class. + +:: + + from dataclasses import dataclass + from typing import Generic, TypeVar + + T = TypeVar("T") + + @dataclass + class LinkedList(Generic[T]): + value: T + next: LinkedList[T] | None = None + + # OK + LinkedList[int](value=1, next=LinkedList[int](value=2)) + # Not OK + LinkedList[int](value=1, next=LinkedList[str](value="hello")) + + +However, annotating the ``next`` attribute as ``LinkedList[T]`` allows invalid +constructions with subclasses: + +:: + + @dataclass + class OrdinalLinkedList(LinkedList[int]): + def ordinal_value(self) -> str: + return as_ordinal(self.value) + + # Should not be OK because LinkedList[int] is not assignable to + # OrdinalLinkedList, but the type checker allows it. + xs = OrdinalLinkedList(value=1, next=LinkedList[int](value=2)) + + if xs.next: + print(xs.next.ordinal_value()) # Runtime Error. + + +This constraint can be expressed using ``next: Self | None``: + +:: + + from typing import Self + + @dataclass + class LinkedList(Generic[T]): + value: T + next: Self | None = None + + @dataclass + class OrdinalLinkedList(LinkedList[int]): + def ordinal_value(self) -> str: + return as_ordinal(self.value) + + xs = OrdinalLinkedList(value=1, next=LinkedList[int](value=2)) + # Type error: Expected OrdinalLinkedList, got LinkedList[int]. + + if xs.next is not None: + xs.next = OrdinalLinkedList(value=3, next=None) # OK + xs.next = LinkedList[int](value=3, next=None) # Not OK + + + +The code above is semantically equivalent to treating each attribute +containing a ``Self`` type as a ``property`` that returns that type: + +:: + + from dataclasses import dataclass + from typing import Any, Generic, TypeVar + + T = TypeVar("T") + Self = TypeVar("Self", bound="LinkedList") + + + class LinkedList(Generic[T]): + value: T + + @property + def next(self: Self) -> Self | None: + return self._next + + @next.setter + def next(self: Self, next: Self | None) -> None: + self._next = next + + class OrdinalLinkedList(LinkedList[int]): + def ordinal_value(self) -> str: + return str(self.value) + +Use in Generic Classes +^^^^^^^^^^^^^^^^^^^^^^ + +``Self`` can also be used in generic class methods: + +:: + + class Container(Generic[T]): + value: T + def set_value(self, value: T) -> Self: ... + + +This is equivalent to writing: + +:: + + Self = TypeVar("Self", bound="Container[Any]") + + class Container(Generic[T]): + value: T + def set_value(self: Self, value: T) -> Self: ... + + +The behavior is to preserve the type argument of the object on which the +method was called. When called on an object with concrete type +``Container[int]``, ``Self`` is bound to ``Container[int]``. When called with +an object of generic type ``Container[T]``, ``Self`` is bound to +``Container[T]``: + +:: + + def object_with_concrete_type() -> None: + int_container: Container[int] + str_container: Container[str] + reveal_type(int_container.set_value(42)) # => Container[int] + reveal_type(str_container.set_value("hello")) # => Container[str] + + def object_with_generic_type( + container: Container[T], value: T, + ) -> Container[T]: + return container.set_value(value) # => Container[T] + + +The PEP doesn’t specify the exact type of ``self.value`` within the method +``set_value``. Some type checkers may choose to implement ``Self`` types using +class-local type variables with ``Self = TypeVar(“Self”, +bound=Container[T])``, which will infer a precise type ``T``. However, given +that class-local type variables are not a standardized type system feature, it +is also acceptable to infer ``Any`` for ``self.value``. We leave this up to +the type checker. + +Note that we reject using ``Self`` with type arguments, such as ``Self[int]``. +This is because it creates ambiguity about the type of the ``self`` parameter +and introduces unnecessary complexity: + +:: + + class Container(Generic[T]): + def foo( + self, other: Self[int], other2: Self, + ) -> Self[str]: # Rejected + ... + +In such cases, we recommend using an explicit type for ``self``: + +:: + + class Container(Generic[T]): + def foo( + self: Container[T], + other: Container[int], + other2: Container[T] + ) -> Container[str]: ... + + +Use in Protocols +^^^^^^^^^^^^^^^^ + +``Self`` is valid within Protocols, similar to its use in classes: + +:: + + from typing import Protocol, Self + + class ShapeProtocol(Protocol): + scale: float + + def set_scale(self, scale: float) -> Self: + self.scale = scale + return self + +is treated equivalently to: + +:: + + from typing import TypeVar + + SelfShape = TypeVar("SelfShape", bound="ShapeProtocol") + + class ShapeProtocol(Protocol): + scale: float + + def set_scale(self: SelfShape, scale: float) -> SelfShape: + self.scale = scale + return self + + +See :pep:`PEP 544 +<544#self-types-in-protocols>` for +details on the behavior of TypeVars bound to protocols. + +Checking a class for assignability to a protocol: If a protocol uses ``Self`` +in methods or attribute annotations, then a class ``Foo`` is :term:`assignable` +to the protocol if its corresponding methods and attribute annotations use +either ``Self`` or ``Foo`` or any of ``Foo``’s subclasses. See the examples +below: + +:: + + from typing import Protocol + + class ShapeProtocol(Protocol): + def set_scale(self, scale: float) -> Self: ... + + class ReturnSelf: + scale: float = 1.0 + + def set_scale(self, scale: float) -> Self: + self.scale = scale + return self + + class ReturnConcreteShape: + scale: float = 1.0 + + def set_scale(self, scale: float) -> ReturnConcreteShape: + self.scale = scale + return self + + class BadReturnType: + scale: float = 1.0 + + def set_scale(self, scale: float) -> int: + self.scale = scale + return 42 + + class ReturnDifferentClass: + scale: float = 1.0 + + def set_scale(self, scale: float) -> ReturnConcreteShape: + return ReturnConcreteShape(...) + + def accepts_shape(shape: ShapeProtocol) -> None: + y = shape.set_scale(0.5) + reveal_type(y) + + def main() -> None: + return_self_shape: ReturnSelf + return_concrete_shape: ReturnConcreteShape + bad_return_type: BadReturnType + return_different_class: ReturnDifferentClass + + accepts_shape(return_self_shape) # OK + accepts_shape(return_concrete_shape) # OK + accepts_shape(bad_return_type) # Not OK + # Not OK because it returns a non-subclass. + accepts_shape(return_different_class) + + +Valid Locations for ``Self`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A ``Self`` annotation is only valid in class contexts, and will always refer +to the encapsulating class. In contexts involving nested classes, ``Self`` +will always refer to the innermost class. + +The following uses of ``Self`` are accepted: + +:: + + class ReturnsSelf: + def foo(self) -> Self: ... # Accepted + + @classmethod + def bar(cls) -> Self: # Accepted + return cls() + + def __new__(cls, value: int) -> Self: ... # Accepted + + def explicitly_use_self(self: Self) -> Self: ... # Accepted + + # Accepted (Self can be nested within other types) + def returns_list(self) -> list[Self]: ... + + # Accepted (Self can be nested within other types) + @classmethod + def return_cls(cls) -> type[Self]: + return cls + + class Child(ReturnsSelf): + # Accepted (we can override a method that uses Self annotations) + def foo(self) -> Self: ... + + class TakesSelf: + def foo(self, other: Self) -> bool: ... # Accepted + + class Recursive: + # Accepted (treated as an @property returning ``Self | None``) + next: Self | None + + class CallableAttribute: + def foo(self) -> int: ... + + # Accepted (treated as an @property returning the Callable type) + bar: Callable[[Self], int] = foo + + class HasNestedFunction: + x: int = 42 + + def foo(self) -> None: + + # Accepted (Self is bound to HasNestedFunction). + def nested(z: int, inner_self: Self) -> Self: + print(z) + print(inner_self.x) + return inner_self + + nested(42, self) # OK + + + class Outer: + class Inner: + def foo(self) -> Self: ... # Accepted (Self is bound to Inner) + + +The following uses of ``Self`` are rejected. + +:: + + def foo(bar: Self) -> Self: ... # Rejected (not within a class) + + bar: Self # Rejected (not within a class) + + class Foo: + # Rejected (Self is treated as unknown). + def has_existing_self_annotation(self: T) -> Self: ... + + class Foo: + def return_concrete_type(self) -> Self: + return Foo() # Rejected (see FooChild below for rationale) + + class FooChild(Foo): + child_value: int = 42 + + def child_method(self) -> None: + # At runtime, this would be Foo, not FooChild. + y = self.return_concrete_type() + + y.child_value + # Runtime error: Foo has no attribute child_value + + class Bar(Generic[T]): + def bar(self) -> T: ... + + class Baz(Bar[Self]): ... # Rejected + +We reject type aliases containing ``Self``. Supporting ``Self`` +outside class definitions can require a lot of special-handling in +type checkers. Given that it also goes against the rest of the PEP to +use ``Self`` outside a class definition, we believe the added +convenience of aliases is not worth it: + +:: + + TupleSelf = Tuple[Self, Self] # Rejected + + class Alias: + def return_tuple(self) -> TupleSelf: # Rejected + return (self, self) + +Note that we reject ``Self`` in staticmethods. ``Self`` does not add much +value since there is no ``self`` or ``cls`` to return. The only possible use +cases would be to return a parameter itself or some element from a container +passed in as a parameter. These don’t seem worth the additional complexity. + +:: + + class Base: + @staticmethod + def make() -> Self: # Rejected + ... + + @staticmethod + def return_parameter(foo: Self) -> Self: # Rejected + ... + +Likewise, we reject ``Self`` in metaclasses. ``Self`` consistently refers to the +same type (that of ``self``). But in metaclasses, it would have to refer to +different types in different method signatures. For example, in ``__mul__``, +``Self`` in the return type would refer to the implementing class +``Foo``, not the enclosing class ``MyMetaclass``. But, in ``__new__``, ``Self`` +in the return type would refer to the enclosing class ``MyMetaclass``. To +avoid confusion, we reject this edge case. + +:: + + class MyMetaclass(type): + def __new__(cls, *args: Any) -> Self: # Rejected + return super().__new__(cls, *args) + + def __mul__(cls, count: int) -> list[Self]: # Rejected + return [cls()] * count + + class Foo(metaclass=MyMetaclass): ... + +.. _`variance-inference`: + +Variance Inference +------------------ + +(Originally specified by :pep:`695`.) + +The introduction of explicit syntax for generic classes in Python 3.12 +eliminates the need for variance to be specified for type +parameters. Instead, type checkers will infer the variance of type parameters +based on their usage within a class. Type parameters are inferred to be +invariant, covariant, or contravariant depending on how they are used. + +Python type checkers already include the ability to determine the variance of +type parameters for the purpose of validating variance within a generic +protocol class. This capability can be used for all classes (whether or not +they are protocols) to calculate the variance of each type parameter. + +The algorithm for computing the variance of a type parameter is as follows. + +For each type parameter in a generic class: + +1. If the type parameter is variadic (``TypeVarTuple``) or a parameter +specification (``ParamSpec``), it is always considered invariant. No further +inference is needed. + +2. If the type parameter comes from a traditional ``TypeVar`` declaration and +is not specified as ``infer_variance`` (see below), its variance is specified +by the ``TypeVar`` constructor call. No further inference is needed. + +3. Create two specialized versions of the class. We'll refer to these as +``upper`` and ``lower`` specializations. In both of these specializations, +replace all type parameters other than the one being inferred by a dummy type +instance (a concrete anonymous class that is assumed to meet the bounds or +constraints of the type parameter). In the ``upper`` specialized class, +specialize the target type parameter with an ``object`` instance. This +specialization ignores the type parameter's upper bound or constraints. In the +``lower`` specialized class, specialize the target type parameter with itself +(i.e. the corresponding type argument is the type parameter itself). + +4. Determine whether ``lower`` can be assigned to ``upper`` using normal +assignability rules. If so, the target type parameter is covariant. If not, +determine whether ``upper`` can be assigned to ``lower``. If so, the target +type parameter is contravariant. If neither of these combinations are +assignable, the target type parameter is invariant. + +Here is an example. + +:: + + class ClassA[T1, T2, T3](list[T1]): + def method1(self, a: T2) -> None: + ... + + def method2(self) -> T3: + ... + +To determine the variance of ``T1``, we specialize ``ClassA`` as follows: + +:: + + upper = ClassA[object, Dummy, Dummy] + lower = ClassA[T1, Dummy, Dummy] + +We find that ``upper`` is not assignable to ``lower``. Likewise, ``lower`` is +not assignable to ``upper``, so we conclude that ``T1`` is invariant. + +To determine the variance of ``T2``, we specialize ``ClassA`` as follows: + +:: + + upper = ClassA[Dummy, object, Dummy] + lower = ClassA[Dummy, T2, Dummy] + +Since ``upper`` is assignable to ``lower``, ``T2`` is contravariant. + +To determine the variance of ``T3``, we specialize ``ClassA`` as follows: + +:: + + upper = ClassA[Dummy, Dummy, object] + lower = ClassA[Dummy, Dummy, T3] + +Since ``lower`` is assignable to ``upper``, ``T3`` is covariant. + + +Auto Variance For TypeVar +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The existing ``TypeVar`` class constructor accepts keyword parameters named +``covariant`` and ``contravariant``. If both of these are ``False``, the +type variable is assumed to be invariant. PEP 695 adds another keyword +parameter named ``infer_variance`` indicating that a type checker should use +inference to determine whether the type variable is invariant, covariant or +contravariant. A corresponding instance variable ``__infer_variance__`` can be +accessed at runtime to determine whether the variance is inferred. Type +variables that are implicitly allocated using the new syntax will always +have ``__infer_variance__`` set to ``True``. + +A generic class that uses the traditional syntax may include combinations of +type variables with explicit and inferred variance. + +:: + + T1 = TypeVar("T1", infer_variance=True) # Inferred variance + T2 = TypeVar("T2") # Invariant + T3 = TypeVar("T3", covariant=True) # Covariant + + # A type checker should infer the variance for T1 but use the + # specified variance for T2 and T3. + class ClassA(Generic[T1, T2, T3]): ... + + +Compatibility with Traditional TypeVars +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The existing mechanism for allocating ``TypeVar``, ``TypeVarTuple``, and +``ParamSpec`` is retained for backward compatibility. However, these +"traditional" type variables should not be combined with type parameters +allocated using the new syntax. Such a combination should be flagged as +an error by type checkers. This is necessary because the type parameter +order is ambiguous. + +It is OK to combine traditional type variables with new-style type parameters +if the class, function, or type alias does not use the new syntax. The +new-style type parameters must come from an outer scope in this case. + +:: + + K = TypeVar("K") + + class ClassA[V](dict[K, V]): ... # Type checker error + + class ClassB[K, V](dict[K, V]): ... # OK + + class ClassC[V]: + # The use of K and V for "method1" is OK because it uses the + # "traditional" generic function mechanism where type parameters + # are implicit. In this case V comes from an outer scope (ClassC) + # and K is introduced implicitly as a type parameter for "method1". + def method1(self, a: V, b: K) -> V | K: ... + + # The use of M and K are not allowed for "method2". A type checker + # should generate an error in this case because this method uses the + # new syntax for type parameters, and all type parameters associated + # with the method must be explicitly declared. In this case, ``K`` + # is not declared by "method2", nor is it supplied by a new-style + # type parameter defined in an outer scope. + def method2[M](self, a: M, b: K) -> M | K: ... diff --git a/docs/spec/glossary.rst b/docs/spec/glossary.rst new file mode 100644 index 000000000..340dcbd4a --- /dev/null +++ b/docs/spec/glossary.rst @@ -0,0 +1,229 @@ +.. _`glossary`: + +Glossary +======== + +This section defines a few terms that may be used elsewhere in the specification. + +.. glossary:: + + annotation expression + An expression that is valid to use within an annotation. This is usually a + :term:`type expression`, sometimes with additional :term:`type qualifiers `. + See :ref:`"Type and annotation expression" ` for details. + + assignable + If a type ``B`` is "assignable to" a type ``A``, a type checker should + not error on the assignment ``x: A = b``, where ``b`` is some expression + whose type is ``B``. Similarly for function calls and returns: ``f(b)`` + where ``def f(x: A): ...`` and ``return b`` inside ``def f(...) -> A: + ...`` are both valid (not type errors) if and only if ``B`` is assignable + to ``A``. In this case ``A`` is "assignable from" ``B``. For :term:`fully + static types `, "assignable to" is equivalent to + ":term:`subtype` of" and "assignable from" is equivalent to + ":term:`supertype` of". For :term:`gradual types `, a type + ``B`` is assignable to a type ``A`` if there exist fully static + :term:`materializations ` ``A'`` and ``B'`` of ``A`` and + ``B``, respectively, such that ``B'`` is a subtype of ``A'``. See + :ref:`type-system-concepts`. + + closed + A :ref:`TypedDict ` type is closed if it may not contain any + additional :term:`items ` beyond those specified in the TypedDict definition. + A closed TypedDict can be created using the ``closed=True`` argument to + :py:func:`typing.TypedDict`, or equivalently by setting ``extra_items=Never``. + Compare :term:`extra items` and :term:`open`. + + consistent + Two :term:`fully static types ` are "consistent with" + each other if they are :term:`equivalent`. Two gradual types are + "consistent with" each other if they could :term:`materialize` to the + same type. See :ref:`type-system-concepts`. If two types are consistent, + they are both :term:`assignable` to and from each other. + + consistent subtype + "Consistent subtype" is synonymous with ":term:`assignable` to" (and + "consistent supertype" is synonymous with "assignable from"). See + :ref:`type-system-concepts`. + + distribution + The packaged file which is used to publish and distribute + a release. (:pep:`426`) + + equivalent + Two :term:`fully static types ` ``A`` and ``B`` are + equivalent if ``A`` is a :term:`subtype` of ``B`` and ``B`` is a + :term:`subtype` of ``A``. This implies that ``A`` and ``B`` represent the + same set of possible runtime objects. Two gradual types ``A`` and ``B`` + are equivalent if all :term:`materializations ` of ``A`` are + also materializations of ``B``, and all materializations of ``B`` are + also materializations of ``A``. + + extra items + A :ref:`TypedDict ` type with extra items may contain arbitrary + additional key-value pairs beyond those specified in the TypedDict definition, but those + values must be of the type specified by the ``extra_items=`` argument to the definition. + A TypedDict with extra items can be created using the ``extra_items=`` + argument to :py:func:`typing.TypedDict`. Extra items may or may not be + :term:`read-only`. Compare :term:`closed` and :term:`open`. + + fully static type + A type is "fully static" if it does not contain any :term:`gradual form`. + A fully static type represents a set of possible runtime values. Fully + static types participate in the :term:`subtype` relation. See + :ref:`type-system-concepts`. + + gradual form + A gradual form is an element of a :term:`type expression` which makes the type it is + part of not a :term:`fully static type`, but rather a representation of a + set of possible static types. See :ref:`type-system-concepts`. The + primary gradual form is :ref:`Any`. The ellipsis (``...``) is a gradual + form in some, but not all, contexts. It is a gradual form when used in a + :ref:`Callable` type, and when used in ``tuple[Any, ...]`` (but not in + other :ref:`tuple ` types). + + gradual type + All types in the Python type system are "gradual". A gradual type may be + a :term:`fully static type`, or it may be :ref:`Any`, or a type that + contains ``Any`` or another :term:`gradual form`. A gradual type does not + necessarily represent a single set of possible runtime values; instead it + can represent a set of possible static types (a set of possible sets of + possible runtime values). Gradual types which are not fully static do not + participate in the :term:`subtype` relation, but they do participate in + :term:`consistency ` and :term:`assignability `. + They can be :term:`materialized ` to a more static, or fully static, + type. See :ref:`type-system-concepts`. + + inhabit + A value is said to inhabit a type if it is a member of the set of values + represented by that type. For example, the value ``42`` inhabits the type + ``int``, and the value ``"hello"`` inhabits the type ``str``. + + inline + Inline type annotations are annotations that are included in the + runtime code using :pep:`526` and + :pep:`3107` syntax (the filename ends in ``.py``). + + item + In the context of a :ref:`TypedDict `, an item consists of a name + (the dictionary key) and a type (representing the type that values corresponding to the key must have). + Items may be :term:`required` or :term:`non-required`, and may be :term:`read-only` or writable. + + materialize + A :term:`gradual type` can be materialized to a more static type + (possibly a :term:`fully static type`) by replacing :ref:`Any` with any + other type, or by replacing the `...` in a :ref:`Callable` type with a + list of types, or by replacing ``tuple[Any, ...]`` with a specific-length + tuple type. This materialization relation is key to defining + :term:`assignability ` for gradual types. See + :ref:`type-system-concepts`. + + module + A file containing Python runtime code or stubbed type information. + + narrow + A :term:`fully static type` ``B`` is narrower than a fully static type + ``A`` if ``B`` is a :term:`subtype` of ``A`` and ``B`` is not + :term:`equivalent` to ``A``. This means that ``B`` represents a proper + subset of the possible objects represented by ``A``. "Type narrowing" is + when a type checker infers that a name or expression must have a narrower + type at some locations in control flow, due to an assignment or a runtime + check of its value. + + nominal + A nominal type (e.g. a class name) represents the set of values whose + ``__class__`` is that type, or any of its subclasses, transitively. In + contrast, see :term:`structural` types. + + non-required + If an :term:`item` in a :ref:`TypedDict ` is non-required, it may or + may not be present on an object of that TypedDict type, but if it is present + it must be of the type specified by the TypedDict definition. + Items can be marked as non-required using the :py:data:`typing.NotRequired` qualifier + or the ``total=False`` argument to :py:func:`typing.TypedDict`. Compare :term:`required`. + + open + A :ref:`TypedDict ` type is open if it may contain arbitrary + additional :term:`items ` beyond those specified in the TypedDict definition. + This is the default behavior for TypedDicts that do not use the ``closed=True`` + or ``extra_items=`` arguments to :py:func:`typing.TypedDict`. + Open TypedDicts behave similarly to TypedDicts with :term:`extra items` of type + ``ReadOnly[object]``, but differ in some behaviors; see the TypedDict specification + chapter for details. + Compare :term:`extra items` and :term:`closed`. + + package + A directory or directories that namespace Python modules. + (Note the distinction between packages and :term:`distributions `. + While most distributions are named after the one package they install, some + distributions install multiple packages.) + + read-only + A read-only :term:`item` in a :ref:`TypedDict ` may not be modified. + Attempts to delete or assign to that item + should be reported as type errors by a type checker. Read-only items are created + using the :py:data:`typing.ReadOnly` qualifier. + + required + If an :term:`item` in a :ref:`TypedDict ` is required, it must be present + in any object of that TypedDict type. Items are + required by default, but items can also be explicitly marked as required using + the :py:data:`typing.Required` qualifier. Compare :term:`non-required`. + + special form + A special form is an object that has a special meaning within the type system, + comparable to a keyword in the language grammar. Examples include ``Any``, + ``Generic``, ``Literal``, and ``TypedDict``. Special forms can often but not always be used + within :ref:`type expressions `. Special forms can usually + be imported from the :py:mod:`typing` module or equivalently from ``typing_extensions``, + but some special forms are placed in other modules. + + structural + A structural type (see e.g. :ref:`Protocols`, :ref:`TypedDict`) defines a + set of values not by their ``__class__``, but by their properties (e.g. + attributes, methods, dictionary key/value types). :ref:`Callable` types + are also structural; a callable type is a subtype of another callable + type based on their signatures, not a subclass relationship. In contrast, + see :term:`nominal` types. + + stub + A file containing only type information, empty of runtime code + (the filename ends in ``.pyi``). See :ref:`stub-files`. + + subtype + A :term:`fully static type` ``B`` is a subtype of a fully static type + ``A`` if and only if the set of possible runtime values represented by + ``B`` is a subset of the set of possible runtime values represented by + ``A``. For :term:`nominal` types (classes), subtyping is defined by + inheritance. For :term:`structural` types, subtyping is defined by a + shared set of attributes/methods or keys. Subtype is the inverse of + :term:`supertype`. A type that is not fully static is not a subtype or + supertype of any other type, but via :term:`materialization + ` can be :term:`assignable` to another type. See + :ref:`type-system-concepts`. + + supertype + A :term:`fully static type` ``A`` is a supertype of a fully static type + ``B`` if and only if the set of possible runtime values represented by + ``A`` is a superset of the set of possible runtime values represented by + ``B``. Supertype is the inverse of :term:`subtype`. See + :ref:`type-system-concepts`. + + type expression + An expression that represents a type. The type system requires the use of type + expressions within :term:`annotation expression` and also in several other contexts. + See :ref:`"Type and annotation expression" ` for details. + + type qualifier + A type qualifier is a :term:`special form` that qualifies a :term:`type expression` to + form an :term:`annotation expression`. For example, the type qualifier :ref:`Final ` + can be used around a type to indicate that the annotated value may not be overridden or modified. + This term is also used for other special forms that modify a type, but using a different + syntactic context, such as the :ref:`@final ` decorator. + + wide + A :term:`fully static type` ``A`` is wider than a fully static type ``B`` + if and only if ``B`` is a :term:`subtype` of ``A`` and ``B`` is not + :term:`equivalent` to ``A``. This means that ``A`` represents a proper + superset of the possible values represented by ``B``. See also + ":term:`narrow`". diff --git a/docs/spec/historical.rst b/docs/spec/historical.rst new file mode 100644 index 000000000..1c209a19b --- /dev/null +++ b/docs/spec/historical.rst @@ -0,0 +1,306 @@ +.. _historical: + +Historical and deprecated features +================================== + +Over the course of the development of the Python type system, several +changes were made to the Python grammar and standard library to make it +easier to use the type system. This specification aims to use the more +modern syntax in all examples, but type checkers should generally support +the older alternatives and treat them as equivalent. + +This section lists all of these cases. + +.. _`type-comments`: + +Type comments +------------- + +No first-class syntax support for explicitly marking variables as being +of a specific type existed when the type system was first designed. +To help with type inference in +complex cases, a comment of the following format may be used:: + + x = [] # type: list[Employee] + x, y, z = [], [], [] # type: list[int], list[int], list[str] + x, y, z = [], [], [] # type: (list[int], list[int], list[str]) + a, b, *c = range(5) # type: float, float, list[float] + x = [1, 2] # type: list[int] + +Type comments should be put on the last line of the statement that +contains the variable definition. + +These should be treated as equivalent to annotating the variables +using :pep:`526` variable annotations:: + + x: list[Employee] = [] + x: list[int] + y: list[int] + z: list[str] + x, y, z = [], [], [] + a: float + b: float + c: list[float] + a, b, *c = range(5) + x: list[int] = [1, 2] + +Type comments can also be placed on +``with`` statements and ``for`` statements, right after the colon. + +Examples of type comments on ``with`` and ``for`` statements:: + + with frobnicate() as foo: # type: int + # Here foo is an int + ... + + for x, y in points: # type: float, float + # Here x and y are floats + ... + +In stubs it may be useful to declare the existence of a variable +without giving it an initial value. This can be done using :pep:`526` +variable annotation syntax:: + + from typing import IO + + stream: IO[str] + +The above syntax is acceptable in stubs for all versions of Python. +However, in non-stub code for versions of Python 3.5 and earlier +there is a special case:: + + from typing import IO + + stream = None # type: IO[str] + +Type checkers should not complain about this (despite the value +``None`` not matching the given type), nor should they change the +inferred type to ``... | None``. The +assumption here is that other code will ensure that the variable is +given a value of the proper type, and all uses can assume that the +variable has the given type. + +.. _`type-comments-function`: + +Type comments on function definitions +------------------------------------- + +Some tools may want to support type annotations in code that must be +compatible with Python 2.7. For this purpose function annotations can be placed in +a ``# type:`` comment. Such a comment must be placed immediately +following the function header (before the docstring). An example: the +following Python 3 code:: + + def embezzle(self, account: str, funds: int = 1000000, *fake_receipts: str) -> None: + """Embezzle funds from account using fake receipts.""" + + +is equivalent to the following:: + + def embezzle(self, account, funds=1000000, *fake_receipts): + # type: (str, int, *str) -> None + """Embezzle funds from account using fake receipts.""" + + +Note that for methods, no type is needed for ``self``. + +For an argument-less method it would look like this:: + + def load_cache(self): + # type: () -> bool + + +Sometimes you want to specify the return type for a function or method +without (yet) specifying the argument types. To support this +explicitly, the argument list may be replaced with an ellipsis. +Example:: + + def send_email(address, sender, cc, bcc, subject, body): + # type: (...) -> bool + """Send an email message. Return True if successful.""" + + +Sometimes you have a long list of parameters and specifying their +types in a single ``# type:`` comment would be awkward. To this end +you may list the arguments one per line and add a ``# type:`` comment +per line after an argument's associated comma, if any. +To specify the return type use the ellipsis syntax. Specifying the return +type is not mandatory and not every argument needs to be given a type. +A line with a ``# type:`` comment should contain exactly one argument. +The type comment for the last argument (if any) should precede the close +parenthesis. Example:: + + def send_email(address, # type: Union[str, List[str]] + sender, # type: str + cc, # type: Optional[List[str]] + bcc, # type: Optional[List[str]] + subject='', + body=None # type: List[str] + ): + # type: (...) -> bool + """Send an email message. Return True if successful.""" + + +Notes: + +- Tools that support this syntax should support it regardless of the + Python version being checked. This is necessary in order to support + code that straddles Python 2 and Python 3. + +- It is not allowed for an argument or return value to have both + a type annotation and a type comment. + +- When using the short form (e.g. ``# type: (str, int) -> None``) + every argument must be accounted for, except the first argument of + instance and class methods (those are usually omitted, but it's + allowed to include them). + +- The return type is mandatory for the short form. If in Python 3 you + would omit some argument or the return type, the Python 2 notation + should use ``Any``. + +- When using the short form, for ``*args`` and ``**kwds``, put 1 or 2 + stars in front of the corresponding type annotation. (As with + Python 3 annotations, the annotation here denotes the type of the + individual argument values, not of the tuple/dict that you receive + as the special argument value ``args`` or ``kwds``.) + +- Like other type comments, any names used in the annotations must be + imported or defined by the module containing the annotation. + +- When using the short form, the entire annotation must be one line. + +- The short form may also occur on the same line as the close + parenthesis, e.g.:: + + def add(a, b): # type: (int, int) -> int + return a + b + +- Misplaced type comments will be flagged as errors by a type checker. + If necessary, such comments could be commented twice. For example:: + + def f(): + '''Docstring''' + # type: () -> None # Error! + + def g(): + '''Docstring''' + # # type: () -> None # This is OK + +When checking Python 2.7 code, type checkers should treat the ``int`` and +``long`` types as equivalent. For parameters typed as ``Text``, arguments of +type ``str`` as well as ``unicode`` should be acceptable. + +.. _`pos-only-double-underscore`: + +Positional-only parameters +-------------------------- + +Some functions are designed to take their arguments only positionally, +and expect their callers never to use the argument's name to provide +that argument by keyword. Before Python 3.8 (:pep:`570`), Python did +not provide a way to declare positional-only parameters. + +To support positional-only parameters in code that must remain compatible +with older versions of Python, type checkers should support the following +special case: all parameters with names that begin but don't end with ``__`` +are assumed to be positional-only:: + + def f(__x: int, __y__: int = 0) -> None: ... + + f(3, __y__=1) # This call is fine. + + f(__x=3) # This call is an error. + +Consistent with :pep:`570` syntax, positional-only parameters cannot appear +after parameters that accept keyword arguments. Type checkers should enforce +this requirement:: + + def g(x: int, __y: int) -> None: ... # Type error + + +Generics in standard collections +-------------------------------- + +Before Python 3.9 (:pep:`585`), standard library generic types like +``list`` could not be parameterized at runtime (i.e., ``list[int]`` +would throw an error). Therefore, the ``typing`` module provided +generic aliases for major builtin and standard library types (e.g., +``typing.List[int]``). + +In each of these cases, type checkers should treat the library type +as equivalent to the alias in the ``typing`` module. This includes: + +* ``list`` and ``typing.List`` +* ``dict`` and ``typing.Dict`` +* ``set`` and ``typing.Set`` +* ``frozenset`` and ``typing.FrozenSet`` +* ``tuple`` and ``typing.Tuple`` +* ``type`` and ``typing.Type`` +* ``collections.deque`` and ``typing.Deque`` +* ``collections.defaultdict`` and ``typing.DefaultDict`` +* ``collections.OrderedDict`` and ``typing.OrderedDict`` +* ``collections.Counter`` and ``typing.Counter`` +* ``collections.ChainMap`` and ``typing.ChainMap`` +* ``collections.abc.Awaitable`` and ``typing.Awaitable`` +* ``collections.abc.Coroutine`` and ``typing.Coroutine`` +* ``collections.abc.AsyncIterable`` and ``typing.AsyncIterable`` +* ``collections.abc.AsyncIterator`` and ``typing.AsyncIterator`` +* ``collections.abc.AsyncGenerator`` and ``typing.AsyncGenerator`` +* ``collections.abc.Iterable`` and ``typing.Iterable`` +* ``collections.abc.Iterator`` and ``typing.Iterator`` +* ``collections.abc.Generator`` and ``typing.Generator`` +* ``collections.abc.Reversible`` and ``typing.Reversible`` +* ``collections.abc.Container`` and ``typing.Container`` +* ``collections.abc.Collection`` and ``typing.Collection`` +* ``collections.abc.Callable`` and ``typing.Callable`` +* ``collections.abc.Set`` and ``typing.AbstractSet`` (note the change in name) +* ``collections.abc.MutableSet`` and ``typing.MutableSet`` +* ``collections.abc.Mapping`` and ``typing.Mapping`` +* ``collections.abc.MutableMapping`` and ``typing.MutableMapping`` +* ``collections.abc.Sequence`` and ``typing.Sequence`` +* ``collections.abc.MutableSequence`` and ``typing.MutableSequence`` +* ``collections.abc.ByteString`` and ``typing.ByteString`` +* ``collections.abc.MappingView`` and ``typing.MappingView`` +* ``collections.abc.KeysView`` and ``typing.KeysView`` +* ``collections.abc.ItemsView`` and ``typing.ItemsView`` +* ``collections.abc.ValuesView`` and ``typing.ValuesView`` +* ``contextlib.AbstractContextManager`` and ``typing.ContextManager`` (note the change in name) +* ``contextlib.AbstractAsyncContextManager`` and ``typing.AsyncContextManager`` (note the change in name) +* ``re.Pattern`` and ``typing.Pattern`` +* ``re.Match`` and ``typing.Match`` + +The generic aliases in the ``typing`` module are considered deprecated +and type checkers may warn if they are used. + +.. _`typing-union`: +.. _`typing-optional`: + +``Union`` and ``Optional`` +-------------------------- + +Before Python 3.10 (:pep:`604`), Python did not support the ``|`` operator +for creating unions of types. Therefore, the ``typing.Union`` :term:`special form` can also +be used to create union types. Type checkers should treat the two forms as equivalent. + +In addition, the ``Optional`` :term:`special form` is equivalent to a union with ``None``. + +Examples: + +* ``int | str`` is the same as ``Union[int, str]`` +* ``int | str | range`` is the same as ``Union[int, str, range]`` +* ``int | None`` is the same as ``Optional[int]`` and ``Union[int, None]`` + +.. _`unpack-pep646`: + +``Unpack`` +---------- + +:pep:`646`, which introduced ``TypeVarTuple`` into Python 3.11, also made two grammar +changes to support use of variadic generics, allowing use of the ``*`` operator in +index operations and in ``*args`` annotations. The ``Unpack[]`` operator was added to +support equivalent semantics on older Python versions. It should be treated as equivalent +to the ``*`` syntax. In particular, the following are equivalent: + +* ``A[*Ts]`` is the same as ``A[Unpack[Ts]]`` +* ``def f(*args: *Ts): ...`` is the same as ``def f(*args: Unpack[Ts]): ...`` diff --git a/docs/spec/index.rst b/docs/spec/index.rst new file mode 100644 index 000000000..47d491766 --- /dev/null +++ b/docs/spec/index.rst @@ -0,0 +1,33 @@ +Specification for the Python type system +======================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + type-system + meta + concepts + annotations + type-forms + special-types + generics + qualifiers + class-compat + aliases + literal + protocol + callables + constructors + overload + exceptions + dataclasses + typeddict + tuples + namedtuples + enums + narrowing + directives + distributing + historical + glossary diff --git a/docs/spec/literal.rst b/docs/spec/literal.rst new file mode 100644 index 000000000..7b0015ca3 --- /dev/null +++ b/docs/spec/literal.rst @@ -0,0 +1,804 @@ +.. _`literal-types`: + +Literals +======== + +.. _`literal`: + +``Literal`` +----------- + +(Originally specified in :pep:`586`.) + + +Core Semantics +^^^^^^^^^^^^^^ + +This section outlines the baseline behavior of literal types. + +Core behavior +""""""""""""" + +Literal types indicate that a variable has a specific and +concrete value. For example, if we define some variable ``foo`` to have +type ``Literal[3]``, we are declaring that ``foo`` must be exactly equal +to ``3`` and no other value. + +Given some value ``v`` that is a member of type ``T``, the type ``Literal[v]`` +is a subtype of ``T``. For example, ``Literal[3]`` is a subtype of ``int``. + +All methods from the parent type will be directly inherited by the +literal type. So, if we have some variable ``foo`` of type ``Literal[3]`` +it’s safe to do things like ``foo + 5`` since ``foo`` inherits ``int``’s +``__add__`` method. The resulting type of ``foo + 5`` is ``int``. + +This "inheriting" behavior is identical to how we +:ref:`handle NewTypes `. + +Equivalence of two Literals +""""""""""""""""""""""""""" + +Two types ``Literal[v1]`` and ``Literal[v2]`` are equivalent when +both of the following conditions are true: + +1. ``type(v1) == type(v2)`` +2. ``v1 == v2`` + +For example, ``Literal[20]`` and ``Literal[0x14]`` are equivalent. +However, ``Literal[0]`` and ``Literal[False]`` are *not* equivalent +despite that ``0 == False`` evaluates to 'true' at runtime: ``0`` +has type ``int`` and ``False`` has type ``bool``. + +Shortening unions of literals +""""""""""""""""""""""""""""" + +Literals are parameterized with one or more values. When a Literal is +parameterized with more than one value, it's treated as exactly equivalent +to the union of those types. That is, ``Literal[v1, v2, v3]`` is equivalent +to ``Literal[v1] | Literal[v2] | Literal[v3]``. + +This shortcut helps make writing signatures for functions that accept +many different literals more ergonomic — for example, functions like +``open(...)``:: + + # Note: this is a simplification of the true type signature. + _PathType = str | bytes | int + + @overload + def open(path: _PathType, + mode: Literal["r", "w", "a", "x", "r+", "w+", "a+", "x+"], + ) -> IO[str]: ... + @overload + def open(path: _PathType, + mode: Literal["rb", "wb", "ab", "xb", "r+b", "w+b", "a+b", "x+b"], + ) -> IO[bytes]: ... + + # Fallback overload for when the user isn't using literal types + @overload + def open(path: _PathType, mode: str) -> IO[Any]: ... + +The provided values do not all have to be members of the same type. +For example, ``Literal[42, "foo", True]`` is a legal type. + +However, Literal **must** be parameterized with at least one type. +Types like ``Literal[]`` or ``Literal`` are illegal. + + +Legal and illegal parameterizations +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This section describes what exactly constitutes a legal ``Literal[...]`` type: +what values may and may not be used as parameters. + +In short, a ``Literal[...]`` type may be parameterized by one or more literal +expressions, and nothing else. + +.. _literal-legal-parameters: + +Legal parameters for ``Literal`` at type check time +""""""""""""""""""""""""""""""""""""""""""""""""""" + +``Literal`` may be parameterized with literal ``int``, ``str``, ``bytes``, +and ``bool`` objects, instances of ``enum.Enum`` subclasses, and ``None``. So for example, all of +the following would be legal:: + + Literal[26] + Literal[0x1A] # Equivalent to Literal[26] + Literal[-4] + Literal["hello world"] + Literal[u"hello world"] # Equivalent to Literal["hello world"] + Literal[b"hello world"] + Literal[True] + + class Color(enum.Enum): + RED = 1 + GREEN = 2 + + Literal[Color.RED] + Literal[None] + +**Note:** Since the type ``None`` is inhabited by just a single +value, the types ``None`` and ``Literal[None]`` are exactly equivalent. +Type checkers may simplify ``Literal[None]`` into just ``None``. + +``Literal`` may also be parameterized by other literal types, or type aliases +to other literal types. For example, the following is legal:: + + ReadOnlyMode = Literal["r", "r+"] + WriteAndTruncateMode = Literal["w", "w+", "wt", "w+t"] + WriteNoTruncateMode = Literal["r+", "r+t"] + AppendMode = Literal["a", "a+", "at", "a+t"] + + AllModes = Literal[ReadOnlyMode, WriteAndTruncateMode, + WriteNoTruncateMode, AppendMode] + +This feature is again intended to help make using and reusing literal types +more ergonomic. + +**Note:** As a consequence of the above rules, type checkers are also expected +to support types that look like the following:: + + Literal[Literal[Literal[1, 2, 3], "foo"], 5, None] + +This should be exactly equivalent to the following type:: + + Literal[1, 2, 3, "foo", 5, None] + +...and also to the following type:: + + Literal[1, 2, 3, "foo", 5] | None + +Illegal parameters for ``Literal`` at type check time +""""""""""""""""""""""""""""""""""""""""""""""""""""" + +The following parameters are intentionally disallowed by design: + +- Arbitrary expressions like ``Literal[3 + 4]`` or + ``Literal["foo".replace("o", "b")]``. + + - Rationale: Literal types are meant to be a + minimal extension to the typing ecosystem and requiring type + checkers to interpret potentially expressions inside types adds too + much complexity. + + - As a consequence, complex numbers like ``Literal[4 + 3j]`` and + ``Literal[-4 + 2j]`` are also prohibited. For consistency, literals like + ``Literal[4j]`` that contain just a single complex number are also + prohibited. + + - The only exceptions to this rule are the unary ``-`` (minus) and unary ``+`` (plus) for ints: types + like ``Literal[-5]`` and ``Literal[+1]`` are *accepted*. + +- Tuples containing valid literal types like ``Literal[(1, "foo", "bar")]``. + The user could always express this type as + ``tuple[Literal[1], Literal["foo"], Literal["bar"]]`` instead. Also, + tuples are likely to be confused with the ``Literal[1, 2, 3]`` + shortcut. + +- Mutable literal data structures like dict literals, list literals, or + set literals: literals are always implicitly final and immutable. So, + ``Literal[{"a": "b", "c": "d"}]`` is illegal. + +- Any other types: for example, ``Literal[Path]``, or + ``Literal[some_object_instance]`` are illegal. This includes typevars: if + ``T`` is a typevar, ``Literal[T]`` is not allowed. Typevars can vary over + only types, never over values. + +The following are provisionally disallowed for simplicity. We can consider +allowing them in the future. + +- Floats: e.g. ``Literal[3.14]``. Representing Literals of infinity or NaN + in a clean way is tricky; real-world APIs are unlikely to vary their + behavior based on a float parameter. + +- Any: e.g. ``Literal[Any]``. ``Any`` is a type, and ``Literal[...]`` is + meant to contain values only. It is also unclear what ``Literal[Any]`` + would actually semantically mean. + +Parameters at runtime +""""""""""""""""""""" + +Although the set of parameters ``Literal[...]`` may contain at type check time +is very small, the actual implementation of ``typing.Literal`` will not perform +any checks at runtime. For example:: + + def my_function(x: Literal[1 + 2]) -> int: + return x * 3 + + x: Literal = 3 + y: Literal[my_function] = my_function + +The type checker should reject this program: all three uses of +``Literal`` are *invalid* according to this spec. However, Python itself +should execute this program with no errors. + +This is partly to help us preserve flexibility in case we want to expand the +scope of what ``Literal`` can be used for in the future, and partly because +it is not possible to detect all illegal parameters at runtime to begin with. +For example, it is impossible to distinguish between ``Literal[1 + 2]`` and +``Literal[3]`` at runtime. + +Literals, enums, and forward references +""""""""""""""""""""""""""""""""""""""" + +One potential ambiguity is between literal strings and forward +references to literal enum members. For example, suppose we have the +type ``Literal["Color.RED"]``. Does this literal type +contain a string literal or a forward reference to some ``Color.RED`` +enum member? + +In cases like these, we always assume the user meant to construct a +literal string. If the user wants a forward reference, they must wrap +the entire literal type in a string -- e.g. ``"Literal[Color.RED]"``. + +Type inference +^^^^^^^^^^^^^^ + +This section describes a few rules regarding type inference and +literals, along with some examples. + +Backwards compatibility +""""""""""""""""""""""" + +When type checkers add support for Literal, it's important they do so +in a way that maximizes backwards-compatibility. Type checkers should +ensure that code that used to type check continues to do so after support +for Literal is added on a best-effort basis. + +This is particularly important when performing type inference. For +example, given the statement ``x = "blue"``, should the inferred +type of ``x`` be ``str`` or ``Literal["blue"]``? + +One naive strategy would be to always assume expressions are intended +to be Literal types. So, ``x`` would always have an inferred type of +``Literal["blue"]`` in the example above. This naive strategy is almost +certainly too disruptive -- it would cause programs like the following +to start failing when they previously did not:: + + # If a type checker infers 'var' has type Literal[3] + # and my_list has type List[Literal[3]]... + var = 3 + my_list = [var] + + # ...this call would be a type-error. + my_list.append(4) + +Another example of when this strategy would fail is when setting fields +in objects:: + + class MyObject: + def __init__(self) -> None: + # If a type checker infers MyObject.field has type Literal[3]... + self.field = 3 + + m = MyObject() + + # ...this assignment would no longer type check + m.field = 4 + +An alternative strategy that *does* maintain compatibility in every case would +be to always assume expressions are *not* Literal types unless they are +explicitly annotated otherwise. A type checker using this strategy would +always infer that ``x`` is of type ``str`` in the first example above. + +This is not the only viable strategy: type checkers should feel free to experiment +with more sophisticated inference techniques. No particular strategy is +mandated, but type checkers should keep in mind the importance of backwards +compatibility. + +Using non-Literals in Literal contexts +"""""""""""""""""""""""""""""""""""""" + +Literal types follow the existing rules regarding subtyping with no additional +special-casing. For example, programs like the following are type safe:: + + def expects_str(x: str) -> None: ... + var: Literal["foo"] = "foo" + + # Legal: Literal["foo"] is a subtype of str + expects_str(var) + +This also means non-Literal types in general are not :term:`assignable` to +Literal types. For example:: + + def expects_literal(x: Literal["foo"]) -> None: ... + + def runner(my_str: str) -> None: + # ILLEGAL: str is not assignable to Literal["foo"] + expects_literal(my_str) + +**Note:** If the user wants their API to support accepting both literals +*and* the original type -- perhaps for legacy purposes -- they should +implement a fallback overload. See :ref:`literalstring-overloads`. + +Interactions with other types and features +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This section discusses how Literal types interact with other existing types. + +Intelligent indexing of structured data +""""""""""""""""""""""""""""""""""""""" + +Literals can be used to "intelligently index" into structured types like +tuples, NamedTuple, and classes. (Note: this is not an exhaustive list). + +For example, type checkers should infer the correct value type when +indexing into a tuple using an int key that corresponds to a valid index:: + + a: Literal[0] = 0 + b: Literal[5] = 5 + + some_tuple: tuple[int, str, List[bool]] = (3, "abc", [True, False]) + reveal_type(some_tuple[a]) # Revealed type is 'int' + some_tuple[b] # Error: 5 is not a valid index into the tuple + +We expect similar behavior when using functions like getattr:: + + class Test: + def __init__(self, param: int) -> None: + self.myfield = param + + def mymethod(self, val: int) -> str: ... + + a: Literal["myfield"] = "myfield" + b: Literal["mymethod"] = "mymethod" + c: Literal["blah"] = "blah" + + t = Test() + reveal_type(getattr(t, a)) # Revealed type is 'int' + reveal_type(getattr(t, b)) # Revealed type is 'Callable[[int], str]' + getattr(t, c) # Error: No attribute named 'blah' in Test + +**Note:** See `Interactions with Final`_ for how we can +express the variable declarations above in a more compact manner. + +Interactions with overloads +""""""""""""""""""""""""""" + +Literal types and overloads do not need to interact in a special +way: the existing rules work fine. + +However, one important use case type checkers must take care to +support is the ability to use a *fallback* when the user is not using literal +types. For example, consider ``open``:: + + _PathType = str | bytes | int + + @overload + def open(path: _PathType, + mode: Literal["r", "w", "a", "x", "r+", "w+", "a+", "x+"], + ) -> IO[str]: ... + @overload + def open(path: _PathType, + mode: Literal["rb", "wb", "ab", "xb", "r+b", "w+b", "a+b", "x+b"], + ) -> IO[bytes]: ... + + # Fallback overload for when the user isn't using literal types + @overload + def open(path: _PathType, mode: str) -> IO[Any]: ... + +If we were to change the signature of ``open`` to use just the first two overloads, +we would break any code that does not pass in a literal string expression. +For example, code like this would be broken:: + + mode: str = pick_file_mode(...) + with open(path, mode) as f: + # f should continue to be of type IO[Any] here + +A little more broadly: we mandate that whenever we add literal types to +some existing API in typeshed, we also always include a fallback overload to +maintain backwards-compatibility. + +Interactions with generics +"""""""""""""""""""""""""" + +Literal types are types, and can be used anywhere a type is expected. + +For example, it is legal to parameterize generic functions or +classes using Literal types:: + + # A simplified definition for Matrix[row, column] + class Matrix[A: int, B: int]: + def __add__(self, other: Matrix[A, B]) -> Matrix[A, B]: ... + def __matmul__[C: int](self, other: Matrix[B, C]) -> Matrix[A, C]: ... + def transpose(self) -> Matrix[B, A]: ... + + foo: Matrix[Literal[2], Literal[3]] = Matrix(...) + bar: Matrix[Literal[3], Literal[7]] = Matrix(...) + + baz = foo @ bar + reveal_type(baz) # Revealed type is 'Matrix[Literal[2], Literal[7]]' + +Similarly, it is legal to use type variables with value restrictions +or bounds involving Literal types:: + + def takes_letter[T: (Literal["a"], Literal["b"], Literal["c"])](value: T) -> T: + return value + + def takes_foo[S: Literal["foo"]](value: S) -> S: + return value + +...although it is unclear when it would ever be useful to construct a +TypeVar with a Literal upper bound. For example, the ``S`` TypeVar in +the above example is essentially pointless: we can get equivalent behavior +by using ``S = Literal["foo"]`` instead. + +**Note:** Literal types and generics deliberately interact in only very +basic and limited ways. In particular, libraries that want to type check +code containing a heavy amount of numeric or numpy-style manipulation will +almost certainly likely find Literal types as described here to be +insufficient for their needs. + +Interactions with enums and exhaustiveness checks +""""""""""""""""""""""""""""""""""""""""""""""""" + +Type checkers should be capable of performing exhaustiveness checks when +working with Literal types that have a closed number of variants, such as +enums. For example, the type checker should be capable of inferring that +the final ``else`` statement must be of type ``str``, since all three +values of the ``Status`` enum have already been exhausted:: + + class Status(Enum): + SUCCESS = 0 + INVALID_DATA = 1 + FATAL_ERROR = 2 + + def parse_status(s: str | Status) -> None: + if s is Status.SUCCESS: + print("Success!") + elif s is Status.INVALID_DATA: + print("The given data is invalid because...") + elif s is Status.FATAL_ERROR: + print("Unexpected fatal error...") + else: + # 's' must be of type 'str' since all other options are exhausted + print("Got custom status: " + s) + +Here, the ``Status`` enum could be treated as being approximately equivalent +to ``Literal[Status.SUCCESS, Status.INVALID_DATA, Status.FATAL_ERROR]`` +and the type of ``s`` narrowed accordingly. + +Interactions with narrowing +""""""""""""""""""""""""""" + +Type checkers may optionally perform additional analysis for both enum and +non-enum Literal types beyond what is described in the section above. + +For example, it may be useful to perform narrowing based on things like +containment or equality checks:: + + def parse_status(status: str) -> None: + if status in ("MALFORMED", "ABORTED"): + # Type checker could narrow 'status' to type + # Literal["MALFORMED", "ABORTED"] here. + return expects_bad_status(status) + + # Similarly, type checker could narrow 'status' to Literal["PENDING"] + if status == "PENDING": + expects_pending_status(status) + +It may also be useful to perform narrowing taking into account expressions +involving Literal bools. For example, we can combine ``Literal[True]``, +``Literal[False]``, and overloads to construct "custom type guards":: + + @overload + def is_int_like(x: int | list[int]) -> Literal[True]: ... + @overload + def is_int_like(x: object) -> bool: ... + def is_int_like(x): ... + + vector: list[int] = [1, 2, 3] + if is_int_like(vector): + vector.append(3) + else: + vector.append("bad") # This branch is inferred to be unreachable + + scalar: int | str + if is_int_like(scalar): + scalar += 3 # Type checks: type of 'scalar' is narrowed to 'int' + else: + scalar += "foo" # Type checks: type of 'scalar' is narrowed to 'str' + +.. _literal-final-interactions: + +Interactions with Final +""""""""""""""""""""""" + +The ``Final`` qualifier can be used to declare that some variable or +attribute cannot be reassigned:: + + foo: Final = 3 + foo = 4 # Error: 'foo' is declared to be Final + +Note that in the example above, we know that ``foo`` will always be equal to +exactly ``3``. A type checker can use this information to deduce that ``foo`` +is valid to use in any context that expects a ``Literal[3]``:: + + def expects_three(x: Literal[3]) -> None: ... + + expects_three(foo) # Type checks, since 'foo' is Final and equal to 3 + +The ``Final`` qualifier serves as a shorthand for declaring that a variable +is *effectively Literal*. + +Type checkers are expected to +support this shortcut. Specifically, given a variable or attribute assignment +of the form ``var: Final = value`` where ``value`` is a valid parameter for +``Literal[...]``, type checkers should understand that ``var`` may be used in +any context that expects a ``Literal[value]``. + +Type checkers are not obligated to understand any other uses of Final. For +example, whether or not the following program type checks is left unspecified:: + + # Note: The assignment does not exactly match the form 'var: Final = value'. + bar1: Final[int] = 3 + expects_three(bar1) # May or may not be accepted by type checkers + + # Note: "Literal[1 + 2]" is not a legal type. + bar2: Final = 1 + 2 + expects_three(bar2) # May or may not be accepted by type checkers + +.. _`literalstring`: + +``LiteralString`` +----------------- + +(Originally specified in :pep:`675`.) + +Valid locations for ``LiteralString`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +``LiteralString`` can be used where any other type can be used: + +:: + + variable_annotation: LiteralString + + def my_function(literal_string: LiteralString) -> LiteralString: ... + + class Foo: + my_attribute: LiteralString + + type_argument: List[LiteralString] + + def enforce_literal[T: LiteralString](value: T) -> T: + return value + +It cannot be nested within unions of ``Literal`` types: + +:: + + bad_union: Literal["hello", LiteralString] # Not OK + bad_nesting: Literal[LiteralString] # Not OK + + +Type inference +^^^^^^^^^^^^^^ + +Inferring ``LiteralString`` +""""""""""""""""""""""""""" + +Any literal string type is assignable to ``LiteralString``. For +example, ``x: LiteralString = "foo"`` is valid because ``"foo"`` is +inferred to be of type ``Literal["foo"]``. + +We also infer ``LiteralString`` in the +following cases: + ++ Addition: ``x + y`` is of type ``LiteralString`` if the types of both ``x`` + and ``y`` are assignable to ``LiteralString``. + ++ Joining: ``sep.join(xs)`` is of type ``LiteralString`` if ``sep``'s + type is assignable to ``LiteralString`` and ``xs``'s type is + assignable to ``Iterable[LiteralString]``. + ++ In-place addition: If ``s`` has type ``LiteralString`` and ``x`` has a type + assignable to ``LiteralString``, then ``s += x`` preserves ``s``'s type as + ``LiteralString``. + ++ String formatting: An f-string has type ``LiteralString`` if and only if its + constituent expressions are literal strings. ``s.format(...)`` is assignable + to ``LiteralString`` if and only if ``s`` and the arguments have types + assignable to ``LiteralString``. + +In all other cases, if one or more of the composed values has a +non-literal type ``str``, the composition of types will have type +``str``. For example, if ``s`` has type ``str``, then ``"hello" + s`` +has type ``str``. This matches the pre-existing behavior of type +checkers. + +``LiteralString`` is assignable to the type ``str``. It inherits all +methods from ``str``. So, if we have a variable ``s`` of type +``LiteralString``, it is safe to write ``s.startswith("hello")``. + +Some type checkers refine the type of a string when doing an equality +check: + +:: + + def foo(s: str) -> None: + if s == "bar": + reveal_type(s) # => Literal["bar"] + +Such a refined type in the if-block is also assignable to +``LiteralString`` because its type is ``Literal["bar"]``. + + +Examples +"""""""" + +See the examples below to help clarify the above rules: + +:: + + + literal_string: LiteralString + s: str = literal_string # OK + + literal_string: LiteralString = s # Error: Expected LiteralString, got str. + literal_string: LiteralString = "hello" # OK + +Addition of literal strings: + +:: + + def expect_literal_string(s: LiteralString) -> None: ... + + expect_literal_string("foo" + "bar") # OK + expect_literal_string(literal_string + "bar") # OK + + literal_string2: LiteralString + expect_literal_string(literal_string + literal_string2) # OK + + plain_string: str + expect_literal_string(literal_string + plain_string) # Not OK. + +Join using literal strings: + +:: + + expect_literal_string(",".join(["foo", "bar"])) # OK + expect_literal_string(literal_string.join(["foo", "bar"])) # OK + expect_literal_string(literal_string.join([literal_string, literal_string2])) # OK + + xs: List[LiteralString] + expect_literal_string(literal_string.join(xs)) # OK + expect_literal_string(plain_string.join([literal_string, literal_string2])) + # Not OK because the separator has type 'str'. + +In-place addition using literal strings: + +:: + + literal_string += "foo" # OK + literal_string += literal_string2 # OK + literal_string += plain_string # Not OK + +Format strings using literal strings: + +:: + + literal_name: LiteralString + expect_literal_string(f"hello {literal_name}") + # OK because it is composed from literal strings. + + expect_literal_string("hello {}".format(literal_name)) # OK + + expect_literal_string(f"hello") # OK + + username: str + expect_literal_string(f"hello {username}") + # NOT OK. The format-string is constructed from 'username', + # which has type 'str'. + + expect_literal_string("hello {}".format(username)) # Not OK + +Other literal types, such as literal integers, are not assignable to ``LiteralString``: + +:: + + some_int: int + expect_literal_string(some_int) # Error: Expected LiteralString, got int. + + literal_one: Literal[1] = 1 + expect_literal_string(literal_one) # Error: Expected LiteralString, got Literal[1]. + + +We can call functions on literal strings: + +:: + + def add_limit(query: LiteralString) -> LiteralString: + return query + " LIMIT = 1" + + def my_query(query: LiteralString, user_id: str) -> None: + sql_connection().execute(add_limit(query), (user_id,)) # OK + +Conditional statements and expressions work as expected: + +:: + + def return_literal_string() -> LiteralString: + return "foo" if condition1() else "bar" # OK + + def return_literal_str2(literal_string: LiteralString) -> LiteralString: + return "foo" if condition1() else literal_string # OK + + def return_literal_str3() -> LiteralString: + if condition1(): + result: Literal["foo"] = "foo" + else: + result: LiteralString = "bar" + + return result # OK + + +Interaction with Type Variables and Generics +"""""""""""""""""""""""""""""""""""""""""""" + +Type variables can use ``LiteralString`` as an upper bound: + +:: + + from typing import Literal, LiteralString + + def literal_identity[T: LiteralString](s: T) -> T: + return s + + hello: Literal["hello"] = "hello" + y = literal_identity(hello) + reveal_type(y) # => Literal["hello"] + + s: LiteralString + y2 = literal_identity(s) + reveal_type(y2) # => LiteralString + + s_error: str + literal_identity(s_error) + # Error: Expected T (bound to LiteralString), got str. + + +``LiteralString`` can be used as a type argument for generic classes: + +:: + + class Container[T]: + def __init__(self, value: T) -> None: + self.value = value + + literal_string: LiteralString = "hello" + x: Container[LiteralString] = Container(literal_string) # OK + + s: str + x_error: Container[LiteralString] = Container(s) # Not OK + +Standard containers like ``List`` work as expected: + +:: + + xs: List[LiteralString] = ["foo", "bar", "baz"] + + +.. _literalstring-overloads: + +Interactions with Overloads +""""""""""""""""""""""""""" + +Literal strings and overloads do not need to interact in a special +way: the existing rules work fine. ``LiteralString`` can be used as a +fallback overload where a specific ``Literal["foo"]`` type does not +match: + +:: + + @overload + def foo(x: Literal["foo"]) -> int: ... + @overload + def foo(x: LiteralString) -> bool: ... + @overload + def foo(x: str) -> str: ... + + x1: int = foo("foo") # First overload. + x2: bool = foo("bar") # Second overload. + s: str + x3: str = foo(s) # Third overload. diff --git a/docs/spec/meta.rst b/docs/spec/meta.rst new file mode 100644 index 000000000..dd50a6e8d --- /dev/null +++ b/docs/spec/meta.rst @@ -0,0 +1,51 @@ +.. _`spec-meta`: + +Meta-topics +=========== + +About this specification +------------------------ + +This document was created following the acceptance of :pep:`729` +to serve as a specification for the Python type system. The +initial text consists of the "Specification" sections of :pep:`484` +and subsequent typing-related PEPs, pasted together and reorganized. +This creates a document that encompasses all aspects of the type +system that have been specified in PEPs, but not necessarily a +coherent whole. The hope is that incremental improvements will +be made to this document to make it more coherent and complete. + +Changing the specification +-------------------------- + +Changes to the specification come in three kinds: + +- Minor, non-substantive changes can simply be proposed as PRs to + the `python/typing `__ repository, + and may be merged by anyone with commit access. Such changes may + include formatting fixes, linking improvements, etc. +- Substantive changes that do not rise to the level of a PEP must + be approved by the Typing Council. The procedure is described below. +- Major changes should go through the PEP process, as described in + :pep:`1`. What counts as a major change is not precisely defined, + but it would generally include any change of a similar magnitude + to `previous typing PEPs `__. + +Changes that need Typing Council approval go through three steps: + +- Open a discussion on `discuss.python.org `__ + describing the issue. +- Open a PR on `python/typing `__ + that changes the spec and, if applicable, the + `conformance test suite `__. +- `Open an issue `__ on + the Typing Council's issue tracker asking for a decision. + +The Typing Council has `published `__ +some guidance on useful information to gather when proposing a change +to the spec, including: + +- A survey of the current behavior of major type checkers. +- A rationale for why the proposed behavior is better than alternatives. +- An implementation or proposed implementation of the proposed behavior + in at least one major type checker. diff --git a/docs/spec/namedtuples.rst b/docs/spec/namedtuples.rst new file mode 100644 index 000000000..87a6e4332 --- /dev/null +++ b/docs/spec/namedtuples.rst @@ -0,0 +1,172 @@ +.. _`namedtuple`: + +Named Tuples +============ + +As with tuples, named tuple classes require some special behaviors for type +checking. + + +Defining Named Tuples +--------------------- + +A named tuple class can be defined using a class syntax or a factory function +call. + +Type checkers should support the class syntax:: + + class Point(NamedTuple): + x: int + y: int + units: str = "meters" + +Fields must be annotated attributes - methods and un-annotated attributes are not +considered fields. Field names may not start with an underscore:: + + class MyTuple(NamedTuple): + x1 = 1 # Not a field + def x2() -> None: pass # Not a field + _x3: int # Type error: illegal field name + +Regardless of whether the class syntax or factory function call is used to define +a named tuple, type checkers should synthesize a ``__new__`` method based on +the named tuple fields. This mirrors the runtime behavior. In the example +above, the synthesized ``__new__`` method would look like the following:: + + def __new__(cls, x: int, y: int, units: str = "meters") -> Self: + ... + +The runtime implementation of ``NamedTuple`` enforces that fields with default +values must come after fields without default values. Type checkers should +likewise enforce this restriction:: + + class Location(NamedTuple): + altitude: float = 0.0 + latitude: float # Type error (previous field has a default value) + longitude: float + +A named tuple class can be subclassed, but any fields added by the subclass +are not considered part of the named tuple type. Type checkers should enforce +that these newly-added fields do not conflict with the named tuple fields +in the base class:: + + class PointWithName(Point): + name: str # OK + x: int # Type error (invalid override of named tuple field) + +Namedtuple fields may be conditional, via checks of the same +:ref:`statically-known conditions` +that a type-checker understands elsewhere, such as Python version:: + + class ConditionalField(NamedTuple): + x: int + if sys.version_info >= (3, 14): + y: str + +In Python 3.11 and newer, the class syntax supports generic named tuple classes. +Type checkers should support this:: + + class Property[T](NamedTuple): + name: str + value: T + + reveal_type(Property("height", 3.4)) # Revealed type is Property[float] + +``NamedTuple`` does not support multiple inheritance. Type checkers should +enforce this limitation:: + + class Unit(NamedTuple, object): # Type error + name: str + +The factory function call supports two variants: ``collections.namedtuple`` and +``typing.NamedTuple``. The latter provides a way to specify the types +of the fields in the tuple whereas the former does not. The ``namedtuple`` +form allows fields to be specified as a tuple or list of strings or a single +string with fields separated by whitespace or commas. The ``NamedTuple`` +functional form accepts an iterable of ``(name, type)`` pairs. +For the ``namedtuple`` form, all fields are assumed to be of type ``Any``. + +A type checker may support the factory function call in its various forms:: + + Point1 = namedtuple('Point1', ['x', 'y']) + Point2 = namedtuple('Point2', ('x', 'y')) + Point3 = namedtuple('Point3', 'x y') + Point4 = namedtuple('Point4', 'x, y') + + Point5 = NamedTuple('Point5', [('x', int), ('y', int)]) + Point6 = NamedTuple('Point6', (('x', int), ('y', int))) + +At runtime, the ``namedtuple`` function disallows field names that begin with +an underscore or are illegal Python identifiers, and either raises an exception +or replaces these fields with a parameter name of the form ``_N``. The behavior +depends on the value of the ``rename`` argument. Type checkers may replicate +this behavior statically:: + + NT1 = namedtuple("NT1", ["a", "a"]) # Type error (duplicate field name) + NT2 = namedtuple("NT2", ["abc", "def"], rename=False) # Type error (illegal field name) + NT3 = namedtuple("NT3", ["abc", "_d"], rename=False) # Type error (illegal field name) + + NT4 = namedtuple("NT4", ["abc", "def"], rename=True) # OK + NT4(abc="", _1="") # OK + + NT5 = namedtuple("NT5", ["abc", "_d"], rename=True) # OK + NT5(abc="", _1="") # OK + +The ``namedtuple`` function also supports a ``defaults`` keyword argument that +specifies default values for the fields. Type checkers may support this:: + + NT4 = namedtuple("NT4", "a b c", defaults=(1, 2)) + NT4() # Type error (too few arguments) + NT4(1) # OK + + +Named Tuple Usage +----------------- + +The fields within a named tuple instance can be accessed by name using an +attribute access (``.``) operator. Type checkers should support this:: + + p = Point(1, 2) + assert_type(p.x, int) + assert_type(p.units, str) + +Like normal tuples, elements of a named tuple can also be accessed by index, +and type checkers should support this:: + + assert_type(p[0], int) + assert_type(p[2], str) + +Type checkers should enforce that named tuple fields cannot be overwritten +or deleted:: + + p.x = 3 # Type error + p[0] = 3 # Type error + del p.x # Type error + del p[0] # Type error + +Like regular tuples, named tuples can be unpacked. Type checkers should understand +this:: + + x, y, units = p + assert_type(x, int) + assert_type(units, str) + + x, y = p # Type error (too few values to unpack) + + +Assignability +------------- + +A named tuple is :term:`assignable` to a ``tuple`` with a known length and +parameterized by types corresponding to the named tuple's individual field +types:: + + p = Point(x=1, y=2, units="inches") + v1: tuple[int, int, str] = p # OK + v2: tuple[Any, ...] = p # OK + v3: tuple[int, int] = p # Type error (too few elements) + v4: tuple[int, str, str] = p # Type error (incompatible element type) + +As with normal tuples, named tuples are covariant in their type parameters:: + + v5: tuple[float, float, str] = p # OK diff --git a/docs/spec/narrowing.rst b/docs/spec/narrowing.rst new file mode 100644 index 000000000..6199aa092 --- /dev/null +++ b/docs/spec/narrowing.rst @@ -0,0 +1,227 @@ +.. _`type-narrowing`: + +Type narrowing +============== + +Type checkers should narrow the types of expressions in +certain contexts. This behavior is currently largely unspecified. + +.. _`typeguard`: + +TypeGuard +--------- + +(Originally specified in :pep:`647`.) + +The symbol ``TypeGuard``, exported from the ``typing`` module, is a :term:`special form` +that accepts a single type argument. It is used to annotate the return type of a +user-defined type guard function. Return statements within a type guard function +should return bool values, and type checkers should verify that all return paths +return a bool. + +``TypeGuard`` is also valid as the return type of a callable, for example +in callback protocols and in the ``Callable`` :term:`special form`. In these +contexts, it is treated as a subtype of bool. For example, ``Callable[..., TypeGuard[int]]`` +is assignable to ``Callable[..., bool]``. + +When ``TypeGuard`` is used to annotate the return type of a function or +method that accepts at least one parameter, that function or method is +treated by type checkers as a user-defined type guard. The type argument +provided for ``TypeGuard`` indicates the type that has been validated by +the function. + +User-defined type guards can be generic functions, as shown in this example: + +:: + + def is_two_element_tuple[T](val: tuple[T, ...]) -> TypeGuard[tuple[T, T]]: + return len(val) == 2 + + def func(names: tuple[str, ...]): + if is_two_element_tuple(names): + reveal_type(names) # tuple[str, str] + else: + reveal_type(names) # tuple[str, ...] + + +Type checkers should assume that type narrowing should be applied to the +expression that is passed as the first positional argument to a user-defined +type guard. If the type guard function accepts more than one argument, no +type narrowing is applied to those additional argument expressions. + +If a type guard function is implemented as an instance method or class method, +the first positional argument maps to the second parameter (after "self" or +"cls"). + +Here are some examples of user-defined type guard functions that accept more +than one argument: + +:: + + def is_str_list(val: list[object], allow_empty: bool) -> TypeGuard[list[str]]: + if len(val) == 0: + return allow_empty + return all(isinstance(x, str) for x in val) + + def is_set_of[T](val: set[Any], type: type[T]) -> TypeGuard[set[T]]: + return all(isinstance(x, type) for x in val) + + +The return type of a user-defined type guard function will normally refer to +a type that is strictly "narrower" than the type of the first argument (that +is, it's a more specific type that can be assigned to the more general type). +However, it is not required that the return type be strictly narrower. This +allows for cases like the example above where ``list[str]`` is not assignable +to ``list[object]``. + +When a conditional statement includes a call to a user-defined type guard +function, and that function returns true, the expression passed as the first +positional argument to the type guard function should be assumed by a static +type checker to take on the type specified in the TypeGuard return type, +unless and until it is further narrowed within the conditional code block. + +Some built-in type guards provide narrowing for both positive and negative +tests (in both the ``if`` and ``else`` clauses). For example, consider the +type guard for an expression of the form ``x is None``. If ``x`` has a type that +is a union of None and some other type, it will be narrowed to ``None`` in the +positive case and the other type in the negative case. User-defined type +guards apply narrowing only in the positive case (the ``if`` clause). The type +is not narrowed in the negative case. + +:: + + OneOrTwoStrs = tuple[str] | tuple[str, str] + def func(val: OneOrTwoStrs): + if is_two_element_tuple(val): + reveal_type(val) # tuple[str, str] + ... + else: + reveal_type(val) # OneOrTwoStrs + ... + + if not is_two_element_tuple(val): + reveal_type(val) # OneOrTwoStrs + ... + else: + reveal_type(val) # tuple[str, str] + ... + +.. _`typeis`: + +TypeIs +------ + +(Originally specified in :pep:`742`.) + +The :term:`special form` ``TypeIs`` is similar in usage, behavior, and runtime +implementation as ``TypeGuard``. + +``TypeIs`` accepts a single type argument and can be used as the return type +of a function. A function annotated as returning a ``TypeIs`` is called a +"type narrowing function". Type narrowing functions must return ``bool`` +values, and the type checker should verify that all return paths return +``bool``. + +Type narrowing functions must accept at least one positional argument. The type +narrowing behavior is applied to the first positional argument passed to +the function. The function may accept additional arguments, but they are +not affected by type narrowing. If a type narrowing function is implemented as +an instance method or class method, the first positional argument maps +to the second parameter (after ``self`` or ``cls``). + +To specify the behavior of ``TypeIs``, we use the following terminology: + +* I = ``TypeIs`` input type +* R = ``TypeIs`` return type +* A = Type of argument passed to type narrowing function (pre-narrowed) +* NP = Narrowed type (positive; used when ``TypeIs`` returned ``True``) +* NN = Narrowed type (negative; used when ``TypeIs`` returned ``False``) + + :: + + def narrower(x: I) -> TypeIs[R]: ... + + def func1(val: A): + if narrower(val): + assert_type(val, NP) + else: + assert_type(val, NN) + +The return type ``R`` must be :term:`assignable` to ``I``. The type checker +should emit an error if this condition is not met. + +Formally, type *NP* should be narrowed to :math:`A \land R`, +the intersection of *A* and *R*, and type *NN* should be narrowed to +:math:`A \land \neg R`, the intersection of *A* and the complement of *R*. +In practice, the theoretic types for strict type guards cannot be expressed +precisely in the Python type system. Type checkers should fall back on +practical approximations of these types. As a rule of thumb, a type checker +should use the same type narrowing logic -- and get results that are consistent +with -- its handling of :py:func:`isinstance`. This guidance allows for changes +and improvements if the type system is extended in the future. + +Type narrowing is applied in both the positive and negative case:: + + from typing import TypeIs, assert_type + + def is_str(x: object) -> TypeIs[str]: + return isinstance(x, str) + + def f(x: str | int) -> None: + if is_str(x): + assert_type(x, str) + else: + assert_type(x, int) + +The final narrowed type may be narrower than **R**, due to the constraints of the +argument's previously-known type:: + + from collections.abc import Awaitable + from typing import Any, TypeIs, assert_type + import inspect + + def isawaitable(x: object) -> TypeIs[Awaitable[Any]]: + return inspect.isawaitable(x) + + def f(x: Awaitable[int] | int) -> None: + if isawaitable(x): + # Type checkers may also infer the more precise type + # "Awaitable[int] | (int & Awaitable[Any])" + assert_type(x, Awaitable[int]) + else: + assert_type(x, int) + +It is an error to narrow to a type that is not :term:`assignable` to the input +type:: + + from typing import TypeIs + + def is_str(x: int) -> TypeIs[str]: # Type checker error + ... + +``TypeIs`` is also valid as the return type of a callable, for example +in callback protocols and in the ``Callable`` :term:`special form`. In these +contexts, it is treated as a subtype of bool. For example, ``Callable[..., TypeIs[int]]`` +is assignable to ``Callable[..., bool]``. + +Unlike ``TypeGuard``, ``TypeIs`` is invariant in its argument type: +``TypeIs[B]`` is not a subtype of ``TypeIs[A]``, +even if ``B`` is a subtype of ``A``. +To see why, consider the following example:: + + def takes_narrower(x: int | str, narrower: Callable[[object], TypeIs[int]]): + if narrower(x): + print(x + 1) # x is an int + else: + print("Hello " + x) # x is a str + + def is_bool(x: object) -> TypeIs[bool]: + return isinstance(x, bool) + + takes_narrower(1, is_bool) # Error: is_bool is not a TypeIs[int] + +(Note that ``bool`` is a subtype of ``int``.) +This code fails at runtime, because the narrower returns ``False`` (1 is not a ``bool``) +and the ``else`` branch is taken in ``takes_narrower()``. +If the call ``takes_narrower(1, is_bool)`` was allowed, type checkers would fail to +detect this error. diff --git a/docs/spec/overload.rst b/docs/spec/overload.rst new file mode 100644 index 000000000..7d9ef1bc1 --- /dev/null +++ b/docs/spec/overload.rst @@ -0,0 +1,428 @@ +.. _`overload`: + +``Overloads`` +============= + +In Python, it is common for callable objects to be polymorphic, meaning +they accept different types of arguments. It is also common for such +callables to return different types depending on the arguments passed to +them. Overloads provide a way to describe the accepted input signatures +and corresponding return types. + + +Overload definitions +^^^^^^^^^^^^^^^^^^^^ + +The ``@overload`` decorator allows describing functions and methods +that support multiple different combinations of argument types. This +pattern is used frequently in builtin modules and types. For example, +the ``__getitem__()`` method of the ``bytes`` type can be described as +follows:: + + from typing import overload + + class bytes: + ... + @overload + def __getitem__(self, i: int) -> int: ... + @overload + def __getitem__(self, s: slice) -> bytes: ... + +This description is more precise than would be possible using unions, +which cannot express the relationship between the argument and return +types:: + + class bytes: + ... + def __getitem__(self, a: int | slice) -> int | bytes: ... + +Another example where ``@overload`` comes in handy is the type of the +builtin ``map()`` function, which takes a different number of +arguments depending on the type of the callable:: + + from typing import overload + from collections.abc import Callable, Iterable, Iterator + + @overload + def map[T1, S](func: Callable[[T1], S], iter1: Iterable[T1]) -> Iterator[S]: ... + @overload + def map[T1, T2, S]( + func: Callable[[T1, T2], S], + iter1: Iterable[T1], + iter2: Iterable[T2], + ) -> Iterator[S]: ... + # ... and we could add more items to support more than two iterables + +Note that we could also easily add items to support ``map(None, ...)``:: + + @overload + def map[T1](func: None, iter1: Iterable[T1]) -> Iterable[T1]: ... + @overload + def map[T1, T2]( + func: None, + iter1: Iterable[T1], + iter2: Iterable[T2], + ) -> Iterable[tuple[T1, T2]]: ... + +Uses of the ``@overload`` decorator as shown above are suitable for +stub files. In regular modules, a series of ``@overload``-decorated +definitions must be followed by exactly one +non-``@overload``-decorated definition (for the same function/method). +The ``@overload``-decorated definitions are for the benefit of the +type checker only, since they will be overwritten by the +non-``@overload``-decorated definition, while the latter is used at +runtime but should be ignored by a type checker. At runtime, calling +an ``@overload``-decorated function directly will raise +``NotImplementedError``. Here's an example of a non-stub overload +that can't easily be expressed using a union or a type variable:: + + @overload + def utf8(value: None) -> None: + pass + @overload + def utf8(value: bytes) -> bytes: + pass + @overload + def utf8(value: unicode) -> bytes: + pass + def utf8(value): + + +A constrained ``TypeVar`` type can sometimes be used instead of +using the ``@overload`` decorator. For example, the definitions +of ``concat1`` and ``concat2`` in this stub file are equivalent:: + + def concat1[S: (str, bytes)](x: S, y: S) -> S: ... + + @overload + def concat2(x: str, y: str) -> str: ... + @overload + def concat2(x: bytes, y: bytes) -> bytes: ... + +Some functions, such as ``map`` or ``bytes.__getitem__`` above, can't +be represented precisely using type variables. We +recommend that ``@overload`` is only used in cases where a type +variable is not sufficient. + +Another important difference between type variables and an ``@overload`` +is that the former can also be used to define constraints for generic +class type parameters. For example, the type parameter of the generic +class ``typing.IO`` is constrained (only ``IO[str]``, ``IO[bytes]`` +and ``IO[Any]`` are valid):: + + class IO[S: (str, bytes)]: ... + + +Invalid overload definitions +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Type checkers should enforce the following rules for overload definitions. + +At least two ``@overload``-decorated definitions must be present. If only +one is present, an error should be reported. + +The ``@overload``-decorated definitions must be followed by an overload +implementation, which does not include an ``@overload`` decorator. Type +checkers should report an error or warning if an implementation is missing. +Overload definitions within stub files, protocols, and on abstract methods +within abstract base classes are exempt from this check. + +If one overload signature is decorated with ``@staticmethod`` or +``@classmethod``, all overload signatures must be similarly decorated. The +implementation, if present, must also have a consistent decorator. Type +checkers should report an error if these conditions are not met. + +If a ``@final`` or ``@override`` decorator is supplied for a function with +overloads, the decorator should be applied only to the overload implementation +if it is present. If an overload implementation isn't present (for example, in +a stub file), the ``@final`` or ``@override`` decorator should be applied only +to the first overload. Type checkers should enforce these rules and generate +an error when they are violated. If a ``@final`` or ``@override`` decorator +follows these rules, a type checker should treat the decorator as if it is +present on all overloads. + +Overloads are allowed to use a mixture of ``async def`` and ``def`` statements +within the same overload definition. Type checkers should convert +``async def`` statements to a non-async signature (wrapping the return +type in a ``Coroutine``) before testing for implementation consistency. + + +Implementation consistency +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If an overload implementation is defined, type checkers should validate +that it is consistent with all of its associated overload signatures. +The implementation should accept all potential sets of arguments +that are accepted by the overloads and should produce all potential return +types produced by the overloads. In typing terms, this means the input +signature of the implementation should be :term:`assignable` to the input +signatures of all overloads, and the return type of all overloads should be +assignable to the return type of the implementation. + +If the implementation is inconsistent with its overloads, a type checker +should report an error:: + + @overload + def func(x: str, /) -> str: ... + @overload + def func(x: int) -> int: ... + + # This implementation is inconsistent with the second overload + # because it does not accept a keyword argument ``x`` and the + # the overload's return type ``int`` is not assignable to the + # implementation's return type ``str``. + def func(x: int | str, /) -> str: + return str(x) + +When a type checker checks the implementation for consistency with overloads, +it should first apply any transforms that change the effective type of the +implementation including the presence of a ``yield`` statement in the +implementation body, the use of ``async def``, and the presence of additional +decorators. + + +Overload call evaluation +^^^^^^^^^^^^^^^^^^^^^^^^ + +When a type checker evaluates the call of an overloaded function, it +attempts to "match" the supplied arguments with one or more overloads. +This section describes the algorithm that type checkers should use +for overload matching. + +Only the overloads (the ``@overload``-decorated signatures) should be +considered for matching purposes. The implementation, if provided, +should be ignored for purposes of overload matching. + +Step 1 +~~~~~~ + +Examine the argument list to determine the number of +positional and keyword arguments. Use this information to eliminate any +overload candidates that are not plausible based on their +input signatures. + +- If no candidate overloads remain, generate an error and stop. +- If only one candidate overload remains, it is the winning match. Evaluate + it as if it were a non-overloaded function call and stop. +- If two or more candidate overloads remain, proceed to step 2. + +Step 2 +~~~~~~ + +Evaluate each remaining overload as a regular (non-overloaded) +call to determine whether it is compatible with the supplied +argument list. Unlike step 1, this step considers the types of the parameters +and arguments. During this step, do not generate any user-visible errors. +Simply record which of the overloads result in evaluation errors. + +- If all overloads result in errors, proceed to step 3. +- If only one overload evaluates without error, it is the winning match. + Evaluate it as if it were a non-overloaded function call and stop. +- If two or more candidate overloads remain, proceed to step 4. + +Step 3 +~~~~~~ + +If step 2 produces errors for all overloads, perform +"argument type expansion". Union types can be expanded +into their constituent subtypes. For example, the type ``int | str`` can +be expanded into ``int`` and ``str``. + +Type expansion should be performed one argument at a time from left to +right. Each expansion results in sets of effective argument types. +For example, if there are two arguments whose types evaluate to +``int | str`` and ``int | bytes``, expanding the first argument type +results in two sets of argument types: ``(int, int | bytes)`` and +``(str, int | bytes)``. If type expansion for the second argument is required, +four sets of argument types are produced: ``(int, int)``, ``(int, bytes)``, +``(str, int)``, and ``(str, bytes)``. + +After each argument's expansion, return to step 2 and evaluate all +expanded argument lists. + +- If all argument lists evaluate successfully, combine their + respective return types by union to determine the final return type + for the call, and stop. +- If argument expansion has been applied to all arguments and one or + more of the expanded argument lists cannot be evaluated successfully, + generate an error and stop. + + +For additional details about argument type expansion, see +`argument-type-expansion`_ below. + +Step 4 +~~~~~~ + +If the argument list is compatible with two or more overloads, +determine whether one or more of the overloads has a variadic parameter +(either ``*args`` or ``**kwargs``) that maps to a corresponding argument +that supplies an indeterminate number of positional or keyword arguments. +If so, eliminate overloads that do not have a variadic parameter. + +- If this results in only one remaining candidate overload, it is + the winning match. Evaluate it as if it were a non-overloaded function + call and stop. +- If two or more candidate overloads remain, proceed to step 5. + +Step 5 +~~~~~~ + +For all arguments, determine whether all possible +:term:`materializations ` of the argument's type are assignable to +the corresponding parameter type for each of the remaining overloads. If so, +eliminate all of the subsequent remaining overloads. + +Consider the following example:: + + @overload + def example(x: list[int]) -> int: ... + @overload + def example(x: list[Any]) -> str: ... + @overload + def example(x: Any) -> Any: ... + + def test(a: list[Any]): + # All materializations of list[Any] will match either the first or + # second overload, so the third overload can be eliminated. + example(a) + +This rule eliminates overloads that will never be chosen even if the +caller eliminates types that include ``Any``. + +If the call involves more than one argument, all possible materializations of +every argument type must be assignable to its corresponding parameter type. +If this condition exists, all subsequent remaining overloads should be eliminated. + +Once this filtering process is applied for all arguments, examine the return +types of the remaining overloads. If these return types include type variables, +they should be replaced with their solved types. If the resulting return types +for all remaining overloads are :term:`equivalent`, proceed to step 6. + +If the return types are not equivalent, overload matching is ambiguous. In +this case, assume a return type of ``Any`` and stop. + +Step 6 +~~~~~~ + +Choose the first remaining candidate overload as the winning +match. Evaluate it as if it were a non-overloaded function call and stop. + +Examples +~~~~~~~~ + +Example 1:: + + @overload + def example1(x: int, y: str) -> int: ... + @overload + def example1(x: str) -> str: ... + + example1() # Error in step 1: no plausible overloads + example1(1, "") # Step 1 eliminates second overload + example1("") # Step 1 eliminates first overload + + example1("", "") # Error in step 2: no compatible overloads + example1(1) # Error in step 2: no compatible overloads + + +Example 2:: + + @overload + def example2(x: int, y: str, z: int) -> str: ... + @overload + def example2(x: int, y: int, z: int) -> int: ... + + def test(values: list[str | int]): + # In this example, argument type expansion is + # performed on the first two arguments. Expansion + # of the third is unnecessary. + r1 = example2(1, values[0], 1) + reveal_type(r1) # Should reveal str | int + + # Here, the types of all three arguments are expanded + # without success. + example2(values[0], values[1], values[2]) # Error in step 3 + + +Example 3:: + + @overload + def example3(x: int, /) -> tuple[int]: ... + @overload + def example3(x: int, y: int, /) -> tuple[int, int]: ... + @overload + def example3(*args: int) -> tuple[int, ...]: ... + + def test(val: list[int]): + # Step 1 eliminates second overload. Step 4 and + # step 5 do not apply. Step 6 picks the first + # overload. + r1 = example3(1) + reveal_type(r1) # Should reveal tuple[int] + + # Step 1 eliminates first overload. Step 4 and + # step 5 do not apply. Step 6 picks the second + # overload. + r2 = example3(1, 2) + reveal_type(r2) # Should reveal tuple[int, int] + + # Step 1 doesn't eliminate any overloads. Step 4 + # picks the third overload. + r3 = example3(*val) + reveal_type(r3) # Should reveal tuple[int, ...] + + +Example 4:: + + @overload + def example4(x: list[int], y: int) -> int: ... + @overload + def example4(x: list[str], y: str) -> int: ... + @overload + def example4(x: int, y: int) -> list[int]: ... + + def test(v1: list[Any], v2: Any): + # Step 2 eliminates the third overload. Step 5 + # determines that first and second overloads + # both apply and are ambiguous due to Any, but + # return types are consistent. + r1 = example4(v1, v2) + reveal_type(r1) # Reveals int + + # Step 2 eliminates the second overload. Step 5 + # determines that first and third overloads + # both apply and are ambiguous due to Any, and + # the return types are inconsistent. + r2 = example4(v2, 1) + reveal_type(r2) # Should reveal Any + + +.. _argument-type-expansion: + +Argument type expansion +^^^^^^^^^^^^^^^^^^^^^^^ + +When performing argument type expansion, a type that is equivalent to +a union of a finite set of subtypes should be expanded into its constituent +subtypes. This includes the following cases. + +1. Explicit unions: Each subtype of the union should be considered as a + separate argument type. For example, the type ``int | str`` should be expanded + into ``int`` and ``str``. + +2. ``bool`` should be expanded into ``Literal[True]`` and ``Literal[False]``. + +3. ``Enum`` types (other than those that derive from ``enum.Flag``) should + be expanded into their literal members. + +4. ``type[A | B]`` should be expanded into ``type[A]`` and ``type[B]``. + +5. Tuples of known length that contain expandable types should be expanded + into all possible combinations of their element types. For example, the type + ``tuple[int | str, bool]`` should be expanded into ``(int, Literal[True])``, + ``(int, Literal[False])``, ``(str, Literal[True])``, and + ``(str, Literal[False])``. + +The above list may not be exhaustive, and additional cases may be added in +the future as the type system evolves. diff --git a/docs/spec/protocol.rst b/docs/spec/protocol.rst new file mode 100644 index 000000000..2803da0a6 --- /dev/null +++ b/docs/spec/protocol.rst @@ -0,0 +1,656 @@ +.. _protocols: + +Protocols +--------- + +(Originally specified in :pep:`544`.) + +Terminology +^^^^^^^^^^^ + +The term *protocols* is used for some types supporting :term:`structural` +subtyping. The reason is that the term *iterator protocol*, +for example, is widely understood in the community, and coming up with +a new term for this concept in a statically typed context would just create +confusion. + +This has the drawback that the term *protocol* becomes overloaded with +two subtly different meanings: the first is the traditional, well-known but +slightly fuzzy concept of protocols such as iterator; the second is the more +explicitly defined concept of protocols in statically typed code. +The distinction is not important most of the time, and in other +cases we can just add a qualifier such as *protocol classes* +when referring to the static type concept. + +If a class includes a protocol in its MRO, the class is called an *explicit* +subclass of the protocol. If a class defines all attributes and methods of a +protocol with types that are :term:`assignable` to the types of the protocol's +attributes and methods, it is said to implement the protocol and to be +assignable to the protocol. If a class is assignable to a protocol but the +protocol is not included in the MRO, the class is *implicitly* assignable to +the protocol. (Note that one can explicitly subclass a protocol and still not +implement it if a protocol attribute is set to ``None`` in the subclass. See +Python :py:ref:`data model ` for details.) + +The attributes (variables and methods) of a protocol that are mandatory for +another class for it to be assignable to the protocol are called "protocol +members". + +.. _protocol-definition: + +Defining a protocol +^^^^^^^^^^^^^^^^^^^ + +Protocols are defined by including a :term:`special form` ``typing.Protocol`` +(an instance of ``abc.ABCMeta``) in the base classes list, typically +at the end of the list. Here is a simple example:: + + from typing import Protocol + + class SupportsClose(Protocol): + def close(self) -> None: + ... + +Now if one defines a class ``Resource`` with a ``close()`` method whose type +signature is :term:`assignable` to ``SupportsClose.close``, it would implicitly +be assignable to ``SupportsClose``, since :term:`structural` assignability is +used for protocol types:: + + class Resource: + ... + def close(self) -> None: + self.file.close() + self.lock.release() + +Apart from a few restrictions explicitly mentioned below, protocol types can +be used in every context where normal types can:: + + def close_all(things: Iterable[SupportsClose]) -> None: + for t in things: + t.close() + + f = open('foo.txt') + r = Resource() + close_all([f, r]) # OK! + close_all([1]) # Error: 'int' has no 'close' method + +Note that both the user-defined class ``Resource`` and the built-in ``IO`` type +(the return type of ``open()``) are assignable to ``SupportsClose``, because +each provides a ``close()`` method with an assignable type signature. + + +Protocol members +^^^^^^^^^^^^^^^^ + +All methods defined in the protocol class body are protocol members, both +normal and decorated with ``@abstractmethod``. If any parameters of a +protocol method are not annotated, then their types are assumed to be ``Any`` +(see :ref:`"The meaning of annotations" `). Bodies of protocol methods are type checked. +An abstract method that should not be called via ``super()`` ought to raise +``NotImplementedError``. Example:: + + from typing import Protocol + from abc import abstractmethod + + class Example(Protocol): + def first(self) -> int: # This is a protocol member + return 42 + + @abstractmethod + def second(self) -> int: # Method without a default implementation + raise NotImplementedError + +Static methods, class methods, and properties are equally allowed +in protocols. + +To define a protocol variable, one can use variable +annotations in the class body. Additional attributes *only* defined in +the body of a method by assignment via ``self`` are not allowed. The rationale +for this is that the protocol class implementation is often not shared by +subtypes, so the interface should not depend on the default implementation. +Examples:: + + from typing import Protocol + + class Template(Protocol): + name: str # This is a protocol member + value: int = 0 # This one too (with default) + + def method(self) -> None: + self.temp: list[int] = [] # Error in type checker + + class Concrete: + def __init__(self, name: str, value: int) -> None: + self.name = name + self.value = value + + def method(self) -> None: + return + + var: Template = Concrete('value', 42) # OK + +To distinguish between protocol class variables and protocol instance +variables, the special :ref:`ClassVar ` annotation should be used. +By default, protocol variables as defined above are considered +readable and writable. To define a read-only protocol variable, one can use +an (abstract) property. + + +Explicitly declaring implementation +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +To explicitly declare that a certain class implements a given protocol, +it can be used as a regular base class. In this case a class could use +default implementations of protocol members. Static analysis tools are +expected to automatically detect that a class implements a given protocol. +So while it's possible to subclass a protocol explicitly, it's *not necessary* +to do so for the sake of type-checking. + +The default implementations cannot be used if the assignable-to relationship is +implicit and only :term:`structural` -- the semantics of inheritance is not +changed. Examples:: + + class PColor(Protocol): + @abstractmethod + def draw(self) -> str: + ... + def complex_method(self) -> int: + # some complex code here + + class NiceColor(PColor): + def draw(self) -> str: + return "deep blue" + + class BadColor(PColor): + def draw(self) -> str: + return super().draw() # Error, no default implementation + + class ImplicitColor: # Note no 'PColor' base here + def draw(self) -> str: + return "probably gray" + def complex_method(self) -> int: + # class needs to implement this + + nice: NiceColor + another: ImplicitColor + + def represent(c: PColor) -> None: + print(c.draw(), c.complex_method()) + + represent(nice) # OK + represent(another) # Also OK + +Note that there is little difference between explicitly subclassing and +implicitly implementing the protocol; the main benefit of explicit subclassing +is to get some protocol methods "for free". In addition, type checkers can +statically verify that the class actually implements the protocol correctly:: + + class RGB(Protocol): + rgb: tuple[int, int, int] + + @abstractmethod + def intensity(self) -> int: + return 0 + + class Point(RGB): + def __init__(self, red: int, green: int, blue: str) -> None: + self.rgb = red, green, blue # Error, 'blue' must be 'int' + + # Type checker might warn that 'intensity' is not defined + +A class can explicitly inherit from multiple protocols and also from normal +classes. In this case methods are resolved using normal MRO and a type checker +verifies that all member assignability is correct. The semantics of +``@abstractmethod`` is not changed; all of them must be implemented by an +explicit subclass before it can be instantiated. + + +Merging and extending protocols +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The general philosophy is that protocols are mostly like regular ABCs, but a +static type checker will handle them specially. Subclassing a protocol class +would not turn the subclass into a protocol unless it also has +``typing.Protocol`` as an explicit base class. Without this base, the class is +"downgraded" to a regular ABC that cannot be used with :term:`structural` +subtyping. The rationale for this rule is that we don't want to accidentally +have some class act as a protocol just because one of its base classes happens +to be one. We still slightly prefer :term:`nominal` subtyping over structural +subtyping in the static typing world. + +A subprotocol can be defined by having *both* one or more protocols as +immediate base classes and also having ``typing.Protocol`` as an immediate +base class:: + + from typing import Protocol + from collections.abc import Sized + + class SizedAndClosable(Sized, Protocol): + def close(self) -> None: + ... + +Now the protocol ``SizedAndClosable`` is a protocol with two methods, +``__len__`` and ``close``. If one omits ``Protocol`` in the base class list, +this would be a regular (non-protocol) class that must implement ``Sized``. +Alternatively, one can implement ``SizedAndClosable`` protocol by merging +the ``SupportsClose`` protocol from the example in the `protocol-definition`_ section +with ``typing.Sized``:: + + from collections.abc import Sized + + class SupportsClose(Protocol): + def close(self) -> None: + ... + + class SizedAndClosable(Sized, SupportsClose, Protocol): + pass + +The two definitions of ``SizedAndClosable`` are equivalent. Subclass +relationships between protocols are not meaningful when considering +assignability, since :term:`structural` :term:`assignability ` is +the criterion, not the MRO. + +If ``Protocol`` is included in the base class list, all the other base classes +must be protocols. A protocol can't extend a regular class. +Note that rules around explicit subclassing are different +from regular ABCs, where abstractness is simply defined by having at least one +abstract method being unimplemented. Protocol classes must be marked +*explicitly*. + +.. _`generic-protocols`: + +Generic protocols +^^^^^^^^^^^^^^^^^ + +Generic protocols are important. For example, ``SupportsAbs``, ``Iterable`` +and ``Iterator`` are generic protocols. They are defined similar to normal +non-protocol generic types:: + + class Iterable[T](Protocol): + @abstractmethod + def __iter__(self) -> Iterator[T]: + ... + +The older (pre-3.12) syntax ``Protocol[T, S, ...]`` remains available as a +shorthand for ``Protocol, Generic[T, S, ...]``. It is an error to combine the +shorthand with ``Generic[T, S, ...]`` or to mix it with the new +``class Iterable[T]`` form:: + + class Iterable(Protocol[T], Generic[T]): # INVALID + ... + + class Iterable[T](Protocol[T]): # INVALID + ... + +When using the generics syntax introduced in Python 3.12, the variance of +type variables is inferred. When using the pre-3.12 generics syntax, variance +must be specified (unless ``infer_variance=True`` is used). Type checkers will +warn if the declared variance does not match the protocol definition. Examples:: + + T = TypeVar('T') + T_co = TypeVar('T_co', covariant=True) + T_contra = TypeVar('T_contra', contravariant=True) + + class Box(Protocol[T_co]): + def content(self) -> T_co: + ... + + box: Box[float] + second_box: Box[int] + box = second_box # This is OK due to the covariance of 'Box'. + + class Sender(Protocol[T_contra]): + def send(self, data: T_contra) -> int: + ... + + sender: Sender[float] + new_sender: Sender[int] + new_sender = sender # OK, 'Sender' is contravariant. + + class Proto(Protocol[T]): + attr: T # this class is invariant, since it has a mutable attribute + + var: Proto[float] + another_var: Proto[int] + var = another_var # Error! 'Proto[float]' is not assignable to 'Proto[int]'. + +Note that unlike nominal classes, de facto covariant protocols cannot be +declared as invariant, since this can break transitivity of subtyping. +For example:: + + T = TypeVar('T') + + class AnotherBox(Protocol[T]): # Error, this protocol is covariant in T, + def content(self) -> T: # not invariant. + ... + + +Recursive protocols +^^^^^^^^^^^^^^^^^^^ + +Recursive protocols are also supported. Forward references to the protocol +class names can be :ref:`given as strings `. Recursive +protocols are useful for representing self-referential data structures +like trees in an abstract fashion:: + + class Traversable(Protocol): + def leaves(self) -> Iterable['Traversable']: + ... + +Note that for recursive protocols, a class is considered assignable to +the protocol in situations where the decision depends on itself. +Continuing the previous example:: + + class SimpleTree: + def leaves(self) -> list['SimpleTree']: + ... + + root: Traversable = SimpleTree() # OK + + class Tree(Generic[T]): + def leaves(self) -> list['Tree[T]']: + ... + + def walk(graph: Traversable) -> None: + ... + tree: Tree[float] = Tree() + walk(tree) # OK, 'Tree[float]' is assignable to 'Traversable' + + +Self-types in protocols +^^^^^^^^^^^^^^^^^^^^^^^ + +The self-types in protocols follow the +:ref:`rules for other methods `. For example:: + + class Copyable(Protocol): + def copy[C: Copyable](self: C) -> C: + + class One: + def copy(self) -> 'One': + ... + + class Other: + def copy[T: Other](self: T) -> T: + ... + + c: Copyable + c = One() # OK + c = Other() # Also OK + +Assignability relationships with other types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Protocols cannot be instantiated, so there are no values whose +runtime type is a protocol. For variables and parameters with protocol types, +assignability relationships are subject to the following rules: + +* A protocol is never assignable to a concrete type. +* A concrete type ``X`` is assignable to a protocol ``P`` if and only if ``X`` + implements all protocol members of ``P`` with assignable types. In other + words, :term:`assignability ` with respect to a protocol is + always :term:`structural`. +* A protocol ``P1`` is assignable to another protocol ``P2`` if ``P1`` defines + all protocol members of ``P2`` with assignable types. + +Generic protocol types follow the same rules of variance as non-protocol +types. Protocol types can be used in all contexts where any other types +can be used, such as in unions, ``ClassVar``, type variables bounds, etc. +Generic protocols follow the rules for generic abstract classes, except for +using structural assignability instead of assignability defined by +inheritance relationships. + +Static type checkers will recognize protocol implementations, even if the +corresponding protocols are *not imported*:: + + # file lib.py + from collections.abc import Sized + + class ListLike[T](Sized, Protocol): + def append(self, x: T) -> None: + pass + + def populate(lst: ListLike[int]) -> None: + ... + + # file main.py + from lib import populate # Note that ListLike is NOT imported + + class MockStack: + def __len__(self) -> int: + return 42 + def append(self, x: int) -> None: + print(x) + + populate([1, 2, 3]) # Passes type check + populate(MockStack()) # Also OK + + +Unions and intersections of protocols +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Unions of protocol classes behaves the same way as for non-protocol +classes. For example:: + + from typing import Protocol + + class Exitable(Protocol): + def exit(self) -> int: + ... + class Quittable(Protocol): + def quit(self) -> int | None: + ... + + def finish(task: Exitable | Quittable) -> int: + ... + class DefaultJob: + ... + def quit(self) -> int: + return 0 + finish(DefaultJob()) # OK + +One can use multiple inheritance to define an intersection of protocols. +Example:: + + from collections.abc import Iterable, Hashable + + class HashableFloats(Iterable[float], Hashable, Protocol): + pass + + def cached_func(args: HashableFloats) -> float: + ... + cached_func((1, 2, 3)) # OK, tuple is both hashable and iterable + + +``type[]`` and class objects vs protocols +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Variables and parameters annotated with ``type[Proto]`` accept only concrete +(non-protocol) :term:`consistent subtypes ` of ``Proto``. +The main reason for this is to allow instantiation of parameters with such +types. For example:: + + class Proto(Protocol): + @abstractmethod + def meth(self) -> int: + ... + class Concrete: + def meth(self) -> int: + return 42 + + def fun(cls: type[Proto]) -> int: + return cls().meth() # OK + fun(Proto) # Error + fun(Concrete) # OK + +The same rule applies to variables:: + + var: Type[Proto] + var = Proto # Error + var = Concrete # OK + var().meth() # OK + +Assigning an ABC or a protocol class to a variable is allowed if it is +not explicitly typed, and such assignment creates a type alias. +For normal (non-abstract) classes, the behavior of ``type[]`` is +not changed. + +A class object is considered an implementation of a protocol if accessing +all members on it results in types assignable to the types of the protocol members. +For example:: + + from typing import Any, Protocol + + class ProtoA(Protocol): + def meth(self, x: int) -> int: ... + class ProtoB(Protocol): + def meth(self, obj: Any, x: int) -> int: ... + + class C: + def meth(self, x: int) -> int: ... + + a: ProtoA = C # Type check error, signatures don't match! + b: ProtoB = C # OK + +.. _`protocol-newtype-aliases`: + +``NewType()`` and type aliases +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Protocols are essentially anonymous. To emphasize this point, static type +checkers might refuse protocol classes inside ``NewType()`` to avoid an +illusion that a distinct type is provided:: + + from typing import NewType, Protocol + from collections.abc import Iterator + + class Id(Protocol): + code: int + secrets: Iterator[bytes] + + UserId = NewType('UserId', Id) # Error, can't provide distinct type + +In contrast, type aliases are fully supported, including generic type +aliases:: + + from collections.abc import Reversible, Iterable, Sized + + class SizedIterable[T](Iterable[T], Sized, Protocol): + pass + + type CompatReversible[T] = Reversible[T] | SizedIterable[T] + + +Modules as implementations of protocols +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A module object is accepted where a protocol is expected if the public +interface of the given module is assignable to the expected protocol. +For example:: + + # file default_config.py + timeout = 100 + one_flag = True + other_flag = False + + # file main.py + import default_config + from typing import Protocol + + class Options(Protocol): + timeout: int + one_flag: bool + other_flag: bool + + def setup(options: Options) -> None: + ... + + setup(default_config) # OK + +To determine assignability of module level functions, the ``self`` argument +of the corresponding protocol methods is dropped. For example:: + + # callbacks.py + def on_error(x: int) -> None: + ... + def on_success() -> None: + ... + + # main.py + import callbacks + from typing import Protocol + + class Reporter(Protocol): + def on_error(self, x: int) -> None: + ... + def on_success(self) -> None: + ... + + rp: Reporter = callbacks # Passes type check + +.. _`runtime-checkable`: + +``@runtime_checkable`` decorator and narrowing types by ``isinstance()`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The default semantics is that ``isinstance()`` and ``issubclass()`` fail +for protocol types. This is in the spirit of duck typing -- protocols +basically would be used to model duck typing statically, not explicitly +at runtime. + +However, it should be possible for protocol types to implement custom +instance and class checks when this makes sense, similar to how ``Iterable`` +and other ABCs in ``collections.abc`` and ``typing`` already do it, +but this is limited to non-generic and unsubscripted generic protocols +(``Iterable`` is statically equivalent to ``Iterable[Any]``). +The ``typing`` module will define a special ``@runtime_checkable`` class decorator +that provides the same semantics for class and instance checks as for +``collections.abc`` classes, essentially making them "runtime protocols":: + + from typing import runtime_checkable, Protocol + + @runtime_checkable + class SupportsClose(Protocol): + def close(self): + ... + + assert isinstance(open('some/file'), SupportsClose) + +Note that instance checks are not 100% reliable statically, which is why +this behavior is opt-in. +The most type checkers can do is to treat ``isinstance(obj, Iterator)`` +roughly as a simpler way to write +``hasattr(x, '__iter__') and hasattr(x, '__next__')``. To minimize +the risks for this feature, the following rules are applied. + +**Definitions**: + +* *Data and non-data protocols*: A protocol is called a non-data protocol + if it only contains methods as members (for example ``Sized``, + ``Iterator``, etc). A protocol that contains at least one non-method member + (like ``x: int``) is called a data protocol. +* *Unsafe overlap*: A type ``X`` is called unsafely overlapping with a protocol + ``P``, if ``X`` is not assignable to ``P``, but it is assignable to the + type-erased version of ``P`` where all members have type ``Any``. In + addition, if at least one element of a union unsafely overlaps with a + protocol ``P``, then the whole union is unsafely overlapping with ``P``. + +**Specification**: + +* A protocol can be used as a second argument in ``isinstance()`` and + ``issubclass()`` only if it is explicitly opt-in by ``@runtime_checkable`` + decorator. This requirement exists because protocol checks are not type safe + in case of dynamically set attributes, and because type checkers can only prove + that an ``isinstance()`` check is safe only for a given class, not for all its + subclasses. +* ``isinstance()`` can be used with both data and non-data protocols, while + ``issubclass()`` can be used only with non-data protocols. This restriction + exists because some data attributes can be set on an instance in constructor + and this information is not always available on the class object. +* Type checkers should reject an ``isinstance()`` or ``issubclass()`` call, if + there is an unsafe overlap between the type of the first argument and + the protocol. +* Type checkers should be able to select a correct element from a union after + a safe ``isinstance()`` or ``issubclass()`` call. For narrowing from non-union + types, type checkers can use their best judgement (this is intentionally + unspecified, since a precise specification would require intersection types). diff --git a/docs/spec/qualifiers.rst b/docs/spec/qualifiers.rst new file mode 100644 index 000000000..6814692e0 --- /dev/null +++ b/docs/spec/qualifiers.rst @@ -0,0 +1,381 @@ +.. _`type-qualifiers`: + +Type qualifiers +=============== + +This chapter describes the behavior of some :term:`type qualifiers `. +Additional type qualifiers are covered in other chapters: + +* :ref:`ClassVar ` +* :ref:`NotRequired ` +* :ref:`ReadOnly ` +* :ref:`Required ` + +.. _`at-final`: + +``@final`` +---------- + +(Originally specified in :pep:`591`.) + +The ``typing.final`` decorator is used to restrict the use of +inheritance and overriding. + +A type checker should prohibit any class decorated with ``@final`` +from being subclassed and any method decorated with ``@final`` from +being overridden in a subclass. The method decorator version may be +used with all of instance methods, class methods, static methods, and properties. + +For example:: + + from typing import final + + @final + class Base: + ... + + class Derived(Base): # Error: Cannot inherit from final class "Base" + ... + +and:: + + from typing import final + + class Base: + @final + def foo(self) -> None: + ... + + class Derived(Base): + def foo(self) -> None: # Error: Cannot override final attribute "foo" + # (previously declared in base class "Base") + ... + + +For overloaded methods, ``@final`` should be placed on the +implementation (or on the first overload, for stubs):: + + from typing import Any, overload + + class Base: + @overload + def method(self) -> None: ... + @overload + def method(self, arg: int) -> int: ... + @final + def method(self, x=None): + ... + +It is an error to use ``@final`` on a non-method function. + +.. _`uppercase-final`: + +``Final`` +--------- + +(Originally specified in :pep:`591`.) + +The ``typing.Final`` :term:`type qualifier` is used to indicate that a +variable or attribute should not be reassigned, redefined, or overridden. + +Syntax +^^^^^^ + +``Final`` may be used in one of several forms: + +* With an explicit type, using the syntax ``Final[]``. Example:: + + ID: Final[float] = 1 + +* With no type annotation. Example:: + + ID: Final = 1 + + The typechecker should apply its usual type inference mechanisms to + determine the type of ``ID`` (here, likely, ``int``). Note that unlike for + generic classes this is *not* the same as ``Final[Any]``. + +* In class bodies and stub files you can omit the right hand side and just write + ``ID: Final[float]``. If the right hand side is omitted, there must + be an explicit type argument to ``Final``. + +* Finally, as ``self.id: Final = 1`` (also optionally with a type in + square brackets). This is allowed *only* in ``__init__`` methods, so + that the final instance attribute is assigned only once when an + instance is created. + + +Semantics and examples +^^^^^^^^^^^^^^^^^^^^^^ + +The two main rules for defining a final name are: + +* There can be *at most one* final declaration per module or class for + a given attribute. There can't be separate class-level and instance-level + constants with the same name. + +* There must be *exactly one* assignment to a final name. + +This means a type checker should prevent further assignments to final +names in type-checked code:: + + from typing import Final + + RATE: Final = 3000 + + class Base: + DEFAULT_ID: Final = 0 + + RATE = 300 # Error: can't assign to final attribute + Base.DEFAULT_ID = 1 # Error: can't override a final attribute + +Note that a type checker need not allow ``Final`` declarations inside loops +since the runtime will see multiple assignments to the same variable in +subsequent iterations. + +Additionally, a type checker should prevent final attributes from +being overridden in a subclass:: + + from typing import Final + + class Window: + BORDER_WIDTH: Final = 2.5 + ... + + class ListView(Window): + BORDER_WIDTH = 3 # Error: can't override a final attribute + +A final attribute declared in a class body without an initializer must +be initialized in the ``__init__`` method (except in stub files):: + + class ImmutablePoint: + x: Final[int] + y: Final[int] # Error: final attribute without an initializer + + def __init__(self) -> None: + self.x = 1 # Good + +The generated ``__init__`` method of :doc:`dataclasses` qualifies for this +requirement: a bare ``x: Final[int]`` is permitted in a dataclass body, because +the generated ``__init__`` will initialize ``x``. + +Type checkers should infer a final attribute that is initialized in a class +body as being a class variable, except in the case of :doc:`dataclasses`, where +``x: Final[int] = 3`` creates a dataclass field and instance-level final +attribute ``x`` with default value ``3``; ``x: ClassVar[Final[int]] = 3`` is +necessary to create a final class variable with value ``3``. In +non-dataclasses, combining ``ClassVar`` and ``Final`` is redundant, and type +checkers may choose to warn or error on the redundancy. + +``Final`` may only be used in assignments or variable annotations. Using it in +any other position is an error. In particular, ``Final`` can't be used in +annotations for function arguments:: + + x: list[Final[int]] = [] # Error! + + def fun(x: Final[List[int]]) -> None: # Error! + ... + +``Final`` may be wrapped only by other type qualifiers (e.g. ``ClassVar`` or +``Annotated``). It cannot be used in a type parameter (e.g. +``list[Final[int]]`` is not permitted.) + +Note that declaring a name as final only guarantees that the name will +not be re-bound to another value, but does not make the value +immutable. Immutable ABCs and containers may be used in combination +with ``Final`` to prevent mutating such values:: + + x: Final = ['a', 'b'] + x.append('c') # OK + + y: Final[Sequence[str]] = ['a', 'b'] + y.append('x') # Error: "Sequence[str]" has no attribute "append" + z: Final = ('a', 'b') # Also works + + +Type checkers should treat uses of a final name that was initialized +with a literal as if it was replaced by the literal. For example, the +following should be allowed:: + + from typing import NamedTuple, Final + + X: Final = "x" + Y: Final = "y" + N = NamedTuple("N", [(X, int), (Y, int)]) + +``Final`` cannot be used as a qualifier for a :ref:`TypedDict ` +item or a :ref:`NamedTuple ` field. Such usage also generates +an error at runtime. + + +Importing ``Final`` Variables +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If a module declares a ``Final`` variable and another module imports that +variable in an import statement by name or wildcard, the imported symbol +inherits the ``Final`` type qualifier. Any attempt to assign a different value +to this symbol should be flagged as an error by a type checker:: + + # lib/submodule.py + from typing import Final + PI: Final = 3.14 + + # lib/__init__.py + from .submodule import PI # PI is Final + + # test1.py + from lib import PI + PI = 0 # Error: Can't assign to Final value + + from lib import PI as PI2 + PI2 = 0 # Error: Can't assign to Final value + + # test2.py + from lib import * + PI = 0 # Error: Can't assign to Final value + + +.. _`annotated`: + +``Annotated`` +------------- + +(Originally specified by :pep:`593`.) + +Syntax +^^^^^^ + +``Annotated`` is parameterized with a *base expression* and at least one +Python value representing associated *metadata*:: + + from typing import Annotated + + Annotated[BaseExpr, Metadata1, Metadata2, ...] + +Here are the specific details of the syntax: + +* The base expression (the first argument to ``Annotated``) must be valid + in the context where it is being used: + + * If ``Annotated`` is used in a place where arbitrary + :term:`annotation expressions ` are allowed, + the base expression may be an annotation expression. + * Otherwise, the base expression must be a valid :term:`type expression`. + +* Multiple metadata elements are supported (``Annotated`` supports variadic + arguments):: + + Annotated[int, ValueRange(3, 10), ctype("char")] + +* There must be at least one metadata element (``Annotated[int]`` is not valid) + +* The order of the metadata is preserved and matters for equality + checks:: + + Annotated[int, ValueRange(3, 10), ctype("char")] != Annotated[ + int, ctype("char"), ValueRange(3, 10) + ] + +* Nested ``Annotated`` types are flattened, with metadata ordered + starting with the innermost ``Annotated`` expression:: + + Annotated[Annotated[int, ValueRange(3, 10)], ctype("char")] == Annotated[ + int, ValueRange(3, 10), ctype("char") + ] + +* Duplicated metadata elements are not removed:: + + Annotated[int, ValueRange(3, 10)] != Annotated[ + int, ValueRange(3, 10), ValueRange(3, 10) + ] + +* ``Annotated`` can be used in definition of nested and generic aliases, + but only if it wraps a :term:`type expression`:: + + type Vec[T] = Annotated[list[tuple[T, T]], MaxLen(10)] + type V = Vec[int] # Annotated[list[tuple[int, int]], MaxLen(10)] + +* As with most :term:`special forms `, ``Annotated`` is not assignable to + ``type`` or ``type[T]``:: + + v1: type[int] = Annotated[int, ""] # Type error + + SmallInt: TypeAlias = Annotated[int, ValueRange(0, 100)] + v2: type[Any] = SmallInt # Type error + +* An attempt to call ``Annotated`` (whether parameterized or not) should be + treated as a type error by type checkers:: + + Annotated() # Type error + Annotated[int, ""](0) # Type error + + SmallInt = Annotated[int, ValueRange(0, 100)] + SmallInt(1) # Type error + +:pep:`593` and an earlier version of this specification used the term +"annotations" instead of "metadata" for the extra arguments to +``Annotated``. The term "annotations" is deprecated to avoid confusion +with the parameter, return, and variable annotations that are part of +the Python syntax. + +Meaning +^^^^^^^ + +The metadata provided by ``Annotated`` can be used for either static +or runtime analysis. If a library (or tool) encounters an instance of +``Annotated[T, x]`` and has no special logic for metadata element ``x``, it +should ignore it and treat the expression as equivalent to ``T``. Thus, in general, +any :term:`type expression` or :term:`annotation expression` may be +wrapped in ``Annotated`` without changing the meaning of the +wrapped expression. However, type +checkers may additionally choose to recognize particular metadata elements and use +them to implement extensions to the standard type system. + +``Annotated`` metadata may apply either to the base expression or to the symbol +being annotated, or even to some other aspect of the program. + +Consuming metadata +^^^^^^^^^^^^^^^^^^ + +Ultimately, deciding how to interpret the metadata (if +at all) is the responsibility of the tool or library encountering the +``Annotated`` type. A tool or library encountering an ``Annotated`` type +can scan through the metadata to determine if they are of interest +(e.g., using ``isinstance()``). + +**Unknown metadata:** When a tool or a library does not support +metadata or encounters an unknown metadata element, it should ignore it +and treat the annotation as the base expression. + +**Namespacing metadata:** Namespaces are not needed for metadata since +the class of the metadata object acts as a namespace. + +**Multiple metadata elements:** It's up to the tool consuming the metadata +to decide whether the client is allowed to have several metadata elements on +one annotation and how to merge those elements. + +Since the ``Annotated`` type allows you to put several metadata elements of +the same (or different) type(s) on any annotation, the tools or libraries +consuming the metadata are in charge of dealing with potential +duplicates. For example, if you are doing value range analysis you might +allow this:: + + T1 = Annotated[int, ValueRange(-10, 5)] + T2 = Annotated[T1, ValueRange(-20, 3)] + +Flattening nested annotations, this translates to:: + + T2 = Annotated[int, ValueRange(-10, 5), ValueRange(-20, 3)] + +Aliases & Concerns over verbosity +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Writing ``typing.Annotated`` everywhere can be quite verbose; +fortunately, the ability to alias types means that in practice we +don't expect clients to have to write lots of boilerplate code:: + + type Const[T] = Annotated[T, my_annotations.CONST] + + class C: + def const_method(self, x: Const[list[int]]) -> int: + ... diff --git a/docs/spec/special-types.rst b/docs/spec/special-types.rst new file mode 100644 index 000000000..eab658b07 --- /dev/null +++ b/docs/spec/special-types.rst @@ -0,0 +1,219 @@ +.. _`special-types`: + +Special types in annotations +============================ + +.. _`any`: + +``Any`` +------- + +``Any`` represents an unknown static type. + +Every type is :term:`assignable` to ``Any``, and ``Any`` is assignable to every +type. + +See :ref:`type-system-concepts` for more discussion of ``Any``. + +A function parameter without an annotation is assumed to be annotated with +``Any``. If a generic type is used without specifying type parameters, +they are assumed to be ``Any``:: + + from collections.abc import Mapping + + def use_map(m: Mapping) -> None: # Same as Mapping[Any, Any] + ... + +This rule also applies to ``tuple``, in annotation context it is equivalent +to ``tuple[Any, ...]``. As well, a bare +``Callable`` in an annotation is equivalent to ``Callable[..., Any]``:: + + from collections.abc import Callable + + def check_args(args: tuple) -> bool: + ... + + check_args(()) # OK + check_args((42, 'abc')) # Also OK + check_args(3.14) # Flagged as error by a type checker + + # A list of arbitrary callables is accepted by this function + def apply_callbacks(cbs: list[Callable]) -> None: + ... + +``Any`` can also be used as a base class. This can be useful for +avoiding type checker errors with classes that can duck type anywhere or +are highly dynamic. + +.. _`none`: + +``None`` +-------- + +When used in a type hint, the expression ``None`` is considered +equivalent to ``type(None)``. + +.. _`noreturn`: + +``NoReturn`` +------------ + +The ``typing`` module provides a :term:`special form` ``NoReturn`` to annotate functions +that never return normally. For example, a function that unconditionally +raises an exception:: + + from typing import NoReturn + + def stop() -> NoReturn: + raise RuntimeError('no way') + +The ``NoReturn`` annotation is used for functions such as ``sys.exit``. +Static type checkers will ensure that functions annotated as returning +``NoReturn`` truly never return, either implicitly or explicitly:: + + import sys + from typing import NoReturn + + def f(x: int) -> NoReturn: # Error, f(0) implicitly returns None + if x != 0: + sys.exit(1) + +The checkers will also recognize that the code after calls to such functions +is unreachable and will behave accordingly:: + + # continue from first example + def g(x: int) -> int: + if x > 0: + return x + stop() + return 'whatever works' # Error might be not reported by some checkers + # that ignore errors in unreachable blocks + +.. _`never`: + +``Never`` +--------- + +Since Python 3.11, the ``typing`` module contains a :term:`special form` +``Never``. It represents the bottom type, a type that represents the empty set +of Python objects. + +The ``Never`` type is equivalent to ``NoReturn``, which is discussed above. +The ``NoReturn`` type is conventionally used in return annotations of +functions, and ``Never`` is typically used in other locations, but the two +types are completely interchangeable. + +.. _`numeric-promotions`: + +Special cases for ``float`` and ``complex`` +------------------------------------------- + +Python's numeric types ``complex``, ``float`` and ``int`` are not +subtypes of each other, but to support common use cases, the type +system contains a straightforward shortcut: +when an argument is annotated as having +type ``float``, an argument of type ``int`` is acceptable; similar, +for an argument annotated as having type ``complex``, arguments of +type ``float`` or ``int`` are acceptable. + +.. _`type-brackets`: + +``type[]`` +---------- + +Sometimes you want to talk about class objects, in particular class +objects that inherit from a given class. This can be spelled as +``type[C]`` where ``C`` is a class. To clarify: while ``C`` (when +used as an annotation) refers to instances of class ``C``, ``type[C]`` +refers to *subclasses* of ``C``. (This is a similar distinction as +between ``object`` and ``type``.) + +For example, suppose we have the following classes:: + + class User: ... # Abstract base for User classes + class BasicUser(User): ... + class ProUser(User): ... + class TeamUser(User): ... + +And suppose we have a function that creates an instance of one of +these classes if you pass it a class object:: + + def new_user(user_class): + user = user_class() + # (Here we could write the user object to a database) + return user + +Without subscripting ``type[]`` the best we could do to annotate ``new_user()`` +would be:: + + def new_user(user_class: type) -> User: + ... + +However using ``type[]`` and a type variable with an upper bound we +can do much better:: + + def new_user[U: User](user_class: type[U]) -> U: + ... + +Now when we call ``new_user()`` with a specific subclass of ``User`` a +type checker will infer the correct type of the result:: + + joe = new_user(BasicUser) # Inferred type is BasicUser + +The value corresponding to ``type[C]`` must be an actual class object +that's a subtype of ``C``, not a :term:`special form` or other kind of type. +In other words, in the +above example calling e.g. ``new_user(BasicUser | ProUser)`` is +rejected by the type checker (in addition to failing at runtime +because you can't instantiate a union). + +Note that it is legal to use a union of classes as the parameter for +``type[]``, as in:: + + def new_non_team_user(user_class: type[BasicUser | ProUser]): + user = new_user(user_class) + ... + +``type[]`` distributes over unions: +``type[A | B]`` is :term:`equivalent` to ``type[A] | type[B]``. + +However, the actual argument passed in at runtime must still be a +concrete class object, e.g. in the above example:: + + new_non_team_user(ProUser) # OK + new_non_team_user(TeamUser) # Disallowed by type checker + +``type[Any]`` is also supported (see below for its meaning). + +``type[T]`` where ``T`` is a type variable is allowed when annotating the +first argument of a class method (see the relevant section). + +Any other :term:`special forms ` like ``Callable`` are not +allowed as an argument to ``type``. + +There are some concerns with this feature: for example when +``new_user()`` calls ``user_class()`` this implies that all subclasses +of ``User`` must support this in their constructor signature. However +this is not unique to ``type[]``: class methods have similar concerns. +A type checker ought to flag violations of such assumptions, but by +default constructor calls that match the constructor signature in the +indicated base class (``User`` in the example above) should be +allowed. A program containing a complex or extensible class hierarchy +might also handle this by using a factory class method. + +When ``type`` is parameterized it requires exactly one parameter. +Plain ``type`` without brackets, the root of Python's metaclass +hierarchy, is equivalent to ``type[Any]``. + +Regarding the behavior of ``type[Any]`` (or ``type``), +accessing attributes of a variable with this type only provides +attributes and methods defined by ``type`` (for example, +``__repr__()`` and ``__mro__``). Such a variable can be called with +arbitrary arguments, and the return type is ``Any``. + +``type`` is covariant in its parameter, because ``type[Derived]`` is a +subtype of ``type[Base]``:: + + def new_pro_user(pro_user_class: type[ProUser]): + user = new_user(pro_user_class) # OK + ... diff --git a/docs/spec/tuples.rst b/docs/spec/tuples.rst new file mode 100644 index 000000000..40afc732b --- /dev/null +++ b/docs/spec/tuples.rst @@ -0,0 +1,156 @@ +.. _`tuples`: + +Tuples +====== + +The ``tuple`` class has some special behaviors and properties that make it +different from other classes from a typing perspective. The most obvious +difference is that ``tuple`` is variadic -- it supports an arbitrary number +of type arguments. At runtime, the sequence of objects contained within the +tuple is fixed at the time of construction. Elements cannot be added, removed, +reordered, or replaced after construction. These properties affect subtyping +rules and other behaviors as described below. + + +Tuple Type Form +--------------- + +The type of a tuple can be expressed by listing the element types. For +example, ``tuple[int, int, str]`` is a tuple containing an ``int``, another +``int``, and a ``str``. + +The empty tuple can be annotated as ``tuple[()]``. + +Arbitrary-length homogeneous tuples can be expressed using one type and an +ellipsis, for example ``tuple[int, ...]``. This type is equivalent to a union +of tuples containing zero or more ``int`` elements (``tuple[()] | +tuple[int] | tuple[int, int] | tuple[int, int, int] | ...``). +Arbitrary-length homogeneous tuples are sometimes referred to as "unbounded +tuples". Both of these terms appear within the typing spec, and they refer to +the same concept. + +The type ``tuple[Any, ...]`` is special in that it is :term:`consistent` with +all tuple types, and :term:`assignable` to a tuple of any length. This is +useful for gradual typing. The type ``tuple`` (with no type arguments provided) +is equivalent to ``tuple[Any, ...]``. + +Arbitrary-length tuples have exactly two type arguments -- the type and +an ellipsis. Any other tuple form that uses an ellipsis is invalid:: + + t1: tuple[int, ...] # OK + t2: tuple[int, int, ...] # Invalid + t3: tuple[...] # Invalid + t4: tuple[..., int] # Invalid + t5: tuple[int, ..., int] # Invalid + t6: tuple[*tuple[str], ...] # Invalid + t7: tuple[*tuple[str, ...], ...] # Invalid + + +Unpacked Tuple Form +------------------- + +An unpacked form of ``tuple`` (using an unpack operator ``*``) can be used +within a tuple type argument list. For example, ``tuple[int, *tuple[str]]`` +is equivalent to ``tuple[int, str]``. Unpacking an unbounded tuple preserves +the unbounded tuple as it is. That is, ``*tuple[int, ...]`` remains +``*tuple[int, ...]``; there's no simpler form. This enables us to specify +types such as ``tuple[int, *tuple[str, ...], str]`` -- a tuple type where the +first element is guaranteed to be of type ``int``, the last element is +guaranteed to be of type ``str``, and the elements in the middle are zero or +more elements of type ``str``. The type ``tuple[*tuple[int, ...]]`` is +equivalent to ``tuple[int, ...]``. + +If an unpacked ``*tuple[Any, ...]`` is embedded within another tuple, that +portion of the tuple is :term:`consistent` with any tuple of any length. + +Only one unbounded tuple can be used within another tuple:: + + t1: tuple[*tuple[str], *tuple[str]] # OK + t2: tuple[*tuple[str, *tuple[str, ...]]] # OK + t3: tuple[*tuple[str, ...], *tuple[int, ...]] # Type error + t4: tuple[*tuple[str, *tuple[str, ...]], *tuple[int, ...]] # Type error + +An unpacked TypeVarTuple counts as an unbounded tuple in the context of this rule:: + + def func[*Ts](t: tuple[*Ts]): + t5: tuple[*tuple[str], *Ts] # OK + t6: tuple[*tuple[str, ...], *Ts] # Type error + +The ``*`` syntax requires Python 3.11 or newer. For older versions of Python, +the ``typing.Unpack`` :term:`special form` can be used: +``tuple[int, Unpack[tuple[str, ...]], int]``. + +Unpacked tuples can also be used for ``*args`` parameters in a function +signature: ``def f(*args: *tuple[int, str]): ...``. Unpacked tuples +can also be used for specializing generic classes or type variables that are +parameterized using a ``TypeVarTuple``. For more details, see +:ref:`args_as_typevartuple`. + + +Type Compatibility Rules +------------------------ + +Because tuple contents are immutable, the element types of a tuple are covariant. +For example, ``tuple[bool, int]`` is a subtype of ``tuple[int, object]``. + +As discussed above, a homogeneous tuple of arbitrary length is equivalent +to a union of tuples of different lengths. That means ``tuple[()]``, +``tuple[int]`` and ``tuple[int, *tuple[int, ...]]`` are all subtypes of +``tuple[int, ...]``. The converse is not true; ``tuple[int, ...]`` is not a +subtype of ``tuple[int]``. + +The type ``tuple[Any, ...]`` is :term:`consistent` with any tuple:: + + def func(t1: tuple[int], t2: tuple[int, ...], t3: tuple[Any, ...]): + v1: tuple[int, ...] = t1 # OK + v2: tuple[Any, ...] = t1 # OK + + v3: tuple[int] = t2 # Type error + v4: tuple[Any, ...] = t2 # OK + + v5: tuple[float, float] = t3 # OK + v6: tuple[int, *tuple[str, ...]] = t3 # OK + + +The length of a tuple at runtime is immutable, so it is safe for type checkers +to use length checks to narrow the type of a tuple:: + + def func(val: tuple[int] | tuple[str, str] | tuple[int, *tuple[str, ...], int]): + if len(val) == 1: + # Type can be narrowed to tuple[int]. + reveal_type(val) # tuple[int] + + if len(val) == 2: + # Type can be narrowed to tuple[str, str] | tuple[int, int]. + reveal_type(val) # tuple[str, str] | tuple[int, int] + + if len(val) == 3: + # Type can be narrowed to tuple[int, str, int]. + reveal_type(val) # tuple[int, str, int] + +This property may also be used to safely narrow tuple types within a ``match`` +statement that uses sequence patterns. + +If a tuple element is a union type, the tuple can be safely expanded into a +union of tuples. For example, ``tuple[int | str]`` is equivalent to +``tuple[int] | tuple[str]``. If multiple elements are union types, full expansion +must consider all combinations. For example, ``tuple[int | str, int | str]`` is +equivalent to ``tuple[int, int] | tuple[int, str] | tuple[str, int] | tuple[str, str]``. +Unbounded tuples cannot be expanded in this manner. + +Type checkers may safely use this equivalency rule when narrowing tuple types:: + + def func(subj: tuple[int | str, int | str]): + match subj: + case x, str(): + reveal_type(subj) # tuple[int | str, str] + case y: + reveal_type(subj) # tuple[int | str, int] + +The ``tuple`` class derives from ``Sequence[T_co]`` where ``T_co`` is a covariant +(non-variadic) type variable. The specialized type of ``T_co`` should be computed +by a type checker as a supertype of all element types. +For example, ``tuple[int, *tuple[str, ...]]`` is a subtype of +``Sequence[int | str]`` or ``Sequence[object]``. + +A zero-length tuple (``tuple[()]``) is a subtype of ``Sequence[Never]``. diff --git a/docs/spec/type-forms.rst b/docs/spec/type-forms.rst new file mode 100644 index 000000000..4dc7c9e35 --- /dev/null +++ b/docs/spec/type-forms.rst @@ -0,0 +1,191 @@ +.. _`type-forms`: + +Type forms +========== + +.. _`typeform`: + +TypeForm +-------- + +(Originally specified in :pep:`747`.) + +When a type expression is evaluated at runtime, the resulting value is a +*type form* object. This value encodes the information supplied in the type +expression, and it represents the type described by that type expression. + +``TypeForm`` is a :term:`special form` that, when used in a type expression, +describes a set of type form objects. It accepts a single type argument, which +must be a :ref:`valid type expression `. +``TypeForm[T]`` describes the set of all type form objects that represent +the type ``T`` or types that are :term:`assignable` to ``T``. For example, +``TypeForm[str | None]`` describes the set of all type form objects that +represent a type assignable to ``str | None``:: + + from typing import Any, Literal, Optional + from typing_extensions import TypeForm + + ok1: TypeForm[str | None] = str | None # OK + ok2: TypeForm[str | None] = str # OK + ok3: TypeForm[str | None] = None # OK + ok4: TypeForm[str | None] = Literal[None] # OK + ok5: TypeForm[str | None] = Optional[str] # OK + ok6: TypeForm[str | None] = "str | None" # OK + ok7: TypeForm[str | None] = Any # OK + + err1: TypeForm[str | None] = str | int # Error + err2: TypeForm[str | None] = list[str | None] # Error + +By this same definition, ``TypeForm[object]`` describes a type form object +that represents the type ``object`` or any type that is assignable to +``object``. Since all types in the Python type system are assignable to +``object``, ``TypeForm[object]`` describes the set of all type form objects +evaluated from all valid type expressions. + +``TypeForm[Any]`` describes a ``TypeForm`` type whose type argument is not +statically known but is a valid type form object. It is assignable both +to and from any other ``TypeForm`` type (because ``Any`` is assignable both +to and from any type). + +The type expression ``TypeForm``, with no type argument provided, is +equivalent to ``TypeForm[Any]``. + +.. _`implicit-typeform-evaluation`: + +Implicit ``TypeForm`` evaluation +-------------------------------- + +When a static type checker encounters a valid type expression, the evaluated +type of this expression should be assignable to ``TypeForm[T]`` if the type it +describes is assignable to ``T``. + +For example, if a static type checker encounters the expression +``str | None``, it may normally evaluate its type as ``UnionType`` because it +produces a runtime value that is an instance of ``types.UnionType``. However, +because this expression is a valid type expression, it is also assignable to +the type ``TypeForm[str | None]``:: + + from types import GenericAlias, UnionType + from typing_extensions import TypeForm + + v1_actual: UnionType = str | None # OK + v1_type_form: TypeForm[str | None] = str | None # OK + + v2_actual: GenericAlias = list[int] # OK + v2_type_form: TypeForm = list[int] # OK + +The ``Annotated`` special form is allowed in type expressions, so it can +also appear in an expression that is assignable to ``TypeForm``. Consistent +with the general rules for ``Annotated``, a static type checker may +choose to ignore any ``Annotated`` metadata that it does not understand:: + + from typing import Annotated + from typing_extensions import TypeForm + + v3: TypeForm[int | str] = Annotated[int | str, "metadata"] # OK + +A string literal expression containing a valid type expression should likewise +be assignable to ``TypeForm``:: + + from typing_extensions import TypeForm + + v4: TypeForm[set[str]] = "set[str]" # OK + +.. _`valid-type-expressions`: + +Valid type expressions +---------------------- + +This specification defines syntactic rules for type expressions in the form of a +:ref:`formal grammar `. Semantic rules are specified as +comments along with the grammar definition. Contextual requirements are +detailed throughout the text in sections that discuss concepts that +appear within type expressions. For example, the special form ``Self`` can be +used in a type expression only within a class, and a type variable can be used +within a type expression only when it is associated with a valid scope. + +A valid type expression is an expression that follows all of the syntactic, +semantic, and contextual rules for a type expression. + +Expressions that are not valid type expressions should not evaluate to a +``TypeForm`` type:: + + from typing import ClassVar, Final, Literal, Optional, Self, TypeVarTuple, Unpack + from typing_extensions import TypeForm + + Ts = TypeVarTuple("Ts") + var = 1 + + bad1: TypeForm = tuple() # Error: call expression not allowed in type expression + bad2: TypeForm = (1, 2) # Error: tuple expression not allowed in type expression + bad3: TypeForm = 1 # Error: non-class object not allowed in type expression + bad4: TypeForm = Self # Error: Self not allowed outside of a class + bad5: TypeForm = Literal[var] # Error: variable not allowed in type expression + bad6: TypeForm = Literal[f""] # Error: f-strings not allowed in type expression + bad7: TypeForm = ClassVar[int] # Error: ClassVar not allowed in type expression + bad8: TypeForm = Final[int] # Error: Final not allowed in type expression + bad9: TypeForm = Unpack[Ts] # Error: Unpack not allowed in this context + bad10: TypeForm = Optional # Error: invalid use of Optional special form + bad11: TypeForm = "int + str" # Error: invalid quoted type expression + +.. _`explicit-typeform-evaluation`: + +Explicit ``TypeForm`` evaluation +-------------------------------- + +``TypeForm`` also acts as a function that can be called with a single +argument. Type checkers should validate that this argument is a valid type +expression:: + + from typing import assert_type + from typing_extensions import TypeForm + + x1 = TypeForm(str | None) + assert_type(x1, TypeForm[str | None]) + + x2 = TypeForm("list[int]") + assert_type(x2, TypeForm[list[int]]) + + x3 = TypeForm("type(1)") # Error: invalid type expression + +The static type of a ``TypeForm(T)`` expression is ``TypeForm[T]``. + +At runtime the ``TypeForm(...)`` callable simply returns the value passed to +it. + +This explicit syntax serves two purposes. First, it documents the developer's +intent to use the value as a type form object. Second, static type checkers +validate that all rules for type expressions are followed:: + + x4 = type(1) # No error, evaluates to "type[int]" + + x5 = TypeForm(type(1)) # Error: call not allowed in type expression + +.. _`typeform-assignability`: + +Assignability +------------- + +``TypeForm`` has a single type parameter, which is covariant. That means +``TypeForm[B]`` is assignable to ``TypeForm[A]`` if ``B`` is assignable to +``A``:: + + from typing_extensions import TypeForm + + def get_type_form() -> TypeForm[int]: ... + + t1: TypeForm[int | str] = get_type_form() # OK + t2: TypeForm[str] = get_type_form() # Error + +``type[T]`` is a subtype of ``TypeForm[T]``, which means that ``type[B]`` is +assignable to ``TypeForm[A]`` if ``B`` is assignable to ``A``:: + + from typing_extensions import TypeForm + + def get_type() -> type[int]: ... + + t3: TypeForm[int | str] = get_type() # OK + t4: TypeForm[str] = get_type() # Error + +``TypeForm`` is a subtype of ``object`` and is assumed to have all of the +attributes and methods of ``object``. diff --git a/docs/spec/type-system.rst b/docs/spec/type-system.rst new file mode 100644 index 000000000..cbd3bff20 --- /dev/null +++ b/docs/spec/type-system.rst @@ -0,0 +1,51 @@ +.. _`type-system`: + +The Python Type System +====================== + +This document describes a specification for the Python type system. + +The type system aims to provide a standard syntax for type annotations, +opening up Python code to easier static analysis and refactoring, +potential runtime type checking, and (perhaps, in some contexts) +code generation utilizing type information. + +Of these goals, static analysis is the most important. This includes +support for off-line type checkers such as mypy, as well as providing +a standard notation that can be used by IDEs for code completion and +refactoring. + +Purpose +------- + +This specification aims to provide a full description of the Python +type system. For type checker authors, it provides a complete +description of expected semantics. For library authors, it provides +guarantees to rely on when working with multiple type checkers. + +The type system was originally specified in a series of PEPs, starting +with :pep:`484`. This document is intended to replace those PEPs, and +was initially created by merging the specification sections of the +various PEPs. However, the PEPs are uneven in depth and do not fully +cover all aspects of the type system. Addressing these issues is an +ongoing project. + +Non-goals +--------- + +While the typing module contains some building blocks for +runtime type checking -- in particular the ``get_type_hints()`` +function -- third party packages would have to be developed to +implement specific runtime type checking functionality, for example +using decorators or metaclasses. Using type hints for performance +optimizations is left as an exercise for the reader. + +It should also be emphasized that **Python will remain a dynamically +typed language, and there is no desire to ever make type hints +mandatory, even by convention.** + +Interpretation +-------------- + +The definition of "MAY", "MUST", and "SHOULD", and "SHOULD NOT" are +to be interpreted as described in :rfc:`2119`. diff --git a/docs/spec/typeddict.rst b/docs/spec/typeddict.rst new file mode 100644 index 000000000..cff7e8499 --- /dev/null +++ b/docs/spec/typeddict.rst @@ -0,0 +1,837 @@ +.. _`typeddict`: +.. _`typed-dictionaries`: + +Typed dictionaries +================== + +(Originally specified in :pep:`589`, with later additions: ``Required`` +and ``NotRequired`` in :pep:`655`, use with ``Unpack`` in :pep:`692`, +``ReadOnly`` in :pep:`705`, and ``closed=True`` and ``extra_items=`` in :pep:`728`.) + +A TypedDict type represents ``dict`` objects that contain only keys of +type ``str``. There are restrictions on which string keys are valid, and +which values can be associated with each key. Values that :term:`inhabit` a +TypedDict type must be instances of ``dict`` itself, not a subclass. + +TypedDict types can define any number of :term:`items `, which are string +keys associated with values of a specified type. For example, +a TypedDict may contain the item ``a: str``, indicating that the key ``a`` +must map to a value of type ``str``. Items may be either :term:`required`, +meaning they must be present in every instance of the TypedDict type, or +:term:`non-required`, meaning they may be omitted, but if they are present, +they must be of the type specified in the TypedDict definition. By default, +all items in a TypedDict are mutable, but items +may also be marked as :term:`read-only`, indicating that they may not be +modified. + +In addition to explicitly specified items, TypedDicts may allow additional +items. By default, TypedDicts are :term:`open`, meaning they may contain an +unknown set of additional items. They may also be marked as :term:`closed`, +in which case they may not contain any keys beyond those explicitly specified. +As a third option, they may be defined with :term:`extra items` of a specific type. +In this case, there may be any number of additional items present at runtime, but +their values must be of the specified type. Extra items may or may not be +:term:`read-only`. Thus, a TypedDict may be open, closed, or have extra items; +we refer to this property as the *openness* of the TypedDict. For many purposes, +an open TypedDict is equivalent to a TypedDict with read-only extra items of +type ``object``, but certain behaviors differ; for example, the +:ref:`TypedDict constructor ` of open TypedDicts does not +allow unrecognized keys. + +A TypedDict is a :term:`structural` type: independent TypedDict types may be +:term:`assignable` to each other based on their structure, even if they do not +share a common base class. For example, two TypedDict types that contain the same +items are :term:`equivalent`. Nevertheless, TypedDict types may inherit from other +TypedDict types to share common items. TypedDict types may also be generic. + +Syntax +------ + +This section outlines the syntax for creating TypedDict types. There are two +syntaxes: the class-based syntax and the functional syntax. + +.. _typeddict-class-based-syntax: + +Class-based Syntax +^^^^^^^^^^^^^^^^^^ + +A TypedDict type can be defined using the class definition syntax with +``typing.TypedDict`` as a direct or indirect base class:: + + from typing import TypedDict + + class Movie(TypedDict): + name: str + year: int + +``Movie`` is a TypedDict type with two items: ``'name'`` (with type +``str``) and ``'year'`` (with type ``int``). + +A TypedDict can also be created through inheritance from one or more +other TypedDict types:: + + class BookBasedMovie(Movie): + based_on: str + +This creates a TypedDict type ``BookBasedMovie`` with three items: +``'name'`` (type ``str``), ``'year'`` (type ``int``), and ``'based_on'`` (type ``str``). +See :ref:`Inheritance ` for more details. + +A generic TypedDict can be created by inheriting from ``Generic`` with a list +of type parameters:: + + from typing import Generic, TypeVar + + T = TypeVar('T') + + class Response(TypedDict, Generic[T]): + status: int + payload: T + +Or, in Python 3.12 and newer, by using the native syntax for generic classes:: + + from typing import TypedDict + + class Response[T](TypedDict): + status: int + payload: T + +It is invalid to specify a base class other than ``TypedDict``, ``Generic``, +or another TypedDict type in a class-based TypedDict definition. +It is also invalid to specify a custom metaclass. + +A TypedDict definition may also contain the following keyword arguments +in the class definition: + +* ``total``: a boolean literal (``True`` or ``False``) indicating whether + all items are :term:`required` (``True``, the default) or :term:`non-required` + (``False``). This affects only items defined in this class, not in any + base classes, and it does not affect any items that use an explicit + ``Required[]`` or ``NotRequired[]`` qualifier. The value must be exactly + ``True`` or ``False``; other expressions are not allowed. +* ``closed``: a boolean literal (``True`` or ``False``) indicating whether + the TypedDict is :term:`closed` (``True``) or :term:`open` (``False``). + The latter is the default, except when inheriting from another TypedDict that + is not open (see :ref:`typeddict-inheritance`), or when the ``extra_items`` + argument is also used. + As with ``total``, the value must be exactly ``True`` or ``False``. It is an error + to use this argument together with ``extra_items=``. +* ``extra_items``: indicates that the TypedDict has :term:`extra items`. The argument + must be a :term:`annotation expression` specifying the type of the extra items. + The :term:`type qualifier` ``ReadOnly[]`` may be used to indicate that the extra items are + :term:`read-only`. Other type qualifiers are not allowed. If the extra items type + is ``Never``, no extra items are allowed, so this is equivalent to ``closed=True``. + +The body of the class definition defines the :term:`items ` of the +TypedDict type. It may also contain a docstring or ``pass`` statements +(primarily to allow the creation of an empty TypedDict). `if` conditions that +the type checker is able to +:ref:`statically evaluate` are also permitted (e.g. +`if sys.version_info > (3, 14)`), in order to specify items that exist only under +the given conditions. No other statements are allowed, and type checkers should +report an error if any are present. Type comments are not supported for +creating TypedDict items. + +.. _`required-notrequired`: +.. _`required`: +.. _`notrequired`: + +An item definition takes the form of an attribute annotation, ``key: T``. ``key`` is +an identifier and corresponds to the string key of the item, and ``T`` is an +:term:`annotation expression` specifying the type of the item value. This annotation +expression contains a :term:`type expression`, optionally qualified with one of the +:term:`type qualifiers ` ``Required``, ``NotRequired``, or ``ReadOnly``. +These type qualifiers may be nested arbitrarily or wrapped in ``Annotated[]``. It is +an error to use both ``Required`` and ``NotRequired`` in the same item definition. +An item is :term:`read-only` if and only if the ``ReadOnly`` qualifier is used. + +To determine whether an item is :term:`required` or :term:`non-required`, the following +procedure is used: + +* If the ``Required`` qualifier is present, the item is required. +* If the ``NotRequired`` qualifier is present, the item is non-required. +* If the ``total`` argument of the TypedDict definition is ``False``, the item is non-required. +* Else, the item is required. + +It is valid to use ``Required[]`` and ``NotRequired[]`` even for +items where it is redundant, to enable additional explicitness if desired. +Note that the value of ``total`` only affects items defined in the current class body, +not in any base classes. Thus, inheritance can be used to create a TypedDict that mixes +required and non-required items without using ``Required[]`` or ``NotRequired[]``. + +The following example demonstrates some of these rules:: + + from typing import TypedDict, NotRequired, Required, ReadOnly, Annotated + + class Movie(TypedDict): + name: str # required, not read-only + year: int # required, not read-only + director: NotRequired[str] # non-required, not read-only + rating: NotRequired[ReadOnly[float]] # non-required, read-only + invalid: Required[NotRequired[int]] # type checker error: both Required and NotRequired used + + class PartialMovie(TypedDict, total=False): + name: str # non-required, not read-only + year: Required[int] # required, not read-only + score: ReadOnly[float] # non-required, read-only + +.. _typeddict-functional-syntax: + +Functional syntax +^^^^^^^^^^^^^^^^^ + +In addition to the class-based syntax, TypedDict types can be created +using an alternative functional syntax. This syntax allows defining +items with keys that are not valid Python identifiers, and it is compatible +with older Python versions such as 3.5 and 2.7 that don't support the +variable definition syntax introduced in :pep:`526`. On the other hand, this syntax +does not support inheritance. + +The functional syntax resembles the traditional syntax for defining named tuples:: + + from typing import TypedDict + + Movie = TypedDict('Movie', {'name': str, 'year': int}) + +The syntax comprises a call to ``TypedDict()``, the result of which must be immediately +assigned to a variable with the same name as the first argument to ``TypedDict()``. + +The call to ``TypedDict()`` must have two positional arguments. The first is a string +literal specifying the name of the TypedDict type. The second is a dictionary specifying +the :term:`items ` of the TypedDict. It must be a dictionary display expression, +not a variable or other expression that evaluates to a dictionary at runtime. +The keys of the dictionary must be string literals and the values must be +:term:`annotation expressions ` following the same rules as +the class-based syntax (i.e., the qualifiers ``Required``, ``NotRequired``, and +``ReadOnly`` are allowed). In addition to the two positional arguments, ``total``, +``closed``, and ``extra_items`` keyword arguments are also supported, with the same +semantics as in the class-based syntax. + +Using TypedDict Types +--------------------- + +Here is an example of how the type ``Movie`` can be used:: + + movie: Movie = {'name': 'Blade Runner', + 'year': 1982} + +An explicit ``Movie`` type annotation is generally needed, as +otherwise an ordinary dictionary type could be assumed by a type +checker, for backwards compatibility. When a type checker can infer +that a constructed dictionary object should be a TypedDict, an +explicit annotation can be omitted. A typical example is a dictionary +object as a function argument. In this example, a type checker is +expected to infer that the dictionary argument should be understood as +a TypedDict:: + + def record_movie(movie: Movie) -> None: ... + + record_movie({'name': 'Blade Runner', 'year': 1982}) + +Another example where a type checker should treat a dictionary display +as a TypedDict is in an assignment to a variable with a previously +declared TypedDict type:: + + movie: Movie + ... + movie = {'name': 'Blade Runner', 'year': 1982} + +Operations on ``movie`` can be checked by a static type checker:: + + movie['director'] = 'Ridley Scott' # Error: invalid key 'director' + movie['year'] = '1982' # Error: invalid value type ("int" expected) + +The code below should be rejected, since ``'title'`` is not a valid +key, and the ``'name'`` key is missing:: + + movie2: Movie = {'title': 'Blade Runner', + 'year': 1982} + +The created TypedDict type object is not a real class object. Here +are the only uses of the type a type checker is expected to allow: + +* It can be used in :term:`type expressions ` to + represent the TypedDict type. + +* It can be used as a callable object with keyword arguments + corresponding to the TypedDict items; see :ref:`typeddict-constructor`. + +* It can be used as a base class, but only when defining a derived + TypedDict (see :ref:`above `). + +In particular, TypedDict type objects cannot be used in +``isinstance()`` tests such as ``isinstance(d, Movie)``. This is +consistent with how ``isinstance()`` is not supported for +other type forms such as ``list[str]``. + +.. _typeddict-constructor: + +The TypedDict constructor +^^^^^^^^^^^^^^^^^^^^^^^^^ + +TypedDict types are callable at runtime and can be used as a constructor +to create values that conform to the TypedDict type. The constructor +takes only keyword arguments, corresponding to the items of the TypedDict. +Example:: + + m = Movie(name='Blade Runner', year=1982) + +When called, the TypedDict type object returns an ordinary +dictionary object at runtime:: + + print(type(m)) # + +Every :term:`required` item must be provided as a keyword argument. :term:`Non-required` +items may be omitted. Whether an item is read-only has no effect on the +constructor. + +Closed and open TypedDicts allow no additional items beyond those explicitly +defined, but TypedDicts with extra items allow arbitrary keyword arguments, +which must be of the specified type. Example:: + + from typing import TypedDict, ReadOnly + + class MovieWithExtras(TypedDict, extra_items=ReadOnly[int | str]): + name: str + year: int + + m1 = MovieWithExtras(name='Blade Runner', year=1982) # OK + m2 = MovieWithExtras(name='The Godfather', year=1972, director='Francis Ford Coppola', rating=9) # OK + m3 = MovieWithExtras(name='Inception', year=2010, budget=160.0) # Type check error: budget must be int or str + +Initialization from dictionary literals +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Type checkers should also allow initializing a value of TypedDict type from +a dictionary literal:: + + m: Movie = {'name': 'Blade Runner', 'year': 1982} # OK + +Or from a call to ``dict()`` with keyword arguments:: + + m: Movie = dict(name='Blade Runner', year=1982) # OK + +In these cases, extra keys should not be allowed unless the TypedDict +is defined to allow :term:`extra items`. In this example, the ``director`` key is not defined in +``Movie`` and is expected to generate an error from a type checker:: + + m: Movie = dict( + name='Alien', + year=1979, + director='Ridley Scott') # error: Unexpected key 'director' + +If a TypedDict has extra items, extra keys are allowed, provided their value +matches the extra items type:: + + class ExtraMovie(TypedDict, extra_items=bool): + name: str + + a: ExtraMovie = {"name": "Blade Runner", "novel_adaptation": True} # OK + b: ExtraMovie = { + "name": "Blade Runner", + "year": 1982, # Not OK. 'int' is not assignable to 'bool' + } + +Here, ``extra_items=bool`` specifies that items other than ``'name'`` +have a value type of ``bool`` and are non-required. + +.. _typeddict-inheritance: + +Inheritance +----------- + +As discussed under :ref:`typeddict-class-based-syntax`, TypedDict types +can inherit from one or more other TypedDict types. In this case the +``TypedDict`` base class should not be included. Example:: + + class BookBasedMovie(Movie): + based_on: str + +Now ``BookBasedMovie`` has keys ``name``, ``year``, and ``based_on``. It is +equivalent to this definition, since TypedDict types are :term:`structural` types:: + + class BookBasedMovie(TypedDict): + name: str + year: int + based_on: str + +Overriding items +^^^^^^^^^^^^^^^^ + +Under limited circumstances, subclasses may redeclare items defined in a superclass with +a different type or different qualifiers. Redeclaring an item with the same type and qualifiers +is always allowed, although it is redundant. + +If an item is mutable in a superclass, it must remain mutable in the subclass. Similarly, +mutable items that are :term:`required` in a superclass must remain required in the subclass, +and mutable :term:`non-required` items in the superclass must remain non-required in the subclass. +However, if the superclass item is :term:`read-only`, a superclass item that is non-required +may be overridden with a required item in the subclass. A read-only item in a superclass +may be redeclared as mutable (that is, without the ``ReadOnly`` qualifier) in a subclass. +These rules are necessary for type safety. + +If an item is read-only in the superclass, the subclass may redeclare it with a different type +that is :term:`assignable` to the superclass type. Otherwise, changing the type of an item is not allowed. +Example:: + + class X(TypedDict): + x: str + y: ReadOnly[int] + z: int + + class Y(X): + x: int # Type check error: cannot overwrite TypedDict field "x" + y: bool # OK: bool is assignable to int, and a mutable item can override a read-only one + z: bool # Type check error: key is mutable, so subclass type must be consistent with superclass + +Openness +^^^^^^^^ + +The openness of a TypedDict (whether it is :term:`open`, :term:`closed`, or has :term:`extra items`) +is inherited from its superclass by default:: + + class ClosedBase(TypedDict, closed=True): + name: str + + class ClosedChild(ClosedBase): # also closed + pass + + class ExtraItemsBase(TypedDict, extra_items=int | None): + name: str + + class ExtraItemsChild(ExtraItemsBase): # also has extra_items=int | None + pass + +However, subclasses may also explicitly use the ``closed`` and ``extra_items`` arguments +to change the openness of the TypedDict, but in some cases this yields a type checker error: + +- If the base class is open, all possible states are allowed in the subclass: it may remain open, + it may be closed (with ``closed=True``), or it may have extra items (with ``extra_items=...``). + +- If the base class is closed, any child classes must also be closed. + +- If the base class has extra items, but they are not read-only, the child class must also allow + the same extra items. + +- If the base class has read-only extra items, the child class may be closed, + or it may redeclare its extra items with a type that is :term:`assignable` to the base class type. + Child classes may also have mutable extra items if the base class has read-only extra items. + +For example:: + + class ExtraItemsRO(TypedDict, extra_items=ReadOnly[int | str]): + name: str + + class ClosedChild(ExtraItemsRO, closed=True): # OK + pass + + # OK, str is assignable to int | str, and mutable extra items can override read-only ones + class NarrowerChild(ExtraItemsRO, extra_items=str): + pass + +When a TypedDict has extra items, this effectively defines the value type of any unnamed +items accepted to the TypedDict and marks them as non-required. Thus, there are some +restrictions on the items that can be added in subclasses. For each item +added in a subclass of a class with extra items of type ``T``, the following rules must be followed: + +- If ``extra_items`` is read-only + + - The item can be either required or non-required + - The item's value type must be :term:`assignable` to ``T`` + +- If ``extra_items`` is not read-only + + - The item must be non-required + - The item's value type must be :term:`consistent` with ``T`` + +For example:: + + class MovieBase(TypedDict, extra_items=int | None): + name: str + + class MovieRequiredYear(MovieBase): # Not OK. Required key 'year' is not known to 'MovieBase' + year: int | None + + class MovieNotRequiredYear(MovieBase): # Not OK. 'int | None' is not consistent with 'int' + year: NotRequired[int] + + class MovieWithYear(MovieBase): # OK + year: NotRequired[int | None] + + class BookBase(TypedDict, extra_items=ReadOnly[int | str]): + title: str + + class Book(BookBase, extra_items=str): # OK + year: int # OK, since extra_items is read-only + +Multiple inheritance +^^^^^^^^^^^^^^^^^^^^ + +TypedDict types may use multiple inheritance to inherit items from multiple +base classes. Here is an example:: + + class X(TypedDict): + x: int + + class Y(TypedDict): + y: str + + class XYZ(X, Y): + z: bool + +The TypedDict ``XYZ`` has three items: ``x`` (type ``int``), ``y`` +(type ``str``), and ``z`` (type ``bool``). + +Multiple inheritance does not allow conflicting types for the same item:: + + class X(TypedDict): + x: int + + class Y(TypedDict): + x: str + + class XYZ(X, Y): # Type check error: cannot overwrite TypedDict field "x" while merging + xyz: bool + +.. _typeddict-assignability: + +Subtyping and assignability +--------------------------- + +Because TypedDict types are :term:`structural` types, a TypedDict ``T1`` is :term:`assignable` to another +TypedDict type ``T2`` if the two are structurally compatible, meaning that all operations that +are allowed on ``T2`` are also allowed on ``T1``. For similar reasons, TypedDict types are +generally not assignable to any specialization of ``dict`` or ``Mapping``, other than ``Mapping[str, object]``, +though certain :term:`closed` TypedDicts and TypedDicts with :term:`extra items` may be assignable +to these types. + +The rest of this section discusses the :term:`subtyping ` rules for TypedDict in more detail. +As with any type, the rules for :term:`assignability ` can be derived from the subtyping +rules using the :term:`materialization ` procedure. Generally, this means that where +":term:`equivalent`" is mentioned below, the :term:`consistency ` relation can be used instead +when implementing assignability, and where ":term:`subtyping `" between elements of a +TypedDict is mentioned, assignability can be used instead when implementing assignability between TypedDicts. + +Subtyping between TypedDict types +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A TypedDict type ``B`` is a :term:`subtype` of a TypedDict type ``A`` if +and only if all of the conditions below are satisfied. For the purposes of these conditions, +an :term:`open` TypedDict is treated as if it had read-only :term:`extra items` of type ``object``. + +The conditions are as follows: + +- For each item in ``A``: + + - If it is required in ``A``: + + - It must also be required in ``B``. + - If it is read-only in ``A``, the item type in ``B`` must be a subtype of the item type in ``A``. + (For :term:`assignability ` between two TypedDicts, the first item must instead + be assignable to the second.) + + - If it is mutable in ``A``, it must also be mutable in ``B``, and the item type in ``B`` must be + :term:`equivalent` to the item type in ``A``. (It follows that for assignability, the two item types + must be :term:`consistent`.) + + - If it is non-required in ``A``: + + - If it is read-only in ``A``: + + - If ``B`` has an item with the same key, its item type must be a subtype of the item type in ``A``. + - Else: + + - If ``B`` is closed, the check succeeds. + - If ``B`` has extra items, the extra items type must be a subtype of the item type in ``A``. + + - If it is mutable in ``A``: + + - If ``B`` has an item with the same key, it must also be mutable and non-required, and its item type must be + :term:`equivalent` to the item type in ``A``. + + - Else: + + - If ``B`` is closed, the check fails. + - If ``B`` has extra items, the extra items type must not be read-only and must + be :term:`equivalent` to the item type in ``A``. + +- If ``A`` is closed, ``B`` must also be closed, and it must not contain any items that are not present in ``A``. +- If ``A`` has read-only extra items, ``B`` must either be closed or also have extra items, and the extra items type in ``B`` + must be a subtype of the extra items type in ``A``. Additionally, for any items in ``B`` that are not present in ``A``, + the item type must be a subtype of the extra items type in ``A``. +- If ``A`` has mutable extra items, ``B`` must also have mutable extra items, and the extra items type in ``B`` + must be :term:`equivalent` to the extra items type in ``A``. Additionally, for any items in ``B`` that are not present in ``A``, + the item type must be :term:`equivalent` to the extra items type in ``A``. + +The intuition behind these rules is that any operation that is valid on ``A`` must also be valid and safe on ``B``. +For example, any key access on ``A`` that is guaranteed to succeed (because the item is required) must also succeed on ``B``, +and any mutating operation (such as setting or deleting a key) that is allowed on ``A`` must also be allowed on ``B``. + +An example where mutability is relevant:: + + class A(TypedDict): + x: int | None + + class B(TypedDict): + x: int + + def f(a: A) -> None: + a['x'] = None + + b: B = {'x': 0} + f(b) # Type check error: 'B' not assignable to 'A' + b['x'] + 1 # Runtime error: None + 1 + +.. _typeddict-mapping: + +Subtyping with ``Mapping`` +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A TypedDict type is a :term:`subtype` of a type of the form ``Mapping[str, VT]`` +when all value types of the items in the TypedDict +are subtypes of ``VT``. For the purpose of this rule, an :term:`open` TypedDict is considered +to have read-only :term:`extra items` of type ``object``. + +For example:: + + class MovieExtraStr(TypedDict, extra_items=str): + name: str + + extra_str: MovieExtraStr = {"name": "Blade Runner", "summary": ""} + str_mapping: Mapping[str, str] = extra_str # OK + + class MovieExtraInt(TypedDict, extra_items=int): + name: str + + extra_int: MovieExtraInt = {"name": "Blade Runner", "year": 1982} + int_mapping: Mapping[str, int] = extra_int # Not OK. 'int | str' is not assignable with 'int' + int_str_mapping: Mapping[str, int | str] = extra_int # OK + +As a consequence, every TypedDict type is :term:`assignable` to ``Mapping[str, object]``. + +.. _typeddict-dict: + +Subtyping with ``dict`` +^^^^^^^^^^^^^^^^^^^^^^^ + +Generally, TypedDict types are not subtypes of any specialization of ``dict[...]`` type, since +dictionary types allow destructive operations, including ``clear()``. They +also allow arbitrary keys to be set, which would compromise type safety. + +However, a TypedDict with :term:`extra items` may be a subtype of ``dict[str, VT]``, +provided certain conditions are met, because it introduces sufficient restrictions +for this subtyping relation to be safe. +A TypedDict type is a subtype of ``dict[str, VT]`` if the following conditions are met: + +- The TypedDict type has mutable :term:`extra items` of a type that is :term:`equivalent` to ``VT``. +- All items on the TypedDict satisfy the following conditions: + + - The value type of the item is :term:`equivalent` to ``VT``. + - The item is not read-only. + - The item is not required. + +For example:: + + class IntDict(TypedDict, extra_items=int): + pass + + class IntDictWithNum(IntDict): + num: NotRequired[int] + + def f(x: IntDict) -> None: + v: dict[str, int] = x # OK + v.clear() # OK + + not_required_num_dict: IntDictWithNum = {"num": 1, "bar": 2} + regular_dict: dict[str, int] = not_required_num_dict # OK + f(not_required_num_dict) # OK + +In this case, some methods that are otherwise unavailable on a TypedDict are allowed, +with signatures matching ``dict[str, VT]`` +(e.g.: ``__setitem__(self, key: str, value: VT) -> None``):: + + not_required_num_dict.clear() # OK + + reveal_type(not_required_num_dict.popitem()) # OK. Revealed type is 'tuple[str, int]' + + def f(not_required_num_dict: IntDictWithNum, key: str): + not_required_num_dict[key] = 42 # OK + del not_required_num_dict[key] # OK + +On the other hand, ``dict[str, VT]`` is not assignable to any TypedDict type, +because such a type includes instances of subclasses of ``dict``:: + + class CustomDict(dict[str, int]): + pass + + def f(might_not_be_a_builtin_dict: dict[str, int]): + int_dict: IntDict = might_not_be_a_builtin_dict # Not OK + + not_a_builtin_dict = CustomDict({"num": 1}) + f(not_a_builtin_dict) + +.. _typeddict-operations: + +Supported and Unsupported Operations +------------------------------------ + +Type checkers should support restricted forms of most ``dict`` +operations on TypedDict objects. The guiding principle is that +operations not involving ``Any`` types should be rejected by type +checkers if they may violate runtime type safety. Here are some of +the most important type safety violations to prevent: + +1. A required key is missing. + +2. A value has an invalid type. + +3. A key that is not defined in the TypedDict type is added. + +4. Read-only items are modified or deleted. + +.. _`readonly`: + +Items that are :term:`read-only` may not be mutated (added, modified, or removed):: + + from typing import ReadOnly + + class Band(TypedDict): + name: str + members: ReadOnly[list[str]] + + blur: Band = {"name": "blur", "members": []} + blur["name"] = "Blur" # OK: "name" is not read-only + blur["members"] = ["Damon Albarn"] # Type check error: "members" is read-only + blur["members"].append("Damon Albarn") # OK: list is mutable + +The exact type checking rules are up to each type checker to decide. +In some cases potentially unsafe operations may be accepted if the +alternative is to generate false positive errors for idiomatic code. +Sometimes, operations on :term:`closed` TypedDicts or TypedDicts with +:term:`extra items` are safe even if they would be unsafe on +:term:`open` TypedDicts, so type checker behavior may depend on the +openness of the TypedDict. + +Allowed keys +^^^^^^^^^^^^ + +Many operations on TypedDict objects involve specifying a dictionary key. +Examples include accessing an item with ``d['key']`` or setting an item with +``d['key'] = value``. + +A key that is not a literal should generally be rejected, since its +value is unknown during type checking, and thus can cause some of the +above violations. This involves both destructive operations such as setting +an item and read-only operations such as subscription expressions. + +The use of a key that is not known to exist should be reported as an error, +even if this wouldn't necessarily generate a runtime type error. These are +often mistakes, and these may insert values with an invalid type if +:term:`structural` :term:`assignability ` hides the types of +certain items. For example, ``d['x'] = 1`` should generate a type check error +if ``'x'`` is not a valid key for ``d`` (which is assumed to be a TypedDict +type), unless ``d`` has mutable :term:`extra items` of a compatible type. + +Type checkers should allow :ref:`final names ` with +string values to be used instead of string literals in operations on +TypedDict objects. For example, this is valid:: + + YEAR: Final = 'year' + + m: Movie = {'name': 'Alien', 'year': 1979} + years_since_epoch = m[YEAR] - 1970 + +Similarly, an expression with a suitable :ref:`literal type ` +can be used instead of a literal value:: + + def get_value(movie: Movie, + key: Literal['year', 'name']) -> int | str: + return movie[key] + +Specific operations +^^^^^^^^^^^^^^^^^^^ + +This section discusses some specific operations in more detail. + +* As an exception to the general rule around non-literal keys, ``d.get(e)`` and ``e in d`` + should be allowed for TypedDict objects, for an arbitrary expression + ``e`` with type ``str``. The motivation is that these are safe and + can be useful for introspecting TypedDict objects. The static type + of ``d.get(e)`` should be the union of all possible item types in ``d`` + if the string value of ``e`` cannot be determined statically. + (This simplifies to ``object`` if ``d`` is :term:`open`.) + +* ``clear()`` is not safe on :term:`open` TypedDicts since it could remove required items, some of which + may not be directly visible because of :term:`structural` + :term:`assignability `. However, this method is safe on + :term:`closed` TypedDicts and TypedDicts with :term:`extra items` if + there are no required or read-only items and there cannot be any subclasses with required + or read-only items. + +* ``popitem()`` is similarly unsafe on many TypedDicts, even + if all known items are :term:`non-required`. + +* ``del obj['key']`` should be rejected unless ``'key'`` is a + non-required, mutable key. + +* Type checkers may allow reading an item using ``d['x']`` even if + the key ``'x'`` is not required, instead of requiring the use of + ``d.get('x')`` or an explicit ``'x' in d`` check. The rationale is + that tracking the existence of keys is difficult to implement in full + generality, and that disallowing this could require many changes to + existing code. + Similarly, type checkers may allow indexed accesses + with arbitrary str keys when a TypedDict is :term:`closed` or has :term:`extra items`. + For example:: + + def bar(movie: MovieExtraInt, key: str) -> None: + reveal_type(movie[key]) # Revealed type is 'str | int' + +* The return types of the ``items()`` and ``values()`` methods can be determined + from the union of all item types in the TypedDict (which would include ``object`` + for :term:`open` TypedDicts). Therefore, type checkers should infer more precise + types for TypedDicts that are not open:: + + from typing import TypedDict + + class MovieExtraInt(TypedDict, extra_items=int): + name: str + + def foo(movie: MovieExtraInt) -> None: + reveal_type(movie.items()) # Revealed type is 'dict_items[str, str | int]' + reveal_type(movie.values()) # Revealed type is 'dict_values[str, str | int]' + +* The ``update()`` method should not allow mutating a read-only item. + Therefore, type checkers should error if a + TypedDict with a read-only item is updated with another TypedDict that declares + that item:: + + class A(TypedDict): + x: ReadOnly[int] + y: int + + a1: A = {"x": 1, "y": 2} + a2: A = {"x": 3, "y": 4} + a1.update(a2) # Type check error: "x" is read-only in A + + Unless the declared value is of bottom type (:data:`~typing.Never`):: + + class B(TypedDict): + x: NotRequired[typing.Never] + y: ReadOnly[int] + + def update_a(a: A, b: B) -> None: + a.update(b) # Accepted by type checker: "x" cannot be set on b + + Note: Nothing will ever match the ``Never`` type, so an item annotated with it must be absent. + +Backwards Compatibility +----------------------- + +To retain backwards compatibility, type checkers should not infer a +TypedDict type unless it is sufficiently clear that this is desired by +the programmer. When unsure, an ordinary dictionary type should be +inferred. Otherwise existing code that type checks without errors may +start generating errors once TypedDict support is added to the type +checker, since TypedDict types are more restrictive than dictionary +types. In particular, they aren't subtypes of dictionary types. diff --git a/docs/tutorials/external_libraries.rst b/docs/tutorials/external_libraries.rst new file mode 100644 index 000000000..66a1d5452 --- /dev/null +++ b/docs/tutorials/external_libraries.rst @@ -0,0 +1,42 @@ +.. _external_libraries: + +************************ +Using External Libraries +************************ + +.. seealso:: + If you are looking for information on how to write type hints for + external libraries, see the :ref:`writing_stubs` guide. + +Many external libraries -- whether installed from the +`Python Package Index `_ (PyPI) or from other sources -- +provide their own type hints. This is indicated by the presence of a +``py.typed`` file in the library's root directory. If you install such a +library, you can use it with any type checker without any additional +configuration. + +Type hints can either be included in the library's source code the same way +as in your own code, or they can be provided in separate so-called +*stub files*. Stub files are named ``.pyi`` and contain only type +hints, without any implementation. + +For libraries that don't include their own type hints, a separate +*stub package* may provide them. These stub packages are often written by the +library authors themselves, by the contributors to the +`typeshed `_ project, or by third-party +contributors. These packages are usually named ``types-`` +or ``-stubs``. These packages can be installed from PyPI as usual, and +they will be automatically discovered by type checkers:: + + pip install requests types-requests + +.. warning:: + + The usual security considerations apply when installing third-party + packages. Only install packages from sources you trust. Stub packages + have the same security implications as any other package. + +.. + TODO: Once development dependencies are supported by pyproject.toml, + and described in https://packaging.python.org/en/latest/guides/writing-pyproject-toml/ + we should recommend installing type stubs as a development dependency. diff --git a/docs/tutorials/index.rst b/docs/tutorials/index.rst new file mode 100644 index 000000000..f84673dad --- /dev/null +++ b/docs/tutorials/index.rst @@ -0,0 +1,15 @@ +:orphan: + +********************* +Type System Tutorials +********************* + +.. + Keep in sync with docs/index.rst. + + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + external_libraries diff --git a/drafts/GENERICS.rst b/drafts/GENERICS.rst deleted file mode 100644 index 424471f08..000000000 --- a/drafts/GENERICS.rst +++ /dev/null @@ -1,155 +0,0 @@ -Generics --------- - -The subscript operation on container types is overloaded to support -specifying the item type for the container as part of a type hint. -Example:: - - from typing import List - - def space_join(a: List[str]) -> str: - """Join list items with spaces.""" - return ' '.join(a) - -Given this definition, the following code will be flagged as a type -error, because the argument has type ``List[int]`` (list of integers) -rather than ``List[str]`` (list of strings):: - - space_join([1, 2, 3]) # Error - -Depending on the definition of the container, multiple type arguments -may be given in this notation:: - - from collections import defaultdict - from typing import Iterable, Dict - - def word_count(words: Iterable[str]) -> Dict[str, int]: - """Count words in document.""" - res = defaultdict(int) - for word in words: - res[word] += 1 - return dict(res) - - print(word_count(['to', 'be', 'or', 'not', 'to', 'be'])) - # {'to': 2, 'be': 2, 'or': 1, 'not': 1} - - -Sometimes we want the type of several arguments and/or the return type -to vary collectively. We can do this using type variables. A type -variable must be defined using the ``TypeVar()`` factory, after which -it can be used in mutiple function or method signatures. This is -called a generic function. Example:: - - from collections import defaultdict - from typing import Iterable, Mapping, TypeVar - - T = TypeVar('T') - - def thing_count(things: Iterable[T]) -> Dict[T, int]: - """Count words in document.""" - res = defaultdict(int) - for thing in things: - res[thing] += 1 - return dict(res) - - print(thing_count([2, 3, 5, 7, 2, 3]) - # {2: 2, 3: 2, 5: 1, 7: 1} - -Note that the argument to ``TypeVar()`` must be a string, and it must -be assigned to a variable with exactly that name. Type variables -cannot be redefined in the same module. These constraints are -enforced by the type checker (but not by the runtime implementation of -``TypeVar()``). - -We can also define generic classes, as follows:: - - from typing import Generic, TypeVar - - T = TypeVar('T') - - class Node(Generic[T]): - - def __init__(self, label: T) -> None: - self.label = label - - def get_label(self) -> T: - return self.label - - def mknod(x: int) -> Node[int]: - return Node(x) - - print(mknod(40).get_label() + 2) - # 42 - -This same mechanism is used to define the container classes exported -by ``typing`` (although the implementation is hairier due to the -desire to also emulate collection ABCs). - -Type variables have a few more tricks up their sleeves: - -* Additional positional arguments must be type expressions that will - be used to constrain the types that are acceptable substitutions. - This feature is used for example by the predefined type variable - ``AnyStr``, which is defined as:: - - AnyStr = TypeVar('AnyStr', str, bytes) - - When such a constrained type variable is used in an argument type, - the actual type must be a subtype of one of the constraints. When - used in a return value type, the inferred return type will be - exactly the corresponding constraint (*not* the inferred argument - type, which may be a subtype thereof). For example:: - - from typing import AnyStr - - def add_strings(a: AnyStr, b: AnyStr) -> AnyStr: - return a+b - - add_string('x', 'y') # 'xy' - add_string(b'a', b'b') # b'ab' - add_string('x', b'z') # Error - - class MyStr(str): - pass - - add_string(MyStr('a'), MyStr('b')) # 'ab', not MyStr('ab') - -* Type variables may be declared as covariant or contravariant. The - default is invariant. Covariance is best explained using an - example:: - - from typing import TypeVar - - T = TypeVar('T') - Tco = TypeVar('Tco', covariant=True) - - class MyTuple(Generic[Tco]): - ... # Implements immutable sequence operations - - class MyList(MyTuple[T]): - ... # Adds mutable sequence operations - - class Employee: - ... - - class Manager(Employee): - ... - - issubclass(MyTuple[Manager], MyTuple[Employee]) # True - issubclass(MyList[Manager], MyList[Employee]) # False - - def print_employees(emps: MyTuple[Employee]) -> None: - for emp in emps: - print(emp) - - def add_employee(emps: MyList[Employee], emp: Employee) -> None: - emps.append(emp) - - mgrs = MyList[Manager](...) # Undecided if this is allowed - print_employees(mgrs) # OK - bob = Manager(...) - add_employee(mgrs, bob) # Error - - For a good if theoretical explanation of covariance and - contravariance see the Wikipedia article: - http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29 diff --git a/drafts/NOTES-AMBV.txt b/drafts/NOTES-AMBV.txt deleted file mode 100644 index f9ee1af95..000000000 --- a/drafts/NOTES-AMBV.txt +++ /dev/null @@ -1,83 +0,0 @@ -============================= -Re: notes from Jython meeting -============================= - -* In PEPs Abstract rarely went straight to the meat of the discussion. - I'll try to make the first few examples in the **Type Definition - Syntax** stronger. There's no reason I'm using ``.format()`` at this - point. - -* Yes, the lack of Callable signature definition is an ommision, it's - going to be hard to use it with the subscription syntax Callable[]. - We can't use the call syntax Callable() if we wish to operate directly - on ABCs, which I believe is what the users will expect. We can't - since the syntax wouldn't work on concrete subclasses of ABCs. - I added this as an open issue in README. - -* The remark about removing ``types`` from being mentioned actually - makes me think we could solve many issues before they arise by - introducing a short section **The place of the ``typing`` module in - the standard library** which would explain how the authors intend for - it to be used and what is its role compared to builtin types, - ``types``, ``collections``, and ``collections.abc``. The worries - that Guido has about the ``types`` module being ill-suited for type - hinted are spot on, we should mention that in the document. - -* I am thrilled by ``Set[Employee]`` returning a new class object that - is a Set-of-Employees. This leaves the door open for runtime - inspection. However, that immediately raises the questions: - - * ``Set is not Set[Employee] == True``, right? - - * ``Set is Set[Any] == True``, right? - - * ``Set[Employee] is Set[Union[Employee]] == True``, right? - - * ``issubclass(Set[Employee], Set)``, right? We must accept a set of - employees in every place that accepts "any set". Taking the latter - check into account, we'd have ``issubclass(Set[Employee], - Set[Any])``. The covariance/contravariance discussion needs to - happen at some point. - -* While I don't see the point of ``AnyStr`` specifically (reeks too much - of ``basestring``), I see that it's a useful example for ``Var``. - You're right it's not equivalent to ``Union[bytes, str]``, it must be - obviously parametric to keep the other occurences in scope consistent. - With ``Var`` the syntax I was thinking about a following expression:: - - AnyStr = Var('AnyStr', base=Union[str, bytes]) - - Now that I saw this, I asked myself if that should be equivalent to - ``typevar('AnyStr', values=(str, bytes))`` and the answer is "No". - Union is not parametrized, hence not kept in sync between occurences. - My mistake makes me wonder if we should make the parametrized types - syntax more explicit for the type definition reader (in Java world - seeing is pretty clear). I will ponder about this some - more. - -* Interoperability of ``typing.List`` with ``java.util.ArrayList`` - sounds good in principle. - -* Realistically I think retroactive conformance in Python is going to be - achieved by means of ABC.register() and/or Protocol.register(). - I have my hopes in using protocols as a more static analysis-friendly - version of __subclasshook__ - -Unadressed matters ------------------- - -Lack of time currently to write about the following, will adress as soon -as I get to it again. - -* how does filter() construct an instance of Y, write an implementation, - extend Callable to say Callable[X] - -* typevar values vs. Var base= - -* should we encourage using abstract types: in principle yes, in - practice there's issues (long type names, strings and bytes being - iterables and sequences) - -* consequences of covariance in generics as compared to invariance - -* support for structural types diff --git a/drafts/NOTES-GUIDO.txt b/drafts/NOTES-GUIDO.txt deleted file mode 100644 index d13953f8c..000000000 --- a/drafts/NOTES-GUIDO.txt +++ /dev/null @@ -1,51 +0,0 @@ -Notes from meeting ------------------- - -Meeting at Dropbox. Attendees: Guido, Jukka, Jim Baker, Jeremy Siek, -Michael Vitousek (student of Jeremy's). - - -- Could have a few examples in Abstract. - -- Discussion of function types, e.g. keyword parameters. Observation - that what the type checker can represent may be more than what the - syntax supports. Jukka says in practice callbacks rarely if ever - are called with callbacks. - -- Any reason you use format() in the first example? (It is worthy of - a very custom checker all by itself.) - -- Let's not mention the 'types' module much, nor use it in examples. - Its types are too concrete for most practical purposes. - E.g. types.FunctionType does not include built-in types nor bound - methods, but those rarely pose problems. - -- The early function/callable examples don't specify the signature. - It would be good to fill in the bodies of the example functions. - -- Discussion of Jython, mostly java.util.List. Jim wants to generate - mypy stubs from .class files and then run standard mypy; he wants - java.util.List to be compatible with typing.List. - -- We want Set[Employee] to actually create a new class object that - knows it is a Set of Employees (not just Set). However - Set[Employee]() should just return an empty concrete set, - i.e. set(). - -- Problems with the filter() example -- how does it construct an - instance of Y? - -- Discussion about typevar('T', values=(alt1, alt2)) vs. Var('T', - base=...). Jeremy doesn't like base= because it reeks of - subclassing too much. Note that AnyStr is very different from - Union[str, bytes]. - -- Jython wants to use type hints for code generation and doesn't want - to change its parser to retain #type: comments. But List[int]() is - ugly and slows down execution. No clear solution; maybe Jython's - type inferencing can infer the type of p in cases like this:: - - def primes(n: int) -> List[int]: - p = [] - ...fill p with n primes... - return p diff --git a/drafts/NOTES-SIEK.txt b/drafts/NOTES-SIEK.txt deleted file mode 100644 index b3fb143cc..000000000 --- a/drafts/NOTES-SIEK.txt +++ /dev/null @@ -1,136 +0,0 @@ -Notes from Meeting at Dropbox, Oct. 3, 2014 -with Guido, Jukka, Jim, Michael, and Jeremy - -Concrete Types vs. Abstract types ---------------------------------- - -Jim cares about interoperability with Java, and specifically wants -code annotated with type List to work with instances of -java.util.ArrayList. Guido stated that it would be reasonable for List -to be more flexible than to just admit the concrete Python list. - - -We discussed whether to minimize the use of concrete types in type -annotations in preference to abstract types. Perhaps the best approach -here is to encourage the use of some types over others in the -documentation and other writing about optional types. - - -Type Annotations in Comments ----------------------------- - -Jim, Jeremy and Michael would like to avoid putting type annotations -in comments because that requires a non-trivial addition to our -implementation. We discussed several alternatives: -* use x = List[int]() - This is a good solution for global, module-level variables - but Guido points out it is not so great for local variables - because there is a performance cost. -* Improve the type inference algorithm to avoid the need for - explicit type annotations for locals, perhaps making more - use of function return type annotations. -* Have type annotations in comments, but give then a lesser - status as a hint, so that implementatiosn that ignore them - are still conformant. (This may already be the case because - the PEP doesn't mandate any static or dynamic checking.) -* Use decorators. Guido points out that this isn't what - decorators where meant for and puts the annotations too - far away from the variable uses. -Here's an example we were discussing: -def f() -> List[int]: - x = List[int]() use this for stub syntax, i.e., global variables - versus - x = [] - versus - x = [] # type: List[int] - - ... - return x - -For Python 3.6, Guido suggests - x : List[int] = [] - - -Type Variables --------------- - -Regarding type variables and bounds, -Jeremy prefers -T = Var('T', implements = C) -to -T = Var('T', base = C) - -It's important that we make clear the distinction between -X = Var('X') -Y = Var('Y', base = Iterable[X]) -versus -X = Var('X') -Y = Iterable[X] - - -The filter example seemed to be missing some pieces. -Instead of -X = Var('X') -Y = Var('Y', base = Iterable[X]) -filter(rule: Callable, collection: Y) -> Y -we should have -filter(rule: Callable[X], collection: Y) -> Y -and there's also an issue with the return type and -how a collection of that type would be created. -Perhaps the return type should have been Iterable[X]. -In any event, writing down the function body and -running it through MyPy would keep us honest. - -We also discussed the nuances of AnyStr and the -values parameter of Var. - -Erasure: should MutableMapping[int] evaluate to -collections.abc.MutableMapping? No, the type parameter needs to be -remembered. - - -Covariance ----------- - -"As with function arguments, generics are covariant, which is in -spirit of duck typing." -Jeremy want's to be able to express strictly static checking as an -option, don't want to limit strength of type system. -Put another way, Jeremy's hope for gradual typing is to let programmers -choose anywhere in between fully dynamic and fully static checking, -but if the type system is weakened by allowing covariance, then -the fully static option goes away. - -Jim wants to generate MyPy-style stub files from Java class files. - - -Syntax for Generic Types ------------------------- -Jeremy voiced a preference to reserve []'s for generic types -and not other types. Jukka shared that he had tried that -and got feedback from users that going with a uniform -syntax, such as the current []'s for all types with sub-parts, -is easier for them. - - -Retroactive Conformance ------------------------ -Jeremy wants to allow concrete types to be usable with -abstract types for which they did not inherit from. -Structural types allow this, and there is research on how to -do this with nominal types. -Jukka voiced the concern that some of those approaches give -up modular type checking. Jeremy agreed that modular type checking -is important, but that perhaps there's a way to have our cake -and eat it too. - -This issue is related to the use of the register method on ABC's to -get the right behavior with isinstance. - - -Structural vs. Nominal Types ----------------------------- -We briefly discussed the possibility of having structual -object types in addition to the nominal types currently in MyPy. -Reticulated currently suppose structural object types and -not nominal types. diff --git a/drafts/VARIANCE.rst b/drafts/VARIANCE.rst deleted file mode 100644 index ffce8c823..000000000 --- a/drafts/VARIANCE.rst +++ /dev/null @@ -1,140 +0,0 @@ -Variance -======== - -Author's note -------------- - -In an earlier draft I had swapped the meanings of 'in' and 'out' -(having misremembered what I had read on Wikipedia). I believe I am -not unique in making this mistake, so we've switched to -covariant=True and contravariant=True. - -Discussion ----------- - -The big question is: If Manager is a subclass of Employee, should -List[Manager] be considered a subclass of List[Employee}? - -If we answer "yes", we may encounter a runtime type error in a program -that passes the type check. I'll show you how. - -Let's first consider an innocent example that does not have a problem:: - - # Example 1 - - def add_employee(emps: List[Employee], new_emp: Employee) -> None: - emps.append(new_emp) - - def add_manager(mgrs: List[Manager], new_mgr: Manager) -> None: - add_employee(mgrs, new_mgr) - -Suppose we define the type checker so that this passes. Now let's -make a change to add_employee():: - - # Example 2 - - def add_employee(emps: List[Employee], new_emp: Employee) -> None: - copy_of_new_emp = Employee() - copy_of_new_emp.name = new_emp.name - # Etc., copy all known Employee fields - emps.append(copy_of_new_emp) - - # add_manager() is unchanged - -Using the above definition of the type checker (where List[Manager] is -a subclass of List[Employee]), example 2 would also type-check, but it -has a bug that example 1 doesn't have: When add_manager() is called, -it now appends a new Employee to the argument list (mgrs), which -violates its type (List[Manager]) at runtime. - -Let's leave aside for now why one would make this change, or how to -fix the bug. In real software development the two functions may be in -different packages, maintained by different teams, and much more -complicated, and that's exactly where you'd like a type checker to -catch problems -- but the type checker has let us down. - -There are languages whose type system has this problem; but they -typically address it in the language by making the append() call in -add_employee() fail at runtime. IIUC Java does this for Arrays -- an -Array object "knows" the item type from its declaration and rejects -new elements that aren't a subclass of this type. - -BTW, the reason why languages introduce such unsound type checks is -that intuitively (i.e. if you haven't considered or encountered cases -like example 2 above) making List[Manager] a subclass of -List[Employee] feels right to many developers. A sound type system -will make example 1 fail the type check, which is hard to explain to -developers, especially since there is no bug when you run it. - -("Sound" is a technical term used for type systems, meaning "won't -pass programs that fail at runtime". There's also a dual property -whose name escapes me, meaning "won't fail programs that pass at -runtime". Both properties are equally elusive. :-) - -Now, PEP 484 doesn't in general make hard promises of soundness -- -there are lots of ways that Python's dynamic typing can do things that -will break a program even though it passes the type check, so why -should we promise soundness for this particular situation (roughly, -defining subclassing of mutable container types based on the item -classes)? - -The reason is that example 2 is a pretty gross violation of an -expectation: you have an object whose declared type is List[Manager] -but, without doing anything sneaky, you've managed to add an Employee -object that isn't a Manager to it, while flying under the radar of the -type checked. - -The proposed solution gives the developer who writes a container class -a way to tell the type checker whether the container should be -considered mutable or immutable. For immutable container -types, problems like example 2 cannot happen (proof left as an -exercise for the reader :-), so the type checker can consider -e.g. Tuple[Manager] a subclass of Tuple[Employee], while disallowing -List[Manager] as a subclass of List[Employee]. - -The proposal adds a way to declare that a type variable follows the -rules for covariance or contravariance, while by default type -variables are invariant:: - - X = TypeVar('X') # Invariant - Y = TypeVar('Y', covariant=True) # Covariant - Z = TypeVar('Z', contravariant=True) # Contravariant - - class A(Generic[X]): - ... - - class B(Generic[Y]): - ... - - class C(Generic[Z]): - ... - - issubclass(A[Manager], A[Employee]) # False - issubclass(B[Manager], B[Employee]) # True - issubclass(B[Employee], B[Manager]) # True (I think) - -Unfortunately this still doesn't let us write example 1. But the -consolation is that this means example 2 won't happen. So how can we -rewrite example 1 in a way that is type-safe under the new rule? - -The solution is perhaps unexpected: We have to write a mutable data -type that's covariant and implements a run-time type check like Java -Array. Here's a sketch of such a data type:: - - T = TypeVar('T', covariant=True) # Covariant - - class MyList(MutableSequence[T]): - def __init__(self): - self._data = [] # type: List[T] - self._item_type = self.__class__.__parameters__[0] - ... - def append(self, item: T): - if not isinstance(item, self._item_type): - raise TypeError("item type %r does not match %r" % - (item.__class__, self._item_type)) - ... - -Reference ---------- - -http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29 diff --git a/python2/mod_generics_cache.py b/python2/mod_generics_cache.py deleted file mode 100644 index d9a60b4b2..000000000 --- a/python2/mod_generics_cache.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Module for testing the behavior of generics across different modules.""" - -from typing import TypeVar, Generic - -T = TypeVar('T') - - -class A(Generic[T]): - pass - - -class B(Generic[T]): - class A(Generic[T]): - pass diff --git a/python2/test_typing.py b/python2/test_typing.py deleted file mode 100644 index 95677c9ea..000000000 --- a/python2/test_typing.py +++ /dev/null @@ -1,2706 +0,0 @@ -from __future__ import absolute_import, unicode_literals - -import collections -import contextlib -import os -import pickle -import re -import subprocess -import sys -import abc -import types -from unittest import TestCase, main, SkipTest -from copy import copy, deepcopy - -from typing import Any, NoReturn -from typing import TypeVar, AnyStr -from typing import T, KT, VT # Not in __all__. -from typing import Union, Optional -from typing import Tuple, List, MutableMapping -from typing import Callable -from typing import Generic, ClassVar, GenericMeta, Final, Literal -from typing import cast -from typing import Type, Protocol, runtime_checkable -from typing import NewType -from typing import NamedTuple, TypedDict -from typing import Pattern, Match -import typing -import weakref -import collections - - -class BaseTestCase(TestCase): - - def assertIsSubclass(self, cls, class_or_tuple, msg=None): - if not issubclass(cls, class_or_tuple): - message = '%r is not a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): - if issubclass(cls, class_or_tuple): - message = '%r is a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - def clear_caches(self): - for f in typing._cleanups: - f() - - -class Employee(object): - pass - - -class Manager(Employee): - pass - - -class Founder(Employee): - pass - - -class ManagingFounder(Manager, Founder): - pass - - -class AnyTests(BaseTestCase): - - def test_any_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(42, Any) - - def test_any_subclass_type_error(self): - with self.assertRaises(TypeError): - issubclass(Employee, Any) - with self.assertRaises(TypeError): - issubclass(Any, Employee) - - def test_repr(self): - self.assertEqual(repr(Any), 'typing.Any') - - def test_errors(self): - with self.assertRaises(TypeError): - issubclass(42, Any) - with self.assertRaises(TypeError): - Any[int] # Any is not a generic type. - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class A(Any): - pass - with self.assertRaises(TypeError): - class A(type(Any)): - pass - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - Any() - with self.assertRaises(TypeError): - type(Any)() - - def test_any_is_subclass(self): - # These expressions must simply not fail. - typing.Match[Any] - typing.Pattern[Any] - typing.IO[Any] - - -class NoReturnTests(BaseTestCase): - - def test_noreturn_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(42, NoReturn) - - def test_noreturn_subclass_type_error(self): - with self.assertRaises(TypeError): - issubclass(Employee, NoReturn) - with self.assertRaises(TypeError): - issubclass(NoReturn, Employee) - - def test_repr(self): - self.assertEqual(repr(NoReturn), 'typing.NoReturn') - - def test_not_generic(self): - with self.assertRaises(TypeError): - NoReturn[int] - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class A(NoReturn): - pass - with self.assertRaises(TypeError): - class A(type(NoReturn)): - pass - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - NoReturn() - with self.assertRaises(TypeError): - type(NoReturn)() - - -class TypeVarTests(BaseTestCase): - - def test_basic_plain(self): - T = TypeVar('T') - # T equals itself. - self.assertEqual(T, T) - # T is an instance of TypeVar - self.assertIsInstance(T, TypeVar) - - def test_typevar_instance_type_error(self): - T = TypeVar('T') - with self.assertRaises(TypeError): - isinstance(42, T) - - def test_typevar_subclass_type_error(self): - T = TypeVar('T') - with self.assertRaises(TypeError): - issubclass(int, T) - with self.assertRaises(TypeError): - issubclass(T, int) - - def test_constrained_error(self): - with self.assertRaises(TypeError): - X = TypeVar('X', int) - X - - def test_union_unique(self): - X = TypeVar('X') - Y = TypeVar('Y') - self.assertNotEqual(X, Y) - self.assertEqual(Union[X], X) - self.assertNotEqual(Union[X], Union[X, Y]) - self.assertEqual(Union[X, X], X) - self.assertNotEqual(Union[X, int], Union[X]) - self.assertNotEqual(Union[X, int], Union[int]) - self.assertEqual(Union[X, int].__args__, (X, int)) - self.assertEqual(Union[X, int].__parameters__, (X,)) - self.assertIs(Union[X, int].__origin__, Union) - - def test_union_constrained(self): - A = TypeVar('A', str, bytes) - self.assertNotEqual(Union[A, str], Union[A]) - - def test_repr(self): - self.assertEqual(repr(T), '~T') - self.assertEqual(repr(KT), '~KT') - self.assertEqual(repr(VT), '~VT') - self.assertEqual(repr(AnyStr), '~AnyStr') - T_co = TypeVar('T_co', covariant=True) - self.assertEqual(repr(T_co), '+T_co') - T_contra = TypeVar('T_contra', contravariant=True) - self.assertEqual(repr(T_contra), '-T_contra') - - def test_no_redefinition(self): - self.assertNotEqual(TypeVar('T'), TypeVar('T')) - self.assertNotEqual(TypeVar('T', int, str), TypeVar('T', int, str)) - - def test_cannot_subclass_vars(self): - with self.assertRaises(TypeError): - class V(TypeVar('T')): - pass - - def test_cannot_subclass_var_itself(self): - with self.assertRaises(TypeError): - class V(TypeVar): - pass - - def test_cannot_instantiate_vars(self): - with self.assertRaises(TypeError): - TypeVar('A')() - - def test_bound_errors(self): - with self.assertRaises(TypeError): - TypeVar('X', bound=42) - with self.assertRaises(TypeError): - TypeVar('X', str, float, bound=Employee) - - def test_no_bivariant(self): - with self.assertRaises(ValueError): - TypeVar('T', covariant=True, contravariant=True) - - -class UnionTests(BaseTestCase): - - def test_basics(self): - u = Union[int, float] - self.assertNotEqual(u, Union) - - def test_subclass_error(self): - with self.assertRaises(TypeError): - issubclass(int, Union) - with self.assertRaises(TypeError): - issubclass(Union, int) - with self.assertRaises(TypeError): - issubclass(int, Union[int, str]) - with self.assertRaises(TypeError): - issubclass(Union[int, str], int) - - def test_union_any(self): - u = Union[Any] - self.assertEqual(u, Any) - u1 = Union[int, Any] - u2 = Union[Any, int] - u3 = Union[Any, object] - self.assertEqual(u1, u2) - self.assertNotEqual(u1, Any) - self.assertNotEqual(u2, Any) - self.assertNotEqual(u3, Any) - - def test_union_object(self): - u = Union[object] - self.assertEqual(u, object) - u = Union[int, object] - self.assertEqual(u, object) - u = Union[object, int] - self.assertEqual(u, object) - - def test_unordered(self): - u1 = Union[int, float] - u2 = Union[float, int] - self.assertEqual(u1, u2) - - def test_single_class_disappears(self): - t = Union[Employee] - self.assertIs(t, Employee) - - def test_base_class_disappears(self): - u = Union[Employee, Manager, int] - self.assertEqual(u, Union[int, Employee]) - u = Union[Manager, int, Employee] - self.assertEqual(u, Union[int, Employee]) - u = Union[Employee, Manager] - self.assertIs(u, Employee) - - def test_union_union(self): - u = Union[int, float] - v = Union[u, Employee] - self.assertEqual(v, Union[int, float, Employee]) - - def test_repr(self): - self.assertEqual(repr(Union), 'typing.Union') - u = Union[Employee, int] - self.assertEqual(repr(u), 'typing.Union[%s.Employee, int]' % __name__) - u = Union[int, Employee] - self.assertEqual(repr(u), 'typing.Union[int, %s.Employee]' % __name__) - T = TypeVar('T') - u = Union[T, int][int] - self.assertEqual(repr(u), repr(int)) - u = Union[List[int], int] - self.assertEqual(repr(u), 'typing.Union[typing.List[int], int]') - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(Union): - pass - with self.assertRaises(TypeError): - class C(type(Union)): - pass - with self.assertRaises(TypeError): - class C(Union[int, str]): - pass - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - Union() - u = Union[int, float] - with self.assertRaises(TypeError): - u() - with self.assertRaises(TypeError): - type(u)() - - def test_union_generalization(self): - self.assertFalse(Union[str, typing.Iterable[int]] == str) - self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int]) - self.assertTrue(Union[str, typing.Iterable] == typing.Iterable) - - def test_union_compare_other(self): - self.assertNotEqual(Union, object) - self.assertNotEqual(Union, Any) - self.assertNotEqual(ClassVar, Union) - self.assertNotEqual(Optional, Union) - self.assertNotEqual([None], Optional) - self.assertNotEqual(Optional, typing.Mapping) - self.assertNotEqual(Optional[typing.MutableMapping], Union) - - def test_optional(self): - o = Optional[int] - u = Union[int, None] - self.assertEqual(o, u) - - def test_empty(self): - with self.assertRaises(TypeError): - Union[()] - - def test_union_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(42, Union[int, str]) - - def test_no_eval_union(self): - u = Union[int, str] - self.assertIs(u._eval_type({}, {}), u) - - def test_function_repr_union(self): - def fun(): pass - self.assertEqual(repr(Union[fun, int]), 'typing.Union[fun, int]') - - def test_union_str_pattern(self): - # Shouldn't crash; see http://bugs.python.org/issue25390 - A = Union[str, Pattern] - A - - def test_etree(self): - # See https://github.com/python/typing/issues/229 - # (Only relevant for Python 2.) - try: - from xml.etree.cElementTree import Element - except ImportError: - raise SkipTest("cElementTree not found") - Union[Element, str] # Shouldn't crash - - def Elem(*args): - return Element(*args) - - Union[Elem, str] # Nor should this - - -class TupleTests(BaseTestCase): - - def test_basics(self): - with self.assertRaises(TypeError): - issubclass(Tuple, Tuple[int, str]) - with self.assertRaises(TypeError): - issubclass(tuple, Tuple[int, str]) - - class TP(tuple): pass - self.assertTrue(issubclass(tuple, Tuple)) - self.assertTrue(issubclass(TP, Tuple)) - - def test_equality(self): - self.assertEqual(Tuple[int], Tuple[int]) - self.assertEqual(Tuple[int, ...], Tuple[int, ...]) - self.assertNotEqual(Tuple[int], Tuple[int, int]) - self.assertNotEqual(Tuple[int], Tuple[int, ...]) - - def test_tuple_subclass(self): - class MyTuple(tuple): - pass - self.assertTrue(issubclass(MyTuple, Tuple)) - - def test_tuple_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance((0, 0), Tuple[int, int]) - isinstance((0, 0), Tuple) - - def test_repr(self): - self.assertEqual(repr(Tuple), 'typing.Tuple') - self.assertEqual(repr(Tuple[()]), 'typing.Tuple[()]') - self.assertEqual(repr(Tuple[int, float]), 'typing.Tuple[int, float]') - self.assertEqual(repr(Tuple[int, ...]), 'typing.Tuple[int, ...]') - - def test_errors(self): - with self.assertRaises(TypeError): - issubclass(42, Tuple) - with self.assertRaises(TypeError): - issubclass(42, Tuple[int]) - - -class CallableTests(BaseTestCase): - - def test_self_subclass(self): - with self.assertRaises(TypeError): - self.assertTrue(issubclass(type(lambda x: x), Callable[[int], int])) - self.assertTrue(issubclass(type(lambda x: x), Callable)) - - def test_eq_hash(self): - self.assertEqual(Callable[[int], int], Callable[[int], int]) - self.assertEqual(len({Callable[[int], int], Callable[[int], int]}), 1) - self.assertNotEqual(Callable[[int], int], Callable[[int], str]) - self.assertNotEqual(Callable[[int], int], Callable[[str], int]) - self.assertNotEqual(Callable[[int], int], Callable[[int, int], int]) - self.assertNotEqual(Callable[[int], int], Callable[[], int]) - self.assertNotEqual(Callable[[int], int], Callable) - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - Callable() - with self.assertRaises(TypeError): - type(Callable)() - c = Callable[[int], str] - with self.assertRaises(TypeError): - c() - with self.assertRaises(TypeError): - type(c)() - - def test_callable_wrong_forms(self): - with self.assertRaises(TypeError): - Callable[(), int] - with self.assertRaises(TypeError): - Callable[[()], int] - with self.assertRaises(TypeError): - Callable[[int, 1], 2] - with self.assertRaises(TypeError): - Callable[int] - - def test_callable_instance_works(self): - def f(): - pass - self.assertIsInstance(f, Callable) - self.assertNotIsInstance(None, Callable) - - def test_callable_instance_type_error(self): - def f(): - pass - with self.assertRaises(TypeError): - self.assertIsInstance(f, Callable[[], None]) - with self.assertRaises(TypeError): - self.assertIsInstance(f, Callable[[], Any]) - with self.assertRaises(TypeError): - self.assertNotIsInstance(None, Callable[[], None]) - with self.assertRaises(TypeError): - self.assertNotIsInstance(None, Callable[[], Any]) - - def test_repr(self): - ct0 = Callable[[], bool] - self.assertEqual(repr(ct0), 'typing.Callable[[], bool]') - ct2 = Callable[[str, float], int] - self.assertEqual(repr(ct2), 'typing.Callable[[str, float], int]') - ctv = Callable[..., str] - self.assertEqual(repr(ctv), 'typing.Callable[..., str]') - - def test_ellipsis_in_generic(self): - # Shouldn't crash; see https://github.com/python/typing/issues/259 - typing.List[Callable[..., str]] - - -XK = TypeVar('XK', unicode, bytes) -XV = TypeVar('XV') - - -class SimpleMapping(Generic[XK, XV]): - - def __getitem__(self, key): - pass - - def __setitem__(self, key, value): - pass - - def get(self, key, default=None): - pass - - -class MySimpleMapping(SimpleMapping[XK, XV]): - - def __init__(self): - self.store = {} - - def __getitem__(self, key): - return self.store[key] - - def __setitem__(self, key, value): - self.store[key] = value - - def get(self, key, default=None): - try: - return self.store[key] - except KeyError: - return default - - -class ProtocolTests(BaseTestCase): - - def test_basic_protocol(self): - @runtime_checkable - class P(Protocol): - def meth(self): - pass - class C(object): pass - class D(object): - def meth(self): - pass - def f(): - pass - self.assertIsSubclass(D, P) - self.assertIsInstance(D(), P) - self.assertNotIsSubclass(C, P) - self.assertNotIsInstance(C(), P) - self.assertNotIsSubclass(types.FunctionType, P) - self.assertNotIsInstance(f, P) - - def test_everything_implements_empty_protocol(self): - @runtime_checkable - class Empty(Protocol): pass - class C(object): pass - def f(): - pass - for thing in (object, type, tuple, C, types.FunctionType): - self.assertIsSubclass(thing, Empty) - for thing in (object(), 1, (), typing, f): - self.assertIsInstance(thing, Empty) - - def test_function_implements_protocol(self): - @runtime_checkable - class Function(Protocol): - def __call__(self, *args, **kwargs): - pass - def f(): - pass - self.assertIsInstance(f, Function) - - def test_no_inheritance_from_nominal(self): - class C(object): pass - class BP(Protocol): pass - with self.assertRaises(TypeError): - class P(C, Protocol): - pass - with self.assertRaises(TypeError): - class P(Protocol, C): - pass - with self.assertRaises(TypeError): - class P(BP, C, Protocol): - pass - class D(BP, C): pass - class E(C, BP): pass - self.assertNotIsInstance(D(), E) - self.assertNotIsInstance(E(), D) - - def test_no_instantiation(self): - class P(Protocol): pass - with self.assertRaises(TypeError): - P() - class C(P): pass - self.assertIsInstance(C(), C) - T = typing.TypeVar('T') - class PG(Protocol[T]): pass - with self.assertRaises(TypeError): - PG() - with self.assertRaises(TypeError): - PG[int]() - with self.assertRaises(TypeError): - PG[T]() - class CG(PG[T]): pass - self.assertIsInstance(CG[int](), CG) - - def test_cannot_instantiate_abstract(self): - @runtime_checkable - class P(Protocol): - @abc.abstractmethod - def ameth(self): - raise NotImplementedError - class B(P): - pass - class C(B): - def ameth(self): - return 26 - with self.assertRaises(TypeError): - B() - self.assertIsInstance(C(), P) - - def test_subprotocols_extending(self): - class P1(Protocol): - def meth1(self): - pass - @runtime_checkable - class P2(P1, Protocol): - def meth2(self): - pass - class C(object): - def meth1(self): - pass - def meth2(self): - pass - class C1(object): - def meth1(self): - pass - class C2(object): - def meth2(self): - pass - self.assertNotIsInstance(C1(), P2) - self.assertNotIsInstance(C2(), P2) - self.assertNotIsSubclass(C1, P2) - self.assertNotIsSubclass(C2, P2) - self.assertIsInstance(C(), P2) - self.assertIsSubclass(C, P2) - - def test_subprotocols_merging(self): - class P1(Protocol): - def meth1(self): - pass - class P2(Protocol): - def meth2(self): - pass - @runtime_checkable - class P(P1, P2, Protocol): - pass - class C(object): - def meth1(self): - pass - def meth2(self): - pass - class C1(object): - def meth1(self): - pass - class C2(object): - def meth2(self): - pass - self.assertNotIsInstance(C1(), P) - self.assertNotIsInstance(C2(), P) - self.assertNotIsSubclass(C1, P) - self.assertNotIsSubclass(C2, P) - self.assertIsInstance(C(), P) - self.assertIsSubclass(C, P) - - def test_protocols_issubclass(self): - T = typing.TypeVar('T') - @runtime_checkable - class P(Protocol): - def x(self): pass - @runtime_checkable - class PG(Protocol[T]): - def x(self): pass - class BadP(Protocol): - def x(self): pass - class BadPG(Protocol[T]): - def x(self): pass - class C(object): - def x(self): pass - self.assertIsSubclass(C, P) - self.assertIsSubclass(C, PG) - self.assertIsSubclass(BadP, PG) - self.assertIsSubclass(PG[int], PG) - self.assertIsSubclass(BadPG[int], P) - self.assertIsSubclass(BadPG[T], PG) - with self.assertRaises(TypeError): - issubclass(C, PG[T]) - with self.assertRaises(TypeError): - issubclass(C, PG[C]) - with self.assertRaises(TypeError): - issubclass(C, BadP) - with self.assertRaises(TypeError): - issubclass(C, BadPG) - with self.assertRaises(TypeError): - issubclass(P, PG[T]) - with self.assertRaises(TypeError): - issubclass(PG, PG[int]) - - def test_protocols_issubclass_non_callable(self): - class C(object): - x = 1 - @runtime_checkable - class PNonCall(Protocol): - x = 1 - with self.assertRaises(TypeError): - issubclass(C, PNonCall) - self.assertIsInstance(C(), PNonCall) - PNonCall.register(C) - with self.assertRaises(TypeError): - issubclass(C, PNonCall) - self.assertIsInstance(C(), PNonCall) - # check that non-protocol subclasses are not affected - class D(PNonCall): pass - self.assertNotIsSubclass(C, D) - self.assertNotIsInstance(C(), D) - D.register(C) - self.assertIsSubclass(C, D) - self.assertIsInstance(C(), D) - with self.assertRaises(TypeError): - issubclass(D, PNonCall) - - def test_protocols_isinstance(self): - T = typing.TypeVar('T') - @runtime_checkable - class P(Protocol): - def meth(x): pass - @runtime_checkable - class PG(Protocol[T]): - def meth(x): pass - class BadP(Protocol): - def meth(x): pass - class BadPG(Protocol[T]): - def meth(x): pass - class C(object): - def meth(x): pass - self.assertIsInstance(C(), P) - self.assertIsInstance(C(), PG) - with self.assertRaises(TypeError): - isinstance(C(), PG[T]) - with self.assertRaises(TypeError): - isinstance(C(), PG[C]) - with self.assertRaises(TypeError): - isinstance(C(), BadP) - with self.assertRaises(TypeError): - isinstance(C(), BadPG) - - def test_protocols_isinstance_init(self): - T = typing.TypeVar('T') - @runtime_checkable - class P(Protocol): - x = 1 - @runtime_checkable - class PG(Protocol[T]): - x = 1 - class C(object): - def __init__(self, x): - self.x = x - self.assertIsInstance(C(1), P) - self.assertIsInstance(C(1), PG) - - def test_protocol_checks_after_subscript(self): - class P(Protocol[T]): pass - class C(P[T]): pass - class Old1: pass - class New1(object): pass - class Old2: pass - class New2(object): pass - CA = C[Any] # noqa - - self.assertNotIsInstance(Old1(), C) - self.assertNotIsInstance(New1(), C) - self.assertNotIsSubclass(Old2, C) - self.assertNotIsSubclass(New2, C) - - class D1(C[Any]): pass - class D2(C[Any]): pass - CI = C[int] # noqa - - self.assertIsInstance(D1(), C) - self.assertIsSubclass(D2, C) - - def test_protocols_support_register(self): - @runtime_checkable - class P(Protocol): - x = 1 - class PM(Protocol): - def meth(self): pass - class D(PM): pass - class C(object): pass - D.register(C) - P.register(C) - self.assertIsInstance(C(), P) - self.assertIsInstance(C(), D) - - def test_none_on_non_callable_doesnt_block_implementation(self): - @runtime_checkable - class P(Protocol): - x = 1 - class A(object): - x = 1 - class B(A): - x = None - class C(object): - def __init__(self): - self.x = None - self.assertIsInstance(B(), P) - self.assertIsInstance(C(), P) - - def test_none_on_callable_blocks_implementation(self): - @runtime_checkable - class P(Protocol): - def x(self): pass - class A(object): - def x(self): pass - class B(A): - x = None - class C(object): - def __init__(self): - self.x = None - self.assertNotIsInstance(B(), P) - self.assertNotIsInstance(C(), P) - - def test_non_protocol_subclasses(self): - class P(Protocol): - x = 1 - @runtime_checkable - class PR(Protocol): - def meth(self): pass - class NonP(P): - x = 1 - class NonPR(PR): pass - class C(object): - x = 1 - class D(object): - def meth(self): pass - self.assertNotIsInstance(C(), NonP) - self.assertNotIsInstance(D(), NonPR) - self.assertNotIsSubclass(C, NonP) - self.assertNotIsSubclass(D, NonPR) - self.assertIsInstance(NonPR(), PR) - self.assertIsSubclass(NonPR, PR) - - def test_custom_subclasshook(self): - class P(Protocol): - x = 1 - class OKClass(object): pass - class BadClass(object): - x = 1 - class C(P): - @classmethod - def __subclasshook__(cls, other): - return other.__name__.startswith("OK") - self.assertIsInstance(OKClass(), C) - self.assertNotIsInstance(BadClass(), C) - self.assertIsSubclass(OKClass, C) - self.assertNotIsSubclass(BadClass, C) - - def test_issubclass_fails_correctly(self): - @runtime_checkable - class P(Protocol): - x = 1 - class C: pass - with self.assertRaises(TypeError): - issubclass(C(), P) - - def test_defining_generic_protocols(self): - T = typing.TypeVar('T') - S = typing.TypeVar('S') - @runtime_checkable - class PR(Protocol[T, S]): - def meth(self): pass - class P(PR[int, T], Protocol[T]): - y = 1 - self.assertIsSubclass(PR[int, T], PR) - self.assertIsSubclass(P[str], PR) - with self.assertRaises(TypeError): - PR[int] - with self.assertRaises(TypeError): - P[int, str] - with self.assertRaises(TypeError): - PR[int, 1] - with self.assertRaises(TypeError): - PR[int, ClassVar] - class C(PR[int, T]): pass - self.assertIsInstance(C[str](), C) - - def test_defining_generic_protocols_old_style(self): - T = typing.TypeVar('T') - S = typing.TypeVar('S') - @runtime_checkable - class PR(Protocol, typing.Generic[T, S]): - def meth(self): pass - class P(PR[int, str], Protocol): - y = 1 - self.assertIsSubclass(PR[int, str], PR) - self.assertIsSubclass(P, PR) - with self.assertRaises(TypeError): - PR[int] - with self.assertRaises(TypeError): - PR[int, 1] - class P1(Protocol, typing.Generic[T]): - def bar(self, x): pass - class P2(typing.Generic[T], Protocol): - def bar(self, x): pass - @runtime_checkable - class PSub(P1[str], Protocol): - x = 1 - class Test(object): - x = 1 - def bar(self, x): - return x - self.assertIsInstance(Test(), PSub) - with self.assertRaises(TypeError): - PR[int, ClassVar] - - def test_init_called(self): - T = typing.TypeVar('T') - class P(Protocol[T]): pass - class C(P[T]): - def __init__(self): - self.test = 'OK' - self.assertEqual(C[int]().test, 'OK') - - def test_protocols_bad_subscripts(self): - T = typing.TypeVar('T') - S = typing.TypeVar('S') - with self.assertRaises(TypeError): - class P(Protocol[T, T]): pass - with self.assertRaises(TypeError): - class P(Protocol[int]): pass - with self.assertRaises(TypeError): - class P(Protocol[T], Protocol[S]): pass - with self.assertRaises(TypeError): - class P(Protocol[T], typing.Mapping[T, S]): pass - - def test_generic_protocols_repr(self): - T = typing.TypeVar('T') - S = typing.TypeVar('S') - class P(Protocol[T, S]): pass - self.assertTrue(repr(P).endswith('P')) - self.assertTrue(repr(P[T, S]).endswith('P[~T, ~S]')) - self.assertTrue(repr(P[int, str]).endswith('P[int, str]')) - - def test_generic_protocols_eq(self): - T = typing.TypeVar('T') - S = typing.TypeVar('S') - class P(Protocol[T, S]): pass - self.assertEqual(P, P) - self.assertEqual(P[int, T], P[int, T]) - self.assertEqual(P[T, T][typing.Tuple[T, S]][int, str], - P[typing.Tuple[int, str], typing.Tuple[int, str]]) - - def test_generic_protocols_special_from_generic(self): - T = typing.TypeVar('T') - class P(Protocol[T]): pass - self.assertEqual(P.__parameters__, (T,)) - self.assertIs(P.__args__, None) - self.assertIs(P.__origin__, None) - self.assertEqual(P[int].__parameters__, ()) - self.assertEqual(P[int].__args__, (int,)) - self.assertIs(P[int].__origin__, P) - - def test_generic_protocols_special_from_protocol(self): - @runtime_checkable - class PR(Protocol): - x = 1 - class P(Protocol): - def meth(self): - pass - T = typing.TypeVar('T') - class PG(Protocol[T]): - x = 1 - def meth(self): - pass - self.assertTrue(P._is_protocol) - self.assertTrue(PR._is_protocol) - self.assertTrue(PG._is_protocol) - with self.assertRaises(AttributeError): - self.assertFalse(P._is_runtime_protocol) - self.assertTrue(PR._is_runtime_protocol) - self.assertTrue(PG[int]._is_protocol) - self.assertEqual(P._get_protocol_attrs(), {'meth'}) - self.assertEqual(PR._get_protocol_attrs(), {'x'}) - self.assertEqual(frozenset(PG._get_protocol_attrs()), - frozenset({'x', 'meth'})) - self.assertEqual(frozenset(PG[int]._get_protocol_attrs()), - frozenset({'x', 'meth'})) - - def test_no_runtime_deco_on_nominal(self): - with self.assertRaises(TypeError): - @runtime_checkable - class C(object): pass - class Proto(Protocol): - x = 1 - with self.assertRaises(TypeError): - @runtime_checkable - class Concrete(Proto): - pass - - def test_none_treated_correctly(self): - @runtime_checkable - class P(Protocol): - x = None # type: int - class B(object): pass - self.assertNotIsInstance(B(), P) - class C(object): - x = 1 - class D(object): - x = None - self.assertIsInstance(C(), P) - self.assertIsInstance(D(), P) - class CI(object): - def __init__(self): - self.x = 1 - class DI(object): - def __init__(self): - self.x = None - self.assertIsInstance(C(), P) - self.assertIsInstance(D(), P) - - def test_protocols_in_unions(self): - class P(Protocol): - x = None # type: int - Alias = typing.Union[typing.Iterable, P] - Alias2 = typing.Union[P, typing.Iterable] - self.assertEqual(Alias, Alias2) - - def test_protocols_pickleable(self): - global P, CP # pickle wants to reference the class by name - T = typing.TypeVar('T') - - @runtime_checkable - class P(Protocol[T]): - x = 1 - class CP(P[int]): - pass - - c = CP() - c.foo = 42 - c.bar = 'abc' - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.x, 1) - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) - s = pickle.dumps(P) - D = pickle.loads(s) - class E(object): - x = 1 - self.assertIsInstance(E(), D) - - def test_supports_int(self): - self.assertIsSubclass(int, typing.SupportsInt) - self.assertNotIsSubclass(str, typing.SupportsInt) - - def test_supports_float(self): - self.assertIsSubclass(float, typing.SupportsFloat) - self.assertNotIsSubclass(str, typing.SupportsFloat) - - def test_supports_complex(self): - - # Note: complex itself doesn't have __complex__. - class C(object): - def __complex__(self): - return 0j - - self.assertIsSubclass(C, typing.SupportsComplex) - self.assertNotIsSubclass(str, typing.SupportsComplex) - - def test_supports_abs(self): - self.assertIsSubclass(float, typing.SupportsAbs) - self.assertIsSubclass(int, typing.SupportsAbs) - self.assertNotIsSubclass(str, typing.SupportsAbs) - - def test_reversible(self): - self.assertIsSubclass(list, typing.Reversible) - self.assertNotIsSubclass(int, typing.Reversible) - - def test_supports_index(self): - self.assertIsSubclass(int, typing.SupportsIndex) - self.assertNotIsSubclass(str, typing.SupportsIndex) - - def test_protocol_instance_works(self): - self.assertIsInstance(0, typing.SupportsAbs) - self.assertNotIsInstance('no', typing.SupportsAbs) - class C1(typing.SupportsInt): - def __int__(self): - return 42 - class C2(C1): - pass - c = C2() - self.assertIsInstance(c, C1) - - def test_collections_protocols_allowed(self): - @runtime_checkable - class Custom(collections.Iterable, Protocol): - def close(self): pass - - class A(object): pass - class B(object): - def __iter__(self): - return [] - def close(self): - return 0 - - self.assertIsSubclass(B, Custom) - self.assertNotIsSubclass(A, Custom) - - -class GenericTests(BaseTestCase): - - def test_basics(self): - X = SimpleMapping[str, Any] - self.assertEqual(X.__parameters__, ()) - with self.assertRaises(TypeError): - X[unicode] - with self.assertRaises(TypeError): - X[unicode, unicode] - Y = SimpleMapping[XK, unicode] - self.assertEqual(Y.__parameters__, (XK,)) - Y[unicode] - with self.assertRaises(TypeError): - Y[unicode, unicode] - self.assertIsSubclass(SimpleMapping[str, int], SimpleMapping) - - def test_generic_errors(self): - T = TypeVar('T') - S = TypeVar('S') - with self.assertRaises(TypeError): - Generic[T]() - with self.assertRaises(TypeError): - Generic[T][T] - with self.assertRaises(TypeError): - Generic[T][S] - with self.assertRaises(TypeError): - isinstance([], List[int]) - with self.assertRaises(TypeError): - issubclass(list, List[int]) - with self.assertRaises(TypeError): - class NewGeneric(Generic): pass - with self.assertRaises(TypeError): - class MyGeneric(Generic[T], Generic[S]): pass - with self.assertRaises(TypeError): - class MyGeneric(List[T], Generic[S]): pass - - def test_init(self): - T = TypeVar('T') - S = TypeVar('S') - with self.assertRaises(TypeError): - Generic[T, T] - with self.assertRaises(TypeError): - Generic[T, S, T] - - def test_repr(self): - self.assertEqual(repr(SimpleMapping), - __name__ + '.' + 'SimpleMapping') - self.assertEqual(repr(MySimpleMapping), - __name__ + '.' + 'MySimpleMapping') - - def test_chain_repr(self): - T = TypeVar('T') - S = TypeVar('S') - - class C(Generic[T]): - pass - - X = C[Tuple[S, T]] - self.assertEqual(X, C[Tuple[S, T]]) - self.assertNotEqual(X, C[Tuple[T, S]]) - - Y = X[T, int] - self.assertEqual(Y, X[T, int]) - self.assertNotEqual(Y, X[S, int]) - self.assertNotEqual(Y, X[T, str]) - - Z = Y[str] - self.assertEqual(Z, Y[str]) - self.assertNotEqual(Z, Y[int]) - self.assertNotEqual(Z, Y[T]) - - self.assertTrue(str(Z).endswith( - '.C[typing.Tuple[str, int]]')) - - def test_new_repr(self): - T = TypeVar('T') - U = TypeVar('U', covariant=True) - S = TypeVar('S') - - self.assertEqual(repr(List), 'typing.List') - self.assertEqual(repr(List[T]), 'typing.List[~T]') - self.assertEqual(repr(List[U]), 'typing.List[+U]') - self.assertEqual(repr(List[S][T][int]), 'typing.List[int]') - self.assertEqual(repr(List[int]), 'typing.List[int]') - - def test_new_repr_complex(self): - T = TypeVar('T') - TS = TypeVar('TS') - - self.assertEqual(repr(typing.Mapping[T, TS][TS, T]), 'typing.Mapping[~TS, ~T]') - self.assertEqual(repr(List[Tuple[T, TS]][int, T]), - 'typing.List[typing.Tuple[int, ~T]]') - self.assertEqual( - repr(List[Tuple[T, T]][List[int]]), - 'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]' - ) - - def test_new_repr_bare(self): - T = TypeVar('T') - self.assertEqual(repr(Generic[T]), 'typing.Generic[~T]') - self.assertEqual(repr(typing.Protocol[T]), 'typing.Protocol[~T]') - class C(typing.Dict[Any, Any]): pass - # this line should just work - repr(C.__mro__) - - def test_dict(self): - T = TypeVar('T') - - class B(Generic[T]): - pass - - b = B() - b.foo = 42 - self.assertEqual(b.__dict__, {'foo': 42}) - - class C(B[int]): - pass - - c = C() - c.bar = 'abc' - self.assertEqual(c.__dict__, {'bar': 'abc'}) - - def test_subscripted_generics_as_proxies(self): - T = TypeVar('T') - class C(Generic[T]): - x = 'def' - self.assertEqual(C[int].x, 'def') - self.assertEqual(C[C[int]].x, 'def') - C[C[int]].x = 'changed' - self.assertEqual(C.x, 'changed') - self.assertEqual(C[str].x, 'changed') - C[List[str]].z = 'new' - self.assertEqual(C.z, 'new') - self.assertEqual(C[Tuple[int]].z, 'new') - - self.assertEqual(C().x, 'changed') - self.assertEqual(C[Tuple[str]]().z, 'new') - - class D(C[T]): - pass - self.assertEqual(D[int].x, 'changed') - self.assertEqual(D.z, 'new') - D.z = 'from derived z' - D[int].x = 'from derived x' - self.assertEqual(C.x, 'changed') - self.assertEqual(C[int].z, 'new') - self.assertEqual(D.x, 'from derived x') - self.assertEqual(D[str].z, 'from derived z') - - def test_abc_registry_kept(self): - T = TypeVar('T') - class C(Generic[T]): pass - C.register(int) - self.assertIsInstance(1, C) - C[int] - self.assertIsInstance(1, C) - - def test_false_subclasses(self): - class MyMapping(MutableMapping[str, str]): pass - self.assertNotIsInstance({}, MyMapping) - self.assertNotIsSubclass(dict, MyMapping) - - def test_abc_bases(self): - class MM(MutableMapping[str, str]): - def __getitem__(self, k): - return None - def __setitem__(self, k, v): - pass - def __delitem__(self, k): - pass - def __iter__(self): - return iter(()) - def __len__(self): - return 0 - # this should just work - MM().update() - self.assertIsInstance(MM(), collections.MutableMapping) - self.assertIsInstance(MM(), MutableMapping) - self.assertNotIsInstance(MM(), List) - self.assertNotIsInstance({}, MM) - - def test_multiple_bases(self): - class MM1(MutableMapping[str, str], collections.MutableMapping): - pass - with self.assertRaises(TypeError): - # consistent MRO not possible - class MM2(collections.MutableMapping, MutableMapping[str, str]): - pass - - def test_orig_bases(self): - T = TypeVar('T') - class C(typing.Dict[str, T]): pass - self.assertEqual(C.__orig_bases__, (typing.Dict[str, T],)) - - def test_naive_runtime_checks(self): - def naive_dict_check(obj, tp): - # Check if a dictionary conforms to Dict type - if len(tp.__parameters__) > 0: - raise NotImplementedError - if tp.__args__: - KT, VT = tp.__args__ - return all( - isinstance(k, KT) and isinstance(v, VT) - for k, v in obj.items() - ) - self.assertTrue(naive_dict_check({'x': 1}, typing.Dict[typing.Text, int])) - self.assertFalse(naive_dict_check({1: 'x'}, typing.Dict[typing.Text, int])) - with self.assertRaises(NotImplementedError): - naive_dict_check({1: 'x'}, typing.Dict[typing.Text, T]) - - def naive_generic_check(obj, tp): - # Check if an instance conforms to the generic class - if not hasattr(obj, '__orig_class__'): - raise NotImplementedError - return obj.__orig_class__ == tp - class Node(Generic[T]): pass - self.assertTrue(naive_generic_check(Node[int](), Node[int])) - self.assertFalse(naive_generic_check(Node[str](), Node[int])) - self.assertFalse(naive_generic_check(Node[str](), List)) - with self.assertRaises(NotImplementedError): - naive_generic_check([1, 2, 3], Node[int]) - - def naive_list_base_check(obj, tp): - # Check if list conforms to a List subclass - return all(isinstance(x, tp.__orig_bases__[0].__args__[0]) - for x in obj) - class C(List[int]): pass - self.assertTrue(naive_list_base_check([1, 2, 3], C)) - self.assertFalse(naive_list_base_check(['a', 'b'], C)) - - def test_multi_subscr_base(self): - T = TypeVar('T') - U = TypeVar('U') - V = TypeVar('V') - class C(List[T][U][V]): pass - class D(C, List[T][U][V]): pass - self.assertEqual(C.__parameters__, (V,)) - self.assertEqual(D.__parameters__, (V,)) - self.assertEqual(C[int].__parameters__, ()) - self.assertEqual(D[int].__parameters__, ()) - self.assertEqual(C[int].__args__, (int,)) - self.assertEqual(D[int].__args__, (int,)) - self.assertEqual(C.__bases__, (List,)) - self.assertEqual(D.__bases__, (C, List)) - self.assertEqual(C.__orig_bases__, (List[T][U][V],)) - self.assertEqual(D.__orig_bases__, (C, List[T][U][V])) - - def test_subscript_meta(self): - T = TypeVar('T') - self.assertEqual(Type[GenericMeta], Type[GenericMeta]) - self.assertEqual(Union[T, int][GenericMeta], Union[GenericMeta, int]) - self.assertEqual(Callable[..., GenericMeta].__args__, (Ellipsis, GenericMeta)) - - def test_generic_hashes(self): - import mod_generics_cache - class A(Generic[T]): - __module__ = 'test_typing' - - class B(Generic[T]): - class A(Generic[T]): - pass - - self.assertEqual(A, A) - self.assertEqual(mod_generics_cache.A[str], mod_generics_cache.A[str]) - self.assertEqual(B.A, B.A) - self.assertEqual(mod_generics_cache.B.A[B.A[str]], - mod_generics_cache.B.A[B.A[str]]) - - self.assertNotEqual(A, B.A) - self.assertNotEqual(A, mod_generics_cache.A) - self.assertNotEqual(A, mod_generics_cache.B.A) - self.assertNotEqual(B.A, mod_generics_cache.A) - self.assertNotEqual(B.A, mod_generics_cache.B.A) - - self.assertNotEqual(A[str], B.A[str]) - self.assertNotEqual(A[List[Any]], B.A[List[Any]]) - self.assertNotEqual(A[str], mod_generics_cache.A[str]) - self.assertNotEqual(A[str], mod_generics_cache.B.A[str]) - self.assertNotEqual(B.A[int], mod_generics_cache.A[int]) - self.assertNotEqual(B.A[List[Any]], mod_generics_cache.B.A[List[Any]]) - - self.assertNotEqual(Tuple[A[str]], Tuple[B.A[str]]) - self.assertNotEqual(Tuple[A[List[Any]]], Tuple[B.A[List[Any]]]) - self.assertNotEqual(Union[str, A[str]], Union[str, mod_generics_cache.A[str]]) - self.assertNotEqual(Union[A[str], A[str]], - Union[A[str], mod_generics_cache.A[str]]) - self.assertNotEqual(typing.FrozenSet[A[str]], - typing.FrozenSet[mod_generics_cache.B.A[str]]) - - self.assertTrue(repr(Tuple[A[str]]).endswith('test_typing.A[str]]')) - self.assertTrue(repr(Tuple[mod_generics_cache.A[str]]) - .endswith('mod_generics_cache.A[str]]')) - - def test_extended_generic_rules_eq(self): - T = TypeVar('T') - U = TypeVar('U') - self.assertEqual(Tuple[T, T][int], Tuple[int, int]) - self.assertEqual(typing.Iterable[Tuple[T, T]][T], typing.Iterable[Tuple[T, T]]) - with self.assertRaises(TypeError): - Tuple[T, int][()] - with self.assertRaises(TypeError): - Tuple[T, U][T, ...] - - self.assertEqual(Union[T, int][int], int) - self.assertEqual(Union[T, U][int, Union[int, str]], Union[int, str]) - class Base(object): pass - class Derived(Base): pass - self.assertEqual(Union[T, Base][Derived], Base) - with self.assertRaises(TypeError): - Union[T, int][1] - - self.assertEqual(Callable[[T], T][KT], Callable[[KT], KT]) - self.assertEqual(Callable[..., List[T]][int], Callable[..., List[int]]) - with self.assertRaises(TypeError): - Callable[[T], U][..., int] - with self.assertRaises(TypeError): - Callable[[T], U][[], int] - - def test_extended_generic_rules_repr(self): - T = TypeVar('T') - self.assertEqual(repr(Union[Tuple, Callable]).replace('typing.', ''), - 'Union[Tuple, Callable]') - self.assertEqual(repr(Union[Tuple, Tuple[int]]).replace('typing.', ''), - 'Tuple') - self.assertEqual(repr(Callable[..., Optional[T]][int]).replace('typing.', ''), - 'Callable[..., Union[int, NoneType]]') - self.assertEqual(repr(Callable[[], List[T]][int]).replace('typing.', ''), - 'Callable[[], List[int]]') - - def test_generic_forvard_ref(self): - LLT = List[List['CC']] - class CC: pass - self.assertEqual(typing._eval_type(LLT, globals(), locals()), List[List[CC]]) - T = TypeVar('T') - AT = Tuple[T, ...] - self.assertIs(typing._eval_type(AT, globals(), locals()), AT) - CT = Callable[..., List[T]] - self.assertIs(typing._eval_type(CT, globals(), locals()), CT) - - def test_extended_generic_rules_subclassing(self): - class T1(Tuple[T, KT]): pass - class T2(Tuple[T, ...]): pass - class C1(Callable[[T], T]): pass - class C2(Callable[..., int]): - def __call__(self): - return None - - self.assertEqual(T1.__parameters__, (T, KT)) - self.assertEqual(T1[int, str].__args__, (int, str)) - self.assertEqual(T1[int, T].__origin__, T1) - - self.assertEqual(T2.__parameters__, (T,)) - with self.assertRaises(TypeError): - T1[int] - with self.assertRaises(TypeError): - T2[int, str] - - self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]') - self.assertEqual(C2.__parameters__, ()) - self.assertIsInstance(C2(), collections.Callable) - self.assertIsSubclass(C2, collections.Callable) - self.assertIsSubclass(C1, collections.Callable) - self.assertIsInstance(T1(), tuple) - self.assertIsSubclass(T2, tuple) - self.assertIsSubclass(Tuple[int, ...], typing.Sequence) - self.assertIsSubclass(Tuple[int, ...], typing.Iterable) - - def test_fail_with_bare_union(self): - with self.assertRaises(TypeError): - List[Union] - with self.assertRaises(TypeError): - Tuple[Optional] - with self.assertRaises(TypeError): - ClassVar[ClassVar] - with self.assertRaises(TypeError): - List[ClassVar[int]] - - def test_fail_with_bare_generic(self): - T = TypeVar('T') - with self.assertRaises(TypeError): - List[Generic] - with self.assertRaises(TypeError): - Tuple[Generic[T]] - with self.assertRaises(TypeError): - List[typing.Protocol] - with self.assertRaises(TypeError): - isinstance(1, Generic) - - def test_type_erasure_special(self): - T = TypeVar('T') - # this is the only test that checks type caching - self.clear_caches() - class MyTup(Tuple[T, T]): pass - self.assertIs(MyTup[int]().__class__, MyTup) - self.assertIs(MyTup[int]().__orig_class__, MyTup[int]) - class MyCall(Callable[..., T]): - def __call__(self): return None - self.assertIs(MyCall[T]().__class__, MyCall) - self.assertIs(MyCall[T]().__orig_class__, MyCall[T]) - class MyDict(typing.Dict[T, T]): pass - self.assertIs(MyDict[int]().__class__, MyDict) - self.assertIs(MyDict[int]().__orig_class__, MyDict[int]) - class MyDef(typing.DefaultDict[str, T]): pass - self.assertIs(MyDef[int]().__class__, MyDef) - self.assertIs(MyDef[int]().__orig_class__, MyDef[int]) - - def test_all_repr_eq_any(self): - objs = (getattr(typing, el) for el in typing.__all__) - for obj in objs: - self.assertNotEqual(repr(obj), '') - self.assertEqual(obj, obj) - if getattr(obj, '__parameters__', None) and len(obj.__parameters__) == 1: - self.assertEqual(obj[Any].__args__, (Any,)) - if isinstance(obj, type): - for base in obj.__mro__: - self.assertNotEqual(repr(base), '') - self.assertEqual(base, base) - - def test_pickle(self): - global C # pickle wants to reference the class by name - T = TypeVar('T') - - class B(Generic[T]): - pass - - class C(B[int]): - pass - - c = C() - c.foo = 42 - c.bar = 'abc' - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) - simples = [Any, Union, Tuple, Callable, ClassVar, List, typing.Iterable] - for s in simples: - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(s, proto) - x = pickle.loads(z) - self.assertEqual(s, x) - - def test_copy_and_deepcopy(self): - T = TypeVar('T') - class Node(Generic[T]): pass - things = [ - Any, - Callable[..., T], - Callable[[int], int], - ClassVar[List[T]], - ClassVar[int], - List['T'], - Node[Any], - Node[T], - Node[int], - Tuple['T', 'T'], - Tuple[Any, Any], - Tuple[T, int], - Union['T', int], - Union[T, int], - typing.Dict[T, Any], - typing.Dict[int, str], - typing.Iterable[Any], - typing.Iterable[T], - typing.Iterable[int], - typing.Mapping['T', int] - ] - for t in things: - self.assertEqual(t, deepcopy(t)) - self.assertEqual(t, copy(t)) - - def test_copy_generic_instances(self): - T = TypeVar('T') - class C(Generic[T]): - def __init__(self, attr): - self.attr = attr - - c = C(42) - self.assertEqual(copy(c).attr, 42) - self.assertEqual(deepcopy(c).attr, 42) - self.assertIsNot(copy(c), c) - self.assertIsNot(deepcopy(c), c) - c.attr = 1 - self.assertEqual(copy(c).attr, 1) - self.assertEqual(deepcopy(c).attr, 1) - ci = C[int](42) - self.assertEqual(copy(ci).attr, 42) - self.assertEqual(deepcopy(ci).attr, 42) - self.assertIsNot(copy(ci), ci) - self.assertIsNot(deepcopy(ci), ci) - ci.attr = 1 - self.assertEqual(copy(ci).attr, 1) - self.assertEqual(deepcopy(ci).attr, 1) - self.assertEqual(ci.__orig_class__, C[int]) - - def test_weakref_all(self): - T = TypeVar('T') - things = [Any, Union[T, int], Callable[..., T], Tuple[Any, Any], - Optional[List[int]], typing.Mapping[int, str], - typing.re.Match[bytes], typing.Iterable['whatever']] - for t in things: - self.assertEqual(weakref.ref(t)(), t) - - def test_parameterized_slots(self): - T = TypeVar('T') - class C(Generic[T]): - __slots__ = ('potato',) - - c = C() - c_int = C[int]() - self.assertEqual(C.__slots__, C[str].__slots__) - - c.potato = 0 - c_int.potato = 0 - with self.assertRaises(AttributeError): - c.tomato = 0 - with self.assertRaises(AttributeError): - c_int.tomato = 0 - - self.assertEqual(typing._eval_type(C['C'], globals(), locals()), C[C]) - self.assertEqual(typing._eval_type(C['C'], globals(), locals()).__slots__, - C.__slots__) - self.assertEqual(copy(C[int]), deepcopy(C[int])) - - def test_parameterized_slots_dict(self): - T = TypeVar('T') - class D(Generic[T]): - __slots__ = {'banana': 42} - - d = D() - d_int = D[int]() - self.assertEqual(D.__slots__, D[str].__slots__) - - d.banana = 'yes' - d_int.banana = 'yes' - with self.assertRaises(AttributeError): - d.foobar = 'no' - with self.assertRaises(AttributeError): - d_int.foobar = 'no' - - def test_errors(self): - with self.assertRaises(TypeError): - B = SimpleMapping[XK, Any] - - class C(Generic[B]): - pass - - def test_repr_2(self): - PY32 = sys.version_info[:2] < (3, 3) - - class C(Generic[T]): - pass - - self.assertEqual(C.__module__, __name__) - if not PY32: - self.assertEqual(C.__qualname__, - 'GenericTests.test_repr_2..C') - self.assertEqual(repr(C).split('.')[-1], 'C') - X = C[int] - self.assertEqual(X.__module__, __name__) - if not PY32: - self.assertTrue(X.__qualname__.endswith('..C')) - self.assertEqual(repr(X).split('.')[-1], 'C[int]') - - class Y(C[int]): - pass - - self.assertEqual(Y.__module__, __name__) - if not PY32: - self.assertEqual(Y.__qualname__, - 'GenericTests.test_repr_2..Y') - self.assertEqual(repr(Y).split('.')[-1], 'Y') - - def test_eq_1(self): - self.assertEqual(Generic, Generic) - self.assertEqual(Generic[T], Generic[T]) - self.assertNotEqual(Generic[KT], Generic[VT]) - - def test_eq_2(self): - - class A(Generic[T]): - pass - - class B(Generic[T]): - pass - - self.assertEqual(A, A) - self.assertNotEqual(A, B) - self.assertEqual(A[T], A[T]) - self.assertNotEqual(A[T], B[T]) - - def test_multiple_inheritance(self): - - class A(Generic[T, VT]): - pass - - class B(Generic[KT, T]): - pass - - class C(A[T, VT], Generic[VT, T, KT], B[KT, T]): - pass - - self.assertEqual(C.__parameters__, (VT, T, KT)) - - def test_nested(self): - - G = Generic - - class Visitor(G[T]): - - a = None - - def set(self, a): - self.a = a - - def get(self): - return self.a - - def visit(self): - return self.a - - V = Visitor[typing.List[int]] - - class IntListVisitor(V): - - def append(self, x): - self.a.append(x) - - a = IntListVisitor() - a.set([]) - a.append(1) - a.append(42) - self.assertEqual(a.get(), [1, 42]) - - def test_type_erasure(self): - T = TypeVar('T') - - class Node(Generic[T]): - def __init__(self, label, - left=None, - right=None): - self.label = label # type: T - self.left = left # type: Optional[Node[T]] - self.right = right # type: Optional[Node[T]] - - def foo(x): - a = Node(x) - b = Node[T](x) - c = Node[Any](x) - self.assertIs(type(a), Node) - self.assertIs(type(b), Node) - self.assertIs(type(c), Node) - self.assertEqual(a.label, x) - self.assertEqual(b.label, x) - self.assertEqual(c.label, x) - - foo(42) - - def test_implicit_any(self): - T = TypeVar('T') - - class C(Generic[T]): - pass - - class D(C): - pass - - self.assertEqual(D.__parameters__, ()) - - with self.assertRaises(Exception): - D[int] - with self.assertRaises(Exception): - D[Any] - with self.assertRaises(Exception): - D[T] - - def test_new_with_args(self): - - class A(Generic[T]): - pass - - class B(object): - def __new__(cls, arg): - # call object.__new__ - obj = super(B, cls).__new__(cls) - obj.arg = arg - return obj - - # mro: C, A, Generic, B, object - class C(A, B): - pass - - c = C('foo') - self.assertEqual(c.arg, 'foo') - - def test_new_with_args2(self): - - class A(object): - def __init__(self, arg): - self.from_a = arg - # call object - super(A, self).__init__() - - # mro: C, Generic, A, object - class C(Generic[T], A): - def __init__(self, arg): - self.from_c = arg - # call Generic - super(C, self).__init__(arg) - - c = C('foo') - self.assertEqual(c.from_a, 'foo') - self.assertEqual(c.from_c, 'foo') - - def test_new_no_args(self): - - class A(Generic[T]): - pass - - with self.assertRaises(TypeError): - A('foo') - - class B(object): - def __new__(cls): - # call object - obj = super(B, cls).__new__(cls) - obj.from_b = 'b' - return obj - - # mro: C, A, Generic, B, object - class C(A, B): - def __init__(self, arg): - self.arg = arg - - def __new__(cls, arg): - # call A - obj = super(C, cls).__new__(cls) - obj.from_c = 'c' - return obj - - c = C('foo') - self.assertEqual(c.arg, 'foo') - self.assertEqual(c.from_b, 'b') - self.assertEqual(c.from_c, 'c') - - -class ClassVarTests(BaseTestCase): - - def test_basics(self): - with self.assertRaises(TypeError): - ClassVar[1] - with self.assertRaises(TypeError): - ClassVar[int, str] - with self.assertRaises(TypeError): - ClassVar[int][str] - - def test_repr(self): - self.assertEqual(repr(ClassVar), 'typing.ClassVar') - cv = ClassVar[int] - self.assertEqual(repr(cv), 'typing.ClassVar[int]') - cv = ClassVar[Employee] - self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__) - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(type(ClassVar)): - pass - with self.assertRaises(TypeError): - class C(type(ClassVar[int])): - pass - - def test_cannot_init(self): - with self.assertRaises(TypeError): - ClassVar() - with self.assertRaises(TypeError): - type(ClassVar)() - with self.assertRaises(TypeError): - type(ClassVar[Optional[int]])() - - def test_no_isinstance(self): - with self.assertRaises(TypeError): - isinstance(1, ClassVar[int]) - with self.assertRaises(TypeError): - issubclass(int, ClassVar) - - -class FinalTests(BaseTestCase): - - def test_basics(self): - with self.assertRaises(TypeError): - Final[1] - with self.assertRaises(TypeError): - Final[int, str] - with self.assertRaises(TypeError): - Final[int][str] - - def test_repr(self): - self.assertEqual(repr(Final), 'typing.Final') - cv = Final[int] - self.assertEqual(repr(cv), 'typing.Final[int]') - cv = Final[Employee] - self.assertEqual(repr(cv), 'typing.Final[%s.Employee]' % __name__) - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(type(Final)): - pass - with self.assertRaises(TypeError): - class C(type(Final[int])): - pass - - def test_cannot_init(self): - with self.assertRaises(TypeError): - Final() - with self.assertRaises(TypeError): - type(Final)() - with self.assertRaises(TypeError): - type(Final[typing.Optional[int]])() - - def test_no_isinstance(self): - with self.assertRaises(TypeError): - isinstance(1, Final[int]) - with self.assertRaises(TypeError): - issubclass(int, Final) - - -class LiteralTests(BaseTestCase): - def test_basics(self): - Literal[1] - Literal[1, 2, 3] - Literal["x", "y", "z"] - Literal[None] - - def test_illegal_parameters_do_not_raise_runtime_errors(self): - # Type checkers should reject these types, but we do not - # raise errors at runtime to maintain maximium flexibility - Literal[int] - Literal[Literal[1, 2], Literal[4, 5]] - Literal[3j + 2, ..., ()] - Literal[b"foo", u"bar"] - Literal[{"foo": 3, "bar": 4}] - Literal[T] - - def test_literals_inside_other_types(self): - typing.List[Literal[1, 2, 3]] - typing.List[Literal[("foo", "bar", "baz")]] - - def test_repr(self): - self.assertEqual(repr(Literal[1]), "typing.Literal[1]") - self.assertEqual(repr(Literal[1, True, "foo"]), "typing.Literal[1, True, u'foo']") - self.assertEqual(repr(Literal[int]), "typing.Literal[int]") - self.assertEqual(repr(Literal), "typing.Literal") - self.assertEqual(repr(Literal[None]), "typing.Literal[None]") - - def test_cannot_init(self): - with self.assertRaises(TypeError): - Literal() - with self.assertRaises(TypeError): - Literal[1]() - with self.assertRaises(TypeError): - type(Literal)() - with self.assertRaises(TypeError): - type(Literal[1])() - - def test_no_isinstance_or_issubclass(self): - with self.assertRaises(TypeError): - isinstance(1, Literal[1]) - with self.assertRaises(TypeError): - isinstance(int, Literal[1]) - with self.assertRaises(TypeError): - issubclass(1, Literal[1]) - with self.assertRaises(TypeError): - issubclass(int, Literal[1]) - - def test_no_subclassing(self): - with self.assertRaises(TypeError): - class Foo(Literal[1]): pass - with self.assertRaises(TypeError): - class Bar(Literal): pass - - def test_no_multiple_subscripts(self): - with self.assertRaises(TypeError): - Literal[1][1] - - -class CastTests(BaseTestCase): - - def test_basics(self): - self.assertEqual(cast(int, 42), 42) - self.assertEqual(cast(float, 42), 42) - self.assertIs(type(cast(float, 42)), int) - self.assertEqual(cast(Any, 42), 42) - self.assertEqual(cast(list, 42), 42) - self.assertEqual(cast(Union[str, float], 42), 42) - self.assertEqual(cast(AnyStr, 42), 42) - self.assertEqual(cast(None, 42), 42) - - def test_errors(self): - # Bogus calls are not expected to fail. - cast(42, 42) - cast('hello', 42) - - -class ForwardRefTests(BaseTestCase): - - def test_forwardref_instance_type_error(self): - fr = typing._ForwardRef('int') - with self.assertRaises(TypeError): - isinstance(42, fr) - - def test_syntax_error(self): - - with self.assertRaises(SyntaxError): - Generic['/T'] - - def test_forwardref_subclass_type_error(self): - fr = typing._ForwardRef('int') - with self.assertRaises(TypeError): - issubclass(int, fr) - - def test_forward_equality(self): - fr = typing._ForwardRef('int') - self.assertEqual(fr, typing._ForwardRef('int')) - self.assertNotEqual(List['int'], List[int]) - - def test_forward_repr(self): - self.assertEqual(repr(List['int']), "typing.List[_ForwardRef(%r)]" % 'int') - - -class OverloadTests(BaseTestCase): - - def test_overload_fails(self): - from typing import overload - - with self.assertRaises(RuntimeError): - - @overload - def blah(): - pass - - blah() - - def test_overload_succeeds(self): - from typing import overload - - @overload - def blah(): - pass - - def blah(): - pass - - blah() - - -class CollectionsAbcTests(BaseTestCase): - - def test_hashable(self): - self.assertIsInstance(42, typing.Hashable) - self.assertNotIsInstance([], typing.Hashable) - - def test_iterable(self): - self.assertIsInstance([], typing.Iterable) - # Due to ABC caching, the second time takes a separate code - # path and could fail. So call this a few times. - self.assertIsInstance([], typing.Iterable) - self.assertIsInstance([], typing.Iterable) - self.assertNotIsInstance(42, typing.Iterable) - # Just in case, also test issubclass() a few times. - self.assertIsSubclass(list, typing.Iterable) - self.assertIsSubclass(list, typing.Iterable) - - def test_iterator(self): - it = iter([]) - self.assertIsInstance(it, typing.Iterator) - self.assertNotIsInstance(42, typing.Iterator) - - def test_sized(self): - self.assertIsInstance([], typing.Sized) - self.assertNotIsInstance(42, typing.Sized) - - def test_container(self): - self.assertIsInstance([], typing.Container) - self.assertNotIsInstance(42, typing.Container) - - def test_abstractset(self): - self.assertIsInstance(set(), typing.AbstractSet) - self.assertNotIsInstance(42, typing.AbstractSet) - - def test_mutableset(self): - self.assertIsInstance(set(), typing.MutableSet) - self.assertNotIsInstance(frozenset(), typing.MutableSet) - - def test_mapping(self): - self.assertIsInstance({}, typing.Mapping) - self.assertNotIsInstance(42, typing.Mapping) - - def test_mutablemapping(self): - self.assertIsInstance({}, typing.MutableMapping) - self.assertNotIsInstance(42, typing.MutableMapping) - - def test_sequence(self): - self.assertIsInstance([], typing.Sequence) - self.assertNotIsInstance(42, typing.Sequence) - - def test_mutablesequence(self): - self.assertIsInstance([], typing.MutableSequence) - self.assertNotIsInstance((), typing.MutableSequence) - - def test_bytestring(self): - self.assertIsInstance(b'', typing.ByteString) - self.assertIsInstance(bytearray(b''), typing.ByteString) - - def test_list(self): - self.assertIsSubclass(list, typing.List) - - def test_deque(self): - self.assertIsSubclass(collections.deque, typing.Deque) - class MyDeque(typing.Deque[int]): pass - self.assertIsInstance(MyDeque(), collections.deque) - - def test_counter(self): - self.assertIsSubclass(collections.Counter, typing.Counter) - - def test_set(self): - self.assertIsSubclass(set, typing.Set) - self.assertNotIsSubclass(frozenset, typing.Set) - - def test_frozenset(self): - self.assertIsSubclass(frozenset, typing.FrozenSet) - self.assertNotIsSubclass(set, typing.FrozenSet) - - def test_dict(self): - self.assertIsSubclass(dict, typing.Dict) - - def test_no_list_instantiation(self): - with self.assertRaises(TypeError): - typing.List() - with self.assertRaises(TypeError): - typing.List[T]() - with self.assertRaises(TypeError): - typing.List[int]() - - def test_list_subclass(self): - - class MyList(typing.List[int]): - pass - - a = MyList() - self.assertIsInstance(a, MyList) - self.assertIsInstance(a, typing.Sequence) - - self.assertIsSubclass(MyList, list) - self.assertNotIsSubclass(list, MyList) - - def test_no_dict_instantiation(self): - with self.assertRaises(TypeError): - typing.Dict() - with self.assertRaises(TypeError): - typing.Dict[KT, VT]() - with self.assertRaises(TypeError): - typing.Dict[str, int]() - - def test_dict_subclass(self): - - class MyDict(typing.Dict[str, int]): - pass - - d = MyDict() - self.assertIsInstance(d, MyDict) - self.assertIsInstance(d, typing.MutableMapping) - - self.assertIsSubclass(MyDict, dict) - self.assertNotIsSubclass(dict, MyDict) - - def test_defaultdict_instantiation(self): - self.assertIs(type(typing.DefaultDict()), collections.defaultdict) - self.assertIs(type(typing.DefaultDict[KT, VT]()), collections.defaultdict) - self.assertIs(type(typing.DefaultDict[str, int]()), collections.defaultdict) - - def test_defaultdict_subclass(self): - - class MyDefDict(typing.DefaultDict[str, int]): - pass - - dd = MyDefDict() - self.assertIsInstance(dd, MyDefDict) - - self.assertIsSubclass(MyDefDict, collections.defaultdict) - self.assertNotIsSubclass(collections.defaultdict, MyDefDict) - - def test_deque_instantiation(self): - self.assertIs(type(typing.Deque()), collections.deque) - self.assertIs(type(typing.Deque[T]()), collections.deque) - self.assertIs(type(typing.Deque[int]()), collections.deque) - class D(typing.Deque[T]): pass - self.assertIs(type(D[int]()), D) - - def test_counter_instantiation(self): - self.assertIs(type(typing.Counter()), collections.Counter) - self.assertIs(type(typing.Counter[T]()), collections.Counter) - self.assertIs(type(typing.Counter[int]()), collections.Counter) - class C(typing.Counter[T]): pass - self.assertIs(type(C[int]()), C) - - def test_counter_subclass_instantiation(self): - - class MyCounter(typing.Counter[int]): - pass - - d = MyCounter() - self.assertIsInstance(d, MyCounter) - self.assertIsInstance(d, typing.Counter) - self.assertIsInstance(d, collections.Counter) - - def test_no_set_instantiation(self): - with self.assertRaises(TypeError): - typing.Set() - with self.assertRaises(TypeError): - typing.Set[T]() - with self.assertRaises(TypeError): - typing.Set[int]() - - def test_set_subclass_instantiation(self): - - class MySet(typing.Set[int]): - pass - - d = MySet() - self.assertIsInstance(d, MySet) - - def test_no_frozenset_instantiation(self): - with self.assertRaises(TypeError): - typing.FrozenSet() - with self.assertRaises(TypeError): - typing.FrozenSet[T]() - with self.assertRaises(TypeError): - typing.FrozenSet[int]() - - def test_frozenset_subclass_instantiation(self): - - class MyFrozenSet(typing.FrozenSet[int]): - pass - - d = MyFrozenSet() - self.assertIsInstance(d, MyFrozenSet) - - def test_no_tuple_instantiation(self): - with self.assertRaises(TypeError): - Tuple() - with self.assertRaises(TypeError): - Tuple[T]() - with self.assertRaises(TypeError): - Tuple[int]() - - def test_generator(self): - def foo(): - yield 42 - g = foo() - self.assertIsSubclass(type(g), typing.Generator) - - def test_no_generator_instantiation(self): - with self.assertRaises(TypeError): - typing.Generator() - with self.assertRaises(TypeError): - typing.Generator[T, T, T]() - with self.assertRaises(TypeError): - typing.Generator[int, int, int]() - - def test_subclassing(self): - - class MMA(typing.MutableMapping): - pass - - with self.assertRaises(TypeError): # It's abstract - MMA() - - class MMC(MMA): - def __getitem__(self, k): - return None - def __setitem__(self, k, v): - pass - def __delitem__(self, k): - pass - def __iter__(self): - return iter(()) - def __len__(self): - return 0 - - self.assertEqual(len(MMC()), 0) - assert callable(MMC.update) - self.assertIsInstance(MMC(), typing.Mapping) - - class MMB(typing.MutableMapping[KT, VT]): - def __getitem__(self, k): - return None - def __setitem__(self, k, v): - pass - def __delitem__(self, k): - pass - def __iter__(self): - return iter(()) - def __len__(self): - return 0 - - self.assertEqual(len(MMB()), 0) - self.assertEqual(len(MMB[str, str]()), 0) - self.assertEqual(len(MMB[KT, VT]()), 0) - - self.assertNotIsSubclass(dict, MMA) - self.assertNotIsSubclass(dict, MMB) - - self.assertIsSubclass(MMA, typing.Mapping) - self.assertIsSubclass(MMB, typing.Mapping) - self.assertIsSubclass(MMC, typing.Mapping) - - self.assertIsInstance(MMB[KT, VT](), typing.Mapping) - self.assertIsInstance(MMB[KT, VT](), collections.Mapping) - - self.assertIsSubclass(MMA, collections.Mapping) - self.assertIsSubclass(MMB, collections.Mapping) - self.assertIsSubclass(MMC, collections.Mapping) - - self.assertIsSubclass(MMB[str, str], typing.Mapping) - self.assertIsSubclass(MMC, MMA) - - class It(typing.Iterable): pass - self.assertNotIsSubclass(list, It) - - class G(typing.Generator[int, int, int]): pass - def g(): yield 0 - self.assertIsSubclass(G, typing.Generator) - self.assertIsSubclass(G, typing.Iterable) - if hasattr(collections, 'Generator'): - self.assertIsSubclass(G, collections.Generator) - self.assertIsSubclass(G, collections.Iterable) - self.assertNotIsSubclass(type(g), G) - - def test_subclassing_subclasshook(self): - - class Base(typing.Iterable): - @classmethod - def __subclasshook__(cls, other): - if other.__name__ == 'Foo': - return True - else: - return False - - class C(Base): pass - class Foo: pass - class Bar: pass - self.assertIsSubclass(Foo, Base) - self.assertIsSubclass(Foo, C) - self.assertNotIsSubclass(Bar, C) - - def test_subclassing_register(self): - - class A(typing.Container): pass - class B(A): pass - - class C: pass - A.register(C) - self.assertIsSubclass(C, A) - self.assertNotIsSubclass(C, B) - - class D: pass - B.register(D) - self.assertIsSubclass(D, A) - self.assertIsSubclass(D, B) - - class M(): pass - collections.MutableMapping.register(M) - self.assertIsSubclass(M, typing.Mapping) - - def test_collections_as_base(self): - - class M(collections.Mapping): pass - self.assertIsSubclass(M, typing.Mapping) - self.assertIsSubclass(M, typing.Iterable) - - class S(collections.MutableSequence): pass - self.assertIsSubclass(S, typing.MutableSequence) - self.assertIsSubclass(S, typing.Iterable) - - class It(collections.Iterable): pass - self.assertIsSubclass(It, typing.Iterable) - - class A(collections.Mapping): pass - class B: pass - A.register(B) - self.assertIsSubclass(B, typing.Mapping) - - -class OtherABCTests(BaseTestCase): - - def test_contextmanager(self): - @contextlib.contextmanager - def manager(): - yield 42 - - cm = manager() - self.assertIsInstance(cm, typing.ContextManager) - self.assertNotIsInstance(42, typing.ContextManager) - - -class TypeTests(BaseTestCase): - - def test_type_basic(self): - - class User(object): pass - class BasicUser(User): pass - class ProUser(User): pass - - def new_user(user_class): - # type: (Type[User]) -> User - return user_class() - - new_user(BasicUser) - - def test_type_typevar(self): - - class User(object): pass - class BasicUser(User): pass - class ProUser(User): pass - - global U - U = TypeVar('U', bound=User) - - def new_user(user_class): - # type: (Type[U]) -> U - return user_class() - - new_user(BasicUser) - - def test_type_optional(self): - A = Optional[Type[BaseException]] # noqa - - def foo(a): - # type: (A) -> Optional[BaseException] - if a is None: - return None - else: - return a() - - assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt) - assert foo(None) is None - - -class NewTypeTests(BaseTestCase): - - def test_basic(self): - UserId = NewType('UserId', int) - UserName = NewType('UserName', str) - self.assertIsInstance(UserId(5), int) - self.assertIsInstance(UserName('Joe'), type('Joe')) - self.assertEqual(UserId(5) + 1, 6) - - def test_errors(self): - UserId = NewType('UserId', int) - UserName = NewType('UserName', str) - with self.assertRaises(TypeError): - issubclass(UserId, int) - with self.assertRaises(TypeError): - class D(UserName): - pass - - -class NamedTupleTests(BaseTestCase): - - def test_basics(self): - Emp = NamedTuple('Emp', [('name', str), ('id', int)]) - self.assertIsSubclass(Emp, tuple) - joe = Emp('Joe', 42) - jim = Emp(name='Jim', id=1) - self.assertIsInstance(joe, Emp) - self.assertIsInstance(joe, tuple) - self.assertEqual(joe.name, 'Joe') - self.assertEqual(joe.id, 42) - self.assertEqual(jim.name, 'Jim') - self.assertEqual(jim.id, 1) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp._fields, ('name', 'id')) - self.assertEqual(Emp._field_types, dict(name=str, id=int)) - - def test_pickle(self): - global Emp # pickle wants to reference the class by name - Emp = NamedTuple('Emp', [('name', str), ('id', int)]) - jane = Emp('jane', 37) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) - - -class TypedDictTests(BaseTestCase): - - def test_basics_iterable_syntax(self): - Emp = TypedDict(b'Emp', {'name': str, 'id': int}) - self.assertIsSubclass(Emp, dict) - self.assertIsSubclass(Emp, typing.MutableMapping) - if sys.version_info[0] >= 3: - import collections.abc - self.assertNotIsSubclass(Emp, collections.abc.Sequence) - jim = Emp(name='Jim', id=1) - self.assertIs(type(jim), dict) - self.assertEqual(jim['name'], 'Jim') - self.assertEqual(jim['id'], 1) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp.__module__, __name__) - self.assertEqual(Emp.__bases__, (dict,)) - self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) - self.assertEqual(Emp.__total__, True) - - def test_basics_keywords_syntax(self): - Emp = TypedDict(b'Emp', name=str, id=int) - self.assertIsSubclass(Emp, dict) - self.assertIsSubclass(Emp, typing.MutableMapping) - if sys.version_info[0] >= 3: - import collections.abc - self.assertNotIsSubclass(Emp, collections.abc.Sequence) - jim = Emp(name='Jim', id=1) - self.assertIs(type(jim), dict) - self.assertEqual(jim['name'], 'Jim') - self.assertEqual(jim['id'], 1) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp.__module__, __name__) - self.assertEqual(Emp.__bases__, (dict,)) - self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) - self.assertEqual(Emp.__total__, True) - - def test_typeddict_errors(self): - Emp = TypedDict(b'Emp', {'name': str, 'id': int}) - self.assertEqual(TypedDict.__module__, 'typing') - jim = Emp(name='Jim', id=1) - with self.assertRaises(TypeError): - isinstance({}, Emp) - with self.assertRaises(TypeError): - isinstance(jim, Emp) - with self.assertRaises(TypeError): - issubclass(dict, Emp) - with self.assertRaises(TypeError): - TypedDict('Hi', x=1) - with self.assertRaises(TypeError): - TypedDict('Hi', [('x', int), ('y', 1)]) - with self.assertRaises(TypeError): - TypedDict('Hi', [('x', int)], y=int) - - def test_pickle(self): - global EmpD # pickle wants to reference the class by name - EmpD = TypedDict(b'EmpD', name=str, id=int) - jane = EmpD({'name': 'jane', 'id': 37}) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) - self.assertEqual(jane2, {'name': 'jane', 'id': 37}) - ZZ = pickle.dumps(EmpD, proto) - EmpDnew = pickle.loads(ZZ) - self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane) - - def test_optional(self): - EmpD = TypedDict(b'EmpD', name=str, id=int) - - self.assertEqual(typing.Optional[EmpD], typing.Union[None, EmpD]) - self.assertNotEqual(typing.List[EmpD], typing.Tuple[EmpD]) - - def test_total(self): - D = TypedDict(b'D', {'x': int}, total=False) - self.assertEqual(D(), {}) - self.assertEqual(D(x=1), {'x': 1}) - self.assertEqual(D.__total__, False) - - -class IOTests(BaseTestCase): - - def test_io_submodule(self): - from typing.io import IO, TextIO, BinaryIO, __all__, __name__ - self.assertIs(IO, typing.IO) - self.assertIs(TextIO, typing.TextIO) - self.assertIs(BinaryIO, typing.BinaryIO) - self.assertEqual(set(__all__), set(['IO', 'TextIO', 'BinaryIO'])) - self.assertEqual(__name__, 'typing.io') - - -class RETests(BaseTestCase): - # Much of this is really testing _TypeAlias. - - def test_basics(self): - pat = re.compile('[a-z]+', re.I) - self.assertIsSubclass(pat.__class__, Pattern) - self.assertIsSubclass(type(pat), Pattern) - self.assertIsInstance(pat, Pattern) - - mat = pat.search('12345abcde.....') - self.assertIsSubclass(mat.__class__, Match) - self.assertIsSubclass(type(mat), Match) - self.assertIsInstance(mat, Match) - - # these should just work - Pattern[Union[str, bytes]] - Match[Union[bytes, str]] - - def test_alias_equality(self): - self.assertEqual(Pattern[str], Pattern[str]) - self.assertNotEqual(Pattern[str], Pattern[bytes]) - self.assertNotEqual(Pattern[str], Match[str]) - self.assertNotEqual(Pattern[str], str) - - def test_errors(self): - with self.assertRaises(TypeError): - # Doesn't fit AnyStr. - Pattern[int] - with self.assertRaises(TypeError): - # Can't change type vars? - Match[T] - m = Match[Union[str, bytes]] - with self.assertRaises(TypeError): - # Too complicated? - m[str] - with self.assertRaises(TypeError): - # We don't support isinstance(). - isinstance(42, Pattern[str]) - with self.assertRaises(TypeError): - # We don't support issubclass(). - issubclass(Pattern[bytes], Pattern[str]) - - def test_repr(self): - self.assertEqual(repr(Pattern), 'Pattern[~AnyStr]') - self.assertEqual(repr(Pattern[unicode]), 'Pattern[unicode]') - self.assertEqual(repr(Pattern[str]), 'Pattern[str]') - self.assertEqual(repr(Match), 'Match[~AnyStr]') - self.assertEqual(repr(Match[unicode]), 'Match[unicode]') - self.assertEqual(repr(Match[str]), 'Match[str]') - - def test_re_submodule(self): - from typing.re import Match, Pattern, __all__, __name__ - self.assertIs(Match, typing.Match) - self.assertIs(Pattern, typing.Pattern) - self.assertEqual(set(__all__), set(['Match', 'Pattern'])) - self.assertEqual(__name__, 'typing.re') - - def test_cannot_subclass(self): - with self.assertRaises(TypeError) as ex: - - class A(typing.Match): - pass - - self.assertEqual(str(ex.exception), - "Cannot subclass typing._TypeAlias") - - -class AllTests(BaseTestCase): - """Tests for __all__.""" - - def test_all(self): - from typing import __all__ as a - # Just spot-check the first and last of every category. - self.assertIn('AbstractSet', a) - self.assertIn('ValuesView', a) - self.assertIn('cast', a) - self.assertIn('overload', a) - # Check that io and re are not exported. - self.assertNotIn('io', a) - self.assertNotIn('re', a) - # Spot-check that stdlib modules aren't exported. - self.assertNotIn('os', a) - self.assertNotIn('sys', a) - # Check that Text is defined. - self.assertIn('Text', a) - # Check previously missing class. - self.assertIn('SupportsComplex', a) - - def test_respect_no_type_check(self): - @typing.no_type_check - class NoTpCheck(object): - class Inn(object): - def __init__(self, x): - # type: (this is not actually a type) -> None # noqa - pass - self.assertTrue(NoTpCheck.__no_type_check__) - self.assertTrue(NoTpCheck.Inn.__init__.__no_type_check__) - - def test_get_type_hints_dummy(self): - - def foo(x): - # type: (int) -> int - return x + 1 - - self.assertIsNone(typing.get_type_hints(foo)) - - def test_typing_compiles_with_opt(self): - file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'typing.py') - try: - subprocess.check_output([sys.executable, '-OO', file_path], - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - self.fail('Module does not compile with optimize=2 (-OO flag).') - - -if __name__ == '__main__': - main() diff --git a/python2/typing.py b/python2/typing.py deleted file mode 100644 index dd16d9af9..000000000 --- a/python2/typing.py +++ /dev/null @@ -1,2550 +0,0 @@ -from __future__ import absolute_import, unicode_literals - -import abc -from abc import abstractmethod, abstractproperty -import collections -import functools -import re as stdlib_re # Avoid confusion with the re we export. -import sys -import types -import copy -try: - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc # Fallback for PY3.2. - - -# Please keep __all__ alphabetized within each category. -__all__ = [ - # Super-special typing primitives. - 'Any', - 'Callable', - 'ClassVar', - 'Final', - 'Generic', - 'Literal', - 'Optional', - 'Protocol', - 'Tuple', - 'Type', - 'TypeVar', - 'Union', - - # ABCs (from collections.abc). - 'AbstractSet', # collections.abc.Set. - 'GenericMeta', # subclass of abc.ABCMeta and a metaclass - # for 'Generic' and ABCs below. - 'ByteString', - 'Container', - 'ContextManager', - 'Hashable', - 'ItemsView', - 'Iterable', - 'Iterator', - 'KeysView', - 'Mapping', - 'MappingView', - 'MutableMapping', - 'MutableSequence', - 'MutableSet', - 'Sequence', - 'Sized', - 'ValuesView', - - # Structural checks, a.k.a. protocols. - 'Reversible', - 'SupportsAbs', - 'SupportsComplex', - 'SupportsFloat', - 'SupportsIndex', - 'SupportsInt', - - # Concrete collection types. - 'Counter', - 'Deque', - 'Dict', - 'DefaultDict', - 'List', - 'Set', - 'FrozenSet', - 'NamedTuple', # Not really a type. - 'TypedDict', # Not really a type. - 'Generator', - - # One-off things. - 'AnyStr', - 'cast', - 'final', - 'get_type_hints', - 'NewType', - 'no_type_check', - 'no_type_check_decorator', - 'NoReturn', - 'overload', - 'runtime_checkable', - 'Text', - 'TYPE_CHECKING', -] - -# The pseudo-submodules 're' and 'io' are part of the public -# namespace, but excluded from __all__ because they might stomp on -# legitimate imports of those modules. - - -def _qualname(x): - if sys.version_info[:2] >= (3, 3): - return x.__qualname__ - else: - # Fall back to just name. - return x.__name__ - - -def _trim_name(nm): - whitelist = ('_TypeAlias', '_ForwardRef', '_TypingBase', '_FinalTypingBase') - if nm.startswith('_') and nm not in whitelist: - nm = nm[1:] - return nm - - -class TypingMeta(type): - """Metaclass for most types defined in typing module - (not a part of public API). - - This also defines a dummy constructor (all the work for most typing - constructs is done in __new__) and a nicer repr(). - """ - - _is_protocol = False - - def __new__(cls, name, bases, namespace): - return super(TypingMeta, cls).__new__(cls, str(name), bases, namespace) - - @classmethod - def assert_no_subclassing(cls, bases): - for base in bases: - if isinstance(base, cls): - raise TypeError("Cannot subclass %s" % - (', '.join(map(_type_repr, bases)) or '()')) - - def __init__(self, *args, **kwds): - pass - - def _eval_type(self, globalns, localns): - """Override this in subclasses to interpret forward references. - - For example, List['C'] is internally stored as - List[_ForwardRef('C')], which should evaluate to List[C], - where C is an object found in globalns or localns (searching - localns first, of course). - """ - return self - - def _get_type_vars(self, tvars): - pass - - def __repr__(self): - qname = _trim_name(_qualname(self)) - return '%s.%s' % (self.__module__, qname) - - -class _TypingBase(object): - """Internal indicator of special typing constructs.""" - __metaclass__ = TypingMeta - __slots__ = ('__weakref__',) - - def __init__(self, *args, **kwds): - pass - - def __new__(cls, *args, **kwds): - """Constructor. - - This only exists to give a better error message in case - someone tries to subclass a special typing object (not a good idea). - """ - if (len(args) == 3 and - isinstance(args[0], str) and - isinstance(args[1], tuple)): - # Close enough. - raise TypeError("Cannot subclass %r" % cls) - return super(_TypingBase, cls).__new__(cls) - - # Things that are not classes also need these. - def _eval_type(self, globalns, localns): - return self - - def _get_type_vars(self, tvars): - pass - - def __repr__(self): - cls = type(self) - qname = _trim_name(_qualname(cls)) - return '%s.%s' % (cls.__module__, qname) - - def __call__(self, *args, **kwds): - raise TypeError("Cannot instantiate %r" % type(self)) - - -class _FinalTypingBase(_TypingBase): - """Internal mix-in class to prevent instantiation. - - Prevents instantiation unless _root=True is given in class call. - It is used to create pseudo-singleton instances Any, Union, Optional, etc. - """ - - __slots__ = () - - def __new__(cls, *args, **kwds): - self = super(_FinalTypingBase, cls).__new__(cls, *args, **kwds) - if '_root' in kwds and kwds['_root'] is True: - return self - raise TypeError("Cannot instantiate %r" % cls) - - def __reduce__(self): - return _trim_name(type(self).__name__) - - -class _ForwardRef(_TypingBase): - """Internal wrapper to hold a forward reference.""" - - __slots__ = ('__forward_arg__', '__forward_code__', - '__forward_evaluated__', '__forward_value__') - - def __init__(self, arg): - super(_ForwardRef, self).__init__(arg) - if not isinstance(arg, basestring): - raise TypeError('Forward reference must be a string -- got %r' % (arg,)) - try: - code = compile(arg, '', 'eval') - except SyntaxError: - raise SyntaxError('Forward reference must be an expression -- got %r' % - (arg,)) - self.__forward_arg__ = arg - self.__forward_code__ = code - self.__forward_evaluated__ = False - self.__forward_value__ = None - - def _eval_type(self, globalns, localns): - if not self.__forward_evaluated__ or localns is not globalns: - if globalns is None and localns is None: - globalns = localns = {} - elif globalns is None: - globalns = localns - elif localns is None: - localns = globalns - self.__forward_value__ = _type_check( - eval(self.__forward_code__, globalns, localns), - "Forward references must evaluate to types.") - self.__forward_evaluated__ = True - return self.__forward_value__ - - def __eq__(self, other): - if not isinstance(other, _ForwardRef): - return NotImplemented - return (self.__forward_arg__ == other.__forward_arg__ and - self.__forward_value__ == other.__forward_value__) - - def __hash__(self): - return hash((self.__forward_arg__, self.__forward_value__)) - - def __instancecheck__(self, obj): - raise TypeError("Forward references cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Forward references cannot be used with issubclass().") - - def __repr__(self): - return '_ForwardRef(%r)' % (self.__forward_arg__,) - - -class _TypeAlias(_TypingBase): - """Internal helper class for defining generic variants of concrete types. - - Note that this is not a type; let's call it a pseudo-type. It cannot - be used in instance and subclass checks in parameterized form, i.e. - ``isinstance(42, Match[str])`` raises ``TypeError`` instead of returning - ``False``. - """ - - __slots__ = ('name', 'type_var', 'impl_type', 'type_checker') - - def __init__(self, name, type_var, impl_type, type_checker): - """Initializer. - - Args: - name: The name, e.g. 'Pattern'. - type_var: The type parameter, e.g. AnyStr, or the - specific type, e.g. str. - impl_type: The implementation type. - type_checker: Function that takes an impl_type instance. - and returns a value that should be a type_var instance. - """ - assert isinstance(name, basestring), repr(name) - assert isinstance(impl_type, type), repr(impl_type) - assert not isinstance(impl_type, TypingMeta), repr(impl_type) - assert isinstance(type_var, (type, _TypingBase)), repr(type_var) - self.name = name - self.type_var = type_var - self.impl_type = impl_type - self.type_checker = type_checker - - def __repr__(self): - return "%s[%s]" % (self.name, _type_repr(self.type_var)) - - def __getitem__(self, parameter): - if not isinstance(self.type_var, TypeVar): - raise TypeError("%s cannot be further parameterized." % self) - if self.type_var.__constraints__ and isinstance(parameter, type): - if not issubclass(parameter, self.type_var.__constraints__): - raise TypeError("%s is not a valid substitution for %s." % - (parameter, self.type_var)) - if isinstance(parameter, TypeVar) and parameter is not self.type_var: - raise TypeError("%s cannot be re-parameterized." % self) - return self.__class__(self.name, parameter, - self.impl_type, self.type_checker) - - def __eq__(self, other): - if not isinstance(other, _TypeAlias): - return NotImplemented - return self.name == other.name and self.type_var == other.type_var - - def __hash__(self): - return hash((self.name, self.type_var)) - - def __instancecheck__(self, obj): - if not isinstance(self.type_var, TypeVar): - raise TypeError("Parameterized type aliases cannot be used " - "with isinstance().") - return isinstance(obj, self.impl_type) - - def __subclasscheck__(self, cls): - if not isinstance(self.type_var, TypeVar): - raise TypeError("Parameterized type aliases cannot be used " - "with issubclass().") - return issubclass(cls, self.impl_type) - - -def _get_type_vars(types, tvars): - for t in types: - if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): - t._get_type_vars(tvars) - - -def _type_vars(types): - tvars = [] - _get_type_vars(types, tvars) - return tuple(tvars) - - -def _eval_type(t, globalns, localns): - if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): - return t._eval_type(globalns, localns) - return t - - -def _type_check(arg, msg): - """Check that the argument is a type, and return it (internal helper). - - As a special case, accept None and return type(None) instead. - Also, _TypeAlias instances (e.g. Match, Pattern) are acceptable. - - The msg argument is a human-readable error message, e.g. - - "Union[arg, ...]: arg should be a type." - - We append the repr() of the actual value (truncated to 100 chars). - """ - if arg is None: - return type(None) - if isinstance(arg, basestring): - arg = _ForwardRef(arg) - if ( - isinstance(arg, _TypingBase) and type(arg).__name__ == '_ClassVar' or - not isinstance(arg, (type, _TypingBase)) and not callable(arg) - ): - raise TypeError(msg + " Got %.100r." % (arg,)) - # Bare Union etc. are not valid as type arguments - if ( - type(arg).__name__ in ('_Union', '_Optional') and - not getattr(arg, '__origin__', None) or - isinstance(arg, TypingMeta) and arg._gorg in (Generic, Protocol) - ): - raise TypeError("Plain %s is not valid as type argument" % arg) - return arg - - -def _type_repr(obj): - """Return the repr() of an object, special-casing types (internal helper). - - If obj is a type, we return a shorter version than the default - type.__repr__, based on the module and qualified name, which is - typically enough to uniquely identify a type. For everything - else, we fall back on repr(obj). - """ - if isinstance(obj, type) and not isinstance(obj, TypingMeta): - if obj.__module__ == '__builtin__': - return _qualname(obj) - return '%s.%s' % (obj.__module__, _qualname(obj)) - if obj is Ellipsis: - return '...' - if isinstance(obj, types.FunctionType): - return obj.__name__ - return repr(obj) - - -class ClassVarMeta(TypingMeta): - """Metaclass for _ClassVar""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(ClassVarMeta, cls).__new__(cls, name, bases, namespace) - return self - - -class _ClassVar(_FinalTypingBase): - """Special type construct to mark class variables. - - An annotation wrapped in ClassVar indicates that a given - attribute is intended to be used as a class variable and - should not be set on instances of that class. Usage:: - - class Starship: - stats = {} # type: ClassVar[Dict[str, int]] # class variable - damage = 10 # type: int # instance variable - - ClassVar accepts only types and cannot be further subscribed. - - Note that ClassVar is not a class itself, and should not - be used with isinstance() or issubclass(). - """ - - __metaclass__ = ClassVarMeta - __slots__ = ('__type__',) - - def __init__(self, tp=None, _root=False): - self.__type__ = tp - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is None: - return cls(_type_check(item, - '{} accepts only types.'.format(cls.__name__[1:])), - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - return type(self)(_eval_type(self.__type__, globalns, localns), - _root=True) - - def __repr__(self): - r = super(_ClassVar, self).__repr__() - if self.__type__ is not None: - r += '[{}]'.format(_type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, _ClassVar): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - -ClassVar = _ClassVar(_root=True) - - -class _FinalMeta(TypingMeta): - """Metaclass for _Final""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(_FinalMeta, cls).__new__(cls, name, bases, namespace) - return self - - -class _Final(_FinalTypingBase): - """A special typing construct to indicate that a name - cannot be re-assigned or overridden in a subclass. - For example: - - MAX_SIZE: Final = 9000 - MAX_SIZE += 1 # Error reported by type checker - - class Connection: - TIMEOUT: Final[int] = 10 - class FastConnector(Connection): - TIMEOUT = 1 # Error reported by type checker - - There is no runtime checking of these properties. - """ - - __metaclass__ = _FinalMeta - __slots__ = ('__type__',) - - def __init__(self, tp=None, **kwds): - self.__type__ = tp - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is None: - return cls(_type_check(item, - '{} accepts only single type.'.format(cls.__name__[1:])), - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - new_tp = _eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(new_tp, _root=True) - - def __repr__(self): - r = super(_Final, self).__repr__() - if self.__type__ is not None: - r += '[{}]'.format(_type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, _Final): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - -Final = _Final(_root=True) - - -def final(f): - """This decorator can be used to indicate to type checkers that - the decorated method cannot be overridden, and decorated class - cannot be subclassed. For example: - - class Base: - @final - def done(self) -> None: - ... - class Sub(Base): - def done(self) -> None: # Error reported by type checker - ... - @final - class Leaf: - ... - class Other(Leaf): # Error reported by type checker - ... - - There is no runtime checking of these properties. - """ - return f - - -class _LiteralMeta(TypingMeta): - """Metaclass for _Literal""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(_LiteralMeta, cls).__new__(cls, name, bases, namespace) - return self - - -class _Literal(_FinalTypingBase): - """A type that can be used to indicate to type checkers that the - corresponding value has a value literally equivalent to the - provided parameter. For example: - - var: Literal[4] = 4 - - The type checker understands that 'var' is literally equal to the - value 4 and no other value. - - Literal[...] cannot be subclassed. There is no runtime checking - verifying that the parameter is actually a value instead of a type. - """ - - __metaclass__ = _LiteralMeta - __slots__ = ('__values__',) - - def __init__(self, values=None, **kwds): - self.__values__ = values - - def __getitem__(self, item): - cls = type(self) - if self.__values__ is None: - if not isinstance(item, tuple): - item = (item,) - return cls(values=item, - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - return self - - def __repr__(self): - r = super(_Literal, self).__repr__() - if self.__values__ is not None: - r += '[{}]'.format(', '.join(map(_type_repr, self.__values__))) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__values__)) - - def __eq__(self, other): - if not isinstance(other, _Literal): - return NotImplemented - if self.__values__ is not None: - return self.__values__ == other.__values__ - return self is other - - -Literal = _Literal(_root=True) - - -class AnyMeta(TypingMeta): - """Metaclass for Any.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(AnyMeta, cls).__new__(cls, name, bases, namespace) - return self - - -class _Any(_FinalTypingBase): - """Special type indicating an unconstrained type. - - - Any is compatible with every type. - - Any assumed to have all methods. - - All values assumed to be instances of Any. - - Note that all the above statements are true from the point of view of - static type checkers. At runtime, Any should not be used with instance - or class checks. - """ - __metaclass__ = AnyMeta - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("Any cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Any cannot be used with issubclass().") - - -Any = _Any(_root=True) - - -class NoReturnMeta(TypingMeta): - """Metaclass for NoReturn.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(NoReturnMeta, cls).__new__(cls, name, bases, namespace) - return self - - -class _NoReturn(_FinalTypingBase): - """Special type indicating functions that never return. - Example:: - - from typing import NoReturn - - def stop() -> NoReturn: - raise Exception('no way') - - This type is invalid in other positions, e.g., ``List[NoReturn]`` - will fail in static type checkers. - """ - __metaclass__ = NoReturnMeta - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("NoReturn cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("NoReturn cannot be used with issubclass().") - - -NoReturn = _NoReturn(_root=True) - - -class TypeVarMeta(TypingMeta): - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - return super(TypeVarMeta, cls).__new__(cls, name, bases, namespace) - - -class TypeVar(_TypingBase): - """Type variable. - - Usage:: - - T = TypeVar('T') # Can be anything - A = TypeVar('A', str, bytes) # Must be str or bytes - - Type variables exist primarily for the benefit of static type - checkers. They serve as the parameters for generic types as well - as for generic function definitions. See class Generic for more - information on generic types. Generic functions work as follows: - - def repeat(x: T, n: int) -> List[T]: - '''Return a list containing n references to x.''' - return [x]*n - - def longest(x: A, y: A) -> A: - '''Return the longest of two strings.''' - return x if len(x) >= len(y) else y - - The latter example's signature is essentially the overloading - of (str, str) -> str and (bytes, bytes) -> bytes. Also note - that if the arguments are instances of some subclass of str, - the return type is still plain str. - - At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError. - - Type variables defined with covariant=True or contravariant=True - can be used do declare covariant or contravariant generic types. - See PEP 484 for more details. By default generic types are invariant - in all type variables. - - Type variables can be introspected. e.g.: - - T.__name__ == 'T' - T.__constraints__ == () - T.__covariant__ == False - T.__contravariant__ = False - A.__constraints__ == (str, bytes) - """ - - __metaclass__ = TypeVarMeta - __slots__ = ('__name__', '__bound__', '__constraints__', - '__covariant__', '__contravariant__') - - def __init__(self, name, *constraints, **kwargs): - super(TypeVar, self).__init__(name, *constraints, **kwargs) - bound = kwargs.get('bound', None) - covariant = kwargs.get('covariant', False) - contravariant = kwargs.get('contravariant', False) - self.__name__ = name - if covariant and contravariant: - raise ValueError("Bivariant types are not supported.") - self.__covariant__ = bool(covariant) - self.__contravariant__ = bool(contravariant) - if constraints and bound is not None: - raise TypeError("Constraints cannot be combined with bound=...") - if constraints and len(constraints) == 1: - raise TypeError("A single constraint is not allowed") - msg = "TypeVar(name, constraint, ...): constraints must be types." - self.__constraints__ = tuple(_type_check(t, msg) for t in constraints) - if bound: - self.__bound__ = _type_check(bound, "Bound must be a type.") - else: - self.__bound__ = None - - def _get_type_vars(self, tvars): - if self not in tvars: - tvars.append(self) - - def __repr__(self): - if self.__covariant__: - prefix = '+' - elif self.__contravariant__: - prefix = '-' - else: - prefix = '~' - return prefix + self.__name__ - - def __instancecheck__(self, instance): - raise TypeError("Type variables cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Type variables cannot be used with issubclass().") - - -# Some unconstrained type variables. These are used by the container types. -# (These are not for export.) -T = TypeVar('T') # Any type. -KT = TypeVar('KT') # Key type. -VT = TypeVar('VT') # Value type. -T_co = TypeVar('T_co', covariant=True) # Any type covariant containers. -V_co = TypeVar('V_co', covariant=True) # Any type covariant containers. -VT_co = TypeVar('VT_co', covariant=True) # Value type covariant containers. -T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. - -# A useful type variable with constraints. This represents string types. -# (This one *is* for export!) -AnyStr = TypeVar('AnyStr', bytes, unicode) - - -def _replace_arg(arg, tvars, args): - """An internal helper function: replace arg if it is a type variable - found in tvars with corresponding substitution from args or - with corresponding substitution sub-tree if arg is a generic type. - """ - - if tvars is None: - tvars = [] - if hasattr(arg, '_subs_tree') and isinstance(arg, (GenericMeta, _TypingBase)): - return arg._subs_tree(tvars, args) - if isinstance(arg, TypeVar): - for i, tvar in enumerate(tvars): - if arg == tvar: - return args[i] - return arg - - -# Special typing constructs Union, Optional, Generic, Callable and Tuple -# use three special attributes for internal bookkeeping of generic types: -# * __parameters__ is a tuple of unique free type parameters of a generic -# type, for example, Dict[T, T].__parameters__ == (T,); -# * __origin__ keeps a reference to a type that was subscripted, -# e.g., Union[T, int].__origin__ == Union; -# * __args__ is a tuple of all arguments used in subscripting, -# e.g., Dict[T, int].__args__ == (T, int). - - -def _subs_tree(cls, tvars=None, args=None): - """An internal helper function: calculate substitution tree - for generic cls after replacing its type parameters with - substitutions in tvars -> args (if any). - Repeat the same following __origin__'s. - - Return a list of arguments with all possible substitutions - performed. Arguments that are generic classes themselves are represented - as tuples (so that no new classes are created by this function). - For example: _subs_tree(List[Tuple[int, T]][str]) == [(Tuple, int, str)] - """ - - if cls.__origin__ is None: - return cls - # Make of chain of origins (i.e. cls -> cls.__origin__) - current = cls.__origin__ - orig_chain = [] - while current.__origin__ is not None: - orig_chain.append(current) - current = current.__origin__ - # Replace type variables in __args__ if asked ... - tree_args = [] - for arg in cls.__args__: - tree_args.append(_replace_arg(arg, tvars, args)) - # ... then continue replacing down the origin chain. - for ocls in orig_chain: - new_tree_args = [] - for arg in ocls.__args__: - new_tree_args.append(_replace_arg(arg, ocls.__parameters__, tree_args)) - tree_args = new_tree_args - return tree_args - - -def _remove_dups_flatten(parameters): - """An internal helper for Union creation and substitution: flatten Union's - among parameters, then remove duplicates and strict subclasses. - """ - - # Flatten out Union[Union[...], ...]. - params = [] - for p in parameters: - if isinstance(p, _Union) and p.__origin__ is Union: - params.extend(p.__args__) - elif isinstance(p, tuple) and len(p) > 0 and p[0] is Union: - params.extend(p[1:]) - else: - params.append(p) - # Weed out strict duplicates, preserving the first of each occurrence. - all_params = set(params) - if len(all_params) < len(params): - new_params = [] - for t in params: - if t in all_params: - new_params.append(t) - all_params.remove(t) - params = new_params - assert not all_params, all_params - # Weed out subclasses. - # E.g. Union[int, Employee, Manager] == Union[int, Employee]. - # If object is present it will be sole survivor among proper classes. - # Never discard type variables. - # (In particular, Union[str, AnyStr] != AnyStr.) - all_params = set(params) - for t1 in params: - if not isinstance(t1, type): - continue - if any(isinstance(t2, type) and issubclass(t1, t2) - for t2 in all_params - {t1} - if not (isinstance(t2, GenericMeta) and - t2.__origin__ is not None)): - all_params.remove(t1) - return tuple(t for t in params if t in all_params) - - -def _check_generic(cls, parameters): - # Check correct count for parameters of a generic cls (internal helper). - if not cls.__parameters__: - raise TypeError("%s is not a generic class" % repr(cls)) - alen = len(parameters) - elen = len(cls.__parameters__) - if alen != elen: - raise TypeError("Too %s parameters for %s; actual %s, expected %s" % - ("many" if alen > elen else "few", repr(cls), alen, elen)) - - -_cleanups = [] - - -def _tp_cache(func): - maxsize = 128 - cache = {} - _cleanups.append(cache.clear) - - @functools.wraps(func) - def inner(*args): - key = args - try: - return cache[key] - except TypeError: - # Assume it's an unhashable argument. - return func(*args) - except KeyError: - value = func(*args) - if len(cache) >= maxsize: - # If the cache grows too much, just start over. - cache.clear() - cache[key] = value - return value - - return inner - - -class UnionMeta(TypingMeta): - """Metaclass for Union.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - return super(UnionMeta, cls).__new__(cls, name, bases, namespace) - - -class _Union(_FinalTypingBase): - """Union type; Union[X, Y] means either X or Y. - - To define a union, use e.g. Union[int, str]. Details: - - - The arguments must be types and there must be at least one. - - - None as an argument is a special case and is replaced by - type(None). - - - Unions of unions are flattened, e.g.:: - - Union[Union[int, str], float] == Union[int, str, float] - - - Unions of a single argument vanish, e.g.:: - - Union[int] == int # The constructor actually returns int - - - Redundant arguments are skipped, e.g.:: - - Union[int, str, int] == Union[int, str] - - - When comparing unions, the argument order is ignored, e.g.:: - - Union[int, str] == Union[str, int] - - - When two arguments have a subclass relationship, the least - derived argument is kept, e.g.:: - - class Employee: pass - class Manager(Employee): pass - Union[int, Employee, Manager] == Union[int, Employee] - Union[Manager, int, Employee] == Union[int, Employee] - Union[Employee, Manager] == Employee - - - Similar for object:: - - Union[int, object] == object - - - You cannot subclass or instantiate a union. - - - You can use Optional[X] as a shorthand for Union[X, None]. - """ - - __metaclass__ = UnionMeta - __slots__ = ('__parameters__', '__args__', '__origin__', '__tree_hash__') - - def __new__(cls, parameters=None, origin=None, *args, **kwds): - self = super(_Union, cls).__new__(cls, parameters, origin, *args, **kwds) - if origin is None: - self.__parameters__ = None - self.__args__ = None - self.__origin__ = None - self.__tree_hash__ = hash(frozenset(('Union',))) - return self - if not isinstance(parameters, tuple): - raise TypeError("Expected parameters=") - if origin is Union: - parameters = _remove_dups_flatten(parameters) - # It's not a union if there's only one type left. - if len(parameters) == 1: - return parameters[0] - self.__parameters__ = _type_vars(parameters) - self.__args__ = parameters - self.__origin__ = origin - # Pre-calculate the __hash__ on instantiation. - # This improves speed for complex substitutions. - subs_tree = self._subs_tree() - if isinstance(subs_tree, tuple): - self.__tree_hash__ = hash(frozenset(subs_tree)) - else: - self.__tree_hash__ = hash(subs_tree) - return self - - def _eval_type(self, globalns, localns): - if self.__args__ is None: - return self - ev_args = tuple(_eval_type(t, globalns, localns) for t in self.__args__) - ev_origin = _eval_type(self.__origin__, globalns, localns) - if ev_args == self.__args__ and ev_origin == self.__origin__: - # Everything is already evaluated. - return self - return self.__class__(ev_args, ev_origin, _root=True) - - def _get_type_vars(self, tvars): - if self.__origin__ and self.__parameters__: - _get_type_vars(self.__parameters__, tvars) - - def __repr__(self): - if self.__origin__ is None: - return super(_Union, self).__repr__() - tree = self._subs_tree() - if not isinstance(tree, tuple): - return repr(tree) - return tree[0]._tree_repr(tree) - - def _tree_repr(self, tree): - arg_list = [] - for arg in tree[1:]: - if not isinstance(arg, tuple): - arg_list.append(_type_repr(arg)) - else: - arg_list.append(arg[0]._tree_repr(arg)) - return super(_Union, self).__repr__() + '[%s]' % ', '.join(arg_list) - - @_tp_cache - def __getitem__(self, parameters): - if parameters == (): - raise TypeError("Cannot take a Union of no types.") - if not isinstance(parameters, tuple): - parameters = (parameters,) - if self.__origin__ is None: - msg = "Union[arg, ...]: each arg must be a type." - else: - msg = "Parameters to generic types must be types." - parameters = tuple(_type_check(p, msg) for p in parameters) - if self is not Union: - _check_generic(self, parameters) - return self.__class__(parameters, origin=self, _root=True) - - def _subs_tree(self, tvars=None, args=None): - if self is Union: - return Union # Nothing to substitute - tree_args = _subs_tree(self, tvars, args) - tree_args = _remove_dups_flatten(tree_args) - if len(tree_args) == 1: - return tree_args[0] # Union of a single type is that type - return (Union,) + tree_args - - def __eq__(self, other): - if isinstance(other, _Union): - return self.__tree_hash__ == other.__tree_hash__ - elif self is not Union: - return self._subs_tree() == other - else: - return self is other - - def __hash__(self): - return self.__tree_hash__ - - def __instancecheck__(self, obj): - raise TypeError("Unions cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Unions cannot be used with issubclass().") - - -Union = _Union(_root=True) - - -class OptionalMeta(TypingMeta): - """Metaclass for Optional.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - return super(OptionalMeta, cls).__new__(cls, name, bases, namespace) - - -class _Optional(_FinalTypingBase): - """Optional type. - - Optional[X] is equivalent to Union[X, None]. - """ - - __metaclass__ = OptionalMeta - __slots__ = () - - @_tp_cache - def __getitem__(self, arg): - arg = _type_check(arg, "Optional[t] requires a single type.") - return Union[arg, type(None)] - - -Optional = _Optional(_root=True) - - -def _next_in_mro(cls): - """Helper for Generic.__new__. - - Returns the class after the last occurrence of Generic or - Generic[...] in cls.__mro__. - """ - next_in_mro = object - # Look for the last occurrence of Generic or Generic[...]. - for i, c in enumerate(cls.__mro__[:-1]): - if isinstance(c, GenericMeta) and c._gorg is Generic: - next_in_mro = cls.__mro__[i + 1] - return next_in_mro - - -def _make_subclasshook(cls): - """Construct a __subclasshook__ callable that incorporates - the associated __extra__ class in subclass checks performed - against cls. - """ - if isinstance(cls.__extra__, abc.ABCMeta): - # The logic mirrors that of ABCMeta.__subclasscheck__. - # Registered classes need not be checked here because - # cls and its extra share the same _abc_registry. - def __extrahook__(cls, subclass): - res = cls.__extra__.__subclasshook__(subclass) - if res is not NotImplemented: - return res - if cls.__extra__ in getattr(subclass, '__mro__', ()): - return True - for scls in cls.__extra__.__subclasses__(): - if isinstance(scls, GenericMeta): - continue - if issubclass(subclass, scls): - return True - return NotImplemented - else: - # For non-ABC extras we'll just call issubclass(). - def __extrahook__(cls, subclass): - if cls.__extra__ and issubclass(subclass, cls.__extra__): - return True - return NotImplemented - return classmethod(__extrahook__) - - -class GenericMeta(TypingMeta, abc.ABCMeta): - """Metaclass for generic types. - - This is a metaclass for typing.Generic and generic ABCs defined in - typing module. User defined subclasses of GenericMeta can override - __new__ and invoke super().__new__. Note that GenericMeta.__new__ - has strict rules on what is allowed in its bases argument: - * plain Generic is disallowed in bases; - * Generic[...] should appear in bases at most once; - * if Generic[...] is present, then it should list all type variables - that appear in other bases. - In addition, type of all generic bases is erased, e.g., C[int] is - stripped to plain C. - """ - - def __new__(cls, name, bases, namespace, - tvars=None, args=None, origin=None, extra=None, orig_bases=None): - """Create a new generic class. GenericMeta.__new__ accepts - keyword arguments that are used for internal bookkeeping, therefore - an override should pass unused keyword arguments to super(). - """ - if tvars is not None: - # Called from __getitem__() below. - assert origin is not None - assert all(isinstance(t, TypeVar) for t in tvars), tvars - else: - # Called from class statement. - assert tvars is None, tvars - assert args is None, args - assert origin is None, origin - - # Get the full set of tvars from the bases. - tvars = _type_vars(bases) - # Look for Generic[T1, ..., Tn]. - # If found, tvars must be a subset of it. - # If not found, tvars is it. - # Also check for and reject plain Generic, - # and reject multiple Generic[...]. - gvars = None - for base in bases: - if base is Generic: - raise TypeError("Cannot inherit from plain Generic") - if (isinstance(base, GenericMeta) and - base.__origin__ in (Generic, Protocol)): - if gvars is not None: - raise TypeError( - "Cannot inherit from Generic[...] or" - " Protocol[...] multiple times.") - gvars = base.__parameters__ - if gvars is None: - gvars = tvars - else: - tvarset = set(tvars) - gvarset = set(gvars) - if not tvarset <= gvarset: - raise TypeError( - "Some type variables (%s) " - "are not listed in %s[%s]" % - (", ".join(str(t) for t in tvars if t not in gvarset), - "Generic" if any(b.__origin__ is Generic - for b in bases) else "Protocol", - ", ".join(str(g) for g in gvars))) - tvars = gvars - - initial_bases = bases - if extra is None: - extra = namespace.get('__extra__') - if extra is not None and type(extra) is abc.ABCMeta and extra not in bases: - bases = (extra,) + bases - bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases) - - # remove bare Generic from bases if there are other generic bases - if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): - bases = tuple(b for b in bases if b is not Generic) - namespace.update({'__origin__': origin, '__extra__': extra}) - self = super(GenericMeta, cls).__new__(cls, name, bases, namespace) - super(GenericMeta, self).__setattr__('_gorg', - self if not origin else origin._gorg) - - self.__parameters__ = tvars - # Be prepared that GenericMeta will be subclassed by TupleMeta - # and CallableMeta, those two allow ..., (), or [] in __args___. - self.__args__ = tuple(Ellipsis if a is _TypingEllipsis else - () if a is _TypingEmpty else - a for a in args) if args else None - # Speed hack (https://github.com/python/typing/issues/196). - self.__next_in_mro__ = _next_in_mro(self) - # Preserve base classes on subclassing (__bases__ are type erased now). - if orig_bases is None: - self.__orig_bases__ = initial_bases - - # This allows unparameterized generic collections to be used - # with issubclass() and isinstance() in the same way as their - # collections.abc counterparts (e.g., isinstance([], Iterable)). - if ( - '__subclasshook__' not in namespace and extra or - # allow overriding - getattr(self.__subclasshook__, '__name__', '') == '__extrahook__' - ): - self.__subclasshook__ = _make_subclasshook(self) - - if origin and hasattr(origin, '__qualname__'): # Fix for Python 3.2. - self.__qualname__ = origin.__qualname__ - self.__tree_hash__ = (hash(self._subs_tree()) if origin else - super(GenericMeta, self).__hash__()) - return self - - def __init__(self, *args, **kwargs): - super(GenericMeta, self).__init__(*args, **kwargs) - if isinstance(self.__extra__, abc.ABCMeta): - self._abc_registry = self.__extra__._abc_registry - self._abc_cache = self.__extra__._abc_cache - elif self.__origin__ is not None: - self._abc_registry = self.__origin__._abc_registry - self._abc_cache = self.__origin__._abc_cache - - # _abc_negative_cache and _abc_negative_cache_version - # realised as descriptors, since GenClass[t1, t2, ...] always - # share subclass info with GenClass. - # This is an important memory optimization. - @property - def _abc_negative_cache(self): - if isinstance(self.__extra__, abc.ABCMeta): - return self.__extra__._abc_negative_cache - return self._gorg._abc_generic_negative_cache - - @_abc_negative_cache.setter - def _abc_negative_cache(self, value): - if self.__origin__ is None: - if isinstance(self.__extra__, abc.ABCMeta): - self.__extra__._abc_negative_cache = value - else: - self._abc_generic_negative_cache = value - - @property - def _abc_negative_cache_version(self): - if isinstance(self.__extra__, abc.ABCMeta): - return self.__extra__._abc_negative_cache_version - return self._gorg._abc_generic_negative_cache_version - - @_abc_negative_cache_version.setter - def _abc_negative_cache_version(self, value): - if self.__origin__ is None: - if isinstance(self.__extra__, abc.ABCMeta): - self.__extra__._abc_negative_cache_version = value - else: - self._abc_generic_negative_cache_version = value - - def _get_type_vars(self, tvars): - if self.__origin__ and self.__parameters__: - _get_type_vars(self.__parameters__, tvars) - - def _eval_type(self, globalns, localns): - ev_origin = (self.__origin__._eval_type(globalns, localns) - if self.__origin__ else None) - ev_args = tuple(_eval_type(a, globalns, localns) for a - in self.__args__) if self.__args__ else None - if ev_origin == self.__origin__ and ev_args == self.__args__: - return self - return self.__class__(self.__name__, - self.__bases__, - dict(self.__dict__), - tvars=_type_vars(ev_args) if ev_args else None, - args=ev_args, - origin=ev_origin, - extra=self.__extra__, - orig_bases=self.__orig_bases__) - - def __repr__(self): - if self.__origin__ is None: - return super(GenericMeta, self).__repr__() - return self._tree_repr(self._subs_tree()) - - def _tree_repr(self, tree): - arg_list = [] - for arg in tree[1:]: - if arg == (): - arg_list.append('()') - elif not isinstance(arg, tuple): - arg_list.append(_type_repr(arg)) - else: - arg_list.append(arg[0]._tree_repr(arg)) - return super(GenericMeta, self).__repr__() + '[%s]' % ', '.join(arg_list) - - def _subs_tree(self, tvars=None, args=None): - if self.__origin__ is None: - return self - tree_args = _subs_tree(self, tvars, args) - return (self._gorg,) + tuple(tree_args) - - def __eq__(self, other): - if not isinstance(other, GenericMeta): - return NotImplemented - if self.__origin__ is None or other.__origin__ is None: - return self is other - return self.__tree_hash__ == other.__tree_hash__ - - def __hash__(self): - return self.__tree_hash__ - - @_tp_cache - def __getitem__(self, params): - if not isinstance(params, tuple): - params = (params,) - if not params and self._gorg is not Tuple: - raise TypeError( - "Parameter list to %s[...] cannot be empty" % _qualname(self)) - msg = "Parameters to generic types must be types." - params = tuple(_type_check(p, msg) for p in params) - if self in (Generic, Protocol): - # Generic can only be subscripted with unique type variables. - if not all(isinstance(p, TypeVar) for p in params): - raise TypeError( - "Parameters to %s[...] must all be type variables" % self.__name__) - if len(set(params)) != len(params): - raise TypeError( - "Parameters to %s[...] must all be unique" % self.__name__) - tvars = params - args = params - elif self in (Tuple, Callable): - tvars = _type_vars(params) - args = params - elif self.__origin__ in (Generic, Protocol): - # Can't subscript Generic[...] or Protocol[...]. - raise TypeError("Cannot subscript already-subscripted %s" % - repr(self)) - else: - # Subscripting a regular Generic subclass. - _check_generic(self, params) - tvars = _type_vars(params) - args = params - - prepend = (self,) if self.__origin__ is None else () - return self.__class__(self.__name__, - prepend + self.__bases__, - dict(self.__dict__), - tvars=tvars, - args=args, - origin=self, - extra=self.__extra__, - orig_bases=self.__orig_bases__) - - def __subclasscheck__(self, cls): - if self.__origin__ is not None: - # These should only be modules within the standard library. - # singledispatch is an exception, because it's a Python 2 backport - # of functools.singledispatch. - whitelist = ['abc', 'functools', 'singledispatch'] - if (sys._getframe(1).f_globals['__name__'] in whitelist or - # The second frame is needed for the case where we came - # from _ProtocolMeta.__subclasscheck__. - sys._getframe(2).f_globals['__name__'] in whitelist): - return False - raise TypeError("Parameterized generics cannot be used with class " - "or instance checks") - if self is Generic: - raise TypeError("Class %r cannot be used with class " - "or instance checks" % self) - return super(GenericMeta, self).__subclasscheck__(cls) - - def __instancecheck__(self, instance): - # Since we extend ABC.__subclasscheck__ and - # ABC.__instancecheck__ inlines the cache checking done by the - # latter, we must extend __instancecheck__ too. For simplicity - # we just skip the cache check -- instance checks for generic - # classes are supposed to be rare anyways. - if hasattr(instance, "__class__"): - return issubclass(instance.__class__, self) - return False - - def __setattr__(self, attr, value): - # We consider all the subscripted genrics as proxies for original class - if ( - attr.startswith('__') and attr.endswith('__') or - attr.startswith('_abc_') - ): - super(GenericMeta, self).__setattr__(attr, value) - else: - super(GenericMeta, self._gorg).__setattr__(attr, value) - - -def _copy_generic(self): - """Hack to work around https://bugs.python.org/issue11480 on Python 2""" - return self.__class__(self.__name__, self.__bases__, dict(self.__dict__), - self.__parameters__, self.__args__, self.__origin__, - self.__extra__, self.__orig_bases__) - - -copy._copy_dispatch[GenericMeta] = _copy_generic - - -# Prevent checks for Generic to crash when defining Generic. -Generic = None - - -def _generic_new(base_cls, cls, *args, **kwds): - # Assure type is erased on instantiation, - # but attempt to store it in __orig_class__ - if cls.__origin__ is None: - if (base_cls.__new__ is object.__new__ and - cls.__init__ is not object.__init__): - return base_cls.__new__(cls) - else: - return base_cls.__new__(cls, *args, **kwds) - else: - origin = cls._gorg - if (base_cls.__new__ is object.__new__ and - cls.__init__ is not object.__init__): - obj = base_cls.__new__(origin) - else: - obj = base_cls.__new__(origin, *args, **kwds) - try: - obj.__orig_class__ = cls - except AttributeError: - pass - obj.__init__(*args, **kwds) - return obj - - -class Generic(object): - """Abstract base class for generic types. - - A generic type is typically declared by inheriting from - this class parameterized with one or more type variables. - For example, a generic mapping type might be defined as:: - - class Mapping(Generic[KT, VT]): - def __getitem__(self, key: KT) -> VT: - ... - # Etc. - - This class can then be used as follows:: - - def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: - try: - return mapping[key] - except KeyError: - return default - """ - - __metaclass__ = GenericMeta - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Generic: - raise TypeError("Type Generic cannot be instantiated; " - "it can be used only as a base class") - return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) - - -class _TypingEmpty(object): - """Internal placeholder for () or []. Used by TupleMeta and CallableMeta - to allow empty list/tuple in specific places, without allowing them - to sneak in where prohibited. - """ - - -class _TypingEllipsis(object): - """Internal placeholder for ... (ellipsis).""" - - -class TupleMeta(GenericMeta): - """Metaclass for Tuple (internal).""" - - @_tp_cache - def __getitem__(self, parameters): - if self.__origin__ is not None or self._gorg is not Tuple: - # Normal generic rules apply if this is not the first subscription - # or a subscription of a subclass. - return super(TupleMeta, self).__getitem__(parameters) - if parameters == (): - return super(TupleMeta, self).__getitem__((_TypingEmpty,)) - if not isinstance(parameters, tuple): - parameters = (parameters,) - if len(parameters) == 2 and parameters[1] is Ellipsis: - msg = "Tuple[t, ...]: t must be a type." - p = _type_check(parameters[0], msg) - return super(TupleMeta, self).__getitem__((p, _TypingEllipsis)) - msg = "Tuple[t0, t1, ...]: each t must be a type." - parameters = tuple(_type_check(p, msg) for p in parameters) - return super(TupleMeta, self).__getitem__(parameters) - - def __instancecheck__(self, obj): - if self.__args__ is None: - return isinstance(obj, tuple) - raise TypeError("Parameterized Tuple cannot be used " - "with isinstance().") - - def __subclasscheck__(self, cls): - if self.__args__ is None: - return issubclass(cls, tuple) - raise TypeError("Parameterized Tuple cannot be used " - "with issubclass().") - - -copy._copy_dispatch[TupleMeta] = _copy_generic - - -class Tuple(tuple): - """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. - - Example: Tuple[T1, T2] is a tuple of two elements corresponding - to type variables T1 and T2. Tuple[int, float, str] is a tuple - of an int, a float and a string. - - To specify a variable-length tuple of homogeneous type, use Tuple[T, ...]. - """ - - __metaclass__ = TupleMeta - __extra__ = tuple - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Tuple: - raise TypeError("Type Tuple cannot be instantiated; " - "use tuple() instead") - return _generic_new(tuple, cls, *args, **kwds) - - -class CallableMeta(GenericMeta): - """ Metaclass for Callable.""" - - def __repr__(self): - if self.__origin__ is None: - return super(CallableMeta, self).__repr__() - return self._tree_repr(self._subs_tree()) - - def _tree_repr(self, tree): - if self._gorg is not Callable: - return super(CallableMeta, self)._tree_repr(tree) - # For actual Callable (not its subclass) we override - # super(CallableMeta, self)._tree_repr() for nice formatting. - arg_list = [] - for arg in tree[1:]: - if not isinstance(arg, tuple): - arg_list.append(_type_repr(arg)) - else: - arg_list.append(arg[0]._tree_repr(arg)) - if arg_list[0] == '...': - return repr(tree[0]) + '[..., %s]' % arg_list[1] - return (repr(tree[0]) + - '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1])) - - def __getitem__(self, parameters): - """A thin wrapper around __getitem_inner__ to provide the latter - with hashable arguments to improve speed. - """ - - if self.__origin__ is not None or self._gorg is not Callable: - return super(CallableMeta, self).__getitem__(parameters) - if not isinstance(parameters, tuple) or len(parameters) != 2: - raise TypeError("Callable must be used as " - "Callable[[arg, ...], result].") - args, result = parameters - if args is Ellipsis: - parameters = (Ellipsis, result) - else: - if not isinstance(args, list): - raise TypeError("Callable[args, result]: args must be a list." - " Got %.100r." % (args,)) - parameters = (tuple(args), result) - return self.__getitem_inner__(parameters) - - @_tp_cache - def __getitem_inner__(self, parameters): - args, result = parameters - msg = "Callable[args, result]: result must be a type." - result = _type_check(result, msg) - if args is Ellipsis: - return super(CallableMeta, self).__getitem__((_TypingEllipsis, result)) - msg = "Callable[[arg, ...], result]: each arg must be a type." - args = tuple(_type_check(arg, msg) for arg in args) - parameters = args + (result,) - return super(CallableMeta, self).__getitem__(parameters) - - -copy._copy_dispatch[CallableMeta] = _copy_generic - - -class Callable(object): - """Callable type; Callable[[int], str] is a function of (int) -> str. - - The subscription syntax must always be used with exactly two - values: the argument list and the return type. The argument list - must be a list of types or ellipsis; the return type must be a single type. - - There is no syntax to indicate optional or keyword arguments, - such function types are rarely used as callback types. - """ - - __metaclass__ = CallableMeta - __extra__ = collections_abc.Callable - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Callable: - raise TypeError("Type Callable cannot be instantiated; " - "use a non-abstract subclass instead") - return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) - - -def cast(typ, val): - """Cast a value to a type. - - This returns the value unchanged. To the type checker this - signals that the return value has the designated type, but at - runtime we intentionally don't check anything (we want this - to be as fast as possible). - """ - return val - - -def _get_defaults(func): - """Internal helper to extract the default arguments, by name.""" - code = func.__code__ - pos_count = code.co_argcount - arg_names = code.co_varnames - arg_names = arg_names[:pos_count] - defaults = func.__defaults__ or () - kwdefaults = func.__kwdefaults__ - res = dict(kwdefaults) if kwdefaults else {} - pos_offset = pos_count - len(defaults) - for name, value in zip(arg_names[pos_offset:], defaults): - assert name not in res - res[name] = value - return res - - -def get_type_hints(obj, globalns=None, localns=None): - """In Python 2 this is not supported and always returns None.""" - return None - - -def no_type_check(arg): - """Decorator to indicate that annotations are not type hints. - - The argument must be a class or function; if it is a class, it - applies recursively to all methods and classes defined in that class - (but not to methods defined in its superclasses or subclasses). - - This mutates the function(s) or class(es) in place. - """ - if isinstance(arg, type): - arg_attrs = arg.__dict__.copy() - for attr, val in arg.__dict__.items(): - if val in arg.__bases__ + (arg,): - arg_attrs.pop(attr) - for obj in arg_attrs.values(): - if isinstance(obj, types.FunctionType): - obj.__no_type_check__ = True - if isinstance(obj, type): - no_type_check(obj) - try: - arg.__no_type_check__ = True - except TypeError: # built-in classes - pass - return arg - - -def no_type_check_decorator(decorator): - """Decorator to give another decorator the @no_type_check effect. - - This wraps the decorator with something that wraps the decorated - function in @no_type_check. - """ - - @functools.wraps(decorator) - def wrapped_decorator(*args, **kwds): - func = decorator(*args, **kwds) - func = no_type_check(func) - return func - - return wrapped_decorator - - -def _overload_dummy(*args, **kwds): - """Helper for @overload to raise when called.""" - raise NotImplementedError( - "You should not call an overloaded function. " - "A series of @overload-decorated functions " - "outside a stub module should always be followed " - "by an implementation that is not @overload-ed.") - - -def overload(func): - """Decorator for overloaded functions/methods. - - In a stub file, place two or more stub definitions for the same - function in a row, each decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - - In a non-stub file (i.e. a regular .py file), do the same but - follow it with an implementation. The implementation should *not* - be decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - def utf8(value): - # implementation goes here - """ - return _overload_dummy - - -_PROTO_WHITELIST = ['Callable', 'Iterable', 'Iterator', - 'Hashable', 'Sized', 'Container', 'Collection', - 'Reversible', 'ContextManager'] - - -class _ProtocolMeta(GenericMeta): - """Internal metaclass for Protocol. - - This exists so Protocol classes can be generic without deriving - from Generic. - """ - def __init__(cls, *args, **kwargs): - super(_ProtocolMeta, cls).__init__(*args, **kwargs) - if not cls.__dict__.get('_is_protocol', None): - cls._is_protocol = any(b is Protocol or - isinstance(b, _ProtocolMeta) and - b.__origin__ is Protocol - for b in cls.__bases__) - if cls._is_protocol: - for base in cls.__mro__[1:]: - if not (base in (object, Generic) or - base.__module__ == '_abcoll' and - base.__name__ in _PROTO_WHITELIST or - isinstance(base, TypingMeta) and base._is_protocol or - isinstance(base, GenericMeta) and base.__origin__ is Generic): - raise TypeError('Protocols can only inherit from other protocols,' - ' got %r' % base) - cls._callable_members_only = all(callable(getattr(cls, attr)) - for attr in cls._get_protocol_attrs()) - - def _no_init(self, *args, **kwargs): - if type(self)._is_protocol: - raise TypeError('Protocols cannot be instantiated') - cls.__init__ = _no_init - - def _proto_hook(cls, other): - if not cls.__dict__.get('_is_protocol', None): - return NotImplemented - if not isinstance(other, type): - # Similar error as for issubclass(1, int) - # (also not a chance for old-style classes) - raise TypeError('issubclass() arg 1 must be a new-style class') - for attr in cls._get_protocol_attrs(): - for base in other.__mro__: - if attr in base.__dict__: - if base.__dict__[attr] is None: - return NotImplemented - break - else: - return NotImplemented - return True - if '__subclasshook__' not in cls.__dict__: - cls.__subclasshook__ = classmethod(_proto_hook) - - def __instancecheck__(self, instance): - # We need this method for situations where attributes are assigned in __init__ - if isinstance(instance, type): - # This looks like a fundamental limitation of Python 2. - # It cannot support runtime protocol metaclasses, On Python 2 classes - # cannot be correctly inspected as instances of protocols. - return False - if ((not getattr(self, '_is_protocol', False) or - self._callable_members_only) and - issubclass(instance.__class__, self)): - return True - if self._is_protocol: - if all(hasattr(instance, attr) and - (not callable(getattr(self, attr)) or - getattr(instance, attr) is not None) - for attr in self._get_protocol_attrs()): - return True - return super(GenericMeta, self).__instancecheck__(instance) - - def __subclasscheck__(self, cls): - if (self.__dict__.get('_is_protocol', None) and - not self.__dict__.get('_is_runtime_protocol', None)): - if (sys._getframe(1).f_globals['__name__'] in ['abc', 'functools'] or - # This is needed because we remove subclasses from unions on Python 2. - sys._getframe(2).f_globals['__name__'] == 'typing'): - return False - raise TypeError("Instance and class checks can only be used with" - " @runtime_checkable protocols") - if (self.__dict__.get('_is_runtime_protocol', None) and - not self._callable_members_only): - if sys._getframe(1).f_globals['__name__'] in ['abc', 'functools']: - return super(GenericMeta, self).__subclasscheck__(cls) - raise TypeError("Protocols with non-method members" - " don't support issubclass()") - return super(_ProtocolMeta, self).__subclasscheck__(cls) - - def _get_protocol_attrs(self): - attrs = set() - for base in self.__mro__[:-1]: # without object - if base.__name__ in ('Protocol', 'Generic'): - continue - annotations = getattr(base, '__annotations__', {}) - for attr in list(base.__dict__.keys()) + list(annotations.keys()): - if (not attr.startswith('_abc_') and attr not in ( - '__abstractmethods__', '__annotations__', '__weakref__', - '_is_protocol', '_is_runtime_protocol', '__dict__', - '__args__', '__slots__', '_get_protocol_attrs', - '__next_in_mro__', '__parameters__', '__origin__', - '__orig_bases__', '__extra__', '__tree_hash__', - '__doc__', '__subclasshook__', '__init__', '__new__', - '__module__', '_MutableMapping__marker', - '__metaclass__', '_gorg', '_callable_members_only')): - attrs.add(attr) - return attrs - - -class Protocol(object): - """Base class for protocol classes. Protocol classes are defined as:: - - class Proto(Protocol): - def meth(self): - # type: () -> int - pass - - Such classes are primarily used with static type checkers that recognize - structural subtyping (static duck-typing), for example:: - - class C: - def meth(self): - # type: () -> int - return 0 - - def func(x): - # type: (Proto) -> int - return x.meth() - - func(C()) # Passes static type check - - See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable - act as simple-minded runtime protocols that checks only the presence of - given attributes, ignoring their type signatures. - - Protocol classes can be generic, they are defined as:: - - class GenProto(Protocol[T]): - def meth(self): - # type: () -> T - pass - """ - - __metaclass__ = _ProtocolMeta - __slots__ = () - _is_protocol = True - - def __new__(cls, *args, **kwds): - if cls._gorg is Protocol: - raise TypeError("Type Protocol cannot be instantiated; " - "it can be used only as a base class") - return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) - - -def runtime_checkable(cls): - """Mark a protocol class as a runtime protocol, so that it - can be used with isinstance() and issubclass(). Raise TypeError - if applied to a non-protocol class. - - This allows a simple-minded structural check very similar to the - one-offs in collections.abc such as Hashable. - """ - if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: - raise TypeError('@runtime_checkable can be only applied to protocol classes,' - ' got %r' % cls) - cls._is_runtime_protocol = True - return cls - - -# Various ABCs mimicking those in collections.abc. -# A few are simply re-exported for completeness. - -Hashable = collections_abc.Hashable # Not generic. - - -class Iterable(Generic[T_co]): - __slots__ = () - __extra__ = collections_abc.Iterable - - -class Iterator(Iterable[T_co]): - __slots__ = () - __extra__ = collections_abc.Iterator - - -@runtime_checkable -class SupportsInt(Protocol): - __slots__ = () - - @abstractmethod - def __int__(self): - pass - - -@runtime_checkable -class SupportsFloat(Protocol): - __slots__ = () - - @abstractmethod - def __float__(self): - pass - - -@runtime_checkable -class SupportsComplex(Protocol): - __slots__ = () - - @abstractmethod - def __complex__(self): - pass - - -@runtime_checkable -class SupportsIndex(Protocol): - __slots__ = () - - @abstractmethod - def __index__(self): - pass - - -@runtime_checkable -class SupportsAbs(Protocol[T_co]): - __slots__ = () - - @abstractmethod - def __abs__(self): - pass - - -if hasattr(collections_abc, 'Reversible'): - class Reversible(Iterable[T_co]): - __slots__ = () - __extra__ = collections_abc.Reversible -else: - @runtime_checkable - class Reversible(Protocol[T_co]): - __slots__ = () - - @abstractmethod - def __reversed__(self): - pass - - -Sized = collections_abc.Sized # Not generic. - - -class Container(Generic[T_co]): - __slots__ = () - __extra__ = collections_abc.Container - - -# Callable was defined earlier. - - -class AbstractSet(Sized, Iterable[T_co], Container[T_co]): - __slots__ = () - __extra__ = collections_abc.Set - - -class MutableSet(AbstractSet[T]): - __slots__ = () - __extra__ = collections_abc.MutableSet - - -# NOTE: It is only covariant in the value type. -class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co]): - __slots__ = () - __extra__ = collections_abc.Mapping - - -class MutableMapping(Mapping[KT, VT]): - __slots__ = () - __extra__ = collections_abc.MutableMapping - - -if hasattr(collections_abc, 'Reversible'): - class Sequence(Sized, Reversible[T_co], Container[T_co]): - __slots__ = () - __extra__ = collections_abc.Sequence -else: - class Sequence(Sized, Iterable[T_co], Container[T_co]): - __slots__ = () - __extra__ = collections_abc.Sequence - - -class MutableSequence(Sequence[T]): - __slots__ = () - __extra__ = collections_abc.MutableSequence - - -class ByteString(Sequence[int]): - pass - - -ByteString.register(str) -ByteString.register(bytearray) - - -class List(list, MutableSequence[T]): - __slots__ = () - __extra__ = list - - def __new__(cls, *args, **kwds): - if cls._gorg is List: - raise TypeError("Type List cannot be instantiated; " - "use list() instead") - return _generic_new(list, cls, *args, **kwds) - - -class Deque(collections.deque, MutableSequence[T]): - __slots__ = () - __extra__ = collections.deque - - def __new__(cls, *args, **kwds): - if cls._gorg is Deque: - return collections.deque(*args, **kwds) - return _generic_new(collections.deque, cls, *args, **kwds) - - -class Set(set, MutableSet[T]): - __slots__ = () - __extra__ = set - - def __new__(cls, *args, **kwds): - if cls._gorg is Set: - raise TypeError("Type Set cannot be instantiated; " - "use set() instead") - return _generic_new(set, cls, *args, **kwds) - - -class FrozenSet(frozenset, AbstractSet[T_co]): - __slots__ = () - __extra__ = frozenset - - def __new__(cls, *args, **kwds): - if cls._gorg is FrozenSet: - raise TypeError("Type FrozenSet cannot be instantiated; " - "use frozenset() instead") - return _generic_new(frozenset, cls, *args, **kwds) - - -class MappingView(Sized, Iterable[T_co]): - __slots__ = () - __extra__ = collections_abc.MappingView - - -class KeysView(MappingView[KT], AbstractSet[KT]): - __slots__ = () - __extra__ = collections_abc.KeysView - - -class ItemsView(MappingView[Tuple[KT, VT_co]], - AbstractSet[Tuple[KT, VT_co]], - Generic[KT, VT_co]): - __slots__ = () - __extra__ = collections_abc.ItemsView - - -class ValuesView(MappingView[VT_co]): - __slots__ = () - __extra__ = collections_abc.ValuesView - - -class ContextManager(Generic[T_co]): - __slots__ = () - - def __enter__(self): - return self - - @abc.abstractmethod - def __exit__(self, exc_type, exc_value, traceback): - return None - - @classmethod - def __subclasshook__(cls, C): - if cls is ContextManager: - # In Python 3.6+, it is possible to set a method to None to - # explicitly indicate that the class does not implement an ABC - # (https://bugs.python.org/issue25958), but we do not support - # that pattern here because this fallback class is only used - # in Python 3.5 and earlier. - if (any("__enter__" in B.__dict__ for B in C.__mro__) and - any("__exit__" in B.__dict__ for B in C.__mro__)): - return True - return NotImplemented - - -class Dict(dict, MutableMapping[KT, VT]): - __slots__ = () - __extra__ = dict - - def __new__(cls, *args, **kwds): - if cls._gorg is Dict: - raise TypeError("Type Dict cannot be instantiated; " - "use dict() instead") - return _generic_new(dict, cls, *args, **kwds) - - -class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): - __slots__ = () - __extra__ = collections.defaultdict - - def __new__(cls, *args, **kwds): - if cls._gorg is DefaultDict: - return collections.defaultdict(*args, **kwds) - return _generic_new(collections.defaultdict, cls, *args, **kwds) - - -class Counter(collections.Counter, Dict[T, int]): - __slots__ = () - __extra__ = collections.Counter - - def __new__(cls, *args, **kwds): - if cls._gorg is Counter: - return collections.Counter(*args, **kwds) - return _generic_new(collections.Counter, cls, *args, **kwds) - - -# Determine what base class to use for Generator. -if hasattr(collections_abc, 'Generator'): - # Sufficiently recent versions of 3.5 have a Generator ABC. - _G_base = collections_abc.Generator -else: - # Fall back on the exact type. - _G_base = types.GeneratorType - - -class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]): - __slots__ = () - __extra__ = _G_base - - def __new__(cls, *args, **kwds): - if cls._gorg is Generator: - raise TypeError("Type Generator cannot be instantiated; " - "create a subclass instead") - return _generic_new(_G_base, cls, *args, **kwds) - - -# Internal type variable used for Type[]. -CT_co = TypeVar('CT_co', covariant=True, bound=type) - - -# This is not a real generic class. Don't use outside annotations. -class Type(Generic[CT_co]): - """A special construct usable to annotate class objects. - - For example, suppose we have the following classes:: - - class User: ... # Abstract base for User classes - class BasicUser(User): ... - class ProUser(User): ... - class TeamUser(User): ... - - And a function that takes a class argument that's a subclass of - User and returns an instance of the corresponding class:: - - U = TypeVar('U', bound=User) - def new_user(user_class: Type[U]) -> U: - user = user_class() - # (Here we could write the user object to a database) - return user - - joe = new_user(BasicUser) - - At this point the type checker knows that joe has type BasicUser. - """ - __slots__ = () - __extra__ = type - - -def NamedTuple(typename, fields): - """Typed version of namedtuple. - - Usage:: - - Employee = typing.NamedTuple('Employee', [('name', str), ('id', int)]) - - This is equivalent to:: - - Employee = collections.namedtuple('Employee', ['name', 'id']) - - The resulting class has one extra attribute: _field_types, - giving a dict mapping field names to types. (The field names - are in the _fields attribute, which is part of the namedtuple - API.) - """ - fields = [(n, t) for n, t in fields] - cls = collections.namedtuple(typename, [n for n, t in fields]) - cls._field_types = dict(fields) - # Set the module to the caller's module (otherwise it'd be 'typing'). - try: - cls.__module__ = sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - return cls - - -def _check_fails(cls, other): - try: - if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools', 'typing']: - # Typed dicts are only for static structural subtyping. - raise TypeError('TypedDict does not support instance and class checks') - except (AttributeError, ValueError): - pass - return False - - -def _dict_new(cls, *args, **kwargs): - return dict(*args, **kwargs) - - -def _typeddict_new(cls, _typename, _fields=None, **kwargs): - total = kwargs.pop('total', True) - if _fields is None: - _fields = kwargs - elif kwargs: - raise TypeError("TypedDict takes either a dict or keyword arguments," - " but not both") - - ns = {'__annotations__': dict(_fields), '__total__': total} - try: - # Setting correct module is necessary to make typed dict classes pickleable. - ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - - return _TypedDictMeta(_typename, (), ns) - - -class _TypedDictMeta(type): - def __new__(cls, name, bases, ns, total=True): - # Create new typed dict class object. - # This method is called directly when TypedDict is subclassed, - # or via _typeddict_new when TypedDict is instantiated. This way - # TypedDict supports all three syntaxes described in its docstring. - # Subclasses and instances of TypedDict return actual dictionaries - # via _dict_new. - ns['__new__'] = _typeddict_new if name == b'TypedDict' else _dict_new - tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns) - - anns = ns.get('__annotations__', {}) - msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" - anns = {n: _type_check(tp, msg) for n, tp in anns.items()} - for base in bases: - anns.update(base.__dict__.get('__annotations__', {})) - tp_dict.__annotations__ = anns - if not hasattr(tp_dict, '__total__'): - tp_dict.__total__ = total - return tp_dict - - __instancecheck__ = __subclasscheck__ = _check_fails - - -TypedDict = _TypedDictMeta(b'TypedDict', (dict,), {}) -TypedDict.__module__ = __name__ -TypedDict.__doc__ = \ - """A simple typed name space. At runtime it is equivalent to a plain dict. - - TypedDict creates a dictionary type that expects all of its - instances to have a certain set of keys, with each key - associated with a value of a consistent type. This expectation - is not checked at runtime but is only enforced by type checkers. - Usage:: - - Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) - - a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK - b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check - - assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') - - The type info could be accessed via Point2D.__annotations__. TypedDict - supports an additional equivalent form:: - - Point2D = TypedDict('Point2D', x=int, y=int, label=str) - """ - - -def NewType(name, tp): - """NewType creates simple unique types with almost zero - runtime overhead. NewType(name, tp) is considered a subtype of tp - by static type checkers. At runtime, NewType(name, tp) returns - a dummy function that simply returns its argument. Usage:: - - UserId = NewType('UserId', int) - - def name_by_id(user_id): - # type: (UserId) -> str - ... - - UserId('user') # Fails type check - - name_by_id(42) # Fails type check - name_by_id(UserId(42)) # OK - - num = UserId(5) + 1 # type: int - """ - - def new_type(x): - return x - - # Some versions of Python 2 complain because of making all strings unicode - new_type.__name__ = str(name) - new_type.__supertype__ = tp - return new_type - - -# Python-version-specific alias (Python 2: unicode; Python 3: str) -Text = unicode - - -# Constant that's True when type checking, but False here. -TYPE_CHECKING = False - - -class IO(Generic[AnyStr]): - """Generic base class for TextIO and BinaryIO. - - This is an abstract, generic version of the return of open(). - - NOTE: This does not distinguish between the different possible - classes (text vs. binary, read vs. write vs. read/write, - append-only, unbuffered). The TextIO and BinaryIO subclasses - below capture the distinctions between text vs. binary, which is - pervasive in the interface; however we currently do not offer a - way to track the other distinctions in the type system. - """ - - __slots__ = () - - @abstractproperty - def mode(self): - pass - - @abstractproperty - def name(self): - pass - - @abstractmethod - def close(self): - pass - - @abstractproperty - def closed(self): - pass - - @abstractmethod - def fileno(self): - pass - - @abstractmethod - def flush(self): - pass - - @abstractmethod - def isatty(self): - pass - - @abstractmethod - def read(self, n=-1): - pass - - @abstractmethod - def readable(self): - pass - - @abstractmethod - def readline(self, limit=-1): - pass - - @abstractmethod - def readlines(self, hint=-1): - pass - - @abstractmethod - def seek(self, offset, whence=0): - pass - - @abstractmethod - def seekable(self): - pass - - @abstractmethod - def tell(self): - pass - - @abstractmethod - def truncate(self, size=None): - pass - - @abstractmethod - def writable(self): - pass - - @abstractmethod - def write(self, s): - pass - - @abstractmethod - def writelines(self, lines): - pass - - @abstractmethod - def __enter__(self): - pass - - @abstractmethod - def __exit__(self, type, value, traceback): - pass - - -class BinaryIO(IO[bytes]): - """Typed version of the return of open() in binary mode.""" - - __slots__ = () - - @abstractmethod - def write(self, s): - pass - - @abstractmethod - def __enter__(self): - pass - - -class TextIO(IO[unicode]): - """Typed version of the return of open() in text mode.""" - - __slots__ = () - - @abstractproperty - def buffer(self): - pass - - @abstractproperty - def encoding(self): - pass - - @abstractproperty - def errors(self): - pass - - @abstractproperty - def line_buffering(self): - pass - - @abstractproperty - def newlines(self): - pass - - @abstractmethod - def __enter__(self): - pass - - -class io(object): - """Wrapper namespace for IO generic classes.""" - - __all__ = ['IO', 'TextIO', 'BinaryIO'] - IO = IO - TextIO = TextIO - BinaryIO = BinaryIO - - -io.__name__ = __name__ + b'.io' -sys.modules[io.__name__] = io - - -Pattern = _TypeAlias('Pattern', AnyStr, type(stdlib_re.compile('')), - lambda p: p.pattern) -Match = _TypeAlias('Match', AnyStr, type(stdlib_re.match('', '')), - lambda m: m.re.pattern) - - -class re(object): - """Wrapper namespace for re type aliases.""" - - __all__ = ['Pattern', 'Match'] - Pattern = Pattern - Match = Match - - -re.__name__ = __name__ + b'.re' -sys.modules[re.__name__] = re diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 637292647..000000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -license-file = LICENSE diff --git a/setup.py b/setup.py deleted file mode 100644 index 39bb41930..000000000 --- a/setup.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -import sys -from setuptools import setup - -if sys.version_info < (2, 7, 0) or (3, 0, 0) <= sys.version_info < (3, 4, 0): - sys.stderr.write('ERROR: You need Python 2.7 or 3.4+ ' - 'to install the typing package.\n') - exit(1) - -version = '3.7.4.3' -description = 'Type Hints for Python' -long_description = '''\ -Typing -- Type Hints for Python - -This is a backport of the standard library typing module to Python -versions older than 3.5. (See note below for newer versions.) - -Typing defines a standard notation for Python function and variable -type annotations. The notation can be used for documenting code in a -concise, standard format, and it has been designed to also be used by -static and runtime type checkers, static analyzers, IDEs and other -tools. - -NOTE: in Python 3.5 and later, the typing module lives in the stdlib, -and installing this package has NO EFFECT, because stdlib takes higher -precedence than the installation directory. To get a newer version of -the typing module in Python 3.5 or later, you have to upgrade to a -newer Python (bugfix) version. For example, typing in Python 3.6.0 is -missing the definition of 'Type' -- upgrading to 3.6.2 will fix this. - -Also note that most improvements to the typing module in Python 3.7 -will not be included in this package, since Python 3.7 has some -built-in support that is not present in older versions (See PEP 560.) - -For package maintainers, it is preferred to use -``typing;python_version<"3.5"`` if your package requires it to support -earlier Python versions. This will avoid shadowing the stdlib typing -module when your package is installed via ``pip install -t .`` on -Python 3.5 or later. -''' - -package_dir = {2: 'python2', 3: 'src'}[sys.version_info.major] - -classifiers = [ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Python Software Foundation License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Topic :: Software Development', -] - -setup(name='typing', - version=version, - description=description, - long_description=long_description, - author='Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Ivan Levkivskyi', - author_email='jukka.lehtosalo@iki.fi', - url='https://docs.python.org/3/library/typing.html', - project_urls={'Source': 'https://github.com/python/typing'}, - license='PSF', - keywords='typing function annotations type hints hinting checking ' - 'checker typehints typehinting typechecking backport', - package_dir={'': package_dir}, - py_modules=['typing'], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', - classifiers=classifiers) diff --git a/src/mod_generics_cache.py b/src/mod_generics_cache.py deleted file mode 100644 index 51cef0f03..000000000 --- a/src/mod_generics_cache.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Module for testing the behavior of generics across different modules.""" - -import sys -from textwrap import dedent -from typing import TypeVar, Generic, Optional - - -if sys.version_info[:2] >= (3, 6): - exec(dedent(""" - default_a: Optional['A'] = None - default_b: Optional['B'] = None - - T = TypeVar('T') - - class A(Generic[T]): - some_b: 'B' - - class B(Generic[T]): - class A(Generic[T]): - pass - - my_inner_a1: 'B.A' - my_inner_a2: A - my_outer_a: 'A' # unless somebody calls get_type_hints with localns=B.__dict__ - """)) -else: # This should stay in sync with the syntax above. - __annotations__ = dict( - default_a=Optional['A'], - default_b=Optional['B'], - ) - default_a = None - default_b = None - - T = TypeVar('T') - - class A(Generic[T]): - __annotations__ = dict( - some_b='B' - ) - - class B(Generic[T]): - class A(Generic[T]): - pass - - __annotations__ = dict( - my_inner_a1='B.A', - my_inner_a2=A, - my_outer_a='A' # unless somebody calls get_type_hints with localns=B.__dict__ - ) diff --git a/src/test_typing.py b/src/test_typing.py deleted file mode 100644 index bddad7bdb..000000000 --- a/src/test_typing.py +++ /dev/null @@ -1,2815 +0,0 @@ -import contextlib -import collections -import os -import pickle -import re -import subprocess -import sys -from unittest import TestCase, main, skipUnless, SkipTest, expectedFailure -from copy import copy, deepcopy - -from typing import Any, NoReturn -from typing import TypeVar, AnyStr -from typing import T, KT, VT # Not in __all__. -from typing import Union, Optional -from typing import Tuple, List, MutableMapping, Iterator -from typing import Callable -from typing import Generic, ClassVar, GenericMeta -from typing import cast -from typing import get_type_hints -from typing import no_type_check, no_type_check_decorator -from typing import Type -from typing import NewType -from typing import NamedTuple -from typing import IO, TextIO, BinaryIO -from typing import Pattern, Match -import abc -import typing -import weakref -try: - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc # Fallback for PY3.2. - - -try: - import mod_generics_cache -except ImportError: - # try to use the builtin one, Python 3.5+ - from test import mod_generics_cache - - -PY36 = sys.version_info[:2] >= (3, 6) - - -class BaseTestCase(TestCase): - - def assertIsSubclass(self, cls, class_or_tuple, msg=None): - if not issubclass(cls, class_or_tuple): - message = '%r is not a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): - if issubclass(cls, class_or_tuple): - message = '%r is a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - def clear_caches(self): - for f in typing._cleanups: - f() - - -class Employee: - pass - - -class Manager(Employee): - pass - - -class Founder(Employee): - pass - - -class ManagingFounder(Manager, Founder): - pass - - -class AnyTests(BaseTestCase): - - def test_any_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(42, Any) - - def test_any_subclass_type_error(self): - with self.assertRaises(TypeError): - issubclass(Employee, Any) - with self.assertRaises(TypeError): - issubclass(Any, Employee) - - def test_repr(self): - self.assertEqual(repr(Any), 'typing.Any') - - def test_errors(self): - with self.assertRaises(TypeError): - issubclass(42, Any) - with self.assertRaises(TypeError): - Any[int] # Any is not a generic type. - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class A(Any): - pass - with self.assertRaises(TypeError): - class A(type(Any)): - pass - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - Any() - with self.assertRaises(TypeError): - type(Any)() - - def test_any_works_with_alias(self): - # These expressions must simply not fail. - typing.Match[Any] - typing.Pattern[Any] - typing.IO[Any] - - -class NoReturnTests(BaseTestCase): - - def test_noreturn_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(42, NoReturn) - - def test_noreturn_subclass_type_error(self): - with self.assertRaises(TypeError): - issubclass(Employee, NoReturn) - with self.assertRaises(TypeError): - issubclass(NoReturn, Employee) - - def test_repr(self): - self.assertEqual(repr(NoReturn), 'typing.NoReturn') - - def test_not_generic(self): - with self.assertRaises(TypeError): - NoReturn[int] - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class A(NoReturn): - pass - with self.assertRaises(TypeError): - class A(type(NoReturn)): - pass - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - NoReturn() - with self.assertRaises(TypeError): - type(NoReturn)() - - -class TypeVarTests(BaseTestCase): - - def test_basic_plain(self): - T = TypeVar('T') - # T equals itself. - self.assertEqual(T, T) - # T is an instance of TypeVar - self.assertIsInstance(T, TypeVar) - - def test_typevar_instance_type_error(self): - T = TypeVar('T') - with self.assertRaises(TypeError): - isinstance(42, T) - - def test_typevar_subclass_type_error(self): - T = TypeVar('T') - with self.assertRaises(TypeError): - issubclass(int, T) - with self.assertRaises(TypeError): - issubclass(T, int) - - def test_constrained_error(self): - with self.assertRaises(TypeError): - X = TypeVar('X', int) - X - - def test_union_unique(self): - X = TypeVar('X') - Y = TypeVar('Y') - self.assertNotEqual(X, Y) - self.assertEqual(Union[X], X) - self.assertNotEqual(Union[X], Union[X, Y]) - self.assertEqual(Union[X, X], X) - self.assertNotEqual(Union[X, int], Union[X]) - self.assertNotEqual(Union[X, int], Union[int]) - self.assertEqual(Union[X, int].__args__, (X, int)) - self.assertEqual(Union[X, int].__parameters__, (X,)) - self.assertIs(Union[X, int].__origin__, Union) - - def test_union_constrained(self): - A = TypeVar('A', str, bytes) - self.assertNotEqual(Union[A, str], Union[A]) - - def test_repr(self): - self.assertEqual(repr(T), '~T') - self.assertEqual(repr(KT), '~KT') - self.assertEqual(repr(VT), '~VT') - self.assertEqual(repr(AnyStr), '~AnyStr') - T_co = TypeVar('T_co', covariant=True) - self.assertEqual(repr(T_co), '+T_co') - T_contra = TypeVar('T_contra', contravariant=True) - self.assertEqual(repr(T_contra), '-T_contra') - - def test_no_redefinition(self): - self.assertNotEqual(TypeVar('T'), TypeVar('T')) - self.assertNotEqual(TypeVar('T', int, str), TypeVar('T', int, str)) - - def test_cannot_subclass_vars(self): - with self.assertRaises(TypeError): - class V(TypeVar('T')): - pass - - def test_cannot_subclass_var_itself(self): - with self.assertRaises(TypeError): - class V(TypeVar): - pass - - def test_cannot_instantiate_vars(self): - with self.assertRaises(TypeError): - TypeVar('A')() - - def test_bound_errors(self): - with self.assertRaises(TypeError): - TypeVar('X', bound=42) - with self.assertRaises(TypeError): - TypeVar('X', str, float, bound=Employee) - - def test_no_bivariant(self): - with self.assertRaises(ValueError): - TypeVar('T', covariant=True, contravariant=True) - - -class UnionTests(BaseTestCase): - - def test_basics(self): - u = Union[int, float] - self.assertNotEqual(u, Union) - - def test_subclass_error(self): - with self.assertRaises(TypeError): - issubclass(int, Union) - with self.assertRaises(TypeError): - issubclass(Union, int) - with self.assertRaises(TypeError): - issubclass(int, Union[int, str]) - with self.assertRaises(TypeError): - issubclass(Union[int, str], int) - - def test_union_any(self): - u = Union[Any] - self.assertEqual(u, Any) - u1 = Union[int, Any] - u2 = Union[Any, int] - u3 = Union[Any, object] - self.assertEqual(u1, u2) - self.assertNotEqual(u1, Any) - self.assertNotEqual(u2, Any) - self.assertNotEqual(u3, Any) - - def test_union_object(self): - u = Union[object] - self.assertEqual(u, object) - u = Union[int, object] - self.assertEqual(u, object) - u = Union[object, int] - self.assertEqual(u, object) - - def test_unordered(self): - u1 = Union[int, float] - u2 = Union[float, int] - self.assertEqual(u1, u2) - - def test_single_class_disappears(self): - t = Union[Employee] - self.assertIs(t, Employee) - - def test_base_class_disappears(self): - u = Union[Employee, Manager, int] - self.assertEqual(u, Union[int, Employee]) - u = Union[Manager, int, Employee] - self.assertEqual(u, Union[int, Employee]) - u = Union[Employee, Manager] - self.assertIs(u, Employee) - - def test_union_union(self): - u = Union[int, float] - v = Union[u, Employee] - self.assertEqual(v, Union[int, float, Employee]) - - def test_repr(self): - self.assertEqual(repr(Union), 'typing.Union') - u = Union[Employee, int] - self.assertEqual(repr(u), 'typing.Union[%s.Employee, int]' % __name__) - u = Union[int, Employee] - self.assertEqual(repr(u), 'typing.Union[int, %s.Employee]' % __name__) - T = TypeVar('T') - u = Union[T, int][int] - self.assertEqual(repr(u), repr(int)) - u = Union[List[int], int] - self.assertEqual(repr(u), 'typing.Union[typing.List[int], int]') - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(Union): - pass - with self.assertRaises(TypeError): - class C(type(Union)): - pass - with self.assertRaises(TypeError): - class C(Union[int, str]): - pass - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - Union() - with self.assertRaises(TypeError): - type(Union)() - u = Union[int, float] - with self.assertRaises(TypeError): - u() - with self.assertRaises(TypeError): - type(u)() - - def test_union_generalization(self): - self.assertFalse(Union[str, typing.Iterable[int]] == str) - self.assertFalse(Union[str, typing.Iterable[int]] == typing.Iterable[int]) - self.assertTrue(Union[str, typing.Iterable] == typing.Iterable) - - def test_union_compare_other(self): - self.assertNotEqual(Union, object) - self.assertNotEqual(Union, Any) - self.assertNotEqual(ClassVar, Union) - self.assertNotEqual(Optional, Union) - self.assertNotEqual([None], Optional) - self.assertNotEqual(Optional, typing.Mapping) - self.assertNotEqual(Optional[typing.MutableMapping], Union) - - def test_optional(self): - o = Optional[int] - u = Union[int, None] - self.assertEqual(o, u) - - def test_empty(self): - with self.assertRaises(TypeError): - Union[()] - - def test_union_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(42, Union[int, str]) - - def test_no_eval_union(self): - u = Union[int, str] - def f(x: u): ... - self.assertIs(get_type_hints(f)['x'], u) - - def test_function_repr_union(self): - def fun() -> int: ... - self.assertEqual(repr(Union[fun, int]), 'typing.Union[fun, int]') - - def test_union_str_pattern(self): - # Shouldn't crash; see http://bugs.python.org/issue25390 - A = Union[str, Pattern] - A - - def test_etree(self): - # See https://github.com/python/typing/issues/229 - # (Only relevant for Python 2.) - try: - from xml.etree.cElementTree import Element - except ImportError: - raise SkipTest("cElementTree not found") - Union[Element, str] # Shouldn't crash - - def Elem(*args): - return Element(*args) - - Union[Elem, str] # Nor should this - - -class TupleTests(BaseTestCase): - - def test_basics(self): - with self.assertRaises(TypeError): - issubclass(Tuple, Tuple[int, str]) - with self.assertRaises(TypeError): - issubclass(tuple, Tuple[int, str]) - - class TP(tuple): ... - self.assertTrue(issubclass(tuple, Tuple)) - self.assertTrue(issubclass(TP, Tuple)) - - def test_equality(self): - self.assertEqual(Tuple[int], Tuple[int]) - self.assertEqual(Tuple[int, ...], Tuple[int, ...]) - self.assertNotEqual(Tuple[int], Tuple[int, int]) - self.assertNotEqual(Tuple[int], Tuple[int, ...]) - - def test_tuple_subclass(self): - class MyTuple(tuple): - pass - self.assertTrue(issubclass(MyTuple, Tuple)) - - def test_tuple_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance((0, 0), Tuple[int, int]) - self.assertIsInstance((0, 0), Tuple) - - def test_repr(self): - self.assertEqual(repr(Tuple), 'typing.Tuple') - self.assertEqual(repr(Tuple[()]), 'typing.Tuple[()]') - self.assertEqual(repr(Tuple[int, float]), 'typing.Tuple[int, float]') - self.assertEqual(repr(Tuple[int, ...]), 'typing.Tuple[int, ...]') - - def test_errors(self): - with self.assertRaises(TypeError): - issubclass(42, Tuple) - with self.assertRaises(TypeError): - issubclass(42, Tuple[int]) - - -class CallableTests(BaseTestCase): - - def test_self_subclass(self): - with self.assertRaises(TypeError): - self.assertTrue(issubclass(type(lambda x: x), Callable[[int], int])) - self.assertTrue(issubclass(type(lambda x: x), Callable)) - - def test_eq_hash(self): - self.assertEqual(Callable[[int], int], Callable[[int], int]) - self.assertEqual(len({Callable[[int], int], Callable[[int], int]}), 1) - self.assertNotEqual(Callable[[int], int], Callable[[int], str]) - self.assertNotEqual(Callable[[int], int], Callable[[str], int]) - self.assertNotEqual(Callable[[int], int], Callable[[int, int], int]) - self.assertNotEqual(Callable[[int], int], Callable[[], int]) - self.assertNotEqual(Callable[[int], int], Callable) - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - Callable() - with self.assertRaises(TypeError): - type(Callable)() - c = Callable[[int], str] - with self.assertRaises(TypeError): - c() - with self.assertRaises(TypeError): - type(c)() - - def test_callable_wrong_forms(self): - with self.assertRaises(TypeError): - Callable[[...], int] - with self.assertRaises(TypeError): - Callable[(), int] - with self.assertRaises(TypeError): - Callable[[()], int] - with self.assertRaises(TypeError): - Callable[[int, 1], 2] - with self.assertRaises(TypeError): - Callable[int] - - def test_callable_instance_works(self): - def f(): - pass - self.assertIsInstance(f, Callable) - self.assertNotIsInstance(None, Callable) - - def test_callable_instance_type_error(self): - def f(): - pass - with self.assertRaises(TypeError): - self.assertIsInstance(f, Callable[[], None]) - with self.assertRaises(TypeError): - self.assertIsInstance(f, Callable[[], Any]) - with self.assertRaises(TypeError): - self.assertNotIsInstance(None, Callable[[], None]) - with self.assertRaises(TypeError): - self.assertNotIsInstance(None, Callable[[], Any]) - - def test_repr(self): - ct0 = Callable[[], bool] - self.assertEqual(repr(ct0), 'typing.Callable[[], bool]') - ct2 = Callable[[str, float], int] - self.assertEqual(repr(ct2), 'typing.Callable[[str, float], int]') - ctv = Callable[..., str] - self.assertEqual(repr(ctv), 'typing.Callable[..., str]') - - def test_callable_with_ellipsis(self): - - def foo(a: Callable[..., T]): - pass - - self.assertEqual(get_type_hints(foo, globals(), locals()), - {'a': Callable[..., T]}) - - def test_ellipsis_in_generic(self): - # Shouldn't crash; see https://github.com/python/typing/issues/259 - typing.List[Callable[..., str]] - - -XK = TypeVar('XK', str, bytes) -XV = TypeVar('XV') - - -class SimpleMapping(Generic[XK, XV]): - - def __getitem__(self, key: XK) -> XV: - ... - - def __setitem__(self, key: XK, value: XV): - ... - - def get(self, key: XK, default: XV = None) -> XV: - ... - - -class MySimpleMapping(SimpleMapping[XK, XV]): - - def __init__(self): - self.store = {} - - def __getitem__(self, key: str): - return self.store[key] - - def __setitem__(self, key: str, value): - self.store[key] = value - - def get(self, key: str, default=None): - try: - return self.store[key] - except KeyError: - return default - - -class ProtocolTests(BaseTestCase): - - def test_supports_int(self): - self.assertIsSubclass(int, typing.SupportsInt) - self.assertNotIsSubclass(str, typing.SupportsInt) - - def test_supports_float(self): - self.assertIsSubclass(float, typing.SupportsFloat) - self.assertNotIsSubclass(str, typing.SupportsFloat) - - def test_supports_complex(self): - - # Note: complex itself doesn't have __complex__. - class C: - def __complex__(self): - return 0j - - self.assertIsSubclass(C, typing.SupportsComplex) - self.assertNotIsSubclass(str, typing.SupportsComplex) - - def test_supports_bytes(self): - - # Note: bytes itself doesn't have __bytes__. - class B: - def __bytes__(self): - return b'' - - self.assertIsSubclass(B, typing.SupportsBytes) - self.assertNotIsSubclass(str, typing.SupportsBytes) - - def test_supports_abs(self): - self.assertIsSubclass(float, typing.SupportsAbs) - self.assertIsSubclass(int, typing.SupportsAbs) - self.assertNotIsSubclass(str, typing.SupportsAbs) - - def test_supports_round(self): - issubclass(float, typing.SupportsRound) - self.assertIsSubclass(float, typing.SupportsRound) - self.assertIsSubclass(int, typing.SupportsRound) - self.assertNotIsSubclass(str, typing.SupportsRound) - - def test_reversible(self): - self.assertIsSubclass(list, typing.Reversible) - self.assertNotIsSubclass(int, typing.Reversible) - - def test_supports_index(self): - self.assertIsSubclass(int, typing.SupportsIndex) - self.assertNotIsSubclass(str, typing.SupportsIndex) - - def test_protocol_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(0, typing.SupportsAbs) - class C1(typing.SupportsInt): - def __int__(self) -> int: - return 42 - class C2(C1): - pass - c = C2() - self.assertIsInstance(c, C1) - - -class GenericTests(BaseTestCase): - - def test_basics(self): - X = SimpleMapping[str, Any] - self.assertEqual(X.__parameters__, ()) - with self.assertRaises(TypeError): - X[str] - with self.assertRaises(TypeError): - X[str, str] - Y = SimpleMapping[XK, str] - self.assertEqual(Y.__parameters__, (XK,)) - Y[str] - with self.assertRaises(TypeError): - Y[str, str] - self.assertIsSubclass(SimpleMapping[str, int], SimpleMapping) - - def test_generic_errors(self): - T = TypeVar('T') - S = TypeVar('S') - with self.assertRaises(TypeError): - Generic[T]() - with self.assertRaises(TypeError): - Generic[T][T] - with self.assertRaises(TypeError): - Generic[T][S] - with self.assertRaises(TypeError): - isinstance([], List[int]) - with self.assertRaises(TypeError): - issubclass(list, List[int]) - with self.assertRaises(TypeError): - class NewGeneric(Generic): ... - with self.assertRaises(TypeError): - class MyGeneric(Generic[T], Generic[S]): ... - with self.assertRaises(TypeError): - class MyGeneric(List[T], Generic[S]): ... - - def test_init(self): - T = TypeVar('T') - S = TypeVar('S') - with self.assertRaises(TypeError): - Generic[T, T] - with self.assertRaises(TypeError): - Generic[T, S, T] - - @skipUnless(PY36, "__init_subclass__ support required") - def test_init_subclass(self): - class X(typing.Generic[T]): - def __init_subclass__(cls, **kwargs): - super().__init_subclass__(**kwargs) - cls.attr = 42 - class Y(X): - pass - self.assertEqual(Y.attr, 42) - with self.assertRaises(AttributeError): - X.attr - X.attr = 1 - Y.attr = 2 - class Z(Y): - pass - class W(X[int]): - pass - self.assertEqual(Y.attr, 2) - self.assertEqual(Z.attr, 42) - self.assertEqual(W.attr, 42) - - def test_repr(self): - self.assertEqual(repr(SimpleMapping), - __name__ + '.' + 'SimpleMapping') - self.assertEqual(repr(MySimpleMapping), - __name__ + '.' + 'MySimpleMapping') - - def test_chain_repr(self): - T = TypeVar('T') - S = TypeVar('S') - - class C(Generic[T]): - pass - - X = C[Tuple[S, T]] - self.assertEqual(X, C[Tuple[S, T]]) - self.assertNotEqual(X, C[Tuple[T, S]]) - - Y = X[T, int] - self.assertEqual(Y, X[T, int]) - self.assertNotEqual(Y, X[S, int]) - self.assertNotEqual(Y, X[T, str]) - - Z = Y[str] - self.assertEqual(Z, Y[str]) - self.assertNotEqual(Z, Y[int]) - self.assertNotEqual(Z, Y[T]) - - self.assertTrue(str(Z).endswith( - '.C[typing.Tuple[str, int]]')) - - def test_new_repr(self): - T = TypeVar('T') - U = TypeVar('U', covariant=True) - S = TypeVar('S') - - self.assertEqual(repr(List), 'typing.List') - self.assertEqual(repr(List[T]), 'typing.List[~T]') - self.assertEqual(repr(List[U]), 'typing.List[+U]') - self.assertEqual(repr(List[S][T][int]), 'typing.List[int]') - self.assertEqual(repr(List[int]), 'typing.List[int]') - - def test_new_repr_complex(self): - T = TypeVar('T') - TS = TypeVar('TS') - - self.assertEqual(repr(typing.Mapping[T, TS][TS, T]), 'typing.Mapping[~TS, ~T]') - self.assertEqual(repr(List[Tuple[T, TS]][int, T]), - 'typing.List[typing.Tuple[int, ~T]]') - self.assertEqual( - repr(List[Tuple[T, T]][List[int]]), - 'typing.List[typing.Tuple[typing.List[int], typing.List[int]]]' - ) - - def test_new_repr_bare(self): - T = TypeVar('T') - self.assertEqual(repr(Generic[T]), 'typing.Generic[~T]') - self.assertEqual(repr(typing._Protocol[T]), 'typing.Protocol[~T]') - class C(typing.Dict[Any, Any]): ... - # this line should just work - repr(C.__mro__) - - def test_dict(self): - T = TypeVar('T') - - class B(Generic[T]): - pass - - b = B() - b.foo = 42 - self.assertEqual(b.__dict__, {'foo': 42}) - - class C(B[int]): - pass - - c = C() - c.bar = 'abc' - self.assertEqual(c.__dict__, {'bar': 'abc'}) - - def test_subscripted_generics_as_proxies(self): - T = TypeVar('T') - class C(Generic[T]): - x = 'def' - self.assertEqual(C[int].x, 'def') - self.assertEqual(C[C[int]].x, 'def') - C[C[int]].x = 'changed' - self.assertEqual(C.x, 'changed') - self.assertEqual(C[str].x, 'changed') - C[List[str]].z = 'new' - self.assertEqual(C.z, 'new') - self.assertEqual(C[Tuple[int]].z, 'new') - - self.assertEqual(C().x, 'changed') - self.assertEqual(C[Tuple[str]]().z, 'new') - - class D(C[T]): - pass - self.assertEqual(D[int].x, 'changed') - self.assertEqual(D.z, 'new') - D.z = 'from derived z' - D[int].x = 'from derived x' - self.assertEqual(C.x, 'changed') - self.assertEqual(C[int].z, 'new') - self.assertEqual(D.x, 'from derived x') - self.assertEqual(D[str].z, 'from derived z') - - def test_abc_registry_kept(self): - T = TypeVar('T') - class C(Generic[T]): ... - C.register(int) - self.assertIsInstance(1, C) - C[int] - self.assertIsInstance(1, C) - - def test_false_subclasses(self): - class MyMapping(MutableMapping[str, str]): pass - self.assertNotIsInstance({}, MyMapping) - self.assertNotIsSubclass(dict, MyMapping) - - def test_abc_bases(self): - class MM(MutableMapping[str, str]): - def __getitem__(self, k): - return None - def __setitem__(self, k, v): - pass - def __delitem__(self, k): - pass - def __iter__(self): - return iter(()) - def __len__(self): - return 0 - # this should just work - MM().update() - self.assertIsInstance(MM(), collections_abc.MutableMapping) - self.assertIsInstance(MM(), MutableMapping) - self.assertNotIsInstance(MM(), List) - self.assertNotIsInstance({}, MM) - - def test_multiple_bases(self): - class MM1(MutableMapping[str, str], collections_abc.MutableMapping): - pass - with self.assertRaises(TypeError): - # consistent MRO not possible - class MM2(collections_abc.MutableMapping, MutableMapping[str, str]): - pass - - def test_orig_bases(self): - T = TypeVar('T') - class C(typing.Dict[str, T]): ... - self.assertEqual(C.__orig_bases__, (typing.Dict[str, T],)) - - def test_naive_runtime_checks(self): - def naive_dict_check(obj, tp): - # Check if a dictionary conforms to Dict type - if len(tp.__parameters__) > 0: - raise NotImplementedError - if tp.__args__: - KT, VT = tp.__args__ - return all( - isinstance(k, KT) and isinstance(v, VT) - for k, v in obj.items() - ) - self.assertTrue(naive_dict_check({'x': 1}, typing.Dict[str, int])) - self.assertFalse(naive_dict_check({1: 'x'}, typing.Dict[str, int])) - with self.assertRaises(NotImplementedError): - naive_dict_check({1: 'x'}, typing.Dict[str, T]) - - def naive_generic_check(obj, tp): - # Check if an instance conforms to the generic class - if not hasattr(obj, '__orig_class__'): - raise NotImplementedError - return obj.__orig_class__ == tp - class Node(Generic[T]): ... - self.assertTrue(naive_generic_check(Node[int](), Node[int])) - self.assertFalse(naive_generic_check(Node[str](), Node[int])) - self.assertFalse(naive_generic_check(Node[str](), List)) - with self.assertRaises(NotImplementedError): - naive_generic_check([1, 2, 3], Node[int]) - - def naive_list_base_check(obj, tp): - # Check if list conforms to a List subclass - return all(isinstance(x, tp.__orig_bases__[0].__args__[0]) - for x in obj) - class C(List[int]): ... - self.assertTrue(naive_list_base_check([1, 2, 3], C)) - self.assertFalse(naive_list_base_check(['a', 'b'], C)) - - def test_multi_subscr_base(self): - T = TypeVar('T') - U = TypeVar('U') - V = TypeVar('V') - class C(List[T][U][V]): ... - class D(C, List[T][U][V]): ... - self.assertEqual(C.__parameters__, (V,)) - self.assertEqual(D.__parameters__, (V,)) - self.assertEqual(C[int].__parameters__, ()) - self.assertEqual(D[int].__parameters__, ()) - self.assertEqual(C[int].__args__, (int,)) - self.assertEqual(D[int].__args__, (int,)) - self.assertEqual(C.__bases__, (List,)) - self.assertEqual(D.__bases__, (C, List)) - self.assertEqual(C.__orig_bases__, (List[T][U][V],)) - self.assertEqual(D.__orig_bases__, (C, List[T][U][V])) - - def test_subscript_meta(self): - T = TypeVar('T') - self.assertEqual(Type[GenericMeta], Type[GenericMeta]) - self.assertEqual(Union[T, int][GenericMeta], Union[GenericMeta, int]) - self.assertEqual(Callable[..., GenericMeta].__args__, (Ellipsis, GenericMeta)) - - def test_generic_hashes(self): - class A(Generic[T]): - ... - - class B(Generic[T]): - class A(Generic[T]): - ... - - self.assertEqual(A, A) - self.assertEqual(mod_generics_cache.A[str], mod_generics_cache.A[str]) - self.assertEqual(B.A, B.A) - self.assertEqual(mod_generics_cache.B.A[B.A[str]], - mod_generics_cache.B.A[B.A[str]]) - - self.assertNotEqual(A, B.A) - self.assertNotEqual(A, mod_generics_cache.A) - self.assertNotEqual(A, mod_generics_cache.B.A) - self.assertNotEqual(B.A, mod_generics_cache.A) - self.assertNotEqual(B.A, mod_generics_cache.B.A) - - self.assertNotEqual(A[str], B.A[str]) - self.assertNotEqual(A[List[Any]], B.A[List[Any]]) - self.assertNotEqual(A[str], mod_generics_cache.A[str]) - self.assertNotEqual(A[str], mod_generics_cache.B.A[str]) - self.assertNotEqual(B.A[int], mod_generics_cache.A[int]) - self.assertNotEqual(B.A[List[Any]], mod_generics_cache.B.A[List[Any]]) - - self.assertNotEqual(Tuple[A[str]], Tuple[B.A[str]]) - self.assertNotEqual(Tuple[A[List[Any]]], Tuple[B.A[List[Any]]]) - self.assertNotEqual(Union[str, A[str]], Union[str, mod_generics_cache.A[str]]) - self.assertNotEqual(Union[A[str], A[str]], - Union[A[str], mod_generics_cache.A[str]]) - self.assertNotEqual(typing.FrozenSet[A[str]], - typing.FrozenSet[mod_generics_cache.B.A[str]]) - - if sys.version_info[:2] > (3, 2): - self.assertTrue(repr(Tuple[A[str]]).endswith('.A[str]]')) - self.assertTrue(repr(Tuple[B.A[str]]).endswith('.B.A[str]]')) - self.assertTrue(repr(Tuple[mod_generics_cache.A[str]]) - .endswith('mod_generics_cache.A[str]]')) - self.assertTrue(repr(Tuple[mod_generics_cache.B.A[str]]) - .endswith('mod_generics_cache.B.A[str]]')) - - def test_extended_generic_rules_eq(self): - T = TypeVar('T') - U = TypeVar('U') - self.assertEqual(Tuple[T, T][int], Tuple[int, int]) - self.assertEqual(typing.Iterable[Tuple[T, T]][T], typing.Iterable[Tuple[T, T]]) - with self.assertRaises(TypeError): - Tuple[T, int][()] - with self.assertRaises(TypeError): - Tuple[T, U][T, ...] - - self.assertEqual(Union[T, int][int], int) - self.assertEqual(Union[T, U][int, Union[int, str]], Union[int, str]) - class Base: ... - class Derived(Base): ... - self.assertEqual(Union[T, Base][Derived], Base) - with self.assertRaises(TypeError): - Union[T, int][1] - - self.assertEqual(Callable[[T], T][KT], Callable[[KT], KT]) - self.assertEqual(Callable[..., List[T]][int], Callable[..., List[int]]) - with self.assertRaises(TypeError): - Callable[[T], U][..., int] - with self.assertRaises(TypeError): - Callable[[T], U][[], int] - - def test_extended_generic_rules_repr(self): - T = TypeVar('T') - self.assertEqual(repr(Union[Tuple, Callable]).replace('typing.', ''), - 'Union[Tuple, Callable]') - self.assertEqual(repr(Union[Tuple, Tuple[int]]).replace('typing.', ''), - 'Tuple') - self.assertEqual(repr(Callable[..., Optional[T]][int]).replace('typing.', ''), - 'Callable[..., Union[int, NoneType]]') - self.assertEqual(repr(Callable[[], List[T]][int]).replace('typing.', ''), - 'Callable[[], List[int]]') - - def test_generic_forward_ref(self): - def foobar(x: List[List['CC']]): ... - class CC: ... - self.assertEqual( - get_type_hints(foobar, globals(), locals()), - {'x': List[List[CC]]} - ) - T = TypeVar('T') - AT = Tuple[T, ...] - def barfoo(x: AT): ... - self.assertIs(get_type_hints(barfoo, globals(), locals())['x'], AT) - CT = Callable[..., List[T]] - def barfoo2(x: CT): ... - self.assertIs(get_type_hints(barfoo2, globals(), locals())['x'], CT) - - def test_extended_generic_rules_subclassing(self): - class T1(Tuple[T, KT]): ... - class T2(Tuple[T, ...]): ... - class C1(Callable[[T], T]): ... - class C2(Callable[..., int]): - def __call__(self): - return None - - self.assertEqual(T1.__parameters__, (T, KT)) - self.assertEqual(T1[int, str].__args__, (int, str)) - self.assertEqual(T1[int, T].__origin__, T1) - - self.assertEqual(T2.__parameters__, (T,)) - with self.assertRaises(TypeError): - T1[int] - with self.assertRaises(TypeError): - T2[int, str] - - self.assertEqual(repr(C1[int]).split('.')[-1], 'C1[int]') - self.assertEqual(C2.__parameters__, ()) - self.assertIsInstance(C2(), collections_abc.Callable) - self.assertIsSubclass(C2, collections_abc.Callable) - self.assertIsSubclass(C1, collections_abc.Callable) - self.assertIsInstance(T1(), tuple) - self.assertIsSubclass(T2, tuple) - self.assertIsSubclass(Tuple[int, ...], typing.Sequence) - self.assertIsSubclass(Tuple[int, ...], typing.Iterable) - - def test_fail_with_bare_union(self): - with self.assertRaises(TypeError): - List[Union] - with self.assertRaises(TypeError): - Tuple[Optional] - with self.assertRaises(TypeError): - ClassVar[ClassVar] - with self.assertRaises(TypeError): - List[ClassVar[int]] - - def test_fail_with_bare_generic(self): - T = TypeVar('T') - with self.assertRaises(TypeError): - List[Generic] - with self.assertRaises(TypeError): - Tuple[Generic[T]] - with self.assertRaises(TypeError): - List[typing._Protocol] - with self.assertRaises(TypeError): - isinstance(1, Generic) - - def test_type_erasure_special(self): - T = TypeVar('T') - # this is the only test that checks type caching - self.clear_caches() - class MyTup(Tuple[T, T]): ... - self.assertIs(MyTup[int]().__class__, MyTup) - self.assertIs(MyTup[int]().__orig_class__, MyTup[int]) - class MyCall(Callable[..., T]): - def __call__(self): return None - self.assertIs(MyCall[T]().__class__, MyCall) - self.assertIs(MyCall[T]().__orig_class__, MyCall[T]) - class MyDict(typing.Dict[T, T]): ... - self.assertIs(MyDict[int]().__class__, MyDict) - self.assertIs(MyDict[int]().__orig_class__, MyDict[int]) - class MyDef(typing.DefaultDict[str, T]): ... - self.assertIs(MyDef[int]().__class__, MyDef) - self.assertIs(MyDef[int]().__orig_class__, MyDef[int]) - # ChainMap was added in 3.3 - if sys.version_info >= (3, 3): - class MyChain(typing.ChainMap[str, T]): ... - self.assertIs(MyChain[int]().__class__, MyChain) - self.assertIs(MyChain[int]().__orig_class__, MyChain[int]) - - def test_all_repr_eq_any(self): - objs = (getattr(typing, el) for el in typing.__all__) - for obj in objs: - self.assertNotEqual(repr(obj), '') - self.assertEqual(obj, obj) - if getattr(obj, '__parameters__', None) and len(obj.__parameters__) == 1: - self.assertEqual(obj[Any].__args__, (Any,)) - if isinstance(obj, type): - for base in obj.__mro__: - self.assertNotEqual(repr(base), '') - self.assertEqual(base, base) - - def test_substitution_helper(self): - T = TypeVar('T') - KT = TypeVar('KT') - VT = TypeVar('VT') - class Map(Generic[KT, VT]): - def meth(self, k: KT, v: VT): ... - StrMap = Map[str, T] - obj = StrMap[int]() - - new_args = typing._subs_tree(obj.__orig_class__) - new_annots = {k: typing._replace_arg(v, type(obj).__parameters__, new_args) - for k, v in obj.meth.__annotations__.items()} - - self.assertEqual(new_annots, {'k': str, 'v': int}) - - def test_pickle(self): - global C # pickle wants to reference the class by name - T = TypeVar('T') - - class B(Generic[T]): - pass - - class C(B[int]): - pass - - c = C() - c.foo = 42 - c.bar = 'abc' - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) - simples = [Any, Union, Tuple, Callable, ClassVar, List, typing.Iterable] - for s in simples: - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(s, proto) - x = pickle.loads(z) - self.assertEqual(s, x) - - def test_copy_and_deepcopy(self): - T = TypeVar('T') - class Node(Generic[T]): ... - things = [Union[T, int], Tuple[T, int], Callable[..., T], Callable[[int], int], - Tuple[Any, Any], Node[T], Node[int], Node[Any], typing.Iterable[T], - typing.Iterable[Any], typing.Iterable[int], typing.Dict[int, str], - typing.Dict[T, Any], ClassVar[int], ClassVar[List[T]], Tuple['T', 'T'], - Union['T', int], List['T'], typing.Mapping['T', int]] - for t in things + [Any]: - self.assertEqual(t, copy(t)) - self.assertEqual(t, deepcopy(t)) - if sys.version_info >= (3, 3): - # From copy module documentation: - # It does "copy" functions and classes (shallow and deeply), by returning - # the original object unchanged; this is compatible with the way these - # are treated by the pickle module. - self.assertTrue(t is copy(t)) - self.assertTrue(t is deepcopy(t)) - - def test_copy_generic_instances(self): - T = TypeVar('T') - class C(Generic[T]): - def __init__(self, attr: T) -> None: - self.attr = attr - - c = C(42) - self.assertEqual(copy(c).attr, 42) - self.assertEqual(deepcopy(c).attr, 42) - self.assertIsNot(copy(c), c) - self.assertIsNot(deepcopy(c), c) - c.attr = 1 - self.assertEqual(copy(c).attr, 1) - self.assertEqual(deepcopy(c).attr, 1) - ci = C[int](42) - self.assertEqual(copy(ci).attr, 42) - self.assertEqual(deepcopy(ci).attr, 42) - self.assertIsNot(copy(ci), ci) - self.assertIsNot(deepcopy(ci), ci) - ci.attr = 1 - self.assertEqual(copy(ci).attr, 1) - self.assertEqual(deepcopy(ci).attr, 1) - self.assertEqual(ci.__orig_class__, C[int]) - - def test_weakref_all(self): - T = TypeVar('T') - things = [Any, Union[T, int], Callable[..., T], Tuple[Any, Any], - Optional[List[int]], typing.Mapping[int, str], - typing.re.Match[bytes], typing.Iterable['whatever']] - for t in things: - self.assertEqual(weakref.ref(t)(), t) - - def test_parameterized_slots(self): - T = TypeVar('T') - class C(Generic[T]): - __slots__ = ('potato',) - - c = C() - c_int = C[int]() - self.assertEqual(C.__slots__, C[str].__slots__) - - c.potato = 0 - c_int.potato = 0 - with self.assertRaises(AttributeError): - c.tomato = 0 - with self.assertRaises(AttributeError): - c_int.tomato = 0 - - def foo(x: C['C']): ... - self.assertEqual(get_type_hints(foo, globals(), locals())['x'], C[C]) - self.assertEqual(get_type_hints(foo, globals(), locals())['x'].__slots__, - C.__slots__) - self.assertEqual(copy(C[int]), deepcopy(C[int])) - - def test_parameterized_slots_dict(self): - T = TypeVar('T') - class D(Generic[T]): - __slots__ = {'banana': 42} - - d = D() - d_int = D[int]() - self.assertEqual(D.__slots__, D[str].__slots__) - - d.banana = 'yes' - d_int.banana = 'yes' - with self.assertRaises(AttributeError): - d.foobar = 'no' - with self.assertRaises(AttributeError): - d_int.foobar = 'no' - - def test_errors(self): - with self.assertRaises(TypeError): - B = SimpleMapping[XK, Any] - - class C(Generic[B]): - pass - - def test_repr_2(self): - PY32 = sys.version_info[:2] < (3, 3) - - class C(Generic[T]): - pass - - self.assertEqual(C.__module__, __name__) - if not PY32: - self.assertEqual(C.__qualname__, - 'GenericTests.test_repr_2..C') - self.assertEqual(repr(C).split('.')[-1], 'C') - X = C[int] - self.assertEqual(X.__module__, __name__) - if not PY32: - self.assertTrue(X.__qualname__.endswith('..C')) - self.assertEqual(repr(X).split('.')[-1], 'C[int]') - - class Y(C[int]): - pass - - self.assertEqual(Y.__module__, __name__) - if not PY32: - self.assertEqual(Y.__qualname__, - 'GenericTests.test_repr_2..Y') - self.assertEqual(repr(Y).split('.')[-1], 'Y') - - def test_eq_1(self): - self.assertEqual(Generic, Generic) - self.assertEqual(Generic[T], Generic[T]) - self.assertNotEqual(Generic[KT], Generic[VT]) - - def test_eq_2(self): - - class A(Generic[T]): - pass - - class B(Generic[T]): - pass - - self.assertEqual(A, A) - self.assertNotEqual(A, B) - self.assertEqual(A[T], A[T]) - self.assertNotEqual(A[T], B[T]) - - def test_multiple_inheritance(self): - - class A(Generic[T, VT]): - pass - - class B(Generic[KT, T]): - pass - - class C(A[T, VT], Generic[VT, T, KT], B[KT, T]): - pass - - self.assertEqual(C.__parameters__, (VT, T, KT)) - - def test_nested(self): - - G = Generic - - class Visitor(G[T]): - - a = None - - def set(self, a: T): - self.a = a - - def get(self): - return self.a - - def visit(self) -> T: - return self.a - - V = Visitor[typing.List[int]] - - class IntListVisitor(V): - - def append(self, x: int): - self.a.append(x) - - a = IntListVisitor() - a.set([]) - a.append(1) - a.append(42) - self.assertEqual(a.get(), [1, 42]) - - def test_type_erasure(self): - T = TypeVar('T') - - class Node(Generic[T]): - def __init__(self, label: T, - left: 'Node[T]' = None, - right: 'Node[T]' = None): - self.label = label # type: T - self.left = left # type: Optional[Node[T]] - self.right = right # type: Optional[Node[T]] - - def foo(x: T): - a = Node(x) - b = Node[T](x) - c = Node[Any](x) - self.assertIs(type(a), Node) - self.assertIs(type(b), Node) - self.assertIs(type(c), Node) - self.assertEqual(a.label, x) - self.assertEqual(b.label, x) - self.assertEqual(c.label, x) - - foo(42) - - def test_implicit_any(self): - T = TypeVar('T') - - class C(Generic[T]): - pass - - class D(C): - pass - - self.assertEqual(D.__parameters__, ()) - - with self.assertRaises(Exception): - D[int] - with self.assertRaises(Exception): - D[Any] - with self.assertRaises(Exception): - D[T] - - def test_new_with_args(self): - - class A(Generic[T]): - pass - - class B: - def __new__(cls, arg): - # call object - obj = super().__new__(cls) - obj.arg = arg - return obj - - # mro: C, A, Generic, B, object - class C(A, B): - pass - - c = C('foo') - self.assertEqual(c.arg, 'foo') - - def test_new_with_args2(self): - - class A: - def __init__(self, arg): - self.from_a = arg - # call object - super().__init__() - - # mro: C, Generic, A, object - class C(Generic[T], A): - def __init__(self, arg): - self.from_c = arg - # call Generic - super().__init__(arg) - - c = C('foo') - self.assertEqual(c.from_a, 'foo') - self.assertEqual(c.from_c, 'foo') - - def test_new_no_args(self): - - class A(Generic[T]): - pass - - with self.assertRaises(TypeError): - A('foo') - - class B: - def __new__(cls): - # call object - obj = super().__new__(cls) - obj.from_b = 'b' - return obj - - # mro: C, A, Generic, B, object - class C(A, B): - def __init__(self, arg): - self.arg = arg - - def __new__(cls, arg): - # call A - obj = super().__new__(cls) - obj.from_c = 'c' - return obj - - c = C('foo') - self.assertEqual(c.arg, 'foo') - self.assertEqual(c.from_b, 'b') - self.assertEqual(c.from_c, 'c') - - -class ClassVarTests(BaseTestCase): - - def test_basics(self): - with self.assertRaises(TypeError): - ClassVar[1] - with self.assertRaises(TypeError): - ClassVar[int, str] - with self.assertRaises(TypeError): - ClassVar[int][str] - - def test_repr(self): - self.assertEqual(repr(ClassVar), 'typing.ClassVar') - cv = ClassVar[int] - self.assertEqual(repr(cv), 'typing.ClassVar[int]') - cv = ClassVar[Employee] - self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__) - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(type(ClassVar)): - pass - with self.assertRaises(TypeError): - class C(type(ClassVar[int])): - pass - - def test_cannot_init(self): - with self.assertRaises(TypeError): - ClassVar() - with self.assertRaises(TypeError): - type(ClassVar)() - with self.assertRaises(TypeError): - type(ClassVar[Optional[int]])() - - def test_no_isinstance(self): - with self.assertRaises(TypeError): - isinstance(1, ClassVar[int]) - with self.assertRaises(TypeError): - issubclass(int, ClassVar) - - -class CastTests(BaseTestCase): - - def test_basics(self): - self.assertEqual(cast(int, 42), 42) - self.assertEqual(cast(float, 42), 42) - self.assertIs(type(cast(float, 42)), int) - self.assertEqual(cast(Any, 42), 42) - self.assertEqual(cast(list, 42), 42) - self.assertEqual(cast(Union[str, float], 42), 42) - self.assertEqual(cast(AnyStr, 42), 42) - self.assertEqual(cast(None, 42), 42) - - def test_errors(self): - # Bogus calls are not expected to fail. - cast(42, 42) - cast('hello', 42) - - -class ForwardRefTests(BaseTestCase): - - def test_basics(self): - - class Node(Generic[T]): - - def __init__(self, label: T): - self.label = label - self.left = self.right = None - - def add_both(self, - left: 'Optional[Node[T]]', - right: 'Node[T]' = None, - stuff: int = None, - blah=None): - self.left = left - self.right = right - - def add_left(self, node: Optional['Node[T]']): - self.add_both(node, None) - - def add_right(self, node: 'Node[T]' = None): - self.add_both(None, node) - - t = Node[int] - both_hints = get_type_hints(t.add_both, globals(), locals()) - self.assertEqual(both_hints['left'], Optional[Node[T]]) - self.assertEqual(both_hints['right'], Optional[Node[T]]) - self.assertEqual(both_hints['left'], both_hints['right']) - self.assertEqual(both_hints['stuff'], Optional[int]) - self.assertNotIn('blah', both_hints) - - left_hints = get_type_hints(t.add_left, globals(), locals()) - self.assertEqual(left_hints['node'], Optional[Node[T]]) - - right_hints = get_type_hints(t.add_right, globals(), locals()) - self.assertEqual(right_hints['node'], Optional[Node[T]]) - - def test_forwardref_instance_type_error(self): - fr = typing._ForwardRef('int') - with self.assertRaises(TypeError): - isinstance(42, fr) - - def test_forwardref_subclass_type_error(self): - fr = typing._ForwardRef('int') - with self.assertRaises(TypeError): - issubclass(int, fr) - - def test_forward_equality(self): - fr = typing._ForwardRef('int') - self.assertEqual(fr, typing._ForwardRef('int')) - self.assertNotEqual(List['int'], List[int]) - - def test_forward_equality_gth(self): - c1 = typing._ForwardRef('C') - c1_gth = typing._ForwardRef('C') - c2 = typing._ForwardRef('C') - c2_gth = typing._ForwardRef('C') - - class C: - pass - def foo(a: c1_gth, b: c2_gth): - pass - - self.assertEqual(get_type_hints(foo, globals(), locals()), {'a': C, 'b': C}) - self.assertEqual(c1, c2) - self.assertEqual(c1, c1_gth) - self.assertEqual(c1_gth, c2_gth) - self.assertEqual(List[c1], List[c1_gth]) - self.assertNotEqual(List[c1], List[C]) - self.assertNotEqual(List[c1_gth], List[C]) - self.assertEquals(Union[c1, c1_gth], Union[c1]) - self.assertEquals(Union[c1, c1_gth, int], Union[c1, int]) - - def test_forward_equality_hash(self): - c1 = typing._ForwardRef('int') - c1_gth = typing._ForwardRef('int') - c2 = typing._ForwardRef('int') - c2_gth = typing._ForwardRef('int') - - def foo(a: c1_gth, b: c2_gth): - pass - get_type_hints(foo, globals(), locals()) - - self.assertEqual(hash(c1), hash(c2)) - self.assertEqual(hash(c1_gth), hash(c2_gth)) - self.assertEqual(hash(c1), hash(c1_gth)) - - def test_forward_equality_namespace(self): - class A: - pass - def namespace1(): - a = typing._ForwardRef('A') - def fun(x: a): - pass - get_type_hints(fun, globals(), locals()) - return a - - def namespace2(): - a = typing._ForwardRef('A') - - class A: - pass - def fun(x: a): - pass - - get_type_hints(fun, globals(), locals()) - return a - - self.assertEqual(namespace1(), namespace1()) - self.assertNotEqual(namespace1(), namespace2()) - - def test_forward_repr(self): - self.assertEqual(repr(List['int']), "typing.List[_ForwardRef('int')]") - - def test_union_forward(self): - - def foo(a: Union['T']): - pass - - self.assertEqual(get_type_hints(foo, globals(), locals()), - {'a': Union[T]}) - - def test_tuple_forward(self): - - def foo(a: Tuple['T']): - pass - - self.assertEqual(get_type_hints(foo, globals(), locals()), - {'a': Tuple[T]}) - - def test_forward_recursion_actually(self): - def namespace1(): - a = typing._ForwardRef('A') - A = a - def fun(x: a): pass - - ret = get_type_hints(fun, globals(), locals()) - return a - - def namespace2(): - a = typing._ForwardRef('A') - A = a - def fun(x: a): pass - - ret = get_type_hints(fun, globals(), locals()) - return a - - def cmp(o1, o2): - return o1 == o2 - - r1 = namespace1() - r2 = namespace2() - self.assertIsNot(r1, r2) - - try: - exc = RecursionError - except NameError: - exc = RuntimeError - self.assertRaises(exc, cmp, r1, r2) - - def test_union_forward_recursion(self): - ValueList = List['Value'] - Value = Union[str, ValueList] - - def c(foo: List[Value]): - pass - def d(foo: Union[Value, ValueList]): - pass - def e(foo: Union[List[Value], ValueList]): - pass - def f(foo: Union[Value, List[Value], ValueList]): - pass - - self.assertEqual(get_type_hints(c, globals(), locals()), - get_type_hints(c, globals(), locals())) - self.assertEqual(get_type_hints(c, globals(), locals()), - {'foo': List[Union[str, List[Union[str, List['Value']]]]]}) - self.assertEqual(get_type_hints(d, globals(), locals()), - {'foo': Union[str, List[Union[str, List['Value']]]]}) - self.assertEqual(get_type_hints(e, globals(), locals()), - {'foo': Union[ - List[Union[str, List[Union[str, List['Value']]]]], - List[Union[str, List['Value']]] - ]}) - self.assertEqual(get_type_hints(f, globals(), locals()), - {'foo': Union[ - str, - List[Union[str, List['Value']]], - List[Union[str, List[Union[str, List['Value']]]]] - ]}) - - def test_callable_forward(self): - - def foo(a: Callable[['T'], 'T']): - pass - - self.assertEqual(get_type_hints(foo, globals(), locals()), - {'a': Callable[[T], T]}) - - def test_callable_with_ellipsis_forward(self): - - def foo(a: 'Callable[..., T]'): - pass - - self.assertEqual(get_type_hints(foo, globals(), locals()), - {'a': Callable[..., T]}) - - def test_syntax_error(self): - - with self.assertRaises(SyntaxError): - Generic['/T'] - - def test_delayed_syntax_error(self): - - def foo(a: 'Node[T'): # noqa - pass - - with self.assertRaises(SyntaxError): - get_type_hints(foo) - - def test_type_error(self): - - def foo(a: Tuple['42']): - pass - - with self.assertRaises(TypeError): - get_type_hints(foo) - - def test_name_error(self): - - def foo(a: 'Noode[T]'): # noqa - pass - - with self.assertRaises(NameError): - get_type_hints(foo, locals()) - - def test_no_type_check(self): - - @no_type_check - def foo(a: 'whatevers') -> {}: # noqa - pass - - th = get_type_hints(foo) - self.assertEqual(th, {}) - - def test_no_type_check_class(self): - - @no_type_check - class C: - def foo(a: 'whatevers') -> {}: # noqa - pass - - cth = get_type_hints(C.foo) - self.assertEqual(cth, {}) - ith = get_type_hints(C().foo) - self.assertEqual(ith, {}) - - def test_no_type_check_no_bases(self): - class C: - def meth(self, x: int): ... - @no_type_check - class D(C): - c = C - # verify that @no_type_check never affects bases - self.assertEqual(get_type_hints(C.meth), {'x': int}) - - def test_meta_no_type_check(self): - - @no_type_check_decorator - def magic_decorator(deco): - return deco - - self.assertEqual(magic_decorator.__name__, 'magic_decorator') - - @magic_decorator - def foo(a: 'whatevers') -> {}: # noqa - pass - - @magic_decorator - class C: - def foo(a: 'whatevers') -> {}: # noqa - pass - - self.assertEqual(foo.__name__, 'foo') - th = get_type_hints(foo) - self.assertEqual(th, {}) - cth = get_type_hints(C.foo) - self.assertEqual(cth, {}) - ith = get_type_hints(C().foo) - self.assertEqual(ith, {}) - - def test_default_globals(self): - code = ("class C:\n" - " def foo(self, a: 'C') -> 'D': pass\n" - "class D:\n" - " def bar(self, b: 'D') -> C: pass\n" - ) - ns = {} - exec(code, ns) - hints = get_type_hints(ns['C'].foo) - self.assertEqual(hints, {'a': ns['C'], 'return': ns['D']}) - - -class OverloadTests(BaseTestCase): - - def test_overload_fails(self): - from typing import overload - - with self.assertRaises(RuntimeError): - - @overload - def blah(): - pass - - blah() - - def test_overload_succeeds(self): - from typing import overload - - @overload - def blah(): - pass - - def blah(): - pass - - blah() - - -ASYNCIO = sys.version_info[:2] >= (3, 5) - -ASYNCIO_TESTS = """ -import asyncio - -T_a = TypeVar('T_a') - -class AwaitableWrapper(typing.Awaitable[T_a]): - - def __init__(self, value): - self.value = value - - def __await__(self) -> typing.Iterator[T_a]: - yield - return self.value - -class AsyncIteratorWrapper(typing.AsyncIterator[T_a]): - - def __init__(self, value: typing.Iterable[T_a]): - self.value = value - - def __aiter__(self) -> typing.AsyncIterator[T_a]: - return self - - @asyncio.coroutine - def __anext__(self) -> T_a: - data = yield from self.value - if data: - return data - else: - raise StopAsyncIteration - -class ACM: - async def __aenter__(self) -> int: - return 42 - async def __aexit__(self, etype, eval, tb): - return None -""" - -if ASYNCIO: - try: - exec(ASYNCIO_TESTS) - except ImportError: - ASYNCIO = False -else: - # fake names for the sake of static analysis - asyncio = None - AwaitableWrapper = AsyncIteratorWrapper = ACM = object - -PY36_TESTS = """ -from test import ann_module, ann_module2, ann_module3 -from typing import AsyncContextManager - -class A: - y: float -class B(A): - x: ClassVar[Optional['B']] = None - y: int - b: int -class CSub(B): - z: ClassVar['CSub'] = B() -class G(Generic[T]): - lst: ClassVar[List[T]] = [] - -class NoneAndForward: - parent: 'NoneAndForward' - meaning: None - -class CoolEmployee(NamedTuple): - name: str - cool: int - -class CoolEmployeeWithDefault(NamedTuple): - name: str - cool: int = 0 - -class XMeth(NamedTuple): - x: int - def double(self): - return 2 * self.x - -class XRepr(NamedTuple): - x: int - y: int = 1 - def __str__(self): - return f'{self.x} -> {self.y}' - def __add__(self, other): - return 0 - -class HasForeignBaseClass(mod_generics_cache.A): - some_xrepr: 'XRepr' - other_a: 'mod_generics_cache.A' - -async def g_with(am: AsyncContextManager[int]): - x: int - async with am as x: - return x - -try: - g_with(ACM()).send(None) -except StopIteration as e: - assert e.args[0] == 42 -""" - -if PY36: - exec(PY36_TESTS) -else: - # fake names for the sake of static analysis - ann_module = ann_module2 = ann_module3 = None - A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = object - XMeth = XRepr = NoneAndForward = HasForeignBaseClass = object - -gth = get_type_hints - - -class GetTypeHintTests(BaseTestCase): - def test_get_type_hints_from_various_objects(self): - # For invalid objects should fail with TypeError (not AttributeError etc). - with self.assertRaises(TypeError): - gth(123) - with self.assertRaises(TypeError): - gth('abc') - with self.assertRaises(TypeError): - gth(None) - - @skipUnless(PY36, 'Python 3.6 required') - def test_get_type_hints_modules(self): - ann_module_type_hints = {1: 2, 'f': Tuple[int, int], 'x': int, 'y': str} - self.assertEqual(gth(ann_module), ann_module_type_hints) - self.assertEqual(gth(ann_module2), {}) - self.assertEqual(gth(ann_module3), {}) - - @skipUnless(PY36, 'Python 3.6 required') - @expectedFailure - def test_get_type_hints_modules_forwardref(self): - # FIXME: This currently exposes a bug in typing. Cached forward references - # don't account for the case where there are multiple types of the same - # name coming from different modules in the same program. - mgc_hints = {'default_a': Optional[mod_generics_cache.A], - 'default_b': Optional[mod_generics_cache.B]} - self.assertEqual(gth(mod_generics_cache), mgc_hints) - - @skipUnless(PY36, 'Python 3.6 required') - def test_get_type_hints_classes(self): - self.assertEqual(gth(ann_module.C), # gth will find the right globalns - {'y': Optional[ann_module.C]}) - self.assertIsInstance(gth(ann_module.j_class), dict) - self.assertEqual(gth(ann_module.M), {'123': 123, 'o': type}) - self.assertEqual(gth(ann_module.D), - {'j': str, 'k': str, 'y': Optional[ann_module.C]}) - self.assertEqual(gth(ann_module.Y), {'z': int}) - self.assertEqual(gth(ann_module.h_class), - {'y': Optional[ann_module.C]}) - self.assertEqual(gth(ann_module.S), {'x': str, 'y': str}) - self.assertEqual(gth(ann_module.foo), {'x': int}) - self.assertEqual(gth(NoneAndForward), - {'parent': NoneAndForward, 'meaning': type(None)}) - self.assertEqual(gth(HasForeignBaseClass), - {'some_xrepr': XRepr, 'other_a': mod_generics_cache.A, - 'some_b': mod_generics_cache.B}) - self.assertEqual(gth(XRepr.__new__), - {'x': int, 'y': int}) - self.assertEqual(gth(mod_generics_cache.B), - {'my_inner_a1': mod_generics_cache.B.A, - 'my_inner_a2': mod_generics_cache.B.A, - 'my_outer_a': mod_generics_cache.A}) - - @skipUnless(PY36, 'Python 3.6 required') - def test_respect_no_type_check(self): - @no_type_check - class NoTpCheck: - class Inn: - def __init__(self, x: 'not a type'): ... # noqa - self.assertTrue(NoTpCheck.__no_type_check__) - self.assertTrue(NoTpCheck.Inn.__init__.__no_type_check__) - self.assertEqual(gth(ann_module2.NTC.meth), {}) - class ABase(Generic[T]): - def meth(x: int): ... - @no_type_check - class Der(ABase): ... - self.assertEqual(gth(ABase.meth), {'x': int}) - - def test_get_type_hints_for_builtins(self): - # Should not fail for built-in classes and functions. - self.assertEqual(gth(int), {}) - self.assertEqual(gth(type), {}) - self.assertEqual(gth(dir), {}) - self.assertEqual(gth(len), {}) - self.assertEqual(gth(object.__str__), {}) - self.assertEqual(gth(object().__str__), {}) - self.assertEqual(gth(str.join), {}) - - def test_previous_behavior(self): - def testf(x, y): ... - testf.__annotations__['x'] = 'int' - self.assertEqual(gth(testf), {'x': int}) - def testg(x: None): ... - self.assertEqual(gth(testg), {'x': type(None)}) - - def test_get_type_hints_for_object_with_annotations(self): - class A: ... - class B: ... - b = B() - b.__annotations__ = {'x': 'A'} - self.assertEqual(gth(b, locals()), {'x': A}) - - @skipUnless(PY36, 'Python 3.6 required') - def test_get_type_hints_ClassVar(self): - self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__), - {'var': typing.ClassVar[ann_module2.CV]}) - self.assertEqual(gth(B, globals()), - {'y': int, 'x': ClassVar[Optional[B]], 'b': int}) - self.assertEqual(gth(CSub, globals()), - {'z': ClassVar[CSub], 'y': int, 'b': int, - 'x': ClassVar[Optional[B]]}) - self.assertEqual(gth(G), {'lst': ClassVar[List[T]]}) - - -class CollectionsAbcTests(BaseTestCase): - - def test_hashable(self): - self.assertIsInstance(42, typing.Hashable) - self.assertNotIsInstance([], typing.Hashable) - - def test_iterable(self): - self.assertIsInstance([], typing.Iterable) - # Due to ABC caching, the second time takes a separate code - # path and could fail. So call this a few times. - self.assertIsInstance([], typing.Iterable) - self.assertIsInstance([], typing.Iterable) - self.assertNotIsInstance(42, typing.Iterable) - # Just in case, also test issubclass() a few times. - self.assertIsSubclass(list, typing.Iterable) - self.assertIsSubclass(list, typing.Iterable) - - def test_iterator(self): - it = iter([]) - self.assertIsInstance(it, typing.Iterator) - self.assertNotIsInstance(42, typing.Iterator) - - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') - def test_awaitable(self): - ns = {} - exec( - "async def foo() -> typing.Awaitable[int]:\n" - " return await AwaitableWrapper(42)\n", - globals(), ns) - foo = ns['foo'] - g = foo() - self.assertIsInstance(g, typing.Awaitable) - self.assertNotIsInstance(foo, typing.Awaitable) - g.send(None) # Run foo() till completion, to avoid warning. - - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') - def test_coroutine(self): - ns = {} - exec( - "async def foo():\n" - " return\n", - globals(), ns) - foo = ns['foo'] - g = foo() - self.assertIsInstance(g, typing.Coroutine) - with self.assertRaises(TypeError): - isinstance(g, typing.Coroutine[int]) - self.assertNotIsInstance(foo, typing.Coroutine) - try: - g.send(None) - except StopIteration: - pass - - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') - def test_async_iterable(self): - base_it = range(10) # type: Iterator[int] - it = AsyncIteratorWrapper(base_it) - self.assertIsInstance(it, typing.AsyncIterable) - self.assertIsInstance(it, typing.AsyncIterable) - self.assertNotIsInstance(42, typing.AsyncIterable) - - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') - def test_async_iterator(self): - base_it = range(10) # type: Iterator[int] - it = AsyncIteratorWrapper(base_it) - self.assertIsInstance(it, typing.AsyncIterator) - self.assertNotIsInstance(42, typing.AsyncIterator) - - def test_sized(self): - self.assertIsInstance([], typing.Sized) - self.assertNotIsInstance(42, typing.Sized) - - def test_container(self): - self.assertIsInstance([], typing.Container) - self.assertNotIsInstance(42, typing.Container) - - def test_collection(self): - if hasattr(typing, 'Collection'): - self.assertIsInstance(tuple(), typing.Collection) - self.assertIsInstance(frozenset(), typing.Collection) - self.assertIsSubclass(dict, typing.Collection) - self.assertNotIsInstance(42, typing.Collection) - - def test_abstractset(self): - self.assertIsInstance(set(), typing.AbstractSet) - self.assertNotIsInstance(42, typing.AbstractSet) - - def test_mutableset(self): - self.assertIsInstance(set(), typing.MutableSet) - self.assertNotIsInstance(frozenset(), typing.MutableSet) - - def test_mapping(self): - self.assertIsInstance({}, typing.Mapping) - self.assertNotIsInstance(42, typing.Mapping) - - def test_mutablemapping(self): - self.assertIsInstance({}, typing.MutableMapping) - self.assertNotIsInstance(42, typing.MutableMapping) - - def test_sequence(self): - self.assertIsInstance([], typing.Sequence) - self.assertNotIsInstance(42, typing.Sequence) - - def test_mutablesequence(self): - self.assertIsInstance([], typing.MutableSequence) - self.assertNotIsInstance((), typing.MutableSequence) - - def test_bytestring(self): - self.assertIsInstance(b'', typing.ByteString) - self.assertIsInstance(bytearray(b''), typing.ByteString) - - def test_list(self): - self.assertIsSubclass(list, typing.List) - - def test_deque(self): - self.assertIsSubclass(collections.deque, typing.Deque) - class MyDeque(typing.Deque[int]): ... - self.assertIsInstance(MyDeque(), collections.deque) - - def test_counter(self): - self.assertIsSubclass(collections.Counter, typing.Counter) - - def test_set(self): - self.assertIsSubclass(set, typing.Set) - self.assertNotIsSubclass(frozenset, typing.Set) - - def test_frozenset(self): - self.assertIsSubclass(frozenset, typing.FrozenSet) - self.assertNotIsSubclass(set, typing.FrozenSet) - - def test_dict(self): - self.assertIsSubclass(dict, typing.Dict) - - def test_no_list_instantiation(self): - with self.assertRaises(TypeError): - typing.List() - with self.assertRaises(TypeError): - typing.List[T]() - with self.assertRaises(TypeError): - typing.List[int]() - - def test_list_subclass(self): - - class MyList(typing.List[int]): - pass - - a = MyList() - self.assertIsInstance(a, MyList) - self.assertIsInstance(a, typing.Sequence) - - self.assertIsSubclass(MyList, list) - self.assertNotIsSubclass(list, MyList) - - def test_no_dict_instantiation(self): - with self.assertRaises(TypeError): - typing.Dict() - with self.assertRaises(TypeError): - typing.Dict[KT, VT]() - with self.assertRaises(TypeError): - typing.Dict[str, int]() - - def test_dict_subclass(self): - - class MyDict(typing.Dict[str, int]): - pass - - d = MyDict() - self.assertIsInstance(d, MyDict) - self.assertIsInstance(d, typing.MutableMapping) - - self.assertIsSubclass(MyDict, dict) - self.assertNotIsSubclass(dict, MyDict) - - def test_defaultdict_instantiation(self): - self.assertIs(type(typing.DefaultDict()), collections.defaultdict) - self.assertIs(type(typing.DefaultDict[KT, VT]()), collections.defaultdict) - self.assertIs(type(typing.DefaultDict[str, int]()), collections.defaultdict) - - def test_defaultdict_subclass(self): - - class MyDefDict(typing.DefaultDict[str, int]): - pass - - dd = MyDefDict() - self.assertIsInstance(dd, MyDefDict) - - self.assertIsSubclass(MyDefDict, collections.defaultdict) - self.assertNotIsSubclass(collections.defaultdict, MyDefDict) - - @skipUnless(sys.version_info >= (3, 3), 'ChainMap was added in 3.3') - def test_chainmap_instantiation(self): - self.assertIs(type(typing.ChainMap()), collections.ChainMap) - self.assertIs(type(typing.ChainMap[KT, VT]()), collections.ChainMap) - self.assertIs(type(typing.ChainMap[str, int]()), collections.ChainMap) - class CM(typing.ChainMap[KT, VT]): ... - self.assertIs(type(CM[int, str]()), CM) - - @skipUnless(sys.version_info >= (3, 3), 'ChainMap was added in 3.3') - def test_chainmap_subclass(self): - - class MyChainMap(typing.ChainMap[str, int]): - pass - - cm = MyChainMap() - self.assertIsInstance(cm, MyChainMap) - - self.assertIsSubclass(MyChainMap, collections.ChainMap) - self.assertNotIsSubclass(collections.ChainMap, MyChainMap) - - def test_deque_instantiation(self): - self.assertIs(type(typing.Deque()), collections.deque) - self.assertIs(type(typing.Deque[T]()), collections.deque) - self.assertIs(type(typing.Deque[int]()), collections.deque) - class D(typing.Deque[T]): ... - self.assertIs(type(D[int]()), D) - - def test_counter_instantiation(self): - self.assertIs(type(typing.Counter()), collections.Counter) - self.assertIs(type(typing.Counter[T]()), collections.Counter) - self.assertIs(type(typing.Counter[int]()), collections.Counter) - class C(typing.Counter[T]): ... - self.assertIs(type(C[int]()), C) - - def test_counter_subclass_instantiation(self): - - class MyCounter(typing.Counter[int]): - pass - - d = MyCounter() - self.assertIsInstance(d, MyCounter) - self.assertIsInstance(d, typing.Counter) - self.assertIsInstance(d, collections.Counter) - - def test_no_set_instantiation(self): - with self.assertRaises(TypeError): - typing.Set() - with self.assertRaises(TypeError): - typing.Set[T]() - with self.assertRaises(TypeError): - typing.Set[int]() - - def test_set_subclass_instantiation(self): - - class MySet(typing.Set[int]): - pass - - d = MySet() - self.assertIsInstance(d, MySet) - - def test_no_frozenset_instantiation(self): - with self.assertRaises(TypeError): - typing.FrozenSet() - with self.assertRaises(TypeError): - typing.FrozenSet[T]() - with self.assertRaises(TypeError): - typing.FrozenSet[int]() - - def test_frozenset_subclass_instantiation(self): - - class MyFrozenSet(typing.FrozenSet[int]): - pass - - d = MyFrozenSet() - self.assertIsInstance(d, MyFrozenSet) - - def test_no_tuple_instantiation(self): - with self.assertRaises(TypeError): - Tuple() - with self.assertRaises(TypeError): - Tuple[T]() - with self.assertRaises(TypeError): - Tuple[int]() - - def test_generator(self): - def foo(): - yield 42 - g = foo() - self.assertIsSubclass(type(g), typing.Generator) - - def test_no_generator_instantiation(self): - with self.assertRaises(TypeError): - typing.Generator() - with self.assertRaises(TypeError): - typing.Generator[T, T, T]() - with self.assertRaises(TypeError): - typing.Generator[int, int, int]() - - @skipUnless(PY36, 'Python 3.6 required') - def test_async_generator(self): - ns = {} - exec("async def f():\n" - " yield 42\n", globals(), ns) - g = ns['f']() - self.assertIsSubclass(type(g), typing.AsyncGenerator) - - @skipUnless(PY36, 'Python 3.6 required') - def test_no_async_generator_instantiation(self): - with self.assertRaises(TypeError): - typing.AsyncGenerator() - with self.assertRaises(TypeError): - typing.AsyncGenerator[T, T]() - with self.assertRaises(TypeError): - typing.AsyncGenerator[int, int]() - - def test_subclassing(self): - - class MMA(typing.MutableMapping): - pass - - with self.assertRaises(TypeError): # It's abstract - MMA() - - class MMC(MMA): - def __getitem__(self, k): - return None - def __setitem__(self, k, v): - pass - def __delitem__(self, k): - pass - def __iter__(self): - return iter(()) - def __len__(self): - return 0 - - self.assertEqual(len(MMC()), 0) - assert callable(MMC.update) - self.assertIsInstance(MMC(), typing.Mapping) - - class MMB(typing.MutableMapping[KT, VT]): - def __getitem__(self, k): - return None - def __setitem__(self, k, v): - pass - def __delitem__(self, k): - pass - def __iter__(self): - return iter(()) - def __len__(self): - return 0 - - self.assertEqual(len(MMB()), 0) - self.assertEqual(len(MMB[str, str]()), 0) - self.assertEqual(len(MMB[KT, VT]()), 0) - - self.assertNotIsSubclass(dict, MMA) - self.assertNotIsSubclass(dict, MMB) - - self.assertIsSubclass(MMA, typing.Mapping) - self.assertIsSubclass(MMB, typing.Mapping) - self.assertIsSubclass(MMC, typing.Mapping) - - self.assertIsInstance(MMB[KT, VT](), typing.Mapping) - self.assertIsInstance(MMB[KT, VT](), collections_abc.Mapping) - - self.assertIsSubclass(MMA, collections_abc.Mapping) - self.assertIsSubclass(MMB, collections_abc.Mapping) - self.assertIsSubclass(MMC, collections_abc.Mapping) - - self.assertIsSubclass(MMB[str, str], typing.Mapping) - self.assertIsSubclass(MMC, MMA) - - class It(typing.Iterable): ... - self.assertNotIsSubclass(list, It) - - class G(typing.Generator[int, int, int]): ... - def g(): yield 0 - self.assertIsSubclass(G, typing.Generator) - self.assertIsSubclass(G, typing.Iterable) - if hasattr(collections_abc, 'Generator'): - self.assertIsSubclass(G, collections_abc.Generator) - self.assertIsSubclass(G, collections_abc.Iterable) - self.assertNotIsSubclass(type(g), G) - - @skipUnless(PY36, 'Python 3.6 required') - def test_subclassing_async_generator(self): - class G(typing.AsyncGenerator[int, int]): - def asend(self, value): - pass - def athrow(self, typ, val=None, tb=None): - pass - - ns = {} - exec('async def g(): yield 0', globals(), ns) - g = ns['g'] - self.assertIsSubclass(G, typing.AsyncGenerator) - self.assertIsSubclass(G, typing.AsyncIterable) - self.assertIsSubclass(G, collections_abc.AsyncGenerator) - self.assertIsSubclass(G, collections_abc.AsyncIterable) - self.assertNotIsSubclass(type(g), G) - - instance = G() - self.assertIsInstance(instance, typing.AsyncGenerator) - self.assertIsInstance(instance, typing.AsyncIterable) - self.assertIsInstance(instance, collections_abc.AsyncGenerator) - self.assertIsInstance(instance, collections_abc.AsyncIterable) - self.assertNotIsInstance(type(g), G) - self.assertNotIsInstance(g, G) - - def test_subclassing_subclasshook(self): - - class Base(typing.Iterable): - @classmethod - def __subclasshook__(cls, other): - if other.__name__ == 'Foo': - return True - else: - return False - - class C(Base): ... - class Foo: ... - class Bar: ... - self.assertIsSubclass(Foo, Base) - self.assertIsSubclass(Foo, C) - self.assertNotIsSubclass(Bar, C) - - def test_subclassing_register(self): - - class A(typing.Container): ... - class B(A): ... - - class C: ... - A.register(C) - self.assertIsSubclass(C, A) - self.assertNotIsSubclass(C, B) - - class D: ... - B.register(D) - self.assertIsSubclass(D, A) - self.assertIsSubclass(D, B) - - class M(): ... - collections_abc.MutableMapping.register(M) - self.assertIsSubclass(M, typing.Mapping) - - def test_collections_as_base(self): - - class M(collections_abc.Mapping): ... - self.assertIsSubclass(M, typing.Mapping) - self.assertIsSubclass(M, typing.Iterable) - - class S(collections_abc.MutableSequence): ... - self.assertIsSubclass(S, typing.MutableSequence) - self.assertIsSubclass(S, typing.Iterable) - - class It(collections_abc.Iterable): ... - self.assertIsSubclass(It, typing.Iterable) - - class A(collections_abc.Mapping, metaclass=abc.ABCMeta): ... - class B: ... - A.register(B) - self.assertIsSubclass(B, typing.Mapping) - - -class OtherABCTests(BaseTestCase): - - def test_contextmanager(self): - @contextlib.contextmanager - def manager(): - yield 42 - - cm = manager() - self.assertIsInstance(cm, typing.ContextManager) - self.assertNotIsInstance(42, typing.ContextManager) - - @skipUnless(ASYNCIO, 'Python 3.5 required') - def test_async_contextmanager(self): - class NotACM: - pass - self.assertIsInstance(ACM(), typing.AsyncContextManager) - self.assertNotIsInstance(NotACM(), typing.AsyncContextManager) - @contextlib.contextmanager - def manager(): - yield 42 - - cm = manager() - self.assertNotIsInstance(cm, typing.AsyncContextManager) - self.assertEqual(typing.AsyncContextManager[int].__args__, (int,)) - with self.assertRaises(TypeError): - isinstance(42, typing.AsyncContextManager[int]) - with self.assertRaises(TypeError): - typing.AsyncContextManager[int, str] - - -class TypeTests(BaseTestCase): - - def test_type_basic(self): - - class User: pass - class BasicUser(User): pass - class ProUser(User): pass - - def new_user(user_class: Type[User]) -> User: - return user_class() - - new_user(BasicUser) - - def test_type_typevar(self): - - class User: pass - class BasicUser(User): pass - class ProUser(User): pass - - U = TypeVar('U', bound=User) - - def new_user(user_class: Type[U]) -> U: - return user_class() - - new_user(BasicUser) - - def test_type_optional(self): - A = Optional[Type[BaseException]] - - def foo(a: A) -> Optional[BaseException]: - if a is None: - return None - else: - return a() - - assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt) - assert foo(None) is None - - -class NewTypeTests(BaseTestCase): - - def test_basic(self): - UserId = NewType('UserId', int) - UserName = NewType('UserName', str) - self.assertIsInstance(UserId(5), int) - self.assertIsInstance(UserName('Joe'), str) - self.assertEqual(UserId(5) + 1, 6) - - def test_errors(self): - UserId = NewType('UserId', int) - UserName = NewType('UserName', str) - with self.assertRaises(TypeError): - issubclass(UserId, int) - with self.assertRaises(TypeError): - class D(UserName): - pass - - -class NamedTupleTests(BaseTestCase): - - def test_basics(self): - Emp = NamedTuple('Emp', [('name', str), ('id', int)]) - self.assertIsSubclass(Emp, tuple) - joe = Emp('Joe', 42) - jim = Emp(name='Jim', id=1) - self.assertIsInstance(joe, Emp) - self.assertIsInstance(joe, tuple) - self.assertEqual(joe.name, 'Joe') - self.assertEqual(joe.id, 42) - self.assertEqual(jim.name, 'Jim') - self.assertEqual(jim.id, 1) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp._fields, ('name', 'id')) - self.assertEqual(Emp.__annotations__, - collections.OrderedDict([('name', str), ('id', int)])) - self.assertIs(Emp._field_types, Emp.__annotations__) - - def test_namedtuple_pyversion(self): - if sys.version_info[:2] < (3, 6): - with self.assertRaises(TypeError): - NamedTuple('Name', one=int, other=str) - with self.assertRaises(TypeError): - class NotYet(NamedTuple): - whatever = 0 - - @skipUnless(PY36, 'Python 3.6 required') - def test_annotation_usage(self): - tim = CoolEmployee('Tim', 9000) - self.assertIsInstance(tim, CoolEmployee) - self.assertIsInstance(tim, tuple) - self.assertEqual(tim.name, 'Tim') - self.assertEqual(tim.cool, 9000) - self.assertEqual(CoolEmployee.__name__, 'CoolEmployee') - self.assertEqual(CoolEmployee._fields, ('name', 'cool')) - self.assertEqual(CoolEmployee.__annotations__, - collections.OrderedDict(name=str, cool=int)) - self.assertIs(CoolEmployee._field_types, CoolEmployee.__annotations__) - - @skipUnless(PY36, 'Python 3.6 required') - def test_annotation_usage_with_default(self): - jelle = CoolEmployeeWithDefault('Jelle') - self.assertIsInstance(jelle, CoolEmployeeWithDefault) - self.assertIsInstance(jelle, tuple) - self.assertEqual(jelle.name, 'Jelle') - self.assertEqual(jelle.cool, 0) - cooler_employee = CoolEmployeeWithDefault('Sjoerd', 1) - self.assertEqual(cooler_employee.cool, 1) - - self.assertEqual(CoolEmployeeWithDefault.__name__, 'CoolEmployeeWithDefault') - self.assertEqual(CoolEmployeeWithDefault._fields, ('name', 'cool')) - self.assertEqual(CoolEmployeeWithDefault._field_types, dict(name=str, cool=int)) - self.assertEqual(CoolEmployeeWithDefault._field_defaults, dict(cool=0)) - - with self.assertRaises(TypeError): - exec(""" -class NonDefaultAfterDefault(NamedTuple): - x: int = 3 - y: int -""") - - @skipUnless(PY36, 'Python 3.6 required') - def test_annotation_usage_with_methods(self): - self.assertEqual(XMeth(1).double(), 2) - self.assertEqual(XMeth(42).x, XMeth(42)[0]) - self.assertEqual(str(XRepr(42)), '42 -> 1') - self.assertEqual(XRepr(1, 2) + XRepr(3), 0) - - with self.assertRaises(AttributeError): - exec(""" -class XMethBad(NamedTuple): - x: int - def _fields(self): - return 'no chance for this' -""") - - with self.assertRaises(AttributeError): - exec(""" -class XMethBad2(NamedTuple): - x: int - def _source(self): - return 'no chance for this as well' -""") - - @skipUnless(PY36, 'Python 3.6 required') - def test_namedtuple_keyword_usage(self): - LocalEmployee = NamedTuple("LocalEmployee", name=str, age=int) - nick = LocalEmployee('Nick', 25) - self.assertIsInstance(nick, tuple) - self.assertEqual(nick.name, 'Nick') - self.assertEqual(LocalEmployee.__name__, 'LocalEmployee') - self.assertEqual(LocalEmployee._fields, ('name', 'age')) - self.assertEqual(LocalEmployee.__annotations__, dict(name=str, age=int)) - self.assertIs(LocalEmployee._field_types, LocalEmployee.__annotations__) - with self.assertRaises(TypeError): - NamedTuple('Name', [('x', int)], y=str) - with self.assertRaises(TypeError): - NamedTuple('Name', x=1, y='a') - - @skipUnless(PY36, 'Python 3.6 required') - def test_namedtuple_special_keyword_names(self): - NT = NamedTuple("NT", cls=type, self=object, typename=str, fields=list) - self.assertEqual(NT.__name__, 'NT') - self.assertEqual(NT._fields, ('cls', 'self', 'typename', 'fields')) - a = NT(cls=str, self=42, typename='foo', fields=[('bar', tuple)]) - self.assertEqual(a.cls, str) - self.assertEqual(a.self, 42) - self.assertEqual(a.typename, 'foo') - self.assertEqual(a.fields, [('bar', tuple)]) - - @skipUnless(PY36, 'Python 3.6 required') - def test_namedtuple_errors(self): - with self.assertRaises(TypeError): - NamedTuple.__new__() - with self.assertRaises(TypeError): - NamedTuple() - with self.assertRaises(TypeError): - NamedTuple('Emp', [('name', str)], None) - with self.assertRaises(ValueError): - NamedTuple('Emp', [('_name', str)]) - - with self.assertWarns(DeprecationWarning): - Emp = NamedTuple(typename='Emp', name=str, id=int) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp._fields, ('name', 'id')) - - with self.assertWarns(DeprecationWarning): - Emp = NamedTuple('Emp', fields=[('name', str), ('id', int)]) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp._fields, ('name', 'id')) - - def test_pickle(self): - global Emp # pickle wants to reference the class by name - Emp = NamedTuple('Emp', [('name', str), ('id', int)]) - jane = Emp('jane', 37) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) - - -class IOTests(BaseTestCase): - - def test_io(self): - - def stuff(a: IO) -> AnyStr: - return a.readline() - - a = stuff.__annotations__['a'] - self.assertEqual(a.__parameters__, (AnyStr,)) - - def test_textio(self): - - def stuff(a: TextIO) -> str: - return a.readline() - - a = stuff.__annotations__['a'] - self.assertEqual(a.__parameters__, ()) - - def test_binaryio(self): - - def stuff(a: BinaryIO) -> bytes: - return a.readline() - - a = stuff.__annotations__['a'] - self.assertEqual(a.__parameters__, ()) - - def test_io_submodule(self): - from typing.io import IO, TextIO, BinaryIO, __all__, __name__ - self.assertIs(IO, typing.IO) - self.assertIs(TextIO, typing.TextIO) - self.assertIs(BinaryIO, typing.BinaryIO) - self.assertEqual(set(__all__), set(['IO', 'TextIO', 'BinaryIO'])) - self.assertEqual(__name__, 'typing.io') - - -class RETests(BaseTestCase): - # Much of this is really testing _TypeAlias. - - def test_basics(self): - pat = re.compile('[a-z]+', re.I) - self.assertIsSubclass(pat.__class__, Pattern) - self.assertIsSubclass(type(pat), Pattern) - self.assertIsInstance(pat, Pattern) - - mat = pat.search('12345abcde.....') - self.assertIsSubclass(mat.__class__, Match) - self.assertIsSubclass(type(mat), Match) - self.assertIsInstance(mat, Match) - - # these should just work - Pattern[Union[str, bytes]] - Match[Union[bytes, str]] - - def test_alias_equality(self): - self.assertEqual(Pattern[str], Pattern[str]) - self.assertNotEqual(Pattern[str], Pattern[bytes]) - self.assertNotEqual(Pattern[str], Match[str]) - self.assertNotEqual(Pattern[str], str) - - def test_errors(self): - with self.assertRaises(TypeError): - # Doesn't fit AnyStr. - Pattern[int] - with self.assertRaises(TypeError): - # Can't change type vars? - Match[T] - m = Match[Union[str, bytes]] - with self.assertRaises(TypeError): - # Too complicated? - m[str] - with self.assertRaises(TypeError): - # We don't support isinstance(). - isinstance(42, Pattern[str]) - with self.assertRaises(TypeError): - # We don't support issubclass(). - issubclass(Pattern[bytes], Pattern[str]) - - def test_repr(self): - self.assertEqual(repr(Pattern), 'Pattern[~AnyStr]') - self.assertEqual(repr(Pattern[str]), 'Pattern[str]') - self.assertEqual(repr(Pattern[bytes]), 'Pattern[bytes]') - self.assertEqual(repr(Match), 'Match[~AnyStr]') - self.assertEqual(repr(Match[str]), 'Match[str]') - self.assertEqual(repr(Match[bytes]), 'Match[bytes]') - - def test_re_submodule(self): - from typing.re import Match, Pattern, __all__, __name__ - self.assertIs(Match, typing.Match) - self.assertIs(Pattern, typing.Pattern) - self.assertEqual(set(__all__), set(['Match', 'Pattern'])) - self.assertEqual(__name__, 'typing.re') - - def test_cannot_subclass(self): - with self.assertRaises(TypeError) as ex: - - class A(typing.Match): - pass - - self.assertEqual(str(ex.exception), - "Cannot subclass typing._TypeAlias") - - -class AllTests(BaseTestCase): - """Tests for __all__.""" - - def test_all(self): - from typing import __all__ as a - # Just spot-check the first and last of every category. - self.assertIn('AbstractSet', a) - self.assertIn('ValuesView', a) - self.assertIn('cast', a) - self.assertIn('overload', a) - if hasattr(contextlib, 'AbstractContextManager'): - self.assertIn('ContextManager', a) - # Check that io and re are not exported. - self.assertNotIn('io', a) - self.assertNotIn('re', a) - # Spot-check that stdlib modules aren't exported. - self.assertNotIn('os', a) - self.assertNotIn('sys', a) - # Check that Text is defined. - self.assertIn('Text', a) - # Check previously missing classes. - self.assertIn('SupportsBytes', a) - self.assertIn('SupportsComplex', a) - - def test_typing_compiles_with_opt(self): - file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'typing.py') - try: - subprocess.check_output([sys.executable, '-OO', file_path], - stderr=subprocess.STDOUT) - except subprocess.CalledProcessError: - self.fail('Module does not compile with optimize=2 (-OO flag).') - - -if __name__ == '__main__': - main() diff --git a/src/typing.py b/src/typing.py deleted file mode 100644 index 70c59d851..000000000 --- a/src/typing.py +++ /dev/null @@ -1,2454 +0,0 @@ -import abc -from abc import abstractmethod, abstractproperty -import collections -import contextlib -import functools -import re as stdlib_re # Avoid confusion with the re we export. -import sys -import types -try: - import collections.abc as collections_abc -except ImportError: - import collections as collections_abc # Fallback for PY3.2. -if sys.version_info[:2] >= (3, 6): - import _collections_abc # Needed for private function _check_methods # noqa -try: - from types import WrapperDescriptorType, MethodWrapperType, MethodDescriptorType -except ImportError: - WrapperDescriptorType = type(object.__init__) - MethodWrapperType = type(object().__str__) - MethodDescriptorType = type(str.join) - - -# Please keep __all__ alphabetized within each category. -__all__ = [ - # Super-special typing primitives. - 'Any', - 'Callable', - 'ClassVar', - 'Generic', - 'Optional', - 'Tuple', - 'Type', - 'TypeVar', - 'Union', - - # ABCs (from collections.abc). - 'AbstractSet', # collections.abc.Set. - 'GenericMeta', # subclass of abc.ABCMeta and a metaclass - # for 'Generic' and ABCs below. - 'ByteString', - 'Container', - 'ContextManager', - 'Hashable', - 'ItemsView', - 'Iterable', - 'Iterator', - 'KeysView', - 'Mapping', - 'MappingView', - 'MutableMapping', - 'MutableSequence', - 'MutableSet', - 'Sequence', - 'Sized', - 'ValuesView', - # The following are added depending on presence - # of their non-generic counterparts in stdlib: - # Awaitable, - # AsyncIterator, - # AsyncIterable, - # Coroutine, - # Collection, - # AsyncGenerator, - # AsyncContextManager - - # Structural checks, a.k.a. protocols. - 'Reversible', - 'SupportsAbs', - 'SupportsBytes', - 'SupportsComplex', - 'SupportsFloat', - 'SupportsIndex', - 'SupportsInt', - 'SupportsRound', - - # Concrete collection types. - 'Counter', - 'Deque', - 'Dict', - 'DefaultDict', - 'List', - 'Set', - 'FrozenSet', - 'NamedTuple', # Not really a type. - 'Generator', - - # One-off things. - 'AnyStr', - 'cast', - 'get_type_hints', - 'NewType', - 'no_type_check', - 'no_type_check_decorator', - 'NoReturn', - 'overload', - 'Text', - 'TYPE_CHECKING', -] - -# The pseudo-submodules 're' and 'io' are part of the public -# namespace, but excluded from __all__ because they might stomp on -# legitimate imports of those modules. - - -def _qualname(x): - if sys.version_info[:2] >= (3, 3): - return x.__qualname__ - else: - # Fall back to just name. - return x.__name__ - - -def _trim_name(nm): - whitelist = ('_TypeAlias', '_ForwardRef', '_TypingBase', '_FinalTypingBase') - if nm.startswith('_') and nm not in whitelist: - nm = nm[1:] - return nm - - -class TypingMeta(type): - """Metaclass for most types defined in typing module - (not a part of public API). - - This overrides __new__() to require an extra keyword parameter - '_root', which serves as a guard against naive subclassing of the - typing classes. Any legitimate class defined using a metaclass - derived from TypingMeta must pass _root=True. - - This also defines a dummy constructor (all the work for most typing - constructs is done in __new__) and a nicer repr(). - """ - - _is_protocol = False - - def __new__(cls, name, bases, namespace, *, _root=False): - if not _root: - raise TypeError("Cannot subclass %s" % - (', '.join(map(_type_repr, bases)) or '()')) - return super().__new__(cls, name, bases, namespace) - - def __init__(self, *args, **kwds): - pass - - def _eval_type(self, globalns, localns): - """Override this in subclasses to interpret forward references. - - For example, List['C'] is internally stored as - List[_ForwardRef('C')], which should evaluate to List[C], - where C is an object found in globalns or localns (searching - localns first, of course). - """ - return self - - def _get_type_vars(self, tvars): - pass - - def __repr__(self): - qname = _trim_name(_qualname(self)) - return '%s.%s' % (self.__module__, qname) - - -class _TypingBase(metaclass=TypingMeta, _root=True): - """Internal indicator of special typing constructs.""" - - __slots__ = ('__weakref__',) - - def __init__(self, *args, **kwds): - pass - - def __new__(cls, *args, **kwds): - """Constructor. - - This only exists to give a better error message in case - someone tries to subclass a special typing object (not a good idea). - """ - if (len(args) == 3 and - isinstance(args[0], str) and - isinstance(args[1], tuple)): - # Close enough. - raise TypeError("Cannot subclass %r" % cls) - return super().__new__(cls) - - # Things that are not classes also need these. - def _eval_type(self, globalns, localns): - return self - - def _get_type_vars(self, tvars): - pass - - def __repr__(self): - cls = type(self) - qname = _trim_name(_qualname(cls)) - return '%s.%s' % (cls.__module__, qname) - - def __call__(self, *args, **kwds): - raise TypeError("Cannot instantiate %r" % type(self)) - - -class _FinalTypingBase(_TypingBase, _root=True): - """Internal mix-in class to prevent instantiation. - - Prevents instantiation unless _root=True is given in class call. - It is used to create pseudo-singleton instances Any, Union, Optional, etc. - """ - - __slots__ = () - - def __new__(cls, *args, _root=False, **kwds): - self = super().__new__(cls, *args, **kwds) - if _root is True: - return self - raise TypeError("Cannot instantiate %r" % cls) - - def __reduce__(self): - return _trim_name(type(self).__name__) - - -class _ForwardRef(_TypingBase, _root=True): - """Internal wrapper to hold a forward reference.""" - - __slots__ = ('__forward_arg__', '__forward_code__', - '__forward_evaluated__', '__forward_value__') - - def __init__(self, arg): - super().__init__(arg) - if not isinstance(arg, str): - raise TypeError('Forward reference must be a string -- got %r' % (arg,)) - try: - code = compile(arg, '', 'eval') - except SyntaxError: - raise SyntaxError('Forward reference must be an expression -- got %r' % - (arg,)) - self.__forward_arg__ = arg - self.__forward_code__ = code - self.__forward_evaluated__ = False - self.__forward_value__ = None - - def _eval_type(self, globalns, localns): - if not self.__forward_evaluated__ or localns is not globalns: - if globalns is None and localns is None: - globalns = localns = {} - elif globalns is None: - globalns = localns - elif localns is None: - localns = globalns - self.__forward_value__ = _type_check( - eval(self.__forward_code__, globalns, localns), - "Forward references must evaluate to types.") - self.__forward_evaluated__ = True - return self.__forward_value__ - - def __eq__(self, other): - if not isinstance(other, _ForwardRef): - return NotImplemented - if self.__forward_evaluated__ and other.__forward_evaluated__: - return (self.__forward_arg__ == other.__forward_arg__ and - self.__forward_value__ == other.__forward_value__) - return self.__forward_arg__ == other.__forward_arg__ - - def __hash__(self): - return hash(self.__forward_arg__) - - def __instancecheck__(self, obj): - raise TypeError("Forward references cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Forward references cannot be used with issubclass().") - - def __repr__(self): - return '_ForwardRef(%r)' % (self.__forward_arg__,) - - -class _TypeAlias(_TypingBase, _root=True): - """Internal helper class for defining generic variants of concrete types. - - Note that this is not a type; let's call it a pseudo-type. It cannot - be used in instance and subclass checks in parameterized form, i.e. - ``isinstance(42, Match[str])`` raises ``TypeError`` instead of returning - ``False``. - """ - - __slots__ = ('name', 'type_var', 'impl_type', 'type_checker') - - def __init__(self, name, type_var, impl_type, type_checker): - """Initializer. - - Args: - name: The name, e.g. 'Pattern'. - type_var: The type parameter, e.g. AnyStr, or the - specific type, e.g. str. - impl_type: The implementation type. - type_checker: Function that takes an impl_type instance. - and returns a value that should be a type_var instance. - """ - assert isinstance(name, str), repr(name) - assert isinstance(impl_type, type), repr(impl_type) - assert not isinstance(impl_type, TypingMeta), repr(impl_type) - assert isinstance(type_var, (type, _TypingBase)), repr(type_var) - self.name = name - self.type_var = type_var - self.impl_type = impl_type - self.type_checker = type_checker - - def __repr__(self): - return "%s[%s]" % (self.name, _type_repr(self.type_var)) - - def __getitem__(self, parameter): - if not isinstance(self.type_var, TypeVar): - raise TypeError("%s cannot be further parameterized." % self) - if self.type_var.__constraints__ and isinstance(parameter, type): - if not issubclass(parameter, self.type_var.__constraints__): - raise TypeError("%s is not a valid substitution for %s." % - (parameter, self.type_var)) - if isinstance(parameter, TypeVar) and parameter is not self.type_var: - raise TypeError("%s cannot be re-parameterized." % self) - return self.__class__(self.name, parameter, - self.impl_type, self.type_checker) - - def __eq__(self, other): - if not isinstance(other, _TypeAlias): - return NotImplemented - return self.name == other.name and self.type_var == other.type_var - - def __hash__(self): - return hash((self.name, self.type_var)) - - def __instancecheck__(self, obj): - if not isinstance(self.type_var, TypeVar): - raise TypeError("Parameterized type aliases cannot be used " - "with isinstance().") - return isinstance(obj, self.impl_type) - - def __subclasscheck__(self, cls): - if not isinstance(self.type_var, TypeVar): - raise TypeError("Parameterized type aliases cannot be used " - "with issubclass().") - return issubclass(cls, self.impl_type) - - -def _get_type_vars(types, tvars): - for t in types: - if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): - t._get_type_vars(tvars) - - -def _type_vars(types): - tvars = [] - _get_type_vars(types, tvars) - return tuple(tvars) - - -def _eval_type(t, globalns, localns): - if isinstance(t, TypingMeta) or isinstance(t, _TypingBase): - return t._eval_type(globalns, localns) - return t - - -def _type_check(arg, msg): - """Check that the argument is a type, and return it (internal helper). - - As a special case, accept None and return type(None) instead. - Also, _TypeAlias instances (e.g. Match, Pattern) are acceptable. - - The msg argument is a human-readable error message, e.g. - - "Union[arg, ...]: arg should be a type." - - We append the repr() of the actual value (truncated to 100 chars). - """ - if arg is None: - return type(None) - if isinstance(arg, str): - arg = _ForwardRef(arg) - if ( - isinstance(arg, _TypingBase) and type(arg).__name__ == '_ClassVar' or - not isinstance(arg, (type, _TypingBase)) and not callable(arg) - ): - raise TypeError(msg + " Got %.100r." % (arg,)) - # Bare Union etc. are not valid as type arguments - if ( - type(arg).__name__ in ('_Union', '_Optional') and - not getattr(arg, '__origin__', None) or - isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol) - ): - raise TypeError("Plain %s is not valid as type argument" % arg) - return arg - - -def _type_repr(obj): - """Return the repr() of an object, special-casing types (internal helper). - - If obj is a type, we return a shorter version than the default - type.__repr__, based on the module and qualified name, which is - typically enough to uniquely identify a type. For everything - else, we fall back on repr(obj). - """ - if isinstance(obj, type) and not isinstance(obj, TypingMeta): - if obj.__module__ == 'builtins': - return _qualname(obj) - return '%s.%s' % (obj.__module__, _qualname(obj)) - if obj is ...: - return('...') - if isinstance(obj, types.FunctionType): - return obj.__name__ - return repr(obj) - - -class _Any(_FinalTypingBase, _root=True): - """Special type indicating an unconstrained type. - - - Any is compatible with every type. - - Any assumed to have all methods. - - All values assumed to be instances of Any. - - Note that all the above statements are true from the point of view of - static type checkers. At runtime, Any should not be used with instance - or class checks. - """ - - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("Any cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Any cannot be used with issubclass().") - - -Any = _Any(_root=True) - - -class _NoReturn(_FinalTypingBase, _root=True): - """Special type indicating functions that never return. - Example:: - - from typing import NoReturn - - def stop() -> NoReturn: - raise Exception('no way') - - This type is invalid in other positions, e.g., ``List[NoReturn]`` - will fail in static type checkers. - """ - - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("NoReturn cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("NoReturn cannot be used with issubclass().") - - -NoReturn = _NoReturn(_root=True) - - -class TypeVar(_TypingBase, _root=True): - """Type variable. - - Usage:: - - T = TypeVar('T') # Can be anything - A = TypeVar('A', str, bytes) # Must be str or bytes - - Type variables exist primarily for the benefit of static type - checkers. They serve as the parameters for generic types as well - as for generic function definitions. See class Generic for more - information on generic types. Generic functions work as follows: - - def repeat(x: T, n: int) -> List[T]: - '''Return a list containing n references to x.''' - return [x]*n - - def longest(x: A, y: A) -> A: - '''Return the longest of two strings.''' - return x if len(x) >= len(y) else y - - The latter example's signature is essentially the overloading - of (str, str) -> str and (bytes, bytes) -> bytes. Also note - that if the arguments are instances of some subclass of str, - the return type is still plain str. - - At runtime, isinstance(x, T) and issubclass(C, T) will raise TypeError. - - Type variables defined with covariant=True or contravariant=True - can be used do declare covariant or contravariant generic types. - See PEP 484 for more details. By default generic types are invariant - in all type variables. - - Type variables can be introspected. e.g.: - - T.__name__ == 'T' - T.__constraints__ == () - T.__covariant__ == False - T.__contravariant__ = False - A.__constraints__ == (str, bytes) - """ - - __slots__ = ('__name__', '__bound__', '__constraints__', - '__covariant__', '__contravariant__') - - def __init__(self, name, *constraints, bound=None, - covariant=False, contravariant=False): - super().__init__(name, *constraints, bound=bound, - covariant=covariant, contravariant=contravariant) - self.__name__ = name - if covariant and contravariant: - raise ValueError("Bivariant types are not supported.") - self.__covariant__ = bool(covariant) - self.__contravariant__ = bool(contravariant) - if constraints and bound is not None: - raise TypeError("Constraints cannot be combined with bound=...") - if constraints and len(constraints) == 1: - raise TypeError("A single constraint is not allowed") - msg = "TypeVar(name, constraint, ...): constraints must be types." - self.__constraints__ = tuple(_type_check(t, msg) for t in constraints) - if bound: - self.__bound__ = _type_check(bound, "Bound must be a type.") - else: - self.__bound__ = None - - def _get_type_vars(self, tvars): - if self not in tvars: - tvars.append(self) - - def __repr__(self): - if self.__covariant__: - prefix = '+' - elif self.__contravariant__: - prefix = '-' - else: - prefix = '~' - return prefix + self.__name__ - - def __instancecheck__(self, instance): - raise TypeError("Type variables cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Type variables cannot be used with issubclass().") - - -# Some unconstrained type variables. These are used by the container types. -# (These are not for export.) -T = TypeVar('T') # Any type. -KT = TypeVar('KT') # Key type. -VT = TypeVar('VT') # Value type. -T_co = TypeVar('T_co', covariant=True) # Any type covariant containers. -V_co = TypeVar('V_co', covariant=True) # Any type covariant containers. -VT_co = TypeVar('VT_co', covariant=True) # Value type covariant containers. -T_contra = TypeVar('T_contra', contravariant=True) # Ditto contravariant. - -# A useful type variable with constraints. This represents string types. -# (This one *is* for export!) -AnyStr = TypeVar('AnyStr', bytes, str) - - -def _replace_arg(arg, tvars, args): - """An internal helper function: replace arg if it is a type variable - found in tvars with corresponding substitution from args or - with corresponding substitution sub-tree if arg is a generic type. - """ - - if tvars is None: - tvars = [] - if hasattr(arg, '_subs_tree') and isinstance(arg, (GenericMeta, _TypingBase)): - return arg._subs_tree(tvars, args) - if isinstance(arg, TypeVar): - for i, tvar in enumerate(tvars): - if arg == tvar: - return args[i] - return arg - - -# Special typing constructs Union, Optional, Generic, Callable and Tuple -# use three special attributes for internal bookkeeping of generic types: -# * __parameters__ is a tuple of unique free type parameters of a generic -# type, for example, Dict[T, T].__parameters__ == (T,); -# * __origin__ keeps a reference to a type that was subscripted, -# e.g., Union[T, int].__origin__ == Union; -# * __args__ is a tuple of all arguments used in subscripting, -# e.g., Dict[T, int].__args__ == (T, int). - - -def _subs_tree(cls, tvars=None, args=None): - """An internal helper function: calculate substitution tree - for generic cls after replacing its type parameters with - substitutions in tvars -> args (if any). - Repeat the same following __origin__'s. - - Return a list of arguments with all possible substitutions - performed. Arguments that are generic classes themselves are represented - as tuples (so that no new classes are created by this function). - For example: _subs_tree(List[Tuple[int, T]][str]) == [(Tuple, int, str)] - """ - - if cls.__origin__ is None: - return cls - # Make of chain of origins (i.e. cls -> cls.__origin__) - current = cls.__origin__ - orig_chain = [] - while current.__origin__ is not None: - orig_chain.append(current) - current = current.__origin__ - # Replace type variables in __args__ if asked ... - tree_args = [] - for arg in cls.__args__: - tree_args.append(_replace_arg(arg, tvars, args)) - # ... then continue replacing down the origin chain. - for ocls in orig_chain: - new_tree_args = [] - for arg in ocls.__args__: - new_tree_args.append(_replace_arg(arg, ocls.__parameters__, tree_args)) - tree_args = new_tree_args - return tree_args - - -def _remove_dups_flatten(parameters): - """An internal helper for Union creation and substitution: flatten Union's - among parameters, then remove duplicates and strict subclasses. - """ - - # Flatten out Union[Union[...], ...]. - params = [] - for p in parameters: - if isinstance(p, _Union) and p.__origin__ is Union: - params.extend(p.__args__) - elif isinstance(p, tuple) and len(p) > 0 and p[0] is Union: - params.extend(p[1:]) - else: - params.append(p) - # Weed out strict duplicates, preserving the first of each occurrence. - all_params = set(params) - if len(all_params) < len(params): - new_params = [] - for t in params: - if t in all_params: - new_params.append(t) - all_params.remove(t) - params = new_params - assert not all_params, all_params - # Weed out subclasses. - # E.g. Union[int, Employee, Manager] == Union[int, Employee]. - # If object is present it will be sole survivor among proper classes. - # Never discard type variables. - # (In particular, Union[str, AnyStr] != AnyStr.) - all_params = set(params) - for t1 in params: - if not isinstance(t1, type): - continue - if any(isinstance(t2, type) and issubclass(t1, t2) - for t2 in all_params - {t1} - if not (isinstance(t2, GenericMeta) and - t2.__origin__ is not None)): - all_params.remove(t1) - return tuple(t for t in params if t in all_params) - - -def _check_generic(cls, parameters): - # Check correct count for parameters of a generic cls (internal helper). - if not cls.__parameters__: - raise TypeError("%s is not a generic class" % repr(cls)) - alen = len(parameters) - elen = len(cls.__parameters__) - if alen != elen: - raise TypeError("Too %s parameters for %s; actual %s, expected %s" % - ("many" if alen > elen else "few", repr(cls), alen, elen)) - - -_cleanups = [] - - -def _tp_cache(func): - """Internal wrapper caching __getitem__ of generic types with a fallback to - original function for non-hashable arguments. - """ - - cached = functools.lru_cache()(func) - _cleanups.append(cached.cache_clear) - - @functools.wraps(func) - def inner(*args, **kwds): - try: - return cached(*args, **kwds) - except TypeError: - pass # All real errors (not unhashable args) are raised below. - return func(*args, **kwds) - return inner - - -class _Union(_FinalTypingBase, _root=True): - """Union type; Union[X, Y] means either X or Y. - - To define a union, use e.g. Union[int, str]. Details: - - - The arguments must be types and there must be at least one. - - - None as an argument is a special case and is replaced by - type(None). - - - Unions of unions are flattened, e.g.:: - - Union[Union[int, str], float] == Union[int, str, float] - - - Unions of a single argument vanish, e.g.:: - - Union[int] == int # The constructor actually returns int - - - Redundant arguments are skipped, e.g.:: - - Union[int, str, int] == Union[int, str] - - - When comparing unions, the argument order is ignored, e.g.:: - - Union[int, str] == Union[str, int] - - - When two arguments have a subclass relationship, the least - derived argument is kept, e.g.:: - - class Employee: pass - class Manager(Employee): pass - Union[int, Employee, Manager] == Union[int, Employee] - Union[Manager, int, Employee] == Union[int, Employee] - Union[Employee, Manager] == Employee - - - Similar for object:: - - Union[int, object] == object - - - You cannot subclass or instantiate a union. - - - You can use Optional[X] as a shorthand for Union[X, None]. - """ - - __slots__ = ('__parameters__', '__args__', '__origin__', '__tree_hash__') - - def __new__(cls, parameters=None, origin=None, *args, _root=False): - self = super().__new__(cls, parameters, origin, *args, _root=_root) - if origin is None: - self.__parameters__ = None - self.__args__ = None - self.__origin__ = None - self.__tree_hash__ = hash(frozenset(('Union',))) - return self - if not isinstance(parameters, tuple): - raise TypeError("Expected parameters=") - if origin is Union: - parameters = _remove_dups_flatten(parameters) - # It's not a union if there's only one type left. - if len(parameters) == 1: - return parameters[0] - self.__parameters__ = _type_vars(parameters) - self.__args__ = parameters - self.__origin__ = origin - # Pre-calculate the __hash__ on instantiation. - # This improves speed for complex substitutions. - subs_tree = self._subs_tree() - if isinstance(subs_tree, tuple): - self.__tree_hash__ = hash(frozenset(subs_tree)) - else: - self.__tree_hash__ = hash(subs_tree) - return self - - def _eval_type(self, globalns, localns): - if self.__args__ is None: - return self - ev_args = tuple(_eval_type(t, globalns, localns) for t in self.__args__) - ev_origin = _eval_type(self.__origin__, globalns, localns) - if ev_args == self.__args__ and ev_origin == self.__origin__: - # Everything is already evaluated. - return self - return self.__class__(ev_args, ev_origin, _root=True) - - def _get_type_vars(self, tvars): - if self.__origin__ and self.__parameters__: - _get_type_vars(self.__parameters__, tvars) - - def __repr__(self): - if self.__origin__ is None: - return super().__repr__() - tree = self._subs_tree() - if not isinstance(tree, tuple): - return repr(tree) - return tree[0]._tree_repr(tree) - - def _tree_repr(self, tree): - arg_list = [] - for arg in tree[1:]: - if not isinstance(arg, tuple): - arg_list.append(_type_repr(arg)) - else: - arg_list.append(arg[0]._tree_repr(arg)) - return super().__repr__() + '[%s]' % ', '.join(arg_list) - - @_tp_cache - def __getitem__(self, parameters): - if parameters == (): - raise TypeError("Cannot take a Union of no types.") - if not isinstance(parameters, tuple): - parameters = (parameters,) - if self.__origin__ is None: - msg = "Union[arg, ...]: each arg must be a type." - else: - msg = "Parameters to generic types must be types." - parameters = tuple(_type_check(p, msg) for p in parameters) - if self is not Union: - _check_generic(self, parameters) - return self.__class__(parameters, origin=self, _root=True) - - def _subs_tree(self, tvars=None, args=None): - if self is Union: - return Union # Nothing to substitute - tree_args = _subs_tree(self, tvars, args) - tree_args = _remove_dups_flatten(tree_args) - if len(tree_args) == 1: - return tree_args[0] # Union of a single type is that type - return (Union,) + tree_args - - def __eq__(self, other): - if isinstance(other, _Union): - return self.__tree_hash__ == other.__tree_hash__ - elif self is not Union: - return self._subs_tree() == other - else: - return self is other - - def __hash__(self): - return self.__tree_hash__ - - def __instancecheck__(self, obj): - raise TypeError("Unions cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Unions cannot be used with issubclass().") - - -Union = _Union(_root=True) - - -class _Optional(_FinalTypingBase, _root=True): - """Optional type. - - Optional[X] is equivalent to Union[X, None]. - """ - - __slots__ = () - - @_tp_cache - def __getitem__(self, arg): - arg = _type_check(arg, "Optional[t] requires a single type.") - return Union[arg, type(None)] - - -Optional = _Optional(_root=True) - - -def _next_in_mro(cls): - """Helper for Generic.__new__. - - Returns the class after the last occurrence of Generic or - Generic[...] in cls.__mro__. - """ - next_in_mro = object - # Look for the last occurrence of Generic or Generic[...]. - for i, c in enumerate(cls.__mro__[:-1]): - if isinstance(c, GenericMeta) and c._gorg is Generic: - next_in_mro = cls.__mro__[i + 1] - return next_in_mro - - -def _make_subclasshook(cls): - """Construct a __subclasshook__ callable that incorporates - the associated __extra__ class in subclass checks performed - against cls. - """ - if isinstance(cls.__extra__, abc.ABCMeta): - # The logic mirrors that of ABCMeta.__subclasscheck__. - # Registered classes need not be checked here because - # cls and its extra share the same _abc_registry. - def __extrahook__(subclass): - res = cls.__extra__.__subclasshook__(subclass) - if res is not NotImplemented: - return res - if cls.__extra__ in subclass.__mro__: - return True - for scls in cls.__extra__.__subclasses__(): - if isinstance(scls, GenericMeta): - continue - if issubclass(subclass, scls): - return True - return NotImplemented - else: - # For non-ABC extras we'll just call issubclass(). - def __extrahook__(subclass): - if cls.__extra__ and issubclass(subclass, cls.__extra__): - return True - return NotImplemented - return __extrahook__ - - -def _no_slots_copy(dct): - """Internal helper: copy class __dict__ and clean slots class variables. - (They will be re-created if necessary by normal class machinery.) - """ - dict_copy = dict(dct) - if '__slots__' in dict_copy: - for slot in dict_copy['__slots__']: - dict_copy.pop(slot, None) - return dict_copy - - -class GenericMeta(TypingMeta, abc.ABCMeta): - """Metaclass for generic types. - - This is a metaclass for typing.Generic and generic ABCs defined in - typing module. User defined subclasses of GenericMeta can override - __new__ and invoke super().__new__. Note that GenericMeta.__new__ - has strict rules on what is allowed in its bases argument: - * plain Generic is disallowed in bases; - * Generic[...] should appear in bases at most once; - * if Generic[...] is present, then it should list all type variables - that appear in other bases. - In addition, type of all generic bases is erased, e.g., C[int] is - stripped to plain C. - """ - - def __new__(cls, name, bases, namespace, - tvars=None, args=None, origin=None, extra=None, orig_bases=None): - """Create a new generic class. GenericMeta.__new__ accepts - keyword arguments that are used for internal bookkeeping, therefore - an override should pass unused keyword arguments to super(). - """ - if tvars is not None: - # Called from __getitem__() below. - assert origin is not None - assert all(isinstance(t, TypeVar) for t in tvars), tvars - else: - # Called from class statement. - assert tvars is None, tvars - assert args is None, args - assert origin is None, origin - - # Get the full set of tvars from the bases. - tvars = _type_vars(bases) - # Look for Generic[T1, ..., Tn]. - # If found, tvars must be a subset of it. - # If not found, tvars is it. - # Also check for and reject plain Generic, - # and reject multiple Generic[...]. - gvars = None - for base in bases: - if base is Generic: - raise TypeError("Cannot inherit from plain Generic") - if (isinstance(base, GenericMeta) and - base.__origin__ is Generic): - if gvars is not None: - raise TypeError( - "Cannot inherit from Generic[...] multiple types.") - gvars = base.__parameters__ - if gvars is None: - gvars = tvars - else: - tvarset = set(tvars) - gvarset = set(gvars) - if not tvarset <= gvarset: - raise TypeError( - "Some type variables (%s) " - "are not listed in Generic[%s]" % - (", ".join(str(t) for t in tvars if t not in gvarset), - ", ".join(str(g) for g in gvars))) - tvars = gvars - - initial_bases = bases - if extra is not None and type(extra) is abc.ABCMeta and extra not in bases: - bases = (extra,) + bases - bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases) - - # remove bare Generic from bases if there are other generic bases - if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): - bases = tuple(b for b in bases if b is not Generic) - namespace.update({'__origin__': origin, '__extra__': extra, - '_gorg': None if not origin else origin._gorg}) - self = super().__new__(cls, name, bases, namespace, _root=True) - super(GenericMeta, self).__setattr__('_gorg', - self if not origin else origin._gorg) - self.__parameters__ = tvars - # Be prepared that GenericMeta will be subclassed by TupleMeta - # and CallableMeta, those two allow ..., (), or [] in __args___. - self.__args__ = tuple(... if a is _TypingEllipsis else - () if a is _TypingEmpty else - a for a in args) if args else None - # Speed hack (https://github.com/python/typing/issues/196). - self.__next_in_mro__ = _next_in_mro(self) - # Preserve base classes on subclassing (__bases__ are type erased now). - if orig_bases is None: - self.__orig_bases__ = initial_bases - - # This allows unparameterized generic collections to be used - # with issubclass() and isinstance() in the same way as their - # collections.abc counterparts (e.g., isinstance([], Iterable)). - if ( - '__subclasshook__' not in namespace and extra or - # allow overriding - getattr(self.__subclasshook__, '__name__', '') == '__extrahook__' - ): - self.__subclasshook__ = _make_subclasshook(self) - if isinstance(extra, abc.ABCMeta): - self._abc_registry = extra._abc_registry - self._abc_cache = extra._abc_cache - elif origin is not None: - self._abc_registry = origin._abc_registry - self._abc_cache = origin._abc_cache - - if origin and hasattr(origin, '__qualname__'): # Fix for Python 3.2. - self.__qualname__ = origin.__qualname__ - self.__tree_hash__ = (hash(self._subs_tree()) if origin else - super(GenericMeta, self).__hash__()) - return self - - # _abc_negative_cache and _abc_negative_cache_version - # realised as descriptors, since GenClass[t1, t2, ...] always - # share subclass info with GenClass. - # This is an important memory optimization. - @property - def _abc_negative_cache(self): - if isinstance(self.__extra__, abc.ABCMeta): - return self.__extra__._abc_negative_cache - return self._gorg._abc_generic_negative_cache - - @_abc_negative_cache.setter - def _abc_negative_cache(self, value): - if self.__origin__ is None: - if isinstance(self.__extra__, abc.ABCMeta): - self.__extra__._abc_negative_cache = value - else: - self._abc_generic_negative_cache = value - - @property - def _abc_negative_cache_version(self): - if isinstance(self.__extra__, abc.ABCMeta): - return self.__extra__._abc_negative_cache_version - return self._gorg._abc_generic_negative_cache_version - - @_abc_negative_cache_version.setter - def _abc_negative_cache_version(self, value): - if self.__origin__ is None: - if isinstance(self.__extra__, abc.ABCMeta): - self.__extra__._abc_negative_cache_version = value - else: - self._abc_generic_negative_cache_version = value - - def _get_type_vars(self, tvars): - if self.__origin__ and self.__parameters__: - _get_type_vars(self.__parameters__, tvars) - - def _eval_type(self, globalns, localns): - ev_origin = (self.__origin__._eval_type(globalns, localns) - if self.__origin__ else None) - ev_args = tuple(_eval_type(a, globalns, localns) for a - in self.__args__) if self.__args__ else None - if ev_origin == self.__origin__ and ev_args == self.__args__: - return self - return self.__class__(self.__name__, - self.__bases__, - _no_slots_copy(self.__dict__), - tvars=_type_vars(ev_args) if ev_args else None, - args=ev_args, - origin=ev_origin, - extra=self.__extra__, - orig_bases=self.__orig_bases__) - - def __repr__(self): - if self.__origin__ is None: - return super().__repr__() - return self._tree_repr(self._subs_tree()) - - def _tree_repr(self, tree): - arg_list = [] - for arg in tree[1:]: - if arg == (): - arg_list.append('()') - elif not isinstance(arg, tuple): - arg_list.append(_type_repr(arg)) - else: - arg_list.append(arg[0]._tree_repr(arg)) - return super().__repr__() + '[%s]' % ', '.join(arg_list) - - def _subs_tree(self, tvars=None, args=None): - if self.__origin__ is None: - return self - tree_args = _subs_tree(self, tvars, args) - return (self._gorg,) + tuple(tree_args) - - def __eq__(self, other): - if not isinstance(other, GenericMeta): - return NotImplemented - if self.__origin__ is None or other.__origin__ is None: - return self is other - return self.__tree_hash__ == other.__tree_hash__ - - def __hash__(self): - return self.__tree_hash__ - - @_tp_cache - def __getitem__(self, params): - if not isinstance(params, tuple): - params = (params,) - if not params and self._gorg is not Tuple: - raise TypeError( - "Parameter list to %s[...] cannot be empty" % _qualname(self)) - msg = "Parameters to generic types must be types." - params = tuple(_type_check(p, msg) for p in params) - if self is Generic: - # Generic can only be subscripted with unique type variables. - if not all(isinstance(p, TypeVar) for p in params): - raise TypeError( - "Parameters to Generic[...] must all be type variables") - if len(set(params)) != len(params): - raise TypeError( - "Parameters to Generic[...] must all be unique") - tvars = params - args = params - elif self in (Tuple, Callable): - tvars = _type_vars(params) - args = params - elif self is _Protocol: - # _Protocol is internal, don't check anything. - tvars = params - args = params - elif self.__origin__ in (Generic, _Protocol): - # Can't subscript Generic[...] or _Protocol[...]. - raise TypeError("Cannot subscript already-subscripted %s" % - repr(self)) - else: - # Subscripting a regular Generic subclass. - _check_generic(self, params) - tvars = _type_vars(params) - args = params - - prepend = (self,) if self.__origin__ is None else () - return self.__class__(self.__name__, - prepend + self.__bases__, - _no_slots_copy(self.__dict__), - tvars=tvars, - args=args, - origin=self, - extra=self.__extra__, - orig_bases=self.__orig_bases__) - - def __subclasscheck__(self, cls): - if self.__origin__ is not None: - if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: - raise TypeError("Parameterized generics cannot be used with class " - "or instance checks") - return False - if self is Generic: - raise TypeError("Class %r cannot be used with class " - "or instance checks" % self) - return super().__subclasscheck__(cls) - - def __instancecheck__(self, instance): - # Since we extend ABC.__subclasscheck__ and - # ABC.__instancecheck__ inlines the cache checking done by the - # latter, we must extend __instancecheck__ too. For simplicity - # we just skip the cache check -- instance checks for generic - # classes are supposed to be rare anyways. - return issubclass(instance.__class__, self) - - def __setattr__(self, attr, value): - # We consider all the subscripted generics as proxies for original class - if ( - attr.startswith('__') and attr.endswith('__') or - attr.startswith('_abc_') or - self._gorg is None # The class is not fully created, see #typing/506 - ): - super(GenericMeta, self).__setattr__(attr, value) - else: - super(GenericMeta, self._gorg).__setattr__(attr, value) - - -# Prevent checks for Generic to crash when defining Generic. -Generic = None - - -def _generic_new(base_cls, cls, *args, **kwds): - # Assure type is erased on instantiation, - # but attempt to store it in __orig_class__ - if cls.__origin__ is None: - if (base_cls.__new__ is object.__new__ and - cls.__init__ is not object.__init__): - return base_cls.__new__(cls) - else: - return base_cls.__new__(cls, *args, **kwds) - else: - origin = cls._gorg - if (base_cls.__new__ is object.__new__ and - cls.__init__ is not object.__init__): - obj = base_cls.__new__(origin) - else: - obj = base_cls.__new__(origin, *args, **kwds) - try: - obj.__orig_class__ = cls - except AttributeError: - pass - obj.__init__(*args, **kwds) - return obj - - -class Generic(metaclass=GenericMeta): - """Abstract base class for generic types. - - A generic type is typically declared by inheriting from - this class parameterized with one or more type variables. - For example, a generic mapping type might be defined as:: - - class Mapping(Generic[KT, VT]): - def __getitem__(self, key: KT) -> VT: - ... - # Etc. - - This class can then be used as follows:: - - def lookup_name(mapping: Mapping[KT, VT], key: KT, default: VT) -> VT: - try: - return mapping[key] - except KeyError: - return default - """ - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Generic: - raise TypeError("Type Generic cannot be instantiated; " - "it can be used only as a base class") - return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) - - -class _TypingEmpty: - """Internal placeholder for () or []. Used by TupleMeta and CallableMeta - to allow empty list/tuple in specific places, without allowing them - to sneak in where prohibited. - """ - - -class _TypingEllipsis: - """Internal placeholder for ... (ellipsis).""" - - -class TupleMeta(GenericMeta): - """Metaclass for Tuple (internal).""" - - @_tp_cache - def __getitem__(self, parameters): - if self.__origin__ is not None or self._gorg is not Tuple: - # Normal generic rules apply if this is not the first subscription - # or a subscription of a subclass. - return super().__getitem__(parameters) - if parameters == (): - return super().__getitem__((_TypingEmpty,)) - if not isinstance(parameters, tuple): - parameters = (parameters,) - if len(parameters) == 2 and parameters[1] is ...: - msg = "Tuple[t, ...]: t must be a type." - p = _type_check(parameters[0], msg) - return super().__getitem__((p, _TypingEllipsis)) - msg = "Tuple[t0, t1, ...]: each t must be a type." - parameters = tuple(_type_check(p, msg) for p in parameters) - return super().__getitem__(parameters) - - def __instancecheck__(self, obj): - if self.__args__ is None: - return isinstance(obj, tuple) - raise TypeError("Parameterized Tuple cannot be used " - "with isinstance().") - - def __subclasscheck__(self, cls): - if self.__args__ is None: - return issubclass(cls, tuple) - raise TypeError("Parameterized Tuple cannot be used " - "with issubclass().") - - -class Tuple(tuple, extra=tuple, metaclass=TupleMeta): - """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. - - Example: Tuple[T1, T2] is a tuple of two elements corresponding - to type variables T1 and T2. Tuple[int, float, str] is a tuple - of an int, a float and a string. - - To specify a variable-length tuple of homogeneous type, use Tuple[T, ...]. - """ - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Tuple: - raise TypeError("Type Tuple cannot be instantiated; " - "use tuple() instead") - return _generic_new(tuple, cls, *args, **kwds) - - -class CallableMeta(GenericMeta): - """Metaclass for Callable (internal).""" - - def __repr__(self): - if self.__origin__ is None: - return super().__repr__() - return self._tree_repr(self._subs_tree()) - - def _tree_repr(self, tree): - if self._gorg is not Callable: - return super()._tree_repr(tree) - # For actual Callable (not its subclass) we override - # super()._tree_repr() for nice formatting. - arg_list = [] - for arg in tree[1:]: - if not isinstance(arg, tuple): - arg_list.append(_type_repr(arg)) - else: - arg_list.append(arg[0]._tree_repr(arg)) - if arg_list[0] == '...': - return repr(tree[0]) + '[..., %s]' % arg_list[1] - return (repr(tree[0]) + - '[[%s], %s]' % (', '.join(arg_list[:-1]), arg_list[-1])) - - def __getitem__(self, parameters): - """A thin wrapper around __getitem_inner__ to provide the latter - with hashable arguments to improve speed. - """ - - if self.__origin__ is not None or self._gorg is not Callable: - return super().__getitem__(parameters) - if not isinstance(parameters, tuple) or len(parameters) != 2: - raise TypeError("Callable must be used as " - "Callable[[arg, ...], result].") - args, result = parameters - if args is Ellipsis: - parameters = (Ellipsis, result) - else: - if not isinstance(args, list): - raise TypeError("Callable[args, result]: args must be a list." - " Got %.100r." % (args,)) - parameters = (tuple(args), result) - return self.__getitem_inner__(parameters) - - @_tp_cache - def __getitem_inner__(self, parameters): - args, result = parameters - msg = "Callable[args, result]: result must be a type." - result = _type_check(result, msg) - if args is Ellipsis: - return super().__getitem__((_TypingEllipsis, result)) - msg = "Callable[[arg, ...], result]: each arg must be a type." - args = tuple(_type_check(arg, msg) for arg in args) - parameters = args + (result,) - return super().__getitem__(parameters) - - -class Callable(extra=collections_abc.Callable, metaclass=CallableMeta): - """Callable type; Callable[[int], str] is a function of (int) -> str. - - The subscription syntax must always be used with exactly two - values: the argument list and the return type. The argument list - must be a list of types or ellipsis; the return type must be a single type. - - There is no syntax to indicate optional or keyword arguments, - such function types are rarely used as callback types. - """ - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Callable: - raise TypeError("Type Callable cannot be instantiated; " - "use a non-abstract subclass instead") - return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) - - -class _ClassVar(_FinalTypingBase, _root=True): - """Special type construct to mark class variables. - - An annotation wrapped in ClassVar indicates that a given - attribute is intended to be used as a class variable and - should not be set on instances of that class. Usage:: - - class Starship: - stats: ClassVar[Dict[str, int]] = {} # class variable - damage: int = 10 # instance variable - - ClassVar accepts only types and cannot be further subscribed. - - Note that ClassVar is not a class itself, and should not - be used with isinstance() or issubclass(). - """ - - __slots__ = ('__type__',) - - def __init__(self, tp=None, **kwds): - self.__type__ = tp - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is None: - return cls(_type_check(item, - '{} accepts only single type.'.format(cls.__name__[1:])), - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - new_tp = _eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(new_tp, _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(_type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, _ClassVar): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - -ClassVar = _ClassVar(_root=True) - - -def cast(typ, val): - """Cast a value to a type. - - This returns the value unchanged. To the type checker this - signals that the return value has the designated type, but at - runtime we intentionally don't check anything (we want this - to be as fast as possible). - """ - return val - - -def _get_defaults(func): - """Internal helper to extract the default arguments, by name.""" - try: - code = func.__code__ - except AttributeError: - # Some built-in functions don't have __code__, __defaults__, etc. - return {} - pos_count = code.co_argcount - arg_names = code.co_varnames - arg_names = arg_names[:pos_count] - defaults = func.__defaults__ or () - kwdefaults = func.__kwdefaults__ - res = dict(kwdefaults) if kwdefaults else {} - pos_offset = pos_count - len(defaults) - for name, value in zip(arg_names[pos_offset:], defaults): - assert name not in res - res[name] = value - return res - - -_allowed_types = (types.FunctionType, types.BuiltinFunctionType, - types.MethodType, types.ModuleType, - WrapperDescriptorType, MethodWrapperType, MethodDescriptorType) - - -def get_type_hints(obj, globalns=None, localns=None): - """Return type hints for an object. - - This is often the same as obj.__annotations__, but it handles - forward references encoded as string literals, and if necessary - adds Optional[t] if a default value equal to None is set. - - The argument may be a module, class, method, or function. The annotations - are returned as a dictionary. For classes, annotations include also - inherited members. - - TypeError is raised if the argument is not of a type that can contain - annotations, and an empty dictionary is returned if no annotations are - present. - - BEWARE -- the behavior of globalns and localns is counterintuitive - (unless you are familiar with how eval() and exec() work). The - search order is locals first, then globals. - - - If no dict arguments are passed, an attempt is made to use the - globals from obj (or the respective module's globals for classes), - and these are also used as the locals. If the object does not appear - to have globals, an empty dictionary is used. - - - If one dict argument is passed, it is used for both globals and - locals. - - - If two dict arguments are passed, they specify globals and - locals, respectively. - """ - - if getattr(obj, '__no_type_check__', None): - return {} - # Classes require a special treatment. - if isinstance(obj, type): - hints = {} - for base in reversed(obj.__mro__): - if globalns is None: - base_globals = sys.modules[base.__module__].__dict__ - else: - base_globals = globalns - ann = base.__dict__.get('__annotations__', {}) - for name, value in ann.items(): - if value is None: - value = type(None) - if isinstance(value, str): - value = _ForwardRef(value) - value = _eval_type(value, base_globals, localns) - hints[name] = value - return hints - - if globalns is None: - if isinstance(obj, types.ModuleType): - globalns = obj.__dict__ - else: - globalns = getattr(obj, '__globals__', {}) - if localns is None: - localns = globalns - elif localns is None: - localns = globalns - hints = getattr(obj, '__annotations__', None) - if hints is None: - # Return empty annotations for something that _could_ have them. - if isinstance(obj, _allowed_types): - return {} - else: - raise TypeError('{!r} is not a module, class, method, ' - 'or function.'.format(obj)) - defaults = _get_defaults(obj) - hints = dict(hints) - for name, value in hints.items(): - if value is None: - value = type(None) - if isinstance(value, str): - value = _ForwardRef(value) - value = _eval_type(value, globalns, localns) - if name in defaults and defaults[name] is None: - value = Optional[value] - hints[name] = value - return hints - - -def no_type_check(arg): - """Decorator to indicate that annotations are not type hints. - - The argument must be a class or function; if it is a class, it - applies recursively to all methods and classes defined in that class - (but not to methods defined in its superclasses or subclasses). - - This mutates the function(s) or class(es) in place. - """ - if isinstance(arg, type): - arg_attrs = arg.__dict__.copy() - for attr, val in arg.__dict__.items(): - if val in arg.__bases__ + (arg,): - arg_attrs.pop(attr) - for obj in arg_attrs.values(): - if isinstance(obj, types.FunctionType): - obj.__no_type_check__ = True - if isinstance(obj, type): - no_type_check(obj) - try: - arg.__no_type_check__ = True - except TypeError: # built-in classes - pass - return arg - - -def no_type_check_decorator(decorator): - """Decorator to give another decorator the @no_type_check effect. - - This wraps the decorator with something that wraps the decorated - function in @no_type_check. - """ - - @functools.wraps(decorator) - def wrapped_decorator(*args, **kwds): - func = decorator(*args, **kwds) - func = no_type_check(func) - return func - - return wrapped_decorator - - -def _overload_dummy(*args, **kwds): - """Helper for @overload to raise when called.""" - raise NotImplementedError( - "You should not call an overloaded function. " - "A series of @overload-decorated functions " - "outside a stub module should always be followed " - "by an implementation that is not @overload-ed.") - - -def overload(func): - """Decorator for overloaded functions/methods. - - In a stub file, place two or more stub definitions for the same - function in a row, each decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - - In a non-stub file (i.e. a regular .py file), do the same but - follow it with an implementation. The implementation should *not* - be decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - def utf8(value): - # implementation goes here - """ - return _overload_dummy - - -class _ProtocolMeta(GenericMeta): - """Internal metaclass for _Protocol. - - This exists so _Protocol classes can be generic without deriving - from Generic. - """ - - def __instancecheck__(self, obj): - if _Protocol not in self.__bases__: - return super().__instancecheck__(obj) - raise TypeError("Protocols cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - if not self._is_protocol: - # No structural checks since this isn't a protocol. - return NotImplemented - - if self is _Protocol: - # Every class is a subclass of the empty protocol. - return True - - # Find all attributes defined in the protocol. - attrs = self._get_protocol_attrs() - - for attr in attrs: - if not any(attr in d.__dict__ for d in cls.__mro__): - return False - return True - - def _get_protocol_attrs(self): - # Get all Protocol base classes. - protocol_bases = [] - for c in self.__mro__: - if getattr(c, '_is_protocol', False) and c.__name__ != '_Protocol': - protocol_bases.append(c) - - # Get attributes included in protocol. - attrs = set() - for base in protocol_bases: - for attr in base.__dict__.keys(): - # Include attributes not defined in any non-protocol bases. - for c in self.__mro__: - if (c is not base and attr in c.__dict__ and - not getattr(c, '_is_protocol', False)): - break - else: - if (not attr.startswith('_abc_') and - attr != '__abstractmethods__' and - attr != '__annotations__' and - attr != '__weakref__' and - attr != '_is_protocol' and - attr != '_gorg' and - attr != '__dict__' and - attr != '__args__' and - attr != '__slots__' and - attr != '_get_protocol_attrs' and - attr != '__next_in_mro__' and - attr != '__parameters__' and - attr != '__origin__' and - attr != '__orig_bases__' and - attr != '__extra__' and - attr != '__tree_hash__' and - attr != '__module__'): - attrs.add(attr) - - return attrs - - -class _Protocol(metaclass=_ProtocolMeta): - """Internal base class for protocol classes. - - This implements a simple-minded structural issubclass check - (similar but more general than the one-offs in collections.abc - such as Hashable). - """ - - __slots__ = () - - _is_protocol = True - - -# Various ABCs mimicking those in collections.abc. -# A few are simply re-exported for completeness. - -Hashable = collections_abc.Hashable # Not generic. - - -if hasattr(collections_abc, 'Awaitable'): - class Awaitable(Generic[T_co], extra=collections_abc.Awaitable): - __slots__ = () - - __all__.append('Awaitable') - - -if hasattr(collections_abc, 'Coroutine'): - class Coroutine(Awaitable[V_co], Generic[T_co, T_contra, V_co], - extra=collections_abc.Coroutine): - __slots__ = () - - __all__.append('Coroutine') - - -if hasattr(collections_abc, 'AsyncIterable'): - - class AsyncIterable(Generic[T_co], extra=collections_abc.AsyncIterable): - __slots__ = () - - class AsyncIterator(AsyncIterable[T_co], - extra=collections_abc.AsyncIterator): - __slots__ = () - - __all__.append('AsyncIterable') - __all__.append('AsyncIterator') - - -class Iterable(Generic[T_co], extra=collections_abc.Iterable): - __slots__ = () - - -class Iterator(Iterable[T_co], extra=collections_abc.Iterator): - __slots__ = () - - -class SupportsInt(_Protocol): - __slots__ = () - - @abstractmethod - def __int__(self) -> int: - pass - - -class SupportsFloat(_Protocol): - __slots__ = () - - @abstractmethod - def __float__(self) -> float: - pass - - -class SupportsComplex(_Protocol): - __slots__ = () - - @abstractmethod - def __complex__(self) -> complex: - pass - - -class SupportsBytes(_Protocol): - __slots__ = () - - @abstractmethod - def __bytes__(self) -> bytes: - pass - - -class SupportsIndex(_Protocol): - __slots__ = () - - @abstractmethod - def __index__(self) -> int: - pass - - -class SupportsAbs(_Protocol[T_co]): - __slots__ = () - - @abstractmethod - def __abs__(self) -> T_co: - pass - - -class SupportsRound(_Protocol[T_co]): - __slots__ = () - - @abstractmethod - def __round__(self, ndigits: int = 0) -> T_co: - pass - - -if hasattr(collections_abc, 'Reversible'): - class Reversible(Iterable[T_co], extra=collections_abc.Reversible): - __slots__ = () -else: - class Reversible(_Protocol[T_co]): - __slots__ = () - - @abstractmethod - def __reversed__(self) -> 'Iterator[T_co]': - pass - - -Sized = collections_abc.Sized # Not generic. - - -class Container(Generic[T_co], extra=collections_abc.Container): - __slots__ = () - - -if hasattr(collections_abc, 'Collection'): - class Collection(Sized, Iterable[T_co], Container[T_co], - extra=collections_abc.Collection): - __slots__ = () - - __all__.append('Collection') - - -# Callable was defined earlier. - -if hasattr(collections_abc, 'Collection'): - class AbstractSet(Collection[T_co], - extra=collections_abc.Set): - __slots__ = () -else: - class AbstractSet(Sized, Iterable[T_co], Container[T_co], - extra=collections_abc.Set): - __slots__ = () - - -class MutableSet(AbstractSet[T], extra=collections_abc.MutableSet): - __slots__ = () - - -# NOTE: It is only covariant in the value type. -if hasattr(collections_abc, 'Collection'): - class Mapping(Collection[KT], Generic[KT, VT_co], - extra=collections_abc.Mapping): - __slots__ = () -else: - class Mapping(Sized, Iterable[KT], Container[KT], Generic[KT, VT_co], - extra=collections_abc.Mapping): - __slots__ = () - - -class MutableMapping(Mapping[KT, VT], extra=collections_abc.MutableMapping): - __slots__ = () - - -if hasattr(collections_abc, 'Reversible'): - if hasattr(collections_abc, 'Collection'): - class Sequence(Reversible[T_co], Collection[T_co], - extra=collections_abc.Sequence): - __slots__ = () - else: - class Sequence(Sized, Reversible[T_co], Container[T_co], - extra=collections_abc.Sequence): - __slots__ = () -else: - class Sequence(Sized, Iterable[T_co], Container[T_co], - extra=collections_abc.Sequence): - __slots__ = () - - -class MutableSequence(Sequence[T], extra=collections_abc.MutableSequence): - __slots__ = () - - -class ByteString(Sequence[int], extra=collections_abc.ByteString): - __slots__ = () - - -class List(list, MutableSequence[T], extra=list): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is List: - raise TypeError("Type List cannot be instantiated; " - "use list() instead") - return _generic_new(list, cls, *args, **kwds) - - -class Deque(collections.deque, MutableSequence[T], extra=collections.deque): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Deque: - return collections.deque(*args, **kwds) - return _generic_new(collections.deque, cls, *args, **kwds) - - -class Set(set, MutableSet[T], extra=set): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Set: - raise TypeError("Type Set cannot be instantiated; " - "use set() instead") - return _generic_new(set, cls, *args, **kwds) - - -class FrozenSet(frozenset, AbstractSet[T_co], extra=frozenset): - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is FrozenSet: - raise TypeError("Type FrozenSet cannot be instantiated; " - "use frozenset() instead") - return _generic_new(frozenset, cls, *args, **kwds) - - -class MappingView(Sized, Iterable[T_co], extra=collections_abc.MappingView): - __slots__ = () - - -class KeysView(MappingView[KT], AbstractSet[KT], - extra=collections_abc.KeysView): - __slots__ = () - - -class ItemsView(MappingView[Tuple[KT, VT_co]], - AbstractSet[Tuple[KT, VT_co]], - Generic[KT, VT_co], - extra=collections_abc.ItemsView): - __slots__ = () - - -class ValuesView(MappingView[VT_co], extra=collections_abc.ValuesView): - __slots__ = () - - -if hasattr(contextlib, 'AbstractContextManager'): - class ContextManager(Generic[T_co], extra=contextlib.AbstractContextManager): - __slots__ = () -else: - class ContextManager(Generic[T_co]): - __slots__ = () - - def __enter__(self): - return self - - @abc.abstractmethod - def __exit__(self, exc_type, exc_value, traceback): - return None - - @classmethod - def __subclasshook__(cls, C): - if cls is ContextManager: - # In Python 3.6+, it is possible to set a method to None to - # explicitly indicate that the class does not implement an ABC - # (https://bugs.python.org/issue25958), but we do not support - # that pattern here because this fallback class is only used - # in Python 3.5 and earlier. - if (any("__enter__" in B.__dict__ for B in C.__mro__) and - any("__exit__" in B.__dict__ for B in C.__mro__)): - return True - return NotImplemented - - -if hasattr(contextlib, 'AbstractAsyncContextManager'): - class AsyncContextManager(Generic[T_co], - extra=contextlib.AbstractAsyncContextManager): - __slots__ = () - - __all__.append('AsyncContextManager') -elif sys.version_info[:2] >= (3, 5): - exec(""" -class AsyncContextManager(Generic[T_co]): - __slots__ = () - - async def __aenter__(self): - return self - - @abc.abstractmethod - async def __aexit__(self, exc_type, exc_value, traceback): - return None - - @classmethod - def __subclasshook__(cls, C): - if cls is AsyncContextManager: - if sys.version_info[:2] >= (3, 6): - return _collections_abc._check_methods(C, "__aenter__", "__aexit__") - if (any("__aenter__" in B.__dict__ for B in C.__mro__) and - any("__aexit__" in B.__dict__ for B in C.__mro__)): - return True - return NotImplemented - -__all__.append('AsyncContextManager') -""") - - -class Dict(dict, MutableMapping[KT, VT], extra=dict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Dict: - raise TypeError("Type Dict cannot be instantiated; " - "use dict() instead") - return _generic_new(dict, cls, *args, **kwds) - - -class DefaultDict(collections.defaultdict, MutableMapping[KT, VT], - extra=collections.defaultdict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is DefaultDict: - return collections.defaultdict(*args, **kwds) - return _generic_new(collections.defaultdict, cls, *args, **kwds) - - -class Counter(collections.Counter, Dict[T, int], extra=collections.Counter): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Counter: - return collections.Counter(*args, **kwds) - return _generic_new(collections.Counter, cls, *args, **kwds) - - -if hasattr(collections, 'ChainMap'): - # ChainMap only exists in 3.3+ - __all__.append('ChainMap') - - class ChainMap(collections.ChainMap, MutableMapping[KT, VT], - extra=collections.ChainMap): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is ChainMap: - return collections.ChainMap(*args, **kwds) - return _generic_new(collections.ChainMap, cls, *args, **kwds) - - -# Determine what base class to use for Generator. -if hasattr(collections_abc, 'Generator'): - # Sufficiently recent versions of 3.5 have a Generator ABC. - _G_base = collections_abc.Generator -else: - # Fall back on the exact type. - _G_base = types.GeneratorType - - -class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co], - extra=_G_base): - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Generator: - raise TypeError("Type Generator cannot be instantiated; " - "create a subclass instead") - return _generic_new(_G_base, cls, *args, **kwds) - - -if hasattr(collections_abc, 'AsyncGenerator'): - class AsyncGenerator(AsyncIterator[T_co], Generic[T_co, T_contra], - extra=collections_abc.AsyncGenerator): - __slots__ = () - - __all__.append('AsyncGenerator') - - -# Internal type variable used for Type[]. -CT_co = TypeVar('CT_co', covariant=True, bound=type) - - -# This is not a real generic class. Don't use outside annotations. -class Type(Generic[CT_co], extra=type): - """A special construct usable to annotate class objects. - - For example, suppose we have the following classes:: - - class User: ... # Abstract base for User classes - class BasicUser(User): ... - class ProUser(User): ... - class TeamUser(User): ... - - And a function that takes a class argument that's a subclass of - User and returns an instance of the corresponding class:: - - U = TypeVar('U', bound=User) - def new_user(user_class: Type[U]) -> U: - user = user_class() - # (Here we could write the user object to a database) - return user - - joe = new_user(BasicUser) - - At this point the type checker knows that joe has type BasicUser. - """ - - __slots__ = () - - -def _make_nmtuple(name, types): - msg = "NamedTuple('Name', [(f0, t0), (f1, t1), ...]); each t must be a type" - types = [(n, _type_check(t, msg)) for n, t in types] - nm_tpl = collections.namedtuple(name, [n for n, t in types]) - # Prior to PEP 526, only _field_types attribute was assigned. - # Now, both __annotations__ and _field_types are used to maintain compatibility. - nm_tpl.__annotations__ = nm_tpl._field_types = collections.OrderedDict(types) - try: - nm_tpl.__module__ = sys._getframe(2).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - return nm_tpl - - -_PY36 = sys.version_info[:2] >= (3, 6) - -# attributes prohibited to set in NamedTuple class syntax -_prohibited = ('__new__', '__init__', '__slots__', '__getnewargs__', - '_fields', '_field_defaults', '_field_types', - '_make', '_replace', '_asdict', '_source') - -_special = ('__module__', '__name__', '__qualname__', '__annotations__') - - -class NamedTupleMeta(type): - - def __new__(cls, typename, bases, ns): - if ns.get('_root', False): - return super().__new__(cls, typename, bases, ns) - if not _PY36: - raise TypeError("Class syntax for NamedTuple is only supported" - " in Python 3.6+") - types = ns.get('__annotations__', {}) - nm_tpl = _make_nmtuple(typename, types.items()) - defaults = [] - defaults_dict = {} - for field_name in types: - if field_name in ns: - default_value = ns[field_name] - defaults.append(default_value) - defaults_dict[field_name] = default_value - elif defaults: - raise TypeError("Non-default namedtuple field {field_name} cannot " - "follow default field(s) {default_names}" - .format(field_name=field_name, - default_names=', '.join(defaults_dict.keys()))) - nm_tpl.__new__.__annotations__ = collections.OrderedDict(types) - nm_tpl.__new__.__defaults__ = tuple(defaults) - nm_tpl._field_defaults = defaults_dict - # update from user namespace without overriding special namedtuple attributes - for key in ns: - if key in _prohibited: - raise AttributeError("Cannot overwrite NamedTuple attribute " + key) - elif key not in _special and key not in nm_tpl._fields: - setattr(nm_tpl, key, ns[key]) - return nm_tpl - - -class NamedTuple(metaclass=NamedTupleMeta): - """Typed version of namedtuple. - - Usage in Python versions >= 3.6:: - - class Employee(NamedTuple): - name: str - id: int - - This is equivalent to:: - - Employee = collections.namedtuple('Employee', ['name', 'id']) - - The resulting class has extra __annotations__ and _field_types - attributes, giving an ordered dict mapping field names to types. - __annotations__ should be preferred, while _field_types - is kept to maintain pre PEP 526 compatibility. (The field names - are in the _fields attribute, which is part of the namedtuple - API.) Alternative equivalent keyword syntax is also accepted:: - - Employee = NamedTuple('Employee', name=str, id=int) - - In Python versions <= 3.5 use:: - - Employee = NamedTuple('Employee', [('name', str), ('id', int)]) - """ - _root = True - - def __new__(*args, **kwargs): - if kwargs and not _PY36: - raise TypeError("Keyword syntax for NamedTuple is only supported" - " in Python 3.6+") - if not args: - raise TypeError('NamedTuple.__new__(): not enough arguments') - _, args = args[0], args[1:] # allow the "cls" keyword be passed - if args: - typename, args = args[0], args[1:] # allow the "typename" keyword be passed - elif 'typename' in kwargs: - typename = kwargs.pop('typename') - import warnings - warnings.warn("Passing 'typename' as keyword argument is deprecated", - DeprecationWarning, stacklevel=2) - else: - raise TypeError("NamedTuple.__new__() missing 1 required positional " - "argument: 'typename'") - if args: - try: - fields, = args # allow the "fields" keyword be passed - except ValueError: - raise TypeError('NamedTuple.__new__() takes from 2 to 3 ' - 'positional arguments but {} ' - 'were given'.format(len(args) + 2)) - elif 'fields' in kwargs and len(kwargs) == 1: - fields = kwargs.pop('fields') - import warnings - warnings.warn("Passing 'fields' as keyword argument is deprecated", - DeprecationWarning, stacklevel=2) - else: - fields = None - - if fields is None: - fields = kwargs.items() - elif kwargs: - raise TypeError("Either list of fields or keywords" - " can be provided to NamedTuple, not both") - return _make_nmtuple(typename, fields) - - __new__.__text_signature__ = '($cls, typename, fields=None, /, **kwargs)' - - -def NewType(name, tp): - """NewType creates simple unique types with almost zero - runtime overhead. NewType(name, tp) is considered a subtype of tp - by static type checkers. At runtime, NewType(name, tp) returns - a dummy function that simply returns its argument. Usage:: - - UserId = NewType('UserId', int) - - def name_by_id(user_id: UserId) -> str: - ... - - UserId('user') # Fails type check - - name_by_id(42) # Fails type check - name_by_id(UserId(42)) # OK - - num = UserId(5) + 1 # type: int - """ - - def new_type(x): - return x - - new_type.__name__ = name - new_type.__supertype__ = tp - return new_type - - -# Python-version-specific alias (Python 2: unicode; Python 3: str) -Text = str - - -# Constant that's True when type checking, but False here. -TYPE_CHECKING = False - - -class IO(Generic[AnyStr]): - """Generic base class for TextIO and BinaryIO. - - This is an abstract, generic version of the return of open(). - - NOTE: This does not distinguish between the different possible - classes (text vs. binary, read vs. write vs. read/write, - append-only, unbuffered). The TextIO and BinaryIO subclasses - below capture the distinctions between text vs. binary, which is - pervasive in the interface; however we currently do not offer a - way to track the other distinctions in the type system. - """ - - __slots__ = () - - @abstractproperty - def mode(self) -> str: - pass - - @abstractproperty - def name(self) -> str: - pass - - @abstractmethod - def close(self) -> None: - pass - - @abstractproperty - def closed(self) -> bool: - pass - - @abstractmethod - def fileno(self) -> int: - pass - - @abstractmethod - def flush(self) -> None: - pass - - @abstractmethod - def isatty(self) -> bool: - pass - - @abstractmethod - def read(self, n: int = -1) -> AnyStr: - pass - - @abstractmethod - def readable(self) -> bool: - pass - - @abstractmethod - def readline(self, limit: int = -1) -> AnyStr: - pass - - @abstractmethod - def readlines(self, hint: int = -1) -> List[AnyStr]: - pass - - @abstractmethod - def seek(self, offset: int, whence: int = 0) -> int: - pass - - @abstractmethod - def seekable(self) -> bool: - pass - - @abstractmethod - def tell(self) -> int: - pass - - @abstractmethod - def truncate(self, size: int = None) -> int: - pass - - @abstractmethod - def writable(self) -> bool: - pass - - @abstractmethod - def write(self, s: AnyStr) -> int: - pass - - @abstractmethod - def writelines(self, lines: List[AnyStr]) -> None: - pass - - @abstractmethod - def __enter__(self) -> 'IO[AnyStr]': - pass - - @abstractmethod - def __exit__(self, type, value, traceback) -> None: - pass - - -class BinaryIO(IO[bytes]): - """Typed version of the return of open() in binary mode.""" - - __slots__ = () - - @abstractmethod - def write(self, s: Union[bytes, bytearray]) -> int: - pass - - @abstractmethod - def __enter__(self) -> 'BinaryIO': - pass - - -class TextIO(IO[str]): - """Typed version of the return of open() in text mode.""" - - __slots__ = () - - @abstractproperty - def buffer(self) -> BinaryIO: - pass - - @abstractproperty - def encoding(self) -> str: - pass - - @abstractproperty - def errors(self) -> Optional[str]: - pass - - @abstractproperty - def line_buffering(self) -> bool: - pass - - @abstractproperty - def newlines(self) -> Any: - pass - - @abstractmethod - def __enter__(self) -> 'TextIO': - pass - - -class io: - """Wrapper namespace for IO generic classes.""" - - __all__ = ['IO', 'TextIO', 'BinaryIO'] - IO = IO - TextIO = TextIO - BinaryIO = BinaryIO - - -io.__name__ = __name__ + '.io' -sys.modules[io.__name__] = io - - -Pattern = _TypeAlias('Pattern', AnyStr, type(stdlib_re.compile('')), - lambda p: p.pattern) -Match = _TypeAlias('Match', AnyStr, type(stdlib_re.match('', '')), - lambda m: m.re.pattern) - - -class re: - """Wrapper namespace for re type aliases.""" - - __all__ = ['Pattern', 'Match'] - Pattern = Pattern - Match = Match - - -re.__name__ = __name__ + '.re' -sys.modules[re.__name__] = re diff --git a/test-requirements.txt b/test-requirements.txt index 1a033f49d..658ae0a52 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,6 +1,3 @@ -flake8; python_version >= '3.6' -flake8-bugbear; python_version >= '3.6' -flake8-pyi; python_version >= '3.6' -pytest>=4.4.1; python_version >= '3.4' -pytest-xdist>=1.18; python_version >= '3.4' -pytest-cov>=2.4.0; python_version >= '3.4' +flake8 +flake8-bugbear +flake8-pyi diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 1cfaa550b..000000000 --- a/tox.ini +++ /dev/null @@ -1,29 +0,0 @@ -[tox] -envlist = py27, py34, py35, py36 - -[testenv] -changedir = src -commands = python -m unittest discover - -[testenv:py27] -changedir = python2 - -[flake8] -# fake builtins for python2/* -builtins = basestring, unicode -max-line-length = 90 -ignore = - # irrelevant plugins - B3, - DW12, - # code is sometimes better without this - E129, - # consistency with mypy - W504 -exclude = - # tests have more relaxed formatting rules - # and its own specific config in .flake8-tests - python2/test_typing.py, - src/test_typing.py, - typing_extensions/src_py2/test_typing_extensions.py, - typing_extensions/src_py3/test_typing_extensions.py, diff --git a/typing_extensions/LICENSE b/typing_extensions/LICENSE deleted file mode 100644 index 583f9f6e6..000000000 --- a/typing_extensions/LICENSE +++ /dev/null @@ -1,254 +0,0 @@ -A. HISTORY OF THE SOFTWARE -========================== - -Python was created in the early 1990s by Guido van Rossum at Stichting -Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands -as a successor of a language called ABC. Guido remains Python's -principal author, although it includes many contributions from others. - -In 1995, Guido continued his work on Python at the Corporation for -National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) -in Reston, Virginia where he released several versions of the -software. - -In May 2000, Guido and the Python core development team moved to -BeOpen.com to form the BeOpen PythonLabs team. In October of the same -year, the PythonLabs team moved to Digital Creations (now Zope -Corporation, see http://www.zope.com). In 2001, the Python Software -Foundation (PSF, see http://www.python.org/psf/) was formed, a -non-profit organization created specifically to own Python-related -Intellectual Property. Zope Corporation is a sponsoring member of -the PSF. - -All Python releases are Open Source (see http://www.opensource.org for -the Open Source Definition). Historically, most, but not all, Python -releases have also been GPL-compatible; the table below summarizes -the various releases. - - Release Derived Year Owner GPL- - from compatible? (1) - - 0.9.0 thru 1.2 1991-1995 CWI yes - 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes - 1.6 1.5.2 2000 CNRI no - 2.0 1.6 2000 BeOpen.com no - 1.6.1 1.6 2001 CNRI yes (2) - 2.1 2.0+1.6.1 2001 PSF no - 2.0.1 2.0+1.6.1 2001 PSF yes - 2.1.1 2.1+2.0.1 2001 PSF yes - 2.1.2 2.1.1 2002 PSF yes - 2.1.3 2.1.2 2002 PSF yes - 2.2 and above 2.1.1 2001-now PSF yes - -Footnotes: - -(1) GPL-compatible doesn't mean that we're distributing Python under - the GPL. All Python licenses, unlike the GPL, let you distribute - a modified version without making your changes open source. The - GPL-compatible licenses make it possible to combine Python with - other software that is released under the GPL; the others don't. - -(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, - because its license has a choice of law clause. According to - CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 - is "not incompatible" with the GPL. - -Thanks to the many outside volunteers who have worked under Guido's -direction to make these releases possible. - - -B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON -=============================================================== - -PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 --------------------------------------------- - -1. This LICENSE AGREEMENT is between the Python Software Foundation -("PSF"), and the Individual or Organization ("Licensee") accessing and -otherwise using this software ("Python") in source or binary form and -its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, PSF hereby -grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, -analyze, test, perform and/or display publicly, prepare derivative works, -distribute, and otherwise use Python alone or in any derivative version, -provided, however, that PSF's License Agreement and PSF's notice of copyright, -i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are -retained in Python alone or in any derivative version prepared by Licensee. - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python. - -4. PSF is making Python available to Licensee on an "AS IS" -basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. Nothing in this License Agreement shall be deemed to create any -relationship of agency, partnership, or joint venture between PSF and -Licensee. This License Agreement does not grant permission to use PSF -trademarks or trade name in a trademark sense to endorse or promote -products or services of Licensee, or any third party. - -8. By copying, installing or otherwise using Python, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 -------------------------------------------- - -BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 - -1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an -office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the -Individual or Organization ("Licensee") accessing and otherwise using -this software in source or binary form and its associated -documentation ("the Software"). - -2. Subject to the terms and conditions of this BeOpen Python License -Agreement, BeOpen hereby grants Licensee a non-exclusive, -royalty-free, world-wide license to reproduce, analyze, test, perform -and/or display publicly, prepare derivative works, distribute, and -otherwise use the Software alone or in any derivative version, -provided, however, that the BeOpen Python License is retained in the -Software, alone or in any derivative version prepared by Licensee. - -3. BeOpen is making the Software available to Licensee on an "AS IS" -basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE -SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS -AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY -DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -5. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -6. This License Agreement shall be governed by and interpreted in all -respects by the law of the State of California, excluding conflict of -law provisions. Nothing in this License Agreement shall be deemed to -create any relationship of agency, partnership, or joint venture -between BeOpen and Licensee. This License Agreement does not grant -permission to use BeOpen trademarks or trade names in a trademark -sense to endorse or promote products or services of Licensee, or any -third party. As an exception, the "BeOpen Python" logos available at -http://www.pythonlabs.com/logos.html may be used according to the -permissions granted on that web page. - -7. By copying, installing or otherwise using the software, Licensee -agrees to be bound by the terms and conditions of this License -Agreement. - - -CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 ---------------------------------------- - -1. This LICENSE AGREEMENT is between the Corporation for National -Research Initiatives, having an office at 1895 Preston White Drive, -Reston, VA 20191 ("CNRI"), and the Individual or Organization -("Licensee") accessing and otherwise using Python 1.6.1 software in -source or binary form and its associated documentation. - -2. Subject to the terms and conditions of this License Agreement, CNRI -hereby grants Licensee a nonexclusive, royalty-free, world-wide -license to reproduce, analyze, test, perform and/or display publicly, -prepare derivative works, distribute, and otherwise use Python 1.6.1 -alone or in any derivative version, provided, however, that CNRI's -License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) -1995-2001 Corporation for National Research Initiatives; All Rights -Reserved" are retained in Python 1.6.1 alone or in any derivative -version prepared by Licensee. Alternately, in lieu of CNRI's License -Agreement, Licensee may substitute the following text (omitting the -quotes): "Python 1.6.1 is made available subject to the terms and -conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the Internet using the following -unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the Internet -using the following URL: http://hdl.handle.net/1895.22/1013". - -3. In the event Licensee prepares a derivative work that is based on -or incorporates Python 1.6.1 or any part thereof, and wants to make -the derivative work available to others as provided herein, then -Licensee hereby agrees to include in any such work a brief summary of -the changes made to Python 1.6.1. - -4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" -basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR -IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND -DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS -FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT -INFRINGE ANY THIRD PARTY RIGHTS. - -5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON -1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS -A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, -OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. - -6. This License Agreement will automatically terminate upon a material -breach of its terms and conditions. - -7. This License Agreement shall be governed by the federal -intellectual property law of the United States, including without -limitation the federal copyright law, and, to the extent such -U.S. federal law does not apply, by the law of the Commonwealth of -Virginia, excluding Virginia's conflict of law provisions. -Notwithstanding the foregoing, with regard to derivative works based -on Python 1.6.1 that incorporate non-separable material that was -previously distributed under the GNU General Public License (GPL), the -law of the Commonwealth of Virginia shall govern this License -Agreement only as to issues arising under or with respect to -Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this -License Agreement shall be deemed to create any relationship of -agency, partnership, or joint venture between CNRI and Licensee. This -License Agreement does not grant permission to use CNRI trademarks or -trade name in a trademark sense to endorse or promote products or -services of Licensee, or any third party. - -8. By clicking on the "ACCEPT" button where indicated, or by copying, -installing or otherwise using Python 1.6.1, Licensee agrees to be -bound by the terms and conditions of this License Agreement. - - ACCEPT - - -CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 --------------------------------------------------- - -Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, -The Netherlands. All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of Stichting Mathematisch -Centrum or CWI not be used in advertising or publicity pertaining to -distribution of the software without specific, written prior -permission. - -STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO -THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE -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. diff --git a/typing_extensions/MANIFEST.in b/typing_extensions/MANIFEST.in deleted file mode 100644 index feda4cf1e..000000000 --- a/typing_extensions/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include LICENSE README.rst -include src_py3/typing_extensions.py -include src_py3/test_typing_extensions.py -include src_py2/typing_extensions.py -include src_py2/test_typing_extensions.py diff --git a/typing_extensions/README.rst b/typing_extensions/README.rst deleted file mode 100644 index 37b8a4c22..000000000 --- a/typing_extensions/README.rst +++ /dev/null @@ -1,90 +0,0 @@ -================= -Typing Extensions -================= - -.. image:: https://badges.gitter.im/python/typing.svg - :alt: Chat at https://gitter.im/python/typing - :target: https://gitter.im/python/typing?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge - -Overview -======== - -The ``typing`` module was added to the standard library in Python 3.5 on -a provisional basis and will no longer be provisional in Python 3.7. However, -this means users of Python 3.5 - 3.6 who are unable to upgrade will not be -able to take advantage of new types added to the ``typing`` module, such as -``typing.Text`` or ``typing.Coroutine``. - -The ``typing_extensions`` module contains both backports of these changes -as well as experimental types that will eventually be added to the ``typing`` -module, such as ``Protocol`` (see PEP 544 for details about protocols and -static duck typing) or ``TypedDict`` (see PEP 589). - -Users of other Python versions should continue to install and use -the ``typing`` module from PyPi instead of using this one unless -specifically writing code that must be compatible with multiple Python -versions or requires experimental types. - -Included items -============== - -This module currently contains the following: - -All Python versions: --------------------- - -- ``ClassVar`` -- ``ContextManager`` -- ``Counter`` -- ``DefaultDict`` -- ``Deque`` -- ``final`` -- ``Final`` -- ``Literal`` -- ``NewType`` -- ``NoReturn`` -- ``overload`` (note that older versions of ``typing`` only let you use ``overload`` in stubs) -- ``Protocol`` (except on Python 3.5.0) -- ``runtime`` (except on Python 3.5.0) -- ``Text`` -- ``Type`` -- ``TypedDict`` -- ``TYPE_CHECKING`` - -Python 3.4+ only: ------------------ - -- ``ChainMap`` - -Python 3.5+ only: ------------------ - -- ``AsyncIterable`` -- ``AsyncIterator`` -- ``AsyncContextManager`` -- ``Awaitable`` -- ``Coroutine`` - -Python 3.6+ only: ------------------ - -- ``AsyncGenerator`` - -Other Notes and Limitations -=========================== - -There are a few types whose interface was modified between different -versions of typing. For example, ``typing.Sequence`` was modified to -subclass ``typing.Reversible`` as of Python 3.5.3. - -These changes are _not_ backported to prevent subtle compatibility -issues when mixing the differing implementations of modified classes. - -Running tests -============= - -To run tests, navigate into the appropriate source directory and run -``test_typing_extensions.py``. You will also need to install the latest -version of ``typing`` if you are using a version of Python that does not -include ``typing`` as a part of the standard library. - diff --git a/typing_extensions/setup.cfg b/typing_extensions/setup.cfg deleted file mode 100644 index 637292647..000000000 --- a/typing_extensions/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[metadata] -license-file = LICENSE diff --git a/typing_extensions/setup.py b/typing_extensions/setup.py deleted file mode 100644 index 023ab3983..000000000 --- a/typing_extensions/setup.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -import sys -from setuptools import setup - -if sys.version_info < (2, 7, 0) or (3, 0, 0) <= sys.version_info < (3, 4, 0): - sys.stderr.write('ERROR: You need Python 2.7 or 3.4+ ' - 'to install the typing package.\n') - exit(1) - -version = '3.7.4.3' -description = 'Backported and Experimental Type Hints for Python 3.5+' -long_description = '''\ -Typing Extensions -- Backported and Experimental Type Hints for Python - -The ``typing`` module was added to the standard library in Python 3.5 on -a provisional basis and will no longer be provisional in Python 3.7. However, -this means users of Python 3.5 - 3.6 who are unable to upgrade will not be -able to take advantage of new types added to the ``typing`` module, such as -``typing.Text`` or ``typing.Coroutine``. - -The ``typing_extensions`` module contains both backports of these changes -as well as experimental types that will eventually be added to the ``typing`` -module, such as ``Protocol`` or ``TypedDict``. - -Users of other Python versions should continue to install and use -the ``typing`` module from PyPi instead of using this one unless specifically -writing code that must be compatible with multiple Python versions or requires -experimental types. -''' - -classifiers = [ - 'Development Status :: 3 - Alpha', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: Python Software Foundation License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Topic :: Software Development', -] - -if sys.version_info.major == 2: - package_dir = 'src_py2' -elif sys.version_info.major == 3: - package_dir = 'src_py3' -else: - raise AssertionError() - -setup(name='typing_extensions', - version=version, - description=description, - long_description=long_description, - author='Guido van Rossum, Jukka Lehtosalo, Lukasz Langa, Michael Lee', - author_email='levkivskyi@gmail.com', - url='https://github.com/python/typing/blob/master/typing_extensions/README.rst', - license='PSF', - keywords='typing function annotations type hints hinting checking ' - 'checker typehints typehinting typechecking backport', - package_dir={'': package_dir}, - py_modules=['typing_extensions'], - classifiers=classifiers, - install_requires=["typing >= 3.7.4; python_version < '3.5'"]) diff --git a/typing_extensions/src_py2/test_typing_extensions.py b/typing_extensions/src_py2/test_typing_extensions.py deleted file mode 100644 index 5c21a6df8..000000000 --- a/typing_extensions/src_py2/test_typing_extensions.py +++ /dev/null @@ -1,456 +0,0 @@ -import sys -import os -import contextlib -import collections -import subprocess -from unittest import TestCase, main - -from typing_extensions import Annotated, NoReturn, ClassVar, IntVar -from typing_extensions import ContextManager, Counter, Deque, DefaultDict -from typing_extensions import NewType, TypeAlias, overload -from typing import Dict, List -import typing -import typing_extensions - - -T = typing.TypeVar('T') -KT = typing.TypeVar('KT') -VT = typing.TypeVar('VT') - - -class BaseTestCase(TestCase): - - def assertIsSubclass(self, cls, class_or_tuple, msg=None): - if not issubclass(cls, class_or_tuple): - message = '%r is not a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): - if issubclass(cls, class_or_tuple): - message = '%r is a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - -class Employee(object): - pass - - -class NoReturnTests(BaseTestCase): - - def test_noreturn_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(42, NoReturn) - - def test_noreturn_subclass_type_error(self): - with self.assertRaises(TypeError): - issubclass(Employee, NoReturn) - with self.assertRaises(TypeError): - issubclass(NoReturn, Employee) - - def test_repr(self): - if hasattr(typing, 'NoReturn'): - self.assertEqual(repr(NoReturn), 'typing.NoReturn') - else: - self.assertEqual(repr(NoReturn), 'typing_extensions.NoReturn') - - def test_not_generic(self): - with self.assertRaises(TypeError): - NoReturn[int] - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class A(NoReturn): - pass - with self.assertRaises(TypeError): - class A(type(NoReturn)): - pass - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - NoReturn() - with self.assertRaises(TypeError): - type(NoReturn)() - - -class ClassVarTests(BaseTestCase): - - def test_basics(self): - with self.assertRaises(TypeError): - ClassVar[1] - with self.assertRaises(TypeError): - ClassVar[int, str] - with self.assertRaises(TypeError): - ClassVar[int][str] - - def test_repr(self): - self.assertEqual(repr(ClassVar), 'typing.ClassVar') - cv = ClassVar[int] - self.assertEqual(repr(cv), 'typing.ClassVar[int]') - cv = ClassVar[Employee] - self.assertEqual(repr(cv), 'typing.ClassVar[%s.Employee]' % __name__) - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(type(ClassVar)): - pass - with self.assertRaises(TypeError): - class C(type(ClassVar[int])): - pass - - def test_cannot_init(self): - with self.assertRaises(TypeError): - ClassVar() - with self.assertRaises(TypeError): - type(ClassVar)() - with self.assertRaises(TypeError): - type(ClassVar[typing.Optional[int]])() - - def test_no_isinstance(self): - with self.assertRaises(TypeError): - isinstance(1, ClassVar[int]) - with self.assertRaises(TypeError): - issubclass(int, ClassVar) - - -class IntVarTests(BaseTestCase): - def test_valid(self): - T_ints = IntVar("T_ints") # noqa - - def test_invalid(self): - with self.assertRaises(TypeError): - T_ints = IntVar("T_ints", int) - with self.assertRaises(TypeError): - T_ints = IntVar("T_ints", bound=int) - with self.assertRaises(TypeError): - T_ints = IntVar("T_ints", covariant=True) # noqa - - -class CollectionsAbcTests(BaseTestCase): - - def test_isinstance_collections(self): - self.assertNotIsInstance(1, collections.Mapping) - self.assertNotIsInstance(1, collections.Iterable) - self.assertNotIsInstance(1, collections.Container) - self.assertNotIsInstance(1, collections.Sized) - with self.assertRaises(TypeError): - isinstance(collections.deque(), typing_extensions.Deque[int]) - with self.assertRaises(TypeError): - issubclass(collections.Counter, typing_extensions.Counter[str]) - - def test_contextmanager(self): - @contextlib.contextmanager - def manager(): - yield 42 - - cm = manager() - self.assertIsInstance(cm, ContextManager) - self.assertNotIsInstance(42, ContextManager) - - with self.assertRaises(TypeError): - isinstance(42, ContextManager[int]) - with self.assertRaises(TypeError): - isinstance(cm, ContextManager[int]) - with self.assertRaises(TypeError): - issubclass(type(cm), ContextManager[int]) - - def test_counter(self): - self.assertIsSubclass(collections.Counter, Counter) - self.assertIs(type(Counter()), collections.Counter) - self.assertIs(type(Counter[T]()), collections.Counter) - self.assertIs(type(Counter[int]()), collections.Counter) - - class A(Counter[int]): pass - class B(Counter[T]): pass - - self.assertIsInstance(A(), collections.Counter) - self.assertIs(type(B[int]()), B) - self.assertEqual(B.__bases__, (typing_extensions.Counter,)) - - def test_deque(self): - self.assertIsSubclass(collections.deque, Deque) - self.assertIs(type(Deque()), collections.deque) - self.assertIs(type(Deque[T]()), collections.deque) - self.assertIs(type(Deque[int]()), collections.deque) - - class A(Deque[int]): pass - class B(Deque[T]): pass - - self.assertIsInstance(A(), collections.deque) - self.assertIs(type(B[int]()), B) - - def test_defaultdict_instantiation(self): - self.assertIsSubclass(collections.defaultdict, DefaultDict) - self.assertIs(type(DefaultDict()), collections.defaultdict) - self.assertIs(type(DefaultDict[KT, VT]()), collections.defaultdict) - self.assertIs(type(DefaultDict[str, int]()), collections.defaultdict) - - class A(DefaultDict[str, int]): pass - class B(DefaultDict[KT, VT]): pass - - self.assertIsInstance(A(), collections.defaultdict) - self.assertIs(type(B[str, int]()), B) - - -class NewTypeTests(BaseTestCase): - - def test_basic(self): - UserId = NewType('UserId', int) - UserName = NewType('UserName', str) - self.assertIsInstance(UserId(5), int) - self.assertIsInstance(UserName('Joe'), type('Joe')) - self.assertEqual(UserId(5) + 1, 6) - - def test_errors(self): - UserId = NewType('UserId', int) - UserName = NewType('UserName', str) - with self.assertRaises(TypeError): - issubclass(UserId, int) - with self.assertRaises(TypeError): - class D(UserName): - pass - - -class OverloadTests(BaseTestCase): - - def test_overload_fails(self): - with self.assertRaises(RuntimeError): - @overload - def blah(): - pass - - blah() - - def test_overload_succeeds(self): - @overload - def blah(): - pass - - def blah(): - pass - - blah() - - -class AnnotatedTests(BaseTestCase): - - def test_repr(self): - self.assertEqual( - repr(Annotated[int, 4, 5]), - "typing_extensions.Annotated[int, 4, 5]" - ) - self.assertEqual( - repr(Annotated[List[int], 4, 5]), - "typing_extensions.Annotated[typing.List[int], 4, 5]" - ) - self.assertEqual(repr(Annotated), "typing_extensions.Annotated") - - def test_flatten(self): - A = Annotated[Annotated[int, 4], 5] - self.assertEqual(A, Annotated[int, 4, 5]) - self.assertEqual(A.__metadata__, (4, 5)) - - def test_specialize(self): - L = Annotated[List[T], "my decoration"] - LI = Annotated[List[int], "my decoration"] - self.assertEqual(L[int], Annotated[List[int], "my decoration"]) - self.assertEqual(L[int].__metadata__, ("my decoration",)) - with self.assertRaises(TypeError): - LI[int] - with self.assertRaises(TypeError): - L[int, float] - - def test_hash_eq(self): - self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4]) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5]) - self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4]) - self.assertEqual( - {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]}, - {Annotated[int, 4, 5], Annotated[T, 4, 5]} - ) - - def test_instantiate(self): - class C: - classvar = 4 - - def __init__(self, x): - self.x = x - - def __eq__(self, other): - if not isinstance(other, C): - return NotImplemented - return other.x == self.x - - A = Annotated[C, "a decoration"] - a = A(5) - c = C(5) - self.assertEqual(a, c) - self.assertEqual(a.x, c.x) - self.assertEqual(a.classvar, c.classvar) - - def test_instantiate_generic(self): - MyCount = Annotated[typing_extensions.Counter[T], "my decoration"] - self.assertEqual(MyCount([4, 4, 5]), {4: 2, 5: 1}) - self.assertEqual(MyCount[int]([4, 4, 5]), {4: 2, 5: 1}) - - def test_cannot_instantiate_forward(self): - A = Annotated["int", (5, 6)] - with self.assertRaises(TypeError): - A(5) - - def test_cannot_instantiate_type_var(self): - A = Annotated[T, (5, 6)] - with self.assertRaises(TypeError): - A(5) - - def test_cannot_getattr_typevar(self): - with self.assertRaises(AttributeError): - Annotated[T, (5, 7)].x - - def test_attr_passthrough(self): - class C: - classvar = 4 - - A = Annotated[C, "a decoration"] - self.assertEqual(A.classvar, 4) - A.x = 5 - self.assertEqual(C.x, 5) - - def test_hash_eq(self): - self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4]) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5]) - self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4]) - self.assertEqual( - {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]}, - {Annotated[int, 4, 5], Annotated[T, 4, 5]} - ) - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(Annotated): - pass - - def test_cannot_check_instance(self): - with self.assertRaises(TypeError): - isinstance(5, Annotated[int, "positive"]) - - def test_cannot_check_subclass(self): - with self.assertRaises(TypeError): - issubclass(int, Annotated[int, "positive"]) - - def test_subst(self): - dec = "a decoration" - dec2 = "another decoration" - - S = Annotated[T, dec2] - self.assertEqual(S[int], Annotated[int, dec2]) - - self.assertEqual(S[Annotated[int, dec]], Annotated[int, dec, dec2]) - L = Annotated[List[T], dec] - - self.assertEqual(L[int], Annotated[List[int], dec]) - with self.assertRaises(TypeError): - L[int, int] - - self.assertEqual(S[L[int]], Annotated[List[int], dec, dec2]) - - D = Annotated[Dict[KT, VT], dec] - self.assertEqual(D[str, int], Annotated[Dict[str, int], dec]) - with self.assertRaises(TypeError): - D[int] - - It = Annotated[int, dec] - with self.assertRaises(TypeError): - It[None] - - LI = L[int] - with self.assertRaises(TypeError): - LI[None] - - def test_annotated_in_other_types(self): - X = List[Annotated[T, 5]] - self.assertEqual(X[int], List[Annotated[int, 5]]) - - -class TypeAliasTests(BaseTestCase): - def test_canonical_usage(self): - Alias = Employee # type: TypeAlias - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - TypeAlias() - - def test_no_isinstance(self): - with self.assertRaises(TypeError): - isinstance(42, TypeAlias) - - def test_no_issubclass(self): - with self.assertRaises(TypeError): - issubclass(Employee, TypeAlias) - - with self.assertRaises(TypeError): - issubclass(TypeAlias, Employee) - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(TypeAlias): - pass - - with self.assertRaises(TypeError): - class C(type(TypeAlias)): - pass - - def test_repr(self): - if hasattr(typing, 'TypeAlias'): - self.assertEqual(repr(TypeAlias), 'typing.TypeAlias') - self.assertEqual(repr(type(TypeAlias)), 'typing.TypeAlias') - else: - self.assertEqual(repr(TypeAlias), 'typing_extensions.TypeAlias') - self.assertEqual(repr(type(TypeAlias)), 'typing_extensions.TypeAlias') - - def test_cannot_subscript(self): - with self.assertRaises(TypeError): - TypeAlias[int] - - -class AllTests(BaseTestCase): - - def test_typing_extensions_includes_standard(self): - a = typing_extensions.__all__ - self.assertIn('ClassVar', a) - self.assertIn('Type', a) - self.assertIn('Counter', a) - self.assertIn('DefaultDict', a) - self.assertIn('Deque', a) - self.assertIn('NewType', a) - self.assertIn('overload', a) - self.assertIn('Text', a) - self.assertIn('TYPE_CHECKING', a) - - def test_typing_extensions_defers_when_possible(self): - exclude = {'overload', 'Text', 'TYPE_CHECKING', 'Final'} - for item in typing_extensions.__all__: - if item not in exclude and hasattr(typing, item): - self.assertIs( - getattr(typing_extensions, item), - getattr(typing, item)) - - def test_typing_extensions_compiles_with_opt(self): - file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'typing_extensions.py') - try: - subprocess.check_output('{} -OO {}'.format(sys.executable, - file_path), - stderr=subprocess.STDOUT, - shell=True) - except subprocess.CalledProcessError: - self.fail('Module does not compile with optimize=2 (-OO flag).') - - -if __name__ == '__main__': - main() diff --git a/typing_extensions/src_py2/typing_extensions.py b/typing_extensions/src_py2/typing_extensions.py deleted file mode 100644 index 62d1e46c7..000000000 --- a/typing_extensions/src_py2/typing_extensions.py +++ /dev/null @@ -1,283 +0,0 @@ -import abc -import typing -from typing import ( # noqa - # These are imported for re-export. - ClassVar, Type, Generic, Callable, GenericMeta, TypingMeta, - Counter, DefaultDict, Deque, TypeVar, Tuple, Final, final, - NewType, overload, Text, TYPE_CHECKING, Literal, TypedDict, Protocol, - SupportsIndex, - runtime_checkable, - # We use internal typing helpers here, but this significantly reduces - # code duplication. (Also this is only until Protocol is in typing.) - _type_vars, _tp_cache, _type_check, -) - -# Please keep __all__ alphabetized within each category. -__all__ = [ - # Super-special typing primitives. - 'ClassVar', - 'Final', - 'Protocol', - 'Type', - 'TypedDict', - - # Concrete collection types. - 'ContextManager', - 'Counter', - 'Deque', - 'DefaultDict', - - # Structural checks, a.k.a. protocols. - 'SupportsIndex', - - # One-off things. - 'final', - 'IntVar', - 'Literal', - 'NewType', - 'overload', - 'runtime_checkable', - 'Text', - 'TYPE_CHECKING', -] - - -if hasattr(typing, 'NoReturn'): - NoReturn = typing.NoReturn -else: - # TODO: Remove once typing.py has been updated - class _NoReturnMeta(typing.TypingMeta): - """Metaclass for NoReturn.""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(_NoReturnMeta, cls).__new__(cls, name, bases, namespace) - return self - - class _NoReturn(typing._FinalTypingBase): - """Special type indicating functions that never return. - Example:: - from typing import NoReturn - def stop() -> NoReturn: - raise Exception('no way') - This type is invalid in other positions, e.g., ``List[NoReturn]`` - will fail in static type checkers. - """ - __metaclass__ = _NoReturnMeta - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("NoReturn cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("NoReturn cannot be used with issubclass().") - - NoReturn = _NoReturn(_root=True) - - -T_co = typing.TypeVar('T_co', covariant=True) - -if hasattr(typing, 'ContextManager'): - ContextManager = typing.ContextManager -else: - # TODO: Remove once typing.py has been updated - class ContextManager(typing.Generic[T_co]): - __slots__ = () - - def __enter__(self): - return self - - @abc.abstractmethod - def __exit__(self, exc_type, exc_value, traceback): - return None - - @classmethod - def __subclasshook__(cls, C): - if cls is ContextManager: - # In Python 3.6+, it is possible to set a method to None to - # explicitly indicate that the class does not implement an ABC - # (https://bugs.python.org/issue25958), but we do not support - # that pattern here because this fallback class is only used - # in Python 3.5 and earlier. - if (any("__enter__" in B.__dict__ for B in C.__mro__) and - any("__exit__" in B.__dict__ for B in C.__mro__)): - return True - return NotImplemented - - -def IntVar(name): - return TypeVar(name) - - -def _is_dunder(name): - """Returns True if name is a __dunder_variable_name__.""" - return len(name) > 4 and name.startswith('__') and name.endswith('__') - - -class AnnotatedMeta(GenericMeta): - """Metaclass for Annotated""" - - def __new__(cls, name, bases, namespace, **kwargs): - if any(b is not object for b in bases): - raise TypeError("Cannot subclass %s" % Annotated) - return super(AnnotatedMeta, cls).__new__(cls, name, bases, namespace, **kwargs) - - @property - def __metadata__(self): - return self._subs_tree()[2] - - def _tree_repr(self, tree): - cls, origin, metadata = tree - if not isinstance(origin, tuple): - tp_repr = typing._type_repr(origin) - else: - tp_repr = origin[0]._tree_repr(origin) - metadata_reprs = ", ".join(repr(arg) for arg in metadata) - return '%s[%s, %s]' % (cls, tp_repr, metadata_reprs) - - def _subs_tree(self, tvars=None, args=None): - if self is Annotated: - return Annotated - res = super(AnnotatedMeta, self)._subs_tree(tvars=tvars, args=args) - # Flatten nested Annotated - if isinstance(res[1], tuple) and res[1][0] is Annotated: - sub_tp = res[1][1] - sub_annot = res[1][2] - return (Annotated, sub_tp, sub_annot + res[2]) - return res - - def _get_cons(self): - """Return the class used to create instance of this type.""" - if self.__origin__ is None: - raise TypeError("Cannot get the underlying type of a non-specialized " - "Annotated type.") - tree = self._subs_tree() - while isinstance(tree, tuple) and tree[0] is Annotated: - tree = tree[1] - if isinstance(tree, tuple): - return tree[0] - else: - return tree - - @_tp_cache - def __getitem__(self, params): - if not isinstance(params, tuple): - params = (params,) - if self.__origin__ is not None: # specializing an instantiated type - return super(AnnotatedMeta, self).__getitem__(params) - elif not isinstance(params, tuple) or len(params) < 2: - raise TypeError("Annotated[...] should be instantiated with at " - "least two arguments (a type and an annotation).") - else: - msg = "Annotated[t, ...]: t must be a type." - tp = typing._type_check(params[0], msg) - metadata = tuple(params[1:]) - return self.__class__( - self.__name__, - self.__bases__, - dict(self.__dict__), - tvars=_type_vars((tp,)), - # Metadata is a tuple so it won't be touched by _replace_args et al. - args=(tp, metadata), - origin=self, - ) - - def __call__(self, *args, **kwargs): - cons = self._get_cons() - result = cons(*args, **kwargs) - try: - result.__orig_class__ = self - except AttributeError: - pass - return result - - def __getattr__(self, attr): - # For simplicity we just don't relay all dunder names - if self.__origin__ is not None and not _is_dunder(attr): - return getattr(self._get_cons(), attr) - raise AttributeError(attr) - - def __setattr__(self, attr, value): - if _is_dunder(attr) or attr.startswith('_abc_'): - super(AnnotatedMeta, self).__setattr__(attr, value) - elif self.__origin__ is None: - raise AttributeError(attr) - else: - setattr(self._get_cons(), attr, value) - - -class Annotated(object): - """Add context specific metadata to a type. - - Example: Annotated[int, runtime_check.Unsigned] indicates to the - hypothetical runtime_check module that this type is an unsigned int. - Every other consumer of this type can ignore this metadata and treat - this type as int. - - The first argument to Annotated must be a valid type, the remaining - arguments are kept as a tuple in the __metadata__ field. - - Details: - - - It's an error to call `Annotated` with less than two arguments. - - Nested Annotated are flattened:: - - Annotated[Annotated[int, Ann1, Ann2], Ann3] == Annotated[int, Ann1, Ann2, Ann3] - - - Instantiating an annotated type is equivalent to instantiating the - underlying type:: - - Annotated[C, Ann1](5) == C(5) - - - Annotated can be used as a generic type alias:: - - Optimized = Annotated[T, runtime.Optimize()] - Optimized[int] == Annotated[int, runtime.Optimize()] - - OptimizedList = Annotated[List[T], runtime.Optimize()] - OptimizedList[int] == Annotated[List[int], runtime.Optimize()] - """ - __metaclass__ = AnnotatedMeta - __slots__ = () - - -class _TypeAliasMeta(typing.TypingMeta): - """Metaclass for TypeAlias""" - - def __new__(cls, name, bases, namespace): - cls.assert_no_subclassing(bases) - self = super(_TypeAliasMeta, cls).__new__(cls, name, bases, namespace) - return self - - def __repr__(self): - return 'typing_extensions.TypeAlias' - - -class _TypeAliasBase(typing._FinalTypingBase): - """Special marker indicating that an assignment should - be recognized as a proper type alias definition by type - checkers. - - For example:: - - Predicate = Callable[..., bool] # type: TypeAlias - - It's invalid when used anywhere except as in the example above. - """ - __metaclass__ = _TypeAliasMeta - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("TypeAlias cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("TypeAlias cannot be used with issubclass().") - - def __repr__(self): - return 'typing_extensions.TypeAlias' - - -TypeAlias = _TypeAliasBase(_root=True) - -# This alias exists for backwards compatibility. -runtime = runtime_checkable diff --git a/typing_extensions/src_py3/test_typing_extensions.py b/typing_extensions/src_py3/test_typing_extensions.py deleted file mode 100644 index 98d02a0b9..000000000 --- a/typing_extensions/src_py3/test_typing_extensions.py +++ /dev/null @@ -1,1942 +0,0 @@ -import sys -import os -import abc -import contextlib -import collections -import pickle -import subprocess -import types -from unittest import TestCase, main, skipUnless, skipIf -from typing import TypeVar, Optional -from typing import T, KT, VT # Not in __all__. -from typing import Tuple, List, Dict, Iterator -from typing import Generic -from typing import no_type_check -from typing_extensions import NoReturn, ClassVar, Final, IntVar, Literal, Type, NewType, TypedDict -from typing_extensions import TypeAlias -try: - from typing_extensions import Protocol, runtime, runtime_checkable -except ImportError: - pass -try: - from typing_extensions import Annotated -except ImportError: - pass -try: - from typing_extensions import get_type_hints -except ImportError: - from typing import get_type_hints - -import typing -import typing_extensions -import collections.abc as collections_abc - -PEP_560 = sys.version_info[:3] >= (3, 7, 0) - -OLD_GENERICS = False -try: - from typing import _type_vars, _next_in_mro, _type_check # noqa -except ImportError: - OLD_GENERICS = True - -# We assume Python versions *below* 3.5.0 will have the most -# up-to-date version of the typing module installed. Since -# the typing module isn't a part of the standard library in older -# versions of Python, those users are likely to have a reasonably -# modern version of `typing` installed from PyPi. -TYPING_LATEST = sys.version_info[:3] < (3, 5, 0) - -# Flags used to mark tests that only apply after a specific -# version of the typing module. -TYPING_3_5_1 = TYPING_LATEST or sys.version_info[:3] >= (3, 5, 1) -TYPING_3_5_3 = TYPING_LATEST or sys.version_info[:3] >= (3, 5, 3) -TYPING_3_6_1 = TYPING_LATEST or sys.version_info[:3] >= (3, 6, 1) -TYPING_3_10_0 = TYPING_LATEST or sys.version_info[:3] >= (3, 10, 0) - -# For typing versions where issubclass(...) and -# isinstance(...) checks are forbidden. -# -# See https://github.com/python/typing/issues/136 -# and https://github.com/python/typing/pull/283 -SUBCLASS_CHECK_FORBIDDEN = TYPING_3_5_3 - -# For typing versions where instantiating collection -# types are allowed. -# -# See https://github.com/python/typing/issues/367 -CAN_INSTANTIATE_COLLECTIONS = TYPING_3_6_1 - -# For Python versions supporting async/await and friends. -ASYNCIO = sys.version_info[:2] >= (3, 5) - -# For checks reliant on Python 3.6 syntax changes (e.g. classvar) -PY36 = sys.version_info[:2] >= (3, 6) - -# Protocols are hard to backport to the original version of typing 3.5.0 -HAVE_PROTOCOLS = sys.version_info[:3] != (3, 5, 0) - - -class BaseTestCase(TestCase): - def assertIsSubclass(self, cls, class_or_tuple, msg=None): - if not issubclass(cls, class_or_tuple): - message = '%r is not a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): - if issubclass(cls, class_or_tuple): - message = '%r is a subclass of %r' % (cls, class_or_tuple) - if msg is not None: - message += ' : %s' % msg - raise self.failureException(message) - - -class Employee: - pass - - -class NoReturnTests(BaseTestCase): - - def test_noreturn_instance_type_error(self): - with self.assertRaises(TypeError): - isinstance(42, NoReturn) - - def test_noreturn_subclass_type_error_1(self): - with self.assertRaises(TypeError): - issubclass(Employee, NoReturn) - - @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") - def test_noreturn_subclass_type_error_2(self): - with self.assertRaises(TypeError): - issubclass(NoReturn, Employee) - - def test_repr(self): - if hasattr(typing, 'NoReturn'): - self.assertEqual(repr(NoReturn), 'typing.NoReturn') - else: - self.assertEqual(repr(NoReturn), 'typing_extensions.NoReturn') - - def test_not_generic(self): - with self.assertRaises(TypeError): - NoReturn[int] - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class A(NoReturn): - pass - if SUBCLASS_CHECK_FORBIDDEN: - with self.assertRaises(TypeError): - class A(type(NoReturn)): - pass - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - NoReturn() - with self.assertRaises(TypeError): - type(NoReturn)() - - -class ClassVarTests(BaseTestCase): - - def test_basics(self): - with self.assertRaises(TypeError): - ClassVar[1] - with self.assertRaises(TypeError): - ClassVar[int, str] - with self.assertRaises(TypeError): - ClassVar[int][str] - - def test_repr(self): - if hasattr(typing, 'ClassVar'): - mod_name = 'typing' - else: - mod_name = 'typing_extensions' - self.assertEqual(repr(ClassVar), mod_name + '.ClassVar') - cv = ClassVar[int] - self.assertEqual(repr(cv), mod_name + '.ClassVar[int]') - cv = ClassVar[Employee] - self.assertEqual(repr(cv), mod_name + '.ClassVar[%s.Employee]' % __name__) - - @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(type(ClassVar)): - pass - with self.assertRaises(TypeError): - class C(type(ClassVar[int])): - pass - - def test_cannot_init(self): - with self.assertRaises(TypeError): - ClassVar() - with self.assertRaises(TypeError): - type(ClassVar)() - with self.assertRaises(TypeError): - type(ClassVar[Optional[int]])() - - def test_no_isinstance(self): - with self.assertRaises(TypeError): - isinstance(1, ClassVar[int]) - with self.assertRaises(TypeError): - issubclass(int, ClassVar) - - -class FinalTests(BaseTestCase): - - def test_basics(self): - with self.assertRaises(TypeError): - Final[1] - with self.assertRaises(TypeError): - Final[int, str] - with self.assertRaises(TypeError): - Final[int][str] - - def test_repr(self): - if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): - mod_name = 'typing' - else: - mod_name = 'typing_extensions' - self.assertEqual(repr(Final), mod_name + '.Final') - cv = Final[int] - self.assertEqual(repr(cv), mod_name + '.Final[int]') - cv = Final[Employee] - self.assertEqual(repr(cv), mod_name + '.Final[%s.Employee]' % __name__) - - @skipUnless(SUBCLASS_CHECK_FORBIDDEN, "Behavior added in typing 3.5.3") - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(type(Final)): - pass - with self.assertRaises(TypeError): - class C(type(Final[int])): - pass - - def test_cannot_init(self): - with self.assertRaises(TypeError): - Final() - with self.assertRaises(TypeError): - type(Final)() - with self.assertRaises(TypeError): - type(Final[Optional[int]])() - - def test_no_isinstance(self): - with self.assertRaises(TypeError): - isinstance(1, Final[int]) - with self.assertRaises(TypeError): - issubclass(int, Final) - - -class IntVarTests(BaseTestCase): - def test_valid(self): - T_ints = IntVar("T_ints") # noqa - - def test_invalid(self): - with self.assertRaises(TypeError): - T_ints = IntVar("T_ints", int) - with self.assertRaises(TypeError): - T_ints = IntVar("T_ints", bound=int) - with self.assertRaises(TypeError): - T_ints = IntVar("T_ints", covariant=True) # noqa - - -class LiteralTests(BaseTestCase): - def test_basics(self): - Literal[1] - Literal[1, 2, 3] - Literal["x", "y", "z"] - Literal[None] - - def test_illegal_parameters_do_not_raise_runtime_errors(self): - # Type checkers should reject these types, but we do not - # raise errors at runtime to maintain maximum flexibility - Literal[int] - Literal[Literal[1, 2], Literal[4, 5]] - Literal[3j + 2, ..., ()] - Literal[b"foo", u"bar"] - Literal[{"foo": 3, "bar": 4}] - Literal[T] - - def test_literals_inside_other_types(self): - List[Literal[1, 2, 3]] - List[Literal[("foo", "bar", "baz")]] - - def test_repr(self): - if hasattr(typing, 'Literal'): - mod_name = 'typing' - else: - mod_name = 'typing_extensions' - self.assertEqual(repr(Literal[1]), mod_name + ".Literal[1]") - self.assertEqual(repr(Literal[1, True, "foo"]), mod_name + ".Literal[1, True, 'foo']") - self.assertEqual(repr(Literal[int]), mod_name + ".Literal[int]") - self.assertEqual(repr(Literal), mod_name + ".Literal") - self.assertEqual(repr(Literal[None]), mod_name + ".Literal[None]") - - def test_cannot_init(self): - with self.assertRaises(TypeError): - Literal() - with self.assertRaises(TypeError): - Literal[1]() - with self.assertRaises(TypeError): - type(Literal)() - with self.assertRaises(TypeError): - type(Literal[1])() - - def test_no_isinstance_or_issubclass(self): - with self.assertRaises(TypeError): - isinstance(1, Literal[1]) - with self.assertRaises(TypeError): - isinstance(int, Literal[1]) - with self.assertRaises(TypeError): - issubclass(1, Literal[1]) - with self.assertRaises(TypeError): - issubclass(int, Literal[1]) - - def test_no_subclassing(self): - with self.assertRaises(TypeError): - class Foo(Literal[1]): pass - with self.assertRaises(TypeError): - class Bar(Literal): pass - - def test_no_multiple_subscripts(self): - with self.assertRaises(TypeError): - Literal[1][1] - - -class OverloadTests(BaseTestCase): - - def test_overload_fails(self): - from typing_extensions import overload - - with self.assertRaises(RuntimeError): - - @overload - def blah(): - pass - - blah() - - def test_overload_succeeds(self): - from typing_extensions import overload - - @overload - def blah(): - pass - - def blah(): - pass - - blah() - - -ASYNCIO_TESTS = """ -from typing import Iterable -from typing_extensions import Awaitable, AsyncIterator - -T_a = TypeVar('T_a') - -class AwaitableWrapper(Awaitable[T_a]): - - def __init__(self, value): - self.value = value - - def __await__(self) -> typing.Iterator[T_a]: - yield - return self.value - -class AsyncIteratorWrapper(AsyncIterator[T_a]): - - def __init__(self, value: Iterable[T_a]): - self.value = value - - def __aiter__(self) -> AsyncIterator[T_a]: - return self - - async def __anext__(self) -> T_a: - data = await self.value - if data: - return data - else: - raise StopAsyncIteration - -class ACM: - async def __aenter__(self) -> int: - return 42 - async def __aexit__(self, etype, eval, tb): - return None -""" - -if ASYNCIO: - try: - exec(ASYNCIO_TESTS) - except ImportError: - ASYNCIO = False -else: - # fake names for the sake of static analysis - asyncio = None - AwaitableWrapper = AsyncIteratorWrapper = ACM = object - -PY36_TESTS = """ -from test import ann_module, ann_module2, ann_module3 -from typing_extensions import AsyncContextManager -from typing import NamedTuple - -class A: - y: float -class B(A): - x: ClassVar[Optional['B']] = None - y: int - b: int -class CSub(B): - z: ClassVar['CSub'] = B() -class G(Generic[T]): - lst: ClassVar[List[T]] = [] - -class Loop: - attr: Final['Loop'] - -class NoneAndForward: - parent: 'NoneAndForward' - meaning: None - -class XRepr(NamedTuple): - x: int - y: int = 1 - def __str__(self): - return f'{self.x} -> {self.y}' - def __add__(self, other): - return 0 - -@runtime -class HasCallProtocol(Protocol): - __call__: typing.Callable - - -async def g_with(am: AsyncContextManager[int]): - x: int - async with am as x: - return x - -try: - g_with(ACM()).send(None) -except StopIteration as e: - assert e.args[0] == 42 - -Label = TypedDict('Label', [('label', str)]) - -class Point2D(TypedDict): - x: int - y: int - -class Point2Dor3D(Point2D, total=False): - z: int - -class LabelPoint2D(Point2D, Label): ... - -class Options(TypedDict, total=False): - log_level: int - log_path: str - -class BaseAnimal(TypedDict): - name: str - -class Animal(BaseAnimal, total=False): - voice: str - tail: bool - -class Cat(Animal): - fur_color: str -""" - -if PY36: - exec(PY36_TESTS) -else: - # fake names for the sake of static analysis - ann_module = ann_module2 = ann_module3 = None - A = B = CSub = G = CoolEmployee = CoolEmployeeWithDefault = object - XMeth = XRepr = HasCallProtocol = NoneAndForward = Loop = object - Point2D = Point2Dor3D = LabelPoint2D = Options = object - BaseAnimal = Animal = Cat = object - -gth = get_type_hints - - -class GetTypeHintTests(BaseTestCase): - @skipUnless(PY36, 'Python 3.6 required') - def test_get_type_hints_modules(self): - ann_module_type_hints = {1: 2, 'f': Tuple[int, int], 'x': int, 'y': str} - self.assertEqual(gth(ann_module), ann_module_type_hints) - self.assertEqual(gth(ann_module2), {}) - self.assertEqual(gth(ann_module3), {}) - - @skipUnless(PY36, 'Python 3.6 required') - def test_get_type_hints_classes(self): - self.assertEqual(gth(ann_module.C, ann_module.__dict__), - {'y': Optional[ann_module.C]}) - self.assertIsInstance(gth(ann_module.j_class), dict) - self.assertEqual(gth(ann_module.M), {'123': 123, 'o': type}) - self.assertEqual(gth(ann_module.D), - {'j': str, 'k': str, 'y': Optional[ann_module.C]}) - self.assertEqual(gth(ann_module.Y), {'z': int}) - self.assertEqual(gth(ann_module.h_class), - {'y': Optional[ann_module.C]}) - self.assertEqual(gth(ann_module.S), {'x': str, 'y': str}) - self.assertEqual(gth(ann_module.foo), {'x': int}) - self.assertEqual(gth(NoneAndForward, globals()), - {'parent': NoneAndForward, 'meaning': type(None)}) - - @skipUnless(PY36, 'Python 3.6 required') - def test_respect_no_type_check(self): - @no_type_check - class NoTpCheck: - class Inn: - def __init__(self, x: 'not a type'): ... # noqa - self.assertTrue(NoTpCheck.__no_type_check__) - self.assertTrue(NoTpCheck.Inn.__init__.__no_type_check__) - self.assertEqual(gth(ann_module2.NTC.meth), {}) - class ABase(Generic[T]): - def meth(x: int): ... - @no_type_check - class Der(ABase): ... - self.assertEqual(gth(ABase.meth), {'x': int}) - - @skipUnless(PY36, 'Python 3.6 required') - def test_get_type_hints_ClassVar(self): - self.assertEqual(gth(ann_module2.CV, ann_module2.__dict__), - {'var': ClassVar[ann_module2.CV]}) - self.assertEqual(gth(B, globals()), - {'y': int, 'x': ClassVar[Optional[B]], 'b': int}) - self.assertEqual(gth(CSub, globals()), - {'z': ClassVar[CSub], 'y': int, 'b': int, - 'x': ClassVar[Optional[B]]}) - self.assertEqual(gth(G), {'lst': ClassVar[List[T]]}) - - @skipUnless(PY36, 'Python 3.6 required') - def test_final_forward_ref(self): - self.assertEqual(gth(Loop, globals())['attr'], Final[Loop]) - self.assertNotEqual(gth(Loop, globals())['attr'], Final[int]) - self.assertNotEqual(gth(Loop, globals())['attr'], Final) - - -class CollectionsAbcTests(BaseTestCase): - - def test_isinstance_collections(self): - self.assertNotIsInstance(1, collections_abc.Mapping) - self.assertNotIsInstance(1, collections_abc.Iterable) - self.assertNotIsInstance(1, collections_abc.Container) - self.assertNotIsInstance(1, collections_abc.Sized) - if SUBCLASS_CHECK_FORBIDDEN: - with self.assertRaises(TypeError): - isinstance(collections.deque(), typing_extensions.Deque[int]) - with self.assertRaises(TypeError): - issubclass(collections.Counter, typing_extensions.Counter[str]) - - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') - def test_awaitable(self): - ns = {} - exec( - "async def foo() -> typing_extensions.Awaitable[int]:\n" - " return await AwaitableWrapper(42)\n", - globals(), ns) - foo = ns['foo'] - g = foo() - self.assertIsInstance(g, typing_extensions.Awaitable) - self.assertNotIsInstance(foo, typing_extensions.Awaitable) - g.send(None) # Run foo() till completion, to avoid warning. - - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') - def test_coroutine(self): - ns = {} - exec( - "async def foo():\n" - " return\n", - globals(), ns) - foo = ns['foo'] - g = foo() - self.assertIsInstance(g, typing_extensions.Coroutine) - with self.assertRaises(TypeError): - isinstance(g, typing_extensions.Coroutine[int]) - self.assertNotIsInstance(foo, typing_extensions.Coroutine) - try: - g.send(None) - except StopIteration: - pass - - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') - def test_async_iterable(self): - base_it = range(10) # type: Iterator[int] - it = AsyncIteratorWrapper(base_it) - self.assertIsInstance(it, typing_extensions.AsyncIterable) - self.assertIsInstance(it, typing_extensions.AsyncIterable) - self.assertNotIsInstance(42, typing_extensions.AsyncIterable) - - @skipUnless(ASYNCIO, 'Python 3.5 and multithreading required') - def test_async_iterator(self): - base_it = range(10) # type: Iterator[int] - it = AsyncIteratorWrapper(base_it) - if TYPING_3_5_1: - self.assertIsInstance(it, typing_extensions.AsyncIterator) - self.assertNotIsInstance(42, typing_extensions.AsyncIterator) - - def test_deque(self): - self.assertIsSubclass(collections.deque, typing_extensions.Deque) - class MyDeque(typing_extensions.Deque[int]): ... - self.assertIsInstance(MyDeque(), collections.deque) - - def test_counter(self): - self.assertIsSubclass(collections.Counter, typing_extensions.Counter) - - @skipUnless(CAN_INSTANTIATE_COLLECTIONS, "Behavior added in typing 3.6.1") - def test_defaultdict_instantiation(self): - self.assertIs( - type(typing_extensions.DefaultDict()), - collections.defaultdict) - self.assertIs( - type(typing_extensions.DefaultDict[KT, VT]()), - collections.defaultdict) - self.assertIs( - type(typing_extensions.DefaultDict[str, int]()), - collections.defaultdict) - - def test_defaultdict_subclass(self): - - class MyDefDict(typing_extensions.DefaultDict[str, int]): - pass - - dd = MyDefDict() - self.assertIsInstance(dd, MyDefDict) - - self.assertIsSubclass(MyDefDict, collections.defaultdict) - if TYPING_3_5_3: - self.assertNotIsSubclass(collections.defaultdict, MyDefDict) - - def test_chainmap_instantiation(self): - self.assertIs(type(typing_extensions.ChainMap()), collections.ChainMap) - self.assertIs(type(typing_extensions.ChainMap[KT, VT]()), collections.ChainMap) - self.assertIs(type(typing_extensions.ChainMap[str, int]()), collections.ChainMap) - class CM(typing_extensions.ChainMap[KT, VT]): ... - if TYPING_3_5_3: - self.assertIs(type(CM[int, str]()), CM) - - def test_chainmap_subclass(self): - - class MyChainMap(typing_extensions.ChainMap[str, int]): - pass - - cm = MyChainMap() - self.assertIsInstance(cm, MyChainMap) - - self.assertIsSubclass(MyChainMap, collections.ChainMap) - if TYPING_3_5_3: - self.assertNotIsSubclass(collections.ChainMap, MyChainMap) - - def test_deque_instantiation(self): - self.assertIs(type(typing_extensions.Deque()), collections.deque) - self.assertIs(type(typing_extensions.Deque[T]()), collections.deque) - self.assertIs(type(typing_extensions.Deque[int]()), collections.deque) - class D(typing_extensions.Deque[T]): ... - if TYPING_3_5_3: - self.assertIs(type(D[int]()), D) - - def test_counter_instantiation(self): - self.assertIs(type(typing_extensions.Counter()), collections.Counter) - self.assertIs(type(typing_extensions.Counter[T]()), collections.Counter) - self.assertIs(type(typing_extensions.Counter[int]()), collections.Counter) - class C(typing_extensions.Counter[T]): ... - if TYPING_3_5_3: - self.assertIs(type(C[int]()), C) - if not PEP_560: - self.assertEqual(C.__bases__, (typing_extensions.Counter,)) - else: - self.assertEqual(C.__bases__, (collections.Counter, typing.Generic)) - - def test_counter_subclass_instantiation(self): - - class MyCounter(typing_extensions.Counter[int]): - pass - - d = MyCounter() - self.assertIsInstance(d, MyCounter) - self.assertIsInstance(d, collections.Counter) - if TYPING_3_5_1: - self.assertIsInstance(d, typing_extensions.Counter) - - @skipUnless(PY36, 'Python 3.6 required') - def test_async_generator(self): - ns = {} - exec("async def f():\n" - " yield 42\n", globals(), ns) - g = ns['f']() - self.assertIsSubclass(type(g), typing_extensions.AsyncGenerator) - - @skipUnless(PY36, 'Python 3.6 required') - def test_no_async_generator_instantiation(self): - with self.assertRaises(TypeError): - typing_extensions.AsyncGenerator() - with self.assertRaises(TypeError): - typing_extensions.AsyncGenerator[T, T]() - with self.assertRaises(TypeError): - typing_extensions.AsyncGenerator[int, int]() - - @skipUnless(PY36, 'Python 3.6 required') - def test_subclassing_async_generator(self): - class G(typing_extensions.AsyncGenerator[int, int]): - def asend(self, value): - pass - def athrow(self, typ, val=None, tb=None): - pass - - ns = {} - exec('async def g(): yield 0', globals(), ns) - g = ns['g'] - self.assertIsSubclass(G, typing_extensions.AsyncGenerator) - self.assertIsSubclass(G, typing_extensions.AsyncIterable) - self.assertIsSubclass(G, collections_abc.AsyncGenerator) - self.assertIsSubclass(G, collections_abc.AsyncIterable) - self.assertNotIsSubclass(type(g), G) - - instance = G() - self.assertIsInstance(instance, typing_extensions.AsyncGenerator) - self.assertIsInstance(instance, typing_extensions.AsyncIterable) - self.assertIsInstance(instance, collections_abc.AsyncGenerator) - self.assertIsInstance(instance, collections_abc.AsyncIterable) - self.assertNotIsInstance(type(g), G) - self.assertNotIsInstance(g, G) - - -class OtherABCTests(BaseTestCase): - - def test_contextmanager(self): - @contextlib.contextmanager - def manager(): - yield 42 - - cm = manager() - self.assertIsInstance(cm, typing_extensions.ContextManager) - self.assertNotIsInstance(42, typing_extensions.ContextManager) - - @skipUnless(ASYNCIO, 'Python 3.5 required') - def test_async_contextmanager(self): - class NotACM: - pass - self.assertIsInstance(ACM(), typing_extensions.AsyncContextManager) - self.assertNotIsInstance(NotACM(), typing_extensions.AsyncContextManager) - @contextlib.contextmanager - def manager(): - yield 42 - - cm = manager() - self.assertNotIsInstance(cm, typing_extensions.AsyncContextManager) - if TYPING_3_5_3: - self.assertEqual(typing_extensions.AsyncContextManager[int].__args__, (int,)) - if TYPING_3_6_1: - with self.assertRaises(TypeError): - isinstance(42, typing_extensions.AsyncContextManager[int]) - with self.assertRaises(TypeError): - typing_extensions.AsyncContextManager[int, str] - - -class TypeTests(BaseTestCase): - - def test_type_basic(self): - - class User: pass - class BasicUser(User): pass - class ProUser(User): pass - - def new_user(user_class: Type[User]) -> User: - return user_class() - - new_user(BasicUser) - - def test_type_typevar(self): - - class User: pass - class BasicUser(User): pass - class ProUser(User): pass - - U = TypeVar('U', bound=User) - - def new_user(user_class: Type[U]) -> U: - return user_class() - - new_user(BasicUser) - - @skipUnless(sys.version_info[:3] != (3, 5, 2), - 'Python 3.5.2 has a somewhat buggy Type impl') - def test_type_optional(self): - A = Optional[Type[BaseException]] - - def foo(a: A) -> Optional[BaseException]: - if a is None: - return None - else: - return a() - - assert isinstance(foo(KeyboardInterrupt), KeyboardInterrupt) - assert foo(None) is None - - -class NewTypeTests(BaseTestCase): - - def test_basic(self): - UserId = NewType('UserId', int) - UserName = NewType('UserName', str) - self.assertIsInstance(UserId(5), int) - self.assertIsInstance(UserName('Joe'), str) - self.assertEqual(UserId(5) + 1, 6) - - def test_errors(self): - UserId = NewType('UserId', int) - UserName = NewType('UserName', str) - with self.assertRaises(TypeError): - issubclass(UserId, int) - with self.assertRaises(TypeError): - class D(UserName): - pass - - -PY36_PROTOCOL_TESTS = """ -class Coordinate(Protocol): - x: int - y: int - -@runtime -class Point(Coordinate, Protocol): - label: str - -class MyPoint: - x: int - y: int - label: str - -class XAxis(Protocol): - x: int - -class YAxis(Protocol): - y: int - -@runtime -class Position(XAxis, YAxis, Protocol): - pass - -@runtime -class Proto(Protocol): - attr: int - def meth(self, arg: str) -> int: - ... - -class Concrete(Proto): - pass - -class Other: - attr: int = 1 - def meth(self, arg: str) -> int: - if arg == 'this': - return 1 - return 0 - -class NT(NamedTuple): - x: int - y: int -""" - -if PY36: - exec(PY36_PROTOCOL_TESTS) -else: - # fake names for the sake of static analysis - Coordinate = Point = MyPoint = BadPoint = NT = object - XAxis = YAxis = Position = Proto = Concrete = Other = object - - -if HAVE_PROTOCOLS: - class ProtocolTests(BaseTestCase): - - def test_basic_protocol(self): - @runtime - class P(Protocol): - def meth(self): - pass - class C: pass - class D: - def meth(self): - pass - def f(): - pass - self.assertIsSubclass(D, P) - self.assertIsInstance(D(), P) - self.assertNotIsSubclass(C, P) - self.assertNotIsInstance(C(), P) - self.assertNotIsSubclass(types.FunctionType, P) - self.assertNotIsInstance(f, P) - - def test_everything_implements_empty_protocol(self): - @runtime - class Empty(Protocol): pass - class C: pass - def f(): - pass - for thing in (object, type, tuple, C, types.FunctionType): - self.assertIsSubclass(thing, Empty) - for thing in (object(), 1, (), typing, f): - self.assertIsInstance(thing, Empty) - - @skipUnless(PY36, 'Python 3.6 required') - def test_function_implements_protocol(self): - def f(): - pass - self.assertIsInstance(f, HasCallProtocol) - - def test_no_inheritance_from_nominal(self): - class C: pass - class BP(Protocol): pass - with self.assertRaises(TypeError): - class P(C, Protocol): - pass - with self.assertRaises(TypeError): - class P(Protocol, C): - pass - with self.assertRaises(TypeError): - class P(BP, C, Protocol): - pass - class D(BP, C): pass - class E(C, BP): pass - self.assertNotIsInstance(D(), E) - self.assertNotIsInstance(E(), D) - - def test_no_instantiation(self): - class P(Protocol): pass - with self.assertRaises(TypeError): - P() - class C(P): pass - self.assertIsInstance(C(), C) - T = TypeVar('T') - class PG(Protocol[T]): pass - with self.assertRaises(TypeError): - PG() - with self.assertRaises(TypeError): - PG[int]() - with self.assertRaises(TypeError): - PG[T]() - class CG(PG[T]): pass - self.assertIsInstance(CG[int](), CG) - - def test_cannot_instantiate_abstract(self): - @runtime - class P(Protocol): - @abc.abstractmethod - def ameth(self) -> int: - raise NotImplementedError - class B(P): - pass - class C(B): - def ameth(self) -> int: - return 26 - with self.assertRaises(TypeError): - B() - self.assertIsInstance(C(), P) - - def test_subprotocols_extending(self): - class P1(Protocol): - def meth1(self): - pass - @runtime - class P2(P1, Protocol): - def meth2(self): - pass - class C: - def meth1(self): - pass - def meth2(self): - pass - class C1: - def meth1(self): - pass - class C2: - def meth2(self): - pass - self.assertNotIsInstance(C1(), P2) - self.assertNotIsInstance(C2(), P2) - self.assertNotIsSubclass(C1, P2) - self.assertNotIsSubclass(C2, P2) - self.assertIsInstance(C(), P2) - self.assertIsSubclass(C, P2) - - def test_subprotocols_merging(self): - class P1(Protocol): - def meth1(self): - pass - class P2(Protocol): - def meth2(self): - pass - @runtime - class P(P1, P2, Protocol): - pass - class C: - def meth1(self): - pass - def meth2(self): - pass - class C1: - def meth1(self): - pass - class C2: - def meth2(self): - pass - self.assertNotIsInstance(C1(), P) - self.assertNotIsInstance(C2(), P) - self.assertNotIsSubclass(C1, P) - self.assertNotIsSubclass(C2, P) - self.assertIsInstance(C(), P) - self.assertIsSubclass(C, P) - - def test_protocols_issubclass(self): - T = TypeVar('T') - @runtime - class P(Protocol): - def x(self): ... - @runtime - class PG(Protocol[T]): - def x(self): ... - class BadP(Protocol): - def x(self): ... - class BadPG(Protocol[T]): - def x(self): ... - class C: - def x(self): ... - self.assertIsSubclass(C, P) - self.assertIsSubclass(C, PG) - self.assertIsSubclass(BadP, PG) - if not PEP_560: - self.assertIsSubclass(PG[int], PG) - self.assertIsSubclass(BadPG[int], P) - self.assertIsSubclass(BadPG[T], PG) - with self.assertRaises(TypeError): - issubclass(C, PG[T]) - with self.assertRaises(TypeError): - issubclass(C, PG[C]) - with self.assertRaises(TypeError): - issubclass(C, BadP) - with self.assertRaises(TypeError): - issubclass(C, BadPG) - with self.assertRaises(TypeError): - issubclass(P, PG[T]) - with self.assertRaises(TypeError): - issubclass(PG, PG[int]) - - def test_protocols_issubclass_non_callable(self): - class C: - x = 1 - @runtime - class PNonCall(Protocol): - x = 1 - with self.assertRaises(TypeError): - issubclass(C, PNonCall) - self.assertIsInstance(C(), PNonCall) - PNonCall.register(C) - with self.assertRaises(TypeError): - issubclass(C, PNonCall) - self.assertIsInstance(C(), PNonCall) - # check that non-protocol subclasses are not affected - class D(PNonCall): ... - self.assertNotIsSubclass(C, D) - self.assertNotIsInstance(C(), D) - D.register(C) - self.assertIsSubclass(C, D) - self.assertIsInstance(C(), D) - with self.assertRaises(TypeError): - issubclass(D, PNonCall) - - def test_protocols_isinstance(self): - T = TypeVar('T') - @runtime - class P(Protocol): - def meth(x): ... - @runtime - class PG(Protocol[T]): - def meth(x): ... - class BadP(Protocol): - def meth(x): ... - class BadPG(Protocol[T]): - def meth(x): ... - class C: - def meth(x): ... - self.assertIsInstance(C(), P) - self.assertIsInstance(C(), PG) - with self.assertRaises(TypeError): - isinstance(C(), PG[T]) - with self.assertRaises(TypeError): - isinstance(C(), PG[C]) - with self.assertRaises(TypeError): - isinstance(C(), BadP) - with self.assertRaises(TypeError): - isinstance(C(), BadPG) - - @skipUnless(PY36, 'Python 3.6 required') - def test_protocols_isinstance_py36(self): - class APoint: - def __init__(self, x, y, label): - self.x = x - self.y = y - self.label = label - class BPoint: - label = 'B' - def __init__(self, x, y): - self.x = x - self.y = y - class C: - def __init__(self, attr): - self.attr = attr - def meth(self, arg): - return 0 - class Bad: pass - self.assertIsInstance(APoint(1, 2, 'A'), Point) - self.assertIsInstance(BPoint(1, 2), Point) - self.assertNotIsInstance(MyPoint(), Point) - self.assertIsInstance(BPoint(1, 2), Position) - self.assertIsInstance(Other(), Proto) - self.assertIsInstance(Concrete(), Proto) - self.assertIsInstance(C(42), Proto) - self.assertNotIsInstance(Bad(), Proto) - self.assertNotIsInstance(Bad(), Point) - self.assertNotIsInstance(Bad(), Position) - self.assertNotIsInstance(Bad(), Concrete) - self.assertNotIsInstance(Other(), Concrete) - self.assertIsInstance(NT(1, 2), Position) - - def test_protocols_isinstance_init(self): - T = TypeVar('T') - @runtime - class P(Protocol): - x = 1 - @runtime - class PG(Protocol[T]): - x = 1 - class C: - def __init__(self, x): - self.x = x - self.assertIsInstance(C(1), P) - self.assertIsInstance(C(1), PG) - - def test_protocols_support_register(self): - @runtime - class P(Protocol): - x = 1 - class PM(Protocol): - def meth(self): pass - class D(PM): pass - class C: pass - D.register(C) - P.register(C) - self.assertIsInstance(C(), P) - self.assertIsInstance(C(), D) - - def test_none_on_non_callable_doesnt_block_implementation(self): - @runtime - class P(Protocol): - x = 1 - class A: - x = 1 - class B(A): - x = None - class C: - def __init__(self): - self.x = None - self.assertIsInstance(B(), P) - self.assertIsInstance(C(), P) - - def test_none_on_callable_blocks_implementation(self): - @runtime - class P(Protocol): - def x(self): ... - class A: - def x(self): ... - class B(A): - x = None - class C: - def __init__(self): - self.x = None - self.assertNotIsInstance(B(), P) - self.assertNotIsInstance(C(), P) - - def test_non_protocol_subclasses(self): - class P(Protocol): - x = 1 - @runtime - class PR(Protocol): - def meth(self): pass - class NonP(P): - x = 1 - class NonPR(PR): pass - class C: - x = 1 - class D: - def meth(self): pass - self.assertNotIsInstance(C(), NonP) - self.assertNotIsInstance(D(), NonPR) - self.assertNotIsSubclass(C, NonP) - self.assertNotIsSubclass(D, NonPR) - self.assertIsInstance(NonPR(), PR) - self.assertIsSubclass(NonPR, PR) - - def test_custom_subclasshook(self): - class P(Protocol): - x = 1 - class OKClass: pass - class BadClass: - x = 1 - class C(P): - @classmethod - def __subclasshook__(cls, other): - return other.__name__.startswith("OK") - self.assertIsInstance(OKClass(), C) - self.assertNotIsInstance(BadClass(), C) - self.assertIsSubclass(OKClass, C) - self.assertNotIsSubclass(BadClass, C) - - def test_issubclass_fails_correctly(self): - @runtime - class P(Protocol): - x = 1 - class C: pass - with self.assertRaises(TypeError): - issubclass(C(), P) - - @skipUnless(not OLD_GENERICS, "New style generics required") - def test_defining_generic_protocols(self): - T = TypeVar('T') - S = TypeVar('S') - @runtime - class PR(Protocol[T, S]): - def meth(self): pass - class P(PR[int, T], Protocol[T]): - y = 1 - self.assertIsSubclass(PR[int, T], PR) - self.assertIsSubclass(P[str], PR) - with self.assertRaises(TypeError): - PR[int] - with self.assertRaises(TypeError): - P[int, str] - with self.assertRaises(TypeError): - PR[int, 1] - if TYPING_3_5_3: - with self.assertRaises(TypeError): - PR[int, ClassVar] - class C(PR[int, T]): pass - self.assertIsInstance(C[str](), C) - - def test_defining_generic_protocols_old_style(self): - T = TypeVar('T') - S = TypeVar('S') - @runtime - class PR(Protocol, Generic[T, S]): - def meth(self): pass - class P(PR[int, str], Protocol): - y = 1 - if not PEP_560: - self.assertIsSubclass(PR[int, str], PR) - else: - with self.assertRaises(TypeError): - self.assertIsSubclass(PR[int, str], PR) - self.assertIsSubclass(P, PR) - with self.assertRaises(TypeError): - PR[int] - if not TYPING_3_10_0: - with self.assertRaises(TypeError): - PR[int, 1] - class P1(Protocol, Generic[T]): - def bar(self, x: T) -> str: ... - class P2(Generic[T], Protocol): - def bar(self, x: T) -> str: ... - @runtime - class PSub(P1[str], Protocol): - x = 1 - class Test: - x = 1 - def bar(self, x: str) -> str: - return x - self.assertIsInstance(Test(), PSub) - if TYPING_3_5_3 and not TYPING_3_10_0: - with self.assertRaises(TypeError): - PR[int, ClassVar] - - def test_init_called(self): - T = TypeVar('T') - class P(Protocol[T]): pass - class C(P[T]): - def __init__(self): - self.test = 'OK' - self.assertEqual(C[int]().test, 'OK') - - @skipUnless(not OLD_GENERICS, "New style generics required") - def test_protocols_bad_subscripts(self): - T = TypeVar('T') - S = TypeVar('S') - with self.assertRaises(TypeError): - class P(Protocol[T, T]): pass - with self.assertRaises(TypeError): - class P(Protocol[int]): pass - with self.assertRaises(TypeError): - class P(Protocol[T], Protocol[S]): pass - with self.assertRaises(TypeError): - class P(typing.Mapping[T, S], Protocol[T]): pass - - @skipUnless(TYPING_3_5_3, 'New style __repr__ and __eq__ only') - def test_generic_protocols_repr(self): - T = TypeVar('T') - S = TypeVar('S') - class P(Protocol[T, S]): pass - # After PEP 560 unsubscripted generics have a standard repr. - if not PEP_560: - self.assertTrue(repr(P).endswith('P')) - self.assertTrue(repr(P[T, S]).endswith('P[~T, ~S]')) - self.assertTrue(repr(P[int, str]).endswith('P[int, str]')) - - @skipUnless(TYPING_3_5_3, 'New style __repr__ and __eq__ only') - def test_generic_protocols_eq(self): - T = TypeVar('T') - S = TypeVar('S') - class P(Protocol[T, S]): pass - self.assertEqual(P, P) - self.assertEqual(P[int, T], P[int, T]) - self.assertEqual(P[T, T][Tuple[T, S]][int, str], - P[Tuple[int, str], Tuple[int, str]]) - - @skipUnless(not OLD_GENERICS, "New style generics required") - def test_generic_protocols_special_from_generic(self): - T = TypeVar('T') - class P(Protocol[T]): pass - self.assertEqual(P.__parameters__, (T,)) - self.assertIs(P.__args__, None) - self.assertIs(P.__origin__, None) - self.assertEqual(P[int].__parameters__, ()) - self.assertEqual(P[int].__args__, (int,)) - self.assertIs(P[int].__origin__, P) - - def test_generic_protocols_special_from_protocol(self): - @runtime - class PR(Protocol): - x = 1 - class P(Protocol): - def meth(self): - pass - T = TypeVar('T') - class PG(Protocol[T]): - x = 1 - def meth(self): - pass - self.assertTrue(P._is_protocol) - self.assertTrue(PR._is_protocol) - self.assertTrue(PG._is_protocol) - if hasattr(typing, 'Protocol'): - self.assertFalse(P._is_runtime_protocol) - else: - with self.assertRaises(AttributeError): - self.assertFalse(P._is_runtime_protocol) - self.assertTrue(PR._is_runtime_protocol) - self.assertTrue(PG[int]._is_protocol) - self.assertEqual(typing_extensions._get_protocol_attrs(P), {'meth'}) - self.assertEqual(typing_extensions._get_protocol_attrs(PR), {'x'}) - self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG)), - frozenset({'x', 'meth'})) - if not PEP_560: - self.assertEqual(frozenset(typing_extensions._get_protocol_attrs(PG[int])), - frozenset({'x', 'meth'})) - - def test_no_runtime_deco_on_nominal(self): - with self.assertRaises(TypeError): - @runtime - class C: pass - class Proto(Protocol): - x = 1 - with self.assertRaises(TypeError): - @runtime - class Concrete(Proto): - pass - - def test_none_treated_correctly(self): - @runtime - class P(Protocol): - x = None # type: int - class B(object): pass - self.assertNotIsInstance(B(), P) - class C: - x = 1 - class D: - x = None - self.assertIsInstance(C(), P) - self.assertIsInstance(D(), P) - class CI: - def __init__(self): - self.x = 1 - class DI: - def __init__(self): - self.x = None - self.assertIsInstance(C(), P) - self.assertIsInstance(D(), P) - - def test_protocols_in_unions(self): - class P(Protocol): - x = None # type: int - Alias = typing.Union[typing.Iterable, P] - Alias2 = typing.Union[P, typing.Iterable] - self.assertEqual(Alias, Alias2) - - def test_protocols_pickleable(self): - global P, CP # pickle wants to reference the class by name - T = TypeVar('T') - - @runtime - class P(Protocol[T]): - x = 1 - class CP(P[int]): - pass - - c = CP() - c.foo = 42 - c.bar = 'abc' - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(c, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.x, 1) - self.assertEqual(x.__dict__, {'foo': 42, 'bar': 'abc'}) - s = pickle.dumps(P) - D = pickle.loads(s) - class E: - x = 1 - self.assertIsInstance(E(), D) - - def test_collections_protocols_allowed(self): - @runtime_checkable - class Custom(collections.abc.Iterable, Protocol): - def close(self): pass - - class A: ... - class B: - def __iter__(self): - return [] - def close(self): - return 0 - - self.assertIsSubclass(B, Custom) - self.assertNotIsSubclass(A, Custom) - - -class TypedDictTests(BaseTestCase): - - def test_basics_iterable_syntax(self): - Emp = TypedDict('Emp', {'name': str, 'id': int}) - self.assertIsSubclass(Emp, dict) - self.assertIsSubclass(Emp, typing.MutableMapping) - if sys.version_info[0] >= 3: - import collections.abc - self.assertNotIsSubclass(Emp, collections.abc.Sequence) - jim = Emp(name='Jim', id=1) - self.assertIs(type(jim), dict) - self.assertEqual(jim['name'], 'Jim') - self.assertEqual(jim['id'], 1) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp.__module__, __name__) - self.assertEqual(Emp.__bases__, (dict,)) - self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) - self.assertEqual(Emp.__total__, True) - - def test_basics_keywords_syntax(self): - Emp = TypedDict('Emp', name=str, id=int) - self.assertIsSubclass(Emp, dict) - self.assertIsSubclass(Emp, typing.MutableMapping) - if sys.version_info[0] >= 3: - import collections.abc - self.assertNotIsSubclass(Emp, collections.abc.Sequence) - jim = Emp(name='Jim', id=1) - self.assertIs(type(jim), dict) - self.assertEqual(jim['name'], 'Jim') - self.assertEqual(jim['id'], 1) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp.__module__, __name__) - self.assertEqual(Emp.__bases__, (dict,)) - self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) - self.assertEqual(Emp.__total__, True) - - def test_typeddict_special_keyword_names(self): - TD = TypedDict("TD", cls=type, self=object, typename=str, _typename=int, - fields=list, _fields=dict) - self.assertEqual(TD.__name__, 'TD') - self.assertEqual(TD.__annotations__, {'cls': type, 'self': object, 'typename': str, - '_typename': int, 'fields': list, '_fields': dict}) - a = TD(cls=str, self=42, typename='foo', _typename=53, - fields=[('bar', tuple)], _fields={'baz', set}) - self.assertEqual(a['cls'], str) - self.assertEqual(a['self'], 42) - self.assertEqual(a['typename'], 'foo') - self.assertEqual(a['_typename'], 53) - self.assertEqual(a['fields'], [('bar', tuple)]) - self.assertEqual(a['_fields'], {'baz', set}) - - @skipIf(hasattr(typing, 'TypedDict'), "Should be tested by upstream") - def test_typeddict_create_errors(self): - with self.assertRaises(TypeError): - TypedDict.__new__() - with self.assertRaises(TypeError): - TypedDict() - with self.assertRaises(TypeError): - TypedDict('Emp', [('name', str)], None) - - with self.assertWarns(DeprecationWarning): - Emp = TypedDict(_typename='Emp', name=str, id=int) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) - - with self.assertWarns(DeprecationWarning): - Emp = TypedDict('Emp', _fields={'name': str, 'id': int}) - self.assertEqual(Emp.__name__, 'Emp') - self.assertEqual(Emp.__annotations__, {'name': str, 'id': int}) - - def test_typeddict_errors(self): - Emp = TypedDict('Emp', {'name': str, 'id': int}) - if sys.version_info[:2] >= (3, 9): - self.assertEqual(TypedDict.__module__, 'typing') - else: - self.assertEqual(TypedDict.__module__, 'typing_extensions') - jim = Emp(name='Jim', id=1) - with self.assertRaises(TypeError): - isinstance({}, Emp) - with self.assertRaises(TypeError): - isinstance(jim, Emp) - with self.assertRaises(TypeError): - issubclass(dict, Emp) - with self.assertRaises(TypeError): - TypedDict('Hi', x=1) - with self.assertRaises(TypeError): - TypedDict('Hi', [('x', int), ('y', 1)]) - with self.assertRaises(TypeError): - TypedDict('Hi', [('x', int)], y=int) - - @skipUnless(PY36, 'Python 3.6 required') - def test_py36_class_syntax_usage(self): - self.assertEqual(LabelPoint2D.__name__, 'LabelPoint2D') - self.assertEqual(LabelPoint2D.__module__, __name__) - self.assertEqual(get_type_hints(LabelPoint2D), {'x': int, 'y': int, 'label': str}) - self.assertEqual(LabelPoint2D.__bases__, (dict,)) - self.assertEqual(LabelPoint2D.__total__, True) - self.assertNotIsSubclass(LabelPoint2D, typing.Sequence) - not_origin = Point2D(x=0, y=1) - self.assertEqual(not_origin['x'], 0) - self.assertEqual(not_origin['y'], 1) - other = LabelPoint2D(x=0, y=1, label='hi') - self.assertEqual(other['label'], 'hi') - - def test_pickle(self): - global EmpD # pickle wants to reference the class by name - EmpD = TypedDict('EmpD', name=str, id=int) - jane = EmpD({'name': 'jane', 'id': 37}) - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(jane, proto) - jane2 = pickle.loads(z) - self.assertEqual(jane2, jane) - self.assertEqual(jane2, {'name': 'jane', 'id': 37}) - ZZ = pickle.dumps(EmpD, proto) - EmpDnew = pickle.loads(ZZ) - self.assertEqual(EmpDnew({'name': 'jane', 'id': 37}), jane) - - def test_optional(self): - EmpD = TypedDict('EmpD', name=str, id=int) - - self.assertEqual(typing.Optional[EmpD], typing.Union[None, EmpD]) - self.assertNotEqual(typing.List[EmpD], typing.Tuple[EmpD]) - - def test_total(self): - D = TypedDict('D', {'x': int}, total=False) - self.assertEqual(D(), {}) - self.assertEqual(D(x=1), {'x': 1}) - self.assertEqual(D.__total__, False) - - if PY36: - self.assertEqual(Options(), {}) - self.assertEqual(Options(log_level=2), {'log_level': 2}) - self.assertEqual(Options.__total__, False) - - @skipUnless(PY36, 'Python 3.6 required') - def test_optional_keys(self): - assert Point2Dor3D.__required_keys__ == frozenset(['x', 'y']) - assert Point2Dor3D.__optional_keys__ == frozenset(['z']) - - @skipUnless(PY36, 'Python 3.6 required') - def test_keys_inheritance(self): - assert BaseAnimal.__required_keys__ == frozenset(['name']) - assert BaseAnimal.__optional_keys__ == frozenset([]) - assert get_type_hints(BaseAnimal) == {'name': str} - - assert Animal.__required_keys__ == frozenset(['name']) - assert Animal.__optional_keys__ == frozenset(['tail', 'voice']) - assert get_type_hints(Animal) == { - 'name': str, - 'tail': bool, - 'voice': str, - } - - assert Cat.__required_keys__ == frozenset(['name', 'fur_color']) - assert Cat.__optional_keys__ == frozenset(['tail', 'voice']) - assert get_type_hints(Cat) == { - 'fur_color': str, - 'name': str, - 'tail': bool, - 'voice': str, - } - - -@skipUnless(TYPING_3_5_3, "Python >= 3.5.3 required") -class AnnotatedTests(BaseTestCase): - - def test_repr(self): - if hasattr(typing, 'Annotated'): - mod_name = 'typing' - else: - mod_name = "typing_extensions" - self.assertEqual( - repr(Annotated[int, 4, 5]), - mod_name + ".Annotated[int, 4, 5]" - ) - self.assertEqual( - repr(Annotated[List[int], 4, 5]), - mod_name + ".Annotated[typing.List[int], 4, 5]" - ) - - def test_flatten(self): - A = Annotated[Annotated[int, 4], 5] - self.assertEqual(A, Annotated[int, 4, 5]) - self.assertEqual(A.__metadata__, (4, 5)) - if PEP_560: - self.assertEqual(A.__origin__, int) - - def test_specialize(self): - L = Annotated[List[T], "my decoration"] - LI = Annotated[List[int], "my decoration"] - self.assertEqual(L[int], Annotated[List[int], "my decoration"]) - self.assertEqual(L[int].__metadata__, ("my decoration",)) - if PEP_560: - self.assertEqual(L[int].__origin__, List[int]) - with self.assertRaises(TypeError): - LI[int] - with self.assertRaises(TypeError): - L[int, float] - - def test_hash_eq(self): - self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4]) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5]) - self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4]) - self.assertEqual( - {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]}, - {Annotated[int, 4, 5], Annotated[T, 4, 5]} - ) - - def test_instantiate(self): - class C: - classvar = 4 - - def __init__(self, x): - self.x = x - - def __eq__(self, other): - if not isinstance(other, C): - return NotImplemented - return other.x == self.x - - A = Annotated[C, "a decoration"] - a = A(5) - c = C(5) - self.assertEqual(a, c) - self.assertEqual(a.x, c.x) - self.assertEqual(a.classvar, c.classvar) - - def test_instantiate_generic(self): - MyCount = Annotated[typing_extensions.Counter[T], "my decoration"] - self.assertEqual(MyCount([4, 4, 5]), {4: 2, 5: 1}) - self.assertEqual(MyCount[int]([4, 4, 5]), {4: 2, 5: 1}) - - def test_cannot_instantiate_forward(self): - A = Annotated["int", (5, 6)] - with self.assertRaises(TypeError): - A(5) - - def test_cannot_instantiate_type_var(self): - A = Annotated[T, (5, 6)] - with self.assertRaises(TypeError): - A(5) - - def test_cannot_getattr_typevar(self): - with self.assertRaises(AttributeError): - Annotated[T, (5, 7)].x - - def test_attr_passthrough(self): - class C: - classvar = 4 - - A = Annotated[C, "a decoration"] - self.assertEqual(A.classvar, 4) - A.x = 5 - self.assertEqual(C.x, 5) - - def test_hash_eq(self): - self.assertEqual(len({Annotated[int, 4, 5], Annotated[int, 4, 5]}), 1) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[int, 5, 4]) - self.assertNotEqual(Annotated[int, 4, 5], Annotated[str, 4, 5]) - self.assertNotEqual(Annotated[int, 4], Annotated[int, 4, 4]) - self.assertEqual( - {Annotated[int, 4, 5], Annotated[int, 4, 5], Annotated[T, 4, 5]}, - {Annotated[int, 4, 5], Annotated[T, 4, 5]} - ) - - def test_cannot_subclass(self): - with self.assertRaisesRegex(TypeError, "Cannot subclass .*Annotated"): - class C(Annotated): - pass - - def test_cannot_check_instance(self): - with self.assertRaises(TypeError): - isinstance(5, Annotated[int, "positive"]) - - def test_cannot_check_subclass(self): - with self.assertRaises(TypeError): - issubclass(int, Annotated[int, "positive"]) - - @skipUnless(PEP_560, "pickle support was added with PEP 560") - def test_pickle(self): - samples = [typing.Any, typing.Union[int, str], - typing.Optional[str], Tuple[int, ...], - typing.Callable[[str], bytes]] - - for t in samples: - x = Annotated[t, "a"] - - for prot in range(pickle.HIGHEST_PROTOCOL + 1): - with self.subTest(protocol=prot, type=t): - pickled = pickle.dumps(x, prot) - restored = pickle.loads(pickled) - self.assertEqual(x, restored) - - global _Annotated_test_G - - class _Annotated_test_G(Generic[T]): - x = 1 - - G = Annotated[_Annotated_test_G[int], "A decoration"] - G.foo = 42 - G.bar = 'abc' - - for proto in range(pickle.HIGHEST_PROTOCOL + 1): - z = pickle.dumps(G, proto) - x = pickle.loads(z) - self.assertEqual(x.foo, 42) - self.assertEqual(x.bar, 'abc') - self.assertEqual(x.x, 1) - - def test_subst(self): - dec = "a decoration" - dec2 = "another decoration" - - S = Annotated[T, dec2] - self.assertEqual(S[int], Annotated[int, dec2]) - - self.assertEqual(S[Annotated[int, dec]], Annotated[int, dec, dec2]) - L = Annotated[List[T], dec] - - self.assertEqual(L[int], Annotated[List[int], dec]) - with self.assertRaises(TypeError): - L[int, int] - - self.assertEqual(S[L[int]], Annotated[List[int], dec, dec2]) - - D = Annotated[Dict[KT, VT], dec] - self.assertEqual(D[str, int], Annotated[Dict[str, int], dec]) - with self.assertRaises(TypeError): - D[int] - - It = Annotated[int, dec] - with self.assertRaises(TypeError): - It[None] - - LI = L[int] - with self.assertRaises(TypeError): - LI[None] - - def test_annotated_in_other_types(self): - X = List[Annotated[T, 5]] - self.assertEqual(X[int], List[Annotated[int, 5]]) - - -@skipUnless(PEP_560, "Python 3.7 required") -class GetTypeHintsTests(BaseTestCase): - def test_get_type_hints(self): - def foobar(x: List['X']): ... - X = Annotated[int, (1, 10)] - self.assertEqual( - get_type_hints(foobar, globals(), locals()), - {'x': List[int]} - ) - self.assertEqual( - get_type_hints(foobar, globals(), locals(), include_extras=True), - {'x': List[Annotated[int, (1, 10)]]} - ) - BA = Tuple[Annotated[T, (1, 0)], ...] - def barfoo(x: BA): ... - self.assertEqual(get_type_hints(barfoo, globals(), locals())['x'], Tuple[T, ...]) - self.assertIs( - get_type_hints(barfoo, globals(), locals(), include_extras=True)['x'], - BA - ) - def barfoo2(x: typing.Callable[..., Annotated[List[T], "const"]], - y: typing.Union[int, Annotated[T, "mutable"]]): ... - self.assertEqual( - get_type_hints(barfoo2, globals(), locals()), - {'x': typing.Callable[..., List[T]], 'y': typing.Union[int, T]} - ) - BA2 = typing.Callable[..., List[T]] - def barfoo3(x: BA2): ... - self.assertIs( - get_type_hints(barfoo3, globals(), locals(), include_extras=True)["x"], - BA2 - ) - - def test_get_type_hints_refs(self): - - Const = Annotated[T, "Const"] - - class MySet(Generic[T]): - - def __ior__(self, other: "Const[MySet[T]]") -> "MySet[T]": - ... - - def __iand__(self, other: Const["MySet[T]"]) -> "MySet[T]": - ... - - self.assertEqual( - get_type_hints(MySet.__iand__, globals(), locals()), - {'other': MySet[T], 'return': MySet[T]} - ) - - self.assertEqual( - get_type_hints(MySet.__iand__, globals(), locals(), include_extras=True), - {'other': Const[MySet[T]], 'return': MySet[T]} - ) - - self.assertEqual( - get_type_hints(MySet.__ior__, globals(), locals()), - {'other': MySet[T], 'return': MySet[T]} - ) - - -class TypeAliasTests(BaseTestCase): - @skipUnless(PY36, 'Python 3.6 required') - def test_canonical_usage_with_variable_annotation(self): - ns = {} - exec('Alias: TypeAlias = Employee', globals(), ns) - - def test_canonical_usage_with_type_comment(self): - Alias = Employee # type: TypeAlias - - def test_cannot_instantiate(self): - with self.assertRaises(TypeError): - TypeAlias() - - def test_no_isinstance(self): - with self.assertRaises(TypeError): - isinstance(42, TypeAlias) - - def test_no_issubclass(self): - with self.assertRaises(TypeError): - issubclass(Employee, TypeAlias) - - if SUBCLASS_CHECK_FORBIDDEN: - with self.assertRaises(TypeError): - issubclass(TypeAlias, Employee) - - def test_cannot_subclass(self): - with self.assertRaises(TypeError): - class C(TypeAlias): - pass - - if SUBCLASS_CHECK_FORBIDDEN: - with self.assertRaises(TypeError): - class C(type(TypeAlias)): - pass - - def test_repr(self): - if hasattr(typing, 'TypeAlias'): - self.assertEqual(repr(TypeAlias), 'typing.TypeAlias') - else: - self.assertEqual(repr(TypeAlias), 'typing_extensions.TypeAlias') - - def test_cannot_subscript(self): - with self.assertRaises(TypeError): - TypeAlias[int] - - -class AllTests(BaseTestCase): - - def test_typing_extensions_includes_standard(self): - a = typing_extensions.__all__ - self.assertIn('ClassVar', a) - self.assertIn('Type', a) - self.assertIn('ChainMap', a) - self.assertIn('ContextManager', a) - self.assertIn('Counter', a) - self.assertIn('DefaultDict', a) - self.assertIn('Deque', a) - self.assertIn('NewType', a) - self.assertIn('overload', a) - self.assertIn('Text', a) - self.assertIn('TYPE_CHECKING', a) - if TYPING_3_5_3: - self.assertIn('Annotated', a) - if PEP_560: - self.assertIn('get_type_hints', a) - - if ASYNCIO: - self.assertIn('Awaitable', a) - self.assertIn('AsyncIterator', a) - self.assertIn('AsyncIterable', a) - self.assertIn('Coroutine', a) - self.assertIn('AsyncContextManager', a) - - if PY36: - self.assertIn('AsyncGenerator', a) - - if TYPING_3_5_3: - self.assertIn('Protocol', a) - self.assertIn('runtime', a) - - def test_typing_extensions_defers_when_possible(self): - exclude = { - 'overload', - 'Text', - 'TypedDict', - 'TYPE_CHECKING', - 'Final', - 'get_type_hints' - } - if sys.version_info[:2] == (3, 8): - exclude |= {'get_args', 'get_origin'} - for item in typing_extensions.__all__: - if item not in exclude and hasattr(typing, item): - self.assertIs( - getattr(typing_extensions, item), - getattr(typing, item)) - - def test_typing_extensions_compiles_with_opt(self): - file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'typing_extensions.py') - try: - subprocess.check_output('{} -OO {}'.format(sys.executable, - file_path), - stderr=subprocess.STDOUT, - shell=True) - except subprocess.CalledProcessError: - self.fail('Module does not compile with optimize=2 (-OO flag).') - - -if __name__ == '__main__': - main() diff --git a/typing_extensions/src_py3/typing_extensions.py b/typing_extensions/src_py3/typing_extensions.py deleted file mode 100644 index 97fa7dc3e..000000000 --- a/typing_extensions/src_py3/typing_extensions.py +++ /dev/null @@ -1,2168 +0,0 @@ -import abc -import collections -import contextlib -import sys -import typing -import collections.abc as collections_abc -import operator - -# These are used by Protocol implementation -# We use internal typing helpers here, but this significantly reduces -# code duplication. (Also this is only until Protocol is in typing.) -from typing import Generic, Callable, TypeVar, Tuple - -# After PEP 560, internal typing API was substantially reworked. -# This is especially important for Protocol class which uses internal APIs -# quite extensivelly. -PEP_560 = sys.version_info[:3] >= (3, 7, 0) - -if PEP_560: - GenericMeta = TypingMeta = type -else: - from typing import GenericMeta, TypingMeta -OLD_GENERICS = False -try: - from typing import _type_vars, _next_in_mro, _type_check -except ImportError: - OLD_GENERICS = True -try: - from typing import _subs_tree # noqa - SUBS_TREE = True -except ImportError: - SUBS_TREE = False -try: - from typing import _tp_cache -except ImportError: - def _tp_cache(x): - return x -try: - from typing import _TypingEllipsis, _TypingEmpty -except ImportError: - class _TypingEllipsis: - pass - - class _TypingEmpty: - pass - - -# The two functions below are copies of typing internal helpers. -# They are needed by _ProtocolMeta - - -def _no_slots_copy(dct): - dict_copy = dict(dct) - if '__slots__' in dict_copy: - for slot in dict_copy['__slots__']: - dict_copy.pop(slot, None) - return dict_copy - - -def _check_generic(cls, parameters): - if not cls.__parameters__: - raise TypeError("%s is not a generic class" % repr(cls)) - alen = len(parameters) - elen = len(cls.__parameters__) - if alen != elen: - raise TypeError("Too %s parameters for %s; actual %s, expected %s" % - ("many" if alen > elen else "few", repr(cls), alen, elen)) - - -if hasattr(typing, '_generic_new'): - _generic_new = typing._generic_new -else: - # Note: The '_generic_new(...)' function is used as a part of the - # process of creating a generic type and was added to the typing module - # as of Python 3.5.3. - # - # We've defined '_generic_new(...)' below to exactly match the behavior - # implemented in older versions of 'typing' bundled with Python 3.5.0 to - # 3.5.2. This helps eliminate redundancy when defining collection types - # like 'Deque' later. - # - # See https://github.com/python/typing/pull/308 for more details -- in - # particular, compare and contrast the definition of types like - # 'typing.List' before and after the merge. - - def _generic_new(base_cls, cls, *args, **kwargs): - return base_cls.__new__(cls, *args, **kwargs) - -# See https://github.com/python/typing/pull/439 -if hasattr(typing, '_geqv'): - from typing import _geqv - _geqv_defined = True -else: - _geqv = None - _geqv_defined = False - -if sys.version_info[:2] >= (3, 6): - import _collections_abc - _check_methods_in_mro = _collections_abc._check_methods -else: - def _check_methods_in_mro(C, *methods): - mro = C.__mro__ - for method in methods: - for B in mro: - if method in B.__dict__: - if B.__dict__[method] is None: - return NotImplemented - break - else: - return NotImplemented - return True - - -# Please keep __all__ alphabetized within each category. -__all__ = [ - # Super-special typing primitives. - 'ClassVar', - 'Final', - 'Type', - - # ABCs (from collections.abc). - # The following are added depending on presence - # of their non-generic counterparts in stdlib: - # 'Awaitable', - # 'AsyncIterator', - # 'AsyncIterable', - # 'Coroutine', - # 'AsyncGenerator', - # 'AsyncContextManager', - # 'ChainMap', - - # Concrete collection types. - 'ContextManager', - 'Counter', - 'Deque', - 'DefaultDict', - 'TypedDict', - - # Structural checks, a.k.a. protocols. - 'SupportsIndex', - - # One-off things. - 'final', - 'IntVar', - 'Literal', - 'NewType', - 'overload', - 'Text', - 'TYPE_CHECKING', -] - -# Annotated relies on substitution trees of pep 560. It will not work for -# versions of typing older than 3.5.3 -HAVE_ANNOTATED = PEP_560 or SUBS_TREE - -if PEP_560: - __all__.extend(["get_args", "get_origin", "get_type_hints"]) - -if HAVE_ANNOTATED: - __all__.append("Annotated") - -# Protocols are hard to backport to the original version of typing 3.5.0 -HAVE_PROTOCOLS = sys.version_info[:3] != (3, 5, 0) - -if HAVE_PROTOCOLS: - __all__.extend(['Protocol', 'runtime', 'runtime_checkable']) - - -# TODO -if hasattr(typing, 'NoReturn'): - NoReturn = typing.NoReturn -elif hasattr(typing, '_FinalTypingBase'): - class _NoReturn(typing._FinalTypingBase, _root=True): - """Special type indicating functions that never return. - Example:: - - from typing import NoReturn - - def stop() -> NoReturn: - raise Exception('no way') - - This type is invalid in other positions, e.g., ``List[NoReturn]`` - will fail in static type checkers. - """ - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("NoReturn cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("NoReturn cannot be used with issubclass().") - - NoReturn = _NoReturn(_root=True) -else: - class _NoReturnMeta(typing.TypingMeta): - """Metaclass for NoReturn""" - def __new__(cls, name, bases, namespace, _root=False): - return super().__new__(cls, name, bases, namespace, _root=_root) - - def __instancecheck__(self, obj): - raise TypeError("NoReturn cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("NoReturn cannot be used with issubclass().") - - class NoReturn(typing.Final, metaclass=_NoReturnMeta, _root=True): - """Special type indicating functions that never return. - Example:: - - from typing import NoReturn - - def stop() -> NoReturn: - raise Exception('no way') - - This type is invalid in other positions, e.g., ``List[NoReturn]`` - will fail in static type checkers. - """ - __slots__ = () - - -# Some unconstrained type variables. These are used by the container types. -# (These are not for export.) -T = typing.TypeVar('T') # Any type. -KT = typing.TypeVar('KT') # Key type. -VT = typing.TypeVar('VT') # Value type. -T_co = typing.TypeVar('T_co', covariant=True) # Any type covariant containers. -V_co = typing.TypeVar('V_co', covariant=True) # Any type covariant containers. -VT_co = typing.TypeVar('VT_co', covariant=True) # Value type covariant containers. -T_contra = typing.TypeVar('T_contra', contravariant=True) # Ditto contravariant. - - -if hasattr(typing, 'ClassVar'): - ClassVar = typing.ClassVar -elif hasattr(typing, '_FinalTypingBase'): - class _ClassVar(typing._FinalTypingBase, _root=True): - """Special type construct to mark class variables. - - An annotation wrapped in ClassVar indicates that a given - attribute is intended to be used as a class variable and - should not be set on instances of that class. Usage:: - - class Starship: - stats: ClassVar[Dict[str, int]] = {} # class variable - damage: int = 10 # instance variable - - ClassVar accepts only types and cannot be further subscribed. - - Note that ClassVar is not a class itself, and should not - be used with isinstance() or issubclass(). - """ - - __slots__ = ('__type__',) - - def __init__(self, tp=None, **kwds): - self.__type__ = tp - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is None: - return cls(typing._type_check(item, - '{} accepts only single type.'.format(cls.__name__[1:])), - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(new_tp, _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, _ClassVar): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - ClassVar = _ClassVar(_root=True) -else: - class _ClassVarMeta(typing.TypingMeta): - """Metaclass for ClassVar""" - - def __new__(cls, name, bases, namespace, tp=None, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - if tp is not None: - self.__type__ = tp - return self - - def __instancecheck__(self, obj): - raise TypeError("ClassVar cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("ClassVar cannot be used with issubclass().") - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is not None: - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - param = typing._type_check( - item, - '{} accepts only single type.'.format(cls.__name__[1:])) - return cls(self.__name__, self.__bases__, - dict(self.__dict__), tp=param, _root=True) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(self.__name__, self.__bases__, - dict(self.__dict__), tp=self.__type__, - _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, ClassVar): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - class ClassVar(typing.Final, metaclass=_ClassVarMeta, _root=True): - """Special type construct to mark class variables. - - An annotation wrapped in ClassVar indicates that a given - attribute is intended to be used as a class variable and - should not be set on instances of that class. Usage:: - - class Starship: - stats: ClassVar[Dict[str, int]] = {} # class variable - damage: int = 10 # instance variable - - ClassVar accepts only types and cannot be further subscribed. - - Note that ClassVar is not a class itself, and should not - be used with isinstance() or issubclass(). - """ - - __type__ = None - -# On older versions of typing there is an internal class named "Final". -if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7): - Final = typing.Final -elif sys.version_info[:2] >= (3, 7): - class _FinalForm(typing._SpecialForm, _root=True): - - def __repr__(self): - return 'typing_extensions.' + self._name - - def __getitem__(self, parameters): - item = typing._type_check(parameters, - '{} accepts only single type'.format(self._name)) - return _GenericAlias(self, (item,)) - - Final = _FinalForm('Final', - doc="""A special typing construct to indicate that a name - cannot be re-assigned or overridden in a subclass. - For example: - - MAX_SIZE: Final = 9000 - MAX_SIZE += 1 # Error reported by type checker - - class Connection: - TIMEOUT: Final[int] = 10 - class FastConnector(Connection): - TIMEOUT = 1 # Error reported by type checker - - There is no runtime checking of these properties.""") -elif hasattr(typing, '_FinalTypingBase'): - class _Final(typing._FinalTypingBase, _root=True): - """A special typing construct to indicate that a name - cannot be re-assigned or overridden in a subclass. - For example: - - MAX_SIZE: Final = 9000 - MAX_SIZE += 1 # Error reported by type checker - - class Connection: - TIMEOUT: Final[int] = 10 - class FastConnector(Connection): - TIMEOUT = 1 # Error reported by type checker - - There is no runtime checking of these properties. - """ - - __slots__ = ('__type__',) - - def __init__(self, tp=None, **kwds): - self.__type__ = tp - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is None: - return cls(typing._type_check(item, - '{} accepts only single type.'.format(cls.__name__[1:])), - _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(new_tp, _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, _Final): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - Final = _Final(_root=True) -else: - class _FinalMeta(typing.TypingMeta): - """Metaclass for Final""" - - def __new__(cls, name, bases, namespace, tp=None, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - if tp is not None: - self.__type__ = tp - return self - - def __instancecheck__(self, obj): - raise TypeError("Final cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Final cannot be used with issubclass().") - - def __getitem__(self, item): - cls = type(self) - if self.__type__ is not None: - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - param = typing._type_check( - item, - '{} accepts only single type.'.format(cls.__name__[1:])) - return cls(self.__name__, self.__bases__, - dict(self.__dict__), tp=param, _root=True) - - def _eval_type(self, globalns, localns): - new_tp = typing._eval_type(self.__type__, globalns, localns) - if new_tp == self.__type__: - return self - return type(self)(self.__name__, self.__bases__, - dict(self.__dict__), tp=self.__type__, - _root=True) - - def __repr__(self): - r = super().__repr__() - if self.__type__ is not None: - r += '[{}]'.format(typing._type_repr(self.__type__)) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__type__)) - - def __eq__(self, other): - if not isinstance(other, Final): - return NotImplemented - if self.__type__ is not None: - return self.__type__ == other.__type__ - return self is other - - class Final(typing.Final, metaclass=_FinalMeta, _root=True): - """A special typing construct to indicate that a name - cannot be re-assigned or overridden in a subclass. - For example: - - MAX_SIZE: Final = 9000 - MAX_SIZE += 1 # Error reported by type checker - - class Connection: - TIMEOUT: Final[int] = 10 - class FastConnector(Connection): - TIMEOUT = 1 # Error reported by type checker - - There is no runtime checking of these properties. - """ - - __type__ = None - - -if hasattr(typing, 'final'): - final = typing.final -else: - def final(f): - """This decorator can be used to indicate to type checkers that - the decorated method cannot be overridden, and decorated class - cannot be subclassed. For example: - - class Base: - @final - def done(self) -> None: - ... - class Sub(Base): - def done(self) -> None: # Error reported by type checker - ... - @final - class Leaf: - ... - class Other(Leaf): # Error reported by type checker - ... - - There is no runtime checking of these properties. - """ - return f - - -def IntVar(name): - return TypeVar(name) - - -if hasattr(typing, 'Literal'): - Literal = typing.Literal -elif sys.version_info[:2] >= (3, 7): - class _LiteralForm(typing._SpecialForm, _root=True): - - def __repr__(self): - return 'typing_extensions.' + self._name - - def __getitem__(self, parameters): - return _GenericAlias(self, parameters) - - Literal = _LiteralForm('Literal', - doc="""A type that can be used to indicate to type checkers - that the corresponding value has a value literally equivalent - to the provided parameter. For example: - - var: Literal[4] = 4 - - The type checker understands that 'var' is literally equal to - the value 4 and no other value. - - Literal[...] cannot be subclassed. There is no runtime - checking verifying that the parameter is actually a value - instead of a type.""") -elif hasattr(typing, '_FinalTypingBase'): - class _Literal(typing._FinalTypingBase, _root=True): - """A type that can be used to indicate to type checkers that the - corresponding value has a value literally equivalent to the - provided parameter. For example: - - var: Literal[4] = 4 - - The type checker understands that 'var' is literally equal to the - value 4 and no other value. - - Literal[...] cannot be subclassed. There is no runtime checking - verifying that the parameter is actually a value instead of a type. - """ - - __slots__ = ('__values__',) - - def __init__(self, values=None, **kwds): - self.__values__ = values - - def __getitem__(self, values): - cls = type(self) - if self.__values__ is None: - if not isinstance(values, tuple): - values = (values,) - return cls(values, _root=True) - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - def _eval_type(self, globalns, localns): - return self - - def __repr__(self): - r = super().__repr__() - if self.__values__ is not None: - r += '[{}]'.format(', '.join(map(typing._type_repr, self.__values__))) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__values__)) - - def __eq__(self, other): - if not isinstance(other, _Literal): - return NotImplemented - if self.__values__ is not None: - return self.__values__ == other.__values__ - return self is other - - Literal = _Literal(_root=True) -else: - class _LiteralMeta(typing.TypingMeta): - """Metaclass for Literal""" - - def __new__(cls, name, bases, namespace, values=None, _root=False): - self = super().__new__(cls, name, bases, namespace, _root=_root) - if values is not None: - self.__values__ = values - return self - - def __instancecheck__(self, obj): - raise TypeError("Literal cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Literal cannot be used with issubclass().") - - def __getitem__(self, item): - cls = type(self) - if self.__values__ is not None: - raise TypeError('{} cannot be further subscripted' - .format(cls.__name__[1:])) - - if not isinstance(item, tuple): - item = (item,) - return cls(self.__name__, self.__bases__, - dict(self.__dict__), values=item, _root=True) - - def _eval_type(self, globalns, localns): - return self - - def __repr__(self): - r = super().__repr__() - if self.__values__ is not None: - r += '[{}]'.format(', '.join(map(typing._type_repr, self.__values__))) - return r - - def __hash__(self): - return hash((type(self).__name__, self.__values__)) - - def __eq__(self, other): - if not isinstance(other, Literal): - return NotImplemented - if self.__values__ is not None: - return self.__values__ == other.__values__ - return self is other - - class Literal(typing.Final, metaclass=_LiteralMeta, _root=True): - """A type that can be used to indicate to type checkers that the - corresponding value has a value literally equivalent to the - provided parameter. For example: - - var: Literal[4] = 4 - - The type checker understands that 'var' is literally equal to the - value 4 and no other value. - - Literal[...] cannot be subclassed. There is no runtime checking - verifying that the parameter is actually a value instead of a type. - """ - - __values__ = None - - -def _overload_dummy(*args, **kwds): - """Helper for @overload to raise when called.""" - raise NotImplementedError( - "You should not call an overloaded function. " - "A series of @overload-decorated functions " - "outside a stub module should always be followed " - "by an implementation that is not @overload-ed.") - - -def overload(func): - """Decorator for overloaded functions/methods. - - In a stub file, place two or more stub definitions for the same - function in a row, each decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - - In a non-stub file (i.e. a regular .py file), do the same but - follow it with an implementation. The implementation should *not* - be decorated with @overload. For example: - - @overload - def utf8(value: None) -> None: ... - @overload - def utf8(value: bytes) -> bytes: ... - @overload - def utf8(value: str) -> bytes: ... - def utf8(value): - # implementation goes here - """ - return _overload_dummy - - -# This is not a real generic class. Don't use outside annotations. -if hasattr(typing, 'Type'): - Type = typing.Type -else: - # Internal type variable used for Type[]. - CT_co = typing.TypeVar('CT_co', covariant=True, bound=type) - - class Type(typing.Generic[CT_co], extra=type): - """A special construct usable to annotate class objects. - - For example, suppose we have the following classes:: - - class User: ... # Abstract base for User classes - class BasicUser(User): ... - class ProUser(User): ... - class TeamUser(User): ... - - And a function that takes a class argument that's a subclass of - User and returns an instance of the corresponding class:: - - U = TypeVar('U', bound=User) - def new_user(user_class: Type[U]) -> U: - user = user_class() - # (Here we could write the user object to a database) - return user - joe = new_user(BasicUser) - - At this point the type checker knows that joe has type BasicUser. - """ - - __slots__ = () - - -# Various ABCs mimicking those in collections.abc. -# A few are simply re-exported for completeness. - -def _define_guard(type_name): - """ - Returns True if the given type isn't defined in typing but - is defined in collections_abc. - - Adds the type to __all__ if the collection is found in either - typing or collection_abc. - """ - if hasattr(typing, type_name): - __all__.append(type_name) - globals()[type_name] = getattr(typing, type_name) - return False - elif hasattr(collections_abc, type_name): - __all__.append(type_name) - return True - else: - return False - - -class _ExtensionsGenericMeta(GenericMeta): - def __subclasscheck__(self, subclass): - """This mimics a more modern GenericMeta.__subclasscheck__() logic - (that does not have problems with recursion) to work around interactions - between collections, typing, and typing_extensions on older - versions of Python, see https://github.com/python/typing/issues/501. - """ - if sys.version_info[:3] >= (3, 5, 3) or sys.version_info[:3] < (3, 5, 0): - if self.__origin__ is not None: - if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: - raise TypeError("Parameterized generics cannot be used with class " - "or instance checks") - return False - if not self.__extra__: - return super().__subclasscheck__(subclass) - res = self.__extra__.__subclasshook__(subclass) - if res is not NotImplemented: - return res - if self.__extra__ in subclass.__mro__: - return True - for scls in self.__extra__.__subclasses__(): - if isinstance(scls, GenericMeta): - continue - if issubclass(subclass, scls): - return True - return False - - -if _define_guard('Awaitable'): - class Awaitable(typing.Generic[T_co], metaclass=_ExtensionsGenericMeta, - extra=collections_abc.Awaitable): - __slots__ = () - - -if _define_guard('Coroutine'): - class Coroutine(Awaitable[V_co], typing.Generic[T_co, T_contra, V_co], - metaclass=_ExtensionsGenericMeta, - extra=collections_abc.Coroutine): - __slots__ = () - - -if _define_guard('AsyncIterable'): - class AsyncIterable(typing.Generic[T_co], - metaclass=_ExtensionsGenericMeta, - extra=collections_abc.AsyncIterable): - __slots__ = () - - -if _define_guard('AsyncIterator'): - class AsyncIterator(AsyncIterable[T_co], - metaclass=_ExtensionsGenericMeta, - extra=collections_abc.AsyncIterator): - __slots__ = () - - -if hasattr(typing, 'Deque'): - Deque = typing.Deque -elif _geqv_defined: - class Deque(collections.deque, typing.MutableSequence[T], - metaclass=_ExtensionsGenericMeta, - extra=collections.deque): - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, Deque): - return collections.deque(*args, **kwds) - return _generic_new(collections.deque, cls, *args, **kwds) -else: - class Deque(collections.deque, typing.MutableSequence[T], - metaclass=_ExtensionsGenericMeta, - extra=collections.deque): - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Deque: - return collections.deque(*args, **kwds) - return _generic_new(collections.deque, cls, *args, **kwds) - - -if hasattr(typing, 'ContextManager'): - ContextManager = typing.ContextManager -elif hasattr(contextlib, 'AbstractContextManager'): - class ContextManager(typing.Generic[T_co], - metaclass=_ExtensionsGenericMeta, - extra=contextlib.AbstractContextManager): - __slots__ = () -else: - class ContextManager(typing.Generic[T_co]): - __slots__ = () - - def __enter__(self): - return self - - @abc.abstractmethod - def __exit__(self, exc_type, exc_value, traceback): - return None - - @classmethod - def __subclasshook__(cls, C): - if cls is ContextManager: - # In Python 3.6+, it is possible to set a method to None to - # explicitly indicate that the class does not implement an ABC - # (https://bugs.python.org/issue25958), but we do not support - # that pattern here because this fallback class is only used - # in Python 3.5 and earlier. - if (any("__enter__" in B.__dict__ for B in C.__mro__) and - any("__exit__" in B.__dict__ for B in C.__mro__)): - return True - return NotImplemented - - -if hasattr(typing, 'AsyncContextManager'): - AsyncContextManager = typing.AsyncContextManager - __all__.append('AsyncContextManager') -elif hasattr(contextlib, 'AbstractAsyncContextManager'): - class AsyncContextManager(typing.Generic[T_co], - metaclass=_ExtensionsGenericMeta, - extra=contextlib.AbstractAsyncContextManager): - __slots__ = () - - __all__.append('AsyncContextManager') -elif sys.version_info[:2] >= (3, 5): - exec(""" -class AsyncContextManager(typing.Generic[T_co]): - __slots__ = () - - async def __aenter__(self): - return self - - @abc.abstractmethod - async def __aexit__(self, exc_type, exc_value, traceback): - return None - - @classmethod - def __subclasshook__(cls, C): - if cls is AsyncContextManager: - return _check_methods_in_mro(C, "__aenter__", "__aexit__") - return NotImplemented - -__all__.append('AsyncContextManager') -""") - - -if hasattr(typing, 'DefaultDict'): - DefaultDict = typing.DefaultDict -elif _geqv_defined: - class DefaultDict(collections.defaultdict, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.defaultdict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, DefaultDict): - return collections.defaultdict(*args, **kwds) - return _generic_new(collections.defaultdict, cls, *args, **kwds) -else: - class DefaultDict(collections.defaultdict, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.defaultdict): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is DefaultDict: - return collections.defaultdict(*args, **kwds) - return _generic_new(collections.defaultdict, cls, *args, **kwds) - - -if hasattr(typing, 'Counter'): - Counter = typing.Counter -elif (3, 5, 0) <= sys.version_info[:3] <= (3, 5, 1): - assert _geqv_defined - _TInt = typing.TypeVar('_TInt') - - class _CounterMeta(typing.GenericMeta): - """Metaclass for Counter""" - def __getitem__(self, item): - return super().__getitem__((item, int)) - - class Counter(collections.Counter, - typing.Dict[T, int], - metaclass=_CounterMeta, - extra=collections.Counter): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, Counter): - return collections.Counter(*args, **kwds) - return _generic_new(collections.Counter, cls, *args, **kwds) - -elif _geqv_defined: - class Counter(collections.Counter, - typing.Dict[T, int], - metaclass=_ExtensionsGenericMeta, extra=collections.Counter): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, Counter): - return collections.Counter(*args, **kwds) - return _generic_new(collections.Counter, cls, *args, **kwds) - -else: - class Counter(collections.Counter, - typing.Dict[T, int], - metaclass=_ExtensionsGenericMeta, extra=collections.Counter): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is Counter: - return collections.Counter(*args, **kwds) - return _generic_new(collections.Counter, cls, *args, **kwds) - - -if hasattr(typing, 'ChainMap'): - ChainMap = typing.ChainMap - __all__.append('ChainMap') -elif hasattr(collections, 'ChainMap'): - # ChainMap only exists in 3.3+ - if _geqv_defined: - class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.ChainMap): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if _geqv(cls, ChainMap): - return collections.ChainMap(*args, **kwds) - return _generic_new(collections.ChainMap, cls, *args, **kwds) - else: - class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT], - metaclass=_ExtensionsGenericMeta, - extra=collections.ChainMap): - - __slots__ = () - - def __new__(cls, *args, **kwds): - if cls._gorg is ChainMap: - return collections.ChainMap(*args, **kwds) - return _generic_new(collections.ChainMap, cls, *args, **kwds) - - __all__.append('ChainMap') - - -if _define_guard('AsyncGenerator'): - class AsyncGenerator(AsyncIterator[T_co], typing.Generic[T_co, T_contra], - metaclass=_ExtensionsGenericMeta, - extra=collections_abc.AsyncGenerator): - __slots__ = () - - -if hasattr(typing, 'NewType'): - NewType = typing.NewType -else: - def NewType(name, tp): - """NewType creates simple unique types with almost zero - runtime overhead. NewType(name, tp) is considered a subtype of tp - by static type checkers. At runtime, NewType(name, tp) returns - a dummy function that simply returns its argument. Usage:: - - UserId = NewType('UserId', int) - - def name_by_id(user_id: UserId) -> str: - ... - - UserId('user') # Fails type check - - name_by_id(42) # Fails type check - name_by_id(UserId(42)) # OK - - num = UserId(5) + 1 # type: int - """ - - def new_type(x): - return x - - new_type.__name__ = name - new_type.__supertype__ = tp - return new_type - - -if hasattr(typing, 'Text'): - Text = typing.Text -else: - Text = str - - -if hasattr(typing, 'TYPE_CHECKING'): - TYPE_CHECKING = typing.TYPE_CHECKING -else: - # Constant that's True when type checking, but False here. - TYPE_CHECKING = False - - -def _gorg(cls): - """This function exists for compatibility with old typing versions.""" - assert isinstance(cls, GenericMeta) - if hasattr(cls, '_gorg'): - return cls._gorg - while cls.__origin__ is not None: - cls = cls.__origin__ - return cls - - -if OLD_GENERICS: - def _next_in_mro(cls): # noqa - """This function exists for compatibility with old typing versions.""" - next_in_mro = object - for i, c in enumerate(cls.__mro__[:-1]): - if isinstance(c, GenericMeta) and _gorg(c) is Generic: - next_in_mro = cls.__mro__[i + 1] - return next_in_mro - - -_PROTO_WHITELIST = ['Callable', 'Awaitable', - 'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator', - 'Hashable', 'Sized', 'Container', 'Collection', 'Reversible', - 'ContextManager', 'AsyncContextManager'] - - -def _get_protocol_attrs(cls): - attrs = set() - for base in cls.__mro__[:-1]: # without object - if base.__name__ in ('Protocol', 'Generic'): - continue - annotations = getattr(base, '__annotations__', {}) - for attr in list(base.__dict__.keys()) + list(annotations.keys()): - if (not attr.startswith('_abc_') and attr not in ( - '__abstractmethods__', '__annotations__', '__weakref__', - '_is_protocol', '_is_runtime_protocol', '__dict__', - '__args__', '__slots__', - '__next_in_mro__', '__parameters__', '__origin__', - '__orig_bases__', '__extra__', '__tree_hash__', - '__doc__', '__subclasshook__', '__init__', '__new__', - '__module__', '_MutableMapping__marker', '_gorg')): - attrs.add(attr) - return attrs - - -def _is_callable_members_only(cls): - return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls)) - - -if hasattr(typing, 'Protocol'): - Protocol = typing.Protocol -elif HAVE_PROTOCOLS and not PEP_560: - class _ProtocolMeta(GenericMeta): - """Internal metaclass for Protocol. - - This exists so Protocol classes can be generic without deriving - from Generic. - """ - if not OLD_GENERICS: - def __new__(cls, name, bases, namespace, - tvars=None, args=None, origin=None, extra=None, orig_bases=None): - # This is just a version copied from GenericMeta.__new__ that - # includes "Protocol" special treatment. (Comments removed for brevity.) - assert extra is None # Protocols should not have extra - if tvars is not None: - assert origin is not None - assert all(isinstance(t, TypeVar) for t in tvars), tvars - else: - tvars = _type_vars(bases) - gvars = None - for base in bases: - if base is Generic: - raise TypeError("Cannot inherit from plain Generic") - if (isinstance(base, GenericMeta) and - base.__origin__ in (Generic, Protocol)): - if gvars is not None: - raise TypeError( - "Cannot inherit from Generic[...] or" - " Protocol[...] multiple times.") - gvars = base.__parameters__ - if gvars is None: - gvars = tvars - else: - tvarset = set(tvars) - gvarset = set(gvars) - if not tvarset <= gvarset: - raise TypeError( - "Some type variables (%s) " - "are not listed in %s[%s]" % - (", ".join(str(t) for t in tvars if t not in gvarset), - "Generic" if any(b.__origin__ is Generic - for b in bases) else "Protocol", - ", ".join(str(g) for g in gvars))) - tvars = gvars - - initial_bases = bases - if (extra is not None and type(extra) is abc.ABCMeta and - extra not in bases): - bases = (extra,) + bases - bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b - for b in bases) - if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): - bases = tuple(b for b in bases if b is not Generic) - namespace.update({'__origin__': origin, '__extra__': extra}) - self = super(GenericMeta, cls).__new__(cls, name, bases, namespace, - _root=True) - super(GenericMeta, self).__setattr__('_gorg', - self if not origin else - _gorg(origin)) - self.__parameters__ = tvars - self.__args__ = tuple(... if a is _TypingEllipsis else - () if a is _TypingEmpty else - a for a in args) if args else None - self.__next_in_mro__ = _next_in_mro(self) - if orig_bases is None: - self.__orig_bases__ = initial_bases - elif origin is not None: - self._abc_registry = origin._abc_registry - self._abc_cache = origin._abc_cache - if hasattr(self, '_subs_tree'): - self.__tree_hash__ = (hash(self._subs_tree()) if origin else - super(GenericMeta, self).__hash__()) - return self - - def __init__(cls, *args, **kwargs): - super().__init__(*args, **kwargs) - if not cls.__dict__.get('_is_protocol', None): - cls._is_protocol = any(b is Protocol or - isinstance(b, _ProtocolMeta) and - b.__origin__ is Protocol - for b in cls.__bases__) - if cls._is_protocol: - for base in cls.__mro__[1:]: - if not (base in (object, Generic) or - base.__module__ == 'collections.abc' and - base.__name__ in _PROTO_WHITELIST or - isinstance(base, TypingMeta) and base._is_protocol or - isinstance(base, GenericMeta) and - base.__origin__ is Generic): - raise TypeError('Protocols can only inherit from other' - ' protocols, got %r' % base) - - def _no_init(self, *args, **kwargs): - if type(self)._is_protocol: - raise TypeError('Protocols cannot be instantiated') - cls.__init__ = _no_init - - def _proto_hook(other): - if not cls.__dict__.get('_is_protocol', None): - return NotImplemented - if not isinstance(other, type): - # Same error as for issubclass(1, int) - raise TypeError('issubclass() arg 1 must be a class') - for attr in _get_protocol_attrs(cls): - for base in other.__mro__: - if attr in base.__dict__: - if base.__dict__[attr] is None: - return NotImplemented - break - annotations = getattr(base, '__annotations__', {}) - if (isinstance(annotations, typing.Mapping) and - attr in annotations and - isinstance(other, _ProtocolMeta) and - other._is_protocol): - break - else: - return NotImplemented - return True - if '__subclasshook__' not in cls.__dict__: - cls.__subclasshook__ = _proto_hook - - def __instancecheck__(self, instance): - # We need this method for situations where attributes are - # assigned in __init__. - if ((not getattr(self, '_is_protocol', False) or - _is_callable_members_only(self)) and - issubclass(instance.__class__, self)): - return True - if self._is_protocol: - if all(hasattr(instance, attr) and - (not callable(getattr(self, attr, None)) or - getattr(instance, attr) is not None) - for attr in _get_protocol_attrs(self)): - return True - return super(GenericMeta, self).__instancecheck__(instance) - - def __subclasscheck__(self, cls): - if self.__origin__ is not None: - if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: - raise TypeError("Parameterized generics cannot be used with class " - "or instance checks") - return False - if (self.__dict__.get('_is_protocol', None) and - not self.__dict__.get('_is_runtime_protocol', None)): - if sys._getframe(1).f_globals['__name__'] in ['abc', - 'functools', - 'typing']: - return False - raise TypeError("Instance and class checks can only be used with" - " @runtime protocols") - if (self.__dict__.get('_is_runtime_protocol', None) and - not _is_callable_members_only(self)): - if sys._getframe(1).f_globals['__name__'] in ['abc', - 'functools', - 'typing']: - return super(GenericMeta, self).__subclasscheck__(cls) - raise TypeError("Protocols with non-method members" - " don't support issubclass()") - return super(GenericMeta, self).__subclasscheck__(cls) - - if not OLD_GENERICS: - @_tp_cache - def __getitem__(self, params): - # We also need to copy this from GenericMeta.__getitem__ to get - # special treatment of "Protocol". (Comments removed for brevity.) - if not isinstance(params, tuple): - params = (params,) - if not params and _gorg(self) is not Tuple: - raise TypeError( - "Parameter list to %s[...] cannot be empty" % self.__qualname__) - msg = "Parameters to generic types must be types." - params = tuple(_type_check(p, msg) for p in params) - if self in (Generic, Protocol): - if not all(isinstance(p, TypeVar) for p in params): - raise TypeError( - "Parameters to %r[...] must all be type variables" % self) - if len(set(params)) != len(params): - raise TypeError( - "Parameters to %r[...] must all be unique" % self) - tvars = params - args = params - elif self in (Tuple, Callable): - tvars = _type_vars(params) - args = params - elif self.__origin__ in (Generic, Protocol): - raise TypeError("Cannot subscript already-subscripted %s" % - repr(self)) - else: - _check_generic(self, params) - tvars = _type_vars(params) - args = params - - prepend = (self,) if self.__origin__ is None else () - return self.__class__(self.__name__, - prepend + self.__bases__, - _no_slots_copy(self.__dict__), - tvars=tvars, - args=args, - origin=self, - extra=self.__extra__, - orig_bases=self.__orig_bases__) - - class Protocol(metaclass=_ProtocolMeta): - """Base class for protocol classes. Protocol classes are defined as:: - - class Proto(Protocol): - def meth(self) -> int: - ... - - Such classes are primarily used with static type checkers that recognize - structural subtyping (static duck-typing), for example:: - - class C: - def meth(self) -> int: - return 0 - - def func(x: Proto) -> int: - return x.meth() - - func(C()) # Passes static type check - - See PEP 544 for details. Protocol classes decorated with - @typing_extensions.runtime act as simple-minded runtime protocol that checks - only the presence of given attributes, ignoring their type signatures. - - Protocol classes can be generic, they are defined as:: - - class GenProto({bases}): - def meth(self) -> T: - ... - """ - __slots__ = () - _is_protocol = True - - def __new__(cls, *args, **kwds): - if _gorg(cls) is Protocol: - raise TypeError("Type Protocol cannot be instantiated; " - "it can be used only as a base class") - if OLD_GENERICS: - return _generic_new(_next_in_mro(cls), cls, *args, **kwds) - return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) - if Protocol.__doc__ is not None: - Protocol.__doc__ = Protocol.__doc__.format(bases="Protocol, Generic[T]" if - OLD_GENERICS else "Protocol[T]") - - -elif PEP_560: - from typing import _type_check, _GenericAlias, _collect_type_vars # noqa - - class _ProtocolMeta(abc.ABCMeta): - # This metaclass is a bit unfortunate and exists only because of the lack - # of __instancehook__. - def __instancecheck__(cls, instance): - # We need this method for situations where attributes are - # assigned in __init__. - if ((not getattr(cls, '_is_protocol', False) or - _is_callable_members_only(cls)) and - issubclass(instance.__class__, cls)): - return True - if cls._is_protocol: - if all(hasattr(instance, attr) and - (not callable(getattr(cls, attr, None)) or - getattr(instance, attr) is not None) - for attr in _get_protocol_attrs(cls)): - return True - return super().__instancecheck__(instance) - - class Protocol(metaclass=_ProtocolMeta): - # There is quite a lot of overlapping code with typing.Generic. - # Unfortunately it is hard to avoid this while these live in two different - # modules. The duplicated code will be removed when Protocol is moved to typing. - """Base class for protocol classes. Protocol classes are defined as:: - - class Proto(Protocol): - def meth(self) -> int: - ... - - Such classes are primarily used with static type checkers that recognize - structural subtyping (static duck-typing), for example:: - - class C: - def meth(self) -> int: - return 0 - - def func(x: Proto) -> int: - return x.meth() - - func(C()) # Passes static type check - - See PEP 544 for details. Protocol classes decorated with - @typing_extensions.runtime act as simple-minded runtime protocol that checks - only the presence of given attributes, ignoring their type signatures. - - Protocol classes can be generic, they are defined as:: - - class GenProto(Protocol[T]): - def meth(self) -> T: - ... - """ - __slots__ = () - _is_protocol = True - - def __new__(cls, *args, **kwds): - if cls is Protocol: - raise TypeError("Type Protocol cannot be instantiated; " - "it can only be used as a base class") - return super().__new__(cls) - - @_tp_cache - def __class_getitem__(cls, params): - if not isinstance(params, tuple): - params = (params,) - if not params and cls is not Tuple: - raise TypeError( - "Parameter list to {}[...] cannot be empty".format(cls.__qualname__)) - msg = "Parameters to generic types must be types." - params = tuple(_type_check(p, msg) for p in params) - if cls is Protocol: - # Generic can only be subscripted with unique type variables. - if not all(isinstance(p, TypeVar) for p in params): - i = 0 - while isinstance(params[i], TypeVar): - i += 1 - raise TypeError( - "Parameters to Protocol[...] must all be type variables." - " Parameter {} is {}".format(i + 1, params[i])) - if len(set(params)) != len(params): - raise TypeError( - "Parameters to Protocol[...] must all be unique") - else: - # Subscripting a regular Generic subclass. - _check_generic(cls, params) - return _GenericAlias(cls, params) - - def __init_subclass__(cls, *args, **kwargs): - tvars = [] - if '__orig_bases__' in cls.__dict__: - error = Generic in cls.__orig_bases__ - else: - error = Generic in cls.__bases__ - if error: - raise TypeError("Cannot inherit from plain Generic") - if '__orig_bases__' in cls.__dict__: - tvars = _collect_type_vars(cls.__orig_bases__) - # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn]. - # If found, tvars must be a subset of it. - # If not found, tvars is it. - # Also check for and reject plain Generic, - # and reject multiple Generic[...] and/or Protocol[...]. - gvars = None - for base in cls.__orig_bases__: - if (isinstance(base, _GenericAlias) and - base.__origin__ in (Generic, Protocol)): - # for error messages - the_base = 'Generic' if base.__origin__ is Generic else 'Protocol' - if gvars is not None: - raise TypeError( - "Cannot inherit from Generic[...]" - " and/or Protocol[...] multiple types.") - gvars = base.__parameters__ - if gvars is None: - gvars = tvars - else: - tvarset = set(tvars) - gvarset = set(gvars) - if not tvarset <= gvarset: - s_vars = ', '.join(str(t) for t in tvars if t not in gvarset) - s_args = ', '.join(str(g) for g in gvars) - raise TypeError("Some type variables ({}) are" - " not listed in {}[{}]".format(s_vars, - the_base, s_args)) - tvars = gvars - cls.__parameters__ = tuple(tvars) - - # Determine if this is a protocol or a concrete subclass. - if not cls.__dict__.get('_is_protocol', None): - cls._is_protocol = any(b is Protocol for b in cls.__bases__) - - # Set (or override) the protocol subclass hook. - def _proto_hook(other): - if not cls.__dict__.get('_is_protocol', None): - return NotImplemented - if not getattr(cls, '_is_runtime_protocol', False): - if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: - return NotImplemented - raise TypeError("Instance and class checks can only be used with" - " @runtime protocols") - if not _is_callable_members_only(cls): - if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']: - return NotImplemented - raise TypeError("Protocols with non-method members" - " don't support issubclass()") - if not isinstance(other, type): - # Same error as for issubclass(1, int) - raise TypeError('issubclass() arg 1 must be a class') - for attr in _get_protocol_attrs(cls): - for base in other.__mro__: - if attr in base.__dict__: - if base.__dict__[attr] is None: - return NotImplemented - break - annotations = getattr(base, '__annotations__', {}) - if (isinstance(annotations, typing.Mapping) and - attr in annotations and - isinstance(other, _ProtocolMeta) and - other._is_protocol): - break - else: - return NotImplemented - return True - if '__subclasshook__' not in cls.__dict__: - cls.__subclasshook__ = _proto_hook - - # We have nothing more to do for non-protocols. - if not cls._is_protocol: - return - - # Check consistency of bases. - for base in cls.__bases__: - if not (base in (object, Generic) or - base.__module__ == 'collections.abc' and - base.__name__ in _PROTO_WHITELIST or - isinstance(base, _ProtocolMeta) and base._is_protocol): - raise TypeError('Protocols can only inherit from other' - ' protocols, got %r' % base) - - def _no_init(self, *args, **kwargs): - if type(self)._is_protocol: - raise TypeError('Protocols cannot be instantiated') - cls.__init__ = _no_init - - -if hasattr(typing, 'runtime_checkable'): - runtime_checkable = typing.runtime_checkable -elif HAVE_PROTOCOLS: - def runtime_checkable(cls): - """Mark a protocol class as a runtime protocol, so that it - can be used with isinstance() and issubclass(). Raise TypeError - if applied to a non-protocol class. - - This allows a simple-minded structural check very similar to the - one-offs in collections.abc such as Hashable. - """ - if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: - raise TypeError('@runtime_checkable can be only applied to protocol classes,' - ' got %r' % cls) - cls._is_runtime_protocol = True - return cls - - -if HAVE_PROTOCOLS: - # Exists for backwards compatibility. - runtime = runtime_checkable - - -if hasattr(typing, 'SupportsIndex'): - SupportsIndex = typing.SupportsIndex -elif HAVE_PROTOCOLS: - @runtime_checkable - class SupportsIndex(Protocol): - __slots__ = () - - @abc.abstractmethod - def __index__(self) -> int: - pass - - -if sys.version_info[:2] >= (3, 9): - # The standard library TypedDict in Python 3.8 does not store runtime information - # about which (if any) keys are optional. See https://bugs.python.org/issue38834 - TypedDict = typing.TypedDict -else: - def _check_fails(cls, other): - try: - if sys._getframe(1).f_globals['__name__'] not in ['abc', - 'functools', - 'typing']: - # Typed dicts are only for static structural subtyping. - raise TypeError('TypedDict does not support instance and class checks') - except (AttributeError, ValueError): - pass - return False - - def _dict_new(*args, **kwargs): - if not args: - raise TypeError('TypedDict.__new__(): not enough arguments') - _, args = args[0], args[1:] # allow the "cls" keyword be passed - return dict(*args, **kwargs) - - _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)' - - def _typeddict_new(*args, total=True, **kwargs): - if not args: - raise TypeError('TypedDict.__new__(): not enough arguments') - _, args = args[0], args[1:] # allow the "cls" keyword be passed - if args: - typename, args = args[0], args[1:] # allow the "_typename" keyword be passed - elif '_typename' in kwargs: - typename = kwargs.pop('_typename') - import warnings - warnings.warn("Passing '_typename' as keyword argument is deprecated", - DeprecationWarning, stacklevel=2) - else: - raise TypeError("TypedDict.__new__() missing 1 required positional " - "argument: '_typename'") - if args: - try: - fields, = args # allow the "_fields" keyword be passed - except ValueError: - raise TypeError('TypedDict.__new__() takes from 2 to 3 ' - 'positional arguments but {} ' - 'were given'.format(len(args) + 2)) - elif '_fields' in kwargs and len(kwargs) == 1: - fields = kwargs.pop('_fields') - import warnings - warnings.warn("Passing '_fields' as keyword argument is deprecated", - DeprecationWarning, stacklevel=2) - else: - fields = None - - if fields is None: - fields = kwargs - elif kwargs: - raise TypeError("TypedDict takes either a dict or keyword arguments," - " but not both") - - ns = {'__annotations__': dict(fields), '__total__': total} - try: - # Setting correct module is necessary to make typed dict classes pickleable. - ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') - except (AttributeError, ValueError): - pass - - return _TypedDictMeta(typename, (), ns) - - _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,' - ' /, *, total=True, **kwargs)') - - class _TypedDictMeta(type): - def __new__(cls, name, bases, ns, total=True): - # Create new typed dict class object. - # This method is called directly when TypedDict is subclassed, - # or via _typeddict_new when TypedDict is instantiated. This way - # TypedDict supports all three syntaxes described in its docstring. - # Subclasses and instances of TypedDict return actual dictionaries - # via _dict_new. - ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new - tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns) - - annotations = {} - own_annotations = ns.get('__annotations__', {}) - own_annotation_keys = set(own_annotations.keys()) - msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" - own_annotations = { - n: typing._type_check(tp, msg) for n, tp in own_annotations.items() - } - required_keys = set() - optional_keys = set() - - for base in bases: - annotations.update(base.__dict__.get('__annotations__', {})) - required_keys.update(base.__dict__.get('__required_keys__', ())) - optional_keys.update(base.__dict__.get('__optional_keys__', ())) - - annotations.update(own_annotations) - if total: - required_keys.update(own_annotation_keys) - else: - optional_keys.update(own_annotation_keys) - - tp_dict.__annotations__ = annotations - tp_dict.__required_keys__ = frozenset(required_keys) - tp_dict.__optional_keys__ = frozenset(optional_keys) - if not hasattr(tp_dict, '__total__'): - tp_dict.__total__ = total - return tp_dict - - __instancecheck__ = __subclasscheck__ = _check_fails - - TypedDict = _TypedDictMeta('TypedDict', (dict,), {}) - TypedDict.__module__ = __name__ - TypedDict.__doc__ = \ - """A simple typed name space. At runtime it is equivalent to a plain dict. - - TypedDict creates a dictionary type that expects all of its - instances to have a certain set of keys, with each key - associated with a value of a consistent type. This expectation - is not checked at runtime but is only enforced by type checkers. - Usage:: - - class Point2D(TypedDict): - x: int - y: int - label: str - - a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK - b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check - - assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') - - The type info can be accessed via the Point2D.__annotations__ dict, and - the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets. - TypedDict supports two additional equivalent forms:: - - Point2D = TypedDict('Point2D', x=int, y=int, label=str) - Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) - - The class syntax is only supported in Python 3.6+, while two other - syntax forms work for Python 2.7 and 3.2+ - """ - - -# Python 3.9+ has PEP 593 (Annotated and modified get_type_hints) -if hasattr(typing, 'Annotated'): - Annotated = typing.Annotated - get_type_hints = typing.get_type_hints - # Not exported and not a public API, but needed for get_origin() and get_args() - # to work. - _AnnotatedAlias = typing._AnnotatedAlias -elif PEP_560: - class _AnnotatedAlias(typing._GenericAlias, _root=True): - """Runtime representation of an annotated type. - - At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't' - with extra annotations. The alias behaves like a normal typing alias, - instantiating is the same as instantiating the underlying type, binding - it to types is also the same. - """ - def __init__(self, origin, metadata): - if isinstance(origin, _AnnotatedAlias): - metadata = origin.__metadata__ + metadata - origin = origin.__origin__ - super().__init__(origin, origin) - self.__metadata__ = metadata - - def copy_with(self, params): - assert len(params) == 1 - new_type = params[0] - return _AnnotatedAlias(new_type, self.__metadata__) - - def __repr__(self): - return "typing_extensions.Annotated[{}, {}]".format( - typing._type_repr(self.__origin__), - ", ".join(repr(a) for a in self.__metadata__) - ) - - def __reduce__(self): - return operator.getitem, ( - Annotated, (self.__origin__,) + self.__metadata__ - ) - - def __eq__(self, other): - if not isinstance(other, _AnnotatedAlias): - return NotImplemented - if self.__origin__ != other.__origin__: - return False - return self.__metadata__ == other.__metadata__ - - def __hash__(self): - return hash((self.__origin__, self.__metadata__)) - - class Annotated: - """Add context specific metadata to a type. - - Example: Annotated[int, runtime_check.Unsigned] indicates to the - hypothetical runtime_check module that this type is an unsigned int. - Every other consumer of this type can ignore this metadata and treat - this type as int. - - The first argument to Annotated must be a valid type (and will be in - the __origin__ field), the remaining arguments are kept as a tuple in - the __extra__ field. - - Details: - - - It's an error to call `Annotated` with less than two arguments. - - Nested Annotated are flattened:: - - Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] - - - Instantiating an annotated type is equivalent to instantiating the - underlying type:: - - Annotated[C, Ann1](5) == C(5) - - - Annotated can be used as a generic type alias:: - - Optimized = Annotated[T, runtime.Optimize()] - Optimized[int] == Annotated[int, runtime.Optimize()] - - OptimizedList = Annotated[List[T], runtime.Optimize()] - OptimizedList[int] == Annotated[List[int], runtime.Optimize()] - """ - - __slots__ = () - - def __new__(cls, *args, **kwargs): - raise TypeError("Type Annotated cannot be instantiated.") - - @_tp_cache - def __class_getitem__(cls, params): - if not isinstance(params, tuple) or len(params) < 2: - raise TypeError("Annotated[...] should be used " - "with at least two arguments (a type and an " - "annotation).") - msg = "Annotated[t, ...]: t must be a type." - origin = typing._type_check(params[0], msg) - metadata = tuple(params[1:]) - return _AnnotatedAlias(origin, metadata) - - def __init_subclass__(cls, *args, **kwargs): - raise TypeError( - "Cannot subclass {}.Annotated".format(cls.__module__) - ) - - def _strip_annotations(t): - """Strips the annotations from a given type. - """ - if isinstance(t, _AnnotatedAlias): - return _strip_annotations(t.__origin__) - if isinstance(t, typing._GenericAlias): - stripped_args = tuple(_strip_annotations(a) for a in t.__args__) - if stripped_args == t.__args__: - return t - res = t.copy_with(stripped_args) - res._special = t._special - return res - return t - - def get_type_hints(obj, globalns=None, localns=None, include_extras=False): - """Return type hints for an object. - - This is often the same as obj.__annotations__, but it handles - forward references encoded as string literals, adds Optional[t] if a - default value equal to None is set and recursively replaces all - 'Annotated[T, ...]' with 'T' (unless 'include_extras=True'). - - The argument may be a module, class, method, or function. The annotations - are returned as a dictionary. For classes, annotations include also - inherited members. - - TypeError is raised if the argument is not of a type that can contain - annotations, and an empty dictionary is returned if no annotations are - present. - - BEWARE -- the behavior of globalns and localns is counterintuitive - (unless you are familiar with how eval() and exec() work). The - search order is locals first, then globals. - - - If no dict arguments are passed, an attempt is made to use the - globals from obj (or the respective module's globals for classes), - and these are also used as the locals. If the object does not appear - to have globals, an empty dictionary is used. - - - If one dict argument is passed, it is used for both globals and - locals. - - - If two dict arguments are passed, they specify globals and - locals, respectively. - """ - hint = typing.get_type_hints(obj, globalns=globalns, localns=localns) - if include_extras: - return hint - return {k: _strip_annotations(t) for k, t in hint.items()} - -elif HAVE_ANNOTATED: - - def _is_dunder(name): - """Returns True if name is a __dunder_variable_name__.""" - return len(name) > 4 and name.startswith('__') and name.endswith('__') - - # Prior to Python 3.7 types did not have `copy_with`. A lot of the equality - # checks, argument expansion etc. are done on the _subs_tre. As a result we - # can't provide a get_type_hints function that strips out annotations. - - class AnnotatedMeta(typing.GenericMeta): - """Metaclass for Annotated""" - - def __new__(cls, name, bases, namespace, **kwargs): - if any(b is not object for b in bases): - raise TypeError("Cannot subclass " + str(Annotated)) - return super().__new__(cls, name, bases, namespace, **kwargs) - - @property - def __metadata__(self): - return self._subs_tree()[2] - - def _tree_repr(self, tree): - cls, origin, metadata = tree - if not isinstance(origin, tuple): - tp_repr = typing._type_repr(origin) - else: - tp_repr = origin[0]._tree_repr(origin) - metadata_reprs = ", ".join(repr(arg) for arg in metadata) - return '%s[%s, %s]' % (cls, tp_repr, metadata_reprs) - - def _subs_tree(self, tvars=None, args=None): # noqa - if self is Annotated: - return Annotated - res = super()._subs_tree(tvars=tvars, args=args) - # Flatten nested Annotated - if isinstance(res[1], tuple) and res[1][0] is Annotated: - sub_tp = res[1][1] - sub_annot = res[1][2] - return (Annotated, sub_tp, sub_annot + res[2]) - return res - - def _get_cons(self): - """Return the class used to create instance of this type.""" - if self.__origin__ is None: - raise TypeError("Cannot get the underlying type of a " - "non-specialized Annotated type.") - tree = self._subs_tree() - while isinstance(tree, tuple) and tree[0] is Annotated: - tree = tree[1] - if isinstance(tree, tuple): - return tree[0] - else: - return tree - - @_tp_cache - def __getitem__(self, params): - if not isinstance(params, tuple): - params = (params,) - if self.__origin__ is not None: # specializing an instantiated type - return super().__getitem__(params) - elif not isinstance(params, tuple) or len(params) < 2: - raise TypeError("Annotated[...] should be instantiated " - "with at least two arguments (a type and an " - "annotation).") - else: - msg = "Annotated[t, ...]: t must be a type." - tp = typing._type_check(params[0], msg) - metadata = tuple(params[1:]) - return self.__class__( - self.__name__, - self.__bases__, - _no_slots_copy(self.__dict__), - tvars=_type_vars((tp,)), - # Metadata is a tuple so it won't be touched by _replace_args et al. - args=(tp, metadata), - origin=self, - ) - - def __call__(self, *args, **kwargs): - cons = self._get_cons() - result = cons(*args, **kwargs) - try: - result.__orig_class__ = self - except AttributeError: - pass - return result - - def __getattr__(self, attr): - # For simplicity we just don't relay all dunder names - if self.__origin__ is not None and not _is_dunder(attr): - return getattr(self._get_cons(), attr) - raise AttributeError(attr) - - def __setattr__(self, attr, value): - if _is_dunder(attr) or attr.startswith('_abc_'): - super().__setattr__(attr, value) - elif self.__origin__ is None: - raise AttributeError(attr) - else: - setattr(self._get_cons(), attr, value) - - def __instancecheck__(self, obj): - raise TypeError("Annotated cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("Annotated cannot be used with issubclass().") - - class Annotated(metaclass=AnnotatedMeta): - """Add context specific metadata to a type. - - Example: Annotated[int, runtime_check.Unsigned] indicates to the - hypothetical runtime_check module that this type is an unsigned int. - Every other consumer of this type can ignore this metadata and treat - this type as int. - - The first argument to Annotated must be a valid type, the remaining - arguments are kept as a tuple in the __metadata__ field. - - Details: - - - It's an error to call `Annotated` with less than two arguments. - - Nested Annotated are flattened:: - - Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3] - - - Instantiating an annotated type is equivalent to instantiating the - underlying type:: - - Annotated[C, Ann1](5) == C(5) - - - Annotated can be used as a generic type alias:: - - Optimized = Annotated[T, runtime.Optimize()] - Optimized[int] == Annotated[int, runtime.Optimize()] - - OptimizedList = Annotated[List[T], runtime.Optimize()] - OptimizedList[int] == Annotated[List[int], runtime.Optimize()] - """ - -# Python 3.8 has get_origin() and get_args() but those implementations aren't -# Annotated-aware, so we can't use those, only Python 3.9 versions will do. -if sys.version_info[:2] >= (3, 9): - get_origin = typing.get_origin - get_args = typing.get_args -elif PEP_560: - from typing import _GenericAlias # noqa - - def get_origin(tp): - """Get the unsubscripted version of a type. - - This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar - and Annotated. Return None for unsupported types. Examples:: - - get_origin(Literal[42]) is Literal - get_origin(int) is None - get_origin(ClassVar[int]) is ClassVar - get_origin(Generic) is Generic - get_origin(Generic[T]) is Generic - get_origin(Union[T, int]) is Union - get_origin(List[Tuple[T, T]][int]) == list - """ - if isinstance(tp, _AnnotatedAlias): - return Annotated - if isinstance(tp, _GenericAlias): - return tp.__origin__ - if tp is Generic: - return Generic - return None - - def get_args(tp): - """Get type arguments with all substitutions performed. - - For unions, basic simplifications used by Union constructor are performed. - Examples:: - get_args(Dict[str, int]) == (str, int) - get_args(int) == () - get_args(Union[int, Union[T, int], str][int]) == (int, str) - get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int]) - get_args(Callable[[], T][int]) == ([], int) - """ - if isinstance(tp, _AnnotatedAlias): - return (tp.__origin__,) + tp.__metadata__ - if isinstance(tp, _GenericAlias) and not tp._special: - res = tp.__args__ - if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis: - res = (list(res[:-1]), res[-1]) - return res - return () - - -if hasattr(typing, 'TypeAlias'): - TypeAlias = typing.TypeAlias -elif sys.version_info[:2] >= (3, 9): - class _TypeAliasForm(typing._SpecialForm, _root=True): - def __repr__(self): - return 'typing_extensions.' + self._name - - @_TypeAliasForm - def TypeAlias(self, parameters): - """Special marker indicating that an assignment should - be recognized as a proper type alias definition by type - checkers. - - For example:: - - Predicate: TypeAlias = Callable[..., bool] - - It's invalid when used anywhere except as in the example above. - """ - raise TypeError("{} is not subscriptable".format(self)) - -elif sys.version_info[:2] >= (3, 7): - class _TypeAliasForm(typing._SpecialForm, _root=True): - def __repr__(self): - return 'typing_extensions.' + self._name - - TypeAlias = _TypeAliasForm('TypeAlias', - doc="""Special marker indicating that an assignment should - be recognized as a proper type alias definition by type - checkers. - - For example:: - - Predicate: TypeAlias = Callable[..., bool] - - It's invalid when used anywhere except as in the example - above.""") - -elif hasattr(typing, '_FinalTypingBase'): - class _TypeAliasMeta(typing.TypingMeta): - """Metaclass for TypeAlias""" - - def __repr__(self): - return 'typing_extensions.TypeAlias' - - class _TypeAliasBase(typing._FinalTypingBase, metaclass=_TypeAliasMeta, _root=True): - """Special marker indicating that an assignment should - be recognized as a proper type alias definition by type - checkers. - - For example:: - - Predicate: TypeAlias = Callable[..., bool] - - It's invalid when used anywhere except as in the example above. - """ - __slots__ = () - - def __instancecheck__(self, obj): - raise TypeError("TypeAlias cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("TypeAlias cannot be used with issubclass().") - - def __repr__(self): - return 'typing_extensions.TypeAlias' - - TypeAlias = _TypeAliasBase(_root=True) -else: - class _TypeAliasMeta(typing.TypingMeta): - """Metaclass for TypeAlias""" - - def __instancecheck__(self, obj): - raise TypeError("TypeAlias cannot be used with isinstance().") - - def __subclasscheck__(self, cls): - raise TypeError("TypeAlias cannot be used with issubclass().") - - def __call__(self, *args, **kwargs): - raise TypeError("Cannot instantiate TypeAlias") - - class TypeAlias(metaclass=_TypeAliasMeta, _root=True): - """Special marker indicating that an assignment should - be recognized as a proper type alias definition by type - checkers. - - For example:: - - Predicate: TypeAlias = Callable[..., bool] - - It's invalid when used anywhere except as in the example above. - """ - __slots__ = () diff --git a/typing_extensions/tox.ini b/typing_extensions/tox.ini deleted file mode 100644 index 7bb6156d7..000000000 --- a/typing_extensions/tox.ini +++ /dev/null @@ -1,9 +0,0 @@ -[tox] -envlist = py27, py34, py35, py36, py37, py38, py39 - -[testenv] -changedir = src_py3 -commands = python -m unittest discover - -[testenv:py27] -changedir = src_py2 diff --git a/update-mypy.sh b/update-mypy.sh deleted file mode 100755 index 704e4bd7f..000000000 --- a/update-mypy.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -xe - -case $# in - 0) echo "Please supply a commit message as argument(s)"; exit 2;; -esac - -HERE=$PWD - -cd ~/src/mypy -git co master -git pull - -cp $HERE/src/typing.py lib-typing/3.2/typing.py -cp $HERE/src/test_typing.py lib-typing/3.2/test_typing.py - -cp $HERE/python2/typing.py lib-typing/2.7/typing.py -cp $HERE/python2/test_typing.py lib-typing/2.7/test_typing.py - -git ci lib-typing -m "$@" diff --git a/update-stdlib.sh b/update-stdlib.sh deleted file mode 100755 index f27560b54..000000000 --- a/update-stdlib.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -xe - -case $# in - 0) echo "Please supply a commit message as argument(s)"; exit 2;; -esac - -HERE=$PWD - -cd ~/src/cpython35 -hg pull -u -cp $HERE/src/typing.py Lib/typing.py -cp $HERE/src/test_typing.py Lib/test/test_typing.py -hg ci -m "$@" - -cd ~/src/cpython36 -hg pull -u ../cpython35 -hg merge 3.5 -hg ci -m "$@ (3.5->3.6)" - -cd ~/src/cpython37 -hg pull -u ../cpython36 -hg merge 3.6 -hg ci -m "$@ (3.6->3.7)"