CherryPaste with Eggs

Mmmmm, what do I smell cooking? Yes I'll take a little cherry pie. Oh could you top it off with an egg and some glue? Perfect... The aim of this article is to explain how to use CherryPy with Paste and then how to deploy an egg using Paste as well. The article takes a very simple example and goes through the process. These steps took me a while because I wasn't very familiar with Paste or eggs, but now I feel a little more comfortable with them. Since a lot of this knowledge isn't collated into any one place I thought I'd share it here.

Contents

Edit

Why Paste?

One of my pet peeves with Python web frameworks is the issue of deployment. Because there are so many frameworks (you could blame this on python making it so easy to create a framework;) ) and so many different web servers, when you commit to a framework you are limiting yourself to that framework (and the web servers it supports). Yet there are useful tools and apps written in other frameworks. How do you take advantage of these? Paste is an attempt to take the WGSI abstraction (that should help alleviate this issue of framework server impedance mismatch) and make it easy to admin and deploy applications. Ideally using Paste one will soon be able to deploy many different types of WSGI compliant components easily and within the same server.

Edit

Install Dependencies

We have a few dependencies that we need in order for everything to work. Luckily there exists Setuptools which allows for really easy installation of packages using eggs (eggs can be though of as analogous to JAR files in Java). We will need to install setuptools, Paste, PasteScript, PasteDeploy, CherryPy, and CherryPaste.

Setuptools is easy to install just download ez_setup.py and run it:

python ez_setup.py

That should add an command, "easy_install" to your machine. Now you should be able to install most of these components doing

easy_install -Z Paste
easy_install -Z PasteScript
easy_install -Z PasteDeploy
easy_install -Z CherryPaste==dev

(Note I like to use the -Z option, so that the egg is unzipped as well. Otherwise the egg will actually be a zip file, which makes browsing the source a little tedious when it is necessary.)

I checked out the development version of CherryPaste. You will also need version 2.2 of CherryPy (I had to check it out from svn and run easy_install on the setup.py file it had):

svn co http://svn.cherrypy.org/trunk/ cherrypy
cd cherrypy
easy_install -Z .


Edit

Modifying the Cherrypy tutorial to run under Paste

For the purposes of this article I'm going to use the tut01_helloworld.py file included in the tutorial directory from the CherryPy distribution.

First lets run that as a CherryPy standalone application to test that CherryPy is working.

cd [directory of cherrypy tutorials]
python tut01_helloworld.py

If you were successful in doing this you should be able to go to http://localhost:8080/ and see a very simple website. The console from where you typed this should look something like this:

mharrison[1196] python tut01_helloworld.py 
20/Jan/2006:10:46:43 CONFIG INFO Server parameters:
20/Jan/2006:10:46:43 CONFIG INFO   server.environment: production
20/Jan/2006:10:46:43 CONFIG INFO   server.log_to_screen: True
20/Jan/2006:10:46:43 CONFIG INFO   server.log_file: 
20/Jan/2006:10:46:43 CONFIG INFO   server.log_tracebacks: True
20/Jan/2006:10:46:43 CONFIG INFO   server.log_request_headers: False
20/Jan/2006:10:46:43 CONFIG INFO   server.protocol_version: HTTP/1.0
20/Jan/2006:10:46:43 CONFIG INFO   server.socket_host: 
20/Jan/2006:10:46:43 CONFIG INFO   server.socket_port: 8080
20/Jan/2006:10:46:43 CONFIG INFO   server.socket_file: 
20/Jan/2006:10:46:43 CONFIG INFO   server.reverse_dns: False
20/Jan/2006:10:46:43 CONFIG INFO   server.socket_queue_size: 5
20/Jan/2006:10:46:43 CONFIG INFO   server.thread_pool: 10
20/Jan/2006:10:46:43 HTTP INFO Serving HTTP on http://localhost:8080/

Edit

Add some Paste in

