Lexpage Porte Quoi    —  Jhiday

Discussions

C'est ici le helpdesk ... python me cherche des noises

Tchou 3555 Bob
Hello

Une fois n'est pas coutume, là je m'arrache les cheveux depuis quelques heures, et la solution est probablement juste là sous mes yeux, mais ...

Alors, voilà, j'ai créé un petit script, et je bute sur un putain de comportement chelou. Le but de ma routine est (entre autre) de prendre une valeur hexa via un prompt, prendre des fichiers svg existants, remplacer une valeur contenue dedans (la #123456) par la valeur choisie et les copier ailleurs avec la nouvelle couleur. Entre autres choses, mais les autres choses ne sont pas buggées (générer les fichiers compass avec les valeurs choisies, générer une page démo, les robots.txt, etc).

Alors, étape 1 : je rentre dans mes fichiers source svg, je les modifie dans le tampon, et les réécrit ailleurs :
shutil.copytree(dir_prog + 'images_src', 'tmpgen')
# boucler dans les images svg et remplacer les valeurs par défaut
# par la couleur choisie
for root, dirs, files in os.walk('tmpgen'):
for fichierSVG in files:
svg = open(root + '/' + fichierSVG, 'r')
svgContent = svg.read()
svg_modifie = svgContent.replace('#123456', couleur_site)
svg_modifie = svg_modifie.replace('XXtextXX', nom_site)
svg.close()
svg = open(root + '/' + fichierSVG, 'w')
svg.write(svg_modifie)
De là, j'ai 6 fichiers svg qui ont été copiés dans un répertoire temporaire nommé tmp_gen ... tout va bien.

Le soucis, c'est que si juste derrière je fais un bête os.system('ls -l tmpgen/') avant la fin du script, l'un des 6 fichiers a une taille différente de celle attendu. Le script s'arrête, sans aucune écriture ultérieure dans mon tete-de-lion.svg bien entendu, et si je ls -l mon répertoire, j'ai la taille correcte.

Mon script est sensé invoquer inkscape pour faire une transformation svg->png (génération de favicons), il me plante _toujours_ sur ce fichier et sur une ligne. Le même inkscape dans le script sur le fichier source fonctionne, le même inkscape hors du script sur le nouveau fichier fonctionne.
os.system('/Applications/Inkscape.app/Contents/Resources/bin/inkscape -z -e apple-touch-icon-120x120-precomposed.png -w 120 -h 120 tmpgen/tete-de-lion.svg')
Un soucis de fichier pas totalement écrit ? Nope, j'ai forcé un sleep de 5 secondes après la copie, ça change rien. Et c'est de mon disque dur à mon disque dur, une copie de quelques ko sur la même partition, ça prend pas si longtemps. Tant que je suis dans le script, le fichier fait 16384 octets. Quand le script a fini son taff (et alors qu'il n'a plus rien à faire sur le fichier), mon fichier fait 19413 octets (et est donc valide SVG) : WTF ?
Quelqu'un aurait-il une idée de ce qui se passe ? Je suis en train de m'arracher les cheveux, je vais pas tarder à m'attaquer à la barbe ! :)

edit : subsystem.call en lieu et place d'os.system ne résoud rien. Le soucis est vraiment qu'un fichier n'est pas intégralement écrit. Voici la totalité de mon code expurgé de beaucoup de choses :
#!/usr/bin/python
# -*- coding: utf-8 -*-

import re
import os
import shutil
import subprocess
import time

dir_prog = '/Users/fred/tools/chartepy/'
dir_inkscape = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'

couleur_site = '#623111'
nom_site = 'test'
choix_qualite = 'y'

# virer le repertoire temporaire des fichiers source :
shutil.rmtree('tmpgen')

shutil.copytree(dir_prog + 'images_src', 'tmpgen')
# boucler dans les images svg et remplacer les valeurs par défaut
# par la couleur choisie
for root, dirs, files in os.walk('tmpgen'):
for fichierSVG in files:
svg = open(root + '/' + fichierSVG, 'r')
svgContent = svg.read()
svg_modifie = svgContent.replace('#123456', couleur_site)
svg_modifie = svg_modifie.replace('XXtextXX', nom_site)
svg.close()
svg = open(root + '/' + fichierSVG, 'w')
svg.write(svg_modifie)

print("5 secondes de pause")
time.sleep(5)

os.system('ls -l tmpgen/tete-de-lion.svg')
Et ça donne :
[~/tmp]@Pom 
fred ? ~/tools/chartepy/test.py
5 secondes de pause
-rw-r--r-- 1 fred staff 16384 14 oct 10:51 tmpgen/tete-de-lion.svg

[~/tmp]@Pom
fred ? ls -al tmpgen/tete-de-lion.svg
-rw-r--r-- 1 fred staff 19413 14 oct 10:51 tmpgen/tete-de-lion.svg


Ce message a été modifié 2 fois. Dernière modification : 14 octobre 2014 à 10:56 par Tchou.

roidelapluie 339 Maitre jedi
Il te manque un svg.close()


