Projet

Général

Profil

DocumentationCodeBonnesPratiques » Historique » Version 28

Philippe Caseiro, 25/04/2014 11:33

1 20 Daniel Dehennin
{{toc}}
2 20 Daniel Dehennin
3 1 Emmanuel GARETTE
h1. DocumentationCodeBonnesPratiques
4 1 Emmanuel GARETTE
5 18 Gwenael Remond
**cette page concerne la documentation développeur**
6 18 Gwenael Remond
(voir aussi [[TestsCodeBonnesPratiques]] pour les autres bonnes pratiques développeur)
7 18 Gwenael Remond
8 1 Emmanuel GARETTE
h2. Généralité
9 1 Emmanuel GARETTE
10 1 Emmanuel GARETTE
* La documentation technique doit être en anglais.
11 5 Gérald Schwartzmann
* Elle est placée dans les docstrings du code.
12 1 Emmanuel GARETTE
13 1 Emmanuel GARETTE
h2. Description du module
14 1 Emmanuel GARETTE
15 12 Daniel Dehennin
La docstring du module est placée en haut du fichier juste après le "shebang":https://fr.wikipedia.org/wiki/Shebang, la déclaration d’"encodage des caractères":https://fr.wikipedia.org/wiki/Codage_de_caract%C3%A8res et la licence du code.
16 2 Gwenael Remond
17 1 Emmanuel GARETTE
<pre>
18 12 Daniel Dehennin
#!/usr/bin/python
19 12 Daniel Dehennin
# -*- coding: utf-8 -*-
20 12 Daniel Dehennin
#
21 16 Emmanuel GARETTE
##########################################################################
22 16 Emmanuel GARETTE
# <nom du projet>
23 19 Daniel Dehennin
# Copyright © 2013 Pôle de compétences EOLE <eole@ac-dijon.fr>
24 14 Daniel Dehennin
#
25 16 Emmanuel GARETTE
# License CeCILL:
26 16 Emmanuel GARETTE
#  * in french: http://www.cecill.info/licences/Licence_CeCILL_V2-fr.html
27 16 Emmanuel GARETTE
#  * in english http://www.cecill.info/licences/Licence_CeCILL_V2-en.html
28 16 Emmanuel GARETTE
##########################################################################
29 2 Gwenael Remond
</pre>
30 2 Gwenael Remond
31 11 Daniel Dehennin
La docstring doit contenir :
32 1 Emmanuel GARETTE
* description rapide en une phrase du module ;
33 1 Emmanuel GARETTE
* description avancée qui explique le rôle du module ;
34 1 Emmanuel GARETTE
* des exemples simples d'utilisation.
35 1 Emmanuel GARETTE
36 1 Emmanuel GARETTE
Les exemples peuvent être un prompt :
37 1 Emmanuel GARETTE
38 1 Emmanuel GARETTE
<pre>
39 1 Emmanuel GARETTE
    >>> is_locked()
40 1 Emmanuel GARETTE
    True
41 1 Emmanuel GARETTE
</pre>
42 1 Emmanuel GARETTE
43 1 Emmanuel GARETTE
ou une portion de code :
44 1 Emmanuel GARETTE
45 1 Emmanuel GARETTE
<pre>
46 1 Emmanuel GARETTE
    ::
47 1 Emmanuel GARETTE
48 1 Emmanuel GARETTE
        from toto.titi import is_locked
49 1 Emmanuel GARETTE
50 1 Emmanuel GARETTE
        toto()
51 1 Emmanuel GARETTE
        is_locked()
52 1 Emmanuel GARETTE
</pre>
53 1 Emmanuel GARETTE
54 1 Emmanuel GARETTE
h2. Docstring des méthodes
55 1 Emmanuel GARETTE
56 3 Gwenael Remond
Seules les méthodes publiques (qui ne commencent pas par "_") seront mises dans la documentation (mais les méthodes privées sont aussi à documenter).
57 1 Emmanuel GARETTE
58 1 Emmanuel GARETTE
La docstring doit contenir :
59 1 Emmanuel GARETTE
60 1 Emmanuel GARETTE
* description rapide en une phrase de la méthode ;
61 1 Emmanuel GARETTE
* si nécessaire une description avancée qui explique le rôle du module ;
62 1 Emmanuel GARETTE
* si nécessaire des exemples simples d'utilisation.
63 1 Emmanuel GARETTE
* les paramètres de la façon suivante (xxx étant le nom du paramètre et yyyyyy la description) :
64 1 Emmanuel GARETTE
65 1 Emmanuel GARETTE
<pre>
66 1 Emmanuel GARETTE
    :param xxx: yyyyyy
67 1 Emmanuel GARETTE
</pre>
68 1 Emmanuel GARETTE
69 1 Emmanuel GARETTE
Si nécessaire les valeurs de retour (yyyyyy étant la description) :
70 1 Emmanuel GARETTE
71 1 Emmanuel GARETTE
<pre>
72 1 Emmanuel GARETTE
    :return: yyyyyy
