Custom Types#
Often, the behavior for a field needs to be customized to support a particular shape or validation method that ParamTools does not support out of the box. In this case, you may use the register_custom_type
function to add your new type
to the ParamTools type registry. Each type
has a corresponding field
that is used for serialization and deserialization. ParamTools will then use this field
any time it is handling a value
, label
, or member
that is of this type
.
ParamTools is built on top of marshmallow
, a general purpose validation library. This means that you must implement a custom marshmallow
field to go along with your new type. Please refer to the marshmallow
docs if you have questions about the use of marshmallow
in the examples below.
32 Bit Integer Example#
ParamTools’s default integer field uses NumPy’s int64
type. This example shows you how to define an int32
type and reference it in your defaults
.
First, let’s define the Marshmallow class:
import marshmallow as ma
import numpy as np
class Int32(ma.fields.Field):
"""
A custom type for np.int32.
https://numpy.org/devdocs/reference/arrays.dtypes.html
"""
# minor detail that makes this play nice with array_first
np_type = np.int32
def _serialize(self, value, *args, **kwargs):
"""Convert np.int32 to basic, serializable Python int."""
return value.tolist()
def _deserialize(self, value, *args, **kwargs):
"""Cast value from JSON to NumPy Int32."""
converted = np.int32(value)
return converted
Now, reference it in our defaults JSON/dict object:
import paramtools as pt
# add int32 type to the paramtools type registry
pt.register_custom_type("int32", Int32())
class Params(pt.Parameters):
defaults = {
"small_int": {
"title": "Small integer",
"description": "Demonstrate how to define a custom type",
"type": "int32",
"value": 2
}
}
params = Params(array_first=True)
print(f"value: {params.small_int}, type: {type(params.small_int)}")
value: 2, type: <class 'numpy.int32'>
One problem with this is that we could run into some deserialization issues. Due to integer overflow, our deserialized result is not the number that we passed in–it’s negative!
params.adjust(dict(
# this number wasn't chosen randomly.
small_int=2147483647 + 1
))
---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
Cell In[3], line 1
----> 1 params.adjust(dict(
2 # this number wasn't chosen randomly.
3 small_int=2147483647 + 1
4 ))
File ~/work/ParamTools/ParamTools/paramtools/parameters.py:257, in Parameters.adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, clobber)
210 def adjust(
211 self,
212 params_or_path: Union[str, Mapping[str, List[ValueObject]]],
(...) 216 clobber: bool = True,
217 ):
218 """
219 Deserialize and validate parameter adjustments. `params_or_path`
220 can be a file path or a `dict` that has not been fully deserialized.
(...) 255 least one existing value item's corresponding label values.
256 """
--> 257 return self._adjust(
258 params_or_path,
259 ignore_warnings=ignore_warnings,
260 raise_errors=raise_errors,
261 extend_adj=extend_adj,
262 clobber=clobber,
263 )
File ~/work/ParamTools/ParamTools/paramtools/parameters.py:291, in Parameters._adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, deserialized, validate, clobber)
289 parsed_params = {}
290 try:
--> 291 parsed_params = self._validator_schema.load(
292 params, ignore_warnings
293 )
294 except MarshmallowValidationError as ve:
295 self._parse_validation_messages(ve.messages, params)
File ~/work/ParamTools/ParamTools/paramtools/schema.py:234, in BaseValidatorSchema.load(self, data, ignore_warnings, deserialized)
232 return self.validate_only(data)
233 else:
--> 234 return super().load(data)
235 finally:
236 self.ignore_warnings = False
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:730, in Schema.load(self, data, many, partial, unknown)
703 def load(
704 self,
705 data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
(...) 709 unknown: types.UnknownOption | None = None,
710 ):
711 """Deserialize a data structure to an object defined by this Schema's fields.
712
713 :param data: The data to deserialize.
(...) 728 if invalid data are passed.
729 """
--> 730 return self._do_load(
731 data, many=many, partial=partial, unknown=unknown, postprocess=True
732 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:887, in Schema._do_load(self, data, many, partial, unknown, postprocess)
884 processed_data = data
885 if not errors:
886 # Deserialize data
--> 887 result = self._deserialize(
888 processed_data,
889 error_store=error_store,
890 many=many,
891 partial=partial,
892 unknown=unknown,
893 )
894 # Run field-level validation
895 self._invoke_field_validators(
896 error_store=error_store, data=result, many=many
897 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:676, in Schema._deserialize(self, data, error_store, many, partial, unknown, index)
666 def getter(
667 val, field_obj=field_obj, field_name=field_name, d_kwargs=d_kwargs
668 ):
669 return field_obj.deserialize(
670 val,
671 field_name,
672 data,
673 **d_kwargs,
674 )
--> 676 value = self._call_and_store(
677 getter_func=getter,
678 data=raw_value,
679 field_name=field_name,
680 error_store=error_store,
681 index=index,
682 )
683 if value is not missing:
684 key = field_obj.attribute or attr_name
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:517, in Schema._call_and_store(getter_func, data, field_name, error_store, index)
507 """Call ``getter_func`` with ``data`` as its argument, and store any `ValidationErrors`.
508
509 :param getter_func: Function for getting the serialized/deserialized
(...) 514 otherwise `None`.
515 """
516 try:
--> 517 value = getter_func(data)
518 except ValidationError as error:
519 error_store.store_error(error.messages, field_name, index=index)
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:669, in Schema._deserialize.<locals>.getter(val, field_obj, field_name, d_kwargs)
666 def getter(
667 val, field_obj=field_obj, field_name=field_name, d_kwargs=d_kwargs
668 ):
--> 669 return field_obj.deserialize(
670 val,
671 field_name,
672 data,
673 **d_kwargs,
674 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/fields.py:374, in Field.deserialize(self, value, attr, data, **kwargs)
372 if self.allow_none and value is None:
373 return None
--> 374 output = self._deserialize(value, attr, data, **kwargs)
375 self._validate(output)
376 return output
File ~/work/ParamTools/ParamTools/paramtools/schema.py:150, in ValueObject._deserialize(self, value, attr, data, partial, many, **kwargs)
144 if not isinstance(value, list) or (
145 isinstance(value, list)
146 and value
147 and not isinstance(value[0], dict)
148 ):
149 value = [{"value": value}]
--> 150 return super()._deserialize(
151 value, attr, data, partial=partial, many=many, **kwargs
152 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/fields.py:621, in Nested._deserialize(self, value, attr, data, partial, **kwargs)
612 """Same as :meth:`Field._deserialize` with additional ``partial`` argument.
613
614 :param partial: For nested schemas, the ``partial``
(...) 618 Add ``partial`` parameter.
619 """
620 self._test_collection(value)
--> 621 return self._load(value, partial=partial)
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/fields.py:597, in Nested._load(self, value, partial)
593 def _load(
594 self, value: typing.Any, partial: bool | types.StrSequenceOrSet | None = None
595 ):
596 try:
--> 597 valid_data = self.schema.load(value, unknown=self.unknown, partial=partial)
598 except ValidationError as error:
599 raise ValidationError(
600 error.messages, valid_data=error.valid_data
601 ) from error
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:730, in Schema.load(self, data, many, partial, unknown)
703 def load(
704 self,
705 data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
(...) 709 unknown: types.UnknownOption | None = None,
710 ):
711 """Deserialize a data structure to an object defined by this Schema's fields.
712
713 :param data: The data to deserialize.
(...) 728 if invalid data are passed.
729 """
--> 730 return self._do_load(
731 data, many=many, partial=partial, unknown=unknown, postprocess=True
732 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:887, in Schema._do_load(self, data, many, partial, unknown, postprocess)
884 processed_data = data
885 if not errors:
886 # Deserialize data
--> 887 result = self._deserialize(
888 processed_data,
889 error_store=error_store,
890 many=many,
891 partial=partial,
892 unknown=unknown,
893 )
894 # Run field-level validation
895 self._invoke_field_validators(
896 error_store=error_store, data=result, many=many
897 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:626, in Schema._deserialize(self, data, error_store, many, partial, unknown, index)
623 ret_l = []
624 else:
625 ret_l = [
--> 626 self._deserialize(
627 d,
628 error_store=error_store,
629 many=False,
630 partial=partial,
631 unknown=unknown,
632 index=idx,
633 )
634 for idx, d in enumerate(data)
635 ]
636 return ret_l
637 ret_d = self.dict_class()
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:676, in Schema._deserialize(self, data, error_store, many, partial, unknown, index)
666 def getter(
667 val, field_obj=field_obj, field_name=field_name, d_kwargs=d_kwargs
668 ):
669 return field_obj.deserialize(
670 val,
671 field_name,
672 data,
673 **d_kwargs,
674 )
--> 676 value = self._call_and_store(
677 getter_func=getter,
678 data=raw_value,
679 field_name=field_name,
680 error_store=error_store,
681 index=index,
682 )
683 if value is not missing:
684 key = field_obj.attribute or attr_name
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:517, in Schema._call_and_store(getter_func, data, field_name, error_store, index)
507 """Call ``getter_func`` with ``data`` as its argument, and store any `ValidationErrors`.
508
509 :param getter_func: Function for getting the serialized/deserialized
(...) 514 otherwise `None`.
515 """
516 try:
--> 517 value = getter_func(data)
518 except ValidationError as error:
519 error_store.store_error(error.messages, field_name, index=index)
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:669, in Schema._deserialize.<locals>.getter(val, field_obj, field_name, d_kwargs)
666 def getter(
667 val, field_obj=field_obj, field_name=field_name, d_kwargs=d_kwargs
668 ):
--> 669 return field_obj.deserialize(
670 val,
671 field_name,
672 data,
673 **d_kwargs,
674 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/fields.py:374, in Field.deserialize(self, value, attr, data, **kwargs)
372 if self.allow_none and value is None:
373 return None
--> 374 output = self._deserialize(value, attr, data, **kwargs)
375 self._validate(output)
376 return output
Cell In[1], line 18, in Int32._deserialize(self, value, *args, **kwargs)
16 def _deserialize(self, value, *args, **kwargs):
17 """Cast value from JSON to NumPy Int32."""
---> 18 converted = np.int32(value)
19 return converted
OverflowError: Python integer 2147483648 out of bounds for int32
Marshmallow Validator#
Fortunately, you can specify a custom validator with marshmallow
or ParamTools. Making this works requires modifying the _deserialize
method to check for overflow like this:
class Int32(ma.fields.Field):
"""
A custom type for np.int32.
https://numpy.org/devdocs/reference/arrays.dtypes.html
"""
# minor detail that makes this play nice with array_first
np_type = np.int32
def _serialize(self, value, *args, **kwargs):
"""Convert np.int32 to basic Python int."""
return value.tolist()
def _deserialize(self, value, *args, **kwargs):
"""Cast value from JSON to NumPy Int32."""
converted = np.int32(value)
# check for overflow and let range validator
# display the error message.
if converted != int(value):
return int(value)
return converted
Now, let’s see how to use marshmallow
to fix this problem:
import marshmallow as ma
import paramtools as pt
# get the minimum and maxium values for 32 bit integers.
min_int32 = -2147483648 # = np.iinfo(np.int32).min
max_int32 = 2147483647 # = np.iinfo(np.int32).max
# add int32 type to the paramtools type registry
pt.register_custom_type(
"int32",
Int32(validate=[
ma.validate.Range(min=min_int32, max=max_int32)
])
)
class Params(pt.Parameters):
defaults = {
"small_int": {
"title": "Small integer",
"description": "Demonstrate how to define a custom type",
"type": "int32",
"value": 2
}
}
params = Params(array_first=True)
params.adjust(dict(
small_int=np.int64(max_int32) + 1
))
---------------------------------------------------------------------------
ValidationError Traceback (most recent call last)
Cell In[5], line 31
19 defaults = {
20 "small_int": {
21 "title": "Small integer",
(...) 25 }
26 }
29 params = Params(array_first=True)
---> 31 params.adjust(dict(
32 small_int=np.int64(max_int32) + 1
33 ))
File ~/work/ParamTools/ParamTools/paramtools/parameters.py:257, in Parameters.adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, clobber)
210 def adjust(
211 self,
212 params_or_path: Union[str, Mapping[str, List[ValueObject]]],
(...) 216 clobber: bool = True,
217 ):
218 """
219 Deserialize and validate parameter adjustments. `params_or_path`
220 can be a file path or a `dict` that has not been fully deserialized.
(...) 255 least one existing value item's corresponding label values.
256 """
--> 257 return self._adjust(
258 params_or_path,
259 ignore_warnings=ignore_warnings,
260 raise_errors=raise_errors,
261 extend_adj=extend_adj,
262 clobber=clobber,
263 )
File ~/work/ParamTools/ParamTools/paramtools/parameters.py:375, in Parameters._adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, deserialized, validate, clobber)
371 # throw error if raise_errors is True or ignore_warnings is False
372 if (raise_errors and has_errors) or (
373 not ignore_warnings and has_warnings
374 ):
--> 375 raise self.validation_error
377 # Update attrs for params that were adjusted.
378 self._set_state(params=parsed_params.keys())
ValidationError: {
"errors": {
"small_int": [
"Must be greater than or equal to -2147483648 and less than or equal to 2147483647."
]
}
}
ParamTools Validator#
Finally, we will use ParamTools to solve this problem. We need to modify how we create our custom marshmallow
field so that it’s wrapped by ParamTools’s PartialField
. This makes it clear that your field still needs to be initialized, and that your custom field is able to receive validation information from the defaults
configuration:
import paramtools as pt
# add int32 type to the paramtools type registry
pt.register_custom_type(
"int32",
pt.PartialField(Int32)
)
class Params(pt.Parameters):
defaults = {
"small_int": {
"title": "Small integer",
"description": "Demonstrate how to define a custom type",
"type": "int32",
"value": 2,
"validators": {
"range": {"min": -2147483648, "max": 2147483647}
}
}
}
params = Params(array_first=True)
params.adjust(dict(
small_int=2147483647 + 1
))
---------------------------------------------------------------------------
OverflowError Traceback (most recent call last)
Cell In[6], line 27
12 defaults = {
13 "small_int": {
14 "title": "Small integer",
(...) 21 }
22 }
25 params = Params(array_first=True)
---> 27 params.adjust(dict(
28 small_int=2147483647 + 1
29 ))
File ~/work/ParamTools/ParamTools/paramtools/parameters.py:257, in Parameters.adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, clobber)
210 def adjust(
211 self,
212 params_or_path: Union[str, Mapping[str, List[ValueObject]]],
(...) 216 clobber: bool = True,
217 ):
218 """
219 Deserialize and validate parameter adjustments. `params_or_path`
220 can be a file path or a `dict` that has not been fully deserialized.
(...) 255 least one existing value item's corresponding label values.
256 """
--> 257 return self._adjust(
258 params_or_path,
259 ignore_warnings=ignore_warnings,
260 raise_errors=raise_errors,
261 extend_adj=extend_adj,
262 clobber=clobber,
263 )
File ~/work/ParamTools/ParamTools/paramtools/parameters.py:291, in Parameters._adjust(self, params_or_path, ignore_warnings, raise_errors, extend_adj, deserialized, validate, clobber)
289 parsed_params = {}
290 try:
--> 291 parsed_params = self._validator_schema.load(
292 params, ignore_warnings
293 )
294 except MarshmallowValidationError as ve:
295 self._parse_validation_messages(ve.messages, params)
File ~/work/ParamTools/ParamTools/paramtools/schema.py:234, in BaseValidatorSchema.load(self, data, ignore_warnings, deserialized)
232 return self.validate_only(data)
233 else:
--> 234 return super().load(data)
235 finally:
236 self.ignore_warnings = False
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:730, in Schema.load(self, data, many, partial, unknown)
703 def load(
704 self,
705 data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
(...) 709 unknown: types.UnknownOption | None = None,
710 ):
711 """Deserialize a data structure to an object defined by this Schema's fields.
712
713 :param data: The data to deserialize.
(...) 728 if invalid data are passed.
729 """
--> 730 return self._do_load(
731 data, many=many, partial=partial, unknown=unknown, postprocess=True
732 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:887, in Schema._do_load(self, data, many, partial, unknown, postprocess)
884 processed_data = data
885 if not errors:
886 # Deserialize data
--> 887 result = self._deserialize(
888 processed_data,
889 error_store=error_store,
890 many=many,
891 partial=partial,
892 unknown=unknown,
893 )
894 # Run field-level validation
895 self._invoke_field_validators(
896 error_store=error_store, data=result, many=many
897 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:676, in Schema._deserialize(self, data, error_store, many, partial, unknown, index)
666 def getter(
667 val, field_obj=field_obj, field_name=field_name, d_kwargs=d_kwargs
668 ):
669 return field_obj.deserialize(
670 val,
671 field_name,
672 data,
673 **d_kwargs,
674 )
--> 676 value = self._call_and_store(
677 getter_func=getter,
678 data=raw_value,
679 field_name=field_name,
680 error_store=error_store,
681 index=index,
682 )
683 if value is not missing:
684 key = field_obj.attribute or attr_name
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:517, in Schema._call_and_store(getter_func, data, field_name, error_store, index)
507 """Call ``getter_func`` with ``data`` as its argument, and store any `ValidationErrors`.
508
509 :param getter_func: Function for getting the serialized/deserialized
(...) 514 otherwise `None`.
515 """
516 try:
--> 517 value = getter_func(data)
518 except ValidationError as error:
519 error_store.store_error(error.messages, field_name, index=index)
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:669, in Schema._deserialize.<locals>.getter(val, field_obj, field_name, d_kwargs)
666 def getter(
667 val, field_obj=field_obj, field_name=field_name, d_kwargs=d_kwargs
668 ):
--> 669 return field_obj.deserialize(
670 val,
671 field_name,
672 data,
673 **d_kwargs,
674 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/fields.py:374, in Field.deserialize(self, value, attr, data, **kwargs)
372 if self.allow_none and value is None:
373 return None
--> 374 output = self._deserialize(value, attr, data, **kwargs)
375 self._validate(output)
376 return output
File ~/work/ParamTools/ParamTools/paramtools/schema.py:150, in ValueObject._deserialize(self, value, attr, data, partial, many, **kwargs)
144 if not isinstance(value, list) or (
145 isinstance(value, list)
146 and value
147 and not isinstance(value[0], dict)
148 ):
149 value = [{"value": value}]
--> 150 return super()._deserialize(
151 value, attr, data, partial=partial, many=many, **kwargs
152 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/fields.py:621, in Nested._deserialize(self, value, attr, data, partial, **kwargs)
612 """Same as :meth:`Field._deserialize` with additional ``partial`` argument.
613
614 :param partial: For nested schemas, the ``partial``
(...) 618 Add ``partial`` parameter.
619 """
620 self._test_collection(value)
--> 621 return self._load(value, partial=partial)
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/fields.py:597, in Nested._load(self, value, partial)
593 def _load(
594 self, value: typing.Any, partial: bool | types.StrSequenceOrSet | None = None
595 ):
596 try:
--> 597 valid_data = self.schema.load(value, unknown=self.unknown, partial=partial)
598 except ValidationError as error:
599 raise ValidationError(
600 error.messages, valid_data=error.valid_data
601 ) from error
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:730, in Schema.load(self, data, many, partial, unknown)
703 def load(
704 self,
705 data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]],
(...) 709 unknown: types.UnknownOption | None = None,
710 ):
711 """Deserialize a data structure to an object defined by this Schema's fields.
712
713 :param data: The data to deserialize.
(...) 728 if invalid data are passed.
729 """
--> 730 return self._do_load(
731 data, many=many, partial=partial, unknown=unknown, postprocess=True
732 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:887, in Schema._do_load(self, data, many, partial, unknown, postprocess)
884 processed_data = data
885 if not errors:
886 # Deserialize data
--> 887 result = self._deserialize(
888 processed_data,
889 error_store=error_store,
890 many=many,
891 partial=partial,
892 unknown=unknown,
893 )
894 # Run field-level validation
895 self._invoke_field_validators(
896 error_store=error_store, data=result, many=many
897 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:626, in Schema._deserialize(self, data, error_store, many, partial, unknown, index)
623 ret_l = []
624 else:
625 ret_l = [
--> 626 self._deserialize(
627 d,
628 error_store=error_store,
629 many=False,
630 partial=partial,
631 unknown=unknown,
632 index=idx,
633 )
634 for idx, d in enumerate(data)
635 ]
636 return ret_l
637 ret_d = self.dict_class()
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:676, in Schema._deserialize(self, data, error_store, many, partial, unknown, index)
666 def getter(
667 val, field_obj=field_obj, field_name=field_name, d_kwargs=d_kwargs
668 ):
669 return field_obj.deserialize(
670 val,
671 field_name,
672 data,
673 **d_kwargs,
674 )
--> 676 value = self._call_and_store(
677 getter_func=getter,
678 data=raw_value,
679 field_name=field_name,
680 error_store=error_store,
681 index=index,
682 )
683 if value is not missing:
684 key = field_obj.attribute or attr_name
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:517, in Schema._call_and_store(getter_func, data, field_name, error_store, index)
507 """Call ``getter_func`` with ``data`` as its argument, and store any `ValidationErrors`.
508
509 :param getter_func: Function for getting the serialized/deserialized
(...) 514 otherwise `None`.
515 """
516 try:
--> 517 value = getter_func(data)
518 except ValidationError as error:
519 error_store.store_error(error.messages, field_name, index=index)
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/schema.py:669, in Schema._deserialize.<locals>.getter(val, field_obj, field_name, d_kwargs)
666 def getter(
667 val, field_obj=field_obj, field_name=field_name, d_kwargs=d_kwargs
668 ):
--> 669 return field_obj.deserialize(
670 val,
671 field_name,
672 data,
673 **d_kwargs,
674 )
File /usr/share/miniconda/envs/paramtools-dev/lib/python3.12/site-packages/marshmallow/fields.py:374, in Field.deserialize(self, value, attr, data, **kwargs)
372 if self.allow_none and value is None:
373 return None
--> 374 output = self._deserialize(value, attr, data, **kwargs)
375 self._validate(output)
376 return output
Cell In[4], line 15, in Int32._deserialize(self, value, *args, **kwargs)
13 def _deserialize(self, value, *args, **kwargs):
14 """Cast value from JSON to NumPy Int32."""
---> 15 converted = np.int32(value)
17 # check for overflow and let range validator
18 # display the error message.
19 if converted != int(value):
OverflowError: Python integer 2147483648 out of bounds for int32