Outils de codage

Complexité de code avec McCabe

Alexandre GALODE

Parmi la liste d'outils dont dispose Python pour assurer du développement de qualité, PEP8, ou encore Pylint, sont probablement les plus connus.

Garants du respect de la PEP8 et de la PEP20, ces outils permettent de s'assurer du respect des bonnes règles de codage Python. Mais qu'en est-il de la complexité du code ? Il s'agit pourtant d'un paramètre important permettant de s'assurer de la pérennité de maintenance dans le temps.

McCabe (prononcez Mac Cabe) est l'outil Python permettant de répondre à cette problématique. Je vous propose de le découvrir plus en détail.

Commentez

- Titre : Outils de codage

- Auteur : Alexandre GALODE

- Parution : 18 septembre 2017

- Public visé :

- Licence : Le contenu de cet article est rédigé par Alexandre GALODE et est mis à disposition selon les termes de la Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 3.0 non transposé. Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright ® 2013 Developpez.com.

Sommaire

- I. Introduction

- II. Principe de fonctionnement

- II-A. Installation

- III. Utilisation basique

- IV. Utilisation avancé

- IV-A. Définir une limite de complexité

- IV-B. Générer un graphique

- IV-B-1. Génération directe d'une image

- IV-B-2. Manipulation avec Graphviz

- IV-C. Mixer les deux options précédentes

- V. Conclusion

- VI. Remerciements

I - Introduction

L'outil a été créé historiquement par Ned Batchelder, auteur entre autres de l'outil coverage, vers 2008.

L'outil d'alors se nommait codepaths.py.

En 2013, l'outil est refondu en McCabe par l'équipe de PyCQA (Python Code Quality Autority), laquelle met à disposition de la communauté tout un lot d'outils, dont Pylint ou encore Flake8.

Depuis lors, cet outil simple, robuste et fiable, ne fait l'objet que de quelques évolutions mineures. Il est par conséquent à considérer comme apte à la production.

Cet article est écrit pour Python3.x. Des différences peuvent exister avec Python2.x.

II - Principe de fonctionnement

L'outil McCabe permet de mesurer la complexité de code. Mais comment cela fonctionne-t-il ?

Cela repose sur la notion de " nombre cyclomatique ", également surnommé " complexité de McCabe ". Introduit par Thomas McCabe en 1976, il s'agit d'un calcul de métrique statique.

Totalement indépendant du langage, il consiste à calculer le nombre de chemins linéaires distincts au sein d'un code, autrement dit le nombre de branches conditionnelles d'un programme.

Pour simplifier, chaque instruction générant une bifurcation (if, for, while...), et donc un nouveau cas d'exécution, incrémente la valeur initiale (le compteur commence à 1). Cas particulier, le try/except incrémentera le compteur d'une unité pour chaque type d'exception défini.

Plus ce nombre sera grand, plus il y aura de cas d'exécution différents, et plus le code sera difficile à comprendre, mais également, de fait, à maintenir et à tester.

Il s'agit donc d'une métrique importante en environnement de test logiciel. Dans les faits, il faut par conséquent un nombre de tests unitaires au minimum égal à la complexité du code.

II-A - Installation

Pour l'installer rien de plus simple. Le paquet, au format Wheel, est disponible sur le Pypi. Aussi, il suffit juste de faire appel à "pip".

pip install mccabe

III - Utilisation basique

Par défaut, McCab vous remontera toutes les erreurs. La commande sera alors la suivante :

python -m mccabe <nom_script>.py

En sortie, vous disposerez alors des lignes posant problème, ainsi que de leur note de complexité. Exemple avec le code de l'outil lui-même :