73 1 Emmanuel GARETTE
</pre>
74 1 Emmanuel GARETTE
75 4 Gwenael Remond
plus d'info sur la syntaxe des docstrings: http://sphinx-doc.org/domains.html#info-field-lists
76 4 Gwenael Remond
77 1 Emmanuel GARETTE
h2. Docstring des classes
78 1 Emmanuel GARETTE
79 10 Gwenael Remond
comme pour la docstring des modules (mais sans la licence)
80 8 Gwenael Remond
81 8 Gwenael Remond
h2. Tips
82 8 Gwenael Remond
83 8 Gwenael Remond
il est possible de faire un lien portant directement sur le redmine de deux manières :
84 8 Gwenael Remond
85 8 Gwenael Remond
<pre> 
86 8 Gwenael Remond
87 8 Gwenael Remond
:issue:`410` 
88 8 Gwenael Remond
89 8 Gwenael Remond
</pre> 
90 8 Gwenael Remond
91 8 Gwenael Remond
est un lien qui porte directement sur la demande 410
92 8 Gwenael Remond
93 8 Gwenael Remond
et
94 8 Gwenael Remond
95 8 Gwenael Remond
<pre>
96 8 Gwenael Remond
97 8 Gwenael Remond
 :eole:`/projects/creole/wiki/Lock24`
