Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 68be2ad

Browse files
authored
Merge pull request #187 from jaredlewis/develop
Update the `defer_entity_syncing` decorator to support an optional handler.
2 parents a92a635 + d994caf commit 68be2ad

File tree

5 files changed

+70
-27
lines changed

5 files changed

+70
-27
lines changed

entity/sync.py

+42-25
Original file line numberDiff line numberDiff line change
@@ -56,40 +56,56 @@ def wrapper(wrapped, instance, args, kwargs):
5656
return wrapper
5757

5858

59-
@wrapt.decorator
60-
def defer_entity_syncing(wrapped, instance, args, kwargs):
59+
def defer_entity_syncing(*args, handler=None):
6160
"""
62-
A decorator that can be used to defer the syncing of entities until after the method has been run
63-
This is being introduced to help avoid deadlocks in the meantime as we attempt to better understand
64-
why they are happening
61+
A decorator for deferring entity syncing until after the function is complete
62+
An optional handler can be specified to handle the entity syncing.
63+
If no handler is passed the default sync_entities method will be called
6564
"""
6665

67-
# Defer entity syncing while we run our method
68-
sync_entities.defer = True
66+
# Set a default handler
67+
handler = handler or sync_entities
6968

70-
# Run the method
71-
try:
72-
return wrapped(*args, **kwargs)
69+
@wrapt.decorator
70+
def wrapper(wrapped, instance, args, kwargs):
71+
"""
72+
A decorator that can be used to defer the syncing of entities until after the method has been run
73+
This is being introduced to help avoid deadlocks in the meantime as we attempt to better understand
74+
why they are happening
75+
"""
7376

74-
# After we run the method disable the deferred syncing
75-
# and sync all the entities that have been buffered to be synced
76-
finally:
77-
# Enable entity syncing again
78-
sync_entities.defer = False
77+
# Defer entity syncing while we run our method
78+
sync_entities.defer = True
7979

80-
# Get the models that need to be synced
81-
model_objs = list(sync_entities.buffer.values())
80+
# Run the method
81+
try:
82+
return wrapped(*args, **kwargs)
8283

83-
# If none is in the model objects we need to sync all
84-
if None in sync_entities.buffer:
85-
model_objs = list()
84+
# After we run the method disable the deferred syncing
85+
# and sync all the entities that have been buffered to be synced
86+
finally:
87+
# Enable entity syncing again
88+
sync_entities.defer = False
8689

87-
# Sync the entities that were deferred if any
88-
if len(sync_entities.buffer):
89-
sync_entities(*model_objs)
90+
# Get the models that need to be synced
91+
model_objs = list(sync_entities.buffer.values())
92+
93+
# If none is in the model objects we need to sync all
94+
if None in sync_entities.buffer:
95+
model_objs = list()
9096

91-
# Clear the buffer
92-
sync_entities.buffer = {}
97+
# Sync the entities that were deferred if any
98+
if len(sync_entities.buffer):
99+
handler(*model_objs)
100+
101+
# Clear the buffer
102+
sync_entities.buffer = {}
103+
104+
# If the decorator is called without arguments
105+
if len(args) == 1 and callable(args[0]):
106+
return wrapper(args[0])
107+
else:
108+
return wrapper
93109

94110

95111
@wrapt.decorator
@@ -193,6 +209,7 @@ def sync_entities(*model_objs):
193209
Args:
194210
model_objs (List[Model]): The model objects to sync. If empty, all entities will be synced
195211
"""
212+
196213
if sync_entities.suppress:
197214
# Return false that we did not do anything
198215
return False

entity/tests/sync_tests.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
suppress_entity_syncing,
1313
)
1414
from entity.signal_handlers import turn_on_syncing, turn_off_syncing
15-
from unittest.mock import patch, MagicMock, call
15+
from unittest.mock import patch, MagicMock, call, Mock
1616

1717
from entity.tests.models import (
1818
Account, Team, EntityPointer, DummyModel, MultiInheritEntity, AccountConfig, TeamConfig, TeamGroup,
@@ -1064,6 +1064,26 @@ def test_method(test):
10641064
# Ensure that we did not call sync entities
10651065
self.assertFalse(mock_sync_entities.called)
10661066

1067+
def test_defer_custom_handler(self):
1068+
# Create a mock handler
1069+
mock_handler = Mock()
1070+
1071+
# Create a test sync method to be decorated
1072+
@defer_entity_syncing(handler=mock_handler)
1073+
def test_method(count=5):
1074+
# Create some entities
1075+
for i in range(count):
1076+
Account.objects.create()
1077+
1078+
# Call the test method
1079+
test_method(count=5)
1080+
1081+
# Assert that we called our custom handler
1082+
self.assertEqual(Entity.objects.all().count(), 0)
1083+
mock_handler.assert_called_once_with(
1084+
*Account.objects.all()
1085+
)
1086+
10671087

10681088
class SuppressEntitySyncingTests(EntityTestCase):
10691089
"""

entity/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '6.2.2'
1+
__version__ = '6.2.3'

manage.py

+4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#!/usr/bin/env python
22
import sys
33

4+
# These lines allow nose tests to work in Python 3.10
5+
import collections.abc
6+
collections.Callable = collections.abc.Callable
7+
48
# Show warnings about django deprecations - uncomment for version upgrade testing
59
import warnings
610
from django.utils.deprecation import RemovedInNextVersionWarning

release_notes.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Release Notes
22

3+
- 6.2.3:
4+
- Update the `defer_entity_syncing` decorator to support an optional handler.
35
- 6.2.2:
46
- Fixed entity group logic string for logic sets containing more than 2 items being operated on
57
- 6.2.1:

0 commit comments

Comments
 (0)