为什么 Twisted 认为我调用了 request.finish() 两次,而实际上我没有调用?

发布于 2024-09-10 04:37:28 字数 2201 浏览 3 评论 0原文

这是我在使用 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() 不是中的最后一行一个函数,然后我在它之后立即返回。

无论如何,我将此类添加到 /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() 我就无法重定向)。

This is an annoying problem I am having with Twisted.web. Basically, I have a class that inherits from twisted.web.resource.Resource and adds some default stuff to Mako templates:

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 ""

Then, and I have narrowed the problem down to this small class (which I tested), I write a resource which inherits from 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()

I'd like to note that, in every other case, if request.finish() isn't the last line in a function, then I return immediately after it.

Anyways, I add this class to the site at /test and when I navigate there, I get a submit button. I click the submit button, and in the console I get:

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

But, I get this ONLY the first time I submit the page. Every other time, it's fine. I would just ignore this, but it's been nagging at me, and I can't for the life of me figure out why it's doing this at all, and why only the first time the page is submitted. I can't seem to find anything online, and even dropping print statements and tracebacks in the request.finish() code didn't reveal anything.

edit

This morning I tried adding a second request.finish() line to the resource, and it still only gave me the error one time. I suppose it will only warn about it in a resource once -- maybe per run of the program, or per session, I'm not sure. In any case, I changed it to:

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()

and just got two messages, one time. I still have no idea why I can't redirect the request without it saying I finished it twice (because I can't redirect without request.finish()).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

半寸时光 2024-09-17 04:37:28

简短回答


必须是:

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()

我进入并在 twisted.web 中签出了 render .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() 然后返回twisted.web.server.NOT_DONE_YET

更多


我发现了一些有关此内容的文档。它与重定向请求无关,而是使用 request.write() 渲染资源。它表示调用 request.finish() 然后返回 NOT_DONE_YET。通过查看 render() 中的代码,我可以明白为什么会出现这种情况。

Short Answer


It has to be:

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

Long Answer


I decided to go sifting through some Twisted source code. I first added a traceback to the area that prints the error if request.finish() is called twice:

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()

I went in and checked out render in twisted.web.server and found this:

    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 is the result of rendering a resource, so once body is populated, in the example case given in my question, finish has already been called on this request object (since self is passed from this method to the resource's render method).

From looking at this code it becomes apparent that by returning NOT_DONE_YET I would avoid the warning.

I could have also changed the last line of that method to:

if not self.finished:
    self.finish()

but, in the interest of not modifying the library, the short answer is:

after calling request.redirect() you must call request.finish() and then return twisted.web.server.NOT_DONE_YET

More


I found some documentation about this. It isn't related to redirecting a request, but instead rendering a resource, using request.write(). It says to call request.finish() and then return NOT_DONE_YET. From looking at the code in render() I can see why that is the case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文