Skip to content

Require marshmallow 4 or later - #107

Merged
Marenz merged 3 commits into
frequenz-floss:v1.x.xfrom
Marenz:marshmallow-4-compat
Sep 7, 2026
Merged

Require marshmallow 4 or later#107
Marenz merged 3 commits into
frequenz-floss:v1.x.xfrom
Marenz:marshmallow-4-compat

Conversation

@Marenz

@Marenz Marenz commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

The code has required marshmallow 4 since 4ddc29d; the metadata still advertises 3.x, which cannot import.

Correct the dependency floor and document the required consumer upgrade.

@Marenz
Marenz requested a review from a team as a code owner December 23, 2025 09:16
@github-actions github-actions Bot added part:docs Affects the documentation part:tests Affects the unit, integration and performance (benchmarks) tests part:code Affects the code in general labels Dec 23, 2025
@Marenz
Marenz force-pushed the marshmallow-4-compat branch from bb481ba to 8245824 Compare December 23, 2025 09:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds backwards-compatible support for marshmallow 4.x by migrating from schema context-based serialization configuration to Python's ContextVar. The changes maintain full API compatibility while deprecating the old constructor-based approach.

Key changes:

  • Introduces serialize_as_string_default as a ContextVar for controlling serialization format globally
  • Deprecates the serialize_as_string_default constructor parameter with a DeprecationWarning
  • Adds backwards compatibility handling in _QuantityField.__init__ to check metadata for per-field configuration

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.

File Description
src/frequenz/quantities/experimental/marshmallow.py Adds QuantitySchema.__init__ with deprecation logic and backwards compatibility for metadata-based field configuration
tests/experimental/test_marshmallow.py Adds two new tests to verify the deprecated constructor API works correctly with both True and False values
RELEASE_NOTES.md Documents the API changes, migration path, and rationale for the marshmallow 4.x compatibility update

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/experimental/test_marshmallow.py Outdated
Comment thread tests/experimental/test_marshmallow.py
Comment thread src/frequenz/quantities/experimental/marshmallow.py Outdated
Comment thread src/frequenz/quantities/experimental/marshmallow.py Outdated
@Marenz
Marenz force-pushed the marshmallow-4-compat branch from 8245824 to 9c30b12 Compare December 23, 2025 09:21
@github-actions github-actions Bot added the part:tooling Affects the development tooling (CI, deployment, dependency management, etc.) label Dec 23, 2025
@Marenz
Marenz force-pushed the marshmallow-4-compat branch 5 times, most recently from 6b536ab to 8cca58a Compare December 23, 2025 10:32
@llucax llucax added the scope:breaking-change Breaking change, users will need to update their code label Jan 5, 2026
@llucax llucax added this to the v2.0.0 milestone Jan 5, 2026

@llucax llucax left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, this is a tricky one, and a reason why we might want to split marshmallow support into a separate package at some point.

For people using marshmallow, this is in fact a breaking change, as marshmallow 4.0 is required, not just optional, but for users not using marshmallow is not a breaking change.

Normally, if any part of the code is backwards incompatible, even if unused, should be a breaking release, I think this should be a breaking release too. The only reason we could try to sell this as a non-breaking release would be because it is an experimental package. But still, we are supposed to be committed to not break even those.

What we could do to keep this backwards compatible, is getting the installed marshmallow version and import a different module that is compatible with one or the other version conditionally based on that.

But I think in this case it makes sense to just bite the bullet and release it as a breaking change. For users not using marshmallow, it will be just changing the dependency and changing no code. For users using marshmallow, they are going through a marshmallow upgrade anyway, so fixing one more piece of code should not be too bad.

I'm not sure if keeping the deprecated ctor makes sense if we ship this in a breaking release anyway. It might ease the upgrading a bit for users if they don't need to make any other marshmallow-related updates, so we could keep it if we want to be extra nice, and only remove the deprecated ctor in v3, but I'm not sure it is worth the trouble.

@Marenz

Marenz commented Jan 12, 2026

Copy link
Copy Markdown
Contributor Author

I think I would be in favor of just breaking it then without a compatible c'tor :)

Signed-off-by: Mathias L. Baumann <mathias.baumann@frequenz.com>
@Marenz
Marenz force-pushed the marshmallow-4-compat branch from 8cca58a to 081d31b Compare August 28, 2026 09:33
@Marenz Marenz changed the title Add backwards-compatible marshmallow 4.x support Require marshmallow 4 or later Aug 28, 2026
Signed-off-by: Mathias L. Baumann <mathias.baumann@frequenz.com>
@Marenz
Marenz requested review from llucax and a lite review from Copilot August 28, 2026 10:12
@Marenz

Marenz commented Aug 28, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Resolved the three stale Copilot threads: they all concerned the serialize_as_string_default shim, which this PR no longer contains — the diff is now just the dependency floor correction plus the release note.

