Skip to content

reject unknown fields#139

Open
the-infinity wants to merge 54 commits intomainfrom
prevent-additional-properties
Open

reject unknown fields#139
the-infinity wants to merge 54 commits intomainfrom
prevent-additional-properties

Conversation

@the-infinity
Copy link
Copy Markdown
Contributor

For strict validation (eg validation services), it makes sense to be able to prevent additional properties. This MR provides this feature.

Open question: it might make sense to enable prevent additional attributes via env or something else, too, to make a strict validation in tests and therefore find inconsistencies. One could just take os.env for that. Do you think this is a good idea? And if yes, how should we call the env var? just VALIDATACLASS_PREVENT_ADDITIONAL_ATTRIBUTES?

These validators used to be StringValidator subclasses that overrode the validate return type in a type-incompatible way. Now they only inherit from Validator and use a separate StringValidator object for basic string validation.
Generic-based typing for Validator classes
This includes a deprecation for using `Default()` without arguments.
@the-infinity
Copy link
Copy Markdown
Contributor Author

Will rename the field accordingly, I just came from the JSON Schema wirld where this makes sense.

About the DataclassValidator: it makes sense to have them both. The decorator-approach makes most sense for JSON Schema translations, where this additionalProperties is a property of the object definition. If you auto-translate JSON Schema into validataclass and therefore create a library of validated objects, you don't know where its's used, so having it at the dataclass directly is important there. This can of course be overwritten (or first time set) at the DataclassValidator . Therefore, why not fulfilling both needs? Will add it to the MR.

About os.env: I like the idea of having a CI testing in struct mode, and normal operations not. By setting the CI in struct mode, you can catch accidental fields in test data which were not handled by accident. Reason for this in general: I like systems which check my code, not manual work. Also, it makes a lot of sense for DATEX2 validation again: DATEX2 is multiple hundred validataclasses. You don't want to subclass all of them and rebuild the whole validataclass tree if you want to have a not so strict normal input validation, but a strict web validator which actually checks if your DATEX2 is valid. This apples to any other complex data model, too: subclassing the specific validataclass means you have to rebuild the whole tree of validators. Subclassing to add a debug attribute means that one has to replace all usages in a project with the sublassed ExtendedDataclassValidator, which is also quite a lot of change for a simple switch. I am open to other solutions there, they should just not end up into too much work on the usage side.. Not important in the first step, though, but I would like to get to this somehow.

@binaryDiv
Copy link
Copy Markdown
Contributor

About the DataclassValidator: it makes sense to have them both.

So the validataclass can define the default behaviour and the DataclassValidator can override that. Yes, I think that makes sense. I thought about that too but thought it's a bit redundant, and my assumption was that in your project you would probably need that setting for every validataclass and not just for a specific subset of them. But if you have a good use case where it makes sense to have that as an inherent property of a validataclass, that makes sense.

You don't want to subclass all of them and rebuild the whole validataclass tree

No, I think you misunderstood me. I wasn't speaking of subclassing the validataclass to add a debug flag, that really doesn't make sense. But it does make sense to define a custom DataclassValidator for your specific application that you can adjust to your needs, rather than enforcing a very specific way to enable a feature via env variable. That's also one of the main design goals of this library: Keep things simple but extendable. It's not a workaround to subclass the DataclassValidator, that's intended usage. You can also keep the name "DataclassValidator", then you don't need to change every usage but only the import lines.

What complicates things is also that you might not actually want "debug mode" to affect every DataclassValidator. Unknown fields are not always an error case, sometimes you explicitly want to validate only a subset of fields (think of validation for responses of outgoing API requests - you may be interested in only 2 fields of the entire response, why validate the entire request?) This use case can be easily solved with the subclassing approach, just use the regular DataclassValidator for those objects. If it's a built-in feature of the DataclassValidator that reads os.env or something like that, you also need to build in a way to "force ignore" unknown fields, so now you have a lot of possible combinations for "flag in validataclass", "flag in DataclassValidator that overrides flag in validataclass", "flag in env that overrides otherflags", "flag that overrides the env flag" etc...

There may be ways to provide a simple and usage-agnostic way to do this, like some sort of global context/configuration of library behavior, but I think it's out of scope for this PR because there's a lot of things to consider to do that right. For now, please just leave it at 1. an option in the validataclass decorator to set the default behaviour, 2. an option in the DataclassValidator to override the default behaviour.

@the-infinity the-infinity force-pushed the prevent-additional-properties branch from 14ca883 to 81e5b42 Compare February 18, 2026 20:02
@the-infinity the-infinity changed the title prevent additional properties reject unknown fields Feb 18, 2026
@the-infinity
Copy link
Copy Markdown
Contributor Author

Changed the naming and added the DataclassValidator option. Did postpone the env thing as I want to play around with that a bit before jumping to conclusions.

@binaryDiv binaryDiv added the new feature New feature that is not (only) a validator class label Mar 16, 2026
def decorator(_cls: type[_T]) -> type[_T]:
# Pop validataclass-specific options before passing kwargs to @dataclass
reject_unknown_fields = kwargs.pop('reject_unknown_fields', False)
reject_unknown_fields = kwargs.pop('reject_unknown_fields', None)
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.

Sorry I didn't notice that earlier, but: Is there a reason you're getting the parameter from kwargs instead of just adding it as a proper parameter? This way you're not getting any type checking or auto completion.

@the-infinity the-infinity force-pushed the prevent-additional-properties branch from 7a4019c to 6ea7ff3 Compare March 27, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature New feature that is not (only) a validator class

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants