Skip to content

Commit

Permalink
Factory.create(): Add construct kwarg
Browse files Browse the repository at this point in the history
  • Loading branch information
leroyvn committed Jul 13, 2021
1 parent 7507805 commit a2e5874
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 2 deletions.
25 changes: 25 additions & 0 deletions docs/rst/usage.rst
Expand Up @@ -117,6 +117,31 @@ Any subtype of an allowed type is allowed:
>>> factory.create("lamb", allowed_cls=Sheep)
Lamb(wool='some')

.. note::

A very common and Pythonic design pattern consists in defining special
constructors using class methods. If you use this approach, *Dessine-moi*
lets you select a constructor using the ``construct`` argument. For
demonstrative purposes, let us attach a class method constructor to our
``Sheep`` class:

.. doctest::

>>> @classmethod
... def unsheavable(cls):
... return cls(wool="none")
>>> Sheep.unsheavable = unsheavable

We can now route object creation to this function using the ``construct``
keyword argument. Since the ``unsheavable()`` class method takes no argument,
we do not pass the ``args`` and ``kwargs`` arguments:

.. doctest::

>>> factory.create("sheep", construct="unsheavable")
Sheep(wool='none')


Convert objects
^^^^^^^^^^^^^^^

Expand Down
10 changes: 9 additions & 1 deletion src/dessinemoi/__init__.py
Expand Up @@ -109,6 +109,7 @@ def create(
self,
type_id: str,
allowed_cls: Optional[Union[Type, Tuple[Type]]] = None,
construct: Optional[str] = None,
args: Optional[Sequence] = None,
kwargs: Optional[MutableMapping] = None,
) -> Any:
Expand All @@ -123,6 +124,10 @@ def create(
restricted. If ``type_id`` does not reference one of these allowed
types, an exception will be raised.
:param construct:
If not ``None``, attempt instantiation using a class method
constructor instead of the default constructor.
:param args:
A sequence of arguments to pass to the constructor of the created
type.
Expand Down Expand Up @@ -153,7 +158,10 @@ def create(
if kwargs is None:
kwargs = dict()

return cls(*args, **kwargs)
if construct is not None:
return getattr(cls, construct)(*args, **kwargs)
else:
return cls(*args, **kwargs)

def _convert_impl(
self,
Expand Down
19 changes: 18 additions & 1 deletion tests/test_dessinemoi.py
Expand Up @@ -10,7 +10,7 @@ def factory():
yield Factory()


def test_factory_create(factory):
def test_factory_instantiate(factory):
# A Factory instance can be created
assert isinstance(factory, Factory)

Expand Down Expand Up @@ -81,6 +81,23 @@ class Ram(Sheep):
factory.create("sheep", args=(5, "Dolly"), allowed_cls=Ram)


def test_factory_classmethod(factory):
@factory.register
@attr.s(frozen=True)
class Sheep:
_TYPE_ID = "sheep"
age = attr.ib()
name = attr.ib()

@classmethod
def old(cls, name):
return cls(15, name)

# The construct parameter allows for the selection of a class method constructor
s = factory.create("sheep", construct="old", kwargs={"name": "Romuald"})
assert s.age == 15


def test_convert(factory):
@factory.register
@attr.s(frozen=True)
Expand Down

0 comments on commit a2e5874

Please sign in to comment.