diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 72c30bb4..ebb6134a 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.23.3 +current_version = 0.23.4 commit = True tag = False message = chore: Bump version from {current_version} to {new_version} diff --git a/.github/workflows/ci-cd.yaml b/.github/workflows/ci-cd.yaml index a787ecf5..4ca0afc4 100644 --- a/.github/workflows/ci-cd.yaml +++ b/.github/workflows/ci-cd.yaml @@ -16,6 +16,10 @@ on: permissions: contents: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + env: PRODUCTION_VCS_REF: refs/heads/master STAGING_VCS_REF: refs/heads/develop @@ -42,6 +46,7 @@ jobs: - workflow_config uses: ./.github/workflows/ci.yaml + secrets: inherit # -----END CI Job----- diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b129e9d0..4e6cf607 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,7 +34,7 @@ jobs: steps: - name: Check Out VCS Repository - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.0 - name: Set Up Python ${{ matrix.python_version }} uses: actions/setup-python@v4.7.0 @@ -45,7 +45,7 @@ jobs: run: make python-virtualenv PYTHON_VIRTUALENV_DIR="venv" - name: Restoring/Saving Cache - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: "venv" key: py-v1-deps-${{ runner.os }}-${{ matrix.python_version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-dev.txt') }}-${{ hashFiles('Makefile', 'make/**.mk') }} @@ -75,7 +75,7 @@ jobs: steps: - name: Check Out VCS Repository - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.0 - name: Set Up Python ${{ matrix.python_version }} uses: actions/setup-python@v4.7.0 @@ -83,10 +83,11 @@ jobs: python-version: "${{ matrix.python_version }}" - name: Restoring/Saving Cache - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: "venv" key: py-v1-deps-${{ runner.os }}-${{ matrix.python_version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-dev.txt') }}-${{ hashFiles('Makefile', 'make/**.mk') }} + fail-on-cache-miss: true - name: Set Tox Environment id: set_tox_environment @@ -136,7 +137,7 @@ jobs: - name: Store Artifacts if: ${{ always() }} - uses: actions/upload-artifact@v3.1.2 + uses: actions/upload-artifact@v3.1.3 with: name: test_reports_${{ matrix.python_version }} path: test-reports/ diff --git a/.github/workflows/dependency-review.yaml b/.github/workflows/dependency-review.yaml index ceeb48b3..2a99b6ae 100644 --- a/.github/workflows/dependency-review.yaml +++ b/.github/workflows/dependency-review.yaml @@ -10,6 +10,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: dependency-review: name: Dependency Review @@ -17,9 +21,9 @@ jobs: steps: - name: Check Out VCS Repository - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.0 - name: Dependency Review - uses: actions/dependency-review-action@v3.0.8 + uses: actions/dependency-review-action@v3.1.0 with: fail-on-severity: critical diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 358f58cf..b2d7cc08 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -38,7 +38,7 @@ jobs: steps: - name: Check Out VCS Repository - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.0 - name: Set Up Python id: set_up_python @@ -47,7 +47,7 @@ jobs: python-version: "3.10.9" - name: Restoring/Saving Cache - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: "venv" key: py-v1-deps-${{ runner.os }}-${{ steps.set_up_python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-dev.txt') }}-${{ hashFiles('Makefile', 'make/**.mk') }} diff --git a/.github/workflows/git-commit-lint.yaml b/.github/workflows/git-commit-lint.yaml index 2f60b4fd..59aee589 100644 --- a/.github/workflows/git-commit-lint.yaml +++ b/.github/workflows/git-commit-lint.yaml @@ -12,6 +12,10 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: git-commit-lint: name: Git Commit Linter diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4ee1b478..9ec0ba4f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -35,7 +35,7 @@ jobs: steps: - name: Check Out VCS Repository - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.0 - name: Set Up Python id: set_up_python @@ -47,7 +47,7 @@ jobs: run: make python-virtualenv PYTHON_VIRTUALENV_DIR="venv" - name: Restoring/Saving Cache - uses: actions/cache@v3.3.1 + uses: actions/cache@v3.3.2 with: path: "venv" key: py-v1-deps-${{ runner.os }}-${{ steps.set_up_python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements-dev.txt') }}-${{ hashFiles('Makefile', 'make/**.mk') }} @@ -68,7 +68,7 @@ jobs: make dist - name: Store Artifacts - uses: actions/upload-artifact@v3.1.2 + uses: actions/upload-artifact@v3.1.3 with: name: release path: ${{ env.ARTIFACTS_PATH }}/ diff --git a/.github/workflows/super-linter.yaml b/.github/workflows/super-linter.yaml index 23ce3bc0..34c63d1b 100644 --- a/.github/workflows/super-linter.yaml +++ b/.github/workflows/super-linter.yaml @@ -24,6 +24,10 @@ permissions: statuses: write checks: write +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: super-linter: name: Super-Linter diff --git a/HISTORY.md b/HISTORY.md index a45478d7..2bfde1ef 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,16 @@ # History +## 0.23.4 (2023-10-23) + +- (PR #547, 2023-10-02) chore: Bump pydantic from 2.3.0 to 2.4.2 +- (PR #544, 2023-10-23) Migrate to `pydantic==2.4.2` +- (PR #545, 2023-10-23) chore: Bump the production-dependencies group with 4 updates +- (PR #549, 2023-10-23) chore: Bump the development-dependencies group with 5 updates +- (PR #553, 2023-10-23) Improve CI/CD workflows +- (PR #554, 2023-10-23) Enable GHA secrets inheritance so that Codecov token can be passed +- (PR #543, 2023-10-23) chore: Bump cryptography from 41.0.3 to 41.0.4 +- (PR #551, 2023-10-23) chore: Bump urllib3 from 1.26.12 to 1.26.18 + ## 0.23.3 (2023-09-14) - (PR #530, 2023-09-05) chore: Bump the development-dependencies group with 9 updates diff --git a/requirements-dev.in b/requirements-dev.in index 45b5a4bb..bfedc238 100644 --- a/requirements-dev.in +++ b/requirements-dev.in @@ -4,15 +4,15 @@ -c requirements.txt -black==23.7.0 +black==23.9.1 bumpversion==0.5.3 -coverage==7.3.0 +coverage==7.3.2 flake8==6.1.0 isort==5.12.0 mypy==1.5.1 -tox==4.8.0 +tox==4.11.3 twine==4.0.2 -types-jsonschema==4.17.0.10 +types-jsonschema==4.19.0.3 types-pyOpenSSL==23.2.0.2 -types-pytz==2023.3.0.1 +types-pytz==2023.3.1.1 wheel==0.41.2 diff --git a/requirements-dev.txt b/requirements-dev.txt index 98c70a5b..adf848c9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,11 @@ # # pip-compile --strip-extras requirements-dev.in # -black==23.7.0 +attrs==20.3.0 + # via + # -c requirements.txt + # referencing +black==23.9.1 # via -r requirements-dev.in bleach==5.0.1 # via readme-renderer @@ -20,7 +24,7 @@ cffi==1.15.1 # via # -c requirements.txt # cryptography -chardet==5.1.0 +chardet==5.2.0 # via tox charset-normalizer==3.1.0 # via requests @@ -28,25 +32,27 @@ click==8.0.3 # via black colorama==0.4.6 # via tox -coverage==7.3.0 +coverage==7.3.2 # via -r requirements-dev.in -cryptography==41.0.3 +cryptography==41.0.4 # via # -c requirements.txt # secretstorage # types-pyopenssl -distlib==0.3.6 +distlib==0.3.7 # via virtualenv docutils==0.19 # via readme-renderer -filelock==3.12.2 +filelock==3.12.4 # via # tox # virtualenv flake8==6.1.0 # via -r requirements-dev.in idna==2.10 - # via requests + # via + # requests + # yarl importlib-metadata==6.1.0 # via # -c requirements.txt @@ -65,6 +71,8 @@ mccabe==0.7.0 # via flake8 mdurl==0.1.2 # via markdown-it-py +multidict==6.0.4 + # via yarl mypy==1.5.1 # via -r requirements-dev.in mypy-extensions==1.0.0 @@ -81,12 +89,12 @@ pathspec==0.9.0 # via black pkginfo==1.8.3 # via twine -platformdirs==3.9.1 +platformdirs==3.11.0 # via # black # tox # virtualenv -pluggy==1.2.0 +pluggy==1.3.0 # via tox pycodestyle==2.11.0 # via flake8 @@ -100,10 +108,16 @@ pygments==2.15.0 # via # readme-renderer # rich -pyproject-api==1.5.3 +pyproject-api==1.6.1 # via tox +pyrsistent==0.17.3 + # via + # -c requirements.txt + # referencing readme-renderer==35.0 # via twine +referencing==0.8.11 + # via types-jsonschema requests==2.31.0 # via # requests-toolbelt @@ -124,15 +138,15 @@ tomli==2.0.1 # mypy # pyproject-api # tox -tox==4.8.0 +tox==4.11.3 # via -r requirements-dev.in twine==4.0.2 # via -r requirements-dev.in -types-jsonschema==4.17.0.10 +types-jsonschema==4.19.0.3 # via -r requirements-dev.in types-pyopenssl==23.2.0.2 # via -r requirements-dev.in -types-pytz==2023.3.0.1 +types-pytz==2023.3.1.1 # via -r requirements-dev.in typing-extensions==4.7.1 # via @@ -140,16 +154,18 @@ typing-extensions==4.7.1 # black # mypy # rich -urllib3==1.26.12 +urllib3==1.26.18 # via # requests # twine -virtualenv==20.24.1 +virtualenv==20.24.5 # via tox webencodings==0.5.1 # via bleach wheel==0.41.2 # via -r requirements-dev.in +yarl==1.9.2 + # via referencing zipp==3.8.1 # via # -c requirements.txt diff --git a/requirements.in b/requirements.in index a44d4bdd..2c9cae49 100644 --- a/requirements.in +++ b/requirements.in @@ -5,7 +5,7 @@ # 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==41.0.3 +cryptography==41.0.4 defusedxml==0.7.1 Django>=2.2.24 djangorestframework>=3.10.3,<3.15 @@ -13,7 +13,7 @@ importlib-metadata==6.1.0 jsonschema==4.17.3 lxml==4.9.2 marshmallow==3.19.0 -pydantic==2.3.0 +pydantic==2.4.2 pyOpenSSL==23.2.0 pytz==2023.3 signxml==3.2.0 diff --git a/requirements.txt b/requirements.txt index 80854c37..a9907a60 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ certifi==2023.7.22 # via signxml cffi==1.15.1 # via cryptography -cryptography==41.0.3 +cryptography==41.0.4 # via # -r requirements.in # pyopenssl @@ -29,7 +29,7 @@ djangorestframework==3.14.0 # via -r requirements.in importlib-metadata==6.1.0 # via -r requirements.in -importlib-resources==6.0.1 +importlib-resources==6.1.0 # via jsonschema jsonschema==4.17.3 # via -r requirements.in @@ -45,9 +45,9 @@ pkgutil-resolve-name==1.3.10 # via jsonschema pycparser==2.20 # via cffi -pydantic==2.3.0 +pydantic==2.4.2 # via -r requirements.in -pydantic-core==2.6.3 +pydantic-core==2.10.1 # via pydantic pyopenssl==23.2.0 # via diff --git a/src/cl_sii/__init__.py b/src/cl_sii/__init__.py index b2fdefc5..dce617c6 100644 --- a/src/cl_sii/__init__.py +++ b/src/cl_sii/__init__.py @@ -5,4 +5,4 @@ """ -__version__ = '0.23.3' +__version__ = '0.23.4' diff --git a/src/cl_sii/dte/data_models.py b/src/cl_sii/dte/data_models.py index dea3fe7b..7537172f 100644 --- a/src/cl_sii/dte/data_models.py +++ b/src/cl_sii/dte/data_models.py @@ -21,7 +21,7 @@ from datetime import date, datetime from typing import Mapping, Optional, Sequence -import pydantic.v1 +import pydantic import cl_sii.contribuyente.constants import cl_sii.rut.constants @@ -98,14 +98,10 @@ def validate_non_empty_bytes(value: bytes) -> None: raise ValueError("Bytes value length is 0.") -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class DteNaturalKey: @@ -165,21 +161,18 @@ def slug(self) -> str: # Validators ########################################################################### - @pydantic.v1.validator('folio') + @pydantic.field_validator('folio') + @classmethod def validate_folio(cls, v: object) -> object: if isinstance(v, int): validate_dte_folio(v) return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class DteDataL0(DteNaturalKey): @@ -214,14 +207,10 @@ def natural_key(self) -> DteNaturalKey: return DteNaturalKey(emisor_rut=self.emisor_rut, tipo_dte=self.tipo_dte, folio=self.folio) -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class DteDataL1(DteDataL0): @@ -322,9 +311,10 @@ def deudor_rut(self) -> Rut: # Validators ########################################################################### - @pydantic.v1.validator('monto_total') - def validate_monto_total(cls, v: object, values: Mapping[str, object]) -> object: - tipo_dte = values.get('tipo_dte') + @pydantic.field_validator('monto_total') + @classmethod + def validate_monto_total(cls, v: object, info: pydantic.ValidationInfo) -> object: + tipo_dte = info.data['tipo_dte'] if isinstance(v, int) and isinstance(tipo_dte, TipoDte): validate_dte_monto_total(v, tipo_dte=tipo_dte) @@ -332,14 +322,10 @@ def validate_monto_total(cls, v: object, values: Mapping[str, object]) -> object return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class DteDataL2(DteDataL1): @@ -433,45 +419,46 @@ def as_dte_data_l1(self) -> DteDataL1: # Validators ########################################################################### - @pydantic.v1.validator('emisor_razon_social', 'receptor_razon_social') + @pydantic.field_validator('emisor_razon_social', 'receptor_razon_social') + @classmethod def validate_contribuyente_razon_social(cls, v: object) -> object: if isinstance(v, str): validate_contribuyente_razon_social(v) return v - @pydantic.v1.validator('firma_documento_dt') + @pydantic.field_validator('firma_documento_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v - @pydantic.v1.validator('signature_value', 'signature_x509_cert_der') + @pydantic.field_validator('signature_value', 'signature_x509_cert_der') + @classmethod def validate_non_empty_bytes(cls, v: object) -> object: if isinstance(v, bytes): validate_non_empty_bytes(v) return v - @pydantic.v1.validator('emisor_giro', 'emisor_email', 'receptor_email') + @pydantic.field_validator('emisor_giro', 'emisor_email', 'receptor_email') + @classmethod def validate_no_leading_or_trailing_whitespace_characters(cls, v: object) -> object: if isinstance(v, str): validate_clean_str(v) return v - @pydantic.v1.validator('emisor_giro', 'emisor_email', 'receptor_email') + @pydantic.field_validator('emisor_giro', 'emisor_email', 'receptor_email') + @classmethod def validate_non_empty_stripped_str(cls, v: object) -> object: if isinstance(v, str): validate_non_empty_str(v) return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class DteXmlReferencia: @@ -579,7 +566,8 @@ class DteXmlReferencia: # Validators ########################################################################### - @pydantic.v1.validator('numero_linea_ref') + @pydantic.field_validator('numero_linea_ref') + @classmethod def validate_numero_linea_ref(cls, value: int) -> int: if ( constants.DTE_REFERENCIA_LINE_NUMBER_MIN_VALUE @@ -595,7 +583,8 @@ def validate_numero_linea_ref(cls, value: int) -> int: value, ) - @pydantic.v1.validator('tipo_documento_ref') + @pydantic.field_validator('tipo_documento_ref') + @classmethod def validate_tipo_documento_ref(cls, value: str) -> str: if 1 <= len(value) <= 3: return value @@ -604,13 +593,15 @@ def validate_tipo_documento_ref(cls, value: str) -> str: "The length of 'tipo_documento_ref' must be a value between 1 and 3", value ) - @pydantic.v1.validator('ind_global') + @pydantic.field_validator('ind_global') + @classmethod def validate_ind_global(cls, value: int | None) -> int | None: if value and value != 1: raise ValueError("Only the value '1' is valid for the field 'ind_global'", value) return value - @pydantic.v1.validator('folio_ref') + @pydantic.field_validator('folio_ref') + @classmethod def validate_folio_ref(cls, value: str) -> str: if ( constants.DTE_REFERENCIA_FOLIO_MIN_LENGTH @@ -626,7 +617,8 @@ def validate_folio_ref(cls, value: str) -> str: value, ) - @pydantic.v1.validator('fecha_ref') + @pydantic.field_validator('fecha_ref') + @classmethod def validate_fecha_ref(cls, value: date) -> date: if ( value < constants.DTE_REFERENCIA_FECHA_NOT_BEFORE @@ -641,7 +633,8 @@ def validate_fecha_ref(cls, value: date) -> date: return value - @pydantic.v1.validator('razon_ref') + @pydantic.field_validator('razon_ref') + @classmethod def validate_razon_ref(cls, value: str | None) -> str | None: if value and len(value) > constants.DTE_REFERENCIA_RAZON_MAX_LENGTH: raise ValueError( @@ -653,14 +646,10 @@ def validate_razon_ref(cls, value: str | None) -> str | None: return value -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class DteXmlData(DteDataL1): @@ -781,37 +770,43 @@ def as_dte_data_l2(self) -> DteDataL2: # Validators ########################################################################### - @pydantic.v1.validator('emisor_razon_social', 'receptor_razon_social') + @pydantic.field_validator('emisor_razon_social', 'receptor_razon_social') + @classmethod def validate_contribuyente_razon_social(cls, v: object) -> object: if isinstance(v, str): validate_contribuyente_razon_social(v) return v - @pydantic.v1.validator('firma_documento_dt') + @pydantic.field_validator('firma_documento_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v - @pydantic.v1.validator('signature_value', 'signature_x509_cert_der') + @pydantic.field_validator('signature_value', 'signature_x509_cert_der') + @classmethod def validate_non_empty_bytes(cls, v: object) -> object: if isinstance(v, bytes): validate_non_empty_bytes(v) return v - @pydantic.v1.validator('emisor_giro', 'emisor_email', 'receptor_email') + @pydantic.field_validator('emisor_giro', 'emisor_email', 'receptor_email') + @classmethod def validate_no_leading_or_trailing_whitespace_characters(cls, v: object) -> object: if isinstance(v, str): validate_clean_str(v) return v - @pydantic.v1.validator('emisor_giro', 'emisor_email', 'receptor_email') + @pydantic.field_validator('emisor_giro', 'emisor_email', 'receptor_email') + @classmethod def validate_non_empty_stripped_str(cls, v: object) -> object: if isinstance(v, str): validate_non_empty_str(v) return v - @pydantic.v1.validator('referencias') + @pydantic.field_validator('referencias') + @classmethod def validate_referencias_numero_linea_ref_order(cls, v: object) -> object: if isinstance(v, Sequence): for idx, referencia in enumerate(v, start=1): @@ -819,13 +814,10 @@ def validate_referencias_numero_linea_ref_order(cls, v: object) -> object: raise ValueError("items must be ordered according to their 'numero_linea_ref'") return v - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_referencias_rut_otro_is_consistent_with_tipo_dte( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - referencias = values['referencias'] - tipo_dte = values['tipo_dte'] + @pydantic.model_validator(mode='after') + def validate_referencias_rut_otro_is_consistent_with_tipo_dte(self) -> DteXmlData: + referencias = self.referencias + tipo_dte = self.tipo_dte if ( isinstance(referencias, Sequence) @@ -840,15 +832,12 @@ def validate_referencias_rut_otro_is_consistent_with_tipo_dte( f" 'Referencia' number {referencia.numero_linea_ref}.", ) - return values + return self - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_referencias_rut_otro_is_consistent_with_emisor_rut( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - referencias = values['referencias'] - emisor_rut = values['emisor_rut'] + @pydantic.model_validator(mode='after') + def validate_referencias_rut_otro_is_consistent_with_emisor_rut(self) -> DteXmlData: + referencias = self.referencias + emisor_rut = self.emisor_rut if isinstance(referencias, Sequence) and isinstance(emisor_rut, Rut): for referencia in referencias: @@ -859,15 +848,12 @@ def validate_referencias_rut_otro_is_consistent_with_emisor_rut( f" 'Referencia' number {referencia.numero_linea_ref}.", ) - return values + return self - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_referencias_codigo_ref_is_consistent_with_tipo_dte( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - referencias = values['referencias'] - tipo_dte = values['tipo_dte'] + @pydantic.model_validator(mode='after') + def validate_referencias_codigo_ref_is_consistent_with_tipo_dte(self) -> DteXmlData: + referencias = self.referencias + tipo_dte = self.tipo_dte if ( isinstance(referencias, Sequence) @@ -882,4 +868,4 @@ def validate_referencias_codigo_ref_is_consistent_with_tipo_dte( f" 'Referencia' number {referencia.numero_linea_ref}.", ) - return values + return self diff --git a/src/cl_sii/dte/parse.py b/src/cl_sii/dte/parse.py index f3c9c968..2639ffa7 100644 --- a/src/cl_sii/dte/parse.py +++ b/src/cl_sii/dte/parse.py @@ -26,7 +26,7 @@ from datetime import date, datetime from typing import Mapping, Optional, Sequence, Tuple -import pydantic.v1 +import pydantic from cl_sii.libs import encoding_utils, tz_utils, xml_utils from cl_sii.libs.xml_utils import XmlElement, XmlElementTree @@ -570,16 +570,17 @@ def _validate_rut(v: object) -> object: return v -class _DteXmlReferenciaParser(pydantic.v1.BaseModel): +class _DteXmlReferenciaParser(pydantic.BaseModel): """ Parser for ``/Documento/Referencia``. """ - class Config: - allow_mutation = False - anystr_strip_whitespace = True - arbitrary_types_allowed = True - extra = pydantic.v1.Extra.forbid + model_config = pydantic.ConfigDict( + arbitrary_types_allowed=True, + extra='forbid', + frozen=True, + str_strip_whitespace=True, + ) ########################################################################### # Fields @@ -601,7 +602,7 @@ class Config: @classmethod def parse_xml(cls, xml_doc: XmlElement) -> _DteXmlReferenciaParser: aec_dict = cls.parse_xml_to_dict(xml_doc) - return cls.parse_obj(aec_dict) + return cls.model_validate(aec_dict) def as_dte_xml_referencia(self) -> data_models.DteXmlReferencia: return data_models.DteXmlReferencia( @@ -636,10 +637,9 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - _validate_rut = pydantic.v1.validator( + _validate_rut = pydantic.field_validator( 'rut_otro', - pre=True, - allow_reuse=True, + mode='before', )(_validate_rut) diff --git a/src/cl_sii/rcv/data_models.py b/src/cl_sii/rcv/data_models.py index 03cd95f6..6a8bec5e 100644 --- a/src/cl_sii/rcv/data_models.py +++ b/src/cl_sii/rcv/data_models.py @@ -8,9 +8,9 @@ import logging from datetime import date, datetime -from typing import ClassVar, Mapping, Optional +from typing import ClassVar, Optional -import pydantic.v1 +import pydantic import cl_sii.dte.data_models from cl_sii.base.constants import SII_OFFICIAL_TZ @@ -22,7 +22,7 @@ logger = logging.getLogger(__name__) -@pydantic.v1.dataclasses.dataclass(frozen=True) +@pydantic.dataclasses.dataclass(frozen=True) class PeriodoTributario: ########################################################################### # constants @@ -41,14 +41,16 @@ class PeriodoTributario: # Validators ########################################################################### - @pydantic.v1.validator('year') + @pydantic.field_validator('year') + @classmethod def validate_year(cls, v: object) -> object: if isinstance(v, int) and v < 1900: # 1900 si an arbitrary number but it more useful than checking not < 1. raise ValueError("Value is out of the valid range for 'year'.") return v - @pydantic.v1.validator('month') + @pydantic.field_validator('month') + @classmethod def validate_month(cls, v: object) -> object: if isinstance(v, int): if v < 1 or v > 12: @@ -97,14 +99,10 @@ def as_datetime(self) -> datetime: ) -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class RcvDetalleEntry: @@ -161,25 +159,24 @@ class RcvDetalleEntry: # Validators ########################################################################### - @pydantic.v1.validator('folio') + @pydantic.field_validator('folio') + @classmethod def validate_folio(cls, v: object) -> object: if isinstance(v, int): cl_sii.dte.data_models.validate_dte_folio(v) return v - @pydantic.v1.validator('fecha_recepcion_dt') + @pydantic.field_validator('fecha_recepcion_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_rcv_kind_is_consistent_with_rc_estado_contable( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - rcv_kind = values.get('RCV_KIND') - rc_estado_contable = values.get('RC_ESTADO_CONTABLE') + @pydantic.model_validator(mode='after') + def validate_rcv_kind_is_consistent_with_rc_estado_contable(self) -> RcvDetalleEntry: + rcv_kind = self.RCV_KIND + rc_estado_contable = self.RC_ESTADO_CONTABLE if isinstance(rcv_kind, RcvKind): if rcv_kind == RcvKind.COMPRAS: @@ -193,7 +190,7 @@ def validate_rcv_kind_is_consistent_with_rc_estado_contable( "'RC_ESTADO_CONTABLE' must be None when 'RCV_KIND' is 'VENTAS'." ) - return values + return self @property def is_dte(self) -> bool: @@ -233,14 +230,10 @@ def as_dte_data_l2(self) -> cl_sii.dte.data_models.DteDataL2: return dte_data -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class RvDetalleEntry(RcvDetalleEntry): @@ -274,27 +267,25 @@ class RvDetalleEntry(RcvDetalleEntry): # Validators ########################################################################### - @pydantic.v1.validator('receptor_razon_social') + @pydantic.field_validator('receptor_razon_social') + @classmethod def validate_contribuyente_razon_social(cls, v: object) -> object: if isinstance(v, str): cl_sii.dte.data_models.validate_contribuyente_razon_social(v) return v - @pydantic.v1.validator('fecha_acuse_dt', 'fecha_reclamo_dt') + @pydantic.field_validator('fecha_acuse_dt', 'fecha_reclamo_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class RcRegistroDetalleEntry(RcvDetalleEntry): @@ -325,27 +316,25 @@ class RcRegistroDetalleEntry(RcvDetalleEntry): # Validators ########################################################################### - @pydantic.v1.validator('emisor_razon_social') + @pydantic.field_validator('emisor_razon_social') + @classmethod def validate_contribuyente_razon_social(cls, v: object) -> object: if isinstance(v, str): cl_sii.dte.data_models.validate_contribuyente_razon_social(v) return v - @pydantic.v1.validator('fecha_acuse_dt') + @pydantic.field_validator('fecha_acuse_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class RcNoIncluirDetalleEntry(RcRegistroDetalleEntry): @@ -358,14 +347,10 @@ class RcNoIncluirDetalleEntry(RcRegistroDetalleEntry): RC_ESTADO_CONTABLE = RcEstadoContable.NO_INCLUIR -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class RcReclamadoDetalleEntry(RcvDetalleEntry): @@ -400,27 +385,25 @@ class RcReclamadoDetalleEntry(RcvDetalleEntry): # Validators ########################################################################### - @pydantic.v1.validator('emisor_razon_social') + @pydantic.field_validator('emisor_razon_social') + @classmethod def validate_contribuyente_razon_social(cls, v: object) -> object: if isinstance(v, str): cl_sii.dte.data_models.validate_contribuyente_razon_social(v) return v - @pydantic.v1.validator('fecha_reclamo_dt') + @pydantic.field_validator('fecha_reclamo_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class RcPendienteDetalleEntry(RcvDetalleEntry): @@ -441,7 +424,8 @@ class RcPendienteDetalleEntry(RcvDetalleEntry): # Validators ########################################################################### - @pydantic.v1.validator('emisor_razon_social') + @pydantic.field_validator('emisor_razon_social') + @classmethod def validate_contribuyente_razon_social(cls, v: object) -> object: if isinstance(v, str): cl_sii.dte.data_models.validate_contribuyente_razon_social(v) diff --git a/src/cl_sii/rtc/data_models.py b/src/cl_sii/rtc/data_models.py index 637019eb..9ef42bd4 100644 --- a/src/cl_sii/rtc/data_models.py +++ b/src/cl_sii/rtc/data_models.py @@ -21,9 +21,9 @@ import dataclasses from datetime import date, datetime -from typing import Any, ClassVar, Mapping, Optional +from typing import ClassVar, Mapping, Optional -import pydantic.v1 +import pydantic from cl_sii.base.constants import SII_OFFICIAL_TZ from cl_sii.dte import data_models as dte_data_models @@ -79,14 +79,10 @@ def validate_cesion_and_dte_montos(cesion_value: int, dte_value: int) -> None: raise ValueError('Value of "cesión" must be <= value of DTE.', cesion_value, dte_value) -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class CesionNaturalKey: @@ -140,27 +136,25 @@ def as_dict(self) -> Mapping[str, object]: # Validators ########################################################################### - @pydantic.v1.validator('dte_key') + @pydantic.field_validator('dte_key') + @classmethod def validate_dte_tipo_dte(cls, v: object) -> object: if isinstance(v, dte_data_models.DteNaturalKey): validate_cesion_dte_tipo_dte(v.tipo_dte) return v - @pydantic.v1.validator('seq') + @pydantic.field_validator('seq') + @classmethod def validate_seq(cls, v: object) -> object: if isinstance(v, int): validate_cesion_seq(v) return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class CesionAltNaturalKey: @@ -245,19 +239,22 @@ def as_dict(self) -> Mapping[str, object]: # Validators ########################################################################### - @pydantic.v1.validator('dte_key') + @pydantic.field_validator('dte_key') + @classmethod def validate_dte_tipo_dte(cls, v: object) -> object: if isinstance(v, dte_data_models.DteNaturalKey): validate_cesion_dte_tipo_dte(v.tipo_dte) return v - @pydantic.v1.validator('fecha_cesion_dt') + @pydantic.field_validator('fecha_cesion_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v - @pydantic.v1.validator('fecha_cesion_dt') + @pydantic.field_validator('fecha_cesion_dt') + @classmethod def truncate_fecha_cesion_dt_to_minutes(cls, v: object) -> object: if isinstance(v, datetime): if v.second != 0: @@ -267,14 +264,10 @@ def truncate_fecha_cesion_dt_to_minutes(cls, v: object) -> object: return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class CesionL0: @@ -390,33 +383,32 @@ def as_dict(self) -> Mapping[str, object]: # Validators ########################################################################### - @pydantic.v1.validator('dte_key') + @pydantic.field_validator('dte_key') + @classmethod def validate_dte_tipo_dte(cls, v: object) -> object: if isinstance(v, dte_data_models.DteNaturalKey): validate_cesion_dte_tipo_dte(v.tipo_dte) return v - @pydantic.v1.validator('seq') + @pydantic.field_validator('seq') + @classmethod def validate_seq(cls, v: object) -> object: if isinstance(v, int): validate_cesion_seq(v) return v - @pydantic.v1.validator('fecha_cesion_dt') + @pydantic.field_validator('fecha_cesion_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - arbitrary_types_allowed=True, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, ), ) class CesionL1(CesionL0): @@ -508,36 +500,30 @@ def as_dte_data_l1(self) -> dte_data_models.DteDataL1: # TODO: Validate value of 'fecha_cesion_dt' in relation to the DTE data. - @pydantic.v1.validator('monto_cedido') + @pydantic.field_validator('monto_cedido') + @classmethod def validate_monto_cedido(cls, v: object) -> object: if isinstance(v, int): validate_cesion_monto(v) return v - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_monto_cedido_does_not_exceed_dte_monto_total( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - monto_cedido = values['monto_cedido'] - dte_monto_total = values['dte_monto_total'] + @pydantic.model_validator(mode='after') + def validate_monto_cedido_does_not_exceed_dte_monto_total(self) -> CesionL1: + monto_cedido = self.monto_cedido + dte_monto_total = self.dte_monto_total if isinstance(monto_cedido, int) and isinstance(dte_monto_total, int): validate_cesion_and_dte_montos(cesion_value=monto_cedido, dte_value=dte_monto_total) - return values + return self -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - anystr_strip_whitespace=True, - arbitrary_types_allowed=True, - min_anystr_length=1, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, + str_min_length=1, + str_strip_whitespace=True, ), ) class CesionL2(CesionL1): @@ -696,43 +682,45 @@ def as_dte_data_l2(self) -> dte_data_models.DteDataL2: # TODO: Validate value of 'fecha_ultimo_vencimiento' in relation to the DTE data. - @pydantic.v1.validator( + @pydantic.field_validator( 'fecha_cesion_dt', 'fecha_firma_dt', ) + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v - @pydantic.v1.validator( + @pydantic.field_validator( 'cedente_razon_social', 'cesionario_razon_social', 'dte_emisor_razon_social', 'dte_receptor_razon_social', ) + @classmethod def validate_contribuyente_razon_social(cls, v: object) -> object: if isinstance(v, str): dte_data_models.validate_contribuyente_razon_social(v) return v - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_dte_data_l2(cls, values: Mapping[str, Any]) -> Mapping[str, object]: - dte_key = values['dte_key'] + @pydantic.model_validator(mode='after') + def validate_dte_data_l2(self) -> CesionL2: + dte_key = self.dte_key try: # Note: Delegate some validation to 'dte_data_models.DteDataL2'. _ = dte_data_models.DteDataL2( emisor_rut=dte_key.emisor_rut if dte_key is not None else None, tipo_dte=dte_key.tipo_dte if dte_key is not None else None, folio=dte_key.folio if dte_key is not None else None, - fecha_emision_date=values['dte_fecha_emision'], # type: ignore[arg-type] - receptor_rut=values['dte_receptor_rut'], # type: ignore[arg-type] - monto_total=values['dte_monto_total'], # type: ignore[arg-type] - emisor_razon_social=values['dte_emisor_razon_social'], - receptor_razon_social=values['dte_receptor_razon_social'], - fecha_vencimiento_date=values['dte_fecha_vencimiento'], + fecha_emision_date=self.dte_fecha_emision, + receptor_rut=self.dte_receptor_rut, + monto_total=self.dte_monto_total, + emisor_razon_social=self.dte_emisor_razon_social, + receptor_razon_social=self.dte_receptor_razon_social, + fecha_vencimiento_date=self.dte_fecha_vencimiento, ) except (TypeError, ValueError): raise - return values + return self diff --git a/src/cl_sii/rtc/data_models_aec.py b/src/cl_sii/rtc/data_models_aec.py index c8a852b2..271af93e 100644 --- a/src/cl_sii/rtc/data_models_aec.py +++ b/src/cl_sii/rtc/data_models_aec.py @@ -9,7 +9,7 @@ from datetime import date, datetime from typing import ClassVar, Mapping, Optional, Sequence, Tuple -import pydantic.v1 +import pydantic from cl_sii.base.constants import SII_OFFICIAL_TZ from cl_sii.dte import data_models as dte_data_models @@ -18,16 +18,12 @@ from . import data_models -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - anystr_strip_whitespace=True, - arbitrary_types_allowed=True, - min_anystr_length=1, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, + str_min_length=1, + str_strip_whitespace=True, ), ) class CesionAecXml: @@ -307,59 +303,58 @@ def as_cesion_l2(self) -> data_models.CesionL2: # Validators ########################################################################### - @pydantic.v1.validator('dte') + @pydantic.field_validator('dte') + @classmethod def validate_dte_tipo_dte(cls, v: object) -> object: if isinstance(v, dte_data_models.DteDataL0): data_models.validate_cesion_dte_tipo_dte(v.tipo_dte) return v - @pydantic.v1.validator('seq') + @pydantic.field_validator('seq') + @classmethod def validate_seq(cls, v: object) -> object: if isinstance(v, int): data_models.validate_cesion_seq(v) return v - @pydantic.v1.validator('monto_cesion') + @pydantic.field_validator('monto_cesion') + @classmethod def validate_monto_cesion(cls, v: object) -> object: if isinstance(v, int): data_models.validate_cesion_monto(v) return v - @pydantic.v1.validator( + @pydantic.field_validator( 'cedente_razon_social', 'cesionario_razon_social', ) + @classmethod def validate_contribuyente_razon_social(cls, v: object) -> object: if isinstance(v, str): dte_data_models.validate_contribuyente_razon_social(v) return v - @pydantic.v1.validator('fecha_cesion_dt') + @pydantic.field_validator('fecha_cesion_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_fecha_cesion_dt_is_consistent_with_dte( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - fecha_cesion_dt = values['fecha_cesion_dt'] - dte = values['dte'] + @pydantic.model_validator(mode='after') + def validate_fecha_cesion_dt_is_consistent_with_dte(self) -> CesionAecXml: + fecha_cesion_dt = self.fecha_cesion_dt + dte = self.dte if isinstance(fecha_cesion_dt, datetime) and isinstance(dte, dte_data_models.DteDataL1): pass # TODO: Validate value of 'fecha_cesion_dt' in relation to the DTE data. - return values + return self - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_monto_cesion_does_not_exceed_dte_monto_total( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - monto_cesion = values['monto_cesion'] - dte = values['dte'] + @pydantic.model_validator(mode='after') + def validate_monto_cesion_does_not_exceed_dte_monto_total(self) -> CesionAecXml: + monto_cesion = self.monto_cesion + dte = self.dte if isinstance(monto_cesion, int) and isinstance(dte, dte_data_models.DteDataL1): data_models.validate_cesion_and_dte_montos( @@ -367,34 +362,27 @@ def validate_monto_cesion_does_not_exceed_dte_monto_total( dte_value=dte.monto_total, ) - return values + return self - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_fecha_ultimo_vencimiento_is_consistent_with_dte( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - fecha_ultimo_vencimiento = values['fecha_ultimo_vencimiento'] - dte = values['dte'] + @pydantic.model_validator(mode='after') + def validate_fecha_ultimo_vencimiento_is_consistent_with_dte(self) -> CesionAecXml: + fecha_ultimo_vencimiento = self.fecha_ultimo_vencimiento + dte = self.dte if isinstance(fecha_ultimo_vencimiento, date) and isinstance( dte, dte_data_models.DteDataL1 ): pass # TODO: Validate value of 'fecha_ultimo_vencimiento' in relation to the DTE data. - return values + return self -@pydantic.v1.dataclasses.dataclass( +@pydantic.dataclasses.dataclass( frozen=True, - config=type( - 'Config', - (), - dict( - anystr_strip_whitespace=True, - arbitrary_types_allowed=True, - min_anystr_length=1, - ), + config=pydantic.ConfigDict( + arbitrary_types_allowed=True, + str_min_length=1, + str_strip_whitespace=True, ), ) class AecXml: @@ -693,26 +681,30 @@ def as_cesion_l2(self) -> data_models.CesionL2: # Validators ########################################################################### - @pydantic.v1.validator('dte') + @pydantic.field_validator('dte') + @classmethod def validate_dte_tipo_dte(cls, v: object) -> object: if isinstance(v, dte_data_models.DteDataL0): data_models.validate_cesion_dte_tipo_dte(v.tipo_dte) return v - @pydantic.v1.validator('fecha_firma_dt') + @pydantic.field_validator('fecha_firma_dt') + @classmethod def validate_datetime_tz(cls, v: object) -> object: if isinstance(v, datetime): tz_utils.validate_dt_tz(v, cls.DATETIME_FIELDS_TZ) return v - @pydantic.v1.validator('cesiones') + @pydantic.field_validator('cesiones') + @classmethod def validate_cesiones_min_items(cls, v: object) -> object: if isinstance(v, Sequence): if len(v) < 1: raise ValueError("must contain at least one item") return v - @pydantic.v1.validator('cesiones') + @pydantic.field_validator('cesiones') + @classmethod def validate_cesiones_seq_order(cls, v: object) -> object: if isinstance(v, Sequence): for idx, cesion in enumerate(v, start=1): @@ -739,13 +731,10 @@ def validate_cesiones_seq_order(cls, v: object) -> object: # return v - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_dte_matches_cesiones_dtes( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - dte = values['dte'] - cesiones = values['cesiones'] + @pydantic.model_validator(mode='after') + def validate_dte_matches_cesiones_dtes(self) -> AecXml: + dte = self.dte + cesiones = self.cesiones if isinstance(dte, dte_data_models.DteXmlData) and isinstance(cesiones, Sequence): if cesiones: @@ -759,13 +748,10 @@ def validate_dte_matches_cesiones_dtes( f" must match {dte_l1.__class__.__name__} with {dte_l1.natural_key}.", ) - return values + return self - @pydantic.v1.root_validator(skip_on_failure=True) - def validate_last_cesion_matches_some_fields( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: + @pydantic.model_validator(mode='after') + def validate_last_cesion_matches_some_fields(self) -> AecXml: field_validations: Sequence[Tuple[str, str]] = [ # (AecXml field, CesionAecXml field): # Even though it seems reasonable to expect that the date in `fecha_firma_dt` @@ -778,13 +764,13 @@ def validate_last_cesion_matches_some_fields( ('cesionario_rut', 'cesionario_rut'), ] - cesiones = values['cesiones'] + cesiones = self.cesiones if isinstance(cesiones, Sequence): if cesiones: last_cesion = cesiones[-1] for self_field, last_cesion_field in field_validations: - self_value = values.get(self_field) + self_value = getattr(self, self_field) last_cesion_value = getattr(last_cesion, last_cesion_field) if self_value != last_cesion_value: @@ -793,15 +779,14 @@ def validate_last_cesion_matches_some_fields( f" {last_cesion_value!r} != {self_value!r}.", ) - return values + return self - @pydantic.v1.root_validator + @pydantic.model_validator(mode='after') def validate_signature_value_and_signature_x509_cert_der_may_only_be_none_together( - cls, - values: Mapping[str, object], - ) -> Mapping[str, object]: - signature_value = values.get('signature_value') - signature_x509_cert_der = values.get('signature_x509_cert_der') + self, + ) -> AecXml: + signature_value = self.signature_value + signature_x509_cert_der = self.signature_x509_cert_der if not ( (signature_value is None and signature_x509_cert_der is None) @@ -812,4 +797,4 @@ def validate_signature_value_and_signature_x509_cert_der_may_only_be_none_togeth " must either both be None or both be not None." ) - return values + return self diff --git a/src/cl_sii/rtc/parse_aec.py b/src/cl_sii/rtc/parse_aec.py index 7cc30f41..6591b10a 100644 --- a/src/cl_sii/rtc/parse_aec.py +++ b/src/cl_sii/rtc/parse_aec.py @@ -21,7 +21,7 @@ from pathlib import Path from typing import Mapping, Optional, Sequence -import pydantic.v1 +import pydantic import cl_sii.dte.data_models import cl_sii.dte.parse @@ -104,16 +104,17 @@ def _validate_rut(v: object) -> object: return v -class _XmlSignature(pydantic.v1.BaseModel): +class _XmlSignature(pydantic.BaseModel): """ Parser for ``//Signature``. """ - class Config: - allow_mutation = False - anystr_strip_whitespace = True - extra = pydantic.v1.Extra.forbid - min_anystr_length = 1 + model_config = pydantic.ConfigDict( + extra='forbid', + frozen=True, + str_min_length=1, + str_strip_whitespace=True, + ) ########################################################################### # Fields @@ -158,11 +159,12 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - @pydantic.v1.validator( + @pydantic.field_validator( 'signature_value', 'key_info_x509_data_x509_cert', - pre=True, + mode='before', ) + @classmethod def validate_base64(cls, v: object) -> object: if isinstance(v, (str, bytes)): v = encoding_utils.decode_base64_strict(v) # Raises ValueError. @@ -178,17 +180,18 @@ def validate_base64(cls, v: object) -> object: # return v -class _Cesionario(pydantic.v1.BaseModel): +class _Cesionario(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Cesiones/Cesion/DocumentoCesion/Cesionario``. """ - class Config: - allow_mutation = False - anystr_strip_whitespace = True - arbitrary_types_allowed = True - extra = pydantic.v1.Extra.forbid - min_anystr_length = 1 + model_config = pydantic.ConfigDict( + arbitrary_types_allowed=True, + extra='forbid', + frozen=True, + str_min_length=1, + str_strip_whitespace=True, + ) ########################################################################### # Fields @@ -220,24 +223,24 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - _validate_rut = pydantic.v1.validator( # type: ignore[pydantic-field] + _validate_rut = pydantic.field_validator( # type: ignore[pydantic-field] 'rut', - pre=True, - allow_reuse=True, + mode='before', )(_validate_rut) -class _RutAutorizado(pydantic.v1.BaseModel): +class _RutAutorizado(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Cesiones/Cesion/DocumentoCesion/Cedente/RUTAutorizado``. """ - class Config: - allow_mutation = False - anystr_strip_whitespace = True - arbitrary_types_allowed = True - extra = pydantic.v1.Extra.forbid - min_anystr_length = 1 + model_config = pydantic.ConfigDict( + arbitrary_types_allowed=True, + extra='forbid', + frozen=True, + str_min_length=1, + str_strip_whitespace=True, + ) ########################################################################### # Fields @@ -265,30 +268,29 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - _empty_str_to_none = pydantic.v1.validator( # type: ignore[pydantic-field] + _empty_str_to_none = pydantic.field_validator( # type: ignore[pydantic-field] 'nombre', - pre=True, - allow_reuse=True, + mode='before', )(_empty_str_to_none) - _validate_rut = pydantic.v1.validator( # type: ignore[pydantic-field] + _validate_rut = pydantic.field_validator( # type: ignore[pydantic-field] 'rut', - pre=True, - allow_reuse=True, + mode='before', )(_validate_rut) -class _Cedente(pydantic.v1.BaseModel): +class _Cedente(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Cesiones/Cesion/DocumentoCesion/Cedente``. """ - class Config: - allow_mutation = False - anystr_strip_whitespace = True - arbitrary_types_allowed = True - extra = pydantic.v1.Extra.forbid - min_anystr_length = 1 + model_config = pydantic.ConfigDict( + arbitrary_types_allowed=True, + extra='forbid', + frozen=True, + str_min_length=1, + str_strip_whitespace=True, + ) ########################################################################### # Fields @@ -338,31 +340,33 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - @pydantic.v1.validator('rut', pre=True) + @pydantic.field_validator('rut', mode='before') + @classmethod def validate_rut(cls, v: object) -> object: if isinstance(v, str): v = Rut(value=v, validate_dv=False) # Raises ValueError if invalid. return v - @pydantic.v1.validator('ruts_autorizados') - def validate_ruts_autorizados_item_count(cls, v: object) -> object: - if isinstance(v, Sequence): - if len(v) < 1: - raise ValueError("must contain at least one item") - if len(v) > 3: - raise ValueError("must contain at most three items") + @pydantic.field_validator('ruts_autorizados') + @classmethod + def validate_ruts_autorizados_item_count(cls, v: Sequence) -> object: + if len(v) < 1: + raise ValueError("must contain at least one item") + if len(v) > 3: + raise ValueError("must contain at most three items") return v -class _IdDte(pydantic.v1.BaseModel): +class _IdDte(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Cesiones/Cesion/DocumentoCesion/IdDTE``. """ - class Config: - allow_mutation = False - arbitrary_types_allowed = True - extra = pydantic.v1.Extra.forbid + model_config = pydantic.ConfigDict( + arbitrary_types_allowed=True, + extra='forbid', + frozen=True, + ) ########################################################################### # Fields @@ -408,35 +412,37 @@ def as_dte_data_l1(self) -> cl_sii.dte.data_models.DteDataL1: # Validators ########################################################################### - _validate_rut_emisor = pydantic.v1.validator( # type: ignore[pydantic-field] + _validate_rut_emisor = pydantic.field_validator( # type: ignore[pydantic-field] 'rut_emisor', - pre=True, - allow_reuse=True, + mode='before', )(_validate_rut) - _validate_rut_receptor = pydantic.v1.validator( # type: ignore[pydantic-field] + _validate_rut_receptor = pydantic.field_validator( # type: ignore[pydantic-field] 'rut_receptor', - pre=True, - allow_reuse=True, + mode='before', )(_validate_rut) - @pydantic.v1.validator('tipo_dte', pre=True) + @pydantic.field_validator('tipo_dte', mode='before') + @classmethod def validate_tipo_dte(cls, v: object) -> object: + if isinstance(v, str): + v = int(v) # Raises ValueError if invalid. if isinstance(v, int): v = TipoDte(v) # Raises ValueError if invalid. return v -class _DocumentoCesion(pydantic.v1.BaseModel): +class _DocumentoCesion(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Cesiones/Cesion/DocumentoCesion``. """ - class Config: - allow_mutation = False - anystr_strip_whitespace = True - extra = pydantic.v1.Extra.forbid - min_anystr_length = 1 + model_config = pydantic.ConfigDict( + extra='forbid', + frozen=True, + str_min_length=1, + str_strip_whitespace=True, + ) ########################################################################### # Fields @@ -498,11 +504,9 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - @pydantic.v1.validator('tmst_cesion') - def validate_datetime(cls, v: object) -> object: - if isinstance(v, str): - v = datetime.fromisoformat(v) - + @pydantic.field_validator('tmst_cesion') + @classmethod + def validate_datetime(cls, v: datetime) -> datetime: if isinstance(v, datetime): v = tz_utils.convert_naive_dt_to_tz_aware( dt=v, @@ -511,14 +515,15 @@ def validate_datetime(cls, v: object) -> object: return v -class _Cesion(pydantic.v1.BaseModel): +class _Cesion(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Cesiones/Cesion``. """ - class Config: - allow_mutation = False - extra = pydantic.v1.Extra.forbid + model_config = pydantic.ConfigDict( + extra='forbid', + frozen=True, + ) ########################################################################### # Fields @@ -585,15 +590,16 @@ def as_cesion_aec_xml(self) -> data_models_aec.CesionAecXml: ) -class _DocumentoDteCedido(pydantic.v1.BaseModel): +class _DocumentoDteCedido(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Cesiones/DTECedido/DocumentoDTECedido``. """ - class Config: - allow_mutation = False - arbitrary_types_allowed = True - extra = pydantic.v1.Extra.forbid + model_config = pydantic.ConfigDict( + arbitrary_types_allowed=True, + extra='forbid', + frozen=True, + ) ########################################################################### # Fields @@ -631,7 +637,8 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - @pydantic.v1.validator('dte', pre=True) + @pydantic.field_validator('dte', mode='before') + @classmethod def validate_dte(cls, v: object) -> object: if isinstance(v, XmlElement): cl_sii.dte.parse.validate_dte_xml(v) @@ -651,14 +658,15 @@ def validate_dte(cls, v: object) -> object: # return v -class _DteCedido(pydantic.v1.BaseModel): +class _DteCedido(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Cesiones/DTECedido``. """ - class Config: - allow_mutation = False - extra = pydantic.v1.Extra.forbid + model_config = pydantic.ConfigDict( + extra='forbid', + frozen=True, + ) ########################################################################### # Fields @@ -702,17 +710,18 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: ) -class _Caratula(pydantic.v1.BaseModel): +class _Caratula(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC/Caratula``. """ - class Config: - allow_mutation = False - anystr_strip_whitespace = True - arbitrary_types_allowed = True - extra = pydantic.v1.Extra.forbid - min_anystr_length = 1 + model_config = pydantic.ConfigDict( + arbitrary_types_allowed=True, + extra='forbid', + frozen=True, + str_min_length=1, + str_strip_whitespace=True, + ) ########################################################################### # Fields @@ -748,47 +757,42 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - _empty_str_to_none = pydantic.v1.validator( # type: ignore[pydantic-field] + _empty_str_to_none = pydantic.field_validator( # type: ignore[pydantic-field] 'nmb_contacto', 'fono_contacto', 'mail_contacto', - pre=True, - allow_reuse=True, + mode='before', )(_empty_str_to_none) - _validate_rut_cedente = pydantic.v1.validator( # type: ignore[pydantic-field] + _validate_rut_cedente = pydantic.field_validator( # type: ignore[pydantic-field] 'rut_cedente', - pre=True, - allow_reuse=True, + mode='before', )(_validate_rut) - _validate_rut_cesionario = pydantic.v1.validator( # type: ignore[pydantic-field] + _validate_rut_cesionario = pydantic.field_validator( # type: ignore[pydantic-field] 'rut_cesionario', - pre=True, - allow_reuse=True, + mode='before', )(_validate_rut) - @pydantic.v1.validator('tmst_firmaenvio') - def validate_datetime(cls, v: object) -> object: - if isinstance(v, str): - v = datetime.fromisoformat(v) - - if isinstance(v, datetime): - v = tz_utils.convert_naive_dt_to_tz_aware( - dt=v, - tz=data_models_aec.AecXml.DATETIME_FIELDS_TZ, - ) + @pydantic.field_validator('tmst_firmaenvio') + @classmethod + def validate_datetime(cls, v: datetime) -> datetime: + v = tz_utils.convert_naive_dt_to_tz_aware( + dt=v, + tz=data_models_aec.AecXml.DATETIME_FIELDS_TZ, + ) return v -class _DocumentoAec(pydantic.v1.BaseModel): +class _DocumentoAec(pydantic.BaseModel): """ Parser for ``/AEC/DocumentoAEC``. """ - class Config: - allow_mutation = False - extra = pydantic.v1.Extra.forbid + model_config = pydantic.ConfigDict( + extra='forbid', + frozen=True, + ) ########################################################################### # Fields @@ -844,7 +848,8 @@ def parse_xml_to_dict(xml_em: XmlElement) -> Mapping[str, object]: # Validators ########################################################################### - @pydantic.v1.validator('cesiones_cesion') + @pydantic.field_validator('cesiones_cesion') + @classmethod def validate_cesiones_cesion_min_items(cls, v: object) -> object: if isinstance(v, Sequence): if len(v) < 1: @@ -852,14 +857,15 @@ def validate_cesiones_cesion_min_items(cls, v: object) -> object: return v -class _Aec(pydantic.v1.BaseModel): +class _Aec(pydantic.BaseModel): """ Parser for ``/AEC``. """ - class Config: - allow_mutation = False - extra = pydantic.v1.Extra.forbid + model_config = pydantic.ConfigDict( + extra='forbid', + frozen=True, + ) ########################################################################### # Fields @@ -875,7 +881,7 @@ class Config: @classmethod def parse_xml(cls, xml_doc: XmlElement) -> _Aec: aec_dict = cls.parse_xml_to_dict(xml_doc) - return cls.parse_obj(aec_dict) + return cls.model_validate(aec_dict) def as_aec_xml(self) -> data_models_aec.AecXml: doc_aec_struct = self.documento_aec diff --git a/src/tests/test_dte_data_models.py b/src/tests/test_dte_data_models.py index 665c9ff3..9ad827c7 100644 --- a/src/tests/test_dte_data_models.py +++ b/src/tests/test_dte_data_models.py @@ -3,7 +3,7 @@ import unittest from datetime import date, datetime -import pydantic.v1 +import pydantic from cl_sii.dte.constants import ( DTE_FOLIO_FIELD_MAX_VALUE, @@ -42,34 +42,40 @@ def test_validate_folio_range(self) -> None: expected_validation_errors = [ { 'loc': ('folio',), - 'msg': "Value is out of the valid range for 'folio'.", + 'msg': "Value error, Value is out of the valid range for 'folio'.", 'type': 'value_error', }, ] # Validate the minimum value of the field folio - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_nk_1, folio=DTE_FOLIO_FIELD_MIN_VALUE - 1, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Validate the maximum value of the field folio - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_nk_1, folio=DTE_FOLIO_FIELD_MAX_VALUE + 1, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_as_dict(self) -> None: self.assertDictEqual( @@ -136,54 +142,63 @@ def test_is_ok_negative_monto_total_in_tipo_dte_liquidacion_factura(self) -> Non tipo_dte=TipoDte.LIQUIDACION_FACTURA_ELECTRONICA, monto_total=-1, ) - except pydantic.v1.ValidationError as exc: + except pydantic.ValidationError as exc: self.fail(f'{exc.__class__.__name__} raised') def test_validate_monto_total_range(self) -> None: expected_validation_errors = [ { 'loc': ('monto_total',), - 'msg': "Value is out of the valid range for 'monto_total'.", + 'msg': "Value error, Value is out of the valid range for 'monto_total'.", 'type': 'value_error', }, ] # Validate the minimum value of the field monto_total - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l1_1, monto_total=DTE_MONTO_TOTAL_FIELD_MIN_VALUE - 1, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Validate the maximum value of the field monto_total - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l1_1, monto_total=DTE_MONTO_TOTAL_FIELD_MAX_VALUE + 1, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Validate the minimum value of the field monto_total # for a tipo_dte FACTURA_ELECTRONICA - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l1_1, monto_total=-1, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_as_dict(self) -> None: self.assertDictEqual( @@ -331,48 +346,54 @@ def test_ok_razon_social_none(self) -> None: emisor_razon_social=None, receptor_razon_social=None, ) - except pydantic.v1.ValidationError as exc: + except pydantic.ValidationError as exc: self.fail(f'{exc.__class__.__name__} raised') def test_validate_emisor_razon_social_empty(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_razon_social',), - 'msg': "Value must not be empty.", + 'msg': "Value error, Value must not be empty.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, emisor_razon_social='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_receptor_razon_social_empty(self) -> None: expected_validation_errors = [ { 'loc': ('receptor_razon_social',), - 'msg': "Value must not be empty.", + 'msg': "Value error, Value must not be empty.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, receptor_razon_social='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_datetime_tz(self) -> None: # Test TZ-awareness: @@ -380,21 +401,24 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('firma_documento_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, firma_documento_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -402,7 +426,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('firma_documento_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -411,7 +435,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, firma_documento_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -420,10 +444,12 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) def test_init_fail_regression_signature_value_bytes_with_x20(self) -> None: bytes_value_with_x20_as_base64 = 'IN2pkDBxqDnGl4Pfvboi' @@ -450,21 +476,24 @@ def test_validate_non_empty_bytes_signature_value(self) -> None: expected_validation_errors = [ { 'loc': ('signature_value',), - 'msg': 'Bytes value length is 0.', + 'msg': 'Value error, Bytes value length is 0.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, signature_value=b'', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_init_fail_regression_signature_cert_der_bytes_with_x20(self) -> None: bytes_value_with_x20_as_base64 = 'IN2pkDBxqDnGl4Pfvboi' @@ -491,47 +520,57 @@ def test_validate_non_empty_bytes_signature_x509_cert_der(self) -> None: expected_validation_errors = [ { 'loc': ('signature_x509_cert_der',), - 'msg': 'Bytes value length is 0.', + 'msg': 'Value error, Bytes value length is 0.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, signature_x509_cert_der=b'', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_no_leading_or_trailing_whitespace_characters_emisor_giro(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_giro',), - 'msg': "('Value has leading or trailing whitespace characters.', ' NASA ')", + 'msg': ( + "Value error, " + "('Value has leading or trailing whitespace characters.', ' NASA ')" + ), 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, emisor_giro=' NASA ', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_no_leading_or_trailing_whitespace_characters_emisor_email(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_email',), 'msg': ( + "Value error, " "(" "'Value has leading or trailing whitespace characters.', " "' fake_emisor_email@test.cl '" @@ -541,23 +580,26 @@ def test_validate_no_leading_or_trailing_whitespace_characters_emisor_email(self }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, emisor_email=' fake_emisor_email@test.cl ', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_no_leading_or_trailing_whitespace_characters_receptor_email(self) -> None: expected_validation_errors = [ { 'loc': ('receptor_email',), 'msg': ( - "(" + "Value error, (" "'Value has leading or trailing whitespace characters.', " "' fake_receptor_email@test.cl '" ")" @@ -566,76 +608,88 @@ def test_validate_no_leading_or_trailing_whitespace_characters_receptor_email(se }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, receptor_email=' fake_receptor_email@test.cl ', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_non_empty_stripped_str_emisor_giro(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_giro',), - 'msg': "String value length (stripped) is 0.", + 'msg': "Value error, String value length (stripped) is 0.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, emisor_giro='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_non_empty_stripped_str_emisor_email(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_email',), - 'msg': "String value length (stripped) is 0.", + 'msg': "Value error, String value length (stripped) is 0.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, emisor_email='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_non_empty_stripped_str_receptor_email(self) -> None: expected_validation_errors = [ { 'loc': ('receptor_email',), - 'msg': "String value length (stripped) is 0.", + 'msg': "Value error, String value length (stripped) is 0.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_l2_1, receptor_email='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_as_dict(self) -> None: self.assertDictEqual( @@ -738,7 +792,7 @@ def _set_obj_2(self) -> None: self.obj_2 = obj def test_create_new_empty_instance(self) -> None: - with self.assertRaises(TypeError): + with self.assertRaises(pydantic.ValidationError): DteXmlReferencia() def test_init_fail_numero_linea_ref_out_of_range(self) -> None: @@ -746,32 +800,46 @@ def test_init_fail_numero_linea_ref_out_of_range(self) -> None: obj = self.obj_1 - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, numero_linea_ref=0, ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('numero_linea_ref',), - 'msg': '("Value \'numero_linea_ref\' must be a value between 1 and 40", 0)', + 'msg': ( + 'Value error, ' + '("Value \'numero_linea_ref\' must be a value between 1 and 40", 0)' + ), 'type': 'value_error', } ], ) - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, numero_linea_ref=41, ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('numero_linea_ref',), - 'msg': '("Value \'numero_linea_ref\' must be a value between 1 and 40", 41)', + 'msg': ( + 'Value error, ' + '("Value \'numero_linea_ref\' must be a value between 1 and 40", 41)' + ), 'type': 'value_error', } ], @@ -782,33 +850,41 @@ def test_init_fail_tipo_documento_ref_invalid(self) -> None: obj = self.obj_1 - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, tipo_documento_ref="8001", ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('tipo_documento_ref',), - 'msg': '("The length of \'tipo_documento_ref\' must be a ' + 'msg': 'Value error, ("The length of \'tipo_documento_ref\' must be a ' 'value between 1 and 3", \'8001\')', 'type': 'value_error', } ], ) - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, tipo_documento_ref="2BAD", ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('tipo_documento_ref',), - 'msg': '("The length of \'tipo_documento_ref\' must be a value ' + 'msg': 'Value error, ("The length of \'tipo_documento_ref\' must be a value ' 'between 1 and 3", \'2BAD\')', 'type': 'value_error', }, @@ -820,17 +896,24 @@ def test_init_fail_ind_global_invalid(self) -> None: obj = self.obj_1 - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, ind_global=2, ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('ind_global',), - 'msg': '("Only the value \'1\' is valid for the field \'ind_global\'", 2)', + 'msg': ( + 'Value error, ' + '("Only the value \'1\' is valid for the field \'ind_global\'", 2)' + ), 'type': 'value_error', } ], @@ -841,17 +924,24 @@ def test_init_fail_folio_ref_empty(self) -> None: obj = self.obj_2 - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, folio_ref="", ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('folio_ref',), - 'msg': '("The length of \'folio_ref\' must be a value between 1 and 18", \'\')', + 'msg': ( + 'Value error, ' + '("The length of \'folio_ref\' must be a value between 1 and 18", \'\')' + ), 'type': 'value_error', } ], @@ -862,34 +952,45 @@ def test_init_fail_fecha_ref_out_of_range(self) -> None: obj = self.obj_1 - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_ref=date(2002, 7, 31), ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('fecha_ref',), - 'msg': '("The date \'fecha_ref\' must be after 2002-08-01 and ' + 'msg': 'Value error, ("The date \'fecha_ref\' must be after 2002-08-01 and ' 'before 2050-12-31", datetime.date(2002, 7, 31))', 'type': 'value_error', } ], ) - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_ref=date(2051, 1, 1), ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('fecha_ref',), - 'msg': '("The date \'fecha_ref\' must be after 2002-08-01 and ' - 'before 2050-12-31", datetime.date(2051, 1, 1))', + 'msg': ( + 'Value error, (' + '"The date \'fecha_ref\' must be after 2002-08-01 and ' + 'before 2050-12-31", datetime.date(2051, 1, 1))' + ), 'type': 'value_error', }, ], @@ -900,7 +1001,7 @@ def test_init_fail_razon_ref_too_long(self) -> None: obj = self.obj_1 - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, razon_ref=( @@ -909,13 +1010,20 @@ def test_init_fail_razon_ref_too_long(self) -> None: ), ) self.assertEqual( - assert_raises_cm.exception.errors(), + assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ), [ { 'loc': ('razon_ref',), - 'msg': "('The maximum length allowed for `razon_ref` is 90', " - "'Lorem ipsum dolor sit amet, consectetur adipiscing elit. " - "Sed metus magna, ultricies sit amet dolor sed')", + 'msg': ( + "Value error, " + "('The maximum length allowed for `razon_ref` is 90', " + "'Lorem ipsum dolor sit amet, consectetur adipiscing elit. " + "Sed metus magna, ultricies sit amet dolor sed')" + ), 'type': 'value_error', } ], @@ -1060,103 +1168,116 @@ def test_validate_emisor_razon_social_empty(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_razon_social',), - 'msg': "Value must not be empty.", + 'msg': "Value error, Value must not be empty.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, emisor_razon_social='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_receptor_razon_social_empty(self) -> None: expected_validation_errors = [ { 'loc': ('receptor_razon_social',), - 'msg': "Value must not be empty.", + 'msg': "Value error, Value must not be empty.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, receptor_razon_social='', ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_emisor_razon_social_none(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_razon_social',), - 'msg': "none is not an allowed value", - 'type': 'type_error.none.not_allowed', + 'msg': 'Input should be a valid string', + 'type': 'string_type', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, emisor_razon_social=None, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_receptor_razon_social_none(self) -> None: expected_validation_errors = [ { 'loc': ('receptor_razon_social',), - 'msg': "none is not an allowed value", - 'type': 'type_error.none.not_allowed', + 'msg': 'Input should be a valid string', + 'type': 'string_type', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, receptor_razon_social=None, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_datetime_tz(self) -> None: # Test TZ-awareness: - expected_validation_errors = [ { 'loc': ('firma_documento_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, firma_documento_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -1164,7 +1285,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('firma_documento_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -1173,7 +1294,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, firma_documento_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -1182,10 +1303,13 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_init_fail_regression_signature_value_bytes_with_x20(self) -> None: bytes_value_with_x20_as_base64 = 'IN2pkDBxqDnGl4Pfvboi' @@ -1212,21 +1336,24 @@ def test_validate_non_empty_bytes_signature_value(self) -> None: expected_validation_errors = [ { 'loc': ('signature_value',), - 'msg': 'Bytes value length is 0.', + 'msg': 'Value error, Bytes value length is 0.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, signature_value=b'', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_init_fail_regression_signature_cert_der_bytes_with_x20(self) -> None: bytes_value_with_x20_as_base64 = 'IN2pkDBxqDnGl4Pfvboi' @@ -1253,48 +1380,57 @@ def test_validate_non_empty_bytes_signature_x509_cert_der(self) -> None: expected_validation_errors = [ { 'loc': ('signature_x509_cert_der',), - 'msg': 'Bytes value length is 0.', + 'msg': 'Value error, Bytes value length is 0.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, signature_x509_cert_der=b'', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_no_leading_or_trailing_whitespace_characters_emisor_giro(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_giro',), - 'msg': "('Value has leading or trailing whitespace characters.', ' NASA ')", + 'msg': ( + "Value error, " + "('Value has leading or trailing whitespace characters.', ' NASA ')" + ), 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, emisor_giro=' NASA ', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_no_leading_or_trailing_whitespace_characters_emisor_email(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_email',), 'msg': ( - "(" + "Value error, (" "'Value has leading or trailing whitespace characters.', " "' fake_emisor_email@test.cl '" ")" @@ -1303,23 +1439,26 @@ def test_validate_no_leading_or_trailing_whitespace_characters_emisor_email(self }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, emisor_email=' fake_emisor_email@test.cl ', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_no_leading_or_trailing_whitespace_characters_receptor_email(self) -> None: expected_validation_errors = [ { 'loc': ('receptor_email',), 'msg': ( - "(" + "Value error, (" "'Value has leading or trailing whitespace characters.', " "' fake_receptor_email@test.cl '" ")" @@ -1328,76 +1467,88 @@ def test_validate_no_leading_or_trailing_whitespace_characters_receptor_email(se }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, receptor_email=' fake_receptor_email@test.cl ', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_non_empty_stripped_str_emisor_giro(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_giro',), - 'msg': "String value length (stripped) is 0.", + 'msg': "Value error, String value length (stripped) is 0.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, emisor_giro='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_non_empty_stripped_str_emisor_email(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_email',), - 'msg': "String value length (stripped) is 0.", + 'msg': "Value error, String value length (stripped) is 0.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, emisor_email='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_non_empty_stripped_str_receptor_email(self) -> None: expected_validation_errors = [ { 'loc': ('receptor_email',), - 'msg': "String value length (stripped) is 0.", + 'msg': "Value error, String value length (stripped) is 0.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.dte_xml_data_1, receptor_email='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_as_dict(self) -> None: self.assertDictEqual( @@ -1548,21 +1699,24 @@ def test_validate_referencias_numero_linea_ref_order(self) -> None: expected_validation_errors = [ { 'loc': ('referencias',), - 'msg': "items must be ordered according to their 'numero_linea_ref'", + 'msg': "Value error, items must be ordered according to their 'numero_linea_ref'", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, referencias=list(reversed(obj.referencias)), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_referencias_rut_otro_is_consistent_with_tipo_dte(self) -> None: obj = self.dte_xml_data_2 @@ -1579,24 +1733,30 @@ def test_validate_referencias_rut_otro_is_consistent_with_tipo_dte(self) -> None expected_validation_errors = [ { - 'loc': ('__root__',), - 'msg': "Setting a 'rut_otro' is not a valid option for this 'tipo_dte':" - " 'tipo_dte' == ," - " 'Referencia' number 1.", + 'loc': (), + 'msg': ( + "Value error, " + "Setting a 'rut_otro' is not a valid option for this 'tipo_dte':" + " 'tipo_dte' == ," + " 'Referencia' number 1." + ), 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, referencias=[obj_referencia], ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_referencias_rut_otro_is_consistent_with_emisor_rut(self) -> None: obj = self.dte_xml_data_2 @@ -1617,24 +1777,30 @@ def test_validate_referencias_rut_otro_is_consistent_with_emisor_rut(self) -> No expected_validation_errors = [ { - 'loc': ('__root__',), - 'msg': "'rut_otro' must be different from 'emisor_rut':" - " Rut('60910000-1') == Rut('60910000-1')," - " 'Referencia' number 1.", + 'loc': (), + 'msg': ( + "Value error, " + "'rut_otro' must be different from 'emisor_rut':" + " Rut('60910000-1') == Rut('60910000-1')," + " 'Referencia' number 1." + ), 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, referencias=[obj_referencia], ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_referencias_codigo_ref_is_consistent_with_tipo_dte(self) -> None: obj = self.dte_xml_data_3 @@ -1645,24 +1811,27 @@ def test_validate_referencias_codigo_ref_is_consistent_with_tipo_dte(self) -> No expected_validation_errors = [ { - 'loc': ('__root__',), - 'msg': "'codigo_ref' is mandatory for this 'tipo_dte':" + 'loc': (), + 'msg': "Value error, 'codigo_ref' is mandatory for this 'tipo_dte':" " 'tipo_dte' == ," " 'Referencia' number 1.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, referencias=[obj_referencia], ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) class FunctionsTest(unittest.TestCase): diff --git a/src/tests/test_rcv_data_models.py b/src/tests/test_rcv_data_models.py index c842c0e3..91004e87 100644 --- a/src/tests/test_rcv_data_models.py +++ b/src/tests/test_rcv_data_models.py @@ -2,7 +2,7 @@ import unittest from datetime import date, datetime -import pydantic.v1 +import pydantic import cl_sii.dte.constants from cl_sii.base.constants import SII_OFFICIAL_TZ @@ -33,55 +33,64 @@ def test_validate_year_range(self) -> None: expected_validation_errors = [ { 'loc': ('year',), - 'msg': "Value is out of the valid range for 'year'.", + 'msg': "Value error, Value is out of the valid range for 'year'.", 'type': 'value_error', }, ] # Validate the minimum value of the field year - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.periodo_tributario_1, year=1899, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_month_range(self) -> None: expected_validation_errors = [ { 'loc': ('month',), - 'msg': "Value is out of the valid range for 'month'.", + 'msg': "Value error, Value is out of the valid range for 'month'.", 'type': 'value_error', }, ] # Validate the minimum value of the field month - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.periodo_tributario_1, month=0, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Validate the maximum value of the field month - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.periodo_tributario_1, month=13, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) class RcvDetalleEntryTest(unittest.TestCase): @@ -105,34 +114,40 @@ def test_validate_folio_range(self) -> None: expected_validation_errors = [ { 'loc': ('folio',), - 'msg': "Value is out of the valid range for 'folio'.", + 'msg': "Value error, Value is out of the valid range for 'folio'.", 'type': 'value_error', }, ] # Validate the minimum value of the field folio - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rcv_detalle_entry_1, folio=cl_sii.dte.constants.DTE_FOLIO_FIELD_MIN_VALUE - 1, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Validate the maximum value of the field folio - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rcv_detalle_entry_1, folio=cl_sii.dte.constants.DTE_FOLIO_FIELD_MAX_VALUE + 1, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_datetime_tz(self) -> None: # Test TZ-awareness: @@ -140,21 +155,24 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('fecha_recepcion_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rcv_detalle_entry_1, fecha_recepcion_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -162,7 +180,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_recepcion_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -171,7 +189,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rcv_detalle_entry_1, fecha_recepcion_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -180,10 +198,13 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) class RvDetalleEntryTest(unittest.TestCase): @@ -222,21 +243,23 @@ def test_validate_receptor_razon_social_empty(self) -> None: expected_validation_errors = [ { 'loc': ('receptor_razon_social',), - 'msg': "Value must not be empty.", + 'msg': "Value error, Value must not be empty.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rv_detalle_entry_1, receptor_razon_social='', ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_datetime_tz(self) -> None: # fecha_acuse_dt @@ -245,21 +268,24 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('fecha_acuse_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rv_detalle_entry_1, fecha_acuse_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -267,7 +293,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_acuse_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -276,7 +302,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rv_detalle_entry_1, fecha_acuse_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -285,10 +311,13 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # fecha_reclamo_dt # Test TZ-awareness: @@ -296,21 +325,24 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('fecha_reclamo_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rv_detalle_entry_1, fecha_reclamo_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -318,7 +350,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_reclamo_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -327,7 +359,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rv_detalle_entry_1, fecha_reclamo_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -336,10 +368,13 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) class RcRegistroDetalleEntryTest(unittest.TestCase): @@ -374,21 +409,24 @@ def test_validate_emisor_razon_social_empty(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_razon_social',), - 'msg': "Value must not be empty.", + 'msg': "Value error, Value must not be empty.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rc_registro_detalle_entry_1, emisor_razon_social='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_datetime_tz(self) -> None: # Test TZ-awareness: @@ -396,21 +434,24 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('fecha_acuse_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rc_registro_detalle_entry_1, fecha_acuse_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -418,7 +459,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_acuse_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -427,7 +468,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rc_registro_detalle_entry_1, fecha_acuse_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -436,10 +477,13 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) class RcNoIncluirDetalleEntryTest(unittest.TestCase): @@ -497,21 +541,24 @@ def test_validate_emisor_razon_social_empty(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_razon_social',), - 'msg': "Value must not be empty.", + 'msg': "Value error, Value must not be empty.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rc_reclamado_detalle_entry_1, emisor_razon_social='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_datetime_tz(self) -> None: # Test TZ-awareness: @@ -519,21 +566,24 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('fecha_reclamo_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rc_reclamado_detalle_entry_1, fecha_reclamo_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -541,7 +591,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_reclamo_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -550,7 +600,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rc_reclamado_detalle_entry_1, fecha_reclamo_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -559,10 +609,13 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) class RcPendienteDetalleEntryTest(unittest.TestCase): @@ -587,18 +640,21 @@ def test_validate_emisor_razon_social_empty(self) -> None: expected_validation_errors = [ { 'loc': ('emisor_razon_social',), - 'msg': "Value must not be empty.", + 'msg': "Value error, Value must not be empty.", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( self.rc_pendiente_detalle_entry_1, emisor_razon_social='', ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) diff --git a/src/tests/test_rtc_data_models.py b/src/tests/test_rtc_data_models.py index dce89cac..e4bd225c 100644 --- a/src/tests/test_rtc_data_models.py +++ b/src/tests/test_rtc_data_models.py @@ -4,7 +4,7 @@ import unittest from datetime import date, datetime -import pydantic.v1 +import pydantic from cl_sii.dte.constants import TipoDte from cl_sii.dte.data_models import DteDataL1, DteDataL2, DteNaturalKey @@ -40,7 +40,7 @@ def _set_obj_1(self) -> None: self.obj_1 = obj def test_create_new_empty_instance(self) -> None: - with self.assertRaises(TypeError): + with self.assertRaises(pydantic.ValidationError): CesionNaturalKey() def test_str_and_repr(self) -> None: @@ -87,11 +87,14 @@ def test_validate_dte_tipo_dte(self) -> None: obj = self.obj_1 expected_validation_error = { 'loc': ('dte_key',), - 'msg': """('Value is not "cedible".', )""", + 'msg': ( + 'Value error, ' + """('Value is not "cedible".', )""" + ), 'type': 'value_error', } - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, dte_key=dataclasses.replace( @@ -100,8 +103,12 @@ def test_validate_dte_tipo_dte(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, [expected_validation_error]) def test_validate_seq(self) -> None: self._set_obj_1() @@ -112,18 +119,22 @@ def test_validate_seq(self) -> None: for test_value in test_values: expected_validation_error = { 'loc': ('seq',), - 'msg': f"""('Value is out of the valid range.', {test_value})""", + 'msg': f"""Value error, ('Value is out of the valid range.', {test_value})""", 'type': 'value_error', } - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, seq=test_value, ) - validation_errors = assert_raises_cm.exception.errors() - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, [expected_validation_error]) class CesionAltNaturalKeyTest(unittest.TestCase): @@ -153,7 +164,7 @@ def _set_obj_1(self) -> None: self.obj_1 = obj def test_create_new_empty_instance(self) -> None: - with self.assertRaises(TypeError): + with self.assertRaises(pydantic.ValidationError): CesionAltNaturalKey() def test_str_and_repr(self) -> None: @@ -207,11 +218,14 @@ def test_validate_dte_tipo_dte(self) -> None: obj = self.obj_1 expected_validation_error = { 'loc': ('dte_key',), - 'msg': """('Value is not "cedible".', )""", + 'msg': ( + 'Value error, ' + """('Value is not "cedible".', )""" + ), 'type': 'value_error', } - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, dte_key=dataclasses.replace( @@ -220,8 +234,12 @@ def test_validate_dte_tipo_dte(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, [expected_validation_error]) def test_validate_datetime_tz(self) -> None: self._set_obj_1() @@ -232,25 +250,29 @@ def test_validate_datetime_tz(self) -> None: expected_validation_error = { 'loc': ('fecha_cesion_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', } - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_cesion_dt=datetime(2019, 4, 5, 12, 57), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, [expected_validation_error]) # Test TZ-value: expected_validation_error = { 'loc': ('fecha_cesion_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, tzinfo=)' ')' @@ -258,7 +280,7 @@ def test_validate_datetime_tz(self) -> None: 'type': 'value_error', } - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_cesion_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -267,8 +289,12 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, [expected_validation_error]) def test_truncate_fecha_cesion_dt_to_minutes(self) -> None: self._set_obj_1() @@ -325,7 +351,7 @@ def _set_obj_1(self) -> None: self.obj_1 = obj def test_create_new_empty_instance(self) -> None: - with self.assertRaises(TypeError): + with self.assertRaises(pydantic.ValidationError): CesionL0() def test_str_and_repr(self) -> None: @@ -427,12 +453,15 @@ def test_validate_dte_tipo_dte(self) -> None: expected_validation_errors = [ { 'loc': ('dte_key',), - 'msg': """('Value is not "cedible".', )""", + 'msg': ( + 'Value error, ' + """('Value is not "cedible".', )""" + ), 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, dte_key=dataclasses.replace( @@ -441,10 +470,12 @@ def test_validate_dte_tipo_dte(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_seq(self) -> None: self._set_obj_1() @@ -456,21 +487,23 @@ def test_validate_seq(self) -> None: expected_validation_errors = [ { 'loc': ('seq',), - 'msg': f"""('Value is out of the valid range.', {test_value})""", + 'msg': f"""Value error, ('Value is out of the valid range.', {test_value})""", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, seq=test_value, ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_datetime_tz(self) -> None: self._set_obj_1() @@ -482,21 +515,23 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('fecha_cesion_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_cesion_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -504,7 +539,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_cesion_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -513,7 +548,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_cesion_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -522,10 +557,12 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) class CesionL1Test(CesionL0Test): @@ -561,7 +598,7 @@ def _set_obj_1(self) -> None: self.obj_1 = obj def test_create_new_empty_instance(self) -> None: - with self.assertRaises(TypeError): + with self.assertRaises(pydantic.ValidationError): CesionL1() def test_str_and_repr(self) -> None: @@ -658,21 +695,23 @@ def test_validate_monto_cedido(self) -> None: expected_validation_errors = [ { 'loc': ('monto_cedido',), - 'msg': f"""('Value is out of the valid range.', {test_value})""", + 'msg': f"""Value error, ('Value is out of the valid range.', {test_value})""", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, monto_cedido=test_value, ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_monto_cedido_does_not_exceed_dte_monto_total(self) -> None: self._set_obj_1() @@ -680,23 +719,25 @@ def test_validate_monto_cedido_does_not_exceed_dte_monto_total(self) -> None: obj = self.obj_1 expected_validation_errors = [ { - 'loc': ('__root__',), - 'msg': """('Value of "cesión" must be <= value of DTE.', 1000, 999)""", + 'loc': (), + 'msg': """Value error, ('Value of "cesión" must be <= value of DTE.', 1000, 999)""", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, monto_cedido=1000, dte_monto_total=999, ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) class CesionL2Test(CesionL1Test): @@ -753,7 +794,7 @@ def _set_obj_1(self) -> None: self.obj_1 = obj def test_create_new_empty_instance(self) -> None: - with self.assertRaises(TypeError): + with self.assertRaises(pydantic.ValidationError): CesionL2() def test_str_and_repr(self) -> None: @@ -885,27 +926,29 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('fecha_cesion_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, { 'loc': ('fecha_firma_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_cesion_dt=datetime(2019, 4, 5, 12, 57, 32), fecha_firma_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -913,7 +956,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_cesion_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -923,7 +966,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_firma_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -932,7 +975,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_cesion_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -945,10 +988,12 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_contribuyente_razon_social(self) -> None: self._set_obj_1() @@ -957,29 +1002,27 @@ def test_validate_contribuyente_razon_social(self) -> None: expected_validation_errors = [ { 'loc': ('cedente_razon_social',), - 'msg': 'ensure this value has at least 1 characters', - 'type': 'value_error.any_str.min_length', - 'ctx': {'limit_value': 1}, + 'msg': 'String should have at least 1 characters', + 'type': 'string_too_short', }, { 'loc': ('cesionario_razon_social',), - 'msg': 'Value exceeds max allowed length.', + 'msg': 'Value error, Value exceeds max allowed length.', 'type': 'value_error', }, { 'loc': ('dte_emisor_razon_social',), - 'msg': 'ensure this value has at least 1 characters', - 'type': 'value_error.any_str.min_length', - 'ctx': {'limit_value': 1}, + 'msg': 'String should have at least 1 characters', + 'type': 'string_too_short', }, { 'loc': ('dte_receptor_razon_social',), - 'msg': 'Value exceeds max allowed length.', + 'msg': 'Value error, Value exceeds max allowed length.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, cedente_razon_social='', @@ -988,7 +1031,9 @@ def test_validate_contribuyente_razon_social(self) -> None: dte_receptor_razon_social='R' * 200, ) - validation_errors = assert_raises_cm.exception.errors() - self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) + self.assertEqual(validation_errors, expected_validation_errors) diff --git a/src/tests/test_rtc_data_models_aec.py b/src/tests/test_rtc_data_models_aec.py index 61e2c6c2..9f7a3854 100644 --- a/src/tests/test_rtc_data_models_aec.py +++ b/src/tests/test_rtc_data_models_aec.py @@ -4,7 +4,7 @@ import unittest from datetime import date, datetime -import pydantic.v1 +import pydantic from cl_sii.dte.constants import TipoDte from cl_sii.dte.data_models import DteDataL1, DteNaturalKey, DteXmlData @@ -101,7 +101,7 @@ def _set_obj_2(self) -> None: self.obj_2 = obj def test_create_new_empty_instance(self) -> None: - with self.assertRaises(TypeError): + with self.assertRaises(pydantic.ValidationError): CesionAecXml() def test_natural_key(self) -> None: @@ -351,7 +351,7 @@ def _set_obj_1(self) -> None: self.obj_1_cesion_2 = obj_cesion_2 def test_create_new_empty_instance(self) -> None: - with self.assertRaises(TypeError): + with self.assertRaises(pydantic.ValidationError): AecXml() def test_natural_key(self) -> None: @@ -459,12 +459,15 @@ def test_validate_dte_tipo_dte(self) -> None: expected_validation_errors = [ { 'loc': ('dte',), - 'msg': """('Value is not "cedible".', )""", + 'msg': ( + "Value error, " + """('Value is not "cedible".', )""" + ), 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, dte=dataclasses.replace( @@ -473,10 +476,13 @@ def test_validate_dte_tipo_dte(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_datetime_tz(self) -> None: self._set_obj_1() @@ -488,21 +494,24 @@ def test_validate_datetime_tz(self) -> None: expected_validation_errors = [ { 'loc': ('fecha_firma_dt',), - 'msg': 'Value must be a timezone-aware datetime object.', + 'msg': 'Value error, Value must be a timezone-aware datetime object.', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_firma_dt=datetime(2019, 4, 5, 12, 57, 32), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # Test TZ-value: @@ -510,7 +519,7 @@ def test_validate_datetime_tz(self) -> None: { 'loc': ('fecha_firma_dt',), 'msg': ( - '(' + 'Value error, (' '''"Timezone of datetime value must be 'America/Santiago'.",''' ' datetime.datetime(2019, 4, 5, 12, 57, 32, tzinfo=)' ')' @@ -519,7 +528,7 @@ def test_validate_datetime_tz(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, fecha_firma_dt=tz_utils.convert_naive_dt_to_tz_aware( @@ -528,10 +537,13 @@ def test_validate_datetime_tz(self) -> None: ), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_cesiones_min_items(self) -> None: self._set_obj_1() @@ -541,21 +553,24 @@ def test_validate_cesiones_min_items(self) -> None: expected_validation_errors = [ { 'loc': ('cesiones',), - 'msg': 'must contain at least one item', + 'msg': 'Value error, must contain at least one item', 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, cesiones=[], ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_cesiones_seq_order(self) -> None: self._set_obj_1() @@ -565,21 +580,24 @@ def test_validate_cesiones_seq_order(self) -> None: expected_validation_errors = [ { 'loc': ('cesiones',), - 'msg': "items must be ordered according to their 'seq'", + 'msg': "Value error, items must be ordered according to their 'seq'", 'type': 'value_error', }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, cesiones=list(reversed(obj.cesiones)), ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) # def test_validate_cesiones_monto_cesion_must_not_increase(self) -> None: # self._set_obj_1() @@ -620,8 +638,9 @@ def test_validate_dte_matches_cesiones_dtes(self) -> None: expected_validation_errors = [ { - 'loc': ('__root__',), + 'loc': (), 'msg': ( + "Value error, " "'dte' of CesionAecXml with CesionNaturalKey(" "dte_key=DteNaturalKey(" "emisor_rut=Rut('76354771-K')," @@ -639,7 +658,7 @@ def test_validate_dte_matches_cesiones_dtes(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, cesiones=[ @@ -654,10 +673,13 @@ def test_validate_dte_matches_cesiones_dtes(self) -> None: ], ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors) def test_validate_last_cesion_matches_some_fields(self) -> None: self._set_obj_1() @@ -666,8 +688,9 @@ def test_validate_last_cesion_matches_some_fields(self) -> None: expected_validation_errors = [ { - 'loc': ('__root__',), + 'loc': (), 'msg': ( + "Value error, " "'cedente_rut' of last 'cesion' must match 'cedente_rut':" " Rut('76389992-6')" " !=" @@ -677,13 +700,16 @@ def test_validate_last_cesion_matches_some_fields(self) -> None: }, ] - with self.assertRaises(pydantic.v1.ValidationError) as assert_raises_cm: + with self.assertRaises(pydantic.ValidationError) as assert_raises_cm: dataclasses.replace( obj, cedente_rut=obj.cesionario_rut, ) - validation_errors = assert_raises_cm.exception.errors() + validation_errors = assert_raises_cm.exception.errors( + include_context=False, + include_input=False, + include_url=False, + ) self.assertEqual(len(validation_errors), len(expected_validation_errors)) - for expected_validation_error in expected_validation_errors: - self.assertIn(expected_validation_error, validation_errors) + self.assertEqual(validation_errors, expected_validation_errors)