I'm a core developer of Hypothesis, and largely responsible for our `from_type()` strategy. As the name suggests, it takes a type and returns an object that can generate instances of it, including for things in the `typing` module that aren't really types at runtime. So far, so good!
For obvious reasons, I'm looking to support TypedDict too, but I've run into a snag: I can't see any way to distinguish between optional and required keys at runtime! It's not actually *incorrect* to treat all keys as required, but we'll miss test cases that could have exposed real bugs.
Consider the following cases:
a: int # a is required
class B(A, total=False):
b: bool # a is required, b is optional
c: str # a is required, b is optional, c is required again
The PEP is clear about the semantics, and this is obvious enough when reading the code. At runtime the __annotations__ attribute of each class gives us the set of allowed keys and the type of each corresponding value, but we have a problem:
- C has __total__==True, but b is not actually required.
- B has __total__==False, but a *is* required.
- I can't see any way to get the parent classes of a TypedDict class!
The _TypedDictMeta metaclass updates the attributes, but leaves no record of the parent type - at runtime A, B, and C all appear to inherit directly from dict.
Is there a way to directly determine which keys are required and which are optional at runtime? If not, is there a way to determine the parent class(es) of a TypedDict? If not, can we please add one - preferably the former - for 3.8.1?
If there's an agreed design I'd be happy to implement it - as a concrete suggestion, I'd add two frozensets, __required_keys__ and __optional_keys__. Obvious, useful, orthogonal to current attributes, and easy to fall back to treating all keys as required.