Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix dataclass containing Any #4356

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions changes/4356-DetachHead.md
@@ -0,0 +1 @@
remove `Any` types from the `dataclass` decorator so it can be used with the `disallow_any_expr` mypy option
12 changes: 6 additions & 6 deletions pydantic/config.py
Expand Up @@ -4,7 +4,7 @@

from typing_extensions import Literal, Protocol

from .typing import AnyCallable
from .typing import AnyArgTCallable, AnyCallable
from .utils import GetterDict
from .version import compiled

Expand Down Expand Up @@ -62,10 +62,10 @@ class ConfigDict(TypedDict, total=False):
getter_dict: Type[GetterDict]
alias_generator: Optional[Callable[[str], str]]
keep_untouched: Tuple[type, ...]
schema_extra: Union[Dict[str, Any], 'SchemaExtraCallable']
json_loads: Callable[[str], Any]
json_dumps: Callable[..., str]
json_encoders: Dict[Type[Any], AnyCallable]
schema_extra: Union[Dict[str, object], 'SchemaExtraCallable']
DetachHead marked this conversation as resolved.
Show resolved Hide resolved
json_loads: Callable[[str], object]
json_dumps: AnyArgTCallable[str]
json_encoders: Dict[Type[object], AnyCallable]
underscore_attrs_are_private: bool

# whether or not inherited models as fields should be reconstructed as base model
Expand Down Expand Up @@ -146,7 +146,7 @@ def prepare_field(cls, field: 'ModelField') -> None:
pass


def get_config(config: Union[ConfigDict, Type[BaseConfig], None]) -> Type[BaseConfig]:
def get_config(config: Union[ConfigDict, Type[object], None]) -> Type[BaseConfig]:
if config is None:
return BaseConfig

Expand Down
26 changes: 14 additions & 12 deletions pydantic/dataclasses.py
Expand Up @@ -68,7 +68,7 @@ class Dataclass:
__pydantic_validate_values__: ClassVar[Callable[['Dataclass'], None]]
__pydantic_has_field_info_default__: ClassVar[bool] # whether a `pydantic.Field` is used as default value

def __init__(self, *args: Any, **kwargs: Any) -> None:
def __init__(self, *args: object, **kwargs: object) -> None:
pass

@classmethod
Expand All @@ -88,6 +88,8 @@ def __validate__(cls: Type['DataclassT'], v: Any) -> 'DataclassT':
'make_dataclass_validator',
]

_T = TypeVar('_T')
DetachHead marked this conversation as resolved.
Show resolved Hide resolved

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

@dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo))
Expand All @@ -100,24 +102,24 @@ def dataclass(
order: bool = False,
unsafe_hash: bool = False,
frozen: bool = False,
config: Union[ConfigDict, Type[Any], None] = None,
config: Union[ConfigDict, Type[object], None] = None,
validate_on_init: Optional[bool] = None,
kw_only: bool = ...,
) -> Callable[[Type[Any]], 'DataclassClassOrWrapper']:
) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']:
...

@dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo))
@overload
def dataclass(
_cls: Type[Any],
_cls: Type[_T],
*,
init: bool = True,
repr: bool = True,
eq: bool = True,
order: bool = False,
unsafe_hash: bool = False,
frozen: bool = False,
config: Union[ConfigDict, Type[Any], None] = None,
config: Union[ConfigDict, Type[object], None] = None,
validate_on_init: Optional[bool] = None,
kw_only: bool = ...,
) -> 'DataclassClassOrWrapper':
Expand All @@ -135,42 +137,42 @@ def dataclass(
order: bool = False,
unsafe_hash: bool = False,
frozen: bool = False,
config: Union[ConfigDict, Type[Any], None] = None,
config: Union[ConfigDict, Type[object], None] = None,
validate_on_init: Optional[bool] = None,
) -> Callable[[Type[Any]], 'DataclassClassOrWrapper']:
) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']:
...

@dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo))
@overload
def dataclass(
_cls: Type[Any],
_cls: Type[_T],
*,
init: bool = True,
repr: bool = True,
eq: bool = True,
order: bool = False,
unsafe_hash: bool = False,
frozen: bool = False,
config: Union[ConfigDict, Type[Any], None] = None,
config: Union[ConfigDict, Type[object], None] = None,
validate_on_init: Optional[bool] = None,
) -> 'DataclassClassOrWrapper':
...


@dataclass_transform(kw_only_default=True, field_descriptors=(Field, FieldInfo))
def dataclass(
_cls: Optional[Type[Any]] = None,
_cls: Optional[Type[_T]] = None,
*,
init: bool = True,
repr: bool = True,
eq: bool = True,
order: bool = False,
unsafe_hash: bool = False,
frozen: bool = False,
config: Union[ConfigDict, Type[Any], None] = None,
config: Union[ConfigDict, Type[object], None] = None,
validate_on_init: Optional[bool] = None,
kw_only: bool = False,
) -> Union[Callable[[Type[Any]], 'DataclassClassOrWrapper'], 'DataclassClassOrWrapper']:
) -> Union[Callable[[Type[_T]], 'DataclassClassOrWrapper'], 'DataclassClassOrWrapper']:
"""
Like the python standard lib dataclasses but with type validation.
The result is either a pydantic dataclass that will validate input data
Expand Down
6 changes: 6 additions & 0 deletions pydantic/typing.py
Expand Up @@ -19,6 +19,7 @@
Set,
Tuple,
Type,
TypeVar,
Union,
_eval_type,
cast,
Expand Down Expand Up @@ -76,9 +77,14 @@ def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> A
return get_type_hints(obj, globalns, localns, include_extras=True)


_T = TypeVar('_T')

AnyCallable = TypingCallable[..., Any]
NoArgAnyCallable = TypingCallable[[], Any]

# workaround for https://github.com/python/mypy/issues/9496
AnyArgTCallable = TypingCallable[..., _T]


# Annotated[...] is implemented by returning an instance of one of these classes, depending on
# python/typing_extensions version.
Expand Down
23 changes: 23 additions & 0 deletions tests/mypy/configs/mypy-plugin-strict-no-any.ini
@@ -0,0 +1,23 @@
[mypy]
DetachHead marked this conversation as resolved.
Show resolved Hide resolved
plugins = pydantic.mypy

follow_imports = silent
strict_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
disallow_any_generics = True
check_untyped_defs = True
no_implicit_reexport = True
disallow_untyped_defs = True
disallow_any_decorated = True
disallow_any_expr = True
disallow_any_explicit = True
disallow_any_unimported = True
disallow_subclassing_any = True
warn_return_any = True

[pydantic-mypy]
init_forbid_extra = True
init_typed = True
warn_required_dynamic_aliases = True
warn_untyped_fields = True
11 changes: 11 additions & 0 deletions tests/mypy/modules/no_any.py
@@ -0,0 +1,11 @@
from pydantic.dataclasses import dataclass


@dataclass
class Foo:
foo: int


@dataclass(config={})
class Bar:
bar: str
1 change: 1 addition & 0 deletions tests/mypy/test_mypy.py
Expand Up @@ -37,6 +37,7 @@
('mypy-default.ini', 'fail3.py', 'fail3.txt'),
('mypy-default.ini', 'fail4.py', 'fail4.txt'),
('mypy-default.ini', 'plugin_success.py', 'plugin_success.txt'),
('mypy-plugin-strict-no-any.ini', 'no_any.py', None),
('pyproject-default.toml', 'success.py', None),
('pyproject-default.toml', 'fail1.py', 'fail1.txt'),
('pyproject-default.toml', 'fail2.py', 'fail2.txt'),
Expand Down