pytest-factoryboy icon indicating copy to clipboard operation
pytest-factoryboy copied to clipboard

@register decorator is incompatible with circular imports

Open jcushman opened this issue 6 years ago • 0 comments

Factoryboy documents the creation of circular dependencies like this:

class UserFactory(factory.Factory):
    class Meta:
        model = User

    username = 'john'
    main_group = factory.SubFactory('users.factories.GroupFactory')

class GroupFactory(factory.Factory):
    class Meta:
        model = Group

    name = "MyGroup"
    owner = factory.SubFactory(UserFactory)

That example will not work with the @register decorator:

@register
class UserFactory(factory.Factory):
    class Meta:
        model = User

    username = 'john'
    main_group = factory.SubFactory('users.factories.GroupFactory')

@register
class GroupFactory(factory.Factory):
    class Meta:
        model = Group

    name = "MyGroup"
    owner = factory.SubFactory(UserFactory)

Throws:

ImportError while loading conftest '/app/_python/conftest.py'.
conftest.py:58: in <module>
    class UserFactory(factory.Factory):
/usr/local/lib/python3.5/site-packages/pytest_factoryboy/fixture.py:91: in register
    subfactory_class = value.get_factory()
/usr/local/lib/python3.5/site-packages/factory/declarations.py:647: in get_factory
    return self.factory_wrapper.get()
/usr/local/lib/python3.5/site-packages/factory/declarations.py:363: in get
    self.name,
/usr/local/lib/python3.5/site-packages/factory/utils.py:20: in import_object
    return getattr(module, str(attribute_name))
E   AttributeError: module 'conftest' has no attribute 'GroupFactory'

The issue seems to be that @register resolves the imports when applied, before the later classes are defined. So a workaround is to use the non-decorator version at the end of the file. This works:

# same as first code block, then ...
register(UserFactory)
register(GroupFactory)

Ideally the decorator form could Just Work, by delaying resolution of the imports until needed. Alternatively maybe the problem could be documented, and register() could detect what's happening and throw a more useful error?

jcushman avatar Oct 03 '19 13:10 jcushman