vendredi 18 septembre 2015

Inspection des propriétés - petite technique pratique

Voici une petite technique intéressante que vous pouvez utiliser dans un python Interactif (comme IEP par exemple).

Cela fonctionne avec tous les objets... mais dans mon cas, c'est avec un module que j'ai utilisé cette technique. Dans l'article précédent, je voulais recharger le module model.py ... mais je voulais aussi m'assurer que mes objets user_table, group_table, permission_table, etc étaient maintenant disponible!

Les petits trucs à retenir de cet article seront
  [ attr for attr in dir(m) if '_table' in attr]
  [ '%s : %s' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]

Mais pour les plus curieux, je vous propose de lire les différentes étapes.

1) Récupérer la référence vers module
import sys
m = sys.modules['model']

2) Faire un dir() --> inefficace
Faire un dir() sur un module (ou un objet) peut retourner un contenu fastidieux à parcourir suivant la taille de l'objet. Dans le cas ci-présent, il n'y a pas moins de 193 entrées! Gloups!

dir( m )
Out[67]: 
['AliasOption',
 'AttributeExtension',
 'BIGINT',
 'BINARY',
 'BLOB',
 ...
 'with_parent',
 'with_polymorphic']

In [68]: len( dir(m) )
Out[68]: 193

3) Utilisons une compréhension list
Avec une simple compréhension liste basé sur dir(), nous allons pouvoir filtrer le contenu retourné à l'aide de l'instruction if.
Comme dir() liste les attribut, il suffit de tester si la chaine de caractère '_table' apparaît dans le nom de l'attribut.
C'est magique :-)

[ attr for attr in dir(m) if '_table' in attr]
Out[69]: 
['group_permission_table',
 'group_table',
 'permission_table',
 'user_group_table',
 'user_table']

4) Avec les valeurs svp
Avoir les noms des attributs c'est bien, avec leur valeur c'est encore mieux!
Cette fois-ci, nous utilisons getattr() pour obtenir la valeur et nous justifions avec ljust() pour le confort de lecture.

In [72]: [ '%s : %s' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]
Out[72]: 
['group_permission_table    : tf_group_permission',
 'group_table               : tf_group',
 'permission_table          : tf_permission',
 'user_group_table          : tf_user_group',
 'user_table                : tf_user']

Attention, c'est uniquement pour faire de l'investigation en live!!!

5) Avec le représentation des valeurs
Si vous jouez avec python depuis un moment, vous avez certainement déjà rencontré la fonction __repr()__. Cette dernière est utilisée par l'interpréteur pour afficher plus de détails  sur les objets (et pas uniquement leur adresse).
Si cela est pratique, cela peut aussi fournir du contenu très... touffu selon les circonstances.

Cette version du script remplace uniquement un des %s par %r (pour demander l'affichage de la "représentation").

In [73]: [ '%s : %r' % (attr.ljust(25), getattr(m,attr)) for attr in dir(m) if '_table' in attr ]
Out[73]: 
["group_permission_table    : Table('tf_group_permission', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id_permission', Integer(), ForeignKey('tf_permission.id'), table=), Column('id_group', Integer(), ForeignKey('tf_group.id'), table=), schema=None)",
 "group_table               : Table('tf_group', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('group_name', Unicode(length=16), table=, nullable=False), schema=None)",
 "permission_table          : Table('tf_permission', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('permission_name', Unicode(length=16), table=, nullable=False), schema=None)",
 "user_group_table          : Table('tf_user_group', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('user_id', Integer(), ForeignKey('tf_user.id'), table=, primary_key=True, nullable=False), Column('group_id', Integer(), ForeignKey('tf_group.id'), table=, primary_key=True, nullable=False), schema=None)",
 "user_table                : Table('tf_user', MetaData(bind=Engine(postgresql://dom:***@hp-minty/dom)), Column('id', Integer(), table=, primary_key=True, nullable=False), Column('user_name', Unicode(length=16), table=, nullable=False), Column('password', Unicode(length=40), table=, nullable=False), Column('display_name', Unicode(length=255), table=, default=ColumnDefault('')), Column('created', DateTime(), table=, default=ColumnDefault(. at 0x04560B70>)), schema=None)"]
Comme vous pouvez le constaté, c'est nettement plus chargé... et avec le retour à la ligne, c'est franchement illisible)
Comme quoi "le mieux est parfois l'ennemi du bien".

Voila, Happy Python Hacking ;-)

Aucun commentaire: