Morsel

Development Status :: 3 - Alpha
Intended Audience :: Developers
License :: OSI Approved :: GNU General Public License (GPL)
Operating System :: OS Independent
Programming Language :: Python
Topic :: Internet :: WWW/HTTP :: WSGI

Download: Morsel-0.1.tar.gz (Development Status :: 3 - Alpha)

Introduction

1. What is WSGI?

The Web Server Gateway Interface (WSGI) is a specification for web servers and application servers to communicate with web applications. It is described in the PEP 333 Python standard.

2. Why is WSGI useful?

For a moment think of a specification for connecting black boxes together. Some boxes have a plug, some have a socket, and some have a plug on one side and a socket on the other. This helps to connect lots of black boxes together and create chains.

    +-------+    +-------+    +-------+    +-------+
    |       |    |       |    |       |    |       |
    | Box 1 ===> > Box 2 ===> > Box 3 ===> > Box 4 | 
    |       |    |       |    |       |    |       |
    +-------+    +-------+    +-------+    +-------+
    

In the web development world, one may connect such software "boxes" to create full-blown web applications:

    +------------+    +--------------------+    +----------------+    +-----+
    |            |    |                    |    |                |    |     |
    | Web Server ===> > Session management ===> > Authentication ===> > App | 
    |            |    |                    |    |                |    |     |
    +------------+    +--------------------+    +----------------+    +-----+
    

WSGI then, is a specification for such box connectors. Think of the specification of how a web server will invoke a web application as a plug and the specification of how the web application will respond to the web server as a socket. Of course, there can be boxes that have a plug on one side and a socket on the other, thus appearing as web servers on one side and as applications on the other; these boxes we call "middleware."

WSGI is useful because people can create software boxes that perform a particular task and connect to other people's boxes. For example, some developers might create session management middleware, others authentication middleware, logging and reporting middleware, thus creating an ecosystem of these boxes which can be plugged together until a desired functionality is achieved. A web developer can mix, match, and create chains of these software boxes to create end-to-end web applications. If this doesn't sound like a useful thing, you're probably a Django developer.

3. What is Morsel?

Morsel is a WSGI web application project generator. It creates a minimal project directory and provides a few files that contain the code for gluing some basic WSGI components together.

4. Why is Morsel useful?

Morsel is useful mainly for two reasons:

Getting Started

1. How do I install Morsel?

Use: python setup.py install and it will get installed in a directory in your sys-path.

2. What are the dependencies of Morsel?

Morsel has the following dependencies:

3. How do I use Morsel?

You have to issue: python -m morsel/morsel -n <project name> to create a WSGI web application project. The "-m" option searches sys.path for the named module and runs the corresponding .py file as a script.

4. How do I run the web application project Morsel generates?

The web application project Morsel generates is a WSGI application and can be served by any WSGI server. By default, the Paste HTTP server is used to serve the web application on port 9090. For this, go to the web application directory and run python start.py.

Alternatively, you may use any WSGI aware web server like CherryPy, or Apache with mod_wsgi.

5. How does a Morsel web application work from a high-level view?

An HTTP request hits the Paste HTTP server and it invokes your WSGI application. The first point of contact is the Routes middleware which according to the routes (mappings of URLs to controllers) you have specified dissects the URL and places the relevant information in the data structure that is passed around from one component to the other (it is a dict conventionally called environ). The next component in line is the WSGI Dispatcher which reads the information from Routes and goes and instantiates the relevant controller to respond to the request. Schematically it looks like this:

                                     ---------
      |------------------- model/            |
      |            |                         |
      |------------------- templates/        |
      |            |                         | - MVC style directory layout
      |            |                         |
      |            |                         |
  controller1 controller2                    |
      |            |                 ---------
      `------------|
            |
            |        ----
           WSGI         |
        Dispatcher      |
            |           |--------------------- WSGI Middleware Stack
            |           |
          Routes        |
            |        ----
            |
     Paste HTTPServer (or any other server you use)
     

Web Application Project Layout

1. What is the layout of the web application projects created by Morsel?

The project layouts created are like the following:

     <project name>/
     |-- config
     |   |-- __init__.py
     |   `-- routing.py
     |-- controllers
     |   |-- __init__.py
     |   `-- default.py
     |-- lib
     |   |-- __init__.py
     |   `-- controller.py
     |-- middleware
     |   `-- __init__.py
     |-- model
     |   `-- __init__.py
     |-- public
     |   `-- static
     |       `-- css
     |           `-- screen.css
     |-- start.py
     `-- templates
     

