From 60f704721a096dced3b19dd465a1e31495fcd802 Mon Sep 17 00:00:00 2001 From: Victorien <65306057+Viicos@users.noreply.github.com> Date: Mon, 14 Oct 2024 17:28:12 +0200 Subject: [PATCH] Upgrade `pyright` to `1.1.384` (#10620) Change tests to align with removal of PEP 746 support. Move Pyright tests out of the mypy CI job. --- .github/workflows/ci.yml | 37 +++++++++++++++-------- .pre-commit-config.yaml | 2 +- Makefile | 2 +- pydantic/_internal/_generate_schema.py | 2 +- pydantic/_internal/_model_construction.py | 13 ++++---- tests/pyright/README.md | 25 +++++++++++++++ tests/pyright/pipeline_api.py | 17 ++++++----- 7 files changed, 69 insertions(+), 29 deletions(-) create mode 100644 tests/pyright/README.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53c4d99e9ae..86307775980 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -267,26 +267,39 @@ jobs: COVERAGE_FILE: coverage/.coverage.linux-py${{ matrix.python-version }}-mypy${{ matrix.mypy-version }} CONTEXT: linux-py${{ matrix.python-version }}-mypy${{ matrix.mypy-version }} + - name: store coverage files + uses: actions/upload-artifact@v4 + with: + name: coverage-${{ matrix.python-version }}-mypy${{ matrix.mypy-version }} + path: coverage + include-hidden-files: true + + test-pyright: + name: Pyright tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: install node for pyright uses: actions/setup-node@v4 with: - node-version: '18' + node-version: '20' + + - uses: pdm-project/setup-pdm@v4 + with: + python-version: '3.12' + cache: true + + - name: install deps + run: | + pdm venv create --force $PYTHON + pdm install - name: install pyright - run: npm install -g pyright@1.1.369 # try to keep this in sync with .pre-commit-config.yaml + run: npm install -g pyright@1.1.384 # keep this in sync with .pre-commit-config.yaml - name: run pyright tests run: make test-pyright - env: - COVERAGE_FILE: coverage/.coverage.linux-py${{ matrix.python-version }}-pyright - CONTEXT: linux-py${{ matrix.python-version }}-pyright - - - name: store coverage files - uses: actions/upload-artifact@v4 - with: - name: coverage-${{ matrix.python-version }}-mypy${{ matrix.mypy-version }} - path: coverage - include-hidden-files: true coverage-combine: needs: [test, test-mypy] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c52b16d6666..2b799d6c00b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,4 +36,4 @@ repos: types: [python] language: node pass_filenames: false - additional_dependencies: ["pyright@1.1.369"] # try to keep this in sync with .github/workflows/ci.yml + additional_dependencies: ["pyright@1.1.384"] # keep this in sync with .github/workflows/ci.yml diff --git a/Makefile b/Makefile index 22e3f873b2d..063e1513db7 100644 --- a/Makefile +++ b/Makefile @@ -60,7 +60,7 @@ test-mypy-update-all: .pdm .PHONY: test-pyright ## Run the pyright integration tests test-pyright: .pdm - pdm run bash -c 'cd tests/pyright && pyright' + pdm run bash -c 'cd tests/pyright && pyright --version && pyright -p pyproject.toml' .PHONY: test ## Run all tests, skipping the type-checker integration tests test: .pdm diff --git a/pydantic/_internal/_generate_schema.py b/pydantic/_internal/_generate_schema.py index 90d5bc1d254..b6301f11653 100644 --- a/pydantic/_internal/_generate_schema.py +++ b/pydantic/_internal/_generate_schema.py @@ -1535,7 +1535,7 @@ def _namedtuple_schema(self, namedtuple_cls: Any, origin: Any) -> core_schema.Co raise PydanticUndefinedAnnotation.from_name_error(e) from e if not annotations: # annotations is empty, happens if namedtuple_cls defined via collections.namedtuple(...) - annotations = {k: Any for k in namedtuple_cls._fields} + annotations: dict[str, Any] = {k: Any for k in namedtuple_cls._fields} if typevars_map: annotations = { diff --git a/pydantic/_internal/_model_construction.py b/pydantic/_internal/_model_construction.py index 9ec5a5ab176..9ed7f9a4c2c 100644 --- a/pydantic/_internal/_model_construction.py +++ b/pydantic/_internal/_model_construction.py @@ -11,7 +11,7 @@ from abc import ABCMeta from functools import lru_cache, partial from types import FunctionType -from typing import Any, Callable, Generic, Literal, NoReturn +from typing import Any, Callable, Generic, Literal, NoReturn, cast import typing_extensions from pydantic_core import PydanticUndefined, SchemaSerializer @@ -136,12 +136,11 @@ def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None: if __pydantic_generic_metadata__: namespace['__pydantic_generic_metadata__'] = __pydantic_generic_metadata__ - cls: type[BaseModel] = super().__new__(mcs, cls_name, bases, namespace, **kwargs) # type: ignore - - BaseModel = import_cached_base_model() + cls = cast('type[BaseModel]', super().__new__(mcs, cls_name, bases, namespace, **kwargs)) + BaseModel_ = import_cached_base_model() mro = cls.__mro__ - if Generic in mro and mro.index(Generic) < mro.index(BaseModel): + if Generic in mro and mro.index(Generic) < mro.index(BaseModel_): warnings.warn( GenericBeforeBaseModelWarning( 'Classes should inherit from `BaseModel` before generic classes (e.g. `typing.Generic[T]`) ' @@ -151,7 +150,9 @@ def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None: ) cls.__pydantic_custom_init__ = not getattr(cls.__init__, '__pydantic_base_init__', False) - cls.__pydantic_post_init__ = None if cls.model_post_init is BaseModel.model_post_init else 'model_post_init' + cls.__pydantic_post_init__ = ( + None if cls.model_post_init is BaseModel_.model_post_init else 'model_post_init' + ) cls.__pydantic_decorators__ = DecoratorInfos.build(cls) diff --git a/tests/pyright/README.md b/tests/pyright/README.md new file mode 100644 index 00000000000..42c961cea78 --- /dev/null +++ b/tests/pyright/README.md @@ -0,0 +1,25 @@ +# Pyright test suite + +Use [`assert_type`](https://docs.python.org/3/library/typing.html#typing.assert_type) to make assertions: + +```python +from typing_extensions import assert_type + +from pydantic import TypeAdapter + +ta1 = TypeAdapter(int) +assert_type(ta1, TypeAdapter[int]) +``` + +To assert on invalid cases, add a `pyright: ignore` comment: + +```python +from pydantic import BaseModel + + +class Model(BaseModel): + a: int + + +Model() # pyright: ignore[reportCallIssue] +``` diff --git a/tests/pyright/pipeline_api.py b/tests/pyright/pipeline_api.py index b95ddbb36da..bb4ccf80968 100644 --- a/tests/pyright/pipeline_api.py +++ b/tests/pyright/pipeline_api.py @@ -3,14 +3,15 @@ from pydantic.experimental.pipeline import validate_as -# this test works by adding type ignores and having pyright fail with -# an unused type ignore error if the type checking isn't working -Annotated[str, validate_as(int)] # type: ignore -Annotated[str, validate_as(str).transform(lambda x: int(x))] # type: ignore -Annotated[float, validate_as(float).gt(0)] # should be able to compare float to int -Annotated[datetime.datetime, validate_as(datetime.datetime).datetime_tz_naive()] -Annotated[datetime.datetime, validate_as(str).datetime_tz_naive()] # type: ignore -Annotated[ +# TODO: since Pyright 1.1.384, support for PEP 746 was disabled. +# `a1` and `a2` should have a `pyright: ignore[reportInvalidTypeArguments]` comment. +a1 = Annotated[str, validate_as(int)] +a2 = Annotated[str, validate_as(str).transform(lambda x: int(x))] +a3 = Annotated[float, validate_as(float).gt(0)] # should be able to compare float to int + +a4 = Annotated[datetime.datetime, validate_as(datetime.datetime).datetime_tz_naive()] +a5 = Annotated[datetime.datetime, validate_as(str).datetime_tz_naive()] # pyright: ignore[reportAttributeAccessIssue] +a6 = Annotated[ datetime.datetime, ( validate_as(str).transform(str.strip).validate_as(datetime.datetime).datetime_tz_naive()