Un an de commit sur cpython

Dans une base de code source, certaines parties sont plus modifiées que d’autres. cpython ne déroge pas à la règle. En analysant les fichiers modifiés durant l’année 2020, on peut voir l’activité intégrée à la branche principale (nommée master).

L’activité d’un fichier est estimée en comptant le nombre de fois où il a été modifié. Dans cet article, l’estimation de l’activité d’un répertoire est la somme de l’activité des répertoires et des fichiers qu’il contient. Par exemple, avec un répertoire R contenant 3 fichiers F1, F2 et F3, un premier commit modifiant F1 et F2, un second modifiant uniquement F2, alors l’activité de F1 vaut 1, F2 vaut 2 et F3 vaut 0. L’activité de R vaut (1 + 2 + 0).

Activité globale

La quantité totale de modifications enregistrées est de 9956.

L’activité dans les répertoires à la racine du dépôt est principalement dans :

  • Lib : code python de la bibliothèque standard
  • Doc : documentation au format reStructuredText. Elle permet la génération de plusieurs documentations visibles sur python.org (dont celle de la bibliothèque standard)
  • Misc : quelques fichiers divers et un répertoire résumant les nouveautés de chaque version
  • Modules : code python et C

Ces quatre répertoires représentent 2/3 de l’activité.

Il existe aussi quelques fichiers à la racine mais leur activité est négligeable.

Dans le répertoire Lib

60% de l’activité dans Lib se situent dans Lib/test et sont donc liés à la modification ou l’ajout de tests unitaires. Étant donné que la bibliothèque standard est couverte par des tests unitaires, il est assez logique que cela représente une part importante de l’activité. Ce répertoire ne contient pas l’intégralité des tests : certains sont placés ailleurs dans l’arborescence. Par exemple Lib/idlelib/idle_test, Lib/distutils/tests, etc.
Loin derrière, et pourtant deuxième en terme d’activité dans Lib, le répertoire idlelib contient du code de l’éditeur IDLE (4%).
Les onze premiers modules actifs (en excluant les tests) représentent 19% de l’activité :

L’activité autour de IDLE me semble étonnante car j’ai l’impression que cet éditeur est peu utilisé et donc je l’imaginais peu actif. De même pour tkinter mais son activité est probablement liée à IDLE qui l’utilise comme interface graphique.

Dans le répertoire Doc

La moitié de l’activité dans ce répertoire est liée à la bibliothèque standard (dans Doc/library). C’est assez logique puisque Lib est très modifié donc la documentation correspondante doit être mise à jour (pour signaler une changement de comportement, un nouveau paramètre, etc.).

Ensuite, avec 20% de l’activité, le répertoire Doc/whatsnew contient les notes de version. L’année a vu la sortie de python 3.9 et le début du développement de la version 3.10 donc on retrouve principalement des modifications pour ces deux fichiers. Je suppose que les autres versions sont dues à des rétroportages ou des modifications cosmétiques.

        whatsnew (267)
            3.9.rst (150)
            3.10.rst (95)
            3.8.rst (10)
            3.7.rst (5)
            3.2.rst (3)
            3.3.rst (1)
            3.5.rst (1)
            3.6.rst (1)
            index.rst (1)

Ce répertoire contient de nombreux autres fichiers de version, commençant avec 2.0.rst, qui n’ont eu aucune activité.

Dans le répertoire Misc

L’activité dans le répertoire Misc est due à l’ajout des nouvelles qui sont placées dans Misc/NEWS.d (94%). Chaque nouveauté ou changement de comportement ajouté à une nouvelle version publiée donne lieu à un nouveau fichier. Contrairement à des fichiers .py qui sont régulièrement modifiés, ces fichiers sont ajoutés en grand nombre mais ne sont plus jamais modifiés. Cet ajout est la dernière étape à effectuer pour proposer une modification sur le code de cpython.
Le fichier ACKS contient 5% des modifications : ce fichier regroupe le nom de toutes les personnes qui ont au moins contribué une fois. Il est donc logique que ce répertoire soit souvent modifié.

Dans le répertoire Modules

