Web development with CherryPy

Introduction

Note:

  1. 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 :-)
  2. 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:

  1. Download and unzip the latest stable version
  2. python setup.py install
  3. cd cherrypy/tutorial/
  4. python tut01_helloworld.py
  5. Aim your browser at http://localhost:8080

    If 8080 is already in use, edit to use another port.

Pros and Cons

Pros

Cons

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)

  1. Unpack the CherryPy package somewhere
  2. Create a directory somewhere (eg. C:\TMP)
  3. 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
  4. In the main directory, create a sub-directory hello/ (ie. C:\TMP\HELLO)
  5. From the CherryPy package, copy cherrypy.py into C:\TMP\HELLO
  6. 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>
     
  7. Still in C:\TMP\HELLO, run "python cherrypy.py hello.cpy" (without the quotes.) This generates helloServer.py
  8. Run this file with "python helloServer.py", which launches a web server on TCP 8000
  9. Aim your browser at http://localhost:8000
  10. 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="'&nbsp;&nbsp;'+color+'&nbsp;'"></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.

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:

  1. The user calls http://localhost/
  2. 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
  3. 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)
  4. 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)
  5. We leave appMenu(), so the relevant aspect part is called in PageLayout, which happens to closed the <div> section that contains the three links
  6. Next, we go back to index, running the instructions it actually contains, which happens to write "Welcome...." to the screen
  7. 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.

Response

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