Auto-Docs pour Python – Vers la science des données
De cela, nous pouvons voir qu’il existe plusieurs structures clés que nous devons être en mesure d’extraire de notre code, à savoir:
- Docstrings: (contenant des descriptions, des paramètres, des paramètres de type, etc.) ce sont les sections de citation de bloc que nous utilisons pour décrire chaque script, classe et fonction.
- Des classes: les définitions de classe elles-mêmes, leurs descriptions (contenues dans la docstring) et les méthodes.
- Les fonctions: les fonctions elles-mêmes, avec leurs docstrings, qui contiennent une description de fonction et des paramètres.
- Paramètres: contenu dans la fonction docstring. Chaque paramètre contient une description, un type de données et un indicateur «facultatif».
Nous prendrons chacun de ces composants et leurs sous-composants respectifs et utiliserons ces informations pour développer une autre structure arborescente de documentation simpliste et compréhensible.
Comme vous pouvez le voir, nous pouvons identifier les docstrings par les triples guillemets qui les entourent. En regex, on écrit (?s)"{3}.*?"{3}
.
Il se passe quelques choses ici.
-
est utilisé comme caractère d’échappement. Nous plaçons cela devant des personnages qui ont d’autres significations en Python
"
ou regex{[()]}
pour «échapper» à leur signification native. Indiquant que nous voulons simplement chercher ce personnage. - Nous avons deux ensembles de
"{3}
, cela correspond simplement aux deux ensembles de triples guillemets que nous avons. -
.*
correspond à n’importe quel caractère sauf les sauts de ligne,?
en fait un quantificateur «paresseux» – arrêtant l’expression régulière correspondant à la toute première instance de"""
à la toute dernière instance de"""
, à la place, nous faisons correspondre le premier et le deuxième, le troisième et le quatrième, etc. -
(?s)
est un modificateur de motif global,s
dit au regex que nous voulons.*
pour correspondre également aux nouvelles lignes.
Le début d’une classe peut être facilement identifié par le mot class
suivi d’un espace, de quelques lettres et de deux points.
Pour nous empêcher de tout faire correspondre après le début d’une classe, nous identifions la fin de la classe comme le premier endroit où une nouvelle ligne est directement suivie d’un caractère de texte.
En regex, on écrit (?sm)class [wd_]+:.*(^w)
.
-
[wd_]
correspond au texte, aux caractères numériques et au trait de soulignement, en ajoutant+
signifie qu’il correspondra à un nombre illimité de ces caractères. -
(?sm)
est à nouveau notre modificateur de modèle global, lem
le drapeau indique au regex que nous voulons^
et$
pour signifier respectivement le début et la fin d’une ligne. -
(^.)
est le début d’une ligne^
immédiatement suivi par n’importe quel caractère.
.
Les fonctions sont un peu différentes. Contrairement aux classes, où nous capturons tout ce qui est contenu dans la classe (afin que nous puissions également extraire les méthodes de classe), nous avons seulement besoin du nom de la fonction et de docstring.
Heureusement, cela rend notre première fonction correspondant à l’expression régulière beaucoup plus facile, nous écrivons (?s)def [wd_]+(.*?):s+"{3}.*?"{3}
.
Descriptions
Chaque fonction contient également un nom, une description et une liste de paramètres que nous devons extraire. Nous le faisons en plusieurs parties, l’extraction des paramètres est un peu plus complexe et nous couvrirons ensuite.
le une fonction Nom nous extrayons en utilisant def [wd_]+(
, avec laquelle nous supprimons def
et (
, en laissant juste le nom de la fonction.
name = re.search(r"def [wd_+(", func).group()
name = name.replace("def ", "").replace("(", "").strip()
La description desc
et la liste des paramètres params
sont extraits en tirant la docstring avec (?s)"{3}.*?"{3}
et se séparer Parametersn
. Nous supprimons ensuite les guillemets triples et les espaces blancs excessifs.
docstring = re.search(r"(?s)"{3}.*"{3}").group()
desc, params = docstring.split("Parametersn")
desc = re.sub(r"s+", " ", desc.replace('"""', "")).strip()
Nous avons notre liste de paramètres contenue dans params
, qui ressemble à ceci:
Pour correspondre à chaque paramètre, nous écrivons (?s)w+ : .*?(?=w+ :)
.
Pour la plupart, nous avons déjà tout couvert, sauf une partie – (?=w+ :)
.
En cela, nous savons que w+ :
correspondra à plusieurs lettres suivies d’un espace et de deux points. Cependant, il est enveloppé dans (?=)
. C’est ce qu’on appelle un lookahead positif affirmation.
En ajoutant cela, nous disons au regex de affirmer (?=)
que ce que nous avons apparié est immédiatement suivi par w :
.
Si nous mettons cela dans un testeur de regex en ligne, nous pouvons voir qu’il fonctionne presque parfaitement, mais s’il manque malheureusement le paramètre final (car il n’est pas suivi par w :
).
Nous résolvons cela en extrayant un seul paramètre à la fois. Dans cette boucle, si nous constatons qu’aucun paramètre n’a été trouvé, nous essayons à nouveau avec une expression de paramètre modifiée (?s)w+ : .*
, extraire le paramètre final.
Et les détails des paramètres?
Notre dernière couche d’extraction la plus profonde nous oblige à extraire le nom du paramètre, le type de données, l’indicateur «facultatif» et la description.
Pour cela, nous divisons simplement notre texte de paramètre new_param
par newlines n
. Maintenant, à l’index 0
nous avons la première ligne contenant le nom du paramètre, le type de données et éventuellement le optionnel drapeau. Modifions le code ci-dessus pour inclure cette extraction.
La seule logique supplémentaire ici est le fractionnement de la première ligne de new_param
par :
, Nous donnant name
et dtype
. Suivi d’une vérification du mot 'optional'
dans dtype
, Nous donnant optional
.
Ensuite, nous ajoutons simplement ces détails de paramètre au nouveau params
dictionnaire. Nous donnant quelque chose comme: