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 ofļ¬‚ine 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 ofļ¬‚ine 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 ofļ¬‚ine 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 ofļ¬‚ine 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 ofļ¬‚ine 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 ofļ¬‚ine 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
                                            ā€¢   ļ¬‚ask
                                            ā€¢   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 ļ¬le!?!
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 ļ¬le? 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 ļ¬gure how to get a
WSGI application.
3.




                                            cherrypy


Monday, 22 August 2011

OK, so from the ļ¬rst 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 conļ¬g bit is a bit strange; ooh, automatic code reloading.; WSGI: I had to google and *then* look at the
source to ļ¬nd 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 ļ¬nd 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 ļ¬ddly 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-ļ¬nd 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 ļ¬rst-class
get(), post() as well as generic route() which conļ¬gure 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 ļ¬les, 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.




                                                  ļ¬‚ask


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
ļ¬‚ask
                         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 conļ¬gured, 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
ļ¬‚ask
                               /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 ā€œļ¬‚askā€; RESTful: request
method in app.route() decorator which also takes URL
ļ¬‚ask
                                                    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 ļ¬gure 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 ļ¬le 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 ļ¬rst 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 ļ¬gure 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 ļ¬lesystem 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. ļ¬gure out what the request.body thing was and how
to use it, and 2. ļ¬gure 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 ļ¬le 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,
                                ļ¬‚ask:0, itty:0, pesto:0, web.py:0, werkzeug:0


Monday, 22 August 2011
Documentation

                   ā€¢ good, downloadable: cgi+wsgiref, ļ¬‚ask,
                         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,
                                 ļ¬‚ask:1, itty:0, pesto:1, web.py:0, werkzeug:1


Monday, 22 August 2011

No ā€œhello worldā€ ... ? Hard to ļ¬nd Redirect/NotFound, RESTful routing, form access, wsgi application?
Ease of Use

                   ā€¢ SIMPLE: pesto, itty, ļ¬‚ask, web.py, bottle
                   ā€¢ Could get complicated: werkzeug, cherrypy
                   ā€¢ Complicated: aspen.io, bobo
                   ā€¢ Just Plain Difļ¬cult: cgi+wsgiref
                         cgi+wsgiref:0, aspen.io:-2, bobo:-2, bottle:2, cherrypy:0,
                              ļ¬‚ask: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, ļ¬‚ask, web.py, cherrypy
                   ā€¢ just plain odd: aspen.io, bobo
                         cgi+wsgiref:1, aspen.io:-2, bobo:-3, bottle:1, cherrypy:-1,
                              ļ¬‚ask:1, itty:1, pesto:3, web.py:0, werkzeug:2


Monday, 22 August 2011
RESTful

                   ā€¢ bottle, ļ¬‚ask, 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,
                                ļ¬‚ask: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, ļ¬‚ask, 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,
                              ļ¬‚ask: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, ļ¬‚ask,
                         aspen.io


                           cgi+wsgiref:2, aspen.io:-5, bobo:-5, bottle:4, cherrypy:0,
                                ļ¬‚ask: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,
                               ļ¬‚ask: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,
                               ļ¬‚ask: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,
                               ļ¬‚ask: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,
                               ļ¬‚ask: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,
                              ļ¬‚ask: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,
                               ļ¬‚ask:3, itty:4, pesto:6, web.py:1, werkzeug:2


Monday, 22 August 2011
The Ofļ¬cial* Scores
                             Rank                     Framework        Score
                                 1                       bottle          7
                                 2                       pesto           6
                                 3                        itty           4
                                 4                 ļ¬‚ask, 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 ļ¬le and compact application code. Awesome!
Extras
            Framework     Sessions Caching                Other
            cgi+wsgiref      āœ—       āœ—
              aspen.io       āœ—       āœ—       static ļ¬le serving
               bobo          āœ—       āœ—
               bottle        āœ—       āœ—       templating, adapters, utilities
             cherrypy       āœ“        āœ“       static ļ¬le serving, plugins
                ļ¬‚ask        āœ“        āœ“       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

