Skip to content

Commit 736833b

Browse files
committed
Fix Python 3.8 and add tox config for cross version testing
1 parent de4ec94 commit 736833b

File tree

6 files changed

+46
-13
lines changed

6 files changed

+46
-13
lines changed

CONTRIBUTING.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,5 @@ Every commit is checked with pre-commit hooks for :
2121
- type safety with [mypy](http://mypy-lang.org/)
2222
- test conformance by running [tests](./tests) with [pytest](https://docs.pytest.org/en/latest/)
2323
- You can run `pytest` from the command line.
24+
25+
- You can also run `tox` from the command line to test in all supported python versions. Note that this will require you to have all supported python versions installed.

marshmallow_dataclass/__init__.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,12 @@ class User:
5454
TypeVar,
5555
Union,
5656
cast,
57-
get_args,
58-
get_origin,
5957
get_type_hints,
6058
overload,
6159
)
6260

6361
import marshmallow
62+
import typing_extensions
6463
import typing_inspect
6564

6665
from marshmallow_dataclass.lazy_class_attribute import lazy_class_attribute
@@ -392,7 +391,13 @@ def _internal_class_schema(
392391
base_schema: Optional[Type[marshmallow.Schema]] = None,
393392
clazz_frame: Optional[types.FrameType] = None,
394393
) -> Type[marshmallow.Schema]:
395-
_RECURSION_GUARD.seen_classes[clazz] = clazz.__name__
394+
if typing_extensions.get_origin(clazz) is Annotated and sys.version_info < (3, 10):
395+
# https://github.com/python/cpython/blob/3.10/Lib/typing.py#L977
396+
class_name = clazz._name or clazz.__origin__.__name__ # type: ignore[attr-defined]
397+
else:
398+
class_name = clazz.__name__
399+
400+
_RECURSION_GUARD.seen_classes[clazz] = class_name
396401
try:
397402
# noinspection PyDataclass
398403
fields: Tuple[dataclasses.Field, ...] = dataclasses.fields(clazz)
@@ -427,11 +432,18 @@ def _internal_class_schema(
427432
include_non_init = getattr(getattr(clazz, "Meta", None), "include_non_init", False)
428433

429434
# Update the schema members to contain marshmallow fields instead of dataclass fields
430-
type_hints = get_type_hints(
431-
clazz,
432-
localns=clazz_frame.f_locals if clazz_frame else None,
433-
include_extras=True,
434-
)
435+
436+
if sys.version_info >= (3, 9):
437+
type_hints = get_type_hints(
438+
clazz,
439+
localns=clazz_frame.f_locals if clazz_frame else None,
440+
include_extras=True,
441+
)
442+
else:
443+
type_hints = get_type_hints(
444+
clazz,
445+
localns=clazz_frame.f_locals if clazz_frame else None,
446+
)
435447
attributes.update(
436448
(
437449
field.name,
@@ -526,8 +538,8 @@ def _field_for_generic_type(
526538
"""
527539
If the type is a generic interface, resolve the arguments and construct the appropriate Field.
528540
"""
529-
origin = get_origin(typ)
530-
arguments = get_args(typ)
541+
origin = typing_extensions.get_origin(typ)
542+
arguments = typing_extensions.get_args(typ)
531543
if origin:
532544
# Override base_schema.TYPE_MAPPING to change the class used for generic types below
533545
type_mapping = base_schema.TYPE_MAPPING if base_schema else {}
@@ -764,7 +776,7 @@ def field_for_schema(
764776
)
765777

766778
# enumerations
767-
if issubclass(typ, Enum):
779+
if inspect.isclass(typ) and issubclass(typ, Enum):
768780
return marshmallow.fields.Enum(typ, **metadata)
769781

770782
# Nested marshmallow dataclass

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
# re: pypy: typed-ast (a dependency of mypy) fails to install on pypy
2525
# https://github.com/python/typed_ast/issues/111
2626
"pytest-mypy-plugins>=1.2.0; implementation_name != 'pypy'",
27+
"tox>=4",
28+
"virtualenv-pyenv",
2729
],
2830
}
2931
EXTRAS_REQUIRE["dev"] = (

tests/test_annotated.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1+
import sys
12
import unittest
2-
from typing import Annotated, Optional
3+
from typing import Optional
34

45
import marshmallow
56
import marshmallow.fields
67

78
from marshmallow_dataclass import dataclass
89

10+
if sys.version_info >= (3, 9):
11+
from typing import Annotated
12+
else:
13+
from typing_extensions import Annotated
14+
915

1016
class TestAnnotatedField(unittest.TestCase):
1117
def test_annotated_field(self):

tests/test_mypy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
reveal_type(user.email) # N: Revealed type is "builtins.str"
2828
2929
User(id=42, email="user@email.com") # E: Argument "id" to "User" has incompatible type "int"; expected "str" [arg-type]
30-
User(id="a"*32, email=["not", "a", "string"]) # E: Argument "email" to "User" has incompatible type "list[str]"; expected "str" [arg-type]
30+
User(id="a"*32, email=["not", "a", "string"]) # E: Argument "email" to "User" has incompatible type "List[str]"; expected "str" [arg-type]
3131
- case: marshmallow_dataclass_keyword_arguments
3232
mypy_config: |
3333
follow_imports = silent

tox.ini

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[tox]
2+
requires =
3+
tox>=4
4+
env_list = py{38,39,310,311,312}
5+
6+
[testenv]
7+
deps = pytest
8+
commands = pytest
9+
extras = dev
10+
set_env =
11+
VIRTUALENV_DISCOVERY = pyenv

0 commit comments

Comments
 (0)