$ python -m mccabe mccabe.py
("141:4: 'PathGraphingAstVisitor.visitClassDef'", 1)
("175:4: 'PathGraphingAstVisitor.visitIf'", 1)
("76:4: 'PathGraph.to_dot'", 4)
("55:4: 'PathNode.to_dot'", 1)
("113:4: 'PathGraphingAstVisitor.visitFunctionDef'", 3)
("292:0: 'get_module_complexity'", 1)
("71:4: 'PathGraph.connect'", 1)
("64:4: 'PathGraph.__init__'", 1)
("179:4: 'PathGraphingAstVisitor._subgraph'", 2)
("163:4: 'PathGraphingAstVisitor.default'", 2)
("85:4: 'PathGraph.complexity'", 1)
("105:4: 'PathGraphingAstVisitor.reset'", 1)
("99:4: 'PathGraphingAstVisitor.__init__'", 1)
("236:4: 'McCabeChecker.__init__'", 1)
("262:4: 'McCabeChecker.run'", 4)
("316:0: 'main'", 7)
("59:4: 'PathNode.dot_id'", 1)
("258:4: 'McCabeChecker.parse_options'", 1)
("43:4: 'ASTVisitor.preorder'", 1)
("192:4: 'PathGraphingAstVisitor._subgraph_parse'", 5)
("155:4: 'PathGraphingAstVisitor.visitSimpleStatement'", 2)
("33:4: 'ASTVisitor.dispatch'", 2)
("25:4: 'ASTVisitor.__init__'", 1)
('TryExcept 13', 3)
("214:4: 'PathGraphingAstVisitor.visitTryExcept'", 1)
("169:4: 'PathGraphingAstVisitor.visitLoop'", 1)
("147:4: 'PathGraphingAstVisitor.appendPathNode'", 2)
("273:0: 'get_code_complexity'", 5)
("299:0: '_read'", 5)
('If 346', 2)
("109:4: 'PathGraphingAstVisitor.dispatch_list'", 2)
("29:4: 'ASTVisitor.default'", 2)
("51:4: 'PathNode.__init__'", 1)
("220:4: 'PathGraphingAstVisitor.visitWith'", 1)
("239:4: 'McCabeChecker.add_options'", 2)

Le rendu de sortie peut différer selon les versions.

IV - Utilisation avancé

McCab étant un outil très simple, seules deux options sont disponibles.

IV-A - Définir une limite de complexité

Définie à 1, la limite par défaut peut s'avérer ne pas forcément correspondre à vos besoins. Pour contourner cette limitation, il faut utiliser l'option " --min ", en passant en paramètre la valeur minimale désirée.

python -m mccabe --min 5 <nom_script>.py

Par exemple, toujours avec mccabe.py :

$ python -m mccabe --min 5 mccabe.py
("316:0: 'main'", 7)
("192:4: 'PathGraphingAstVisitor._subgraph_parse'", 5)
("273:0: 'get_code_complexity'", 5)
("299:0: '_read'", 5)

IV-B - Générer un graphique

McCab vous offre la possibilité de générer un graphique des différents chemins analysés. Outre le fait de fournir une représentation graphique, plus parlante parfois que la sortie basique, cela permet de simplifier le travail de création des tests unitaires, puisqu'on visualise alors parfaitement tous les cas possibles.

python -m mccabe --dot <nom_script>.py

Ce faisant, vous verrez que le graph généré est au format " dot ". Il faut alors utiliser l'outil graphviz. Deux solutions s'offrent à vous :

- générer directement un graphique au format image ;

- manipuler le fichier dot avec Graphviz.

Quelle que soit la solution retenue, pour générer un fichier dot avec McCabe, il faut utiliser l'option " --dot ". Cependant, le fichier sera alors généré dans le terminal. Pour pallier cela, il faut rediriger la sortie vers un fichier.

python -m mccabe --dot <nom_script>.py >> out.dot

Graphviz doit être installé depuis pip, mais également depuis l'installeur dédié à votre OS, que vous pourrez trouver sur le site officiel de l'outil.

Une fois installé, il vous faudra modifier votre variable d'environnement PATH sous Windows, afin d'y ajouter le chemin vers le dossier « GraphvizX.YZ\bin ».

