django-bulk-hooks 0.2.15__py3-none-any.whl → 0.2.17__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-bulk-hooks might be problematic. Click here for more details.
- django_bulk_hooks/__init__.py +20 -24
- django_bulk_hooks/changeset.py +1 -1
- django_bulk_hooks/conditions.py +8 -12
- django_bulk_hooks/decorators.py +15 -11
- django_bulk_hooks/dispatcher.py +19 -10
- django_bulk_hooks/factory.py +36 -38
- django_bulk_hooks/handler.py +5 -6
- django_bulk_hooks/helpers.py +4 -3
- django_bulk_hooks/models.py +12 -13
- django_bulk_hooks/operations/__init__.py +5 -5
- django_bulk_hooks/operations/analyzer.py +14 -14
- django_bulk_hooks/operations/bulk_executor.py +220 -129
- django_bulk_hooks/operations/coordinator.py +82 -61
- django_bulk_hooks/operations/mti_handler.py +91 -60
- django_bulk_hooks/operations/mti_plans.py +23 -14
- django_bulk_hooks/operations/record_classifier.py +184 -0
- django_bulk_hooks/queryset.py +5 -3
- django_bulk_hooks/registry.py +53 -43
- {django_bulk_hooks-0.2.15.dist-info → django_bulk_hooks-0.2.17.dist-info}/METADATA +1 -1
- django_bulk_hooks-0.2.17.dist-info/RECORD +26 -0
- django_bulk_hooks-0.2.15.dist-info/RECORD +0 -25
- {django_bulk_hooks-0.2.15.dist-info → django_bulk_hooks-0.2.17.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.2.15.dist-info → django_bulk_hooks-0.2.17.dist-info}/WHEEL +0 -0
django_bulk_hooks/registry.py
CHANGED
|
@@ -8,14 +8,13 @@ deterministic priority ordering.
|
|
|
8
8
|
import logging
|
|
9
9
|
import threading
|
|
10
10
|
from collections.abc import Callable
|
|
11
|
-
from typing import Dict, List, Optional, Tuple, Type, Union
|
|
12
11
|
|
|
13
12
|
from django_bulk_hooks.enums import Priority
|
|
14
13
|
|
|
15
14
|
logger = logging.getLogger(__name__)
|
|
16
15
|
|
|
17
16
|
# Type alias for hook info tuple
|
|
18
|
-
HookInfo =
|
|
17
|
+
HookInfo = tuple[type, str, Callable | None, int]
|
|
19
18
|
|
|
20
19
|
|
|
21
20
|
class HookRegistry:
|
|
@@ -30,17 +29,17 @@ class HookRegistry:
|
|
|
30
29
|
|
|
31
30
|
def __init__(self):
|
|
32
31
|
"""Initialize an empty registry with thread-safe storage."""
|
|
33
|
-
self._hooks:
|
|
32
|
+
self._hooks: dict[tuple[type, str], list[HookInfo]] = {}
|
|
34
33
|
self._lock = threading.RLock()
|
|
35
34
|
|
|
36
35
|
def register(
|
|
37
36
|
self,
|
|
38
|
-
model:
|
|
37
|
+
model: type,
|
|
39
38
|
event: str,
|
|
40
|
-
handler_cls:
|
|
39
|
+
handler_cls: type,
|
|
41
40
|
method_name: str,
|
|
42
|
-
condition:
|
|
43
|
-
priority:
|
|
41
|
+
condition: Callable | None,
|
|
42
|
+
priority: int | Priority,
|
|
44
43
|
) -> None:
|
|
45
44
|
"""
|
|
46
45
|
Register a hook handler for a model and event.
|
|
@@ -64,16 +63,14 @@ class HookRegistry:
|
|
|
64
63
|
# Sort by priority (lower values first)
|
|
65
64
|
hooks.sort(key=lambda x: x[3])
|
|
66
65
|
logger.debug(
|
|
67
|
-
f"Registered {handler_cls.__name__}.{method_name} "
|
|
68
|
-
f"for {model.__name__}.{event} (priority={priority})"
|
|
66
|
+
f"Registered {handler_cls.__name__}.{method_name} for {model.__name__}.{event} (priority={priority})",
|
|
69
67
|
)
|
|
70
68
|
else:
|
|
71
69
|
logger.debug(
|
|
72
|
-
f"Hook {handler_cls.__name__}.{method_name} "
|
|
73
|
-
f"already registered for {model.__name__}.{event}"
|
|
70
|
+
f"Hook {handler_cls.__name__}.{method_name} already registered for {model.__name__}.{event}",
|
|
74
71
|
)
|
|
75
72
|
|
|
76
|
-
def get_hooks(self, model:
|
|
73
|
+
def get_hooks(self, model: type, event: str) -> list[HookInfo]:
|
|
77
74
|
"""
|
|
78
75
|
Get all hooks for a model and event.
|
|
79
76
|
|
|
@@ -97,13 +94,17 @@ class HookRegistry:
|
|
|
97
94
|
"before_create",
|
|
98
95
|
]:
|
|
99
96
|
logger.debug(
|
|
100
|
-
f"get_hooks {model.__name__}.{event} found {len(hooks)} hooks"
|
|
97
|
+
f"get_hooks {model.__name__}.{event} found {len(hooks)} hooks",
|
|
101
98
|
)
|
|
102
99
|
|
|
103
100
|
return hooks
|
|
104
101
|
|
|
105
102
|
def unregister(
|
|
106
|
-
self,
|
|
103
|
+
self,
|
|
104
|
+
model: type,
|
|
105
|
+
event: str,
|
|
106
|
+
handler_cls: type,
|
|
107
|
+
method_name: str,
|
|
107
108
|
) -> None:
|
|
108
109
|
"""
|
|
109
110
|
Unregister a specific hook handler.
|
|
@@ -124,9 +125,7 @@ class HookRegistry:
|
|
|
124
125
|
hooks = self._hooks[key]
|
|
125
126
|
# Filter out the specific hook
|
|
126
127
|
self._hooks[key] = [
|
|
127
|
-
(h_cls, m_name, cond, pri)
|
|
128
|
-
for h_cls, m_name, cond, pri in hooks
|
|
129
|
-
if not (h_cls == handler_cls and m_name == method_name)
|
|
128
|
+
(h_cls, m_name, cond, pri) for h_cls, m_name, cond, pri in hooks if not (h_cls == handler_cls and m_name == method_name)
|
|
130
129
|
]
|
|
131
130
|
|
|
132
131
|
# Clean up empty hook lists
|
|
@@ -134,8 +133,7 @@ class HookRegistry:
|
|
|
134
133
|
del self._hooks[key]
|
|
135
134
|
|
|
136
135
|
logger.debug(
|
|
137
|
-
f"Unregistered {handler_cls.__name__}.{method_name} "
|
|
138
|
-
f"for {model.__name__}.{event}"
|
|
136
|
+
f"Unregistered {handler_cls.__name__}.{method_name} for {model.__name__}.{event}",
|
|
139
137
|
)
|
|
140
138
|
|
|
141
139
|
def clear(self) -> None:
|
|
@@ -155,7 +153,7 @@ class HookRegistry:
|
|
|
155
153
|
|
|
156
154
|
logger.debug("Cleared all registered hooks")
|
|
157
155
|
|
|
158
|
-
def list_all(self) ->
|
|
156
|
+
def list_all(self) -> dict[tuple[type, str], list[HookInfo]]:
|
|
159
157
|
"""
|
|
160
158
|
Get all registered hooks for debugging.
|
|
161
159
|
|
|
@@ -165,8 +163,20 @@ class HookRegistry:
|
|
|
165
163
|
with self._lock:
|
|
166
164
|
return dict(self._hooks)
|
|
167
165
|
|
|
166
|
+
@property
|
|
167
|
+
def hooks(self) -> dict[tuple[type, str], list[HookInfo]]:
|
|
168
|
+
"""
|
|
169
|
+
Expose internal hooks dictionary for testing purposes.
|
|
170
|
+
|
|
171
|
+
This property provides direct access to the internal hooks storage
|
|
172
|
+
to allow tests to clear the registry state between test runs.
|
|
173
|
+
"""
|
|
174
|
+
return self._hooks
|
|
175
|
+
|
|
168
176
|
def count_hooks(
|
|
169
|
-
self,
|
|
177
|
+
self,
|
|
178
|
+
model: type | None = None,
|
|
179
|
+
event: str | None = None,
|
|
170
180
|
) -> int:
|
|
171
181
|
"""
|
|
172
182
|
Count registered hooks, optionally filtered by model and/or event.
|
|
@@ -182,27 +192,19 @@ class HookRegistry:
|
|
|
182
192
|
if model is None and event is None:
|
|
183
193
|
# Count all hooks
|
|
184
194
|
return sum(len(hooks) for hooks in self._hooks.values())
|
|
185
|
-
|
|
195
|
+
if model is not None and event is not None:
|
|
186
196
|
# Count hooks for specific model and event
|
|
187
197
|
return len(self._hooks.get((model, event), []))
|
|
188
|
-
|
|
198
|
+
if model is not None:
|
|
189
199
|
# Count all hooks for a model
|
|
190
|
-
return sum(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
)
|
|
195
|
-
else: # event is not None
|
|
196
|
-
# Count all hooks for an event
|
|
197
|
-
return sum(
|
|
198
|
-
len(hooks)
|
|
199
|
-
for (_, e), hooks in self._hooks.items()
|
|
200
|
-
if e == event
|
|
201
|
-
)
|
|
200
|
+
return sum(len(hooks) for (m, _), hooks in self._hooks.items() if m == model)
|
|
201
|
+
# event is not None
|
|
202
|
+
# Count all hooks for an event
|
|
203
|
+
return sum(len(hooks) for (_, e), hooks in self._hooks.items() if e == event)
|
|
202
204
|
|
|
203
205
|
|
|
204
206
|
# Global singleton registry
|
|
205
|
-
_registry:
|
|
207
|
+
_registry: HookRegistry | None = None
|
|
206
208
|
_registry_lock = threading.Lock()
|
|
207
209
|
|
|
208
210
|
|
|
@@ -229,12 +231,12 @@ def get_registry() -> HookRegistry:
|
|
|
229
231
|
|
|
230
232
|
# Backward-compatible module-level functions
|
|
231
233
|
def register_hook(
|
|
232
|
-
model:
|
|
234
|
+
model: type,
|
|
233
235
|
event: str,
|
|
234
|
-
handler_cls:
|
|
236
|
+
handler_cls: type,
|
|
235
237
|
method_name: str,
|
|
236
|
-
condition:
|
|
237
|
-
priority:
|
|
238
|
+
condition: Callable | None,
|
|
239
|
+
priority: int | Priority,
|
|
238
240
|
) -> None:
|
|
239
241
|
"""
|
|
240
242
|
Register a hook handler (backward-compatible function).
|
|
@@ -245,7 +247,7 @@ def register_hook(
|
|
|
245
247
|
registry.register(model, event, handler_cls, method_name, condition, priority)
|
|
246
248
|
|
|
247
249
|
|
|
248
|
-
def get_hooks(model:
|
|
250
|
+
def get_hooks(model: type, event: str) -> list[HookInfo]:
|
|
249
251
|
"""
|
|
250
252
|
Get hooks for a model and event (backward-compatible function).
|
|
251
253
|
|
|
@@ -256,7 +258,10 @@ def get_hooks(model: Type, event: str) -> List[HookInfo]:
|
|
|
256
258
|
|
|
257
259
|
|
|
258
260
|
def unregister_hook(
|
|
259
|
-
model:
|
|
261
|
+
model: type,
|
|
262
|
+
event: str,
|
|
263
|
+
handler_cls: type,
|
|
264
|
+
method_name: str,
|
|
260
265
|
) -> None:
|
|
261
266
|
"""
|
|
262
267
|
Unregister a hook handler (backward-compatible function).
|
|
@@ -278,7 +283,7 @@ def clear_hooks() -> None:
|
|
|
278
283
|
registry.clear()
|
|
279
284
|
|
|
280
285
|
|
|
281
|
-
def list_all_hooks() ->
|
|
286
|
+
def list_all_hooks() -> dict[tuple[type, str], list[HookInfo]]:
|
|
282
287
|
"""
|
|
283
288
|
List all registered hooks (backward-compatible function).
|
|
284
289
|
|
|
@@ -286,3 +291,8 @@ def list_all_hooks() -> Dict[Tuple[Type, str], List[HookInfo]]:
|
|
|
286
291
|
"""
|
|
287
292
|
registry = get_registry()
|
|
288
293
|
return registry.list_all()
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
# Expose hooks dictionary for testing purposes
|
|
297
|
+
# This provides backward compatibility with tests that expect to access _hooks directly
|
|
298
|
+
_hooks = get_registry().hooks
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
django_bulk_hooks/__init__.py,sha256=ujBvX-GY4Pg4ACBh7RXm_MOmi2eAowf5s7pG2SXWdpo,2276
|
|
2
|
+
django_bulk_hooks/changeset.py,sha256=wU6wckJEQ_hG5UZq_9g_kWODoKwEj99JrtacVUQ9BxA,7445
|
|
3
|
+
django_bulk_hooks/conditions.py,sha256=v2DMFmWI7bppBQw5qdbO5CmQRN_QtUwnBjcyKBJLLbw,8030
|
|
4
|
+
django_bulk_hooks/constants.py,sha256=PxpEETaO6gdENcTPoXS586lerGKVP3nmjpDvOkmhYxI,509
|
|
5
|
+
django_bulk_hooks/context.py,sha256=mqaC5-yESDTA5ruI7fuXlt8qSgKuOFp0mjq7h1-4HdQ,1926
|
|
6
|
+
django_bulk_hooks/decorators.py,sha256=P7cvzFgORJRW-YQHNAxNXqQOP9OywBmA7Rz9kiJoxUk,12237
|
|
7
|
+
django_bulk_hooks/dispatcher.py,sha256=fOWh1gtukdegMgku7qdc-Hix6G6ZCKMetONFiNKIBWI,8842
|
|
8
|
+
django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
|
|
9
|
+
django_bulk_hooks/factory.py,sha256=OgrOmLbIzhrSKTDx06oGMAVsEb0NoVOmW5IdLsMz_Qs,19938
|
|
10
|
+
django_bulk_hooks/handler.py,sha256=i0M4mdx3vgXIb8mA1S5ZBW_8ezECd8yTZaj9QNUm8P8,4738
|
|
11
|
+
django_bulk_hooks/helpers.py,sha256=Nw8eXryLUUquW7AgiuKp0PQT3Pq6HAHsdP-xAtqhmjA,3216
|
|
12
|
+
django_bulk_hooks/manager.py,sha256=3mFzB0ZzHHeXWdKGObZD_H0NlskHJc8uYBF69KKdAXU,4068
|
|
13
|
+
django_bulk_hooks/models.py,sha256=4Vvi2LiGP0g4j08a5liqBROfsO8Wd_ermBoyjKwfrPU,2512
|
|
14
|
+
django_bulk_hooks/operations/__init__.py,sha256=BtJYjmRhe_sScivLsniDaZmBkm0ZLvcmzXFKL7QY2Xg,550
|
|
15
|
+
django_bulk_hooks/operations/analyzer.py,sha256=8xNdsoeOCHsIIP7ztIuFHDewBR2dDSS6auOrSU8zmqE,9007
|
|
16
|
+
django_bulk_hooks/operations/bulk_executor.py,sha256=QmfcEUJAVHaMPzYe4yDwQ0i-9FvxDaTWy17mcmn2-Yw,22371
|
|
17
|
+
django_bulk_hooks/operations/coordinator.py,sha256=iSi-x3fq6_3UygWGeQY8Fq_gZWIqq_8N7WddPhVeygk,23526
|
|
18
|
+
django_bulk_hooks/operations/mti_handler.py,sha256=G-pxkzIqHqXGshRGksqmsN1J3rlzePUZrSv4wm7D3cQ,19162
|
|
19
|
+
django_bulk_hooks/operations/mti_plans.py,sha256=YP7LcV9Z8UqNS_x74OswF9_5swqruRTdAu6z-J_R6C0,3377
|
|
20
|
+
django_bulk_hooks/operations/record_classifier.py,sha256=KzUoAhfoqzFVrOabNZAby9Akb54h-fAQZmb8O-fIx_0,6221
|
|
21
|
+
django_bulk_hooks/queryset.py,sha256=rvJgQLwtSJztwc68nkJ6xwCsnbXQvkvS6_dbGGj8TFo,5886
|
|
22
|
+
django_bulk_hooks/registry.py,sha256=QyeA2OqNdMAMaLjFU9UF0YGhKiPKbZkmFQpLgof7uNs,9038
|
|
23
|
+
django_bulk_hooks-0.2.17.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
24
|
+
django_bulk_hooks-0.2.17.dist-info/METADATA,sha256=ue7kA8E8s2Tha6v_EXhQ41oORwxZ1rTWMem14TNqLGg,9265
|
|
25
|
+
django_bulk_hooks-0.2.17.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
26
|
+
django_bulk_hooks-0.2.17.dist-info/RECORD,,
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
django_bulk_hooks/__init__.py,sha256=4QlWY5rqR9o2ddrNB-ypM4s1GtNJ1ZvL2ABhybaPpio,1823
|
|
2
|
-
django_bulk_hooks/changeset.py,sha256=WALeiWDcjOBNdCKeidVKOPKAySKj9ZOvUJ-kWaVZYhM,7444
|
|
3
|
-
django_bulk_hooks/conditions.py,sha256=qtGjToKXC8FPUPK31Mib-GMzc9GSdrH90M2pT3CIsh8,8111
|
|
4
|
-
django_bulk_hooks/constants.py,sha256=PxpEETaO6gdENcTPoXS586lerGKVP3nmjpDvOkmhYxI,509
|
|
5
|
-
django_bulk_hooks/context.py,sha256=mqaC5-yESDTA5ruI7fuXlt8qSgKuOFp0mjq7h1-4HdQ,1926
|
|
6
|
-
django_bulk_hooks/decorators.py,sha256=hc8MSG5XXEiT5kgsf4Opzpj8jAb-OYqcvssuZxCIncQ,11894
|
|
7
|
-
django_bulk_hooks/dispatcher.py,sha256=-aXu3hiVHvEqLU2jFHLXy8idMtVWRtkgMCRKtsw48sI,8677
|
|
8
|
-
django_bulk_hooks/enums.py,sha256=Zo8_tJzuzZ2IKfVc7gZ-0tWPT8q1QhqZbAyoh9ZVJbs,381
|
|
9
|
-
django_bulk_hooks/factory.py,sha256=JmjQiJPfAnytXrO6r6qOadX5yX0-sfpbZ9V8nwX3MAg,20013
|
|
10
|
-
django_bulk_hooks/handler.py,sha256=2-k0GPWGSQ6acfvV0qJgDH8aa0z51DqdpX5vSJ6Uawk,4759
|
|
11
|
-
django_bulk_hooks/helpers.py,sha256=Yopvl588VbKOi2kHEsQcEcI5jw5jiNA2MuF6Ce1VP0c,3174
|
|
12
|
-
django_bulk_hooks/manager.py,sha256=3mFzB0ZzHHeXWdKGObZD_H0NlskHJc8uYBF69KKdAXU,4068
|
|
13
|
-
django_bulk_hooks/models.py,sha256=62tn5wL55EjJVOsZofMluhEJB8bH7CzBvH0vd214_RY,2570
|
|
14
|
-
django_bulk_hooks/operations/__init__.py,sha256=5L5NnwiFw8Yn5WO6-38eGdCYBkA0URpwyDcAdeYfc5w,550
|
|
15
|
-
django_bulk_hooks/operations/analyzer.py,sha256=s6FM53ho1raPdKU-VjjW0SWymXyrJe0I_Wu8XsXFdSY,9065
|
|
16
|
-
django_bulk_hooks/operations/bulk_executor.py,sha256=7VJgeTFcMQ9ZELvCV6WR6udUPJNL6Kf-w9iEva6pIPA,18271
|
|
17
|
-
django_bulk_hooks/operations/coordinator.py,sha256=eCEbD2AhL4-dMFqybLFdyg7z6m4P122iXNaiJ21hD7A,22675
|
|
18
|
-
django_bulk_hooks/operations/mti_handler.py,sha256=eIH-tImMqcWR5lLQr6Ca-HeVYta-UkXk5X5fcpS885Y,18245
|
|
19
|
-
django_bulk_hooks/operations/mti_plans.py,sha256=fHUYbrUAHq8UXqxgAD43oHdTxOnEkmpxoOD4Qrzfqk8,2878
|
|
20
|
-
django_bulk_hooks/queryset.py,sha256=ody4MXrRREL27Ts2ey1UpS0tb5Dxnw-6kN3unxPQ3zY,5860
|
|
21
|
-
django_bulk_hooks/registry.py,sha256=UPerNhtVz_9tKZqrYSZD2LhjAcs4F6hVUuk8L5oOeHc,8821
|
|
22
|
-
django_bulk_hooks-0.2.15.dist-info/LICENSE,sha256=dguKIcbDGeZD-vXWdLyErPUALYOvtX_fO4Zjhq481uk,1088
|
|
23
|
-
django_bulk_hooks-0.2.15.dist-info/METADATA,sha256=0yb6odX8h5GwO7rCLC6OgnCpasxUfnI0ZTGS2sKN_wo,9265
|
|
24
|
-
django_bulk_hooks-0.2.15.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
25
|
-
django_bulk_hooks-0.2.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|