Skip to content

Commit

Permalink
Fix new ruff rules
Browse files Browse the repository at this point in the history
- Remove obsolete ignores
- Replace "from typing import Sequence, Mapping, Iterable" by
  "from collections.abc ...".
  With this fix type-checking in ToMapping and ToTypedMapping converters
  and include type of js-object in message of FromJsonConversionError to
  improve clarity.
  • Loading branch information
volkerstampa committed Nov 30, 2024
1 parent 458e388 commit 0b21fb4
Show file tree
Hide file tree
Showing 8 changed files with 27 additions and 18 deletions.
3 changes: 2 additions & 1 deletion jsonype/base_types.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from typing import Literal, Mapping, Sequence, Union
from collections.abc import Mapping, Sequence
from typing import Literal, Union

JsonNull = Literal[None]
JsonSimple = Union[int, float, str, bool]
Expand Down
15 changes: 10 additions & 5 deletions jsonype/basic_from_json_converters.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from abc import ABC, abstractmethod
from collections.abc import Iterable, Mapping, Sequence
from inspect import isclass
from types import NoneType
from typing import (Any, Callable, Generic, Iterable, Literal, Mapping, Optional, Protocol,
Sequence, TypeVar, Union, cast, get_args, runtime_checkable)
from typing import (Any, Callable, Generic, Literal, Optional, Protocol, TypeVar, Union, cast,
get_args, runtime_checkable)

from jsonype.base_types import Json, JsonSimple

Expand All @@ -12,7 +13,8 @@