Ce message a été modifié 1 fois. Dernière modification : 14 octobre 2014 à 11:04 par roidelapluie.

Tchou 3555 Bob
Putain de saloperie de merde de chié de putain !

Voilà pourquoi le pair programming c'est bien (même si j'ai des doutes sur celui proposé dans la vidéo), c'est toujours les trucs les plus cons qui sont sous tes yeux que tu vois pas !

Merci !
PetitCalgon 2660 Bob
En C#, on a des using qui permettent de libérer l'objet une fois qu'on en a plus besoin, si l'objet est intelligent, il appelle la fonction close si il était ouvert (au hasard un FileStream par exemple).

:angel:
roidelapluie 339 Maitre jedi
PetitCalgonEn C#, on a des using qui permettent de libérer l'objet une fois qu'on en a plus besoin, si l'objet est intelligent, il appelle la fonction close si il était ouvert (au hasard un FileStream par exemple).

:angel:
Même chose en python:
>>> with open('workfile', 'r') as f:
... read_data = f.read()
>>> f.closed
True
Guybrush 8340 Bob
Oui, les contextes, c'est trèèèès pratique en Python !

Sinon, pense à utiliser os.path.join() au lieu de ton root + '/' + filename :-)
Tchou 3555 Bob
Bah je débute, faire le truc en python était un peu un challenge.

Le truc bizarre c'est que ça a marché, puis à un moment indéterminé, ça n'a plus fonctionné. J'ai dû supprimer cette ligne. Et comme c'est un script que je modifie une fois de temps en temps entre deux trucs, j'ai pas fait gaffe. La dernière fois que j'y avais touché avant hier soir, ça remonte à quelques mois.

Là, à le relire en l'ayant posté, je ne peux que remarquer le mélange camelCase et snake_case dans mes variables ! Ah bravo fredo ! C'est quoi la convention en python ? Snake je crois ?
Guybrush 8340 Bob
Oui, snake_case majoritairement.

Pour tes variables de configuration, tu peux les préfixer par un underscore pour indiquer que ce sont des "rouages internes". Et pour tes scripts, de préférence, mets ton code dans une fonction (que tu appelles par exemple main() si t'as pas d'idée) et en fin de fichier, tu peux mettre :
if __name__ == '__main__':
main()
En gros, si ton fichier est exécuté comme script (et donc son nom vaut "__main__" ; dans le cas d'un import, __name__ vaut le nom du fichier), alors il exécute main(). Ca te permet de rendre ton code importable (si besoin) ailleurs, de forcer l'écriture d'au moins une fonction... l'idéal serait d'éviter tes variables globales, et donc d'avoir quelque chose du genre :
import machin
import truc

def main(x, y, z):
# code de la fonction

if __name__ == '__main__':
main('root', 'filename', 42)
Ensuite, tu vas découvrir les joies des modules docopt [edit : et pas doctest] et d'argparse, et en 2 temps 3 mouvements, t'auras un joli truc exécutable qui gère plein de paramètres super facilement (honnêtement, jete un oeil à argparse.... l'usage basique est VRAIMENT très simple à mettre en place !!).


Ce message a été modifié 1 fois. Dernière modification : 14 octobre 2014 à 12:55 par Guybrush.

Tchou 3555 Bob
argparse ... à quoi cela peut-il bien servir vu le nom ? Qu'avais-je en tête juste avant de faire un ctrl+r sur lexpage ? Faire en sorte de mettre mes variables en argument de mon programme avec le prompt en fallback... hmmm ... parser mes arguments ... à quoi peut donc servir argparse ? :D

C'est un outil à mon usage unique (pour le moment, une fois modularisé, je pourrai le diffuser), mais ouais le modulariser est une idée. J'ai une charte graphique que j'ai industrialisée, et le but est en une commande de faire une tonne de trucs chiants (genre entre autre je génere 13 tailles de favicon (merci les smartphones ! 13 ! :angry2: ) dans mon script). J'avais une première version de cet outil déjà existante, mais je l'avais fait faire par un stagiaire, et ... comment dire ... comment dire ... enfin bref, je l'ai reprise à la base, et avec le challenge de le faire en python.


Ce message a été modifié 1 fois. Dernière modification : 14 octobre 2014 à 12:07 par Tchou.

Guybrush 8340 Bob
Bah argparse te permet de définir simplement les paramètres de ton script, en donnant leur nom court, le nom long, le type, la doc et la clé cible pour le stockage. Une fois le parseur appelé, tu récupères un dictionnaire (une hashmap si tu veux) avec en clés les paramètres, et en valeur ce qui a été passé. C'est très simple à utiliser (en fonctions avancées : groupes de paramètres, exclusions, etc.).

Si tu connais déjà bien la manière de documenter un outil en ligne de commandes, docopt (et pas doctest, je me suis trompé) te permet de définir la documentation, et il s'occupe lui-même de la parser pour te générer le dictionnaire avec les clés/valeurs.

Répondre

Vous devez être inscrit et identifié.