configuration – Sam & Max http://sametmax.com Du code, du cul Thu, 05 Sep 2019 08:22:03 +0000 en-US hourly 1 https://wordpress.org/?v=4.9.7 32490438 Regrouper ses fichiers de settings avec stow http://sametmax.com/regrouper-ses-fichiers-de-settings-avec-stow/ http://sametmax.com/regrouper-ses-fichiers-de-settings-avec-stow/#comments Fri, 06 Oct 2017 08:24:03 +0000 http://sametmax.com/?p=23681 .machins. Par exemple le .bashrc pour la config du bash, le .mozilla qui contient toutes vos données Firefox, le .ssh avec toutes vos clés privées, le .local/share/virtualenvs avec les envs virtuels Python créés par pew ou .config/sublime-text-3 pour la configuration de Sublime text, etc.]]> Sous Linux, le dossier utilisateur est blindé de fichiers de configuration. Les fameux .machins. Par exemple le .bashrc pour la config du bash, le .mozilla qui contient toutes vos données Firefox, le .ssh avec toutes vos clés privées, le .local/share/virtualenvs avec les envs virtuels Python créés par pew ou .config/sublime-text-3 pour la configuration de Sublime text, etc.

Au final, voici tous les fichiers de conf qui sont importants pour moi de près ou de loin:

├── .autoenv
├── .bashrc
├── .config
│   ├── autostart
│   ├── Code
│   ├── copyq
│   ├── fish
│   ├── gtg
│   ├── liferea
│   ├── pulse
│   ├── stremio
│   ├── sublime-text-3
│   ├── transmission
│   ├── user-dirs.dirs
│   ├── user-dirs.locale
│   ├── variety
│   ├── VeraCrypt
│   ├── Zeal
│   └── zim
├── .django-completion.bash
├── .editorconfig
├── .git-aware-prompt
├── .git-completion.bash
├── .gitconfig
├── .gitignore
├── .git-prompt.sh
├── .git.scmbrc
├── .jupyter
├── .lastpass
├── .liferea_1.8
├── .local
│   └── share
        ├── gtg
        ├── keyrings
        ├── liferea
        ├── omf
        ├── TowerFall
        ├── virtualenvs
        └── Zeal
├── .mozilla
├── .netrc
├── .oh-my-zsh
├── .openambit
├── .pypirc
├── .scmbrc
├── .scm_breeze
├── .sshplus
├── .vscode
│   └── extensions
└── .zshrc

Quand on bidouille, on les change souvent. On les backup aussi, pour pouvoir les porter d’un laptop à un autre, les synchroniser, les uploader sur un serveur ou les récup lors d’une réinstallation. Parce que quand on a tuné ses terminaux et éditeurs aux petits oignons, on a pas envie de recommencer à poil.

Pour bien faciliter les choses, ils sont éparpillés un peu partout, dans des sous-dossiers différents.

Et je sais pas quel vil individu a suggéré une fois que faire une partition séparée pour /home était la solution de Skippy à tous les soucis, mais perso, ça me cause plus de bugs qu’autre chose quand on change de versions d’OS.

Bref, laissez tomber vos vieilles croyances issues de charlatans de sectes. Moi, j’ai vu la lumière (lien de don bitcoin en bas à droite de la page), et elle s’appelle GNU stow.

Stow est un vieil utilitaire (donc sagesse millénaire des anciens, vous pouvez avoir confiance, prenez ce cristal aussi il est en promo), qui est grosso merdo un ln -s récursive. C’est-à-dire que ça fait des symlinks des fichiers et des dossiers que vous lui passez.

On peut l’utiliser pour plein de choses, mais l’usage sacré implique le sacrifice d’une vierge à Max, puis de déplacer tous les fichiers de settings qu’on souhaite gérer dans un seul dossier.

Par exemple, moi j’ai:

/home/user/church/settings/

    ├── .autoenv
    ├── .bashrc
    ├── .config
    │   ├── autostart
    │   ├── Code
    │   ├── copyq
    │   ├── fish
    │   ├── gtg
    ...

Au lieu de les avoir éparpillées partout, toutes les brebis sont maintenant regroupées dans une seule église.

