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.

@@ -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 = Tuple[Type, str, Optional[Callable], int]
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: Dict[Tuple[Type, str], List[HookInfo]] = {}
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: Type,
37
+ model: type,
39
38
  event: str,
40
- handler_cls: Type,
39
+ handler_cls: type,
41
40
  method_name: str,
42
- condition: Optional[Callable],
43
- priority: Union[int, 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: Type, event: str) -> List[HookInfo]:
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, model: Type, event: str, handler_cls: Type, method_name: str
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) -> Dict[Tuple[Type, str], List[HookInfo]]:
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, model: Optional[Type] = None, event: Optional[str] = None
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
- elif model is not None and event is not None:
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
- elif model is not None:
198
+ if model is not None:
189
199
  # Count all hooks for a model
190
- return sum(
191
- len(hooks)
192
- for (m, _), hooks in self._hooks.items()
193
- if m == model
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: Optional[HookRegistry] = None
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: Type,
234
+ model: type,
233
235
  event: str,
234
- handler_cls: Type,
236
+ handler_cls: type,
235
237
  method_name: str,
236
- condition: Optional[Callable],
237
- priority: Union[int, 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: Type, event: str) -> List[HookInfo]:
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: Type, event: str, handler_cls: Type, method_name: str
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() -> Dict[Tuple[Type, str], List[HookInfo]]:
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: django-bulk-hooks
3
- Version: 0.2.15
3
+ Version: 0.2.17
4
4
  Summary: Hook-style hooks for Django bulk operations like bulk_create and bulk_update.
5
5
  License: MIT
6
6
  Keywords: django,bulk,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,,