pytest-dev / pytest-dev/pytest-factoryboy

@register decorator is incompatible with circular imports

Open
#82 0 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
400
Forks
44
Avg merge
6h 34m
Merged PRs (30d)
1

Description

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?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at pytest_factoryboy/fixture.py, where register is applied in the traceback, and reproduce the UserFactory/GroupFactory example with the string SubFactory reference. Trace when the referenced factory is resolved and add coverage for the decorator form; done means circular registration works or the failure is reported and documented clearly.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.