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()).
Short Answer
It has to be:
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:... 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
renderintwisted.web.serverand found this:bodyis the result of rendering a resource, so oncebodyis populated, in the example case given in my question,finishhas already been called on this request object (sinceselfis passed from this method to the resource’s render method).From looking at this code it becomes apparent that by returning
NOT_DONE_YETI would avoid the warning.I could have also changed the last line of that method to:
but, in the interest of not modifying the library, the short answer is:
after calling
request.redirect()you must callrequest.finish()and thenreturn twisted.web.server.NOT_DONE_YETMore
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 callrequest.finish()and then returnNOT_DONE_YET. From looking at the code inrender()I can see why that is the case.