brubeck DocumentationRelease a
James Dennis
September 27, 2017
Contents
1 Installing The Environment 31.1 ZeroMQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2 Mongrel2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.3 Virtualenv & Virtualenvwrapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41.4 Python Packages & Brubeck . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 A Demo 72.1 Mongrel2 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
3 Learn By Example 93.1 Abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.2 Kicking Mongrel2’s Tires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4 The Demos 11
5 Classes And Functions 13
6 URL Design And Handling 15
7 Template Rendering 17
8 Authentication 198.1 Auth Over POST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198.2 Authenticated Website . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
9 Deploying 239.1 Mongrel2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239.2 WSGI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239.3 Deployment Environments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
10 Copyright 2012 J2 Labs LLC. All rights reserved. 27
11 Features 2911.1 Message Handlers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2911.2 Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3111.3 Data Modeling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3311.4 AutoAPI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3611.5 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
i
11.6 File Uploading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3911.7 QuerySets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
12 Architecture 4112.1 Concurrency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4112.2 ZeroMQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4312.3 Brubeck and ZMQ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4412.4 Dependencies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
13 API 4713.1 brubeck Package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
14 Summary 4914.1 What Is Brubeck? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4914.2 Example: Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4914.3 Complete Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5014.4 Contact Us . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Python Module Index 53
ii
brubeck Documentation, Release a
Contents 1
brubeck Documentation, Release a
2 Contents
CHAPTER 1
Installing The Environment
First, we have to install a few things. Brubeck depends on Mongrel2, ZeroMQ and a few python packages.
All three packages live in github, so we’ll clone the repos to our Desktop.
$ cd ~/Desktop/$ git clone https://github.com/j2labs/brubeck.git$ git clone https://github.com/zedshaw/mongrel2.git$ wget http://download.zeromq.org/historic/zeromq-2.1.9.tar.gz$ tar zxf zeromq-2.1.9.tar.gz
ZeroMQ
ZeroMQ, from a Python perspective, is actually two pieces: libzmq and pyzmq. libzmq must be installed by hand likeyou see below.
$ cd ~/Desktop/zeromq-2.1.9$ ./autogen.sh$ ./configure ## for mac ports use: ./configure --prefix=/opt/local$ make$ sudo make install
Mongrel2
Mongrel2 is also painless to setup.
$ cd ~/Desktop/mongrel2$ make ## for mac ports use: make macports## Mongrel 2 requires sqlite3 and dev libraries of sqlite3$ sudo apt-get install sqlite3
3
brubeck Documentation, Release a
$ sudo apt-get install libsqlite3-dev$ sudo make install
There are a few compile options available at the bottom of Mongrel2’s Makefile. Take a look if the code abovedoesn’t compile successfully.
Virtualenv & Virtualenvwrapper
Brubeck works great with virtualenv. I highly recommend using it.
Virtualenv is a way to construct isolated python environments. Very handy for managing multiple environments in asingle machine.
Install both virtualenv and virtualenvwrapper with pip.
pip install virtualenv virtualenvwrapper
Then, we must configure our shell to know where to store our virtualenv’s. While we’re there, we’ll source thevirtualenvwrapper shell script.
Open your .profile or .bashrc and add the following two lines.
export WORKON_HOME="~/.virtualenvs"source /usr/local/bin/virtualenvwrapper.sh
By sourcing virtualenvwrapper, you get a simple interface for creating, managing and removing virutalenv environ-ments.
$ mkvirtualenv <env_name> # Creates a virtual environment$ deactivate # Turn off a virtual environment$ workon <env_name> # Turn on a virtual environment
For more information, see my quick & dirty howto.
• Quick & Dirty Virtualenv & Virtualenvwrapper
Python Packages & Brubeck
If you have pip installed, you can install everything with the requirements file.
$ cd ~/Desktop/brubeck$ pip install -I -r ./envs/brubeck.reqs
We now choose either eventlet or gevent and install the relevent requirements file in the same directory.
To install eventlet support:
$ pip install -I -r ./envs/eventlet.reqs
To install gevent support:
$ pip install -I -r ./envs/gevent.reqs
Note that gevent requires libevent, which should be available on the package-manager of your choice.
4 Chapter 1. Installing The Environment
brubeck Documentation, Release a
Brubeck Itself
As the last step, install Brubeck.
$ cd ~/Desktop/brubeck$ python setup.py install
1.4. Python Packages & Brubeck 5
brubeck Documentation, Release a
6 Chapter 1. Installing The Environment
CHAPTER 2
A Demo
Assuming the environment installation went well we can now turn on Brubeck.
First, we setup the Mongrel2 config.
$ cd ~/Desktop/brubeck/demos$ m2sh load -config mongrel2.conf -db the.db$ m2sh start -db the.db -host localhost
Now we’ll turn on a Brubeck instance.
$ cd ~/Desktop/brubeck/demos$ ./demo_minimal.py
If you see Brubeck v0.x.x online ]------------ we can try loading a URL in a browser. Now try a webrequest.
Mongrel2 Configuration
Mongrel2 is a separate process from Brubeck, so it is configured separately.
This is what the Mongrel2 configuration looks like for the demo project.
brubeck_handler = Handler(send_spec='ipc://127.0.0.1:9999',send_ident='34f9ceee-cd52-4b7f-b197-88bf2f0ec378',recv_spec='ipc://127.0.0.1:9998',recv_ident='')
brubeck_host = Host(name="localhost",routes={'/': brubeck_handler})
brubeck_serv = Server(
7
brubeck Documentation, Release a
uuid="f400bf85-4538-4f7a-8908-67e313d515c2",access_log="/log/mongrel2.access.log",error_log="/log/mongrel2.error.log",chroot="./",default_host="localhost",name="brubeck test",pid_file="/run/mongrel2.pid",port=6767,hosts = [brubeck_host])
settings = {"zeromq.threads": 1}
servers = [brubeck_serv]
In short: any requests for http://localhost:6767/ should be sent to the Brubeck handler.
Don’t forget that our Brubeck handler is only configured to answer http://localhost:6767/brubeck fornow. You could add another route once you’re comfortable building MessageHandler‘s
The web server answers requests on port 6767. It logs to the ./log directory. It also writes a pidfile in the ./rundirectory.
8 Chapter 2. A Demo
CHAPTER 3
Learn By Example
Each demo attempts to explain some of the nuances of Brubeck.
Each example should be run from inside the demos directory after Brubeck has been installed.
This document assumes you have already read the README. If you have not, please read that and come back after.
Abstract
We begin by building some knowledge of Mongrel2’s internals using sqlite3 and m2reader.py.
Then there are four sets of demos. The first set contains the two demos from the README that build request handlersusing classes or functions. Then we discuss how URL’s are mapped to handlers. Template rendering is then shownfor Jinja2, Tornado templates and Mako. This doc is then finished with an explanation of authentication over two finaldemos.
Kicking Mongrel2’s Tires
Each of these tests can be run underneath the same Mongrel2 instance. You can bring the handlers down and back upwithout taking Mongrel2 down.
First, we parse the config file into a sqlite database. Configuring the database this way makes the experience of editingconfigs as easy as editing text, but the database is stored in a programmatically friendly way too via SQLite.
There is no need to edit the config so we can just load the config into a database using m2sh load.
$ m2sh load -config mongrel2.conf -db the.db
Now we have a sqlite database representing our config. If you have sqlite installed, open the database and take a look.You can start by typing .tables at the prompt to get a table list.
9
brubeck Documentation, Release a
$ sqlite3 the.dbsqlite> .tablesdirectory host mimetype route settinghandler log proxy server statisticsqlite> select * from route;1|/|0|1|1|handler2|/media/|0|1|1|dir
We can then turn Mongrel2 on with m2sh start.
$ m2sh start -db the.db -host localhost... # lots of output[INFO] (src/handler.c:285) Binding handler PUSH socket ipc://127.0.0.1:9999 with→˓identity: 34f9ceee-cd52-4b7f-b197-88bf2f0ec378[INFO] (src/handler.c:311) Binding listener SUB socket ipc://127.0.0.1:9998→˓subscribed to:[INFO] (src/control.c:401) Setting up control socket in at ipc://run/control
OK. Mongrel2 is now listening on port 6767 and sending messages down a ZeroMQ push socket, ipc://127.0.0.1:9999
m2reader.py
Wanna see what Mongrel2 is actually saying? Turn on m2reader.py. It won’t respond with a proper web request,but you can see the entire JSON message passed to Brubeck from Mongrel2.
$ ./m2reader.py34f9ceee-cd52-4b7f-b197-88bf2f0ec378 0 / 571:{"PATH":"/","x-forwarded-for":"127.0.0.1→˓","accept-language":"en-US,en;q=0.8","accept-encoding":"gzip,deflate,sdch",→˓"connection":"keep-alive","accept-charset":"ISO-8859-1,utf-8;q=0.7,*;q=0.3","accept→˓":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8","user-agent":→˓"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.30 (KHTML, like→˓Gecko) Chrome/12.0.742.122 Safari/534.30","host":"localhost:6767","METHOD":"GET",→˓"VERSION":"HTTP/1.1","URI":"/","PATTERN":"/"},0:,
Brubeck’s job is to generate a response and send it to Mongrel2, which Mongrel2 then then forwards to our user.
10 Chapter 3. Learn By Example
CHAPTER 4
The Demos
On the agenda:
• Classes and Functions
• URL design and handling
• Template rendering
• Authentication
11
brubeck Documentation, Release a
12 Chapter 4. The Demos
CHAPTER 5
Classes And Functions
• https://github.com/j2labs/brubeck/blob/master/demos/demo_minimal.py
• https://github.com/j2labs/brubeck/blob/master/demos/demo_noclasses.py
As we saw in the README there are two ways of writing message handlers. demo_minimal.py implements aclass that implements a get() function to answer HTTP GET. demo_noclasses.py implements a function withit’s URL mapping specified with the add_route decorator.
13
brubeck Documentation, Release a
14 Chapter 5. Classes And Functions
CHAPTER 6
URL Design And Handling
• https://github.com/j2labs/brubeck/blob/master/demos/demo_urlargs.py
URL’s are matched by regular expression. Sometimes parameters we need are part of the URL. Here is a quick glanceat the URL’s used for this demo.
urls = [(r'^/class/(\w+)$', NameHandler),(r'^/fun/(?P<name>\w+)$', name_handler),(r'^/', IndexHandler)]
In spite of being the last URL listed above, IndexHandler is the first class defined. This class responds to HTTPGET with the string 'Take five!'. That’s it.
class IndexHandler(WebMessageHandler):def get(self):
self.set_body('Take five!')return self.render()
The next class, NameHandler, defines it’s get() function differently from IndexHandler. The new definitionincludes the parameter name. Notice that in the urls above we asign NameHandler to pattern '^/class/(\w+)$'.
Whatever matches (\w+) will be the value of the name argument below.
class NameHandler(WebMessageHandler):def get(self, name):
self.set_body('Take five, %s!' % (name))return self.render()
The third handler defined is not a class. This handler is defined as a function. And notice that it also has a nameargument tacked on.
def name_handler(application, message, name):return http_response('Take five, %s!' % (name), 200, 'OK', {})
We then map all three URL’s to the relevant handlers and instantiate a Brubeck instance.
15
brubeck Documentation, Release a
app = Brubeck(**config)
But hey, we’ll add one more function just because we still can.
The add_route decorator is now available to us on the Brubeck instance (app). Wrap any function with thisdecorator to assign it to a URL pattern and HTTP method. Passing parameters in URL’s works fine here too.
@app.add_route('^/deco/(?P<name>\w+)$', method='GET')def new_name_handler(application, message, name):
return http_response('Take five, %s!' % (name), 200, 'OK', {})
Then we turn it on by calling run() and all four URL’s can answer requests. Try this one: http://localhost:6767/class/james. Or this one: http://localhost:6767/fun/james. Or this one: http://localhost:6767/deco/james.
The only URL left is the boring one: http://localhost:6767/.
16 Chapter 6. URL Design And Handling
CHAPTER 7
Template Rendering
• https://github.com/j2labs/brubeck/blob/master/demos/demo_jinja2.py
• https://github.com/j2labs/brubeck/blob/master/demos/demo_tornado.py
• https://github.com/j2labs/brubeck/blob/master/demos/demo_mako.py
Template rendering is adequately covered as part of the README for now.
17
brubeck Documentation, Release a
18 Chapter 7. Template Rendering
CHAPTER 8
Authentication
Authentication comes in many forms. The first example will cover the basic system for authenticating requests. Thesecond demo will combine cookies, templates and a hard coded user to demonstrate a full login system.
Auth Over POST
• https://github.com/j2labs/brubeck/blob/master/demos/demo_auth.py
To place authentication restrictions on any function you can use the @authenticated decorator. The purpose of thisdecorator is tell the web server to fail with errors sent via the relevant protocol. When using a WebMessageHandlererrors will be sent as HTTP level errors. We will discuss another decorator @web_authenticated in the nextsection.
Here is what using it looks like.
@authenticateddef post(self):
...
For the purpose of the demonstration I hardcode a User instance with the username ‘jd’ and the password ‘foo’.Brubeck comes with a User and UserProfile model but we only use the User model here.
demo_user = User.create_user('jd', 'foo')
All get_current_user does is check the request arguments for a username and password and validate them.Brubeck makes the authenticated user available for you as self.current_user.
Let’s try it using it curl.
$ curl -d "username=jd&password=foo" localhost:6767/brubeckjd logged in successfully!
Now let’s see it fail. We will tell curl to fail silently, meaning it won’t print out any returned HTML, so we can see the401 error Brubeck returns.
19
brubeck Documentation, Release a
$ curl -f -d "username=jd&password=bar" localhost:6767/brubeckcurl: (22) The requested URL returned error: 401
Someone could build the first draft of an API using this example. All errors would be passed via HTTP.
Authenticated Website
• https://github.com/j2labs/brubeck/blob/master/demos/demo_login.py
This example is considerably more involved. Let’s look at the URL’s before we dig in.
handler_tuples = [(r'^/login', LoginHandler),(r'^/logout', LogoutHandler),(r'^/', LandingHandler),
]
We can probably guess that LoginHandler logs a user in and LogoutHandler logs a user out. But what happensif we visit http://localhost:6767/ before logging in?
Redirection
Try visiting http://localhost:6767 and you’ll be redirected to http://localhost:6767/login. This happens because wewrapped LoginHandler‘s get() method with the @web_authenticated decorator.
class LandingHandler(CustomAuthMixin, Jinja2Rendering):@web_authenticateddef get(self):
...
Failures to pass authentication are redirected to the application’s login_url, as specified in Brubeck’s config.
config = {...'login_url': '/login',
}
If you need to redirect a user to the login url at any point in your code, you could write the following.
return self.redirect(self.application.login_url)
The implementation of LoginHandler is straight forward. The get() method renders the login template withfields for a username and password. The implementation of post() has the @web_authenticated decora-tor on it, meaning it expects auth credentials to be provided. If the credentials pass post() then calls self.redirect('/') to send a logged-in user to the landing page.
Authentication Tracking
A cookie was set the first time @web_authenticated was called because we provided the correct username andpassword. This doesn’t happen automatically. It happened because of these two lines in get_current_user.
self.set_cookie('username', username) # DEMO: Don't actually put aself.set_cookie('password', password) # password in a cookie...
20 Chapter 8. Authentication
brubeck Documentation, Release a
Notice the comment suggesting you shouldn’t actually store a password in the cookie. This is done to keep the demofocused. Secure cookies will be covered soon..
Authenticated Browsing
Now that we’re logged in, LandingHandler let’s us call get() and it renders landing.html. It simply sayshello and offers a logout button.
Clicking logout sends us to http://localhost:6767/logout and LogoutHandler calls self.delete_cookies().We are no longer authenticated so it sends us the login screen when it’s finished.
Secure Cookies
Brubeck also supports secure cookies. This is what it looks like to use them.
Setting one:
self.set_cookie('user_id', username,secret=self.application.cookie_secret)
Reading one:
user_id = self.get_cookie('user_id',secret=self.application.cookie_secret)
The List Surf project features secure cookies in it’s authentication system.
8.2. Authenticated Website 21
brubeck Documentation, Release a
22 Chapter 8. Authentication
CHAPTER 9
Deploying
Brubeck can support Mongrel2 or WSGI.
Mongrel2
Mongrel2 is an asynchronous and language-agnostic (!) web server by Zed Shaw. Mongrel2 handles everythingrelevant to HTTP or Web Sockets and has facilities for passing request handling to external services via ZeroMQguide sockets.
This decoupling of the webserver from the request handling allows for interesting web service topologies. It alsoallows for easy scaling too, as servers can be added or taken down as necessary with restarting or HUPing anything.
If you are using Mongrel2, you will need to turn Mongrel2 on in addition to running a Brubeck process. This can be alittle tedious while developing, but it leads to efficient production deployment capabilities similar to that of HAProxyor Nginx.
Interacting with Mongrel2 is best done with the m2sh command.
$ m2sh load -config mongrel2.conf -db the.db$ m2sh start -db the.db -every
Mongrel2 is now running.
If you want Mongrel2 to run on port 80 you will need to use sudo. This also causes Mongrel2 to run in the backgroundand detach from the command shell. In this case, you can stop Mongrel2 using another m2sh command.
$ m2sh stop -db the.db -every
WSGI
Brubeck supports WSGI by way of it’s concurrency systems. This means you can put it behind Gunicorn or runBrubeck apps on Heroku.
23
brubeck Documentation, Release a
From an app design point of view, it is a one line change to specify a WSGI handler instead of a Mongrel2 handler.
• Gevent WSGI
• Eventlet WSGI
• Brubeck WSGI Demo
Deployment Environments
There are multiple ways to deploy Brubeck. A vanilla Ubuntu system on AWS or Linode can work well. A Herokudyno can work.
Quickness
Quickness is a project for experimenting. It helps experimenters by creating a simple environment for deploying bigideas, like Brubeck and all of it’s dependencies or Erlang or Clojure & Java & any other things worth having whenusing Clojure.
It is built with Ubuntu in mind and works nicely with Vagrant.
A typical Quickness install of Brubeck looks like this:
$ git clone https://github.com/j2labs/quickness.git$ source quickness/env/profileQ: quick_newQ: quick_install brubeck
Quickness is developed by the same folks that build Brubeck & DictShield. This deployment strategy uses Mongrel2as the web server. This involves compiling and installing both ZeroMQ and Mongrel2, but Quickness will handle allof that for you.
• Quickness
Heroku
To deploy to Heroku your app needs to be configured to use WSGI, which you’ll see in the snippet below, and y
Install Heroku Toolbelt
Prepare the project directory
$ mkdir herokuapp && cd herokuapp
Initialize our git repo and pull Brubeck in
$ git init$ git submodule add git://github.com/j2labs/brubeck.git brubeck$ git submodule init$ git submodule update
Initialize our Heroku app
$ heroku login$ heroku create --stack cedar
24 Chapter 9. Deploying
brubeck Documentation, Release a
Set up the environment
$ virtualenv --distribute venv$ source venv/bin/activate$ pip install dictshield ujson gevent$ pip freeze -l > requirements.txt
Create .gitignore.
$ cat .gitignorevenv
*.pyc
Create Procfile
$ cat Procileweb: python app.py
Create .env
$ cat .envPYTHONPATH=brubeck
Create app.py
import os
from brubeck.request_handling import Brubeck, WebMessageHandlerfrom brubeck.connections import WSGIConnection
class DemoHandler(WebMessageHandler):def get(self):
self.set_body("Hello, from Brubeck!")return self.render()
config = {'msg_conn': WSGIConnection(int(os.environ.get('PORT', 6767))),'handler_tuples': [
(r'^/', DemoHandler)]
}
if __name__ == '__main__':app = Brubeck(**config)app.run()
Try it out
$ foreman start
You should now be able to visit localhost:5000. Notice that this uses port 5000 instead of the usual 6767.
Is it working? Great! Let’s put it on Heroku
git add .git commit -m "init"git push heroku master
Seems like Heroku will clobber whatever PYTHONPATH you set when you first push a Python project, so set it now
9.3. Deployment Environments 25
brubeck Documentation, Release a
heroku config:add PYTHONPATH=/app/brubeck/:/app/
Navigate to your new Brubeck app on Heroku!
Gunicorn
Instructions coming soon.
26 Chapter 9. Deploying
CHAPTER 10
Copyright 2012 J2 Labs LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that thefollowing conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the followingdisclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the follow-ing disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY J2 Labs LLC ‘‘AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-NESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL J2 Labs LLC OR CON-TRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CON-SEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODSOR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSEDAND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUD-ING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVENIF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the authors and should not beinterpreted as representing official policies, either expressed or implied, of J2 Labs LLC.
27
brubeck Documentation, Release a
28 Chapter 10. Copyright 2012 J2 Labs LLC. All rights reserved.
CHAPTER 11
Features
Message Handlers
Let’s take a look at that demo handler from before.
class DemoHandler(WebMessageHandler):def get(self):
self.set_body('Take five')return self.render()
options = {'handler_tuples': [(r'^/', DemoHandler)],'msg_conn': WSGIConnection(port=6767),
}
29
brubeck Documentation, Release a
app = Brubeck(**options)app.run()
The DemoHandler class has a get() implementation, so we know that handler answers HTTP GET and thathandler is mapped to the root URL, ‘/’.
Brubeck is also configured to run as a WSGI server on port 6767. Turn the app on and it will answer requests athttp://localhost:6767.
Handling Requests
The framework can be used for different requirements. It can be lean and lightweight for high throughput or you canfatten it up and use it for rendering pages in a database backed CMS.
The general architecture of the system is to map requests for a specific URL to some callable for processing the request.The configuration attempts to match handlers to URL’s by inspecting a list of (url pattern, callable) tuples.First regex to match provides the callable.
Some people like to use classes as handlers. Some folks prefer to use functions. Brubeck supports both.
The HTTP methods allowed are: GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE, CONNECT.
MessageHandler Classes
When a class model is used, the class will be instantiated for the life of the request and then thrown away. This keepsour memory requirements nice and light.
Brubeck’s MessageHandler design is similar to what you see in Tornado, or web.py.
To answer HTTP GET requests, implement get() on a WebMessageHandler instance.
class DemoHandler(WebMessageHandler):def get(self):
self.set_body('Take five!')return self.render()
Then we add DemoHandler to the routing config and instantiate a Brubeck instance.
urls = [(r'^/brubeck', DemoHandler)]config = {
'handler_tuples': urls,...
}
Brubeck(**config).run()
Notice the url regex is ^/brubeck. This will put our handler code on http://hostname/brubeck. (Probablyhttp://localhost:6767/brubeck).
• Runnable demo
Functions and Decorators
If you’d prefer to just use a simple function, you instantiate a Brubeck instance and wrap your function with theadd_route decorator.
30 Chapter 11. Features
brubeck Documentation, Release a
Your function will be given two arguments. First, is the application itself. This provides the function with a hookalmost all the information it might need. The second argument, the message, provides all the information availableabout the request.
That looks like this:
app = Brubeck(mongrel2_pair=('ipc://127.0.0.1:9999','ipc://127.0.0.1:9998'))
@app.add_route('^/brubeck', method='GET')def foo(application, message):
return http_response('Take five!', 200, 'OK', {})
app.run()
• Runnable demo
Templates
Brubeck currently supports Jinja2, Tornado, Mako or Pystache templates.
Template support is contained in brubeck.templates as rendering handlers. Each handler will attach arender_template function to your handler and overwrite the default render_error to produce templatederrors messages.
Using a template system is then as easy as calling render_template with the template filename and some context,just like you’re used to.
Jinja2 Example
Using Jinja2 template looks like this.
11.2. Templates 31
brubeck Documentation, Release a
from brubeck.templating import Jinja2Rendering
class DemoHandler(WebMessageHandler, Jinja2Rendering):def get(self):
context = {'name': 'J2D2',
}return self.render_template('success.html', **context)
The corresponding HTML looks like this:
<html><head>
<title>Jinja2 Render</title></head><body>
<p>Take five, {{ name }}!</p></body></html>
• Runnable demo
• Demo templates
Template Loading
In addition to using a rendering handler, you need to provide the path to your templates.
That looks like this:
from brubeck.templating import load_jinja2_env
config = {template_loader=load_jinja2_env('./templates/jinja2')...
}
Using a function here keeps the config lightweight and flexible. template_loader needs to be some function thatreturns an environment.
Demos
• Jinja2 (Code, Templates)
• Mako (Code, Templates)
• Tornado (Code, Templates)
• Mustache (Code, Templates)
Is your favorite template system not in this list? Please take a look at the other implementations. It’s probably easy toadd support.
• brubeck.templating
32 Chapter 11. Features
brubeck Documentation, Release a
Data Modeling
Brubeck uses DictShield for modeling.
DictShield offers input validation and structuring without taking a stance on what database you should be using. Thereare many good reasons to use all kinds of databases. DictShield only cares about Python dictionaries. If you can getyour data into those, DictShield will handle the rest.
DictShield strives to be database agnostic in the same way that Mongrel2 is language agnostic.
• DictShield
A Look At The Code
Let’s say we’re going to store a BlogPost and all of it’s comments in a single structure. Maybe we’ll even keep a copyof the author information there too.
An Author will have some information we only want to share with the author, like the email address associated witha post. But we want every user to be able to see the author’s username and name, so those will be public fields.
class Author(EmbeddedDocument):name = StringField()username = StringField()email = EmailField()a_setting = BooleanField() # privateis_active = BooleanField() # private_private_fields=['is_active']_public_fields=['username', 'name']
A Comment will contain the comment text, username and email address of the commenter. We only show the emailaddress to the owner of the blog though, so it’s not listedd as a public field.
11.3. Data Modeling 33
brubeck Documentation, Release a
class Comment(EmbeddedDocument):text = StringField()username = StringField()email = EmailField()_public_fields=['username', 'text']
And now the BlogPost. It will have a title, content, author, a post date, the list of comments, and a flag for whetheror not it’s a deleted entry (eg. a tombstone).
class BlogPost(Document):title = StringField()content = StringField()author = EmbeddedDocumentField(Author)post_date = DateTimeField(default=datetime.datetime.now)comments = ListField(EmbeddedDocumentField(Comment))deleted = BooleanField()_private_fields=['personal_thoughts']_public_fields=['author', 'content', 'comments']
Notice that the BlogPost has a ListField containing a list of Comments objects. It also has anEmbededDocumentField anytime it’s using another DictShield model as the field’s value.
Using It
This is what it might look to instantiate the structures.
>>> author = Author(name='james', username='j2d2', email='[email protected]',... a_setting=True, is_active=True)>>> comment1 = Comment(text='This post was awesome!', username='bro',... email='[email protected]')>>> comment2 = Comment(text='This post is ridiculous', username='barbie',... email='[email protected]')>>> content = """Retro single-origin coffee chambray stumptown, scenester VHS... bicycle rights 8-bit keytar aesthetic cosby sweater photo booth. Gluten-free... trust fund keffiyeh dreamcatcher skateboard, williamsburg yr salvia tattooed... """>>> blogpost = BlogPost(title='Hipster Hodgepodge', author=author, content=content,... comments=[comment1, comment2], deleted=False)
We’d probably call to_python() to make the data suitable for saving in a database. This process converts the valuesexactly as they’re found into a dictionary of Python values.
>>> blogpost.to_python(){
'_types': ['BlogPost'],'_cls': 'BlogPost''post_date': datetime.datetime(2012, 4, 22, 13, 6, 50, 530609),'deleted': False,'title': u'Hipster Hodgepodge','content': u'Retro single-origin coffee chambray stumptown, scenester
→˓VHS\nbicycle rights 8-bit keytar aesthetic cosby sweater photo booth. Gluten-→˓free\ntrust fund keffiyeh dreamcatcher skateboard, williamsburg yr salvia tattooed\n→˓',
'author': {'username': u'j2d2','_types': ['Author'],
34 Chapter 11. Features
brubeck Documentation, Release a
'name': u'james','a_setting': True,'is_active': True,'_cls': 'Author','email': u'[email protected]'
},'comments': [
{'username': u'bro','text': u'This post was awesome!','_types': ['Comment'],'email': u'[email protected]','_cls': 'Comment'
},{
'username': u'barbie','text': u'This post is ridiculous','_types': ['Comment'],'email': u'[email protected]','_cls': 'Comment'
}],
}
DictShield also has the concept of an owner formalized in the make_*_ownersafe() function, which can serializeto either Python or JSON. Notice that the date is converted to iso8601 format too.
>>> BlogPost.make_json_ownersafe(blogpost){
"post_date": "2012-04-22T13:06:50.530609","deleted": false,"title": "Hipster Hodgepodge","content": "Retro single-origin coffee chambray stumptown, scenester VHS\nbicycle
→˓rights 8-bit keytar aesthetic cosby sweater photo booth. Gluten-free\ntrust fund→˓keffiyeh dreamcatcher skateboard, williamsburg yr salvia tattooed\n"
"author": {"username": "j2d2","a_setting": true,"name": "james","email": "[email protected]"
},"comments": [
{"username": "bro","text": "This post was awesome!","email": "[email protected]"
},{
"username": "barbie","text": "This post is ridiculous","email": "[email protected]"
}],
}
This is what the document looks like serialized for the general public. The same basic mechanism is at work as theother serilializations, but this has removed the data that is not for public consumption, like email addresses.
11.3. Data Modeling 35
brubeck Documentation, Release a
>>> BlogPost.make_json_publicsafe(blogpost){
"content": "Retro single-origin coffee chambray stumptown, scenester VHS\nbicycle→˓rights 8-bit keytar aesthetic cosby sweater photo booth. Gluten-free\ntrust fund→˓keffiyeh dreamcatcher skateboard, williamsburg yr salvia tattooed\n"
"author": {"username": "j2d2","name": "james"
},"comments": [
{"username": "bro","text": "This post was awesome!"
},{
"username": "barbie","text": "This post is ridiculous"
}],
}
Notice that in all of these cases, the permissions and the serialization was done recursively across the structures andembedded structures.
AutoAPI
Brubeck combines the metaprogramming in DictShield along with the assumption that REST is generally similar toCRUD to provide a mechanism for generating APIs from DictShield models.
There are two things that must be consider:
1. Data Processing
36 Chapter 11. Features
brubeck Documentation, Release a
2. Persistence
Data Processing
The data processing is essentially to provide GET, POST, PUT and DELETE for a document design. The documentprovides a mechanism for validating the ID of a document, which is useful for both GET and DELETE. It also providesthe mechanism for validating an entire document, as we’d expect to receive with either POST or PUT.
We could define a simple model to look like this:
class Todo(Document):completed = BooleanField(default=False)text = StringField(required=True)class Meta:
id_options = {'auto_fill': True}
DictShield provides a way to validate input against this structure. It also provides a way to format output, like whenwe serialize the structure to JSON, with some fields removed, based on what permissions are available to the user.
We’ll create a Todo instance.
>>> t = Todo(text='This is some text')>>> t.validate()True
Let’s serialize it to a Python dictionary. This is probably what we’d save in a database.
>>> t.to_python(){'_types': ['Todo'], 'text': u'This is some text', 'completed': False, '_id': UUID(→˓'c4ac6aff-737c-47db-ab07-fbe402b08d1c'), '_cls': 'Todo'}
Or maybe we’re just gonna store JSON in something.
>>> t.to_json()'{"_types": ["Todo"], "text": "This is some text", "completed": false, "_id":→˓"7e48a600-f599-4a3a-9244-73760841f70e", "_cls": "Todo"}'
It’s useful for APIs too, because you can combine one of it’s make_safe functions with whatever access rights theuser has. DictShield provides the concept of owner and public in the form of a blacklist and a whitelist respectively.
>>> Todo.make_json_ownersafe(t)'{"text": "This is some text", "completed": false}'>>>
If we provide GET and PUT we will need to handle ID fields. DictShield documents let us validate individual fields ifwe want. That looks like this:
>>> Todo.id.validate('c4ac6aff-737c-47db-ab07-fbe402b08d1c')UUID('c4ac6aff-737c-47db-ab07-fbe402b08d1c')
We can see that the returned value is the input coerced into the type of the field.
This is what failed validation looks like, notice that the input is a munged version of the input above.
>>> Todo.id.validate('c4ac6aff-737c-47db-ab07-fbe402b0c')Traceback (most recent call last):
File "<stdin>", line 1, in <module>File "/Users/jd/Projects/dictshield/dictshield/fields/base.py", line 178, in
→˓validate
11.4. AutoAPI 37
brubeck Documentation, Release a
self.field_name, value)dictshield.base.ShieldException: Not a valid UUID value - None:c4ac6aff-737c-47db-→˓ab07-fbe402b0c
Persistence
Persistence is then handled by way of a QuerySet. A dict based QuerySet, called DictQueryset, is provided bydefault. Other implementations for supporting MongoDB, Redis and MySQL are on the way.
The interface to the QuerySets is defined in the AbstractQueryset. We see some familiar names de-fined: create(), read(), update() and destroy(). These functions then either call create_one orcreate_many for each CRUD operation.
CRUD doesn’t map exactly to REST, but it’s close, so Brubeck attempts to accurately cover REST’s behavior usingCRUD operations. It’s not a 1:1 mapping.
The DictQueryset then subclasses AbstractQueryset and implements create_one, create_many, etc.These functions are focused primarily around a document’s ID. The ID, as provided by DictShield, is how we identifywhich documents should be deleted or updated or retrieved.
Putting Both Together
Putting the two together is a simple process.
First we import the persistence layer and define the data’s structure:
from brubeck.queryset import DictQueryset
class Todo(Document):completed = BooleanField(default=False)text = StringField(required=True)class Meta:
id_options = {'auto_fill': True}
Then we subclass AutoAPIBase and define two fields, queries and model. The model is our Document fromabove. The queries is whichever queryset we’re using.
class TodosAPI(AutoAPIBase):queries = DictQueryset()model = Todo
Setup a Brubeck instance as you normally would, but then register the AutoApi instance with the app.
app = Brubeck(...)app.register_api(TodosAPI)
Done.
Examples
Brubeck comes with an AutoAPI example that is slightly more elaborate than what we see above.
• AutoAPI Demo
38 Chapter 11. Features
brubeck Documentation, Release a
There is also an example where Brubeck’s AutoAPI is used in conjunction with the well known Todo list javascriptdemo.
• Todos
File Uploading
Brubeck supports file uploading as form-urlencoded or as multipart form data. It’s easy to upload a file to Brubeckusing curl.
$ cd brubeck/demos$ ./demo_multipart.py
In this demo we see code that finds each file uploaded in a field on the request message. That looks like this:
class UploadHandler(...):def post(self):
file_one = self.message.files['data'][0]i = Image.open(StringIO.StringIO(file_one['body']))i.save('word.png')...
This demo receives an image and writes it to the file system as word.png. It wouldn’t be much work to adjust thisto whatever your needs are.
The demo also uses PIL, so install that if you don’t already have it.
$ pip install PIL
Use sudo if necessary.
11.6. File Uploading 39
brubeck Documentation, Release a
Trying It
If you’re using Mongrel2, you’ll need to turn that on too. It works fine with WSGI too.
$ m2sh load -db the.db -config mongrel2.conf$ m2sh start -db the.db -every
OK. Now we can use curl to upload some image.
$ curl -F [email protected] http://localhost:6767/
The end result is that you’ll have an image called word.png written to the same directory as your Brubeck process.
QuerySets
There are times when a carefully crafted query is right and there are times when a simple CRUD interface can do thetrick. Brubeck provides querysets that implement a simple CRUD interface. This is useful for the AutoAPI, supportfor basic caching and a consistent way of handling database connections.
A Queryset is then an implementation of CRUD functions for a single item or multiple items. This type of interfacemakes the system consistent with the idea that data should be stored in a simple manner, eg. a key that maps to aparticular document.
If you need anything more complicated than that, it’s easy enough to go from the DictShield model into somethingthat will fit nicely with your custom query.
Querysets are an area of active development but are still young in implementation.
40 Chapter 11. Features
CHAPTER 12
Architecture
Concurrency
Brubeck is basically a pipeline of coroutines attempting to fulfill web requests. Each MessageHandler is executedas a coroutine. Greenlet’s, the coroutines in Brubeck, are optimized for fast context-switching.
Coroutines, combined with a scheduler (aka “a hub”), make for an interesting and lightweight alternative to threads.Greenlets are so lightweight that we don’t have to think too hard on how many we spawn, and Brubeck handles eachrequest as a single coroutine.
Brubeck supports Eventlet and Gevent. They are similar in design. Both use Greenlets for coroutines. Both provide amechanism for converting blocking network drivers into nonblocking. They both provide a schedular, aka a “hub”, toprovide thread-like behavior.
41
brubeck Documentation, Release a
The Flow
Processing flows from the incoming message to a function that processes that message into the form of a Request.This request will operate until it reaches some point of I/O, or, it completes.
Brubeck has a scheduler, like Twisted’s Reactor or Tornado’s IOLoop, but it’s behind the scenes. Being behind thescenes allows it to create a simple interface to nonblocking behavior, but can be confusing upfront.
If you’re reaching out to the database, Brubeck might go back and check for incoming messages. If you’re reachingout to some http service, Brubeck might check for incoming messages, or complete that other request that now hasdata from the database. In this sense, the context switching is implicit.
Brubeck can offer nonblocking access to:
• SSH w/ paramiko
• MySQL
• Postgres
• Redis w/ redis-py
• MongoDB w/ pymongo
• Riak w/ riak
• Memcache
Gevent
Gevent was started by Denis Bilenko and is written to use libevent. Gevent’s performance characteristics suggestit is very fast, stable and efficient on resources.
Install the envs/gevent.reqs to use gevent.
• Gevent
• Gevent Introduction
Extras:
• MySQL w/ ultramysql
• Postgres w/ psychopg
• Memcache w/ ultramemcache
Eventlet
Eventlet is distinct for being mostly in Python. It later added support for libevent too. Eventlet was started bydevelopers at Linden Labs and used to support Second Life.
Install envs/eventlet.reqs to use eventlet.
• Eventlet.
• Eventlet History
Extras:
• Database Connection Pooling
42 Chapter 12. Architecture
brubeck Documentation, Release a
Making A Decision
I tend to choose gevent. My tests have shown that it is significantly faster and lighter on resources than Eventlet.
If you have virtualenv, try experimenting and seeing which one you like best.
ZeroMQ
ZeroMQ, aka ZMQ, is essentially a sockets framework. It makes it easy to build messaging topologies across differenttypes of sockets. They also are language agnostic by way of having driver implementations in every language: Scheme,Java, C, Ruby, Haskell, Erlang; and Brubeck uses the Python driver.
It is common for service oriented architectures to be constructed with HTTP interfaces, but Brubeck believes ZMQ isa more suitable tool. It provides multiple message distribution patterns and lets you open multiple types of sockets.
Simple Examples
Here is a simple job distributor. It passes messages round-robin to any connected hosts.
import zmqimport time
ctx = zmq.Context()s = ctx.socket(zmq.PUSH)s.bind("ipc://hellostream:5678")
while True:s.send("hello")print 'Sending a hello'time.sleep(1)
12.2. ZeroMQ 43
brubeck Documentation, Release a
This what a simple consumer could look like. See what happens if you hook up multiple consumers.
import zmqimport datetime
ctx = zmq.Context()s = ctx.socket(zmq.PULL)s.connect("ipc://hellostream:5678")
while True:msg = s.recv()print 'Received:', msg
Brubeck and ZMQ
Brubeck can uses this system when it communicates with Mongrel2. It can also use this to talk to pools of workers, orAMQP servers, or data mining engines.
ZMQ is part of Brubeck’s concurrency pool, so working with it is just like working with any networked system. Whenyou use Brubeck with Mongrel2, you communicate with Mongrel2 over two ZMQ sockets.
There is a PUSH/PULL socket that Mongrel2 uses to send messages to handlers, like Brubeck. An added bonus is thatPUSH/PULL sockets automatically load balance requests between any connected handlers. Add another handler andit is automatically part of the round robin queue.
When the handlers are ready to respond, they use a PUB/SUB socket, meaning Mongrel2 subscribes to responses fromBrubeck handlers. This can be interesting for multiple reasons, such as having media served from a single Brubeckhandler to multiple Mongrel2 frontends.
Having two sockets allows for an interesting messaging topology between Mongrel2 and Brubeck. All of ZeroMQis available to you for communicating with workers too. You might enjoy building an image processing system inScheme and can do so by opening a ZeroMQ socket in your Scheme process to connect with a Brubeck socket.ZeroMQ is mostly language agnostic.
44 Chapter 12. Architecture
brubeck Documentation, Release a
Dependencies
Brubeck leverages a few awesome Python packages and some other stuff, mainly in C, for a significant piece of it’scapabilities. Credit must be given where credit is due.
Web Serving
Brubeck can support Mongrel2 or WSGI.
Mongrel2
Mongrel2 is an asynchronous and language-agnostic (!!) web server by Zed Shaw. Mongrel2 handles everythingrelevant to HTTP or Web Sockets and has facilities for passing request handling to external services via ZeroMQguide sockets.
This decoupling of the webserver from the request handling allows for interesting web service topologies. It alsoallows for easy scaling too, as servers can be added or taken down as necessary with restarting or HUPing anything.
WSGI
Brubeck also supports WSGI. This means you can put it behind Gunicorn or run Brubeck apps on Heroku.
WSGI support is provided by each of the concurrency options, which are described next.
Concurrency
Brubeck is basically a pipeline of coroutines attempting to fulfill web requests. Each MessageHandler is executedas a coroutine, implemented as a greenlet.
Greenlet’s are a Python implementation of coroutines optimized for fast context-switching. Greenlet’s can be thoughtof as similar to generators that don’t require a yield statement.
Coroutines, combined with a scheduler (aka “a hub”), make for an interesting and lightweight alternative to threads.Greenlets are so lightweight that we don’t have to think too hard on how many we spawn, and Brubeck handlers eachrequest as a single coroutine.
Eventlet
Eventlet is an implementation of a scheduling system. In addition to scheduling, it will convert your blocking callsinto nonblocking automatically as part of it’s scheduling.
This makes building nonblocking, asynchronous systems look the same as building blocking, synchronous systems.The kind that normally live in threads.
Eventlet was started by developers at Linden Labs and used to support Second Life.
Install envs/eventlet.reqs to use eventlet.
• Eventlet.
• Eventlet History
12.4. Dependencies 45
brubeck Documentation, Release a
Gevent
Gevent was started by Denis Bilenko as an alternative to Eventlet. It is similar in design but uses an event loopimplemented in C; libevent. It will be soon be on the newer libev.
Tests suggest that Gevent’s performance characteristics are both lightweight and very fast.
Install the envs/gevent.reqs to use gevent.
• Gevent
• Gevent Introduction
Alternatives
There are also reasonable arguments for explicit context switching. Or perhaps even a different language. If you preferthat model, I recommend the systems below:
• Twisted Project
• Node.js
• EventMachine
DictShield
DictShield offers input validation and structuring without taking a stance on what database you should be using. Thereare many good reasons to use all kinds of databases. DictShield only cares about Python dictionaries. If you can getyour data into those, DictShield will handle the rest.
DictShield strives to be database agnostic in the same way that Mongrel2 is language agnostic.
• DictShield
46 Chapter 12. Architecture
CHAPTER 13
API
brubeck Package
mod auth
mod caching
class brubeck.caching.BaseCacheStore(**kwargs)Bases: object
Ram based cache storage. Essentially uses a dictionary stored in the app to store cache id => serialized cachedata
delete(key)Remove all data for the key from storage.
delete_expired()Deletes sessions with timestamps in the past from storage.
load(key)Load the stored data from storage backend or return None if the session was not found. Stale cookies aretreated as empty.
save(key, data, expire=None)Save the cache data and metadata to the backend storage if necessary, as defined by self.dirty == True. Onsuccessful save set dirty to False.
class brubeck.caching.RedisCacheStore(redis_connection=None, **kwargs)Bases: brubeck.caching.BaseCacheStore
Redis cache using Redis’ EXPIRE command to set expiration time. delete_expired raises NotImplementedError.Pass the Redis connection instance as db_conn.
IMPORTANT NOTE:
47
brubeck Documentation, Release a
This caching store uses a flat namespace for storing keys since we cannot set an EXPIRE for a hash field. Usedifferent Redis databases to keep applications from overwriting keys of other applications.
The Redis connection uses the redis-py api located here: https://github.com/andymccurdy/redis-py
delete(key)
delete_expired()
load(key)return the value of key. If key does not exist or has expired, hget will return None
save(key, data, expire=None)expire will be a Unix timestamp from time.time() + <value> which is a value in seconds.
brubeck.caching.generate_session_id()Returns random 32 bit string for cache id
mod datamosh
mod models
mod mongrel2
mod queryset
mod request_handling
mod templating
mod timekeeping
48 Chapter 13. API
CHAPTER 14
Summary
• Code
What Is Brubeck?
Brubeck is a flexible Python web framework that aims to make the process of building scalable web services easy.
Brubeck’s design is discussed in depth in the provided documentation. There, you will find lots of code samples forbuilding request handlers, authentication, rendering templates, managing databases and more.
Goals
• Be Fast : Brubeck is currently very fast. We intend to keep it that way.
• Scalable : Massive scaling capabilities should be available out of the box.
• Friendly : Should be easy for Python hackers of any skill level to use.
• Pluggable : Brubeck can speak to any language and any database.
Example: Hello World
This is a whole Brubeck application.
class DemoHandler(WebMessageHandler):def get(self):
self.set_body('Hello world')return self.render()
49
brubeck Documentation, Release a
urls = [(r'^/', DemoHandler)]msg_conn = Mongrel2Connection('ipc://127.0.0.1:9999',
'ipc://127.0.0.1:9998')
app = Brubeck(msg_conn=msg_conn,handler_tuples=urls)
app.run()
Complete Examples
Listsurf is a simple to way to save links. Yeah... another delicious clone!
It serves as a basic demonstration of what a complete site looks like when you build with Brubeck. It has authenticationwith secure cookies, offers a JSON API, uses Jinja2 for templating and stores data in MongoDB .
• Listsurf Code
Readify is a more elaborate form of Listsurf.
User’s have profiles, you can mark things as liked, archived (out of your stream, kept) or you can delete them. Thelinks can also be tagged for easy finding. This project also splits the API out from the Web system into two separateprocesses, each reading from a single Mongrel2.
You could actually run four Web processes and four API processes as easily as just turning each of them on four times.
This project roughly represents a typical organization of Brubeck’s components. Most notably is the separation ofhandlers, models and queries into isolated python files.
• Readify Code
SpotiChat is a chat app for spotify user.
SpotiChat provides chat for users listening to the same song with Spotify. The chat is handled via request handlers thatgo to sleep until incoming messages need to be distributed to connect clients. The messages are backed by Redis too.
• SpotiChat Code
no.js is a javascript-free chat system.
It works by using the old META Refresh trick, combined with long-polling. It even works in IE4!
• No.js Code
Contributors
Brubeck wouldn’t be what it is without help from:
James Dennis , Andrew Gwozdziewycz , Malcolm Matalka , Dion Paragas , Duane Griffin , Faruk Akgul , SethMurphy , John Krauss , Ben Beecher , Jordan Orelli , Michael Larsen , Moritz , Dmitrijs Milajevs , Paul Winkler ,Chris McCulloh , Nico Mandery , Victor Trac
Contact Us
If you discover bugs or want to suggest features, please use our issue tracker .
Also consider joining our mailing list: brubeck-dev .
50 Chapter 14. Summary
brubeck Documentation, Release a
You can find some of us in #brubeck on freenode too.
14.4. Contact Us 51
brubeck Documentation, Release a
52 Chapter 14. Summary
Python Module Index
bbrubeck.caching, 47
53
brubeck Documentation, Release a
54 Python Module Index
Index
BBaseCacheStore (class in brubeck.caching), 47brubeck.caching (module), 47
Ddelete() (brubeck.caching.BaseCacheStore method), 47delete() (brubeck.caching.RedisCacheStore method), 48delete_expired() (brubeck.caching.BaseCacheStore
method), 47delete_expired() (brubeck.caching.RedisCacheStore
method), 48
Ggenerate_session_id() (in module brubeck.caching), 48
Lload() (brubeck.caching.BaseCacheStore method), 47load() (brubeck.caching.RedisCacheStore method), 48
RRedisCacheStore (class in brubeck.caching), 47
Ssave() (brubeck.caching.BaseCacheStore method), 47save() (brubeck.caching.RedisCacheStore method), 48
55