Add new validator: AnyDictValidator#148
Conversation
| ] | ||
|
|
||
|
|
||
| class AnyDictValidator(DictValidator[object]): |
There was a problem hiding this comment.
Out of curiosity what is the advantage of object of Any here?
There was a problem hiding this comment.
I used it in this case because I noticed that it improves the type checking. But it's actually a good question in general, so I looked up again what the exact difference is, and... I think we should generally use object instead of Any more often. 😅 (Like, not everywhere, but in a lot of places object might be better.)
Basically, Any doesn't just mean "any possible type", it actually is more a kind of pseudotype to locally disable type checking for a given variable. So I guess it's less "any type" and more "unknown type" in a way. Setting Any as a type is essentially the same as having no type annotation at all, with the only difference being that someone explicitly declared the variable as "this is untyped, but on purpose".
object on the other hand is a static type that can stand for any possible type (because every type in Python inherits from object, so it's the base type). The difference is that with object you have statically typed code, meaning mypy will do strict type checking on it.
What this means in practice: Any allows (almost?) any operation on the variable (including assignments!), although for concrete types the operation might not actually be valid. If you type a variable as object, mypy will only allow operations on the variable that are supported by object, i.e. only operations that are valid for every type in Python.
For example, if you have this code:
var.some_method_that_might_not_exist()
var[i]
var + 5
explicitly_typed_Var: str = varIf var is typed as Any, mypy will accept that code. But if var is typed as object, mypy should report errors for every line: Not every type has a method with that specific name, not every type is indexable, not every type supports + with an integer, and the last line is invalid because you're can't assign object to an str variable.
The last one is also the reason I used object for the AnyDictValidator. Because if you use Any, defining a validataclass field like foo: dict[str, str] = AnyDictValidator() would be allowed by mypy (because it's valid to assign Any to str). But that would be wrong here, because the AnyDictValidator accepts user input that could very well be a dict[str, int] or some other dict.
For more info, see also the mypy docs: https://mypy.readthedocs.io/en/stable/dynamic_typing.html
This PR adds a new validator, the
AnyDictValidator. This validator accepts any dictionary without validating its values, as long as the keys are strings (just like in aDictValidator).It's essentially a shorthand for
DictValidator(default_validator=AnythingValidator()).Please note that this validator differs from an
AnythingValidator(allowed_types=[dict]), because the latter does no validation on the dictionary at all and therefore allows non-string keys.Since JSON objects cannot have non-string keys, this doesn't seem to make any difference in practice. However, the
AnyDictValidatorallows for better typing, because it always returns an object of typedict[str, object], while the AnythingValidator returnsdict[Any, Any].