On Sep 11, 2007, at 9:13 AM, Chris AtLee wrote:

The first is that using abort() from a generator method doesn't seem
to be caught properly by pylons (or maybe paste) so instead of the
client getting a 404 error, he sees a "Server Error" page (HTTP status
200).

Correct, you can't abort in a generator. Technically, when you return a generator, Pylons wraps up shop, and the generator passes fully out of the Pylons middleware as it is then used by the WSGI server. When abort is actually called, there is no middleware then to catch the aborted exception and handle it.

On top of that, there's also the violation of the WSGI spec in this case, since you returned a response (which the response is set to status code 200), then you're trying to change it *After* its already started sending data to the client. This just won't work. You can't send data to the client then change your mind in the middle (after sending a status code 200) that its then going to be a 404.

In the future, I think the HTTP spec might allow such a thing, I recall reading about something like that, but at the moment its a no- go. Returning generators should be used purely for streaming large data sets when you already know its not a 404.

The second issue is that the globals object seems to go away between
the time when the generator stops yielding data and when the finally
clause is executed.  This only seems to happen when debug=false in the
.ini file.

Yep, again, once the generator is passed up the stack, Pylons wraps up its globals (if it didn't, they'd leak). You can grab local copies of some of the globals in your generator like:

def somegen():
    req = request._current_obj()
    # do stuff
    yield 'some data'

    def test1(self):
        abort(404)
        yield "Hello World"

Pylons can handle an action returning a generator, but the action is not allowed to actually *be* a generator. You should rewrite this like so:

def test1(self):
    def somegen():
        yield "Hello World"
    abort(404)
    return somegen()

This way you can throw the 404 before the generator is returned, which causes the HTTP headers (and status code) to be printed to the client right away.

    def test2(self):
        try:
            print "G is", g
            g.foo = "bar"
            for i in range(50):
                time.sleep(0.1)
                yield "%s<br/>\n" % i
        finally:
            print "G is", g
            print g.foo

Rewriting it to properly return a generator (rather than *Be* a generator):

    def test2(self):
        def somegen():
            myg = g._current_obj()
            try:
                print "G is", myg
                myg.foo = "bar"
                for i in range(50):
                    time.sleep(0.1)
                    yield "%s<br/>\n" % i
            finally:
                print "G is", myg
                print myg.foo
        return somegen()

That should work.

Cheers,
Ben

Attachment: smime.p7s
Description: S/MIME cryptographic signature

Reply via email to