CherryPy - A Minimalist Python Web Framework¶
Foreword¶
Why CherryPy?¶
CherryPy is among the oldest web framework available for Python, yet many people aren’t aware of its existence. One of the reason for this is that CherryPy is not a complete stack with built-in support for a multi-tier architecture. It doesn’t provide frontend utilities nor will it tell you how to speak with your storage. Instead, CherryPy’s take is to let the developer make those decisions. This is a contrasting position compared to other well-known frameworks.
CherryPy has a clean interface and does its best to stay out of your way whilst providing a reliable scaffolding for you to build from.
Typical use-cases for CherryPy go from regular web application with user frontends (think blogging, CMS, portals, ecommerce) to web-services only.
Here are some reasons you would want to choose CherryPy:
Simplicity
Developing with CherryPy is a simple task. “Hello, world” is only a few lines long, and does not require the developer to learn the entire (albeit very manageable) framework all at once. The framework is very pythonic; that is, it follows Python’s conventions very nicely (code is sparse and clean).
Contrast this with J2EE and Python’s most popular and visible web frameworks: Django, Zope, Pylons, and Turbogears. In all of them, the learning curve is massive. In these frameworks, “Hello, world” requires the programmer to set up a large scaffold which spans multiple files and to type a lot of boilerplate code. CherryPy succeeds because it does not include the bloat of other frameworks, allowing the programmer to write their web application quickly while still maintaining a high level of organization and scalability.
CherryPy is also very modular. The core is fast and clean, and extension features are easy to write and plug in using code or the elegant config system. The primary components (server, engine, request, response, etc.) are all extendable (even replaceable) and well-managed.
In short, CherryPy empowers the developer to work with the framework, not against or around it.
Power
CherryPy leverages all of the power of Python. Python is a dynamic language which allows for rapid development of applications. Python also has an extensive built-in API which simplifies web app development. Even more extensive, however, are the third-party libraries available for Python. These range from object-relational mappers to form libraries, to an automatic Python optimizer, a Windows exe generator, imaging libraries, email support, HTML templating engines, etc. CherryPy applications are just like regular Python applications. CherryPy does not stand in your way if you want to use these brilliant tools.
CherryPy also provides tools and plugins, which are powerful extension points needed to develop world-class web applications.
Maturity
Maturity is extremely important when developing a real-world application. Unlike many other web frameworks, CherryPy has had many final, stable releases. It is fully bugtested, optimized, and proven reliable for real-world use. The API will not suddenly change and break backwards compatibility, so your applications are assured to continue working even through subsequent updates in the current version series.
CherryPy is also a “3.0” project: the first edition of CherryPy set the tone, the second edition made it work, and the third edition makes it beautiful. Each version built on lessons learned from the previous, bringing the developer a superior tool for the job.
Community
CherryPy has an devoted community that develops deployed CherryPy applications and are willing and ready to assist you on the CherryPy mailing list or IRC (#cherrypy on OFTC). The developers also frequent the list and often answer questions and implement features requested by the end-users.
Deployability
Unlike many other Python web frameworks, there are cost-effective ways to deploy your CherryPy application.
Out of the box, CherryPy includes its own production-ready HTTP server to host your application. CherryPy can also be deployed on any WSGI-compliant gateway (a technology for interfacing numerous types of web servers): mod_wsgi, FastCGI, SCGI, IIS, uwsgi, tornado, etc. Reverse proxying is also a common and easy way to set it up.
In addition, CherryPy is pure-python and is compatible with Python 2.3. This means that CherryPy will run on all major platforms that Python will run on (Windows, MacOSX, Linux, BSD, etc).
webfaction.com, run by the inventor of CherryPy, is a commercial web host that offers CherryPy hosting packages (in addition to several others).
It’s free!
All of CherryPy is licensed under the open-source BSD license, which means CherryPy can be used commercially for ZERO cost.
Where to go from here?
Check out the tutorials to start enjoying the fun!
Success Stories¶
You are interested in CherryPy but you would like to hear more from people using it, or simply check out products or application running it.
If you would like to have your CherryPy powered website or product listed here, contact us via our mailing list or IRC (#cherrypy on OFTC).
Websites running atop CherryPy¶
Hulu Deejay and Hulu Sod - Hulu uses CherryPy for some projects. “The service needs to be very high performance. Python, together with CherryPy, gunicorn, and gevent more than provides for this.”
Netflix - Netflix uses CherryPy as a building block in their infrastructure: “Restful APIs to large applications with requests, providing web interfaces with CherryPy and Bottle, and crunching data with scipy.”
Urbanility - French website for local neighbourhood assets in Rennes, France.
MROP Supply - Webshop for industrial equipment, developed using CherryPy 3.2.2 utilizing Python 3.2, with libs: Jinja2-2.6, davispuh-MySQL-for-Python-3-3403794, pyenchant-1.6.5 (for search spelling). “I’m coming over from .net development and found Python and CherryPy to be surprisingly minimalistic. No unnecessary overhead - build everything you need without the extra fluff. I’m a fan!”
CherryMusic - A music streaming server written in python: Stream your own music collection to all your devices! CherryMusic is open source.
YouGov Global - International market research firm, conducts millions of surveys on CherryPy yearly.
Aculab Cloud - Voice and fax applications on the cloud. A simple telephony API for Python, C#, C++, VB, etc... The website and all front-end and back-end web services are built with CherryPy, fronted by nginx (just handling the ssh and reverse-proxy), and running on AWS in two regions.
Learnit Training - Dutch website for an IT, Management and Communication training company. Built on CherryPy 3.2.0 and Python 2.7.3, with oursql and DBUtils libraries, amongst others.
Linstic - Sticky Notes in your browser (with linking).
Almad’s Homepage - Simple homepage with blog.
Fight.Watch - Twitch.tv web portal for fighting games. Built on CherryPy 3.3.0 and Python 2.7.3 with Jinja 2.7.2 and SQLAlchemy 0.9.4.
Products based on CherryPy¶
SABnzbd - Open Source Binary Newsreader written in Python.
Headphones - Third-party add-on for SABnzbd.
SickBeard - “Sick Beard is a PVR for newsgroup users (with limited torrent support). It watches for new episodes of your favorite shows and when they are posted it downloads them, sorts and renames them, and optionally generates metadata for them.”
TurboGears - The rapid web development megaframework. Turbogears 1.x used Cherrypy. “CherryPy is the underlying application server for TurboGears. It is responsible for taking the requests from the user’s browser, parses them and turns them into calls into the Python code of the web application. Its role is similar to application servers used in other programming languages”.
Indigo - “An intelligent home control server that integrates home control hardware modules to provide control of your home. Indigo’s built-in Web server and client/server architecture give you control and access to your home remotely from other Macs, PCs, internet tablets, PDAs, and mobile phones.”
SlikiWiki - Wiki built on CherryPy and featuring WikiWords, automatic backlinking, site map generation, full text search, locking for concurrent edits, RSS feed embedding, per page access control lists, and page formatting using PyTextile markup.”
read4me - read4me is a Python feed-reading web service.
Firebird QA tools - Firebird QA tools are based on CherryPy.
salt-api - A REST API for Salt, the infrastructure orchestration tool.
Installation¶
CherryPy is a pure Python library. This has various consequences:
- It can run anywhere Python runs
- It does not require a C compiler
- It can run on various implementations of the Python language: CPython, IronPython, Jython and PyPy
Contents
Requirements¶
CherryPy does not have any mandatory requirements. However certain features it comes with
will require you install certain packages. To simplify installing additional
dependencies CherryPy enables you to specify extras in your requirements (e.g.
cherrypy[json,routes_dispatcher,ssl]
):
- doc – for documentation related stuff
- json – for custom JSON processing library
- routes_dispatcher – routes for declarative URL mapping dispatcher
- ssl – for OpenSSL bindings, useful in Python environments not having the builtin ssl
module
- testing
- memcached_session – enables memcached backend session
- xcgi
Supported python version¶
CherryPy supports Python 2.7 through to 3.5.
Installing¶
CherryPy can be easily installed via common Python package managers such as setuptools or pip.
$ easy_install cherrypy
$ pip install cherrypy
You may also get the latest CherryPy version by grabbing the source code from Github:
$ git clone https://github.com/cherrypy/cherrypy
$ cd cherrypy
$ python setup.py install
Test your installation¶
CherryPy comes with a set of simple tutorials that can be executed once you have deployed the package.
$ python -m cherrypy.tutorial.tut01_helloworld
Point your browser at http://127.0.0.1:8080 and enjoy the magic.
Once started the above command shows the following logs:
[15/Feb/2014:21:51:22] ENGINE Listening for SIGHUP.
[15/Feb/2014:21:51:22] ENGINE Listening for SIGTERM.
[15/Feb/2014:21:51:22] ENGINE Listening for SIGUSR1.
[15/Feb/2014:21:51:22] ENGINE Bus STARTING
[15/Feb/2014:21:51:22] ENGINE Started monitor thread 'Autoreloader'.
[15/Feb/2014:21:51:22] ENGINE Started monitor thread '_TimeoutMonitor'.
[15/Feb/2014:21:51:22] ENGINE Serving on http://127.0.0.1:8080
[15/Feb/2014:21:51:23] ENGINE Bus STARTED
We will explain what all those lines mean later on, but suffice to know that once you see the last two lines, your server is listening and ready to receive requests.
Run it¶
During development, the easiest path is to run your application as follow:
$ python myapp.py
As long as myapp.py defines a “__main__” section, it will run just fine.
cherryd¶
Another way to run the application is through the cherryd
script
which is installed along side CherryPy.
Note
This utility command will not concern you if you embed your application with another framework.
Command-Line Options¶
-
-c
,
--config
¶
Specify config file(s)
-
-d
¶
Run the server as a daemon
-
-e
,
--environment
¶
Apply the given config environment (defaults to None)
-
-s
¶
Start a SCGI server instead of the default HTTP server
-
-i
,
--import
¶
Specify modules to import
-
-p
,
--pidfile
¶
Store the process id in the given file (defaults to None)
-
-P
,
--Path
¶
Add the given paths to sys.path
Tutorials¶
This tutorial will walk you through basic but complete CherryPy applications that will show you common concepts as well as slightly more advanced ones.
Contents
- Tutorials
- Tutorial 1: A basic web application
- Tutorial 2: Different URLs lead to different functions
- Tutorial 3: My URLs have parameters
- Tutorial 4: Submit this form
- Tutorial 5: Track my end-user’s activity
- Tutorial 6: What about my javascripts, CSS and images?
- Tutorial 7: Give us a REST
- Tutorial 8: Make it smoother with Ajax
- Tutorial 9: Data is all my life
- Tutorial 10: Make it a modern single-page application with React.js
- Tutorial 11: Organize my code
Tutorial 1: A basic web application¶
The following example demonstrates the most basic application you could write with CherryPy. It starts a server and hosts an application that will be served at request reaching http://127.0.0.1:8080/
1 2 3 4 5 6 7 8 9 10 11 | import cherrypy
class HelloWorld(object):
@cherrypy.expose
def index(self):
return "Hello world!"
if __name__ == '__main__':
cherrypy.quickstart(HelloWorld())
|
Store this code snippet into a file named tut01.py and execute it as follows:
$ python tut01.py
This will display something along the following:
1 2 3 4 5 6 7 8 9 10 11 | [24/Feb/2014:21:01:46] ENGINE Listening for SIGHUP.
[24/Feb/2014:21:01:46] ENGINE Listening for SIGTERM.
[24/Feb/2014:21:01:46] ENGINE Listening for SIGUSR1.
[24/Feb/2014:21:01:46] ENGINE Bus STARTING
CherryPy Checker:
The Application mounted at '' has an empty config.
[24/Feb/2014:21:01:46] ENGINE Started monitor thread 'Autoreloader'.
[24/Feb/2014:21:01:46] ENGINE Started monitor thread '_TimeoutMonitor'.
[24/Feb/2014:21:01:46] ENGINE Serving on http://127.0.0.1:8080
[24/Feb/2014:21:01:46] ENGINE Bus STARTED
|
This tells you several things. The first three lines indicate
the server will handle signal
for you. The next line tells you
the current state of the server, as that
point it is in STARTING stage. Then, you are notified your
application has no specific configuration set to it.
Next, the server starts a couple of internal utilities that
we will explain later. Finally, the server indicates it is now
ready to accept incoming communications as it listens on
the address 127.0.0.1:8080. In other words, at that stage your
application is ready to be used.
Before moving on, let’s discuss the message regarding the lack of configuration. By default, CherryPy has a feature which will review the syntax correctness of settings you could provide to configure the application. When none are provided, a warning message is thus displayed in the logs. That log is harmless and will not prevent CherryPy from working. You can refer to the documentation above to understand how to set the configuration.
Tutorial 2: Different URLs lead to different functions¶
Your applications will obviously handle more than a single URL. Let’s imagine you have an application that generates a random string each time it is called:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import random
import string
import cherrypy
class StringGenerator(object):
@cherrypy.expose
def index(self):
return "Hello world!"
@cherrypy.expose
def generate(self):
return ''.join(random.sample(string.hexdigits, 8))
if __name__ == '__main__':
cherrypy.quickstart(StringGenerator())
|
Save this into a file named tut02.py and run it as follows:
$ python tut02.py
Go now to http://localhost:8080/generate and your browser will display a random string.
Let’s take a minute to decompose what’s happening here. This is the URL that you have typed into your browser: http://localhost:8080/generate
This URL contains various parts:
- http:// which roughly indicates it’s a URL using the HTTP protocol (see RFC 2616).
- localhost:8080 is the server’s address. It’s made of a hostname and a port.
- /generate which is the path segment of the URL. This is what CherryPy uses to locate an exposed function or method to respond.
Here CherryPy uses the index() method to handle / and the generate() method to handle /generate
Tutorial 3: My URLs have parameters¶
In the previous tutorial, we have seen how to create an application that could generate a random string. Let’s now assume you wish to indicate the length of that string dynamically.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import random
import string
import cherrypy
class StringGenerator(object):
@cherrypy.expose
def index(self):
return "Hello world!"
@cherrypy.expose
def generate(self, length=8):
return ''.join(random.sample(string.hexdigits, int(length)))
if __name__ == '__main__':
cherrypy.quickstart(StringGenerator())
|
Save this into a file named tut03.py and run it as follows:
$ python tut03.py
Go now to http://localhost:8080/generate?length=16 and your browser will display a generated string of length 16. Notice how we benefit from Python’s default arguments’ values to support URLs such as http://localhost:8080/generate still.
In a URL such as this one, the section after ? is called a query-string. Traditionally, the query-string is used to contextualize the URL by passing a set of (key, value) pairs. The format for those pairs is key=value. Each pair being separated by a & character.
Notice how we have to convert the given length value to an integer. Indeed, values are sent out from the client to our server as strings.
Much like CherryPy maps URL path segments to exposed functions, query-string keys are mapped to those exposed function parameters.
Tutorial 4: Submit this form¶
CherryPy is a web framework upon which you build web applications. The most traditional shape taken by applications is through an HTML user-interface speaking to your CherryPy server.
Let’s see how to handle HTML forms via the following example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import random
import string
import cherrypy
class StringGenerator(object):
@cherrypy.expose
def index(self):
return """<html>
<head></head>
<body>
<form method="get" action="generate">
<input type="text" value="8" name="length" />
<button type="submit">Give it now!</button>
</form>
</body>
</html>"""
@cherrypy.expose
def generate(self, length=8):
return ''.join(random.sample(string.hexdigits, int(length)))
if __name__ == '__main__':
cherrypy.quickstart(StringGenerator())
|
Save this into a file named tut04.py and run it as follows:
$ python tut04.py
Go now to http://localhost:8080/ and your browser and this will display a simple input field to indicate the length of the string you want to generate.
Notice that in this example, the form uses the GET method and when you pressed the Give it now! button, the form is sent using the same URL as in the previous tutorial. HTML forms also support the POST method, in that case the query-string is not appended to the URL but it sent as the body of the client’s request to the server. However, this would not change your application’s exposed method because CherryPy handles both the same way and uses the exposed’s handler parameters to deal with the query-string (key, value) pairs.
Tutorial 5: Track my end-user’s activity¶
It’s not uncommon that an application needs to follow the user’s activity for a while. The usual mechanism is to use a session identifier that is carried during the conversation between the user and your application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import random
import string
import cherrypy
class StringGenerator(object):
@cherrypy.expose
def index(self):
return """<html>
<head></head>
<body>
<form method="get" action="generate">
<input type="text" value="8" name="length" />
<button type="submit">Give it now!</button>
</form>
</body>
</html>"""
@cherrypy.expose
def generate(self, length=8):
some_string = ''.join(random.sample(string.hexdigits, int(length)))
cherrypy.session['mystring'] = some_string
return some_string
@cherrypy.expose
def display(self):
return cherrypy.session['mystring']
if __name__ == '__main__':
conf = {
'/': {
'tools.sessions.on': True
}
}
cherrypy.quickstart(StringGenerator(), '/', conf)
|
Save this into a file named tut05.py and run it as follows:
$ python tut05.py
In this example, we generate the string as in the previous tutorial but also store it in the current session. If you go to http://localhost:8080/, generate a random string, then go to http://localhost:8080/display, you will see the string you just generated.
The lines 30-34 show you how to enable the session support in your CherryPy application. By default, CherryPy will save sessions in the process’s memory. It supports more persistent backends as well.
Tutorial 6: What about my javascripts, CSS and images?¶
Web applications are usually also made of static content such as javascript, CSS files or images. CherryPy provides support to serve static content to end-users.
Let’s assume, you want to associate a stylesheet with your application to display a blue background color (why not?).
First, save the following stylesheet into a file named style.css and stored into a local directory public/css.
1 2 3 | body {
background-color: blue;
}
|
Now let’s update the HTML code so that we link to the stylesheet using the http://localhost:8080/static/css/style.css URL.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | import os, os.path
import random
import string
import cherrypy
class StringGenerator(object):
@cherrypy.expose
def index(self):
return """<html>
<head>
<link href="/static/css/style.css" rel="stylesheet">
</head>
<body>
<form method="get" action="generate">
<input type="text" value="8" name="length" />
<button type="submit">Give it now!</button>
</form>
</body>
</html>"""
@cherrypy.expose
def generate(self, length=8):
some_string = ''.join(random.sample(string.hexdigits, int(length)))
cherrypy.session['mystring'] = some_string
return some_string
@cherrypy.expose
def display(self):
return cherrypy.session['mystring']
if __name__ == '__main__':
conf = {
'/': {
'tools.sessions.on': True,
'tools.staticdir.root': os.path.abspath(os.getcwd())
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': './public'
}
}
cherrypy.quickstart(StringGenerator(), '/', conf)
|
Save this into a file named tut06.py and run it as follows:
$ python tut06.py
Going to http://localhost:8080/, you should be greeted by a flashy blue color.
CherryPy provides support to serve a single file or a complete directory structure. Most of the time, this is what you’ll end up doing so this is what the code above demonstrates. First, we indicate the root directory of all of our static content. This must be an absolute path for security reason. CherryPy will complain if you provide only relative paths when looking for a match to your URLs.
Then we indicate that all URLs which path segment starts with /static will be served as static content. We map that URL to the public directory, a direct child of the root directory. The entire sub-tree of the public directory will be served as static content. CherryPy will map URLs to path within that directory. This is why /static/css/style.css is found in public/css/style.css.
Tutorial 7: Give us a REST¶
It’s not unusual nowadays that web applications expose some sort of datamodel or computation functions. Without going into its details, one strategy is to follow the REST principles edicted by Roy T. Fielding.
Roughly speaking, it assumes that you can identify a resource and that you can address that resource through that identifier.
“What for?” you may ask. Well, mostly, these principles are there to ensure that you decouple, as best as you can, the entities your application expose from the way they are manipulated or consumed. To embrace this point of view, developers will usually design a web API that expose pairs of (URL, HTTP method, data, constraints).
Note
You will often hear REST and web API together. The former is one strategy to provide the latter. This tutorial will not go deeper in that whole web API concept as it’s a much more engaging subject, but you ought to read more about it online.
Lets go through a small example of a very basic web API mildly following REST principles.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import random
import string
import cherrypy
@cherrypy.expose
class StringGeneratorWebService(object):
@cherrypy.tools.accept(media='text/plain')
def GET(self):
return cherrypy.session['mystring']
def POST(self, length=8):
some_string = ''.join(random.sample(string.hexdigits, int(length)))
cherrypy.session['mystring'] = some_string
return some_string
def PUT(self, another_string):
cherrypy.session['mystring'] = another_string
def DELETE(self):
cherrypy.session.pop('mystring', None)
if __name__ == '__main__':
conf = {
'/': {
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
'tools.sessions.on': True,
'tools.response_headers.on': True,
'tools.response_headers.headers': [('Content-Type', 'text/plain')],
}
}
cherrypy.quickstart(StringGeneratorWebService(), '/', conf)
|
Save this into a file named tut07.py and run it as follows:
$ python tut07.py
Before we see it in action, let’s explain a few things. Until now, CherryPy was creating a tree of exposed methods that were used to match URLs. In the case of our web API, we want to stress the role played by the actual requests’ HTTP methods. So we created methods that are named after them and they are all exposed at once by decorating the class itself with cherrypy.expose.
However, we must then switch from the default mechanism of matching
URLs to method for one that is aware of the whole HTTP method
shenanigan. This is what goes on line 27 where we create
a MethodDispatcher
instance.
Then we force the responses content-type to be text/plain and we finally ensure that GET requests will only be responded to clients that accept that content-type by having a Accept: text/plain header set in their request. However, we do this only for that HTTP method as it wouldn’t have much meaning on the other methods.
For the purpose of this tutorial, we will be using a Python client rather than your browser as we wouldn’t be able to actually try our web API otherwise.
Please install requests through the following command:
$ pip install requests
Then fire up a Python terminal and try the following commands:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | >>> import requests
>>> s = requests.Session()
>>> r = s.get('http://127.0.0.1:8080/')
>>> r.status_code
500
>>> r = s.post('http://127.0.0.1:8080/')
>>> r.status_code, r.text
(200, u'04A92138')
>>> r = s.get('http://127.0.0.1:8080/')
>>> r.status_code, r.text
(200, u'04A92138')
>>> r = s.get('http://127.0.0.1:8080/', headers={'Accept': 'application/json'})
>>> r.status_code
406
>>> r = s.put('http://127.0.0.1:8080/', params={'another_string': 'hello'})
>>> r = s.get('http://127.0.0.1:8080/')
>>> r.status_code, r.text
(200, u'hello')
>>> r = s.delete('http://127.0.0.1:8080/')
>>> r = s.get('http://127.0.0.1:8080/')
>>> r.status_code
500
|
The first and last 500 responses stem from the fact that, in the first case, we haven’t yet generated a string through POST and, on the latter case, that it doesn’t exist after we’ve deleted it.
Lines 12-14 show you how the application reacted when our client requested the generated string as a JSON format. Since we configured the web API to only support plain text, it returns the appropriate HTTP error code.
Note
We use the Session interface of requests so that it takes care of carrying the session id stored in the request cookie in each subsequent request. That is handy.
Important
It’s all about RESTful URLs these days, isn’t it?
It is likely your URL will be made of dynamic parts that you
will not be able to match to page handlers. For example,
/library/12/book/15
cannot be directly handled by the
default CherryPy dispatcher since the segments 12
and
15
will not be matched to any Python callable.
This can be easily workaround with two handy CherryPy features explained in the advanced section.
Tutorial 8: Make it smoother with Ajax¶
In the recent years, web applications have moved away from the simple pattern of “HTML forms + refresh the whole page”. This traditional scheme still works very well but users have become used to web applications that don’t refresh the entire page. Broadly speaking, web applications carry code performed client-side that can speak with the backend without having to refresh the whole page.
This tutorial will involve a little more code this time around. First, let’s see our CSS stylesheet located in public/css/style.css.
1 2 3 4 5 6 7 | body {
background-color: blue;
}
#the-string {
display: none;
}
|
We’re adding a simple rule about the element that will display the generated string. By default, let’s not show it up. Save the following HTML code into a file named index.html.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | <!DOCTYPE html>
<html>
<head>
<link href="/static/css/style.css" rel="stylesheet">
<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#generate-string").click(function(e) {
$.post("/generator", {"length": $("input[name='length']").val()})
.done(function(string) {
$("#the-string").show();
$("#the-string input").val(string);
});
e.preventDefault();
});
$("#replace-string").click(function(e) {
$.ajax({
type: "PUT",
url: "/generator",
data: {"another_string": $("#the-string input").val()}
})
.done(function() {
alert("Replaced!");
});
e.preventDefault();
});
$("#delete-string").click(function(e) {
$.ajax({
type: "DELETE",
url: "/generator"
})
.done(function() {
$("#the-string").hide();
});
e.preventDefault();
});
});
</script>
</head>
<body>
<input type="text" value="8" name="length"/>
<button id="generate-string">Give it now!</button>
<div id="the-string">
<input type="text" />
<button id="replace-string">Replace</button>
<button id="delete-string">Delete it</button>
</div>
</body>
</html>
|
We’ll be using the jQuery framework out of simplicity but feel free to replace it with your favourite tool. The page is composed of simple HTML elements to get user input and display the generated string. It also contains client-side code to talk to the backend API that actually performs the hard work.
Finally, here’s the application’s code that serves the HTML page above and responds to requests to generate strings. Both are hosted by the same application server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | import os, os.path
import random
import string
import cherrypy
class StringGenerator(object):
@cherrypy.expose
def index(self):
return open('index.html')
@cherrypy.expose
class StringGeneratorWebService(object):
@cherrypy.tools.accept(media='text/plain')
def GET(self):
return cherrypy.session['mystring']
def POST(self, length=8):
some_string = ''.join(random.sample(string.hexdigits, int(length)))
cherrypy.session['mystring'] = some_string
return some_string
def PUT(self, another_string):
cherrypy.session['mystring'] = another_string
def DELETE(self):
cherrypy.session.pop('mystring', None)
if __name__ == '__main__':
conf = {
'/': {
'tools.sessions.on': True,
'tools.staticdir.root': os.path.abspath(os.getcwd())
},
'/generator': {
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
'tools.response_headers.on': True,
'tools.response_headers.headers': [('Content-Type', 'text/plain')],
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': './public'
}
}
webapp = StringGenerator()
webapp.generator = StringGeneratorWebService()
cherrypy.quickstart(webapp, '/', conf)
|
Save this into a file named tut08.py and run it as follows:
$ python tut08.py
Go to http://127.0.0.1:8080/ and play with the input and buttons to generate, replace or delete the strings. Notice how the page isn’t refreshed, simply part of its content.
Notice as well how your frontend converses with the backend using a straightfoward, yet clean, web service API. That same API could easily be used by non-HTML clients.
Tutorial 9: Data is all my life¶
Until now, all the generated strings were saved in the session, which by default is stored in the process memory. Though, you can persist sessions on disk or in a distributed memory store, this is not the right way of keeping your data on the long run. Sessions are there to identify your user and carry as little amount of data as necessary for the operation carried by the user.
To store, persist and query data you need a proper database server. There exist many to choose from with various paradigm support:
- relational: PostgreSQL, SQLite, MariaDB, Firebird
- column-oriented: HBase, Cassandra
- key-store: redis, memcached
- document oriented: Couchdb, MongoDB
- graph-oriented: neo4j
Let’s focus on the relational ones since they are the most common and probably what you will want to learn first.
For the sake of reducing the number of dependencies for these
tutorials, we will go for the sqlite
database which
is directly supported by Python.
Our application will replace the storage of the generated string from the session to a SQLite database. The application will have the same HTML code as tutorial 08. So let’s simply focus on the application code itself:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | import os, os.path
import random
import sqlite3
import string
import time
import cherrypy
DB_STRING = "my.db"
class StringGenerator(object):
@cherrypy.expose
def index(self):
return open('index.html')
@cherrypy.expose
class StringGeneratorWebService(object):
@cherrypy.tools.accept(media='text/plain')
def GET(self):
with sqlite3.connect(DB_STRING) as c:
cherrypy.session['ts'] = time.time()
r = c.execute("SELECT value FROM user_string WHERE session_id=?",
[cherrypy.session.id])
return r.fetchone()
def POST(self, length=8):
some_string = ''.join(random.sample(string.hexdigits, int(length)))
with sqlite3.connect(DB_STRING) as c:
cherrypy.session['ts'] = time.time()
c.execute("INSERT INTO user_string VALUES (?, ?)",
[cherrypy.session.id, some_string])
return some_string
def PUT(self, another_string):
with sqlite3.connect(DB_STRING) as c:
cherrypy.session['ts'] = time.time()
c.execute("UPDATE user_string SET value=? WHERE session_id=?",
[another_string, cherrypy.session.id])
def DELETE(self):
cherrypy.session.pop('ts', None)
with sqlite3.connect(DB_STRING) as c:
c.execute("DELETE FROM user_string WHERE session_id=?",
[cherrypy.session.id])
def setup_database():
"""
Create the `user_string` table in the database
on server startup
"""
with sqlite3.connect(DB_STRING) as con:
con.execute("CREATE TABLE user_string (session_id, value)")
def cleanup_database():
"""
Destroy the `user_string` table from the database
on server shutdown.
"""
with sqlite3.connect(DB_STRING) as con:
con.execute("DROP TABLE user_string")
if __name__ == '__main__':
conf = {
'/': {
'tools.sessions.on': True,
'tools.staticdir.root': os.path.abspath(os.getcwd())
},
'/generator': {
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
'tools.response_headers.on': True,
'tools.response_headers.headers': [('Content-Type', 'text/plain')],
},
'/static': {
'tools.staticdir.on': True,
'tools.staticdir.dir': './public'
}
}
cherrypy.engine.subscribe('start', setup_database)
cherrypy.engine.subscribe('stop', cleanup_database)
webapp = StringGenerator()
webapp.generator = StringGeneratorWebService()
cherrypy.quickstart(webapp, '/', conf)
|
Save this into a file named tut09.py and run it as follows:
$ python tut09.py
Let’s first see how we create two functions that create and destroy the table within our database. These functions are registered to the CherryPy’s server on lines 85-86, so that they are called when the server starts and stops.
Next, notice how we replaced all the session code with calls to the database. We use the session id to identify the user’s string within our database. Since the session will go away after a while, it’s probably not the right approach. A better idea would be to associate the user’s login or more resilient unique identifier. For the sake of our demo, this should do.
Important
In this example, we must still set the session to a dummy value so that the session is not discarded on each request by CherryPy. Since we now use the database to store the generated string, we simply store a dummy timestamp inside the session.
Note
Unfortunately, sqlite in Python forbids us to share a connection between threads. Since CherryPy is a multi-threaded server, this would be an issue. This is the reason why we open and close a connection to the database on each call. This is clearly not really production friendly, and it is probably advisable to either use a more capable database engine or a higher level library, such as SQLAlchemy, to better support your application’s needs.
Tutorial 10: Make it a modern single-page application with React.js¶
In the recent years, client-side single-page applications (SPA) have gradually eaten server-side generated content web applications’s lunch.
This tutorial demonstrates how to integrate with React.js, a Javascript library for SPA released by Facebook in 2013. Please refer to React.js documentation to learn more about it.
To demonstrate it, let’s use the code from tutorial 09. However, we will be replacing the HTML and Javascript code.
First, let’s see how our HTML code has changed:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <!DOCTYPE html>
<html>
<head>
<link href="/static/css/style.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.min.js"></script>
</head>
<body>
<div id="generator"></div>
<script type="text/babel" src="static/js/gen.js"></script>
</body>
</html>
|
Basically, we have removed the entire Javascript code that was using jQuery.
Instead, we load the React.js library as well as a new, local,
Javascript module, named gen.js
and located in the public/js
directory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | var StringGeneratorBox = React.createClass({
handleGenerate: function() {
var length = this.state.length;
this.setState(function() {
$.ajax({
url: this.props.url,
dataType: 'text',
type: 'POST',
data: {
"length": length
},
success: function(data) {
this.setState({
length: length,
string: data,
mode: "edit"
});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url,
status, err.toString()
);
}.bind(this)
});
});
},
handleEdit: function() {
var new_string = this.state.string;
this.setState(function() {
$.ajax({
url: this.props.url,
type: 'PUT',
data: {
"another_string": new_string
},
success: function() {
this.setState({
length: new_string.length,
string: new_string,
mode: "edit"
});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url,
status, err.toString()
);
}.bind(this)
});
});
},
handleDelete: function() {
this.setState(function() {
$.ajax({
url: this.props.url,
type: 'DELETE',
success: function() {
this.setState({
length: "8",
string: "",
mode: "create"
});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url,
status, err.toString()
);
}.bind(this)
});
});
},
handleLengthChange: function(length) {
this.setState({
length: length,
string: "",
mode: "create"
});
},
handleStringChange: function(new_string) {
this.setState({
length: new_string.length,
string: new_string,
mode: "edit"
});
},
getInitialState: function() {
return {
length: "8",
string: "",
mode: "create"
};
},
render: function() {
return (
<div className="stringGenBox">
<StringGeneratorForm onCreateString={this.handleGenerate}
onReplaceString={this.handleEdit}
onDeleteString={this.handleDelete}
onLengthChange={this.handleLengthChange}
onStringChange={this.handleStringChange}
mode={this.state.mode}
length={this.state.length}
string={this.state.string}/>
</div>
);
}
});
var StringGeneratorForm = React.createClass({
handleCreate: function(e) {
e.preventDefault();
this.props.onCreateString();
},
handleReplace: function(e) {
e.preventDefault();
this.props.onReplaceString();
},
handleDelete: function(e) {
e.preventDefault();
this.props.onDeleteString();
},
handleLengthChange: function(e) {
e.preventDefault();
var length = React.findDOMNode(this.refs.length).value.trim();
this.props.onLengthChange(length);
},
handleStringChange: function(e) {
e.preventDefault();
var string = React.findDOMNode(this.refs.string).value.trim();
this.props.onStringChange(string);
},
render: function() {
if (this.props.mode == "create") {
return (
<div>
<input type="text" ref="length" defaultValue="8" value={this.props.length} onChange={this.handleLengthChange} />
<button onClick={this.handleCreate}>Give it now!</button>
</div>
);
} else if (this.props.mode == "edit") {
return (
<div>
<input type="text" ref="string" value={this.props.string} onChange={this.handleStringChange} />
<button onClick={this.handleReplace}>Replace</button>
<button onClick={this.handleDelete}>Delete it</button>
</div>
);
}
return null;
}
});
React.render(
<StringGeneratorBox url="/generator" />,
document.getElementById('generator')
);
|
Wow! What a lot of code for something so simple, isn’t it?
The entry point is the last few lines where we indicate that we
want to render the HTML code of the StringGeneratorBox
React.js
class inside the generator
div.
When the page is rendered, so is that component. Notice how it is also made of another component that renders the form itself.
This might be a little over the top for such a simple example but hopefully will get you started with React.js in the process.
There is not much to say and, hopefully, the meaning of that code is rather clear. The component has an internal state in which we store the current string as generated/modified by the user.
When the user changes the content of the input boxes, the state is updated on the client side. Then, when a button is clicked, that state is sent out to the backend server using the API endpoint and the appropriate action takes places. Then, the state is updated and so is the view.
Tutorial 11: Organize my code¶
CherryPy comes with a powerful architecture that helps you organizing your code in a way that should make it easier to maintain and more flexible.
Several mechanisms are at your disposal, this tutorial will focus on the three main ones:
In order to understand them, let’s imagine you are at a superstore:
- You have several tills and people queuing for each of them (those are your requests)
- You have various sections with food and other stuff (these are your data)
- Finally you have the superstore people and their daily tasks to make sure sections are always in order (this is your backend)
In spite of being really simplistic, this is not far from how your application behaves. CherryPy helps you structure your application in a way that mirrors these high-level ideas.
Dispatchers¶
Coming back to the superstore example, it is likely that you will want to perform operations based on the till:
- Have a till for baskets with less than ten items
- Have a till for disabled people
- Have a till for pregnant women
- Have a till where you can only using the store card
To support these use-cases, CherryPy provides a mechanism called a dispatcher. A dispatcher is executed early during the request processing in order to determine which piece of code of your application will handle the incoming request. Or, to continue on the store analogy, a dispatcher will decide which till to lead a customer to.
Tools¶
Let’s assume your store has decided to operate a discount spree but, only for a specific category of customers. CherryPy will deal with such use case via a mechanism called a tool.
A tool is a piece of code that runs on a per-request basis in order to perform additional work. Usually a tool is a simple Python function that is executed at a given point during the process of the request by CherryPy.
Plugins¶
As we have seen, the store has a crew of people dedicated to manage the stock and deal with any customers’ expectation.
In the CherryPy world, this translates into having functions that run outside of any request life-cycle. These functions should take care of background tasks, long lived connections (such as those to a database for instance), etc.
Plugins are called that way because they work along with the CherryPy engine and extend it with your operations.
Basics¶
The following sections will drive you through the basics of a CherryPy application, introducing some essential concepts.
Contents
The one-minute application example¶
The most basic application you can write with CherryPy involves almost all its core concepts.
1 2 3 4 5 6 7 8 9 | import cherrypy
class Root(object):
@cherrypy.expose
def index(self):
return "Hello World!"
if __name__ == '__main__':
cherrypy.quickstart(Root(), '/')
|
First and foremost, for most tasks, you will never need more than a single import statement as demonstrated in line 1.
Before discussing the meat, let’s jump to line 9 which shows, how to host your application with the CherryPy application server and serve it with its builtin HTTP server at the ‘/’ path. All in one single line. Not bad.
Let’s now step back to the actual application. Even though CherryPy
does not mandate it, most of the time your applications
will be written as Python classes. Methods of those classes will
be called by CherryPy to respond to client requests. However,
CherryPy needs to be aware that a method can be used that way, we
say the method needs to be exposed. This is precisely
what the cherrypy.expose()
decorator does in line 4.
Save the snippet in a file named myapp.py and run your first CherryPy application:
$ python myapp.py
Then point your browser at http://127.0.0.1:8080. Tada!
Note
CherryPy is a small framework that focuses on one single task: take a HTTP request and locate the most appropriate Python function or method that match the request’s URL. Unlike other well-known frameworks, CherryPy does not provide a built-in support for database access, HTML templating or any other middleware nifty features.
In a nutshell, once CherryPy has found and called an exposed method, it is up to you, as a developer, to provide the tools to implement your application’s logic.
CherryPy takes the opinion that you, the developer, know best.
Warning
The previous example demonstrated the simplicty of the CherryPy interface but, your application will likely contain a few other bits and pieces: static service, more complex structure, database access, etc. This will be developed in the tutorial section.
CherryPy is a minimal framework but not a bare one, it comes with a few basic tools to cover common usages that you would expect.
Hosting one or more applications¶
A web application needs an HTTP server to be accessed to. CherryPy provides its own, production ready, HTTP server. There are two ways to host an application with it. The simple one and the almost-as-simple one.
Single application¶
The most straightforward way is to use cherrypy.quickstart()
function. It takes at least one argument, the instance of the
application to host. Two other settings are optionals. First, the
base path at which the application will be accessible from. Second,
a config dictionary or file to configure your application.
cherrypy.quickstart(Blog())
cherrypy.quickstart(Blog(), '/blog')
cherrypy.quickstart(Blog(), '/blog', {'/': {'tools.gzip.on': True}})
The first one means that your application will be available at http://hostname:port/ whereas the other two will make your blog application available at http://hostname:port/blog. In addition, the last one provides specific settings for the application.
Note
Notice in the third case how the settings are still relative to the application, not where it is made available at, hence the {‘/’: ... } rather than a {‘/blog’: ... }
Multiple applications¶
The cherrypy.quickstart()
approach is fine for a single application,
but lacks the capacity to host several applications with the server.
To achieve this, one must use the cherrypy.tree.mount
function as follows:
cherrypy.tree.mount(Blog(), '/blog', blog_conf)
cherrypy.tree.mount(Forum(), '/forum', forum_conf)
cherrypy.engine.start()
cherrypy.engine.block()
Essentially, cherrypy.tree.mount
takes the same parameters as cherrypy.quickstart()
: an application,
a hosting path segment and a configuration. The last two lines
are simply starting application server.
Important
cherrypy.quickstart()
and cherrypy.tree.mount
are not exclusive. For instance, the previous lines can be written as:
cherrypy.tree.mount(Blog(), '/blog', blog_conf)
cherrypy.quickstart(Forum(), '/forum', forum_conf)
Note
You can also host foreign WSGI application.
Logging¶
Logging is an important task in any application. CherryPy will log all incoming requests as well as protocol errors.
To do so, CherryPy manages two loggers:
- an access one that logs every incoming requests
- an application/error log that traces errors or other application-level messages
Your application may leverage that second logger by calling
cherrypy.log()
.
cherrypy.log("hello there")
You can also log an exception:
try:
...
except:
cherrypy.log("kaboom!", traceback=True)
Both logs are writing to files identified by the following keys in your configuration:
log.access_file
for incoming requests using the common log formatlog.error_file
for the other log
See also
Refer to the cherrypy._cplogging
module for more
details about CherryPy’s logging architecture.
Disable logging¶
You may be interested in disabling either logs.
To disable file logging, simply set a en empty string to the
log.access_file
or log.error_file
keys in your
global configuration.
To disable, console logging, set log.screen
to False.
cherrypy.config.update({'log.screen': False,
'log.access_file': '',
'log.error_file': ''})
Play along with your other loggers¶
Your application may obviously already use the logging
module to trace application level messages. Below is a simple
example on setting it up.
import logging
import logging.config
import cherrypy
logger = logging.getLogger()
db_logger = logging.getLogger('db')
LOG_CONF = {
'version': 1,
'formatters': {
'void': {
'format': ''
},
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s: %(message)s'
},
},
'handlers': {
'default': {
'level':'INFO',
'class':'logging.StreamHandler',
'formatter': 'standard',
'stream': 'ext://sys.stdout'
},
'cherrypy_console': {
'level':'INFO',
'class':'logging.StreamHandler',
'formatter': 'void',
'stream': 'ext://sys.stdout'
},
'cherrypy_access': {
'level':'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'void',
'filename': 'access.log',
'maxBytes': 10485760,
'backupCount': 20,
'encoding': 'utf8'
},
'cherrypy_error': {
'level':'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'void',
'filename': 'errors.log',
'maxBytes': 10485760,
'backupCount': 20,
'encoding': 'utf8'
},
},
'loggers': {
'': {
'handlers': ['default'],
'level': 'INFO'
},
'db': {
'handlers': ['default'],
'level': 'INFO' ,
'propagate': False
},
'cherrypy.access': {
'handlers': ['cherrypy_access'],
'level': 'INFO',
'propagate': False
},
'cherrypy.error': {
'handlers': ['cherrypy_console', 'cherrypy_error'],
'level': 'INFO',
'propagate': False
},
}
}
class Root(object):
@cherrypy.expose
def index(self):
logger.info("boom")
db_logger.info("bam")
cherrypy.log("bang")
return "hello world"
if __name__ == '__main__':
cherrypy.config.update({'log.screen': False,
'log.access_file': '',
'log.error_file': ''})
cherrypy.engine.unsubscribe('graceful', cherrypy.log.reopen_files)
logging.config.dictConfig(LOG_CONF)
cherrypy.quickstart(Root())
In this snippet, we create a configuration dictionary
that we pass on to the logging
module to configure
our loggers:
- the default root logger is associated to a single stream handler
- a logger for the db backend with also a single stream handler
In addition, we re-configure the CherryPy loggers:
- the top-level
cherrypy.access
logger to log requests into a file- the
cherrypy.error
logger to log everything else into a file and to the console
We also prevent CherryPy from trying to open its log files when the autoreloader kicks in. This is not strictly required since we do not even let CherryPy open them in the first place. But, this avoids wasting time on something useless.
Configuring¶
CherryPy comes with a fine-grained configuration mechanism and settings can be set at various levels.
See also
Once you have the reviewed the basics, please refer to the in-depth discussion around configuration.
Global server configuration¶
To configure the HTTP and application servers,
use the cherrypy.config.update()
method.
cherrypy.config.update({'server.socket_port': 9090})
The cherrypy.config
object is a dictionary and the
update method merges the passed dictionary into it.
You can also pass a file instead (assuming a server.conf file):
[global]
server.socket_port: 9090
cherrypy.config.update("server.conf")
Warning
cherrypy.config.update()
is not meant to be used to configure the application.
It is a common mistake. It is used to configure the server and engine.
Per-application configuration¶
To configure your application, pass in a dictionary or a file when you associate your application to the server.
cherrypy.quickstart(myapp, '/', {'/': {'tools.gzip.on': True}})
or via a file (called app.conf for instance):
[/]
tools.gzip.on: True
cherrypy.quickstart(myapp, '/', "app.conf")
Although, you can define most of your configuration in a global fashion, it is sometimes convenient to define them where they are applied in the code.
class Root(object):
@cherrypy.expose
@cherrypy.tools.gzip()
def index(self):
return "hello world!"
A variant notation to the above:
class Root(object):
@cherrypy.expose
def index(self):
return "hello world!"
index._cp_config = {'tools.gzip.on': True}
Both methods have the same effect so pick the one that suits your style best.
Additional application settings¶
You can add settings that are not specific to a request URL and retrieve them from your page handler as follows:
[/]
tools.gzip.on: True
[googleapi]
key = "..."
appid = "..."
class Root(object):
@cherrypy.expose
def index(self):
google_appid = cherrypy.request.app.config['googleapi']['appid']
return "hello world!"
cherrypy.quickstart(Root(), '/', "app.conf")
Cookies¶
CherryPy uses the Cookie
module from python and in particular the
Cookie.SimpleCookie
object type to handle cookies.
- To send a cookie to a browser, set
cherrypy.response.cookie[key] = value
. - To retrieve a cookie sent by a browser, use
cherrypy.request.cookie[key]
. - To delete a cookie (on the client side), you must send the cookie with its expiration time set to 0:
cherrypy.response.cookie[key] = value
cherrypy.response.cookie[key]['expires'] = 0
It’s important to understand that the request cookies are not automatically
copied to the response cookies. Clients will send the same cookies on every
request, and therefore cherrypy.request.cookie
should be populated each
time. But the server doesn’t need to send the same cookies with every response;
therefore, cherrypy.response.cookie
will usually be empty. When you wish
to “delete” (expire) a cookie, therefore, you must set
cherrypy.response.cookie[key] = value
first, and then set its expires
attribute to 0.
Extended example:
import cherrypy
class MyCookieApp(object):
@cherrypy.expose
def set(self):
cookie = cherrypy.response.cookie
cookie['cookieName'] = 'cookieValue'
cookie['cookieName']['path'] = '/'
cookie['cookieName']['max-age'] = 3600
cookie['cookieName']['version'] = 1
return "<html><body>Hello, I just sent you a cookie</body></html>"
@cherrypy.expose
def read(self):
cookie = cherrypy.request.cookie
res = """<html><body>Hi, you sent me %s cookies.<br />
Here is a list of cookie names/values:<br />""" % len(cookie)
for name in cookie.keys():
res += "name: %s, value: %s<br>" % (name, cookie[name].value)
return res + "</body></html>"
if __name__ == '__main__':
cherrypy.quickstart(MyCookieApp(), '/cookie')
Using sessions¶
Sessions are one of the most common mechanism used by developers to identify users and synchronize their activity. By default, CherryPy does not activate sessions because it is not a mandatory feature to have, to enable it simply add the following settings in your configuration:
[/]
tools.sessions.on: True
cherrypy.quickstart(myapp, '/', "app.conf")
Sessions are, by default, stored in RAM so, if you restart your server all of your current sessions will be lost. You can store them in memcached or on the filesystem instead.
Using sessions in your applications is done as follows:
import cherrypy
@cherrypy.expose
def index(self):
if 'count' not in cherrypy.session:
cherrypy.session['count'] = 0
cherrypy.session['count'] += 1
In this snippet, everytime the the index page handler is called, the current user’s session has its ‘count’ key incremented by 1.
CherryPy knows which session to use by inspecting the cookie sent alongside the request. This cookie contains the session identifier used by CherryPy to load the user’s session from the storage.
See also
Refer to the cherrypy.lib.sessions
module for more
details about the session interface and implementation.
Notably you will learn about sessions expiration.
Filesystem backend¶
Using a filesystem is a simple to not lose your sessions between reboots. Each session is saved in its own file within the given directory.
[/]
tools.sessions.on: True
tools.sessions.storage_class = cherrypy.lib.sessions.FileSession
tools.sessions.storage_path = "/some/directory"
Memcached backend¶
Memcached is a popular key-store on top of your RAM, it is distributed and a good choice if you want to share sessions outside of the process running CherryPy.
Requires that the Python
memcached
package is installed, which may be indicated by installing
cherrypy[memcached_session]
.
[/]
tools.sessions.on: True
tools.sessions.storage_class = cherrypy.lib.sessions.MemcachedSession
Other backends¶
Any other library may implement a session backend. Simply subclass
cherrypy.lib.sessions.Session
and indicate that subclass as
tools.sessions.storage_class
.
Static content serving¶
CherryPy can serve your static content such as images, javascript and CSS resources, etc.
Note
CherryPy uses the mimetypes
module to determine the
best content-type to serve a particular resource. If the choice
is not valid, you can simply set more media-types as follows:
import mimetypes
mimetypes.types_map['.csv'] = 'text/csv'
Serving a single file¶
You can serve a single file as follows:
[/style.css]
tools.staticfile.on = True
tools.staticfile.filename = "/home/site/style.css"
CherryPy will automatically respond to URLs such as http://hostname/style.css.
Serving a whole directory¶
Serving a whole directory is similar to a single file:
[/static]
tools.staticdir.on = True
tools.staticdir.dir = "/home/site/static"
Assuming you have a file at static/js/my.js, CherryPy will automatically respond to URLs such as http://hostname/static/js/my.js.
Note
CherryPy always requires the absolute path to the files or directories it will serve. If you have several static sections to configure but located in the same root directory, you can use the following shortcut:
[/]
tools.staticdir.root = "/home/site"
[/static]
tools.staticdir.on = True
tools.staticdir.dir = "static"
Specifying an index file¶
By default, CherryPy will respond to the root of a static directory with an 404 error indicating the path ‘/’ was not found. To specify an index file, you can use the following:
[/static]
tools.staticdir.on = True
tools.staticdir.dir = "/home/site/static"
tools.staticdir.index = "index.html"
Assuming you have a file at static/index.html, CherryPy will automatically respond to URLs such as http://hostname/static/ by returning its contents.
Allow files downloading¶
Using "application/x-download"
response content-type,
you can tell a browser that a resource should be downloaded
onto the user’s machine rather than displayed.
You could for instance write a page handler as follows:
from cherrypy.lib.static import serve_file
@cherrypy.expose
def download(self, filepath):
return serve_file(filepath, "application/x-download", "attachment")
Assuming the filepath is a valid path on your machine, the response would be considered as a downloadable content by the browser.
Warning
The above page handler is a security risk on its own since any file of the server could be accessed (if the user running the server had permissions on them).
Dealing with JSON¶
CherryPy has built-in support for JSON encoding and decoding of the request and/or response.
Decoding request¶
To automatically decode the content of a request using JSON:
class Root(object):
@cherrypy.expose
@cherrypy.tools.json_in()
def index(self):
data = cherrypy.request.json
The json attribute attached to the request contains the decoded content.
Encoding response¶
To automatically encode the content of a response using JSON:
class Root(object):
@cherrypy.expose
@cherrypy.tools.json_out()
def index(self):
return {'key': 'value'}
CherryPy will encode any content returned by your page handler using JSON. Not all type of objects may natively be encoded.
Authentication¶
CherryPy provides support for two very simple authentication mechanisms, both described in RFC 2617: Basic and Digest. They are most commonly known to trigger a browser’s popup asking users their name and password.
Basic¶
Basic authentication is the simplest form of authentication however it is not a secure one as the user’s credentials are embedded into the request. We advise against using it unless you are running on SSL or within a closed network.
from cherrypy.lib import auth_basic
USERS = {'jon': 'secret'}
def validate_password(realm, username, password):
if username in USERS and USERS[username] == password:
return True
return False
conf = {
'/protected/area': {
'tools.auth_basic.on': True,
'tools.auth_basic.realm': 'localhost',
'tools.auth_basic.checkpassword': validate_password
}
}
cherrypy.quickstart(myapp, '/', conf)
Simply put, you have to provide a function that will be called by CherryPy passing the username and password decoded from the request.
The function can read its data from any source it has to: a file, a database, memory, etc.
Digest¶
Digest authentication differs by the fact the credentials are not carried on by the request so it’s a little more secure than basic.
CherryPy’s digest support has a similar interface to the basic one explained above.
from cherrypy.lib import auth_digest
USERS = {'jon': 'secret'}
conf = {
'/protected/area': {
'tools.auth_digest.on': True,
'tools.auth_digest.realm': 'localhost',
'tools.auth_digest.get_ha1': auth_digest.get_ha1_dict_plain(USERS),
'tools.auth_digest.key': 'a565c27146791cfb'
}
}
cherrypy.quickstart(myapp, '/', conf)
Favicon¶
CherryPy serves its own sweet red cherrypy as the default favicon using the static file tool. You can serve your own favicon as follows:
import cherrypy
class HelloWorld(object):
@cherrypy.expose
def index(self):
return "Hello World!"
if __name__ == '__main__':
cherrypy.quickstart(HelloWorld(), '/',
{
'/favicon.ico':
{
'tools.staticfile.on': True,
'tools.staticfile.filename': '/path/to/myfavicon.ico'
}
}
)
Please refer to the static serving section for more details.
You can also use a file to configure it:
[/favicon.ico]
tools.staticfile.on: True
tools.staticfile.filename: "/path/to/myfavicon.ico"
import cherrypy
class HelloWorld(object):
@cherrypy.expose
def index(self):
return "Hello World!"
if __name__ == '__main__':
cherrypy.quickstart(HelloWorld(), '/', app.conf)
Advanced¶
CherryPy has support for more advanced features that these sections will describe.
Contents
Set aliases to page handlers¶
A fairly unknown, yet useful, feature provided by the cherrypy.expose()
decorator is to support aliases.
Let’s use the template provided by tutorial 03:
import random
import string
import cherrypy
class StringGenerator(object):
@cherrypy.expose(['generer', 'generar'])
def generate(self, length=8):
return ''.join(random.sample(string.hexdigits, int(length)))
if __name__ == '__main__':
cherrypy.quickstart(StringGenerator())
In this example, we create localized aliases for the page handler. This means the page handler will be accessible via:
- /generate
- /generer (French)
- /generar (Spanish)
Obviously, your aliases may be whatever suits your needs.
Note
The alias may be a single string or a list of them.
RESTful-style dispatching¶
The term RESTful URL is sometimes used to talk about friendly URLs that nicely map to the entities an application exposes.
Important
We will not enter the debate around what is restful or not but we will showcase two mechanisms to implement the usual idea in your CherryPy application.
Let’s assume you wish to create an application that exposes music bands and their records. Your application will probably have the following URLs:
- http://hostname/<artist>/
- http://hostname/<artist>/albums/<album_title>/
It’s quite clear you would not create a page handler named after every possible band in the world. This means you will need a page handler that acts as a proxy for all of them.
The default dispatcher cannot deal with that scenario on its own because it expects page handlers to be explicitely declared in your source code. Luckily, CherryPy provides ways to support those use cases.
See also
This section extends from this stackoverflow response.
The special _cp_dispatch method¶
_cp_dispatch
is a special method you declare in any of your controller
to massage the remaining segments before CherryPy gets to process them.
This offers you the capacity to remove, add or otherwise handle any segment
you wish and, even, entirely change the remaining parts.
import cherrypy
class Band(object):
def __init__(self):
self.albums = Album()
def _cp_dispatch(self, vpath):
if len(vpath) == 1:
cherrypy.request.params['name'] = vpath.pop()
return self
if len(vpath) == 3:
cherrypy.request.params['artist'] = vpath.pop(0) # /band name/
vpath.pop(0) # /albums/
cherrypy.request.params['title'] = vpath.pop(0) # /album title/
return self.albums
return vpath
@cherrypy.expose
def index(self, name):
return 'About %s...' % name
class Album(object):
@cherrypy.expose
def index(self, artist, title):
return 'About %s by %s...' % (title, artist)
if __name__ == '__main__':
cherrypy.quickstart(Band())
Notice how the controller defines _cp_dispatch, it takes a single argument, the URL path info broken into its segments.
The method can inspect and manipulate the list of segments, removing any or adding new segments at any position. The new list of segments is then sent to the dispatcher which will use it to locate the appropriate resource.
In the above example, you should be able to go to the following URLs:
The /nirvana/
segment is associated to the band and
the /nevermind/
segment relates to the album.
To achieve this, our _cp_dispatch method works on the idea that the default dispatcher matches URLs against page handler signatures and their position in the tree of handlers.
In this case, we take the dynamic segments in the URL (band and record names), we inject them into the request parameters and we remove them from the segment lists as if they had never been there in the first place.
In other words, _cp_dispatch makes it as if we were working on the following URLs:
The popargs decorator¶
cherrypy.popargs()
is more straightforward as it gives a name to any segment
that CherryPy wouldn’t be able to interpret otherwise. This makes the
matching of segments with page handler signatures easier and helps CherryPy
understand the structure of your URL.
import cherrypy
@cherrypy.popargs('band_name')
class Band(object):
def __init__(self):
self.albums = Album()
@cherrypy.expose
def index(self, band_name):
return 'About %s...' % band_name
@cherrypy.popargs('album_title')
class Album(object):
@cherrypy.expose
def index(self, band_name, album_title):
return 'About %s by %s...' % (album_title, band_name)
if __name__ == '__main__':
cherrypy.quickstart(Band())
This works similarly to _cp_dispatch but, as said above, is more explicit and localized. It says:
- take the first segment and store it into a parameter named band_name
- take again the first segment (since we removed the previous first) and store it into a parameter named album_title
Note that the decorator accepts more than a single binding. For instance:
@cherrypy.popargs('album_title')
class Album(object):
def __init__(self):
self.tracks = Track()
@cherrypy.popargs('track_num', 'track_title')
class Track(object):
@cherrypy.expose
def index(self, band_name, album_title, track_num, track_title):
...
This would handle the following URL:
Notice finally how the whole stack of segments is passed to each page handler so that you have the full context.
Error handling¶
CherryPy’s HTTPError
class supports raising immediate responses in the case of
errors.
class Root:
@cherrypy.expose
def thing(self, path):
if not authorized():
raise cherrypy.HTTPError(401, 'Unauthorized')
try:
file = open(path)
except FileNotFoundError:
raise cherrypy.HTTPError(404)
HTTPError.handle
is a context manager which supports translating exceptions
raised in the app into an appropriate HTTP response, as in the second example.
class Root:
@cherrypy.expose
def thing(self, path):
with cherrypy.HTTPError.handle(FileNotFoundError, 404):
file = open(path)
Streaming the response body¶
CherryPy handles HTTP requests, packing and unpacking the low-level details, then passing control to your application’s page handler, which produce the body of the response. CherryPy allows you to return body content in a variety of types: a string, a list of strings, a file. CherryPy also allows you to yield content, rather than return content. When you use “yield”, you also have the option of streaming the output.
In general, it is safer and easier to not stream output. Therefore,
streaming output is off by default. Streaming output and also using sessions
requires a good understanding of how session locks work
.
The “normal” CherryPy response process¶
When you provide content from your page handler, CherryPy manages the conversation between the HTTP server and your code like this:

