Python/FAQ/CGI программирование

Материал из Wiki.crossplatform.ru

Перейти к: навигация, поиск
· Python ·

Содержание

[править] 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@@