diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 2d0041e9..811d0a9b 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.19.0 +current_version = 0.20.0 commit = True tag = True message = chore: Bump version from {current_version} to {new_version} diff --git a/.github/workflows/dependency-review.yaml b/.github/workflows/dependency-review.yaml new file mode 100644 index 00000000..d78b8ab7 --- /dev/null +++ b/.github/workflows/dependency-review.yaml @@ -0,0 +1,25 @@ +name: Dependency Review + +on: + pull_request: + types: + - opened + - reopened + - synchronize + +permissions: + contents: read + +jobs: + dependency-review: + name: Dependency Review + runs-on: ubuntu-22.04 + + steps: + - name: Check Out VCS Repository + uses: actions/checkout@v3.3.0 + + - name: Dependency Review + uses: actions/dependency-review-action@v3.0.3 + with: + fail-on-severity: critical diff --git a/.markdownlint.yaml b/.markdownlint.yaml index 10a74361..ea67d22b 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -12,10 +12,14 @@ heading-style: line-length: line_length: 100 + code_block_line_length: 200 + tables: false no-duplicate-heading: allow_different_nesting: true +no-emphasis-as-heading: false + no-multiple-blanks: maximum: 2 diff --git a/HISTORY.md b/HISTORY.md index c0e802ba..63a54c1b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,25 @@ # History +## 0.20.0 (2023-02-10) + +- (PR #403, 2023-01-05) chore(deps): Bump pyopenssl from 22.0.0 to 22.1.0 +- (PR #406, 2023-01-05) chore(deps-dev): Bump mypy from 0.982 to 0.991 +- (PR #405, 2023-01-23) chore(deps): Bump jsonschema from 4.16.0 to 4.17.3 +- (PR #422, 2023-01-26) Add GitHub Dependency Review +- (PR #423, 2023-01-26) Improve GitHub Dependency Review +- (PR #411, 2023-01-26) chore(deps-dev): Bump black from 22.10.0 to 22.12.0 +- (PR #425, 2023-01-27) Update Markdownlint configuration +- (PR #410, 2023-02-03) chore(deps): Bump pydantic from 1.10.2 to 1.10.4 +- (PR #433, 2023-02-03) Improve type checking +- (PR #434, 2023-02-03) Improve type checking +- (PR #424, 2023-02-06) chore(deps-dev): Bump coverage from 6.5.0 to 7.1.0 +- (PR #414, 2023-02-06) chore(deps): Bump lxml from 4.9.1 to 4.9.2 +- (PR #420, 2023-02-06) chore(deps): Bump pytz from 2022.6 to 2022.7.1 +- (PR #431, 2023-02-06) chore(deps): Bump pyopenssl from 22.1.0 to 23.0.0 +- (PR #436, 2023-02-08) chore(deps): Bump cryptography from 38.0.4 to 39.0.1 +- (PR #435, 2023-02-08) chore(deps): Bump django from 3.2.16 to 3.2.17 +- (PR #426, 2023-02-08) chore(deps): Bump marshmallow from 2.21.0 to 3.19.0 + ## 0.19.0 (2023-01-05) - (PR #398, 2022-11-23) fix: Fix Dependabot error pip.….Error: Constraints cannot have extras diff --git a/Makefile b/Makefile index d3e83402..3bdf1216 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ install-deps-dev: ## Install dependencies for development lint: ## run tools for code style analysis, static type check, etc flake8 --config=setup.cfg cl_sii scripts tests - mypy --config-file setup.cfg cl_sii scripts + mypy isort --check-only . $(BLACK) --check . diff --git a/cl_sii/__init__.py b/cl_sii/__init__.py index d1b4d32e..8d06d84f 100644 --- a/cl_sii/__init__.py +++ b/cl_sii/__init__.py @@ -5,4 +5,4 @@ """ -__version__ = '0.19.0' +__version__ = '0.20.0' diff --git a/cl_sii/extras/mm_fields.py b/cl_sii/extras/mm_fields.py index 840d83b9..a85bcf56 100644 --- a/cl_sii/extras/mm_fields.py +++ b/cl_sii/extras/mm_fields.py @@ -4,13 +4,16 @@ (for serializers) """ +from __future__ import annotations + + try: import marshmallow except ImportError as exc: # pragma: no cover raise ImportError("Package 'marshmallow' is required to use this module.") from exc import datetime -from typing import Optional +from typing import Any, Mapping, Optional import marshmallow.fields @@ -46,13 +49,18 @@ class RutField(marshmallow.fields.Field): default_error_messages = { 'invalid': 'Not a syntactically valid RUT.', + 'type': 'Invalid type.', } - def _serialize(self, value: Optional[object], attr: str, obj: object) -> Optional[str]: + def _serialize( + self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any + ) -> Optional[str]: validated = self._validated(value) return validated.canonical if validated is not None else None - def _deserialize(self, value: str, attr: str, data: dict) -> Optional[Rut]: + def _deserialize( + self, value: str, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any + ) -> Optional[Rut]: return self._validated(value) def _validated(self, value: Optional[object]) -> Optional[Rut]: @@ -61,10 +69,10 @@ def _validated(self, value: Optional[object]) -> Optional[Rut]: else: try: validated = Rut(value, validate_dv=False) # type: ignore - except TypeError: - self.fail('type') - except ValueError: - self.fail('invalid') + except TypeError as exc: + raise self.make_error('type') from exc + except ValueError as exc: + raise self.make_error('invalid') from exc return validated @@ -89,13 +97,18 @@ class TipoDteField(marshmallow.fields.Field): default_error_messages = { 'invalid': 'Not a valid Tipo DTE.', + 'type': 'Invalid type.', } - def _serialize(self, value: Optional[object], attr: str, obj: object) -> Optional[int]: + def _serialize( + self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any + ) -> Optional[int]: validated: Optional[TipoDte] = self._validated(value) return validated.value if validated is not None else None - def _deserialize(self, value: object, attr: str, data: dict) -> Optional[TipoDte]: + def _deserialize( + self, value: object, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any + ) -> Optional[TipoDte]: return self._validated(value) def _validated(self, value: Optional[object]) -> Optional[TipoDte]: @@ -104,21 +117,21 @@ def _validated(self, value: Optional[object]) -> Optional[TipoDte]: else: if isinstance(value, bool): # is value is bool, `isinstance(value, int)` is True and `int(value)` works! - self.fail('type') + raise self.make_error('type') try: value = int(value) # type: ignore - except ValueError: + except ValueError as exc: # `int('x')` raises 'ValueError', not 'TypeError' - self.fail('type') - except TypeError: + raise self.make_error('type') from exc + except TypeError as exc: # `int(date(2018, 10, 10))` raises 'TypeError', unlike `int('x')` - self.fail('type') + raise self.make_error('type') from exc try: validated = TipoDte(value) # type: ignore - except ValueError: + except ValueError as exc: # TipoDte('x') raises 'ValueError', not 'TypeError' - self.fail('invalid') + raise self.make_error('invalid') from exc return validated @@ -142,13 +155,18 @@ class RcvTipoDoctoField(marshmallow.fields.Field): default_error_messages = { 'invalid': "Not a valid RCV's Tipo de Documento.", + 'type': "Invalid type.", } - def _serialize(self, value: Optional[object], attr: str, obj: object) -> Optional[int]: + def _serialize( + self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any + ) -> Optional[int]: validated: Optional[RcvTipoDocto] = self._validated(value) return validated.value if validated is not None else None - def _deserialize(self, value: object, attr: str, data: dict) -> Optional[RcvTipoDocto]: + def _deserialize( + self, value: object, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any + ) -> Optional[RcvTipoDocto]: return self._validated(value) def _validated(self, value: Optional[object]) -> Optional[RcvTipoDocto]: @@ -157,21 +175,21 @@ def _validated(self, value: Optional[object]) -> Optional[RcvTipoDocto]: else: if isinstance(value, bool): # is value is bool, `isinstance(value, int)` is True and `int(value)` works! - self.fail('type') + raise self.make_error('type') try: value = int(value) # type: ignore - except ValueError: + except ValueError as exc: # `int('x')` raises 'ValueError', not 'TypeError' - self.fail('type') - except TypeError: + raise self.make_error('type') from exc + except TypeError as exc: # `int(date(2018, 10, 10))` raises 'TypeError', unlike `int('x')` - self.fail('type') + raise self.make_error('type') from exc try: validated = RcvTipoDocto(value) # type: ignore - except ValueError: + except ValueError as exc: # RcvTipoDocto('x') raises 'ValueError', not 'TypeError' - self.fail('invalid') + raise self.make_error('invalid') from exc return validated @@ -186,14 +204,19 @@ class RcvPeriodoTributarioField(marshmallow.fields.Field): default_error_messages = { 'invalid': "Not a valid RCV Periodo Tributario.", + 'type': "Invalid type.", } _string_format = '%Y-%m' # Example: '2019-12' - def _serialize(self, value: Optional[object], attr: str, obj: object) -> Optional[str]: + def _serialize( + self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any + ) -> Optional[str]: validated: Optional[RcvPeriodoTributario] = self._validated(value) return validated.as_date().strftime(self._string_format) if validated is not None else None - def _deserialize(self, value: object, attr: str, data: dict) -> Optional[RcvPeriodoTributario]: + def _deserialize( + self, value: object, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any + ) -> Optional[RcvPeriodoTributario]: return self._validated(value) def _validated(self, value: Optional[object]) -> Optional[RcvPeriodoTributario]: @@ -203,10 +226,10 @@ def _validated(self, value: Optional[object]) -> Optional[RcvPeriodoTributario]: try: value = datetime.datetime.strptime(value, self._string_format) # type: ignore value = value.date() - except ValueError: - self.fail('invalid') - except TypeError: - self.fail('type') + except ValueError as exc: + raise self.make_error('invalid') from exc + except TypeError as exc: + raise self.make_error('type') from exc validated = RcvPeriodoTributario.from_date(value) # type: ignore diff --git a/cl_sii/libs/csv_utils.py b/cl_sii/libs/csv_utils.py index 5eaf0cc1..b32abe76 100644 --- a/cl_sii/libs/csv_utils.py +++ b/cl_sii/libs/csv_utils.py @@ -1,5 +1,5 @@ import csv -from typing import IO, Sequence, Type, Union +from typing import IO, Optional, Sequence, Type, Union def create_csv_dict_reader( @@ -7,7 +7,7 @@ def create_csv_dict_reader( csv_dialect: Type[csv.Dialect], row_dict_extra_fields_key: Union[str, None] = None, expected_fields_strict: bool = True, - expected_field_names: Sequence[str] = None, + expected_field_names: Optional[Sequence[str]] = None, ) -> csv.DictReader: """ Create a CSV dict reader with custom options. diff --git a/cl_sii/libs/mm_utils.py b/cl_sii/libs/mm_utils.py index 33a78fdd..67c3126a 100644 --- a/cl_sii/libs/mm_utils.py +++ b/cl_sii/libs/mm_utils.py @@ -1,5 +1,7 @@ +from __future__ import annotations + from datetime import date, datetime -from typing import Any, Union +from typing import Any, Mapping, Optional, Union import marshmallow import marshmallow.fields @@ -22,10 +24,6 @@ def validate_no_unexpected_input_fields( Usage:: class MySchema(marshmallow.Schema): - - class Meta: - strict = True - folio = marshmallow.fields.Integer() @marshmallow.validates_schema(pass_original=True) @@ -36,7 +34,7 @@ def validate_schema(self, data: dict, original_data: dict) -> None: # Original inspiration from # https://marshmallow.readthedocs.io/en/2.x-line/extending.html#validating-original-input-data fields_name_or_load_from = { - field.name if field.load_from is None else field.load_from + field.name if field.data_key is None else field.data_key for field_key, field in schema.fields.items() } unexpected_input_fields = set(original_data) - fields_name_or_load_from @@ -83,7 +81,7 @@ class CustomMarshmallowDateField(marshmallow.fields.Field): 'format': '"{input}" cannot be formatted as a date.', } - def __init__(self, format: str = None, **kwargs: Any) -> None: + def __init__(self, format: Optional[str] = None, **kwargs: Any) -> None: """Constructor. :param format: Either ``"iso"`` (for ISO-8601) or a date format str. @@ -98,11 +96,13 @@ def __init__(self, format: str = None, **kwargs: Any) -> None: # TODO: for 'marshmallow 3', rename 'dateformat' to 'datetimeformat'. self.dateformat = format - def _add_to_schema(self, field_name: str, schema: marshmallow.Schema) -> None: - super()._add_to_schema(field_name, schema) + def _bind_to_schema(self, field_name: str, schema: marshmallow.Schema) -> None: + super()._bind_to_schema(field_name, schema) self.dateformat = self.dateformat or schema.opts.dateformat - def _serialize(self, value: date, attr: str, obj: object) -> Union[str, None]: + def _serialize( + self, value: date, attr: str | None, obj: object, **kwargs: Any + ) -> Union[str, None]: if value is None: return None self.dateformat = self.dateformat or self.DEFAULT_FORMAT @@ -110,29 +110,31 @@ def _serialize(self, value: date, attr: str, obj: object) -> Union[str, None]: if format_func: try: date_str = format_func(value) - except (AttributeError, ValueError): - self.fail('format', input=value) + except (AttributeError, ValueError) as exc: + raise self.make_error('format', input=value) from exc else: date_str = value.strftime(self.dateformat) return date_str - def _deserialize(self, value: str, attr: str, data: dict) -> date: + def _deserialize( + self, value: str, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any + ) -> date: if not value: # Falsy values, e.g. '', None, [] are not valid - self.fail('invalid') + raise self.make_error('invalid') self.dateformat = self.dateformat or self.DEFAULT_FORMAT func = self.DATEFORMAT_DESERIALIZATION_FUNCS.get(self.dateformat) if func: try: date_value = func(value) # type: date - except (TypeError, AttributeError, ValueError): - self.fail('invalid') + except (TypeError, AttributeError, ValueError) as exc: + raise self.make_error('invalid') from exc elif self.dateformat: try: date_value = datetime.strptime(value, self.dateformat).date() - except (TypeError, AttributeError, ValueError): - self.fail('invalid') + except (TypeError, AttributeError, ValueError) as exc: + raise self.make_error('invalid') from exc else: - self.fail('invalid') + raise self.make_error('invalid') return date_value diff --git a/cl_sii/libs/rows_processing.py b/cl_sii/libs/rows_processing.py index 459b4bab..082d4395 100644 --- a/cl_sii/libs/rows_processing.py +++ b/cl_sii/libs/rows_processing.py @@ -1,6 +1,6 @@ import csv import logging -from typing import Dict, Iterable, Sequence, Tuple +from typing import Dict, Iterable, Optional, Sequence, Tuple import marshmallow @@ -24,8 +24,8 @@ def csv_rows_mm_deserialization_iterator( csv_reader: csv.DictReader, row_schema: marshmallow.Schema, n_rows_offset: int = 0, - max_n_rows: int = None, - fields_to_remove_names: Sequence[str] = None, + max_n_rows: Optional[int] = None, + fields_to_remove_names: Optional[Sequence[str]] = None, ) -> Iterable[Tuple[int, Dict[str, object], Dict[str, object], dict]]: """ Marshmallow deserialization iterator over CSV rows. @@ -78,8 +78,8 @@ def rows_mm_deserialization_iterator( rows_iterator: Iterable[Dict[str, object]], row_schema: marshmallow.Schema, n_rows_offset: int = 0, - max_n_rows: int = None, - fields_to_remove_names: Sequence[str] = None, + max_n_rows: Optional[int] = None, + fields_to_remove_names: Optional[Sequence[str]] = None, ) -> Iterable[Tuple[int, Dict[str, object], Dict[str, object], dict]]: """ Marshmallow deserialization iterator. @@ -122,36 +122,12 @@ def rows_mm_deserialization_iterator( row_data.pop(_field_name, None) try: - mm_result: marshmallow.UnmarshalResult = row_schema.load(row_data) - deserialized_row_data: dict = mm_result.data + deserialized_row_data: dict = row_schema.load(row_data) raised_validation_errors: dict = {} - returned_validation_errors: dict = mm_result.errors except marshmallow.ValidationError as exc: deserialized_row_data = {} raised_validation_errors = dict(exc.normalized_messages()) - returned_validation_errors = {} validation_errors = raised_validation_errors - if returned_validation_errors: - if row_schema.strict: - # 'marshmallow.schema.BaseSchema': - # > :param bool strict: If `True`, raise errors if invalid data are passed in - # > instead of failing silently and storing the errors. - logger.error( - "Marshmallow schema is 'strict' but validation errors were returned by " - "method 'load' ('UnmarshalResult.errors') instead of being raised. " - "Errors: %s", - repr(returned_validation_errors), - ) - if raised_validation_errors: - logger.fatal( - "Programming error: either returned or raised validation errors " - "(depending on 'strict') but never both. " - "Returned errors: %s. Raised errors: %s", - repr(returned_validation_errors), - repr(raised_validation_errors), - ) - - validation_errors.update(returned_validation_errors) yield row_ix, row_data, deserialized_row_data, validation_errors diff --git a/cl_sii/libs/tz_utils.py b/cl_sii/libs/tz_utils.py index 3ba84e82..0fe59009 100644 --- a/cl_sii/libs/tz_utils.py +++ b/cl_sii/libs/tz_utils.py @@ -11,7 +11,7 @@ """ from datetime import datetime -from typing import Union +from typing import Optional, Union import pytz import pytz.tzinfo @@ -85,7 +85,7 @@ def convert_naive_dt_to_tz_aware(dt: datetime, tz: PytzTimezone) -> datetime: return dt_tz_aware -def convert_tz_aware_dt_to_naive(dt: datetime, tz: PytzTimezone = None) -> datetime: +def convert_tz_aware_dt_to_naive(dt: datetime, tz: Optional[PytzTimezone] = None) -> datetime: """ Convert a timezone-aware datetime object to an offset-naive one. diff --git a/cl_sii/libs/xml_utils.py b/cl_sii/libs/xml_utils.py index 49ec992a..f0728b4f 100644 --- a/cl_sii/libs/xml_utils.py +++ b/cl_sii/libs/xml_utils.py @@ -362,7 +362,7 @@ def write_xml_doc(xml_doc: XmlElement, output: IO[bytes]) -> None: def verify_xml_signature( xml_doc: XmlElement, - trusted_x509_cert: Union[crypto_utils.X509Cert, crypto_utils._X509CertOpenSsl] = None, + trusted_x509_cert: Optional[Union[crypto_utils.X509Cert, crypto_utils._X509CertOpenSsl]] = None, xml_verifier: Optional[signxml.XMLVerifier] = None, xml_verifier_supports_multiple_signatures: bool = False, ) -> Tuple[bytes, XmlElementTree, XmlElementTree]: diff --git a/cl_sii/rcv/parse_csv.py b/cl_sii/rcv/parse_csv.py index 7f6ca0b9..e17f0176 100644 --- a/cl_sii/rcv/parse_csv.py +++ b/cl_sii/rcv/parse_csv.py @@ -7,7 +7,7 @@ import csv import logging from datetime import date, datetime -from typing import Callable, Dict, Iterable, Optional, Sequence, Tuple +from typing import Any, Callable, Dict, Iterable, Optional, Sequence, Tuple import marshmallow @@ -87,7 +87,7 @@ def parse_rcv_venta_csv_file( rut: Rut, input_file_path: str, n_rows_offset: int = 0, - max_n_rows: int = None, + max_n_rows: Optional[int] = None, ) -> Iterable[Tuple[Optional[RvDetalleEntry], int, Dict[str, object], Dict[str, object]]]: """ Parse entries from an RV ("Registro de Ventas") (CSV file). @@ -199,7 +199,7 @@ def parse_rcv_compra_registro_csv_file( rut: Rut, input_file_path: str, n_rows_offset: int = 0, - max_n_rows: int = None, + max_n_rows: Optional[int] = None, ) -> Iterable[Tuple[Optional[RcRegistroDetalleEntry], int, Dict[str, object], Dict[str, object]]]: """ Parse entries from an RC ("Registro de Compras") / "registro" (CSV file). @@ -280,7 +280,7 @@ def parse_rcv_compra_no_incluir_csv_file( rut: Rut, input_file_path: str, n_rows_offset: int = 0, - max_n_rows: int = None, + max_n_rows: Optional[int] = None, ) -> Iterable[Tuple[Optional[RcNoIncluirDetalleEntry], int, Dict[str, object], Dict[str, object]]]: """ Parse entries from an RC ("Registro de Compras") / "no incluir" (CSV file). @@ -355,7 +355,7 @@ def parse_rcv_compra_reclamado_csv_file( rut: Rut, input_file_path: str, n_rows_offset: int = 0, - max_n_rows: int = None, + max_n_rows: Optional[int] = None, ) -> Iterable[Tuple[Optional[RcReclamadoDetalleEntry], int, Dict[str, object], Dict[str, object]]]: """ Parse entries from an RC ("Registro de Compras") / "reclamado" (CSV file). @@ -430,7 +430,7 @@ def parse_rcv_compra_pendiente_csv_file( rut: Rut, input_file_path: str, n_rows_offset: int = 0, - max_n_rows: int = None, + max_n_rows: Optional[int] = None, ) -> Iterable[Tuple[Optional[RcPendienteDetalleEntry], int, Dict[str, object], Dict[str, object]]]: """ Parse entries from an RC ("Registro de Compras") / "pendiente" (CSV file). @@ -507,7 +507,7 @@ def parse_rcv_compra_pendiente_csv_file( class _RcvCsvRowSchemaBase(marshmallow.Schema): @marshmallow.validates_schema(pass_original=True) - def validate_schema(self, data: dict, original_data: dict) -> None: + def validate_schema(self, data: dict, original_data: dict, **kwargs: Any) -> None: mm_utils.validate_no_unexpected_input_fields(self, data, original_data) # @marshmallow.validates('field_x') @@ -524,37 +524,34 @@ class RcvVentaCsvRowSchema(_RcvCsvRowSchemaBase): FIELD_FECHA_ACUSE_DT_TZ = SII_OFFICIAL_TZ FIELD_FECHA_RECLAMO_DT_TZ = SII_OFFICIAL_TZ - class Meta: - strict = True - ########################################################################### # basic fields ########################################################################### tipo_docto = mm_fields.RcvTipoDoctoField( required=True, - load_from='Tipo Doc', + data_key='Tipo Doc', ) folio = marshmallow.fields.Integer( required=True, - load_from='Folio', + data_key='Folio', ) fecha_emision_date = mm_utils.CustomMarshmallowDateField( format='%d/%m/%Y', # e.g. '22/10/2018' required=True, - load_from='Fecha Docto', + data_key='Fecha Docto', ) receptor_rut = mm_fields.RutField( required=True, - load_from='Rut cliente', + data_key='Rut cliente', ) monto_total = marshmallow.fields.Integer( required=True, - load_from='Monto total', + data_key='Monto total', ) receptor_razon_social = marshmallow.fields.String( required=True, - load_from='Razon Social', + data_key='Razon Social', ) ########################################################################### @@ -572,23 +569,23 @@ class Meta: fecha_recepcion_dt = marshmallow.fields.DateTime( format='%d/%m/%Y %H:%M:%S', # e.g. '23/10/2018 01:54:13' required=True, - load_from='Fecha Recepcion', + data_key='Fecha Recepcion', ) fecha_acuse_dt = marshmallow.fields.DateTime( format='%d/%m/%Y %H:%M:%S', # e.g. '23/10/2018 01:54:13' required=False, allow_none=True, - load_from='Fecha Acuse Recibo', + data_key='Fecha Acuse Recibo', ) fecha_reclamo_dt = marshmallow.fields.DateTime( format='%d/%m/%Y %H:%M:%S', # e.g. '23/10/2018 01:54:13' required=False, allow_none=True, - load_from='Fecha Reclamo', + data_key='Fecha Reclamo', ) @marshmallow.pre_load - def preprocess(self, in_data: dict) -> dict: + def preprocess(self, in_data: dict, **kwargs: Any) -> dict: # note: required fields checks are run later on automatically thus we may not assume that # values of required fields (`required=True`) exist. @@ -606,7 +603,7 @@ def preprocess(self, in_data: dict) -> dict: return in_data @marshmallow.post_load - def postprocess(self, data: dict) -> dict: + def postprocess(self, data: dict, **kwargs: Any) -> dict: # >>> data['fecha_recepcion_dt'].isoformat() # '2018-10-23T01:54:13' data['fecha_recepcion_dt'] = tz_utils.convert_naive_dt_to_tz_aware( @@ -673,37 +670,34 @@ class RcvCompraRegistroCsvRowSchema(_RcvCsvRowSchemaBase): FIELD_FECHA_RECEPCION_DT_TZ = SII_OFFICIAL_TZ FIELD_FECHA_ACUSE_DT_TZ = SII_OFFICIAL_TZ - class Meta: - strict = True - ########################################################################### # basic fields ########################################################################### emisor_rut = mm_fields.RutField( required=True, - load_from='RUT Proveedor', + data_key='RUT Proveedor', ) tipo_docto = mm_fields.RcvTipoDoctoField( required=True, - load_from='Tipo Doc', + data_key='Tipo Doc', ) folio = marshmallow.fields.Integer( required=True, - load_from='Folio', + data_key='Folio', ) fecha_emision_date = mm_utils.CustomMarshmallowDateField( format='%d/%m/%Y', # e.g. '22/10/2018' required=True, - load_from='Fecha Docto', + data_key='Fecha Docto', ) monto_total = marshmallow.fields.Integer( required=True, - load_from='Monto Total', + data_key='Monto Total', ) emisor_razon_social = marshmallow.fields.String( required=True, - load_from='Razon Social', + data_key='Razon Social', ) ########################################################################### @@ -721,17 +715,17 @@ class Meta: fecha_recepcion_dt = marshmallow.fields.DateTime( format='%d/%m/%Y %H:%M:%S', # e.g. '23/10/2018 01:54:13' required=True, - load_from='Fecha Recepcion', + data_key='Fecha Recepcion', ) fecha_acuse_dt = marshmallow.fields.DateTime( format='%d/%m/%Y %H:%M:%S', # e.g. '23/10/2018 01:54:13' required=True, allow_none=True, - load_from='Fecha Acuse', + data_key='Fecha Acuse', ) @marshmallow.pre_load - def preprocess(self, in_data: dict) -> dict: + def preprocess(self, in_data: dict, **kwargs: Any) -> dict: # note: required fields checks are run later on automatically thus we may not assume that # values of required fields (`required=True`) exist. @@ -746,7 +740,7 @@ def preprocess(self, in_data: dict) -> dict: return in_data @marshmallow.post_load - def postprocess(self, data: dict) -> dict: + def postprocess(self, data: dict, **kwargs: Any) -> dict: # >>> data['fecha_recepcion_dt'].isoformat() # '2018-10-23T01:54:13' data['fecha_recepcion_dt'] = tz_utils.convert_naive_dt_to_tz_aware( @@ -840,37 +834,34 @@ class RcvCompraReclamadoCsvRowSchema(_RcvCsvRowSchemaBase): FIELD_FECHA_RECEPCION_DT_TZ = SII_OFFICIAL_TZ FIELD_FECHA_RECLAMO_DT_TZ = SII_OFFICIAL_TZ - class Meta: - strict = True - ########################################################################### # basic fields ########################################################################### emisor_rut = mm_fields.RutField( required=True, - load_from='RUT Proveedor', + data_key='RUT Proveedor', ) tipo_docto = mm_fields.RcvTipoDoctoField( required=True, - load_from='Tipo Doc', + data_key='Tipo Doc', ) folio = marshmallow.fields.Integer( required=True, - load_from='Folio', + data_key='Folio', ) fecha_emision_date = mm_utils.CustomMarshmallowDateField( format='%d/%m/%Y', # e.g. '22/10/2018' required=True, - load_from='Fecha Docto', + data_key='Fecha Docto', ) monto_total = marshmallow.fields.Integer( required=True, - load_from='Monto Total', + data_key='Monto Total', ) emisor_razon_social = marshmallow.fields.String( required=True, - load_from='Razon Social', + data_key='Razon Social', ) ########################################################################### @@ -888,7 +879,7 @@ class Meta: fecha_recepcion_dt = marshmallow.fields.DateTime( format='%d/%m/%Y %H:%M:%S', # e.g. '23/10/2018 01:54:13' required=True, - load_from='Fecha Recepcion', + data_key='Fecha Recepcion', ) fecha_reclamo_dt = marshmallow.fields.DateTime( # note: for some reason the rows with 'tipo_docto' equal to @@ -897,11 +888,11 @@ class Meta: format='%d/%m/%Y %H:%M:%S', # e.g. '23/10/2018 01:54:13' required=False, allow_none=True, - load_from='Fecha Reclamo', + data_key='Fecha Reclamo', ) @marshmallow.pre_load - def preprocess(self, in_data: dict) -> dict: + def preprocess(self, in_data: dict, **kwargs: Any) -> dict: # note: required fields checks are run later on automatically thus we may not assume that # values of required fields (`required=True`) exist. @@ -919,7 +910,7 @@ def preprocess(self, in_data: dict) -> dict: return in_data @marshmallow.post_load - def postprocess(self, data: dict) -> dict: + def postprocess(self, data: dict, **kwargs: Any) -> dict: # >>> data['fecha_recepcion_dt'].isoformat() # '2018-10-23T01:54:13' data['fecha_recepcion_dt'] = tz_utils.convert_naive_dt_to_tz_aware( @@ -980,37 +971,34 @@ class RcvCompraPendienteCsvRowSchema(_RcvCsvRowSchemaBase): FIELD_FECHA_RECEPCION_DT_TZ = SII_OFFICIAL_TZ FIELD_FECHA_ACUSE_DT_TZ = SII_OFFICIAL_TZ - class Meta: - strict = True - ########################################################################### # basic fields ########################################################################### emisor_rut = mm_fields.RutField( required=True, - load_from='RUT Proveedor', + data_key='RUT Proveedor', ) tipo_docto = mm_fields.RcvTipoDoctoField( required=True, - load_from='Tipo Doc', + data_key='Tipo Doc', ) folio = marshmallow.fields.Integer( required=True, - load_from='Folio', + data_key='Folio', ) fecha_emision_date = mm_utils.CustomMarshmallowDateField( format='%d/%m/%Y', # e.g. '22/10/2018' required=True, - load_from='Fecha Docto', + data_key='Fecha Docto', ) monto_total = marshmallow.fields.Integer( required=True, - load_from='Monto Total', + data_key='Monto Total', ) emisor_razon_social = marshmallow.fields.String( required=True, - load_from='Razon Social', + data_key='Razon Social', ) ########################################################################### @@ -1028,11 +1016,11 @@ class Meta: fecha_recepcion_dt = marshmallow.fields.DateTime( format='%d/%m/%Y %H:%M:%S', # e.g. '23/10/2018 01:54:13' required=True, - load_from='Fecha Recepcion', + data_key='Fecha Recepcion', ) @marshmallow.pre_load - def preprocess(self, in_data: dict) -> dict: + def preprocess(self, in_data: dict, **kwargs: Any) -> dict: # note: required fields checks are run later on automatically thus we may not assume that # values of required fields (`required=True`) exist. @@ -1047,7 +1035,7 @@ def preprocess(self, in_data: dict) -> dict: return in_data @marshmallow.post_load - def postprocess(self, data: dict) -> dict: + def postprocess(self, data: dict, **kwargs: Any) -> dict: # >>> data['fecha_recepcion_dt'].isoformat() # '2018-10-23T01:54:13' data['fecha_recepcion_dt'] = tz_utils.convert_naive_dt_to_tz_aware( @@ -1131,7 +1119,7 @@ def _parse_rcv_csv_file( fields_to_remove_names: Sequence[str], input_file_path: str, n_rows_offset: int, - max_n_rows: int = None, + max_n_rows: Optional[int] = None, ) -> Iterable[Tuple[Optional[RcvDetalleEntry], int, Dict[str, object], Dict[str, object]]]: """ Parse entries from an RC or RV (CSV file). diff --git a/mypy.ini b/mypy.ini new file mode 100644 index 00000000..d551da5f --- /dev/null +++ b/mypy.ini @@ -0,0 +1,38 @@ +[mypy] +python_version = 3.7 +platform = linux +files = + cl_sii, + scripts +plugins = + pydantic.mypy + +follow_imports = normal +ignore_missing_imports = False +strict_optional = True +disallow_untyped_defs = True +check_untyped_defs = True +warn_return_any = True + +show_column_numbers = True +show_error_codes = True +show_error_context = True +error_summary = True + +[mypy-defusedxml.*] +ignore_missing_imports = True + +[mypy-django.*] +ignore_missing_imports = True + +[mypy-lxml.*] +ignore_missing_imports = True + +[mypy-rest_framework.*] +ignore_missing_imports = True + +[pydantic-mypy] +init_forbid_extra = True +init_typed = True +warn_required_dynamic_aliases = True +warn_untyped_fields = True diff --git a/requirements-dev.in b/requirements-dev.in index 1c7f3742..b4044492 100644 --- a/requirements-dev.in +++ b/requirements-dev.in @@ -4,13 +4,16 @@ -c requirements.txt -black==22.10.0 +black==22.12.0 bumpversion==0.5.3 codecov==2.1.12 -coverage==6.5.0 +coverage==7.1.0 flake8==4.0.1 isort==5.10.1 -mypy==0.982 +mypy==0.991 tox==3.25.1 twine==3.1.1 +types-jsonschema==4.17.0.3 +types-pyOpenSSL==23.0.0.2 +types-pytz==2022.7.1.0 wheel==0.38.4 diff --git a/requirements-dev.txt b/requirements-dev.txt index 7964f465..e693f7a4 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ # appdirs==1.4.4 # via virtualenv -black==22.10.0 +black==22.12.0 # via -r requirements-dev.in bleach==5.0.1 # via readme-renderer @@ -26,14 +26,15 @@ click==8.0.3 # via black codecov==2.1.12 # via -r requirements-dev.in -coverage==6.5.0 +coverage==7.1.0 # via # -r requirements-dev.in # codecov -cryptography==38.0.4 +cryptography==39.0.1 # via # -c requirements.txt # secretstorage + # types-pyopenssl distlib==0.3.6 # via virtualenv docutils==0.19 @@ -66,14 +67,16 @@ keyring==21.4.0 # via twine mccabe==0.6.1 # via flake8 -mypy==0.982 +mypy==0.991 # via -r requirements-dev.in mypy-extensions==0.4.3 # via # black # mypy -packaging==20.4 - # via tox +packaging==23.0 + # via + # -c requirements.txt + # tox pathspec==0.9.0 # via black pkginfo==1.8.3 @@ -94,8 +97,6 @@ pyflakes==2.4.0 # via flake8 pygments==2.13.0 # via readme-renderer -pyparsing==3.0.9 - # via packaging readme-renderer==35.0 # via twine requests==2.25.1 @@ -110,7 +111,6 @@ secretstorage==3.3.3 six==1.16.0 # via # bleach - # packaging # tox # virtualenv toml==0.10.2 @@ -129,6 +129,12 @@ typed-ast==1.5.4 # via # black # mypy +types-jsonschema==4.17.0.3 + # via -r requirements-dev.in +types-pyopenssl==23.0.0.2 + # via -r requirements-dev.in +types-pytz==2022.7.1.0 + # via -r requirements-dev.in typing-extensions==4.3.0 # via # -c requirements.txt diff --git a/requirements.in b/requirements.in index 56fcfafc..30db7fa5 100644 --- a/requirements.in +++ b/requirements.in @@ -5,15 +5,15 @@ # Note: To install a package from a Git VCS repository, see the following example: # git+https://github.com/example/example.git@example-vcs-ref#egg=example-pkg[foo,bar]==1.42.3 -cryptography==38.0.4 +cryptography==39.0.1 defusedxml==0.7.1 Django>=2.2.24 djangorestframework>=3.10.3,<3.15 importlib-metadata==1.6.0 -jsonschema==4.16.0 -lxml==4.9.1 -marshmallow==2.21.0 -pydantic==1.10.2 -pyOpenSSL==22.0.0 -pytz==2022.6 +jsonschema==4.17.3 +lxml==4.9.2 +marshmallow==3.19.0 +pydantic==1.10.4 +pyOpenSSL==23.0.0 +pytz==2022.7.1 signxml==2.10.1 diff --git a/requirements.txt b/requirements.txt index ae25ef16..c2df4346 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,14 +12,14 @@ certifi==2022.6.15 # via signxml cffi==1.15.1 # via cryptography -cryptography==38.0.4 +cryptography==39.0.1 # via # -r requirements.in # pyopenssl # signxml defusedxml==0.7.1 # via -r requirements.in -django==3.2.16 +django==3.2.17 # via # -r requirements.in # djangorestframework @@ -29,29 +29,31 @@ importlib-metadata==1.6.0 # via # -r requirements.in # jsonschema -importlib-resources==5.10.0 +importlib-resources==5.10.2 # via jsonschema -jsonschema==4.16.0 +jsonschema==4.17.3 # via -r requirements.in -lxml==4.9.1 +lxml==4.9.2 # via # -r requirements.in # signxml -marshmallow==2.21.0 +marshmallow==3.19.0 # via -r requirements.in +packaging==23.0 + # via marshmallow pkgutil-resolve-name==1.3.10 # via jsonschema pycparser==2.20 # via cffi -pydantic==1.10.2 +pydantic==1.10.4 # via -r requirements.in -pyopenssl==22.0.0 +pyopenssl==23.0.0 # via # -r requirements.in # signxml pyrsistent==0.17.3 # via jsonschema -pytz==2022.6 +pytz==2022.7.1 # via # -r requirements.in # django diff --git a/setup.cfg b/setup.cfg index 97c770b7..45faa047 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,60 +16,6 @@ show_missing = True [coverage:html] directory = test-reports/coverage/html -[mypy] -python_version = 3.7 -platform = linux -plugins = - pydantic.mypy - -follow_imports = normal -ignore_missing_imports = False -strict_optional = True -disallow_untyped_defs = True -check_untyped_defs = True -warn_return_any = True - -show_column_numbers = True -show_error_codes = True -show_error_context = True -error_summary = True - -[mypy-cryptography.*] -ignore_missing_imports = True - -[mypy-defusedxml.*] -ignore_missing_imports = True - -[mypy-django.*] -ignore_missing_imports = True - -[mypy-jsonschema] -ignore_missing_imports = True - -[mypy-lxml.*] -ignore_missing_imports = True - -[mypy-marshmallow.*] -ignore_missing_imports = True - -[mypy-OpenSSL.*] -ignore_missing_imports = True - -[mypy-signxml.*] -ignore_missing_imports = True - -[mypy-rest_framework.*] -ignore_missing_imports = True - -[mypy-pytz.*] -ignore_missing_imports = True - -[pydantic-mypy] -init_forbid_extra = True -init_typed = True -warn_required_dynamic_aliases = True -warn_untyped_fields = True - [flake8] ignore = # W503 line break before binary operator diff --git a/setup.py b/setup.py index b9a26902..22039c43 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def get_version(*file_paths: Sequence[str]) -> str: 'defusedxml>=0.6.0,<1', 'jsonschema>=3.1.1', 'lxml>=4.6.5,<5', - 'marshmallow>=2.19.2,<3', + 'marshmallow>=3,<4', 'pydantic>=1.6.2,!=1.7.*,!=1.8.*,!=1.9.*', 'pyOpenSSL>=22.0.0', 'pytz>=2019.3', diff --git a/tests/test_dte_constants.py b/tests/test_dte_constants.py index 1354b4ab..3c737b19 100644 --- a/tests/test_dte_constants.py +++ b/tests/test_dte_constants.py @@ -5,7 +5,7 @@ class TipoDteTest(unittest.TestCase): - def test_members(self): + def test_members(self) -> None: self.assertSetEqual( {x for x in TipoDte}, { @@ -19,7 +19,7 @@ def test_members(self): }, ) - def test_FACTURA_ELECTRONICA(self): + def test_FACTURA_ELECTRONICA(self) -> None: value = TipoDte.FACTURA_ELECTRONICA self.assertEqual(value.name, 'FACTURA_ELECTRONICA') @@ -37,7 +37,7 @@ def test_FACTURA_ELECTRONICA(self): for (result, expected) in assertions: self.assertEqual(result, expected) - def test_FACTURA_NO_AFECTA_O_EXENTA_ELECTRONICA(self): + def test_FACTURA_NO_AFECTA_O_EXENTA_ELECTRONICA(self) -> None: value = TipoDte.FACTURA_NO_AFECTA_O_EXENTA_ELECTRONICA self.assertEqual(value.name, 'FACTURA_NO_AFECTA_O_EXENTA_ELECTRONICA') @@ -55,7 +55,7 @@ def test_FACTURA_NO_AFECTA_O_EXENTA_ELECTRONICA(self): for (result, expected) in assertions: self.assertTrue(result is expected) - def test_LIQUIDACION_FACTURA_ELECTRONICA(self): + def test_LIQUIDACION_FACTURA_ELECTRONICA(self) -> None: value = TipoDte.LIQUIDACION_FACTURA_ELECTRONICA self.assertEqual(value.name, 'LIQUIDACION_FACTURA_ELECTRONICA') @@ -73,7 +73,7 @@ def test_LIQUIDACION_FACTURA_ELECTRONICA(self): for (result, expected) in assertions: self.assertEqual(result, expected) - def test_FACTURA_COMPRA_ELECTRONICA(self): + def test_FACTURA_COMPRA_ELECTRONICA(self) -> None: value = TipoDte.FACTURA_COMPRA_ELECTRONICA self.assertEqual(value.name, 'FACTURA_COMPRA_ELECTRONICA') @@ -91,7 +91,7 @@ def test_FACTURA_COMPRA_ELECTRONICA(self): for (result, expected) in assertions: self.assertTrue(result is expected) - def test_GUIA_DESPACHO_ELECTRONICA(self): + def test_GUIA_DESPACHO_ELECTRONICA(self) -> None: value = TipoDte.GUIA_DESPACHO_ELECTRONICA self.assertEqual(value.name, 'GUIA_DESPACHO_ELECTRONICA') @@ -109,7 +109,7 @@ def test_GUIA_DESPACHO_ELECTRONICA(self): for (result, expected) in assertions: self.assertTrue(result is expected) - def test_NOTA_DEBITO_ELECTRONICA(self): + def test_NOTA_DEBITO_ELECTRONICA(self) -> None: value = TipoDte.NOTA_DEBITO_ELECTRONICA self.assertEqual(value.name, 'NOTA_DEBITO_ELECTRONICA') @@ -127,7 +127,7 @@ def test_NOTA_DEBITO_ELECTRONICA(self): for (result, expected) in assertions: self.assertTrue(result is expected) - def test_NOTA_CREDITO_ELECTRONICA(self): + def test_NOTA_CREDITO_ELECTRONICA(self) -> None: value = TipoDte.NOTA_CREDITO_ELECTRONICA self.assertEqual(value.name, 'NOTA_CREDITO_ELECTRONICA') diff --git a/tests/test_dte_data_models.py b/tests/test_dte_data_models.py index b056be2d..03314600 100644 --- a/tests/test_dte_data_models.py +++ b/tests/test_dte_data_models.py @@ -317,7 +317,7 @@ def setUp(self) -> None: receptor_email=None, ) - def test_constants_match(self): + def test_constants_match(self) -> None: self.assertEqual( DteXmlData.DATETIME_FIELDS_TZ, DteDataL2.DATETIME_FIELDS_TZ, @@ -775,7 +775,7 @@ def setUp(self) -> None: receptor_email=None, ) - def test_constants_match(self): + def test_constants_match(self) -> None: self.assertEqual( DteXmlData.DATETIME_FIELDS_TZ, DteDataL2.DATETIME_FIELDS_TZ, diff --git a/tests/test_dte_parse.py b/tests/test_dte_parse.py index 1bd838a9..adf65b76 100644 --- a/tests/test_dte_parse.py +++ b/tests/test_dte_parse.py @@ -432,7 +432,7 @@ def setUpClass(cls) -> None: ) ) - def test_data(self): + def test_data(self) -> None: self.assertEqual( self._TEST_DTE_1_SIGNATURE_VALUE, b'~\xc6\x0f\xe6\x9f\xe55\xfa\x1f\x03?\x0f9(k&:\x97t\x14\xcd6\xdb\xef\xe3\xf4\xd6' diff --git a/tests/test_extras_mm_fields.py b/tests/test_extras_mm_fields.py index 489cdc6f..23b58333 100644 --- a/tests/test_extras_mm_fields.py +++ b/tests/test_extras_mm_fields.py @@ -26,25 +26,28 @@ class MyBadObj: def __init__(self, some_field: int) -> None: self.some_field = some_field - class MyMmSchema(marshmallow.Schema): - class Meta: - strict = False + class DumpMyMmSchema(marshmallow.Schema): + emisor_rut = RutField( + required=True, + ) + other_field = marshmallow.fields.Integer( + required=False, + ) + class LoadMyMmSchema(marshmallow.Schema): emisor_rut = RutField( required=True, - load_from='RUT of Emisor', + data_key='RUT of Emisor', ) other_field = marshmallow.fields.Integer( required=False, ) class MyMmSchemaStrict(marshmallow.Schema): - class Meta: - strict = True emisor_rut = RutField( required=True, - load_from='RUT of Emisor', + data_key='RUT of Emisor', ) other_field = marshmallow.fields.Integer( required=False, @@ -52,108 +55,107 @@ class Meta: self.MyObj = MyObj self.MyBadObj = MyBadObj - self.MyMmSchema = MyMmSchema + self.LoadMyMmSchema = LoadMyMmSchema + self.DumpMyMmSchema = DumpMyMmSchema self.MyMmSchemaStrict = MyMmSchemaStrict def test_load_ok_valid(self) -> None: - schema = self.MyMmSchema() + schema = self.LoadMyMmSchema() data_valid_1 = {'RUT of Emisor': '1-1'} data_valid_2 = {'RUT of Emisor': Rut('1-1')} data_valid_3 = {'RUT of Emisor': ' 1.111.111-k \t '} result = schema.load(data_valid_1) - self.assertDictEqual(dict(result.data), {'emisor_rut': Rut('1-1')}) - self.assertDictEqual(dict(result.errors), {}) + self.assertDictEqual(dict(result), {'emisor_rut': Rut('1-1')}) result = schema.load(data_valid_2) - self.assertDictEqual(dict(result.data), {'emisor_rut': Rut('1-1')}) - self.assertDictEqual(dict(result.errors), {}) + self.assertDictEqual(dict(result), {'emisor_rut': Rut('1-1')}) result = schema.load(data_valid_3) - self.assertDictEqual(dict(result.data), {'emisor_rut': Rut('1111111-K')}) - self.assertDictEqual(dict(result.errors), {}) + self.assertDictEqual(dict(result), {'emisor_rut': Rut('1111111-K')}) def test_dump_ok_valid(self) -> None: - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() obj_valid_1 = self.MyObj(emisor_rut=Rut('1-1')) obj_valid_2 = self.MyObj(emisor_rut=None) - data, errors = schema.dump(obj_valid_1) + data = schema.dump(obj_valid_1) self.assertDictEqual(data, {'emisor_rut': '1-1', 'other_field': None}) - self.assertDictEqual(errors, {}) - data, errors = schema.dump(obj_valid_2) + data = schema.dump(obj_valid_2) self.assertDictEqual(data, {'emisor_rut': None, 'other_field': None}) - self.assertDictEqual(errors, {}) def test_dump_ok_strange(self) -> None: # If the class of the object to be dumped has attributes that do not match at all the - # fields of the schema, there are no errors! Even if the schema has `strict = True` set. + # fields of the schema, there are no errors! - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() schema_strict = self.MyMmSchemaStrict() obj_valid_1 = self.MyBadObj(some_field=123) obj_valid_2 = self.MyBadObj(some_field=None) - data, errors = schema.dump(obj_valid_1) - self.assertEqual((data, errors), ({}, {})) + data = schema.dump(obj_valid_1) + self.assertEqual(data, {}) - data, errors = schema_strict.dump(obj_valid_1) - self.assertEqual((data, errors), ({}, {})) + data = schema_strict.dump(obj_valid_1) + self.assertEqual(data, {}) - data, errors = schema.dump(obj_valid_2) - self.assertEqual((data, errors), ({}, {})) + data = schema.dump(obj_valid_2) + self.assertEqual(data, {}) - data, errors = schema_strict.dump(obj_valid_2) - self.assertEqual((data, errors), ({}, {})) + data = schema_strict.dump(obj_valid_2) + self.assertEqual(data, {}) def test_load_fail(self) -> None: - schema = self.MyMmSchema() + schema = self.LoadMyMmSchema() data_invalid_1 = {'RUT of Emisor': '123123123123'} data_invalid_2 = {'RUT of Emisor': 123} data_invalid_3 = {'RUT of Emisor': None} data_invalid_4 = {} - result = schema.load(data_invalid_1) - self.assertDictEqual(dict(result.data), {}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_1) self.assertDictEqual( - dict(result.errors), {'RUT of Emisor': ['Not a syntactically valid RUT.']} + cm.exception.messages, {'RUT of Emisor': ['Not a syntactically valid RUT.']} ) - result = schema.load(data_invalid_2) - self.assertDictEqual(dict(result.data), {}) - self.assertDictEqual(dict(result.errors), {'RUT of Emisor': ['Invalid type.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_2) + self.assertDictEqual(cm.exception.messages, {'RUT of Emisor': ['Invalid type.']}) - result = schema.load(data_invalid_3) - self.assertDictEqual(dict(result.data), {}) - self.assertDictEqual(dict(result.errors), {'RUT of Emisor': ['Field may not be null.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_3) + self.assertDictEqual(cm.exception.messages, {'RUT of Emisor': ['Field may not be null.']}) - result = schema.load(data_invalid_4) - self.assertDictEqual(dict(result.data), {}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_4) self.assertDictEqual( - dict(result.errors), {'RUT of Emisor': ['Missing data for required field.']} + cm.exception.messages, {'RUT of Emisor': ['Missing data for required field.']} ) def test_dump_fail(self) -> None: - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() obj_invalid_1 = self.MyObj(emisor_rut=20) obj_invalid_2 = self.MyObj(emisor_rut='123123123123') obj_invalid_3 = self.MyObj(emisor_rut='') - data, errors = schema.dump(obj_invalid_1) - self.assertDictEqual(errors, {'emisor_rut': ['Invalid type.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_1) + self.assertEqual(cm.exception.messages, ['Invalid type.']) - data, errors = schema.dump(obj_invalid_2) - self.assertDictEqual(errors, {'emisor_rut': ['Not a syntactically valid RUT.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_2) + self.assertEqual(cm.exception.messages, ['Not a syntactically valid RUT.']) - data, errors = schema.dump(obj_invalid_3) - self.assertDictEqual(errors, {'emisor_rut': ['Not a syntactically valid RUT.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_3) + self.assertEqual(cm.exception.messages, ['Not a syntactically valid RUT.']) class TipoDteFieldTest(unittest.TestCase): @@ -167,25 +169,27 @@ class MyBadObj: def __init__(self, some_field: int) -> None: self.some_field = some_field - class MyMmSchema(marshmallow.Schema): - class Meta: - strict = False + class LoadMyMmSchema(marshmallow.Schema): + tipo_dte = TipoDteField( + required=True, + data_key='source field name', + ) + other_field = marshmallow.fields.Integer( + required=False, + ) + class DumpMyMmSchema(marshmallow.Schema): tipo_dte = TipoDteField( required=True, - load_from='source field name', ) other_field = marshmallow.fields.Integer( required=False, ) class MyMmSchemaStrict(marshmallow.Schema): - class Meta: - strict = True - tipo_dte = TipoDteField( required=True, - load_from='source field name', + data_key='source field name', ) other_field = marshmallow.fields.Integer( required=False, @@ -193,93 +197,93 @@ class Meta: self.MyObj = MyObj self.MyBadObj = MyBadObj - self.MyMmSchema = MyMmSchema + self.LoadMyMmSchema = LoadMyMmSchema + self.DumpMyMmSchema = DumpMyMmSchema self.MyMmSchemaStrict = MyMmSchemaStrict def test_load_ok_valid(self) -> None: - schema = self.MyMmSchema() + schema = self.LoadMyMmSchema() data_valid_1 = {'source field name': 33} data_valid_2 = {'source field name': TipoDte(33)} data_valid_3 = {'source field name': ' 33 \t '} - result = schema.load(data_valid_1) - self.assertDictEqual(dict(result.data), {'tipo_dte': TipoDte(33)}) - self.assertDictEqual(dict(result.errors), {}) + data = schema.load(data_valid_1) + self.assertDictEqual(data, {'tipo_dte': TipoDte(33)}) - result = schema.load(data_valid_2) - self.assertDictEqual(dict(result.data), {'tipo_dte': TipoDte(33)}) - self.assertDictEqual(dict(result.errors), {}) + data = schema.load(data_valid_2) + self.assertDictEqual(data, {'tipo_dte': TipoDte(33)}) - result = schema.load(data_valid_3) - self.assertDictEqual(dict(result.data), {'tipo_dte': TipoDte(33)}) - self.assertDictEqual(dict(result.errors), {}) + data = schema.load(data_valid_3) + self.assertDictEqual(data, {'tipo_dte': TipoDte(33)}) def test_dump_ok_valid(self) -> None: - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() obj_valid_1 = self.MyObj(tipo_dte=TipoDte(33)) obj_valid_2 = self.MyObj(tipo_dte=None) - data, errors = schema.dump(obj_valid_1) + data = schema.dump(obj_valid_1) self.assertDictEqual(data, {'tipo_dte': 33, 'other_field': None}) - self.assertDictEqual(errors, {}) - data, errors = schema.dump(obj_valid_2) + data = schema.dump(obj_valid_2) self.assertDictEqual(data, {'tipo_dte': None, 'other_field': None}) - self.assertDictEqual(errors, {}) def test_dump_ok_strange(self) -> None: # If the class of the object to be dumped has attributes that do not match at all the - # fields of the schema, there are no errors! Even if the schema has `strict = True` set. + # fields of the schema, there are no errors! - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() schema_strict = self.MyMmSchemaStrict() obj_valid_1 = self.MyBadObj(some_field=123) obj_valid_2 = self.MyBadObj(some_field=None) - data, errors = schema.dump(obj_valid_1) - self.assertEqual((data, errors), ({}, {})) + data = schema.dump(obj_valid_1) + self.assertEqual(data, {}) - data, errors = schema_strict.dump(obj_valid_1) - self.assertEqual((data, errors), ({}, {})) + data = schema_strict.dump(obj_valid_1) + self.assertEqual(data, {}) - data, errors = schema.dump(obj_valid_2) - self.assertEqual((data, errors), ({}, {})) + data = schema.dump(obj_valid_2) + self.assertEqual(data, {}) - data, errors = schema_strict.dump(obj_valid_2) - self.assertEqual((data, errors), ({}, {})) + data = schema_strict.dump(obj_valid_2) + self.assertEqual(data, {}) def test_load_fail(self) -> None: - schema = self.MyMmSchema() + schema = self.LoadMyMmSchema() data_invalid_1 = {'source field name': '123'} data_invalid_2 = {'source field name': True} data_invalid_3 = {'source field name': None} data_invalid_4 = {} - result = schema.load(data_invalid_1) - self.assertDictEqual(dict(result.data), {}) - self.assertDictEqual(dict(result.errors), {'source field name': ['Not a valid Tipo DTE.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_1) + self.assertDictEqual( + cm.exception.messages, {'source field name': ['Not a valid Tipo DTE.']} + ) - result = schema.load(data_invalid_2) - self.assertDictEqual(dict(result.data), {}) - self.assertDictEqual(dict(result.errors), {'source field name': ['Invalid type.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_2) + self.assertDictEqual(cm.exception.messages, {'source field name': ['Invalid type.']}) - result = schema.load(data_invalid_3) - self.assertDictEqual(dict(result.data), {}) - self.assertDictEqual(dict(result.errors), {'source field name': ['Field may not be null.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_3) + self.assertDictEqual( + cm.exception.messages, {'source field name': ['Field may not be null.']} + ) - result = schema.load(data_invalid_4) - self.assertDictEqual(dict(result.data), {}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_4) self.assertDictEqual( - dict(result.errors), {'source field name': ['Missing data for required field.']} + cm.exception.messages, {'source field name': ['Missing data for required field.']} ) def test_dump_fail(self) -> None: - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() obj_invalid_1 = self.MyObj(tipo_dte=100) obj_invalid_2 = self.MyObj(tipo_dte=True) @@ -287,16 +291,25 @@ def test_dump_fail(self) -> None: obj_invalid_4 = self.MyObj(tipo_dte='') obj_invalid_5 = self.MyObj(tipo_dte=date(2018, 12, 23)) - data, errors = schema.dump(obj_invalid_1) - self.assertDictEqual(errors, {'tipo_dte': ['Not a valid Tipo DTE.']}) - data, errors = schema.dump(obj_invalid_2) - self.assertDictEqual(errors, {'tipo_dte': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_3) - self.assertDictEqual(errors, {'tipo_dte': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_4) - self.assertDictEqual(errors, {'tipo_dte': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_5) - self.assertDictEqual(errors, {'tipo_dte': ['Invalid type.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_1) + self.assertEqual(cm.exception.messages, ['Not a valid Tipo DTE.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_2) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_3) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_4) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_5) + self.assertEqual(cm.exception.messages, ['Invalid type.']) class RcvTipoDoctoFieldTest(unittest.TestCase): @@ -310,25 +323,27 @@ class MyBadObj: def __init__(self, some_field: int) -> None: self.some_field = some_field - class MyMmSchema(marshmallow.Schema): - class Meta: - strict = False + class DumpMyMmSchema(marshmallow.Schema): + tipo_docto = RcvTipoDoctoField( + required=True, + ) + other_field = marshmallow.fields.Integer( + required=False, + ) + class LoadMyMmSchema(marshmallow.Schema): tipo_docto = RcvTipoDoctoField( required=True, - load_from='source field name', + data_key='source field name', ) other_field = marshmallow.fields.Integer( required=False, ) class MyMmSchemaStrict(marshmallow.Schema): - class Meta: - strict = True - tipo_docto = RcvTipoDoctoField( required=True, - load_from='source field name', + data_key='source field name', ) other_field = marshmallow.fields.Integer( required=False, @@ -336,95 +351,93 @@ class Meta: self.MyObj = MyObj self.MyBadObj = MyBadObj - self.MyMmSchema = MyMmSchema + self.LoadMyMmSchema = LoadMyMmSchema + self.DumpMyMmSchema = DumpMyMmSchema self.MyMmSchemaStrict = MyMmSchemaStrict def test_load_ok_valid(self) -> None: - schema = self.MyMmSchema() + schema = self.LoadMyMmSchema() data_valid_1 = {'source field name': 33} data_valid_2 = {'source field name': RcvTipoDocto(33)} data_valid_3 = {'source field name': ' 33 \t '} result = schema.load(data_valid_1) - self.assertDictEqual(dict(result.data), {'tipo_docto': RcvTipoDocto(33)}) - self.assertDictEqual(dict(result.errors), {}) + self.assertDictEqual(dict(result), {'tipo_docto': RcvTipoDocto(33)}) result = schema.load(data_valid_2) - self.assertDictEqual(dict(result.data), {'tipo_docto': RcvTipoDocto(33)}) - self.assertDictEqual(dict(result.errors), {}) + self.assertDictEqual(dict(result), {'tipo_docto': RcvTipoDocto(33)}) result = schema.load(data_valid_3) - self.assertDictEqual(dict(result.data), {'tipo_docto': RcvTipoDocto(33)}) - self.assertDictEqual(dict(result.errors), {}) + self.assertDictEqual(dict(result), {'tipo_docto': RcvTipoDocto(33)}) def test_dump_ok_valid(self) -> None: - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() obj_valid_1 = self.MyObj(tipo_docto=RcvTipoDocto(33)) obj_valid_2 = self.MyObj(tipo_docto=None) - data, errors = schema.dump(obj_valid_1) + data = schema.dump(obj_valid_1) self.assertDictEqual(data, {'tipo_docto': 33, 'other_field': None}) - self.assertDictEqual(errors, {}) - data, errors = schema.dump(obj_valid_2) + data = schema.dump(obj_valid_2) self.assertDictEqual(data, {'tipo_docto': None, 'other_field': None}) - self.assertDictEqual(errors, {}) def test_dump_ok_strange(self) -> None: # If the class of the object to be dumped has attributes that do not match at all the - # fields of the schema, there are no errors! Even if the schema has `strict = True` set. + # fields of the schema, there are no errors! - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() schema_strict = self.MyMmSchemaStrict() obj_valid_1 = self.MyBadObj(some_field=123) obj_valid_2 = self.MyBadObj(some_field=None) - data, errors = schema.dump(obj_valid_1) - self.assertEqual((data, errors), ({}, {})) + data = schema.dump(obj_valid_1) + self.assertEqual(data, {}) - data, errors = schema_strict.dump(obj_valid_1) - self.assertEqual((data, errors), ({}, {})) + data = schema_strict.dump(obj_valid_1) + self.assertEqual(data, {}) - data, errors = schema.dump(obj_valid_2) - self.assertEqual((data, errors), ({}, {})) + data = schema.dump(obj_valid_2) + self.assertEqual(data, {}) - data, errors = schema_strict.dump(obj_valid_2) - self.assertEqual((data, errors), ({}, {})) + data = schema_strict.dump(obj_valid_2) + self.assertEqual(data, {}) def test_load_fail(self) -> None: - schema = self.MyMmSchema() + schema = self.LoadMyMmSchema() data_invalid_1 = {'source field name': '123'} data_invalid_2 = {'source field name': True} data_invalid_3 = {'source field name': None} data_invalid_4 = {} - result = schema.load(data_invalid_1) - self.assertDictEqual(dict(result.data), {}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_1) self.assertDictEqual( - dict(result.errors), {'source field name': ["Not a valid RCV's Tipo de Documento."]} + cm.exception.messages, {'source field name': ["Not a valid RCV's Tipo de Documento."]} ) - result = schema.load(data_invalid_2) - self.assertDictEqual(dict(result.data), {}) - self.assertDictEqual(dict(result.errors), {'source field name': ['Invalid type.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_2) + self.assertDictEqual(cm.exception.messages, {'source field name': ['Invalid type.']}) - result = schema.load(data_invalid_3) - self.assertDictEqual(dict(result.data), {}) - self.assertDictEqual(dict(result.errors), {'source field name': ['Field may not be null.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_3) + self.assertDictEqual( + cm.exception.messages, {'source field name': ['Field may not be null.']} + ) - result = schema.load(data_invalid_4) - self.assertDictEqual(dict(result.data), {}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_4) self.assertDictEqual( - dict(result.errors), {'source field name': ['Missing data for required field.']} + cm.exception.messages, {'source field name': ['Missing data for required field.']} ) def test_dump_fail(self) -> None: - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() obj_invalid_1 = self.MyObj(tipo_docto=100) obj_invalid_2 = self.MyObj(tipo_docto=True) @@ -432,16 +445,25 @@ def test_dump_fail(self) -> None: obj_invalid_4 = self.MyObj(tipo_docto='') obj_invalid_5 = self.MyObj(tipo_docto=date(2018, 12, 23)) - data, errors = schema.dump(obj_invalid_1) - self.assertDictEqual(errors, {'tipo_docto': ["Not a valid RCV's Tipo de Documento."]}) - data, errors = schema.dump(obj_invalid_2) - self.assertDictEqual(errors, {'tipo_docto': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_3) - self.assertDictEqual(errors, {'tipo_docto': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_4) - self.assertDictEqual(errors, {'tipo_docto': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_5) - self.assertDictEqual(errors, {'tipo_docto': ['Invalid type.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_1) + self.assertEqual(cm.exception.messages, ["Not a valid RCV's Tipo de Documento."]) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_2) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_3) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_4) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_5) + self.assertEqual(cm.exception.messages, ['Invalid type.']) class RcvPeriodoTributarioFieldTest(unittest.TestCase): @@ -459,25 +481,27 @@ class MyBadObj: def __init__(self, some_field: int) -> None: self.some_field = some_field - class MyMmSchema(marshmallow.Schema): - class Meta: - strict = False + class DumpMyMmSchema(marshmallow.Schema): + periodo_tributario = RcvPeriodoTributarioField( + required=True, + ) + other_field = marshmallow.fields.Integer( + required=False, + ) + class LoadMyMmSchema(marshmallow.Schema): periodo_tributario = RcvPeriodoTributarioField( required=True, - load_from='source field name', + data_key='source field name', ) other_field = marshmallow.fields.Integer( required=False, ) class MyMmSchemaStrict(marshmallow.Schema): - class Meta: - strict = True - periodo_tributario = RcvPeriodoTributarioField( required=True, - load_from='source field name', + data_key='source field name', ) other_field = marshmallow.fields.Integer( required=False, @@ -485,11 +509,12 @@ class Meta: self.MyObj = MyObj self.MyBadObj = MyBadObj - self.MyMmSchema = MyMmSchema + self.DumpMyMmSchema = DumpMyMmSchema + self.LoadMyMmSchema = LoadMyMmSchema self.MyMmSchemaStrict = MyMmSchemaStrict def test_load_ok_valid(self) -> None: - schema = self.MyMmSchema() + schema = self.LoadMyMmSchema() data_valid_1 = {'source field name': '2019-12'} data_valid_2 = {'source field name': RcvPeriodoTributario(year=2019, month=12)} @@ -498,75 +523,68 @@ def test_load_ok_valid(self) -> None: result = schema.load(data_valid_1) self.assertEqual( - dict(result.data), + dict(result), {'periodo_tributario': RcvPeriodoTributario(year=2019, month=12)}, ) - self.assertEqual(dict(result.errors), {}) result = schema.load(data_valid_2) self.assertEqual( - dict(result.data), + dict(result), {'periodo_tributario': RcvPeriodoTributario(year=2019, month=12)}, ) - self.assertEqual(dict(result.errors), {}) result = schema.load(data_valid_3) self.assertEqual( - dict(result.data), + dict(result), {'periodo_tributario': RcvPeriodoTributario(year=2019, month=9)}, ) - self.assertEqual(dict(result.errors), {}) result = schema.load(data_valid_4) self.assertEqual( - dict(result.data), + dict(result), {'periodo_tributario': RcvPeriodoTributario(year=2019, month=9)}, ) - self.assertEqual(dict(result.errors), {}) def test_dump_ok_valid(self) -> None: - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() obj_valid_1 = self.MyObj(periodo_tributario=RcvPeriodoTributario(year=2019, month=12)) obj_valid_2 = self.MyObj(periodo_tributario=RcvPeriodoTributario(year=2019, month=9)) obj_valid_3 = self.MyObj(periodo_tributario=None) - data, errors = schema.dump(obj_valid_1) + data = schema.dump(obj_valid_1) self.assertEqual(data, {'periodo_tributario': '2019-12', 'other_field': None}) - self.assertEqual(errors, {}) - data, errors = schema.dump(obj_valid_2) + data = schema.dump(obj_valid_2) self.assertEqual(data, {'periodo_tributario': '2019-09', 'other_field': None}) - self.assertEqual(errors, {}) - data, errors = schema.dump(obj_valid_3) + data = schema.dump(obj_valid_3) self.assertEqual(data, {'periodo_tributario': None, 'other_field': None}) - self.assertEqual(errors, {}) def test_dump_ok_strange(self) -> None: # If the class of the object to be dumped has attributes that do not match at all the - # fields of the schema, there are no errors! Even if the schema has `strict = True` set. + # fields of the schema, there are no errors! - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() schema_strict = self.MyMmSchemaStrict() obj_valid_1 = self.MyBadObj(some_field=123) obj_valid_2 = self.MyBadObj(some_field=None) - data, errors = schema.dump(obj_valid_1) - self.assertEqual((data, errors), ({}, {})) + data = schema.dump(obj_valid_1) + self.assertEqual(data, {}) - data, errors = schema_strict.dump(obj_valid_1) - self.assertEqual((data, errors), ({}, {})) + data = schema_strict.dump(obj_valid_1) + self.assertEqual(data, {}) - data, errors = schema.dump(obj_valid_2) - self.assertEqual((data, errors), ({}, {})) + data = schema.dump(obj_valid_2) + self.assertEqual(data, {}) - data, errors = schema_strict.dump(obj_valid_2) - self.assertEqual((data, errors), ({}, {})) + data = schema_strict.dump(obj_valid_2) + self.assertEqual(data, {}) def test_load_fail(self) -> None: - schema = self.MyMmSchema() + schema = self.LoadMyMmSchema() data_invalid_1 = {'source field name': '2019-12-01'} data_invalid_2 = {'source field name': 201912} @@ -574,37 +592,34 @@ def test_load_fail(self) -> None: data_invalid_4 = {'source field name': None} data_invalid_5 = {} - result = schema.load(data_invalid_1) - self.assertEqual(dict(result.data), {}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_1) self.assertEqual( - dict(result.errors), - {'source field name': ["Not a valid RCV Periodo Tributario."]}, + cm.exception.messages, {'source field name': ['Not a valid RCV Periodo Tributario.']} ) - result = schema.load(data_invalid_2) - self.assertEqual(dict(result.data), {}) - self.assertEqual(dict(result.errors), {'source field name': ['Invalid type.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_2) + self.assertEqual(cm.exception.messages, {'source field name': ['Invalid type.']}) - result = schema.load(data_invalid_3) - self.assertEqual(dict(result.data), {}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_3) self.assertEqual( - dict(result.errors), - {'source field name': ["Not a valid RCV Periodo Tributario."]}, + cm.exception.messages, {'source field name': ["Not a valid RCV Periodo Tributario."]} ) - result = schema.load(data_invalid_4) - self.assertEqual(dict(result.data), {}) - self.assertEqual(dict(result.errors), {'source field name': ['Field may not be null.']}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_4) + self.assertEqual(cm.exception.messages, {'source field name': ['Field may not be null.']}) - result = schema.load(data_invalid_5) - self.assertEqual(dict(result.data), {}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.load(data_invalid_5) self.assertEqual( - dict(result.errors), - {'source field name': ['Missing data for required field.']}, + cm.exception.messages, {'source field name': ['Missing data for required field.']} ) def test_dump_fail(self) -> None: - schema = self.MyMmSchema() + schema = self.DumpMyMmSchema() obj_invalid_1 = self.MyObj(periodo_tributario='2019-12-01') obj_invalid_2 = self.MyObj(periodo_tributario=date(2019, 12, 1)) @@ -613,15 +628,26 @@ def test_dump_fail(self) -> None: obj_invalid_5 = self.MyObj(periodo_tributario=201912) obj_invalid_6 = self.MyObj(periodo_tributario=' 2019-12-01') - data, errors = schema.dump(obj_invalid_1) - self.assertEqual(errors, {'periodo_tributario': ["Not a valid RCV Periodo Tributario."]}) - data, errors = schema.dump(obj_invalid_2) - self.assertEqual(errors, {'periodo_tributario': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_3) - self.assertEqual(errors, {'periodo_tributario': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_4) - self.assertEqual(errors, {'periodo_tributario': ["Not a valid RCV Periodo Tributario."]}) - data, errors = schema.dump(obj_invalid_5) - self.assertEqual(errors, {'periodo_tributario': ['Invalid type.']}) - data, errors = schema.dump(obj_invalid_6) - self.assertEqual(errors, {'periodo_tributario': ["Not a valid RCV Periodo Tributario."]}) + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_1) + self.assertEqual(cm.exception.messages, ["Not a valid RCV Periodo Tributario."]) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_2) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_3) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_4) + self.assertEqual(cm.exception.messages, ["Not a valid RCV Periodo Tributario."]) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_5) + self.assertEqual(cm.exception.messages, ['Invalid type.']) + + with self.assertRaises(marshmallow.ValidationError) as cm: + schema.dump(obj_invalid_6) + self.assertEqual(cm.exception.messages, ["Not a valid RCV Periodo Tributario."]) diff --git a/tests/test_libs_csv_utils.py b/tests/test_libs_csv_utils.py index fa3c8e61..7162430e 100644 --- a/tests/test_libs_csv_utils.py +++ b/tests/test_libs_csv_utils.py @@ -4,6 +4,6 @@ class FunctionsTest(unittest.TestCase): - def test_create_csv_dict_reader(self): + def test_create_csv_dict_reader(self) -> None: # TODO: implement for 'create_csv_dict_reader'. pass diff --git a/tests/test_libs_dataclass_utils.py b/tests/test_libs_dataclass_utils.py index 8028046e..21633e5c 100644 --- a/tests/test_libs_dataclass_utils.py +++ b/tests/test_libs_dataclass_utils.py @@ -34,7 +34,7 @@ class DataclassBWithMixin(DataclassBWithoutMixin, DcDeepCompareMixin): class NotADataclassWithMixin(DcDeepCompareMixin): - def __init__(self, *args, **kwargs): + def __init__(self, *args: object, **kwargs: object) -> None: self.args = args self.kwargs = kwargs diff --git a/tests/test_libs_encoding_utils.py b/tests/test_libs_encoding_utils.py index a2eb554a..f6f7e4d9 100644 --- a/tests/test_libs_encoding_utils.py +++ b/tests/test_libs_encoding_utils.py @@ -8,14 +8,14 @@ class FunctionsTest(unittest.TestCase): - def test_clean_base64(self): + def test_clean_base64(self) -> None: # TODO: implement for function 'clean_base64'. pass - def test_decode_base64_strict(self): + def test_decode_base64_strict(self) -> None: # TODO: implement for function 'decode_base64_strict'. pass - def test_validate_base64(self): + def test_validate_base64(self) -> None: # TODO: implement for function 'validate_base64'. pass diff --git a/tests/test_libs_io_utils.py b/tests/test_libs_io_utils.py index 2b3b13e7..e278f699 100644 --- a/tests/test_libs_io_utils.py +++ b/tests/test_libs_io_utils.py @@ -8,7 +8,7 @@ class FunctionsTest(unittest.TestCase): - def test_with_encoding_utf8(self): + def test_with_encoding_utf8(self) -> None: filename = pathlib.Path(__file__).with_name('test_libs_io_utils-test-file-1.tmp') filename.touch() @@ -99,7 +99,7 @@ def test_with_encoding_utf8(self): filename.unlink() - def test_with_mode_x(self): + def test_with_mode_x(self) -> None: # For the sake of simplicity test here both 'with_mode_binary' and 'with_mode_text'. filename = pathlib.Path(__file__).with_name('test_libs_io_utils-test-file-2.tmp') diff --git a/tests/test_libs_mm_utils.py b/tests/test_libs_mm_utils.py index 97e2ded3..b53f0331 100644 --- a/tests/test_libs_mm_utils.py +++ b/tests/test_libs_mm_utils.py @@ -13,6 +13,6 @@ def test_x(self) -> None: class FunctionsTest(unittest.TestCase): - def test_validate_no_unexpected_input_fields(self): + def test_validate_no_unexpected_input_fields(self) -> None: # TODO: implement for 'validate_no_unexpected_input_fields'. pass diff --git a/tests/test_libs_rows_processing.py b/tests/test_libs_rows_processing.py index f4f1e818..d6c4b969 100644 --- a/tests/test_libs_rows_processing.py +++ b/tests/test_libs_rows_processing.py @@ -7,10 +7,10 @@ class FunctionsTest(unittest.TestCase): - def test_csv_rows_mm_deserialization_iterator(self): + def test_csv_rows_mm_deserialization_iterator(self) -> None: # TODO: implement for 'csv_rows_mm_deserialization_iterator'. pass - def test_rows_mm_deserialization_iterator(self): + def test_rows_mm_deserialization_iterator(self) -> None: # TODO: implement for 'rows_mm_deserialization_iterator'. pass diff --git a/tests/test_libs_xml_utils.py b/tests/test_libs_xml_utils.py index 5d7a261f..c6733098 100644 --- a/tests/test_libs_xml_utils.py +++ b/tests/test_libs_xml_utils.py @@ -261,7 +261,7 @@ def test_fail_included_cert_not_from_a_known_ca(self) -> None: verify_xml_signature(xml_doc, trusted_x509_cert=None) self.assertEqual( cm.exception.args, - ("[20, 0, 'unable to get local issuer certificate']",), + ('unable to get local issuer certificate',), ) def test_fail_signed_data_modified(self) -> None: diff --git a/tests/test_rcv_constants.py b/tests/test_rcv_constants.py index 857ef28d..3ae8533c 100644 --- a/tests/test_rcv_constants.py +++ b/tests/test_rcv_constants.py @@ -6,7 +6,7 @@ class RcvKindTest(unittest.TestCase): - def test_members(self): + def test_members(self) -> None: self.assertSetEqual( {x for x in RcvKind}, { @@ -15,10 +15,10 @@ def test_members(self): }, ) - def test_values_type(self): + def test_values_type(self) -> None: self.assertSetEqual({type(x.value) for x in RcvKind}, {str}) - def test_is_estado_contable_compatible(self): + def test_is_estado_contable_compatible(self) -> None: self.assertTrue(RcvKind.VENTAS.is_estado_contable_compatible(None)) self.assertTrue(RcvKind.COMPRAS.is_estado_contable_compatible(RcEstadoContable.REGISTRO)) self.assertTrue(RcvKind.COMPRAS.is_estado_contable_compatible(RcEstadoContable.NO_INCLUIR)) @@ -33,7 +33,7 @@ def test_is_estado_contable_compatible(self): class RcEstadoContableTest(unittest.TestCase): - def test_members(self): + def test_members(self) -> None: self.assertSetEqual( {x for x in RcEstadoContable}, { @@ -44,12 +44,12 @@ def test_members(self): }, ) - def test_values_type(self): + def test_values_type(self) -> None: self.assertSetEqual({type(x.value) for x in RcEstadoContable}, {str}) class RcvTipoDoctoTest(unittest.TestCase): - def test_members(self): + def test_members(self) -> None: self.assertSetEqual( {x for x in RcvTipoDocto}, { @@ -105,16 +105,16 @@ def test_members(self): }, ) - def test_values_type(self): + def test_values_type(self) -> None: self.assertSetEqual({type(x.value) for x in RcvTipoDocto}, {int}) - def test_of_some_member(self): + def test_of_some_member(self) -> None: value = RcvTipoDocto.FACTURA_ELECTRONICA self.assertEqual(value.name, 'FACTURA_ELECTRONICA') self.assertEqual(value.value, 33) - def test_as_tipo_dte(self): + def test_as_tipo_dte(self) -> None: self.assertEqual( RcvTipoDocto.FACTURA_ELECTRONICA.as_tipo_dte(), TipoDte.FACTURA_ELECTRONICA, diff --git a/tests/test_rcv_data_models.py b/tests/test_rcv_data_models.py index bc8e02f5..4fc556d4 100644 --- a/tests/test_rcv_data_models.py +++ b/tests/test_rcv_data_models.py @@ -212,7 +212,7 @@ def setUp(self) -> None: ), ) - def test_constants_match(self): + def test_constants_match(self) -> None: self.assertEqual( RvDetalleEntry.DATETIME_FIELDS_TZ, RcvDetalleEntry.DATETIME_FIELDS_TZ, @@ -364,7 +364,7 @@ def setUp(self) -> None: ), ) - def test_constants_match(self): + def test_constants_match(self) -> None: self.assertEqual( RcRegistroDetalleEntry.DATETIME_FIELDS_TZ, RcvDetalleEntry.DATETIME_FIELDS_TZ, @@ -487,7 +487,7 @@ def setUp(self) -> None: ), ) - def test_constants_match(self): + def test_constants_match(self) -> None: self.assertEqual( RcReclamadoDetalleEntry.DATETIME_FIELDS_TZ, RcvDetalleEntry.DATETIME_FIELDS_TZ, diff --git a/tests/test_rtc_data_models.py b/tests/test_rtc_data_models.py index 384566eb..e8fbb814 100644 --- a/tests/test_rtc_data_models.py +++ b/tests/test_rtc_data_models.py @@ -614,7 +614,7 @@ def test_as_dict(self) -> None: ) self.assertEqual(obj.as_dict(), expected_output) - def test_as_cesion_l0(self): + def test_as_cesion_l0(self) -> None: self._set_obj_1() obj = self.obj_1 @@ -634,7 +634,7 @@ def test_as_cesion_l0(self): ) self.assertEqual(obj.as_cesion_l0(), expected_output) - def test_as_dte_data_l1(self): + def test_as_dte_data_l1(self) -> None: self._set_obj_1() obj = self.obj_1 @@ -833,7 +833,7 @@ def test_as_dict(self) -> None: ) self.assertEqual(obj.as_dict(), expected_output) - def test_as_cesion_l1(self): + def test_as_cesion_l1(self) -> None: self._set_obj_1() obj = self.obj_1 @@ -858,7 +858,7 @@ def test_as_cesion_l1(self): ) self.assertEqual(obj.as_cesion_l1(), expected_output) - def test_as_dte_data_l2(self): + def test_as_dte_data_l2(self) -> None: self._set_obj_1() obj = self.obj_1 diff --git a/tests/test_rtc_parse_aec.py b/tests/test_rtc_parse_aec.py index 09e013e7..2bd416d3 100644 --- a/tests/test_rtc_parse_aec.py +++ b/tests/test_rtc_parse_aec.py @@ -19,7 +19,7 @@ class AecXmlSchemaTest(unittest.TestCase): """ @unittest.skip("TODO: Implement for 'AEC_XML_SCHEMA_OBJ'.") - def test_AEC_XML_SCHEMA_OBJ(self): + def test_AEC_XML_SCHEMA_OBJ(self) -> None: self.assertIsNotNone(AEC_XML_SCHEMA_OBJ)