first full commit

This commit is contained in:
Remy
2011-05-20 19:28:27 -07:00
parent e4a1d6a4a1
commit 9be4f3fc21
16 changed files with 0 additions and 849 deletions

View File

@@ -1,16 +0,0 @@
CherryPy Tutorials
------------------------------------------------------------------------
This is a series of tutorials explaining how to develop dynamic web
applications using CherryPy. A couple of notes:
- Each of these tutorials builds on the ones before it. If you're
new to CherryPy, we recommend you start with 01_helloworld.py and
work your way upwards. :)
- In most of these tutorials, you will notice that all output is done
by returning normal Python strings, often using simple Python
variable substitution. In most real-world applications, you will
probably want to use a separate template package (like Cheetah,
CherryTemplate or XML/XSL).

View File

@@ -1,3 +0,0 @@
# This is used in test_config to test unrepr of "from A import B"
thing2 = object()

View File

@@ -1,168 +0,0 @@
'''
Bonus Tutorial: Using SQLObject
This is a silly little contacts manager application intended to
demonstrate how to use SQLObject from within a CherryPy2 project. It
also shows how to use inline Cheetah templates.
SQLObject is an Object/Relational Mapper that allows you to access
data stored in an RDBMS in a pythonic fashion. You create data objects
as Python classes and let SQLObject take care of all the nasty details.
This code depends on the latest development version (0.6+) of SQLObject.
You can get it from the SQLObject Subversion server. You can find all
necessary information at <http://www.sqlobject.org>. This code will NOT
work with the 0.5.x version advertised on their website!
This code also depends on a recent version of Cheetah. You can find
Cheetah at <http://www.cheetahtemplate.org>.
After starting this application for the first time, you will need to
access the /reset URI in order to create the database table and some
sample data. Accessing /reset again will drop and re-create the table,
so you may want to be careful. :-)
This application isn't supposed to be fool-proof, it's not even supposed
to be very GOOD. Play around with it some, browse the source code, smile.
:)
-- Hendrik Mans <hendrik@mans.de>
'''
import cherrypy
from Cheetah.Template import Template
from sqlobject import *
# configure your database connection here
__connection__ = 'mysql://root:@localhost/test'
# this is our (only) data class.
class Contact(SQLObject):
lastName = StringCol(length = 50, notNone = True)
firstName = StringCol(length = 50, notNone = True)
phone = StringCol(length = 30, notNone = True, default = '')
email = StringCol(length = 30, notNone = True, default = '')
url = StringCol(length = 100, notNone = True, default = '')
class ContactManager:
def index(self):
# Let's display a list of all stored contacts.
contacts = Contact.select()
template = Template('''
<h2>All Contacts</h2>
#for $contact in $contacts
<a href="mailto:$contact.email">$contact.lastName, $contact.firstName</a>
[<a href="./edit?id=$contact.id">Edit</a>]
[<a href="./delete?id=$contact.id">Delete</a>]
<br/>
#end for
<p>[<a href="./edit">Add new contact</a>]</p>
''', [locals(), globals()])
return template.respond()
index.exposed = True
def edit(self, id = 0):
# we really want id as an integer. Since GET/POST parameters
# are always passed as strings, let's convert it.
id = int(id)
if id > 0:
# if an id is specified, we're editing an existing contact.
contact = Contact.get(id)
title = "Edit Contact"
else:
# if no id is specified, we're entering a new contact.
contact = None
title = "New Contact"
# In the following template code, please note that we use
# Cheetah's $getVar() construct for the form values. We have
# to do this because contact may be set to None (see above).
template = Template('''
<h2>$title</h2>
<form action="./store" method="POST">
<input type="hidden" name="id" value="$id" />
Last Name: <input name="lastName" value="$getVar('contact.lastName', '')" /><br/>
First Name: <input name="firstName" value="$getVar('contact.firstName', '')" /><br/>
Phone: <input name="phone" value="$getVar('contact.phone', '')" /><br/>
Email: <input name="email" value="$getVar('contact.email', '')" /><br/>
URL: <input name="url" value="$getVar('contact.url', '')" /><br/>
<input type="submit" value="Store" />
</form>
''', [locals(), globals()])
return template.respond()
edit.exposed = True
def delete(self, id):
# Delete the specified contact
contact = Contact.get(int(id))
contact.destroySelf()
return 'Deleted. <a href="./">Return to Index</a>'
delete.exposed = True
def store(self, lastName, firstName, phone, email, url, id = None):
if id and int(id) > 0:
# If an id was specified, update an existing contact.
contact = Contact.get(int(id))
# We could set one field after another, but that would
# cause multiple UPDATE clauses. So we'll just do it all
# in a single pass through the set() method.
contact.set(
lastName = lastName,
firstName = firstName,
phone = phone,
email = email,
url = url)
else:
# Otherwise, add a new contact.
contact = Contact(
lastName = lastName,
firstName = firstName,
phone = phone,
email = email,
url = url)
return 'Stored. <a href="./">Return to Index</a>'
store.exposed = True
def reset(self):
# Drop existing table
Contact.dropTable(True)
# Create new table
Contact.createTable()
# Create some sample data
Contact(
firstName = 'Hendrik',
lastName = 'Mans',
email = 'hendrik@mans.de',
phone = '++49 89 12345678',
url = 'http://www.mornography.de')
return "reset completed!"
reset.exposed = True
print("If you're running this application for the first time, please go to http://localhost:8080/reset once in order to create the database!")
cherrypy.quickstart(ContactManager())

