On Nov 8, 2007, at 1:04 PM, Mike Orr wrote:
I'm all for fail-safe solutions with implementations that users can understand and verify they're correct. The @generator looks like the best compromise. The _buffet is also straightforward though ugly.What would the overhead be of creating a context object for all requests? That would avoid the ugly decorator. There's no reason we can't require users to call 'self.render' instead of 'render' if it streamlines the implementation and fixes a design bug. The first two solutions are the least desirable because if even the Pylons developers can't be sure they'll work in all cases, they're too fragile.
I've made some changes in the current pylons-dev trunk to address issues one is likely to encounter when running in an async environment, which also addresses the generator issue. They both need to be able to get references to the objects currently in pylons SOP's, but without using something as global as a thread-local (or in the generators case, globals that were already cleaned up).
The code I added puts references to the c, request, and response global objects, in the environ. The request is slightly delicate, because request holds a copy of environ... which then holds a reference to request. Python has no problem collecting circular references, but they're cleaned up quite a bit slower than when they're non-circular, so at the end of the WSGI PylonsApp, it deletes the environ reference to the request object.
The code add's _py_c, _py_request, and _py_response to the controller instance, which are references to the same objects the SOP's proxy to. This way someone running in twisted or some other async based server can still use Pylons and just avoid any of the pylons SOP's. It's a little more work, but no more work any any other system that works under async.
Since the controller is holding references to these objects, its also pretty easy to add a special render method to the controller, that includes references to the same objects the SOP's reference, when it makes the render call. This way you could just use self.render('/some/ template.html') inside a generator, with no worries.
Or as Phil pointed out:@generator # assumes the method is a generator, and creates a context object for it to use
def yieldstuff(self, pylons, id):
pylons.session['stuff'] = get_stuff(id, pylons.request.POST.get
('value'))
yield pylons.render('/a.mako')
pylons.c.name = None
yield pylons.render('/b.mako')
yield 'more stuff'
Would all just work. I'm uncertain if I'd want it all to be attached
to another pylons object, I'd prefer having to ask for the ones you
need, like so:
@generator # assumes the method is a generator, and creates a context object for it to use
def yieldstuff(self, c, session, request, render, id):
session['stuff'] = get_stuff(id, request.POST.get('value'))
yield render('/a.mako')
c.name = None
yield render('/b.mako')
yield 'more stuff'
If that method doesn't need one of the SOP's locally for a generator,
you just don't ask for it.
Any preferences on which one we should use? Cons of each approach: - pylons object with the other main vars attached:We have to instantiate yet another object per request. It can also confuse a user because there's the global pylons package name, and the method local pylons object which is totally different.
- attach each one to self:We get in the way of anyone wanting to use 'request', 'response', 'session', or 'c' as an action name.
- users asks for them in function argsWe get in the way of anyone wanting to use request, response, session, or c as a routes dict variable
I'm leaning towards the first one so far, despite the possible user confusion about scoping of the pylons namespace, just because it doesn't possibly conflict with action or route names.
Cheers, Ben
smime.p7s
Description: S/MIME cryptographic signature