Le répertoire Modules contient une partie du code C de l’interpréteur. Contrairement aux répertoires précédents, l’activité est répartie beaucoup plus largement.
Il faut les 14 premiers répertoires et fichiers pour représenter 50% de l’activité de Modules.

Méthode de calcul et limites

Le code source ayant permis de produire l’arborescence d’activité est disponible à https://github.com/sblondon/commitstats. J’espère qu’il n’y a pas d’anomalie (ou sans impact réel) sur les calculs de stats.

L’arborescence est affichée sur la sortie standard :

. (9956)
    Lib (2765)
        test (1695)
            test_asyncio (74)
                test_events.py (11)
                test_tasks.py (10)
[...]
    README.rst (12)
    .travis.yml (10)
    aclocal.m4 (6)
    .gitattributes (2)
    .gitignore (2)
    LICENSE (2)
    m4 (2)
        ax_c_float_words_bigendian.m4 (1)
        ax_check_openssl.m4 (1)

Les fichiers de statistiques générés sont téléchargeables. Les scripts de génération des graphiques y sont aussi (et ont encore moins d’intérêt).

Seule la branche master est prise en compte donc les développements en cours et ceux en attente d’intégration sont ignorés.

Les modifications considèrent l’ensemble de l’année 2020 donc une partie des développements de la version 3.9 et le début de 3.10 puisque la version 3.9 est sortie en octobre 2020.

Plutôt que de compter le nombre de fois qu’un fichier a été modifié, il serait peut-être préférable de compter le nombre total de lignes modifiées.

La bibliothèque tree_output https://pypi.org/project/tree_output/ permettrait une sortie plus élégante.

Catégories :Python

Effet boomerang pour les contributions à du logiciel libre

Lorsqu’on corrige une anomalie ou qu’on a ajoute une fonctionnalité à un logiciel ou une bibliothèque, on peut la garder pour soi ou la partager. La deuxième solution demande un peu plus d’efforts à court terme mais est préférable à long terme.

Le délai de retour d’un correctif ou d’une amélioration dépend de l’endroit où il est proposé : plus il est poussé loin et plus il va mettre de temps à revenir mais le nombre de personnes/machines bénéficiant du correctif sera plus grand.

Les différentes possibilités

Effet boomerang d'une contribution

Gardé pour soi

Cette méthode est absente du schéma puisqu’elle consiste à ne pas envoyer la modification à l’extérieur. C’est le plus rapide à mettre en œuvre, d’autant plus que la qualité du correctif n’est validée qu’en interne (juste soi-même ou par l’équipe intégrant la modification). Diverses façons d’arriver à ses fins sont possibles comme surcharger la signature de la méthode qui pose problème, attraper l’erreur non traitée correctement, intégrer la bibliothèque dans le code et la modifier directement (vendoring), etc.

Par contre, personne d’autre ne bénéficie du correctif et personne d’extérieur ne fera une revue de code.

Publication solitaire

