SlideShare a Scribd company logo
1 of 95
Download to read offline
web




Monday, 22 August 2011
web
                         micro-framework



Monday, 22 August 2011
web
                         micro-framework

                           Ba!le!
Monday, 22 August 2011
aka




Monday, 22 August 2011
aka
        Richard surveys the landscape
       so you (probably) don’t have to


Monday, 22 August 2011
Introduction




Monday, 22 August 2011

I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but
I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
Introduction
                   • I write code for a telco




Monday, 22 August 2011

I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but
I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
Introduction
                   • I write code for a telco
                   • Lots of discrete system applications




Monday, 22 August 2011

I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but
I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
Introduction
                   • I write code for a telco
                   • Lots of discrete system applications
                   • Connected through HTTP




Monday, 22 August 2011

I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but
I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
Introduction
                   • I write code for a telco
                   • Lots of discrete system applications
                   • Connected through HTTP
                   • I write little HTTP servers all the time



Monday, 22 August 2011

I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but
I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
Introduction
                   • I write code for a telco
                   • Lots of discrete system applications
                   • Connected through HTTP
                   • I write little HTTP servers all the time
                   • I’ve written half a dozen micro-frameworks


Monday, 22 August 2011

I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but
I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
What’s IN?




Monday, 22 August 2011

“standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get
middleware. Downloadable docs for offline development are a real bonus.
What’s IN?
                   • easy to understand and use
                         (docs, minimal magic, no surprises, terse)




Monday, 22 August 2011

“standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get
middleware. Downloadable docs for offline development are a real bonus.
What’s IN?
                   • easy to understand and use
                         (docs, minimal magic, no surprises, terse)
                   • HTTP request/response




Monday, 22 August 2011

“standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get
middleware. Downloadable docs for offline development are a real bonus.
What’s IN?
                   • easy to understand and use
                         (docs, minimal magic, no surprises, terse)
                   • HTTP request/response
                   • URL routing (“RESTful”)



Monday, 22 August 2011

“standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get
middleware. Downloadable docs for offline development are a real bonus.
What’s IN?
                   • easy to understand and use
                         (docs, minimal magic, no surprises, terse)
                   • HTTP request/response
                   • URL routing (“RESTful”)
                   • WSGI


Monday, 22 August 2011

“standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get
middleware. Downloadable docs for offline development are a real bonus.
What’s IN?
                   • easy to understand and use
                         (docs, minimal magic, no surprises, terse)
                   • HTTP request/response
                   • URL routing (“RESTful”)
                   • WSGI
                   • PyPy and Python 3...
Monday, 22 August 2011

“standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get
middleware. Downloadable docs for offline development are a real bonus.
What’s OUT?




Monday, 22 August 2011

I like dbapiext / explicitness; SQLAlchemy works plenty well; templating is handled by other things (though some frameworks have
something built-in)
What’s OUT?


                   • Object-Relational Manager
                         (actually, any sort of DB wrapper)




Monday, 22 August 2011

I like dbapiext / explicitness; SQLAlchemy works plenty well; templating is handled by other things (though some frameworks have
something built-in)
What’s OUT?


                   • Object-Relational Manager
                         (actually, any sort of DB wrapper)
                   • Template engine


Monday, 22 August 2011

I like dbapiext / explicitness; SQLAlchemy works plenty well; templating is handled by other things (though some frameworks have
something built-in)
Who’s out?
                   • Mega Frameworks:
                         django, grok, pyramid, web2py, zope
                   • CubicWeb (never heard of it)
                   • milla (too young / incomplete)
                   • pump (too late to include)
                   • routes+webob (too complicated)
Monday, 22 August 2011
WIKI




Monday, 22 August 2011

In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I
also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of
brevity.
WIKI

                   • page view (GET /PageName)




Monday, 22 August 2011

In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I
also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of
brevity.
WIKI

                   • page view (GET /PageName)
                   • creation, editing (GET & POST /edit)



Monday, 22 August 2011

In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I
also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of
brevity.
WIKI

                   • page view (GET /PageName)
                   • creation, editing (GET & POST /edit)
                   • redirect (GET /)


Monday, 22 August 2011

In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I
also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of
brevity.
WIKI

                   • page view (GET /PageName)
                   • creation, editing (GET & POST /edit)
                   • redirect (GET /)
                   • not found (GET /favicon.ico)

Monday, 22 August 2011

In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I
also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of
brevity.
The Contenders
                                            •   cgi+wsgiref
                                            •   aspen.io
                                            •   bobo
                                            •   bottle
                                            •   cherrypy
                                            •   flask
                                            •   itty
                                            •   pesto
                                            •   web.py
                                            •   werkzeug


Monday, 22 August 2011

10 frameworks, cgi+wsgiref is my baseline
The Criteria
                                               • documentation
                                               • ease of use
                                               • magic
                                               • RESTful
                                               • WSGI
                                               • Python 3
                                               • PyPy
                                               • amount of typing
                                               • size of the framework
Monday, 22 August 2011

Just to recap, this is what I’m looking for.
1.




                         cgi&wsgiref


Monday, 22 August 2011

DOCUMENTATION: just the standard library, EASE OF USE: a bit of work but not hard, EXTRAS: none, MAGIC: no, RESTful: no, WSGI:
yes
cgi+wsgiref
                             simple WSGI app
                              class NotFound(Exception):
                                  pass
                              class Redirect(Exception):
                                  def __init__(self, location):
                                       self.location = location

                              def app(environ, start_response):
                                  headers = [('Content-type', 'text/html')]
                                  status = '200 OK'
                                  try:
                                       for p, m, c in urls:
                                           if m != environ['REQUEST_METHOD']: continue
                                           m = re.match(p, environ['PATH_INFO'])
                                           if m is None: continue
                                           response = c(environ, **m.groupdict())
                                       raise NotFound
                                  except NotFound:
                                       status = '404 Not Found'
                                       response = 'Not found: %s' % environ['PATH_INFO']
                                  except Redirect, e:
                                       status = '301 Moved Permanently'
                                       headers.append(('Location', e.location))
                                       response = ''
                                  start_response(status, headers)
                                  return response

Monday, 22 August 2011

No top-level app in wsgiref, so I had to write my own. This, by the way, is why I've written a half a dozen web frameworks.
cgi+wsgiref
                              simple WSGI app
                         def app(environ, start_response):
                             headers = [('Content-type', 'text/html')]
                             status = '200 OK'
                             try:
                                  for p, m, c in urls:
                                      if m != environ['REQUEST_METHOD']: continue
                                      m = re.match(p, environ['PATH_INFO'])
                                      if m is None: continue
                                      response = c(environ, **m.groupdict())
                                  raise NotFound
                             except NotFound:
                                  status = '404 Not Found'
                                  response = 'Not found: %s' % environ['PATH_INFO']
                             except Redirect, e:
                                  status = '301 Moved Permanently'
                                  headers.append(('Location', e.location))
                                  response = ''
                             start_response(status, headers)
                             return response

Monday, 22 August 2011

These are the WSGI bits.
cgi+wsgiref
                             simple WSGI app
                         def app(environ, start_response):
                             headers = [('Content-type', 'text/html')]
                             status = '200 OK'
                             try:
                                  for p, m, c in urls:
                                      if m != environ['REQUEST_METHOD']: continue
                                      m = re.match(p, environ['PATH_INFO'])
                                      if m is None: continue
                                      response = c(environ, **m.groupdict())
                                  raise NotFound
                             except NotFound:
                                  status = '404 Not Found'
                                  response = 'Not found: %s' % environ['PATH_INFO']
                             except Redirect, e:
                                  status = '301 Moved Permanently'
                                  headers.append(('Location', e.location))
                                  response = ''
                             start_response(status, headers)
                             return response

Monday, 22 August 2011

Given a list of potential actions containing a URL pattern to match, a request method and a callable, we see which one to call, if any.
cgi+wsgiref
                                        actions
              def index(environ):
                  raise Redirect('/FrontPage')

              def page(environ, name):
                  if not storage.wikiname_re.match(name):
                      raise NotFound
                  return wiki.render_page(name)

              def edit_form(environ, name):
                  return wiki.render_edit_form(name)

              def edit(environ, name):
                  fields = cgi.FieldStorage(fp=environ['wsgi.input'],
                      environ=environ)
                  if 'submit' in fields:
                      wiki.store_page(name, fields.getfirst('content'))
                  raise Redirect("/%s" % name)
Monday, 22 August 2011

Handling forms is manual, of course.
cgi+wsgiref
                                           url mapping

      urls = [
          ('^/$', 'GET', index),
          ('^/(?P<name>w+)/?$', 'GET', page),
          ('^/(?P<name>w+)/edit$', 'GET', edit_form),
          ('^/(?P<name>w+)/edit$', 'POST', edit),
      ]




Monday, 22 August 2011

Finally we construct our list of potential handlers.
cgi+wsgiref
                         serving HTTP

   server = wsgiref.simple_server.make_server('127.0.0.1', 8080, app)
   server.serve_forever()




Monday, 22 August 2011
2.




                                                     bobo


Monday, 22 August 2011

bobo started out life as an awesome object publisher (get attributes and items) and then it became Zope. sadly as Zope grew, so bobo was
tainted and now it’s not the simple publisher it once was. DOCUMENTATION: limited and focused on The Bobo Way - EASE OF USE: it's
just not straight-forward, can't just create an app and publish it - I must publish a module file!?!
bobo
                                                       index

                              @bobo.query('/')
                              def index():
                                  return bobo.redirect("/FrontPage")




Monday, 22 August 2011

Simple enough. MAGIC: bobo ... that’s the module. Decorating for scanning. I did have some transient, unexplained trouble with this
redirect ending up at “/FrontPage/”. There also doesn’t appear to be a redirect exception.
bobo
                                           /PageName
                         @bobo.subroute('/:name?', scan=True)
                         class Page(object):
                             def __init__(self, request, name=None):
                                 if not storage.wikiname_re.match(name):
                                     raise bobo.NotFound('/%s' % name)
                                 self.name = name

                              @bobo.query('')
                              def index(self):
                                  return wiki.render_page(name)




Monday, 22 August 2011

RESTful: quite confusing bobo.route()/bobo.subroute() request method in decorator. So here we’re setting up a “sub”route directing the
URLs starting with that pattern to the decorated class (any callable will do). Instances of the class will be created being passed the name
from the URL pattern. the docs seemed to imply that @bobo.query with no args would expose the function named - but that didn’t
work. scan? wha? well, without it the decorations in the class aren’t ... known.
bobo
                                        index


    AttributeError: Page instance has no attribute 'bobo_response'




