Web alkalmazások Python nyelven - alapok

Habár a legnagyobb népszerűségnek a PHP nyelv örvend a web alkalmazások készítésekor, a Python legalább olyan hatékony nyelv lehet erre a feladatra. Külön öröm, hogy akárcsak a PHP esetén a Python nyelvhez is van Apache modul, ami lehetővé teszi, hogy egy beépített értelmezővel hajtassuk végre az alkalmazások. Ez a modul a ModPython. Ebben az írásban röviden bemutatom, hogyan lehet egyszerű web alkalmazásokat fejleszteni ennek az Apache modulnak a segítségével.

Feltételezem, hogy a rendszerünkre a ModPython megfelelően telepítve van és az Apache be is tölti indulásakor.

Elöljáróban annyit kell tudnunk az Apache web szerver belső működéséről, hogy az minden beérkező HTTP kéréssel kapcsolatos műveleteket ún. handler (kezelő) függvényekkel, modulokkal hajtat végre. Alap értelmezésben ezek a handlerek C nyelven íródnak és egy részük az Apache web szerver standard részét képezik. Köszönhetően azonban az Apache modularitásának ilyen handlerek a kérések feldolgozásának egy-egy fázisához más nyelveken is írhatóak.

Maga a ModPython valójában két részből áll:

  1. egy az Apache webszerverbe betölthető mod_python nevű modulból. Ez egy olyan speciális C nyelven írt handler modul ami maga szinte semmit sem csinál, hanem egy környezettet teremt a Python nyelvű alkalmazások számára, hogy a HTTP kéréseket feldolgozhassák,
  2. egy mod_python nevű Python csomagból (package), amit azoknak a Python alkalmazásoknak kell használni amik Apache handlerként működnek.

Ennek következtében minden Apache által használt Python alkalmazás célszerűen az alábbi sorral kezdődik:

from mod_python import apache

(számos ModPythonnal érkező handler már importálja ezt, így ezt a legtöbb esetben csak a legalacsonyabb szinten kell elvégeznünk, mint azt később látni fogjuk)

Az Apache számára a konfigurációs állományokban lehet meghatározni, hogy egy-egy URL feldolgozásakor milyen handlereket hajtson végre. Ennek konfigurálására szolgálnak a SetHandler és AddHandler konfigurációs parancsok. A ModPython használata során számos Python kezdetű konfigurációs paranccsal tudjuk szabályozni a mod_python Apache modul működését.

A ModPython alapvetően az alábbi különféle előre elkészített handler típusokat ismeri:

  1. natív vagy basic: minden a mi kezünkben van. A leggyorsabb és egyben a legalacsonyabb szintű hozzáférést biztosítja a HTTP kérések feldolgozásához. Itt jegyezzük meg, hogy a ModPython sokkal általánosabban épül be az Apache lelkivilágába mind mondjuk a PHP, így a natív handlerekkel nem csak HTML oldalakat generáló alkalmazásokat, hanem akár speciális működést végző (pl.: speciális loggoló) Apache handlereket is készíthetünk. Itt könnyen hozzáférhetünk Python alkalmazásokból az Apache belső változóihoz is.
  2. publisher: a leggyakrabban használt handler, mivel a natív handlerben elvégzendő számos ismétlődő feladatot automatizál és kényelmesen alakítja a GET, POST lekérdezéseket függvényváltozókká.
  3. cgipublisher: a hagyományos CGI környezetet próbálja emulálni a Python alkalmazások számára, azaz a kommunikáció a standard input és output keresztül történik a web szerverrel. Ezt ritkán használjuk.
  4. psp (Python Server Pages): ez a működés hasonlít leginkább a PHP-jéhoz. Ennek segítségével a HTML és Python kódok keverékét tartalmazó dinamikus web oldalakat hozhatunk létre. A
    <?php ?>
    párnak itt a
    <% %>, <%= %>, <%@ %>
    pároknak felelnek meg.

PSP Handler

A PSP handler a legmagasabb szintű megoldás. Használatához az alábbi Apache konfigurációra van szükség:

<Directory foo/bar>
   AddHandler mod_python .psp
   PythonHandler mod_python.psp
</Directory>

Értelem szerűen a .htaccess állományban a Directory tag-ek elhagyhatóak. Ez a beállítás az mondja az Apache-nak, hogy a foo/bar alkönyvtár minden .psp kiterjesztésű állományát a mod_python Apache modul kezeljen le (AddHandler mod_python .psp) a konkrét handler pedig a mod_python Python csomag psp modulja legyen (PythonHandler mod_python.psp).

Ennek hatására minden foo/bar könyvárban található .psp kiterjesztésű állományt az alábbiak szerint értelmez a rendszer: minden a korábban megadott speciális karakter sorozatok közötti részt Python nyelvű utasításokként, a maradékot standard szövegként, amit egy az egyben visszaad.

Ennek megfelelően egy egyszerű psp így néz ki:

<html>
<%
import time
%>
<body>
<%
for i in range(5):
%>
Hello world, the time is: <%=time.strftime("%Y-%m-%d, %H:%M:%S")%><br>
<%
%>
</body>
</html>

A Hello world utáni üres sor jelzi az értelmezőnek a for ciklus végét. Némi kísérletezés után könnyen rá lehet jönni, hogyan kell a Python nyelv indentálás alapú szintaxisát a PSP-n belül érvényesíteni.

Publisher handler

A publisher handler a Zope rendszer ZPublisher megoldásának mintájára készült és jó kompromisszum a PSP és a natív handler között.

