Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use introspection for improved ergonomics #774

Open
rbarrois opened this issue Aug 19, 2020 · 1 comment
Open

Use introspection for improved ergonomics #774

rbarrois opened this issue Aug 19, 2020 · 1 comment

Comments

@rbarrois
Copy link
Member

The problem

If a developer wants to use various helpers in a given declaration (for instance, having a unique company name), they have to go through unnecessary complicated hoops:

class ContractFactory(factory.Factory):
    class Meta:
        model = Contract

    class Params:
        company_base = factory.Faker('company')
    company = factory.LazyAttributeSequence(lambda o, n: "{c} {n}".format(c=o.company, n=n))

There is no way to pass the result of a factory.Faker() call to a factory.Sequence declaration, for instance.

Proposed solution

One option would be to add an extra declaration, for instance:

company = factory.Transform(
    lambda c, n: "{c} {n}".format(c=o.company, n=n),
    c=factory.Faker('company'),
    n=factory.Sequence(str),
)

However, this requires us to add more and more building blocks as user needs evolve.

Another option would be to play with method introspection: if the default values of a function's arguments are factory.Declaration subclasses, we could automatically detect it:

def company(self, base=factory.Faker('company'), n=factory.Sequence()):
    return "{base} {n}".format(base=base, n=n)

Summary

Pros:

  • Simpler API
  • Avoids the need for more and more declarations for advanced use cases

Cons:

  • Somewhat magic: nothing explains why those attributes are detected
  • Doesn't work with complex structures (def foo(x=SomeClass(y=factory.Faker('name'))))

Alternative

A possible compromise would be to only inspect the parameters of factory.Declaration subclasses:

@factory.lazy_attribute
def company(self, base=factory.Faker('company'), n=factory.Sequence()):
    return "{base} {n}".format(base=base, n=n)

Here, the reader would have an idea that something specific is happening thanks to the @lazy_attribute decorator.

@timorthi
Copy link

Chiming in here from #767 . From an API perspective, the alternative proposal looks the most intuitive to me. Anectodally, I expected RelatedFactoryList(SomeFactory, size=factory.SelfAttribute('foo')) to work because the base object had already been instantiated by the time RelatedFactoryList is called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants