from pyweb import *
instead of:import pyweb
Otherwise, your code will be full of pyweb
dot this and pyweb dot that
and a bunch of lexical pollution.page.intro = div(style="font-family:monospace")
page.body.add(page.intro)
page.intro = div()
page.body.add(page.intro)
page.intro.style = "font-family: monospace"
page.intro = div()
page.body.add(page.intro)
page.intro.add(attr(style="font-family: monospace"))
Attributes |
Type |
Description |
| tagopen, tagclose |
string |
The text name of the tag. Eg the
'table' tag has a value of 'table', so this attribute is used to
generate the '<table...>'
tag. If you're subclassing a tag, you need to explicitly set this
attribute to that of the tag you're subclassing. If the tag requires no
closing tag (eg <br>),
then the attribute notagclose
is
set. |
| content |
list |
This lists the content which
will be generated between the <tagname...>
and </tagname> tags. Items in this list can be strings or nested tag objects. |
| attr |
dict |
The attributes which will be
output as tag attributes (<tagname attr1="value1" attr2="value2"...>) No need to access this directly. If you set any attribute of the tag object, the attribute will be added to this dict. So "mytag.someattribute = somevalue" causes "mytag.attr[someattribute] = somevalue". There is an exception with the html tag class. Here, if you set an attribute, an attempt is made to decide whether the attribute belongs to the <head>...</head> part or the <body>...</body> part, and it is forwarded on to the head or body attributes (which are actually head and body tag objects. (Sorry if this confuses). |
| add |
method |
Call this to add one or more
items to the tag object's content. Each item you add should normally be a string or another tag object. But in addition to passing strings and tag objects, there are two other magickal entities:
Note also that the .add() method returns a ref to its tag object , which can offer some syntactic convenience for doing stuff on the fly. |
| set |
method |
Call this to totally replace the
content of the tag object. Anything previously stored in the content dict will be discarded, and
the arguments to this set()
call
added in its place. |
| setattr |
method |
A bit ancient, but some people
may want this to stay available. Pass it a bunch of keyword args to add
to the existing set of attributes. |
| attrdump |
method |
You don't need to call this
directly. This method is used as part of the rendering process (see
method render() below) to
convert the attr dict to a
string in the form attr1="val1"
attr2="val2"... |
| render |
method |
Recursively formats the tag, its
attributes, and its (tag and string) contents into real live HTML code. |
Entity |
Type |
Description |
| page | class pyweb.http |
The top-level document object. Typically, you won't instantiate more than one of these per web hit (unless you're into smart stuff like pickling page templates into disk files or databases). |
| page.head | class pyweb.head |
A tag object comprising the <head...> ... </head>
part of the document. |
| page.head.title |
class pyweb.head.title |
The 'title' attribute of the <head> tag. Defaults to "None so far" |
| page.head.style | class pyweb.style |
Builds and renders the embedded
stylesheet. (Sorry, but external stylesheets aren't supported yet.) A page.head.style object is created and added automatically when the pyweb.head object is created (and therefore, when any pyweb.html or pyweb.http objects are created). You can build up your stylesheet in several different ways:
Note that, for convenience, 'page.style' is available as shorthand for page.head.style. Examples:
|
| page.body |
class pyweb.body |
A tag object comprising the <body...> ... </body>
part of the document |
| page.send | method | Invokes page.render() (and the render() methods of any nested tag
objects) to create a string comprising the full HTML page, including
the
HTTP reply headers. Call this method when you've finished constructing the page, and exit the script immediately after the call. |
| page.session |
class pyweb.httpenv |
This doesn't get rendered in the
actual HTML, but gives easy convenient access to most aspects of the
whole http request/response cycle. For instance, HTTP environment
variables, cookies and form/url variables. You normally should never need to instantiate this yourself, as it gets automatically created within an http object on creation. Read on - there's lots of magickal stuff in this object. |
| page.session.env | dict |
This contains all the HTTP
environment headers, including: DOCUMENT_ROOT,
HTTP_HOST, HTTP_USER_AGENT, PATH, QUERY_STRING, REDIRECT_QUERY_STRING,
REMOTE_ADDR', 'REMOTE_PORT', REQUEST_METHOD, REQUEST_URI,
SCRIPT_FILENAME, SCRIPT_NAME, SERVER_ADDR, SERVER_NAME, SERVER_PORT,
SERVER_PROTOCOL, HTTP_X_FORWARDED_FOR
Obviously you can't set these (or it wouldn't have any effect if you did), but these headers contain all kinds of useful info about the client and his/her request. |
| page.session.fields |
class cgi.FieldStorage (acts like a dict) |
The fields which are passed in
the request, either within the URL (with 'http://site.com?var1=val1&var2=val2...'),
and/or as fields in a POSTed
form. Again, this is readonly, because nothing here will get sent to the client. |
| page.session.cookies |
class Cookie.SimpleCookie (acts somewhat like a dict) |
This is a read/write object.
Contains the cookies which are received from the client browser. Any
values remaining in this object at time of rendering will get sent to
the client. Note that in addition to setting values, as in 'page.session.cookies['cookie1'] = value1', you can also set cookie attributes such as 'expires', 'path', 'comment', 'domain', 'max-age', 'secure' and 'version'. For example: # Create a new cookie As per the http protocol, the cookies get sent back to the browser immediately before the usual Content-type: text/html header. Important - to retrieve the value of a cookie, you need to use the form: page.session.cookies['cookiename'].value Please don't interfere with any cookie with a name beginning with '__data'. These cookies are special (see page.session.data below). Security note - do not assume that cookies will come back in the form in which you set them. There are people who'll try to subvert the security of your server-side code through malformed cookies. So treat all incoming cookie values as if they might be deliberately tampered with. |
| page.session.data |
class pyweb._datastore |
This is one of the most
'automagickal' features of pyWeb. It's used for persistent per-client data storage, and offers much more flexibility and security than normal cookies. You add data to this object by simply setting attributes. When the page gets rendered, this data gets pickled, zlib-compressed, converted to base 64, cryptographically signed then written to one or more cookies of the name '__data[_nnn]' (but you don't even have to know about that). For example: page.session.data.timed_visited += 1
|
| page.session.error |
string |
If the client request and all
the fields and cookies were received successfully, and if the page.session.data was successfully
recreated, then this attribute will be set to an empty string. Otherwise, it will be set to one of the following values:
|
| page.session.setDataExpiry |
method |
Call this with the number of
seconds you want the datastore cookies (the persistent data in self.session.data) to remain. Note that this doesn't guarantee you the cookies will stay intact. The client (and/or client browser) may reject your cookies, or have a strict quota on cookie storage space (older cookies get deleted), or (vague possibility) some smart-assed website somewhere else might try tampering with your *own* cookies. |
| page.style |
instance of pyweb.style |
Shorthand for page.head.style (see above) |
| page.contentType |
string |
Defaults to 'text/html'. Determines the value of
the Content-Type: header
which is transmitted when the page is rendered out. You can set this to anything you like, which will prove necessary if your code is generating something like a tarball or graphic image on the fly, |
<table cellpadding="3" border="0" align="center" cellspacing="0">
<tr>
<td>first row</td><td>second row</td></tr>
</table>
#!/usr/bin/env python
from pyweb import *
http().send()
#!/usr/bin/env python
from pyweb import *
page = http()
page.title = "My first pyWeb page"
page.add("Hello, this is your first pyWeb-generated page")
page.send()
#!/usr/bin/env python
from pyweb import *
page = http()
page.title="My next pyWeb page"
page.add(h1("My next pyWeb page"))
page.add(h2("A subheading"))
page.add(p("A normal body text paragraph"),
p(attr(style="color:red"),
"Another paragraph in red text"))
page.send()
#!/usr/bin/env python
from pyweb import *
class colourtext(span):
def __init__(self, colour, *args):
# you need the special keywords 'tagopen' and 'tagclose' to
# make your custom tag object render properly.
span.__init__(self, tagopen="span", tagclose="span")
self.tagopen = 'span'
self.tagclose = 'span'
self.style = "color: %s" % colour
for item in args:
self.add(item)
page = http()
page.title = "My next pyWeb page"
page.add(h1("My next pyWeb page"))
page.add(h2("A subheading"))
page.add(p("A normal body text paragraph"),
p("Second paragraph with different colours: ",
colourtext("red", "red "),
colourtext("#0000C0", "blue "),
br(),
colourtext("#008000", "dark green")))
page.send()
#!/usr/bin/env python
from pyweb import *
page = http()
page.head.title = "pyWeb - Cookies demo"
page.add(h1("This page uses cookies"))
page.add(h2("A subheading"))
# Get username from form fields, if present.
if page.session.fields.has_key('username'):
# retrieve the form field, and stick it in a cookie
username = page.session.fields['username']
page.session.cookies['username'] = username # set the cookie
elif page.session.cookies.has_key('username'):
# not in form fields, but present in cookie
# Never forget to do the '.value' attribute fetch
username = page.session.cookies['username'].value
else:
# not present at all new visit - prompt user to enter their name
username = None
if username:
page.add(p("Welcome back, %s!" % username))
else:
page.add(form(attr(method="POST"),
"Please enter your name: ",
input(attr(type="text", name="username", size=40)),
input(attr(type="submit", value="Send")),
)
)
page.send()
#!/usr/bin/env python
from pyweb import *
page = http()
page.title = "pyWeb - Using the persistent datastore"
page.add(h1("Persistent Client-Side Datastore Demo"))
# Get username from form fields, if present.
hits = page.session.data.get('hits', 0)
page.add(p("You have visited this page %d times" % hits))
page.session.data.hits = hits + 1
page.send() # the updated data store gets sent back to client browser
#!/usr/bin/env python
from pyweb import *
page = http()
page.title = "pyWeb - logical object addressing"
page.body.add(h1("Example 7 - addressing tag objects logically"),
# makes this tag addressible as 'page.firstpara'
p(save(page, "firstpara"),
"This is the first sentence of firstpara."),
# make this next tag addressible as 'page.secondpara'
p(save(page, "secondpara"),
"This is the first sentence of secondpara."),
)
# with the objects thus 'tagged', you can address them willy-nilly
page.secondpara.add(" This is the second sentence of secondpara.")
page.firstpara.add(" This is another sentence for firstpara.")
page.send()
page.body.usernamefieldwith:
page.body.midtable.mainrow.midcell.loginform.usernamefieldThe former example takes advantage of the 'shortcutting' which save() offers, while the latter example is forced to go step by step through an unbroken lineage of ancestry from the top-level page object right down through the nested tags till it finds the field.
#!/usr/bin/env python
from pyweb import *
class swallowDataPage(http):
def __init__(self, coconuts):
http.__init__(self)
self.title = "pyWeb - addressing physically"
self.body.add(h1("Physical object addressing"),
# outer paragraph
p(tagid('para1'),
"An African Swallow can carry ",
span(tagid('num_coconuts')),
" coconuts"))
# go down the ancestry tree to add content
self.body.para1.num_coconuts.add("0.715")
page = swallowDataPage("0.715")
page.send()
#!/usr/bin/env python
"""
example9a.cgir
Demonstrates pyWeb using a database back-end.
This version uses the 'Metakit' database (http://www.equi4.com/metakit),
an increasingly popular alternative to traditional SQL-based databases.
The 'metakitplus' module is my own enhancement wrapper, which you can
get from http://www.freenet.org.nz/python/metakit
"""
from pyweb import *
import metakitplus
# function to create an edit/create form object
def editform(row, first, last, city):
# set row to -1 for creating a new row
return form(attr(method="POST"),
table(attr(cellspacing=0, cellpadding=5, align='center'),
tr(td(attr(align="right"), "First Name:"),
td(attr(align="left"),
input(attr(name='first', type='text',
value=first, size=64, maxlen=64)))),
tr(td(attr(align="right"), "Last Name:"),
td(attr(align="left"),
input(attr(name='last', type='text',
value=last, size=64, maxlen=64)))),
tr(td(attr(align="right"), "City:"),
td(attr(align="left"),
input(attr(name='city', type='text',
value=city, size=64, maxlen=64))))),
center(input(attr(type="submit", name="op", value="Save")),
input(attr(type="submit", name="op", value="Cancel"))),
input(attr(type="hidden", name="row", value=row)))
# function to display the contents of our db table in an html table
def displaytable(view):
t = table(attr(cellspacing=0, cellpadding=5, align="center", border=1),
tr(td(attr(colspan=4, align="center"), h3("Table contents"))),
tr(td(b("First Name")),
td(b("Last Name")),
td(attr(colspan=2), b("City"))))
nitems = len(view)
for idx in range(nitems):
item = view[idx]
t.add(tr(td(item.first), td(item.last), td(item.city),
td(form(attr(method="POST"),
input(attr(type="submit", name="op", value="Edit")),
input(attr(type="submit", name="op", value="Delete")),
input(attr(type="hidden", name="row", value=idx))
))))
t.add(tr(td(attr(colspan=4, align="center"),
form(attr(method="POST"),
input(attr(type="submit", name="op", value="Create New"))))))
return t
# declare table structure
dbfile = "example9.db"
tablename = "people"
format = "first:S,last:S,city:S"
schema = "%s[%s]" % (tablename, format)
# it's always wise to put your entire script in a try..except block so you
# can catch anything that goes wrong
try:
# open database
try:
db = metakitplus.storage(dbfile, 1)
except:
page = http()
page.title = "pyWeb example - error"
page.add(h1("pyWeb Example 9 - error"),
p("We can't open/create the metakit database file '%s'" % dbfile),
p("Please make the necessary changes to your directory and try again"))
page.send()
sys.exit(0)
# Open table if it exists, otherwise create it
if tablename in db.tables:
view = db.view(tablename)
else:
view = db.getas(schema)
# Create page object and fill in the basics
page = http()
page.title = "pyWeb database access"
page.bgcolor = "black"
# format it as a table within a table.
# if you don't like it, track me down and shoot me :)
page.add(table(attr(bgcolor="white", width="100%", height="100%",
cellspacing=0, cellpadding=5),
tr(td(attr(align="center", valign="middle"),
h1("Example 9a - using Metakit database"),
table(attr(bgcolor="#ffffe0", width="90%", height="90%",
cellspacing=0, cellpadding=5),
tr(td(save(page, 'table'),
attr(align="center", valign="middle"),
)))))))
# Decide action based on form variables
fields = page.session.fields # easier shorthand
op = fields.get('op', 'none')
if op in ['Edit', 'Delete', 'Save']:
try:
row = int(fields.row)
except:
row = -1
# Take required action
if op in ['new', "Create New"]:
# creating a new record
page.table.add(editform('', '', '', ''))
elif op == 'Edit':
# select a row for editing
page.table.add(editform(row,
view[row].first,
view[row].last,
view[row].city))
elif op == 'Save':
# saving data from a new or edit form
if row == -1:
# Save a whole new record
view.append(first=fields.get('first', ''),
last=fields.get('last', ''),
city=fields.get('city', ''))
page.table.add("New record added successfully",
displaytable(view))
elif row >= 0 and row < len(view):
# saving previously edited data
thisrow = view[row]
thisrow.first = fields.get('first', '')
thisrow.last = fields.get('last', '')
thisrow.city = fields.get('city', '')
page.table.add("Changes saved successfully",
displaytable(view))
else:
page.table.add("Invalid row: %d" % row,
displaytable(view))
elif op == 'Delete':
if row >= 0 and row <= len(view):
view.delete(row)
status = "Item deleted successfully"
else:
status = "Invalid row '%s'" % row
page.table.add(status,
displaytable(view))
# if table is empty, prompt to add content
else:
if len(view) == 0:
page.table.add(p("The database table is presently empty"),
form(attr(method='POST'),
input(attr(type='hidden', name='op', value='new')),
input(attr(type='submit', name='button',
value="Create New Record"))))
else:
page.table.add(displaytable(view))
# All done (hopefully)
db.commit(1)
page.send()
except:
# Log the exception to a log file
print_exception("example9.err")