Ok, now lets see if we can add Paste into this. What value does Paste add? Well, if you are using other apps under Paste, it makes the configuration for deployment identical. Another value is that it should allow you to run multiple applications in the same server. (CherryPy alone doesn't provide this functionality currently). First create a Paste configuration file (named config.ini) like the following:

[app:main]
use = egg:CherryPaste
root_object = tut01_helloworld:HelloWorld()

[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 8080

This tells Paste to use CherryPaste for the app we are deploying. For the root object, use an instance of the HelloWorld class found in the tut01_helloworld package.

It also tells Paste that for the main server, listen on the loopback address and serve on port 8080.

To deploy using paste type the following.

mharrison[1226] paster  serve config.ini 

Simple right? Hmmm, maybe not, mine crashes, complaining that it can't find helloworld.

mharrison[1221] paster  serve config.ini 
20/Jan/2006:10:54:38 CONFIG INFO Server parameters:
20/Jan/2006:10:54:38 CONFIG INFO   server.environment: development
20/Jan/2006:10:54:38 CONFIG INFO   server.log_to_screen: True
20/Jan/2006:10:54:38 CONFIG INFO   server.log_file: 
20/Jan/2006:10:54:38 CONFIG INFO   server.log_tracebacks: True
20/Jan/2006:10:54:38 CONFIG INFO   server.log_request_headers: True
20/Jan/2006:10:54:38 CONFIG INFO   server.protocol_version: HTTP/1.0
20/Jan/2006:10:54:38 CONFIG INFO   server.socket_host: 
20/Jan/2006:10:54:38 CONFIG INFO   server.socket_port: 8080
20/Jan/2006:10:54:38 CONFIG INFO   server.socket_file: 
20/Jan/2006:10:54:38 CONFIG INFO   server.reverse_dns: False
20/Jan/2006:10:54:38 CONFIG INFO   server.socket_queue_size: 5
20/Jan/2006:10:54:38 CONFIG INFO   server.thread_pool: 0
Traceback (most recent call last):
  File "/data1/virtualpython/bin/paster", line 5, in ?
    pkg_resources.run_script('PasteScript==0.4.1', 'paster')
  File "/data1/virtualpython/lib/python2.3/site-packages/
setuptools-0.6a10dev_r42047-py2.3.egg/pkg_resources.py", line 407, in run_script
    self.require(requires)[0].run_script(script_name, ns)
  File "/data1/virtualpython/lib/python2.3/site-packages/
setuptools-0.6a10dev_r42047-py2.3.egg/pkg_resources.py", line 920, in run_script
    execfile(script_filename, namespace, namespace)
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteScript-0.4.1-py2.3.egg/EGG-INFO/scripts/paster", line 18, in ?
    command.run()
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteScript-0.4.1-py2.3.egg/paste/script/command.py", line 67, in run
    invoke(command, command_name, options, args[1:])
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteScript-0.4.1-py2.3.egg/paste/script/command.py", line 107, in invoke
    exit_code = runner.run(args)
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteScript-0.4.1-py2.3.egg/paste/script/command.py", line 202, in run
    result = self.command()
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteScript-0.4.1-py2.3.egg/paste/script/serve.py", line 170, in command
    relative_to=base)
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteDeploy-0.4-py2.3.egg/paste/deploy/loadwsgi.py", line 183, in loadapp
    return loadobj(APP, uri, name=name, **kw)
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteDeploy-0.4-py2.3.egg/paste/deploy/loadwsgi.py", line 204, in loadobj
    return context.create()
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteDeploy-0.4-py2.3.egg/paste/deploy/loadwsgi.py", line 574, in create
    return self.object_type.invoke(self)
  File "/data1/virtualpython/lib/python2.3/site-packages/
PasteDeploy-0.4-py2.3.egg/paste/deploy/loadwsgi.py", line 93, in invoke
    return context.object(context.global_conf, **context.local_conf)
  File "/data1/virtualpython/lib/python2.3/site-packages/
CherryPaste-0.0dev_r4526-py2.3.egg/cherrypaste/pastify.py", line 92, in make_app
    root_object = import_string.eval_import(root_object)
  File "/data1/virtualpython/lib/python2.3/site-packages/
Paste-0.4-py2.3.egg/paste/util/import_string.py", line 20, in eval_import
    module = import_module(module_name)
  File "/data1/virtualpython/lib/python2.3/site-packages/
Paste-0.4-py2.3.egg/paste/util/import_string.py", line 49, in import_module
    mod = __import__(s)
ImportError: No module named helloworld

After some more perusal of the documentation, I ended up going to the Paste mailing list for a solution. Ian Bicking was kind enough to nudge me in the right direction. The python sys.path variable (you can think of this as somewhat analagous to CLASSPATH if you are coming from a Java world) normally contains the local directory, but this in NOT the case when using Paste, the local directory (it shows up as '' when you print sys.path) is removed from sys.path. To adjust for this set PYTHONPATH appropriately (or put helloworld in another directory that sys.path scans for files). Since I'm running the command from the same directory that contains my python code and I'm using bash I do the following:

export PYTHONPATH=.

Now I run the paster command again, and I'm serving up pages using Paste.

mharrison[1229] paster  serve config.ini 
20/Jan/2006:11:04:19 CONFIG INFO Server parameters:
20/Jan/2006:11:04:19 CONFIG INFO   server.environment: development
20/Jan/2006:11:04:19 CONFIG INFO   server.log_to_screen: True
20/Jan/2006:11:04:19 CONFIG INFO   server.log_file: 
20/Jan/2006:11:04:19 CONFIG INFO   server.log_tracebacks: True
20/Jan/2006:11:04:19 CONFIG INFO   server.log_request_headers: True
20/Jan/2006:11:04:19 CONFIG INFO   server.protocol_version: HTTP/1.0
20/Jan/2006:11:04:19 CONFIG INFO   server.socket_host: 
20/Jan/2006:11:04:19 CONFIG INFO   server.socket_port: 8080
20/Jan/2006:11:04:19 CONFIG INFO   server.socket_file: 
20/Jan/2006:11:04:19 CONFIG INFO   server.reverse_dns: False
20/Jan/2006:11:04:19 CONFIG INFO   server.socket_queue_size: 5
20/Jan/2006:11:04:19 CONFIG INFO   server.thread_pool: 0
Starting server in PID 19902.
serving on 127.0.0.1:8080

Edit

Stir in some eggs

Now we want to package up our application as an egg and deploy that egg using paste. Why? Those coming from a Java world could think of an egg as analogous to a WAR file. Ie, it's one file that contains everything the app needs to run. How would we do deploy using eggs? The egg documentation is pretty good, but it doesn't give an example. We are in luck though, because Eggs are basically a superset of the standard library distutils. To create an egg, create a setup.py file just like you would do if you were using distutils. And if you browse through the distutils documentation you will find a very simple example of creating a setup.py file. This example is basically all we need to create our egg. My setup.py file looks like this:

from setuptools import setup

setup(name="helloworld",
      version="0.1",
      py_modules=["tut01_helloworld"]
      )

Basically, this simple file says, the name of our package will be helloworld and it includes the tut01_helloworld.py file in it. (I also gave it version information).

To create an egg from this do the following:

python setup.py bdist_egg

This will create a build and a dist. In the dist (distribution) directory should be your newly laid egg. Update your PYTHONPATH so that the directory containing the egg is on it or install the egg like this:

easy_install setup.py

The configuration file also needs to be changed to know about the egg. We need to add a require key to it like this:

[app:main]
use = egg:CherryPaste
require = helloworld
root_object = tut01_helloworld:HelloWorld

[server:main]
use = egg:Paste#http
host = 127.0.0.1
port = 8080

You should be able to use the same paster command and now serve the simple page from an egg.

Edit

Conclusion

The purpose of this article is to provide a very walkthrough of using Paste and eggs together. Much thanks to Ian Bicking for his wonderful work in developing and evangelizing paste as well as his patience in answering my questions. Feel free to email Matt (mharrison@spikesource.com) with any questions or suggestions.


Most Recent

Most Popular

Most Active Categories




Back To Top Add New Article Printable Page
MediaWiki

This page has been accessed 4,283 times.

This page was last modified 00:42, 27 January 2006.