Skip to content

Commit

Permalink
Fix miscellaneous issues
Browse files Browse the repository at this point in the history
  • Loading branch information
hunyadi committed Oct 29, 2023
1 parent 60dbc7b commit 822e762
Show file tree
Hide file tree
Showing 14 changed files with 71 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ RUN python3 -m pip install --upgrade pip
COPY dist/*.whl dist/
RUN python3 -m pip install `ls -1 dist/*.whl`
COPY tests/*.py tests/
RUN cd tests && python3 -m unittest
RUN python3 -m unittest discover
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
recursive-include tests *.py
9 changes: 9 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[mypy]
check_untyped_defs = True
disallow_untyped_calls = True
disallow_untyped_decorators = True
disallow_untyped_defs = True
ignore_missing_imports = True
show_column_numbers = True
warn_redundant_casts = True
warn_unused_ignores = True
18 changes: 8 additions & 10 deletions strong_typing/auxiliary.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def typeannotation(
...


@dataclass_transform()
@dataclass_transform(eq_default=True, order_default=False)
def typeannotation(
cls: Optional[Type[T]] = None, *, eq: bool = True, order: bool = False
) -> Union[Type[T], Callable[[Type[T]], Type[T]]]:
Expand Down Expand Up @@ -88,31 +88,27 @@ def wrap(cls: Type[T]) -> Type[T]:


@typeannotation
@dataclass(frozen=True)
class Alias:
"Alternative name of a property, typically used in JSON serialization."

name: str


@typeannotation
@dataclass(frozen=True)
class Signed:
"Signedness of an integer type."

is_signed: bool


@typeannotation
@dataclass(frozen=True)
class Storage:
"Number of bytes the binary representation of an integer type takes, e.g. 4 bytes for an int32."

bytes: int


@typeannotation
@dataclass(frozen=True)
class IntegerRange:
"Minimum and maximum value of an integer. The range is inclusive."

Expand All @@ -121,7 +117,6 @@ class IntegerRange:


@typeannotation
@dataclass(frozen=True)
class Precision:
"Precision of a floating-point value."

Expand All @@ -134,7 +129,6 @@ def integer_digits(self) -> int:


@typeannotation
@dataclass(frozen=True)
class TimePrecision:
"""
Precision of a timestamp or time interval.
Expand All @@ -146,23 +140,27 @@ class TimePrecision:


@typeannotation
@dataclass(frozen=True)
class Length:
"Exact length of a string."

value: int


@typeannotation
class MinLength:
"Minimum length of a string."

value: int


@typeannotation
@dataclass(frozen=True)
class MaxLength:
"Maximum length of a string."

value: int


@typeannotation
@dataclass(frozen=True)
class SpecialConversion:
"Indicates that the annotated type is subject to custom conversion rules."

Expand Down
31 changes: 18 additions & 13 deletions strong_typing/inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -621,11 +621,16 @@ def run(
:returns: Types referenced by the given type or special form.
"""

if typ in self.graph:
if typ is type(None) or typ is Any:
return

if typ is type(None):
return
if isinstance(typ, type):
self.graph[cls].add(typ)

if typ in self.graph:
return

self.graph[typ] = set()

metadata = getattr(typ, "__metadata__", None)
if metadata is not None:
Expand All @@ -641,23 +646,24 @@ def run(
evaluated_type = evaluate_type(typ, module)
return self.run(evaluated_type, cls, module)

# type is a regular type
# type is a special form
origin = typing.get_origin(typ)
if origin in [list, dict, set, tuple, Union]:
if origin in [list, dict, frozenset, set, tuple, Union]:
for arg in typing.get_args(typ):
self.run(arg, cls, module)
return
elif origin is Literal:
return

# type is a regular type
elif is_dataclass_type(typ) or is_type_enum(typ) or isinstance(typ, type):
self.graph[cls].add(typ)
self.graph[typ] = set()
context = sys.modules[typ.__module__]
if is_dataclass_type(typ):
for field in dataclass_fields(typ):
self.run(field.type, typ, sys.modules[typ.__module__])
self.run(field.type, typ, context)
else:
for field_name, field_type in get_resolved_hints(typ).items():
self.run(field_type, typ, sys.modules[typ.__module__])
self.run(field_type, typ, context)
return

raise TypeError(f"expected: type-like; got: {typ}")
Expand Down Expand Up @@ -933,7 +939,7 @@ def check(self, typ: TypeLike, obj: Any) -> bool:


def check_recursive(
obj: T,
obj: object,
/,
*,
pred: Optional[Callable[[Type[T], T], bool]] = None,
Expand All @@ -943,8 +949,7 @@ def check_recursive(
"""
Checks if a predicate applies to all nested member properties of an object recursively.
:param typ: The type to recurse into.
:param obj: The object to inspect recursively. Must be an instance of the given type.
:param obj: The object to inspect recursively.
:param pred: The predicate to test on member properties. Takes a property type and a property value.
:param type_pred: Constrains the check to properties of an expected type. Properties of other types pass automatically.
:param value_pred: Verifies a condition on member property values (of an expected type).
Expand Down Expand Up @@ -976,4 +981,4 @@ def check_recursive(
elif pred is None:
pred = lambda typ, obj: True

return RecursiveChecker(pred).check(type(obj), obj) # type: ignore
return RecursiveChecker(pred).check(type(obj), obj)
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions tests/test_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import uuid
from typing import Dict, List, Literal, Optional, Set, Union

from test_sample_types import (
from strong_typing.exception import JsonKeyError, JsonTypeError, JsonValueError
from strong_typing.serialization import json_to_object, object_to_json

from .sample_types import (
UID,
BinaryValueWrapper,
ClassA,
Expand All @@ -20,9 +23,6 @@
Suit,
)

from strong_typing.exception import JsonKeyError, JsonTypeError, JsonValueError
from strong_typing.serialization import json_to_object, object_to_json


def test_function() -> None:
pass
Expand Down
4 changes: 2 additions & 2 deletions tests/test_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from dataclasses import dataclass
from typing import Optional, TypeVar

from test_sample_exceptions import CustomException

from strong_typing.docstring import has_default_docstring, has_docstring, parse_type

from .sample_exceptions import CustomException

T = TypeVar("T")


Expand Down
11 changes: 8 additions & 3 deletions tests/test_inspection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
import sys
import unittest
from dataclasses import dataclass
from typing import Dict, List, NamedTuple, Optional, Set, Tuple, Union

from test_sample_types import CompositeDataclass, NestedDataclass, SimpleDataclass
from typing import Any, Dict, List, NamedTuple, Optional, Set, Tuple, Union

from strong_typing.auxiliary import Annotated, typeannotation
from strong_typing.inspection import (
Expand All @@ -26,6 +24,8 @@
unwrap_union_types,
)

from .sample_types import CompositeDataclass, NestedDataclass, SimpleDataclass


class Side(enum.Enum):
"An enumeration with string values."
Expand Down Expand Up @@ -70,9 +70,14 @@ class TestInspection(unittest.TestCase):
def test_simple(self) -> None:
module = sys.modules[self.__module__]
self.assertSetEqual(get_referenced_types(type(None), module), set())
self.assertSetEqual(get_referenced_types(Any, module), set())
self.assertSetEqual(get_referenced_types(int, module), set([int]))
self.assertSetEqual(get_referenced_types(Optional[str], module), set([str]))
self.assertSetEqual(get_referenced_types(List[str], module), set([str]))
self.assertSetEqual(get_referenced_types(List[List[str]], module), set([str]))
self.assertSetEqual(
get_referenced_types(List[Optional[str]], module), set([str])
)
self.assertSetEqual(
get_referenced_types(Dict[int, bool], module), set([int, bool])
)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
import uuid
from dataclasses import dataclass

from test_timer import Timer

from strong_typing.serialization import json_to_object, object_to_json

from .timer import Timer


def time_today(time_of_day: datetime.time) -> datetime.datetime:
return datetime.datetime.combine(
Expand Down
24 changes: 12 additions & 12 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,6 @@
import uuid
from typing import Any, Dict, List, Set, Tuple, Union

from test_sample_types import (
UID,
AnnotatedSimpleDataclass,
BinaryTree,
Side,
SimpleDataclass,
SimpleTypedNamedTuple,
SimpleValueWrapper,
Suit,
ValueExample,
)

from strong_typing.auxiliary import Annotated, IntegerRange, Precision, int32, uint64
from strong_typing.core import JsonType
from strong_typing.schema import (
Expand All @@ -27,6 +15,18 @@
get_class_docstrings,
)

from .sample_types import (
UID,
AnnotatedSimpleDataclass,
BinaryTree,
Side,
SimpleDataclass,
SimpleTypedNamedTuple,
SimpleValueWrapper,
Suit,
ValueExample,
)


class TestSchema(unittest.TestCase):
def test_schema(self) -> None:
Expand Down
12 changes: 6 additions & 6 deletions tests/test_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
import uuid
from typing import Dict

from test_sample_types import (
from strong_typing.core import JsonType
from strong_typing.exception import JsonValueError
from strong_typing.schema import validate_object
from strong_typing.serialization import object_to_json

from .sample_types import (
UID,
AnnotatedSimpleDataclass,
BinaryValueWrapper,
Expand All @@ -23,11 +28,6 @@
Suit,
)

from strong_typing.core import JsonType
from strong_typing.exception import JsonValueError
from strong_typing.schema import validate_object
from strong_typing.serialization import object_to_json


def test_function() -> None:
pass
Expand Down
File renamed without changes.

0 comments on commit 822e762

Please sign in to comment.