Archive de la catégorie «python2.5»

25 mars 2008 – vers un parser global

mars 25, 2008

14h19 : j’ai ecrit plusieurs parser pour différents format de fichiers.
ceux-ci sont toujours basé sur la meme structure. un objet item dans lequel seront stockés les info d’une ligne (ou d’un ensemble de lignes) et un objet collection qui rassemble l’ensemble des items.
l’objet collections gere a peu pret tout. cela dit c’est l’objet item qui parse les lignes individuelles mais ce n’est peut-etre pas une bonne idée.
en fait si on veut créer un super-parser d’ou hériteront tous les autres, c’est compliqué de travailler avec 2 classes qu’ils faudra manipuler.
meme si je préfère donner plus de responsabilité a la classe item, je vais bcp limiter celle-ci et les transferer sur la classe collections.
a moins que je puisse vraiment les donner a la classe item en lui transferant la fonction de parsing.
bon, il faut que je vois comment on passe des fonctions en argument.
en cherchant ca, j’ai trrouver un passage d’un livre sur les test de fonction.
il semble que ce soit très important. je vais donc essayer de travailler dans ce sens en créant des fonction de test de mon programme.
la fonction assert permet de faire les test facilement.
–> le passage d’une fonction en argument est on ne peut plus simple. je vais donc utiliser cela avec ma classe item.
bon, commençons ce parser de façon simple.
tout d’abord il faut créer une classe items qui sera ma collection d’item.
celle-ci va commencer par traiter un fichier ou un filehandle.
le nome du fichier ou l’instance du filehandle correspondant est passé en argument.
je met en place un système de gestion d’erreur pour tester la validité des fichiers présents.

class Items(list) :  '''  class Items  classe generique pour creer un parser.  cette classe est une collections d'objet item  '''  def __init__(self, file_arg):      '''      x = Items(filename) or x = Items(filehandle)      open and read the file to parse.      parse it according to the specified options      '''      try :          fhin = self.get_fhin(file_arg)      except TypeError, e :          print e          raise

      except IOError :          raise

  def get_fhin(self, file_arg):      '''      fhin = x.get_fhin(file_arg)      test the type of the argument, return the matching filehandle if any or raise a TypeError      '''      if type(file_arg) == file :          return file_arg      if type(file_arg) == str :          try :              return self.open_file(file_arg)          except IOError, e :              print e              raise

      raise TypeError, 'the argument is not a regular file object'

  def open_file(self, filename):      if not isfile(filename) :          raise IOError, "file %s doesn't exist" % filename      if not access(filename, R_OK) :          raise IOError, "permission denied for file %s" % filename      return open(filename, 'r')

j’implémente en meme temps un module de test pour ce programme. j’utilise le framework unittest qui est très simple.
pour cette première partie, dans lme module de test je créé d’abord un fichier a parser et je vérifie qu’il s’ouvre bien.
je teste aussi que les erreurs sont bien repérées.

from parser import *from string import joinimport unittestfrom os import remove, chmod

class parser_test_case(unittest.TestCase):  '''  Classe de test de parser.py.  herite de unittest.TestCase  execute automatiquement la methode setUp a l'initialisation  puis toute les fonctions commencant par test_  puis la methode tearDown (uniquement si setUp n'a pas renvoye d'erreur)  '''

  def setUp(self):      '''      mise en place des element de test      s'applique automatiquement la fin du test      '''      #definition d'un nom de fichier      self.filename = 'tmp_parse.txt'      #creation du fichier temporaire      self.create_tmp_file(filename = self.filename)

  def tearDown(self):      '''      nettoyage des elements mis en place dans le setup      s'applique automatiquement la fin du test      '''      #effece le fichier cree dans le setup      remove(self.filename)   

  def test_read_file(self):      '''      fonction test de lecture de fichier           '''      #ouvre le fichier qui a ete cree dans le setup en lecture      fhin = open(self.filename)      #cree une instance de items avec ce filehandle      test_item = Items(fhin)      #teste si une instance de Items a bien ete cree      self.assert_(isinstance(test_item, Items))      #ferme le fichier      fhin.close()      #cree une instance de items en passant juste le nom de fichier      test_item = Items(self.filename)      #teste si une instance de Items a bien ete cree      self.assert_(isinstance(test_item, Items))      #teste si le passage d'un argument du mauvais type renvoie bien l'erreur      self.assertRaises(TypeError, Items, 2)      #teste si l'ouverture d'un fichier inexistant renvoie une erreur      self.assertRaises(IOError, Items, 'toto')      #retire tous les droits d'acces au fichier cree      chmod(self.filename, 000)      #test si l'ouverture de fichier sans droit de lecture renvoie uen erreur      self.assertRaises(IOError, Items, self.filename)

  def write_header(self, fhout):      '''      x.write_header(fhout)      ecriture d'un header dans le fichier      '''      #defini le header      header = '#header line'      #ecrit le header dans le fichier      print >>fhout,header

  def write_line(self, fhout, values):      '''      x.write_line(fhout, values)      ecriture des valeurs contenues dans une collection (values)      sous la forme d'une ligne dons les element sont separes par une tabulation      '''      #concatene les valeurs de la liste      line = join(values, '\t')      #ecrit la ligne dans le fichier      print >>fhout, line

  def write_data(self, fhout):      '''      x.write_data(fhout)      ecrit le contenu du fichier      '''      #definit les intitules des colonnes           attr = ('col1', 'col2')      #defini les valeurs sous forme d'une liste de liste. chaque sous-liste correspond a une ligne      data = (              ('data1.1',),              ('data1.2',),              )      #appel la fonction write_line pour ecrire les noms des colonnes      self.write_line(fhout, attr)      #map sur la liste data pour appeler sur chaque element la fonction write_line      map(lambda x :  self.write_line(fhout, x), data)

  def create_tmp_file(self, filename):      '''      x.create_tmp_file(filename)      creation d'un fichier qui sera parse      '''      # creation du fichier      fhout = open(filename,'w')      #appel de la fonction de creation du header      self.write_header(fhout)      #appel de la fonction de creation du corp du fichier      self.write_data(fhout)      #fermetur du fichier      fhout.close()

