Bug: Calling build of a factory with parameter for an optional nested field does not work
skallfass opened this issue · 2 comments
Description
In this example I expect the assertion error to never be raised, but it is.
It seems as the argument for nested
in this case is not used on build.
I think the problem is related to the kwargs
used in https://github.com/litestar-org/polyfactory/blob/main/polyfactory/factories/base.py#L729 as they are mutable and modified in functions called by this function.
If kwargs are not used directly but as kwargs.copy()
, the problem does not occur.
URL to code causing the issue
https://github.com/litestar-org/polyfactory/blob/main/polyfactory/factories/base.py#L729
MCVE
from typing import Optional, TypedDict
from polyfactory.factories.typed_dict_factory import TypedDictFactory
class NestedStructure(TypedDict):
field_a: int
field_b: str
class MainStructure(TypedDict):
name: str
nested: Optional[NestedStructure]
class MainStructureFactory(TypedDictFactory[MainStructure]):
__model__ = MainStructure
for attempt in range(10):
example = MainStructureFactory.build(nested=NestedStructure(field_a=1, field_b="2"))
assert example["nested"], f"attempt {attempt} failed"
Steps to reproduce
1. Run the example code
2. See assertion error
Screenshots
No response
Logs
Traceback (most recent call last):
File ".poly_check.py", line 23, in <module>
assert example["nested"], f"attempt {attempt} failed"
│ └ 4
└ {'nested': None, 'name': 'JtCbpukJvunqdfIKCZis'}
AssertionError: attempt 4 failed
Release Version
2.8.0
Platform
- Linux
- Mac
- Windows
- Other (Please specify in the description above)
Funding
- If you would like to see an issue prioritized, make a pledge towards it!
- We receive the pledge once the issue is completed & verified
Hi. Thanks for reporting this! This is indeed a bug. It's due to the fact that it's not possible to figure out that an instance of a dictionary is an instance of a TypedDict
. This causes extract_field_build_parameters
to return the value for nested
even though it shouldn't.
There are two workarounds for this:
-
Don't use a
TypedDict
for your nested structure but instead use adataclass
or pydantic model etc. This is an easy option if you don't need to use aTypedDict
. -
If you need to absolutely use a
TypedDict
, you could override thebuild
method on the factory with something like this:
class NestedStructure(TypedDict):
field_a: int
field_b: str
class MainStructure(TypedDict):
name: str
nested: Optional[NestedStructure]
class MainStructureFactory(TypedDictFactory[MainStructure]):
__model__ = MainStructure
_sentinel = object()
@classmethod
def build(cls, **kwargs: Any) -> MainStructure:
nested = kwargs.pop("nested", cls._sentinel)
instance = super().build(**kwargs)
if nested is not cls._sentinel:
instance["nested"] = nested
return instance
I'm closing this since I don't think there's an obvious fix for this due to how TypedDict
works.