Skip to content

Upgrading marshmallow 3 to 4

Breaking3.26.2 → 4.3.1Breaking29Security1Platform1New5Changed7Fixed17Other1

BreakingUpgrade verdict

29 breaking changes and a platform requirement change and a security fix.

  • Code that relied on UUID fields being a kind of String field will no longer match.
  • Date parsing now uses Python's built-in parsers, which also accepts time offsets and bare YYYY-MM-DD datetimes, but removes the old helper functions.
  • These date formatting helpers are gone from the utils module.
  • The RFC 2822 date parsing and formatting helpers are gone from the utils module.
Top 5 of 31 that need code or config changes

Python requirement changed in 4.1.0: >=3.9 → >=3.10.

Drops packaging.

Installs 0 packages in all (was 1).

Breaking29
marshmallow.fields.UUID no longer subclasses marshmallow.fields.String.
marshmallow.fields.UUIDmarshmallow.fields.String
Uses stdlib fromisoformat for date, time and datetime deserialization, changing accepted formats and removing utils helpers.
datetime.date.fromisoformatdatetime.time.fromisoformatdatetime.datetime.fromisoformatfrom_iso_datefrom_iso_timefrom_iso_datetimemarshmallow.utils
Removes isoformat, to_iso_time and to_iso_datetime from marshmallow.utils.
isoformatto_iso_timeto_iso_datetimemarshmallow.utils
Removes from_rfc and rfcformat from marshmallow.utils.
from_rfcrfcformatmarshmallow.utils
Removes is_keyed_tuple from marshmallow.utils.
is_keyed_tuplemarshmallow.utils
Removes get_fixed_timezone from marshmallow.utils.
get_fixed_timezonemarshmallow.utils
marshmallow.fields.Boolean no longer serializes non-boolean values.
marshmallow.fields.Boolean
Renames the pass_many parameter to pass_collection in pre/post processing methods.
pass_manypass_collection
TimeDelta no longer truncates float values when deserializing, preserving microseconds.
marshmallow.fields.TimeDelta
Removes the serialization_type parameter from TimeDelta.
marshmallow.fields.TimeDeltaserialization_type
Removes implicit field creation via the fields or additional Meta options with undeclared fields.
fieldsadditional
Removes the ordered class Meta option; field order is preserved by default.
orderedSchema.dict_classOrderedDict
Removes the marshmallow.base module.
marshmallow.base
Removes the ordered class Meta option, deprecated in 3.26.0.
orderedmarshmallow.Schema.Meta
Custom validators must raise ValidationError for invalid values; returning False is no longer supported.
marshmallow.exceptions.ValidationErrormarshmallow.fields.Dict
Removes the __version__, __parsed_version__, and __version_info__ attributes.
__version____parsed_version____version_info__
Removes the default and missing parameters, replaced by dump_default and load_default.
defaultmissingdump_defaultload_default
Removes passing field metadata via keyword arguments; use the metadata argument instead.
metadata
Removes marshmallow.utils.pprint; use pprint.pprint instead.
marshmallow.utils.pprintpprint.pprint
Removes passing "self" to fields.Nested; use a callable instead.
fields.Nested
Removes Field.fail, replaced by Field.make_error in 3.0.0.
Field.failField.make_error
Removes the json_module class Meta option; use render_module instead.
json_modulerender_module
@validates accepts multiple field names, and decorated methods now receive data_key as a keyword argument.
validates
Renames the schema parameter to parent in Field._bind_to_schema.
marshmallow.fields.Field._bind_to_schema
Removes Schema's context attribute; use contextvars.ContextVar or the experimental Context manager instead.
Schemacontextcontextvars.ContextVarContext
Arguments to decorators are now keyword-only.
decorators
Renames the json_data parameter of Schema.loads to s and makes it positional-only.
marshmallow.Schema.loadsjson_datas
marshmallow.fields.Number is no longer usable as a field in a schema.
marshmallow.fields.Numbermarshmallow.fields.Integermarshmallow.fields.Floatmarshmallow.fields.Decimal
marshmallow.fields.Mapping is no longer usable as a field in a schema.
marshmallow.fields.Mapping
Security1
Fixes CVE-2025-68480 by merging error store messages without rebuilding collections.
Medium · CVSS 5.3
Platform1
Drops support for Python 3.9.

