python-library-callback 0.1.1__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.
callback/__init__.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import ClassVar, Callable, TypeVar
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import inspect
|
|
6
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
T = TypeVar("T", bound="Callback")
|
|
10
|
+
|
|
11
|
+
class Callback():
|
|
12
|
+
"""
|
|
13
|
+
回调
|
|
14
|
+
|
|
15
|
+
定义回调结构时:
|
|
16
|
+
class A(Callback):
|
|
17
|
+
attr: type
|
|
18
|
+
|
|
19
|
+
触发回调时:
|
|
20
|
+
cb = A.trigger(attr=value) # 同步
|
|
21
|
+
cb = await A.atrigger(attr=value) # 异步
|
|
22
|
+
|
|
23
|
+
为函数注册回调时:
|
|
24
|
+
@A
|
|
25
|
+
def func(cb:A):
|
|
26
|
+
pass
|
|
27
|
+
"""
|
|
28
|
+
function_registry: ClassVar[dict[str, list[Callable]]] = {}
|
|
29
|
+
"""注册的函数列表"""
|
|
30
|
+
_async: ClassVar[bool] = False
|
|
31
|
+
"""是否异步"""
|
|
32
|
+
|
|
33
|
+
def __new__(cls, *args, **kwargs):
|
|
34
|
+
"""支持把 Callback 子类直接当装饰器使用"""
|
|
35
|
+
if len(args) == 1 and not kwargs and callable(args[0]):
|
|
36
|
+
func = args[0]
|
|
37
|
+
if cls._async != asyncio.iscoroutinefunction(func):
|
|
38
|
+
raise ValueError(
|
|
39
|
+
f"函数{func}是{'异步' if asyncio.iscoroutinefunction(func) else '同步'},"
|
|
40
|
+
f"但回调{cls.__name__}是{'异步' if cls._async else '同步'}"
|
|
41
|
+
)
|
|
42
|
+
cls.register(func)
|
|
43
|
+
return func
|
|
44
|
+
return super().__new__(cls)
|
|
45
|
+
|
|
46
|
+
def __init__(self, *args, **kwargs):
|
|
47
|
+
"""初始化事件实例"""
|
|
48
|
+
field_names = list(self.__class__.__annotations__.keys())
|
|
49
|
+
for i, arg in enumerate(args):
|
|
50
|
+
if i < len(field_names):
|
|
51
|
+
setattr(self, field_names[i], arg)
|
|
52
|
+
else:
|
|
53
|
+
raise ValueError(f"参数过多[{i}]: {arg}")
|
|
54
|
+
for key, value in kwargs.items():
|
|
55
|
+
if key in field_names:
|
|
56
|
+
setattr(self, key, value)
|
|
57
|
+
else:
|
|
58
|
+
raise ValueError(f"未知属性[{key}]: {value}")
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def register(cls, func: Callable):
|
|
62
|
+
"""注册函数"""
|
|
63
|
+
try:
|
|
64
|
+
if cls.__name__ not in cls.function_registry:
|
|
65
|
+
cls.function_registry[cls.__name__] = []
|
|
66
|
+
if func not in cls.function_registry[cls.__name__]:
|
|
67
|
+
cls.function_registry[cls.__name__].append(func)
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.exception(f"注册函数{func}失败: {e}")
|
|
70
|
+
raise e
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def trigger(cls:type[T],*args,**kwargs) -> T:
|
|
74
|
+
"""同步触发回调"""
|
|
75
|
+
try:
|
|
76
|
+
self = cls(*args, **kwargs)
|
|
77
|
+
|
|
78
|
+
self.before_trigger()
|
|
79
|
+
funcs = cls.function_registry.get(cls.__name__, [])
|
|
80
|
+
with ThreadPoolExecutor() as executor:
|
|
81
|
+
futures = [executor.submit(self._call_registered, func, self) for func in funcs]
|
|
82
|
+
for future in futures:
|
|
83
|
+
future.result()
|
|
84
|
+
self.after_trigger()
|
|
85
|
+
|
|
86
|
+
return self
|
|
87
|
+
except Exception as e:
|
|
88
|
+
logger.exception(f"触发回调{cls}失败: {e}")
|
|
89
|
+
raise e
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
async def atrigger(cls:type[T],*args,**kwargs) -> T:
|
|
93
|
+
"""异步触发回调"""
|
|
94
|
+
try:
|
|
95
|
+
self = cls(*args, **kwargs)
|
|
96
|
+
|
|
97
|
+
await self.before_atrigger()
|
|
98
|
+
funcs = cls.function_registry.get(cls.__name__, [])
|
|
99
|
+
tasks = [self._acall_registered(func, self) for func in funcs]
|
|
100
|
+
if tasks:
|
|
101
|
+
await asyncio.gather(*tasks)
|
|
102
|
+
await self.after_atrigger()
|
|
103
|
+
|
|
104
|
+
return self
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logger.exception(f"异步触发回调{cls}失败: {e}")
|
|
107
|
+
raise e
|
|
108
|
+
|
|
109
|
+
def before_trigger(self) -> None:
|
|
110
|
+
"""同步触发前钩子"""
|
|
111
|
+
pass
|
|
112
|
+
|
|
113
|
+
async def before_atrigger(self) -> None:
|
|
114
|
+
"""异步触发前钩子"""
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
def after_trigger(self) -> None:
|
|
118
|
+
"""同步触发后钩子"""
|
|
119
|
+
pass
|
|
120
|
+
|
|
121
|
+
async def after_atrigger(self) -> None:
|
|
122
|
+
"""异步触发后钩子"""
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
@staticmethod
|
|
126
|
+
def _call_registered(func: Callable, cb: "Callback"):
|
|
127
|
+
"""同步调用注册的函数,支持不传参数"""
|
|
128
|
+
try:
|
|
129
|
+
sig = inspect.signature(func)
|
|
130
|
+
except (TypeError, ValueError):
|
|
131
|
+
return func(cb)
|
|
132
|
+
|
|
133
|
+
params = list(sig.parameters.values())
|
|
134
|
+
positional = [
|
|
135
|
+
p for p in params
|
|
136
|
+
if p.kind in (
|
|
137
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
138
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
139
|
+
)
|
|
140
|
+
]
|
|
141
|
+
has_varargs = any(p.kind == inspect.Parameter.VAR_POSITIONAL for p in params)
|
|
142
|
+
|
|
143
|
+
if has_varargs or len(positional) >= 1:
|
|
144
|
+
return func(cb)
|
|
145
|
+
return func()
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
async def _acall_registered(func: Callable, cb: "Callback"):
|
|
149
|
+
"""异步调用注册的函数,支持不传参数"""
|
|
150
|
+
try:
|
|
151
|
+
sig = inspect.signature(func)
|
|
152
|
+
except (TypeError, ValueError):
|
|
153
|
+
return await func(cb)
|
|
154
|
+
|
|
155
|
+
params = list(sig.parameters.values())
|
|
156
|
+
positional = [
|
|
157
|
+
p for p in params
|
|
158
|
+
if p.kind in (
|
|
159
|
+
inspect.Parameter.POSITIONAL_ONLY,
|
|
160
|
+
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
161
|
+
)
|
|
162
|
+
]
|
|
163
|
+
has_varargs = any(p.kind == inspect.Parameter.VAR_POSITIONAL for p in params)
|
|
164
|
+
|
|
165
|
+
if has_varargs or len(positional) >= 1:
|
|
166
|
+
return await func(cb)
|
|
167
|
+
return await func()
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def get_all(cls) -> list[type[Callback]]:
|
|
171
|
+
"""获取所有回调"""
|
|
172
|
+
return list(cls.__subclasses__())
|
|
173
|
+
|
|
174
|
+
__all__ = [
|
|
175
|
+
"Callback",
|
|
176
|
+
]
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
callback/__init__.py,sha256=qO8BKnAGnLx2NfPrpGsIlbDJPL9F9GF8kX_hvYE3p_I,5725
|
|
2
|
+
python_library_callback-0.1.1.dist-info/METADATA,sha256=2Ut-7Y9aFx7m6QYE1YXB06jtV1IfHpPdYnWcbgUOqZQ,91
|
|
3
|
+
python_library_callback-0.1.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
4
|
+
python_library_callback-0.1.1.dist-info/RECORD,,
|