Il est très important de garder l’organisation des dossiers et des sous-dossiers d’origine. Ici vous voyez que j’ai le dossier Code, qui est le dossier de settings de VSCode. Mais il est DANS un dossier .config, car avant mon regroupement il était dans /home/user/.config/.

En revanche, il n’est pas du tout nécessaire que .config contienne tous les dossiers qu’il avait précédemment. Seuls ceux qui vous intéressent. Le reste peut rester à sa place initiale, dans le /home/user/.config/.

Donc je résume:

  • Listez les fichiers et dossiers de settings qui vous intéressent.
  • Déplacez les tous dans un dossier commun en gardant une arborescence similaire.
  • Laissez les fichiers qui ne vous intéressent pas là où ils sont.
  • Priez (une bonne pratique avant toute opération informatique).

Arrive le messie, Stow.

D’abord, il faut l’installer, mais comme c’est un outil vénérable, il est dans les dépôts. Sous Ubuntu, le psaume “apt install stow” fera l’affaire.

Ensuite, on prêche. Je me perds dans mes propres paraboles, mais les voies du seigneur sont impénétrables, contrairement à celles d’Abella Anderson. Bref on demande à stow de traiter récursivement tout le contenu du dossier settings qui est dans /home/user/church afin de le linker vers /home/user/:

stow -d /home/user/church -t /home/user/ settings

Stow va prendre récursivement tous les dossiers qui sont dans /home/user/church/settings, et les comparer à ceux dans /home/user. Si ils existent, il va ne rien faire, mais si ils n’existent pas, il va créer un lien vers chacun de ceux manquants. Pour les fichiers, si ils n’existent pas, il va créer un lien, sinon il va vous afficher une erreur, afin de ne pas écraser quelque chose d’important et vous signalez qu’il y un souci.

Le but de tout ça ?

Pour votre système et tous vos logiciels, ça ne change rien. Ils vont tomber sur les liens et avoir l’impression que tous les fichiers de configs sont à leur place et vont continuer à fonctionner dans la joie et le gospel.

Et pour vous, ben vous avez un seul endroit où tous les fichiers importants sont regroupés. Plus besoin de les chercher. Facile à backuper et à restaurer. On peut même tout foutre sous Git.

Loué soit le sauveur.

Vive moi.

]]>
http://sametmax.com/regrouper-ses-fichiers-de-settings-avec-stow/feed/ 15 23681
Personnalisez le démarrage d’iPython http://sametmax.com/personnalisez-le-demarrage-dipython/ http://sametmax.com/personnalisez-le-demarrage-dipython/#comments Sun, 16 Dec 2012 13:14:54 +0000 http://sametmax.com/?p=3643 iPython, c’est bon. Et ça peut être encore meilleur.

Avant on ajoutait un peu de sel à notre super shell en éditant les fichiers .ipython/ipythonrc et .ipython/ipy_user_conf.py. Mais ça c’était avant.

Maintenant on fait :

$ ipython profile create

Ce qui va créer un fichier ~/.config/ipython/profile_default/ipython_config.py (et ipython_qtconsole_config.py si vous avez installé la version qt).

Il ne vous reste plus qu’à éditer ce fichier pour personnaliser le démarrage de iPython.

Executer du code au démarrage

N’importe quelle ligne de code Python ou de commande magique (les trucs qui commencent pas ‘%’ qui ne fonctionnent que dans iPython) !

Très utile pour les imports par exemple. Voici ce que je fais moi au démarrage :

c.TerminalIPythonApp.exec_lines = [
'%doctest_mode',
'import os, sys, re, json',
'from datetime import datetime, timedelta',
'''
try:
    from path import path
except ImportError:
    pass
''',
'''
try:
    import requests
except ImportError:
    pass
''',
'''
try:
    from batbelt.structs import *
    from batbelt.objects import attr
    from batbelt.strings import slugify, normalize, json_dumps, json_loads
except ImportError:
    pass
'''
]

Du coup j’ai quasiment jamais besoin d’importer un truc, car ce que j’utilise le plus est déjà là. %doctest_mode me permet de coller des docstrings sans me fouler.

Programmation graphique

