%include "default.mgp"
%page
%center, size 10
Introduktion till
mod_python och HTMLgen
%size 5
(Eller: Bah!)
%size 7
Erik Forsberg
%size 6, font "typewriter"
forsberg@lysator.liu.se
%bar "#dee4c4", vgap 10
%page
Innehåll
Inledning: CGI i Python
Det vanliga sättet, cgi-modulen och #!
Det mindre vanliga sättet, fastcgi.
Ett jätteintegrerat sätt, Zope
Ett annat mindre vanligt sätt, mod_python
(H)u(T)tu(M)ul(L)e-generering med python
%page
CGI i Python
CGI är inte Perl :-)
Skriv ut Lite headers (Content-Type framförallt) och sen vad du vill skriva.
Kör ditt skript som vanligt med #!/usr/bin/env python
Minimalt exempel:
%font "typewriter", size 3
#!/usr/bin/env/python
print "Content-Type: text/plain\n"
print "Hello, World!"
%font "standard", size 5
På Lysator: Kalla saken för *.cgi och låt det vara exekverbart, samt ha rätt path till Python..
%page
Det vanliga sättet, cgi-modulen och #!
Exempel
%font "typewriter", size 3
#!/usr/bin/env python
import cgi
form = cgi.FieldStorage()
if not form.has_key("name"):
print "
Error
"
print "Please fill in the name"
return
print "
name:", form["name"].value
...further form processing here...
%font "standard", size 5
Ett gäng funktioner för att parsa indata på ett mer "kontrollerat" sätt finns.
Se referensen.
%page
Det mindre vanliga sättet, fastcgi.
En process som anropas via en socket från oebbservern
Du slipper spawna process för varje anrop.
Det är betydligt enklare att hålla i ett state
Öppen standard. Går att köra på flera oebbservrar.
Exempel på applikation: WebKOM
http://www.fastcgi.com
%page
Ett jätteintegrerat sätt, Zope
Ämne för en framtida UppLYSning (?)
http://www.zope.org/
%page
Ett annat mindre vanligt sätt, mod_python
Modul för Apache
Varje Apacheserver har en Pythoninterpretator i sig
Process behöver inte spawnas för varje anrop.
Databasanslutningar etc. och vissa data finns kvar mellan anrop
http://www.modpython.org
%page
Installation av mod_python
Kompileras och installeras som alla andra Apache-moduler.
Finns som Debian-paket.
Fungerar med Python 2.1
%page
Konfiguration av mod_python
Enkelt exempel
%font "typewriter", size 3
# srm.conf
Alias /modpyex/ /home/erik/dev/python/modpyex/
Options -Indexes
SetHandler python-program
PythonHandler ex
%font "standard", size 5
Ger ungefär samma funktionalitet som ett CGI-script.
%page
Exempel på script
%font "typewriter", size 3
from mod_python import apache
def handler(req):
req.contenttype = "text/plain"
req.send_http_header()
req.write("Hello, world!")
return apache.OK
%font "standard", size 5
%page
Objekt
Requestobjekt
Skickas med som argument till handlern vid varje request
Metoder och medlemmar som ger information om requesten.
add_handler() kan dynamiskt bestämma vad som ska hända efter den nuvarande handlern.
get_basic_auth_pw() hämtar info från 'Basic authentication'
get_remote_host() hämtar info om klienten (IP/hostnamn)
read() läser data från klienten (ex.vis POST)
method innehåller metoden som användes vid requesten.
headers_in innehåller alla headers som klienten sände
unparsed_uri innehåller oparsad URI
uri innehåller path-delen av URI.
connection är ett Connection-objekt
server är ett Server-objekt.
%page
Requestobjekt, fortsättning
Metoder och medlemmar som används för att skicka tillbaka info till klienten.
send_http_header() skickar headers. Måste köras före write()
write() skriver data till klienten.
headers_out innehåller alla headers som kommer att sändas när send_http_header() körs.
content_type sätter man till vad datat har för typ, innan send_http_header()
%page
Objekt, fortsättning
Connectionobjekt
Innehåller information om anslutningen som används för just den här requesten
local_addr, remote_addr, keepalives, etc..
Mer finns, se mod_pythons dokumentation.
Serverobjekt
Refererar till servern som anropet kom till
register_cleanup() registrerar ett anrop som körs när just det här serverbarnet dör.
Info om serverns hostname, administratör, port, keep-alive-inställningar etc..
Se mod_pythons dokumentation.
%page
Handlers
Ger olika hakar i requestens gång
PythonPostReadRequestHandler
PythonTransHandler
PythonHeaderParserHandler
PythonAccessHandler
PythonAuthenHandler
PythonTypeHandler
PythonFixupHandler
PythonHandler
PythonInitHandler
PythonLogHandler
PythonCleanupHandler
Standardhandlers
%page
Handlers, fortsättning
PythonPostReadRequestHandler
Körs precis efter att requesten blivit läst
Används ex.vis för att avgöra vad som ska köras baserat på headers
Typisk sak att göra: Sätt en handler med add_handler()
Kan ej existera i eller .htaccess
PythonTransHandler
Körs efter PythonPostReadRequstHandler
Används för att översätta URI till riktigt filnamn
Kan ej existera i eller .htaccess
PythonHeaderParserHandler
Körs efter PythonTransHandler
Ungefär som PythonPostReadRequestHandler, men nu vet vi vart requesten ska.
%page
Handlers, fortsättning
PythonAccessHandler
Körs efter PythonHeaderParserHandler
Används ex.vis för att kolla om ett visst IP är tillåtet för just den här requesten.
Fånigt Exempel
%font "typewriter", size 3
def accesshandler(req):
apache.log_error("PythonAccessHandler now running, remote IP = %s" % req.connection.remote_ip)
if "207.46." == req.connection.remote_ip[0:7]: # Disallow some MS employees..
return apache.HTTP_FORBIDDEN
else:
return apache.OK
%font "standard", size 5
%page
Handlers, fortsättning
PythonAuthenHandler
Körs efter PythonAccessHandler
Avgör om användaren i andra änden är tillåten, med Basic Auth.
Kräver givetvis att Authentifiering är påslaget i Apache.
Exempel:
%font "typewriter", size 3
def authenhandler(req):
apache.log_error("PythonAuthenHandler now running")
pw = req.get_basic_auth_pw()
user = req.connection.user
if user == "RING" and pw == "IKEA":
return apache.OK
else:
return apache.HTTP_UNAUTHORIZED
%font "standard", size 5
Smartare är givetvis att glo i nån slags databas efter rätt data.
%page
Handlers, fortsättning
PythonTypeHandler
"This routine is called to determine and/or set the various document
type information bits, like Content-type (via r->content_type),
language, et cetera"
Konstig!
%page
Handlers, fortsättning
PythonFixupHandler
Körs före content-handlern, men efter authentifiering.
Kan användas för att fixa headers eller motsvarande så att de följer någon slags standard.
Kunde inte komma på något bra exempel :-)
%page
Handlers, fortsättning
PythonHandler
Används för att ta göra nått vettigt :-)
GrundTODO:
Läs vad som ska göras i req
Skicka headers med send_http_header()
Skriv saker med req.write()
Returnera rätt returvärde
apache.OK
apache.HTTP_INTERNAL_SERVER_ERROR
apache.HTTP_NOT_FOUND
...
Klart!
%page
PythonHandler, fortsättning
Ett fakultetsexempel:
%font "typewriter", size 3
def handler(req):
form = util.FieldStorage(req)
apache.log_error("PythonHandler now running")
req.contenttype = "text/html"
req.send_http_header()
if form.has_key("fakof"):
fakof = int(form["fakof"])
req.res = 1
while fakof:
req.res*=fakof
fakof-=1
req.fakof = int(form["fakof"])
req.write("fak(%d) = %d" % (req.fakof, req.res))
else:
req.write("Please give me a value as the parameter 'fakof'")
return apache.OK
%font "standard", size 5
%page
Handlers, fortsättning
PythonLogHandler
Loggar saker efter svaret på requesten
Lagom fånigt exempel:
%font "typewriter", size 3
def loghandler(req):
try:
apache.log_error("Calculated fak(%d) which is %d" % (req.fakof, req.res))
except AttributeError:
apache.log_error("No value was submitted for calculation")
return apache.OK
%font "standard", size 5
Notera vidareskickning av data i req
%font "typewriter", size 3
[Sat Sep 22 16:29:09 2001] [error] Calculated fak(12) which is 479001600
%font "standard", size 5
%page
Handlers, fortsättning
PythonCleanupHandler
Anropas absolut sist, innan requestobjektet förstörs av apache
Returvärdet spelar ingen roll, till skillnad från alla andra Handlers.
Används kanske för att låsa upp lås eller motsv.
%page
Standardhandlers
CGI Handler - emulerar CGI-miljön för att köra gamla script under mod_python
Zpublisher - knyckt från Zope.
%page
Standardhandlers, fortsättning
Publisher handler - smidigt sätt att göra många funktioner tillgängliga
Givet följande Apache-konfiguration:
%font "typewriter", size 3
Alias /modpypub/ /home/erik/dev/python/modpypub/
SetHandler python-program
PythonHandler mod_python.publisher
%font "standard", size 5
..och följande kod i form.py
%font "typewriter", size 3
def say(req, what="Nothing"):
return "I am saying %s" % what
%font "standard", size 5
..så kommer en request till /modpypub/form/say ge..
%font "typewriter", size 4
"I am saying Nothing"
%font "standard", size 5
%page
Andra Apachekonfigurationsdirektiv
PythonEnablePdb
debugga moduler i Pdb
PythonDebug
Skicka ofångade Exceptions även till klienten
PythonImport
importera moduler en gång för alla.
OBS: Körs innan all konfiguration är klar. Undvik.
%page
Apachedirektiv, fortsättning..
PythonInterpPerDirectory
En subinterpretator per directory
Default: En per virtuell server
PythonInterpPerDirective
En subinterpretator per Directive
PythonInterpreter
bestäm vilken subinterpretator som ska användas
%page
Apachedirektiv, fortsättning..
PythonHandlerModule
minimera antalet Handler-Directives
PythonAutoReload - kolla om filen har ändrats sedan sista requesten
Typiskt bra vid utveckling. Ändra i filen, gör reload och se resultatet.
Funkar inte för moduler, men det går att gå runt. Såhär:
%font "typewriter", size 3
DEBUG = 1
if DEBUG:
modpydb = reload(modpydb)
%font "standard", size 5
Nu laddas modpydb om varje gång en request utförs. Stängs lämpligen av i släppt kod.
%page
Apachedirektiv, fortsättning.. (forts.)
PythonOptimize - slå av/på optimering
PythonOption - ge parametrar som kan hämtas med req.get_options()
Användbar ex.vis vid många virtuella servrar
PythonPath - sätt Pythons sys.path
OBS: Byter ut den.
%font "typewriter", size 3
PythonPath "sys.path+['/mydir']"
%font "standard", size 5
%page
mod_python.util
FieldStorage
%font "typewriter", size 3
form = util.FieldStorage(req)
form.has_key("fakof"):
form["fakof"]
%font "standard", size 5
som cgi.FieldStorage, fast med dictionaryinterface
Field
Används i princip bara vid filuppladdningar
parse_qs
Tolka en application/x-www-form-urlencoded, returnera dictionary
parse_qsl
Tolka en application/x-www-form-urlencoded, returnera lista name/value.
%page
Ett exempel på lagom bra databasaccess
%font "typewriter", size 3
import MySQLdb
import string
from constants import DB, USER, PASS, UNIX_SOCKET
def _db_login(relogin = 0):
"""Login to the database
"""
global DB_CONN
if relogin:
DB_CONN = MySQLdb.connect(db=DB, user=USER, passwd=PASS,
unix_socket=UNIX_SOCKET)
return DB_CONN
else:
try:
d = DB_CONN
return d
except NameError:
DB_CONN = MySQLdb.connect(db=DB, user=USER, passwd=PASS,
unix_socket=UNIX_SOCKET)
return DB_CONN
%font "standard", size 5
%page
modpydb.py fortsättning..
%font "typewriter", size 3
def run_sql(sql, param=None, n=0, with_desc=0):
""" Runs SQL on the server and returns result
"""
db = _db_login()
if param:
param = tuple(param)
try:
cur = db.cursor()
rc = cur.execute(sql, param)
except:
# it's possible that mysql connection
# timed out. Try one more time.
db = _db_login(relogin = 1)
cur = db.cursor()
rc = cur.execute(sql, param)
%font "standard", size 5
%page
modpydb.py fortsättning..
%font "typewriter", size 3
if string.upper(string.split(sql)[0]) in \
("SELECT", "SHOW", "DESC", "DESCRIBE"):
if n:
recset = cur.fetchmany(n)
else:
recset = cur.fetchall()
if with_desc:
return recset, cur.description
else:
return recset
else:
return rc
def insert_id():
""" Get the insert ID of the last insert
"""
db = _db_login()
return db._db.insert_id()
%font "standard", size 5
En databasöppning per apacheprocess.
Databaskopplet återanvänds
%page
(POOM)
En klass definieras för varje möjlig sida/kommando.
Varje klass har en metod "response"
%font "typewriter", size 3
dispatchers = {"mainpage" : MainPageHandler}
def handler(req):
cmdpart = commandpart(req.uri)
try:
response_type = dispatchers[cmdpart]
action = response_type(req)
return action.response()
except:
%font "standard", size 5
%page
Exempel, fortsättning..
%font "typewriter", size 3
import traceback
timetext = time.strftime("%y%m%d-%H%M", time.localtime(time.time()))
f = open(join(constants.TRACEBACKDIR, timetext), 'w')
f.write("Time of exception: %s\n\n" % timetext)
traceback.print_exc(file=f)
f.write("\n")
f.write("Remote IP: %s\n" % req.connection.remote_ip)
f.write("Request : %s\n" % req.the_request)
f.close()
f = open(join(constants.TRACEBACKDIR, timetext), 'r')
ret = Error(req).response(title="Exception",
errortext = "
%s
" % f.read())
f.close()
return ret
%font "standard", size 5
%font "typewriter", size 3
class MainPageHandler():
def __init__(self, req):
self.req = req
def response(self):
...
%font "standard", size 5
%page
%center, size 10
HTMLgen
%size 5
(En oerhört användarfientlig genomgång av en skojig modul)
%bar "#dee4c4", vgap 10
%page
HTML-generering med HTMLgen
Generering av HTML
Från template..
Med färdiga dokumentklasser..
Eller bara de bitar du behöver.
Dokumentation: http://starship.python.net/crew/friedrich/HTMLgen/html/main.html
Download: http://starship.python.net/lib.html
RPM: http://www.lysator.liu.se/lyskom/klienter/webkom/
'apt-get install htmlgen'
%page
HTMLgen med templates - TemplateDocument
Exempeltemplate
%font "typewriter", size 3
Test av template
{foo}