2. What is the purpose of each directory/file?

Creating your Application

1. How do I create a controller?

To create a controller you can either create a class for a callable object which is a WSGI application, or a classwith methods which are WSGI applications either by writing your own or inheriting from the Morsel Controller class. Inheriting from Morsel's Controller class provides a render_response method which is useful for rendering Mako templates as part of your request response. Examples of all cases follow:

    # Class for a callable object.
    class MyController(object):
        """A basic WSGI Controller"""
        def __init__(self):
            pass

        def __call__(self, environ, start_response):
            status = '200 OK'
            response_headers = [('Content-type','text/plain')]
            start_response(status, response_headers)
            return ['Hello world!\n']

    # Class with methods that are WSGI applications.
    class MyControllerWithMethods(object):
        def index(self, environ, start_response):
            status = '200 OK'
            response_headers = [('Content-type','text/plain')]
            start_response(status, response_headers)
            return ['Hello world!\n']

    # Inheriting from Morsel's Controller class.
    # Of course you can inherit from Controller without having to use 
    # render_response.
    from lib.controller import Controller

    class MyController(Controller):
        def index(self, environ, start_response):
            response = self._render_response("my_controller.mako", 
                                             {'some_template_var': 'some_value'})
            return response(environ, start_response)
    

2. How do I route requests to my controller?

Morsel WSGI application projects use Routes and WSGI Dispatcher for URL parsing and dispatching. As an example, assume that you have created a file called controllers/hello.py in which you define a controller class called HelloController. HelloController has an index() method which is a WSGI application. The steps to route requests to this controller are as follows:

3. How do I use a template with my controller?

The base controller class Controller has a render_response method that takes the filename of a Mako template and dictionary of arguments to pass to it. If your controller classes inherit from it, all you need to do is call this method. For example:

    from lib.controller import Controller

    class DefaultController(Controller):
        def index(self, environ, start_response):
            response = self._render_response("default.mako", {'title': 'webapp3'})
            return response(environ, start_response)
    

4. How do I parse GET form variables?

You can either parse the GET variables directly from environ['QUERY_STRING'] or import parse_formvars from paste.request in your controller and call it with the arguments: qs = parse_formvars(environ, include_get_vars=True). If for example the quersy string was "var1=hello&var2=world" qs will be a MultiDict object MultiDict([('var1', 'hello'), ('var2', 'world')]).

5. How do I parse POST form variables?

You may use paste.request.parse_formvars as described in previous question.

6. How do I use sessions?

For session support you may use the Beaker session middleware. To do so, in <project name>/middleware/__init__.py add the following:

    # At imports
    from beaker.session import SessionMiddleware
    # ...
    # After app = RoutesMiddleware(dispatcher, mapper)
    app = SessionMiddleware(app)
    

In your controller you may then use:

    session = environ['beaker.session']
    if not session.has_key('value'):
        session['value'] = 0
    session['value'] += 1
    session.save()
    

7. How do I gzip responses?

Paste comes to resque again:

    # At imports
    from paste.gzipper import make_gzip_middleware
    #...
    # Some point after app = RoutesMiddleware(dispatcher, mapper)
    app = make_gzip_middleware(app)
    

8. How do I use SQLAlchemy in my Morsel WSGI application project?

You may use Alchemyware, an SQLAlchemy middleware.

9. How do I serve static files?

In <project name>/middleware/__init__.py you may specify URLs that map to certain directories like <project name>/public/static for static file serving. For example, in __init__.py:

    dispatcher = WSGIDispatcher(controller_dirs = ["controllers"])
    dispatcher.add_static_dir('/javascript/', 'public/static/javascript/')
    

10. How do load controllers from more than one directories?

In <project name>/middleware/__init__.py you may specify more controller directories when wrapping-in the WSGI Dispatcher: dispatcher = WSGIDispatcher(controller_dirs = ["controllers", "other"])