Formatter du code

Il est important de pouvoir suivre des règles dans l’écriture de code. Cela permet notamment une lecture (ou relecture) de code plus simple et de pouvoir partager avec d’autres développeur le travail réalisé. Il me parait indispensable lors d’une revue de code d’avoir un code formatté.

Heureusement il existe de nombreux outils qui facilite ce travail.

Note

Il est possible de réaliser le “format” de notre code via notre éditeur préféré vim, puisque ce dernier permet de lancer des commandes depuis son interface

Nous allons traiter les principaux languages que j’utilise

XML

Il existe un utilitaire pour traiter du code xml xmllint. Il existe une version windows (non testé) sur le site http://code.google.com/p/xmllint/

Pour debian son installation est simple

apt-get install libxml2-utils

Concernant son utilisation, nous avons un fichier nommé toto.xml contenant

<tutu><a>a</a><b>b</b></tutu>

on traite le fichier par xmllin

xmllint --format toto.xml --output toto.xml

et on obtient un fichier xml contenant

<?xml version="1.0"?>
<tutu>
    <a>a</a>
    <b>b</b>
</tutu>

Note

xmllint fait beaucoup plus, il peut utiliser un xpath pour faire de la recherche dans les fichiers xml

xmllint --xpath "/bookstore/book/price" books.xml

Java

Il existe une librairie java externe qui réalise cette tâche: jalopy.

Il est hebergé sur sourceforge et il existe sous forme de plugin pour eclipse par exemple.

son installation

wget http://sourceforge.net/projects/jalopy/files/plugin%20console/0.1-1.5rc3/jalopy-console-0.1-1.5rc3.zip/download
mv download jalopy-1.5.zip
unzip jalopy-1.5.zip
cd jalopy-console-0.1-1.5rc3

On peut obtenir le manuel de jalopy sur http://jalopy.sourceforge.net/existing/manual.html

On trouve deux fichier pour lancer jalopy :

  • un fichier sh pour linux
  • un fichier bat pour windows

mais cela revien à faire

java -cp lib/jalopy.jar:lib/jalopy-console.jar:lib/log4j.jar:lib/getopt.jar de.hunsicker.jalopy.plugin.console.ConsolePlugin -v

Note

on peut faire plus simple concernant cette ligne de commande