iPython possède une boucle principale. Les toolkits graphiques aussi. Si vous faites vos essais du code du second dans le premier, ça va bloquer. Heureusement on peut demander à iPython d’intégrer la main loop d’un des toolkits graphiques les plus célèbres en choisissant parmi : ‘qt’, ‘wx’, ‘gtk’, ‘glut’, ‘pyglet’ et ‘osx’ et en settant :

c.TerminalIPythonApp.gui = 'qt'

Virer le header

Quand on lance le shell, il vous raconte sa vie. Pour lui dire de fermer sa gueule :

c.TerminalIPythonApp.display_banner = False

Lancer un script complet

Bon, ça c’est si vous avez de gros besoins…

c.TerminalIPythonApp.file_to_run = '/path/to/script.py'

Sauter la confirmation à la fermeture

Oui, je suis sûr te vouloir te fermer connard ! Tu crois que j’ai appuyé sur Ctrl + d par erreur en éternuant ?

c.TerminalInteractiveShell.confirm_exit = False

Choisir son éditeur externe

Si vous tapez %edit dans iPython, il ouvre un éditeur. Vous tapez votre code, vous sauvegardez, vous fermez. Et iPython récupère le code et l’éxécute. Par défault il utilise VI. On peut faire mieux.

c.TerminalInteractiveShell.editor = '/chemin/vers/sublime-text -w'
]]>
http://sametmax.com/personnalisez-le-demarrage-dipython/feed/ 9 3643
Votre avis sur ce projet de lib de gestion de configuration Python ? http://sametmax.com/votre-avis-sur-ce-projet-de-lib-de-gestion-de-configuration-python/ http://sametmax.com/votre-avis-sur-ce-projet-de-lib-de-gestion-de-configuration-python/#comments Wed, 01 Aug 2012 21:36:27 +0000 http://sametmax.com/?p=1444 Question: cette idée m’est venue durant un long trajet. Est-ce que ça vaut le coup d’être codé ? Qu’est-ce que vous en pensez ? Est-ce qu’il y a une grosse faille ? Est-ce que ça n’existe pas déjà ?

Sauvegarder la configuration d’un programme Python tient du challenge. Pas parce que que c’est difficile ou qu’on manque d’outils, mais à cause du trop grand nombre de choix:

  • Pur Python (Django) ?
  • INI (défaut dans la lib standard) ?
  • JSON (sublime-text) ?
  • gconf (Gnome) ?
  • base de registre (Windows) ?

Toutes ces solutions ont des parsers différents, des outputs différents, et il faut se rajouter les checks, le casting et la gestion des erreurs à la main.

Je me prends à rêver à un moyen de lire et écrire la configuration d’un programme en Python, indépendamment de la source:

  • fichier de config JSON
  • fichier de config ini
  • fichier de config YML
  • fichier de config Python
  • fichier de config XML
  • fichier de config CSV
  • gconf
  • base de registre
  • base de données SQL
  • base de données NoSQL (redis, mongo, etc)

Principe

On donne une définition de la configuration qu’on souhaite stocker, comme un modèle d’ORM:

class MaConfiguration(Configuration):

    date = DateField(help_text="This is the date")
    nom = TextField(required=True)
    age = IntegerField(default=12) # default peut être un callable
    temperature = FloatField()
    tags = ListField(values_type=str) # values_type est optionnel
    attrs = DictField(values_type=int)

    def set_tags():
        # appel quand tag est setté
        # raise validation error
        # ou fait une conversion

    def get_tags():
        # appel quand tags est getté

    def load_tags():
        # appel quand tags est récupé depuis la source de config

    def save-tags():
        # appel quand tags est sauvegardé dans la source de config

    class nesting(Configuration):
        # on peut nester les fields à l'infinie

        force = IntegerField(checks=RangeCheck(gt=3))
        description = TextField(checks=RegexCheck(r'yeah', error_message=u"I said yeah"))
        pi = FloatField()

Et on l’utilise comme ça:

conf = MaConfiguration('file://path/to/config/file') # ou redis://db:port, etc
print conf.date
print conf.nesting.force
print conf.get('bibi', 'doh') # fallback possible
try:
    print conf.void