La vitesse de mise au point est équivalente à la méthode précédente. Le déploiement prend un peu de temps. Elle permet de rendre la modification disponible pour les autres utilisateurs s’ils la trouvent. Ce phénomène est visible avec l’incitation au fork proposée par les forges comme Github, Gitlab, Bitbucket, etc, sans pousser la modification vers le développeur amont (via une pull request). Cette technique est utilisée lorsque des développeurs :

  1. forkent un dépôt,
  2. créent un commit modifiant le nouveau dépôt,
  3. font dépendre leur application de ce commit (npm install git+https://alice@forge...).

Cependant, ce comportement n’est pas limité aux forges publiques : il existait déjà avant en publiant le correctif dans un article de blog, une liste de diffusion (liste non exhaustive).

Envoyé vers la distribution

La correction est envoyée dans la distribution Linux utilisée ; c’est souvent fait en incluant la modification dans un fichier, attaché à un rapport de bogue sur la distribution.

Une fois que la modification sera intégrée et qu’un nouveau paquet sera publié, l’ensemble des utilisateurs, y compris l’auteur, en bénéficieront. Cela évite de maintenir la modification de son côté lorsque de nouvelles versions du paquet seront publiées. La durée d’intégration est plus longue selon la réactivité du mainteneur du paquet et le mode de publication (version espacée ou rolling release). Bien évidemment, le bénéfice de la modification sera perdu en cas de changement de distribution.

Cette solution est nécessaire pour corriger/améliorer un élément spécifique d’un paquet de la distribution. Cela peut arriver dans deux cas :

  • soit parce que c’est un paquet spécifique à la distribution (le paquet apt pour debian par exemple)
  • soit parce qu’une modification du logiciel spécifique à la distribution a une influence sur la modification soumise

Envoyé vers le développeur amont

Plutôt que d’envoyer le correctif vers la distribution utilisée, il est alors envoyé directement vers le développeur du logiciel. Si le développement est hébergé sur une forge publique, cela suppose le faire un fork (comme dans le cas d’une publication solitaire), puis de faire une pull request (terme Github), merge request (terme Gitlab). Sinon, il faut regarder quelle est la forme attendue : en postant sur une liste de diffusion, ou directement vers le mainteneur, etc. Il sera nécessaire de répondre aux remarques et demandes de corrections pour que la modification soit intégrée dans le logiciel amont.

Comme dans le cas d’une intégration dans la distribution, une fois intégrée, la modification sera disponible pour tous :

  • soit en réutilisant le logiciel directement via le paquet issus du langage (par exemple gem pour ruby, wheel pour python, crate pour Rust, etc.)
  • soit parce que la version du logiciel sera elle aussi intégrée dans la distribution et donc obtenue dans un paquet de la distribution

Ce type de contributions sont les plus longues à revenir au contributeur mais elles permettent le déploiement le plus large (et donc la plus grande disponibilité pour soi et pour les autres).

Autres considérations

S’il n’est pas possible d’attendre le retour de la modification, la solution optimale est de faire un correctif local et un envoi vers la distribution ou vers le développeur amont (pour qu’une solution long terme soit aussi disponible automatiquement).

Pour bénéficier de l’envoi amont, il faut que le temps de retour soit inférieur au temps de changement de techno/bibliothèque. Dans un écosystème où les outils et bibliothèques sont abandonnés très rapidement, l’effort d’intégration peut être perçu comme vain puisque la personne ayant fait le développement aura peu le temps d’en profiter. D’un point de vue général, avec une majorité de personnes faisant ce calcul, cela ne fait qu’empirer le problème, avec des multitudes de fork s’ignorant mutuellement, chacun avec une fonctionnalité ou une correction différente. Javascript me semble être dans cette situation.

À propos du schéma

Le schéma a été réalisé avec Dia, installable par le paquet du même nom pour Debian et dérivées.
Fichier source .dia

Catégories :Debian

How to self-host Expo webapp with Apache 2.4

This short howto comes from a github pull request not merged.

1. Build your Expo web app

Use the command provided by expo:
$ expo build:web

2. Provide the web-build/ directory to the server

Several ways are available according to your workflow.

For example:

  • copy the web-build/ directory to /path/to/web-build/ on the server with scp or sftp commands. `ssh` server is required on the server. Create distant path (/path/to/) before copying web-build/ directory.
  • configure Continuous Integration to build and deploy the web-build/ directory.

3. Configure Apache webserver

Apache will host the generated static files.

Create a file at /etc/apache2/sites-available/expo.conf with:

<VirtualHost ip-server:80>
    ServerAdmin your-email@address.tld
    ServerName domain-for-the-app
    Alias / /path/to/web-build/
    <Directory /path/to/web-build/>
            Require all granted
    </Directory>
</VirtualHost>

You have to change ip-server, your-email@address.tld, domain-for-the-app and /path/to/web-build/ according to your setup.

  • ip-server: IP used by the server to receive requests from the clients
  • your-email@address.tld: e-mail address shown to the client if a server error occurs
  • domain-for-the-app: domain where the files are served to the client. When users go to `http://domain-for-the-app` with a browser, the app will be loaded
  • /path/to/web-build/: path where the web-build/ directory is

4. Enable the new VirtualHost


$ sudo a2ensite expo

5. Restart Apache


$ sudo systemctl restart apache2

6. It works!

Check that the webapp is available at domain-for-the-app with a browser.

Catégories :Autre Étiquettes :

Configuration(s) grâce à ConfigParser

configparser est un module de la bibliothèque standard Python permettant de lire ou d’écrire une configuration. C’est utile pour séparer code source et paramètres. D’autres solutions sont possibles comme du json (disponible dans la bibliothèque standard) ou, en utilisant des bibliothèques tierces disponibles sur pypi.org, du yaml voire toml (pour ceux qui veulent le dernier truc à la mode).

La classe configparser.ConfigParser() permet de lire différentes sources (une chaîne de caractères, un fichier ou un dictionnaire) mais aussi d’écrire des configurations dans un fichier.

conf = """
[batman]
talent = riche
"""
import configparser
config = configparser.ConfigParser()
config.read_string(conf)
# config["batman"]["talent"] vaut "riche"

with open('superheros.ini', 'w') as f:
    config.write(f)
# le fichier superheros.ini contient les données de Batman

Un aperçu plus large des possibilités est présenté dans l’introduction du module.

Possibilité moins connue, ConfigParser peut lire plusieurs configurations successives. Les données lues écrasent les données précédentes ; les données non modifiées restent.

Supposons une initialisation avec un dictionnaire:

PAR_DEFAUT = {
    "superman": {"talent": "vole avec son slip sur son pantalon"},
    "kleptomane": {"talent": "vole des petites cuillères"},
}

ConfigParser peut charger cette configuration avec la méthode read_dict().

config = configparser.ConfigParser()
config.read_dict(PAR_DEFAUT)
# config['superman']["talent"] vaut "vole avec son slip sur son pantalon"
  • le comportement d’une instance de ConfigParser() ressemble à celui d’un dictionnaire
  • les données sont obligatoirement dans une section (batman, superman, kleptomane)
  • les valeurs sont toujours des chaînes de caractères

Ces valeurs par défaut pourraient être écrasées par un fichier de configuration. Le format de fichier attendu est de type .ini.

Avec un fichier de configuration /etc/heros.ini contenant:

[superman]
talent=vole sans être décoiffé

config.read("/etc/heros.ini")
# config["superman"]["talent"] vaut maintenant "vole sans être décoiffé"
# config["kleptomane"]["talent"] est inchangé

Évidemment, les noms doivent être impérativement identiques aux valeurs par défaut pour qu’elles écrasent la valeur précédente et non qu’elles co-existent et soient ignorées par la suite.

Il est possible de redéfinir à nouveau la configuration avec d’autres fichiers (~/config/superheros.conf par exemple) en faisant de nouveaux appels config.read(). Idem avec config.read_dict() ou config.read_string().

Le comportement d’écrasements successifs des données n’est pas explicite dans la documentation Python mais une amélioration de la documentation est en attente.

Catégories :Python

Suppression automatique de fichiers

Il est pratique d’automatiser la suppression des fichiers dont on ne se sert plus. Trois astuces complémentaires pour réaliser cela sur un ordinateur de bureau :

Écrire dans /tmp

Étant donné que le répertoire /tmp est vidé au démarrage de la machine, faire ses essais dans ce répertoire évite d’avoir à les supprimer manuellement.

Vider la corbeille de bureau

Sous Debian et dérivées, installer le paquet trash-cli et ajouter la ligne suivante dans sa crontab :

15 13 * * * trash-empty 30

Ainsi, tous les jours, à 13h15, les fichiers placés dans la corbeille depuis plus de 30 jours seront supprimés.
Cela fonctionne avec tout environnement de bureau respectant Freedesktop (Gnome, KDE, etc.).

Supprimer des e-mails automatiquement

Thunderbird permet de supprimer des e-mails en fonction de leur âge. Pratique pour les répertoires comme la corbeille, le spam ou certains répertoires dont le contenu n’a plus d’intérêt avec le temps.
Faire un clic droit sur le répertoire à nettoyer automatiquement et cliquer sur Propriétés :

Cette possibilité n’est pas une exclusivité de Thunderbird, il existe des équivalents dans d’autres clients e-mails.

Voilà, il ne reste plus qu’à automatiser la création des fichiers et recevoir un peu plus de spam ! 😉

Catégories :Autre