polyfactory icon indicating copy to clipboard operation
polyfactory copied to clipboard

Bug: Support custom types providers for which there already exists a registered base factory

Open mjmaurer opened this issue 1 year ago • 2 comments

Description

It seems like defining a type in get_provider_map(cls) wouldn't work if that type is already handled by an existing base_factory (such as dataclass, Pydantic model, etc).

It's possible this is intended, as the Handling Custom Types docs only specifies a plain-old Python class CustomSecret.

Please see the MCVE below for what my desired behavior would be. Is this currently possible? I've tried playing around with __base_factory_overrides__ but didn't have any luck.

URL to code causing the issue

No response

MCVE

from pydantic import BaseModel, ConfigDict
from polyfactory.factories.pydantic_factory import ModelFactory

class CustomSecretPydantic(BaseModel):
    value: str


class CustomSecretPlain:
    def __init__(self, value: str) -> None:
        self.value = value

    def __repr__(self) -> str:
        return self.value

    def __str__(self) -> str:
        return self.value


class MyModel(BaseModel):
    model_config = ConfigDict(arbitrary_types_allowed=True)

    id: int
    secret: CustomSecretPydantic


class MyFactory(ModelFactory[MyModel]):

    @classmethod
    def get_provider_map(cls) -> dict[type, Any]:
        providers_map = super().get_provider_map()

        return {
            **providers_map,
            CustomSecretPydantic: lambda: CustomSecretPydantic.model_validate({"value": "asdf"}),
            CustomSecretPlain: lambda: CustomSecretPlain(value="asdf"),
        }


if __name__ == "__main__":

    print(MyFactory.build())

Steps to reproduce

1. Run code
2. Observe that "asdf" is NOT generated

I'd expect that it would be.

Screenshots

"In the format of: ![SCREENSHOT_DESCRIPTION](SCREENSHOT_LINK.png)"

Logs

No response

Release Version

2.17.0

Platform

  • [ ] Linux
  • [X] Mac
  • [ ] Windows
  • [ ] Other (Please specify in the description above)

mjmaurer avatar Nov 22 '24 22:11 mjmaurer

Hi @mjmaurer ,

Looks like this currently isn't possible but PRs welcome to fix.

Couple of potential workarounds

  • Set this at the field level.
  • Create a separate factory that targets this model only and register as default type, e.g. something like
class CustomSecretPydantic(ModelFactory[CustomSecretPydantic]):
      __set_as_default_factory_for_type__ = True

     @classmethod
     def process_kwargs(cls, **kwargs: Any) -> dict[str, Any]:
           return {"value": "asdf"}

adhtruong avatar Nov 24 '24 16:11 adhtruong

Thanks, the latter option worked perfectly!

mjmaurer avatar Nov 26 '24 16:11 mjmaurer