Generated by DepDelta from published Python source. Not mixed with prose notes.

  • removed: export pprint
  • removed: class FieldABC(ABC)
  • removed: class SchemaABC(ABC)
  • removed: method FieldABC.deserialize(value)
  • removed: method FieldABC.serialize(attr, obj, accessor=…)
  • removed: method SchemaABC.dump(obj, *, many: bool | None=…)
  • removed: method SchemaABC.dumps(obj, *, many: bool | None=…)
  • removed: method SchemaABC.load(data, *, many: bool | None=…, partial=…, unknown=…)
  • removed: method SchemaABC.loads(json_data, *, many: bool | None=…, partial=…, unknown=…, **kwargs)
  • added: variable EXCLUDE: typing.Final
  • added: variable INCLUDE: typing.Final
  • added: variable RAISE: typing.Final
  • added: variable missing: typing.Final
  • changed: function post_dump(fn: Callable[..., Any] | None=…, pass_many: bool=…, pass_original: bool=…) -> Callable[..., Any] → function post_dump(fn: typing.Callable[..., typing.Any] | None=…, *, pass_collection: bool=…, pass_original: bool=…) -> typing.Callable[..., typing.Any]
  • changed: function post_load(fn: Callable[..., Any] | None=…, pass_many: bool=…, pass_original: bool=…) -> Callable[..., Any] → function post_load(fn: typing.Callable[..., typing.Any] | None=…, *, pass_collection: bool=…, pass_original: bool=…) -> typing.Callable[..., typing.Any]
  • changed: function pre_dump(fn: Callable[..., Any] | None=…, pass_many: bool=…) -> Callable[..., Any] → function pre_dump(fn: typing.Callable[..., typing.Any] | None=…, *, pass_collection: bool=…) -> typing.Callable[..., typing.Any]
  • changed: function pre_load(fn: Callable[..., Any] | None=…, pass_many: bool=…) -> Callable[..., Any] → function pre_load(fn: typing.Callable[..., typing.Any] | None=…, *, pass_collection: bool=…) -> typing.Callable[..., typing.Any]
  • changed: function set_hook(fn: Callable[..., Any] | None, tag: str, many: bool=…, **kwargs: Any) -> Callable[..., Any] → function set_hook(fn: typing.Callable[..., typing.Any] | None, tag: str, *, many: bool=…, **kwargs: typing.Any) -> typing.Callable[..., typing.Any]
  • changed: function validates(field_name: str) -> Callable[..., Any] → function validates(*field_names: str) -> typing.Callable[..., typing.Any]
  • changed: function validates_schema(fn: Callable[..., Any] | None=…, pass_many: bool=…, pass_original: bool=…, skip_on_field_errors: bool=…) -> Callable[..., Any] → function validates_schema(fn: typing.Callable[..., typing.Any] | None=…, *, pass_collection: bool=…, pass_original: bool=…, skip_on_field_errors: bool=…) -> typing.Callable[..., typing.Any]
  • removed: class FieldInstanceResolutionError(MarshmallowError, TypeError)
  • changed: constructor ValidationError(message: str | list | dict, field_name: str=…, data: typing.Mapping[str, typing.Any] | typing.Iterable[typing.Mapping[str, typing.Any]] | None=…, valid_data: list[dict[str, typing.Any]] | dict[str, typing.Any] | None=…, **kwargs) → constructor ValidationError(message: str | list | dict, field_name: str=…, data: typing.Mapping[str, typing.Any] | typing.Iterable[typing.Mapping[str, typing.Any]] | None=…, valid_data: list[typing.Any] | dict[str, typing.Any] | None=…, **kwargs)
  • added: class Context(contextlib.AbstractContextManager, typing.Generic[_ContextT])
  • added: constructor Context(context: _ContextT) -> None
  • added: static method Context.get(default: _DefaultT | EllipsisType=…) -> _ContextT | _DefaultT
  • added: variable EllipsisType
  • removed: constructor DateTime(format: str | None=…, **kwargs) -> None
  • removed: field DateTime.default_error_messages
  • removed: field Mapping.mapping_type
  • removed: field Number.num_type: type
  • removed: method Field.fail(key: str, **kwargs)
  • removed: property Field.context -> dict | None
  • removed: property Field.default
  • removed: property Field.missing
  • changed: class Boolean(Field) → class Boolean(Field[bool])
  • changed: class Constant(Field) → class Constant(Field[_ContantT])
  • changed: class Date(DateTime) → class Date(_TemporalField[dt.date])
  • changed: class DateTime(Field) → class DateTime(_TemporalField[dt.datetime])
  • changed: class Dict(Mapping) → class Dict(Mapping[dict])
  • changed: class Enum(Field) → class Enum(Field[_EnumT])
  • changed: class Field(FieldABC) → class Field(typing.Generic[_InternalT])
  • changed: class IP(Field) → class IP(Field[ipaddress.IPv4Address | ipaddress.IPv6Address])
  • changed: class IPInterface(Field) → class IPInterface(Field[ipaddress.IPv4Interface | ipaddress.IPv6Interface])
  • changed: class List(Field) → class List(Field[list[_InternalT | None]])
  • changed: class Mapping(Field) → class Mapping(Field[_MappingT])
  • changed: class Number(Field, typing.Generic[_NumType]) → class Number(Field[_NumT])
  • changed: class Raw(Field) → class Raw(Field[typing.Any])
  • changed: class String(Field) → class String(Field[str])
  • changed: class Time(DateTime) → class Time(_TemporalField[dt.time])
  • changed: class TimeDelta(Field) → class TimeDelta(Field[dt.timedelta])
  • changed: class Tuple(Field) → class Tuple(Field[tuple])
  • changed: class UUID(String) → class UUID(Field[uuid.UUID])
  • changed: constructor AwareDateTime(format: str | None=…, *, default_timezone: dt.tzinfo | None=…, **kwargs) -> None → constructor AwareDateTime(format: str | None=…, *, default_timezone: dt.tzinfo | None=…, **kwargs: Unpack[_BaseFieldKwargs]) -> None
  • changed: constructor Boolean(*, truthy: typing.Iterable | None=…, falsy: typing.Iterable | None=…, **kwargs) → constructor Boolean(*, truthy: typing.Iterable | None=…, falsy: typing.Iterable | None=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Constant(constant: typing.Any, **kwargs) → constructor Constant(constant: _ContantT, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Decimal(places: int | None=…, rounding: str | None=…, *, allow_nan: bool=…, as_string: bool=…, **kwargs) → constructor Decimal(places: int | None=…, rounding: str | None=…, *, allow_nan: bool=…, as_string: bool=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Email(*args, **kwargs) -> None → constructor Email(**kwargs: Unpack[_BaseFieldKwargs]) -> None
  • changed: constructor Enum(enum: type[EnumType], *, by_value: bool | Field | type[Field]=…, **kwargs) → constructor Enum(enum: type[_EnumT], *, by_value: bool | Field | type[Field]=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Field(*, load_default: typing.Any=…, missing: typing.Any=…, dump_default: typing.Any=…, default: typing.Any=…, data_key: str | None=…, attribute: str | None=…, validate: types.Validator | typing.Iterable[types.Validator] | None=…, required: bool=…, allow_none: bool | None=…, load_only: bool=…, dump_only: bool=…, error_messages: dict[str, str] | None=…, metadata: typing.Mapping[str, ty… → constructor Field(*, load_default: typing.Any=…, dump_default: typing.Any=…, data_key: str | None=…, attribute: str | None=…, validate: types.Validator | typing.Iterable[types.Validator] | None=…, pre_load: types.PreLoadCallable | typing.Iterable[types.PreLoadCallable] | None=…, post_load: types.PostLoadCallable | typing.Iterable[types.PostLoadCallable] | None=…, required: bool=…, allow_none: boo…
  • changed: constructor Float(*, allow_nan: bool=…, as_string: bool=…, **kwargs) → constructor Float(*, allow_nan: bool=…, as_string: bool=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Function(serialize: typing.Callable[[typing.Any], typing.Any] | typing.Callable[[typing.Any, dict], typing.Any] | None=…, deserialize: typing.Callable[[typing.Any], typing.Any] | typing.Callable[[typing.Any, dict], typing.Any] | None=…, **kwargs) → constructor Function(serialize: typing.Callable[[typing.Any], typing.Any] | typing.Callable[[typing.Any, dict], typing.Any] | None=…, deserialize: typing.Callable[[typing.Any], typing.Any] | typing.Callable[[typing.Any, dict], typing.Any] | None=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor IP(*args, exploded=…, **kwargs) → constructor IP(*, exploded: bool=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor IPInterface(*args, exploded: bool=…, **kwargs) → constructor IPInterface(*, exploded: bool=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Integer(*, strict: bool=…, **kwargs) → constructor Integer(*, strict: bool=…, as_string: bool=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor List(cls_or_instance: Field | type[Field], **kwargs) → constructor List(cls_or_instance: Field[_InternalT] | type[Field[_InternalT]], **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Mapping(keys: Field | type[Field] | None=…, values: Field | type[Field] | None=…, **kwargs) → constructor Mapping(keys: Field | type[Field] | None=…, values: Field | type[Field] | None=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Method(serialize: str | None=…, deserialize: str | None=…, **kwargs) → constructor Method(serialize: str | None=…, deserialize: str | None=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor NaiveDateTime(format: str | None=…, *, timezone: dt.timezone | None=…, **kwargs) -> None → constructor NaiveDateTime(format: str | None=…, *, timezone: dt.timezone | None=…, **kwargs: Unpack[_BaseFieldKwargs]) -> None
  • changed: constructor Nested(nested: Schema | SchemaMeta | str | dict[str, Field] | typing.Callable[[], Schema | SchemaMeta | dict[str, Field]], *, dump_default: typing.Any=…, default: typing.Any=…, only: types.StrSequenceOrSet | None=…, exclude: types.StrSequenceOrSet=…, many: bool=…, unknown: str | None=…, **kwargs) → constructor Nested(nested: Schema | SchemaMeta | str | dict[str, Field] | typing.Callable[[], Schema | SchemaMeta | str | dict[str, Field]], *, only: types.StrSequenceOrSet | None=…, exclude: types.StrSequenceOrSet=…, many: bool | None=…, unknown: types.UnknownOption | None=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Number(*, as_string: bool=…, **kwargs) → constructor Number(*, as_string: bool=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Pluck(nested: Schema | SchemaMeta | str | typing.Callable[[], Schema], field_name: str, *, many: bool=…, unknown: str | None=…, **kwargs) → constructor Pluck(nested: Schema | SchemaMeta | str | typing.Callable[[], Schema], field_name: str, *, many: bool=…, unknown: types.UnknownOption | None=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor TimeDelta(precision: str=…, serialization_type: type[int | float]=…, **kwargs) → constructor TimeDelta(precision: str=…, **kwargs: Unpack[_BaseFieldKwargs]) -> None
  • changed: constructor Tuple(tuple_fields: typing.Iterable[Field] | typing.Iterable[type[Field]], **kwargs) → constructor Tuple(tuple_fields: typing.Iterable[Field | type[Field]], **kwargs: Unpack[_BaseFieldKwargs])
  • changed: constructor Url(*, relative: bool=…, absolute: bool=…, schemes: types.StrSequenceOrSet | None=…, require_tld: bool=…, **kwargs) → constructor Url(*, relative: bool=…, absolute: bool=…, schemes: types.StrSequenceOrSet | None=…, require_tld: bool=…, **kwargs: Unpack[_BaseFieldKwargs])
  • changed: field DateTime.DESERIALIZATION_FUNCS: dict[str, typing.Callable[[str], typing.Any]] → field DateTime.DESERIALIZATION_FUNCS: dict[str, typing.Callable[[str], dt.datetime]]
  • changed: field DateTime.SERIALIZATION_FUNCS: dict[str, typing.Callable[[typing.Any], str | float]] → field DateTime.SERIALIZATION_FUNCS: dict[str, typing.Callable[[dt.datetime], str | float]]
  • changed: field Field.default_error_messages: dict[str, str] → field Field.default_error_messages: types.ErrorMessages
  • changed: method Field.deserialize(value: typing.Any, attr: str | None=…, data: typing.Mapping[str, typing.Any] | None=…, **kwargs) → method Field.deserialize(value: None, attr: str | None=…, data: typing.Mapping[str, typing.Any] | None=…, **kwargs) -> _InternalT | None | (value: typing.Any, attr: str | None=…, data: typing.Mapping[str, typing.Any] | None=…, **kwargs) -> _InternalT
  • changed: method Field.get_value(obj: typing.Any, attr: str, accessor: typing.Callable[[typing.Any, str, typing.Any], typing.Any] | None=…, default: typing.Any=…) → method Field.get_value(obj: typing.Any, attr: str, accessor: typing.Callable[[typing.Any, str, typing.Any], typing.Any] | None=…, default: typing.Any=…) -> _InternalT
  • added: property Mapping.mapping_type -> type[_MappingT]
  • added: property Number.num_type -> type[_NumT]
  • removed: field Schema.Meta.ordered: typing.ClassVar[bool]
  • removed: property Schema.dict_class -> type[dict]
  • changed: class Schema(base.SchemaABC) → class Schema
  • changed: constructor Schema(*, only: types.StrSequenceOrSet | None=…, exclude: types.StrSequenceOrSet=…, many: bool | None=…, context: dict | None=…, load_only: types.StrSequenceOrSet=…, dump_only: types.StrSequenceOrSet=…, partial: bool | types.StrSequenceOrSet | None=…, unknown: str | None=…) → constructor Schema(*, only: types.StrSequenceOrSet | None=…, exclude: types.StrSequenceOrSet=…, many: bool | None=…, load_only: types.StrSequenceOrSet=…, dump_only: types.StrSequenceOrSet=…, partial: bool | types.StrSequenceOrSet | None=…, unknown: types.UnknownOption | None=…)
  • changed: constructor SchemaOpts(meta: type, ordered: bool=…) → constructor SchemaOpts(meta: type)
  • changed: field Schema.Meta.unknown: typing.ClassVar[str] → field Schema.Meta.unknown: typing.ClassVar[types.UnknownOption]
  • changed: method Schema.load(data: typing.Mapping[str, typing.Any] | typing.Iterable[typing.Mapping[str, typing.Any]], *, many: bool | None=…, partial: bool | types.StrSequenceOrSet | None=…, unknown: str | None=…) → method Schema.load(data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]], *, many: bool | None=…, partial: bool | types.StrSequenceOrSet | None=…, unknown: types.UnknownOption | None=…)
  • changed: method Schema.loads(json_data: str | bytes | bytearray, *, many: bool | None=…, partial: bool | types.StrSequenceOrSet | None=…, unknown: str | None=…, **kwargs) → method Schema.loads(s: str | bytes | bytearray, /, *, many: bool | None=…, partial: bool | types.StrSequenceOrSet | None=…, unknown: types.UnknownOption | None=…, **kwargs)
  • changed: method Schema.validate(data: typing.Mapping[str, typing.Any] | typing.Iterable[typing.Mapping[str, typing.Any]], *, many: bool | None=…, partial: bool | types.StrSequenceOrSet | None=…) -> dict[str, list[str]] → method Schema.validate(data: Mapping[str, typing.Any] | Sequence[Mapping[str, typing.Any]], *, many: bool | None=…, partial: bool | types.StrSequenceOrSet | None=…) -> dict[str, list[str]]
  • added: field Schema.dict_class: type[dict]
  • changed: method RenderModule.loads(json_data: str | bytes | bytearray, *args: typing.Any, **kwargs: typing.Any) -> typing.Any → method RenderModule.loads(s: str | bytes | bytearray, *args: typing.Any, **kwargs: typing.Any) -> typing.Any
  • changed: variable StrSequenceOrSet → variable StrSequenceOrSet: typing.TypeAlias
  • changed: variable Validator → variable Validator: typing.TypeAlias
  • added: variable ErrorMessageValue: typing.TypeAlias
  • added: variable ErrorMessages: typing.TypeAlias
  • added: variable PostLoadCallable
  • added: variable PreLoadCallable
  • added: variable T
  • added: variable UnknownOption: typing.TypeAlias
  • removed: function from_iso_date(value)
  • removed: function from_iso_datetime(value)
  • removed: function from_iso_time(value)
  • removed: function from_rfc(datestring: str) -> dt.datetime
  • removed: function get_fixed_timezone(offset: float | dt.timedelta) -> dt.timezone
  • removed: function get_func_args(func: typing.Callable) -> list[str]
  • removed: function is_instance_or_subclass(val, class_) -> bool
  • removed: function is_keyed_tuple(obj) -> bool
  • removed: function isoformat(datetime: dt.datetime) -> str
  • removed: function pprint(obj, *args, **kwargs) -> None
  • removed: function resolve_field_instance(cls_or_instance: type[Field] | Field) -> Field
  • removed: function rfcformat(datetime: dt.datetime) -> str
  • removed: function to_iso_date(date: dt.date) -> str
  • removed: function to_iso_time(time: dt.time) -> str
  • removed: function validate_unknown_parameter_value(obj: typing.Any) -> str
  • removed: variable EXCLUDE
  • removed: variable INCLUDE
  • removed: variable RAISE
  • removed: variable missing
  • changed: function is_collection(obj) -> bool → function is_collection(obj) -> typing.TypeGuard[typing.Iterable]
  • changed: function is_generator(obj) -> bool → function is_generator(obj) -> typing.TypeGuard[typing.Generator]
  • changed: function is_iterable_but_not_string(obj) -> bool → function is_iterable_but_not_string(obj) -> typing.TypeGuard[typing.Iterable]
  • added: function is_sequence_but_not_string(obj) -> typing.TypeGuard[Sequence]
  • removed: field And.default_error_message
  • changed: constructor And(*validators: types.Validator, error: str | None=…) → constructor And(*validators: types.Validator)
  • changed: constructor Range(min=…, max=…, *, min_inclusive: bool=…, max_inclusive: bool=…, error: str | None=…) → constructor Range(min: typing.Any=…, max: typing.Any=…, *, min_inclusive: bool=…, max_inclusive: bool=…, error: str | None=…)
  • changed: constructor Regexp(regex: str | bytes | typing.Pattern, flags: int=…, *, error: str | None=…) → constructor Regexp(regex: str | bytes | re.Pattern[str] | re.Pattern[bytes], flags: int=…, *, error: str | None=…)
  • removed: class ChangedInMarshmallow4Warning(Marshmallow4Warning)
  • removed: class Marshmallow4Warning(DeprecationWarning)
  • removed: class RemovedInMarshmallow4Warning(Marshmallow4Warning)