java -classpath lib/* de.hunsicker.jalopy.plugin.console.ConsolePlugin -v

Prenons l’exemple du fichier test/Test.java contenant le code

public class Test {
private String x = "hello ";
public String getText(String name) {
string s = x + name;return s;}}

Note

ce code est valide, java ne gère pas d’indentation

Nous allons traiter ce fichier par jalopy

java -cp lib/jalopy.jar:lib/jalopy-console.jar:lib/log4j.jar:lib/getopt.jar de.hunsicker.jalopy.plugin.console.ConsolePlugin test/Test.java

et nous obtenons

public class Test {
    private String x = "hello ";

    public String getText(String name) {
        string s = x + name;

        return s;
    }
}

On peut traiter un répertoire entier via l’argument

jalopy -r myDir

On peut aussi faire que le traitement n’écrase pas les originaux

jalopy -d /otherDir

Afin de rendre l’utilisation plus simple de jalopy on peut:

  • placer le soft dans /opt
  • ajouter un alias dans son .bashrc
alias jalopy="java -cp /opt/jalopy/lib/jalopy.jar:/opt/jalopy/lib/jalopy-console.jar:/opt/jalopy/lib/log4j.jar:/opt/jalopy/lib/getopt.jar de.hunsicker.jalopy.plugin.console.ConsolePlugin"

SQL

Pour sql on va utiliser un module python nommer sqlparse. Il fonctionne avec toutes versions de Python supérieur à 2.5

apt-get install python-pip
pip install sqlparse

Son utilisation est assez simple

import sqlparse

code="SELECT tutu,titi from toto"
r = sqlparse.format(code, reindent=True, keyword_case='upper')
print(r)

Python

Python possède son propre outil de contrôle qui valide le code python par rapport à la norme identifié dans le pep8 de python.

pip install pep8
pip install autopep8

Son utilisation est assez simple

import pep8
import autopep8

r = autopep8.fix_code(code)
print(r)

Pour la gestion de fichier en Python, XML et SQL sans passer par une console python, on peut utiliser le code suivant, placé dans le fichier prettycode.py

import sys
import locale
from optparse import OptionParser


VERSION="0.2"
PROG="prettycode"
DESCRIPTION="""format code"""
AUTHOR="Frederic Aoustin"

PARAMS_PARSER = {
    'sql' : "'reindent' : True, 'keyword_case' : 'upper'",
    'python' : "",
    'xml' : ""
}

is_Windows = sys.platform.startswith('win')
if is_Windows:
    import ctypes
    strcpy = ctypes.cdll.msvcrt.strcpy
    ocb = ctypes.windll.user32.OpenClipboard    #Basic Clipboard functions
    ecb = ctypes.windll.user32.EmptyClipboard
    gcd = ctypes.windll.user32.GetClipboardData
    scd = ctypes.windll.user32.SetClipboardData
    ccb = ctypes.windll.user32.CloseClipboard
    ga = ctypes.windll.kernel32.GlobalAlloc    # Global Memory allocation
    gl = ctypes.windll.kernel32.GlobalLock     # Global Memory Locking
    gul = ctypes.windll.kernel32.GlobalUnlock
    GMEM_DDESHARE = 0x2000

if sys.version_info < (3, 0):
    str = unicode
    import sqlparse
else:
    import sqlparse3 as sqlparse
import pep8
import autopep8

import xml.dom.minidom

if __name__ == '__main__':
    parser = OptionParser(version="%s %s" % (PROG,VERSION))
    parser.description= DESCRIPTION
    parser.epilog = AUTHOR
    parser.add_option("-i", "--file-in",
        dest = "ifile",
        help = "file format",
        default = None,
        type = "string")
    parser.add_option("-o", "--file-out",
        dest = "ofile",
        help = "file formatting",
        default = None ,
        type = "string")
    parser.add_option("-s", "--syntax",
        dest = "syntax",
        choices = ['sql','python','xml'],
        help ="syntax format",
        default = "sql",
        type = "choice")
    parser.add_option("-e", "--encoding",
        dest = "encoding",
        help = "encoding data (default: %s)" % locale.getdefaultlocale()[1],
        default = locale.getdefaultlocale()[1],
        type = "string")
    parser.add_option("-p", "--other-parameter",
        dest = "args",
        help = "add parameter for parser (%s)" % ('; '.join([i+' => '+ PARAMS_PARSER[i] for i in PARAMS_PARSER.keys()])) ,
        default = None,
        type = "string")
    try:
        (options, args) = parser.parse_args()
        if options.args == None:
            options.args = PARAMS_PARSER[options.syntax]
        exec("args = {%s}" % options.args)
        if options.ifile == None:
            if is_Windows:
                ocb(None)
                p_contents = gcd(1)
                code = str(ctypes.c_char_p(p_contents).value, options.encoding)
                ccb()
            else:
                raise AttributeError("file in is unknown")
        else:
            f = open(options.ifile,'r')
            code = ''.join(f.readlines())
            f.close()
        if options.syntax == 'sql':
            result_parser = sqlparse.format(code, **args)
        if options.syntax == 'python':
            result_parser = autopep8.fix_code(code, **args)
        if options.syntax == 'xml':
            xml = xml.dom.minidom.parseString(code)
            result_parser = xml.toprettyxml()
        if options.ofile != None:
            f = open(options.ofile,'w')#, encoding='utf-8')
            f.write(result_parser)
            f.close()
        else:
            print(result_parser)
    except Exception as e:
        print(parser.error(e))
        parser.print_help()
        sys.exit(1)

L’utilisation de ce programme est assez simple

python prettycode -i tutu.py -o tutu.py -s python