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




Monday, 22 August 2011
web
                         micro-framework



Monday, 22 August 2011
web
                         micro-framework

                           Ba!le!
Monday, 22 August 2011
aka




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


Monday, 22 August 2011
Introduction




Monday, 22 August 2011

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




Monday, 22 August 2011

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




Monday, 22 August 2011

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




Monday, 22 August 2011

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



Monday, 22 August 2011

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


Monday, 22 August 2011

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




Monday, 22 August 2011

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




Monday, 22 August 2011

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




Monday, 22 August 2011

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



Monday, 22 August 2011

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


Monday, 22 August 2011

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

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




Monday, 22 August 2011

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


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




Monday, 22 August 2011

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


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


Monday, 22 August 2011

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




Monday, 22 August 2011

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

                   • page view (GET /PageName)




Monday, 22 August 2011

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

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



Monday, 22 August 2011

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

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


Monday, 22 August 2011

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

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

Monday, 22 August 2011

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


Monday, 22 August 2011

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

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




                         cgi&wsgiref


Monday, 22 August 2011

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

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

Monday, 22 August 2011

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

Monday, 22 August 2011

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

Monday, 22 August 2011

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

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

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

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

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011
2.




                                                     bobo


Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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


    AttributeError: Page instance has no attribute 'bobo_response'




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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




                                            cherrypy


Monday, 22 August 2011

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

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

                         edit = WikiEdit()




Monday, 22 August 2011

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

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

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




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

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




Monday, 22 August 2011

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




                                         web.py


Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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




                         bo!le


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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

                         wiki = storage.Storage('contents')

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

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

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

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

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


Monday, 22 August 2011

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




                                                            i!y


Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

                                                         run_itty()




Monday, 22 August 2011

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




                                                  flask


Monday, 22 August 2011

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

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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


                                                    app.run(debug=True)




Monday, 22 August 2011

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




                         pesto


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

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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




                                        werkzeug


Monday, 22 August 2011

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

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

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




Monday, 22 August 2011

MAGIC: no magic
werkzeug
                         /PageName/edit

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

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




Monday, 22 August 2011
werkzeug
                                   URL mapping

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




Monday, 22 August 2011

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

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



Monday, 22 August 2011

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




                         a#en.io


Monday, 22 August 2011
Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

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




Monday, 22 August 2011

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

                                              from aspen import server
                                              server.main()




Monday, 22 August 2011

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

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

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


Monday, 22 August 2011
Documentation

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


Monday, 22 August 2011

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

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


Monday, 22 August 2011
Avoiding Magic

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


Monday, 22 August 2011
RESTful

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


Monday, 22 August 2011

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

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


Monday, 22 August 2011
WSGI

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


Monday, 22 August 2011
Python 3


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


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


Monday, 22 August 2011
PyPy




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


Monday, 22 August 2011

PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
PyPy

                                 THEY

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


Monday, 22 August 2011

PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
PyPy

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


Monday, 22 August 2011

PyPy 1.5.0-alpha0 with GCC 4.0.1 on OS X
PyPy

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


Monday, 22 August 2011

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



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


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



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


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

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


                                      Bo!le!


Monday, 22 August 2011

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

More Related Content

What's hot

JSON improvements in MySQL 8.0
JSON improvements in MySQL 8.0JSON improvements in MySQL 8.0
JSON improvements in MySQL 8.0Mydbops
 
Best Practices for Becoming an Exceptional Postgres DBA
Best Practices for Becoming an Exceptional Postgres DBA Best Practices for Becoming an Exceptional Postgres DBA
Best Practices for Becoming an Exceptional Postgres DBA EDB
 
Dynomite: A Highly Available, Distributed and Scalable Dynamo Layer--Ioannis ...
Dynomite: A Highly Available, Distributed and Scalable Dynamo Layer--Ioannis ...Dynomite: A Highly Available, Distributed and Scalable Dynamo Layer--Ioannis ...
Dynomite: A Highly Available, Distributed and Scalable Dynamo Layer--Ioannis ...Redis Labs
 
Mastering PostgreSQL Administration
Mastering PostgreSQL AdministrationMastering PostgreSQL Administration
Mastering PostgreSQL AdministrationEDB
 
MySQL Shell - The Best MySQL DBA Tool
MySQL Shell - The Best MySQL DBA ToolMySQL Shell - The Best MySQL DBA Tool
MySQL Shell - The Best MySQL DBA ToolMiguel Araújo
 
Découverte de Elastic search
Découverte de Elastic searchDécouverte de Elastic search
Découverte de Elastic searchJEMLI Fathi
 