Monday, 22 August 2011

without scan I get this bizarro error
bobo
                             /PageName/edit
                         @bobo.query('/edit', 'GET')
                         def edit(self):
                             return wiki.render_edit_form(name)

                         @bobo.post('/edit', 'POST')
                         def do_edit(self, content, submit=False):
                             if submit:
                                 wiki.store_page(self.name, content)
                             return bobo.redirect("/%s" % self.name)




Monday, 22 August 2011

Again the docs seemed to lie. @post needs that “POST” argument or it gets both GET AND POST. PRO: uses WebOb request/response.
bobo
                               running the app

                           if __name__ == "__main__":
                               import boboserver
                               boboserver.server(['-f', __file__])




Monday, 22 August 2011

And then we serve the application by pointing boboserver at the what now? We publish the module file? This is pretty wacky. WSGI:
kinda (paste deploy) - no idea how to plug into my own WSGI server. I poked and poked at the source and could not figure how to get a
WSGI application.
3.




                                            cherrypy


Monday, 22 August 2011

OK, so from the first example this looks to be a little different to others - kinda close to bobo by publishing objects and methods. But it’s
not RESTish. DOCS: pro: uses Sphinx, con: a bit light-on in some places. The default dispatcher doesn't make RESTish control easy so I'm
using a RESTish MethodDispatcher, but the only documentation for it is a simple example which doesn't really explain things in much
detail. I guessed at some of the semantics, and it worked!
cherrypy
                                                     index
                  @cherrypy.popargs('name')
                  class Wiki(object):
                      exposed = True

                         def GET(self, name=None):
                             if not name:
                                 raise cherrypy.HTTPRedirect("/FrontPage")
                             if not storage.wikiname_re.match(name):
                                 raise cherrypy.NotFound('/%s' % name)
                                 return wiki.render_page(name)

                         edit = WikiEdit()




Monday, 22 August 2011

Docs search found me the HTTPRedirect and NotFound which is nice.Yay Sphinx! “exposed” may be done in a bunch of different ways,
not explained clearly in one place AFAICT.
cherrypy
                                       edit
       class WikiEdit(object):
           exposed = True

                def GET(self, name):
                    return wiki.render_edit_form(name)

                def POST(self, name, content='', submit=None, cancel=None):
                    if submit:
                        wiki.store_page(name, content)
                    raise cherrypy.HTTPRedirect("/%s" % name)




Monday, 22 August 2011
cherrypy
                                      the funky bit
                         conf = {
                             '/': {
                                  'request.dispatch':
                                      cherrypy.dispatch.MethodDispatcher(),
                             }
                         }

                         if __name__ == "__main__":
                             cherrypy.quickstart(Wiki(), '/', conf)




Monday, 22 August 2011

EASE OF USE: simple, though the config bit is a bit strange; ooh, automatic code reloading.; WSGI: I had to google and *then* look at the
source to find out how to get a WSGI application: cherrypy.Application, cherrypy.request.wsgi_environ; MAGIC: minimal - just the
cherrypy.request module global
4.




                                         web.py


Monday, 22 August 2011

DOCUMENTATION: great website, easy to get started
web.py
                         index and /PageName
                         class index:
                             def GET(self):
                                 raise web.SeeOther("/FrontPage")

                         class page:
                             def GET(self, name):
                                 if not storage.wikiname_re.match(name):
                                     raise web.NotFound('/%s' % name)
                                 return wiki.render_page(name)




Monday, 22 August 2011

RESTful: url mapping through ugly URLs tuple (url, name, url, name, ...) and handler named is class with request method methods GET(),
POST(), ...
web.py
                                /PageName/edit
          class edit:
              def GET(self, name):
                  return wiki.render_edit_form(name)

                   def POST(self, name):
                       f = web.input('content', submit=False, cancel=False)
                       if f.submit:
                           wiki.store_page(name, f.content)
                       raise web.seeother("/%s" % name)




Monday, 22 August 2011

DOCS: was hard to find how web.input() worked; EASE OF USE: simple enough class-per-URL-match; Form doesn't support submit
buttons?! Forms are a bit of a fiddly way to get to simple form submitted data. For this simple case (very much like most of my uses) it's
actually easier to access the submitted information directly without involving the Form class through web.input().
web.py
                                                    serving
                            urls = (
                                '/', 'index',
                                '/(.+)/edit', 'edit',
                                '/(.+)', 'page',
                            )
                            app = web.application(urls, globals())

                            if __name__ == "__main__":
                                app.run()




Monday, 22 August 2011

MAGIC: url mapping through names looked up in globals? Ugh. module-level form input() global; EXTRAS: session, openid, utilities,
templating, form and db abstraction; WSGI: hard-to-find web.application(...).wsgifunc(*middleware)
5.




                         bo!le


Monday, 22 August 2011
bottle
                         index and /PageName
                     @get('/')
                     def index():
                         redirect("/FrontPage")

                     @get('/:name')
                     def display(name):
                         if not storage.wikiname_re.match(name):
                             abort(404, 'Not Found :/%s' % name)
                         return wiki.render_page(name)




Monday, 22 August 2011