View File

@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>403 Unauthorized</title>
</head>
<body>
<h2>You can't do that!</h2>
<p>%(message)s</p>
<p>This is a custom error page that is read from a file.<p>
<pre>%(traceback)s</pre>
</body>
</html>

Binary file not shown.

View File

@@ -1,35 +0,0 @@
"""
Tutorial - Hello World
The most basic (working) CherryPy application possible.
"""
# Import CherryPy global namespace
import cherrypy
class HelloWorld:
""" Sample request handler class. """
def index(self):
# CherryPy will call this method for the root URI ("/") and send
# its return value to the client. Because this is tutorial
# lesson number 01, we'll just send something really simple.
# How about...
return "Hello world!"
# Expose the index method through the web. CherryPy will never
# publish methods that don't have the exposed attribute set to True.
index.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(HelloWorld(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(HelloWorld(), config=tutconf)

View File

@@ -1,32 +0,0 @@
"""
Tutorial - Multiple methods
This tutorial shows you how to link to other methods of your request
handler.
"""
import cherrypy
class HelloWorld:
def index(self):
# Let's link to another method here.
return 'We have an <a href="showMessage">important message</a> for you!'
index.exposed = True
def showMessage(self):
# Here's the important message!
return "Hello world!"
showMessage.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(HelloWorld(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(HelloWorld(), config=tutconf)

View File

@@ -1,53 +0,0 @@
"""
Tutorial - Passing variables
This tutorial shows you how to pass GET/POST variables to methods.
"""
import cherrypy
class WelcomePage:
def index(self):
# Ask for the user's name.
return '''
<form action="greetUser" method="GET">
What is your name?
<input type="text" name="name" />
<input type="submit" />
</form>'''
index.exposed = True
def greetUser(self, name = None):
# CherryPy passes all GET and POST variables as method parameters.
# It doesn't make a difference where the variables come from, how
# large their contents are, and so on.
#
# You can define default parameter values as usual. In this
# example, the "name" parameter defaults to None so we can check
# if a name was actually specified.
if name:
# Greet the user!
return "Hey %s, what's up?" % name
else:
if name is None:
# No name was specified
return 'Please enter your name <a href="./">here</a>.'
else:
return 'No, really, enter your name <a href="./">here</a>.'
greetUser.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(WelcomePage(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(WelcomePage(), config=tutconf)

View File

@@ -1,98 +0,0 @@
"""
Tutorial - Multiple objects
This tutorial shows you how to create a site structure through multiple
possibly nested request handler objects.
"""
import cherrypy
class HomePage:
def index(self):
return '''
<p>Hi, this is the home page! Check out the other
fun stuff on this site:</p>
<ul>
<li><a href="/joke/">A silly joke</a></li>
<li><a href="/links/">Useful links</a></li>
</ul>'''
index.exposed = True
class JokePage:
def index(self):
return '''
<p>"In Python, how do you create a string of random
characters?" -- "Read a Perl file!"</p>
<p>[<a href="../">Return</a>]</p>'''
index.exposed = True
class LinksPage:
def __init__(self):
# Request handler objects can create their own nested request
# handler objects. Simply create them inside their __init__
# methods!
self.extra = ExtraLinksPage()
def index(self):
# Note the way we link to the extra links page (and back).
# As you can see, this object doesn't really care about its
# absolute position in the site tree, since we use relative
# links exclusively.
return '''
<p>Here are some useful links:</p>
<ul>
<li><a href="http://www.cherrypy.org">The CherryPy Homepage</a></li>
<li><a href="http://www.python.org">The Python Homepage</a></li>
</ul>
<p>You can check out some extra useful
links <a href="./extra/">here</a>.</p>
<p>[<a href="../">Return</a>]</p>
'''
index.exposed = True
class ExtraLinksPage:
def index(self):
# Note the relative link back to the Links page!
return '''
<p>Here are some extra useful links:</p>
<ul>
<li><a href="http://del.icio.us">del.icio.us</a></li>
<li><a href="http://www.mornography.de">Hendrik's weblog</a></li>
</ul>
<p>[<a href="../">Return to links page</a>]</p>'''
index.exposed = True
# Of course we can also mount request handler objects right here!
root = HomePage()
root.joke = JokePage()
root.links = LinksPage()
# Remember, we don't need to mount ExtraLinksPage here, because
# LinksPage does that itself on initialization. In fact, there is
# no reason why you shouldn't let your root object take care of
# creating all contained request handler objects.
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(root, config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(root, config=tutconf)

View File

@@ -1,83 +0,0 @@
"""
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.
"""
import cherrypy
class Page:
# Store the page title in a class attribute
title = 'Untitled Page'
def header(self):
return '''
<html>
<head>
<title>%s</title>
<head>
<body>
<h2>%s</h2>
''' % (self.title, self.title)
def footer(self):
return '''
</body>
</html>
'''
# Note that header and footer don't get their exposed attributes
# set to True. This isn't necessary since the user isn't supposed
# to call header or footer directly; instead, we'll call them from
# within the actually exposed handler methods defined in this
# class' subclasses.
class HomePage(Page):
# Different title for this page
title = 'Tutorial 5'
def __init__(self):
# create a subpage
self.another = AnotherPage()
def index(self):
# Note that we call the header and footer methods inherited
# from the Page class!
return self.header() + '''
<p>
Isn't this exciting? There's
<a href="./another/">another page</a>, too!
</p>
''' + self.footer()
index.exposed = True
class AnotherPage(Page):
title = 'Another Page'
def index(self):
return self.header() + '''
<p>
And this is the amazing second page!
</p>
''' + self.footer()
index.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(HomePage(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(HomePage(), config=tutconf)

View File

@@ -1,64 +0,0 @@
"""
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.
"""
import cherrypy
class UsersPage:
def index(self):
# Since this is just a stupid little example, we'll simply
# display a list of links to random, made-up users. In a real
# application, this could be generated from a database result set.
return '''
<a href="./remi">Remi Delon</a><br/>
<a href="./hendrik">Hendrik Mans</a><br/>
<a href="./lorenzo">Lorenzo Lamas</a><br/>
'''
index.exposed = True
def default(self, user):
# Here we react depending on the virtualPath -- the part of the
# path that could not be mapped to an object method. In a real
# application, we would probably do some database lookups here
# instead of the silly if/elif/else construct.
if user == 'remi':
out = "Remi Delon, CherryPy lead developer"
elif user == 'hendrik':
out = "Hendrik Mans, CherryPy co-developer & crazy German"
elif user == 'lorenzo':
out = "Lorenzo Lamas, famous actor and singer!"
else:
out = "Unknown user. :-("
return '%s (<a href="./">back</a>)' % out
default.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(UsersPage(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(UsersPage(), config=tutconf)

View File

@@ -1,44 +0,0 @@
"""
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.
"""
import cherrypy
class HitCounter:
_cp_config = {'tools.sessions.on': True}
def index(self):
# Increase the silly hit counter
count = cherrypy.session.get('count', 0) + 1
# Store the new value in the session dictionary
cherrypy.session['count'] = count
# And display a silly hit count message!
return '''
During your current session, you've viewed this
page %s times! Your life is a patio of fun!
''' % count
index.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(HitCounter(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(HitCounter(), config=tutconf)

View File

@@ -1,47 +0,0 @@
"""
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. ;-)
"""
import cherrypy
class GeneratorDemo:
def header(self):
return "<html><body><h2>Generators rule!</h2>"
def footer(self):
return "</body></html>"
def index(self):
# Let's make up a list of users for presentation purposes
users = ['Remi', 'Carlos', 'Hendrik', 'Lorenzo Lamas']
# Every yield line adds one part to the total result body.
yield self.header()
yield "<h3>List of users:</h3>"
for user in users:
yield "%s<br/>" % user
yield self.footer()
index.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(GeneratorDemo(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(GeneratorDemo(), config=tutconf)

View File

@@ -1,107 +0,0 @@
"""
Tutorial: File upload and download
Uploads
-------
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.
Downloads
---------
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.
"""
import os
localDir = os.path.dirname(__file__)
absDir = os.path.join(os.getcwd(), localDir)
import cherrypy
from cherrypy.lib import static
class FileDemo(object):
def index(self):
return """
<html><body>
<h2>Upload a file</h2>
<form action="upload" method="post" enctype="multipart/form-data">
filename: <input type="file" name="myFile" /><br />
<input type="submit" />
</form>
<h2>Download a file</h2>
<a href='download'>This one</a>
</body></html>
"""
index.exposed = True
def upload(self, myFile):
out = """<html>
<body>
myFile length: %s<br />
myFile filename: %s<br />
myFile mime-type: %s
</body>
</html>"""
# Although this just counts the file length, it demonstrates
# how to read large files in chunks instead of all at once.
# CherryPy reads the uploaded file into a temporary file;
# myFile.file.read reads from that.
size = 0
while True:
data = myFile.file.read(8192)
if not data:
break
size += len(data)
return out % (size, myFile.filename, myFile.content_type)
upload.exposed = True
def download(self):
path = os.path.join(absDir, "pdf_file.pdf")
return static.serve_file(path, "application/x-download",
"attachment", os.path.basename(path))
download.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(FileDemo(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(FileDemo(), config=tutconf)

View File

@@ -1,81 +0,0 @@
"""
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.
"""
import os
localDir = os.path.dirname(__file__)
curpath = os.path.normpath(os.path.join(os.getcwd(), localDir))
import cherrypy
class HTTPErrorDemo(object):
# Set a custom response for 403 errors.
_cp_config = {'error_page.403' : os.path.join(curpath, "custom_error.html")}
def index(self):
# display some links that will result in errors
tracebacks = cherrypy.request.show_tracebacks
if tracebacks:
trace = 'off'
else:
trace = 'on'
return """
<html><body>
<p>Toggle tracebacks <a href="toggleTracebacks">%s</a></p>
<p><a href="/doesNotExist">Click me; I'm a broken link!</a></p>
<p><a href="/error?code=403">Use a custom error page from a file.</a></p>
<p>These errors are explicitly raised by the application:</p>
<ul>
<li><a href="/error?code=400">400</a></li>
<li><a href="/error?code=401">401</a></li>
<li><a href="/error?code=402">402</a></li>
<li><a href="/error?code=500">500</a></li>
</ul>
<p><a href="/messageArg">You can also set the response body
when you raise an error.</a></p>
</body></html>
""" % trace
index.exposed = True
def toggleTracebacks(self):
# simple function to toggle tracebacks on and off
tracebacks = cherrypy.request.show_tracebacks
cherrypy.config.update({'request.show_tracebacks': not tracebacks})
# redirect back to the index
raise cherrypy.HTTPRedirect('/')
toggleTracebacks.exposed = True
def error(self, code):
# raise an error based on the get query
raise cherrypy.HTTPError(status = code)
error.exposed = True
def messageArg(self):
message = ("If you construct an HTTPError with a 'message' "
"argument, it wil be placed on the error page "
"(underneath the status line by default).")
raise cherrypy.HTTPError(500, message=message)
messageArg.exposed = True
import os.path
tutconf = os.path.join(os.path.dirname(__file__), 'tutorial.conf')
if __name__ == '__main__':
# CherryPy always starts with app.root when trying to map request URIs
# to objects, so we need to mount a request handler root. A request
# to '/' will be mapped to HelloWorld().index().
cherrypy.quickstart(HTTPErrorDemo(), config=tutconf)
else:
# This branch is for the test suite; you can ignore it.
cherrypy.tree.mount(HTTPErrorDemo(), config=tutconf)

View File

@@ -1,4 +0,0 @@
[global]
server.socket_host = "127.0.0.1"
server.socket_port = 8181
server.thread_pool = 10