Skip to content

Commit

Permalink
Fix mypy for pydantic v1 and v2
Browse files Browse the repository at this point in the history
This required that we allow unused igores since the location
of the ignores depends on the version of pydantic and mypy
always picks up the first definition it sees even if that
is a pydantic V2 code path and pydantic is v1.
  • Loading branch information
timj committed Jul 18, 2023
1 parent e96015d commit 92a5351
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 8 deletions.
2 changes: 1 addition & 1 deletion mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ disallow_untyped_defs = True
disallow_incomplete_defs = True
strict_equality = True
warn_unreachable = True
warn_unused_ignores = True
warn_unused_ignores = False

# ...except the modules and subpackages below (can't find a way to do line
# breaks in the lists of modules).
Expand Down
51 changes: 46 additions & 5 deletions python/lsst/daf/butler/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

import sys
from collections.abc import Callable
from typing import Any
from typing import TYPE_CHECKING, Any

from pydantic import BaseModel
from pydantic.version import VERSION as PYDANTIC_VERSION
Expand Down Expand Up @@ -54,8 +54,8 @@ class _BaseModelCompat(BaseModel):
def json(
self,
*,
include: set[int] | set[str] | dict[int, Any] | dict[str, Any] | None = None,
exclude: set[int] | set[str] | dict[int, Any] | dict[str, Any] | None = None,
include: set[int] | set[str] | dict[int, Any] | dict[str, Any] | None = None, # type: ignore
exclude: set[int] | set[str] | dict[int, Any] | dict[str, Any] | None = None, # type: ignore
by_alias: bool = False,
skip_defaults: bool | None = None,
exclude_unset: bool = False,
Expand Down Expand Up @@ -84,9 +84,46 @@ def parse_obj(cls, obj: Any) -> Self:
# Catch warnings and call BaseModel.parse_obj directly?
return cls.model_validate(obj)

if TYPE_CHECKING and not PYDANTIC_V2:
# mypy sees the first definition of a class and ignores any
# redefinition. This means that if mypy is run with pydantic v1
# it will not see the classes defined in the else block below.

@classmethod

Check warning on line 92 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L92

Added line #L92 was not covered by tests
def model_construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self:
return cls()

Check warning on line 94 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L94

Added line #L94 was not covered by tests

@classmethod

Check warning on line 96 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L96

Added line #L96 was not covered by tests
def model_validate(
cls,
obj: Any,
*,
strict: bool | None = None,
from_attributes: bool | None = None,
context: dict[str, Any] | None = None,
) -> Self:
return cls()

Check warning on line 105 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L105

Added line #L105 was not covered by tests

def model_dump_json(

Check warning on line 107 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L107

Added line #L107 was not covered by tests
self,
*,
indent: int | None = None,
include: set[int] | set[str] | dict[int, Any] | dict[str, Any] | None = None,
exclude: set[int] | set[str] | dict[int, Any] | dict[str, Any] | None = None,
by_alias: bool = False,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
round_trip: bool = False,
warnings: bool = True,
) -> str:
return ""

Check warning on line 120 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L120

Added line #L120 was not covered by tests

else:

class _BaseModelCompat(BaseModel): # type:ignore[no-redef]

Check warning on line 124 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L124

Added line #L124 was not covered by tests
"""Methods from pydantic v2 that can be used in pydantic v1."""

@classmethod

Check warning on line 127 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L127

Added line #L127 was not covered by tests
def model_validate(
cls,
Expand All @@ -112,10 +149,14 @@ def model_dump_json(
warnings: bool = True,
) -> str:
return self.json(

Check warning on line 151 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L151

Added line #L151 was not covered by tests
include=include,
exclude=exclude,
include=include, # type: ignore
exclude=exclude, # type: ignore
by_alias=by_alias,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)

@classmethod # type: ignore

Check warning on line 160 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L160

Added line #L160 was not covered by tests
def model_construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self:
return cls.construct(_fields_set=_fields_set, **values)

Check warning on line 162 in python/lsst/daf/butler/_compat.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/daf/butler/_compat.py#L162

Added line #L162 was not covered by tests
2 changes: 1 addition & 1 deletion python/lsst/daf/butler/core/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ def __str__(self) -> str:


if PYDANTIC_V2:
from pydantic import RootModel
from pydantic import RootModel # type: ignore

class _ButlerLogRecords(RootModel):
root: list[ButlerLogRecord]
Expand Down
2 changes: 1 addition & 1 deletion python/lsst/daf/butler/registry/wildcards.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ def process(element: Any, alreadyCoerced: bool = False) -> EllipsisType | None:


if PYDANTIC_V2:
from pydantic import RootModel
from pydantic import RootModel # type: ignore

class _CollectionSearch(RootModel):
root: tuple[str, ...]
Expand Down

0 comments on commit 92a5351

Please sign in to comment.