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

Car*_*ers 10 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 ""
Run Code Online (Sandbox Code Playgroud)

然后,我把问题缩小到这个小类(我测试过),我写了一个继承自的资源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()
Run Code Online (Sandbox Code Playgroud)

我想要注意的是,在所有其他情况下,如果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()
Run Code Online (Sandbox Code Playgroud)

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

Car*_*ers 10

简答


它一定要是:

request.redirect("/test")
request.finish()
return twisted.web.server.NOT_DONE_YET
Run Code Online (Sandbox Code Playgroud)

答案很长


我决定对一些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
    #....
Run Code Online (Sandbox Code Playgroud)
...
  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()

我走进去,签出rendertwisted.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()
Run Code Online (Sandbox Code Playgroud)

body是渲染资源的结果,所以一旦body填充,在我的问题中给出的示例情况下,finish已经在此请求对象上调用(因为self从此方法传递到资源的render方法).

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

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

if not self.finished:
    self.finish()
Run Code Online (Sandbox Code Playgroud)

但是,为了不修改图书馆,简短的答案是:

打电话后request.redirect()你必须打电话request.finish()然后return twisted.web.server.NOT_DONE_YET

更多


我找到了一些关于此的文档.它与重定向请求无关,而是使用相关的呈现资源request.write().它说打电话request.finish()然后回来NOT_DONE_YET.从查看代码render()我可以看出为什么会这样.