Notice that the HTTP server gathers all output first and then writes everything to the client at once: status, headers, and body. This works well for static or simple pages, since the entire response can be changed at any time, either in your application code, or by the CherryPy framework.
How “streaming output” works with CherryPy¶
When you set the config entry “response.stream” to True (and use “yield”), CherryPy manages the conversation between the HTTP server and your code like this:

When you stream, your application doesn’t immediately pass raw body content back to CherryPy or to the HTTP server. Instead, it passes back a generator. At that point, CherryPy finalizes the status and headers, before the generator has been consumed, or has produced any output. This is necessary to allow the HTTP server to send the headers and pieces of the body as they become available.
Once CherryPy has set the status and headers, it sends them to the HTTP server, which then writes them out to the client. From that point on, the CherryPy framework mostly steps out of the way, and the HTTP server essentially requests content directly from your application code (your page handler method).
Therefore, when streaming, if an error occurs within your page handler, CherryPy will not catch it–the HTTP server will catch it. Because the headers (and potentially some of the body) have already been written to the client, the server cannot know a safe means of handling the error, and will therefore simply close the connection (the current, builtin servers actually write out a short error message in the body, but this may be changed, and is not guaranteed behavior for all HTTP servers you might use with CherryPy).
In addition, you cannot manually modify the status or headers within your page handler if that handler method is a streaming generator, because the method will not be iterated over until after the headers have been written to the client. This includes raising exceptions like HTTPError, NotFound, InternalRedirect and HTTPRedirect. To use a streaming generator while modifying headers, you would have to return a generator that is separate from (or embedded in) your page handler. For example:
class Root:
@cherrypy.expose
def thing(self):
cherrypy.response.headers['Content-Type'] = 'text/plain'
if not authorized():
raise cherrypy.NotFound()
def content():
yield "Hello, "
yield "world"
return content()
thing._cp_config = {'response.stream': True}
Streaming generators are sexy, but they play havoc with HTTP. CherryPy allows you to stream output for specific situations: pages which take many minutes to produce, or pages which need a portion of their content immediately output to the client. Because of the issues outlined above, it is usually better to flatten (buffer) content rather than stream content. Do otherwise only when the benefits of streaming outweigh the risks.
Response timeouts¶
CherryPy responses include 3 attributes related to time:
response.time
: thetime.time()
at which the response beganresponse.timeout
: the number of seconds to allow responses to runresponse.timed_out
: a boolean indicating whether the response has timed out (default False).
The request processing logic inspects the value of response.timed_out
at
various stages; if it is ever True, then TimeoutError
is raised.
You are free to do the same within your own code.
Rather than calculate the difference by hand, you can call
response.check_timeout
to set timed_out
for you.
Note
The default response timeout is 300 seconds.
Timeout Monitor¶
In addition, CherryPy includes a cherrypy.engine.timeout_monitor
which
monitors all active requests in a separate thread; periodically, it calls
check_timeout
on them all. It is subscribed by default. To turn it off:
[global]
engine.timeout_monitor.on: False
or:
cherrypy.engine.timeout_monitor.unsubscribe()
You can also change the interval (in seconds) at which the timeout monitor runs:
[global]
engine.timeout_monitor.frequency: 60 * 60
The default is once per minute. The above example changes that to once per hour.
Deal with signals¶
This engine plugin is instantiated automatically as
cherrypy.engine.signal_handler.
However, it is only subscribed automatically by cherrypy.quickstart()
.
So if you want signal handling and you’re calling:
tree.mount()
engine.start()
engine.block()
on your own, be sure to add before you start the engine:
engine.signals.subscribe()
Windows Console Events¶
Microsoft Windows uses console events to communicate some signals, like Ctrl-C. Deploying CherryPy on Windows platforms requires Python for Windows Extensions, which are installed automatically, being provided an extra dependency with environment marker. With that installed, CherryPy will handle Ctrl-C and other console events (CTRL_C_EVENT, CTRL_LOGOFF_EVENT, CTRL_BREAK_EVENT, CTRL_SHUTDOWN_EVENT, and CTRL_CLOSE_EVENT) automatically, shutting down the bus in preparation for process exit.
Securing your server¶
Note
This section is not meant as a complete guide to securing a web application or ecosystem. Please review the various guides provided at OWASP.
There are several settings that can be enabled to make CherryPy pages more secure. These include:
Transmitting data:
- Use Secure Cookies
Rendering pages:
- Set HttpOnly cookies
- Set XFrame options
- Enable XSS Protection
- Set the Content Security Policy
An easy way to accomplish this is to set headers with a tool and wrap your entire CherryPy application with it:
import cherrypy
# set the priority according to your needs if you are hooking something
# else on the 'before_finalize' hook point.
@cherrypy.tools.register('before_finalize', priority=60)
def secureheaders():
headers = cherrypy.response.headers
headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block'
headers['Content-Security-Policy'] = "default-src='self'"
Note
Read more about those headers.
Then, in the configuration file (or any other place that you want to enable the tool):
[/]
tools.secureheaders.on = True
If you use sessions you can also enable these settings:
[/]
tools.sessions.on = True
# increase security on sessions
tools.sessions.secure = True
tools.sessions.httponly = True
If you use SSL you can also enable Strict Transport Security:
# add this to secureheaders():
# only add Strict-Transport headers if we're actually using SSL; see the ietf spec
# "An HSTS Host MUST NOT include the STS header field in HTTP responses
# conveyed over non-secure transport"
# http://tools.ietf.org/html/draft-ietf-websec-strict-transport-sec-14#section-7.2
if (cherrypy.server.ssl_certificate != None and cherrypy.server.ssl_private_key != None):
headers['Strict-Transport-Security'] = 'max-age=31536000' # one year
Next, you should probably use SSL.
Multiple HTTP servers support¶
CherryPy starts its own HTTP server whenever you start the engine. In some cases, you may wish to host your application on more than a single port. This is easily achieved:
from cherrypy._cpserver import Server
server = Server()
server.socket_port = 8090
server.subscribe()
You can create as many server
server instances as you need, once subscribed,
they will follow the CherryPy engine’s life-cycle.
WSGI support¶
CherryPy supports the WSGI interface defined in PEP 333 as well as its updates in PEP 3333. It means the following:
- You can host a foreign WSGI application with the CherryPy server
- A CherryPy application can be hosted by another WSGI server
Make your CherryPy application a WSGI application¶
A WSGI application can be obtained from your application as follows:
import cherrypy
wsgiapp = cherrypy.Application(StringGenerator(), '/', config=myconf)
Simply use the wsgiapp instance in any WSGI-aware server.
Host a foreign WSGI application in CherryPy¶
Assuming you have a WSGI-aware application, you can host it
in your CherryPy server using the cherrypy.tree.graft
facility.
def raw_wsgi_app(environ, start_response):
status = '200 OK'
response_headers = [('Content-type','text/plain')]
start_response(status, response_headers)
return ['Hello world!']
cherrypy.tree.graft(raw_wsgi_app, '/')
Important
You cannot use tools with a foreign WSGI application. However, you can still benefit from the CherryPy bus.
No need for the WSGI interface?¶
The default CherryPy HTTP server supports the WSGI interfaces defined in PEP 333 and PEP 3333. However, if your application is a pure CherryPy application, you can switch to a HTTP server that by-passes the WSGI layer altogether. It will provide a slight performance increase.
import cherrypy
class Root(object):
@cherrypy.expose
def index(self):
return "Hello World!"
if __name__ == '__main__':
from cherrypy._cpnative_server import CPHTTPServer
cherrypy.server.httpserver = CPHTTPServer(cherrypy.server)
cherrypy.quickstart(Root(), '/')
Important
Using the native server, you will not be able to graft a WSGI application as shown in the previous section. Doing so will result in a server error at runtime.
WebSocket support¶
WebSocket is a recent application protocol that came to life from the HTML5 working-group in response to the needs for bi-directional communication. Various hacks had been proposed such as Comet, polling, etc.
WebSocket is a socket that starts its life from a HTTP upgrade request. Once the upgrade is performed, the underlying socket is kept opened but not used in a HTTP context any longer. Instead, both connected endpoints may use the socket to push data to the other end.
CherryPy itself does not support WebSocket, but the feature is provided by an external library called ws4py.
Database support¶
CherryPy does not bundle any database access but its architecture makes it easy to integrate common database interfaces such as the DB-API specified in PEP 249. Alternatively, you can also use an ORM such as SQLAlchemy or SQLObject.
You will find here a recipe on how integrating SQLAlchemy using a mix of plugins and tools.
HTML Templating support¶
CherryPy does not provide any HTML template but its architecture makes it easy to integrate one. Popular ones are Mako or Jinja2.
You will find here a recipe on how to integrate them using a mix plugins and tools.
Testing your application¶
Web applications, like any other kind of code, must be tested. CherryPy provides
a helper class
to ease writing
functional tests.
Here is a simple example for a basic echo application:
import cherrypy
from cherrypy.test import helper
class SimpleCPTest(helper.CPWebCase):
def setup_server():
class Root(object):
@cherrypy.expose
def echo(self, message):
return message
cherrypy.tree.mount(Root())
setup_server = staticmethod(setup_server)
def test_message_should_be_returned_as_is(self):
self.getPage("/echo?message=Hello%20world")
self.assertStatus('200 OK')
self.assertHeader('Content-Type', 'text/html;charset=utf-8')
self.assertBody('Hello world')
def test_non_utf8_message_will_fail(self):
"""
CherryPy defaults to decode the query-string
using UTF-8, trying to send a query-string with
a different encoding will raise a 404 since
it considers it's a different URL.
"""
self.getPage("/echo?message=A+bient%F4t",
headers=[
('Accept-Charset', 'ISO-8859-1,utf-8'),
('Content-Type', 'text/html;charset=ISO-8859-1')
]
)
self.assertStatus('404 Not Found')
As you can see the, test inherits from that helper class. You should
setup your application and mount it as per-usual. Then, define your various
tests and call the helper getPage()
method to perform a request. Simply use the various specialized
assert* methods to validate your workflow and data.
You can then run the test using py.test as follows:
$ py.test -s test_echo_app.py
The -s
is necessary because the CherryPy class also wraps stdin and stdout.
Note
Although they are written using the typical pattern the
unittest
module supports, they are not bare unit tests.
Indeed, a whole CherryPy stack is started for you and runs your application.
If you want to really unit test your CherryPy application, meaning without
having to start a server, you may want to have a look at
this recipe.
Configure¶
Configuration in CherryPy is implemented via dictionaries. Keys are strings which name the mapped value; values may be of any type.
In CherryPy 3, you use configuration (files or dicts) to set attributes
directly on the engine, server, request, response, and log objects. So the
best way to know the full range of what’s available in the config file is to
simply import those objects and see what help(obj)
tells you.
Note
If you are new to CherryPy, please refer first to the simpler basic config section first.
Contents
Architecture¶
The first thing you need to know about CherryPy 3’s configuration is that it separates global config from application config. If you’re deploying multiple applications at the same site (and more and more people are, as Python web apps are tending to decentralize), you need to be careful to separate the configurations, as well. There’s only ever one “global config”, but there is a separate “app config” for each app you deploy.
CherryPy Requests are part of an Application, which runs in a global context, and configuration data may apply to any of those three scopes. Let’s look at each of those scopes in turn.
Global config¶
Global config entries apply everywhere, and are stored in
cherrypy.config
. This flat dict only holds
global config data; that is, “site-wide” config entries which affect all
mounted applications.
Global config is stored in the
cherrypy.config
dict,
and you therefore update it by calling cherrypy.config.update(conf)
.
The conf
argument can be either a filename, an open file, or a dict of
config entries. Here’s an example of passing a dict argument:
cherrypy.config.update({'server.socket_host': '64.72.221.48',
'server.socket_port': 80,
})
The server.socket_host
option in this example determines on which network
interface CherryPy will listen. The server.socket_port
option declares
the TCP port on which to listen.
Application config¶
Application entries apply to a single mounted application, and are stored on
each Application object itself as
app.config
. This is a two-level
dict where each top-level key is a path, or “relative URL” (for example,
"/"
or "/my/page"
), and each value is a dict of config entries.
The URL’s are relative to the script name (mount point) of the Application.
Usually, all this data is provided in the call to
tree.mount(root(), script_name='/path/to', config=conf)
,
although you may also use app.merge(conf)
.
The conf
argument can be either a filename, an open file, or a dict of
config entries.
Configuration file example:
[/]
tools.trailing_slash.on = False
request.dispatch: cherrypy.dispatch.MethodDispatcher()
or, in python code:
config = {'/':
{
'request.dispatch': cherrypy.dispatch.MethodDispatcher(),
'tools.trailing_slash.on': False,
}
}
cherrypy.tree.mount(Root(), config=config)
CherryPy only uses sections that start with "/"
(except
[global]
, see below). That means you can place your own configuration
entries in a CherryPy config file by giving them a section name which does not
start with "/"
. For example, you might include database entries like this:
[global]
server.socket_host: "0.0.0.0"
[Databases]
driver: "postgres"
host: "localhost"
port: 5432
[/path]
response.timeout: 6000
Then, in your application code you can read these values during request time
via cherrypy.request.app.config['Databases']
. For code that is outside the
request process, you’ll have to pass a reference to your Application around.
Request config¶
Each Request object possesses a single
request.config
dict. Early in the
request process, this dict is populated by merging Global config, Application
config, and any config acquired while looking up the page handler (see next).
This dict contains only those config entries which apply to the given request.
Note
when you do an InternalRedirect
,
this config attribute is recalculated for the new path.
Declaration¶
Configuration data may be supplied as a Python dictionary, as a filename, or as an open file object.
Configuration files¶
When you supply a filename or file, CherryPy uses Python’s builtin ConfigParser;
you declare Application config by writing each path as a section header,
and each entry as a "key: value"
(or "key = value"
) pair:
[/path/to/my/page]
response.stream: True
tools.trailing_slash.extra = False
Combined Configuration Files¶
If you are only deploying a single application, you can make a single config
file that contains both global and app entries. Just stick the global entries
into a config section named [global]
, and pass the same file to both
config.update
and
tree.mount <cherrypy._cptree.Tree.mount()
. If you’re calling
cherrypy.quickstart(app root, script name, config)
, it will pass the
config to both places for you. But as soon as you decide to add another
application to the same site, you need to separate the two config files/dicts.
Separate Configuration Files¶
If you’re deploying more than one application in the same process, you need
(1) file for global config, plus (1) file for each Application.
The global config is applied by calling
cherrypy.config.update
,
and application config is usually passed in a call to
cherrypy.tree.mount
.
In general, you should set global config first, and then mount each application with its own config. Among other benefits, this allows you to set up global logging so that, if something goes wrong while trying to mount an application, you’ll see the tracebacks. In other words, use this order:
# global config
cherrypy.config.update({'environment': 'production',
'log.error_file': 'site.log',
# ...
})
# Mount each app and pass it its own config
cherrypy.tree.mount(root1, "", appconf1)
cherrypy.tree.mount(root2, "/forum", appconf2)
cherrypy.tree.mount(root3, "/blog", appconf3)
if hasattr(cherrypy.engine, 'block'):
# 3.1 syntax
cherrypy.engine.start()
cherrypy.engine.block()
else:
# 3.0 syntax
cherrypy.server.quickstart()
cherrypy.engine.start()
Values in config files use Python syntax¶
Config entries are always a key/value pair, like server.socket_port = 8080
.
The key is always a name, and the value is always a Python object. That is,
if the value you are setting is an int
(or other number), it needs to look
like a Python int
; for example, 8080
. If the value is a string, it
needs to be quoted, just like a Python string. Arbitrary objects can also be
created, just like in Python code (assuming they can be found/imported).
Here’s an extended example, showing you some of the different types:
[global]
log.error_file: "/home/fumanchu/myapp.log"
environment = 'production'
server.max_request_body_size: 1200
[/myapp]
tools.trailing_slash.on = False
request.dispatch: cherrypy.dispatch.MethodDispatcher()
_cp_config: attaching config to handlers¶
Config files have a severe limitation: values are always keyed by URL. For example:
[/path/to/page]
methods_with_bodies = ("POST", "PUT", "PROPPATCH")
It’s obvious that the extra method is the norm for that path; in fact, the code could be considered broken without it. In CherryPy, you can attach that bit of config directly on the page handler:
@cherrypy.expose
def page(self):
return "Hello, world!"
page._cp_config = {"request.methods_with_bodies": ("POST", "PUT", "PROPPATCH")}
_cp_config
is a reserved attribute which the dispatcher looks for at
each node in the object tree. The _cp_config
attribute must be a CherryPy
config dictionary. If the dispatcher finds a _cp_config
attribute,
it merges that dictionary into the rest of the config. The entire merged
config dictionary is placed in
cherrypy.request.config
.
This can be done at any point in the tree of objects; for example, we could have attached that config to a class which contains the page method:
class SetOPages:
_cp_config = {"request.methods_with_bodies": ("POST", "PUT", "PROPPATCH")}
@cherrypy.expose
def page(self):
return "Hullo, Werld!"
Note
This behavior is only guaranteed for the default dispatcher. Other
dispatchers may have different restrictions on where you can attach
_cp_config
attributes.
This technique allows you to:
- Put config near where it’s used for improved readability and maintainability.
- Attach config to objects instead of URL’s. This allows multiple URL’s to point to the same object, yet you only need to define the config once.
- Provide defaults which are still overridable in a config file.
Namespaces¶
Because config entries usually just set attributes on objects, they’re almost
all of the form: object.attribute
. A few are of the form:
object.subobject.attribute
. They look like normal Python attribute chains,
because they work like them. We call the first name in the chain the
“config namespace”. When you provide a config entry, it is bound as early
as possible to the actual object referenced by the namespace; for example,
the entry response.stream
actually sets the stream
attribute of
cherrypy.response
! In this way,
you can easily determine the default value by firing up a python interpreter
and typing:
>>> import cherrypy
>>> cherrypy.response.stream
False
Each config namespace has its own handler; for example, the “request” namespace has a handler which takes your config entry and sets that value on the appropriate “request” attribute. There are a few namespaces, however, which don’t work like normal attributes behind the scenes; however, they still use dotted keys and are considered to “have a namespace”.
Builtin namespaces¶
Entries from each namespace may be allowed in the global, application root
("/"
) or per-path config, or a combination:
Scope | Global | Application Root | App Path |
engine | X | ||
hooks | X | X | X |
log | X | X | |
request | X | X | X |
response | X | X | X |
server | X | ||
tools | X | X | X |
engine¶
Entries in this namespace controls the ‘application engine’. These can only be
declared in the global config. Any attribute of
cherrypy.engine
may be set
in config; however, there are a few extra entries available in config:
- Plugin attributes. Many of the Engine Plugins are themselves attributes of
cherrypy.engine
. You can set any attribute of an attached plugin by simply naming it. For example, there is an instance of theAutoreloader
class atengine.autoreload
; you can set its “frequency” attribute via the config entryengine.autoreload.frequency = 60
. In addition, you can turn such plugins on and off by settingengine.autoreload.on = True
orFalse
.engine.SIGHUP/SIGTERM
: These entries can be used to set the list of listeners for the given channel. Mostly, this is used to turn off the signal handling one gets automatically viacherrypy.quickstart()
.
hooks¶
Declares additional request-processing functions. Use this to append your own
Hook
functions to the request. For example,
to add my_hook_func
to the before_handler
hookpoint:
[/]
hooks.before_handler = myapp.my_hook_func
log¶
Configures logging. These can only be declared in the global config (for global
logging) or [/]
config (for each application).
See LogManager
for the list of
configurable attributes. Typically, the “access_file”, “error_file”, and
“screen” attributes are the most commonly configured.
request¶
Sets attributes on each Request. See the
Request
class for a complete list.
response¶
Sets attributes on each Response. See the
Response
class for a complete list.
server¶
Controls the default HTTP server via
cherrypy.server
(see that class for a
complete list of configurable attributes). These can only be
declared in the global config.
tools¶
Enables and configures additional request-processing packages. See the
/tutorial/tools
overview for more information.
wsgi¶
Adds WSGI middleware to an Application’s “pipeline”. These can only be declared in the app’s root config (“/”).
wsgi.pipeline
: Appends to the WSGi pipeline. The value must be a list of (name, app factory) pairs. Each app factory must be a WSGI callable class (or callable that returns a WSGI callable); it must take an initial ‘nextapp’ argument, plus any optional keyword arguments. The optional arguments may be configured viawsgi.<name>.<arg>
.wsgi.response_class
: Overrides the defaultResponse
class.
checker¶
Controls the “checker”, which looks for common errors in app state (including
config) when the engine starts. You can turn off individual checks by setting
them to False
in config. See cherrypy._cpchecker.Checker
for a
complete list. Global config only.
Custom config namespaces¶
You can define your own namespaces if you like, and they can do far more than
simply set attributes. The test/test_config
module, for example, shows an
example of a custom namespace that coerces incoming params and outgoing body
content. The cherrypy._cpwsgi
module includes an additional, builtin
namespace for invoking WSGI middleware.
In essence, a config namespace handler is just a function, that gets passed
any config entries in its namespace. You add it to a namespaces registry
(a dict), where keys are namespace names and values are handler functions.
When a config entry for your namespace is encountered, the corresponding
handler function will be called, passing the config key and value; that is,
namespaces[namespace](k, v)
. For example, if you write:
def db_namespace(k, v):
if k == 'connstring':
orm.connect(v)
cherrypy.config.namespaces['db'] = db_namespace
then cherrypy.config.update({"db.connstring": "Oracle:host=1.10.100.200;sid=TEST"})
will call db_namespace('connstring', 'Oracle:host=1.10.100.200;sid=TEST')
.
The point at which your namespace handler is called depends on where you add it:
Scope | Namespace dict | Handler is called in |
Global | cherrypy.config.namespaces |
cherrypy.config.update |
Application | app.namespaces |
Application.merge (which is called by cherrypy.tree.mount) |
Request | app.request_class.namespaces |
Request.configure (called for each request, after the handler is looked up) |
The name can be any string, and the handler must be either a callable or a (Python 2.5 style) context manager.
If you need additional code to run when all your namespace keys are collected, you can supply a callable context manager in place of a normal function for the handler. Context managers are defined in PEP 343.
Environments¶
The only key that does not exist in a namespace is the “environment” entry.
It only applies to the global config, and only when you use
cherrypy.config.update
. This special
entry imports other config entries from the following template stored in
cherrypy._cpconfig.environments[environment]
.
Config.environments = environments = {
'staging': {
'engine.autoreload.on': False,
'checker.on': False,
'tools.log_headers.on': False,
'request.show_tracebacks': False,
'request.show_mismatched_params': False,
},
'production': {
'engine.autoreload.on': False,
'checker.on': False,
'tools.log_headers.on': False,
'request.show_tracebacks': False,
'request.show_mismatched_params': False,
'log.screen': False,
},
'embedded': {
# For use with CherryPy embedded in another deployment stack.
'engine.autoreload.on': False,
'checker.on': False,
'tools.log_headers.on': False,
'request.show_tracebacks': False,
'request.show_mismatched_params': False,
'log.screen': False,
'engine.SIGHUP': None,
'engine.SIGTERM': None,
},
'test_suite': {
'engine.autoreload.on': False,
'checker.on': False,
'tools.log_headers.on': False,
'request.show_tracebacks': True,
'request.show_mismatched_params': True,
'log.screen': False,
},
}
If you find the set of existing environments (production, staging, etc) too limiting or just plain wrong, feel free to extend them or add new environments:
cherrypy._cpconfig.environments['staging']['log.screen'] = False
cherrypy._cpconfig.environments['Greek'] = {
'tools.encode.encoding': 'ISO-8859-7',
'tools.decode.encoding': 'ISO-8859-7',
}
Extend¶
CherryPy is truly an open framework, you can extend and plug new functions at will either server-side or on a per-requests basis. Either way, CherryPy is made to help you build your application and support your architecture via simple patterns.
Contents
Server-wide functions¶
CherryPy can be considered both as a HTTP library as much as a web application framework. In that latter case, its architecture provides mechanisms to support operations accross the whole server instance. This offers a powerful canvas to perform persistent operations as server-wide functions live outside the request processing itself. They are available to the whole process as long as the bus lives.
Typical use cases:
- Keeping a pool of connection to an external server so that your need not to re-open them on each request (database connections for instance).
- Background processing (say you need work to be done without blocking the whole request itself).
Publish/Subscribe pattern¶
CherryPy’s backbone consists of a bus system implementing a simple publish/subscribe messaging pattern. Simply put, in CherryPy everything is controlled via that bus. One can easily picture the bus as a sushi restaurant’s belt as in the picture below.
You can subscribe and publish to channels on a bus. A channel is bit like a unique identifier within the bus. When a message is published to a channel, the bus will dispatch the message to all subscribers for that channel.
One interesting aspect of a pubsub pattern is that it promotes decoupling between a caller and the callee. A published message will eventually generate a response but the publisher does not know where that response came from.
Thanks to that decoupling, a CherryPy application can easily access functionalities without having to hold a reference to the entity providing that functionality. Instead, the application simply publishes onto the bus and will receive the appropriate response, which is all that matter.
Typical pattern¶
Let’s take the following dummy application:
import cherrypy
class ECommerce(object):
def __init__(self, db):
self.mydb = db
@cherrypy.expose
def save_kart(self, cart_data):
cart = Cart(cart_data)
self.mydb.save(cart)
if __name__ == '__main__':
cherrypy.quickstart(ECommerce(), '/')
The application has a reference to the database but this creates a fairly strong coupling between the database provider and the application.
Another approach to work around the coupling is by using a pubsub workflow:
import cherrypy
class ECommerce(object):
@cherrypy.expose
def save_kart(self, cart_data):
cart = Cart(cart_data)
cherrypy.engine.publish('db-save', cart)
if __name__ == '__main__':
cherrypy.quickstart(ECommerce(), '/')
In this example, we publish a cart instance to db-save channel. One or many subscribers can then react to that message and the application doesn’t have to know about them.
Note
This approach is not mandatory and it’s up to you to decide how to design your entities interaction.
Implementation details¶
CherryPy’s bus implementation is simplistic as it registers functions to channels. Whenever a message is published to a channel, each registered function is applied with that message passed as a parameter.
The whole behaviour happens synchronously and, in that sense, if a subscriber takes too long to process a message, the remaining subscribers will be delayed.
CherryPy’s bus is not an advanced pubsub messaging broker system such as provided by zeromq or RabbitMQ. Use it with the understanding that it may have a cost.
Engine as a pubsub bus¶
As said earlier, CherryPy is built around a pubsub bus. All entities that the framework manages at runtime are working on top of a single bus instance, which is named the engine.
The bus implementation therefore provides a set of common channels which describe the application’s lifecycle:
O
|
V
STOPPING --> STOPPED --> EXITING -> X
A A |
| \___ |
| \ |
| V V
STARTED <-- STARTING
The states’ transitions trigger channels to be published to so that subscribers can react to them.
One good example is the HTTP server which will tranisition from a “STOPPED” stated to a “STARTED” state whenever a message is published to the start channel.
Built-in channels¶
In order to support its life-cycle, CherryPy defines a set of common channels that will be published to at various states:
- “start”: When the bus is in the “STARTING” state
- “main”: Periodically from the CherryPy’s mainloop
- “stop”: When the bus is in the “STOPPING” state
- “graceful”: When the bus requests a reload of subscribers
- “exit”: When the bus is in the “EXITING” state
This channel will be published to by the engine automatically. Register therefore any subscribers that would need to react to the transition changes of the engine.
In addition, a few other channels are also published to during the request processing.
- `“before_request”: right before the request is processed by CherryPy
- “after_request”: right after it has been processed
Also, from the cherrypy.process.plugins.ThreadManager
plugin:
- “acquire_thread”
- “start_thread”
- “stop_thread”
- “release_thread”
Bus API¶
In order to work with the bus, the implementation provides the following simple API:
- The channel parameter is a string identifying the channel to which the message should be sent to
- *args is the message and may contain any valid Python values or objects.
- The channel parameter is a string identifying the channel the callable will be registered to.
- callable is a Python function or method which signature must match what will be published.
- The channel parameter is a string identifying the channel the callable was registered to.
- callable is the Python function or method which was registered.
Plugins¶
Plugins, simply put, are entities that play with the bus, either by publishing or subscribing to channels, usually both at the same time.
Important
Plugins are extremely useful whenever you have functionalities:
- Available accross the whole application server
- Associated to the application’s life-cycle
- You want to avoid being strongly coupled to the application
Create a plugin¶
A typical plugin looks like this:
import cherrypy
from cherrypy.process import wspbus, plugins
class DatabasePlugin(plugins.SimplePlugin):
def __init__(self, bus, db_klass):
plugins.SimplePlugin.__init__(self, bus)
self.db = db_klass()
def start(self):
self.bus.log('Starting up DB access')
self.bus.subscribe("db-save", self.save_it)
def stop(self):
self.bus.log('Stopping down DB access')
self.bus.unsubscribe("db-save", self.save_it)
def save_it(self, entity):
self.db.save(entity)
The cherrypy.process.plugins.SimplePlugin
is a helper
class provided by CherryPy that will automatically subscribe
your start and stop methods to the related channels.
When the start and stop channels are published on, those methods are called accordingly.
Notice then how our plugin subscribes to the db-save channel so that the bus can dispatch messages to the plugin.
Enable a plugin¶
To enable the plugin, it has to be registered to the the bus as follows:
DatabasePlugin(cherrypy.engine, SQLiteDB).subscribe()
The SQLiteDB here is a fake class that is used as our database provider.
Disable a plugin¶
You can also unregister a plugin as follows:
someplugin.unsubscribe()
This is often used when you want to prevent the default HTTP server from being started by CherryPy, for instance if you run on top of a different HTTP server (WSGI capable):
cherrypy.server.unsubscribe()
Let’s see an example using this default application:
import cherrypy
class Root(object):
@cherrypy.expose
def index(self):
return "hello world"
if __name__ == '__main__':
cherrypy.quickstart(Root())
For instance, this is what you would see when running this application:
[27/Apr/2014:13:04:07] ENGINE Listening for SIGHUP.
[27/Apr/2014:13:04:07] ENGINE Listening for SIGTERM.
[27/Apr/2014:13:04:07] ENGINE Listening for SIGUSR1.
[27/Apr/2014:13:04:07] ENGINE Bus STARTING
[27/Apr/2014:13:04:07] ENGINE Started monitor thread 'Autoreloader'.
[27/Apr/2014:13:04:07] ENGINE Started monitor thread '_TimeoutMonitor'.
[27/Apr/2014:13:04:08] ENGINE Serving on http://127.0.0.1:8080
[27/Apr/2014:13:04:08] ENGINE Bus STARTED
Now let’s unsubscribe the HTTP server:
import cherrypy
class Root(object):
@cherrypy.expose
def index(self):
return "hello world"
if __name__ == '__main__':
cherrypy.server.unsubscribe()
cherrypy.quickstart(Root())
This is what we get:
[27/Apr/2014:13:08:06] ENGINE Listening for SIGHUP.
[27/Apr/2014:13:08:06] ENGINE Listening for SIGTERM.
[27/Apr/2014:13:08:06] ENGINE Listening for SIGUSR1.
[27/Apr/2014:13:08:06] ENGINE Bus STARTING
[27/Apr/2014:13:08:06] ENGINE Started monitor thread 'Autoreloader'.
[27/Apr/2014:13:08:06] ENGINE Started monitor thread '_TimeoutMonitor'.
[27/Apr/2014:13:08:06] ENGINE Bus STARTED
As you can see, the server is not started. The missing:
[27/Apr/2014:13:04:08] ENGINE Serving on http://127.0.0.1:8080
Per-request functions¶
One of the most common task in a web application development is to tailor the request’s processing to the runtime context.
Within CherryPy, this is performed via what are called tools. If you are familiar with Django or WSGI middlewares, CherryPy tools are similar in spirit. They add functions that are applied during the request/response processing.
Hook point¶
A hook point is a point during the request/response processing.
Here is a quick rundown of the “hook points” that you can hang your tools on:
- “on_start_resource” - The earliest hook; the Request-Line and request headers have been processed and a dispatcher has set request.handler and request.config.
- “before_request_body” - Tools that are hooked up here run right before the request body would be processed.
- “before_handler” - Right before the request.handler (the exposed callable that was found by the dispatcher) is called.
- “before_finalize” - This hook is called right after the page handler has been processed and before CherryPy formats the final response object. It helps you for example to check for what could have been returned by your page handler and change some headers if needed.
- “on_end_resource” - Processing is complete - the response is ready to be returned. This doesn’t always mean that the request.handler (the exposed page handler) has executed! It may be a generator. If your tool absolutely needs to run after the page handler has produced the response body, you need to either use on_end_request instead, or wrap the response.body in a generator which applies your tool as the response body is being generated.
- “before_error_response” - Called right before an error response (status code, body) is set.
- “after_error_response” - Called right after the error response (status code, body) is set and just before the error response is finalized.
- “on_end_request” - The request/response conversation is over, all data has been written to the client, nothing more to see here, move along.
Tools¶
A tool is a simple callable object (function, method, object implementing a __call__ method) that is attached to a hook point.
Below is a simple tool that is attached to the before_finalize hook point, hence after the page handler was called:
@cherrypy.tools.register('before_finalize')
def logit():
print(cherrypy.request.remote.ip)
Tools can also be created and assigned manually. The decorator registration is equivalent to:
cherrypy.tools.logit = cherrypy.Tool('before_finalize', logit)
Using that tool is as simple as follows:
class Root(object):
@cherrypy.expose
@cherrypy.tools.logit()
def index(self):
return "hello world"
Obviously the tool may be declared the other usual ways.
Note
The name of the tool, technically the attribute set to cherrypy.tools, does not have to match the name of the callable. However, it is that name that will be used in the configuration to refer to that tool.
Stateful tools¶
The tools mechanism is really flexible and enables rich per-request functionalities.
Straight tools as shown in the previous section are usually good enough. However, if your workflow requires some sort of state during the request processing, you will probably want a class-based approach:
import time
import cherrypy
class TimingTool(cherrypy.Tool):
def __init__(self):
cherrypy.Tool.__init__(self, 'before_handler',
self.start_timer,
priority=95)
def _setup(self):
cherrypy.Tool._setup(self)
cherrypy.request.hooks.attach('before_finalize',
self.end_timer,
priority=5)
def start_timer(self):
cherrypy.request._time = time.time()
def end_timer(self):
duration = time.time() - cherrypy.request._time
cherrypy.log("Page handler took %.4f" % duration)
cherrypy.tools.timeit = TimingTool()
This tool computes the time taken by the page handler for a given request. It stores the time at which the handler is about to get called and logs the time difference right after the handler returned its result.
The import bits is that the cherrypy.Tool
constructor
allows you to register to a hook point but, to attach the
same tool to a different hook point, you must use the
cherrypy.request.hooks.attach
method.
The cherrypy.Tool._setup
method is automatically called by CherryPy when the tool
is applied to the request.
Next, let’s see how to use our tool:
class Root(object):
@cherrypy.expose
@cherrypy.tools.timeit()
def index(self):
return "hello world"
Tools ordering¶
Since you can register many tools at the same hookpoint, you may wonder in which order they will be applied.
CherryPy offers a deterministic, yet so simple, mechanism to do so. Simply set the priority attribute to a value from 1 to 100, lower values providing greater priority.
If you set the same priority for several tools, they will be called in the order you declare them in your configuration.
Toolboxes¶
All of the builtin CherryPy tools are collected into a Toolbox called
cherrypy.tools
. It responds to config entries in the "tools"
namespace. You can add your own Tools to this Toolbox
as described above.
You can also make your own Toolboxes if you need more modularity. For example, you might create multiple Tools for working with JSON, or you might publish a set of Tools covering authentication and authorization from which everyone could benefit (hint, hint). Creating a new Toolbox is as simple as:
import cherrypy
# Create a new Toolbox.
newauthtools = cherrypy._cptools.Toolbox("newauth")
# Add a Tool to our new Toolbox.
@newauthtools.register('before_request_body')
def check_access(default=False):
if not getattr(cherrypy.request, "userid", default):
raise cherrypy.HTTPError(401)
Then, in your application, use it just like you would use cherrypy.tools
,
with the additional step of registering your toolbox with your app.
Note that doing so automatically registers the "newauth"
config namespace;
you can see the config entries in action below:
import cherrypy
class Root(object):
@cherrypy.expose
def default(self):
return "Hello"
conf = {
'/demo': {
'newauth.check_access.on': True,
'newauth.check_access.default': True,
}
}
app = cherrypy.tree.mount(Root(), config=conf)
Request parameters manipulation¶
HTTP uses strings to carry data between two endpoints. However your application may make better use of richer object types. As it wouldn’t be really readable, nor a good idea regarding maintenance, to let each page handler deserialize data, it’s a common pattern to delegate this functions to tools.
For instance, let’s assume you have a user id in the query-string and some user data stored into a database. You could retrieve the data, create an object and pass it on to the page handler instead of the user id.
import cherrypy
class UserManager(cherrypy.Tool):
def __init__(self):
cherrypy.Tool.__init__(self, 'before_handler',
self.load, priority=10)
def load(self):
req = cherrypy.request
# let's assume we have a db session
# attached to the request somehow
db = req.db
# retrieve the user id and remove it
# from the request parameters
user_id = req.params.pop('user_id')
req.params['user'] = db.get(int(user_id))
cherrypy.tools.user = UserManager()
class Root(object):
@cherrypy.expose
@cherrypy.tools.user()
def index(self, user):
return "hello %s" % user.name
In other words, CherryPy give you the power to:
- inject data, that wasn’t part of the initial request, into the page handler
- remove data as well
- convert data into a different, more useful, object to remove that burden from the page handler itself
Tailored dispatchers¶
Dispatching is the art of locating the appropriate page handler for a given request. Usually, dispatching is based on the request’s URL, the query-string and, sometimes, the request’s method (GET, POST, etc.).
Based on this, CherryPy comes with various dispatchers already.
In some cases however, you will need a little more. Here is an example of dispatcher that will always ensure the incoming URL leads to a lower-case page handler.
import random
import string
import cherrypy
from cherrypy._cpdispatch import Dispatcher
class StringGenerator(object):
@cherrypy.expose
def generate(self, length=8):
return ''.join(random.sample(string.hexdigits, int(length)))
class ForceLowerDispatcher(Dispatcher):
def __call__(self, path_info):
return Dispatcher.__call__(self, path_info.lower())
if __name__ == '__main__':
conf = {
'/': {
'request.dispatch': ForceLowerDispatcher(),
}
}
cherrypy.quickstart(StringGenerator(), '/', conf)
Once you run this snipper, go to:
In both cases, you will be led to the generate page handler. Without our home-made dispatcher, the second one would fail and return a 404 error (RFC 2616#sec10.4.5).
Tool or dispatcher?¶
In the previous example, why not simply use a tool? Well, the sooner a tool can be called is always after the page handler has been found. In our example, it would be already too late as the default dispatcher would have not even found a match for /GENerAte.
A dispatcher exists mostly to determine the best page handler to serve the requested resource.
On ther other hand, tools are there to adapt the request’s processing to the runtime context of the application and the request’s content.
Usually, you will have to write a dispatcher only if you have a very specific use case to locate the most adequate page handler. Otherwise, the default ones will likely suffice.
Request body processors¶
Since its 3.2 release, CherryPy provides a really elegant
and powerful mechanism to deal with a request’s body based
on its mimetype. Refer to the cherrypy._cpreqbody
module
to understand how to implement your own processors.
Deploy¶
CherryPy stands on its own, but as an application server, it is often located in shared or complex environments. For this reason, it is not uncommon to run CherryPy behind a reverse proxy or use other servers to host the application.
Note
CherryPy’s server has proven reliable and fast enough for years now. If the volume of traffic you receive is average, it will do well enough on its own. Nonetheless, it is common to delegate the serving of static content to more capable servers such as nginx or CDN.
Contents
Run as a daemon¶
CherryPy allows you to easily decouple the current process from the parent environment, using the traditional double-fork:
from cherrypy.process.plugins import Daemonizer
d = Daemonizer(cherrypy.engine)
d.subscribe()
Note
This engine plugin is only available on Unix and similar systems which provide fork().
If a startup error occurs in the forked children, the return code from the parent process will still be 0. Errors in the initial daemonizing process still return proper exit codes, but errors after the fork won’t. Therefore, if you use this plugin to daemonize, don’t use the return code as an accurate indicator of whether the process fully started. In fact, that return code only indicates if the process successfully finished the first fork.
The plugin takes optional arguments to redirect standard streams: stdin
,
stdout
, and stderr
. By default, these are all redirected to
/dev/null
, but you’re free to send them to log files or elsewhere.
Warning
You should be careful to not start any threads before this plugin runs. The plugin will warn if you do so, because ”...the effects of calling functions that require certain resources between the call to fork() and the call to an exec function are undefined”. (ref). It is for this reason that the Server plugin runs at priority 75 (it starts worker threads), which is later than the default priority of 65 for the Daemonizer.
Run as a different user¶
Use this engine plugin to start your CherryPy site as root (for example, to listen on a privileged port like 80) and then reduce privileges to something more restricted.
This priority of this plugin’s “start” listener is slightly higher than the priority for server.start in order to facilitate the most common use: starting on a low port (which requires root) and then dropping to another user.
DropPrivileges(cherrypy.engine, uid=1000, gid=1000).subscribe()
PID files¶
The PIDFile engine plugin is pretty straightforward: it writes the process id to a file on start, and deletes the file on exit. You must provide a ‘pidfile’ argument, preferably an absolute path:
PIDFile(cherrypy.engine, '/var/run/myapp.pid').subscribe()
Systemd socket activation¶
Socket Activation is a systemd feature that allows to setup a system so that the systemd will sit on a port and start services ‘on demand’ (a little bit like inetd and xinetd used to do).
CherryPy has built-in socket activation support, if run from a systemd service file it will detect the LISTEN_PID environment variable to know that it should consider fd 3 to be the passed socket.
To read more about socket activation: http://0pointer.de/blog/projects/socket-activation.html
Control via Supervisord¶
Supervisord is a powerful process control and management tool that can perform a lot of tasks around process monitoring.
Below is a simple supervisor configuration for your CherryPy application.
[unix_http_server]
file=/tmp/supervisor.sock
[supervisord]
logfile=/tmp/supervisord.log ; (main log file;default $CWD/supervisord.log)
logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
logfile_backups=10 ; (num of main logfile rotation backups;default 10)
loglevel=info ; (log level;default info; others: debug,warn,trace)
pidfile=/tmp/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
nodaemon=false ; (start in foreground if true;default false)
minfds=1024 ; (min. avail startup file descriptors;default 1024)
minprocs=200 ; (min. avail process descriptors;default 200)
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///tmp/supervisor.sock
[program:myapp]
command=python server.py
environment=PYTHONPATH=.
directory=.
This could control your server via the server.py
module as
the application entry point.
import cherrypy
class Root(object):
@cherrypy.expose
def index(self):
return "Hello World!"
cherrypy.config.update({'server.socket_port': 8090,
'engine.autoreload.on': False,
'log.access_file': './access.log',
'log.error_file': './error.log'})
cherrypy.quickstart(Root())
To take the configuration (assuming it was saved in a file
called supervisor.conf
) into account:
$ supervisord -c supervisord.conf
$ supervisorctl update
Now, you can point your browser at http://localhost:8090/ and it will display Hello World!.
To stop supervisor, type:
$ supervisorctl shutdown
This will obviously shutdown your application.
SSL support¶
Note
You may want to test your server for SSL using the services from Qualys, Inc.
CherryPy can encrypt connections using SSL to create an https connection. This keeps your web traffic secure. Here’s how.
- Generate a private key. We’ll use openssl and follow the OpenSSL Keys HOWTO.:
$ openssl genrsa -out privkey.pem 2048
You can create either a key that requires a password to use, or one without a password. Protecting your private key with a password is much more secure, but requires that you enter the password every time you use the key. For example, you may have to enter the password when you start or restart your CherryPy server. This may or may not be feasible, depending on your setup.
If you want to require a password, add one of the -aes128
, -aes192
or -aes256
switches to the command above. You should not use any of the DES, 3DES, or SEED algoritms to protect your password, as they are insecure.
SSL Labs recommends using 2048-bit RSA keys for security (see references section at the end).
- Generate a certificate. We’ll use openssl and follow the OpenSSL Certificates HOWTO. Let’s start off with a self-signed certificate for testing:
$ openssl req -new -x509 -days 365 -key privkey.pem -out cert.pem
openssl will then ask you a series of questions. You can enter whatever values are applicable, or leave most fields blank. The one field you must fill in is the ‘Common Name’: enter the hostname you will use to access your site. If you are just creating a certificate to test on your own machine and you access the server by typing ‘localhost’ into your browser, enter the Common Name ‘localhost’.
Decide whether you want to use python’s built-in SSL library, or the pyOpenSSL library. CherryPy supports either.
- Built-in. To use python’s built-in SSL, add the following line to your CherryPy config:
cherrypy.server.ssl_module = 'builtin'
- pyOpenSSL. Because python did not have a built-in SSL library when CherryPy was first created, the default setting is to use pyOpenSSL. To use it you’ll need to install it (we could recommend you install cython first):
$ pip install cython, pyOpenSSL
Add the following lines in your CherryPy config to point to your certificate files:
cherrypy.server.ssl_certificate = "cert.pem"
cherrypy.server.ssl_private_key = "privkey.pem"
- If you have a certificate chain at hand, you can also specify it:
cherrypy.server.ssl_certificate_chain = "certchain.perm"
- Start your CherryPy server normally. Note that if you are debugging locally and/or using a self-signed certificate, your browser may show you security warnings.
WSGI servers¶
Embedding into another WSGI framework¶
Though CherryPy comes with a very reliable and fast enough HTTP server, you may wish to integrate your CherryPy application within a different framework. To do so, we will benefit from the WSGI interface defined in PEP 333 and PEP 3333.
Note that you should follow some basic rules when embedding CherryPy in a third-party WSGI server:
If you rely on the “main” channel to be published on, as it would happen within the CherryPy’s mainloop, you should find a way to publish to it within the other framework’s mainloop.
Start the CherryPy’s engine. This will publish to the “start” channel of the bus.
cherrypy.engine.start()
Stop the CherryPy’s engine when you terminate. This will publish to the “stop” channel of the bus.
cherrypy.engine.stop()
Do not call
cherrypy.engine.block()
.Disable the built-in HTTP server since it will not be used.
cherrypy.server.unsubscribe()
Disable autoreload. Usually other frameworks won’t react well to it, or sometimes, provide the same feature.
cherrypy.config.update({'engine.autoreload.on': False})
Disable CherryPy signals handling. This may not be needed, it depends on how the other framework handles them.
cherrypy.engine.signals.subscribe()
Use the
"embedded"
environment configuration scheme.cherrypy.config.update({'environment': 'embedded'})
Essentially this will disable the following:
- Stdout logging
- Autoreloader
- Configuration checker
- Headers logging on error
- Tracebacks in error
- Mismatched params error during dispatching
- Signals (SIGHUP, SIGTERM)
Tornado¶
You can use tornado HTTP server as follow:
import cherrypy
class Root(object):
@cherrypy.expose
def index(self):
return "Hello World!"
if __name__ == '__main__':
import tornado
import tornado.httpserver
import tornado.wsgi
# our WSGI application
wsgiapp = cherrypy.tree.mount(Root())
# Disable the autoreload which won't play well
cherrypy.config.update({'engine.autoreload.on': False})
# let's not start the CherryPy HTTP server
cherrypy.server.unsubscribe()
# use CherryPy's signal handling
cherrypy.engine.signals.subscribe()
# Prevent CherryPy logs to be propagated
# to the Tornado logger
cherrypy.log.error_log.propagate = False
# Run the engine but don't block on it
cherrypy.engine.start()
# Run thr tornado stack
container = tornado.wsgi.WSGIContainer(wsgiapp)
http_server = tornado.httpserver.HTTPServer(container)
http_server.listen(8080)
# Publish to the CherryPy engine as if
# we were using its mainloop
tornado.ioloop.PeriodicCallback(lambda: cherrypy.engine.publish('main'), 100).start()
tornado.ioloop.IOLoop.instance().start()
Twisted¶
You can use Twisted HTTP server as follow:
import cherrypy
from twisted.web.wsgi import WSGIResource
from twisted.internet import reactor
from twisted.internet import task
# Our CherryPy application
class Root(object):
@cherrypy.expose
def index(self):
return "hello world"
# Create our WSGI app from the CherryPy application
wsgiapp = cherrypy.tree.mount(Root())
# Configure the CherryPy's app server
# Disable the autoreload which won't play well
cherrypy.config.update({'engine.autoreload.on': False})
# We will be using Twisted HTTP server so let's
# disable the CherryPy's HTTP server entirely
cherrypy.server.unsubscribe()
# If you'd rather use CherryPy's signal handler
# Uncomment the next line. I don't know how well this
# will play with Twisted however
#cherrypy.engine.signals.subscribe()
# Publish periodically onto the 'main' channel as the bus mainloop would do
task.LoopingCall(lambda: cherrypy.engine.publish('main')).start(0.1)
# Tie our app to Twisted
reactor.addSystemEventTrigger('after', 'startup', cherrypy.engine.start)
reactor.addSystemEventTrigger('before', 'shutdown', cherrypy.engine.exit)
resource = WSGIResource(reactor, reactor.getThreadPool(), wsgiapp)
Notice how we attach the bus methods to the Twisted’s own lifecycle.
Save that code into a module named cptw.py and run it as follows:
$ twistd -n web --port 8080 --wsgi cptw.wsgiapp
uwsgi¶
You can use uwsgi HTTP server as follow:
import cherrypy
# Our CherryPy application
class Root(object):
@cherrypy.expose
def index(self):
return "hello world"
cherrypy.config.update({'engine.autoreload.on': False})
cherrypy.server.unsubscribe()
cherrypy.engine.start()
wsgiapp = cherrypy.tree.mount(Root())
Save this into a Python module called mymod.py and run it as follows:
$ uwsgi --socket 127.0.0.1:8080 --protocol=http --wsgi-file mymod.py --callable wsgiapp
Virtual Hosting¶
CherryPy has support for virtual-hosting. It does so through a dispatchers that locate the appropriate resource based on the requested domain.
Below is a simple example for it:
import cherrypy
class Root(object):
def __init__(self):
self.app1 = App1()
self.app2 = App2()
class App1(object):
@cherrypy.expose
def index(self):
return "Hello world from app1"
class App2(object):
@cherrypy.expose
def index(self):
return "Hello world from app2"
if __name__ == '__main__':
hostmap = {
'company.com:8080': '/app1',
'home.net:8080': '/app2',
}
config = {
'request.dispatch': cherrypy.dispatch.VirtualHost(**hostmap)
}
cherrypy.quickstart(Root(), '/', {'/': config})
In this example, we declare two domains and their ports:
- company.com:8080
- home.net:8080
Thanks to the cherrypy.dispatch.VirtualHost
dispatcher,
we tell CherryPy which application to dispatch to when a request
arrives. The dispatcher looks up the requested domain and
call the according application.
Note
To test this example, simply add the following rules to your hosts file:
127.0.0.1 company.com
127.0.0.1 home.net
Reverse-proxying¶
Nginx¶
nginx is a fast and modern HTTP server with a small footprint. It is a popular choice as a reverse proxy to application servers such as CherryPy.
This section will not cover the whole range of features nginx provides. Instead, it will simply provide you with a basic configuration that can be a good starting point.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | upstream apps {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
gzip_http_version 1.0;
gzip_proxied any;
gzip_min_length 500;
gzip_disable "MSIE [1-6]\.";
gzip_types text/plain text/xml text/css
text/javascript
application/javascript;
server {
listen 80;
server_name www.example.com;
access_log /app/logs/www.example.com.log combined;
error_log /app/logs/www.example.com.log;
location ^~ /static/ {
root /app/static/;
}
location / {
proxy_pass http://apps;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $server_name;
}
}
|
Edit this configuration to match your own paths. Then, save this configuration
into a file under /etc/nginx/conf.d/
(assuming Ubuntu).
The filename is irrelevant. Then run the following commands:
$ sudo service nginx stop
$ sudo service nginx start
Hopefully, this will be enough to forward requests hitting
the nginx frontend to your CherryPy application. The upstream
block defines the addresses of your CherryPy instances.
It shows that you can load-balance between two application servers. Refer to the nginx documentation to understand how this achieved.
upstream apps {
server 127.0.0.1:8080;
server 127.0.0.1:8081;
}
Later on, this block is used to define the reverse proxy section.
Now, let’s see our application:
import cherrypy
class Root(object):
@cherrypy.expose
def index(self):
return "hello world"
if __name__ == '__main__':
cherrypy.config.update({
'server.socket_port': 8080,
'tools.proxy.on': True,
'tools.proxy.base': 'http://www.example.com'
})
cherrypy.quickstart(Root())
If you run two instances of this code, one on each port defined in the nginx section, you will be able to reach both of them via the load-balancing done by nginx.
Notice how we define the proxy tool. It is not mandatory and used only so that the CherryPy request knows about the true client’s address. Otherwise, it would know only about the nginx’s own address. This is most visible in the logs.
The base
attribute should match the server_name
section of the nginx configuration.
Support¶
You’ve read the documentation and you’ve brushed up on the basics of Python and web development, but you still could use some help. Users have several options.
I have a question¶
If you have a question and cannot find an answer for it in issues or the the documentation, please create an issue.
Questions and their answers have great value for the community, and a tip is to really put the effort in and write a good explanation, you will get better and quicker answers. Examples are strongly encouraged.
I have found a bug¶
If no one have already, create an issue. Be sure to provide ample information, remember that any help won’t be better than your explanation.
Unless something is very obviously wrong, you are likely to be asked to provide a working example, displaying the erroneous behaviour.
Note: While this might feel troublesome, a tip is to always make a separate example that have the same dependencies as your project. It is great for troubleshooting those annoying problems where you don’t know if the problem is at your end or the components. Also, you can then easily fork and provide as an example. You will get answers and resolutions way quicker. Also, many other open source projects require it.
I have a feature request¶
Good stuff! Please create an issue! Note: Features are more likely to be added the more users they seem to benefit.
I want to converse¶
The gitter page is good for when you want to discuss in real time or get pointed in the right direction.
Contribute¶
CherryPy is a community-maintained, open-source project hosted at Github. The project active encourages aspiring and experienced users to dive in and add their best contribution to the project.
How can you contribute? Well, first search the docs and the project page to see if someone has already reported your issue.
StackOverflow¶
On StackOverflow, there are questions tagged with ‘cherrypy’. Answer unanswered questions, add an improved answer, clarify an answer with a comment, or ask more meaningful questions there. Earn reputation and share experience.
CherryPy also maintains a StackOverflow Wiki where anyone can publish tricks and techniques and refine others.
Filing Bug Reports¶
If you find a bug, an issue where the product doesn’t behave as you expect, you may file a bug report at the project page. Be sure to include what your expectation was, what happened instead, details about your system that might be relevant, and steps that someone else could take to replicate your finding. The more detailed and exact your description, the better one of the volunteers on the project may be able to help resolve your issue.
Fixing Bugs¶
CherryPy has a number of open, reported issues. Some of them are complicated and difficult, but others are more straightforward and shovel-ready. Feel free to find one that you think you can solve or introduce yourself and ask for guidance in our gitter channel.
As you work through the issue and commit changes to your clone of the repository, be sure to add issue references to your changes (like “Fixes #999” or “Ref #999”) so your changes link to the issue and vice-versa.
Writing Pull Requests¶
To contribute, first read How to write the perfect pull request and file your contribution with the CherryPy Project page.
Testing¶
To run the regression tests, first install tox:
pip install 'tox>=2.5'
then run it
tox
To run individual tests type:
tox -- -k test_foo
Glossary¶
- application
- A CherryPy application is simply a class instance containing at least one page handler.
- controller
- Loose name commonly given to a class owning at least one exposed method
- exposed
A Python function or method which has an attribute called exposed set to True. This attribute can be set directly or via the
cherrypy.expose()
decorator.@cherrypy.expose def method(...): ...
is equivalent to:
def method(...): ... method.exposed = True
- page handler
- Name commonly given to an exposed method
History¶
v10.2.0¶
- #794: Prefer setting max-age for session cookie expiration, moving MSIE hack into a function documenting its purpose.
v10.1.0¶
- Bump to cheroot 5.1.0.
v10.0.0¶
20 Jan 2017
#1332: CherryPy now uses portend for checking and waiting on ports for startup and teardown checks. The following names are no longer present:
- cherrypy._cpserver.client_host
- cherrypy._cpserver.check_port
- cherrypy._cpserver.wait_for_free_port
- cherrypy._cpserver.wait_for_occupied_port
- cherrypy.process.servers.check_port
- cherrypy.process.servers.wait_for_free_port
- cherrypy.process.servers.wait_for_occupied_port
Use this functionality from the portend package directly.
v8.9.0¶
13 Jan 2017
#1547: Replaced
cherryd
distutils script with a setuptools console entry point.When running CherryPy in daemon mode, the forked process no longer changes directory to
/
. If that behavior is something on which your application relied and should rely, please file a ticket with the project.
v8.6.0¶
27 Dec 2016
- #1538 and #1090: Removed cruft from the setup script and instead rely on include_package_data to ensure the relevant files are included in the package. Note, this change does cause LICENSE.md no longer to be included in the installed package.
v8.5.0¶
26 Dec 2016
v8.3.1¶
25 Dec 2016
- #1537: Disable dependency on pypiwin32 on Python 3.6 until a viable build of pypiwin32 can be made on that Python version.
v8.3.0¶
24 Dec 2016
- Consolidated some documentation and include the more concise readme in the package long description, as found on PyPI.
v8.1.1¶
27 Sep 2016
v8.1.0¶
04 Sep 2016
- #1473:
HTTPError
now also works as a context manager. - #1487: The sessions tool now accepts a
storage_class
parameter, which supersedes the new deprecatedstorage_type
parameter. Thestorage_class
should be the actual Session subclass to be used. - Releases now use
setuptools_scm
to track the release versions. Therefore, releases can be cut by simply tagging a commit in the repo. Versions numbers are now stored in exactly one place.
v8.0.1¶
03 Sep 2016
v8.0.0¶
02 Sep 2016
- #1483: Remove Deprecated constructs:
cherrypy.lib.http
module.unrepr
,modules
, andattributes
incherrypy.lib
.
- #1476: Drop support for python-memcached<1.58
- #1401: Handle NoSSLErrors.
- #1489: In
wsgiserver.WSGIGateway.respond
, the application must now yield bytes and not text, as the spec requires. If text is received, it will now raise a ValueError instead of silently encoding using ISO-8859-1. - Removed unicode filename from the package, working around pip #3894 and setuptools #704.
v7.1.0¶
25 Jul 2016
- # 1458: Implement systemd’s socket activation mechanism for
CherryPy servers, based on work sponsored by Endless Computers.
Socket Activation allows one to setup a system so that systemd will sit on a port and start services ‘on demand’ (a little bit like inetd and xinetd used to do).
v7.0.0¶
24 Jul 2016
Removed the long-deprecated backward compatibility for legacy config keys in the engine. Use the config for the namespaced-plugins instead:
- autoreload_on -> autoreload.on
- autoreload_frequency -> autoreload.frequency
- autoreload_match -> autoreload.match
- reload_files -> autoreload.files
- deadlock_poll_frequency -> timeout_monitor.frequency
v6.2.0¶
18 Jul 2016
#1441: Added tool to automatically convert request params based on type annotations (primarily in Python 3). For example:
@cherrypy.tools.params() def resource(self, limit: int):
assert isinstance(limit, int)
v6.1.1¶
16 Jul 2016
- Issue #1411: Fix issue where autoreload fails when
the host interpreter for CherryPy was launched using
python -m
.
v6.1.0¶
14 Jul 2016
- Combined wsgiserver2 and wsgiserver3 modules into a
single module,
cherrypy.wsgiserver
.
v6.0.0¶
05 Jun 2016
- Setuptools is now required to build CherryPy. Pure distutils installs are no longer supported. This change allows CherryPy to depend on other packages and re-use code from them. It’s still possible to install pre-built CherryPy packages (wheels) using pip without Setuptools.
- six is now a requirement and subsequent requirements will be declared in the project metadata.
- #1440: Back out changes from #1432 attempting to
fix redirects with Unicode URLs, as it also had the
unintended consequence of causing the ‘Location’
to be
bytes
on Python 3. cherrypy.expose
now works on classes.cherrypy.config
decorator is now used throughout the code internally.
v5.6.0¶
05 Jun 2016
@cherrypy.expose
now will also set the exposed attribute on a class.- Rewrote all tutorials and internal usage to prefer
the decorator usage of
expose
rather than setting the attribute explicitly. - Removed test-specific code from tutorials.
v5.5.0¶
05 Jun 2016
- #1397: Fix for filenames with semicolons and quote characters in filenames found in headers.
- #1311: Added decorator for registering tools.
- #1194: Use simpler encoding rules for SCRIPT_NAME
and PATH_INFO environment variables in CherryPy Tree
allowing non-latin characters to pass even when
wsgi.version
is notu.0
. - #1352: Ensure that multipart fields are decoded even when cached in a file.
v5.4.0¶
10 May 2016
cherrypy.test.webtest.WebCase
now honors a ‘WEBTEST_INTERACTIVE’ environment variable to disable interactive tests (still enabled by default). Set to ‘0’ or ‘false’ or ‘False’ to disable interactive tests.- #1408: Fix AttributeError when listiterator was accessed
using the
next
attribute. - #748: Removed
cherrypy.lib.sessions.PostgresqlSession
. - #1432: Fix errors with redirects to Unicode URLs.
v5.3.0¶
30 Apr 2016
v5.1.0¶
- Bugfix issue #1315 for
test_HTTP11_pipelining
test in Python 3.5 - Bugfix issue #1382 regarding the keyword arguments support for Python 3 on the config file.
- Bugfix issue #1406 for
test_2_KeyboardInterrupt
test in Python 3.5. by monkey patching the HTTPRequest given a bug on CPython that is affecting the testsuite (https://bugs.python.org/issue23377). - Add additional parameter
raise_subcls
to the tests helpers openURL andCPWebCase.getPage
to have finer control on which exceptions can be raised. - Add support for direct keywords on the calls (e.g.
foo=bar
) on the config file under Python 3. - Add additional validation to determine if the process is running
as a daemon on
cherrypy.process.plugins.SignalHandler
to allow the execution of the testsuite under CI tools.
v5.0.0¶
- Removed deprecated support for
ssl_certificate
andssl_private_key
attributes and implicit construction of SSL adapter on Python 2 WSGI servers. - Default SSL Adapter on Python 2 is the builtin SSL adapter, matching Python 3 behavior.
- Pull request #94: In proxy tool, defer to Host header for resolving the base if no base is supplied.
v4.0.0¶
- Drop support for Python 2.5 and earlier.
- No longer build Windows installers by default.
v3.8.2¶
- Pull Request #116: Correct InternalServerError when null bytes in static file path. Now responds with 404 instead.
v3.8.0¶
- Pull Request #96: Pass
exc_info
to logger as keyword rather than formatting the error and injecting into the message.
v3.7.0¶
- CherryPy daemon may now be invoked with
python -m cherrypy
in addition to thecherryd
script. - Issue #1298: Fix SSL handling on CPython 2.7 with builtin SSL module and pyOpenSSL 0.14. This change will break PyPy for now.
- Several documentation fixes.
v3.6.0¶
- Fixed HTTP range headers for negative length larger than content size.
- Disabled universal wheel generation as wsgiserver has Python duality.
- Pull Request #42: Correct TypeError in
check_auth
when encrypt is used. - Pull Request #59: Correct signature of HandlerWrapperTool.
- Pull Request #60: Fix error in SessionAuth where login_screen was incorrectly used.
- Issue #1077: Support keyword-only arguments in dispatchers (Python 3).
- Issue #1019: Allow logging host name in the access log.
- Pull Request #50: Fixed race condition in session cleanup.
v3.5.0¶
- Issue #1301: When the incoming queue is full, now reject additional connections. This functionality was added to CherryPy 3.0, but unintentionally lost in 3.1.
v3.4.0¶
- Miscellaneous quality improvements.
v3.3.0¶
CherryPy adopts semver.
Modules¶
cherrypy package¶
Subpackages¶
cherrypy.lib package¶
Submodules¶
cherrypy.lib.auth module¶
-
cherrypy.lib.auth.
basic_auth
(realm, users, encrypt=None, debug=False)[source]¶ If auth fails, raise 401 with a basic authentication header.
- realm
- A string containing the authentication realm.
- users
- A dict of the form: {username: password} or a callable returning a dict.
- encrypt
- callable used to encrypt the password returned from the user-agent. if None it defaults to a md5 encryption.
cherrypy.lib.auth_basic module¶
This module provides a CherryPy 3.x tool which implements the server-side of HTTP Basic Access Authentication, as described in RFC 2617.
Example usage, using the built-in checkpassword_dict function which uses a dict as the credentials store:
userpassdict = {'bird' : 'bebop', 'ornette' : 'wayout'}
checkpassword = cherrypy.lib.auth_basic.checkpassword_dict(userpassdict)
basic_auth = {'tools.auth_basic.on': True,
'tools.auth_basic.realm': 'earth',
'tools.auth_basic.checkpassword': checkpassword,
}
app_config = { '/' : basic_auth }
-
cherrypy.lib.auth_basic.
basic_auth
(realm, checkpassword, debug=False)[source]¶ A CherryPy tool which hooks at before_handler to perform HTTP Basic Access Authentication, as specified in RFC 2617.
If the request has an ‘authorization’ header with a ‘Basic’ scheme, this tool attempts to authenticate the credentials supplied in that header. If the request has no ‘authorization’ header, or if it does but the scheme is not ‘Basic’, or if authentication fails, the tool sends a 401 response with a ‘WWW-Authenticate’ Basic header.
- realm
- A string containing the authentication realm.
- checkpassword
- A callable which checks the authentication credentials. Its signature is checkpassword(realm, username, password). where username and password are the values obtained from the request’s ‘authorization’ header. If authentication succeeds, checkpassword returns True, else it returns False.
-
cherrypy.lib.auth_basic.
checkpassword_dict
(user_password_dict)[source]¶ Returns a checkpassword function which checks credentials against a dictionary of the form: {username : password}.
If you want a simple dictionary-based authentication scheme, use checkpassword_dict(my_credentials_dict) as the value for the checkpassword argument to basic_auth().
cherrypy.lib.auth_digest module¶
An implementation of the server-side of HTTP Digest Access Authentication, which is described in RFC 2617.
Example usage, using the built-in get_ha1_dict_plain function which uses a dict of plaintext passwords as the credentials store:
userpassdict = {'alice' : '4x5istwelve'}
get_ha1 = cherrypy.lib.auth_digest.get_ha1_dict_plain(userpassdict)
digest_auth = {'tools.auth_digest.on': True,
'tools.auth_digest.realm': 'wonderland',
'tools.auth_digest.get_ha1': get_ha1,
'tools.auth_digest.key': 'a565c27146791cfb',
}
app_config = { '/' : digest_auth }
-
class
cherrypy.lib.auth_digest.
HttpDigestAuthorization
(auth_header, http_method, debug=False)[source]¶ Bases:
object
Class to parse a Digest Authorization header and perform re-calculation of the digest.
-
is_nonce_stale
(max_age_seconds=600)[source]¶ Returns True if a validated nonce is stale. The nonce contains a timestamp in plaintext and also a secure hash of the timestamp. You should first validate the nonce to ensure the plaintext timestamp is not spoofed.
-
request_digest
(ha1, entity_body='')[source]¶ Calculates the Request-Digest. See RFC 2617 section 3.2.2.1.
- ha1
- The HA1 string obtained from the credentials store.
- entity_body
- If ‘qop’ is set to ‘auth-int’, then A2 includes a hash of the “entity body”. The entity body is the part of the message which follows the HTTP headers. See RFC 2617 section 4.3. This refers to the entity the user agent sent in the request which has the Authorization header. Typically GET requests don’t have an entity, and POST requests do.
-
validate_nonce
(s, key)[source]¶ Validate the nonce. Returns True if nonce was generated by synthesize_nonce() and the timestamp is not spoofed, else returns False.
- s
- A string related to the resource, such as the hostname of the server.
- key
- A secret string known only to the server.
Both s and key must be the same values which were used to synthesize the nonce we are trying to validate.
-
-
cherrypy.lib.auth_digest.
digest_auth
(realm, get_ha1, key, debug=False)[source]¶ A CherryPy tool which hooks at before_handler to perform HTTP Digest Access Authentication, as specified in RFC 2617.
If the request has an ‘authorization’ header with a ‘Digest’ scheme, this tool authenticates the credentials supplied in that header. If the request has no ‘authorization’ header, or if it does but the scheme is not “Digest”, or if authentication fails, the tool sends a 401 response with a ‘WWW-Authenticate’ Digest header.
- realm
- A string containing the authentication realm.
- get_ha1
- A callable which looks up a username in a credentials store
and returns the HA1 string, which is defined in the RFC to be
MD5(username : realm : password). The function’s signature is:
get_ha1(realm, username)
where username is obtained from the request’s ‘authorization’ header. If username is not found in the credentials store, get_ha1() returns None. - key
- A secret string known only to the server, used in the synthesis of nonces.
-
cherrypy.lib.auth_digest.
get_ha1_dict
(user_ha1_dict)[source]¶ Returns a get_ha1 function which obtains a HA1 password hash from a dictionary of the form: {username : HA1}.
If you want a dictionary-based authentication scheme, but with pre-computed HA1 hashes instead of plain-text passwords, use get_ha1_dict(my_userha1_dict) as the value for the get_ha1 argument to digest_auth().
-
cherrypy.lib.auth_digest.
get_ha1_dict_plain
(user_password_dict)[source]¶ Returns a get_ha1 function which obtains a plaintext password from a dictionary of the form: {username : password}.
If you want a simple dictionary-based authentication scheme, with plaintext passwords, use get_ha1_dict_plain(my_userpass_dict) as the value for the get_ha1 argument to digest_auth().
-
cherrypy.lib.auth_digest.
get_ha1_file_htdigest
(filename)[source]¶ Returns a get_ha1 function which obtains a HA1 password hash from a flat file with lines of the same format as that produced by the Apache htdigest utility. For example, for realm ‘wonderland’, username ‘alice’, and password ‘4x5istwelve’, the htdigest line would be:
alice:wonderland:3238cdfe91a8b2ed8e39646921a02d4c
If you want to use an Apache htdigest file as the credentials store, then use get_ha1_file_htdigest(my_htdigest_file) as the value for the get_ha1 argument to digest_auth(). It is recommended that the filename argument be an absolute path, to avoid problems.
-
cherrypy.lib.auth_digest.
md5_hex
(s)¶
-
cherrypy.lib.auth_digest.
synthesize_nonce
(s, key, timestamp=None)[source]¶ Synthesize a nonce value which resists spoofing and can be checked for staleness. Returns a string suitable as the value for ‘nonce’ in the www-authenticate header.
- s
- A string related to the resource, such as the hostname of the server.
- key
- A secret string known only to the server.
- timestamp
- An integer seconds-since-the-epoch timestamp
cherrypy.lib.caching module¶
CherryPy implements a simple caching system as a pluggable Tool. This tool tries to be an (in-process) HTTP/1.1-compliant cache. It’s not quite there yet, but it’s probably good enough for most sites.
In general, GET responses are cached (along with selecting headers) and, if another request arrives for the same resource, the caching Tool will return 304 Not Modified if possible, or serve the cached response otherwise. It also sets request.cached to True if serving a cached representation, and sets request.cacheable to False (so it doesn’t get cached again).
If POST, PUT, or DELETE requests are made for a cached resource, they invalidate (delete) any cached response.
Configuration file example:
[/]
tools.caching.on = True
tools.caching.delay = 3600
You may use a class other than the default
MemoryCache
by supplying the config
entry cache_class
; supply the full dotted name of the replacement class
as the config value. It must implement the basic methods get
, put
,
delete
, and clear
.
You may set any attribute, including overriding methods, on the cache
instance by providing them in config. The above sets the
delay
attribute, for example.
-
class
cherrypy.lib.caching.
AntiStampedeCache
[source]¶ Bases:
dict
A storage system for cached items which reduces stampede collisions.
-
wait
(key, timeout=5, debug=False)[source]¶ Return the cached value for the given key, or None.
If timeout is not None, and the value is already being calculated by another thread, wait until the given timeout has elapsed. If the value is available before the timeout expires, it is returned. If not, None is returned, and a sentinel placed in the cache to signal other threads to wait.
If timeout is None, no waiting is performed nor sentinels used.
-
-
class
cherrypy.lib.caching.
MemoryCache
[source]¶ Bases:
cherrypy.lib.caching.Cache
An in-memory cache for varying response content.
Each key in self.store is a URI, and each value is an AntiStampedeCache. The response for any given URI may vary based on the values of “selecting request headers”; that is, those named in the Vary response header. We assume the list of header names to be constant for each URI throughout the lifetime of the application, and store that list in
self.store[uri].selecting_headers
.The items contained in
self.store[uri]
have keys which are tuples of request header values (in the same order as the names in its selecting_headers), and values which are the actual responses.-
antistampede_timeout
= 5¶ Seconds to wait for other threads to release a cache lock.
-
debug
= False¶
-
delay
= 600¶ Seconds until the cached content expires; defaults to 600 (10 minutes).
-
expire_cache
()[source]¶ Continuously examine cached objects, expiring stale ones.
This function is designed to be run in its own daemon thread, referenced at
self.expiration_thread
.
-
expire_freq
= 0.1¶ Seconds to sleep between cache expiration sweeps.
-
maxobj_size
= 100000¶ The maximum size of each cached object in bytes; defaults to 100 KB.
-
maxobjects
= 1000¶ The maximum number of cached objects; defaults to 1000.
-
maxsize
= 10000000¶ The maximum size of the entire cache in bytes; defaults to 10 MB.
-
-
cherrypy.lib.caching.
expires
(secs=0, force=False, debug=False)[source]¶ Tool for influencing cache mechanisms using the ‘Expires’ header.
- secs
Must be either an int or a datetime.timedelta, and indicates the number of seconds between response.time and when the response should expire. The ‘Expires’ header will be set to response.time + secs. If secs is zero, the ‘Expires’ header is set one year in the past, and the following “cache prevention” headers are also set:
- Pragma: no-cache
- Cache-Control’: no-cache, must-revalidate
- force
If False, the following headers are checked:
- Etag
- Last-Modified
- Age
- Expires
If any are already present, none of the above response headers are set.
-
cherrypy.lib.caching.
get
(invalid_methods=('POST', 'PUT', 'DELETE'), debug=False, **kwargs)[source]¶ Try to obtain cached output. If fresh enough, raise HTTPError(304).
- If POST, PUT, or DELETE:
- invalidates (deletes) any cached response for this resource
- sets request.cached = False
- sets request.cacheable = False
- else if a cached copy exists:
- sets request.cached = True
- sets request.cacheable = False
- sets response.headers to the cached values
- checks the cached Last-Modified response header against the current If-(Un)Modified-Since request headers; raises 304 if necessary.
- sets response.status and response.body to the cached values
- returns True
- otherwise:
- sets request.cached = False
- sets request.cacheable = True
- returns False
cherrypy.lib.covercp module¶
Code-coverage tools for CherryPy.
To use this module, or the coverage tools in the test suite, you need to download ‘coverage.py’, either Gareth Rees’ original implementation or Ned Batchelder’s enhanced version:
To turn on coverage tracing, use the following code:
cherrypy.engine.subscribe('start', covercp.start)
DO NOT subscribe anything on the ‘start_thread’ channel, as previously recommended. Calling start once in the main thread should be sufficient to start coverage on all threads. Calling start again in each thread effectively clears any coverage data gathered up to that point.
Run your code, then use the covercp.serve()
function to browse the
results in a web browser. If you run this module from the command line,
it will call serve()
for you.
-
cherrypy.lib.covercp.
get_tree
(base, exclude, coverage=<coverage.control.Coverage object>)[source]¶ Return covered module names as a nested dict.
cherrypy.lib.cpstats module¶
CPStats, a package for collecting and reporting on program statistics.
Statistics about program operation are an invaluable monitoring and debugging tool. Unfortunately, the gathering and reporting of these critical values is usually ad-hoc. This package aims to add a centralized place for gathering statistical performance data, a structure for recording that data which provides for extrapolation of that data into more useful information, and a method of serving that data to both human investigators and monitoring software. Let’s examine each of those in more detail.
Just as Python’s logging module provides a common importable for gathering and sending messages, performance statistics would benefit from a similar common mechanism, and one that does not require each package which wishes to collect stats to import a third-party module. Therefore, we choose to re-use the logging module by adding a statistics object to it.
That logging.statistics object is a nested dict. It is not a custom class, because that would:
- require libraries and applications to import a third-party module in order to participate
- inhibit innovation in extrapolation approaches and in reporting tools, and
- be slow.
There are, however, some specifications regarding the structure of the dict.:
{
+----"SQLAlchemy": {
| "Inserts": 4389745,
| "Inserts per Second":
| lambda s: s["Inserts"] / (time() - s["Start"]),
| C +---"Table Statistics": {
| o | "widgets": {-----------+
N | l | "Rows": 1.3M, | Record
a | l | "Inserts": 400, |
m | e | },---------------------+
e | c | "froobles": {
s | t | "Rows": 7845,
p | i | "Inserts": 0,
a | o | },
c | n +---},
e | "Slow Queries":
| [{"Query": "SELECT * FROM widgets;",
| "Processing Time": 47.840923343,
| },
| ],
+----},
}
The logging.statistics dict has four levels. The topmost level is nothing more than a set of names to introduce modularity, usually along the lines of package names. If the SQLAlchemy project wanted to participate, for example, it might populate the item logging.statistics[‘SQLAlchemy’], whose value would be a second-layer dict we call a “namespace”. Namespaces help multiple packages to avoid collisions over key names, and make reports easier to read, to boot. The maintainers of SQLAlchemy should feel free to use more than one namespace if needed (such as ‘SQLAlchemy ORM’). Note that there are no case or other syntax constraints on the namespace names; they should be chosen to be maximally readable by humans (neither too short nor too long).
Each namespace, then, is a dict of named statistical values, such as ‘Requests/sec’ or ‘Uptime’. You should choose names which will look good on a report: spaces and capitalization are just fine.
In addition to scalars, values in a namespace MAY be a (third-layer)
dict, or a list, called a “collection”. For example, the CherryPy
StatsTool
keeps track of what each request is doing (or has most
recently done) in a ‘Requests’ collection, where each key is a thread ID; each
value in the subdict MUST be a fourth dict (whew!) of statistical data about
each thread. We call each subdict in the collection a “record”. Similarly,
the StatsTool
also keeps a list of slow queries, where each record
contains data about each slow query, in order.
Values in a namespace or record may also be functions, which brings us to:
The collection of statistical data needs to be fast, as close to unnoticeable as possible to the host program. That requires us to minimize I/O, for example, but in Python it also means we need to minimize function calls. So when you are designing your namespace and record values, try to insert the most basic scalar values you already have on hand.
When it comes time to report on the gathered data, however, we usually have
much more freedom in what we can calculate. Therefore, whenever reporting
tools (like the provided StatsPage
CherryPy class) fetch the contents
of logging.statistics for reporting, they first call
extrapolate_statistics (passing the whole statistics dict as the only
argument). This makes a deep copy of the statistics dict so that the
reporting tool can both iterate over it and even change it without harming
the original. But it also expands any functions in the dict by calling them.
For example, you might have a ‘Current Time’ entry in the namespace with the
value “lambda scope: time.time()”. The “scope” parameter is the current
namespace dict (or record, if we’re currently expanding one of those
instead), allowing you access to existing static entries. If you’re truly
evil, you can even modify more than one entry at a time.
However, don’t try to calculate an entry and then use its value in further extrapolations; the order in which the functions are called is not guaranteed. This can lead to a certain amount of duplicated work (or a redesign of your schema), but that’s better than complicating the spec.
After the whole thing has been extrapolated, it’s time for:
The StatsPage
class grabs the logging.statistics dict, extrapolates
it all, and then transforms it to HTML for easy viewing. Each namespace gets
its own header and attribute table, plus an extra table for each collection.
This is NOT part of the statistics specification; other tools can format how
they like.
You can control which columns are output and how they are formatted by updating StatsPage.formatting, which is a dict that mirrors the keys and nesting of logging.statistics. The difference is that, instead of data values, it has formatting values. Use None for a given key to indicate to the StatsPage that a given column should not be output. Use a string with formatting (such as ‘%.3f’) to interpolate the value(s), or use a callable (such as lambda v: v.isoformat()) for more advanced formatting. Any entry which is not mentioned in the formatting dict is output unchanged.
Although the HTML output takes pains to assign unique id’s to each <td> with statistical data, you’re probably better off fetching /cpstats/data, which outputs the whole (extrapolated) logging.statistics dict in JSON format. That is probably easier to parse, and doesn’t have any formatting controls, so you get the “original” data in a consistently-serialized format. Note: there’s no treatment yet for datetime objects. Try time.time() instead for now if you can. Nagios will probably thank you.
It is recommended each namespace have an “Enabled” item which, if False, stops collection (but not reporting) of statistical data. Applications SHOULD provide controls to pause and resume collection by setting these entries to False or True, if present.
To collect statistics on CherryPy applications:
from cherrypy.lib import cpstats
appconfig['/']['tools.cpstats.on'] = True
To collect statistics on your own code:
import logging
# Initialize the repository
if not hasattr(logging, 'statistics'): logging.statistics = {}
# Initialize my namespace
mystats = logging.statistics.setdefault('My Stuff', {})
# Initialize my namespace's scalars and collections
mystats.update({
'Enabled': True,
'Start Time': time.time(),
'Important Events': 0,
'Events/Second': lambda s: (
(s['Important Events'] / (time.time() - s['Start Time']))),
})
...
for event in events:
...
# Collect stats
if mystats.get('Enabled', False):
mystats['Important Events'] += 1
To report statistics:
root.cpstats = cpstats.StatsPage()
To format statistics reports:
See 'Reporting', above.
-
class
cherrypy.lib.cpstats.
ByteCountWrapper
(rfile)[source]¶ Bases:
object
Wraps a file-like object, counting the number of bytes read.
-
class
cherrypy.lib.cpstats.
StatsPage
[source]¶ Bases:
object
-
formatting
= {'CherryPy Applications': {'Uptime': '%.3f', 'Bytes Read/Second': '%.3f', 'Current Time': <function <lambda> at 0x7f7de2730578>, 'Enabled': <function _pause_resume at 0x7f7de27306e0>, 'Slow Queries': {'End Time': None, 'Start Time': <function <lambda> at 0x7f7de2730578>, 'Processing Time': '%.3f'}, 'Requests': {'Bytes Read': '%s', 'End Time': None, 'Processing Time': '%.3f', 'Start Time': None, 'Bytes Written': '%s'}, 'Requests/Second': '%.3f', 'Bytes Written/Request': '%.3f', 'URI Set Tracking': {'Max': '%.3f', 'Sum': '%.3f', 'Avg': '%.3f', 'Min': '%.3f'}, 'Total Time': '%.3f', 'Start Time': <function <lambda> at 0x7f7de2730578>, 'Bytes Written/Second': '%.3f', 'Bytes Read/Request': '%.3f'}, 'CherryPy WSGIServer': {'Connections/second': '%.3f', 'Enabled': <function _pause_resume at 0x7f7de2730758>, 'Start time': <function <lambda> at 0x7f7de2730578>}}¶
-
-
class
cherrypy.lib.cpstats.
StatsTool
[source]¶ Bases:
cherrypy._cptools.Tool
Record various information about the current request.
-
cherrypy.lib.cpstats.
average_uriset_time
(s)¶
-
cherrypy.lib.cpstats.
extrapolate_statistics
(scope)[source]¶ Return an extrapolated copy of the given scope.
-
cherrypy.lib.cpstats.
iso_format
(v)¶
-
cherrypy.lib.cpstats.
locale_date
(v)¶
-
cherrypy.lib.cpstats.
proc_time
(s)¶
cherrypy.lib.cptools module¶
Functions for builtin CherryPy tools.
-
class
cherrypy.lib.cptools.
SessionAuth
[source]¶ Bases:
object
Assert that the user is logged in.
-
debug
= False¶
-
do_login
(username, password, from_page='..', **kwargs)[source]¶ Login. May raise redirect, or return True if request handled.
-
do_logout
(from_page='..', **kwargs)[source]¶ Logout. May raise redirect, or return True if request handled.
-
session_key
= 'username'¶
-
-
cherrypy.lib.cptools.
accept
(media=None, debug=False)[source]¶ Return the client’s preferred media-type (from the given Content-Types).
If ‘media’ is None (the default), no test will be performed.
If ‘media’ is provided, it should be the Content-Type value (as a string) or values (as a list or tuple of strings) which the current resource can emit. The client’s acceptable media ranges (as declared in the Accept request header) will be matched in order to these Content-Type values; the first such string is returned. That is, the return value will always be one of the strings provided in the ‘media’ arg (or None if ‘media’ is None).
If no match is found, then HTTPError 406 (Not Acceptable) is raised. Note that most web browsers send / as a (low-quality) acceptable media range, which should match any Content-Type. In addition, ”...if no Accept header field is present, then it is assumed that the client accepts all media types.”
Matching types are checked in order of client preference first, and then in the order of the given ‘media’ values.
Note that this function does not honor accept-params (other than “q”).
-
cherrypy.lib.cptools.
allow
(methods=None, debug=False)[source]¶ Raise 405 if request.method not in methods (default [‘GET’, ‘HEAD’]).
The given methods are case-insensitive, and may be in any order. If only one method is allowed, you may supply a single string; if more than one, supply a list of strings.
Regardless of whether the current method is allowed or not, this also emits an ‘Allow’ response header, containing the given methods.
-
cherrypy.lib.cptools.
autovary
(ignore=None, debug=False)[source]¶ Auto-populate the Vary response header based on request.header access.
-
cherrypy.lib.cptools.
convert_params
(exception=<type 'exceptions.ValueError'>, error=400)[source]¶ Convert request params based on function annotations, with error handling.
- exception
- Exception class to catch.
- status
- The HTTP error code to return to the client on failure.
-
cherrypy.lib.cptools.
flatten
(debug=False)[source]¶ Wrap response.body in a generator that recursively iterates over body.
This allows cherrypy.response.body to consist of ‘nested generators’; that is, a set of generators that yield generators.
-
cherrypy.lib.cptools.
ignore_headers
(headers=('Range', ), debug=False)[source]¶ Delete request headers whose field names are included in ‘headers’.
This is a useful tool for working behind certain HTTP servers; for example, Apache duplicates the work that CP does for ‘Range’ headers, and will doubly-truncate the response.
-
cherrypy.lib.cptools.
log_request_headers
(debug=False)[source]¶ Write request headers to the cherrypy error log.
-
cherrypy.lib.cptools.
log_traceback
(severity=40, debug=False)[source]¶ Write the last error’s traceback to the cherrypy error log.
-
cherrypy.lib.cptools.
proxy
(base=None, local='X-Forwarded-Host', remote='X-Forwarded-For', scheme='X-Forwarded-Proto', debug=False)[source]¶ Change the base URL (scheme://host[:port][/path]).
For running a CP server behind Apache, lighttpd, or other HTTP server.
For Apache and lighttpd, you should leave the ‘local’ argument at the default value of ‘X-Forwarded-Host’. For Squid, you probably want to set tools.proxy.local = ‘Origin’.
If you want the new request.base to include path info (not just the host), you must explicitly set base to the full base path, and ALSO set ‘local’ to ‘’, so that the X-Forwarded-Host request header (which never includes path info) does not override it. Regardless, the value for ‘base’ MUST NOT end in a slash.
cherrypy.request.remote.ip (the IP address of the client) will be rewritten if the header specified by the ‘remote’ arg is valid. By default, ‘remote’ is set to ‘X-Forwarded-For’. If you do not want to rewrite remote.ip, set the ‘remote’ arg to an empty string.
-
cherrypy.lib.cptools.
redirect
(url='', internal=True, debug=False)[source]¶ Raise InternalRedirect or HTTPRedirect to the given url.
-
cherrypy.lib.cptools.
referer
(pattern, accept=True, accept_missing=False, error=403, message='Forbidden Referer header.', debug=False)[source]¶ Raise HTTPError if Referer header does/does not match the given pattern.
- pattern
- A regular expression pattern to test against the Referer.
- accept
- If True, the Referer must match the pattern; if False, the Referer must NOT match the pattern.
- accept_missing
- If True, permit requests with no Referer header.
- error
- The HTTP error code to return to the client on failure.
- message
- A string to include in the response body on failure.
-
cherrypy.lib.cptools.
response_headers
(headers=None, debug=False)[source]¶ Set headers on the response.
-
cherrypy.lib.cptools.
session_auth
(**kwargs)[source]¶ Session authentication hook.
Any attribute of the SessionAuth class may be overridden via a keyword arg to this function:
_debug_message: instancemethod anonymous: instancemethod check_username_and_password: instancemethod debug: bool do_check: instancemethod do_login: instancemethod do_logout: instancemethod login_screen: instancemethod on_check: instancemethod on_login: instancemethod on_logout: instancemethod run: instancemethod session_key: str
-
cherrypy.lib.cptools.
trailing_slash
(missing=True, extra=False, status=None, debug=False)[source]¶ Redirect if path_info has (missing|extra) trailing slash.
Validate the current ETag against If-Match, If-None-Match headers.
If autotags is True, an ETag response-header value will be provided from an MD5 hash of the response body (unless some other code has already provided an ETag header). If False (the default), the ETag will not be automatic.
WARNING: the autotags feature is not designed for URL’s which allow methods other than GET. For example, if a POST to the same URL returns no content, the automatic ETag will be incorrect, breaking a fundamental use for entity tags in a possibly destructive fashion. Likewise, if you raise 304 Not Modified, the response body will be empty, the ETag hash will be incorrect, and your application will break. See RFC 2616 Section 14.24.
cherrypy.lib.encoding module¶
-
class
cherrypy.lib.encoding.
ResponseEncoder
(**kwargs)[source]¶ -
add_charset
= True¶
-
debug
= False¶
-
default_encoding
= 'utf-8'¶
-
encode_stream
(encoding)[source]¶ Encode a streaming response body.
Use a generator wrapper, and just pray it works as the stream is being written out.
-
encoding
= None¶
-
errors
= 'strict'¶
-
failmsg
= 'Response body could not be encoded with %r.'¶
-
text_only
= True¶
-
-
cherrypy.lib.encoding.
compress
(body, compress_level)[source]¶ Compress ‘body’ at the given compress_level.
-
cherrypy.lib.encoding.
decode
(encoding=None, default_encoding='utf-8')[source]¶ Replace or extend the list of charsets used to decode a request entity.
Either argument may be a single string or a list of strings.
- encoding
- If not None, restricts the set of charsets attempted while decoding a request entity to the given set (even if a different charset is given in the Content-Type request header).
- default_encoding
- Only in effect if the ‘encoding’ argument is not given. If given, the set of charsets attempted while decoding a request entity is extended with the given value(s).
-
cherrypy.lib.encoding.
gzip
(compress_level=5, mime_types=['text/html', 'text/plain'], debug=False)[source]¶ Try to gzip the response body if Content-Type in mime_types.
cherrypy.response.headers[‘Content-Type’] must be set to one of the values in the mime_types arg before calling this function.
- The provided list of mime-types must be of one of the following form:
- type/subtype
- type/*
- type/*+subtype
- No compression is performed if any of the following hold:
- The client sends no Accept-Encoding request header
- No ‘gzip’ or ‘x-gzip’ is present in the Accept-Encoding header
- No ‘gzip’ or ‘x-gzip’ with a qvalue > 0 is present
- The ‘identity’ value is given with a qvalue > 0.
cherrypy.lib.gctools module¶
-
class
cherrypy.lib.gctools.
GCRoot
[source]¶ Bases:
object
A CherryPy page handler for testing reference leaks.
-
classes
= [(<class 'cherrypy._cprequest.Request'>, 2, 2, 'Should be 1 in this request thread and 1 in the main thread.'), (<class 'cherrypy._cprequest.Response'>, 2, 2, 'Should be 1 in this request thread and 1 in the main thread.'), (<class 'cherrypy._cpwsgi.AppResponse'>, 1, 1, 'Should be 1 in this request thread only.')]¶
-
-
class
cherrypy.lib.gctools.
ReferrerTree
(ignore=None, maxdepth=2, maxparents=10)[source]¶ Bases:
object
An object which gathers all referrers of an object to a given depth.
-
peek_length
= 40¶
-
cherrypy.lib.httpauth module¶
This module defines functions to implement HTTP Digest Authentication (RFC 2617). This has full compliance with ‘Digest’ and ‘Basic’ authentication methods. In ‘Digest’ it supports both MD5 and MD5-sess algorithms.
- Usage:
First use ‘doAuth’ to request the client authentication for a certain resource. You should send an httplib.UNAUTHORIZED response to the client so he knows he has to authenticate itself.
Then use ‘parseAuthorization’ to retrieve the ‘auth_map’ used in ‘checkResponse’.
To use ‘checkResponse’ you must have already verified the password associated with the ‘username’ key in ‘auth_map’ dict. Then you use the ‘checkResponse’ function to verify if the password matches the one sent by the client.
SUPPORTED_ALGORITHM - list of supported ‘Digest’ algorithms SUPPORTED_QOP - list of supported ‘Digest’ ‘qop’.
-
cherrypy.lib.httpauth.
digestAuth
(realm, algorithm='MD5', nonce=None, qop='auth')[source]¶ Challenges the client for a Digest authentication.
-
cherrypy.lib.httpauth.
doAuth
(realm)[source]¶ ‘doAuth’ function returns the challenge string b giving priority over Digest and fallback to Basic authentication when the browser doesn’t support the first one.
This should be set in the HTTP header under the key ‘WWW-Authenticate’.
-
cherrypy.lib.httpauth.
checkResponse
(auth_map, password, method='GET', encrypt=None, **kwargs)[source]¶ ‘checkResponse’ compares the auth_map with the password and optionally other arguments that each implementation might need.
If the response is of type ‘Basic’ then the function has the following signature:
checkBasicResponse(auth_map, password) -> bool
If the response is of type ‘Digest’ then the function has the following signature:
checkDigestResponse(auth_map, password, method='GET', A1=None) -> bool
The ‘A1’ argument is only used in MD5_SESS algorithm based responses. Check md5SessionKey() for more info.
-
cherrypy.lib.httpauth.
parseAuthorization
(credentials)[source]¶ parseAuthorization will convert the value of the ‘Authorization’ key in the HTTP header to a map itself. If the parsing fails ‘None’ is returned.
-
cherrypy.lib.httpauth.
md5SessionKey
(params, password)[source]¶ If the “algorithm” directive’s value is “MD5-sess”, then A1 [the session key] is calculated only once - on the first request by the client following receipt of a WWW-Authenticate challenge from the server.
This creates a ‘session key’ for the authentication of subsequent requests and responses which is different for each “authentication session”, thus limiting the amount of material hashed with any one key.
Because the server need only use the hash of the user credentials in order to create the A1 value, this construction could be used in conjunction with a third party authentication service so that the web server would not need the actual password value. The specification of such a protocol is beyond the scope of this specification.
cherrypy.lib.httputil module¶
HTTP library functions.
This module contains functions for building an HTTP application framework: any one, not just one whose name starts with “Ch”. ;) If you reference any modules from some popular framework inside this module, FuManChu will personally hang you up by your thumbs and submit you to a public caning.
-
class
cherrypy.lib.httputil.
AcceptElement
(value, params=None)[source]¶ Bases:
cherrypy.lib.httputil.HeaderElement
An element (with parameters) from an Accept* header’s element list.
AcceptElement objects are comparable; the more-preferred object will be “less than” the less-preferred object. They are also therefore sortable; if you sort a list of AcceptElement objects, they will be listed in priority order; the most preferred value will be first. Yes, it should have been the other way around, but it’s too late to fix now.
-
qvalue
¶ The qvalue, or priority, of this value.
-
-
class
cherrypy.lib.httputil.
CaseInsensitiveDict
[source]¶ Bases:
dict
A case-insensitive dict subclass.
Each key is changed on entry to str(key).title().
-
class
cherrypy.lib.httputil.
HeaderElement
(value, params=None)[source]¶ Bases:
object
An element (with parameters) from an HTTP header’s element list.
-
class
cherrypy.lib.httputil.
HeaderMap
[source]¶ Bases:
cherrypy.lib.httputil.CaseInsensitiveDict
A dict subclass for HTTP request and response headers.
Each key is changed on entry to str(key).title(). This allows headers to be case-insensitive and avoid duplicates.
Values are header values (decoded according to RFC 2047 if necessary).
-
classmethod
encode_header_items
(header_items)[source]¶ Prepare the sequence of name, value tuples into a form suitable for transmitting on the wire for HTTP.
-
encodings
= ['ISO-8859-1']¶
-
protocol
= (1, 1)¶
-
use_rfc_2047
= True¶
-
classmethod
-
class
cherrypy.lib.httputil.
Host
(ip, port, name=None)[source]¶ Bases:
object
An internet address.
- name
- Should be the client’s host name. If not available (because no DNS lookup is performed), the IP address should be used instead.
-
ip
= '0.0.0.0'¶
-
name
= 'unknown.tld'¶
-
port
= 80¶
-
cherrypy.lib.httputil.
decode_TEXT
(value)[source]¶ Decode RFC 2047 TEXT (e.g. “=?utf-8?q?f=C3=BCr?=” -> “fxfcr”).
-
cherrypy.lib.httputil.
get_ranges
(headervalue, content_length)[source]¶ Return a list of (start, stop) indices from a Range header, or None.
Each (start, stop) tuple will be composed of two ints, which are suitable for use in a slicing operation. That is, the header “Range: bytes=3-6”, if applied against a Python string, is requesting resource[3:7]. This function will return the list [(3, 7)].
If this function returns an empty list, you should return HTTP 416.
-
cherrypy.lib.httputil.
header_elements
(fieldname, fieldvalue)[source]¶ Return a sorted HeaderElement list from a comma-separated header string.
-
cherrypy.lib.httputil.
parse_query_string
(query_string, keep_blank_values=True, encoding='utf-8')[source]¶ Build a params dictionary from a query_string.
Duplicate key/value pairs in the provided query_string will be returned as {‘key’: [val1, val2, ...]}. Single key/values will be returned as strings: {‘key’: ‘value’}.
-
cherrypy.lib.httputil.
protocol_from_http
(protocol_str)[source]¶ Return a protocol tuple from the given ‘HTTP/x.y’ string.
-
cherrypy.lib.httputil.
urljoin
(*atoms)[source]¶ Return the given path *atoms, joined into a single URL.
This will correctly join a SCRIPT_NAME and PATH_INFO into the original URL, even if either atom is blank.
cherrypy.lib.jsontools module¶
-
cherrypy.lib.jsontools.
json_in
(content_type=[u'application/json', u'text/javascript'], force=True, debug=False, processor=<function json_processor>)[source]¶ Add a processor to parse JSON request entities: The default processor places the parsed data into request.json.
Incoming request entities which match the given content_type(s) will be deserialized from JSON to the Python equivalent, and the result stored at cherrypy.request.json. The ‘content_type’ argument may be a Content-Type string or a list of allowable Content-Type strings.
If the ‘force’ argument is True (the default), then entities of other content types will not be allowed; “415 Unsupported Media Type” is raised instead.
Supply your own processor to use a custom decoder, or to handle the parsed data differently. The processor can be configured via tools.json_in.processor or via the decorator method.
Note that the deserializer requires the client send a Content-Length request header, or it will raise “411 Length Required”. If for any other reason the request entity cannot be deserialized from JSON, it will raise “400 Bad Request: Invalid JSON document”.
You must be using Python 2.6 or greater, or have the ‘simplejson’ package importable; otherwise, ValueError is raised during processing.
-
cherrypy.lib.jsontools.
json_out
(content_type='application/json', debug=False, handler=<function json_handler>)[source]¶ Wrap request.handler to serialize its output to JSON. Sets Content-Type.
If the given content_type is None, the Content-Type response header is not set.
Provide your own handler to use a custom encoder. For example cherrypy.config[‘tools.json_out.handler’] = <function>, or @json_out(handler=function).
You must be using Python 2.6 or greater, or have the ‘simplejson’ package importable; otherwise, ValueError is raised during processing.
cherrypy.lib.lockfile module¶
Platform-independent file locking. Inspired by and modeled after zc.lockfile.
-
exception
cherrypy.lib.lockfile.
LockError
(path)[source]¶ Bases:
exceptions.Exception
Could not obtain a lock
-
msg
= 'Unable to lock %r'¶
-
-
cherrypy.lib.lockfile.
LockFile
[source]¶ alias of
UnixLockFile
-
class
cherrypy.lib.lockfile.
SystemLockFile
(path)[source]¶ Bases:
object
An abstract base class for platform-specific locking.
-
exception
cherrypy.lib.lockfile.
UnlockError
(path)[source]¶ Bases:
cherrypy.lib.lockfile.LockError
Could not release a lock
-
msg
= 'Unable to unlock %r'¶
-
cherrypy.lib.locking module¶
-
class
cherrypy.lib.locking.
LockChecker
(session_id, timeout)[source]¶ Bases:
object
Keep track of the time and detect if a timeout has expired
cherrypy.lib.profiler module¶
Profiler tools for CherryPy.
You can profile any of your pages as follows:
from cherrypy.lib import profiler
class Root:
p = profiler.Profiler("/path/to/profile/dir")
@cherrypy.expose
def index(self):
self.p.run(self._index)
def _index(self):
return "Hello, world!"
cherrypy.tree.mount(Root())
You can also turn on profiling for all requests
using the make_app
function as WSGI middleware.
This module can be used whenever you make changes to CherryPy,
to get a quick sanity-check on overall CP performance. Use the
--profile
flag when running the test suite. Then, use the serve()
function to browse the results in a web browser. If you run this
module from the command line, it will call serve()
for you.
cherrypy.lib.reprconf module¶
Generic configuration system using unrepr.
Configuration data may be supplied as a Python dictionary, as a filename, or as an open file object. When you supply a filename or file, Python’s builtin ConfigParser is used (with some extensions).
Configuration keys are separated into namespaces by the first ”.” in the key.
The only key that cannot exist in a namespace is the “environment” entry. This special entry ‘imports’ other config entries from a template stored in the Config.environments dict.
You can define your own namespaces to be called when new config is merged by adding a named handler to Config.namespaces. The name can be any string, and the handler must be either a callable or a context manager.
-
class
cherrypy.lib.reprconf.
Config
(file=None, **kwargs)[source]¶ Bases:
dict
A dict-like set of configuration data, with defaults and namespaces.
May take a file, filename, or dict.
-
defaults
= {}¶
-
environments
= {}¶
-
namespaces
= cherrypy.lib.reprconf.NamespaceSet({'engine': <function _engine_namespace_handler at 0x7f7de3180230>, 'checker': <function <lambda> at 0x7f7de330e668>, 'tree': <function _tree_namespace_handler at 0x7f7de31802a8>, 'log': <function <lambda> at 0x7f7de330e5f0>, 'server': <function _server_namespace_handler at 0x7f7de3181ed8>})¶
-
-
class
cherrypy.lib.reprconf.
NamespaceSet
[source]¶ Bases:
dict
A dict of config namespace names and handlers.
Each config entry should begin with a namespace name; the corresponding namespace handler will be called once for each config entry in that namespace, and will be passed two arguments: the config key (with the namespace removed) and the config value.
Namespace handlers may be any Python callable; they may also be Python 2.5-style ‘context managers’, in which case their __enter__ method should return a callable to be used as the handler. See cherrypy.tools (the Toolbox class) for an example.
-
__call__
(config)[source]¶ Iterate through config and pass it to each namespace handler.
- config
- A flat dict, where keys use dots to separate namespaces, and values are arbitrary.
The first name in each config key is used to look up the corresponding namespace handler. For example, a config entry of {‘tools.gzip.on’: v} will call the ‘tools’ namespace handler with the args: (‘gzip.on’, v)
-
copy
()¶
-
-
class
cherrypy.lib.reprconf.
Parser
(defaults=None, dict_type=<class 'collections.OrderedDict'>, allow_no_value=False)[source]¶ Bases:
ConfigParser.ConfigParser
Sub-class of ConfigParser that keeps the case of options and that raises an exception if the file cannot be read.
-
cherrypy.lib.reprconf.
as_dict
(config)[source]¶ Return a dict from ‘config’ whether it is a dict, file, or filename.
-
cherrypy.lib.reprconf.
attributes
(full_attribute_name)[source]¶ Load a module and retrieve an attribute of that module.
cherrypy.lib.sessions module¶
Session implementation for CherryPy.
You need to edit your config file to use sessions. Here’s an example:
[/]
tools.sessions.on = True
tools.sessions.storage_class = cherrypy.lib.sessions.FileSession
tools.sessions.storage_path = "/home/site/sessions"
tools.sessions.timeout = 60
This sets the session to be stored in files in the directory
/home/site/sessions, and the session timeout to 60 minutes. If you omit
storage_class
, the sessions will be saved in RAM.
tools.sessions.on
is the only required line for working sessions,
the rest are optional.
By default, the session ID is passed in a cookie, so the client’s browser must have cookies enabled for your site.
To set data for the current session, use
cherrypy.session['fieldname'] = 'fieldvalue'
;
to get data use cherrypy.session.get('fieldname')
.
By default, the 'locking'
mode of sessions is 'implicit'
, which means
the session is locked early and unlocked late. Be mindful of this default mode
for any requests that take a long time to process (streaming responses,
expensive calculations, database lookups, API calls, etc), as other concurrent
requests that also utilize sessions will hang until the session is unlocked.
If you want to control when the session data is locked and unlocked,
set tools.sessions.locking = 'explicit'
. Then call
cherrypy.session.acquire_lock()
and cherrypy.session.release_lock()
.
Regardless of which mode you use, the session is guaranteed to be unlocked when
the request is complete.
You can force a session to expire with cherrypy.lib.sessions.expire()
.
Simply call that function at the point you want the session to expire, and it
will cause the session cookie to expire client-side.
If CherryPy receives, via a request cookie, a session id that it does not recognize, it will reject that id and create a new one to return in the response cookie. This helps prevent session fixation attacks. However, CherryPy “recognizes” a session id by looking up the saved session data for that id. Therefore, if you never save any session data, you will get a new session id for every request.
If you run multiple instances of CherryPy (for example via mod_python behind Apache prefork), you most likely cannot use the RAM session backend, since each instance of CherryPy will have its own memory space. Use a different backend instead, and verify that all instances are pointing at the same file or db location. Alternately, you might try a load balancer which makes sessions “sticky”. Google is your friend, there.
The response cookie will possess an expiration date to inform the client at which point to stop sending the cookie back in requests. If the server time and client time differ, expect sessions to be unreliable. Make sure the system time of your server is accurate.
CherryPy defaults to a 60-minute session timeout, which also applies to the cookie which is sent to the client. Unfortunately, some versions of Safari (“4 public beta” on Windows XP at least) appear to have a bug in their parsing of the GMT expiration date–they appear to interpret the date as one hour in the past. Sixty minutes minus one hour is pretty close to zero, so you may experience this bug as a new session id for every request, unless the requests are less than one second apart. To fix, try increasing the session.timeout.
On the other extreme, some users report Firefox sending cookies after their expiration date, although this was on a system with an inaccurate system time. Maybe FF doesn’t trust system time.
-
class
cherrypy.lib.sessions.
FileSession
(id=None, **kwargs)[source]¶ Bases:
cherrypy.lib.sessions.Session
Implementation of the File backend for sessions
- storage_path
- The folder where session data will be saved. Each session will be saved as pickle.dump(data, expiration_time) in its own file; the filename will be self.SESSION_PREFIX + self.id.
- lock_timeout
- A timedelta or numeric seconds indicating how long to block acquiring a lock. If None (default), acquiring a lock will block indefinitely.
-
LOCK_SUFFIX
= '.lock'¶
-
SESSION_PREFIX
= 'session-'¶
-
pickle_protocol
= 2¶
-
class
cherrypy.lib.sessions.
MemcachedSession
(id=None, **kwargs)[source]¶ Bases:
cherrypy.lib.sessions.Session
-
locks
= {}¶
-
mc_lock
= <_RLock owner=None count=0>¶
-
servers
= ['127.0.0.1:11211']¶
-
-
class
cherrypy.lib.sessions.
RamSession
(id=None, **kwargs)[source]¶ Bases:
cherrypy.lib.sessions.Session
-
cache
= {}¶
-
locks
= {}¶
-
-
class
cherrypy.lib.sessions.
Session
(id=None, **kwargs)[source]¶ Bases:
object
A CherryPy dict-like Session object (one per request).
-
clean_freq
= 5¶ The poll rate for expired session cleanup in minutes.
-
clean_thread
= None¶ Class-level Monitor which calls self.clean_up.
-
debug
= False¶ If True, log debug information.
-
id
¶ The current session ID.
-
id_observers
= None¶ A list of callbacks to which to pass new id’s.
-
loaded
= False¶ If True, data has been retrieved from storage. This should happen automatically on the first attempt to access session data.
-
locked
= False¶ If True, this session instance has exclusive read/write access to session data.
-
missing
= False¶ True if the session requested by the client did not exist.
-
now
()[source]¶ Generate the session specific concept of ‘now’.
Other session providers can override this to use alternative, possibly timezone aware, versions of ‘now’.
-
originalid
= None¶ The session id passed by the client. May be missing or unsafe.
-
pop
(key, default=False)[source]¶ Remove the specified key and return the corresponding value. If key is not found, default is returned if given, otherwise KeyError is raised.
-
regenerated
= False¶ True if the application called session.regenerate(). This is not set by internal calls to regenerate the session id.
-
timeout
= 60¶ Number of minutes after which to delete session data.
-
-
cherrypy.lib.sessions.
init
(storage_type=None, path=None, path_header=None, name='session_id', timeout=60, domain=None, secure=False, clean_freq=5, persistent=True, httponly=False, debug=False, **kwargs)[source]¶ Initialize session object (using cookies).
- storage_class
- The Session subclass to use. Defaults to RamSession.
- storage_type
- (deprecated) One of ‘ram’, ‘file’, memcached’. This will be used to look up the corresponding class in cherrypy.lib.sessions globals. For example, ‘file’ will use the FileSession class.
- path
- The ‘path’ value to stick in the response cookie metadata.
- path_header
- If ‘path’ is None (the default), then the response cookie ‘path’ will be pulled from request.headers[path_header].
- name
- The name of the cookie.
- timeout
- The expiration timeout (in minutes) for the stored session data. If ‘persistent’ is True (the default), this is also the timeout for the cookie.
- domain
- The cookie domain.
- secure
- If False (the default) the cookie ‘secure’ value will not be set. If True, the cookie ‘secure’ value will be set (to 1).
- clean_freq (minutes)
- The poll rate for expired session cleanup.
- persistent
- If True (the default), the ‘timeout’ argument will be used to expire the cookie. If False, the cookie will not have an expiry, and the cookie will be a “session cookie” which expires when the browser is closed.
- httponly
- If False (the default) the cookie ‘httponly’ value will not be set. If True, the cookie ‘httponly’ value will be set (to 1).
Any additional kwargs will be bound to the new Session instance, and may be specific to the storage type. See the subclass of Session you’re using for more information.
Set a response cookie for the client.
- path
- the ‘path’ value to stick in the response cookie metadata.
- path_header
- if ‘path’ is None (the default), then the response cookie ‘path’ will be pulled from request.headers[path_header].
- name
- the name of the cookie.
- timeout
- the expiration timeout for the cookie. If 0 or other boolean False, no ‘expires’ param will be set, and the cookie will be a “session cookie” which expires when the browser is closed.
- domain
- the cookie domain.
- secure
- if False (the default) the cookie ‘secure’ value will not be set. If True, the cookie ‘secure’ value will be set (to 1).
- httponly
- If False (the default) the cookie ‘httponly’ value will not be set. If True, the cookie ‘httponly’ value will be set (to 1).
cherrypy.lib.static module¶
-
cherrypy.lib.static.
serve_download
(path, name=None)[source]¶ Serve ‘path’ as an application/x-download attachment.
-
cherrypy.lib.static.
serve_file
(path, content_type=None, disposition=None, name=None, debug=False)[source]¶ Set status, headers, and body in order to serve the given path.
The Content-Type header will be set to the content_type arg, if provided. If not provided, the Content-Type will be guessed by the file extension of the ‘path’ argument.
If disposition is not None, the Content-Disposition header will be set to “<disposition>; filename=<name>”. If name is None, it will be set to the basename of path. If disposition is None, no Content-Disposition header will be written.
-
cherrypy.lib.static.
serve_fileobj
(fileobj, content_type=None, disposition=None, name=None, debug=False)[source]¶ Set status, headers, and body in order to serve the given file object.
The Content-Type header will be set to the content_type arg, if provided.
If disposition is not None, the Content-Disposition header will be set to “<disposition>; filename=<name>”. If name is None, ‘filename’ will not be set. If disposition is None, no Content-Disposition header will be written.
CAUTION: If the request contains a ‘Range’ header, one or more seek()s will be performed on the file object. This may cause undesired behavior if the file object is not seekable. It could also produce undesired results if the caller set the read position of the file object prior to calling serve_fileobj(), expecting that the data would be served starting from that position.
-
cherrypy.lib.static.
staticdir
(section, dir, root='', match='', content_types=None, index='', debug=False)[source]¶ Serve a static resource from the given (root +) dir.
- match
- If given, request.path_info will be searched for the given regular expression before attempting to serve static content.
- content_types
- If given, it should be a Python dictionary of {file-extension: content-type} pairs, where ‘file-extension’ is a string (e.g. “gif”) and ‘content-type’ is the value to write out in the Content-Type response header (e.g. “image/gif”).
- index
- If provided, it should be the (relative) name of a file to serve for directory requests. For example, if the dir argument is ‘/home/me’, the Request-URI is ‘myapp’, and the index arg is ‘index.html’, the file ‘/home/me/myapp/index.html’ will be sought.
-
cherrypy.lib.static.
staticfile
(filename, root=None, match='', content_types=None, debug=False)[source]¶ Serve a static resource from the given (root +) filename.
- match
- If given, request.path_info will be searched for the given regular expression before attempting to serve static content.
- content_types
- If given, it should be a Python dictionary of {file-extension: content-type} pairs, where ‘file-extension’ is a string (e.g. “gif”) and ‘content-type’ is the value to write out in the Content-Type response header (e.g. “image/gif”).
cherrypy.lib.xmlrpcutil module¶
Module contents¶
CherryPy Library
-
class
cherrypy.lib.
file_generator
(input, chunkSize=65536)[source]¶ Bases:
object
Yield the given input (a file object) in chunks (default 64k). (Core)
-
next
()¶
-
-
cherrypy.lib.
file_generator_limited
(fileobj, count, chunk_size=65536)[source]¶ Yield the given file object in chunks, stopping after count bytes has been emitted. Default chunk size is 64kB. (Core)
cherrypy.process package¶
Submodules¶
cherrypy.process.plugins module¶
Site services for use with a Web Site Process Bus.
-
class
cherrypy.process.plugins.
Autoreloader
(bus, frequency=1, match='.*')[source]¶ Bases:
cherrypy.process.plugins.Monitor
Monitor which re-executes the process when files change.
This plugin restarts the process (via
os.execv()
) if any of the files it monitors change (or is deleted). By default, the autoreloader monitors all imported modules; you can add to the set by adding toautoreload.files
:cherrypy.engine.autoreload.files.add(myFile)
If there are imported files you do not wish to monitor, you can adjust the
match
attribute, a regular expression. For example, to stop monitoring cherrypy itself:cherrypy.engine.autoreload.match = r'^(?!cherrypy).+'
Like all
Monitor
plugins, the autoreload plugin takes afrequency
argument. The default is 1 second; that is, the autoreloader will examine files once each second.-
files
= None¶ The set of files to poll for modifications.
-
frequency
= 1¶ The interval in seconds at which to poll for modified files.
-
match
= '.*'¶ A regular expression by which to match filenames.
-
-
class
cherrypy.process.plugins.
BackgroundTask
(interval, function, args=[], kwargs={}, bus=None)[source]¶ Bases:
threading.Thread
A subclass of threading.Thread whose run() method repeats.
Use this class for most repeating tasks. It uses time.sleep() to wait for each interval, which isn’t very responsive; that is, even if you call self.cancel(), you’ll have to wait until the sleep() call finishes before the thread stops. To compensate, it defaults to being daemonic, which means it won’t delay stopping the whole process.
-
class
cherrypy.process.plugins.
Daemonizer
(bus, stdin='/dev/null', stdout='/dev/null', stderr='/dev/null')[source]¶ Bases:
cherrypy.process.plugins.SimplePlugin
Daemonize the running script.
Use this with a Web Site Process Bus via:
Daemonizer(bus).subscribe()
When this component finishes, the process is completely decoupled from the parent environment. Please note that when this component is used, the return code from the parent process will still be 0 if a startup error occurs in the forked children. Errors in the initial daemonizing process still return proper exit codes. Therefore, if you use this plugin to daemonize, don’t use the return code as an accurate indicator of whether the process fully started. In fact, that return code only indicates if the process succesfully finished the first fork.
-
class
cherrypy.process.plugins.
DropPrivileges
(bus, umask=None, uid=None, gid=None)[source]¶ Bases:
cherrypy.process.plugins.SimplePlugin
Drop privileges. uid/gid arguments not available on Windows.
Special thanks to Gavin Baker
-
gid
¶ The gid under which to run. Availability: Unix.
-
uid
¶ The uid under which to run. Availability: Unix.
-
umask
¶ The default permission mode for newly created files and directories.
Usually expressed in octal format, for example,
0644
. Availability: Unix, Windows.
-
-
class
cherrypy.process.plugins.
Monitor
(bus, callback, frequency=60, name=None)[source]¶ Bases:
cherrypy.process.plugins.SimplePlugin
WSPBus listener to periodically run a callback in its own thread.
-
callback
= None¶ The function to call at intervals.
-
frequency
= 60¶ The time in seconds between callback runs.
-
thread
= None¶ A
BackgroundTask
thread.
-
-
class
cherrypy.process.plugins.
PIDFile
(bus, pidfile)[source]¶ Bases:
cherrypy.process.plugins.SimplePlugin
Maintain a PID file via a WSPBus.
-
class
cherrypy.process.plugins.
PerpetualTimer
(*args, **kwargs)[source]¶ Bases:
threading._Timer
A responsive subclass of threading.Timer whose run() method repeats.
Use this timer only when you really need a very interruptible timer; this checks its ‘finished’ condition up to 20 times a second, which can results in pretty high CPU usage
-
class
cherrypy.process.plugins.
SignalHandler
(bus)[source]¶ Bases:
object
Register bus channels (and listeners) for system signals.
You can modify what signals your application listens for, and what it does when it receives signals, by modifying
SignalHandler.handlers
, a dict of {signal name: callback} pairs. The default set is:handlers = {'SIGTERM': self.bus.exit, 'SIGHUP': self.handle_SIGHUP, 'SIGUSR1': self.bus.graceful, }
The
SignalHandler.handle_SIGHUP`()
method callsbus.restart()
if the process is daemonized, butbus.exit()
if the process is attached to a TTY. This is because Unix window managers tend to send SIGHUP to terminal windows when the user closes them.Feel free to add signals which are not available on every platform. The
SignalHandler
will ignore errors raised from attempting to register handlers for unknown signals.-
handlers
= {}¶ A map from signal names (e.g. ‘SIGTERM’) to handlers (e.g. bus.exit).
-
set_handler
(signal, listener=None)[source]¶ Subscribe a handler for the given signal (number or name).
If the optional ‘listener’ argument is provided, it will be subscribed as a listener for the given signal’s channel.
If the given signal name or number is not available on the current platform, ValueError is raised.
-
signals
= {1: 'SIGHUP', 2: 'SIGINT', 3: 'SIGQUIT', 4: 'SIGILL', 5: 'SIGTRAP', 6: 'SIGABRT', 7: 'SIGBUS', 8: 'SIGFPE', 9: 'SIGKILL', 10: 'SIGUSR1', 11: 'SIGSEGV', 12: 'SIGUSR2', 13: 'SIGPIPE', 14: 'SIGALRM', 15: 'SIGTERM', 17: 'SIGCLD', 18: 'SIGCONT', 19: 'SIGSTOP', 20: 'SIGTSTP', 21: 'SIGTTIN', 22: 'SIGTTOU', 23: 'SIGURG', 24: 'SIGXCPU', 25: 'SIGXFSZ', 26: 'SIGVTALRM', 27: 'SIGPROF', 28: 'SIGWINCH', 29: 'SIGPOLL', 30: 'SIGPWR', 31: 'SIGSYS', 34: 'SIGRTMIN', 64: 'SIGRTMAX'}¶ A map from signal numbers to names.
-
-
class
cherrypy.process.plugins.
SimplePlugin
(bus)[source]¶ Bases:
object
Plugin base class which auto-subscribes methods for known channels.
-
class
cherrypy.process.plugins.
ThreadManager
(bus)[source]¶ Bases:
cherrypy.process.plugins.SimplePlugin
Manager for HTTP request threads.
If you have control over thread creation and destruction, publish to the ‘acquire_thread’ and ‘release_thread’ channels (for each thread). This will register/unregister the current thread and publish to ‘start_thread’ and ‘stop_thread’ listeners in the bus as needed.
If threads are created and destroyed by code you do not control (e.g., Apache), then, at the beginning of every HTTP request, publish to ‘acquire_thread’ only. You should not publish to ‘release_thread’ in this case, since you do not know whether the thread will be re-used or not. The bus will call ‘stop_thread’ listeners for you when it stops.
-
acquire_thread
()[source]¶ Run ‘start_thread’ listeners for the current thread.
If the current thread has already been seen, any ‘start_thread’ listeners will not be run again.
-
graceful
()¶ Release all threads and run all ‘stop_thread’ listeners.
-
threads
= None¶ A map of {thread ident: index number} pairs.
-
cherrypy.process.servers module¶
Starting in CherryPy 3.1, cherrypy.server is implemented as an
Engine Plugin. It’s an instance of
cherrypy._cpserver.Server
, which is a subclass of
cherrypy.process.servers.ServerAdapter
. The ServerAdapter
class
is designed to control other servers, as well.
If you need to start more than one HTTP server (to serve on multiple ports, or protocols, etc.), you can manually register each one and then start them all with engine.start:
s1 = ServerAdapter(
cherrypy.engine,
MyWSGIServer(host='0.0.0.0', port=80)
)
s2 = ServerAdapter(
cherrypy.engine,
another.HTTPServer(host='127.0.0.1', SSL=True)
)
s1.subscribe()
s2.subscribe()
cherrypy.engine.start()
There are also FlupFCGIServer and FlupSCGIServer classes in
cherrypy.process.servers
. To start an fcgi server, for example,
wrap an instance of it in a ServerAdapter:
addr = ('0.0.0.0', 4000)
f = servers.FlupFCGIServer(application=cherrypy.tree, bindAddress=addr)
s = servers.ServerAdapter(cherrypy.engine, httpserver=f, bind_addr=addr)
s.subscribe()
The cherryd
startup script will do the above for
you via its -f flag.
Note that you need to download and install flup
yourself, whether you use cherryd
or not.
A very simple setup lets your cherry run with FastCGI.
You just need the flup library,
plus a running Apache server (with mod_fastcgi
) or lighttpd server.
hello.py:
#!/usr/bin/python
import cherrypy
class HelloWorld:
'''Sample request handler class.'''
@cherrypy.expose
def index(self):
return "Hello world!"
cherrypy.tree.mount(HelloWorld())
# CherryPy autoreload must be disabled for the flup server to work
cherrypy.config.update({'engine.autoreload.on':False})
Then run /deployguide/cherryd
with the ‘-f’ arg:
cherryd -c <myconfig> -d -f -i hello.py
At the top level in httpd.conf:
FastCgiIpcDir /tmp
FastCgiServer /path/to/cherry.fcgi -idle-timeout 120 -processes 4
And inside the relevant VirtualHost section:
# FastCGI config
AddHandler fastcgi-script .fcgi
ScriptAliasMatch (.*$) /path/to/cherry.fcgi$1
For Lighttpd you can follow these
instructions. Within lighttpd.conf
make sure mod_fastcgi
is
active within server.modules
. Then, within your $HTTP["host"]
directive, configure your fastcgi script like the following:
$HTTP["url"] =~ "" {
fastcgi.server = (
"/" => (
"script.fcgi" => (
"bin-path" => "/path/to/your/script.fcgi",
"socket" => "/tmp/script.sock",
"check-local" => "disable",
"disable-time" => 1,
"min-procs" => 1,
"max-procs" => 1, # adjust as needed
),
),
)
} # end of $HTTP["url"] =~ "^/"
Please see Lighttpd FastCGI Docs for an explanation of the possible configuration options.
-
class
cherrypy.process.servers.
FlupCGIServer
(*args, **kwargs)[source]¶ Bases:
object
Adapter for a flup.server.cgi.WSGIServer.
-
class
cherrypy.process.servers.
FlupFCGIServer
(*args, **kwargs)[source]¶ Bases:
object
Adapter for a flup.server.fcgi.WSGIServer.
-
class
cherrypy.process.servers.
FlupSCGIServer
(*args, **kwargs)[source]¶ Bases:
object
Adapter for a flup.server.scgi.WSGIServer.
-
class
cherrypy.process.servers.
ServerAdapter
(bus, httpserver=None, bind_addr=None)[source]¶ Bases:
object
Adapter for an HTTP server.
If you need to start more than one HTTP server (to serve on multiple ports, or protocols, etc.), you can manually register each one and then start them all with bus.start:
s1 = ServerAdapter(bus, MyWSGIServer(host='0.0.0.0', port=80)) s2 = ServerAdapter(bus, another.HTTPServer(host='127.0.0.1', SSL=True)) s1.subscribe() s2.subscribe() bus.start()
-
bound_addr
¶ The bind address, or if it’s an ephemeral port and the socket has been bound, return the actual port bound.
-
description
¶ A description about where this server is bound.
-
cherrypy.process.win32 module¶
Windows service. Requires pywin32.
-
class
cherrypy.process.win32.
ConsoleCtrlHandler
(bus)[source]¶ Bases:
cherrypy.process.plugins.SimplePlugin
A WSPBus plugin for handling Win32 console events (like Ctrl-C).
-
class
cherrypy.process.win32.
Win32Bus
[source]¶ Bases:
cherrypy.process.wspbus.Bus
A Web Site Process Bus implementation for Win32.
Instead of time.sleep, this bus blocks using native win32event objects.
-
state
¶
-
cherrypy.process.wspbus module¶
An implementation of the Web Site Process Bus.
This module is completely standalone, depending only on the stdlib.
A Bus object is used to contain and manage site-wide behavior: daemonization, HTTP server start/stop, process reload, signal handling, drop privileges, PID file management, logging for all of these, and many more.
In addition, a Bus object provides a place for each web framework to register code that runs in response to site-wide events (like process start and stop), or which controls or otherwise interacts with the site-wide components mentioned above. For example, a framework which uses file-based templates would add known template filenames to an autoreload component.
Ideally, a Bus object will be flexible enough to be useful in a variety of invocation scenarios:
- The deployer starts a site from the command line via a framework-neutral deployment script; applications from multiple frameworks are mixed in a single site. Command-line arguments and configuration files are used to define site-wide components such as the HTTP server, WSGI component graph, autoreload behavior, signal handling, etc.
- The deployer starts a site via some other process, such as Apache; applications from multiple frameworks are mixed in a single site. Autoreload and signal handling (from Python at least) are disabled.
- The deployer starts a site via a framework-specific mechanism; for example, when running tests, exploring tutorials, or deploying single applications from a single framework. The framework controls which site-wide components are enabled as it sees fit.
The Bus object in this package uses topic-based publish-subscribe messaging to accomplish all this. A few topic channels are built in (‘start’, ‘stop’, ‘exit’, ‘graceful’, ‘log’, and ‘main’). Frameworks and site containers are free to define their own. If a message is sent to a channel that has not been defined or has no listeners, there is no effect.
In general, there should only ever be a single Bus object per process. Frameworks and site containers share a single Bus object by publishing messages and subscribing listeners.
The Bus object works as a finite state machine which models the current state of the process. Bus methods move it from one state to another; those methods then publish to subscribed listeners on the channel for the new state.:
O
|
V
STOPPING --> STOPPED --> EXITING -> X
A A |
| \___ |
| \ |
| V V
STARTED <-- STARTING
-
class
cherrypy.process.wspbus.
Bus
[source]¶ Bases:
object
Process state-machine and messenger for HTTP site deployment.
All listeners for a given channel are guaranteed to be called even if others at the same channel fail. Each failure is logged, but execution proceeds on to the next listener. The only way to stop all processing from inside a listener is to raise SystemExit and stop the whole server.
-
block
(interval=0.1)[source]¶ Wait for the EXITING state, KeyboardInterrupt or SystemExit.
This function is intended to be called only by the main thread. After waiting for the EXITING state, it also waits for all threads to terminate, and then calls os.execv if self.execv is True. This design allows another thread to call bus.restart, yet have the main thread perform the actual execv call (required on some platforms).
-
execv
= False¶
-
log
(msg='', level=20, traceback=False)[source]¶ Log the given message. Append the last traceback if requested.
-
max_cloexec_files
= 524288¶
-
restart
()[source]¶ Restart the process (may close connections).
This method does not restart the process from the calling thread; instead, it stops the bus and asks the main thread to call execv.
-
start_with_callback
(func, args=None, kwargs=None)[source]¶ Start ‘func’ in a new thread T, then start self (and return T).
-
state
= states.STOPPED¶
-
states
= <cherrypy.process.wspbus._StateEnum object>¶
-
Module contents¶
Site container for an HTTP server.
A Web Site Process Bus object is used to connect applications, servers, and frameworks with site-wide services such as daemonization, process reload, signal handling, drop privileges, PID file management, logging for all of these, and many more.
The ‘plugins’ module defines a few abstract and concrete services for use with the bus. Some use tool-specific channels; see the documentation for each class.
cherrypy.scaffold package¶
Module contents¶
<MyProject>, a CherryPy application.
Use this as a base for creating new CherryPy applications. When you want to make a new app, copy and paste this folder to some other location (maybe site-packages) and rename it to the name of your project, then tweak as desired.
Even before any tweaking, this should serve a few demonstration pages. Change to this directory and run:
cherryd -c site.conf
cherrypy.test package¶
Submodules¶
cherrypy.test.benchmark module¶
CherryPy Benchmark Tool
- Usage:
- benchmark.py [options]
–null: use a null Request object (to bench the HTTP server only) –notests: start the server but do not run the tests; this allows
you to check the tested pages with a browser
–help: show this help message –cpmodpy: run tests via apache on 54583 (with the builtin _cpmodpy) –modpython: run tests via apache on 54583 (with modpython_gateway) –ab=path: Use the ab script/executable at ‘path’ (see below) –apache=path: Use the apache script/exe at ‘path’ (see below)
To run the benchmarks, the Apache Benchmark tool “ab” must either be on your system path, or specified via the –ab=path option.
To run the modpython tests, the “apache” executable or script must be on your system path, or provided via the –apache=path option. On some platforms, “apache” may be called “apachectl” or “apache2ctl”–create a symlink to them if needed.
-
class
cherrypy.test.benchmark.
ABSession
(path='/cpbench/users/rdelon/apps/blog/hello', requests=1000, concurrency=10)[source]¶ A session of ‘ab’, the Apache HTTP server benchmarking tool.
Example output from ab:
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.121.2.1 $> apache-2.0 Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Copyright (c) 1998-2002 The Apache Software Foundation, http://www.apache.org/
Benchmarking 127.0.0.1 (be patient) Completed 100 requests Completed 200 requests Completed 300 requests Completed 400 requests Completed 500 requests Completed 600 requests Completed 700 requests Completed 800 requests Completed 900 requests
Server Software: CherryPy/3.1beta Server Hostname: 127.0.0.1 Server Port: 54583
Document Path: /static/index.html Document Length: 14 bytes
Concurrency Level: 10 Time taken for tests: 9.643867 seconds Complete requests: 1000 Failed requests: 0 Write errors: 0 Total transferred: 189000 bytes HTML transferred: 14000 bytes Requests per second: 103.69 [#/sec] (mean) Time per request: 96.439 [ms] (mean) Time per request: 9.644 [ms] (mean, across all concurrent requests) Transfer rate: 19.08 [Kbytes/sec] received
- Connection Times (ms)
- min mean[+/-sd] median max
Connect: 0 0 2.9 0 10 Processing: 20 94 7.3 90 130 Waiting: 0 43 28.1 40 100 Total: 20 95 7.3 100 130
- Percentage of the requests served within a certain time (ms)
- 50% 100 66% 100 75% 100 80% 100 90% 100 95% 100 98% 100 99% 110
100% 130 (longest request)
Finished 1000 requests
-
parse_patterns
= [('complete_requests', 'Completed', '^Complete requests:\\s*(\\d+)'), ('failed_requests', 'Failed', '^Failed requests:\\s*(\\d+)'), ('requests_per_second', 'req/sec', '^Requests per second:\\s*([0-9.]+)'), ('time_per_request_concurrent', 'msec/req', '^Time per request:\\s*([0-9.]+).*concurrent requests\\)$'), ('transfer_rate', 'KB/sec', '^Transfer rate:\\s*([0-9.]+)')]¶
cherrypy.test.checkerdemo module¶
Demonstration app for cherrypy.checker.
This application is intentionally broken and badly designed. To demonstrate the output of the CherryPy Checker, simply execute this module.
cherrypy.test.helper module¶
A library of helper functions for the CherryPy test suite.
-
class
cherrypy.test.helper.
CPProcess
(wait=False, daemonize=False, ssl=False, socket_host=None, socket_port=None)[source]¶ Bases:
object
-
access_log
= '/home/docs/checkouts/readthedocs.org/user_builds/cherrypy-wolph/envs/latest/local/lib/python2.7/site-packages/CherryPy-10.0.1.dev10+ng1a62192-py2.7.egg/cherrypy/test/test.access.log'¶
-
config_file
= '/home/docs/checkouts/readthedocs.org/user_builds/cherrypy-wolph/envs/latest/local/lib/python2.7/site-packages/CherryPy-10.0.1.dev10+ng1a62192-py2.7.egg/cherrypy/test/test.conf'¶
-
config_template
= "[global]\nserver.socket_host: '%(host)s'\nserver.socket_port: %(port)s\nchecker.on: False\nlog.screen: False\nlog.error_file: r'%(error_log)s'\nlog.access_file: r'%(access_log)s'\n%(ssl)s\n%(extra)s\n"¶
-
error_log
= '/home/docs/checkouts/readthedocs.org/user_builds/cherrypy-wolph/envs/latest/local/lib/python2.7/site-packages/CherryPy-10.0.1.dev10+ng1a62192-py2.7.egg/cherrypy/test/test.error.log'¶
-
pid_file
= '/home/docs/checkouts/readthedocs.org/user_builds/cherrypy-wolph/envs/latest/local/lib/python2.7/site-packages/CherryPy-10.0.1.dev10+ng1a62192-py2.7.egg/cherrypy/test/test.pid'¶
-
-
class
cherrypy.test.helper.
CPWebCase
(methodName='runTest')[source]¶ Bases:
cherrypy.test.webtest.WebCase
-
assertErrorPage
(status, message=None, pattern='')[source]¶ Compare the response body with a built in error page.
The function will optionally look for the regexp pattern, within the exception embedded in the error page.
-
available_servers
= {'wsgi': <class 'cherrypy.test.helper.LocalWSGISupervisor'>, 'wsgi_u': <function get_wsgi_u_supervisor at 0x7f7de1bf1398>, 'cpmodpy': <function get_cpmodpy_supervisor at 0x7f7de1beccf8>, 'modfastcgi': <function get_modfastcgi_supervisor at 0x7f7de1bf1320>, 'modpygw': <function get_modpygw_supervisor at 0x7f7de1bf11b8>, 'modwsgi': <function get_modwsgi_supervisor at 0x7f7de1bf1230>, 'modfcgid': <function get_modfcgid_supervisor at 0x7f7de1bf12a8>, 'native': <class 'cherrypy.test.helper.NativeServerSupervisor'>}¶
-
date_tolerance
= 2¶
-
default_server
= 'wsgi'¶
-
do_gc_test
= False¶
-
getPage
(url, headers=None, method='GET', body=None, protocol=None, raise_subcls=None)[source]¶ Open the url. Return status, headers, body.
raise_subcls must be a tuple with the exceptions classes or a single exception class that are not going to be considered a socket.error regardless that they were are subclass of a socket.error and therefore not considered for a connection retry.
-
scheme
= 'http'¶
-
script_name
= ''¶
-
-
class
cherrypy.test.helper.
LocalSupervisor
(**kwargs)[source]¶ Bases:
cherrypy.test.helper.Supervisor
Base class for modeling/controlling servers which run in the same process.
When the server side runs in a different process, start/stop can dump all state between each test module easily. When the server side runs in the same process as the client, however, we have to do a bit more work to ensure config and mounted apps are reset between tests.
-
using_apache
= False¶
-
using_wsgi
= False¶
-
-
class
cherrypy.test.helper.
LocalWSGISupervisor
(**kwargs)[source]¶ Bases:
cherrypy.test.helper.LocalSupervisor
Server supervisor for the builtin WSGI server.
-
httpserver_class
= 'cherrypy._cpwsgi_server.CPWSGIServer'¶
-
using_apache
= False¶
-
using_wsgi
= True¶
-
-
class
cherrypy.test.helper.
NativeServerSupervisor
(**kwargs)[source]¶ Bases:
cherrypy.test.helper.LocalSupervisor
Server supervisor for the builtin HTTP server.
-
httpserver_class
= 'cherrypy._cpnative_server.CPHTTPServer'¶
-
using_apache
= False¶
-
using_wsgi
= False¶
-
-
class
cherrypy.test.helper.
Supervisor
(**kwargs)[source]¶ Bases:
object
Base class for modeling and controlling servers during testing.
-
cherrypy.test.helper.
log_to_stderr
(msg, level)¶
cherrypy.test.logtest module¶
logtest, a unittest.TestCase helper for testing log output.
-
class
cherrypy.test.logtest.
LogCase
[source]¶ Bases:
object
unittest.TestCase mixin for testing log messages.
- logfile: a filename for the desired log. Yes, I know modes are evil,
- but it makes the test functions so much cleaner to set this once.
- lastmarker: the last marker in the log. This can be used to search for
- messages since the last marker.
- markerPrefix: a string with which to prefix log markers. This should be
- unique enough from normal log output to use for marker identification.
-
assertInLog
(line, marker=None)[source]¶ Fail if the given (partial) line is not in the log.
The log will be searched from the given marker to the next marker. If marker is None, self.lastmarker is used. If the log hasn’t been marked (using self.markLog), the entire log will be searched.
-
assertLog
(sliceargs, lines, marker=None)[source]¶ Fail if log.readlines()[sliceargs] is not contained in ‘lines’.
The log will be searched from the given marker to the next marker. If marker is None, self.lastmarker is used. If the log hasn’t been marked (using self.markLog), the entire log will be searched.
-
assertNotInLog
(line, marker=None)[source]¶ Fail if the given (partial) line is in the log.
The log will be searched from the given marker to the next marker. If marker is None, self.lastmarker is used. If the log hasn’t been marked (using self.markLog), the entire log will be searched.
-
lastmarker
= None¶
-
logfile
= None¶
-
markerPrefix
= 'test suite marker: '¶
cherrypy.test.modfastcgi module¶
Wrapper for mod_fastcgi, for use as a CherryPy HTTP server when testing.
To autostart fastcgi, the “apache” executable or script must be on your system path, or you must override the global APACHE_PATH. On some platforms, “apache” may be called “apachectl”, “apache2ctl”, or “httpd”–create a symlink to them if needed.
You’ll also need the WSGIServer from flup.servers. See http://projects.amor.org/misc/wiki/ModPythonGateway
- Apache processes Range headers automatically; CherryPy’s truncated
output is then truncated again by Apache. See test_core.testRanges. This was worked around in http://www.cherrypy.org/changeset/1319.
- Apache does not allow custom HTTP methods like CONNECT as per the spec.
See test_core.testHTTPMethods.
Max request header and body settings do not work with Apache.
- Apache replaces status “reason phrases” automatically. For example,
CherryPy may set “304 Not modified” but Apache will write out “304 Not Modified” (capital “M”).
Apache does not allow custom error codes as per the spec.
- Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
Request-URI too early.
- mod_python will not read request bodies which use the “chunked”
transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block instead of REQUEST_CHUNKED_DECHUNK, see Apache2’s http_protocol.c and mod_python’s requestobject.c).
- Apache will output a “Content-Length: 0” response header even if there’s
no response entity body. This isn’t really a bug; it just differs from the CherryPy default.
-
class
cherrypy.test.modfastcgi.
ModFCGISupervisor
(**kwargs)[source]¶ Bases:
cherrypy.test.helper.LocalWSGISupervisor
-
httpserver_class
= 'cherrypy.process.servers.FlupFCGIServer'¶
-
template
= '\n# Apache2 server conf file for testing CherryPy with mod_fastcgi.\n# fumanchu: I had to hard-code paths due to crazy Debian layouts :(\nServerRoot /usr/lib/apache2\nUser #1000\nErrorLog %(root)s/mod_fastcgi.error.log\n\nDocumentRoot "%(root)s"\nServerName 127.0.0.1\nListen %(port)s\nLoadModule fastcgi_module modules/mod_fastcgi.so\nLoadModule rewrite_module modules/mod_rewrite.so\n\nOptions +ExecCGI\nSetHandler fastcgi-script\nRewriteEngine On\nRewriteRule ^(.*)$ /fastcgi.pyc [L]\nFastCgiExternalServer "%(server)s" -host 127.0.0.1:4000\n'¶
-
using_apache
= True¶
-
using_wsgi
= True¶
-
cherrypy.test.modfcgid module¶
Wrapper for mod_fcgid, for use as a CherryPy HTTP server when testing.
To autostart fcgid, the “apache” executable or script must be on your system path, or you must override the global APACHE_PATH. On some platforms, “apache” may be called “apachectl”, “apache2ctl”, or “httpd”–create a symlink to them if needed.
You’ll also need the WSGIServer from flup.servers. See http://projects.amor.org/misc/wiki/ModPythonGateway
- Apache processes Range headers automatically; CherryPy’s truncated
output is then truncated again by Apache. See test_core.testRanges. This was worked around in http://www.cherrypy.org/changeset/1319.
- Apache does not allow custom HTTP methods like CONNECT as per the spec.
See test_core.testHTTPMethods.
Max request header and body settings do not work with Apache.
- Apache replaces status “reason phrases” automatically. For example,
CherryPy may set “304 Not modified” but Apache will write out “304 Not Modified” (capital “M”).
Apache does not allow custom error codes as per the spec.
- Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
Request-URI too early.
- mod_python will not read request bodies which use the “chunked”
transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block instead of REQUEST_CHUNKED_DECHUNK, see Apache2’s http_protocol.c and mod_python’s requestobject.c).
- Apache will output a “Content-Length: 0” response header even if there’s
no response entity body. This isn’t really a bug; it just differs from the CherryPy default.
-
class
cherrypy.test.modfcgid.
ModFCGISupervisor
(**kwargs)[source]¶ Bases:
cherrypy.test.helper.LocalSupervisor
-
template
= '\n# Apache2 server conf file for testing CherryPy with mod_fcgid.\n\nDocumentRoot "%(root)s"\nServerName 127.0.0.1\nListen %(port)s\nLoadModule fastcgi_module modules/mod_fastcgi.dll\nLoadModule rewrite_module modules/mod_rewrite.so\n\nOptions ExecCGI\nSetHandler fastcgi-script\nRewriteEngine On\nRewriteRule ^(.*)$ /fastcgi.pyc [L]\nFastCgiExternalServer "%(server)s" -host 127.0.0.1:4000\n'¶
-
using_apache
= True¶
-
using_wsgi
= True¶
-
cherrypy.test.modpy module¶
Wrapper for mod_python, for use as a CherryPy HTTP server when testing.
To autostart modpython, the “apache” executable or script must be on your system path, or you must override the global APACHE_PATH. On some platforms, “apache” may be called “apachectl” or “apache2ctl”– create a symlink to them if needed.
If you wish to test the WSGI interface instead of our _cpmodpy interface, you also need the ‘modpython_gateway’ module at: http://projects.amor.org/misc/wiki/ModPythonGateway
- Apache processes Range headers automatically; CherryPy’s truncated
output is then truncated again by Apache. See test_core.testRanges. This was worked around in http://www.cherrypy.org/changeset/1319.
- Apache does not allow custom HTTP methods like CONNECT as per the spec.
See test_core.testHTTPMethods.
Max request header and body settings do not work with Apache.
- Apache replaces status “reason phrases” automatically. For example,
CherryPy may set “304 Not modified” but Apache will write out “304 Not Modified” (capital “M”).
Apache does not allow custom error codes as per the spec.
- Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the
Request-URI too early.
- mod_python will not read request bodies which use the “chunked”
transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block instead of REQUEST_CHUNKED_DECHUNK, see Apache2’s http_protocol.c and mod_python’s requestobject.c).
- Apache will output a “Content-Length: 0” response header even if there’s
no response entity body. This isn’t really a bug; it just differs from the CherryPy default.
cherrypy.test.modwsgi module¶
Wrapper for mod_wsgi, for use as a CherryPy HTTP server.
To autostart modwsgi, the “apache” executable or script must be on your system path, or you must override the global APACHE_PATH. On some platforms, “apache” may be called “apachectl” or “apache2ctl”– create a symlink to them if needed.
##1. Apache processes Range headers automatically; CherryPy’s truncated ## output is then truncated again by Apache. See test_core.testRanges. ## This was worked around in http://www.cherrypy.org/changeset/1319. 2. Apache does not allow custom HTTP methods like CONNECT as per the spec.
See test_core.testHTTPMethods.
3. Max request header and body settings do not work with Apache. ##4. Apache replaces status “reason phrases” automatically. For example, ## CherryPy may set “304 Not modified” but Apache will write out ## “304 Not Modified” (capital “M”). ##5. Apache does not allow custom error codes as per the spec. ##6. Apache (or perhaps modpython, or modpython_gateway) unquotes %xx in the ## Request-URI too early. 7. mod_wsgi will not read request bodies which use the “chunked”
transfer-coding (it passes REQUEST_CHUNKED_ERROR to ap_setup_client_block instead of REQUEST_CHUNKED_DECHUNK, see Apache2’s http_protocol.c and mod_python’s requestobject.c).
- When responding with 204 No Content, mod_wsgi adds a Content-Length
header for you.
- When an error is raised, mod_wsgi has no facility for printing a
traceback as the response content (it’s sent to the Apache log instead).
Startup and shutdown of Apache when running mod_wsgi seems slow.
-
class
cherrypy.test.modwsgi.
ModWSGISupervisor
(**kwargs)[source]¶ Bases:
cherrypy.test.helper.Supervisor
Server Controller for ModWSGI and CherryPy.
-
template
= '\n# Apache2 server conf file for testing CherryPy with modpython_gateway.\n\nServerName 127.0.0.1\nDocumentRoot "/"\nListen %(port)s\n\nAllowEncodedSlashes On\nLoadModule rewrite_module modules/mod_rewrite.so\nRewriteEngine on\nRewriteMap escaping int:escape\n\nLoadModule log_config_module modules/mod_log_config.so\nLogFormat "%%h %%l %%u %%t \\"%%r\\" %%>s %%b \\"%%{Referer}i\\" \\"%%{User-agent}i\\"" combined\nCustomLog "%(curdir)s/apache.access.log" combined\nErrorLog "%(curdir)s/apache.error.log"\nLogLevel debug\n\nLoadModule wsgi_module modules/mod_wsgi.so\nLoadModule env_module modules/mod_env.so\n\nWSGIScriptAlias / "%(curdir)s/modwsgi.py"\nSetEnv testmod %(testmod)s\n'¶
-
using_apache
= True¶
-
using_wsgi
= True¶
-
cherrypy.test.sessiondemo module¶
A session demonstration app.
cherrypy.test.test_auth_basic module¶
cherrypy.test.test_auth_digest module¶
cherrypy.test.test_bus module¶
cherrypy.test.test_caching module¶
cherrypy.test.test_compat module¶
cherrypy.test.test_config module¶
Tests for the CherryPy configuration system.
-
class
cherrypy.test.test_config.
CallablesInConfigTest
(methodName='runTest')[source]¶ Bases:
unittest.case.TestCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_config.
ConfigTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
cherrypy.test.test_config.
StringIOFromNative
(x)¶
cherrypy.test.test_config_server module¶
Tests for the CherryPy configuration system.
cherrypy.test.test_conn module¶
Tests for TCP connection handling, including proper and timely close.
-
class
cherrypy.test.test_conn.
BadRequestTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_conn.
ConnectionCloseTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_conn.
ConnectionTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_conn.
LimitedRequestQueueTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_conn.
PipelineTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
cherrypy.test.test_conn.
socket_reset_errors
= [104, 'Remote end closed connection without response']¶ reset error numbers available on this platform
cherrypy.test.test_core module¶
Basic tests for the CherryPy core: request handling.
-
class
cherrypy.test.test_core.
CoreRequestHandlingTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
cookies module fails to reject invalid cookies https://github.com/cherrypy/cherrypy/issues/1405
cherrypy.test.test_dynamicobjectmapping module¶
-
class
cherrypy.test.test_dynamicobjectmapping.
DynamicObjectMappingTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
cherrypy.test.test_encoding module¶
cherrypy.test.test_etags module¶
cherrypy.test.test_http module¶
cherrypy.test.test_httpauth module¶
cherrypy.test.test_httplib module¶
Tests for cherrypy/lib/httputil.py.
cherrypy.test.test_iterator module¶
-
class
cherrypy.test.test_iterator.
IteratorBase
[source]¶ Bases:
object
-
created
= 0¶
-
datachunk
= 'butternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squashbutternut squash'¶
-
-
class
cherrypy.test.test_iterator.
OurIterator
[source]¶ Bases:
cherrypy.test.test_iterator.IteratorBase
-
closed_off
= False¶
-
count
= 0¶
-
next
()¶
-
started
= False¶
-
-
class
cherrypy.test.test_iterator.
OurUnclosableIterator
[source]¶ Bases:
cherrypy.test.test_iterator.OurIterator
-
close
= 'close'¶
-
cherrypy.test.test_json module¶
cherrypy.test.test_logging module¶
Basic tests for the CherryPy core: request handling.
-
class
cherrypy.test.test_logging.
AccessLogTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
,cherrypy.test.logtest.LogCase
-
logfile
= '/home/docs/checkouts/readthedocs.org/user_builds/cherrypy-wolph/envs/latest/local/lib/python2.7/site-packages/CherryPy-10.0.1.dev10+ng1a62192-py2.7.egg/cherrypy/test/access.log'¶
-
static
setup_server
()¶
-
-
class
cherrypy.test.test_logging.
ErrorLogTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
,cherrypy.test.logtest.LogCase
-
logfile
= '/home/docs/checkouts/readthedocs.org/user_builds/cherrypy-wolph/envs/latest/local/lib/python2.7/site-packages/CherryPy-10.0.1.dev10+ng1a62192-py2.7.egg/cherrypy/test/error.log'¶
-
static
setup_server
()¶
-
cherrypy.test.test_mime module¶
Tests for various MIME issues, including the safe_multipart Tool.
-
class
cherrypy.test.test_mime.
MultipartTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_mime.
SafeMultipartHandlingTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
cherrypy.test.test_misc_tools module¶
-
class
cherrypy.test.test_misc_tools.
AcceptTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_misc_tools.
AutoVaryTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_misc_tools.
RefererTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_misc_tools.
ResponseHeadersTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
cherrypy.test.test_objectmapping module¶
cherrypy.test.test_params module¶
cherrypy.test.test_proxy module¶
cherrypy.test.test_refleaks module¶
Tests for refleaks.
cherrypy.test.test_request_obj module¶
Basic tests for the cherrypy.Request object.
cherrypy.test.test_routes module¶
cherrypy.test.test_session module¶
-
class
cherrypy.test.test_session.
MemcachedSessionTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_session.
SessionTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
cherrypy.test.test_sessionauthenticate module¶
cherrypy.test.test_states module¶
-
class
cherrypy.test.test_states.
ServerStateTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
-
class
cherrypy.test.test_states.
WaitTests
(methodName='runTest')[source]¶ Bases:
unittest.case.TestCase
-
test_safe_wait_INADDR_ANY
()[source]¶ Wait on INADDR_ANY should not raise IOError
In cases where the loopback interface does not exist, CherryPy cannot effectively determine if a port binding to INADDR_ANY was effected. In this situation, CherryPy should assume that it failed to detect the binding (not that the binding failed) and only warn that it could not verify it.
-
cherrypy.test.test_static module¶
-
class
cherrypy.test.test_static.
StaticTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
py27_on_windows
= False¶
-
cherrypy.test.test_tools module¶
Test the various means of instantiating and invoking tools.
-
class
cherrypy.test.test_tools.
SessionAuthTest
(methodName='runTest')[source]¶ Bases:
unittest.case.TestCase
cherrypy.test.test_tutorials module¶
cherrypy.test.test_virtualhost module¶
cherrypy.test.test_wsgi_ns module¶
cherrypy.test.test_wsgi_unix_socket module¶
-
class
cherrypy.test.test_wsgi_unix_socket.
USocketHTTPConnection
(path)[source]¶ Bases:
httplib.HTTPConnection
HTTPConnection over a unix socket.
-
class
cherrypy.test.test_wsgi_unix_socket.
WSGI_UnixSocket_Test
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
Test basic behavior on a cherrypy wsgi server listening on a unix socket.
It exercises the config option server.socket_file.
-
HTTP_CONN
= <cherrypy.test.test_wsgi_unix_socket.USocketHTTPConnection instance>¶
-
pytestmark
= [<MarkDecorator 'skipif' {'args': ("sys.platform == 'win32'",), 'kwargs': {}}>]¶
-
cherrypy.test.test_wsgi_vhost module¶
cherrypy.test.test_wsgiapps module¶
-
class
cherrypy.test.test_wsgiapps.
WSGIGraftTests
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
wsgi_output
= 'Hello, world!\nThis is a wsgi app running within CherryPy!'¶
-
cherrypy.test.test_xmlrpc module¶
-
class
cherrypy.test.test_xmlrpc.
HTTPSTransport
(use_datetime=0)[source]¶ Bases:
xmlrpclib.SafeTransport
Subclass of SafeTransport to fix sock.recv errors (by using file).
-
class
cherrypy.test.test_xmlrpc.
XmlRpcTest
(methodName='runTest')[source]¶ Bases:
cherrypy.test.helper.CPWebCase
-
static
setup_server
()¶
-
static
cherrypy.test.webtest module¶
Extensions to unittest for web frameworks.
Use the WebCase.getPage method to request a page from your HTTP server.
If you have control over your server process, you can handle errors in the server-side of the HTTP conversation a bit better. You must run both the client (your WebCase tests) and the server in the same process (but in separate threads, obviously).
When an error occurs in the framework, call server_error. It will print the traceback to stdout, and keep any assertions you have from running (the assumption is that, if the server errors, the page output will not be of further significance to your tests).
-
class
cherrypy.test.webtest.
ReloadingTestLoader
[source]¶ Bases:
unittest.loader.TestLoader
-
loadTestsFromName
(name, module=None)[source]¶ Return a suite of all tests cases given a string specifier.
The name may resolve either to a module, a test case class, a test method within a test case class, or a callable object which returns a TestCase or TestSuite instance.
The method optionally resolves the names relative to a given module.
-
-
class
cherrypy.test.webtest.
TerseTestResult
(stream, descriptions, verbosity)[source]¶ Bases:
unittest.runner.TextTestResult
-
class
cherrypy.test.webtest.
TerseTestRunner
(stream=<open file '<stderr>', mode 'w'>, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None)[source]¶ Bases:
unittest.runner.TextTestRunner
A test runner class that displays results in textual form.
-
class
cherrypy.test.webtest.
WebCase
(methodName='runTest')[source]¶ Bases:
unittest.case.TestCase
-
HOST
= '127.0.0.1'¶
-
HTTP_CONN
¶ alias of
HTTPConnection
-
PORT
= 8000¶
-
PROTOCOL
= 'HTTP/1.1'¶
-
assertHeaderIn
(key, values, msg=None)[source]¶ Fail if header indicated by key doesn’t have one of the values.
-
assertHeaderItemValue
(key, value, msg=None)[source]¶ Fail if the header does not contain the specified value
-
assertMatchesBody
(pattern, msg=None, flags=0)[source]¶ Fail if value (a regex pattern) is not in self.body.
-
body
= None¶
-
console_height
= 30¶
-
encoding
= 'utf-8'¶
-
getPage
(url, headers=None, method='GET', body=None, protocol=None, raise_subcls=None)[source]¶ Open the url with debugging support. Return status, headers, body.
raise_subcls must be a tuple with the exceptions classes or a single exception class that are not going to be considered a socket.error regardless that they were are subclass of a socket.error and therefore not considered for a connection retry.
-
headers
= None¶
-
interface
()[source]¶ Return an IP address for a client connection.
If the server is listening on ‘0.0.0.0’ (INADDR_ANY) or ‘::’ (IN6ADDR_ANY), this will return the proper localhost.
-
persistent
¶
-
scheme
= 'http'¶
-
set_persistent
(on=True, auto_open=False)[source]¶ Make our HTTP_CONN persistent (or not).
If the ‘on’ argument is True (the default), then self.HTTP_CONN will be set to an instance of HTTPConnection (or HTTPS if self.scheme is “https”). This will then persist across requests.
We only allow for a single open connection, so if you call this and we currently have an open connection, it will be closed.
-
status
= None¶
-
time
= None¶
-
url
= None¶
-
-
cherrypy.test.webtest.
cleanHeaders
(headers, method, body, host, port)[source]¶ Return request headers, with required headers added (if missing).
-
cherrypy.test.webtest.
interface
(host)[source]¶ Return an IP address for a client connection given the server host.
If the server is listening on ‘0.0.0.0’ (INADDR_ANY) or ‘::’ (IN6ADDR_ANY), this will return the proper localhost.
-
cherrypy.test.webtest.
openURL
(url, headers=None, method='GET', body=None, host='127.0.0.1', port=8000, http_conn=<class httplib.HTTPConnection>, protocol='HTTP/1.1', raise_subcls=None)[source]¶ Open the given HTTP resource and return status, headers, and body.
raise_subcls must be a tuple with the exceptions classes or a single exception class that are not going to be considered a socket.error regardless that they were are subclass of a socket.error and therefore not considered for a connection retry.
cherrypy.tutorial package¶
Submodules¶
cherrypy.tutorial.tut01_helloworld module¶
Tutorial - Hello World
The most basic (working) CherryPy application possible.
cherrypy.tutorial.tut02_expose_methods module¶
Tutorial - Multiple methods
This tutorial shows you how to link to other methods of your request handler.
cherrypy.tutorial.tut03_get_and_post module¶
Tutorial - Passing variables
This tutorial shows you how to pass GET/POST variables to methods.
cherrypy.tutorial.tut04_complex_site module¶
Tutorial - Multiple objects
This tutorial shows you how to create a site structure through multiple possibly nested request handler objects.
cherrypy.tutorial.tut05_derived_objects module¶
Tutorial - Object inheritance
You are free to derive your request handler classes from any base class you wish. In most real-world applications, you will probably want to create a central base class used for all your pages, which takes care of things like printing a common page header and footer.
-
class
cherrypy.tutorial.tut05_derived_objects.
AnotherPage
[source]¶ Bases:
cherrypy.tutorial.tut05_derived_objects.Page
-
title
= 'Another Page'¶
-
-
class
cherrypy.tutorial.tut05_derived_objects.
HomePage
[source]¶ Bases:
cherrypy.tutorial.tut05_derived_objects.Page
-
title
= 'Tutorial 5'¶
-
cherrypy.tutorial.tut06_default_method module¶
Tutorial - The default method
Request handler objects can implement a method called “default” that is called when no other suitable method/object could be found. Essentially, if CherryPy2 can’t find a matching request handler object for the given request URI, it will use the default method of the object located deepest on the URI path.
Using this mechanism you can easily simulate virtual URI structures by parsing the extra URI string, which you can access through cherrypy.request.virtualPath.
The application in this tutorial simulates an URI structure looking like /users/<username>. Since the <username> bit will not be found (as there are no matching methods), it is handled by the default method.
cherrypy.tutorial.tut07_sessions module¶
Tutorial - Sessions
Storing session data in CherryPy applications is very easy: cherrypy provides a dictionary called “session” that represents the session data for the current user. If you use RAM based sessions, you can store any kind of object into that dictionary; otherwise, you are limited to objects that can be pickled.
cherrypy.tutorial.tut08_generators_and_yield module¶
Bonus Tutorial: Using generators to return result bodies
Instead of returning a complete result string, you can use the yield statement to return one result part after another. This may be convenient in situations where using a template package like CherryPy or Cheetah would be overkill, and messy string concatenation too uncool. ;-)
cherrypy.tutorial.tut09_files module¶
Tutorial: File upload and download
When a client uploads a file to a CherryPy application, it’s placed on disk immediately. CherryPy will pass it to your exposed method as an argument (see “myFile” below); that arg will have a “file” attribute, which is a handle to the temporary uploaded file. If you wish to permanently save the file, you need to read() from myFile.file and write() somewhere else.
Note the use of ‘enctype=”multipart/form-data”’ and ‘input type=”file”’ in the HTML which the client uses to upload the file.
If you wish to send a file to the client, you have two options: First, you can simply return a file-like object from your page handler. CherryPy will read the file and serve it as the content (HTTP body) of the response. However, that doesn’t tell the client that the response is a file to be saved, rather than displayed. Use cherrypy.lib.static.serve_file for that; it takes four arguments:
serve_file(path, content_type=None, disposition=None, name=None)
Set “name” to the filename that you expect clients to use when they save your file. Note that the “name” argument is ignored if you don’t also provide a “disposition” (usually “attachement”). You can manually set “content_type”, but be aware that if you also use the encoding tool, it may choke if the file extension is not recognized as belonging to a known Content-Type. Setting the content_type to “application/x-download” works in most cases, and should prompt the user with an Open/Save dialog in popular browsers.
cherrypy.tutorial.tut10_http_errors module¶
Tutorial: HTTP errors
HTTPError is used to return an error response to the client. CherryPy has lots of options regarding how such errors are logged, displayed, and formatted.
Module contents¶
Submodules¶
cherrypy.daemon module¶
The CherryPy daemon.
Module contents¶
CherryPy is a pythonic, object-oriented HTTP framework.
CherryPy consists of not one, but four separate API layers.
The APPLICATION LAYER is the simplest. CherryPy applications are written as a tree of classes and methods, where each branch in the tree corresponds to a branch in the URL path. Each method is a ‘page handler’, which receives GET and POST params as keyword arguments, and returns or yields the (HTML) body of the response. The special method name ‘index’ is used for paths that end in a slash, and the special method name ‘default’ is used to handle multiple paths via a single handler. This layer also includes:
- the ‘exposed’ attribute (and cherrypy.expose)
- cherrypy.quickstart()
- _cp_config attributes
- cherrypy.tools (including cherrypy.session)
- cherrypy.url()
The ENVIRONMENT LAYER is used by developers at all levels. It provides information about the current request and response, plus the application and server environment, via a (default) set of top-level objects:
- cherrypy.request
- cherrypy.response
- cherrypy.engine
- cherrypy.server
- cherrypy.tree
- cherrypy.config
- cherrypy.thread_data
- cherrypy.log
- cherrypy.HTTPError, NotFound, and HTTPRedirect
- cherrypy.lib
The EXTENSION LAYER allows advanced users to construct and share their own plugins. It consists of:
- Hook API
- Tool API
- Toolbox API
- Dispatch API
- Config Namespace API
Finally, there is the CORE LAYER, which uses the core API’s to construct the default components which are available at higher layers. You can think of the default components as the ‘reference implementation’ for CherryPy. Megaframeworks (and advanced users) may replace the default components with customized or extended components. The core API’s are:
- Application API
- Engine API
- Request API
- Server API
- WSGI API
These API’s are described in the CherryPy specification.
-
cherrypy.
quickstart
(root=None, script_name='', config=None)[source]¶ Mount the given root, start the builtin server (and engine), then block.
- root: an instance of a “controller class” (a collection of page handler
- methods) which represents the root of the application.
- script_name: a string containing the “mount point” of the application.
This should start with a slash, and be the path portion of the URL at which to mount the given root. For example, if root.index() will handle requests to “http://www.example.com:8080/dept/app1/”, then the script_name argument would be “/dept/app1”.
It MUST NOT end in a slash. If the script_name refers to the root of the URI, it MUST be an empty string (not “/”).
- config: a file or dict containing application config. If this contains
- a [global] section, those entries will be used in the global (site-wide) config.
CherryPy is a pythonic, object-oriented web framework.
CherryPy allows developers to build web applications in much the same way they would build any other object-oriented Python program. This results in smaller source code developed in less time.
CherryPy is now more than ten years old and it is has proven to be fast and reliable. It is being used in production by many sites, from the simplest to the most demanding.
A CherryPy application typically looks like this:
import cherrypy
class HelloWorld(object):
@cherrypy.expose
def index(self):
return "Hello World!"
cherrypy.quickstart(HelloWorld())
In order to make the most of CherryPy, you should start with the tutorials that will lead you through the most common aspects of the framework. Once done, you will probably want to browse through the basics and advanced sections that will demonstrate how to implement certain operations. Finally, you will want to carefully read the configuration and extend sections that go in-depth regarding the powerful features provided by the framework.
Above all, have fun with your application!