class FromJsonConversionError(ValueError):
def __init__(self, js: Json, target_type: type, reason: str | None = None) -> None:
super().__init__(f"Cannot convert {js} to {target_type}{f': {reason}' if reason else ''}",
super().__init__(f"Cannot convert {js} (type: {type(js)}) "
f"to {target_type}{f': {reason}' if reason else ''}",
js, target_type)


Expand Down Expand Up @@ -273,7 +275,9 @@ class ToMapping(FromJsonConverter[Mapping[str, TargetType_co], TargetType_co]):
"""

def can_convert(self, target_type: type, origin_of_generic: Optional[type]) -> bool:
return isclass(origin_of_generic) and issubclass(cast(type, origin_of_generic), Mapping)
return ((isclass(origin_of_generic) and issubclass(cast(type, origin_of_generic), Mapping))
or (isclass(target_type) and issubclass(target_type, Mapping)
and not isinstance(target_type, HasRequiredKeys)))

def convert(
self,
Expand Down Expand Up @@ -325,7 +329,8 @@ def __init__(self, strict: bool = False) -> None:
self.strict = strict

def can_convert(self, target_type: type, origin_of_generic: Optional[type]) -> bool:
return isclass(target_type) and issubclass(target_type, Mapping)
return (isclass(target_type) and issubclass(target_type, Mapping)
and isinstance(target_type, HasRequiredKeys))

def convert(
self,
Expand Down
3 changes: 2 additions & 1 deletion jsonype/basic_to_json_converters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from abc import ABC, abstractmethod
from typing import Any, Callable, Generic, Mapping, Sequence, TypeVar, get_args
from collections.abc import Mapping, Sequence
from typing import Any, Callable, Generic, TypeVar, get_args

from jsonype.base_types import Json, JsonNull, JsonSimple

Expand Down
3 changes: 2 additions & 1 deletion jsonype/dataclass_converters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from collections.abc import Mapping
from dataclasses import MISSING, Field, fields, is_dataclass
from typing import Any, Callable, ClassVar, Mapping, Optional, Protocol, TypeVar
from typing import Any, Callable, ClassVar, Optional, Protocol, TypeVar

from jsonype.base_types import Json
from jsonype.basic_from_json_converters import (ContainedTargetType_co, FromJsonConversionError,
Expand Down
5 changes: 3 additions & 2 deletions jsonype/named_tuple_converters.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from collections.abc import Iterable, Mapping
from inspect import isclass
# pyflakes wants NamedTuple to be imported as its used as bounds-parameter below
# noinspection PyUnresolvedReferences
from typing import (Any, Callable, Iterable, Mapping, NamedTuple, Optional, Protocol, # noqa: W0611
Self, TypeVar, cast, runtime_checkable)
from typing import (Any, Callable, NamedTuple, Optional, Protocol, Self, TypeVar, # noqa: W0611
cast, runtime_checkable)

from jsonype import Json, ToJsonConverter
from jsonype.basic_from_json_converters import (FromJsonConversionError, FromJsonConverter,
Expand Down
4 changes: 2 additions & 2 deletions jsonype/typed_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class TypedJson:
... person = TypedJson(strict=True).from_json(js, Person)
... except ValueError as e:
... print(e) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
("Cannot convert {'street': '...', ..., 'zip': 'ignored'}
("Cannot convert {'street': '...', ..., 'zip': 'ignored'} (type: <class 'dict'>)
to <class 'Address'>: unexpected keys: {'zip'}", ...
>>>
>>> # JSON-types must match expected types:
Expand All @@ -81,7 +81,7 @@ class TypedJson:
... person = typed_json.from_json(js, Person)
... except ValueError as e:
... print(e) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE
("Cannot convert 5 to <class 'int'>", ...
("Cannot convert 5 (type: <class 'str'>) to <class 'int'>", ...
"""

def __init__(self, strict: bool = False) -> None:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ select = ["ALL"]
ignore = ["D100", "D101", "D102", "D103", "D104", "D107", "D203", "D213",
"D406", "D407", "D410", "D411", "D413",
"ARG002",
"ANN101", "ANN401", "ANN102",
"ANN401",
"S101", "S311",
"FA102",
"COM812",
Expand Down
10 changes: 5 additions & 5 deletions tests/jsonype/test_typed_json.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from collections.abc import Iterable, Mapping, Sequence
from dataclasses import dataclass, make_dataclass
from inspect import get_annotations
from json import dumps, loads
from random import choice, choices, gauss, randint, randrange, uniform
from string import ascii_letters, digits, printable
from sys import float_info
from types import NoneType
from typing import (Any, Callable, Iterable, List, Mapping, NamedTuple, Optional, Sequence, Tuple,
TypeAlias, TypedDict, TypeVar, Union, cast)
from typing import Any, Callable, NamedTuple, Optional, TypeAlias, TypedDict, TypeVar, Union, cast

from pytest import mark, raises

Expand Down Expand Up @@ -233,14 +233,14 @@ def _random_untyped_list(size: int, factories: Sequence[ObjectFactory[_T]]) \
unambiguous_factories = tuple(
frozenset(_unambiguous_types_factories()).intersection(frozenset(factories)))
seq, _types = _random_values(size, unambiguous_factories)
return list(seq), List
return list(seq), list


def _random_tuple(size: int, factories: Sequence[ObjectFactory[_T]]) \
-> tuple[tuple[_T, ...], type[tuple[_T, ...]]]:
seq, types = _random_values(size, factories)
# tuple[*var] it interpreted as object, so it needs a cast
return tuple(seq), cast(type[tuple[_T, ...]], Tuple[*types])
return tuple(seq), cast(type[tuple[_T, ...]], tuple[*types]) # type: ignore[valid-type]


def _random_tuple_with_ellipsis(size: int, factories: Sequence[ObjectFactory[_T]]) \
Expand All @@ -250,7 +250,7 @@ def _random_tuple_with_ellipsis(size: int, factories: Sequence[ObjectFactory[_T]
seq, types = _random_values(size, unambiguous_factories)
# tuple[*var] it interpreted as object, so it needs a cast
return tuple(seq), cast(type[tuple[_T, ...]],
Tuple[*insert_random_ellipsis(types)])
tuple[*insert_random_ellipsis(types)]) # type: ignore[misc]


def _random_map(size: int, factories: Sequence[ObjectFactory[_T]]) \
Expand Down

0 comments on commit 0b21fb4

Please sign in to comment.