A publisher handler használatához az Apache konfigurációs állományba az alábbi, a PSP-hez hasonló bejegyzést kell tennünk:

<Directory foo/bar>
   SetHandler mod_python
   PythonHandler mod_python.publisher
</Directory>

Ezzel arra utasítjuk a web szervert, hogy a foo/bar URL alatt mindent a mod_python Apache modullal kezeljen le. Aki a konkrét kéréseket le fogja kezelni, az pedig nem más, mint a mod_python Python csomag publisher modulja legyen.

A legegyszerűbb publisher handlerrel készült Python web alkalmazás kódja ezek után így néz ki:

def index(req):
     return "Hello World"

A fenti kódot az index.py nevű állományba elhelyezve a böngészőben a http://localhost/foo/bar -nak megfelelő URL begépelésével meg is kapjuk a kívánt eredményt. Ebből kiderül, hogy a publisher handler a fenti konfigurációban alapértelmezésben az index.py állomány (ha nincs konkrétan a foo/bar után semmi megadva) index nevű függvényét hívja meg megfelelő paraméterekkel.

Értelem szerűen ha a foo/bar/barmi explicit hivatkozást tesszünk és nincs ilyen könyvtár a foo/bar alatt, akkor alapértelmezésben a barmi.py állomány index nevű függvénye hívódik meg.

Természetesen lehetőség van az indextől eltérő függvény meg hívására is. Ha a fenti alkalmazásunkat (index.py) kiegészítjük egy ize nevű függvénnyel az alábbiak szerint:

def index(req):
     return "Hello World"
def ize(req):
     return "Hello Ize"

akkor a http://localhost/foo/bar/ize hivatkozás a kívánt eredményt adja. Természetesen a fenti két szabály kombinációjával tetszőleges Python modul tetszőleges nem aláhúzással (_) kezdődő függvénye meghívható.

A GET és POST metódusokkal hívott alkalmazások esetén két módszer is van a GET és a POST változók kezelésére:

A publisher handler először ellenőrzi, hogy a hívott URL egy hívható (callable) függvénynek felel-e meg. Amennyiben igen úgy megvizsgálja, hogy milyen nevű argumentumok vannak definiálva és azt összeveti a GET és POST változókkal, amik egyeznek azt beállítja a Request objektum mellett, a többit lenyeli, hacsak nincs **kw típusú argumentum definiálva. Példa:

def myform(req, a, b):
    return "Ezt kaptam %s %s" % (a, b)

A http://localhost/foo/bar/myform?a=1&b=2&c=3 hatására az a

Ezt kaptam 1 2
eredményt kell kapjuk.

Az első paraméterben kapott Request objektum form változója egy dictionary-ben tartalmazza a form POST és GET változókat.

Natív vagy Basic handler

A legalacsonyabb szintű kezelés a natív vagy baisc handlerrel végezhetjük el. Használatához az Apache konfigurációs állományban az alábbihoz hasonló bejegyzést kell tennünk:

<Directory foo/bar>
   AddHandler mod_python .py
   PythonHandler valami
</Directory>

Ennek hatására minden .py kiterjesztésű URL-t a foo/bar alatt a valami.py állományban definiált handler függvénnyel fog kezelni az Apache webszerver.

Ezek alapján vegyük a klasszikus legegyszerűbb alkalmazást, aminek a kódja így néz ki:

from mod_python import apache

def handler(req):
        req.content_type = 'text/html'
        req.write("Hello World!")
        return apache.OK

Jól látható, hogy a handler függvény argumentumában egy Request objektumot kap, amin végzet műveletek fogják meghatározni a handler működését. A handler visszatérési értékei az alábbiak lehetnek:

  1. OK: minden rendben volt a feldolgozáskor, mehet tovább a szokásos feldolgozási menet. Több handler modul ezt a kérést már nem fogja feldolgozni.
  2. Error: az apache modul HTTP_ kezdődően tartalmazza a HTTP visszatérési kódokat amik segítségével korrekt hibakódot adhatunk vissza.
  3. DONE: a teljes kérés fel van dolgozva lehet loggolni és visszatérni a klienshez az eredménnyel.
  4. DECLINED: ez a handler modul visszautasította az adott HTTP kérés feldolgozását, lépj a következő handlerre.
Szalai Ferenc | 2006, augusztus 14 - 20:16 | |
durumdara (nem ellenőrzött), 2006, szeptember 14 - 11:26

Annyit hozzátennék, hogy a php és python alapú webfejlesztés között nagy különbség adódik a python előfordítása és az objektumok perzisztens tárolása miatt.

A php forráskódokban történő módosítás azonnali változást hoz, mert a lefordított objektumok nem tárolódnak sehol, a kód mindig újrainterpretálásra kerül.

A python esetében ez nem minden esetben történik meg.
Ezért az összes python alapú projektnél (zope, mod_python, skunkweb, stb.) tapasztaltam, hogy a változások flush-elésére külön technikákat vetettek be.

Zope-nál a python scriptekből eleve újrafordított verzió készül a módosításkor, a külső modulok (termékek) esetében pedig muszáj refresh-t kérni, ha változtattunk valamit.
A mod_python-nál külön megemlítik a FAQ-ban a trükköt, amivel ezt a gondot kontrollálni lehet, de a SkunkWeb szintúgy küszködik, ha változtatni kell valamin.

Legtöbbször a legegyszerűbb fejlesztői frissítési eszköz az "Apache restart" szokott lenni...

Ezt figyelembe kell venni, ha python segítségével kívánunk dolgozni, mert fentiek miatt több türelemre van szükség a fejlesztés tekintetében.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.