MySQL Monitoring with Zabbix
MySQL Monitoring with ZabbixMySQL Monitoring with Zabbix
MySQL Monitoring with ZabbixFromDual GmbH
Ā 
IntelĀ® RDT Hands-on Lab
IntelĀ® RDT Hands-on LabIntelĀ® RDT Hands-on Lab
IntelĀ® RDT Hands-on LabMichelle Holley
Ā 
IP Virtual Server(IPVS) 101
IP Virtual Server(IPVS) 101IP Virtual Server(IPVS) 101
IP Virtual Server(IPVS) 101HungWei Chiu
Ā 
containerd恮ꦂ要ćØ꜀čæ‘ć®ę©Ÿčƒ½
containerd恮ꦂ要ćØ꜀čæ‘ć®ę©Ÿčƒ½containerd恮ꦂ要ćØ꜀čæ‘ć®ę©Ÿčƒ½
containerd恮ꦂ要ćØ꜀čæ‘ć®ę©Ÿčƒ½Kohei Tokunaga
Ā 
L3HA-VRRP-20141201
L3HA-VRRP-20141201L3HA-VRRP-20141201
L3HA-VRRP-20141201Manabu Ori
Ā 
Maxscale switchover, failover, and auto rejoin
Maxscale switchover, failover, and auto rejoinMaxscale switchover, failover, and auto rejoin
Maxscale switchover, failover, and auto rejoinWagner Bianchi
Ā 
MySQL Administrator 2021 - ė„¤ģ˜¤ķ“ė”œė°”
MySQL Administrator 2021 - ė„¤ģ˜¤ķ“ė”œė°”MySQL Administrator 2021 - ė„¤ģ˜¤ķ“ė”œė°”
MySQL Administrator 2021 - ė„¤ģ˜¤ķ“ė”œė°”NeoClova
Ā 
Linux cgroups and namespaces
Linux cgroups and namespacesLinux cgroups and namespaces
Linux cgroups and namespacesLocaweb
Ā 
VMworld 2013: ESXi Native Networking Driver Model - Delivering on Simplicity ...
VMworld 2013: ESXi Native Networking Driver Model - Delivering on Simplicity ...VMworld 2013: ESXi Native Networking Driver Model - Delivering on Simplicity ...
VMworld 2013: ESXi Native Networking Driver Model - Delivering on Simplicity ...VMworld
Ā 
Virtualisation de Machines avec Windows Hyper V
Virtualisation de Machines avec Windows Hyper VVirtualisation de Machines avec Windows Hyper V
Virtualisation de Machines avec Windows Hyper Vfabricemeillon
Ā 
P2P Container Image Distribution on IPFS With containerd and nerdctl
P2P Container Image Distribution on IPFS With containerd and nerdctlP2P Container Image Distribution on IPFS With containerd and nerdctl
P2P Container Image Distribution on IPFS With containerd and nerdctlKohei Tokunaga
Ā 
Architectural caching patterns for kubernetes
Architectural caching patterns for kubernetesArchitectural caching patterns for kubernetes
Architectural caching patterns for kubernetesRafał Leszko
Ā 
Cephć®ćƒ™ćƒ³ćƒćƒžćƒ¼ć‚Æć‚’ć—ć¾ć—ćŸ
Cephć®ćƒ™ćƒ³ćƒćƒžćƒ¼ć‚Æć‚’ć—ć¾ć—ćŸCephć®ćƒ™ćƒ³ćƒćƒžćƒ¼ć‚Æć‚’ć—ć¾ć—ćŸ
Cephć®ćƒ™ćƒ³ćƒćƒžćƒ¼ć‚Æć‚’ć—ć¾ć—ćŸOSSćƒ©ćƒœę Ŗ式会ē¤¾
Ā 
Open stack ha design & deployment kilo
Open stack ha design & deployment   kiloOpen stack ha design & deployment   kilo
Open stack ha design & deployment kiloSteven Li
Ā 
PrƩsentation VDI - Virtual Desktop Infrastucture
PrƩsentation VDI - Virtual Desktop InfrastucturePrƩsentation VDI - Virtual Desktop Infrastucture
PrƩsentation VDI - Virtual Desktop InfrastucturePatricia NENZI
Ā 
MySQL InnoDB Cluster ģ†Œź°œ
MySQL InnoDB Cluster ģ†Œź°œMySQL InnoDB Cluster ģ†Œź°œ
MySQL InnoDB Cluster ģ†Œź°œrockplace
Ā 
Reactive Web 101: WebFlux, WebClient, and Reactor Netty
Reactive Web 101: WebFlux, WebClient, and Reactor NettyReactive Web 101: WebFlux, WebClient, and Reactor Netty
Reactive Web 101: WebFlux, WebClient, and Reactor NettyVMware Tanzu
Ā 
Security Monitoring with eBPF
Security Monitoring with eBPFSecurity Monitoring with eBPF
Security Monitoring with eBPFAlex Maestretti
Ā 
Packet Walk(s) In Kubernetes
Packet Walk(s) In KubernetesPacket Walk(s) In Kubernetes
Packet Walk(s) In KubernetesDon Jayakody
Ā 

