aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/changelog.rst4
-rw-r--r--factory/django.py9
-rw-r--r--tests/test_django.py21
3 files changed, 32 insertions, 2 deletions
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 13fdd68..0cf8368 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -12,6 +12,10 @@ ChangeLog
- Add support for getting/setting :mod:`factory.fuzzy`'s random state (see :issue:`175`, :issue:`185`).
- Support lazy evaluation of iterables in :class:`factory.fuzzy.FuzzyChoice` (see :issue:`184`).
+*Bugfix:*
+
+ - Avoid issues when using :meth:`factory.django.mute_signals` on a base factory class (see :issue:`183`).
+
*Deprecation:*
- Remove deprecated features from :ref:`v2.4.0`
diff --git a/factory/django.py b/factory/django.py
index 7050366..e823ee9 100644
--- a/factory/django.py
+++ b/factory/django.py
@@ -269,6 +269,9 @@ class mute_signals(object):
signal.receivers = receivers
self.paused = {}
+ def copy(self):
+ return mute_signals(*self.signals)
+
def __call__(self, callable_obj):
if isinstance(callable_obj, base.FactoryMetaClass):
# Retrieve __func__, the *actual* callable object.
@@ -277,7 +280,8 @@ class mute_signals(object):
@classmethod
@functools.wraps(generate_method)
def wrapped_generate(*args, **kwargs):
- with self:
+ # A mute_signals() object is not reentrant; use a copy everytime.
+ with self.copy():
return generate_method(*args, **kwargs)
callable_obj._generate = wrapped_generate
@@ -286,7 +290,8 @@ class mute_signals(object):
else:
@functools.wraps(callable_obj)
def wrapper(*args, **kwargs):
- with self:
+ # A mute_signals() object is not reentrant; use a copy everytime.
+ with self.copy():
return callable_obj(*args, **kwargs)
return wrapper
diff --git a/tests/test_django.py b/tests/test_django.py
index 0cbef19..4653305 100644
--- a/tests/test_django.py
+++ b/tests/test_django.py
@@ -592,6 +592,27 @@ class PreventSignalsTestCase(unittest.TestCase):
self.assertSignalsReactivated()
+ def test_class_decorator_with_subfactory(self):
+ @factory.django.mute_signals(signals.pre_save, signals.post_save)
+ class WithSignalsDecoratedFactory(factory.django.DjangoModelFactory):
+ class Meta:
+ model = models.WithSignals
+
+ @factory.post_generation
+ def post(obj, create, extracted, **kwargs):
+ if not extracted:
+ WithSignalsDecoratedFactory.create(post=42)
+
+ # This will disable the signals (twice), create two objects,
+ # and reactivate the signals.
+ WithSignalsDecoratedFactory()
+
+ self.assertEqual(self.handlers.pre_init.call_count, 2)
+ self.assertFalse(self.handlers.pre_save.called)
+ self.assertFalse(self.handlers.post_save.called)
+
+ self.assertSignalsReactivated()
+
def test_class_decorator_build(self):
@factory.django.mute_signals(signals.pre_save, signals.post_save)
class WithSignalsDecoratedFactory(factory.django.DjangoModelFactory):