异步感知的事件框架

metapensiero.signal的Python项目详细描述


异步感知的事件框架

内容

目标

这个包实现了一个能够处理 同步和异步事件处理程序。它可以按原样使用 或作为班级成员。

如果您在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.signal
0

测试

要运行测试,应在包根目录下运行以下命令:

$ pip install metapensiero.signal
1

构建状态 https://travis-ci.org/azazazel75/metapensero.signal.svg?branch=master

更改

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() 函数传递给包装器
  • 更新的文档

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java如何避免springbootstarterparent和dependency中的flyway包冲突   java文本文件写入不起作用   java获取名为DAO类的Bean创建异常   java类路径和清单文件   如何使用Java中的模式来获取像C中的“sscanf”这样的组?   java Spring自动连接具有构造函数的类   clientSecurity运行的java geode示例失败   java JavaFX:在FXML中从ResourceBundle访问非string对象   java登录到google firebase   java对象在通过构造函数后不会更改。为什么?   java(Android)对特定页面的操作webView   javascript如何在webview的js中从java调用变量   持久化—一种在Java中存储和检索对象而不使用关系数据库的简单方法?   java Ldap获取通讯组列表中的用户   java检查温度条件   java后退按钮警报框(Android Studio)   数组java列表问题:为什么我运行它时,它会在第三次打印时打印出4?   java相对路径与绝对路径