Bug: Random values from a base Enum
Description
I don't think it's a bug, but not sure:
Consider this example:
from dataclasses import dataclass
from enum import Enum
from polyfactory.factories import DataclassFactory
class BaseEnum(Enum): ...
class OtherEnum(BaseEnum): # <--- inherits from BaseEnum
VALUE_1 = 1
VALUE_2 = 2
@dataclass
class MyModel:
values: dict[BaseEnum, int] # <--- a dict with keys from the BaseEnum
class MyModelFactory(DataclassFactory[MyModel]): ...
if __name__ == "__main__":
my_model = MyModelFactory.build()
This will raise an IndexError: IndexError: Cannot choose from an empty sequence.
Which is obvious as Litestar will try to populate the keys in the values dict by doing this: __random__(list(BaseEnum)). That results in an empty list which gives the error.
But the BaseEnum (which I don't have control over) is only the base enum for the OtherEnum which does contain values.
Is there any way in my factory to tell when BaseEnum is encountered it should pick values from OtherEnum ?
URL to code causing the issue
No response
MCVE
Steps to reproduce
Run the above example.
Screenshots
Logs
Release Version
2.19.0
Platform
- [x] Linux
- [ ] Mac
- [ ] Windows
- [ ] Other (Please specify in the description above)
Hi! If you can not do something like this:
class MyModel:
values: dict[OtherEnum, int]
Maybe this decision will suit you? Any of Use(), lambda or @classmethod.
I think you mean I explicity/manually fill the values dictionary?.
I could do that, but my problem is that my real data is really nested and that would require me to do the manual filling in a lot of places.
I have indeed no control of the shape of my model. So
class MyModel:
values: dict[OtherEnum, int]
is not possible unfortunately.
I'm struggling with the same or a similar issue.
from enum import Enum
from polyfactory.factories.pydantic_factory import ModelFactory
from pydantic import Field
from pydantic_settings import BaseSettings
class SubSettings(BaseSettings):
enum: Enum = Field(..., exclude=True)
class Settings(BaseSettings):
sub_settings: SubSettings
class DummyEnum(str, Enum):
DUMMY = "dummy"
class SubSettingsFactory(ModelFactory[SubSettings]):
@classmethod
def enum(cls) -> DummyEnum:
return DummyEnum.DUMMY
class SettingsFactory(ModelFactory[Settings]):
@classmethod
def sub_settings(cls) -> SubSettings:
return SubSettingsFactory.build()
if __name__ == "__main__":
SubSettingsFactory.build()
SettingsFactory.build()
This works. However, I didn't think that this part was necessary.
class SettingsFactory(ModelFactory[Settings]):
@classmethod
def sub_settings(cls) -> SubSettings:
return SubSettingsFactory.build()
Based on the docs, it seems like this should be good enough.
class SettingsFactory(ModelFactory[Settings]): ...
However, when I use this, SettingsFactory.build() fails.
@ericchansen this case is supported if configure this as the default factory https://polyfactory.litestar.dev/latest/usage/configuration.html#defining-default-factories
For the original case, this is particularly well supported and current behaviour is correct.
As workaround, could override get_field_value and then check FieldMeta.annnotation and map this type explicitly. Some thing like
class MyBaseFactory(ModelFactory):
@classmethod
def get_field_value(
field_meta: FieldMeta,
field_build_parameters: Any | None = None,
build_context: BuildContext | None = None,
) -> Any:
if field_meta.annotation is BaseEnum:
# Logic for mapping this
return super().get_field_value(....)
PRs welcome to have this more generalised, maybe as a configuration on factory to map one type to another.