Skip to content

Commit

Permalink
really drop python<=3.7 support
Browse files Browse the repository at this point in the history
Filter all code over `pyupgrade --py38`.

Signed-off-by: Tomasz Kłoczko <kloczek@github.com>
  • Loading branch information
kloczek committed Mar 19, 2024
1 parent 09f4456 commit f829337
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 89 deletions.
4 changes: 2 additions & 2 deletions pydantic/types.py
Expand Up @@ -2425,7 +2425,7 @@ class Model(BaseModel):
def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
return core_schema.with_info_after_validator_function(
function=self.decode_str,
schema=super(EncodedStr, self).__get_pydantic_core_schema__(source=source, handler=handler), # noqa: UP008
schema=super().__get_pydantic_core_schema__(source=source, handler=handler), # noqa: UP008
serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode_str),
)

Expand All @@ -2449,7 +2449,7 @@ def encode_str(self, value: str) -> str:
Returns:
The encoded data.
"""
return super(EncodedStr, self).encode(value=value.encode()).decode() # noqa: UP008
return super().encode(value=value.encode()).decode() # noqa: UP008

def __hash__(self) -> int:
return hash(self.encoder)
Expand Down
9 changes: 2 additions & 7 deletions pydantic/v1/dataclasses.py
Expand Up @@ -416,15 +416,10 @@ def create_pydantic_model_from_dataclass(
return model


if sys.version_info >= (3, 8):

def _is_field_cached_property(obj: 'Dataclass', k: str) -> bool:
return isinstance(getattr(type(obj), k, None), cached_property)
def _is_field_cached_property(obj: 'Dataclass', k: str) -> bool:
return isinstance(getattr(type(obj), k, None), cached_property)

else:

def _is_field_cached_property(obj: 'Dataclass', k: str) -> bool:
return False


def _dataclass_validate_values(self: 'Dataclass') -> None:
Expand Down
3 changes: 2 additions & 1 deletion pydantic/v1/fields.py
Expand Up @@ -26,7 +26,8 @@
Union,
)

from typing_extensions import Annotated, Final
from typing_extensions import Annotated
from typing import Final

from . import errors as errors_
from .class_validators import Validator, make_generic_validator, prep_validators
Expand Down
3 changes: 1 addition & 2 deletions pydantic/v1/generics.py
Expand Up @@ -31,8 +31,7 @@

if sys.version_info >= (3, 10):
from typing import _UnionGenericAlias
if sys.version_info >= (3, 8):
from typing import Literal
from typing import Literal

GenericModelT = TypeVar('GenericModelT', bound='GenericModel')
TypeVarType = Any # since mypy doesn't allow the use of TypeVar as a type
Expand Down
96 changes: 29 additions & 67 deletions pydantic/v1/typing.py
Expand Up @@ -29,11 +29,11 @@

from typing_extensions import (
Annotated,
Final,
Literal,
NotRequired as TypedDictNotRequired,
Required as TypedDictRequired,
)
from typing import Final

try:
from typing import _TypingBase as typing_base # type: ignore
Expand Down Expand Up @@ -97,72 +97,44 @@ def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> A
LITERAL_TYPES.add(typing.Literal)


if sys.version_info < (3, 8):
from typing import get_origin as _typing_get_origin

def get_origin(t: Type[Any]) -> Optional[Type[Any]]:
if type(t).__name__ in AnnotatedTypeNames:
# weirdly this is a runtime requirement, as well as for mypy
return cast(Type[Any], Annotated)
return getattr(t, '__origin__', None)

else:
from typing import get_origin as _typing_get_origin

def get_origin(tp: Type[Any]) -> Optional[Type[Any]]:
"""
def get_origin(tp: Type[Any]) -> Optional[Type[Any]]:
"""
We can't directly use `typing.get_origin` since we need a fallback to support
custom generic classes like `ConstrainedList`
It should be useless once https://github.com/cython/cython/issues/3537 is
solved and https://github.com/pydantic/pydantic/pull/1753 is merged.
"""
if type(tp).__name__ in AnnotatedTypeNames:
return cast(Type[Any], Annotated) # mypy complains about _SpecialForm
return _typing_get_origin(tp) or getattr(tp, '__origin__', None)
if type(tp).__name__ in AnnotatedTypeNames:
return cast(Type[Any], Annotated) # mypy complains about _SpecialForm
return _typing_get_origin(tp) or getattr(tp, '__origin__', None)


if sys.version_info < (3, 8):
from typing import _GenericAlias

def get_args(t: Type[Any]) -> Tuple[Any, ...]:
"""Compatibility version of get_args for python 3.7.
Mostly compatible with the python 3.8 `typing` module version
and able to handle almost all use cases.
"""
if type(t).__name__ in AnnotatedTypeNames:
return t.__args__ + t.__metadata__
if isinstance(t, _GenericAlias):
res = t.__args__
if t.__origin__ is Callable and res and res[0] is not Ellipsis:
res = (list(res[:-1]), res[-1])
return res
return getattr(t, '__args__', ())

else:
from typing import get_args as _typing_get_args
from typing import get_args as _typing_get_args

