Python 将上下文管理器与协同程序一起使用

Python 将上下文管理器与协同程序一起使用,python,asynchronous,tornado,coroutine,Python,Asynchronous,Tornado,Coroutine,此代码不起作用 from contextlib import contextmanager import tornado.ioloop import tornado.web

此代码不起作用

from contextlib import contextmanager                                                     
import tornado.ioloop                                                                     
import tornado.web                                                                        
from tornado import gen                                                                   
from tornado.httpclient import AsyncHTTPClient                                            


@contextmanager                                                                           
def hello():                                                                              
    print("hello in")                                                                     
    yield                                                                                 
    print("hello out")                                                                    


class MainHandler(tornado.web.RequestHandler):                                            
    @gen.coroutine                                                                        
    def get(self):                                                                        
        client = AsyncHTTPClient()                                                        
        with hello():                                                                     
            result = yield client.fetch("http://localhost")                               
        return "Hello "+str(result)                                                       

app = tornado.web.Application([('/', MainHandler)])                                       
app.listen(12345)                                                                         
tornado.ioloop.IOLoop.current().start()                                                   
它不起作用的原因是上下文管理器和协同程序在行为上是不兼容的

您是否确认实现这一点的唯一方法是使用
try finally
(如果必须在许多地方使用上下文管理器代码,则特别烦人)。也许有个微妙的把戏我不知道?谷歌搜索没有帮助

编辑

这是我得到的输出

(venv) sborini@Mac-34363bd19f52:tornado$ python test.py 
hello in
ERROR:tornado.application:Uncaught exception GET / (::1)
HTTPServerRequest(protocol='http', host='localhost:12345', method='GET', uri='/', version='HTTP/1.1', remote_ip='::1', headers={'Upgrade-Insecure-Requests': '1', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.84 Safari/537.36', 'Accept-Language': 'en-US,en;q=0.8,it;q=0.6', 'Connection': 'keep-alive', 'Host': 'localhost:12345', 'Accept-Encoding': 'gzip, deflate, sdch'})
Traceback (most recent call last):
  File "/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site-packages/tornado/web.py", line 1445, in _execute
    result = yield result
  File "/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "<string>", line 3, in raise_exc_info
  File "/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site-packages/tornado/gen.py", line 1014, in run
    yielded = self.gen.throw(*exc_info)
  File "test.py", line 20, in get
    result = yield client.fetch("http://localhost")
  File "/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "<string>", line 3, in raise_exc_info
ConnectionRefusedError: [Errno 61] Connection refused
ERROR:tornado.access:500 GET / (::1) 5.04ms
(venv)sborini@Mac-34363bd19f52:tornado$python test.py
你好,在
错误:tornado。应用程序:未捕获异常GET/(::1)
HTTPServerRequest(protocol='http',host='localhost:12345',method='GET',uri='/',version='http/1.1',remote_ip='::1',headers={'Upgrade-unsecure-Requests':'1','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','User-Agent':'Mozilla/5.0(Macintosh;Intel Mac OS X 10_5)AppleWebKit/537.36(KHTML,像Gecko)Chrome/51.0.2704.84 Safari/537.36,“接受语言”:en-US,en;q=0.8,it;q=0.6,“连接”:keep-alive;“主机”:localhost:12345;“接受编码”:gzip,deflate,sdch)
回溯(最近一次呼叫最后一次):
文件“/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site packages/tornado/web.py”,第1445行,在
结果=产量结果
文件“/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site packages/tornado/gen.py”,第1008行,运行中
value=future.result()
结果文件为“/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site packages/tornado/concurrent.py”,第232行
提升exc信息(自身exc信息)
文件“”,第3行,在raise_exc_信息中
文件“/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site packages/tornado/gen.py”,第1014行,运行中
屈服=自我生成抛出(*exc_信息)
get中第20行的文件“test.py”
结果=产生客户端。获取(“http://localhost")
文件“/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site packages/tornado/gen.py”,第1008行,运行中
value=future.result()
结果文件为“/Users/sborini/Work/Experiments/tornado/venv/lib/python3.4/site packages/tornado/concurrent.py”,第232行
提升exc信息(自身exc信息)
文件“”,第3行,在raise_exc_信息中
ConnectionRefusedError:[Errno 61]连接被拒绝
错误:tornado.access:500 GET/(::1)5.04ms
关键是我从来没有收到过
hello out
消息。我希望,一旦
fetch
产生了未来和未来的错误,我会返回屈服点,获取异常,并离开上下文,触发
打印('hello out')


请注意,如果我只做一个
尝试:最后:
围绕
收益率
代码的结构是正确的,这样混合上下文管理器和协同程序是可以的。
@contextmanager
@coroutine
装饰器在各自的内部为
收益率
分配各自的含义装饰功能,但它们仍然是独立的

如前所述,如果fetch to
http://localhost
成功(或者如果您将其更改为指向工作的服务器),但如果提取引发异常,它将不会打印“hello out”。为此,您需要在装饰器中使用
try/finally

@contextmanager
def hello():                                                                              
    print("hello in")                                                                     
    try:
        yield                                                                                 
    finally:
        print("hello out")   

此代码中的另一个错误是,您正在从
get()
返回值。忽略
get()
的返回值;在Tornado中,要生成输出,必须调用
self.write()
(或
finish()
render()

from contextlib import contextmanager                                                     

@contextmanager                                                                           
def hello():                                                                              
    print("hello in")
    try:
        yield
    except:
        print("an exception was thrown into the generator! exit code would not have been run!")
        raise #commenting this out would suppress the original error which __exit__ can do by returning True
    finally:
        print("hello out")                                                                    


def get():                                                       
    with hello():                                                                     
        result = yield "VALUE"                              
    return "Hello "+str(result)                                                       

gen = get()
next(gen)
gen.throw(TypeError)
此代码示例的输出为:

hello in
an exception was thrown into the generator! exit code would not have been run!
hello out
Traceback (most recent call last):
  File "/Users/Tadhg/Documents/codes/test.py", line 24, in <module>
    gen.throw(TypeError)
  File "/Users/Tadhg/Documents/codes/test.py", line 19, in get
    result = yield "VALUE"
TypeError

通过这种方式,您可以保证退出代码将在带有
块的
末尾运行。

这大概是因为您正在使用
\uuuuuuuuuuuuuuuuuuuu
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu退出
进行一些操作。我没有使用异步。我仍然使用带有香草旋风的python 3.4。@jonrsharpeCorrect的观察。但问题是,我从来没有看到
hello out
为了清晰起见修改过帖子。顺便说一句,我很惊讶它没有修改。我没有看到不应该修改的直接原因,我同意你的观察。但我无法让它工作,因为某些原因,我不明白为什么。你会犯错误是因为它无法连接到端口80上的本地主机。代码正在工作(在Ben的注释之后)。因为
yield fetch
会引发所有HTTPErrors,而您的代码不会处理这些错误-结果是500@kwarunek我知道。这不是重点。重点是我从未看到hello退出,我怀疑原因是异常被调度到上下文管理器中的yield,并且从未到达协同例程
class hello:
    def __enter__(self):
        print("hello in")
    def __exit__(self,*args):
        print("hello out")