From 779710ce92646110fbfb65be46cb359417164b70 Mon Sep 17 00:00:00 2001 From: Lexi Stelter Date: Wed, 7 Aug 2024 18:20:21 +0200 Subject: [PATCH 1/3] Add py.typed file to make the package PEP 561 compatible --- src/validataclass/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/validataclass/py.typed diff --git a/src/validataclass/py.typed b/src/validataclass/py.typed new file mode 100644 index 0000000..e69de29 From 1694bbb7f9c86795000f88590fdacb36b0f3add7 Mon Sep 17 00:00:00 2001 From: Lexi Stelter Date: Wed, 7 Aug 2024 18:24:52 +0200 Subject: [PATCH 2/3] Explicitly re-export imports using __all__ This is necessary to avoid errors from the no-implicit-reexport rule both in the library and in projects using the library. It's also cleaner to have explicit exports. --- .github/workflows/tests.yml | 2 +- pyproject.toml | 3 - src/validataclass/dataclasses/__init__.py | 10 ++++ src/validataclass/exceptions/__init__.py | 71 ++++++++++++++++++----- src/validataclass/helpers/__init__.py | 27 +++++++-- src/validataclass/validators/__init__.py | 39 +++++++++++-- tests/dataclasses/validataclass_test.py | 2 + 7 files changed, 126 insertions(+), 28 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f9ec65d..20512d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,7 +36,7 @@ jobs: - name: Install dependencies run: pip install --upgrade pip tox - - name: Run unit tests with tox + - name: Run test suite with tox # Run tox using the version of Python in `PATH` run: tox run -e clean,py,report,flake8,mypy -- --junit-xml=reports/pytest_${{ matrix.python-version }}.xml diff --git a/pyproject.toml b/pyproject.toml index 3951594..2ba1514 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,9 +18,6 @@ explicit_package_bases = true # Enable strict type checking strict = true -# Ignore errors like `Module "validataclass.exceptions" does not explicitly export attribute "..."` -no_implicit_reexport = false - [[tool.mypy.overrides]] module = 'tests.*' diff --git a/src/validataclass/dataclasses/__init__.py b/src/validataclass/dataclasses/__init__.py index ccdb45d..9d1c855 100644 --- a/src/validataclass/dataclasses/__init__.py +++ b/src/validataclass/dataclasses/__init__.py @@ -8,3 +8,13 @@ from .validataclass import validataclass from .validataclass_field import validataclass_field from .validataclass_mixin import ValidataclassMixin + +__all__ = [ + 'Default', + 'DefaultFactory', + 'DefaultUnset', + 'NoDefault', + 'ValidataclassMixin', + 'validataclass', + 'validataclass_field', +] diff --git a/src/validataclass/exceptions/__init__.py b/src/validataclass/exceptions/__init__.py index 5edda93..b04b316 100644 --- a/src/validataclass/exceptions/__init__.py +++ b/src/validataclass/exceptions/__init__.py @@ -4,26 +4,34 @@ Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. """ -# "Meta exceptions" (no validation errors, but logic errors in the validators, e.g. when specifying invalid options for -# a validator) +from .base_exceptions import ValidationError +from .common_exceptions import ( + FieldNotAllowedError, + InvalidTypeError, + RequiredValueError, +) +from .dataclass_exceptions import DataclassPostValidationError +from .datetime_exceptions import ( + DateTimeRangeError, + InvalidDateError, + InvalidDateTimeError, + InvalidTimeError, +) +from .dict_exceptions import ( + DictFieldsValidationError, + DictInvalidKeyTypeError, + DictRequiredFieldError, +) +from .email_exceptions import InvalidEmailError +from .list_exceptions import ( + ListItemsValidationError, + ListLengthError, +) from .meta_exceptions import ( DataclassInvalidPreValidateSignatureException, DataclassValidatorFieldException, InvalidValidatorOptionException, ) - -# Base exception classes for validation errors -from .base_exceptions import ValidationError - -# Common validation errors used throughout the library -from .common_exceptions import RequiredValueError, FieldNotAllowedError, InvalidTypeError - -# More specific validation errors -from .dataclass_exceptions import DataclassPostValidationError -from .datetime_exceptions import InvalidDateError, InvalidTimeError, InvalidDateTimeError, DateTimeRangeError -from .dict_exceptions import DictFieldsValidationError, DictInvalidKeyTypeError, DictRequiredFieldError -from .email_exceptions import InvalidEmailError -from .list_exceptions import ListItemsValidationError, ListLengthError from .misc_exceptions import ValueNotAllowedError from .number_exceptions import ( DecimalPlacesError, @@ -40,3 +48,36 @@ StringTooShortError, ) from .url_exceptions import InvalidUrlError + +__all__ = [ + 'DataclassInvalidPreValidateSignatureException', + 'DataclassPostValidationError', + 'DataclassValidatorFieldException', + 'DateTimeRangeError', + 'DecimalPlacesError', + 'DictFieldsValidationError', + 'DictInvalidKeyTypeError', + 'DictRequiredFieldError', + 'FieldNotAllowedError', + 'InvalidDateError', + 'InvalidDateTimeError', + 'InvalidDecimalError', + 'InvalidEmailError', + 'InvalidIntegerError', + 'InvalidTimeError', + 'InvalidTypeError', + 'InvalidUrlError', + 'InvalidValidatorOptionException', + 'ListItemsValidationError', + 'ListLengthError', + 'NonFiniteNumberError', + 'NumberRangeError', + 'RegexMatchError', + 'RequiredValueError', + 'StringInvalidCharactersError', + 'StringInvalidLengthError', + 'StringTooLongError', + 'StringTooShortError', + 'ValidationError', + 'ValueNotAllowedError', +] diff --git a/src/validataclass/helpers/__init__.py b/src/validataclass/helpers/__init__.py index 8492ac0..547e3b6 100644 --- a/src/validataclass/helpers/__init__.py +++ b/src/validataclass/helpers/__init__.py @@ -4,17 +4,34 @@ Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. """ +# Requirements for the __getattr__ definition below import importlib import warnings from typing import Any -from .datetime_range import BaseDateTimeRange, DateTimeRange, DateTimeOffsetRange -from .unset_value import UnsetValue, UnsetValueType, OptionalUnset, OptionalUnsetNone, unset_to_none +# Re-export symbols from modules +from .datetime_range import ( + BaseDateTimeRange, + DateTimeOffsetRange, + DateTimeRange, +) +from .unset_value import ( + OptionalUnset, + OptionalUnsetNone, + UnsetValue, + UnsetValueType, + unset_to_none, +) -# Defining __all__ is necessary here because of the definition of __getattr__() below. __all__ = [ - 'BaseDateTimeRange', 'DateTimeRange', 'DateTimeOffsetRange', - 'UnsetValue', 'UnsetValueType', 'OptionalUnset', 'OptionalUnsetNone', 'unset_to_none', + 'BaseDateTimeRange', + 'DateTimeOffsetRange', + 'DateTimeRange', + 'OptionalUnset', + 'OptionalUnsetNone', + 'UnsetValue', + 'UnsetValueType', + 'unset_to_none', ] diff --git a/src/validataclass/validators/__init__.py b/src/validataclass/validators/__init__.py index d9c3e7c..0eeb1e1 100644 --- a/src/validataclass/validators/__init__.py +++ b/src/validataclass/validators/__init__.py @@ -4,10 +4,6 @@ Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. """ -# Abstract base class -from .validator import Validator - -# Validators from .allow_empty_string import AllowEmptyString from .any_of_validator import AnyOfValidator from .anything_validator import AnythingValidator @@ -33,3 +29,38 @@ from .string_validator import StringValidator from .time_validator import TimeValidator, TimeFormat from .url_validator import UrlValidator +from .validator import Validator + +__all__ = [ + 'AllowEmptyString', + 'AnyOfValidator', + 'AnythingValidator', + 'BigIntegerValidator', + 'BooleanValidator', + 'DataclassValidator', + 'DateTimeFormat', + 'DateTimeValidator', + 'DateValidator', + 'DecimalValidator', + 'DictValidator', + 'DiscardValidator', + 'EmailValidator', + 'EnumValidator', + 'FloatToDecimalValidator', + 'FloatValidator', + 'IntegerValidator', + 'ListValidator', + 'NoneToUnsetValue', + 'Noneable', + 'NumericValidator', + 'RegexValidator', + 'RejectValidator', + 'StringValidator', + 'T_Dataclass', + 'T_Enum', + 'T_ListItem', + 'TimeFormat', + 'TimeValidator', + 'UrlValidator', + 'Validator', +] diff --git a/tests/dataclasses/validataclass_test.py b/tests/dataclasses/validataclass_test.py index 61aad5f..bec424d 100644 --- a/tests/dataclasses/validataclass_test.py +++ b/tests/dataclasses/validataclass_test.py @@ -3,6 +3,8 @@ Copyright (c) 2021, binary butterfly GmbH and contributors Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. """ +# (Ignore comparison-overlap errors, which happen in comparisons like `assert fields['baz'].type is Optional[str]`) +# mypy: no-strict-equality import dataclasses from typing import Dict, List, Optional, Union From faf22b61da987e670afcf9c65ef8b3ab416f3894 Mon Sep 17 00:00:00 2001 From: Lexi Stelter Date: Wed, 7 Aug 2024 19:00:33 +0200 Subject: [PATCH 3/3] Fix import loop in validataclass.validators --- src/validataclass/validators/__init__.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/validataclass/validators/__init__.py b/src/validataclass/validators/__init__.py index 0eeb1e1..4094dff 100644 --- a/src/validataclass/validators/__init__.py +++ b/src/validataclass/validators/__init__.py @@ -4,6 +4,10 @@ Use of this source code is governed by an MIT-style license that can be found in the LICENSE file. """ +# Abstract base class (needs to be imported first to avoid import loops) +from .validator import Validator # isort:skip + +# Validators from .allow_empty_string import AllowEmptyString from .any_of_validator import AnyOfValidator from .anything_validator import AnythingValidator @@ -11,7 +15,7 @@ from .boolean_validator import BooleanValidator from .dataclass_validator import DataclassValidator, T_Dataclass from .date_validator import DateValidator -from .datetime_validator import DateTimeValidator, DateTimeFormat +from .datetime_validator import DateTimeFormat, DateTimeValidator from .decimal_validator import DecimalValidator from .dict_validator import DictValidator from .discard_validator import DiscardValidator @@ -27,9 +31,8 @@ from .regex_validator import RegexValidator from .reject_validator import RejectValidator from .string_validator import StringValidator -from .time_validator import TimeValidator, TimeFormat +from .time_validator import TimeFormat, TimeValidator from .url_validator import UrlValidator -from .validator import Validator __all__ = [ 'AllowEmptyString',