Working on git initiation.
This commit is contained in:
parent
f927edc3d6
commit
a22d58466b
|
@ -28,6 +28,7 @@ jobs:
|
|||
- drf-initiation
|
||||
- django-initiation
|
||||
- python-perfs
|
||||
- git-initiation
|
||||
secrets:
|
||||
deploy_key: ${{ secrets.deploy_key }}
|
||||
known_hosts: ${{ secrets.known_hosts }}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
.PHONY: help
|
||||
help:
|
||||
@echo "Usage:"
|
||||
@echo " make static # to build static version."
|
||||
@echo " make test # to run tests."
|
||||
@echo " make rsync # rsync to prod"
|
||||
@echo " make clean"
|
||||
|
||||
.PHONY: static
|
||||
static: output/index.html
|
||||
cp -a static/ output/
|
||||
|
||||
.PHONY: check
|
||||
test:
|
||||
python test.py *.md
|
||||
|
||||
%.html: %.md
|
||||
mdtoreveal $< --output $@
|
||||
|
||||
output/index.md: git.md
|
||||
mkdir -p output
|
||||
cat $< > $@
|
||||
|
||||
.PHONY: rsync
|
||||
rsync: static
|
||||
rsync -vah --delete output/ mdk_fr@mdk.fr:/var/www/mdk.fr/git-initiation/
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -fr output/
|
||||
|
||||
.PHONY: serve
|
||||
serve:
|
||||
python -m http.server -d output/
|
|
@ -0,0 +1 @@
|
|||
# GIT
|
|
@ -0,0 +1,646 @@
|
|||
# Initiation Git
|
||||
|
||||
par
|
||||
|
||||
Julien Palard <julien@palard.fr>
|
||||
|
||||
https://mdk.fr
|
||||
|
||||
::: notes
|
||||
|
||||
Introduce yourself!
|
||||
|
||||
|
||||
# Git
|
||||
|
||||
Git est un gestionnaire de code source distribué.
|
||||
|
||||
|
||||
## Git : qui l'utilise ?
|
||||
|
||||
- Le kernel Linux
|
||||
- Google, Microsoft, Facebook, Netflix, ...
|
||||
- PostgreSQL
|
||||
- Python, perl, Go, swift, typescript, rust, Julia, scala, ...
|
||||
- ...
|
||||
|
||||
|
||||
## Git : qui l'utilise ?
|
||||
|
||||
|
||||
![](static/stats.png)
|
||||
|
||||
::: notes
|
||||
|
||||
Où plutôt qui ne l'utilise pas encore ?
|
||||
|
||||
https://insights.stackoverflow.com/trends?tags=git%2Csvn%2Cmercurial%2Ctfs
|
||||
|
||||
|
||||
## Historique et alternatives
|
||||
|
||||
- SCCS (1972) (un fichier, local)
|
||||
- RCS (1982) (un fichier, local)
|
||||
- CVS (1986) (projet, client-server)
|
||||
- Subversion (2000)
|
||||
- BitKeeper (2000)
|
||||
- Bazaar (2005)
|
||||
- Mercurial (2005)
|
||||
- git (2005)
|
||||
- Fossil (2007)
|
||||
|
||||
::: notes
|
||||
|
||||
Et bien d'autres !
|
||||
|
||||
|
||||
## Git est décentralisé
|
||||
|
||||
Il n'y a ni serveur, ni client : tout le monde est « au même niveau » :
|
||||
|
||||
- On peut travailler seul sur sa machine sur son projet.
|
||||
- On peut travailler une journée sans connexion à internet.
|
||||
|
||||
|
||||
::: notes
|
||||
|
||||
Désambiguer git vs github. J'aime l'analogie : « Git est a Github ce
|
||||
qu'une photo est a Dropbox ».
|
||||
|
||||
|
||||
# Introduction
|
||||
|
||||
Commençons par imaginer un projet : l'écriture d'un livre.
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
Dans notre projet, `git` va nous simplifier deux choses :
|
||||
|
||||
- La collaboration.
|
||||
- La journalisation des changements (qui a écrit quoi, quand, pourquoi).
|
||||
|
||||
|
||||
## Collaborer
|
||||
|
||||
Sans git :
|
||||
|
||||
> Moi je modifie le fichier les jours pairs et toi les jours impairs.
|
||||
|
||||
Sinon quand on sauvegarde on écrase les modifications de l'autre !
|
||||
|
||||
::: notes
|
||||
|
||||
Imaginons deux auteurs qui travaillent sur un livre, partagé via
|
||||
Dropbox dans un format propriétaire binaire...
|
||||
|
||||
|
||||
## Conserver l'historique
|
||||
|
||||
Sans git, très vite on se retrouve avec :
|
||||
|
||||
- mybook.tex
|
||||
- mybook-2.tex
|
||||
- Copy of mybook-2.tex
|
||||
- mybook-2-alternative-end.tex
|
||||
- mybook-2-alternative-end-old.tex
|
||||
- mybook-final.tex
|
||||
- mybook-final-1.tex
|
||||
- mybook-final-final.tex
|
||||
- mybook-final-preprint.tex
|
||||
- mybook-final-preprint-corrected.tex
|
||||
- mybook-final-preprint-corrected-final.tex
|
||||
|
||||
|
||||
## Conserver l'historique
|
||||
|
||||
> Pourtant hier soir, quand je suis parti, ça marchait…
|
||||
|
||||
ou :
|
||||
|
||||
> Mais que fait ce code ? Pourquoi il est là ? Qui l'a écrit ? Pourquoi ?
|
||||
|
||||
::: notes
|
||||
|
||||
Leur montrer le premier commit de Python par exemple (dédramatiser sur
|
||||
la ligne de commande).
|
||||
|
||||
|
||||
## Conserver l'historique
|
||||
|
||||
On peut même demander à `git` de nous trouver automatiquement quelle
|
||||
modification à introduit un bug.
|
||||
|
||||
::: notes
|
||||
|
||||
À condition de pouvoir lui dire :
|
||||
|
||||
- Comment reproduire le bug
|
||||
- Quelle version est connue pour fonctionner.
|
||||
- Quelle version est connue pour ne plus fonctionner.
|
||||
|
||||
|
||||
## Introduction
|
||||
|
||||
Comment, sans `git`, pourraît-on résoudre ces deux problèmes ?
|
||||
|
||||
|
||||
## Analogie
|
||||
|
||||
En prenant une photo du projet (entier) à chaque modification.
|
||||
|
||||
Au dos de la photo on pourrait même rajouter des meta-données :
|
||||
|
||||
- Nom de celui qui a fait la photo.
|
||||
- Date à laquelle la photo à été prise.
|
||||
- Pourquoi on a pris la photo à ce moment là.
|
||||
- Et pourquoi pas une signature !
|
||||
|
||||
|
||||
::: notes
|
||||
|
||||
Le projet c'est par exemple l'assemblage d'un moteur de fusée...
|
||||
|
||||
Attention pas de "zoom sur une morceau", on prend toujours l'objet entier.
|
||||
|
||||
|
||||
## Analogie
|
||||
|
||||
Est-ce que ces photos résolvent nos problèmes ?
|
||||
|
||||
|
||||
## Analogie
|
||||
|
||||
Pour conserver l'historique, oui, c'est gagné !
|
||||
|
||||
::: notes
|
||||
|
||||
On aura même un gros carton de photos avec à chaque fois qui à fait
|
||||
quoi, quand, pourquoi, c'est parfait 50 ans plus tard pour faire de
|
||||
l'archeologie.
|
||||
|
||||
|
||||
## Analogie
|
||||
|
||||
Pour collaborer on va devoir s'envoyer les photos par pigeon voyageur.
|
||||
|
||||
- À chaque fois que je reçois une photo, j'applique la modification à ma version.
|
||||
- À chaque fois que j'ai terminé une étape, j'envoie une photo de ma progression.
|
||||
|
||||
|
||||
## Analogie
|
||||
|
||||
Parfois deux photos vont se contredire, par exemple :
|
||||
|
||||
- Un collègue à essayé de nouveaux injecteurs en cuivre.
|
||||
- Vous avez essayé de nouveaux injecteurs imprimés en 3D.
|
||||
|
||||
::: notes
|
||||
|
||||
Ça s'appelle un `conflit`, et ce n'est pas grave, ça se résoud, mais
|
||||
pas toujours automatiquement : il faut parfois qu'un humain choisisse
|
||||
ce qu'il considère être le mieux.
|
||||
|
||||
|
||||
## Analogie
|
||||
|
||||
Je vais devoir prendre une décision :
|
||||
|
||||
- Prendre ma version.
|
||||
- Prendre sa version.
|
||||
- Prendre un peu des deux.
|
||||
|
||||
Le résultat sera un nouvel état, encore plus avancé du projet, vite,
|
||||
je le photographie, je commente au dos de la photo les raisons de ce
|
||||
choix, et je l'envoie au collègue.
|
||||
|
||||
::: notes
|
||||
|
||||
Ça s'appelle un `merge`.
|
||||
|
||||
|
||||
## Analogie
|
||||
|
||||
Mais souvent les séries de photos ne vont pas se contredire : chacun travaille sur sa partie.
|
||||
|
||||
::: notes
|
||||
|
||||
Par exemple vous recevez 42 photos d'un collègue qui a passé une
|
||||
semaine à travailler sur la turbopompe, il était seul à travailler
|
||||
dessus, vous appliquez son travail sans conflit.
|
||||
|
||||
|
||||
## Analogie
|
||||
|
||||
Catastrophe, une pile de photo est tombée, et personne ne sait comment
|
||||
remettre ça dans l'ordre.
|
||||
|
||||
Qu'aurai-on pu faire pour ne pas que ça arrive ?
|
||||
|
||||
::: notes
|
||||
|
||||
Écrire au dos la photo "parente" : l'état précédent, c'est ce que fait git.
|
||||
|
||||
Notez qu'une photo peut avoir 0 (initial commt), 1 (normal commit), ou
|
||||
deux (merge commit) parents.
|
||||
|
||||
|
||||
# Avec git
|
||||
|
||||
- « Prendre une photo » c'est « un `commit` », en français c'est « valider ».
|
||||
- Envoyer des photos c'est « un `push` ».
|
||||
- Un conflit c'est « un `conflit` ».
|
||||
- La photo de résolution d'un conflit c'est « un `merge` ».
|
||||
|
||||
|
||||
## Avec git
|
||||
|
||||
Comme avec une photo on peut aussi choisir ce qu'on prend, ça
|
||||
s'appelle le « staging area », la scène dans notre analogie.
|
||||
|
||||
|
||||
## Avec git
|
||||
|
||||
Typiquement on ne va pas photographier nos outils, la servante, ...
|
||||
|
||||
Ils ne font pas vraiment partie du projet : on les pousse hors champ à
|
||||
chaque photo.
|
||||
|
||||
(C'est le `.gitignore`.)
|
||||
|
||||
::: notes
|
||||
|
||||
De toute façons, sur un même projet, on utilise pas tous les mêmes outils.
|
||||
|
||||
## Avec git
|
||||
|
||||
Mais typiquement on va ajouter à la photo chaque nouvelle pièce, dès
|
||||
sa réception, même si elle n'est pas encore bien branchée.
|
||||
|
||||
(C'est un `git add`.)
|
||||
|
||||
|
||||
## Avec git
|
||||
|
||||
Comme avec nos photos, chaque `commit` permet de connaitre l'état
|
||||
**complet** du projet au moment où il à été fait.
|
||||
|
||||
::: notes
|
||||
|
||||
Insister sur le fait qu'il n'est **pas** nécessaire d'avoir les
|
||||
commits précédents.
|
||||
|
||||
|
||||
# Petite parenthèse CLI
|
||||
|
||||
`git` n'est pas graphique : il nous faut un bon terminal et un bon
|
||||
shell.
|
||||
|
||||
## Le terminal
|
||||
|
||||
Tout émulateur de terminal sur MacOS et Linux fera l'affaire.
|
||||
|
||||
::: notes
|
||||
|
||||
Oui c'est un abus de langage mais je ne vais pas dire "émulateur de
|
||||
terminal" à chaque fois.
|
||||
|
||||
|
||||
## Le terminal
|
||||
|
||||
Sur Windows :
|
||||
|
||||
- Oubliez `cmd.exe`, ça n'est plus maintenu depuis plus de 10 ans.
|
||||
- `PowerShell` : sa syntaxe n'est pas standard, mais ça fonctionne.
|
||||
- WSL : Ça vous ouvrira d'autres portes intéressantes.
|
||||
- git-for-windows : L'émulateur de terminal est correct, et ça vous apporte bash.
|
||||
|
||||
|
||||
## Le shell
|
||||
|
||||
Tout bon shell (bash, zsh, fish, tcsh, ...) fera l'affaire.
|
||||
|
||||
Les caractéristiques d'un bon shell, pour moi, c'est :
|
||||
|
||||
- De bons raccourcis clavier.
|
||||
- Une bonne auto-complétion.
|
||||
|
||||
::: notes
|
||||
|
||||
Une démo s'impose, au moins Ctrl-e, Ctrl-a, Ctrl-k, Ctrl-y.
|
||||
|
||||
|
||||
## Le shell
|
||||
|
||||
Les commandes indispensables :
|
||||
|
||||
- `git` : pour versionner ses projets.
|
||||
- `ls` : pour `list` directory entries.
|
||||
- `cd` : pour `change directory`.
|
||||
|
||||
::: notes
|
||||
|
||||
Dans l'ordre dans lequel je les utilise le plus souvent, #truestory.
|
||||
|
||||
Une démo s'impose.
|
||||
|
||||
|
||||
## Le shell
|
||||
|
||||
Les commandes utiles :
|
||||
|
||||
- `rm` : pour supprimer.
|
||||
- `mv` : pour renommer ou déplacer.
|
||||
- `mkdir` : pour créer un dossier.
|
||||
- `man` : pour lire le manuel.
|
||||
|
||||
::: notes
|
||||
|
||||
Une démo s'impose.
|
||||
|
||||
|
||||
# Installation de git
|
||||
|
||||
Utilisez votre gestionnaire de paquets habituel :
|
||||
|
||||
```text
|
||||
apt install git # sur Debian
|
||||
dnf install git # sur Fedora
|
||||
pkg install git # sur FreeBSD
|
||||
brew install git # sur MacOS avec homebrew
|
||||
port install git # sur MacOS avec MacPorts
|
||||
...
|
||||
...
|
||||
```
|
||||
|
||||
::: notes
|
||||
|
||||
J'aimerai un git >= 2.23.
|
||||
|
||||
|
||||
## Installation de git
|
||||
|
||||
Sinon, sur Windows, téléchargez git via :
|
||||
|
||||
https://git-scm.com/download/
|
||||
|
||||
ou git, un émulateur de terminal, et un shell via :
|
||||
|
||||
https://gitforwindows.org/
|
||||
|
||||
|
||||
## Installation de git
|
||||
|
||||
Est-ce que ça fonctionne chez vous ?
|
||||
|
||||
```text
|
||||
git --version
|
||||
```
|
||||
|
||||
|
||||
# Travailler localement
|
||||
|
||||
## `git config`
|
||||
|
||||
Git aura vite besoin de savoir qui vous êtes, on va commencer par lui dire :
|
||||
|
||||
git config --global --edit
|
||||
|
||||
|
||||
## `git init`
|
||||
|
||||
À exécuter en étant dans le dossier racine du projet à versionner.
|
||||
|
||||
Cette commande ne fait qu'une chose : créer le dossier `.git`.
|
||||
|
||||
|
||||
## Le dossier `.git`
|
||||
|
||||
C'est là où `git` range l'historique du projet, un peu de
|
||||
configuration, quelques méta-informations, ...
|
||||
|
||||
Petite exploration.
|
||||
|
||||
|
||||
## `git clone`
|
||||
|
||||
Sert à télécharger un projet (via HTTP, HTTPS, SSH, ...), essayez :
|
||||
|
||||
```bash
|
||||
git clone https://github.com/python/cpython
|
||||
cd cpython
|
||||
```
|
||||
|
||||
|
||||
## `git log`
|
||||
|
||||
Nous permet de consulter le journal, l'historique du projet :
|
||||
|
||||
- Chercher le travail d'un auteur en particulier.
|
||||
- Chercher le travail sur un fichier en particulier.
|
||||
- Chercher les modifications qui contiennent un mot spécifique.
|
||||
- ...
|
||||
|
||||
|
||||
## `git log`
|
||||
|
||||
Trouvez-moi la date du dernier commit de Python 2 via:
|
||||
|
||||
```text
|
||||
$ git log v2.7.18
|
||||
```
|
||||
|
||||
## `git log`
|
||||
|
||||
Lisez les dernières modifications de cPython via :
|
||||
|
||||
```text
|
||||
$ git log
|
||||
```
|
||||
|
||||
|
||||
## `git log`
|
||||
|
||||
Jettez un œil aux dernières modifications de la documentation de Python via :
|
||||
|
||||
```text
|
||||
$ git log Doc/
|
||||
```
|
||||
|
||||
|
||||
## `git log`
|
||||
|
||||
Trouvez-moi le commit le plus récent de Guido van Rossum en utilisant `--author`.
|
||||
|
||||
```text
|
||||
$ man git log
|
||||
```
|
||||
|
||||
|
||||
## `git status`
|
||||
|
||||
Vous pouvez abuser de cette commande, elle vous donnera même de bons
|
||||
conseils :
|
||||
|
||||
```text
|
||||
$ git status
|
||||
Sur la branche main
|
||||
Fichiers non suivis:
|
||||
(utilisez "git add <fichier>..." pour inclure dans ce qui
|
||||
sera validé)
|
||||
main.py
|
||||
README.md
|
||||
```
|
||||
|
||||
## `git add`
|
||||
|
||||
Dans notre analogie de la photo c'est<br/> « ajouter à la scène ».
|
||||
|
||||
Ajouter un fichier, ou ajouter une modification, mais ajouter.
|
||||
|
||||
::: notes
|
||||
|
||||
Insister qu'après avoir modifié deux choses, on peut, si on le
|
||||
souhaite, n'en ajouter qu'une pour faire un premier commit, puis
|
||||
ajouter la seconde pour faire un second commit.
|
||||
|
||||
|
||||
## `git commit`
|
||||
|
||||
Dans notre analogie de la photo, c'est « prendre une photo ».
|
||||
|
||||
Comme dans un manuel pour monter un meuble : on veut prendre une photo à chaque étape
|
||||
du projet.
|
||||
|
||||
|
||||
::: notes
|
||||
|
||||
Rappelez-vous qu'au dos de la photo on a des informations: qui, quand, un commentaire, ...
|
||||
|
||||
|
||||
## `git commit`
|
||||
|
||||
Une autre analogie pour `git commit` :<br/>
|
||||
c'est la sauvegarde dans un jeu vidéo.
|
||||
|
||||
::: notes
|
||||
|
||||
Il vaut mieux en faire un peu trop qu'oublier d'en faire.
|
||||
|
||||
On pourra toujours revenir à une sauvegarde en cas de problème.
|
||||
|
||||
|
||||
## `git grep`
|
||||
|
||||
Un substitut à `grep`, cherchons la définition de la fonction `random` :
|
||||
|
||||
git grep "def random("
|
||||
|
||||
Rapide, pour chercher dans ~200k lignes de code ?
|
||||
|
||||
|
||||
## `git diff`
|
||||
|
||||
Dans notre analogie de la photo, `git diff` joue aux sept différences :
|
||||
|
||||
- Entre notre travail et la `staging area`.
|
||||
- Entre la `staging area` et le dernier commit.
|
||||
- Entre deux commits.
|
||||
|
||||
::: notes
|
||||
|
||||
Insister sur le fait que git ne stocke pas les différences, il stocke
|
||||
bien l'état complet du projet, tel une photo. `git diff` nous calcule
|
||||
donc les différences à la demande.
|
||||
|
||||
|
||||
## `git diff`
|
||||
|
||||
Jettons un œil aux différences entre Python 3.10.0rc2 et Python 3.10.0 :
|
||||
|
||||
```text
|
||||
git diff v3.10.0rc2 v3.10.0
|
||||
```
|
||||
|
||||
## `git show`
|
||||
|
||||
Dans notre analogie de la photo, `git show` nous montre une photo...
|
||||
|
||||
...ou pas : nous afficher le projet entier serait monumental.
|
||||
|
||||
|
||||
## `git show`
|
||||
|
||||
`git show` montre surtout les métadonnées, et la différence apportée
|
||||
par un commit.
|
||||
|
||||
Regardons ce commit :
|
||||
|
||||
```text
|
||||
git show e1d455f3
|
||||
```
|
||||
|
||||
## Mise en pratique
|
||||
|
||||
Créez un dossier, initialisez git, et dans un fichier disons
|
||||
`git-day-1.md` prenez des notes.
|
||||
|
||||
Créez quelques commits pour apprivoiser :
|
||||
|
||||
- git add
|
||||
- git commit
|
||||
- git log
|
||||
- git show
|
||||
- git grep
|
||||
|
||||
|
||||
## `git branch` et `git switch`
|
||||
|
||||
|
||||
|
||||
## `git tag`
|
||||
## `git switch`
|
||||
## `git checkout`
|
||||
## `git rebase -i`
|
||||
## `git tag`
|
||||
## `git reset`
|
||||
## `git mv`
|
||||
|
||||
Ou `git rm` / `git add` ?
|
||||
|
||||
C'est pareil.
|
||||
|
||||
::: notes
|
||||
|
||||
Bonne pratique : Soit on modifie, soit on déplace, mais pas les deux en même temps.
|
||||
|
||||
Commençons avec un seul fichier, seul sur ma machine.
|
||||
|
||||
::: notes
|
||||
|
||||
Faire un `git init`, et quelques `git add` / `git commit`, et beaucoup
|
||||
de `git status`. Ne pas utiliser ses alias. KISS.
|
||||
|
||||
Puis leur faire faire.
|
||||
|
||||
|
||||
|
||||
# Travailler ensemble
|
||||
|
||||
## `git remote`
|
||||
|
||||
## `git fetch`
|
||||
|
||||
## `git push` / `git pull`
|
||||
|
||||
## TODO
|
||||
|
||||
- Technique : blob, tree, commit, tag.
|
||||
- Technique : graphe orienté sans cycles.
|
||||
- Technique low level : En partant des commandes de base.
|
||||
- Analogie "photo" : un commit est une photo (donc immuable) de l'état du projet.
|
||||
- Envoyer la photo par la poste c'est "git push" : une fois poussé il n'y a plus moyen de rien changer.
|
||||
- Tant qu'elle n'est pas postée, on peut toujours la déchirer.
|
||||
|
||||
|
||||
Ajouter https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRMWWzVbPEKe6JITYNNJ0AqsqWQlIxV2KDHxQ&usqp=CAU dans une slide :)
|
Binary file not shown.
After Width: | Height: | Size: 65 KiB |
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
from string import ascii_letters, digits
|
||||
import re
|
||||
import doctest
|
||||
from tempfile import NamedTemporaryFile
|
||||
import importlib.util
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("file", nargs="+")
|
||||
parser.add_argument("-v", "--verbose", action="store_true")
|
||||
parser.add_argument("-d", "--debug", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
for file_to_test in args.file:
|
||||
with open(file_to_test) as f:
|
||||
source = f.read()
|
||||
|
||||
with NamedTemporaryFile(mode="w", suffix=".py") as f:
|
||||
for example_match in re.finditer("```python.*?```", source, re.S):
|
||||
example = example_match.group()
|
||||
example = "\n".join(example.split("\n")[1:-1])
|
||||
lineno = source[:example_match.start()].count("\n") + 1
|
||||
function_name = ''.join(letter if letter in ascii_letters + digits else '_' for letter in file_to_test[:-3]) + "_line_" + str(lineno)
|
||||
if example.startswith(">>> "):
|
||||
if '"""' in example:
|
||||
f.write(f"""def _{function_name}():\n r'''""" + example + """\n'''\n\n""")
|
||||
else:
|
||||
f.write(f'''def _{function_name}():\n r"""''' + example + '''\n"""\n\n''')
|
||||
else:
|
||||
f.write(example + "\n\n")
|
||||
f.flush()
|
||||
if args.debug:
|
||||
with open(f.name) as py_source:
|
||||
print(py_source.read())
|
||||
spec = importlib.util.spec_from_file_location("to_test", f.name)
|
||||
to_test = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(to_test)
|
||||
doctest.testmod(to_test, verbose=args.verbose)
|
Loading…
Reference in New Issue