if __name__ == '__main__' :  #lance tous les classes de test du module  unittest.main()

je recupère en sortie

the argument is not a regular file objectfile toto doesn't existpermission denied for file tmp_parse.txt.----------------------------------------------------------------------Ran 1 test in 0.001s

OK

j’ai donc maintenant une objet capable d’ouvrir un fichier ou de récupéré un filehandle. bon, c’est pas très avancé pour l’instant.
une fois qu’on a le filehandle, il faut donc le parcourir.
on a plusieurs choix. parcourir tout le ficher, mettre les ligne dans une structure et y revenir après, ou analyser chaque ligne et faire l’action qui va bien en fonction de ce qu’on voit.
la première méthode permet un parsing plus complexe car on peut ainsi analyser les lignes assez finement mais ca necessite de stocker les info dans une liste qui peut s’averer grosse.
la deuxième n’a pas cet inconvénient mias necessite de faire le parsing a la volée, il faut donc que toutes les actions soient définies avant.
partons sur la méthode utilisant moins de mémoire. on a plusieurs chose a retrouver.
il y a donc la partie header, la liste des colonnes et les valeurs.
mais ca c’est en supposant que le fichier est toujours sous cette forme, des meta-données en haut, puis un tableau avec le nom des colonnes dans la première ligne.
c’est vrai que c’est comme ca dans la majorité des cas. on peu imaginer que pour des fichiers très différents on fera un parser très différent.
maintenant il se pose un autre problème. mers teste sur les fichier gal m’ont montré qu’il était plus intéressant pour ce type de fichier de savoir le nombre de ligne du header (qui est indiqué dans la 2eme ligne du fichier). du coup le parsing du header se fait sur un nb de ligne définit. mais ce n’est pas possible dans les cas plus généraux.
a mon avis il faut que les méthodes de parsing soient modifiables ou alors que simplement surclasser les méthodes dans les classes spécifique du fichier gal.
ca semble plutot pas mal de surclasser.
dans ce cas, ca permet un peu de liberté et surtout ca evite d’avoir a penser à des méthodes trop générales.
reprennons. il nous faut une méthode browse qui va parcourir le fichier et extraire chaque ligne. on peut imaginer une méthode spéciale qui précederai le browse pour recuperer des infos dans les première lignes. ca serait une action optionnelle un peu dans le genre du setup de testCase.
le browse que j’utilise jusqu’a present tire parti des list comprehension ce qui permet un parcours rapide du fichier et créé une liste qui va etre ajoutée a iItems (qui herite du format list).
le probleme est que si je fais ca sur tout le fichier, il va y avoir des lignes a traiter different et que je ne veux pas ajouter dans la liste.
il me faut un systeme pour trier les ligne dans le liste comprehension, le tri va alors renvoyer vers une methode traitant les ligne de headers. c’est pourtant pas tout a fait correcte de faire ca.

20 février 2008 – installation du module pg pour python2.5 sur berthemorisot

février 20, 2008

14h59: comme rien n’est jamais simple, il est impossible d’utiliser python2.5 par défaut sur berthemorisot a cause de CENTOS qui nécessite python2.3 pour fonctionner (on croit réver).
mais comme python2.5 est en génral plus rapide que le 2.3 et gère mieux certaines chose, c qd meme pas mal de l’utilser si possible.
il est installé dans ~/tools/python-2.5.0/ et on l’appelle par la commande ~>python2.5
je veux utiliser PyGreSQL dans python2.5. il me faut donc l’installer car il n’est pas par défaut. et bien sur pas possible de passer par les rpm ou yum.
donc deja il faut aller chercher le fichier sur http://www.pygresql.org/ et télécharger ftp://ftp.pygresql.org/pub/distrib/PyGreSQL.tgz
moi j’ai copié l’archive dans ~/tools/python-2.5.0/lib/python2.5/site-package/ mais je ne suis pas sur que ce soit totalement nécésssaire.
là j’ai dezipé l’archive
~>gunzip PyGreSQL.tgz
et dearchivé
~>tar -vxf PyGreSQL.tar
j’ai alors un répertoire PyGreSQL-3.8.1 ou je me rend
je suis les instruction d’installation et je lance la commande ~>python2.5 setyp.py build et la bien sur j’ai une erreur

sh: pg_config: command not found
Traceback (most recent call last):
File "setup.py", line 81, in
pg_include_dir = pg_config('includedir')
File "setup.py", line 51, in pg_config
raise Exception, "pg_config tool is not available."
Exception: pg_config tool is not available.

en fait le programme appelle pg_config (fourni avec la base pgresql) sauf que ce programme n’est pas accessible ainsi. il faut donner son chemin complet /usr/local/pgsql/bin/pg_config et la ca marche
il y a qq erreur mais ca a l’air de marcher qd meme
puis on fait ~>python2.5 setup.py install
encore qq erreur mais ca semble marcher qd meme
et puis je lance python2.5 et je tape import pg
et là miracle, ca marche
donc c cool, ca a fonctionner sans trop de galère. mais j’ai peur qd meme de voir apparaitre qq erreur a l’utilisation, enfin on verra