What's hot (20)

MySQL Monitoring with Zabbix
MySQL Monitoring with ZabbixMySQL Monitoring with Zabbix
MySQL Monitoring with Zabbix
Ā 
IntelĀ® RDT Hands-on Lab
IntelĀ® RDT Hands-on LabIntelĀ® RDT Hands-on Lab
IntelĀ® RDT Hands-on Lab
Ā 
IP Virtual Server(IPVS) 101
IP Virtual Server(IPVS) 101IP Virtual Server(IPVS) 101
IP Virtual Server(IPVS) 101
Ā 
containerd恮ꦂ要ćØ꜀čæ‘ć®ę©Ÿčƒ½
containerd恮ꦂ要ćØ꜀čæ‘ć®ę©Ÿčƒ½containerd恮ꦂ要ćØ꜀čæ‘ć®ę©Ÿčƒ½
containerd恮ꦂ要ćØ꜀čæ‘ć®ę©Ÿčƒ½
Ā 
L3HA-VRRP-20141201
L3HA-VRRP-20141201L3HA-VRRP-20141201
L3HA-VRRP-20141201
Ā 
Maxscale switchover, failover, and auto rejoin
Maxscale switchover, failover, and auto rejoinMaxscale switchover, failover, and auto rejoin
Maxscale switchover, failover, and auto rejoin
Ā 
MySQL Administrator 2021 - ė„¤ģ˜¤ķ“ė”œė°”
MySQL Administrator 2021 - ė„¤ģ˜¤ķ“ė”œė°”MySQL Administrator 2021 - ė„¤ģ˜¤ķ“ė”œė°”
MySQL Administrator 2021 - ė„¤ģ˜¤ķ“ė”œė°”
Ā 
Linux cgroups and namespaces
Linux cgroups and namespacesLinux cgroups and namespaces
Linux cgroups and namespaces
Ā 
VMworld 2013: ESXi Native Networking Driver Model - Delivering on Simplicity ...
VMworld 2013: ESXi Native Networking Driver Model - Delivering on Simplicity ...VMworld 2013: ESXi Native Networking Driver Model - Delivering on Simplicity ...
VMworld 2013: ESXi Native Networking Driver Model - Delivering on Simplicity ...
Ā 
Virtualisation de Machines avec Windows Hyper V
Virtualisation de Machines avec Windows Hyper VVirtualisation de Machines avec Windows Hyper V
Virtualisation de Machines avec Windows Hyper V
Ā 
P2P Container Image Distribution on IPFS With containerd and nerdctl
P2P Container Image Distribution on IPFS With containerd and nerdctlP2P Container Image Distribution on IPFS With containerd and nerdctl
P2P Container Image Distribution on IPFS With containerd and nerdctl
Ā 
Architectural caching patterns for kubernetes
Architectural caching patterns for kubernetesArchitectural caching patterns for kubernetes
Architectural caching patterns for kubernetes
Ā 
Cephć®ćƒ™ćƒ³ćƒćƒžćƒ¼ć‚Æć‚’ć—ć¾ć—ćŸ
Cephć®ćƒ™ćƒ³ćƒćƒžćƒ¼ć‚Æć‚’ć—ć¾ć—ćŸCephć®ćƒ™ćƒ³ćƒćƒžćƒ¼ć‚Æć‚’ć—ć¾ć—ćŸ
Cephć®ćƒ™ćƒ³ćƒćƒžćƒ¼ć‚Æć‚’ć—ć¾ć—ćŸ
Ā 
Open stack ha design & deployment kilo
Open stack ha design & deployment   kiloOpen stack ha design & deployment   kilo
Open stack ha design & deployment kilo
Ā 
PrƩsentation VDI - Virtual Desktop Infrastucture
PrƩsentation VDI - Virtual Desktop InfrastucturePrƩsentation VDI - Virtual Desktop Infrastucture
PrƩsentation VDI - Virtual Desktop Infrastucture
Ā 
MySQL InnoDB Cluster ģ†Œź°œ
MySQL InnoDB Cluster ģ†Œź°œMySQL InnoDB Cluster ģ†Œź°œ
MySQL InnoDB Cluster ģ†Œź°œ
Ā 
Reactive Web 101: WebFlux, WebClient, and Reactor Netty
Reactive Web 101: WebFlux, WebClient, and Reactor NettyReactive Web 101: WebFlux, WebClient, and Reactor Netty
Reactive Web 101: WebFlux, WebClient, and Reactor Netty
Ā 
Security Monitoring with eBPF
Security Monitoring with eBPFSecurity Monitoring with eBPF
Security Monitoring with eBPF
Ā 
Linux one vs x86
Linux one vs x86 Linux one vs x86
Linux one vs x86
Ā 
Packet Walk(s) In Kubernetes
Packet Walk(s) In KubernetesPacket Walk(s) In Kubernetes
Packet Walk(s) In Kubernetes
Ā 

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
Ā 
Message queueing
Message queueingMessage queueing
Message queueingRichard 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

Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
Ā 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
Ā 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
Ā 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
Ā 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
Ā 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
Ā 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
Ā 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
Ā 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
Ā 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
Ā 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
Ā 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
Ā 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
Ā 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
Ā 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
Ā 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
Ā 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machinePadma Pradeep
Ā 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
Ā 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
Ā 

