tags:

views:

315

answers:

3

In WSGI, post data is consumed by reading the file-like object environ['wsgi.input']. If a second element in the stack also wants to read post data it may hang the program by reading when there's nothing more to read.

How should I copy the POST data so it can be processed multiple times?

A: 

If you're gonna read it in one fell swoop, you could always read it in, create a CStringIO file-like object of the stuff you've read and then assign it back, like this:

import cStringIO
import copy
lines = []
for line in environ['wsgi.input']:
    lines.append(line)
newlines = copy.copy(lines)
environ['wsgi.input'] = cStringIO.StringIO(''.join(newlines))

There's most likely a more efficient way to do this, but I in general find wsgi's post stuff pretty brittle if you want to do anything non-trivial (like read post data muptiple times)...

Paul Huff
And, bobince _is_ the more efficient way to do that :)
Paul Huff
Using a for/in loop on wsgi.input like that could be very memory/time inefficient. This is because if in worst case you had a large file where all data consisted of empty lines, you would end up creating a very large list where each entry is a single character. Also not sure why you bother with copy.copy() given you join it back together straight away anyway.
Graham Dumpleton
+4  A: 

You could try putting a file-like replica of the stream back in the environment:

from cStringIO import StringIO

length= int(environ.get('CONTENT_LENGTH', '0'))
body= StringIO(environ['wsgi_input'].read(length))
environ['wsgi_input']= body

Needing to do this is a bit of a smell, though. Ideally only one piece of code should be parsing the query string and post body, and delivering the results to other components.

bobince
You shouldn't rely on being able to default content length to -1. There is nothing in WSGI specification that says an implementation should accept -1 as argument to read() to mean read all input. An implementation may decide to raise an exception in that circumstance. In fact the specification probably even says that if CONTENT_LENGTH is not present or empty that it must be interpreted to mean '0', or not input available.
Graham Dumpleton
Ah yeh... not quite sure why I put that, my own actual code uses 0 :-) It's your intention to change this behaviour in WSGI though, right?
bobince
I somewhat doubt WSGI will see any changes now.
Graham Dumpleton
+2  A: 

Go have a look at WebOb package. It provides functionality that allows one to designate that wsgi.input should be made seekable. This has the effect of allowing you to rewind the input stream such that content can be replayed through different handler. Even if you don't use WebOb, the way it does this should be instructive as would trust Ian to have done this in an appropriate way. For search results in documentation go here.

Graham Dumpleton