a little wsgi framework

T

timmyt

i'm interested in getting opinions on a small wsgi framework i
assembled from webob, sqlalchemy, genshi, and various code fragments i
found on the inter-tubes

here is the interesting glue - any comments / suggestions would be
much appreciated

------------------
the wsgi app
------------------
def application(environ, start_response):
path = environ.get('PATH_INFO', '').lstrip('/')

for regex, callback in urls:
match = re.search(regex, path)

if match:
environ['myapp.url_args'] = match.groups()
request = webob.Request(environ)

try:
return callback(request, start_response)
except Exception, ex:
start_response('500 Internal Server Error', [('Content-
Type', 'text/plain')])
return [traceback.format_exc()]

start_response('404 Not Found', [('Content-Type', 'text/plain')])
return ["Couldn't find the URL specified."]


----------------------------------
the controller decorator
----------------------------------
def web_decorator(filename, method='html'):

def decorator(target):

def wrapper(request, start_response):

#genshi TemplateLoader
template = loader.load(filename)

global config

try:
return_dict = target(request, start_response)
return_string = template.generate(**return_dict).render
(method)
config['database.Session'].commit()
except:
config['database.Session'].rollback()
raise
finally:
config['database.Session'].remove()

#TODO: alter 'Content-Type' per method being passed
start_response('200 OK', [('Content-Type', 'text/html')])
return [return_string]

return wrapper

return decorator
 
H

Hao Lian

timmyt said:
i'm interested in getting opinions on a small wsgi framework i
assembled from webob, sqlalchemy, genshi, and various code fragments i
found on the inter-tubes

here is the interesting glue - any comments / suggestions would be
much appreciated

Fun! Since you're already using WebOb, you should
give webob.Response a try, which will handle your
headers in a sexier way. (If you start using
paste.httpexceptions and the middleware, you can
avoid manually sending headers altogether.)

http://pythonpaste.org/webob/reference.html#response-as-a-wsgi-application
http://pythonpaste.org/modules/httpexceptions.html#paste.httpexceptions.HTTPExceptionHandler
global config

try:
return_dict = target(request, start_response)
return_string = template.generate(**return_dict).render
(method)
config['database.Session'].commit()
except:
config['database.Session'].rollback()
raise
finally:
config['database.Session'].remove()

The config global probably isn't thread-safe
depending on what you're doing with the
application. A safer way to go would be use Paste
Registry like Pylons does with its globals or just
attach it to Request.

http://pythonpaste.org/webob/reference.html#ad-hoc-attributes
 
B

Bruno Desthuilliers

timmyt a écrit :
i'm interested in getting opinions on a small wsgi framework i
assembled from webob, sqlalchemy, genshi, and various code fragments i
found on the inter-tubes

here is the interesting glue - any comments / suggestions would be
much appreciated

<meta>
Well... My first comment would be about the usefulness of yet another
Python web framework, but let's not worry about this...
------------------
the wsgi app
------------------
def application(environ, start_response):
path = environ.get('PATH_INFO', '').lstrip('/')

for regex, callback in urls:
match = re.search(regex, path)


I don't know where these "urls" come from. But anyway : if they live in
some sort of long-running process, you may want to precompile them.
if match:
environ['myapp.url_args'] = match.groups()
request = webob.Request(environ)

try:
return callback(request, start_response)
except Exception, ex:

How are redirect etc handled ?
start_response('500 Internal Server Error', [('Content-
Type', 'text/plain')])
return [traceback.format_exc()]

A configuration option controlling the display of the traceback would be
fine - I surely don't want the traceback being displayed to anyone when
the app goes into production.

Also, logging the error and traceback might be useful.

start_response('404 Not Found', [('Content-Type', 'text/plain')])
return ["Couldn't find the URL specified."]

And in both cases, having the ability to use "custom" 500 and 404 pages
might be nice too.


May I suggest some renaming here ?

filename => path (or 'template_path' ?)
method => content_type
target => controller or function or callback or....
def wrapper(request, start_response):

#genshi TemplateLoader
template = loader.load(filename)

global config

Probably not thread-safe....
try:
return_dict = target(request, start_response)
return_string = template.generate(**return_dict).render
(method)


Renaming again:
return_dict => context
return_string => data or text or ???
config['database.Session'].commit()
except:
config['database.Session'].rollback()
raise
finally:
config['database.Session'].remove()

This doesn't leave much control on transactions... Sometimes you want to
handle it manually one way or another.
#TODO: alter 'Content-Type' per method being passed
start_response('200 OK', [('Content-Type', 'text/html')])
return [return_string]

Ok, so here again, I don't understand how redirects can work. Also, if
you do use start_response here, I don't get why you still pass it to the
callback function.
return wrapper

return decorator

slightly OT, but preserving infos on the decorated function might help
too (introspection, debugging etc).


My 2 cents...
 
T

timmyt

timmyt a écrit :
i'm interested in getting opinions on a smallwsgiframework i
assembled from webob, sqlalchemy, genshi, and various code fragments i
found on the inter-tubes
here is the interesting glue - any comments / suggestions would be
much appreciated

<meta>
Well... My first comment would be about the usefulness of yet another
Python web framework, but let's not worry about this...
    for regex, callback in urls:
        match = re.search(regex, path)

I don't know where these "urls" come from. But anyway : if they live in
some sort of long-running process, you may want to precompile them.
        if match:
            environ['myapp.url_args'] = match.groups()
            request = webob.Request(environ)
            try:
                return callback(request, start_response)
            except Exception, ex:

How are redirect etc handled ?
                start_response('500 Internal Server Error', [('Content-
Type', 'text/plain')])
                return [traceback.format_exc()]

A configuration option controlling the display of the traceback would be
fine - I surely don't want the traceback being displayed to anyone when
  the app goes into production.

Also, logging the error and traceback might be useful.
    start_response('404 Not Found', [('Content-Type', 'text/plain')])
    return ["Couldn't find the URL specified."]

And in both cases, having the ability to use "custom" 500 and 404 pages
might be nice too.


    def decorator(target):

May I suggest some renaming here ?

filename => path (or 'template_path' ?)
method   => content_type
target   => controller or function or callback or....
        def wrapper(request, start_response):
            #genshi TemplateLoader
            template = loader.load(filename)
            global config

Probably not thread-safe....
            try:
                return_dict = target(request, start_response)
                return_string = template.generate(**return_dict).render
(method)

Renaming again:
return_dict => context
return_string => data or text or ???
                config['database.Session'].commit()
            except:
                config['database.Session'].rollback()
                raise
            finally:
                config['database.Session'].remove()

This doesn't leave much control on transactions... Sometimes you want to
handle it manually one way or another.
            #TODO: alter 'Content-Type' per method being passed
            start_response('200 OK', [('Content-Type', 'text/html')])
            return [return_string]

Ok, so here again, I don't understand how redirects can work. Also, if
you do use start_response here, I don't get why you still pass it to the
callback function.
        return wrapper
    return decorator

slightly OT, but preserving infos on the decorated function might help
too (introspection, debugging etc).

My 2 cents...

thank you gentlemen

i'm now looking for Redirect exceptions as well, and returning the
appropriate response code (stole this idea from turbogears)

i like doing the response codes and exceptions myself which is why i'm
not using the webob response - i think it's more explicit and
straightforward

the conditional display of errors goes without say - that will be
trivial to add

the 'global config' line was an error - config is a module level
variable set before the application definition

i assume as long as i don't write to the config variable within the
application it is thread-safe - is that correct

the underlying assumption is the wsgi application setup does not have
to be thread safe - this is only run once per process
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,982
Messages
2,570,186
Members
46,744
Latest member
CortneyMcK

Latest Threads

Top