98 8 Gwenael Remond
99 8 Gwenael Remond
</pre>
100 8 Gwenael Remond
101 8 Gwenael Remond
qui est simplement un raccourci vers http://dev-eole.ac-dijon.fr/projects/creole/wiki/Lock24
102 17 Daniel Dehennin
103 17 Daniel Dehennin
h2. Exemples
104 17 Daniel Dehennin
105 17 Daniel Dehennin
Plusieurs fichiers sources ont reçu une attention assez particulière sur la documentation :
106 17 Daniel Dehennin
107 17 Daniel Dehennin
- python-pyeole:source:pyeole/decorator.py
108 17 Daniel Dehennin
- python-pyeole:source:pyeole/log.py
109 17 Daniel Dehennin
- python-pyeole:source:pyeole/lock.py
110 21 Philippe Caseiro
111 21 Philippe Caseiro
h2. Traduction et python
112 22 Philippe Caseiro
113 24 Philippe Caseiro
114 24 Philippe Caseiro
115 22 Philippe Caseiro
Voici un petit aide mémoire pour traduire une application python de la manière la plus simple possible.
116 22 Philippe Caseiro
117 26 Philippe Caseiro
Pour traduire une application il faut en premier avant tout une bibliothèque qui gère les traductions, la première
118 26 Philippe Caseiro
qui viens à l'esprit est _gettext_, donc logiquement nous allons nous tourner vers _python-gettext_.
119 22 Philippe Caseiro
120 26 Philippe Caseiro
Il faut noter qu'il est important de faire les choses dans l'ordre, les messages par défaut des applications doivent être dans *une seule et unique langue*, l'usage 
121 22 Philippe Caseiro
est de tout écrire en anglais et ensuite de mettre une traduction pour les autres langues.
122 22 Philippe Caseiro
123 26 Philippe Caseiro
Le principe de gettext est de remplacer tous les messages par l'appel à une "fonction/méthode/whatever" qui vas
124 22 Philippe Caseiro
faire les opérations de traduction.
125 22 Philippe Caseiro
126 26 Philippe Caseiro
Dans le monde _gettext_ cette "fonction" a pour nom '_'. 
127 22 Philippe Caseiro
128 26 Philippe Caseiro
La première chose à faire est de mettre a disposition une "fonction" '_' dans votre application python. (Les exemples suivants proviennent de pyeole)
129 22 Philippe Caseiro
130 22 Philippe Caseiro
h3. i18n.py
131 22 Philippe Caseiro
132 26 Philippe Caseiro
On crée un "module" i18n.py (mais il pourrais très bien avoir un autre nom) et on y fait les déclarations et les initialisations nécessaires pour la traduction.
133 22 Philippe Caseiro
134 26 Philippe Caseiro
* Dans ce module on importe gettext et les modules nécessaires à la gestion de la traduction (python-gettext)
135 22 Philippe Caseiro
<pre>
136 22 Philippe Caseiro
import gettext # Gettext lui même
137 22 Philippe Caseiro
import os      # Pour les variables d'environement LANG/LC_ALL
138 22 Philippe Caseiro
import sys     # Pour la gestion des fichiers de traduction
139 22 Philippe Caseiro
import locale  # Pour la gestion des "locale"
140 22 Philippe Caseiro
</pre>
141 22 Philippe Caseiro
* On définis le nom du domaine de traduction (le plus souvent le nom de l'application)
142 22 Philippe Caseiro
<pre>
143 22 Philippe Caseiro
APP_NAME = 'pyeole'
144 22 Philippe Caseiro
</pre>
145 22 Philippe Caseiro
* On récupère les répertoires qui vont contentir les traductions
146 22 Philippe Caseiro
<pre>
147 22 Philippe Caseiro
# Traduction dir
148 22 Philippe Caseiro
APP_DIR = os.path.join(sys.prefix, 'share')
149 22 Philippe Caseiro
LOCALE_DIR = os.path.join(APP_DIR, 'locale')
150 22 Philippe Caseiro
</pre>
151 22 Philippe Caseiro
* On récupère la langue par défaut et on y ajoute l'anglais pour les messages non traduits
152 23 Philippe Caseiro
<pre>
153 22 Philippe Caseiro
# Default Lanugage
154 22 Philippe Caseiro
DEFAULT_LANG = os.environ.get('LANG', '').split(':')
155 1 Emmanuel GARETTE
DEFAULT_LANG += ['en_US']
156 23 Philippe Caseiro
</pre>
157 23 Philippe Caseiro
* On définis la langue par défaut (je ne suis pas certain que cette partie ne soit pas inutile :)
158 23 Philippe Caseiro
<pre>
159 22 Philippe Caseiro
languages = []
160 22 Philippe Caseiro
lc, encoding = locale.getdefaultlocale()
161 22 Philippe Caseiro
if lc:
162 22 Philippe Caseiro
    languages = [lc]
163 1 Emmanuel GARETTE
164 1 Emmanuel GARETTE
languages += DEFAULT_LANG
165 23 Philippe Caseiro
</pre>
166 23 Philippe Caseiro
* On fait les initialisations gettext
167 23 Philippe Caseiro
<pre>
168 1 Emmanuel GARETTE
mo_location = LOCALE_DIR
169 1 Emmanuel GARETTE
170 23 Philippe Caseiro
gettext.find(APP_NAME, mo_location) # On cherche les traductions
171 23 Philippe Caseiro
gettext.textdomain(APP_NAME)        # On crée le domaine de traduction (ou il vas chercher les messages)
172 23 Philippe Caseiro
gettext.bind_textdomain_codeset(APP_NAME, "UTF-8") # On définis le jeux de caracètres des traductions (utf8)
173 23 Philippe Caseiro
</pre>
174 23 Philippe Caseiro
* On initialise l'objet qui vas réaliser les traductions pour notre application 
175 23 Philippe Caseiro
<pre>
176 22 Philippe Caseiro
t = gettext.translation(APP_NAME, fallback=True)
177 23 Philippe Caseiro
</pre>
178 23 Philippe Caseiro
* Enfin on créer la "fonction/methode/whatever" '_' :
179 23 Philippe Caseiro
<pre>
180 1 Emmanuel GARETTE
def _(msg):
181 23 Philippe Caseiro
    return t.ugettext(msg) # on retourne le résultat de la traduction ! ugettext peut poser problème il ne prend que des chaines utf8
182 1 Emmanuel GARETTE
</pre>
183 24 Philippe Caseiro
184 24 Philippe Caseiro
Vous n'avez plus qu'a importer la "fonction/méthode" '_' dans vos fichier .py et l'utiliser
185 24 Philippe Caseiro
186 24 Philippe Caseiro
Exemple : 
187 24 Philippe Caseiro
<pre>
188 24 Philippe Caseiro
from pyeole.translation.i18n import _   # j'ai mis le module i18n.py dans un répertoire spécifique (pour le plaisir)
189 24 Philippe Caseiro
190 24 Philippe Caseiro
....
191 24 Philippe Caseiro
192 24 Philippe Caseiro
msg = _(u'Install: {0}.')
193 24 Philippe Caseiro
log.info(msg.format(u', '.join(to_install)))
194 24 Philippe Caseiro
</pre>
195 24 Philippe Caseiro
196 24 Philippe Caseiro
h3. let's translate
197 24 Philippe Caseiro
198 27 Philippe Caseiro
Une fois le code en place il faut créer les fichiers de traduction. Il existe plusieurs utilitaires pour faire de la traduction, personnellement j'utilise "poedit" (ou vim :)
199 24 Philippe Caseiro
200 27 Philippe Caseiro
Il faut générer le fichier de référence des messages, pour ce faire on utilise la commande _xgettext_. 
201 1 Emmanuel GARETTE
202 27 Philippe Caseiro
On place les fichiers de traductions dans un répertoire *translation* à la racine du dépôt.
203 27 Philippe Caseiro
204 1 Emmanuel GARETTE
<pre>
205 28 Philippe Caseiro
dev@host:~ $ cd code/pyeole 
206 28 Philippe Caseiro
dev@host:~/code/pyeole $ # mkdir translation
207 28 Philippe Caseiro
dev@host:~/code/pyeole cd translation
208 28 Philippe Caseiro
dev@host:~/code/pyeole/translation # xgettext --language=Python --keyword=_  --output=pyeole-next.pot  --from-code=UTF-8 `find ../pyeole -name "*.py"`
209 24 Philippe Caseiro
</pre>
210 24 Philippe Caseiro
211 27 Philippe Caseiro
Attention il est possible que _xgettext_ plante a cause de certaines chaines de commentaire.
212 24 Philippe Caseiro
213 27 Philippe Caseiro
Une fois que la commande _xgettext_ ce termine elle génère un fichier ".pot" 
214 24 Philippe Caseiro
215 24 Philippe Caseiro
Pour générer les traductions on utilise poedit.
216 24 Philippe Caseiro
217 24 Philippe Caseiro
# Lancer poedit 
218 24 Philippe Caseiro
# Fichier --> Nouveau depuis un fichier POT/PO
219 24 Philippe Caseiro
# Ouvrir votre fichier .pot
220 24 Philippe Caseiro
# Séléctionner la langue de traduction
221 24 Philippe Caseiro
# Traduire les chaines disponibles 
222 24 Philippe Caseiro
# Sauvegarder le fichier de traduction dans le répertoire translation/<CODE_LANGUE>/<mon_appli>.po (translation/fr/pyeole.po)
223 24 Philippe Caseiro
224 24 Philippe Caseiro
h3. plus qu'a installer
225 25 Philippe Caseiro
226 25 Philippe Caseiro
Pour que les fichiers de traductions soit pris en compte par la génération du paquet il faut faire des mise à jour dans le setup.py.
227 25 Philippe Caseiro
228 25 Philippe Caseiro
On créer ajoute une petite fonction qui vas créer les fichier de traduction a partir des .po et oui les .po ne sont pas les fichiers 
229 25 Philippe Caseiro
utilisés par gettext mais juste leur "code source". Il faut impérativement que la traduction soit la plus rapide possible donc qui dit
230 25 Philippe Caseiro
rapide dit fichiers binaires. 
231 25 Philippe Caseiro
232 25 Philippe Caseiro
Voici le code qui gènère les .mo
233 25 Philippe Caseiro
234 25 Philippe Caseiro
<pre>
235 25 Philippe Caseiro
trad_root = "share/locale"
236 25 Philippe Caseiro
trad_dir = "LC_MESSAGES"
237 25 Philippe Caseiro
here = dirname(abspath(__file__))
238 25 Philippe Caseiro
239 25 Philippe Caseiro
def build_translation():
240 25 Philippe Caseiro
   po_path = join(here, "translation")
241 25 Philippe Caseiro
   result = []
242 25 Philippe Caseiro
243 25 Philippe Caseiro
   for top, dirs, files in walk(po_path):
244 25 Philippe Caseiro
       for directory in dirs:
245 25 Philippe Caseiro
           tr_dir = join(top, directory)
246 25 Philippe Caseiro
           # Search for .po file for a traduction
247 25 Philippe Caseiro
           trad_files = []
248 25 Philippe Caseiro
           for pot_file in glob(join(tr_dir, "*.po")):
249 25 Philippe Caseiro
               mo_file = splitext(pot_file)[0] + ".mo"
250 25 Philippe Caseiro
               lang = basename(tr_dir)
251 25 Philippe Caseiro
               cmd = ["msgfmt", pot_file, "-o", mo_file]
252 25 Philippe Caseiro
               dest = join(trad_root, lang, trad_dir)
253 25 Philippe Caseiro
               trad_files.append(join("translation", lang, basename(mo_file)))
254 25 Philippe Caseiro
               call(cmd)
255 25 Philippe Caseiro
           result.append((dest, trad_files))
256 25 Philippe Caseiro
257 25 Philippe Caseiro
   return result
258 25 Philippe Caseiro
259 25 Philippe Caseiro
translations = build_translation()
260 25 Philippe Caseiro
</pre>
261 25 Philippe Caseiro
262 25 Philippe Caseiro
Voici les directives supplémentaires pour la fonction setup()
263 25 Philippe Caseiro
264 25 Philippe Caseiro
<pre>
265 25 Philippe Caseiro
    data_files = translations
266 25 Philippe Caseiro
<pre>
267 25 Philippe Caseiro
268 25 Philippe Caseiro
269 25 Philippe Caseiro
h2. Des questions ?
270 25 Philippe Caseiro
271 25 Philippe Caseiro
Voila une fois que vous avez fait toutes ces opérations votre appli doit parler d'autres langues.
272 24 Philippe Caseiro
273 1 Emmanuel GARETTE
Si vous avez des questions cette partie à été écrite par Philippe "Puppet_Master" Caseiro.