Files
Matthew Gamble bc1f09b3e4 Support kwargs model config in Mypy plugin (#4912)
Previously, the Mypy plugin only supported drawing settings from a
Config class. Now, it fully supports drawing settings from class kwargs
too.

Co-authored-by: Matthew Gamble <matthew.gamble@rea-group.com>
2023-01-13 13:54:26 +00:00

322 lines
6.0 KiB
Python

from typing import Any, Generic, List, Optional, Set, TypeVar, Union
from pydantic import BaseModel, Extra, Field, validator
from pydantic.dataclasses import dataclass
from pydantic.generics import GenericModel
class Model(BaseModel):
x: int
y: str
def method(self) -> None:
pass
class Config:
alias_generator = None
allow_mutation = False
extra = Extra.forbid
def config_method(self) -> None:
...
model = Model(x=1, y='y', z='z')
model = Model(x=1)
model.y = 'a'
Model.from_orm({})
Model.from_orm({}) # type: ignore[pydantic-orm] # noqa F821
class KwargsModel(BaseModel, alias_generator=None, allow_mutation=False, extra=Extra.forbid):
x: int
y: str
def method(self) -> None:
pass
kwargs_model = KwargsModel(x=1, y='y', z='z')
kwargs_model = KwargsModel(x=1)
kwargs_model.y = 'a'
KwargsModel.from_orm({})
KwargsModel.from_orm({}) # type: ignore[pydantic-orm] # noqa F821
class ForbidExtraModel(BaseModel):
class Config:
extra = 'forbid'
ForbidExtraModel(x=1)
class KwargsForbidExtraModel(BaseModel, extra='forbid'):
pass
KwargsForbidExtraModel(x=1)
class ForbidExtraModel2(BaseModel):
class Config:
extra = 'forbid'
validate_all = False
Config.validate_all = True
ForbidExtraModel2(x=1)
class BadExtraModel(BaseModel):
class Config:
extra = 1 # type: ignore[pydantic-config] # noqa F821
extra = 1
class KwargsBadExtraModel(BaseModel, extra=1):
pass
class BadConfig1(BaseModel):
class Config:
orm_mode: Any = {} # not sensible, but should still be handled gracefully
class KwargsBadConfig1(BaseModel, orm_mode={}):
pass
class BadConfig2(BaseModel):
class Config:
orm_mode = list # not sensible, but should still be handled gracefully
class KwargsBadConfig2(BaseModel, orm_mode=list):
pass
class InheritingModel(Model):
class Config:
allow_mutation = True
class KwargsInheritingModel(KwargsModel, allow_mutation=True):
pass
class DefaultTestingModel(BaseModel):
# Required
a: int
b: int = ...
c: int = Field(...)
d: Union[int, str]
e = ...
# Not required
f: Optional[int]
g: int = 1
h: int = Field(1)
i: int = Field(None)
j = 1
DefaultTestingModel()
class UndefinedAnnotationModel(BaseModel):
undefined: Undefined # noqa F821
UndefinedAnnotationModel()
Model.model_construct(x=1)
Model.model_construct(_fields_set={'x'}, x=1, y='2')
Model.model_construct(x='1', y='2')
# Strict mode fails
inheriting = InheritingModel(x='1', y='1')
Model(x='1', y='2')
class Blah(BaseModel):
fields_set: Optional[Set[str]] = None
# (comment to keep line numbers unchanged)
T = TypeVar('T')
class Response(GenericModel, Generic[T]):
data: T
error: Optional[str]
response = Response[Model](data=model, error=None)
response = Response[Model](data=1, error=None)
class AliasModel(BaseModel):
x: str = Field(..., alias='y')
z: int
AliasModel(y=1, z=2)
x_alias = 'y'
class DynamicAliasModel(BaseModel):
x: str = Field(..., alias=x_alias)
z: int
DynamicAliasModel(y='y', z='1')
class DynamicAliasModel2(BaseModel):
x: str = Field(..., alias=x_alias)
z: int
class Config:
allow_population_by_field_name = True
DynamicAliasModel2(y='y', z=1)
DynamicAliasModel2(x='y', z=1)
class KwargsDynamicAliasModel(BaseModel, allow_population_by_field_name=True):
x: str = Field(..., alias=x_alias)
z: int
KwargsDynamicAliasModel(y='y', z=1)
KwargsDynamicAliasModel(x='y', z=1)
class AliasGeneratorModel(BaseModel):
x: int
class Config:
alias_generator = lambda x: x + '_' # noqa E731
AliasGeneratorModel(x=1)
AliasGeneratorModel(x_=1)
AliasGeneratorModel(z=1)
class AliasGeneratorModel2(BaseModel):
x: int = Field(..., alias='y')
class Config: # type: ignore[pydantic-alias] # noqa F821
alias_generator = lambda x: x + '_' # noqa E731
class UntypedFieldModel(BaseModel):
x: int = 1
y = 2
z = 2 # type: ignore[pydantic-field] # noqa F821
AliasGeneratorModel2(x=1)
AliasGeneratorModel2(y=1, z=1)
class KwargsAliasGeneratorModel(BaseModel, alias_generator=lambda x: x + '_'):
x: int
KwargsAliasGeneratorModel(x=1)
KwargsAliasGeneratorModel(x_=1)
KwargsAliasGeneratorModel(z=1)
class KwargsAliasGeneratorModel2(BaseModel, alias_generator=lambda x: x + '_'):
x: int = Field(..., alias='y')
KwargsAliasGeneratorModel2(x=1)
KwargsAliasGeneratorModel2(y=1, z=1)
class CoverageTester(Missing): # noqa F821
def from_orm(self) -> None:
pass
CoverageTester().from_orm()
@dataclass(config={})
class AddProject:
name: str
slug: Optional[str]
description: Optional[str]
p = AddProject(name='x', slug='y', description='z')
# Same as Model, but with frozen = True
class FrozenModel(BaseModel):
x: int
y: str
class Config:
alias_generator = None
frozen = True
extra = Extra.forbid
frozenmodel = FrozenModel(x=1, y='b')
frozenmodel.y = 'a'
class InheritingModel2(FrozenModel):
class Config:
frozen = False
inheriting2 = InheritingModel2(x=1, y='c')
inheriting2.y = 'd'
def _default_factory() -> str:
return 'x'
test: List[str] = []
class FieldDefaultTestingModel(BaseModel):
# Default
e: int = Field(None)
f: int = None
# Default factory
g: str = Field(default_factory=set)
h: int = Field(default_factory=_default_factory)
i: List[int] = Field(default_factory=list)
l_: str = Field(default_factory=3)
# Default and default factory
m: int = Field(default=1, default_factory=list)
class ModelWithAnnotatedValidator(BaseModel):
name: str
@validator('name')
def noop_validator_with_annotations(self, name: str) -> str:
# This is a mistake: the first argument to a validator is the class itself,
# like a classmethod.
self.instance_method()
return name
def instance_method(self) -> None:
...