IV-B-1 - Génération directe d'une image

Il s'agira probablement pour vous du cas le plus fréquent. En effet, éditer le fichier dot n'a un intérêt que très limité et représente un besoin très spécifique. La ligne de commande, pour générer une image est très simple :

python -m mccabe --dot <nom_script>.py | dot -T<format_desire> <fichier_entree>.dot -o <fichier_sortie>.<format_desire>

Pour reprendre l'exemple mccabe.py, pour une sortie en png, cela donnerait :

python -m mccabe --dot mccabe.py | dot -Tpng -o mccabe.png

En sortie, nous aurions alors l'image suivante :

Le graphique généré fonctionne de la manière suivante : chaque bulle contient un texte décrivant son type (ligne (stmt pour statement) ou condition (if)…), ainsi que le numéro de la ligne concernée.

IV-B-2 - Manipulation avec Graphviz

Graphviz est un outil prenant des fichiers dot en entrée, permettant leur manipulation éventuelle, et pouvant générer un fichier image en sortie. Il supporte de nombreux formats, dont les suivants :

- eps ;

- gif ;

- jpg ;

- pdf ;

- png ;

- ps ;

- svg.

Sous windows, l'installation met à disposition un exécutable gvedit de manipulation. Sous Linux, il faut installer en sus de Graphviz, une interface graphique. Dans tous les cas cependant, la manipulation du graphique passe par la modification du contenu du fichier dot.

Les fichiers dot sont des fichiers texte écrit dans un langage permettant de définir des liens/nœuds qui ne se recouvrent pas et qui ne se croisent pas.

La manipulation de ces fichiers vous permettra alors de modifier, par exemple des formes, des couleurs ou des polices.

Une fois ce fichier généré et éventuellement modifié, il ne vous reste alors plus qu'à générer le fichier de sortie. Pour cela, toujours en ligne de commande, il suffit de lancer la commande suivante :

dot -T<format_desire> <fichier_entree>.dot -o <fichier_sortie>.<format_desire>

Par exemple :

dot -Tpng out.dot -o out.png

IV-C - Mixer les deux options précédentes

Une chose importante à savoir est qu'il est possible d'ajuster le contenu des graphiques générés en mixant les deux options précédemment vues.

Ainsi, rappelez-vous, la valeur par défaut de filtrage est de 1. Les deux commandes suivantes auront donc le même résultat :

python -m mccabe --dot mccabe.py | dot -Tpng -o mccabe.png
python -m mccabe --min 1 --dot mccabe.py | dot -Tpng -o mccabe.png

Le graphique généré sera alors le même que précédemment.

Maintenant, filtrons à une complexité de 5 :

python -m mccabe --min 5 --dot mccabe.py | dot -Tpng -o mccabe.png

Le nouveau graphique est le suivant :

Le schéma est d'ores et déjà plus simple, car il ne se concentre que sur certaines complexités uniquement.

V - Conclusion

Comme nous venons de le voir ensemble, McCab vient rejoindre la liste d'outils indispensables pour générer du code de qualité. En fournissant une métrique statique, il permet de disposer d'une référence fiable reposant sur du code, et non une évaluation relative au niveau d'un développeur donné qui analyserait le code.

Du point de vue communautaire, il permet de s'assurer que le code fourni sur un dépôt sera facilement réutilisable et maintenable par un tiers, afin d'obtenir, si besoin est, plus facilement de l'aide.

D'un point de vue professionnel, il permet de garantir à toute société que le code sera aisément maintenable par n'importe quel Pythoniste en interne, et cela même si le codeur originel ne fait plus partie des effectifs.

J'espère que cet article vous aura convaincu de l'utilité de cet outil, et que ce dernier vous aidera à l'avenir à améliorer et optimiser votre code.

VI - Remerciements

- Malick

- jacques_jean