%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} %font "standard", size 5 Exempelkod för generering %font "typewriter", size 3 from HTMLgen import * T = TemplateDocument("foo.tmpl.html") T.substitutions["foo"] = "Ja, eller en kaktus" T.write("foo.html") %font "standard", size 5 %page ..med templates, fortsättning. Resultat %font "typewriter", size 3 Test av template Ja, eller en kaktus %font "standard", size 5 %page HTMLgen med templates - AutoTemplateDocument Samma fil kan användas som både template och utfil. Exempelkod, samma infil som tidigare: %font "typewriter", size 3 from HTMLgen import * AT = AutoTemplateDocument("foo.tmpl.html") AT.substitutions["foo"] = "Våld och vaselin" AT.write("foo.html") %font "standard", size 5 foo.html ser nu ut såhär: %font "typewriter", size 3 Test av template Våld och vaselin %font "standard", size 5 %page AutoTemplateDocument, fortsättning Filen kan åter läsas in i ett AutoTemplateDocument och skrivas ut igen. Behovet av ett extra template är alltså eliminerat. %page HTMLgen med färdiga dokumentklasser BasicDocument FramesetDocument SimpleDocument SeriesDocument %page HTMLgen med BasicDocument Enkel klass med bara de enklaste HTML-funktionerna definierade Exempelkod: %font "typewriter", size 3 from HTMLgen import BasicDocument import HTMLcolors B = BasicDocument(title = "A test document", bgcolor = HTMLcolors.BLACK, textcolor = HTMLcolors.RED, linkcolor = HTMLcolors.OLIVE) B.append("Foo!") print B %font "standard", size 5 %page ..med BasicDocument, fortsättning Resultat %font "typewriter", size 3 A test document Foo! %font "standard", size 5 %page HTMLgen med FramesetDocument För dokument med Frames. Addera instanser av Frameset Addera instanser av Frame till Framset %page HTMLgen med SimpleDocument Använder en resursfil för att sätta diverse dokumentattribut %font "typewriter", size 3 # Resource file used by the Document class of the HTML module from HTMLcolors import * stylesheet = 'HTMLgen.css' author = '2000 Erik Forsberg' email = 'forsberg@lysator.liu.se' bgcolor = WHITE textcolor = BLACK linkcolor = RED vlinkcolor = RED6 banner = None logo = ('Buzz.gif', 56, 51) blank = (blank.gif', 66, 22) prev = ('back.gif', 66, 22) next = ('next.gif', 66, 22) top = ('top.gif', 66, 22) home = ('home.gif', 66, 22) gohome = None background = '../image/bg-dots.gif' %font "standard", size 5 %page Exempel med ovanst. resursfil: %font "typewriter", size 3 from HTMLgen import * sd = SimpleDocument(resource="foo.RC") k = Container() k.append(Heading(1, "Header")) k.append("Lite text..") k.append(HR()) sd.append(k) print sd sd.write("/tmp/foo.SimpleDocument.html") %font "standard", size 5 %page ..ger resultatet.. %font "typewriter", size 3

Header

Lite text..
%font "standard", size 5 %page HTMLgen med SeriesDocument Påbyggd SimpleDocument med header och footer mm. %page Övriga HTMLgen Meta Href Name MailTo List, 4 typer Form, med alla inputsorter Table, två sorter Alla övriga HTML-taggar har varsin klass. %page Tabellgenerering Två sätt Table TableLite %page Tabellgenerering - Table Generera tabeller från Python-listor Exempel: %font "typewriter", size 3 from HTMLgen import * tb = [["13-12-1", 14000], ["13-12-2", 15600]] t = Table("TableCaption", heading=["Kontonummer", "Belopp"], body=tb) print t %font "standard", size 5 %page ..ger resultatet.. %font "typewriter", size 3

TableCaption
KontonummerBelopp
13-12-1 14000
13-12-2 15600

%font "standard", size 5 %page Tabellgenerering - TableLite %font "typewriter", size 3 from HTMLgen import * tl = TableLite() tl.append(TH(TD("Bankkonto"), TD("Belopp"))) tl.append(TR(TD("13-21-1"), TD(133434))) print tl %font "standard", size 5 ..ger output.. %font "typewriter", size 3
BankkontoBelopp
13-21-1133434
%font "standard", size 5 %page Container Är en klass vari man kan samla andra klasser. Exemplet.. %font "typewriter", size 3 from HTMLgen import * import HTMLcolors k = Container() k.append(Heading(1, "Header")) k.append("Lite text..") k.append(HR()) print k %font "standard", size 5 ..ger output.. %font "typewriter", size 3

Header

Lite text..
%font "standard", size 5 %page %center, size 10 FIN %size 5 %size 6, font "typewriter" http://www.lysator.liu.se/~forsberg/ forsberg@lysator.liu.se %bar "#dee4c4", vgap 10