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.

@@ -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
- logger.debug(
46
- f"Unregistered base hook: {base_cls.__name__}.{method_name} "
47
- f"(superseded by {cls.__name__})",
48
- )
49
-
50
- # Step 2: Register all hook methods on this class (including inherited ones)
51
- # Walk the MRO to find ALL methods with hook decorators
52
- all_hook_methods = {}
53
- for klass in reversed(cls.__mro__): # Start from most base class
54
- if not isinstance(klass, HookMeta):
55
- continue
56
- for method_name, method in klass.__dict__.items():
57
- if hasattr(method, "hooks_hooks"):
58
- # Store with method name as key - child methods will override parent
59
- all_hook_methods[method_name] = method
60
-
61
- # Step 3: Register all hook methods with THIS class as the handler
62
- if cls not in mcs._class_hook_map:
63
- mcs._class_hook_map[cls] = set()
64
-
65
- for method_name, method in all_hook_methods.items():
66
- if hasattr(method, "hooks_hooks"):
67
- for model_cls, event, condition, priority in method.hooks_hooks:
68
- key = (model_cls, event, cls, method_name)
69
- if key not in HookMeta._registered:
70
- register_hook(
71
- model=model_cls,
72
- event=event,
73
- handler_cls=cls,
74
- method_name=method_name,
75
- condition=condition,
76
- priority=priority,
77
- )
78
- HookMeta._registered.add(key)
79
- mcs._class_hook_map[cls].add(key)
80
- logger.debug(
81
- f"Registered hook: {cls.__name__}.{method_name} "
82
- f"for {model_cls.__name__}.{event}",
83
- )
84
-
85
- @classmethod
86
- def re_register_all_hooks(mcs):
87
- """Re-register all hooks for all existing Hook classes."""
88
- # Clear the registered set and class hook map so we can re-register
89
- HookMeta._registered.clear()
90
- mcs._class_hook_map.clear()
91
-
92
- # Find all Hook classes and re-register their hooks
93
- import gc
94
-
95
- registered_classes = set()
96
- for obj in gc.get_objects():
97
- if isinstance(obj, type) and isinstance(obj, HookMeta):
98
- if obj not in registered_classes:
99
- registered_classes.add(obj)
100
- mcs._register_hooks_for_class(obj)
101
-
102
-
103
- class Hook(metaclass=HookMeta):
104
- """
105
- Base class for hook handlers.
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
+