Skip to content

Commit

Permalink
Fix Python 3.8 and add tox config for cross version testing
Browse files Browse the repository at this point in the history
  • Loading branch information
mvanderlee committed Mar 10, 2024
1 parent de4ec94 commit 736833b
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 13 deletions.
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ Every commit is checked with pre-commit hooks for :
- type safety with [mypy](http://mypy-lang.org/)
- test conformance by running [tests](./tests) with [pytest](https://docs.pytest.org/en/latest/)
- You can run `pytest` from the command line.

- 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.
34 changes: 23 additions & 11 deletions marshmallow_dataclass/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,12 @@ class User:
TypeVar,
Union,
cast,
get_args,
get_origin,
get_type_hints,
overload,
)

import marshmallow
import typing_extensions
import typing_inspect

from marshmallow_dataclass.lazy_class_attribute import lazy_class_attribute
Expand Down Expand Up @@ -392,7 +391,13 @@ def _internal_class_schema(
base_schema: Optional[Type[marshmallow.Schema]] = None,
clazz_frame: Optional[types.FrameType] = None,
) -> Type[marshmallow.Schema]:
_RECURSION_GUARD.seen_classes[clazz] = clazz.__name__
if typing_extensions.get_origin(clazz) is Annotated and sys.version_info < (3, 10):
# https://github.com/python/cpython/blob/3.10/Lib/typing.py#L977
class_name = clazz._name or clazz.__origin__.__name__ # type: ignore[attr-defined]
else:
class_name = clazz.__name__

_RECURSION_GUARD.seen_classes[clazz] = class_name
try:
# noinspection PyDataclass
fields: Tuple[dataclasses.Field, ...] = dataclasses.fields(clazz)
Expand Down Expand Up @@ -427,11 +432,18 @@ def _internal_class_schema(
include_non_init = getattr(getattr(clazz, "Meta", None), "include_non_init", False)

# Update the schema members to contain marshmallow fields instead of dataclass fields
type_hints = get_type_hints(
clazz,
localns=clazz_frame.f_locals if clazz_frame else None,
include_extras=True,
)

if sys.version_info >= (3, 9):
type_hints = get_type_hints(
clazz,
localns=clazz_frame.f_locals if clazz_frame else None,
include_extras=True,
)
else:
type_hints = get_type_hints(
clazz,
localns=clazz_frame.f_locals if clazz_frame else None,
)
attributes.update(
(
field.name,
Expand Down Expand Up @@ -526,8 +538,8 @@ def _field_for_generic_type(
"""
If the type is a generic interface, resolve the arguments and construct the appropriate Field.
"""
origin = get_origin(typ)
arguments = get_args(typ)
origin = typing_extensions.get_origin(typ)
arguments = typing_extensions.get_args(typ)
if origin:
# Override base_schema.TYPE_MAPPING to change the class used for generic types below
type_mapping = base_schema.TYPE_MAPPING if base_schema else {}
Expand Down Expand Up @@ -764,7 +776,7 @@ def field_for_schema(
)

# enumerations
if issubclass(typ, Enum):
if inspect.isclass(typ) and issubclass(typ, Enum):
return marshmallow.fields.Enum(typ, **metadata)

# Nested marshmallow dataclass
Expand Down
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
# 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'",
"tox>=4",
"virtualenv-pyenv",
],
}
EXTRAS_REQUIRE["dev"] = (
Expand Down
8 changes: 7 additions & 1 deletion tests/test_annotated.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import sys
import unittest
from typing import Annotated, Optional
from typing import Optional

import marshmallow
import marshmallow.fields

from marshmallow_dataclass import dataclass

if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated


class TestAnnotatedField(unittest.TestCase):
def test_annotated_field(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_mypy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
reveal_type(user.email) # N: Revealed type is "builtins.str"
User(id=42, email="[email protected]") # E: Argument "id" to "User" has incompatible type "int"; expected "str" [arg-type]
User(id="a"*32, email=["not", "a", "string"]) # E: Argument "email" to "User" has incompatible type "list[str]"; expected "str" [arg-type]
User(id="a"*32, email=["not", "a", "string"]) # E: Argument "email" to "User" has incompatible type "List[str]"; expected "str" [arg-type]
- case: marshmallow_dataclass_keyword_arguments
mypy_config: |
follow_imports = silent
Expand Down
11 changes: 11 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[tox]
requires =
tox>=4
env_list = py{38,39,310,311,312}

[testenv]
deps = pytest
commands = pytest
extras = dev
set_env =
VIRTUALENV_DISCOVERY = pyenv

0 comments on commit 736833b

Please sign in to comment.