Warn when blocks receive unexpected fields - #22772
Warn when blocks receive unexpected fields#22772devin-ai-integration[bot] wants to merge 4 commits into
Conversation
closes #8642 Co-authored-by: alex.s <alex.s@prefect.io> Co-Authored-By: alex.s <ajstreed1@gmail.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Co-authored-by: alex.s <alex.s@prefect.io> Co-Authored-By: alex.s <ajstreed1@gmail.com>
Co-authored-by: alex.s <alex.s@prefect.io> Co-Authored-By: alex.s <ajstreed1@gmail.com>
Merging this PR will not alter performance
Comparing Footnotes
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 54397e06f7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| token = _hydrating_block_document.set(True) | ||
| try: | ||
| block = block_cls.model_validate(block_document.data) |
There was a problem hiding this comment.
Suppress warnings in the automation hydration path
Webhook and notification automations hydrate persisted blocks through src/prefect/server/events/actions.py::_load_block_from_block_document, which calls block_cls.model_validate(block_document.data) directly and therefore never sets this context variable. When such a document contains a field removed from the installed block class, automation execution still emits the warning this wrapper is intended to suppress; with warnings treated as errors, the loader catches it and reports the block as invalid. Apply the suppression at every persisted-document hydration entry point or route that loader through a shared helper.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 0696e3a — hydration suppression now lives in Block._validate_block_document_data, and _load_block_from_block_document in server/events/actions.py goes through it instead of calling model_validate directly.
| def __init__(self, *args: Any, **kwargs: Any): | ||
| self._warn_on_unexpected_fields(kwargs) | ||
| super().__init__(*args, **kwargs) |
There was a problem hiding this comment.
Defer warnings until a union candidate validates
When a block is validated from an untagged dictionary as one member of a union, Pydantic may invoke several candidates before selecting the valid one. Because the warning runs before super().__init__, a failed candidate warns about fields belonging to the successful candidate—for example, validating {"b": 4} against A | B warns from A before B succeeds. Under a warnings-as-errors policy, this can reject otherwise valid input, so only warn after the candidate itself has validated successfully. This changes behavior on the public Block construction API, which requires backward compatibility.
AGENTS.md reference: src/prefect/AGENTS.md:L5-L7
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 0696e3a. The warning now runs after super().__init__, and is skipped entirely when Block.__init__ was invoked by pydantic validating data into a block (nested block or union candidate) rather than by a direct call. Test added for an untagged Union[Left, Right] of blocks under warnings-as-errors.
| elif isinstance(field.validation_alias, AliasChoices): | ||
| known_names.update( | ||
| choice | ||
| for choice in field.validation_alias.choices | ||
| if isinstance(choice, str) | ||
| ) |
There was a problem hiding this comment.
Recognize AliasPath inputs before warning
Pydantic also permits validation_alias=AliasPath("payload", "x"), and permits an AliasPath inside AliasChoices; in that case Block(payload={"x": 1}) is valid and consumes payload, but this code records only string choices and incorrectly warns that payload is unexpected. Include the leading input key from AliasPath values so valid public block constructors do not emit—or, with warnings treated as errors, raise—this new warning.
AGENTS.md reference: src/prefect/AGENTS.md:L5-L7
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 0696e3a — AliasPath (standalone and inside AliasChoices) now contributes its leading input key to the known names, with a test covering validation_alias=AliasPath("payload", "x").
Co-authored-by: alex.s <alex.s@prefect.io> Co-Authored-By: alex.s <ajstreed1@gmail.com>
closes #8642
This PR changes behavior: constructing a block with a keyword that isn't a field on the class now emits a
UserWarningnaming the offending keyword(s), instead of silently storing it as extra data.Details
Blocks set
extra="allow"so that a block document saved under an older schema still loads after a field is removed from the class. That also means a typo is silently accepted:Block.__init__now checks incoming keywords against field names and aliases (includingAliasPathandAliasChoices) and warns when any are unrecognized. Keepingextra="allow"preserves the existing hydration behavior covered byTestBlockSchemaMigration.test_rm_field_from_schema_loads_with_validation.The warning is only for direct construction of a block. It is skipped when pydantic is validating data into a block — a nested block or a candidate member of a union, where the data may belong to a different type — and when hydrating a persisted block document, so a document that still carries a removed field loads quietly. Persisted-document hydration goes through
Block._validate_block_document_data, used by both_from_block_documentand the automations loader inserver/events/actions.py.No warning is emitted for
block_type_slugor for the_block_document_id/_block_document_name/_is_anonymouskeys thatser_modeladds to serialized blocks.Tests in
tests/blocks/test_core.py::TestUnexpectedFieldscover the typo warning, aliases, alias paths, the discriminator, unions, and removed-field hydration.MockCredentialsintests/runner/test_storage.pynow declares theaccess_tokenfield those tests pass to it.Checklist
mint.json.Link to Devin session: https://app.devin.ai/sessions/e46425af6e7546a1b53dfe405057099c
Requested by: @desertaxle