|
Python/FAQ/CGI программирование
Материал из Wiki.crossplatform.ru
[править] Introduction
# Introduction
#
# There is no standard cgi/web framework in python,
# this is reason for ranting now and then.
#
# See `PyWebOff <http://pyre.third-bit.com/pyweb/index.html>`__
# which compares CherryPy, Quixote, Twisted, WebWare and Zope
# Karrigell and print stantements.
#
# Then there is Nevow and Standalone ZPT.
[править] Writing a CGI Script
# Partial implementation of PLEAC Python section 19.1
# Written by Seo Sanghyeon
# Standard CGI module is where PERL shines. Python
# module, cgi, is nothing but a form parser. So it is
# not really fair to compare these two. But I hesitate
# to introduce any non-standard module. After all,
# which one should I choose?
# I would stick to simple print statements. I believe
# the following is close to how these tasks are usually
# done in Python.
#-----------------------------
#!/usr/bin/env python
# hiweb - using FieldStorage class to get at form data
import cgi
form = cgi.FieldStorage()
# get a value from the form
value = form.getvalue("PARAM_NAME")
# print a standard header
print "Content-Type: text/html"
print
# print a document
print "<P>You typed: <TT>%s</TT></P>" % (
cgi.escape(value),
)
#-----------------------------
import cgi
form = cgi.FieldStorage()
who = form.getvalue("Name")
phone = form.getvalue("Number")
picks = form.getvalue("Choices")
# if you want to assure `picks' to be a list
picks = form.getlist("Choices")
#-----------------------------
# Not Implemented
# To implement -EXPIRES => '+3d', I need to study about
import cgi
import datetime
time_format = "%a, %d %b %Y %H:%M:%S %Z"
print "Expires: %s" % (
(datetime.datetime.now()
+ datetime.timedelta(+3)).strftime(time_format)
)
print "Date: %s" % (datetime.datetime.now().strftime(time_format))
print "Content-Type: text/plain; charset=ISO-8859-1"
#-----------------------------
# NOTES
# CGI::param() is a multi-purpose function. Here I want to
# note which Python functions correspond to it.
# PERL version 5.6.1, CGI.pm version 2.80.
# Python version 2.2.3. cgi.py CVS revision 1.68.
# Assume that `form' is the FieldStorage instance.
# param() with zero argument returns parameter names as
# a list. It is `form.keys()' in Python, following Python's
# usual mapping interface.
# param() with one argument returns the value of the named
# parameter. It is `form.getvalue()', but there are some
# twists:
# 1) A single value is passed.
# No problem.
# 2) Multiple values are passed.
# PERL: in LIST context, you get a list. in SCALAR context,
# you get the first value from the list.
# Python: `form.getvalue()' returns a list if multiple
# values are passed, a raw value if a single value
# is passed. With `form.getlist()', you always
# get a list. (When a single value is passed, you
# get a list with one element.) With `form.getfirst()',
# you always get a value. (When multiple values are
# passed, you get the first one.)
# 3) Parameter name is given, but no value is passed.
# PERL: returns an empty string, not undef. POD says this
# feature is new in 2.63, and was introduced to avoid
# "undefined value" warnings when running with the
# -w switch.
# Python: tricky. If you want black values to be retained,
# you should pass a nonzero `keep_blank_values' keyword
# argument. Default is not to retain blanks. In case
# values are not retained, see below.
# 4) Even parameter name is never mentioned.
# PERL: returns undef.
# Python: returns None, or whatever you passed as the second
# argument, or `default` keyword argument. This is
# consistent with `get()' method of the Python mapping
# interface.
# param() with more than one argument modifies the already
# set form data. This functionality is not available in Python
# cgi module.
[править] Redirecting Error Messages
# enable() from 'cgitb' module, by default, redirects traceback
# to the browser. It is defined as 'enable(display=True, logdir=None,
# context=5)'.
# equivalent to importing CGI::Carp::fatalsToBrowser.
import cgitb
cgitb.enable()
# to suppress browser output, you should explicitly say so.
import cgitb
cgitb.enable(display=False)
# equivalent to call CGI::Carp::carpout with temporary files.
import cgitb
cgitb.enable(logdir="/var/local/cgi-logs/")
# Python exception, traceback facilities are much richer than PERL's
# die and its friends. You can use your custom exception formatter
# by replacing sys.excepthook. (equivalent to CGI::Carp::set_message.)
# Default formatter is available as traceback.print_exc() in pure
# Python. In fact, what cgitb.enable() does is replacing excepthook
# to cgitb.handler(), which knows how to format exceptions to HTML.
# If this is not enough, (usually this is enough!) Python 2.3 comes
# with a new standard module called 'logging', which is complex, but
# very flexible and entirely customizable.
[править] Fixing a 500 Server Error
#
# download the following standalone program
#!/usr/bin/python
# webwhoami - show web users id
import getpass
print "Content-Type: text/plain\n"
print "Running as %s\n" % getpass.getuser()
# STDOUT/ERR flushing
#
# In contrast to what the perl cookbook says, modpython.org tells
# STDERR is buffered too.
[править] Writing a Safe CGI Program
# @@INCOMPLETE@@
# @@INCOMPLETE@@
[править] Making CGI Scripts Efficient
# use mod_python in the Apache web server.
# Load the module in httpd.conf or apache.conf
LoadModule python_module libexec/mod_python.so
<Directory /some/directory/htdocs/test>
AddHandler mod_python .py
PythonHandler mptest
PythonDebug On
</Directory>
# test.py file in /some/directory/htdocs/test
from mod_python import apache
def handler(req):
req.write("Hello World!")
return apache.OK
[править] Executing Commands Without Shell Escapes
import os
os.system("command %s %s" % (input, " ".join(files))) # UNSAFE
# python doc lib cgi-security it says
#
# To be on the safe side, if you must pass a string gotten from a form to a shell
# command, you should make sure the string contains only alphanumeric characters, dashes,
# underscores, and periods.
import re
cmd = "command %s %s" % (input, " ".join(files))
if re.search(r"[^a-zA-Z0-9._\-]", cmd):
print "rejected"
sys.exit(1)
os.system(cmd)
trans = string.maketrans(string.ascii_letters+string.digits+"-_.",
# @@INCOMPLETE@@
# @@INCOMPLETE@@
[править] Formatting Lists and Tables with HTML Shortcuts
#-----------------------------
# This uses nevow's (http://nevow.com) stan; there's no standard
# way to generate HTML, though there are many implementations of
# this basic idea.
from nevow import tags as T
print T.ol[T.li['red'], T.li['blue'], T.li['green']]
# <ol><li>red</li><li>blue</li><li>green</li></ol>
names = 'Larry Moe Curly'.split()
print T.ul[ [T.li(type="disc")[name] for name in names] ]
# <ul><li type="disc">Larry</li><li type="disc">Moe</li>
# <li type="disc">Curly</li></ul>
#-----------------------------
print T.li["alpha"]
# <li>alpha</li>
print T.li['alpha'], T.li['omega']
# <li>alpha</li> <li>omega</li>
#-----------------------------
states = {
"Wisconsin": [ "Superior", "Lake Geneva", "Madison" ],
"Colorado": [ "Denver", "Fort Collins", "Boulder" ],
"Texas": [ "Plano", "Austin", "Fort Stockton" ],
"California": [ "Sebastopol", "Santa Rosa", "Berkeley" ],
}
print "<TABLE> <CAPTION>Cities I Have Known</CAPTION>";
print T.tr[T.th('State'), T.th('Cities')]
for k in sorted(states.keys()):
print T.tr[ [T.th(k)] + [T.td(city) for city in sorted(states[k])] ]
print "</TABLE>";
#-----------------------------
# <TABLE> <CAPTION>Cities I Have Known</CAPTION>
#
# <TR><TH>State</TH> <TH>Cities</TH></TR>
#
# <TR><TH>California</TH> <TD>Berkeley</TD> <TD>Santa Rosa</TD>
#
# <TD>Sebastopol</TD> </TR>
#
# <TR><TH>Colorado</TH> <TD>Boulder</TD> <TD>Denver</TD>
#
# <TD>Fort Collins</TD> </TR>
#
# <TR><TH>Texas</TH> <TD>Austin</TD> <TD>Fort Stockton</TD>
#
# <TD>Plano</TD></TR>
#
# <TR><TH>Wisconsin</TH> <TD>Lake Geneva</TD> <TD>Madison</TD>
#
# <TD>Superior</TD></TR>
#
# </TABLE>
#-----------------------------
print T.table[
[T.caption['Cities I have Known'],
T.tr[T.th['State'], T.th['Cities']] ] +
[T.tr[ [T.th(k)] + [T.td(city) for city in sorted(states[k])]]
for k in sorted(states.keys())]]
#-----------------------------
# salcheck - check for salaries
import MySQLdb
import cgi
form = cgi.FieldStorage()
if 'limit' in form:
limit = int(form['limit'].value)
else:
limit = ''
# There's not a good way to start an HTML/XML construct with stan
# without completing it.
print '<html><head><title>Salary Query</title></head><body>'
print T.h1['Search']
print '<form>'
print T.p['Enter minimum salary',
T.input(type="text", name="limit", value=limit)]
print T.input(type="submit")
print '</form>'
if limit:
dbconn = MySQLdb.connect(db='somedb', host='server.host.dom',
port=3306, user='username',
passwd='password')
cursor = dbconn.cursor()
cursor.execute("""
SELECT name, salary FROM employees
WHERE salary > %s""", (limit,))
print T.h1["Results"]
print "<TABLE BORDER=1>"
for row in cursor.fetchall():
print T.tr[ [T.td(cell) for cell in row] ]
print "</TABLE>\n";
cursor.close()
dbconn.close()
print '</body></html>'
#-----------------------------
[править] Redirecting to a Different Location
#-----------------------------
url = "http://python.org/pypi"
print "Location: %s\n" % url
raise SystemExit
#-----------------------------
# oreobounce - set a cookie and redirect the browser
import Cookie
import time
c = Cookie.SimpleCookie()
c['filling'] = 'vanilla cr?me'
now = time.time()
future = now + 3*(60*60*24*30) # 3 months
expire_date = time.strftime('%a %d %b %Y %H:%M:%S GMT', future)
c['filling']['expires'] = expire_date
c['filling']['domain'] = '.python.org'
whither = "http://somewhere.python.org/nonesuch.html"
# Prints the cookie header
print 'Status: 302 Moved Temporarily'
print c
print 'Location:', whither
print
#-----------------------------
#Status: 302 Moved Temporarily
#Set-Cookie: filling=vanilla%20cr%E4me; domain=.perl.com;
# expires=Tue, 21-Jul-1998 11:58:55 GMT
#Location: http://somewhere.perl.com/nonesuch.html
#-----------------------------
# os_snipe - redirect to a Jargon File entry about current OS
import os, re
dir = 'http://www.wins.uva.nl/%7Emes/jargon'
matches = [
(r'Mac', 'm/Macintrash.html'),
(r'Win(dows )?NT', 'e/evilandrude.html'),
(r'Win|MSIE|WebTV', 'm/MicroslothWindows.html'),
(r'Linux', 'l/Linux.html'),
(r'HP-UX', 'h/HP-SUX.html'),
(r'SunOS', 's/ScumOS.html'),
(None, 'a/AppendixB.html'),
]
for regex, page in matches:
if not regex: # default
break
if re.search(regex, os.environ['HTTP_USER_AGENT']):
break
print 'Location: %s/%s\n' % (dir, page)
#-----------------------------
# There's no special way to print headers
print 'Status: 204 No response'
print
#-----------------------------
#Status: 204 No response
#-----------------------------
[править] Debugging the Raw HTTP Exchange
# download the following standalone program
#!/usr/bin/python
# dummyhttpd - start a HTTP daemon and print what the client sends
import SocketServer
# or use BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer
def adr_str(adr):
return "%s:%d" % adr
class RequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
print "client access from %s" % adr_str(self.client_address)
print self.request.recv(10000)
self.request.send("Content-Type: text/plain\n"
"Server: dymmyhttpd/1.0.0\n"
"\n...\n")
self.request.close()
adr = ('127.0.0.1', 8001)
print "Please contact me at <http://%s>" % adr_str(adr)
server = SocketServer.TCPServer(adr, RequestHandler)
server.serve_forever()
server.server_close()
[править] Managing Cookies
import Cookie
cookies = Cookie.SimpleCookie()
# SimpleCookie is more secure, but does not support all characters.
cookies["preference-name"] = "whatever you'd like"
print cookies
# download the following standalone program
#!/usr/bin/python
# ic_cookies - sample CGI script that uses a cookie
import cgi
import os
import Cookie
import datetime
cookname = "favorite-ice-cream" # SimpleCookie does not support blanks
fieldname = "flavor"
cookies = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE",""))
if cookies.has_key(cookname):
favorite = cookies[cookname].value
else:
favorite = "mint"
form = cgi.FieldStorage()
if not form.has_key(fieldname):
print "Content-Type: text/html"
print "\n"
print "<html><body>"
print "<h1>Hello Ice Cream</h1>"
print "<form>"
print 'Please select a flavor: <input type="text" name="%s" value="%s" />' % (
fieldname, favorite )
print "</form>"
print "<hr />"
print "</body></html>"
else:
favorite = form[fieldname].value
cookies[cookname] = favorite
expire = datetime.datetime.now() + datetime.timedelta(730)
cookies[cookname]["expires"] = expire.strftime("%a, %d %b %Y %H:00:00 GMT")
cookies[cookname]["path"] = "/"
print "Content-Type: text/html"
print cookies
print "\n"
print "<html><body>"
print "<h1>Hello Ice Cream</h1>"
print "<p>You chose as your favorite flavor \"%s\"</p>" % favorite
print "</body></html>"
[править] Creating Sticky Widgets
# @@INCOMPLETE@@
# @@INCOMPLETE@@
[править] Writing a Multiscreen CGI Script
# @@INCOMPLETE@@
# @@INCOMPLETE@@
[править] Saving a Form to a File or Mail Pipe
#-----------------------------
# first open and exclusively lock the file
import os, cgi, fcntl, cPickle
fh = open('/tmp/formlog', 'ab')
fcntl.flock(fh.fileno(), fcntl.LOCK_EX)
form = cgi.FieldStorage()
# This doesn't produce a readable file; we copy the environment so
# that we save a plain dictionary (os.environ is a dictionary-like
# object).
cPickle.dump((form, os.environ.copy()) fh)
fh.close()
#-----------------------------
import cgi, smtplib, sys
form = cgi.FieldStorage()
email = """\
From: %S
To: hisname@hishost.com
Subject: mailed form submission
""" % sys.argv[0]
for key in form:
values = form[key]
if not isinstance(values, list):
value = [values.value]
else:
value = [v.value for v in values]
for item in values:
email += '\n%s: %s' % (key, value)
server = smtplib.SMTP('localhost')
server.sendmail(sys.argv[0], ['hisname@hishost.com'], email)
server.quit()
#-----------------------------
# @@INCOMPLETE@@ I don't get the point of these:
# param("_timestamp", scalar localtime);
# param("_environs", %ENV);
#-----------------------------
import fcntl, cPickle
fh = open('/tmp/formlog', 'rb')
fcntl.flock(fh.fileno(), fcntl.LOCK_SH)
count = 0
while True:
try:
form, environ = cPickle.load(fh)
except EOFError:
break
if environ.get('REMOTE_HOST').endswith('perl.com'):
continue
if 'items requested' in form:
count += int(form['items requested'].value)
print 'Total orders:', count
#-----------------------------
[править] Program: chemiserie
# @@INCOMPLETE@@
# @@INCOMPLETE@@
|