Recently uploaded (20)

Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Ā 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
Ā 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
Ā 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Ā 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
Ā 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
Ā 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
Ā 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
Ā 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
Ā 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
Ā 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
Ā 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
Ā 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
Ā 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
Ā 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
Ā 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
Ā 
Install Stable Diffusion in windows machine
Install Stable Diffusion in windows machineInstall Stable Diffusion in windows machine
Install Stable Diffusion in windows machine
Ā 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
Ā 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
Ā 
Hot Sexy call girls in Panjabi Bagh šŸ” 9953056974 šŸ” Delhi escort Service
Hot Sexy call girls in Panjabi Bagh šŸ” 9953056974 šŸ” Delhi escort ServiceHot Sexy call girls in Panjabi Bagh šŸ” 9953056974 šŸ” Delhi escort Service
Hot Sexy call girls in Panjabi Bagh šŸ” 9953056974 šŸ” Delhi escort Service
Ā 

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 ofļ¬‚ine 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 ofļ¬‚ine 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 ofļ¬‚ine 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 ofļ¬‚ine 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 ofļ¬‚ine 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 ofļ¬‚ine 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 ā€¢ ļ¬‚ask ā€¢ 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 ļ¬le!?!
  • 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 ļ¬le? 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 ļ¬gure how to get a WSGI application.
  • 42. 3. cherrypy Monday, 22 August 2011 OK, so from the ļ¬rst 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 conļ¬g bit is a bit strange; ooh, automatic code reloading.; WSGI: I had to google and *then* look at the source to ļ¬nd 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 ļ¬nd 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 ļ¬ddly 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-ļ¬nd 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 ļ¬rst-class get(), post() as well as generic route() which conļ¬gure 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 ļ¬les, 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. ļ¬‚ask 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. ļ¬‚ask 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 conļ¬gured, 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. ļ¬‚ask /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 ā€œļ¬‚askā€; RESTful: request method in app.route() decorator which also takes URL
  • 62. ļ¬‚ask 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 ļ¬gure 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 ļ¬le 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 ļ¬rst 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 ļ¬gure 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 ļ¬lesystem 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. ļ¬gure out what the request.body thing was and how to use it, and 2. ļ¬gure 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 ļ¬le 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, ļ¬‚ask:0, itty:0, pesto:0, web.py:0, werkzeug:0 Monday, 22 August 2011
  • 80. Documentation ā€¢ good, downloadable: cgi+wsgiref, ļ¬‚ask, 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, ļ¬‚ask:1, itty:0, pesto:1, web.py:0, werkzeug:1 Monday, 22 August 2011 No ā€œhello worldā€ ... ? Hard to ļ¬nd Redirect/NotFound, RESTful routing, form access, wsgi application?
  • 81. Ease of Use ā€¢ SIMPLE: pesto, itty, ļ¬‚ask, web.py, bottle ā€¢ Could get complicated: werkzeug, cherrypy ā€¢ Complicated: aspen.io, bobo ā€¢ Just Plain Difļ¬cult: cgi+wsgiref cgi+wsgiref:0, aspen.io:-2, bobo:-2, bottle:2, cherrypy:0, ļ¬‚ask: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, ļ¬‚ask, web.py, cherrypy ā€¢ just plain odd: aspen.io, bobo cgi+wsgiref:1, aspen.io:-2, bobo:-3, bottle:1, cherrypy:-1, ļ¬‚ask:1, itty:1, pesto:3, web.py:0, werkzeug:2 Monday, 22 August 2011
  • 83. RESTful ā€¢ bottle, ļ¬‚ask, 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, ļ¬‚ask: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, ļ¬‚ask, 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, ļ¬‚ask: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, ļ¬‚ask, aspen.io cgi+wsgiref:2, aspen.io:-5, bobo:-5, bottle:4, cherrypy:0, ļ¬‚ask: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, ļ¬‚ask: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, ļ¬‚ask: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, ļ¬‚ask: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, ļ¬‚ask: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, ļ¬‚ask: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, ļ¬‚ask:3, itty:4, pesto:6, web.py:1, werkzeug:2 Monday, 22 August 2011
  • 93. The Ofļ¬cial* Scores Rank Framework Score 1 bottle 7 2 pesto 6 3 itty 4 4 ļ¬‚ask, 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 ļ¬le and compact application code. Awesome!
  • 95. Extras Framework Sessions Caching Other cgi+wsgiref āœ— āœ— aspen.io āœ— āœ— static ļ¬le serving bobo āœ— āœ— bottle āœ— āœ— templating, adapters, utilities cherrypy āœ“ āœ“ static ļ¬le serving, plugins ļ¬‚ask āœ“ āœ“ debugger, shell, werkzeug itty āœ— āœ— pesto āœ“ āœ“ utilities for testing web.py āœ“ āœ— openid, templating, form & db werkzeug āœ“ āœ“ ATOM, secure cookies Monday, 22 August 2011