def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]:
"""
def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]:
"""
In python 3.9, `typing.Dict`, `typing.List`, ...
do have an empty `__args__` by default (instead of the generic ~T for example).
In order to still support `Dict` for example and consider it as `Dict[Any, Any]`,
we retrieve the `_nparams` value that tells us how many parameters it needs.
"""
if hasattr(tp, '_nparams'):
return (Any,) * tp._nparams
# Special case for `tuple[()]`, which used to return ((),) with `typing.Tuple`
# in python 3.10- but now returns () for `tuple` and `Tuple`.
# This will probably be clarified in pydantic v2
try:
if tp == Tuple[()] or sys.version_info >= (3, 9) and tp == tuple[()]: # type: ignore[misc]
return ((),)
# there is a TypeError when compiled with cython
except TypeError: # pragma: no cover
pass
return ()
if hasattr(tp, '_nparams'):
return (Any,) * tp._nparams
# Special case for `tuple[()]`, which used to return ((),) with `typing.Tuple`
# in python 3.10- but now returns () for `tuple` and `Tuple`.
# This will probably be clarified in pydantic v2
try:
if tp == Tuple[()] or sys.version_info >= (3, 9) and tp == tuple[()]: # type: ignore[misc]
return ((),)
# there is a TypeError when compiled with cython
except TypeError: # pragma: no cover
pass
return ()

def get_args(tp: Type[Any]) -> Tuple[Any, ...]:
"""Get type arguments with all substitutions performed.
def get_args(tp: Type[Any]) -> Tuple[Any, ...]:
"""Get type arguments with all substitutions performed.
For unions, basic simplifications used by Union constructor are performed.
Examples::
Expand All @@ -172,10 +144,10 @@ def get_args(tp: Type[Any]) -> Tuple[Any, ...]:
get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
get_args(Callable[[], T][int]) == ([], int)
"""
if type(tp).__name__ in AnnotatedTypeNames:
return tp.__args__ + tp.__metadata__
# the fallback is needed for the same reasons as `get_origin` (see above)
return _typing_get_args(tp) or getattr(tp, '__args__', ()) or _generic_get_args(tp)
if type(tp).__name__ in AnnotatedTypeNames:
return tp.__args__ + tp.__metadata__
# the fallback is needed for the same reasons as `get_origin` (see above)
return _typing_get_args(tp) or getattr(tp, '__args__', ()) or _generic_get_args(tp)


if sys.version_info < (3, 9):
Expand Down Expand Up @@ -326,17 +298,7 @@ def is_union(tp: Optional[Type[Any]]) -> bool:
NONE_TYPES: Tuple[Any, Any, Any] = (None, NoneType, Literal[None])


if sys.version_info < (3, 8):
# Even though this implementation is slower, we need it for python 3.7:
# In python 3.7 "Literal" is not a builtin type and uses a different
# mechanism.
# for this reason `Literal[None] is Literal[None]` evaluates to `False`,
# breaking the faster implementation used for the other python versions.

def is_none_type(type_: Any) -> bool:
return type_ in NONE_TYPES

elif sys.version_info[:2] == (3, 8):
if sys.version_info[:2] == (3, 8):

def is_none_type(type_: Any) -> bool:
for none_type in NONE_TYPES:
Expand Down
10 changes: 5 additions & 5 deletions tests/benchmarks/test_fastapi_startup_generics.py
Expand Up @@ -59,17 +59,17 @@ class GetModel2(GetModel[T], Generic[T]):
bar: str

class GetManyModel(BaseModel, Generic[T]):
res: List[T]
res: list[T]

class GetManyModel2(GetManyModel[T], Generic[T]):
foo: str
bar: str

class GetManyModel3(BaseModel, Generic[T]):
res: Dict[str, T]
res: dict[str, T]

class GetManyModel4(BaseModel, Generic[T]):
res: Dict[str, List[T]]
res: dict[str, list[T]]

class PutModel(BaseModel, Generic[T]):
data: T
Expand All @@ -79,13 +79,13 @@ class PutModel2(PutModel[T], Generic[T]):
bar: str

class PutManyModel(BaseModel, Generic[T]):
data: List[T]
data: list[T]

class PutManyModel2(PutManyModel[T], Generic[T]):
foo: str
bar: str

api_models: List[Any] = [
api_models: list[Any] = [
GetModel,
GetModel2,
GetManyModel,
Expand Down
10 changes: 5 additions & 5 deletions tests/benchmarks/test_fastapi_startup_simple.py
Expand Up @@ -20,7 +20,7 @@ class User(BaseModel):
id: int
username: str
email: EmailStr
full_name: Optional[str] = None
full_name: str | None = None

class Address(BaseModel):
street: str
Expand All @@ -32,7 +32,7 @@ class Product(BaseModel):
id: int
name: str
price: Annotated[float, Gt(0)]
description: Optional[str] = None
description: str | None = None

class BlogPost(BaseModel):
title: Annotated[str, StringConstraints(pattern=r'[A-Za-z0-9]+')]
Expand All @@ -43,13 +43,13 @@ class BlogPost(BaseModel):
class Website(BaseModel):
name: str
url: AnyUrl
description: Optional[str] = None
description: str | None = None

class Order(BaseModel):
order_id: str
customer: User
shipping_address: Address
products: List[Product]
products: list[Product]

class Comment(BaseModel):
text: str
Expand All @@ -65,7 +65,7 @@ class Event(BaseModel):

class Category(BaseModel):
name: str
description: Optional[str] = None
description: str | None = None

ReviewGroup = List[Dict[Tuple[User, Product], Comment]]

Expand Down

0 comments on commit f829337

Please sign in to comment.