为什么Twisted认为我正在调用request.finish()两次当我不是?

时间:2010-07-15 11:09:39

标签: python twisted.web

这是我在使用Twisted.web时遇到的烦人问题。基本上,我有一个继承自twisted.web.resource.Resource的类,并为Mako模板添加了一些默认内容:

from twisted.web.resource import Resource
from mako.lookup import TemplateLookup
from project.session import SessionData
from project.security import make_nonce


class Page(Resource):

    template = ""

    def display(self, request, **kwargs):
        session = SessionData(request.getSession())

        if self.template:
            templates = TemplateLookup(directories=['templates'])
            template = templates.get_template(self.template)
            return template.render(user=session.user,
                                   info=session.info,
                                   current_path=request.path,
                                   nonce=make_nonce(session),
                                   **kwargs)
        else:
            return ""

然后,我已将问题缩小到这个小类(我测试过),我写了一个继承自Page的资源:

class Test(pages.Page):
    def render_GET(self, request):
        return "<form method='post'><input type='submit'></form>"
    def render_POST(self, request):
        request.redirect("/test")
        request.finish()

我想要注意的是,在其他所有情况下,如果request.finish()不是函数的最后一行,那么我return就在它之后。

无论如何,我将此课程添加到/test的网站,当我在那里导航时,我会收到一个提交按钮。我点击提交按钮,在控制台中我得到:

C:\Python26\lib\site-packages\twisted\web\server.py:200: UserWarning: Warning! request.finish called twice.
  self.finish()

但是,我第一次提交页面时才会收到此信息。每隔一段时间,这很好。我会忽略这一点,但它一直在唠叨我,我不能为我的生活找出它为什么这样做,以及为什么只有第一次提交页面。我似乎无法在网上找到任何东西,甚至在request.finish()代码中删除了打印语句和回溯也没有透露任何内容。

修改

今天早上我尝试在资源中添加第二条request.finish()行,但它仍然只给了我一次错误。我想它只会在一次资源中警告它 - 也许每次运行程序,或者每次会话,我不确定。无论如何,我把它改为:

class Test(pages.Page):
    def render_GET(self, request):
        return "<form method='post'><input type='submit'></form>"
    def render_POST(self, request):
        request.redirect("/test")
        request.finish()
        request.finish()

只有两条消息,一次。我仍然不知道为什么我不能重新定向请求而没有说我完成了两次(因为我不能在没有request.finish()的情况下重定向)。

1 个答案:

答案 0 :(得分:10)

简答


必须是:

request.redirect("/test")
request.finish()
return twisted.web.server.NOT_DONE_YET

长答案


我决定对一些Twisted源代码进行筛选。我首先在该区域添加了一个回溯,如果request.finish()被调用两次,则会打印错误:

def finish(self):
    import traceback #here
    """
    Indicate that all response data has been written to this L{Request}.
    """
    if self._disconnected:
        raise RuntimeError(
            "Request.finish called on a request after its connection was lost; "
            "use Request.notifyFinish to keep track of this.")
    if self.finished:
        warnings.warn("Warning! request.finish called twice.", stacklevel=2)
        traceback.print_stack() #here
        return
    #....
...
  File "C:\Python26\lib\site-packages\twisted\web\server.py", line 200, in render
    self.finish()
  File "C:\Python26\lib\site-packages\twisted\web\http.py", line 904, in finish
    traceback.print_stack()

我进去并在render中查看twisted.web.server并找到了:

    if body == NOT_DONE_YET:
        return
    if type(body) is not types.StringType:
        body = resource.ErrorPage(
            http.INTERNAL_SERVER_ERROR,
            "Request did not return a string",
            "Request: " + html.PRE(reflect.safe_repr(self)) + "<br />" +
            "Resource: " + html.PRE(reflect.safe_repr(resrc)) + "<br />" +
            "Value: " + html.PRE(reflect.safe_repr(body))).render(self)

    if self.method == "HEAD":
        if len(body) > 0:
            # This is a Bad Thing (RFC 2616, 9.4)
            log.msg("Warning: HEAD request %s for resource %s is"
                    " returning a message body."
                    "  I think I'll eat it."
                    % (self, resrc))
            self.setHeader('content-length', str(len(body)))
        self.write('')
    else:
        self.setHeader('content-length', str(len(body)))
        self.write(body)
    self.finish()

body是渲染资源的结果,因此填充body后,在我的问题中给出的示例案例中,finish已在此请求对象上调用(从self从此方法传递到资源的render方法。

通过查看此代码,很明显,通过返回NOT_DONE_YET,我会避免警告。

我本可以将该方法的最后一行更改为:

if not self.finished:
    self.finish()

但是,为了不修改库,简短的回答是:

致电request.redirect()后,您必须致电request.finish(),然后致电return twisted.web.server.NOT_DONE_YET

更多


我发现了一些documentation。它与重定向请求无关,而是使用request.write()呈现资源。它说要拨打request.finish()然后返回NOT_DONE_YET。通过查看render()中的代码,我可以看出为什么会出现这种情况。