异步感知的事件框架
metapensiero.signal的Python项目详细描述
异步感知的事件框架
内容
- 异步感知的事件框架
- 测试
- 构建状态
-
更改
- 0.1(2015-12-27)
- 0.2(2015-12-27)
- 0.3(2016-01-22)
- 0.4(2016-01-22)
- 0.5(2016-01-22)
- 0.6(2016-01-28)
- 0.7(2016-02-03)
- 0.8(2016-03-24)
- 0.9(2016-03-26)
目标
这个包实现了一个能够处理 同步和异步事件处理程序。它可以按原样使用 或作为班级成员。
如果您在Python2.7上使用它,您将只得到同步处理程序 管理,但有一种方法可以将其绑定到 一种普通的方式。检查 external.py 子模块和测试 了解更多信息。
安装
要安装软件包,请执行以下命令:
$ pip install metapensiero.signal
用法
基本功能
这个包提供的最重要的组件是类 信号 :
frommetapensiero.signalimportSignalsignal=Signal()called1=Falsecalled2=Falsedefhandler1(arg,kw):nonlocalcalled1called1=(arg,kw)defhandler2(arg,kw):nonlocalcalled2called2=(arg,kw)signal.connect(handler1)signal.connect(handler2)signal.notify(1,kw='a')assertcalled1==(1,'a')assertcalled2==(1,'a')
如您所见,在信号为 启动 只需调用信号的 connect() 方法 实例。要删除相同的方法,可以使用 disconnect() 方法:
如上所示,触发事件的方法是调用 notify() 方法和传递给的任何参数或关键字参数 该函数将添加到处理程序调用中。
通过调用 clear() 方法。
不仅可以有同步处理程序,还可以有 异步处理程序:
importasynciofrommetapensiero.signalimportSignal@asyncio.coroutinedeftest_with_mixed_handlers():signal=Signal()called1=Falsecalled2=Falseh1=asyncio.Event()@asyncio.coroutinedefhandler1(arg,kw):nonlocalcalled1called1=(arg,kw)h1.set()defhandler2(arg,kw):nonlocalcalled2called2=(arg,kw)signal.connect(handler1)signal.connect(handler2)signal.notify(1,kw='a')assertcalled2==(1,'a')assertcalled1==Falseyield fromh1.wait()assertcalled1==(1,'a')loop=asyncio.get_event_loop()loop.run_until_complete(test_with_mixed_handlers())
正如您在本例中所看到的,var 在紧接着 通知具有预期值,但var cal的值LeD1 没有。要得到它,代码必须挂起自身并等待 要设置的标志事件。这是因为 handler1() 被安排为 以异步方式执行。确保_future()但它不会等待 通过 notify() 方法得到结果。
使用标志来同步有点傻,如果我们有 多个异步处理程序?我们是否必须创建一个异步事件 为所有人举个例子然后等所有人?如果 异步处理程序的实际数量事先不知道,什么 我们应该这样做吗?
事务支持
妹妹的包裹就在这里 metapensero.asyncio.transaction 很方便。 信号类 与它一起工作以确保两个协程(一个调用 notify() 和 handler1() )可以同步。
要做到这一点,外部代码必须启动一个事务 如果它已就位,则 信号 类代码将自动添加 任何异步事件处理程序。
为了总结这个特性,前面的例子也可以编写 AS:
importasynciofrommetapensiero.signalimportSignalfrommetapensiero.asyncioimporttransaction@asyncio.coroutinedeftest_with_mixed_handlers():signal=Signal()called1=Falsecalled2=False@asyncio.coroutinedefhandler1(arg,kw):nonlocalcalled1called1=(arg,kw)h1.set()defhandler2(arg,kw):nonlocalcalled2called2=(arg,kw)signal.connect(handler1)signal.connect(handler2)trans=transaction.begin()signal.notify(1,kw='a')assertcalled2==(1,'a')assertcalled1==Falseyield fromtrans.end()assertcalled1==(1,'a')loop=asyncio.get_event_loop()loop.run_until_complete(test_with_mixed_handlers())
或者,对于Python3.5,我们可以使用异步上下文管理器,因此它变成:
importasynciofrommetapensiero.signalimportSignalfrommetapensiero.asyncioimporttransactionasyncdeftest_with_mixed_handlers():signal=Signal()called1=Falsecalled2=Falseasyncdefhandler1(arg,kw):nonlocalcalled1called1=(arg,kw)h1.set()defhandler2(arg,kw):nonlocalcalled2called2=(arg,kw)signal.connect(handler1)signal.connect(handler2)asyncwithtransaction.begin():signal.notify(1,kw='a')assertcalled2==(1,'a')assertcalled1==Falseassertcalled1==(1,'a')loop=asyncio.get_event_loop()loop.run_until_complete(test_with_mixed_handlers())
这样,调用上下文有一个通用的和可伸缩的方法 使运行 notify() 的代码块与副作用同步, 即使它们是异步的,而且它们的编号未知。
将信号与类结合使用
实例类也可以用作 班级。在这种情况下,提供一个decorator来声明 类级处理程序。为了让这个特性工作,用户类必须 有一个特定的元类:
frommetapensiero.signalimportSignal,SignalAndHandlerInitMeta,handlerclassA(metaclass=SignalAndHandlerInitMeta):click=Signal()def__init__(self):self.called=False@handler('click')defonclick(self,arg,kw):self.called=(arg,kw)a1=A()asserta1.called==Falsea1.click.notify(1,kw='a')asserta1.called==(1,'a')
当然,类级处理程序可以是异步的:
importasynciofrommetapensiero.asyncioimporttransactionfrommetapensiero.signalimportSignal,SignalAndHandlerInitMeta,handlerclassA(metaclass=SignalAndHandlerInitMeta):click=Signal()def__init__(self):self.called=Falseself.called2=False@handler('click')defonclick(self,arg,kw):self.called=(arg,kw)@handler('click')@asyncio.coroutinedefclick2(self,arg,kw):self.called2=(arg,kw)a1=A()@asyncio.coroutinedefrunner():asserta1.called==Falseasserta1.called2==Falsetrans=transaction.begin()a1.click.notify(1,kw='a')asserta1.called==(1,'a')asserta1.called2==Falseyield fromtrans.end()asserta1.called2==(1,'a')loop=asyncio.get_event_loop()loop.run_until_complete(runner())
当然,您可以使用不带用户类的 信号 类 检测,但您必须通过 您自己:
classB:# the name here is needed for classes that don't explicitly support# signalsclick=Signal('click')def__init__(self):self.called=Falseself.click.connect(self.onclick)defonclick(self,arg,kw):self.called=(arg,kw)b=B()assertb.called==Falseb.onclick.notify(1,kw='b')assertb.called==(1,'b')
信号支持两种方式来扩展其功能。第一个是 全局的,用于将信号插入到其他事件中 系统。请查看 external.py 中的代码和 为了学习如何使用这些抽象类,它们 给你一个进入信号机的方法做你的事情。
第二种方法是根据每个信号定义三个函数 环绕 notify() , connect() , disconnect() 和 通过decorators将它们附加到每个信号实例,就像 内置属性。
每个函数都将接收 自定义内部信号方法的行为,并将接收 每个函数都必须调用以触发的另一个参数 默认行为。包装函数的返回值将 返回到调用上下文而不是默认返回值。
下面是一个例子,注意每个包装的签名 因为你必须尊重这一点:
frommetapensiero.signalimportSignal,SignalAndHandlerInitMeta,handlerc=dict(called=0,connnect_handler=None,handler_called=0,handler_args=None,disconnnect_handler=None,handler2_called=0,handler2_args=None)classA(metaclass=SignalAndHandlerInitMeta):@Signaldefclick(self,subscribers,notify,*args,**kwargs):c['called']+=1c['wrap_args']=(args,kwargs)assertlen(subscribers)==2assertisinstance(self,A)notify('foo',k=2)return'mynotify'@click.on_connectdefclick(self,handler,subscribers,connect):c['called']+=1c['connect_handler']=handlerassertlen(subscribers)==0connect(handler)return'myconnect'@click.on_disconnectdefclick(self,handler,subscribers,disconnect):c['called']+=1c['disconnect_handler']=handlerassertlen(subscribers)==1disconnect(handler)return'mydisconnect'@handler('click')defhandler(self,*args,**kwargs):c['handler_called']+=1c['handler_args']=(args,kwargs)a=A()defhandler2(*args,**kwargs):c['handler2_called']+=1c['handler2_args']=(args,kwargs)res=a.click.connect(handler2)assertres=='myconnect'res=a.click.notify('bar',k=1)assertres=='mynotify'res=a.click.disconnect(handler2)assertres=='mydisconnect'assertc['called']==3assertc['wrap_args']==(('bar',),{'k':1})assertc['handler_called']==1assertc['handler_args']==(('foo',),{'k':2})assertc['handler2_called']==1assertc['handler2_args']==(('foo',),{'k':2})assertc['disconnect_handler']==handler2assertc['connect_handler']==handler2
如您所见,通过这种方式将包装器管理为默认值 行为,您可以修改参数,返回自定义值,甚至 避免触发默认行为。
在某些情况下,您希望在 on_connect 或 on_disconnect 包装器,例如 处理程序有可能连接到信号太晚, 已经送达通知的。在这种情况下,你可以 想在包装器中检查这种情况并立即通知 如果是这样的话,我们会延迟回拨。
该系统提供了一种使用 信号的设备 内部机械,所以处理可能的corou通过附加尖刺 进行中的交易。连接和断开 前面示例的wrappers参数有另一个成员 函数 notify() 将回调作为第一个参数, 然后传递给处理程序的任何其他参数。所以, 让我们看看示例:
$ pip install metapensiero.signal0
测试
要运行测试,应在包根目录下运行以下命令:
$ pip install metapensiero.signal1
更改
0.1(2015-12-27)
- 从rocky移到这里代码
- 添加文档
- 移到此处也测试
0.2(2015-12-27)
- 文档修复
0.3(2016-01-22)
- 添加通用插件基础结构
- python 2.7同步处理的兼容性
0.4(2016-01-22)
- 允许 notify() , connect() 和 disconnect() 计算 通过包装函数和装饰器
0.5(2016-01-22)
- 让皮高兴
0.6(2016-01-28)
- 添加 clear() 方法
0.7(2016-02-03)
- 在通知期间强制重新引发捕获的异常
0.8(2016-03-24)
- 外部插件的改进,现在用于浣熊。
0.9(2016-03-26)
- 更好的参数命名
- 将其他 notify() 函数传递给包装器
- 更新的文档