@Marenz
Marenz enabled auto-merge August 28, 2026 10:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment thread RELEASE_NOTES.md
Comment thread pyproject.toml
Signed-off-by: Mathias L. Baumann <mathias.baumann@frequenz.com>
@llucax

llucax commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

If we go with this, we should release it as 2.0, unless you did some testing and at least none of our projects breaks when just bumping marshmallow to 4.x.

@llucax

llucax commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

The code has required marshmallow 4 since 4ddc29d; the metadata still advertises 3.x, which cannot import.

Is this really true? I thought we ended up doing a change that really supported both 3.x and 4.x.

@Marenz

Marenz commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

🤖 Checked empirically: the branch only imports on marshmallow 4 — Field[Quantity] (marshmallow.py:45) needs 4's generic Field; on 3.26.2 it's TypeError: type 'Field' is not subscriptable at import. The dual 3/4 shim you remember was in an earlier revision and got removed, so the claim holds.

On breakage: fcr#591 fails CI, but only as a pip resolver conflict against released quantities 1.0.0, which pins marshmallow<4 — that's the metadata bug this PR fixes. No marshmallow-4 API breakage in fcr's or app-edge's own code.

Since this is all under frequenz.quantities.experimental, exempt from semver per experimental-packages.md, I'd keep it patch/minor rather than 2.0.

@llucax llucax left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright then, lets go.

@Marenz
Marenz added this pull request to the merge queue Sep 7, 2026
Merged via the queue into frequenz-floss:v1.x.x with commit 7757a92 Sep 7, 2026
9 checks passed
@Marenz
Marenz deleted the marshmallow-4-compat branch September 7, 2026 08:44
llucax added a commit to llucax/frequenz-sdk-python that referenced this pull request Sep 8, 2026
`frequenz-quantities` 1.0.2 changed its `marshmallow` optional dependency
floor from `< 4` to `>= 4` (frequenz-floss/frequenz-quantities-python#107),
correcting metadata that had been wrong since that library was ported to
`marshmallow` 4.  That cap was the only thing holding us on `marshmallow`
3, as our own requirement has allowed `< 5` since 870e880, so CI now
resolves `marshmallow` 4.

`marshmallow` 4 removed support for validators that signal failure by
returning `False`, deprecated in 3.24.0:

    Custom validators must raise a `ValidationError` for invalid values
    (deprecated in 3.24.0).  Returning `False` is no longer supported.

The configuration test dataclasses used exactly that pattern, a
`lambda s: s.startswith("test")` in the field metadata.  The validator is
still registered and still called, only its return value is ignored, so
the invalid value is accepted and `test_load_config_dataclass` and
`test_new_receiver_configurations[Validation Error]` fail.

Replacing the lambdas with a function that raises restores the checks.
The message stays `Invalid value.`, which is what `marshmallow` 3 emitted
through its `validator_failed` error key, so the expected output in
`test_manager.py` is unchanged and the surrounding test cases remain
consistent in asserting on `marshmallow`'s stock messages.

Nothing under `src/` needed changing: the validators there are
`marshmallow.validate.Range` and `OneOf`, which already raise.  A raising
validator also works on `marshmallow` 3, so the supported range is not
narrowed, but the same silent acceptance can hit users who wrote their
own boolean validators, hence the release notes entry.

Signed-off-by: Leandro Lucarella <luca-frequenz@llucax.com>
llucax added a commit to llucax/frequenz-sdk-python that referenced this pull request Sep 8, 2026
…ss#1456)

CI started failing on `v1.x.x` yesterday with three unrelated-looking
config test failures. They come from the `frequenz-quantities` 1.0.2
release, which raised its `marshmallow` floor to 4
(frequenz-floss/frequenz-quantities-python#107) and thereby dragged
`marshmallow` 4 into our environment for the first time. Marshmallow 4
dropped support for validators that return `False` instead of raising,
which is what the config test dataclasses were doing, so the validation
silently stopped happening.

The fix is test-only, but there is a release notes entry because the
same silent acceptance hits anyone who wrote boolean validators for
their own config dataclasses.

Heads-up for whoever reviews: the docs job on this PR will stay red, for
a second and unrelated problem from the same `frequenz-quantities` 1.0.2
release. That one is a docstring in `frequenz-quantities` using a
relative mkdocstrings cross-reference, which misresolves whenever it is
rendered under a path other than the one it was written in, as happens
here with `BaseConfigSchema` inheriting `QuantitySchema.TYPE_MAPPING`.
It cannot be fixed on this side, only worked around by dropping
documentation, and frequenz-floss/frequenz-quantities-python#170 already
fixes it upstream. frequenz-floss#1455 is blocked on the same thing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

part:code Affects the code in general part:docs Affects the documentation part:tests Affects the unit, integration and performance (benchmarks) tests part:tooling Affects the development tooling (CI, deployment, dependency management, etc.) scope:breaking-change Breaking change, users will need to update their code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants