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

release 2.10.0 has broken passing conditional kwargs to post_generation methods #466

Closed
dlobue opened this Issue Apr 5, 2018 · 1 comment

Comments

Projects
None yet
2 participants
@dlobue

dlobue commented Apr 5, 2018

This issue is easiesr to show than explain. Here's a simplified version of my code:

    import factory
    from factory.fuzzy import FuzzyText


    class CopyTarget(object):
        def __init__(self, label, plain_m2m=None):
            self.label = label
            self.plain_m2m = plain_m2m or []


    class Other(object):
        def __init__(self, label):
            self.label = label


    class BaseFactory(factory.Factory):
        label = FuzzyText()


    class OtherFactory(BaseFactory):
        class Meta:
            model = Other


    class CopyTargetFactory(BaseFactory):
        class Meta:
            model = CopyTarget

        class Params:
            empty = factory.Trait(
                plain_m2m=None,
                plain_m2m__num=0,
            )

        plain_m2m__num = 3

        @factory.post_generation
        def plain_m2m(self, create, extracted, num=0, **kwargs):
            if not create:
                # Simple build, do nothing.
                return

            if extracted is not None:
                if not isinstance(extracted, Iterable):
                    extracted = [extracted]
                self.plain_m2m.extend(*extracted)
            elif num:
                self.plain_m2m.extend(*(OtherFactory()
                                     for _ in xrange(num)))



    if __name__ == '__main__':
        value = CopyTargetFactory()

What's happening is in 2.10.0 is xrange is not receiving an integer like it expects. Here's the traceback:

Traceback (most recent call last):
  File "/root/docs/notes/gist/factoryboy_maybe_broke_postgen.py", line 55, in <module>
    value = CopyTargetFactory()
  File "/root/.pyenv/versions/frontend/lib/python2.7/site-packages/factory/base.py", line 46, in __call__
    return cls.create(**kwargs)
  File "/root/.pyenv/versions/frontend/lib/python2.7/site-packages/factory/base.py", line 563, in create
    return cls._generate(enums.CREATE_STRATEGY, kwargs)
  File "/root/.pyenv/versions/frontend/lib/python2.7/site-packages/factory/base.py", line 500, in _generate
    return step.build()
  File "/root/.pyenv/versions/frontend/lib/python2.7/site-packages/factory/builder.py", line 293, in build
    context=postgen_context,
  File "/root/.pyenv/versions/frontend/lib/python2.7/site-packages/factory/declarations.py", line 472, in call
    context=context,
  File "/root/.pyenv/versions/frontend/lib/python2.7/site-packages/factory/declarations.py", line 610, in call
    instance, create, context.value, **context.extra)
  File "/root/docs/notes/gist/factoryboy_maybe_broke_postgen.py", line 50, in plain_m2m
    for _ in xrange(num)))
TypeError: an integer is required

For some reason, num is an instance of Maybe. Here's the repr of it: Maybe(<SelfAttribute(u'empty', default=False)>, yes=0, no=3). I've used this code successfully in factory-boy version 2.7.0 and 2.9.2.

@rbarrois

This comment has been minimized.

Member

rbarrois commented May 4, 2018

Indeed, that's a bug. Basically, the use of the Trait turns the plain_m2m__num=3 declaration into a Maybe(decider, yes, no) declaration.

And that declaration is not resolved before being passed to the post_generation declaration.

@rbarrois rbarrois added the Bug label May 4, 2018

@rbarrois rbarrois self-assigned this May 4, 2018

rbarrois added a commit that referenced this issue May 4, 2018

Add reproduction for issue #466.
When combining a Trait and a post_generation declaration, the Trait's
Maybe is not resolved before calling the post_generation declaration.

rbarrois added a commit that referenced this issue May 4, 2018

Increase testing regarding issue #466.
The problem is that declarations passed as kwargs to a post-generation
hook aren't resolved, unless that declaration is a RelatedFactory.

rbarrois added a commit that referenced this issue May 4, 2018

Evaluate extra kwargs passed to declarations.
Unroll a declaration's context before evaluating it (i.e evaluate all
lazy declarations in that context before passing them as kwargs to the
actual declaration's evaluate() / call() function).

Note: this allows for the following code:

class SomeFactory:
    value__low = factory.Sequence(lambda n: n)
    value = factory.fuzzy.FuzzyInteger(low=3, high=100)

Whereas passing ``FuzzyInteger(low=factory.Sequence(lambda n: n))``
wouldn't work as expected.

Whether that feature is supported or not has not been decided yet; it's
considered to be an undefined behaviour and a side-effect of internal
changes.

Closes #466.

rbarrois added a commit that referenced this issue May 4, 2018

Add reproduction for issue #466.
When combining a Trait and a post_generation declaration, the Trait's
Maybe is not resolved before calling the post_generation declaration.

rbarrois added a commit that referenced this issue May 4, 2018

Increase testing regarding issue #466.
The problem is that declarations passed as kwargs to a post-generation
hook aren't resolved, unless that declaration is a RelatedFactory.

@rbarrois rbarrois closed this in 3d502ff May 4, 2018

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