有没有办法获取添加到 SQLAlchemy 模型的所有自定义事件侦听器的列表?
Is there a way to get a list of all custom event listeners added to a SQLAlchemy model?
假设我已经向模型添加了一些事件侦听器,并且我想获取模型的所有这些添加事件的列表,以在使用断言进行测试期间验证它们的存在。有办法吗?
我知道 SQLAlchemy 的 inspect
,我目前使用它来断言列和关系的存在。但是,有没有办法也通过 inspect
获取自定义事件侦听器列表?如果没有,还有其他方法吗?我只想获取已明确添加到模型中的事件,而不是默认存在的事件(如果可能)。
我希望如何检索事件侦听器的示例:
def test_schema(self):
# sanity checks
# this will raise any flags in the event schema is modified, so we know to update the appropriate tests
assert tuple(inspect(MyModel).columns.keys()) == (
"id", "module", "slug", "display_name"
)
assert tuple(inspect(MyModel).relationships.keys()) == ("accounts", "reports", "jobs")
assert tuple(inspect(MyModel).events) == (
"{event_function_name}_{trigger_action}",
"{notify_manager_of_billing_changes}_{after_update}"
)
def notify_manager_of_billing_changes(mapper, connection, model_instance):
print(model_instance.billing_address)
from sqlalchemy import event
event.listen(MyModel, "after_update", notify_manager_of_billing_changes, retval=False)
测试特定事件侦听器
这样的测试public API是:
assert event.contains(MyModel, "after_update", notify_manager_of_billing_changes)
获取所有自定义事件侦听器的列表
SQLAlchemy 不跟踪函数名,只跟踪它的 id
1 和一个 wrap
函数2.
1 如 id(notify_manager_of_billing_changes)
.
2 不使用 functools.wraps
!
在答案How can I get the values of the locals of a function的call_function_get_frame
的帮助下,添加一个except IndexError:
,我们可以从wrap
函数中获取对fn
的引用.
import sys
from sqlalchemy.orm import Mapper
def call_function_get_frame(func, *args, **kwargs):
"""
Calls the function *func* with the specified arguments and keyword
arguments and snatches its local frame before it actually executes.
"""
frame = None
trace = sys.gettrace()
def snatch_locals(_frame, name, arg):
nonlocal frame
if frame is None and name == 'call':
frame = _frame
sys.settrace(trace)
return trace
sys.settrace(snatch_locals)
try:
result = func(*args, **kwargs)
except IndexError: # Added
result = None # Added
finally:
sys.settrace(trace)
return frame, result
def get_events(mapper):
events = []
dispatch = mapper.dispatch
for event_name in dispatch._event_names:
listeners = getattr(dispatch, event_name).listeners
for wrap in listeners:
frame, result = call_function_get_frame(wrap)
events.append(f"{{{frame.f_locals['fn'].__name__}}}_{{{event_name}}}")
return events
Mapper.events = property(get_events)
用法,如题中所愿:
assert tuple(inspect(MyModel).events) == (
# "{event_function_name}_{trigger_action}",
"{notify_manager_of_billing_changes}_{after_update}",
)
假设我已经向模型添加了一些事件侦听器,并且我想获取模型的所有这些添加事件的列表,以在使用断言进行测试期间验证它们的存在。有办法吗?
我知道 SQLAlchemy 的 inspect
,我目前使用它来断言列和关系的存在。但是,有没有办法也通过 inspect
获取自定义事件侦听器列表?如果没有,还有其他方法吗?我只想获取已明确添加到模型中的事件,而不是默认存在的事件(如果可能)。
我希望如何检索事件侦听器的示例:
def test_schema(self):
# sanity checks
# this will raise any flags in the event schema is modified, so we know to update the appropriate tests
assert tuple(inspect(MyModel).columns.keys()) == (
"id", "module", "slug", "display_name"
)
assert tuple(inspect(MyModel).relationships.keys()) == ("accounts", "reports", "jobs")
assert tuple(inspect(MyModel).events) == (
"{event_function_name}_{trigger_action}",
"{notify_manager_of_billing_changes}_{after_update}"
)
def notify_manager_of_billing_changes(mapper, connection, model_instance):
print(model_instance.billing_address)
from sqlalchemy import event
event.listen(MyModel, "after_update", notify_manager_of_billing_changes, retval=False)
测试特定事件侦听器
这样的测试public API是:
assert event.contains(MyModel, "after_update", notify_manager_of_billing_changes)
获取所有自定义事件侦听器的列表
SQLAlchemy 不跟踪函数名,只跟踪它的 id
1 和一个 wrap
函数2.
1 如 id(notify_manager_of_billing_changes)
.
2 不使用 functools.wraps
!
在答案How can I get the values of the locals of a function的call_function_get_frame
的帮助下,添加一个except IndexError:
,我们可以从wrap
函数中获取对fn
的引用.
import sys
from sqlalchemy.orm import Mapper
def call_function_get_frame(func, *args, **kwargs):
"""
Calls the function *func* with the specified arguments and keyword
arguments and snatches its local frame before it actually executes.
"""
frame = None
trace = sys.gettrace()
def snatch_locals(_frame, name, arg):
nonlocal frame
if frame is None and name == 'call':
frame = _frame
sys.settrace(trace)
return trace
sys.settrace(snatch_locals)
try:
result = func(*args, **kwargs)
except IndexError: # Added
result = None # Added
finally:
sys.settrace(trace)
return frame, result
def get_events(mapper):
events = []
dispatch = mapper.dispatch
for event_name in dispatch._event_names:
listeners = getattr(dispatch, event_name).listeners
for wrap in listeners:
frame, result = call_function_get_frame(wrap)
events.append(f"{{{frame.f_locals['fn'].__name__}}}_{{{event_name}}}")
return events
Mapper.events = property(get_events)
用法,如题中所愿:
assert tuple(inspect(MyModel).events) == (
# "{event_function_name}_{trigger_action}",
"{notify_manager_of_billing_changes}_{after_update}",
)