Dernière màj. 13/10/2014
Pour naviguer dans ce document, utilisez les flèches gauche et droite ou la molette de la souris. Pour obtenir de l'aide, tapez la lettre 'h'.
Un des premiers réflexes à acquérir : le mode interactif.
$ python
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
[GCC 4.8.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
IPython offers a combination of convenient shell features, special commands and a history mechanism for both input (command history) and output (results caching, similar to Mathematica). It is intended to be a fully compatible replacement for the standard Python interpreter, while offering vastly improved functionality and flexibility.
$ ipython
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
Type "copyright", "credits" or "license" for more information.
IPython 1.2.1 -- An enhanced Interactive Python.
In [1]:
Le matériel de cette présentation vous est fourni par l'intermédiaire d'un fichier tar compressé.
tarfile est un module de la librairie standard Python. Son code (il est écrit en Python) est directement accessible à partir de la page de documentation.
reads and writes gzip, bz2 and lzma compressed archives if the respective modules are available
extraction du fichier du TP avec la commande :
python -m tarfile -e TPpython_maizi.tar.gz
Un module doit pouvoir être importé. Deux possibilités pour vérifier rapidement si les modules tarfile et gzip sont présents sur votre poste de travail :
python -c 'import tarfile, gzip'
import tarfile, gzip
Si les modules sont présents rien ne sera affiché à l'écran. En cas d'erreur en revanche, l'affichage suivant apparaîtra :
$ python -c 'import toto'
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: No module named toto
La dernière ligne indique que le module toto n'a pas été trouvé.
Vous serez sans doute amenés à travailler avec les deux versions !
Suit une très succinte présentation des raisons qui ont amené au passage à Pyhton 3 et des incompatibilités qui existent entre les deux versions.
Python 3.0 will break backwards compatibility with Python 2.x.
Rupture de compatibilité pour apporter des améliorations :
... The most drastic improvement is the better Unicode support (with all text strings being Unicode by default) as well as saner bytes/Unicode separation.
le PEP 358, (Python Enhancement Proposal), décrit la proposition de mise en place du type bytes dans python 2.6.
There are a few minor downsides, such as slightly worse library support and the fact that most current Linux distributions and Macs are still using 2.x as default
Python 3 Wall of Superpowers, il y a encore quelques temps nommé «Python 3 Wall of shame».
Il reste tout de même des packages qui ne sont pas et en seront sans doute pas portés en Python 3. Si vous voulez utiliser ces packages, vous devrez alors utiliser Python 2.
Vous pouvez avoir besoin de spécifier la version de Python que vous utilisez
pour exécuter le script. Sur un système Unix, la première ligne si elle
commence par #!
(shebang) indique au système l'interpréteur qu'il devra
utiliser. Par exemple :
#!/usr/bin/python3
ou
#!/usr/bin/python2.6
L'écriture #!/usr/bin/python
référence "en dur" l'interpréteur Python
installé sur votre système (en règle générale la version 2).
En revanche, #!/usr/bin/env python
référence la première
commande python trouvée dans votre PATH ou dans le PATH de celui qui
exécute le script.
Si vous utilisez un gestionnaire de version de code (git, mercurial, ...) et si vous devez tester votre code avec différentes version de Python, la seconde option est à privilégier.
Par défaut, sous Linux, python référence (aujourd'hui) Python 2.7. Vous pourriez être amené à développer du code qui devra fonctionner indiféremment sous 2.7 et 3+.
Rajoutez la ligne suivante dans votre .profile
:
if [ -d "$HOME/bin" ] ; then ; PATH="$HOME/bin:$PATH" ; fi
Créez le répertoire bin
mkdir ~/bin
Créez dans ce répertoire un lien vers l'interpréteur Python avec lequel vous voulez tester votre code :
ln -s /usr/bin/python3 ~/bin/python
hash -r
# remplacer python3 par python2 pour tester avec Python2
Commencez tous vos scripts par :
#!/usr/bin/env python
démonstration de la mise en place du lien :
Comment prendre en charge les différences entre les version 2 et 3 de Python. Le module sys
import sys
if sys.version_info.major < 3:
# faire quelque chose pour adapter le code Python 2
Par exemple :
if sys.version_info.major < 3:
input = raw_input # pas vraiment vrai
else:
unicode = str # quelques petites différences ici aussi
Ou ne pas prendre en charge les différences :
if sys.version_info.major < 3:
print("Désolé ! Ce programme nécessite une version 3.x de Python.")
sys.exit()
Les deux "transparents" suivants présentent les différences du type str entre les version 2 et 3 de Python.
En python2 str et une séquence d'octets.
$ python2
Python 2.7.6 (default, Mar 22 2014, 22:59:56)
>>> "chaîne"
'cha\xc3\xaene'
>>> "chaîne" == bytes("chaîne")
True
>>> str == bytes
True
>>> "chaîne".decode("utf-8") == unicode("chaîne", "utf-8")
True
>>> "chaîne" == unicode("chaîne", "utf-8").encode("utf-8")
True
Observez dans un interpréteur Python 2 le résultat de
print([c for c in "chaîne"])
En python3, str est de l'unicode. En revanche, le mot clef unicode n'existe plus.
$ python3
Python 3.4.0 (default, Apr 11 2014, 13:05:11)
>>> "chaîne"
'chaîne'
>>> "chaîne".encode()
b'cha\xc3\xaene'
>>> "chaîne".encode() == bytes("chaîne", "utf-8")
True
>>> "chaîne" == str("chaîne".encode("utf-16"), "utf-16")
True
>>> "chaîne" == bytes("chaîne", "utf-8").decode()
True
>>> "chaîne" == "chaîne".encode().decode()
True
L'encodage par défaut est UTF-8 pour str.encode() et bytes.decode(). Ça n'est pas le cas en Pyhon 2 où il faut spécifier le type d'encodage de caractères.
print([c for c in "chaîne"]) # affiche correctement les caractères
Nous allons maintenant voir les différences qui existent entre scripts et modules et comment transformer (ou écrire) un script pour qu'il soit utilisable comme module.
A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py appended. Within a module, the module's name (as a string) is available as the value of the global variable name
#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""
affiche_kek_chose:
usage :
* python affiche_kek_chose <kek chose>
* ./affiche_kek... (vous n'avez pas oublié chmod +x aff...)
"""
import sys
def afficher(qui, quoi):
"""Affiche qui affiche quoi..."""
print('{} affiche : "{}"'.format(qui, quoi))
afficher(sys.argv[0], sys.argv[1])
Manifestement ! Si on cherche à l'importer, une erreur se produit :
In [1]: import affiche_kek_chose
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-1-7432c9ad8fa9> in <module>()
----> 1 import affiche_kek_chose
/home/joel/TPPYTHON/ScriptsVSModules/ex1/affiche_kek_chose.py in <module>()
15 print('{} affiche : "{}"'.format(qui, quoi))
16
---> 17 afficher(sys.argv[0], sys.argv[1])
IndexError: list index out of range
Tout se passe comme si on invoquait le script sans argument.
$ ./affiche_kek_chose.py
Traceback (most recent call last):
File "./affiche_kek_chose.py", line 17, in <module>
afficher(sys.argv[0], sys.argv[1])
IndexError: list index out of range
La ligne posant problème est : afficher(sys.argv[0], sys.argv[1])
Question : quel est le type d'objet sys.argv ? Jetez un oeil à sa documentation
>>> import sys
>>> type(sys.argv)
La documentation dit :
If no script name was passed to the Python interpreter, argv[0] is the empty string.
>>> sys.argv[0]
''
En revanche, si sys.argv[0] est toujours défini, c'est sys.argv[1] qui ne l'est pas dans le cas du lancement du script sans argument comme dans le cas de la tentative d'import.
Exo 3 : trouvez un moyen simple pour permettre l'import du module affiche_kek_chose. Le moyen le plus "économique" consiste à rajouter un caractère....
A module can discover whether or not it is running in the main scope by checking its own
__name__
.
Exo 4 :
affiche_kek_chose
pour afficher la variable __name__
.__name__
est l'élément discriminent).exemples d'usage : le code du module fileinput, le code du module string, le code du module webbrowser.
Exo 3 : Le moyen le plus simple consiste à commenter la ligne posant problème :
# afficher(sys.argv[0], sys.argv[1])
Exo 4 : Dans le cas du script, l'affichage de la variable __name__
affiche la valeur __main__
. Dans le cas de l'import, __name__
affiche affiche_kek_chose. C'est le nom qui est utilisé pour réaliser
l'import
A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py appended. Within a module, the module's name (as a string) is available as the value of the global variable name
Pour un script la variable __name__
est égale à la chaîne "__main__"
.
La solution consiste donc à n'exécuter l'appel de la fonction que si
__name__
est égale à __main__
.
[...]
if __name__ == '__main__':
afficher(sys.argv[0], sys.argv[1])
Il est maintenant possible d'utiliser le script comme un module.
que se passe t'il si vous lancez la commande suivante :
python affiche_kek_chose.py kek chose
modifiez le programme pour obtenir :
affiche_kek_chose affiche : "kek chose"
(réf. Arbitrary Argument Lists)
a
def ma_fonction(param1, param2=None, param3='truc'):
a = param1
if param2:
# faire quelque chose
if param3 == 'turc':
# le truc
else:
# pas le truc
ma_fonction('un', param3='machin')
Une forme souvent rencontrée :
def ma_fonction(param1, *args, **kwargs):
for arg in args:
# faire quelque chose
for key, value in kwargs.items():
# faire autre chose
# les trois invocations de ma_fonction sont équivalentes
ma_fonction('un', 'deux', 'trois', quatre=4, cinq='cinq')
l = ['deux', 'trois']
d = {'quatre': 4, 'cinq': 'cinq'}
ma_fonction('un', *l, **d)
Packages are a way of structuring Python’s module namespace by using “dotted module names”. For example, the module name A.B designates a submodule named B in a package named A.
/usr/lib/python3.4$ tree xml | grep -v pyc
xml
├── __init__.py
├── dom
│ ├── __init__.py
│ ├── domreg.py
│ ├── [...]
├── etree
│ ├── __init__.py
│ ├── cElementTree.py
│ ├── [...]
Notez la présence d'un fichier __init__.py
dans chaque répertoire du
package.
__init__.py
dans chaque répertoire>>> import xml. <TAB>
...a
Suivent 3 exercices correspondant à des cas pratiques :
Les pages suivantes seront remaniées dans les jour/semaines qui viennent.
Merci de m'adresser vos questions/suggestions pour permettre d'améliorer ce document.
joel.maizi at lirmm.fr 13/10/2014
Quelqu'un vous a fait une mauvaise blague. Il a saccagé un de vos répertoires en y laissant deux fichiers.
un fichier ls contenant la liste des fichiers tels qu'ils étaient à l'origine dans le répertoire :
-rw-rw-r-- 1 joel joel 20887 sept. 6 11:06 fichierA
-rw-rw-r-- 1 joel joel 384 sept. 7 10:54 fichierB
-rw-rw-r-- 1 joel joel 1025 sept. 13 09:22 fichierC
-rw-rw-r-- 1 joel joel 377 juil. 5 16:48 fichierD
...
un fichier bundle contenant tous les fichiers concaténés.
| ... fichierA ... | ... fichierB | ... |
À vous de jouer pour reconstituer le répertoire.
NB. Il n'y a pas de séparateur entre les fichiers dans le fichier bundle. Les fichiers se trouvent dans le répertoire TPPython/MauvaiseBlague/data.
a
Il contient le résultat de la commande ls -l faite dans le répertoire avant la suppression des fichiers. Les informations d'une ligne ls -l sont dans l'ordre :
Dans un premier temps vous allez extraire de cette liste les noms et tailles des fichiers. Avec le fichier ls suivant :
-rw-rw-r-- 1 joel joel 20887 sept. 6 11:06 fichierA
-rw-rw-r-- 1 joel joel 384 sept. 7 10:54 fichierB
-rw-rw-r-- 1 joel joel 1025 sept. 13 09:22 fichierC
-rw-rw-r-- 1 joel joel 377 juil. 5 16:48 fichierD
le résultat devra être :
fichierA : 20887
fichierB : 384
fichierC : 1025
fichierD : 377
À utiliser :
a
#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""
Lecture du fichier ls
"""
with open('ls') as f:
for line in f:
ls_list = line.split()
print("{}: {}".format(ls_list[8], ls_list[4]))
vs.
f = open('ls')
for line in f:
[...]
f.close()
En cas de plantage dans la boucle for, la première version fermera correctement le fichier contrairement à la seconde.
a
class Ls:
"""Ls(<ls -l line>)
découpe la ligne et associe chaque élément à l'attribut portant son nom
"""
def __init__(self, ls_line):
ls_list = ls_line.split()
self.rights = ls_list[0]
self.nlinks = ls_list[1]
self.owner = ls_list[2]
self.group = ls_list[3]
self.size = ls_list[4]
self.month = ls_list[5]
self.day = ls_list[6]
self.hour = ls_list[7]
self.name = ls_list[8]
Ce qui permet l'écriture d'une boucle plus lisible
with open('ls') as f:
for ls_line in f:
ls = Ls(ls_line)
print("{}: {}".format(ls.name, ls.size))
a
class Bundle:
def __init__(self, directory):
self.__dir = directory
with open('{}/ls'.format(self.__dir)) as f:
self.__lss = [Ls(ls_line) for ls_line in f]
self.__filenames = [ls.name for ls in self.__lss]
def list(self):
for ls in self.__lss:
print("{}: {}".format(ls.name, ls.size))
if __name__ == '__main__':
bundle = Bundle(sys.argv[1]) # argv[1] est le répertoire
bundle.list()
__index(self, filename)
qui retourne la position
du fichier portant le nom filename dans le fichier bundle.__offset(self, filename)
qui retourne la position
du premier caractère du fichier filename dans le fichier bundle.__content(self, filename)
qui retourne le contenu
du fichier filename dans le fichier bundle.explication sur les listes de compréhension (cf. slide suivant)
a
Exemple de construction alléatoire d'une adresse IP :
>>> from random import randint
>>> randint(10,254)
167
>>> [i for i in range(4)]
[0, 1, 2, 3]
>>> [i for i in range(4) if i % 2]
[1, 3]
>>> [randint(10,254) for i in range(4)]
[81, 97, 157, 169]
>>> [str(randint(10,254)) for i in range(4)]
['56', '135', '212', '58']
>>> print(".".join([str(randint(10,254)) for i in range(4)]))
'239.246.133.137'
La dernière ligne est à comparer avec le code suivant sans liste de compréhension :
from random import randint
res = [] # variable intermédiaire qui contiendra la liste
for i in range(4):
res.append(str(randint(10,254)))
print(".".join(res))
del(res) # suppression de la variable intermédiaire
a
D'après la doc de open(), en cas de problème, une exception OSError est "levée" (depuis la version 3.3 de Python, IOError pour les versions antérieures).
$ ipython
In [1]: from exceptions import OSError
In [2]: ose = OSError()
In [3]: ose.<TAB>
ose.args ose.errno ose.filename ose.message ose.strerror
Ce qui pourrait donner :
try:
with open('{}/ls'.format(self.__dir)) as f:
self.__lss = [Ls(ls_line) for ls_line in f]
except OSError as err:
print(err)
sys.exit()
a
__index()
Rappel :
self.__filenames = [ls.name for ls in self.__lss]
self.__filenames
On utilise la méthode list.index()
def __index(self, filename):
"""Retourne l'indice du fichier filename (commence à 0)
"""
return self.__filenames.index(filename)
__offset()
Écrire la méthode __offset(self, filename)
qui retourne la position
du premier caractère du fichier filename dans le fichier bundle.
def __offset(self, filename):
"""Retourne la position du premier octet du fichier filename
"""
offset = 0
for i in range(self.__index(filename)):
offset += self.__lss[i].size
return offset
Écrire cette méthode en une line : liste de compréhension + sum()
__size()
def __size(self, filename):
"""Retourne la taille du fichier filename
"""
return self.__lss[self.__index(filename)].size
a
__content()
Écrire la méthode __content(self, filename)
qui retourne le contenu
du fichier filename dans le fichier bundle. Utiliser
seek() et
read().
def __content(self, filename):
"""Le contenu retourné est de type "bytes"
"""
with open('bundle', "rb") as f:
offset = self.__offset(filename)
size = self.__size(filename)
f.seek(offset)
return f.read(size)
essayez le programme avec "r" à la place de "rb". Que se passe-t'il ?
a
Cf. le howto concernant l'utilisation du module argparse.
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument(
"repertoire", help="Répertoire contenant les fichiers")
parser.add_argument(
"fichiers", nargs="*",
help="Nom du(des) fichier(s) à afficher ou enregistrer")
parser.add_argument(
"-w", "--write", help="Restaure le(s) fichier(s)",
action="store_true")
args = parser.parse_args()
directory = args.repertoire
fichiers = args.fichiers
write = args.write
bundle = Bundle(directory)
if not fichiers:
bundle.list()
else:
for f_name in fichiers:
if not args.write: bundle.cat(f_name)
else: bundle.write(f_name)
a
Vous trouverez dans le répertoire Analog, un fichier de log apache. Il correspond à quelques heures d'activité du serveur www.jeuxdemots.org.
Parser
en surchargeant la méthode alias
.a
#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""
génère un fichier <nom_fichier>.ano dans lequel les adresses IP
sont anonymisées.
L'adresse IP doit être en début de ligne.
"""
import sys
from random import randint
def get_random_ip():
"""Anonymiseur d'IP"""
return ".".join((str(randint(10, 254)) for i in range(4)))
def anon_file(filename):
"""Retourne une à une chaque ligne du fichier après avoir
anonymisé l'IP.
"""
# le dico des ip anonymisées
d_aip = {}
with open(filename) as f:
for line in f:
ip, rline = line.strip().split(" ", 1)
if not ip in d_aip:
d_aip[ip] = get_random_ip()
yield "{} {}".format(d_aip[ip], rline)
if __name__ == '__main__':
filename = sys.argv[1]
a_filename = "{}.ano".format(filename)
with open(a_filename, "w") as sf:
for line in anon_file(filename):
sf.write("{}\n".format(line))
Il n'était pas question d'exposer les IP réelles ce qui a été l'occasion d'écrire un script permettant l'anonymisation des IP. Les IP du fichier jdm_access.log ne sont pas les IP des machines ayant effectué les requêtes.
Takes the Apache logging format defined in your httpd.conf and generates a regular expression which is used to a line from the log file and return it as a dictionary with keys corresponding to the fields defined in the log format.
{
'%>s': '200',
'%b': '2607',
'%h': '212.74.15.68',
'%l': '-',
'%r': 'GET /images/previous.png HTTP/1.1',
[...]
}
You can also re-map the field names by subclassing (or re-pointing) the alias method.
créer un module my_apachelog.py contenant la classe MyParser héritant de la classe Parser et redéfinissant '%h' en 'host', '%t' en 'time', '%r' en 'request' et '%>s' en 'status'
from apachelog import Parser, parse_date
parse_date = parse_date
class MyParser(Parser):
def alias(self, name):
d_name = {
'%h':'host', '%t':'time', '%r':'request', '%>s':'status'}
if name in d_name:
return d_name[name]
return name
Écriture d'un parser efficace.
#!/usr/bin/env python
#-*- coding: utf-8 -*-
import sys
from datetime import datetime
from my_apachelog import MyParser, parse_date
fmt = r'%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"'
def parse_apache_log(fichier, format_):
p = MyParser(format_)
with open(fichier) as f:
for line in f:
yield p.parse(line)
if __name__ == '__main__':
parse_apache_log('jdm_access.log', fmt)
Observez comme l'exécution est immédiate. La fonction utilise yield ce qui en fait un générateur.
a
En utilisant la même syntaxe que les listes de compréhension, mais avec des parenthèses au lieu des crochets :
>>> g = (i for i in range(4))
>>> type(g)
<class 'generator'>
>>> l = [i for i in range(4)]
>>> type(l)
<class 'list'>
>>> for i in g: print(i)
...
0
1
2
3
>>> for i in g: print(i)
...
>>>
En utilisant yield dans une fonction.
Dans les deux cas Beware the Python generators
a
la version originale
def parse_apache_log(fichier, format):
p = Parser(format)
with open(fichier) as f:
for line in f:
yield p.parse(line)
un peu plus condensée mais équivalente
def parse_apache_log(fichier, format):
p = Parser(format)
return (p.parse(line) for line in open(fichier))
très semblable à la précédente, mais tellement différente...
def parse_apache_log(fichier, format)
p = Parser(format)
return [p.parse(line) for line in open(fichier)]
a
Afficher les lignes ayant statut dont le code est 500.
import sys
from analog1 import parse_apache_log, fmt
"""
Exemple d'usage : Afficher les lignes ayant un statut dont le code est
500 (Internal Server Error)
"""
for line in parse_apache_log('jdm_access.log', fmt):
if line['status'] == '500':
print(
line['host'], line['time'], line['request'], line['status'])
Transformer ce script en module
Ecrivez un module permettant de filtrer les logs suivant :
Le module devra proposer les méthodes :
from analog1 import parse_apache_log, fmt
def get_by_strict_match(logfile, string, field):
for line in parse_apache_log(logfile, fmt):
if line[field] == string:
yield line
def get_by_status(logfile, status):
return get_by_strict_match(logfile, status, 'status')
def get_by_host(logfile, host):
return get_by_strict_match(logfile, host, 'host')
def get_by_partial_match(logfile, string, field):
for line in parse_apache_log(logfile, fmt):
if line[field].find(string) == 0:
yield line
def get_by_request(logfile, request):
request = request.split('?')[0]
return get_by_partial_match(logfile, request, 'request')
def get_by_datetime(logfile, datetime):
return get_by_partial_match(logfile, datetime, 'time')
get_by_min = get_by_datetime
get_by_hour = get_by_datetime
get_by_day = get_by_datetime
WSGI is the Web Server Gateway Interface. It is a specification that describes how web server communicates with web applications, and how web applications can be chained together to process one request.
WSGI is Python standard described in detail in PEP 3333.
Nous partons de l'exemple d'usage proposé dans la documentation du module wsgiref.
Nous allons mettre en place une application qui nous permettra d'interroger notre analyseur de logs par l'intermédiaire d'une page web.
a
from wsgiref.util import setup_testing_defaults
from wsgiref.simple_server import make_server
def simple_app(environ, start_response):
setup_testing_defaults(environ)
status = '200 OK'
headers = [('Content-type', 'text/plain; charset=utf-8')]
start_response(status, headers)
ret = [("%s: %s\n" % (key, value)).encode("utf-8")
for key, value in environ.items()]
return ret
httpd = make_server('', 8000, simple_app)
print("Serving on port 8000...")
httpd.serve_forever()
a
Les modifications à apporter figurent ci dessous :
env_orig = None
def simple_app(environ, start_response):
global env_orig
if env_orig == None:
env_orig = environ
[...]
ret = [("%s: %s\n" % (key, value)).encode("utf-8")
for key, value in environ.items() if env_orig[key] != value]
return ret
ajout d'un test dans la liste de compréhension pour ne retrouner que les valeurs qui ont changé.
if env_orig[key] != value
Pour l'URL http://localhost:8000/?statut=500, c'est la clef QUERY_STRING de l'environnement qui contient l'information.
QUERY_STRING: status=500
Pour http://localhost:8000/statut/500, c'est PATH_INFO
PATH_INFO: /status/500
Il ne nous reste plus qu'à choisir un protocole que saura interpréter notre serveur.
Proposition : le PATH_INFO représentera /<opération>/<valeur>
, les opérations
étant :
/<opération>/<value>
,<table>
,Solution partielle :
[...]
log_path = "/home/joel/TPPYTHON/AnaLog/"
logfile = '{}/jdm_access.log'.format(log_path)
sys.path.insert(0, log_path)
[...]
def simple_app(environ, start_response):
[...]
ret = []
for line in analogmod.get_by_status(logfile, '500'):
ret += [
"{} {} {} {}\n".format(
line['host'], line['time'],
line['request'], line['status']).encode("utf-8")
]
return ret
def to_utf8(line):
return "{} {} {} {}\n".format(
line['host'], line['time'],
line['request'], line['status']).encode("utf-8")
def simple_app(environ, start_response):
d_op = {
'status': analogmod.get_by_status,
'request': analogmod.get_by_request,
'host': analogmod.get_by_host,
'time': analogmod.get_by_datetime
}
[...]
path_info = environ['PATH_INFO']
ret = ["rien".encode("utf-8")]
if path_info:
ret = []
try:
key, value = path_info.split('/', 2)[1:]
ret.append(to_utf8(line) for line in d_op[key](logfile, value))
return ret
except Exception as err:
return [str(err).encode("utf-8")]
return ret
ATTENTION ! Code non testé...
Solution partielle
def a(line, op):
elt = line[op]
return """<a href="/{}/{}">{}</a>""".format(op, elt, elt)
def to_html(line):
return "<tr>{}</tr>".format("<td>{}</td>".format(
"</td><td>".join(
(a(line, 'host'), a(line, 'time'),
a(line, 'request'), a(line, 'status')
)
)
)).encode("utf-8")
Merci pour votre attention
Table of Contents | t |
---|---|
Exposé | ESC |
Full screen slides | e |
Presenter View | p |
Source Files | s |
Slide Numbers | n |
Toggle screen blanking | b |
Show/hide slide context | c |
Notes | 2 |
Help | h |