diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dca4a3b..0aa3b11 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,11 @@ repos: - repo: https://github.com/asottile/pyupgrade - rev: v3.2.3 + rev: v3.3.1 hooks: - id: pyupgrade args: ["--py36-plus"] - repo: https://github.com/python/black - rev: 22.10.0 + rev: 23.1.0 hooks: - id: black language_version: python3 @@ -16,13 +16,13 @@ repos: - id: flake8 additional_dependencies: ['flake8-bugbear==22.10.27'] - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.991 + rev: v1.1.1 hooks: - id: mypy additional_dependencies: [marshmallow-enum,typeguard,marshmallow] args: [--show-error-codes] - repo: https://github.com/asottile/blacken-docs - rev: v1.12.1 + rev: 1.13.0 hooks: - id: blacken-docs additional_dependencies: [black==22.8.0] diff --git a/CHANGELOG.md b/CHANGELOG.md index 86e8958..5d8ecfe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,12 @@ ## v8.5.12 (unreleased) - - Fix to work with typeguard 3.x + - Fixes to work with typeguard 3.x. + - Add the @dataclass_transform decorator ([PEP 681]) to + `marshmallow_dataclass.dataclass`. This fixes our mypy plugin for + mypy>=1.1.1. + +[PEP 681]: https://peps.python.org/pep-0681/ ## v8.5.11 (2023-01-08) diff --git a/marshmallow_dataclass/__init__.py b/marshmallow_dataclass/__init__.py index 106cde2..b28cfc5 100644 --- a/marshmallow_dataclass/__init__.py +++ b/marshmallow_dataclass/__init__.py @@ -37,6 +37,7 @@ class User: import collections.abc import dataclasses import inspect +import sys import threading import types import warnings @@ -68,6 +69,12 @@ class User: from marshmallow_dataclass.lazy_class_attribute import lazy_class_attribute +if sys.version_info >= (3, 11): + from typing import dataclass_transform +else: + from typing_extensions import dataclass_transform + + __all__ = ["dataclass", "add_schema", "class_schema", "field_for_schema", "NewType"] NoneType = type(None) @@ -115,6 +122,7 @@ def dataclass( # _cls should never be specified by keyword, so start it with an # underscore. The presence of _cls is used to detect if this # decorator is being called with parameters or not. +@dataclass_transform() def dataclass( _cls: Optional[Type[_U]] = None, *, diff --git a/marshmallow_dataclass/union_field.py b/marshmallow_dataclass/union_field.py index 6e87e29..ffe998d 100644 --- a/marshmallow_dataclass/union_field.py +++ b/marshmallow_dataclass/union_field.py @@ -1,4 +1,5 @@ import copy +import inspect from typing import List, Tuple, Any, Optional import typeguard @@ -8,7 +9,19 @@ from typeguard import TypeCheckError # type: ignore[attr-defined] except ImportError: # typeguard < 3 - TypeCheckError = TypeError + TypeCheckError = TypeError # type: ignore[misc, assignment] + +if "argname" not in inspect.signature(typeguard.check_type).parameters: + + def _check_type(value, expected_type, argname: str): + return typeguard.check_type(value=value, expected_type=expected_type) + +else: + # typeguard < 3.0.0rc2 + def _check_type(value, expected_type, argname: str): + return typeguard.check_type( # type: ignore[call-overload] + value=value, expected_type=expected_type, argname=argname + ) class Union(fields.Field): @@ -46,9 +59,7 @@ def _serialize(self, value: Any, attr: Optional[str], obj, **kwargs) -> Any: return value for typ, field in self.union_fields: try: - typeguard.check_type( - value=value, expected_type=typ, argname=attr or "anonymous" - ) + _check_type(value=value, expected_type=typ, argname=attr or "anonymous") return field._serialize(value, attr, obj, **kwargs) except TypeCheckError as e: errors.append(e) @@ -61,7 +72,7 @@ def _deserialize(self, value: Any, attr: Optional[str], data, **kwargs) -> Any: for typ, field in self.union_fields: try: result = field.deserialize(value, **kwargs) - typeguard.check_type( + _check_type( value=result, expected_type=typ, argname=attr or "anonymous" ) return result diff --git a/setup.py b/setup.py index 325b350..4d3c282 100644 --- a/setup.py +++ b/setup.py @@ -22,7 +22,7 @@ "marshmallow-enum; python_version < '3.7'", "marshmallow>=3.18.0,<4.0; python_version >= '3.7'", ], - "union": ["typeguard"], + "union": ["typeguard>=2.0.0,<4.0.0"], "lint": ["pre-commit~=2.17"], ':python_version == "3.6"': ["dataclasses", "types-dataclasses<0.6.4"], "docs": ["sphinx"], @@ -31,10 +31,6 @@ # re: pypy: typed-ast (a dependency of mypy) fails to install on pypy # https://github.com/python/typed_ast/issues/111 "pytest-mypy-plugins>=1.2.0; implementation_name != 'pypy'", - # `Literal` was introduced in: - # - Python 3.8 (https://www.python.org/dev/peps/pep-0586) - # - typing-extensions 3.7.2 (https://github.com/python/typing/pull/591) - "typing-extensions>=3.7.2; python_version < '3.8'", ], } EXTRAS_REQUIRE["dev"] = ( @@ -64,6 +60,10 @@ install_requires=[ "marshmallow>=3.13.0,<4.0", "typing-inspect>=0.8.0", + # `dataclass_transform` was introduced in: + # - Python 3.11 (https://www.python.org/dev/peps/pep-0681) + # - typing-extensions 4.1.0 + "typing-extensions>=4.1.0; python_version < '3.11'", ], extras_require=EXTRAS_REQUIRE, package_data={"marshmallow_dataclass": ["py.typed"]},