Skip to content

Commit ea5f5e6

Browse files
Merge pull request #426 from fyntex/task/COMPCLDATA-201-update-marshmallow-to-major-version-3
chore(deps): Bump marshmallow from 2.21.0 to 3.19.0
2 parents 871426b + 6ff9db7 commit ea5f5e6

File tree

10 files changed

+382
-369
lines changed

10 files changed

+382
-369
lines changed

cl_sii/extras/mm_fields.py

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44
(for serializers)
55
66
"""
7+
from __future__ import annotations
8+
9+
710
try:
811
import marshmallow
912
except ImportError as exc: # pragma: no cover
1013
raise ImportError("Package 'marshmallow' is required to use this module.") from exc
1114

1215
import datetime
13-
from typing import Optional
16+
from typing import Any, Mapping, Optional
1417

1518
import marshmallow.fields
1619

@@ -46,13 +49,18 @@ class RutField(marshmallow.fields.Field):
4649

4750
default_error_messages = {
4851
'invalid': 'Not a syntactically valid RUT.',
52+
'type': 'Invalid type.',
4953
}
5054

51-
def _serialize(self, value: Optional[object], attr: str, obj: object) -> Optional[str]:
55+
def _serialize(
56+
self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any
57+
) -> Optional[str]:
5258
validated = self._validated(value)
5359
return validated.canonical if validated is not None else None
5460

55-
def _deserialize(self, value: str, attr: str, data: dict) -> Optional[Rut]:
61+
def _deserialize(
62+
self, value: str, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any
63+
) -> Optional[Rut]:
5664
return self._validated(value)
5765

5866
def _validated(self, value: Optional[object]) -> Optional[Rut]:
@@ -61,10 +69,10 @@ def _validated(self, value: Optional[object]) -> Optional[Rut]:
6169
else:
6270
try:
6371
validated = Rut(value, validate_dv=False) # type: ignore
64-
except TypeError:
65-
self.fail('type')
66-
except ValueError:
67-
self.fail('invalid')
72+
except TypeError as exc:
73+
raise self.make_error('type') from exc
74+
except ValueError as exc:
75+
raise self.make_error('invalid') from exc
6876
return validated
6977

7078

@@ -89,13 +97,18 @@ class TipoDteField(marshmallow.fields.Field):
8997

9098
default_error_messages = {
9199
'invalid': 'Not a valid Tipo DTE.',
100+
'type': 'Invalid type.',
92101
}
93102

94-
def _serialize(self, value: Optional[object], attr: str, obj: object) -> Optional[int]:
103+
def _serialize(
104+
self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any
105+
) -> Optional[int]:
95106
validated: Optional[TipoDte] = self._validated(value)
96107
return validated.value if validated is not None else None
97108

98-
def _deserialize(self, value: object, attr: str, data: dict) -> Optional[TipoDte]:
109+
def _deserialize(
110+
self, value: object, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any
111+
) -> Optional[TipoDte]:
99112
return self._validated(value)
100113

101114
def _validated(self, value: Optional[object]) -> Optional[TipoDte]:
@@ -104,21 +117,21 @@ def _validated(self, value: Optional[object]) -> Optional[TipoDte]:
104117
else:
105118
if isinstance(value, bool):
106119
# is value is bool, `isinstance(value, int)` is True and `int(value)` works!
107-
self.fail('type')
120+
raise self.make_error('type')
108121
try:
109122
value = int(value) # type: ignore
110-
except ValueError:
123+
except ValueError as exc:
111124
# `int('x')` raises 'ValueError', not 'TypeError'
112-
self.fail('type')
113-
except TypeError:
125+
raise self.make_error('type') from exc
126+
except TypeError as exc:
114127
# `int(date(2018, 10, 10))` raises 'TypeError', unlike `int('x')`
115-
self.fail('type')
128+
raise self.make_error('type') from exc
116129

117130
try:
118131
validated = TipoDte(value) # type: ignore
119-
except ValueError:
132+
except ValueError as exc:
120133
# TipoDte('x') raises 'ValueError', not 'TypeError'
121-
self.fail('invalid')
134+
raise self.make_error('invalid') from exc
122135
return validated
123136

124137

@@ -142,13 +155,18 @@ class RcvTipoDoctoField(marshmallow.fields.Field):
142155

143156
default_error_messages = {
144157
'invalid': "Not a valid RCV's Tipo de Documento.",
158+
'type': "Invalid type.",
145159
}
146160

147-
def _serialize(self, value: Optional[object], attr: str, obj: object) -> Optional[int]:
161+
def _serialize(
162+
self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any
163+
) -> Optional[int]:
148164
validated: Optional[RcvTipoDocto] = self._validated(value)
149165
return validated.value if validated is not None else None
150166

151-
def _deserialize(self, value: object, attr: str, data: dict) -> Optional[RcvTipoDocto]:
167+
def _deserialize(
168+
self, value: object, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any
169+
) -> Optional[RcvTipoDocto]:
152170
return self._validated(value)
153171

154172
def _validated(self, value: Optional[object]) -> Optional[RcvTipoDocto]:
@@ -157,21 +175,21 @@ def _validated(self, value: Optional[object]) -> Optional[RcvTipoDocto]:
157175
else:
158176
if isinstance(value, bool):
159177
# is value is bool, `isinstance(value, int)` is True and `int(value)` works!
160-
self.fail('type')
178+
raise self.make_error('type')
161179
try:
162180
value = int(value) # type: ignore
163-
except ValueError:
181+
except ValueError as exc:
164182
# `int('x')` raises 'ValueError', not 'TypeError'
165-
self.fail('type')
166-
except TypeError:
183+
raise self.make_error('type') from exc
184+
except TypeError as exc:
167185
# `int(date(2018, 10, 10))` raises 'TypeError', unlike `int('x')`
168-
self.fail('type')
186+
raise self.make_error('type') from exc
169187

170188
try:
171189
validated = RcvTipoDocto(value) # type: ignore
172-
except ValueError:
190+
except ValueError as exc:
173191
# RcvTipoDocto('x') raises 'ValueError', not 'TypeError'
174-
self.fail('invalid')
192+
raise self.make_error('invalid') from exc
175193
return validated
176194

177195

@@ -186,14 +204,19 @@ class RcvPeriodoTributarioField(marshmallow.fields.Field):
186204

187205
default_error_messages = {
188206
'invalid': "Not a valid RCV Periodo Tributario.",
207+
'type': "Invalid type.",
189208
}
190209
_string_format = '%Y-%m' # Example: '2019-12'
191210

192-
def _serialize(self, value: Optional[object], attr: str, obj: object) -> Optional[str]:
211+
def _serialize(
212+
self, value: Optional[object], attr: str | None, obj: object, **kwargs: Any
213+
) -> Optional[str]:
193214
validated: Optional[RcvPeriodoTributario] = self._validated(value)
194215
return validated.as_date().strftime(self._string_format) if validated is not None else None
195216

196-
def _deserialize(self, value: object, attr: str, data: dict) -> Optional[RcvPeriodoTributario]:
217+
def _deserialize(
218+
self, value: object, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any
219+
) -> Optional[RcvPeriodoTributario]:
197220
return self._validated(value)
198221

199222
def _validated(self, value: Optional[object]) -> Optional[RcvPeriodoTributario]:
@@ -203,10 +226,10 @@ def _validated(self, value: Optional[object]) -> Optional[RcvPeriodoTributario]:
203226
try:
204227
value = datetime.datetime.strptime(value, self._string_format) # type: ignore
205228
value = value.date()
206-
except ValueError:
207-
self.fail('invalid')
208-
except TypeError:
209-
self.fail('type')
229+
except ValueError as exc:
230+
raise self.make_error('invalid') from exc
231+
except TypeError as exc:
232+
raise self.make_error('type') from exc
210233

211234
validated = RcvPeriodoTributario.from_date(value) # type: ignore
212235

cl_sii/libs/mm_utils.py

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from __future__ import annotations
2+
13
from datetime import date, datetime
2-
from typing import Any, Optional, Union
4+
from typing import Any, Mapping, Optional, Union
35

46
import marshmallow
57
import marshmallow.fields
@@ -22,10 +24,6 @@ def validate_no_unexpected_input_fields(
2224
Usage::
2325
2426
class MySchema(marshmallow.Schema):
25-
26-
class Meta:
27-
strict = True
28-
2927
folio = marshmallow.fields.Integer()
3028
3129
@marshmallow.validates_schema(pass_original=True)
@@ -36,7 +34,7 @@ def validate_schema(self, data: dict, original_data: dict) -> None:
3634
# Original inspiration from
3735
# https://marshmallow.readthedocs.io/en/2.x-line/extending.html#validating-original-input-data
3836
fields_name_or_load_from = {
39-
field.name if field.load_from is None else field.load_from
37+
field.name if field.data_key is None else field.data_key
4038
for field_key, field in schema.fields.items()
4139
}
4240
unexpected_input_fields = set(original_data) - fields_name_or_load_from
@@ -98,41 +96,45 @@ def __init__(self, format: Optional[str] = None, **kwargs: Any) -> None:
9896
# TODO: for 'marshmallow 3', rename 'dateformat' to 'datetimeformat'.
9997
self.dateformat = format
10098

101-
def _add_to_schema(self, field_name: str, schema: marshmallow.Schema) -> None:
102-
super()._add_to_schema(field_name, schema)
99+
def _bind_to_schema(self, field_name: str, schema: marshmallow.Schema) -> None:
100+
super()._bind_to_schema(field_name, schema)
103101
self.dateformat = self.dateformat or schema.opts.dateformat
104102

105-
def _serialize(self, value: date, attr: str, obj: object) -> Union[str, None]:
103+
def _serialize(
104+
self, value: date, attr: str | None, obj: object, **kwargs: Any
105+
) -> Union[str, None]:
106106
if value is None:
107107
return None
108108
self.dateformat = self.dateformat or self.DEFAULT_FORMAT
109109
format_func = self.DATEFORMAT_SERIALIZATION_FUNCS.get(self.dateformat, None)
110110
if format_func:
111111
try:
112112
date_str = format_func(value)
113-
except (AttributeError, ValueError):
114-
self.fail('format', input=value)
113+
except (AttributeError, ValueError) as exc:
114+
raise self.make_error('format', input=value) from exc
115115
else:
116116
date_str = value.strftime(self.dateformat)
117117

118118
return date_str
119119

120-
def _deserialize(self, value: str, attr: str, data: dict) -> date:
120+
def _deserialize(
121+
self, value: str, attr: str | None, data: Mapping[str, Any] | None, **kwargs: Any
122+
) -> date:
121123
if not value: # Falsy values, e.g. '', None, [] are not valid
122-
self.fail('invalid')
124+
raise self.make_error('invalid')
123125
self.dateformat = self.dateformat or self.DEFAULT_FORMAT
124126
func = self.DATEFORMAT_DESERIALIZATION_FUNCS.get(self.dateformat)
125127
if func:
126128
try:
127129
date_value = func(value) # type: date
128-
except (TypeError, AttributeError, ValueError):
129-
self.fail('invalid')
130+
except (TypeError, AttributeError, ValueError) as exc:
131+
raise self.make_error('invalid') from exc
130132
elif self.dateformat:
131133
try:
132134
date_value = datetime.strptime(value, self.dateformat).date()
133-
except (TypeError, AttributeError, ValueError):
134-
self.fail('invalid')
135+
except (TypeError, AttributeError, ValueError) as exc:
136+
raise self.make_error('invalid') from exc
135137
else:
136-
self.fail('invalid')
138+
raise self.make_error('invalid')
137139

138140
return date_value

cl_sii/libs/rows_processing.py

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -122,36 +122,12 @@ def rows_mm_deserialization_iterator(
122122
row_data.pop(_field_name, None)
123123

124124
try:
125-
mm_result: marshmallow.UnmarshalResult = row_schema.load(row_data)
126-
deserialized_row_data: dict = mm_result.data
125+
deserialized_row_data: dict = row_schema.load(row_data)
127126
raised_validation_errors: dict = {}
128-
returned_validation_errors: dict = mm_result.errors
129127
except marshmallow.ValidationError as exc:
130128
deserialized_row_data = {}
131129
raised_validation_errors = dict(exc.normalized_messages())
132-
returned_validation_errors = {}
133130

134131
validation_errors = raised_validation_errors
135-
if returned_validation_errors:
136-
if row_schema.strict:
137-
# 'marshmallow.schema.BaseSchema':
138-
# > :param bool strict: If `True`, raise errors if invalid data are passed in
139-
# > instead of failing silently and storing the errors.
140-
logger.error(
141-
"Marshmallow schema is 'strict' but validation errors were returned by "
142-
"method 'load' ('UnmarshalResult.errors') instead of being raised. "
143-
"Errors: %s",
144-
repr(returned_validation_errors),
145-
)
146-
if raised_validation_errors:
147-
logger.fatal(
148-
"Programming error: either returned or raised validation errors "
149-
"(depending on 'strict') but never both. "
150-
"Returned errors: %s. Raised errors: %s",
151-
repr(returned_validation_errors),
152-
repr(raised_validation_errors),
153-
)
154-
155-
validation_errors.update(returned_validation_errors)
156132

157133
yield row_ix, row_data, deserialized_row_data, validation_errors

0 commit comments

Comments
 (0)