PRO: SINGLE FILE! bottle.py; EASE OF USE: very simple; CON: MAGIC module global magic for request, response :-( PRO: Has first-class
get(), post() as well as generic route() which configure a default app (you can also create other apps). DOCUMENTATION: very good,
downloadable
bottle
                               /PageName/edit
                @get('/:name/edit')
                def edit_form(name):
                    return wiki.render_edit_form(name)

                @post('/:name/edit')
                def edit(name):
                    if request.POST.get('submit'):
                        wiki.store_page(name, request.POST['content'])
                    redirect("/%s" % name)




Monday, 22 August 2011

RESTful: get(), post() decorators which take URL. request.POST has the posted form vars (there’s also request.params which combines
GET and POST vars).
bottle
                                    serving HTTP

                         if __name__ == "__main__":
                             run(host='127.0.0.1', port=8080)




Monday, 22 August 2011

WSGI: Had to look in the source but bottle.default_app.wsgi is WSGI. Bottle also has the concept of an app stack, but I didn’t look into
that and am not sure what the point is. EXTRAS: static files, adapters (eg werkzeug), SimpleTemplate, lots of convenience utilities
from bottle import get, post, request, run, abort, redirect
                         import storage

                         wiki = storage.Storage('contents')

                         @get('/')
                         def index():
                             redirect("/FrontPage")

                         @get('/:name')
                         def display(name):
                             if not storage.wikiname_re.match(name):
                                 abort(404, 'Not Found :/%s' % name)
                             return wiki.render_page(name)

                         @get('/:name/edit')
                         def edit_form(name):
                             return wiki.render_edit_form(name)

                         @post('/:name/edit')
                         def edit(name):
                             if request.POST.get('submit'):
                                 wiki.store_page(name, request.POST['content'])
                             redirect("/%s" % name)

                         if __name__ == "__main__":
                             run(host='127.0.0.1', port=8080)


Monday, 22 August 2011

Seriously, that’s it. That’s all the overhead that bottle imposes when you write a web app with it!
6.




                                                            i!y


Monday, 22 August 2011

itty is really similar to bottle. They both aspire to the same simple API.
itty
                         index and /PageName
                          @get('/')
                          def index(request):
                              raise Redirect("/FrontPage")

                          @get('/(?P<name>w+)')
                          def display(request, name):
                              if not storage.wikiname_re.match(name):
                                  raise NotFound('/%s' % name)
                              return wiki.render_page(name)




Monday, 22 August 2011

get, Redirect and NotFound imported from itty (from itty import *) MAGIC module globals; DOCS: very little but straightforward and
good examples; EASE OF USE: simple; RESTful: get(), post() decorators which take URL;
itty
                              /PageName/edit
             @get('/(?P<name>w+)/edit')
             def edit_form(request, name):
                 return wiki.render_edit_form(name)

             @post('/(?P<name>w+)/edit')
             def edit(request, name):
                 if request.POST.get('submit'):
                     wiki.store_page(name, request.POST['content'])
                 raise Redirect("/%s" % name)




Monday, 22 August 2011

request.POST simple enough; CON: Redirects appear in the log with tracebacks??!?
itty
                                                    serving

                                                         run_itty()




Monday, 22 August 2011

WSGI: itty.handle_request (the docs don't make this clear ... and *why* request._environ ??)
7.




                                                  flask


Monday, 22 August 2011

Con: depends on two external libraries: the Jinja2 template engine and the Werkzeug WSGI toolkit. DOCS: very good, and downloadable,
though werkzeug references were tricky
flask
                         index and /PageName
                          app = Flask(__name__)

                          @app.route("/")
                          def index():
                              return redirect("/FrontPage")

                          @app.route('/<name>')
                          def display(name):
                              if not storage.wikiname_re.match(name):
                                  raise NotFound('/%s' % name)
                              return wiki.render_page(name)




Monday, 22 August 2011

Where itty and bottle had a single default app that was being configured, here we explicitly create our app. CON: There appears to be no
way to raise a redirect exception. NotFound needs to be imported from werkzeug directly. No way to return a NotFound result? EASE
OF USE: requirement to fall back to werkzeug sometimes makes things tricky
flask
                               /PageName/edit
                 @app.route('/<name>/edit', methods=['GET'])
                 def edit_form(name):
                     return wiki.render_edit_form(name)

                 @app.route('/<name>/edit', methods=['POST'])
                 def edit(name):
                     if request.form.get('submit'):
                         wiki.store_page(name, request.form['content'])
                     return redirect("/%s" % name)




Monday, 22 August 2011

Con: in the quickstart, it wasn't clear where request came from. MAGIC: request is module-level global from “flask”; RESTful: request
method in app.route() decorator which also takes URL
flask
                                                    serving


                                                    app.run(debug=True)




Monday, 22 August 2011

Pro: debugger works really well; WSGI: the Flask object is a WSGI app; EXTRAS: werkzeug, test support, debugger, secure cookies, Jinja2,
shell
8.




                         pesto


Monday, 22 August 2011
pesto
                         index and /PageName
                         app = dispatcher_app()

                         @app.match('/', 'GET')
                         def index(request):
                             return Response.redirect("/FrontPage")

                         @app.match('/<name:unicode>', 'GET')
                         def display(request, name):
                             if not storage.wikiname_re.match(name):
                                 return Response.not_found('/%s' % name)
                             page = wiki.render_page(name)
                             return Response(page)




Monday, 22 August 2011

explicit app creation - nice. DOCUMENTATION: good; EASE OF USE: simple; MAGIC: no magic whatsoever; RESTful: request method in
app.match() decorator which also takes URL; unfortunately can’t just return the page contents from the function, have to wrap it in a
Response.
pesto
                                /PageName/edit
                 @app.match('/<name:unicode>/edit', 'GET')
                 def edit_form(request, name):
                     page = wiki.render_edit_form(name)
                     return Response(page)

                 @app.match('/<name:unicode>/edit', 'POST')
                 def edit(request, name):
                     if request.form.get('submit'):
                         wiki.store_page(name, request.form['content'])
                     return Response.redirect("/%s" % name)




Monday, 22 August 2011

Very, very similar to itty and bottle... the type is not optional - I just assumed unicode would be the default.
pesto
                                                     serving

          httpd = simple_server.make_server('127.0.0.1', 8080, app)
          httpd.serve_forever()




Monday, 22 August 2011

WSGI: app is a WSGI app; EXTRAS: utilities for testing, sessions, caching, wsgi
9.




                                        werkzeug


Monday, 22 August 2011

DOCUMENTATION: very good, downloadable: The docs are a bit interestingly laid-out, with the sample program being overly complex to
start with, so I had to delve into the actual API docs to figure things out. Having said that, the example program does talk about structure,
ORM layers, templating, URLs and other handy things.
werkzeug
                         index and /PageName

                          def index(request):
                              raise RequestRedirect("/FrontPage")

                          def page(request, name):
                              if not storage.wikiname_re.match(name):
                                  raise NotFound('/%s' % name)
                              return wiki.render_page(name)




Monday, 22 August 2011

MAGIC: no magic
werkzeug
                         /PageName/edit

                def edit_form(request, name):
                    return wiki.render_edit_form(name)

                def edit(request, name):
                    if request.form.get('submit'):
                        wiki.store_page(name, request.form['content'])
                    raise RequestRedirect("/%s" % name)




Monday, 22 August 2011
werkzeug
                                   URL mapping

       url_map = Map([
           Rule('/', endpoint=index, methods=['GET']),
           Rule('/<name>/edit', endpoint=edit_form, methods=['GET']),
           Rule('/<name>/edit', endpoint=edit, methods=['POST']),
           Rule('/<name>', endpoint=page, methods=['GET']),
       ])




Monday, 22 August 2011

RESTful: Map with Rules having URL and request method type mapping to endpoint callable
werkzeug
                   application and serving
    def application(environ, start_response):
        request = Request(environ)
        urls = url_map.bind_to_environ(environ)
        try:
             m, args = urls.match()
             r = m(request, **args)
             start_response('200 OK', [('Content-Type', 'text/html')])
             return r
        except HTTPException, e:
             return e(environ, start_response)

    if __name__ == "__main__":
        from werkzeug.serving import run_simple
        run_simple('localhost', 8080, application)



Monday, 22 August 2011

EASE OF USE: simple except writing my own WSGI app; EXTRAS: contributed code for sessions, ATOM, secure cookies and caching;
WSGI: had to write my own, but it was short
10.




                         a#en.io


Monday, 22 August 2011
Monday, 22 August 2011

Quite a different approach to things. Even though this is going against my "no templates" rule there is no way to use this framework
*without* using its templating...
aspen.io
                                               index.html

                from aspen import Response
                ^L
                raise Response(301, headers={'Location': '/FrontPage'})
                ^L




Monday, 22 August 2011

EASE OF USE: wildly different and some things are inexplicably hard; EXTRAS: static file serving; MAGIC: the page break stuff
(initialisation, action, template) hurts understandability. Oh, aspen, "raise Response(301, headers={'Location': path+'/'})" really??!?
aspen.io
                         directory structure

                                                   .aspen
                                                   index.html
                                                   %name/index.html
                                                   %name/edit




Monday, 22 August 2011

Aspen sees the %name directory and assigns the first part of the URL to the path variable “name”. Extra modules (storage, html, ...) have
to be placed in the hidden ".aspen" folder.
aspen.io
                         %name/index.html
                           from aspen import Response
                           import storage

                           wiki = storage.Storage('contents')
                           ^L
                           name = request.path['name']
                           if not storage.wikiname_re.match(name):
                               raise Response(404, '/%s' % name)
                           page = wiki.render_page(name)
                           ^L
                           {{ page }}




Monday, 22 August 2011

I had to go off to the Tornado docs to figure how to HTML escaping was handled (though I probably could've just tried and discovered
that it's not escaped by default). Not escaped HTML by default? RESTful: URLs map to filesystem paths
aspen.io
                                         %name/edit
             from aspen import Response
             import storage

             wiki = storage.Storage('contents')
             ^L
             name = request.path['name']
             if request.method == 'POST':
                 if 'submit' in request.body:
                     wiki.store_page(name, request.body.one('content'))
                 raise Response(301, headers={'Location': '/' + name})
             page = wiki.render_edit_form(name)
             response.headers.set('Content-Type', 'text/html')
             ^L
             {{ page }}




Monday, 22 August 2011

The documentation is *seriously* thin. I found myself having to read the source to: 1. figure out what the request.body thing was and how
to use it, and 2. figure out how to get a redirect back to the client. Oh, text/html isn’t the DEFAULT? RESTful: manually test for request
method type in handler
aspen.io
                                      serving HTTP

                                              from aspen import server
                                              server.main()




Monday, 22 August 2011

Put this in a file in the aspen site’s directory. Or use the aspen command-line program. WSGI: appears to be no way to get a WSGI app.
I’ve given aspen a rough time here because it’s not really suitable for my purposes, but I can see how it’d actually be really nice to develop
a simple website with.
So, how do they rank?

                   • Let’s score them
                   • +1 or -1 on each of:
                         Documentation, Ease of Use, Avoiding
                         Magic, RESTful, WSGI, Python 3, PyPy and
                         SLOC

                           cgi+wsgiref:0, aspen.io:0, bobo:0, bottle:0, cherrypy:0,
                                flask:0, itty:0, pesto:0, web.py:0, werkzeug:0


Monday, 22 August 2011
Documentation

                   • good, downloadable: cgi+wsgiref, flask,
                         werkzeug, bottle
                   • good: pesto
                   • could be better: web.py, itty, cherrypy
                   • bad: bobo, aspen.io
                            cgi+wsgiref:1, aspen.io:-1, bobo:-1, bottle:1, cherrypy:0,
                                 flask:1, itty:0, pesto:1, web.py:0, werkzeug:1


Monday, 22 August 2011

No “hello world” ... ? Hard to find Redirect/NotFound, RESTful routing, form access, wsgi application?
Ease of Use

                   • SIMPLE: pesto, itty, flask, web.py, bottle
                   • Could get complicated: werkzeug, cherrypy
                   • Complicated: aspen.io, bobo
                   • Just Plain Difficult: cgi+wsgiref
                         cgi+wsgiref:0, aspen.io:-2, bobo:-2, bottle:2, cherrypy:0,
                              flask:2, itty:1, pesto:2, web.py:1, werkzeug:1


Monday, 22 August 2011
Avoiding Magic

                   • none: cgi+wsgiref, werkzeug, pesto
                   • WSGI not apparent: itty
                   • module-level: bottle, flask, web.py, cherrypy
                   • just plain odd: aspen.io, bobo
                         cgi+wsgiref:1, aspen.io:-2, bobo:-3, bottle:1, cherrypy:-1,
                              flask:1, itty:1, pesto:3, web.py:0, werkzeug:2


Monday, 22 August 2011
RESTful

                   • bottle, flask, itty, pesto, web.py, werkzeug
                   • not obvious: bobo, cherrypy
                   • manual test method: aspen.io
                   • NO: cgi+wsgiref
                           cgi+wsgiref:0, aspen.io:-3, bobo:-3, bottle:2, cherrypy:-1,
                                flask:2, itty:2, pesto:4, web.py:1, werkzeug:3


Monday, 22 August 2011

This actually dictated the structure of my wiki code more than anything else.
RESTful

                   • Ignoring aspen.io there was 2 approaches:
                    • decorate callable
                    • listing maps directly to callable


Monday, 22 August 2011
WSGI

               • bottle, flask, pesto, cgi+wsgiref
               • not obvious: itty, web.py, cherrypy
               • manual: werkzeug
               • unknown: bobo
               • NO: aspen.io
                         cgi+wsgiref:1, aspen.io:-4, bobo:-4, bottle:3, cherrypy: -1,
                              flask:3, itty:2, pesto:5, web.py:1, werkzeug:2


Monday, 22 August 2011
Python 3


                   • yes: cgi+wsgiref, bottle, cherrypy
                   • no: bobo, itty, web.py, pesto, werkzeug, flask,
                         aspen.io


                           cgi+wsgiref:2, aspen.io:-5, bobo:-5, bottle:4, cherrypy:0,
                                flask:2, itty:1, pesto:4, web.py:0, werkzeug:1


Monday, 22 August 2011
PyPy




                          cgi+wsgiref:3, aspen.io:-4, bobo:-4, bottle:5, cherrypy:1,
                               flask:3, itty:2, pesto:5, web.py:1, werkzeug:2


Monday, 22 August 2011

PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
PyPy

                                 THEY

                          cgi+wsgiref:3, aspen.io:-4, bobo:-4, bottle:5, cherrypy:1,
                               flask:3, itty:2, pesto:5, web.py:1, werkzeug:2


Monday, 22 August 2011

PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
PyPy

                                 THEY
                                  ALL
                          cgi+wsgiref:3, aspen.io:-4, bobo:-4, bottle:5, cherrypy:1,
                               flask:3, itty:2, pesto:5, web.py:1, werkzeug:2


Monday, 22 August 2011

PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
PyPy

                                 THEY
                                  ALL
                                  RUN
                          cgi+wsgiref:3, aspen.io:-4, bobo:-4, bottle:5, cherrypy:1,
                               flask:3, itty:2, pesto:5, web.py:1, werkzeug:2


Monday, 22 August 2011

PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
wiki.py line/byte count
                                        38/905 bottle
                                        38/982 itty
                                        41/1061 flask
                                        43/1148 bobo
                                        45/1071 web.py
                                        45/1301 pesto
                                        51/1267 cherrypy
                                        55/1730 werkzeug
                                        77/2126 aspen.io
                                        78/2142 cgi+wsgiref



                         cgi+wsgiref:2, aspen.io:-5, bobo:-2, bottle:6, cherrypy:1,
                              flask:4, itty:3, pesto:6, web.py:1, werkzeug:2


Monday, 22 August 2011
framework line count
                                         593 itty
                                         2441 cgi+wsgiref
                                         2697 bottle
                                         5600 aspen.io
                                         6649 pesto
                                         8044 bobo
                                         10198 web.py
                                         16563 werkzeug
                                         27768 cherrypy
                                         32245 flask



                          cgi+wsgiref:3, aspen.io:-5, bobo:-2, bottle:7, cherrypy:0,
                               flask:3, itty:4, pesto:6, web.py:1, werkzeug:2


Monday, 22 August 2011
The Official* Scores
                             Rank                     Framework        Score
                                 1                       bottle          7
                                 2                       pesto           6
                                 3                        itty           4
                                 4                 flask, cgi+wsgiref     3
                                 5                    werkzeug           2
                                 6                      web.py           1
                                 7                     cherrypy          0
                                 8                       bobo           -2
                                 9                     aspen.io         -5
Monday, 22 August 2011

The scores, as determined solely by my own criteria and weighting.
OMGz0R a winner?


                                      Bo!le!


Monday, 22 August 2011

Simple to use, great docs, RESTful and WSGI, Python 3. A single file and compact application code. Awesome!
Extras
            Framework     Sessions Caching                Other
            cgi+wsgiref      ✗       ✗
              aspen.io       ✗       ✗       static file serving
               bobo          ✗       ✗
               bottle        ✗       ✗       templating, adapters, utilities
             cherrypy       ✓        ✓       static file serving, plugins
                flask        ✓        ✓       debugger, shell, werkzeug
                itty        ✗        ✗
               pesto        ✓        ✓       utilities for testing
               web.py       ✓        ✗       openid, templating, form & db
             werkzeug       ✓        ✓       ATOM, secure cookies
Monday, 22 August 2011

More Related Content

What's hot

[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint [D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint NAVER D2
 
PFsense 방화벽 소개
PFsense 방화벽 소개PFsense 방화벽 소개
PFsense 방화벽 소개ajj007
 
The Parquet Format and Performance Optimization Opportunities
The Parquet Format and Performance Optimization OpportunitiesThe Parquet Format and Performance Optimization Opportunities
The Parquet Format and Performance Optimization OpportunitiesDatabricks
 
Optimizing Hive Queries
Optimizing Hive QueriesOptimizing Hive Queries
Optimizing Hive QueriesOwen O'Malley
 
An introduction to Apache Thrift
An introduction to Apache ThriftAn introduction to Apache Thrift
An introduction to Apache ThriftMike Frampton
 
SQL-on-Hadoop Tutorial
SQL-on-Hadoop TutorialSQL-on-Hadoop Tutorial
SQL-on-Hadoop TutorialDaniel Abadi
 
Cubing and Metrics in SQL, oh my!
Cubing and Metrics in SQL, oh my!Cubing and Metrics in SQL, oh my!
Cubing and Metrics in SQL, oh my!Julian Hyde
 
Investing the Effects of Overcommitting YARN resources
Investing the Effects of Overcommitting YARN resourcesInvesting the Effects of Overcommitting YARN resources
Investing the Effects of Overcommitting YARN resourcesDataWorks Summit/Hadoop Summit
 
OracleStore: A Highly Performant RawStore Implementation for Hive Metastore
OracleStore: A Highly Performant RawStore Implementation for Hive MetastoreOracleStore: A Highly Performant RawStore Implementation for Hive Metastore
OracleStore: A Highly Performant RawStore Implementation for Hive MetastoreDataWorks Summit
 
Unity and WebSockets
Unity and WebSocketsUnity and WebSockets
Unity and WebSocketsJosh Glover
 
Webscale PostgreSQL - JSONB and Horizontal Scaling Strategies
Webscale PostgreSQL - JSONB and Horizontal Scaling StrategiesWebscale PostgreSQL - JSONB and Horizontal Scaling Strategies
Webscale PostgreSQL - JSONB and Horizontal Scaling StrategiesJonathan Katz
 
Apache Kylin: Speed Up Cubing with Apache Spark with Luke Han and Shaofeng Shi
 Apache Kylin: Speed Up Cubing with Apache Spark with Luke Han and Shaofeng Shi Apache Kylin: Speed Up Cubing with Apache Spark with Luke Han and Shaofeng Shi
Apache Kylin: Speed Up Cubing with Apache Spark with Luke Han and Shaofeng ShiDatabricks
 
The evolution of Apache Calcite and its Community
The evolution of Apache Calcite and its CommunityThe evolution of Apache Calcite and its Community
The evolution of Apache Calcite and its CommunityJulian Hyde
 
RESTful API Design Best Practices Using ASP.NET Web API
RESTful API Design Best Practices Using ASP.NET Web APIRESTful API Design Best Practices Using ASP.NET Web API
RESTful API Design Best Practices Using ASP.NET Web API💻 Spencer Schneidenbach
 
Bytecode Manipulation with a Java Agent and Byte Buddy
Bytecode Manipulation with a Java Agent and Byte BuddyBytecode Manipulation with a Java Agent and Byte Buddy
Bytecode Manipulation with a Java Agent and Byte BuddyKoichi Sakata
 
InfluxDB IOx Tech Talks: Query Engine Design and the Rust-Based DataFusion in...
InfluxDB IOx Tech Talks: Query Engine Design and the Rust-Based DataFusion in...InfluxDB IOx Tech Talks: Query Engine Design and the Rust-Based DataFusion in...
InfluxDB IOx Tech Talks: Query Engine Design and the Rust-Based DataFusion in...InfluxData
 
Alles was Sie über HCL Notes 14 wissen müssen
Alles was Sie über HCL Notes 14 wissen müssenAlles was Sie über HCL Notes 14 wissen müssen
Alles was Sie über HCL Notes 14 wissen müssenpanagenda
 

What's hot (20)

[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint [D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
[D2] java 애플리케이션 트러블 슈팅 사례 & pinpoint
 
PFsense 방화벽 소개
PFsense 방화벽 소개PFsense 방화벽 소개
PFsense 방화벽 소개
 
The Parquet Format and Performance Optimization Opportunities
The Parquet Format and Performance Optimization OpportunitiesThe Parquet Format and Performance Optimization Opportunities
The Parquet Format and Performance Optimization Opportunities
 
Optimizing Hive Queries
Optimizing Hive QueriesOptimizing Hive Queries
Optimizing Hive Queries
 
An introduction to Apache Thrift
An introduction to Apache ThriftAn introduction to Apache Thrift
An introduction to Apache Thrift
 
SQL-on-Hadoop Tutorial
SQL-on-Hadoop TutorialSQL-on-Hadoop Tutorial
SQL-on-Hadoop Tutorial
 
Cubing and Metrics in SQL, oh my!
Cubing and Metrics in SQL, oh my!Cubing and Metrics in SQL, oh my!
Cubing and Metrics in SQL, oh my!
 
Investing the Effects of Overcommitting YARN resources
Investing the Effects of Overcommitting YARN resourcesInvesting the Effects of Overcommitting YARN resources
Investing the Effects of Overcommitting YARN resources
 
Apache Ranger
Apache RangerApache Ranger
Apache Ranger
 
OracleStore: A Highly Performant RawStore Implementation for Hive Metastore
OracleStore: A Highly Performant RawStore Implementation for Hive MetastoreOracleStore: A Highly Performant RawStore Implementation for Hive Metastore
OracleStore: A Highly Performant RawStore Implementation for Hive Metastore
 
Unity and WebSockets
Unity and WebSocketsUnity and WebSockets
Unity and WebSockets
 
Webscale PostgreSQL - JSONB and Horizontal Scaling Strategies
Webscale PostgreSQL - JSONB and Horizontal Scaling StrategiesWebscale PostgreSQL - JSONB and Horizontal Scaling Strategies
Webscale PostgreSQL - JSONB and Horizontal Scaling Strategies
 
Apache Kylin: Speed Up Cubing with Apache Spark with Luke Han and Shaofeng Shi
 Apache Kylin: Speed Up Cubing with Apache Spark with Luke Han and Shaofeng Shi Apache Kylin: Speed Up Cubing with Apache Spark with Luke Han and Shaofeng Shi
Apache Kylin: Speed Up Cubing with Apache Spark with Luke Han and Shaofeng Shi
 
The evolution of Apache Calcite and its Community
The evolution of Apache Calcite and its CommunityThe evolution of Apache Calcite and its Community
The evolution of Apache Calcite and its Community
 
Prestogres internals
Prestogres internalsPrestogres internals
Prestogres internals
 
RESTful API Design Best Practices Using ASP.NET Web API
RESTful API Design Best Practices Using ASP.NET Web APIRESTful API Design Best Practices Using ASP.NET Web API
RESTful API Design Best Practices Using ASP.NET Web API
 
Bytecode Manipulation with a Java Agent and Byte Buddy
Bytecode Manipulation with a Java Agent and Byte BuddyBytecode Manipulation with a Java Agent and Byte Buddy
Bytecode Manipulation with a Java Agent and Byte Buddy
 
InfluxDB IOx Tech Talks: Query Engine Design and the Rust-Based DataFusion in...
InfluxDB IOx Tech Talks: Query Engine Design and the Rust-Based DataFusion in...InfluxDB IOx Tech Talks: Query Engine Design and the Rust-Based DataFusion in...
InfluxDB IOx Tech Talks: Query Engine Design and the Rust-Based DataFusion in...
 
Alles was Sie über HCL Notes 14 wissen müssen
Alles was Sie über HCL Notes 14 wissen müssenAlles was Sie über HCL Notes 14 wissen müssen
Alles was Sie über HCL Notes 14 wissen müssen
 
The Impala Cookbook
The Impala CookbookThe Impala Cookbook
The Impala Cookbook
 

Viewers also liked

Bottle - Python Web Microframework (english)
Bottle - Python Web Microframework (english)Bottle - Python Web Microframework (english)
Bottle - Python Web Microframework (english)Markus Zapke-Gründemann
 
GDC 2015 - Low-latency Multiplayer Gaming with AWS
GDC 2015 - Low-latency Multiplayer Gaming with AWS GDC 2015 - Low-latency Multiplayer Gaming with AWS
GDC 2015 - Low-latency Multiplayer Gaming with AWS Nate Wiger
 
Scrum Team Workshop Training Agenda
Scrum Team Workshop Training AgendaScrum Team Workshop Training Agenda
Scrum Team Workshop Training AgendaKarlo Magdic
 
Why I liked Mezzanine CMS
Why I liked Mezzanine CMSWhy I liked Mezzanine CMS
Why I liked Mezzanine CMSRenyi Khor
 
How we use Bottle and Elasticsearch
How we use Bottle and ElasticsearchHow we use Bottle and Elasticsearch
How we use Bottle and Elasticsearchswee meng ng
 
Scalable Gaming with AWS - GDC 2014
Scalable Gaming with AWS - GDC 2014Scalable Gaming with AWS - GDC 2014
Scalable Gaming with AWS - GDC 2014Nate Wiger
 

Viewers also liked (6)

Bottle - Python Web Microframework (english)
Bottle - Python Web Microframework (english)Bottle - Python Web Microframework (english)
Bottle - Python Web Microframework (english)
 
GDC 2015 - Low-latency Multiplayer Gaming with AWS
GDC 2015 - Low-latency Multiplayer Gaming with AWS GDC 2015 - Low-latency Multiplayer Gaming with AWS
GDC 2015 - Low-latency Multiplayer Gaming with AWS
 
Scrum Team Workshop Training Agenda
Scrum Team Workshop Training AgendaScrum Team Workshop Training Agenda
Scrum Team Workshop Training Agenda
 
Why I liked Mezzanine CMS
Why I liked Mezzanine CMSWhy I liked Mezzanine CMS
Why I liked Mezzanine CMS
 
How we use Bottle and Elasticsearch
How we use Bottle and ElasticsearchHow we use Bottle and Elasticsearch
How we use Bottle and Elasticsearch
 
Scalable Gaming with AWS - GDC 2014
Scalable Gaming with AWS - GDC 2014Scalable Gaming with AWS - GDC 2014
Scalable Gaming with AWS - GDC 2014
 

Similar to Web micro-framework BATTLE!

Ontology Development Kit: Bio-Ontologies 2019
Ontology Development Kit: Bio-Ontologies 2019Ontology Development Kit: Bio-Ontologies 2019
Ontology Development Kit: Bio-Ontologies 2019Chris Mungall
 
Integrating ECM (WebCenter Content) with your Enterprise! 5 Tips to Try, 5 Tr...
Integrating ECM (WebCenter Content) with your Enterprise! 5 Tips to Try, 5 Tr...Integrating ECM (WebCenter Content) with your Enterprise! 5 Tips to Try, 5 Tr...
Integrating ECM (WebCenter Content) with your Enterprise! 5 Tips to Try, 5 Tr...Brian Huff
 
Open Source Junction: Apache Wookie and W3C Widgets
Open Source Junction: Apache Wookie and W3C WidgetsOpen Source Junction: Apache Wookie and W3C Widgets
Open Source Junction: Apache Wookie and W3C Widgetsscottw
 
Building OBO Foundry ontology using semantic web tools
Building OBO Foundry ontology using semantic web toolsBuilding OBO Foundry ontology using semantic web tools
Building OBO Foundry ontology using semantic web toolsMelanie Courtot
 
How and Why you can and should Participate in Open Source Projects (AMIS, Sof...
How and Why you can and should Participate in Open Source Projects (AMIS, Sof...How and Why you can and should Participate in Open Source Projects (AMIS, Sof...
How and Why you can and should Participate in Open Source Projects (AMIS, Sof...Lucas Jellema
 
State of jQuery June 2013 - Portland
State of jQuery June 2013 - PortlandState of jQuery June 2013 - Portland
State of jQuery June 2013 - Portlanddmethvin
 
Scaling with swagger
Scaling with swaggerScaling with swagger
Scaling with swaggerTony Tam
 
jQueryTO: State of jQuery March 2013
jQueryTO: State of jQuery March 2013jQueryTO: State of jQuery March 2013
jQueryTO: State of jQuery March 2013dmethvin
 
BarCamb Connotea by Ian Mulvany
BarCamb Connotea by Ian MulvanyBarCamb Connotea by Ian Mulvany
BarCamb Connotea by Ian MulvanyIan Mulvany
 
Social dev camp_2011
Social dev camp_2011Social dev camp_2011
Social dev camp_2011Craig Ulliott
 
jQuery 1.4-1.6 Best new features
jQuery 1.4-1.6 Best new featuresjQuery 1.4-1.6 Best new features
jQuery 1.4-1.6 Best new featuresRyan Blunden
 
Drupal and Apache Stanbol
Drupal and Apache StanbolDrupal and Apache Stanbol
Drupal and Apache StanbolAlkuvoima
 
Positioning Yourself for the Future
Positioning Yourself for the FuturePositioning Yourself for the Future
Positioning Yourself for the FutureScott Lowe
 
Sean coates fifty things and tricks, confoo 2011
Sean coates fifty things and tricks, confoo 2011Sean coates fifty things and tricks, confoo 2011
Sean coates fifty things and tricks, confoo 2011Bachkoutou Toutou
 
OOR Architecture - Towards a Network of Linked Ontology Repositories
OOR Architecture - Towards a Network of Linked Ontology RepositoriesOOR Architecture - Towards a Network of Linked Ontology Repositories
OOR Architecture - Towards a Network of Linked Ontology RepositoriesKim Viljanen
 
Lean Startup with WebObjects
Lean Startup with WebObjectsLean Startup with WebObjects
Lean Startup with WebObjectsWO Community
 
Conferences andcommunity
Conferences andcommunityConferences andcommunity
Conferences andcommunityJeff Carouth
 

Similar to Web micro-framework BATTLE! (20)

Ontology Development Kit: Bio-Ontologies 2019
Ontology Development Kit: Bio-Ontologies 2019Ontology Development Kit: Bio-Ontologies 2019
Ontology Development Kit: Bio-Ontologies 2019
 
Integrating ECM (WebCenter Content) with your Enterprise! 5 Tips to Try, 5 Tr...
Integrating ECM (WebCenter Content) with your Enterprise! 5 Tips to Try, 5 Tr...Integrating ECM (WebCenter Content) with your Enterprise! 5 Tips to Try, 5 Tr...
Integrating ECM (WebCenter Content) with your Enterprise! 5 Tips to Try, 5 Tr...
 
Open Source Junction: Apache Wookie and W3C Widgets
Open Source Junction: Apache Wookie and W3C WidgetsOpen Source Junction: Apache Wookie and W3C Widgets
Open Source Junction: Apache Wookie and W3C Widgets
 
Building OBO Foundry ontology using semantic web tools
Building OBO Foundry ontology using semantic web toolsBuilding OBO Foundry ontology using semantic web tools
Building OBO Foundry ontology using semantic web tools
 
How and Why you can and should Participate in Open Source Projects (AMIS, Sof...
How and Why you can and should Participate in Open Source Projects (AMIS, Sof...How and Why you can and should Participate in Open Source Projects (AMIS, Sof...
How and Why you can and should Participate in Open Source Projects (AMIS, Sof...
 
State of jQuery June 2013 - Portland
State of jQuery June 2013 - PortlandState of jQuery June 2013 - Portland
State of jQuery June 2013 - Portland
 
Scaling with swagger
Scaling with swaggerScaling with swagger
Scaling with swagger
 
Lift Introduction
Lift IntroductionLift Introduction
Lift Introduction
 
jQueryTO: State of jQuery March 2013
jQueryTO: State of jQuery March 2013jQueryTO: State of jQuery March 2013
jQueryTO: State of jQuery March 2013
 
BarCamb Connotea by Ian Mulvany
BarCamb Connotea by Ian MulvanyBarCamb Connotea by Ian Mulvany
BarCamb Connotea by Ian Mulvany
 
Social dev camp_2011
Social dev camp_2011Social dev camp_2011
Social dev camp_2011
 
jQuery 1.4-1.6 Best new features
jQuery 1.4-1.6 Best new featuresjQuery 1.4-1.6 Best new features
jQuery 1.4-1.6 Best new features
 
Drupal and Apache Stanbol
Drupal and Apache StanbolDrupal and Apache Stanbol
Drupal and Apache Stanbol
 
Positioning Yourself for the Future
Positioning Yourself for the FuturePositioning Yourself for the Future
Positioning Yourself for the Future
 
Sean coates fifty things and tricks, confoo 2011
Sean coates fifty things and tricks, confoo 2011Sean coates fifty things and tricks, confoo 2011
Sean coates fifty things and tricks, confoo 2011
 
caseywest
caseywestcaseywest
caseywest
 
caseywest
caseywestcaseywest
caseywest
 
OOR Architecture - Towards a Network of Linked Ontology Repositories
OOR Architecture - Towards a Network of Linked Ontology RepositoriesOOR Architecture - Towards a Network of Linked Ontology Repositories
OOR Architecture - Towards a Network of Linked Ontology Repositories
 
Lean Startup with WebObjects
Lean Startup with WebObjectsLean Startup with WebObjects
Lean Startup with WebObjects
 
Conferences andcommunity
Conferences andcommunityConferences andcommunity
Conferences andcommunity
 

More from Richard Jones

Introduction to Game Programming
Introduction to Game ProgrammingIntroduction to Game Programming
Introduction to Game ProgrammingRichard Jones
 
Message Queueing - by an MQ noob
Message Queueing - by an MQ noobMessage Queueing - by an MQ noob
Message Queueing - by an MQ noobRichard Jones
 
Intro to Game Programming
Intro to Game ProgrammingIntro to Game Programming
Intro to Game ProgrammingRichard Jones
 
Introduction to Game Programming Tutorial
Introduction to Game Programming TutorialIntroduction to Game Programming Tutorial
Introduction to Game Programming TutorialRichard Jones
 
State of Python (2010)
State of Python (2010)State of Python (2010)
State of Python (2010)Richard Jones
 
What's New In Python 2.6
What's New In Python 2.6What's New In Python 2.6
What's New In Python 2.6Richard Jones
 
What's New In Python 2.5
What's New In Python 2.5What's New In Python 2.5
What's New In Python 2.5Richard Jones
 
What's New In Python 2.4
What's New In Python 2.4What's New In Python 2.4
What's New In Python 2.4Richard Jones
 
Tkinter Does Not Suck
Tkinter Does Not SuckTkinter Does Not Suck
Tkinter Does Not SuckRichard Jones
 

More from Richard Jones (12)

Angboard
AngboardAngboard
Angboard
 
Don't do this
Don't do thisDon't do this
Don't do this
 
Introduction to Game Programming
Introduction to Game ProgrammingIntroduction to Game Programming
Introduction to Game Programming
 
Message Queueing - by an MQ noob
Message Queueing - by an MQ noobMessage Queueing - by an MQ noob
Message Queueing - by an MQ noob
 
Message queueing
Message queueingMessage queueing
Message queueing
 
Intro to Game Programming
Intro to Game ProgrammingIntro to Game Programming
Intro to Game Programming
 
Introduction to Game Programming Tutorial
Introduction to Game Programming TutorialIntroduction to Game Programming Tutorial
Introduction to Game Programming Tutorial
 
State of Python (2010)
State of Python (2010)State of Python (2010)
State of Python (2010)
 
What's New In Python 2.6
What's New In Python 2.6What's New In Python 2.6
What's New In Python 2.6
 
What's New In Python 2.5
What's New In Python 2.5What's New In Python 2.5
What's New In Python 2.5
 
What's New In Python 2.4
What's New In Python 2.4What's New In Python 2.4
What's New In Python 2.4
 
Tkinter Does Not Suck
Tkinter Does Not SuckTkinter Does Not Suck
Tkinter Does Not Suck
 

Recently uploaded

Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Mark Goldstein
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch TuesdayIvanti
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesBernd Ruecker
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructureitnewsafrica
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Alkin Tezuysal
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality AssuranceInflectra
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Nikki Chapple
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 

Recently uploaded (20)

Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
Arizona Broadband Policy Past, Present, and Future Presentation 3/25/24
 
2024 April Patch Tuesday
2024 April Patch Tuesday2024 April Patch Tuesday
2024 April Patch Tuesday
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architectures
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
Unleashing Real-time Insights with ClickHouse_ Navigating the Landscape in 20...
 
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance[Webinar] SpiraTest - Setting New Standards in Quality Assurance
[Webinar] SpiraTest - Setting New Standards in Quality Assurance
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 

Web micro-framework BATTLE!

  • 2. web micro-framework Monday, 22 August 2011
  • 3. web micro-framework Ba!le! Monday, 22 August 2011
  • 5. aka Richard surveys the landscape so you (probably) don’t have to Monday, 22 August 2011
  • 6. Introduction Monday, 22 August 2011 I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
  • 7. Introduction • I write code for a telco Monday, 22 August 2011 I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
  • 8. Introduction • I write code for a telco • Lots of discrete system applications Monday, 22 August 2011 I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
  • 9. Introduction • I write code for a telco • Lots of discrete system applications • Connected through HTTP Monday, 22 August 2011 I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
  • 10. Introduction • I write code for a telco • Lots of discrete system applications • Connected through HTTP • I write little HTTP servers all the time Monday, 22 August 2011 I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
  • 11. Introduction • I write code for a telco • Lots of discrete system applications • Connected through HTTP • I write little HTTP servers all the time • I’ve written half a dozen micro-frameworks Monday, 22 August 2011 I primarily develop systems for routing calls and texts, and provisioning phone numbers and things like that. I also maintain websites, but I’m not talking about those today. I’m talking about HTTP as a layer for connecting my systems.
  • 12. What’s IN? Monday, 22 August 2011 “standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get middleware. Downloadable docs for offline development are a real bonus.
  • 13. What’s IN? • easy to understand and use (docs, minimal magic, no surprises, terse) Monday, 22 August 2011 “standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get middleware. Downloadable docs for offline development are a real bonus.
  • 14. What’s IN? • easy to understand and use (docs, minimal magic, no surprises, terse) • HTTP request/response Monday, 22 August 2011 “standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get middleware. Downloadable docs for offline development are a real bonus.
  • 15. What’s IN? • easy to understand and use (docs, minimal magic, no surprises, terse) • HTTP request/response • URL routing (“RESTful”) Monday, 22 August 2011 “standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get middleware. Downloadable docs for offline development are a real bonus.
  • 16. What’s IN? • easy to understand and use (docs, minimal magic, no surprises, terse) • HTTP request/response • URL routing (“RESTful”) • WSGI Monday, 22 August 2011 “standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get middleware. Downloadable docs for offline development are a real bonus.
  • 17. What’s IN? • easy to understand and use (docs, minimal magic, no surprises, terse) • HTTP request/response • URL routing (“RESTful”) • WSGI • PyPy and Python 3... Monday, 22 August 2011 “standard lib”, access to form vars, redirection (with raise), OO/REST-ish app with GET/POST, WSGI so it’s easy to serve and get middleware. Downloadable docs for offline development are a real bonus.
  • 18. What’s OUT? Monday, 22 August 2011 I like dbapiext / explicitness; SQLAlchemy works plenty well; templating is handled by other things (though some frameworks have something built-in)
  • 19. What’s OUT? • Object-Relational Manager (actually, any sort of DB wrapper) Monday, 22 August 2011 I like dbapiext / explicitness; SQLAlchemy works plenty well; templating is handled by other things (though some frameworks have something built-in)
  • 20. What’s OUT? • Object-Relational Manager (actually, any sort of DB wrapper) • Template engine Monday, 22 August 2011 I like dbapiext / explicitness; SQLAlchemy works plenty well; templating is handled by other things (though some frameworks have something built-in)
  • 21. Who’s out? • Mega Frameworks: django, grok, pyramid, web2py, zope • CubicWeb (never heard of it) • milla (too young / incomplete) • pump (too late to include) • routes+webob (too complicated) Monday, 22 August 2011
  • 22. WIKI Monday, 22 August 2011 In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of brevity.
  • 23. WIKI • page view (GET /PageName) Monday, 22 August 2011 In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of brevity.
  • 24. WIKI • page view (GET /PageName) • creation, editing (GET & POST /edit) Monday, 22 August 2011 In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of brevity.
  • 25. WIKI • page view (GET /PageName) • creation, editing (GET & POST /edit) • redirect (GET /) Monday, 22 August 2011 In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of brevity.
  • 26. WIKI • page view (GET /PageName) • creation, editing (GET & POST /edit) • redirect (GET /) • not found (GET /favicon.ico) Monday, 22 August 2011 In the following there's some core wiki code which is used to manipulate the wiki storage and render pages. When I wrote the wiki apps I also implemented history and reversion to reinforce each framework’s approach, but I’ve left that out of these slides in the interests of brevity.
  • 27. The Contenders • cgi+wsgiref • aspen.io • bobo • bottle • cherrypy • flask • itty • pesto • web.py • werkzeug Monday, 22 August 2011 10 frameworks, cgi+wsgiref is my baseline
  • 28. The Criteria • documentation • ease of use • magic • RESTful • WSGI • Python 3 • PyPy • amount of typing • size of the framework Monday, 22 August 2011 Just to recap, this is what I’m looking for.
  • 29. 1. cgi&wsgiref Monday, 22 August 2011 DOCUMENTATION: just the standard library, EASE OF USE: a bit of work but not hard, EXTRAS: none, MAGIC: no, RESTful: no, WSGI: yes
  • 30. cgi+wsgiref simple WSGI app class NotFound(Exception): pass class Redirect(Exception): def __init__(self, location): self.location = location def app(environ, start_response): headers = [('Content-type', 'text/html')] status = '200 OK' try: for p, m, c in urls: if m != environ['REQUEST_METHOD']: continue m = re.match(p, environ['PATH_INFO']) if m is None: continue response = c(environ, **m.groupdict()) raise NotFound except NotFound: status = '404 Not Found' response = 'Not found: %s' % environ['PATH_INFO'] except Redirect, e: status = '301 Moved Permanently' headers.append(('Location', e.location)) response = '' start_response(status, headers) return response Monday, 22 August 2011 No top-level app in wsgiref, so I had to write my own. This, by the way, is why I've written a half a dozen web frameworks.
  • 31. cgi+wsgiref simple WSGI app def app(environ, start_response): headers = [('Content-type', 'text/html')] status = '200 OK' try: for p, m, c in urls: if m != environ['REQUEST_METHOD']: continue m = re.match(p, environ['PATH_INFO']) if m is None: continue response = c(environ, **m.groupdict()) raise NotFound except NotFound: status = '404 Not Found' response = 'Not found: %s' % environ['PATH_INFO'] except Redirect, e: status = '301 Moved Permanently' headers.append(('Location', e.location)) response = '' start_response(status, headers) return response Monday, 22 August 2011 These are the WSGI bits.
  • 32. cgi+wsgiref simple WSGI app def app(environ, start_response): headers = [('Content-type', 'text/html')] status = '200 OK' try: for p, m, c in urls: if m != environ['REQUEST_METHOD']: continue m = re.match(p, environ['PATH_INFO']) if m is None: continue response = c(environ, **m.groupdict()) raise NotFound except NotFound: status = '404 Not Found' response = 'Not found: %s' % environ['PATH_INFO'] except Redirect, e: status = '301 Moved Permanently' headers.append(('Location', e.location)) response = '' start_response(status, headers) return response Monday, 22 August 2011 Given a list of potential actions containing a URL pattern to match, a request method and a callable, we see which one to call, if any.
  • 33. cgi+wsgiref actions def index(environ): raise Redirect('/FrontPage') def page(environ, name): if not storage.wikiname_re.match(name): raise NotFound return wiki.render_page(name) def edit_form(environ, name): return wiki.render_edit_form(name) def edit(environ, name): fields = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ) if 'submit' in fields: wiki.store_page(name, fields.getfirst('content')) raise Redirect("/%s" % name) Monday, 22 August 2011 Handling forms is manual, of course.
  • 34. cgi+wsgiref url mapping urls = [ ('^/$', 'GET', index), ('^/(?P<name>w+)/?$', 'GET', page), ('^/(?P<name>w+)/edit$', 'GET', edit_form), ('^/(?P<name>w+)/edit$', 'POST', edit), ] Monday, 22 August 2011 Finally we construct our list of potential handlers.
  • 35. cgi+wsgiref serving HTTP server = wsgiref.simple_server.make_server('127.0.0.1', 8080, app) server.serve_forever() Monday, 22 August 2011
  • 36. 2. bobo Monday, 22 August 2011 bobo started out life as an awesome object publisher (get attributes and items) and then it became Zope. sadly as Zope grew, so bobo was tainted and now it’s not the simple publisher it once was. DOCUMENTATION: limited and focused on The Bobo Way - EASE OF USE: it's just not straight-forward, can't just create an app and publish it - I must publish a module file!?!
  • 37. bobo index @bobo.query('/') def index(): return bobo.redirect("/FrontPage") Monday, 22 August 2011 Simple enough. MAGIC: bobo ... that’s the module. Decorating for scanning. I did have some transient, unexplained trouble with this redirect ending up at “/FrontPage/”. There also doesn’t appear to be a redirect exception.
  • 38. bobo /PageName @bobo.subroute('/:name?', scan=True) class Page(object): def __init__(self, request, name=None): if not storage.wikiname_re.match(name): raise bobo.NotFound('/%s' % name) self.name = name @bobo.query('') def index(self): return wiki.render_page(name) Monday, 22 August 2011 RESTful: quite confusing bobo.route()/bobo.subroute() request method in decorator. So here we’re setting up a “sub”route directing the URLs starting with that pattern to the decorated class (any callable will do). Instances of the class will be created being passed the name from the URL pattern. the docs seemed to imply that @bobo.query with no args would expose the function named - but that didn’t work. scan? wha? well, without it the decorations in the class aren’t ... known.
  • 39. bobo index AttributeError: Page instance has no attribute 'bobo_response' Monday, 22 August 2011 without scan I get this bizarro error
  • 40. bobo /PageName/edit @bobo.query('/edit', 'GET') def edit(self): return wiki.render_edit_form(name) @bobo.post('/edit', 'POST') def do_edit(self, content, submit=False): if submit: wiki.store_page(self.name, content) return bobo.redirect("/%s" % self.name) Monday, 22 August 2011 Again the docs seemed to lie. @post needs that “POST” argument or it gets both GET AND POST. PRO: uses WebOb request/response.
  • 41. bobo running the app if __name__ == "__main__": import boboserver boboserver.server(['-f', __file__]) Monday, 22 August 2011 And then we serve the application by pointing boboserver at the what now? We publish the module file? This is pretty wacky. WSGI: kinda (paste deploy) - no idea how to plug into my own WSGI server. I poked and poked at the source and could not figure how to get a WSGI application.
  • 42. 3. cherrypy Monday, 22 August 2011 OK, so from the first example this looks to be a little different to others - kinda close to bobo by publishing objects and methods. But it’s not RESTish. DOCS: pro: uses Sphinx, con: a bit light-on in some places. The default dispatcher doesn't make RESTish control easy so I'm using a RESTish MethodDispatcher, but the only documentation for it is a simple example which doesn't really explain things in much detail. I guessed at some of the semantics, and it worked!
  • 43. cherrypy index @cherrypy.popargs('name') class Wiki(object): exposed = True def GET(self, name=None): if not name: raise cherrypy.HTTPRedirect("/FrontPage") if not storage.wikiname_re.match(name): raise cherrypy.NotFound('/%s' % name) return wiki.render_page(name) edit = WikiEdit() Monday, 22 August 2011 Docs search found me the HTTPRedirect and NotFound which is nice.Yay Sphinx! “exposed” may be done in a bunch of different ways, not explained clearly in one place AFAICT.
  • 44. cherrypy edit class WikiEdit(object): exposed = True def GET(self, name): return wiki.render_edit_form(name) def POST(self, name, content='', submit=None, cancel=None): if submit: wiki.store_page(name, content) raise cherrypy.HTTPRedirect("/%s" % name) Monday, 22 August 2011
  • 45. cherrypy the funky bit conf = { '/': { 'request.dispatch': cherrypy.dispatch.MethodDispatcher(), } } if __name__ == "__main__": cherrypy.quickstart(Wiki(), '/', conf) Monday, 22 August 2011 EASE OF USE: simple, though the config bit is a bit strange; ooh, automatic code reloading.; WSGI: I had to google and *then* look at the source to find out how to get a WSGI application: cherrypy.Application, cherrypy.request.wsgi_environ; MAGIC: minimal - just the cherrypy.request module global
  • 46. 4. web.py Monday, 22 August 2011 DOCUMENTATION: great website, easy to get started
  • 47. web.py index and /PageName class index: def GET(self): raise web.SeeOther("/FrontPage") class page: def GET(self, name): if not storage.wikiname_re.match(name): raise web.NotFound('/%s' % name) return wiki.render_page(name) Monday, 22 August 2011 RESTful: url mapping through ugly URLs tuple (url, name, url, name, ...) and handler named is class with request method methods GET(), POST(), ...
  • 48. web.py /PageName/edit class edit: def GET(self, name): return wiki.render_edit_form(name) def POST(self, name): f = web.input('content', submit=False, cancel=False) if f.submit: wiki.store_page(name, f.content) raise web.seeother("/%s" % name) Monday, 22 August 2011 DOCS: was hard to find how web.input() worked; EASE OF USE: simple enough class-per-URL-match; Form doesn't support submit buttons?! Forms are a bit of a fiddly way to get to simple form submitted data. For this simple case (very much like most of my uses) it's actually easier to access the submitted information directly without involving the Form class through web.input().
  • 49. web.py serving urls = ( '/', 'index', '/(.+)/edit', 'edit', '/(.+)', 'page', ) app = web.application(urls, globals()) if __name__ == "__main__": app.run() Monday, 22 August 2011 MAGIC: url mapping through names looked up in globals? Ugh. module-level form input() global; EXTRAS: session, openid, utilities, templating, form and db abstraction; WSGI: hard-to-find web.application(...).wsgifunc(*middleware)
  • 50. 5. bo!le Monday, 22 August 2011
  • 51. bottle index and /PageName @get('/') def index(): redirect("/FrontPage") @get('/:name') def display(name): if not storage.wikiname_re.match(name): abort(404, 'Not Found :/%s' % name) return wiki.render_page(name) Monday, 22 August 2011 PRO: SINGLE FILE! bottle.py; EASE OF USE: very simple; CON: MAGIC module global magic for request, response :-( PRO: Has first-class get(), post() as well as generic route() which configure a default app (you can also create other apps). DOCUMENTATION: very good, downloadable
  • 52. bottle /PageName/edit @get('/:name/edit') def edit_form(name): return wiki.render_edit_form(name) @post('/:name/edit') def edit(name): if request.POST.get('submit'): wiki.store_page(name, request.POST['content']) redirect("/%s" % name) Monday, 22 August 2011 RESTful: get(), post() decorators which take URL. request.POST has the posted form vars (there’s also request.params which combines GET and POST vars).
  • 53. bottle serving HTTP if __name__ == "__main__": run(host='127.0.0.1', port=8080) Monday, 22 August 2011 WSGI: Had to look in the source but bottle.default_app.wsgi is WSGI. Bottle also has the concept of an app stack, but I didn’t look into that and am not sure what the point is. EXTRAS: static files, adapters (eg werkzeug), SimpleTemplate, lots of convenience utilities
  • 54. from bottle import get, post, request, run, abort, redirect import storage wiki = storage.Storage('contents') @get('/') def index(): redirect("/FrontPage") @get('/:name') def display(name): if not storage.wikiname_re.match(name): abort(404, 'Not Found :/%s' % name) return wiki.render_page(name) @get('/:name/edit') def edit_form(name): return wiki.render_edit_form(name) @post('/:name/edit') def edit(name): if request.POST.get('submit'): wiki.store_page(name, request.POST['content']) redirect("/%s" % name) if __name__ == "__main__": run(host='127.0.0.1', port=8080) Monday, 22 August 2011 Seriously, that’s it. That’s all the overhead that bottle imposes when you write a web app with it!
  • 55. 6. i!y Monday, 22 August 2011 itty is really similar to bottle. They both aspire to the same simple API.
  • 56. itty index and /PageName @get('/') def index(request): raise Redirect("/FrontPage") @get('/(?P<name>w+)') def display(request, name): if not storage.wikiname_re.match(name): raise NotFound('/%s' % name) return wiki.render_page(name) Monday, 22 August 2011 get, Redirect and NotFound imported from itty (from itty import *) MAGIC module globals; DOCS: very little but straightforward and good examples; EASE OF USE: simple; RESTful: get(), post() decorators which take URL;
  • 57. itty /PageName/edit @get('/(?P<name>w+)/edit') def edit_form(request, name): return wiki.render_edit_form(name) @post('/(?P<name>w+)/edit') def edit(request, name): if request.POST.get('submit'): wiki.store_page(name, request.POST['content']) raise Redirect("/%s" % name) Monday, 22 August 2011 request.POST simple enough; CON: Redirects appear in the log with tracebacks??!?
  • 58. itty serving run_itty() Monday, 22 August 2011 WSGI: itty.handle_request (the docs don't make this clear ... and *why* request._environ ??)
  • 59. 7. flask Monday, 22 August 2011 Con: depends on two external libraries: the Jinja2 template engine and the Werkzeug WSGI toolkit. DOCS: very good, and downloadable, though werkzeug references were tricky
  • 60. flask index and /PageName app = Flask(__name__) @app.route("/") def index(): return redirect("/FrontPage") @app.route('/<name>') def display(name): if not storage.wikiname_re.match(name): raise NotFound('/%s' % name) return wiki.render_page(name) Monday, 22 August 2011 Where itty and bottle had a single default app that was being configured, here we explicitly create our app. CON: There appears to be no way to raise a redirect exception. NotFound needs to be imported from werkzeug directly. No way to return a NotFound result? EASE OF USE: requirement to fall back to werkzeug sometimes makes things tricky
  • 61. flask /PageName/edit @app.route('/<name>/edit', methods=['GET']) def edit_form(name): return wiki.render_edit_form(name) @app.route('/<name>/edit', methods=['POST']) def edit(name): if request.form.get('submit'): wiki.store_page(name, request.form['content']) return redirect("/%s" % name) Monday, 22 August 2011 Con: in the quickstart, it wasn't clear where request came from. MAGIC: request is module-level global from “flask”; RESTful: request method in app.route() decorator which also takes URL
  • 62. flask serving app.run(debug=True) Monday, 22 August 2011 Pro: debugger works really well; WSGI: the Flask object is a WSGI app; EXTRAS: werkzeug, test support, debugger, secure cookies, Jinja2, shell
  • 63. 8. pesto Monday, 22 August 2011
  • 64. pesto index and /PageName app = dispatcher_app() @app.match('/', 'GET') def index(request): return Response.redirect("/FrontPage") @app.match('/<name:unicode>', 'GET') def display(request, name): if not storage.wikiname_re.match(name): return Response.not_found('/%s' % name) page = wiki.render_page(name) return Response(page) Monday, 22 August 2011 explicit app creation - nice. DOCUMENTATION: good; EASE OF USE: simple; MAGIC: no magic whatsoever; RESTful: request method in app.match() decorator which also takes URL; unfortunately can’t just return the page contents from the function, have to wrap it in a Response.
  • 65. pesto /PageName/edit @app.match('/<name:unicode>/edit', 'GET') def edit_form(request, name): page = wiki.render_edit_form(name) return Response(page) @app.match('/<name:unicode>/edit', 'POST') def edit(request, name): if request.form.get('submit'): wiki.store_page(name, request.form['content']) return Response.redirect("/%s" % name) Monday, 22 August 2011 Very, very similar to itty and bottle... the type is not optional - I just assumed unicode would be the default.
  • 66. pesto serving httpd = simple_server.make_server('127.0.0.1', 8080, app) httpd.serve_forever() Monday, 22 August 2011 WSGI: app is a WSGI app; EXTRAS: utilities for testing, sessions, caching, wsgi
  • 67. 9. werkzeug Monday, 22 August 2011 DOCUMENTATION: very good, downloadable: The docs are a bit interestingly laid-out, with the sample program being overly complex to start with, so I had to delve into the actual API docs to figure things out. Having said that, the example program does talk about structure, ORM layers, templating, URLs and other handy things.
  • 68. werkzeug index and /PageName def index(request): raise RequestRedirect("/FrontPage") def page(request, name): if not storage.wikiname_re.match(name): raise NotFound('/%s' % name) return wiki.render_page(name) Monday, 22 August 2011 MAGIC: no magic
  • 69. werkzeug /PageName/edit def edit_form(request, name): return wiki.render_edit_form(name) def edit(request, name): if request.form.get('submit'): wiki.store_page(name, request.form['content']) raise RequestRedirect("/%s" % name) Monday, 22 August 2011
  • 70. werkzeug URL mapping url_map = Map([ Rule('/', endpoint=index, methods=['GET']), Rule('/<name>/edit', endpoint=edit_form, methods=['GET']), Rule('/<name>/edit', endpoint=edit, methods=['POST']), Rule('/<name>', endpoint=page, methods=['GET']), ]) Monday, 22 August 2011 RESTful: Map with Rules having URL and request method type mapping to endpoint callable
  • 71. werkzeug application and serving def application(environ, start_response): request = Request(environ) urls = url_map.bind_to_environ(environ) try: m, args = urls.match() r = m(request, **args) start_response('200 OK', [('Content-Type', 'text/html')]) return r except HTTPException, e: return e(environ, start_response) if __name__ == "__main__": from werkzeug.serving import run_simple run_simple('localhost', 8080, application) Monday, 22 August 2011 EASE OF USE: simple except writing my own WSGI app; EXTRAS: contributed code for sessions, ATOM, secure cookies and caching; WSGI: had to write my own, but it was short
  • 72. 10. a#en.io Monday, 22 August 2011
  • 73. Monday, 22 August 2011 Quite a different approach to things. Even though this is going against my "no templates" rule there is no way to use this framework *without* using its templating...
  • 74. aspen.io index.html from aspen import Response ^L raise Response(301, headers={'Location': '/FrontPage'}) ^L Monday, 22 August 2011 EASE OF USE: wildly different and some things are inexplicably hard; EXTRAS: static file serving; MAGIC: the page break stuff (initialisation, action, template) hurts understandability. Oh, aspen, "raise Response(301, headers={'Location': path+'/'})" really??!?
  • 75. aspen.io directory structure .aspen index.html %name/index.html %name/edit Monday, 22 August 2011 Aspen sees the %name directory and assigns the first part of the URL to the path variable “name”. Extra modules (storage, html, ...) have to be placed in the hidden ".aspen" folder.
  • 76. aspen.io %name/index.html from aspen import Response import storage wiki = storage.Storage('contents') ^L name = request.path['name'] if not storage.wikiname_re.match(name): raise Response(404, '/%s' % name) page = wiki.render_page(name) ^L {{ page }} Monday, 22 August 2011 I had to go off to the Tornado docs to figure how to HTML escaping was handled (though I probably could've just tried and discovered that it's not escaped by default). Not escaped HTML by default? RESTful: URLs map to filesystem paths
  • 77. aspen.io %name/edit from aspen import Response import storage wiki = storage.Storage('contents') ^L name = request.path['name'] if request.method == 'POST': if 'submit' in request.body: wiki.store_page(name, request.body.one('content')) raise Response(301, headers={'Location': '/' + name}) page = wiki.render_edit_form(name) response.headers.set('Content-Type', 'text/html') ^L {{ page }} Monday, 22 August 2011 The documentation is *seriously* thin. I found myself having to read the source to: 1. figure out what the request.body thing was and how to use it, and 2. figure out how to get a redirect back to the client. Oh, text/html isn’t the DEFAULT? RESTful: manually test for request method type in handler
  • 78. aspen.io serving HTTP from aspen import server server.main() Monday, 22 August 2011 Put this in a file in the aspen site’s directory. Or use the aspen command-line program. WSGI: appears to be no way to get a WSGI app. I’ve given aspen a rough time here because it’s not really suitable for my purposes, but I can see how it’d actually be really nice to develop a simple website with.
  • 79. So, how do they rank? • Let’s score them • +1 or -1 on each of: Documentation, Ease of Use, Avoiding Magic, RESTful, WSGI, Python 3, PyPy and SLOC cgi+wsgiref:0, aspen.io:0, bobo:0, bottle:0, cherrypy:0, flask:0, itty:0, pesto:0, web.py:0, werkzeug:0 Monday, 22 August 2011
  • 80. Documentation • good, downloadable: cgi+wsgiref, flask, werkzeug, bottle • good: pesto • could be better: web.py, itty, cherrypy • bad: bobo, aspen.io cgi+wsgiref:1, aspen.io:-1, bobo:-1, bottle:1, cherrypy:0, flask:1, itty:0, pesto:1, web.py:0, werkzeug:1 Monday, 22 August 2011 No “hello world” ... ? Hard to find Redirect/NotFound, RESTful routing, form access, wsgi application?
  • 81. Ease of Use • SIMPLE: pesto, itty, flask, web.py, bottle • Could get complicated: werkzeug, cherrypy • Complicated: aspen.io, bobo • Just Plain Difficult: cgi+wsgiref cgi+wsgiref:0, aspen.io:-2, bobo:-2, bottle:2, cherrypy:0, flask:2, itty:1, pesto:2, web.py:1, werkzeug:1 Monday, 22 August 2011
  • 82. Avoiding Magic • none: cgi+wsgiref, werkzeug, pesto • WSGI not apparent: itty • module-level: bottle, flask, web.py, cherrypy • just plain odd: aspen.io, bobo cgi+wsgiref:1, aspen.io:-2, bobo:-3, bottle:1, cherrypy:-1, flask:1, itty:1, pesto:3, web.py:0, werkzeug:2 Monday, 22 August 2011
  • 83. RESTful • bottle, flask, itty, pesto, web.py, werkzeug • not obvious: bobo, cherrypy • manual test method: aspen.io • NO: cgi+wsgiref cgi+wsgiref:0, aspen.io:-3, bobo:-3, bottle:2, cherrypy:-1, flask:2, itty:2, pesto:4, web.py:1, werkzeug:3 Monday, 22 August 2011 This actually dictated the structure of my wiki code more than anything else.
  • 84. RESTful • Ignoring aspen.io there was 2 approaches: • decorate callable • listing maps directly to callable Monday, 22 August 2011
  • 85. WSGI • bottle, flask, pesto, cgi+wsgiref • not obvious: itty, web.py, cherrypy • manual: werkzeug • unknown: bobo • NO: aspen.io cgi+wsgiref:1, aspen.io:-4, bobo:-4, bottle:3, cherrypy: -1, flask:3, itty:2, pesto:5, web.py:1, werkzeug:2 Monday, 22 August 2011
  • 86. Python 3 • yes: cgi+wsgiref, bottle, cherrypy • no: bobo, itty, web.py, pesto, werkzeug, flask, aspen.io cgi+wsgiref:2, aspen.io:-5, bobo:-5, bottle:4, cherrypy:0, flask:2, itty:1, pesto:4, web.py:0, werkzeug:1 Monday, 22 August 2011
  • 87. PyPy cgi+wsgiref:3, aspen.io:-4, bobo:-4, bottle:5, cherrypy:1, flask:3, itty:2, pesto:5, web.py:1, werkzeug:2 Monday, 22 August 2011 PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
  • 88. PyPy THEY cgi+wsgiref:3, aspen.io:-4, bobo:-4, bottle:5, cherrypy:1, flask:3, itty:2, pesto:5, web.py:1, werkzeug:2 Monday, 22 August 2011 PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
  • 89. PyPy THEY ALL cgi+wsgiref:3, aspen.io:-4, bobo:-4, bottle:5, cherrypy:1, flask:3, itty:2, pesto:5, web.py:1, werkzeug:2 Monday, 22 August 2011 PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
  • 90. PyPy THEY ALL RUN cgi+wsgiref:3, aspen.io:-4, bobo:-4, bottle:5, cherrypy:1, flask:3, itty:2, pesto:5, web.py:1, werkzeug:2 Monday, 22 August 2011 PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
  • 91. wiki.py line/byte count 38/905 bottle 38/982 itty 41/1061 flask 43/1148 bobo 45/1071 web.py 45/1301 pesto 51/1267 cherrypy 55/1730 werkzeug 77/2126 aspen.io 78/2142 cgi+wsgiref cgi+wsgiref:2, aspen.io:-5, bobo:-2, bottle:6, cherrypy:1, flask:4, itty:3, pesto:6, web.py:1, werkzeug:2 Monday, 22 August 2011
  • 92. framework line count 593 itty 2441 cgi+wsgiref 2697 bottle 5600 aspen.io 6649 pesto 8044 bobo 10198 web.py 16563 werkzeug 27768 cherrypy 32245 flask cgi+wsgiref:3, aspen.io:-5, bobo:-2, bottle:7, cherrypy:0, flask:3, itty:4, pesto:6, web.py:1, werkzeug:2 Monday, 22 August 2011
  • 93. The Official* Scores Rank Framework Score 1 bottle 7 2 pesto 6 3 itty 4 4 flask, cgi+wsgiref 3 5 werkzeug 2 6 web.py 1 7 cherrypy 0 8 bobo -2 9 aspen.io -5 Monday, 22 August 2011 The scores, as determined solely by my own criteria and weighting.
  • 94. OMGz0R a winner? Bo!le! Monday, 22 August 2011 Simple to use, great docs, RESTful and WSGI, Python 3. A single file and compact application code. Awesome!
  • 95. Extras Framework Sessions Caching Other cgi+wsgiref ✗ ✗ aspen.io ✗ ✗ static file serving bobo ✗ ✗ bottle ✗ ✗ templating, adapters, utilities cherrypy ✓ ✓ static file serving, plugins flask ✓ ✓ debugger, shell, werkzeug itty ✗ ✗ pesto ✓ ✓ utilities for testing web.py ✓ ✗ openid, templating, form & db werkzeug ✓ ✓ ATOM, secure cookies Monday, 22 August 2011