except ValueDoesNotExist:
    pass
conf.nom = "boo"
conf.save()

Les données sont évidement castées automatiquement dans le bon type, à la sauvegarde, le type de valeur et la cohérence de la configuration est vérifiée.

Bien sûr on peut sauvegarder dans une autre source/format.

Ce n’est pas un parseur de fichiers, il faut avoir une définition de la structure de la source de configuration pour qu’on puisse la lire. Mais on peut générer une définition (approximative) à partir d’une source de config existante pour se faciliter la tâche.

Hierarchie

Les frameworks tels que symfony utilisent plusieurs niveaux de configuration, le niveau le plus bas écrasant toujours la valeur du niveau le plus haut.

conf = MaConfiguration({'name': 'root',
                        'path': '/path/to/config/file',
                        'children': (
                            {'name': 'child1',
                             'path': '/path/to/subconfig/file',
                             children: (... etc ...)
                            },
                            {'name': 'child2',
                             'path': /path/to/subconfig/file
                            }
                        })

with conf.from('/root/child1') as subconf:
    print subconf.bibi

Ca va d’abord chercher dans le sous fichier de config, et si ça n’existe pas, la valeur du parent, en remontant la chaîne jusqu’en haut, si rien n’existe, retourne la valeur par défaut.

Signals

Même principe qu’en Django, mais appliquer à l’objet de configuration:

  • on_load_source
  • on_load_value
  • on_save_value
  • on_change_value
  • on_get_value
  • on_save_source
  • on_save

On peut enregistrer un callback dans le process Python courant pour qu’il soit appelé quand un de ces événements se déclenche.

On peut aussi lancer un daemon qui check pour tout changement dans la source de config et qui appelle un callback (par exemple un web callback ou un message AMQP pour synchro des config de serveurs) au changement de la config.

Templating

Quand on fait des fichiers de settings à redistribuer (ex: fichier de settings par défaut de Django), on veut qu’il soit présenté d’une certaine façon: ordre des variables, texte qui n’a rien à voir avec la config et qui doit être inamovible, etc.

conf.save() doit donc optionnellement accepter un template qui ressemble à un truc comme ça:

%(variable_name)s
%(variable_name)s
%(variable_name)s

# texte fixe

%(variable_name)s

%(section_name)s // pour une intégrer une section complète

%(section_name)s // pour organiser le niveau de nesting
%(variable_name)s
%(variable_name)s

Dans lequel %(variable_name)s sera replacé par l’intégralité de ce qui concerne la variable (nom, valeur, commentaires). On devrait pouvoir aussi demander a dumper une section entière.

Exemples et documentations

conf.save() par défaut ajoute les help_text en tant que commentaires si possibles, afin que le fichier soit documenté.

print conf.exemple() devrait dumper un faux fichier de configuration avec les valeurs par défaut, ou en l’absence de tel, un exemple arbitraire extrapolé du type. Ainsi il est facile de donner une fichier d’exemple dans sa doc.

Extensibilité

Evidément on peut faire ses propres sous classes de Configuration et Field, afin de distribuer des outils réutilisables.

Il faut aussi permettre la gestion de backends pour les parseurs et les dialectes. Un parseur est quelque chose qui récupère les données depuis la source de configuration, et qui les transforme en un arbre de données en objects Python. Un dialecte est ce qui va prendre cet arbre et caster ses valeurs pour lui donner du sens.

Par exemple, le parseur yml lit le fichier et le met sous forme d’arbre, tandis que le dialecte yml parse les types yml (int, string, date) et caste les valeurs.

Ils sont séparés car on peut très bien avoir un parseur et un dialecte différent (ex: parseur XML, dialecte issue d’un DTD particulière)

Cela permettra ainsi à la communauté de contribuer des parseurs et dialectes pour des fichiers de configs particuliers comme ceux d’Apache ou d’Nginx.

On pourra imaginer avoir une banque de parseurs et dialectes, et pouvoir faire des trucs comme ça:

conf = NginxCongiguration('/path/to/nginx/file')

Gestion des erreurs

Un grand soin doit être apporté à la gestion des erreurs, pour rapporter rapidement et clairement à l’utilisateur ce qui n’est pas configuré correcrtement: type, valeur obligatoire, problème de droits, etc. Car la plus grande source d’erreur dans la config, c’est la spécificité (syntax, format, etc) de la source de config elle-même.

Les formats des sources de données ne sont pas compatibles entre eux, il faut donc créer une liste de capabilité pour chacun d’eux et crasher explicitement quand on essaye de faire quelque chose d’impossible (comme parser un yml avec des valeurs nested et sauver le résultat dans un CSV).

Loader ?

Trouver la source de configuration est un travail en soi. Entre le chemin absolu par défaut, le chemin relatif par défaut, ou l’url + port ou la socket par défaut, ou le chemin d’import d’un module Python, c’est tout un truc.

Et que faire si le fichier n’existe pas ? Le créer ? Lever une erreur ? Et si il n’y a pas les droits ?

Que faire si la ressource n’est pas accessible temporairement (un BDD, redis, un lecteur réseaux): attendre jusqu’au timeout, retry, erreur ?

Et que faire si on passe un dossier: scanner récursivement ? Ignore-t-on les fichiers cachés ? Que faire avec les fichiers de mêmes noms ?

Bref, charger le fichier de configuration c’est un gros algo à lui tout seul, et je me demande si c’est le boulot de la lib. Si oui, il faudra créer des loaders et les mettre par défaut selon le type de source. Mais il faut réussir à faire ça sans complexifier l’API. L’outil doit être puissant, mais rester simple.

Rester simple

Justement…

Evidément tout ça peut rendre l’utilisation très lourde, et je suis assez fan des libs comme peewee: simple et efficace, Pareto friendly.

L’idée est qu’une personne doit pouvoir faire ça:

from configlib import Configuration
class MaConfig(Configuration):
    nom = TextField()
    age = IntegerField()

conf = Maconfig('/path/to/config/file')

Et que ce soit le premier truc dans la doc, facile à trouver, facile à comprendre. Que les personnes qui ne veulent pas s’embrouiller avec les détails s’y dessus puissent les ignorer. Sinon Max ne l’utilisera pas :-p

Ils se rendent pas compte

On se rend pas compte à quel point un truc aussi simple que la gestion d’une configuration puisse comporter autant de pièges et de possibilités. Ça me rappelle mon premier rapport de stage à l’université, où j’avais présenté mon grand projet: une classe de logging pour PHP.

A la fin de la présentation, la prof me demande:

– Mais ça sert à quoi votre truc à part à écrire du texte ?
– Ben, c’est le but d’écrire du texte, c’est une classe de logging.
– Mais le minimum que vous pouvez faire avec c’est quoi ?
– Heu… $log->info(‘Foo’)
– Ah, c’est tout ?

Oui connasse, c’est tout. J’ai passé 20 minutes à t’expliquer que je gérais plusieurs niveaux de verbosité, de type de messages, de formating d’output (incluant la stacktrace), qu’il y avait un fichier et une API de config, un système de locking sur le fichier de sortie, et une génération dynamique de la fonction de message pour éviter les blocs conditionnels et blocker le moins de temps possible lors de l’écriture. Ah oui, y a des tests unitaires, et les commentaires permettent aussi de générer la documentation avec Doxygen.

Mais oui c’est tout, je suis juste en seconde année, et toi t’as pas programmé pour vivre depuis près de 10 ans.

Aujourd’hui il y a des libs de logging en PHP bien meilleures que la mienne, mais mon travail est toujours massivement en prod. Je comprends que personne ne passe trop de temps sur ce genre de libs, c’est beaucoup de boulot, et c’est très ingrat.

J’imagine même pas les programmeurs du noyaux linux. Un scheduler ? Ca fait quoi ? – Ben ça permet de déterminer quand un programme à le droit à du temps CPU. – Quoi, c’est tout ? Ca fait deux ans que tu bosses sur un patch d’a peine 2000 lignes juste pour ça ? Eh ben, ça valait le coup de faire un doctorat en théorie des graphes tient !

]]>
http://sametmax.com/votre-avis-sur-ce-projet-de-lib-de-gestion-de-configuration-python/feed/ 25 1444