Open Source 101 2022 - MySQL Indexes and Histograms
Open Source 101 2022 - MySQL Indexes and HistogramsOpen Source 101 2022 - MySQL Indexes and Histograms
Open Source 101 2022 - MySQL Indexes and HistogramsFrederic Descamps
 
Architecture of exadata database machine – Part II
Architecture of exadata database machine – Part IIArchitecture of exadata database machine – Part II
Architecture of exadata database machine – Part IIParesh Nayak,OCP®,Prince2®
 
Linux and H/W optimizations for MySQL
Linux and H/W optimizations for MySQLLinux and H/W optimizations for MySQL
Linux and H/W optimizations for MySQLYoshinori Matsunobu
 
MariaDB Server Performance Tuning & Optimization
MariaDB Server Performance Tuning & OptimizationMariaDB Server Performance Tuning & Optimization
MariaDB Server Performance Tuning & OptimizationMariaDB plc
 
Apache Cassandra Multi-Datacenter Essentials (Julien Anguenot, iLand Internet...
Apache Cassandra Multi-Datacenter Essentials (Julien Anguenot, iLand Internet...Apache Cassandra Multi-Datacenter Essentials (Julien Anguenot, iLand Internet...
Apache Cassandra Multi-Datacenter Essentials (Julien Anguenot, iLand Internet...DataStax
 
Postgresql database administration volume 1
Postgresql database administration volume 1Postgresql database administration volume 1
Postgresql database administration volume 1Federico Campoli
 
Percona Live 2022 - MySQL Architectures
Percona Live 2022 - MySQL ArchitecturesPercona Live 2022 - MySQL Architectures
Percona Live 2022 - MySQL ArchitecturesFrederic Descamps
 
MySQL Architecture and Engine
MySQL Architecture and EngineMySQL Architecture and Engine
MySQL Architecture and EngineAbdul Manaf
 
MySQL Shell for Database Engineers
MySQL Shell for Database EngineersMySQL Shell for Database Engineers
MySQL Shell for Database EngineersMydbops
 
ProxySQL - High Performance and HA Proxy for MySQL
ProxySQL - High Performance and HA Proxy for MySQLProxySQL - High Performance and HA Proxy for MySQL
ProxySQL - High Performance and HA Proxy for MySQLRené Cannaò
 
Maximizing performance via tuning and optimization
Maximizing performance via tuning and optimizationMaximizing performance via tuning and optimization
Maximizing performance via tuning and optimizationMariaDB plc
 
Achieving compliance With MongoDB Security
Achieving compliance With MongoDB Security Achieving compliance With MongoDB Security
Achieving compliance With MongoDB Security Mydbops
 

What's hot (20)

Postgresql tutorial
Postgresql tutorialPostgresql tutorial
Postgresql tutorial
 
JSON improvements in MySQL 8.0
JSON improvements in MySQL 8.0JSON improvements in MySQL 8.0
JSON improvements in MySQL 8.0
 
Best Practices for Becoming an Exceptional Postgres DBA
Best Practices for Becoming an Exceptional Postgres DBA Best Practices for Becoming an Exceptional Postgres DBA
Best Practices for Becoming an Exceptional Postgres DBA
 
Dynomite: A Highly Available, Distributed and Scalable Dynamo Layer--Ioannis ...
Dynomite: A Highly Available, Distributed and Scalable Dynamo Layer--Ioannis ...Dynomite: A Highly Available, Distributed and Scalable Dynamo Layer--Ioannis ...
Dynomite: A Highly Available, Distributed and Scalable Dynamo Layer--Ioannis ...
 
Mastering PostgreSQL Administration
Mastering PostgreSQL AdministrationMastering PostgreSQL Administration
Mastering PostgreSQL Administration
 
MySQL Shell - The Best MySQL DBA Tool
MySQL Shell - The Best MySQL DBA ToolMySQL Shell - The Best MySQL DBA Tool
MySQL Shell - The Best MySQL DBA Tool
 
Découverte de Elastic search
Découverte de Elastic searchDécouverte de Elastic search
Découverte de Elastic search
 
Open Source 101 2022 - MySQL Indexes and Histograms
Open Source 101 2022 - MySQL Indexes and HistogramsOpen Source 101 2022 - MySQL Indexes and Histograms
Open Source 101 2022 - MySQL Indexes and Histograms
 
Architecture of exadata database machine – Part II
Architecture of exadata database machine – Part IIArchitecture of exadata database machine – Part II
Architecture of exadata database machine – Part II
 
Linux and H/W optimizations for MySQL
Linux and H/W optimizations for MySQLLinux and H/W optimizations for MySQL
Linux and H/W optimizations for MySQL
 
MariaDB Server Performance Tuning & Optimization
MariaDB Server Performance Tuning & OptimizationMariaDB Server Performance Tuning & Optimization
MariaDB Server Performance Tuning & Optimization
 
Apache Cassandra Multi-Datacenter Essentials (Julien Anguenot, iLand Internet...
Apache Cassandra Multi-Datacenter Essentials (Julien Anguenot, iLand Internet...Apache Cassandra Multi-Datacenter Essentials (Julien Anguenot, iLand Internet...
Apache Cassandra Multi-Datacenter Essentials (Julien Anguenot, iLand Internet...
 
Postgresql database administration volume 1
Postgresql database administration volume 1Postgresql database administration volume 1
Postgresql database administration volume 1
 
Percona Live 2022 - MySQL Architectures
Percona Live 2022 - MySQL ArchitecturesPercona Live 2022 - MySQL Architectures
Percona Live 2022 - MySQL Architectures
 
MySQL Architecture and Engine
MySQL Architecture and EngineMySQL Architecture and Engine
MySQL Architecture and Engine
 
MySQL Shell for Database Engineers
MySQL Shell for Database EngineersMySQL Shell for Database Engineers
MySQL Shell for Database Engineers
 
Mysql Optimization
Mysql OptimizationMysql Optimization
Mysql Optimization
 
ProxySQL - High Performance and HA Proxy for MySQL
ProxySQL - High Performance and HA Proxy for MySQLProxySQL - High Performance and HA Proxy for MySQL
ProxySQL - High Performance and HA Proxy for MySQL
 
Maximizing performance via tuning and optimization
Maximizing performance via tuning and optimizationMaximizing performance via tuning and optimization
Maximizing performance via tuning and optimization
 
Achieving compliance With MongoDB Security
Achieving compliance With MongoDB Security Achieving compliance With MongoDB Security
Achieving compliance With MongoDB Security
 

Viewers also liked

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

Viewers also liked (6)

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

Similar to Web micro-framework BATTLE!

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

Similar to Web micro-framework BATTLE! (20)

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

More from Richard Jones

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

More from Richard Jones (12)

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

Recently uploaded

activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfJamie (Taka) Wang
 
Designing A Time bound resource download URL
Designing A Time bound resource download URLDesigning A Time bound resource download URL
Designing A Time bound resource download URLRuncy Oommen
 
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesMd Hossain Ali
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfinfogdgmi
 
UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6DianaGray10
 
NIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopNIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopBachir Benyammi
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxGDSC PJATK
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8DianaGray10
 
Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Commit University
 
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UbiTrack UK
 
Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsSeth Reyes
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXTarek Kalaji
 
Bird eye's view on Camunda open source ecosystem
Bird eye's view on Camunda open source ecosystemBird eye's view on Camunda open source ecosystem
Bird eye's view on Camunda open source ecosystemAsko Soukka
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxMatsuo Lab
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPathCommunity
 
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Will Schroeder
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfDianaGray10
 
Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Adtran
 
Nanopower In Semiconductor Industry.pdf
Nanopower  In Semiconductor Industry.pdfNanopower  In Semiconductor Industry.pdf
Nanopower In Semiconductor Industry.pdfPedro Manuel
 

Recently uploaded (20)

20230104 - machine vision
20230104 - machine vision20230104 - machine vision
20230104 - machine vision
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
 
Designing A Time bound resource download URL
Designing A Time bound resource download URLDesigning A Time bound resource download URL
Designing A Time bound resource download URL
 
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdf
 
UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6
 
NIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 WorkshopNIST Cybersecurity Framework (CSF) 2.0 Workshop
NIST Cybersecurity Framework (CSF) 2.0 Workshop
 
Cybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptxCybersecurity Workshop #1.pptx
Cybersecurity Workshop #1.pptx
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8
 
Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)
 
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
 
Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and Hazards
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBX
 
Bird eye's view on Camunda open source ecosystem
Bird eye's view on Camunda open source ecosystemBird eye's view on Camunda open source ecosystem
Bird eye's view on Camunda open source ecosystem
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptx
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation Developers
 
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
Apres-Cyber - The Data Dilemma: Bridging Offensive Operations and Machine Lea...
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
 
Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™Meet the new FSP 3000 M-Flex800™
Meet the new FSP 3000 M-Flex800™
 
Nanopower In Semiconductor Industry.pdf
Nanopower  In Semiconductor Industry.pdfNanopower  In Semiconductor Industry.pdf
Nanopower In Semiconductor Industry.pdf
 

Web micro-framework BATTLE!

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