django-bulk-hooks 0.2.41__py3-none-any.whl → 0.2.43__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/dispatcher.py +242 -255
- django_bulk_hooks/factory.py +541 -563
- django_bulk_hooks/handler.py +106 -114
- django_bulk_hooks/operations/bulk_executor.py +512 -576
- django_bulk_hooks/operations/mti_handler.py +7 -0
- django_bulk_hooks/queryset.py +188 -191
- django_bulk_hooks/registry.py +277 -298
- {django_bulk_hooks-0.2.41.dist-info → django_bulk_hooks-0.2.43.dist-info}/METADATA +1 -1
- {django_bulk_hooks-0.2.41.dist-info → django_bulk_hooks-0.2.43.dist-info}/RECORD +11 -11
- {django_bulk_hooks-0.2.41.dist-info → django_bulk_hooks-0.2.43.dist-info}/LICENSE +0 -0
- {django_bulk_hooks-0.2.41.dist-info → django_bulk_hooks-0.2.43.dist-info}/WHEEL +0 -0
django_bulk_hooks/handler.py
CHANGED
|
@@ -1,114 +1,106 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
|
-
from django_bulk_hooks.registry import register_hook
|
|
4
|
-
|
|
5
|
-
logger = logging.getLogger(__name__)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class HookMeta(type):
|
|
9
|
-
_registered = set()
|
|
10
|
-
_class_hook_map: dict[
|
|
11
|
-
type, set[tuple],
|
|
12
|
-
] = {} # Track which hooks belong to which class
|
|
13
|
-
|
|
14
|
-
def __new__(mcs, name, bases, namespace):
|
|
15
|
-
cls = super().__new__(mcs, name, bases, namespace)
|
|
16
|
-
mcs._register_hooks_for_class(cls)
|
|
17
|
-
return cls
|
|
18
|
-
|
|
19
|
-
@classmethod
|
|
20
|
-
def _register_hooks_for_class(mcs, cls):
|
|
21
|
-
"""
|
|
22
|
-
Register hooks for a given class following OOP inheritance semantics.
|
|
23
|
-
|
|
24
|
-
- Child classes inherit all parent hook methods
|
|
25
|
-
- Child overrides replace parent implementations (not add to them)
|
|
26
|
-
- Child can add new hook methods
|
|
27
|
-
"""
|
|
28
|
-
from django_bulk_hooks.registry import unregister_hook
|
|
29
|
-
|
|
30
|
-
# Step 1: Unregister ALL hooks from parent classes in the MRO
|
|
31
|
-
# This ensures only the most-derived class owns the active hooks,
|
|
32
|
-
# providing true OOP semantics (overrides replace, others are inherited once).
|
|
33
|
-
for base in cls.__mro__[1:]: # Skip cls itself, start from first parent
|
|
34
|
-
if not isinstance(base, HookMeta):
|
|
35
|
-
continue
|
|
36
|
-
|
|
37
|
-
if base in mcs._class_hook_map:
|
|
38
|
-
for model_cls, event, base_cls, method_name in list(
|
|
39
|
-
mcs._class_hook_map[base],
|
|
40
|
-
):
|
|
41
|
-
key = (model_cls, event, base_cls, method_name)
|
|
42
|
-
if key in HookMeta._registered:
|
|
43
|
-
unregister_hook(model_cls, event, base_cls, method_name)
|
|
44
|
-
HookMeta._registered.discard(key)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Hooks are registered via the @hook decorator and executed by
|
|
108
|
-
the HookDispatcher. This class serves as a base for all hook
|
|
109
|
-
handlers and uses HookMeta for automatic registration.
|
|
110
|
-
|
|
111
|
-
All hook execution logic has been moved to HookDispatcher for
|
|
112
|
-
a single, consistent execution path.
|
|
113
|
-
"""
|
|
114
|
-
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from django_bulk_hooks.registry import register_hook
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class HookMeta(type):
|
|
9
|
+
_registered = set()
|
|
10
|
+
_class_hook_map: dict[
|
|
11
|
+
type, set[tuple],
|
|
12
|
+
] = {} # Track which hooks belong to which class
|
|
13
|
+
|
|
14
|
+
def __new__(mcs, name, bases, namespace):
|
|
15
|
+
cls = super().__new__(mcs, name, bases, namespace)
|
|
16
|
+
mcs._register_hooks_for_class(cls)
|
|
17
|
+
return cls
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def _register_hooks_for_class(mcs, cls):
|
|
21
|
+
"""
|
|
22
|
+
Register hooks for a given class following OOP inheritance semantics.
|
|
23
|
+
|
|
24
|
+
- Child classes inherit all parent hook methods
|
|
25
|
+
- Child overrides replace parent implementations (not add to them)
|
|
26
|
+
- Child can add new hook methods
|
|
27
|
+
"""
|
|
28
|
+
from django_bulk_hooks.registry import unregister_hook
|
|
29
|
+
|
|
30
|
+
# Step 1: Unregister ALL hooks from parent classes in the MRO
|
|
31
|
+
# This ensures only the most-derived class owns the active hooks,
|
|
32
|
+
# providing true OOP semantics (overrides replace, others are inherited once).
|
|
33
|
+
for base in cls.__mro__[1:]: # Skip cls itself, start from first parent
|
|
34
|
+
if not isinstance(base, HookMeta):
|
|
35
|
+
continue
|
|
36
|
+
|
|
37
|
+
if base in mcs._class_hook_map:
|
|
38
|
+
for model_cls, event, base_cls, method_name in list(
|
|
39
|
+
mcs._class_hook_map[base],
|
|
40
|
+
):
|
|
41
|
+
key = (model_cls, event, base_cls, method_name)
|
|
42
|
+
if key in HookMeta._registered:
|
|
43
|
+
unregister_hook(model_cls, event, base_cls, method_name)
|
|
44
|
+
HookMeta._registered.discard(key)
|
|
45
|
+
|
|
46
|
+
# Step 2: Register all hook methods on this class (including inherited ones)
|
|
47
|
+
# Walk the MRO to find ALL methods with hook decorators
|
|
48
|
+
all_hook_methods = {}
|
|
49
|
+
for klass in reversed(cls.__mro__): # Start from most base class
|
|
50
|
+
if not isinstance(klass, HookMeta):
|
|
51
|
+
continue
|
|
52
|
+
for method_name, method in klass.__dict__.items():
|
|
53
|
+
if hasattr(method, "hooks_hooks"):
|
|
54
|
+
# Store with method name as key - child methods will override parent
|
|
55
|
+
all_hook_methods[method_name] = method
|
|
56
|
+
|
|
57
|
+
# Step 3: Register all hook methods with THIS class as the handler
|
|
58
|
+
if cls not in mcs._class_hook_map:
|
|
59
|
+
mcs._class_hook_map[cls] = set()
|
|
60
|
+
|
|
61
|
+
for method_name, method in all_hook_methods.items():
|
|
62
|
+
if hasattr(method, "hooks_hooks"):
|
|
63
|
+
for model_cls, event, condition, priority in method.hooks_hooks:
|
|
64
|
+
key = (model_cls, event, cls, method_name)
|
|
65
|
+
if key not in HookMeta._registered:
|
|
66
|
+
register_hook(
|
|
67
|
+
model=model_cls,
|
|
68
|
+
event=event,
|
|
69
|
+
handler_cls=cls,
|
|
70
|
+
method_name=method_name,
|
|
71
|
+
condition=condition,
|
|
72
|
+
priority=priority,
|
|
73
|
+
)
|
|
74
|
+
HookMeta._registered.add(key)
|
|
75
|
+
mcs._class_hook_map[cls].add(key)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def re_register_all_hooks(mcs):
|
|
79
|
+
"""Re-register all hooks for all existing Hook classes."""
|
|
80
|
+
# Clear the registered set and class hook map so we can re-register
|
|
81
|
+
HookMeta._registered.clear()
|
|
82
|
+
mcs._class_hook_map.clear()
|
|
83
|
+
|
|
84
|
+
# Find all Hook classes and re-register their hooks
|
|
85
|
+
import gc
|
|
86
|
+
|
|
87
|
+
registered_classes = set()
|
|
88
|
+
for obj in gc.get_objects():
|
|
89
|
+
if isinstance(obj, type) and isinstance(obj, HookMeta):
|
|
90
|
+
if obj not in registered_classes:
|
|
91
|
+
registered_classes.add(obj)
|
|
92
|
+
mcs._register_hooks_for_class(obj)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Hook(metaclass=HookMeta):
|
|
96
|
+
"""
|
|
97
|
+
Base class for hook handlers.
|
|
98
|
+
|
|
99
|
+
Hooks are registered via the @hook decorator and executed by
|
|
100
|
+
the HookDispatcher. This class serves as a base for all hook
|
|
101
|
+
handlers and uses HookMeta for automatic registration.
|
|
102
|
+
|
|
103
|
+
All hook execution logic has been moved to HookDispatcher for
|
|
104
|
+
a single, consistent execution path.
|
|
105
|
+
"""
|
|
106
|
+
|