如今咱们来谈一些(黑)科技,但愿能给你们一些启发和帮助。如今我有一个策略文件addition.policy
:python
load!: addition_delegate.py await: first_string -> s1 apply: concat_with_time(s1) -> s1 await: second_string -> s2 apply: concat_with_time(s2) -> s2 await: return_result apply: join_with_linefeed(s1, s2) -> result yield: result
还有一个委托函数的Python源码addition_delegate.py
:git
# addition_delegate.py def concat_with_time(s): import time return str(s) + time.ctime() def join_with_linefeed(s1, s2): return "%s\n%s\n" % (str(s1), str(s2))
这是什么语法?可是咱们大抵都能明白它想干什么:前后获取两个字符串,分别将它们和时间拼接在一块儿,而后在获取return_result
后回传结果。而后呢?而后咱们有一些Web接口,简单地用web.py编写main.py
:github
#!/usr/bin/env python import web from policy import resume class first_str_view: def GET(self): resume("addition.policy", "first_str", value = web.input()["value"], anew = True) return "" class second_str_view: def GET(self): resume("addition.policy", "second_str", value = web.input()["value"]) return "" class return_result_view: def GET(self): return resume("addition.policy", "return_result") urls = [ '/first_str/?', first_str_view, '/second_str/?', second_str_view, '/return_result/?', return_result_view, ] if __name__ == "__main__": app = web.application(urls, globals()) app.run()
就算没用过web.py的人都大抵能明白这个结构是什么意思了,除了那个resume
有点不知所谓以外,可是结合上面的那个addition.policy
,好像看上去也挺合理,大概就是从acquire
处断开,而后获得输入后继续执行那个policy。如你所料:web
$ ./main.py 9999 & [1] 19121 http://0.0.0.0:9999/ $ curl "http://localhost:9999/first_str?value=First+Record+" $ curl "http://localhost:9999/second_str?value=Second+Record+" $ curl "http://localhost:9999/return_result" First Record Sat Sep 5 15:59:25 2015 Second Record Sat Sep 5 15:59:28 2015
这样能够解决不少问题。好比在用户更变邮箱的时候,用户先提交新邮箱,而后还要等等他何时去邮箱里收验证邮件,这样更变邮箱的操做才完成。还有一些更麻烦的操做,整个流程下来,要收几回输入,而后才能真正地输入成功存进数据库。举个例子,你能够简单地写一个策略文件,让它控制整个流程,接口只须要跟用户打交道就行了:数据库
load!: email_service assert!: is_authenticated await: modify_email -> address apply: send_verf_email(address) await: verf_email_recv apply: save_current_user_info(address)
不得不说这种模型有点像是协程(coroutine),可是不是用它来实现的,毕竟:一次请求完成了整个线程大大小小都结束了哪里还有协程啊对吧。这也不是WebSocket能解决的:好比收验证邮件,都在第二个地方链接了,哪里还有Socket可言。这里针对的状况是,两次链接之间的时间段是断开的状况。(若是非要用设计模式来讲,我以为是一个策略+状态+解释器的组合,然而我并不喜欢被设计模式拘束)设计模式
主要是在模拟恢复执行的时候能较好地恢复原有上下文,在Python有exec的状况下,想办法生成可配合执行Python代码是一个不错的选择。恢复执行有这些步骤:app
解析策略文件curl
从持久存储设备中反序列化上下文ide
找到断点应该在哪里,按照这个位置,执行一些每次都要执行的语句(标 !
号)函数
一直执行到下一个await点,退出执行
先看一下resume()
函数的一个实现是什么样子的:
from policy import policy import pickle import os def resume(pf, await_tag, value = None, anew = False): c = context.start_new() if anew else \ pickle.load(file("context.dump", "rb")) p = policy.load(pf) p.load_context(c) p.provide(await_tag, value) ret = None try: ret = p.resume() finally: os.remove("context.dump") if p.is_end(): os.remove("context.dump") return ret
这里的context
和policy
是我对这个模型的一个实现,能够看出这二者是分开保存的,Policy几乎就是一个常量了,硬编码在一个文件里。而Context在每一次退出执行的时候都要保存一下,除非已经执行结束了,或者执行出现了错误(也至关于执行结束),才把它削除。
Policy-Control
已经推上了Github,代码很短,欢迎各位围观:https://github.com/Shihira/policy-control
附:语法清单
digit := '0' | ... | '9' underscore := '_' symbol ::= letter | underscore { letter | underscore | digit } command ::= symbol variable ::= symbol string ::= '"' { [ 0x00 | ... | 0x21 | 0x23 | ... | 0x7f | '\n' | '\r' | '\"' | '\\' ] } '"' value ::= string | variable parameter ::= value parameter-list ::= '(' [ parameter-list ',' parameter | parameter ] ')' argument ::= symbol [ parameter-list ] argument-list ::= argument-list argument | argument command-line ::= command [ '!' ] ':' argument-list [ '->' variable ] policy ::= policy \n command-line | command-line