Web development with CherryPy
Introduction
Note:
- Since I'm just beginning with CherryPy, most of the stuff below is
excerpts from the site's documentation. Too lazy to put references everywhere
:-)
- This article was originally written in 2004, before 0.9 came out. As
of July 2006, CherryPy is now 2.x.
TurboGears
Setup
Here's how to get up and running with CherryPy using its embedded HTTP server,
which is good enough for testing purposes:
- Download and unzip the latest stable version
- python setup.py install
- cd cherrypy/tutorial/
- python tut01_helloworld.py
- Aim your browser at http://localhost:8080
If
8080 is already in use, edit to use another port.
Pros and Cons
Pros
- Small, yet powerful
- Less abstract than Zope, closer to Python
- Simple but sufficient templating language
- Easier access to the source code as it lives on the filesystem instead
of inside the ZODB (ie. no need to use FTP or WebDAV to edit files with
one's favorite text editor, and source code can be watched with an SCM)
- Good documentation for newbies
- Includes its own web server, whose performance can be improved by allowing
it to spawn multiple processes or threads to handle more than one request
simultaneously. Makes it easier for newbies to take a quick look instead
of having to worry about making CherryPy work with a foreign web server
right from the start
- Come to think of it, it makes more sense to map sub-directories into
calls to methods located in classes, instead of scattering code into multiple
sub-directories on the filesystem.
In other words, users don't care
whether http://localhost/sub/index is a call to a file named index actually
located in the web server's docs/sub/, or just a call to a method named
index() in a class named Sub
Cons
- When working on a piece of code, it's a pain to have to kill the running
CherryPy web server, recompile, and relaunch the server every time a change
is made to the code. This can be eased a bit by using a script, eg. sthing
like:
python cherrypy.py %1%.cpy > log.txt
python %1%Server.py
> log.txt
Can't use "http://srv/sub/" instead of "http://srv/sub/index"
Supported in the up-coming release 0.9
- What about security, as opposed to Zope? Apparently, the only protection
si that routines in a "function" section are not callable, but
others are
- Since CherryPy is an application server, seems impossible to distribute
a CherryPy package to be included in an existing, legacy web server, ie.
plug CherryPy into one part of the site
Structure of a CherryPy source file
A Python script written in CherryPy consists in one or more sections:
- use SomeOtherSourceFile #If you need to share code between code between
several source files
-
- #An abstract class can only be derived. Useful to share stuff
- CherryClass Root abstract:
- aspect:
- #Add AOP stuff here
- variable:
- # Class variables here
- function:
- #One or more functions to handle logic
- mask:
- #One or more masks to handle display
in CHTL or CGTL
- view:
- #One or more views to link masks and functions
- # A view should always return a string
Note that a function cannot be "called" directly from a browser.
Only views and masks, which return some rendered data, can be "called"
directly.
CherryPy sits between an application server and a compiler. You write source
files, compile them with CherryPy and CherryPy generates an executable containing
everything to run the website (including an HTTP server)
Like a compiler, it reads input files and generates an executable. The executable
contains everything to run the website, including its own HTTP server.
Like an application server, it delivers highly-dynamic web sites that can
be linked to other ressources, like a database for instance.
In a server generated by CherryPy, every request from a client (for instance,
a browser requesting an URL) to a "directory" is transformed into a call to the method of a class
which corresponds to this directory. For instance, http://localhost/support/index
is actually a call to the method "index" in the class Support, which
can either be a stand-alone class, or (usually) derived from the Root class
where you will put stuff that is common among all parts of your application.
The parameters sent by the client become the arguments of the function.
Hello world (static)
- Unpack the CherryPy package somewhere
- Create a directory somewhere (eg. C:\TMP)
- From the CherryPy package, copy the src/ sub-directory (eg. C:\TMP\SRC;
Only needed to compile, but not needed to run a compiled server.) Later,
when you use additional modules via the "use" command, you will
also need to copy the lib/ directory into eg. C:\TMP\LIB
- In the main directory, create a sub-directory hello/ (ie. C:\TMP\HELLO)
- From the CherryPy package, copy cherrypy.py into C:\TMP\HELLO
- In C:\TMP\HELLO, create a file named hello.cpy with the following content:
CherryClass
Root:
mask:
def index(self):
<html><body>
Hello,
world !
</body></html>
- Still in C:\TMP\HELLO, run "python cherrypy.py hello.cpy"
(without the quotes.) This generates helloServer.py
- Run this file with "python helloServer.py", which launches
a web server on TCP 8000
- Aim your browser at http://localhost:8000
- To kill the running server, just close the DOS box
Thus, the URL http://localhost:8000/ triggers a call to the index method
of the Root class. This means that http://localhost/page and http://localhost/root/page
are perfectly equivalent.
The executable generated by CherryPy turns "CherryClass Root" into
an instance of the class with a lower-case first letter (ie. root). Therefore,
CherryClass names should always start with an upper case letter. This instance
is a global variable and can be acces from anywhere in the program.
Hello world (dynamic)
This example uses CHTL tags to add dynamic stuff. Create and compile the
following hello2.py file, and launch the program as previously explained:
- CherryClass Root:
- mask:
- def index(self, name="you"):
- <html><body>
- Hello,
<b py-eval="name"></b> !
- <form
py-attr="request.base" action="" method="get">
- Enter
your name: <input name=name type=text><br>
- <input
type=submit value=OK>
- </form>
- </body></html>
CHTL and CGTL
CherryPy comes with two templating languages: CHTL (CherryPy HTML
Templating Language; Web designers-friendly) and CGTL (CherryPy Generic
Templating Language; Cannot be edited safely with WYSIWYG HTML editors). CHTL
can be edited safely in a WYSIWYG HTML editor but is a bit more verbose than
CGTL, which can only be edited safely in a text editor.
Both CHTL and CGTL only use 6 tags: py-eval, py-attr, py-exec, py-code, py-if
(with py-else) and py-for. Note that you can NEVER use double-quotes inside
the Python code (even with a backslash before). If you need to put some double-quotes
in a string, use chr(34)
py-include
Used to insert the content of an external file. Very convenient to let designers
play with HTML, and keep the mask section tidy. Here's a sample in CGTL:
- CherryClass Root:
- mask:
- def index(self):
- <py-include="main">
The CHTL equivalent would be <div py-include="main">header</div>
.
Note: The external file must actually have the extension ".cpyt",
ie. although we're reffering to "main", this template must be named
"main.cpyt".
To pass parameters from a method in a mask section to a template (ie. a .cpyt
file containing CHTL or CGTL):
Caller (http://localhost/test/index)
- CherryClass Test:
- mask:
- def index(self,name='John'):
- <py-include="index">
Callee (http://localhost/test/index.cpyt)
- <py-eval="name">
py-eval
An example in CHTL: This is a long string with a <div py-eval="'variable'"></div>
in it.
In CGTL: <py-eval="2*2">
py-attr
<td py-attr="'red'" bgColor=""> is rendered as
<td bgColor="red">
py-exec
Used to execute one line of Python code
py-code
Used to execute a block of Python code. Here's an example in CHTL:
- <div py-exec="a=2"></div>
- <div py-code="
- if a==2:
- b=1
- else:
- b=2
- "></div>
Its CGTL equivalent:
- <py-exec="a=2">
- <py-code="
- if a==2:
- b=1
- else:
- b==2
- ">
py-if py-else
In CHTL:
- <div py-if="1==1">
- OK
- </div>
- <div py-else>
- Not OK
- </div>
In CGTL:
- <py-if="1==1">
- OK
- </py-if>
- <py-else>
- Not OK
- </py-else>
py-for
Note: In a py-for loop, CherryPy sets two handy special variables for you:
_index and _end. The former is an integer containing the current number of iterations
(from 0 to n-1). The latter contains the total number of iteration minus one.
in CHTL:
- <div py-for="i in range(10)">
- <div py-eval="i"></div>
- </div>
In CGTL:
- <py-for="i in range(10)">
- <py-eval="i">
- </py-for>
Here's a bigger sample in CHTL. The main page displays a link which is a
call to the embedded webColor() function. request is a global variable set by
CherryPy for each request of a client. It is an instance of a class with several
variables. One of them is called base and contains the base URL of the website:
- CherryClass Root:
- mask:
- def index(self):
- <html><body>
- <a
py-attr="request.base+'/webColors'"
href="">
- Click
here to see a nice table with all web colors
- </a>
- </body></html>
-
- def webColors(self):
- <html><body>
- <div
py-exec="codeList=['00', '33', '66', '99', 'CC', 'FF']"></div>
- <table
border=1>
- <div
py-for="r in codeList">
- <div
py-for="g in codeList">
- <tr>
- <div
py-for="b in codeList">
- <div
py-exec="color='#%s%s%s'%(r,g,b)"></div>
- <td
py-attr="color" bgColor="" py-eval="' '+color+' '"></td>
- </div>
- </tr>
- </div>
- </div>
- </body></html>
Masks, Functions, Views, Aspect
Here's an example to display a list of books in the form of links. When a
user clicks on one of the titles, the script returns the details of this book:
- CherryClass Root:
- variable:
- bookListData=[
- ('Harry Potter and the
Goblet of Fire', 'J. K. Rowling', '9$'),
- ('The flying cherry',
'Remi Delon', '5$'),
- ('I love cherry pie',
'Eric Williams', '6$'),
- ('CherryPy rules', 'David
stults', '7$')
- ]
-
- function:
- def getBookListData(self):
- return self.bookListData
-
- def getBookData(self, id):
- return self.bookListData[id]
-
- mask:
- def index(self):
- <html><body>
- Hi,
choose a book from the list below:<br>
- <py-for="title,
dummy, dummy in self.getBookListData()">
- <a
py-attr="'displayBook?id=%s'%_index" href="" py-eval="title"></a><br>
- </py-for>
- </body></html>
-
- def displayBook(self,
id):
- <html><body>
- <py-exec="title,
author, price=self.getBookData(int(id))">
- Details
about the book:<br>
- Title:
<py-eval="title"><br>
- Author:
<py-eval="author"><br>
- Price:
<py-eval="price"><br>
- </body></html>
Aspect
Aspect is based on AOP (Aspect oriented programming), which is a way to include
code at the beginning or at the end of each of the methods of all derived classes.
More info in Zen
and the Art of Aspect-Oriented Programming. Here's a sample:
- CherryClass NormalHeaderAndFooter:
- aspect:
- (1) start:
- _page.append("<html><body>I'm
the header<br><br>")
- (1) end:
- _page.append("<br><br>I'm
the footer</body></html>")
-
- #Root derived from NormalHeaderAndFooter
- CherryClass Root(NormalHeaderAndFooter):
- mask:
- def index(self):
- Hello,
world !
- def otherPage(self):
- I
love cherry pie !
Start and End indicate the parts that will be added before and after each
methods. The (1) is actually a condition, a way to include stuff only if the
condition is true; In this example, the header and footer sections are added
every time since the condition is always true (1). The condition can also be
a Python expression to limit this action to only some methods, if you wish.
In this case, the condition can include the type of methods (mask, view, variable)
and/or its name (the name of the method to which this aspect will apply.) For
instance, you can restrict a start aspect to such and such methods: function.name
in ('index', 'edit') start: .
Here's the previous sample with extra conditions:
- CherryClass NormalHeaderAndFooter:
- #This aspect part will not be included when calling yetAnotherPage()
- aspect:
- (function.type=='mask'
and function.name!='yetAnotherPage') start:
- _page.append("<html><body>I'm
the header<br><br>")
- (function.type=='mask'
and function.name!='yetAnotherPage') end:
- _page.append("<br><br>I'm
the footer</body></html>")
-
- CherryClass Root(NormalHeaderAndFooter):
- mask:
- def index(self):
- Hello,
world !
- def otherPage(self):
- I
love cherry pie !
- def yetAnotherPage(self):
- <html><body
bgcolor=red>
- I
love cherry pie !
- </body></html>
Here, the aspect parts will be added before and after all methods... except
yetAnotherPage(). If you get an "NameError: global name '_page' is not
defined" when using aspect sections, read this.
Aspect is thought to work with mask sections, not with views, but there is
way around this if you prefer to use views:
- CherryClass Base:
- mask:
- def header(self):
- #Outsourced
HTML to template
- <py-include="header">
-
- def mymain(self):
- <py-include="main">
-
- aspect:
- (function.type=='view')
start:
- myPage=[]
- myPage.append(self.header())
-
- CherryClass Root(Base):
- view:
- def index(self):
- myPage.append(self.mymain())
As Root is derived from Base, when the user hits http://localhost/, CherryPy
first executes the aspect section of Base (here, it adds a header to the page
by calling Base.header, and then executes Root.index which calls Base.mymain().
In the upcoming release 0.9, a new condition is function.isHidden,
which restricts the condition to ... well, hidden functions :-)
Forms
Rémi is currently working on CherryObject to make forms easier to use. In
the mean time, here's how to work with them.
The Form module defines four CherryClasses: FormField, FormSeparator, DefaultFormMask
(You’ll probably want to use your own masks for your own design), and Form.
Here's what those classes contain:
FormField
A FormField instance is used for each of the form fields.
function: __init__(label, name, typ, mask=None, mandatory=0, size=15, optionList=[],
defaultValue='', validate=None)
FormSeparator
A FormSeparator instance is used to display some text or images between the
different fields of the form.
function: __init__(label, mask)
DefaultFormMask
This CherryClass contains a default implementation of a mask for the fields.
You'll probably want to use your own masks for your own design. The next section
explains how to write your own field masks.
Here's how to customize how a text widget will be displayed:
- if field.typ=='text':
- result='%s: <input type=text name="%s"
value="%s" size="%s">'%(
- field.label, field.name,
field.currentValue, field.size)
- if field.errorMessage:
- result+=' <font color=red>%s</font>'%field.errorMessage
- return result+'<br>'
Form
The is the main CherryClass of the module. To create a form, you should declare
a CherryClass that inherits from Form.
- variable: method
- variable: enctype
- variable: fieldList
- function: formView(leaveValues=0)
- function: validateFields()
- function: setFieldErrorMessage(fieldName, errorMessage)
- function: getFieldOptionList(fieldName)
- function: getFieldDefaultValue(fieldName)
- function: setFieldDefaultValue(fieldName, defaultValue)
- function: getFieldNameList(exceptList=[])
- function: validateForm()
- view: postForm(**kw)
Here's a sample:
- CherryClass Test(Form):
- function:
- #Here, define the items
to build the form
- def __init__(self):
- headerSep=FormSeparator('',
defaultFormMask.defaultHeader)
- textField=FormField(label='Text field:',
name='textField', mandatory=1, typ='text')
- selectField=FormField(label='Select field:',
name='selectField', mandatory=1, typ='select', optionList=['Option 1', 'Option 2',
'Option 3'])
- submitField=FormField(label='',
name='Submit', typ='submit')
- footerSep=FormSeparator('',
defaultFormMask.defaultFooter)
- self.fieldList=[headerSep,
textField, selectField, submitField, footerSep]
-
- mask:
- # Here's the actual form
- def index(self, **kw):
- Here's the form:<br>
- <py-eval="self.formView(0)">
-
- view:
- #This view automatically called when user submits
form
- #Overwrite this view and add your own code to handle the form data.
- def postForm(self, **kw):
- if
self.validateForm():
- res="<html><body>"
- for
key, value in kw.items():
- res+="%s : %s <br>"%(key,
value)
- return
res+"</body></html>"
- else:
- return
"<html><body><font color=red>Fill out missing fields</font>"+self.formView(1)+"</body></html>"
Sharing common stuff
To share elements, build an parent class, and derive this class in child
classes. For instance:
- #######################
- CherryClass ParentClass abstract:
- #######################
- mask:
- def header(self):
- <html><body>
- <center>
- Here's
the header<p>
-
- def footer(self):
- and
here's the footer
- </center>
- </body></html>
-
- #######################
- CherryClass ChildClass(ParentClass):
- #######################
- view:
- def index(self):
- page=self.header()
- page+="<h1>Hello
World!</h1><p>"
- page+=self.footer()
- return page
-
- #######################
- CherryClass Root(ChildClass):
- #######################
If a project consists in mutliple but independent files, just compile them
into a single server: python ../cherrypy.py Hello1.cpy Hello2.cpy Hello3.cpy
. The resulting server will be named after the file source file, ie. Hello1Server.py,
here.
If a source file depends on another, you just need to add the "use"
keyword on the first line of the caller and compile the source files that do
not include the "use" keyword, eg. if "Main.cpy" includes
"use BoldTime", which is a reference to BoldTime.cpy, you just need
to compile the caller source using "python cherrypy.py Main.cpy".
Note that the "use" line must be on the very first line, with no comments
preceding it. As usual with CherryPy, the class name starts with an upper-case
letter (eg. BoldTime), but an instance of a class starts with a lower-case letter
(eg. boldTime.myfunc).
If "use"d source files are located in a different directory from
the caller scripts, either use the -I switch to tell CherryPy where to find
those additional files (eg. python cherrypy.py -I /modules Main.cpy) or create
and set an environment variable CHERRYPY_HOME (in which case, CherryPy will
also look in CHERRYPY_HOME/lib and CHERRYPY_HOME/src for modules.)
Customizing a navigation bar in each section
Here's a tip given by Scott Luck to customize a navigation bar in each section
of a site:
- CherryClass PageLayout hidden:
- aspect:
- ((function.type=='mask') and (not function.isHidden))
start:
- _page.append("""Header""")
- _page.append(str(self.appMenu()))
-
- ((function.type=='mask') and (not function.isHidden))
end:
- _page.append("""Footer""")
-
- ((function.type=='mask') and (function.name=="appMenu"))
start:
- _page.append("""<!--
Application Menu -->""")
- _page.append("""<div
class="appMenu">""")
- _page.append("""<a
href=\"""")
- _page.append(str(request.base
+ '/index'))
- _page.append("""\">Index</a>
| """)
-
- ((function.type=='mask') and (function.name=="appMenu"))
end:
- _page.append("""</div><!--
End Application Menu -->""")
-
- mask:
- def appMenu(self)
hidden:
- #If you want something
more than "Index" on the application menu bar
- #then you should override
this method in your CherryClass
- #Normally the appMenu
mask that you add will just be a list of links
- #The code below will
be added to your method by the aspect
- <!-- Application
Menu -->
- <div class="appMenu">
- <a py-attr="request.base
+ '/index'" href="">Index</a>
- </div>
- <!-- End Application
Menu -->
-
-
- CherryClass Root(PageLayout):
- mask:
- def appMenu(self)
hidden:
- <a href="menu">Menu</a>
| <a href="help">Help</a>
-
- def index(self):
- Welcome
to the Intranet Applications Page!<br><br>
Here's how it works:
- The user calls http://localhost/
- Root.Index() is called. Since it is a mask and is not hidden, this triggers
the first start/end aspect section in PageLayout which adds a header
- Next, the start part of this aspect section makes a call to appMenu(),
which triggers the second aspect section and adds a div section that includes
a first link (index)
- Since we overriden appMenu() in Root, this is the version that is called
instead of PageLayout.appMenu. In our version, we add a couple more links
(menu and help)
- We leave appMenu(), so the relevant aspect part is called in PageLayout,
which happens to closed the <div> section that contains the three
links
- Next, we go back to index, running the instructions it actually contains,
which happens to write "Welcome...." to the screen
- Finally, as we leave index(), the ad hoc aspect section in PageLayout
adds a footer
In other words, if you wish to customize the navigation bar in each section
of the site, you just need to derive the class from PageLayout, override its
appMenu() routine, and add the relevant links. Brilliant :-)
Subdirectories
When accessing sub-directories, while web servers look for items in sub-directories
on the filesystem, CherryPy just calls a routine in the form dir1_dir2_dir3.page(),
where dir1_dir2_dir3 is an class derived from the CherryPy class (or, rather,
since class declarations start with an uppercase letter and class instances
start with a lowercase letter, the class is Dir1_dir2_dir3 while the instance
is dir1_dir2_dir3), and page() is a routine located in this derived class.
When the URL only contains just the hostname with no appended reference to
a sub-directory (ie. http://localhost/), CherryPy calls root.index() implicitely.
In other cases, you must explicitely provide the name of the method you wish
to call, ie. CherryPy will not implicitely call index if you type the URL http://localhost/sub/
: In this case, you must provide http://localhost/sub/index, which corresponds
to sub.index().
Here's a sample which displays the familiar "Hello, world!" when
calling http://localhost/, and displays a form when calling http://localhost/sub/index:
- CherryClass Root:
- mask:
- def index(self):
- <html><body>
- Hello,
world !
- </body></html>
-
- CherryClass Sub:
- mask:
- def index(self, name="you"):
- <html><body>
- Hello,
<b py-eval="name"></b> !
- <form
py-attr="self.getPath()+'/index'" action=""
method="get">
- Enter
your name: <input name=name type=text><br>
- <input
type=submit value=OK>
- </form>
- </body></html>
As explained above, you must use "/index" instead of just refering
to the current "directory" sub/ because CherryPy will not call
index implicitely unless you are located at the root level of the site.
Configuration
To modify the default settings that a CherryPy server uses, just create a
configuration file in the same directory where the server lives, and name it
after the source file. For instance, if the source file is hello/Hello.cpy,
the resulting server is HelloServer.py; In this case, its configuration file
is HelloServer.cfg. Use the -C switch to use a different configuration file,
eg. "python HelloServer.py -C /dir1/dir2/myConfigFile.cfg".
Using a different port number
- [server]
- socketPort=80
Some other options are available in the [server] section of the config file.
Check out the "Deploying your website for production" chapter for
more information about the different options.
Serving static contents
- [staticContent]
- static=/home/remi/static
- data/images=/home/remi/images
Using this configuration, http://localhost/static/styleSheet.css tells the
server to return /home/remi/static/styleSheet.css, while http://localhost/data/images/girl.jpg
servers /home/remi/images/girl.jpg.
Note: Under Windows, use short filenames, eg. static=d:\Documents\CODESN~1\Python\cherry\hello\static
To let Apache serve static content: http://sourceforge.net/mailarchive/message.php?msg_id=4190130
Accessing custom sections from a configuration file
If the configuration file includes the following:
- [user]
- name=Remi
-
- [database]
- login=remiLogin
- password=remiPassword
those tuples can be access thus:
- CherryClass Root:
-
- view:
- def index(self):
- <html><body>
- Hello,
<py-eval="configFile.get('user','name')"><br>
- to
connect to the database, you should use:<br>
- <py-eval="'Login:%s,
Password:%s'%(configFile.get('database','login'),
configFile.get('database','password'))">
- </body></html>
Security
http://sourceforge.net/mailarchive/message.php?msg_id=4071518
CherryPy variables and functions
Variables
Request
This class instance contains all the informations about the request that
was sent by the client.
- request.headerMap: request.headerMap['user-agent']
- request.cookieMap: (this information is also available through request.headerMap['cookie'])
- request.base: Using "http://localhost:8000/dir/page?key1=value1&key2=value2",
returns 'http://localhost:8000' (equivalent to 'http://'+request.headerMap['host'])
- request.path: Using "http://localhost:8000/dir/page?key1=value1&key2=value2",
returns 'dir/page'
- request.paramMap: Using "http://localhost:8000/dir/page?key1=value1&key2=value2",
returns {'key1': 'value1', 'key2': 'value2'}
- request.originalPath: (See next item)
- request.originalParamMap: Copies of request.path and request.paramMap,
but unlike those, originalPath and originalParamMap cannot be modified
- request.browserUrl: The URL as it appears in the browser window
- request.method: Either GET or POST
- request.rawHeader: Raw header as it was sent by the client
Response
- response.headerMap: All keys and values that will be sent in the header
of the response. Those can be changed. By default, CherryPy sets the following
keys and values in the map:
"status": 200
"content-type":
"text/html"
"server": "CherryPy 0.1"
"date":
current date
"set-cookie": []
"content-length":
0
- response.body: Body of the response; Can only be used in 3 special
functions
Functions
In your code, you can define special functions that will change the server's
behavior. To define these functions, just use Python's regular syntax and define
them outside all CherryClasses. When you use different modules, you can define
the same function in different modules. In this case, CherryPy will just concatenate
the bodies of all functions, in the same order it reads the files.
initServer
Called by the server during initialization.
initRequest
initNonStaticRequest
initResponse
initNonStaticResponse
For those functions, see doc/tut/node16.html, section 14.2.2
onError
Deploying CherryPy
Low traffic
By default, CherryPy's web server cannot handle concurrent requests, but
it can be configured to fork a new process to handle every incoming request
or launch a fixed number of processes when started (not available under Windows),
or create a new thread (available under Windows.) You can also consider load
balancing.
Here are the relevant settings in the configuration file:
- [server]
- #Those options are mutually exclusive...
- socketPort=80
- socketFile=/tmp/mySocket.soc
-
- #Either use forking or threading, but not both...
- forking=1
- fixedNumberOfProcesses=10
- threading=1
-
- #To have an SSL server. See the relevant HowTo
- sslKeyFile
- sslCertificateFile
-
- #To have an XML-RPC server. See the HowTo
- xmlRpc
High traffic
To deploy CherryPy behind another web server, there is a HowTo in the documentation
that explains how to set this up. Says Rémi: "If you're concerned about
the server crashing once in a while, you can set it up to run behind Apache
via PersistentCGI and configure the PersistentCGI script to automatically restart
the CherryPy server if it detects that is is not running anymore."
Standard Modules
Make sure the lib/ directory lives at the same level where the script lives,
eg. c:\tmp\myscripts\sendmail.cpy and c:\tmp\lib. Do not name this script mail.cpy
as it will create an error at compile time (infinite loop). Like the src/ directory,
/lib is only needed to compile, but not to run a compiled server.
Mail
- use Mail
-
- CherryClass MyMail(Mail):
- function:
- def __init__(self):
- self.smtpServer='mail.acme.com'
-
- CherryClass Root:
- mask:
- def index(self):
- <py-exec="myMail.sendMail('jane.doe@acme.com',
'john.doe@acme.com', '', 'text/plain', 'Hello', 'Hello,\nthis is me')">
- <html><body>
- Hi,
I just sent an e-mail to you@yourhost.com
- </body></html>
HttpAuthenticate
The following takes advantage of the HttpAuthenticate and CookiAuthenticate
classes so you just need to derive their getPasswordListForLogin() method to
let those classes handle authentication:
- use HttpAuthenticate
-
- CherryClass Root(HttpAuthenticate):
- function:
- def getPasswordListForLogin(login):
- if login=='mySecretLogin':
return ['mySecretPassword']
- return []
-
- mask:
- def index(self):
- <html><body>
- Hello.
I see you know the secret login and password ...
- </body></html>
-
- def unauthorized(self):
- <html><body>
- Hey
dude, get out ! You're not allowed here if you don't know the login/password
- </body></html>
CookieAuthenticate
doc/html/lib/node9.html
Forms
An exemple is provided in the demo that come's with CherryPy, and the CherryPy
Library Reference has some documentation about this module.
MySQL
doc/html/lib/module-MySql.html . Note that you need to have the MySQLdb
Python package installed.
Here's a sample that connects to the local MySQL server, uses the "mydb"
database which contains the "clients" table which has two columns:
- use MySql
-
- CherryClass MyDb(MySql):
- function:
- def __init__(self):
- self.openConnection('localhost',
'root', '', 'mydb')
-
- CherryClass Root:
- mask:
- def index(self):
- <html><body>
- <table
width=100% border=1>
- <tr>
- <th>Field1</th>
- <th>Field2</th>
- </tr>
- <py-for="field1,field2
in myDb.query('select * from clients')">
- <tr>
- <td><py-eval="field1"></td>
- <td><py-eval="field2"></td>
- </tr>
- </py-for>
- </table>
- </body></html>
Here's a sample to query a database, and display only one column through
a mask section:
- CherryClass Test:
- mask:
- def index(self):
- <py-exec="table
= myDb.query('select name from kids')">
- <!--
table is a two-dimension table, with rows and columns -->
- <py-for="row
in table">
- <py-eval="row[0]"><br>
- </py-for>
MaskTools
doc/html/lib/module-MaskTools.html
Q&A
How come more than one process can be launched on TCP 8000?
By mistake, I let a running CherryPy server on 8000, compiled and ran a second
one, with no error. How come?
What are hidden sections/class?
By design, functions are not callable through a URL, but you might also want
to forbid users from calling other sections, or even an entire class directly:
That's what the "hidden" keyword is used for.
Here's a sample that forbids the user from accessing the hidden mask through
http://localhost/commonMasks/redLabel?label=test :
- CherryClass CommonMasks:
- mask:
- def redLabel(self, label) hidden:
- <b><font color=red
py-eval="label"></font></b>
-
- CherryClass Root:
- mask:
- def index(self):
- <html><body>
- Hello,
<py-eval="commonMasks.redLabel('world')">
- </body></html>
Here's an example to hide an entire class:
- CherryClass CommonMasks hidden:
How to get the complete URL to the current class?
self.getPath(): by default, CherryPy creates a method called getPath for
each CherryClass. This method returns the URL of the CherryClass.
In a form with multiple submit buttons, how to tell which one was pressed?
- if kw.has_key('cmdChildCreate'):
- myPage.append("<td>\nYou have select
" + kw['cmdChildCreate'] + "\n</td>\n")
How to combine views and masks correctly?
Depending on which button the user clicked in a form, I need to display a
different action script. Don't know what the best way to do this:
- view:
- def action(self, **kw):
- if
kw.has_key("cmdCreate"):
- return
kw["cmdCreate"]
- elif
kw.has_key("cmdView"):
- res="<html><body>"
- res+="<td
width=80% valign='top' align='left' class='newsText'>"
- res+="<table
border=1>"
- etc....
Is there a way to call a mask from a view?
My code looks OK, but I get a syntax error
From the tutorial: "CherryPy’s policy is that one tab should be used
to indent a block (it’s easier to hit once the TAB key rather than 4 times the
SPACE key). However, we know that some people have some reasons not to use tabs
(for instance, sending a program with tabs by email or posting it to a newsgroup
doesn’t work very well). Therefore, it is also possible to use whitespaces to
indent blocks in CherryPy’s source files. By default, CherryPy will expect 4
whitespaces for one indentation. If you use something else (for instance, 3
whitespaces), then you have to use the -W 3 option to tell CherryPy about it.
The way it works is very simple: when it reads a file, CherryPy’s preprocessor
starts by replacing all occurences of 4 whitespaces (or 3, depending on the
-W option) with one tab. Please note that, unlike Python, one tab can never
correspond to 2 indentation levels. It always corresponds to one indentation
level."
I'll add to this, that a syntax error in one section can have its cause lying
elsewhere. For instance, a missing ) in the class below caused a syntax error
in the aspect section in the Base class from which it was derived:
- CherryClass Base abstract:
- aspect:
- (function.type=='view')
start:
- myPage=[]
- myPage.append(self.header())
- myPage.append(self.nav())
-
- (function.type=='view')
end:
- myPage.append(self.footer())
- return
'' . join(myPage)
- CherryClass Root(Base):
- view:
- def index(self):
- parents=[]
- for
row in myDb.query('select * from parent'):
- for
col in row:
- parents.append(col)
- myPage.append(self.Create(parents)
Compiling this generates the following hard-to-find error:
- File "clshServer.py", line 718
- myPage.append(self.footer())
- ^
- SyntaxError: invalid syntax
I need to include more than one "use" line
use Form,MySql
What's AOP?
http://www.cherrypy.org/static/html/howto/node12.html
In Aspect, why does an excluded function still get handled?
In Base class, I have the following:
- aspect:
- (function.type=='view'
and function.name!='index') start:
Still, Root.index gets to use Base's aspect stuff.
CherryPy generates .py files: Can customers see the source?
Is it an issue if the source files do not start with an uppercase letter?
The documentation for the py-code part says : "If you want to render
some data inside the Python code, you must append it to the _page variable".
Why the need for _ ?
- <html><body>
- Integers from 0 to 9:
- <div py-code="
- for i in range(10):
- _page.append("%s
"%i)
- "></div>
- </body></html>
Why masks and versions?
Masks let you just copy/paste HTML, with or without any embedded CHTL/CGTL
code, while versions require you to put the HTML code into a variable, eg.
- page = "<html>"
- page+= "</html>"
As we saw in section 2.3, CherryPy source files are made of class and
method declarations. So how to include templates in these source files? The
solution I chose was to add another keyword before method definitions to specify
whether what's coming up is a regular python method or a method using a templating
language. The former methods are called views; the latter ones are called
masks.
So what's the difference between a function and a view if both are written
using regular python? The difference is that views correspond to a URL and should
return a python string containing some rendered data pulled from the file the
URL links to. Functions are not directly accessible with a URL and they can
return anything they want. Let's look for instance at the following actual CherryPy
code, which shows all three of its special structures within a CherryClass.
Should index be located in mask or version?
Why initRequest/initNonStaticRequest, initResponse/initNonStaticResponse?
No way to have a single function to handle request/response?
Is there a way to implement acquisition à la Zope?
If CherryPy does not provide Zope-like acquisition,
how can we share objects between pages that are located in deeper sub-directories
(eg. navigation bar, etc.)?
You can just derive your CherryClasses like you would derive a normal class in
python ... Ie. if you want to share common stuff with methods in other classes,
just derive their class from the Root class where this common stuff will be
defined.
Any way to have CherryPy call Root.index if other classes don't implement
it?
Just derive those
classes from Root
Any way to have CherryPy call Myclass.index when calling http://localhost/myclass/
?
Supposed to be available
in release 0.9. In the mean time, you can use this:
- def initNonStaticRequest():
- if request.path in ('studio','writing','resume',
...):
- request.path+='/index'
Any way to use index.html instead of just index?
guess not : The default
object in Zope is index_html
What features will CherryObjects offer?
http://cherrypy.org/myForum/viewThread?threadId=15
A mask includes links to other objects but CherryPy can't find them
In the following snippet, theme.css lives in the same directory where the
generated server lives, but CherryPy can't find it (no error, the file is just
ignored):
- CherryClass Root:
- mask:
- def index(self):
- <html>
- <head>
- <link
rel="stylesheet" type='text/css' href="theme.css"/>
- <title>CLSH</title>
Path to static stuff must be set in the configuration file.
CherryPy vs. Zope?
http://sourceforge.net/mailarchive/forum.php?thread_id=1990353&forum_id=9986
CherryPy vs. Quixote?
http://sourceforge.net/mailarchive/message.php?msg_id=5506255
The mailing list archives over at SourceForge are displayed as one big HTML
document
In the main page,
select Threaded instead of the default Nested.
Resources