Tento text je napsán pro Linux a Apache.

Představme si, že jsme v situaci, kdy potřebujeme napsat CGI skript přistupující např. k MySQL databázi, ale i když je na daném počítači nainstalována a nainstalován je i Python, chybí MySQLdb a, co je ještě horší, i libmysqlclient. Administrátorská práva nemáme a admin nám nechce potřebné knihovny nainstalovat. Jak z toho ven?

Začněme tím jednodušším, tj. MySQLdb. Využijeme toho, že existuje distribuční balík pro naši distribuci a někam ho rozbalíme (to umí třeba Midnight Commander; některé distribuce snad umožňují instalaci balíku do uživatelského adresáře). Cestu do toho adresáře (tj. toho, co obsahuje např. _mysql.so) pak jednoduše přidáme na úplný začátek našeho CGI skriptu do proměnné sys.path. Příklad testovacího skriptu:

#!/usr/bin/python
# -*- coding: UTF-8 -*-

import cgi

import sys     # v mysql_python mame MySQLdb
sys.path.append('/home/~username/mysql_python')

import MySQLdb

print 'Content-Type: text/html; charset=UTF-8\n'
print '<h1>Hello</h1>'

conn = MySQLdb.connect(db=' ... ', passwd=' ... ')
conn.set_character_set('utf8')
c = conn.cursor()
c.execute('select * from clovek')
for row in c.fetchall():
    print row[1], '<br>'
c.close()

print '<hr>'
form = cgi.FieldStorage()
for i in form:
        print i, form[i].value, '<br>'

Druhý problém je složitější. Podobně jako v prvním případě stáhneme a rozbalíme balíček s libmysqlclient, ale potřebujeme, aby v době spuštění skriptu byl adresář s touto knihovnou v LD_LIBRARY_PATH. Apache má sice direktivu SetEnv, ale na tu je v .htaccess už příliš pozdě. Řešením může být druhý skript, který má název prvního skriptu jako parametr. Ten nastaví LD_LIBRARY_PATH a pak spustí náš skript. Příklad takového skriptu:

#!/usr/bin/python

from os import environ

import cgitb; cgitb.enable()

def print_error(msg):
    import sys
    print 'Content-Type: text/html\n'
    print '<h1>Error</h1>'
    print '<xmp style="color:red">%s</xmp>' % msg
    sys.exit()

filename = None
method = environ['REQUEST_METHOD']

if   method == 'GET':
        import cgi
        data = cgi.FieldStorage()
        if data.has_key('script'):
                filename = data['script'].value
elif method == 'POST':
        for pair in environ['QUERY_STRING'].split('&'):
                key, value = pair.split('=')
                if key == 'script':
                        filename = value
                        break

if filename:
    import re
    if re.match(r'^\w+$', filename):
        import os
        filename += '.py'
        if os.path.exists(filename):
            from subprocess import Popen, PIPE
            # podle toho, kde mame libmysqlclient
            environ["LD_LIBRARY_PATH"] = "/home/~username/lib"
            p = Popen(('/usr/bin/python', filename), env=environ, stderr=PIPE)
            if p.wait() <> 0:
                print_error(p.stderr.read())
        else:
            print_error('Script does not exists!')
    else:
        print_error('Script filename must be [a-zA-Z0-9_]+ !')
else:
    print 'Location: http:// .... \n'

To sice už funguje, ale pokud už je aplikace vyvinutá, asi se nám nebude chtít měnit všechna URL z neco.py na run.py?script=neco . To lze vyřešit pomocí modulu rewrite. Příklad .htaccess:

AddHandler cgi-script .py .cgi
RewriteEngine On
RewriteCond %{QUERY_STRING} ^(.*)$
RewriteRule ^([^run]+?)\.py$ /adresa_nasich_stranek/cgi-bin/run.py?script=$1&%1 [L]

P.S. Nenapadl mě žádný výstižný text této stránky, tak pokud vás políbí můza neváhejte a přejmenujte.