Skip to content

Commit

Permalink
feat: autofill model argument when calling create_factory with re…
Browse files Browse the repository at this point in the history
…ceiving factory `__model__` (#429)

Co-authored-by: Benoit.Godard <godardb@delawareconsulting.com>
Co-authored-by: guacs <126393040+guacs@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 31, 2023
1 parent b7b88a8 commit 155f4a4
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 3 deletions.
27 changes: 27 additions & 0 deletions docs/examples/declaring_factories/test_example_8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from dataclasses import dataclass
from enum import Enum

from polyfactory.factories import DataclassFactory


class Species(str, Enum):
CAT = "Cat"
DOG = "Dog"
RABBIT = "Rabbit"
MOUSE = "Mouse"


@dataclass
class Pet:
name: str
species: Species
sound: str


def test_imperative_sub_factory_creation() -> None:
pet_factory = DataclassFactory.create_factory(model=Pet)
cat_factory = pet_factory.create_factory(species=Species.CAT)
cat_instance = cat_factory.build()

assert isinstance(cat_instance, Pet)
assert cat_instance.species == Species.CAT
8 changes: 8 additions & 0 deletions docs/usage/declaring_factories.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,11 @@ You can also use this method to create factories imperatively:
.. literalinclude:: /examples/declaring_factories/test_example_6.py
:caption: Imperative factory creation
:language: python

Eventually you can use this method on an existing concrete factory to create a sub factory overriding some parent configuration:

.. literalinclude:: /examples/declaring_factories/test_example_8.py
:caption: Imperative sub factory creation
:language: python

In this case you don't need to specify the `model` argument to the :meth:`create_factory <polyfactory.factories.base.BaseFactory.create_factory>` method. The one from the parent factory will be used.
13 changes: 10 additions & 3 deletions polyfactory/factories/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,23 +426,30 @@ def _create_generic_fn() -> Callable:
@classmethod
def create_factory(
cls: type[F],
model: type[T],
model: type[T] | None = None,
bases: tuple[type[BaseFactory[Any]], ...] | None = None,
**kwargs: Any,
) -> type[F]:
"""Generate a factory for the given type dynamically.
:param model: A type to model.
:param model: A type to model. Defaults to current factory __model__ if any.
Otherwise, raise an error
:param bases: Base classes to use when generating the new class.
:param kwargs: Any kwargs.
:returns: A 'ModelFactory' subclass.
"""
if model is None:
try:
model = cls.__model__
except AttributeError as ex:
msg = "A 'model' argument is required when creating a new factory from a base one"
raise TypeError(msg) from ex
return cast(
"Type[F]",
type(
f"{model.__name__}Factory",
f"{model.__name__}Factory", # pyright: ignore[reportOptionalMemberAccess]
(*(bases or ()), cls),
{"__model__": model, **kwargs},
),
Expand Down
17 changes: 17 additions & 0 deletions tests/test_base_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from pydantic.main import BaseModel

from polyfactory.factories import DataclassFactory
from polyfactory.factories.base import BaseFactory
from polyfactory.factories.pydantic_factory import ModelFactory


Expand Down Expand Up @@ -78,3 +79,19 @@ class MyFactory(FooModelFactory):
# see https://github.com/litestar-org/polyfactory/issues/198
ModelFactory._base_factories.remove(FooModelFactory)
ModelFactory._base_factories.remove(DummyModelFactory)


def test_create_factory_without_model_reuse_current_factory_model() -> None:
@dataclass
class Foo:
pass

factory = DataclassFactory.create_factory(model=Foo)
sub_factory = factory.create_factory()

assert sub_factory.__model__ == Foo


def test_create_factory_from_base_factory_without_providing_a_model_raises_error() -> None:
with pytest.raises(TypeError):
BaseFactory.create_factory()

0 comments on commit 155f4a4

Please sign in to comment.