diff --git a/jsonmodels/builders.py b/jsonmodels/builders.py index c63ec16..120d832 100644 --- a/jsonmodels/builders.py +++ b/jsonmodels/builders.py @@ -4,7 +4,7 @@ from collections import defaultdict from . import errors -from .fields import NotSet +from .fields import Value, NotSet class Builder: @@ -41,7 +41,7 @@ def count_type(self, type): return self.types_count[type] @staticmethod - def maybe_build(value): + def maybe_build(value: Value) -> dict[str, Value] | Value: return value.build() if isinstance(value, Builder) else value def add_definition(self, builder): diff --git a/jsonmodels/fields.py b/jsonmodels/fields.py index 0cbe2de..6aa4e31 100644 --- a/jsonmodels/fields.py +++ b/jsonmodels/fields.py @@ -1,13 +1,19 @@ import datetime import re from weakref import WeakKeyDictionary -from typing import Any, Optional +from typing import Any, Callable, Optional, TypeVar, cast from dateutil.parser import parse from .collections import ModelCollection from .errors import ValidationError +Validator = Callable | object +Validators = list[Validator] # TODO create typing.py file for those +Model = TypeVar("Model") +T = TypeVar("T") +Value = Any + # unique marker for "no default value specified". None is not good enough since # it is a completely valid default value. NotSet = object() @@ -17,18 +23,18 @@ class BaseField: """Base class for all fields.""" - types: tuple[Optional[Any]] = tuple() + types: tuple = tuple() def __init__( self, - required=False, - nullable=False, - help_text=None, - validators=None, - default=NotSet, - name=None, - ): - self.memory = WeakKeyDictionary() + required: bool = False, + nullable: bool = False, + help_text: Optional[str] = None, + validators: Optional[Callable] = None, + default: Value = NotSet, + name: Optional[str] = None, # TODO this should be required? + ) -> None: + self.memory: WeakKeyDictionary = WeakKeyDictionary() self.required = required self.help_text = help_text self.nullable = nullable @@ -43,18 +49,18 @@ def __init__( def has_default(self) -> bool: return self._default is not NotSet - def _assign_validators(self, validators): + def _assign_validators(self, validators: Validators) -> None: if validators and not isinstance(validators, list): validators = [validators] self.validators = validators or [] - def __set__(self, instance, value) -> None: + def __set__(self, instance: Model, value: Value) -> None: self._finish_initialization(type(instance)) value = self.parse_value(value) self.validate(value) self.memory[instance._cache_key] = value - def __get__(self, instance, owner=None): + def __get__(self: T, instance: T, owner: Model = None) -> T: # TODO types are good? if instance is None: self._finish_initialization(owner) return self @@ -64,28 +70,28 @@ def __get__(self, instance, owner=None): self._check_value(instance) return self.memory[instance._cache_key] - def _finish_initialization(self, owner): + def _finish_initialization(self, owner: Model) -> None: pass - def _check_value(self, obj): + def _check_value(self, obj: Model) -> None: if obj._cache_key not in self.memory: self.__set__(obj, self.get_default_value()) - def validate_for_object(self, obj) -> None: + def validate_for_object(self, obj: Model) -> None: value = self.__get__(obj) self.validate(value) - def validate(self, value) -> None: + def validate(self, value: Value) -> None: self._check_types() self._validate_against_types(value) self._check_against_required(value) self._validate_with_custom_validators(value) - def _check_against_required(self, value): + def _check_against_required(self, value: Value) -> None: if value is None and self.required: raise ValidationError("Field is required!") - def _validate_against_types(self, value): + def _validate_against_types(self, value: Value) -> None: if value is not None and not isinstance(value, self.types): raise ValidationError( 'Value is wrong, expected type "{types}"'.format( @@ -94,18 +100,18 @@ def _validate_against_types(self, value): value, ) - def _check_types(self): + def _check_types(self) -> None: if self.types is None: raise ValidationError( 'Field "{type}" is not usable, try ' "different field type.".format(type=type(self).__name__) ) - def to_struct(self, value): + def to_struct(self, value: Value) -> Value: """Cast value to Python structure.""" return value - def parse_value(self, value): + def parse_value(self, value: Value) -> Value: """Parse value from primitive to desired format. Each field can parse value to form it wants it to be (like string or @@ -114,7 +120,7 @@ def parse_value(self, value): """ return value - def _validate_with_custom_validators(self, value): + def _validate_with_custom_validators(self, value: Value) -> None: if value is None and self.nullable: return @@ -124,7 +130,7 @@ def _validate_with_custom_validators(self, value): except AttributeError: validator(value) - def get_default_value(self): + def get_default_value(self) -> Value | None: """Get default value for field. Each field can specify its default. @@ -132,13 +138,13 @@ def get_default_value(self): """ return self._default if self.has_default else None - def _validate_name(self): + def _validate_name(self) -> None: if self.name is None: return if not re.match(r"^[A-Za-z_](([\w\-]*)?\w+)?$", self.name): raise ValueError("Wrong name", self.name) - def structue_name(self, default) -> str: + def structue_name(self, default: str) -> str: return self.name if self.name is not None else default @@ -146,7 +152,7 @@ class StringField(BaseField): """String field.""" - types = (str,) + types: tuple[type] = (str,) class IntField(BaseField): @@ -155,7 +161,7 @@ class IntField(BaseField): types = (int,) - def parse_value(self, value) -> int: + def parse_value(self, value: Value) -> int | None: """Cast value to `int`, e.g. from string or long""" parsed = super().parse_value(value) if parsed is None: @@ -176,7 +182,7 @@ class BoolField(BaseField): types = (bool,) - def parse_value(self, value): + def parse_value(self, value: Value) -> bool | None: """Cast value to `bool`.""" parsed = super().parse_value(value) return bool(parsed) if parsed is not None else None @@ -195,7 +201,13 @@ class ListField(BaseField): types = (list,) - def __init__(self, items_types=None, item_validators=(), *args, **kwargs): + def __init__( + self, + items_types: Optional[list[type]] = None, + item_validators: Optional[tuple[Validator]] = (), + *args: Any, + **kwargs: Any, + ) -> None: """Init. `ListField` is **always not required**. If you want to control number @@ -208,13 +220,13 @@ def __init__(self, items_types=None, item_validators=(), *args, **kwargs): super().__init__(*args, **kwargs) self.required = False - def get_default_value(self): + def get_default_value(self) -> Optional[Value]: default = super().get_default_value() if default is None: return ModelCollection(self) return default - def _assign_types(self, items_types): + def _assign_types(self, items_types: list[type]) -> None: if items_types: try: self.items_types = tuple(items_types) @@ -231,13 +243,13 @@ def _assign_types(self, items_types): types.append(type_) self.items_types = tuple(types) - def validate(self, value: Any) -> None: + def validate(self, value: Value) -> None: super().validate(value) for item in value: self.validate_single_value(item) - def validate_single_value(self, value: Any) -> None: + def validate_single_value(self, value: Value) -> None: for validator in self.item_validators: try: validator.validate(value) @@ -256,7 +268,7 @@ def validate_single_value(self, value: Any) -> None: ) ) - def parse_value(self, values): + def parse_value(self, values: list[Value]) -> list[Value]: """Cast value to proper collection.""" result = self.get_default_value() @@ -268,7 +280,7 @@ def parse_value(self, values): return [self._cast_value(value) for value in values] - def _cast_value(self, value): + def _cast_value(self, value: Value) -> Value: if isinstance(value, self.items_types): return value else: @@ -279,7 +291,7 @@ def _cast_value(self, value): ) return self.items_types[0](**value) - def _finish_initialization(self, owner): + def _finish_initialization(self, owner: Model) -> None: super()._finish_initialization(owner) types = [] @@ -290,13 +302,13 @@ def _finish_initialization(self, owner): types.append(type) self.items_types = tuple(types) - def _elem_to_struct(self, value): + def _elem_to_struct(self, value: Value) -> Value | dict[str, Value]: try: return value.to_struct() except AttributeError: return value - def to_struct(self, values): + def to_struct(self, values: Value) -> list[Value | dict[str, Value]]: return [self._elem_to_struct(v) for v in values] @@ -304,11 +316,11 @@ class EmbeddedField(BaseField): """Field for embedded models.""" - def __init__(self, model_types, *args, **kwargs): + def __init__(self, model_types: list[type], *args: Any, **kwargs: Any) -> None: self._assign_model_types(model_types) super().__init__(*args, **kwargs) - def _assign_model_types(self, model_types): + def _assign_model_types(self, model_types: list[type]) -> None: if not isinstance(model_types, (list, tuple)): model_types = (model_types,) @@ -320,7 +332,7 @@ def _assign_model_types(self, model_types): types.append(type_) self.types = tuple(types) - def _finish_initialization(self, owner): + def _finish_initialization(self, owner: Model) -> None: super()._finish_initialization(owner) types = [] @@ -331,14 +343,14 @@ def _finish_initialization(self, owner): types.append(type) self.types = tuple(types) - def validate(self, value): + def validate(self, value: Value) -> None: super().validate(value) try: value.validate() except AttributeError: pass - def parse_value(self, value): + def parse_value(self, value: Value) -> Model: """Parse value to proper model type.""" if not isinstance(value, dict): return value @@ -346,7 +358,7 @@ def parse_value(self, value): embed_type = self._get_embed_type() return embed_type(**value) - def _get_embed_type(self): + def _get_embed_type(self) -> Model: if len(self.types) != 1: raise ValidationError( 'Cannot decide which type to choose from "{types}".'.format( @@ -355,20 +367,20 @@ def _get_embed_type(self): ) return self.types[0] - def to_struct(self, value): + def to_struct(self, value: Value) -> dict[str, Value]: return value.to_struct() class _LazyType: - def __init__(self, path): + def __init__(self, path: str) -> None: self.path = path - def evaluate(self, base_cls): + def evaluate(self, base_cls: Model) -> Any: module, type_name = _evaluate_path(self.path, base_cls) - return _import(module, type_name) + return cast(type, _import(module, type_name)) -def _evaluate_path(relative_path, base_cls): +def _evaluate_path(relative_path: str, base_cls: type) -> tuple[Any, str]: base_module = base_cls.__module__ modules = _get_modules(relative_path, base_module) @@ -380,7 +392,7 @@ def _evaluate_path(relative_path, base_cls): return module, type_name -def _get_modules(relative_path, base_module): +def _get_modules(relative_path: str, base_module: str) -> Any: canonical_path = relative_path.lstrip(".") canonical_modules = canonical_path.split(".") @@ -395,7 +407,7 @@ def _get_modules(relative_path, base_module): return parent_modules[: parents_amount * -1] + canonical_modules -def _import(module_name, type_name): +def _import(module_name: str, type_name: str) -> Any: module = __import__(module_name, fromlist=[type_name]) try: return getattr(module, type_name) @@ -409,7 +421,9 @@ class TimeField(StringField): types = (datetime.time,) - def __init__(self, str_format=None, *args, **kwargs): + def __init__( + self, str_format: Optional[str] = None, *args: Any, **kwargs: Any + ) -> None: """Init. :param str str_format: Format to cast time to (if `None` - casting to @@ -419,13 +433,13 @@ def __init__(self, str_format=None, *args, **kwargs): self.str_format = str_format super().__init__(*args, **kwargs) - def to_struct(self, value): + def to_struct(self, value: datetime.time) -> str: """Cast `time` object to string.""" if self.str_format: return value.strftime(self.str_format) return value.isoformat() - def parse_value(self, value): + def parse_value(self, value: Value) -> Optional[datetime.time]: """Parse string into instance of `time`.""" if value is None: return value @@ -441,7 +455,9 @@ class DateField(StringField): types = (datetime.date,) default_format = "%Y-%m-%d" - def __init__(self, str_format=None, *args, **kwargs): + def __init__( + self, str_format: Optional[str] = None, *args: Any, **kwargs: Any + ) -> None: """Init. :param str str_format: Format to cast date to (if `None` - casting to @@ -451,13 +467,13 @@ def __init__(self, str_format=None, *args, **kwargs): self.str_format = str_format super().__init__(*args, **kwargs) - def to_struct(self, value): + def to_struct(self, value: datetime.date) -> str: """Cast `date` object to string.""" if self.str_format: return value.strftime(self.str_format) return value.strftime(self.default_format) - def parse_value(self, value): + def parse_value(self, value: Value) -> Optional[datetime.date]: """Parse string into instance of `date`.""" if value is None: return value @@ -472,7 +488,9 @@ class DateTimeField(StringField): types = (datetime.datetime,) - def __init__(self, str_format=None, *args, **kwargs): + def __init__( + self, str_format: Optional[str] = None, *args: Any, **kwargs: Any + ) -> None: """Init. :param str str_format: Format to cast datetime to (if `None` - casting @@ -482,13 +500,13 @@ def __init__(self, str_format=None, *args, **kwargs): self.str_format = str_format super().__init__(*args, **kwargs) - def to_struct(self, value): + def to_struct(self, value: datetime.datetime) -> str: """Cast `datetime` object to string.""" if self.str_format: return value.strftime(self.str_format) return value.isoformat() - def parse_value(self, value): + def parse_value(self, value: Value) -> Optional[datetime.datetime]: """Parse string into instance of `datetime`.""" if isinstance(value, datetime.datetime): return value diff --git a/jsonmodels/models.py b/jsonmodels/models.py index 35610c0..3652a64 100644 --- a/jsonmodels/models.py +++ b/jsonmodels/models.py @@ -1,15 +1,21 @@ +from typing import Any, Dict, Generator, Tuple, Type + from . import errors, parsers from .errors import ValidationError from .fields import BaseField +Values = Dict[str, Any] +Fields = Tuple[str, BaseField] +FieldsWithNames = Tuple[str, str, BaseField] + class JsonmodelMeta(type): - def __new__(cls, name, bases, attributes): + def __new__(cls: Type[type], name: str, bases: tuple, attributes: dict) -> type: cls.validate_fields(attributes) return super(cls, cls).__new__(cls, name, bases, attributes) @staticmethod - def validate_fields(attributes): + def validate_fields(attributes: dict[str, Any]) -> None: fields = { key: value for key, value in attributes.items() @@ -27,11 +33,11 @@ class Base(metaclass=JsonmodelMeta): """Base class for all models.""" - def __init__(self, **kwargs): + def __init__(self, **kwargs: Values) -> None: self._cache_key = _CacheKey() self.populate(**kwargs) - def populate(self, **values): + def populate(self, **values: Values) -> None: """Populate values to fields. Skip non-existing.""" values = values.copy() fields = list(self.iterate_with_name()) @@ -42,7 +48,7 @@ def populate(self, **values): if name in values: self.set_field(field, name, values.pop(name)) - def get_field(self, field_name): + def get_field(self, field_name: str) -> BaseField: """Get field associated with given attribute.""" for attr_name, field in self: if field_name == attr_name: @@ -50,18 +56,18 @@ def get_field(self, field_name): raise errors.FieldNotFound("Field not found", field_name) - def set_field(self, field, field_name, value): + def set_field(self, field: BaseField, field_name: str, value: Any) -> None: """Sets the value of a field.""" try: field.__set__(self, value) except ValidationError as error: raise ValidationError(f"Error for field '{field_name}': {error}.") - def __iter__(self): + def __iter__(self) -> Generator[Fields, None, None]: """Iterate through fields and values.""" yield from self.iterate_over_fields() - def validate(self): + def validate(self) -> None: """Explicitly validate all the fields.""" for name, field in self: try: @@ -73,7 +79,7 @@ def validate(self): ) @classmethod - def iterate_over_fields(cls): + def iterate_over_fields(cls) -> Generator[Fields, None, None]: """Iterate through fields as `(attribute_name, field_instance)`.""" for attr in dir(cls): clsattr = getattr(cls, attr) @@ -81,7 +87,7 @@ def iterate_over_fields(cls): yield attr, clsattr @classmethod - def iterate_with_name(cls): + def iterate_with_name(cls) -> Generator[FieldsWithNames, None, None]: """Iterate over fields, but also give `structure_name`. Format is `(attribute_name, structue_name, field_instance)`. @@ -92,16 +98,16 @@ def iterate_with_name(cls): structure_name = field.structue_name(attr_name) yield attr_name, structure_name, field - def to_struct(self): + def to_struct(self) -> dict[str, Any]: """Cast model to Python structure.""" return parsers.to_struct(self) @classmethod - def to_json_schema(cls): + def to_json_schema(cls) -> str: """Generate JSON schema for model.""" return parsers.to_json_schema(cls) - def __repr__(self): + def __repr__(self) -> str: attrs = {} for name, _ in self: attr = getattr(self, name) @@ -113,16 +119,16 @@ def __repr__(self): fields=", ".join("{0[0]}={0[1]}".format(x) for x in sorted(attrs.items())), ) - def __str__(self): + def __str__(self) -> str: return f"{self.__class__.__name__} object" - def __setattr__(self, name, value): + def __setattr__(self, name: str, value: Any) -> None: try: return super().__setattr__(name, value) except ValidationError as error: raise ValidationError(f"Error for field '{name}'.", error) - def __eq__(self, other): + def __eq__(self, other: object) -> bool: if type(other) is not type(self): return False @@ -142,7 +148,7 @@ def __eq__(self, other): return True - def __ne__(self, other): + def __ne__(self, other: object) -> bool: return not (self == other) diff --git a/jsonmodels/parsers.py b/jsonmodels/parsers.py index 36af245..abfcdfc 100644 --- a/jsonmodels/parsers.py +++ b/jsonmodels/parsers.py @@ -1,16 +1,13 @@ """Parsers to change model structure into different ones.""" +from typing import Any + import inspect from . import builders, errors, fields -def to_struct(model): - """Cast instance of model to python structure. - - :param model: Model to be casted. - :rtype: ``dict`` - - """ +def to_struct(model: fields.Model) -> dict[str, Any]: + """Cast instance of model to python structure.""" model.validate() resp = {} @@ -24,18 +21,13 @@ def to_struct(model): return resp -def to_json_schema(cls): - """Generate JSON schema for given class. - - :param cls: Class to be casted. - :rtype: ``dict`` - - """ +def to_json_schema(cls: fields.Model) -> dict[str, Any]: + """Generate JSON schema for given class.""" builder = build_json_schema(cls) return builder.build() -def build_json_schema(value, parent_builder=None): +def build_json_schema(value, parent_builder=None) -> builders.PrimitiveBuilder | builders.ObjectBuilder: from .models import Base cls = value if inspect.isclass(value) else value.__class__ @@ -45,7 +37,7 @@ def build_json_schema(value, parent_builder=None): return build_json_schema_primitive(cls, parent_builder) -def build_json_schema_object(cls, parent_builder=None): +def build_json_schema_object(cls, parent_builder=None) -> builders.ObjectBuilder: builder = builders.ObjectBuilder(cls, parent_builder) if builder.count_type(builder.type) > 1: return builder @@ -59,7 +51,7 @@ def build_json_schema_object(cls, parent_builder=None): return builder -def _parse_list(field, parent_builder): +def _parse_list(field, parent_builder) -> dict[str, Any]: builder = builders.ListBuilder( parent_builder, field.nullable, default=field._default ) @@ -68,7 +60,7 @@ def _parse_list(field, parent_builder): return builder.build() -def _parse_embedded(field, parent_builder): +def _parse_embedded(field, parent_builder) -> dict[str, Any] | fields.Value: builder = builders.EmbeddedBuilder( parent_builder, field.nullable, default=field._default ) @@ -77,13 +69,13 @@ def _parse_embedded(field, parent_builder): return builder.build() -def build_json_schema_primitive(cls, parent_builder): +def build_json_schema_primitive(cls, parent_builder) -> builders.PrimitiveBuilder: builder = builders.PrimitiveBuilder(cls, parent_builder) return builder -def _create_primitive_field_schema(field): - if isinstance(field, fields.StringField): +def _create_primitive_field_schema(field) -> dict[str, Any]: + if isinstance(field, fields.StringField): # TODO rewrite this as switch case? obj_type = "string" elif isinstance(field, fields.IntField): obj_type = "number" @@ -98,10 +90,11 @@ def _create_primitive_field_schema(field): f"Field {type(field).__class__.__name__} is not supported!" ) + schema: dict[str, Any] = {} if field.nullable: - obj_type = [obj_type, "null"] - - schema = {"type": obj_type} + schema = {"type": [obj_type, "null"]} + else: + schema = {"type": obj_type} if field.has_default: schema["default"] = field._default diff --git a/mypy.ini b/mypy.ini index 1b4f07c..0fa0de0 100644 --- a/mypy.ini +++ b/mypy.ini @@ -4,5 +4,5 @@ disallow_any_unimported = True no_implicit_optional = True warn_return_any = True warn_unused_configs = True -warn_usued_ignores = True +warn_unused_ignores = True show_error_codes = True diff --git a/requirements.txt b/requirements.txt index 6100a25..c5134dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,5 +17,6 @@ pytest-cov sphinxcontrib-spelling tox types-python-dateutil +types-setuptools virtualenv wheel diff --git a/setup.py b/setup.py index 2751208..4a678ff 100755 --- a/setup.py +++ b/setup.py @@ -18,16 +18,16 @@ class PyTest(TestCommand): user_options = [("pytest-args=", "a", "Arguments to pass to py.test")] - def initialize_options(self): + def initialize_options(self) -> None: TestCommand.initialize_options(self) self.pytest_args = ["--cov", PROJECT_NAME] - def finalize_options(self): + def finalize_options(self) -> None: TestCommand.finalize_options(self) self.test_args = [] self.test_suite = True - def run_tests(self): + def run_tests(self) -> None: import pytest errno = pytest.main(self.pytest_args) diff --git a/tests/test_struct.py b/tests/test_struct.py index 7de2193..85ad14c 100644 --- a/tests/test_struct.py +++ b/tests/test_struct.py @@ -1,16 +1,9 @@ -from datetime import datetime - import pytest from jsonmodels import errors, fields, models -class _DateField(fields.BaseField): - - _types = (datetime,) - - -def test_to_struct_basic(): +def test_to_struct_basic() -> None: class Person(models.Base): name = fields.StringField(required=True) @@ -42,7 +35,7 @@ class Person(models.Base): assert pattern == alan.to_struct() -def test_to_struct_nested_1(): +def test_to_struct_nested_1() -> None: class Car(models.Base): brand = fields.StringField() @@ -72,7 +65,7 @@ class ParkingPlace(models.Base): assert pattern == place.to_struct() -def test_to_struct_nested_2(): +def test_to_struct_nested_2() -> None: class Viper(models.Base): serial = fields.StringField() @@ -111,7 +104,7 @@ class Parking(models.Base): assert pattern == parking.to_struct() -def test_to_struct_with_non_models_types(): +def test_to_struct_with_non_models_types() -> None: class Person(models.Base): names = fields.ListField(str) @@ -135,14 +128,14 @@ class Person(models.Base): pattern == person.to_struct() -def test_to_struct_with_multi_non_models_types(): +def test_to_struct_with_multi_non_models_types() -> None: class Person(models.Base): name = fields.StringField() mix = fields.ListField((str, float)) person = Person() - pattern = {"mix": []} + pattern: dict[str, list] = {"mix": []} assert pattern == person.to_struct() person.mix.append("something") @@ -158,7 +151,7 @@ class Person(models.Base): assert pattern == person.to_struct() -def test_list_to_struct(): +def test_list_to_struct() -> None: class Cat(models.Base): name = fields.StringField(required=True) breed = fields.StringField() @@ -184,7 +177,7 @@ class Person(models.Base): assert pattern == person.to_struct() -def test_to_struct_time(): +def test_to_struct_time() -> None: class Clock(models.Base): time = fields.TimeField() @@ -195,7 +188,7 @@ class Clock(models.Base): assert pattern == clock.to_struct() -def test_to_struct_date(): +def test_to_struct_date() -> None: class Event(models.Base): start = fields.DateField() @@ -206,7 +199,7 @@ class Event(models.Base): assert pattern == event.to_struct() -def test_to_struct_datetime(): +def test_to_struct_datetime() -> None: class Event(models.Base): start = fields.DateTimeField() diff --git a/tests/test_validation.py b/tests/test_validation.py index aaf3247..a616afb 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -1,4 +1,5 @@ """Test for validators.""" +from typing import Any import pytest @@ -6,20 +7,20 @@ class FakeValidator: - def __init__(self): + def __init__(self) -> None: self.called_with = None self.called_amount = 0 - def validate(self, value): + def validate(self, value: Any) -> None: self.called_amount = self.called_amount + 1 self.called_with = value - def assert_called_once_with(self, value): + def assert_called_once_with(self, value: Any) -> None: if value != self.called_with or self.called_amount != 1: raise AssertionError('Assert called once with "{}" failed!') -def test_validation(): +def test_validation() -> None: validator1 = FakeValidator() validator2 = FakeValidator() @@ -27,7 +28,7 @@ def test_validation(): called = [] arg = [] - def validator3(value): + def validator3(value: Any) -> None: called.append(1) arg.append(value) @@ -51,7 +52,7 @@ class Person(models.Base): assert 33 == arg.pop() -def test_validators_are_always_iterable(): +def test_validators_are_always_iterable() -> None: class Person(models.Base): children = fields.ListField() @@ -61,7 +62,7 @@ class Person(models.Base): assert isinstance(alan.get_field("children").validators, list) -def test_get_field_not_found(): +def test_get_field_not_found() -> None: class Person(models.Base): children = fields.ListField() @@ -72,7 +73,7 @@ class Person(models.Base): alan.get_field("bazinga") -def test_min_validation(): +def test_min_validation() -> None: validator = validators.Min(3) assert 3 == validator.minimum_value @@ -86,7 +87,7 @@ def test_min_validation(): validator.validate(-2) -def test_exclusive_validation(): +def test_exclusive_validation() -> None: validator = validators.Min(3, True) assert 3 == validator.minimum_value @@ -100,7 +101,7 @@ def test_exclusive_validation(): validator.validate(-2) -def test_max_validation(): +def test_max_validation() -> None: validator = validators.Max(42) assert 42 == validator.maximum_value @@ -113,7 +114,7 @@ def test_max_validation(): validator.validate(43) -def test_max_exclusive_validation(): +def test_max_exclusive_validation() -> None: validator = validators.Max(42, True) assert 42 == validator.maximum_value @@ -127,7 +128,7 @@ def test_max_exclusive_validation(): validator.validate(43) -def test_regex_validation(): +def test_regex_validation() -> None: validator = validators.Regex("some") assert "some" == validator.pattern @@ -140,7 +141,7 @@ def test_regex_validation(): validator.validate("trololo") -def test_regex_validation_flags(): +def test_regex_validation_flags() -> None: # Invalid flags ignored validator = validators.Regex("foo", bla=True, ble=False, ignorecase=True) assert validator.flags == [validators.Regex.FLAGS["ignorecase"]] @@ -163,7 +164,7 @@ def test_regex_validation_flags(): assert validator.flags == [] -def test_regex_validation_for_wrong_type(): +def test_regex_validation_for_wrong_type() -> None: validator = validators.Regex("some") assert "some" == validator.pattern @@ -172,7 +173,7 @@ def test_regex_validation_for_wrong_type(): validator.validate(1) -def test_validation_2(): +def test_validation_2() -> None: validator = validators.Regex("^some[0-9]$") assert "^some[0-9]$" == validator.pattern @@ -188,7 +189,7 @@ def test_validation_2(): validator.validate("trololo") -def test_validation_ignorecase(): +def test_validation_ignorecase() -> None: validator = validators.Regex("^some$") validator.validate("some") with pytest.raises(errors.ValidationError): @@ -199,7 +200,7 @@ def test_validation_ignorecase(): validator.validate("SoMe") -def test_validation_multiline(): +def test_validation_multiline() -> None: validator = validators.Regex("^s.*e$") validator.validate("some") with pytest.raises(errors.ValidationError): @@ -210,7 +211,7 @@ def test_validation_multiline(): validator.validate("some\nso more") -def test_regex_validator(): +def test_regex_validator() -> None: class Person(models.Base): name = fields.StringField( @@ -225,7 +226,7 @@ class Person(models.Base): person.name = "Jimmy" -def test_regex_validator_when_ecma_regex_given(): +def test_regex_validator_when_ecma_regex_given() -> None: class Person(models.Base): name = fields.StringField( @@ -240,7 +241,7 @@ class Person(models.Base): person.name = "Jimmy" -def test_init(): +def test_init() -> None: validator = validators.Length(0, 10) assert 0 == validator.minimum_value assert 10 == validator.maximum_value @@ -257,7 +258,7 @@ def test_init(): validators.Length() -def test_length_validation_string_min_max(): +def test_length_validation_string_min_max() -> None: validator = validators.Length(1, 10) validator.validate("word") validator.validate("w" * 10) @@ -269,7 +270,7 @@ def test_length_validation_string_min_max(): validator.validate("na" * 10) -def test_length_validation_string_min(): +def test_length_validation_string_min() -> None: validator = validators.Length(minimum_value=1) validator.validate("a") validator.validate("aasdasd" * 1000) @@ -277,7 +278,7 @@ def test_length_validation_string_min(): validator.validate("") -def test_length_validation_string_max(): +def test_length_validation_string_max() -> None: validator = validators.Length(maximum_value=10) validator.validate("") validator.validate("a") @@ -286,7 +287,7 @@ def test_length_validation_string_max(): validator.validate("a" * 11) -def test_length_validation_list_min_max(): +def test_length_validation_list_min_max() -> None: validator = validators.Length(1, 10) validator.validate([1, 2, 3, 4]) validator.validate([1] * 10) @@ -298,7 +299,7 @@ def test_length_validation_list_min_max(): validator.validate([1, 2] * 10) -def test_length_validation_list_min(): +def test_length_validation_list_min() -> None: validator = validators.Length(minimum_value=1) validator.validate([1]) validator.validate(range(1000)) @@ -306,7 +307,7 @@ def test_length_validation_list_min(): validator.validate([]) -def test_length_validation_list_max(): +def test_length_validation_list_max() -> None: validator = validators.Length(maximum_value=10) validator.validate([]) validator.validate([1]) @@ -315,7 +316,7 @@ def test_length_validation_list_max(): validator.validate([1] * 11) -def test_validation_nullable(): +def test_validation_nullable() -> None: class Emb(models.Base): name = fields.StringField(nullable=True) @@ -328,7 +329,7 @@ class User(models.Base): user.validate() -def test_enum_validation(): +def test_enum_validation() -> None: validator = validators.Enum("cat", "dog", "fish") validator.validate("cat")