Voici les grandes lignes de cette évaluation; les détails sont donnés dans la suite, mais dans les grandes lignes:
-
on vous fournit un dépôt git contenant un code de départ; en fait principalement 3 fichiers contenant le squelette du code que vous devez écrire
-
dans une première phase de découverte, vous allez devoir comprendre par vous-même comment sont conçus divers formats de stockage des notebooks Jupyter (les .ipynb, les .py notamment)
-
vous devrez ensuite écrire le code demandé, sachant que les fichiers squelette contiennent une description de ce qui est attendu, sous forme de code exécutable (donc utile pour vos tests)
-
on vous propose également, sous forme de points bonus, d'écrire un rapport qui explicite votre démarche
-
pour le rendu: les modalités exactes peuvent dépendre de votre professeur, mais dans tous les cas vous devrez repoussez votre repo git sur github; vous pouvez committer et pousser aussi souvent que vous le jugez utile, les copies seront relevées à l'issue de la deadline qui est fixée au
20 Janvier 2022 à minuit
Ce dépôt git contient principalement les fichiers:
-
🗒️
README.md
: description du projet (ce document) -
📁
samples
: notebooks de référence, qui vont servir de cobaye pour faire tourner votre code (notez bien que tous ces fichiers sont encodés en UTF-8) -
trois ébauches de programme
que vous allez devoir compléter - chacun fait l'objet d'une section de cet énoncé
-
⚙️
environment.yml
: fichier pour créer l'environnement condapython-advanced-eval
qui contient les modules Python dont vous aurez besoingrader.py
: fichier qui effectue la correction automatique sur votre machine, et dont la note correspond à celle calculée sur GitHub
Votre clone de ce dépôt (votre rendu) devra au final comporter les fichiers :
- 🐍
notebook_v0.py
,notebook_v1.py
etnotebook_v0.py
: vos programmes finalisés. - 📘
rapport.ipynb
: expériences commentées et analyses, documentation des développements, etc.. Vous êtes invité à y expliquer votre démarche, surtout lorsque vous avez rencontré des difficultés, et à y présenter les problèmes, options (comment aborder le problème), décisions (quelle option choisir), résultats (si/comment ça a fonctionné). Cette partie sera notée par des points bonus.
Pour créer l'environnement conda:
conda env create -f environment.yml
Dans l'environnement conda python-advanced-eval
:
-
Pour produire la documentation de par exemple
notebook_v0.py
:python -m pydoc notebook_v0.py
il est sans doute habile de stocker cette documentation dans un fichier comme ceci (depuis un terminal bash)
python -m pydoc notebook_v0 > notebook_v0_doc.py
et remarquez qu'alors
notebook_v0_doc.py
contient une description de chacune des fonctions que vous allez devoir écrire, avec des exemples que vous pouvez copier-coller dansipython
pour tester votre code. -
Pour tester les exemples de code contenus dans un fichier :
python -m doctest notebook_v0.py
Évidemment au tout début, le squelette qu'on vous fournit ne contient aucune implémentation, et à ce stade de nombreux tests échouent 😭.
-
Pour lancer l'ensemble des tests et calculer votre note:
python grader.py
Évidemment, vous devriez obtenir 0/20.
-
Prenez garde enfin à ne pas modifier les docstrings qui sont présents dans les fichiers ébauche, ce qui pourrait produire des résultats de test erronés dans votre environnement, et vous induire en erreur
Le format standard décrivant les notebooks Jupyter est le format ipynb
(pour IPython notebook);
les fichiers dans ce format utilisent l'extension .ipynb
.
Dans ce projet, nous vous suggérons pour comprendre ce format d'étudier
quelques notebooks élementaires, disponibles dans le dossier 📁 samples
.
Si plus de détails sont nécessaires, le format est documenté ici :
Le format ipynb est un dialecte du format JSON. Voilà un exemple de fichier JSON valide :
{
"id": 984549706549166055,
"text": "🔥 This is fine. 🔥",
"isRetweet": false,
"isDeleted": false,
"favorites": 49,
"retweets": 255,
"date":"2011-08-02 18:07:48",
"isFlagged": true,
"meta": null
}
Le module standard json
de Python permet de faire la conversion
entre de tels contenus textuels et les objets natifs de Python (dictionnaires,
lists, chaînes de caractères, etc.). L'exemple JSON précédent serait par exemple
converti dans le dictionnaire Python :
{'id': 984549706549166055, 'text': '🔥 This is fine. 🔥', 'isRetweet': False, 'isDeleted': False, 'favorites': 49, 'retweets': 255, 'date': '2011-08-02 18:07:48', 'isFlagged': True, 'meta': None}
Pour faciliter la manipulation de notebooks Jupyter en Python :
- 🚀 Développez les fonctions
load_ipynb
etsave_ipynb
.
Etudiez la structure de haut-niveau des notebooks ipynb, puis :
- 🚀 Développez les fonctions
get_format_version
,get_metadata
etget_cells
.
Le format
percent
est une variante du format standard .ipynb
pour décrire les notebooks Jupyter.
Les notebooks sont alors représentés comme du code source Python avec le contenu
Markdown en commentaire ; le résultat de l'exécution des cellules n'est pas pris
en compte. Par exemple, le notebook contenu dans samples/hello-world.ipynb
serait représenté dans ce format par le texte :
# %% [markdown]
# Hello world!
# ============
# Print `Hello world!`:
# %%
print("Hello world!")
# %% [markdown]
# Goodbye! 👋
Ce format permet de manipuler les notebooks comme du code Python classique avec
les avantages afférents. Certains environnements de développement, dont VS Code,
fournissent de plus un support spécifique pour ce format. Quand la bibliothèque
python jupytext
est installée, l'application Jupyter notebook devient aussi
capable de lire ce format (ce qu'on a utilisé en cours tout au long du semestre)
- 🚀 Développez la fonction
to_percent
.
🗝️ Pour valider le code, on pourra convertir les notebook Jupyter de reférence en fichiers source Python au format percent, puis les valider avec VS Code ou le Jupyter notebook avec l'extension jupytext.
Starboard est une plate-forme open-source de notebooks s'exécutant intégralement dans le navigateur web, où peuvent être utilisés les langages Python et Javascript.
Les notebooks Starboard sont décrits dans une variante du format
percent,
stockés dans des fichiers d'extensions .nb
et exportés comme des fichiers HTML
pour être exploités dans un navigateur.
ℹ️ Vous pouvez essayer ce système ici sur un notebook intitulé "🐍 Python support in Starboard notebook" qui illustre ce qu'on peut faire.
Pour voir ce notebook au format .nb
, ouvrez le notebook puis
cliquez sur le très discret bouton "Source" dans le pied de page du document.
De là, pour voir ce notebook au format HTML, cherchez un bouton 'Export HTML'
- 🚀 Développez la fonction
to_starboard
.
🗝️ Pour valider ce code, on pourra convertir les notebooks Jupyter de reférence
(dans le dossier samples
) en fichiers Starboard HTML, puis vérifier en les
ouvrant dans un navigateur Web que la conversion est valide.
Dans toute cette partie on suppose que le notebook a été au préalable exécuté par ailleurs
Pour nettoyer un notebook de toute trace d'exécution :
- 🚀 Développez la fonction
clear_outputs
.
Capturez le résultat des appels à print
:
- 🚀 Développez la fonction
get_stream
.
L'exécution d'une cellule de code peut engendrer des erreurs; pour les analyser :
- 🚀 Développez la fonction
get_exceptions
.
Pour capturer les images générées par matplotlib :
- 🚀 Développez la fonction
get_images
.
🗝️ On pourra utiliser la fonction imshow
de Matplotlib pour valider
les résultats.
Le code produit précédemment est parfaitement fonctionnel, mais il manque fondamentalement de structure. En particulier, nous devons passer le gros dictionnaire contenant tout le contenu du notebook initial, ce format étant peu expressif et non spécifique à notre domaine d'étude.
Cette partie vous propose donc de créer une représentation objet du contenu du notebook, qui permettra d'améliorer sensiblement le code:
- en le rendant plus lisible
- en ne gardant en mémoire que les données qui nous sont réellement utiles (en supprimant par exemple les "meta-datas")
- en permettant de découpler le chargement du notebook de l'exploitation de ces données
Dans la suite de ce sujet, nous allons développer deux versions
"orientées-objet" (c'est à dire avec des class
es) de notre code initial":
- une version qui construit un
Notebook
en prenant en argument le contenu du fichier.ipynb
- une autre version qui construit un
Notebook
en prenant directement le contenu des cellules; ce sera l'objet de la section suivante
Pour la première version, les développements demandés doivent être réalisés dans
le fichier notebook_v1.py
.
Vous êtes évidemment invités à utiliser vos travaux précédents comme références, ainsi que les tests fournis, pour guider votre réalisation.
Le fichier notebook_v0_objet.py
contient le squelette de la classe Notebook
représentant le notebook Jupyter ainsi que deux classes CodeCell
et
MarkdownCell
représentant les différentes cellules.
Remplissez tous les constructeurs (méthodes __init__
) des classes proposées:
- 🚀 Développez la méthode
__init__
de la classeNotebook
. - 🚀 Développez la méthode
__init__
de la classeCodeCell
. - 🚀 Développez la méthode
__init__
de la classeMarkdownCell
.
Le fonctionnement attendu est le suivant:
- la classe Notebook se construit à partir du contenu du fichier chargé
- elle contient un numéro de version (attribut
version
) - et une liste de cellules (
cells
) contenant soit des instances deCodeCell
(pour les cellules Python) ou deMarkdownCell
(pour les cellules Markdown)
on aura donc un fonctionnement que l'on peut illustrer ainsi:
┌─────────────────────────┐
│ Notebook │
├─────────────────────────┤
│ │
│ version │ ┌────────────────────┐
│ │ | liste de CodeCell │
│ cells ─────────┐ │ │ ou MarkdownCell │
│ ▼ │ └────────────────────┘
│ ┌─────┬─────┬─────┬─────┼─────┬─────┬─────┬─────┬─────┐
│ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │
│ └─────┴─────┴─────┴─────┼─────┴─────┴─────┴─────┴─────┘
│ │
└─────────────────────────┘
Nous ne chargerons pas les metadata
ni les outputs
dans cette partie, les
classe CodeCell
et MarkdownCell
aurons donc les propriétés suivantes:
┌─────────────────────────┐ ┌─────────────────────────┐
│ CodeCell │ │ MarkdownCell │
├─────────────────────────┤ ├─────────────────────────┤
│ id │ │ id │
│ type │ │ type │
│ execution_count │ │ source │
│ source │ └───▲─────────────────────┘
└─────▲───────────────────┘ │
│ │
│ │ source est une
└──────────────────────────┴─ liste de str
(1 par ligne)
- 🚀 (BONUS, Optionnel) Développez une classe
Cell
parente deCodeCell
etMarkdownCell
pour factoriser le code commun.
On rajoute ensuite une méthode statique (= de classe) pour faciliter le chargement d'un fichier dans un Notebook:
- 🚀 Développez la méthode statique
from_file
de la classeNotebook
.
On peut itérer un Notebook
, ce qui revient à parcourir ses cellules dans
l'ordre avec une boucle for
:
- 🚀 Développez la méthode
__iter__
de la classeNotebook
.
De même, nous pouvons créer une classe Serializer
qui permet de sauvegarder un
Notebook
sur le disque.
Attention cependant:
- il faudra rajouter un objet vide
metadata
au notebook au format JSON - de même il faudra rajouter un objet vide
metadata
aux cellules
vous pouvez vous laisser guider par les tests pour valider votre implémentation.
- 🚀 Développez les méthodes de la classe
Serializer
.
Nous pouvons aussi utiliser ce format Notebook
"objet" pour réaliser des
transformations de format, par exemple le transformer au format "py-percent":
- 🚀 Développez les méthodes de la classe
PyPercentSerializer
.
Nous proposons également d'afficher le contenu du notebook dans le terminal, de façon personnalisée:
- 🚀 Développez les méthodes de la classe
Outliner
.
Nous allons maintenant modifier la façon dont les objets Notebook
, CodeCell
et MarkdownCell
sont construits.
L'idée est de découpler complètement note code représentant les notebooks de la
façon dont ils sont enregistrés dans les fichiers .ipynb
.
💡 Si vous faites correctement votre travail, les classes PyPercentSerializer
, Serializer
et Outliner
développées précedemment
doivent fonctionner avec cette nouvelle version de la class Notebook
!.
C'est une illustration de l'intérêt de l'encapsulation !
Ouvrez désormais le fichier notebook_v2.py
.
- 🚀 Développez la méthode
__init__
simplifiée pour la classeNotebook
. - 🚀 Développez la méthode
__init__
simplifiée pour la classeCodeCell
. - 🚀 Développez la méthode
__init__
simplifiée pour la classeMarkdownCell
.
vous pourrez évidemment vous inspirer de ce que vous aviez fait pour la version précédente.
Notre class Notebook
ne comprenant plus le format .ipynb
, nous devons
extraire la logique de chargement dans une classe dédiée:
- 🚀 Développez les méthode de la class
NotebookLoader
.
On se propose de transformer le notebook en Markdown.
Pour cela:
- nous générons un nouveau notebook
- les cellules de type "code" seront transformées en cellules de type "markdown".
En pratique la transformation en markdown se fera en "entourant" le code de délimiteurs spéciaux, ainsi:
print("Hello world")
devient:
``` python print("Hello world") ```
- 🚀 Développez les méthode de la class
Markdownizer
.
Une autre transformation possible est la suppression des cellules
MarkdownCell
, pour ne garder que le code:
- 🚀 Développez les méthode de la class
MarkdownLesser
.
Enfin, comme nous avons découplé Notebook
de la structure JSON des fichiers
.ipynb
, on peut désormais faire le code nécessaire pour recharger les
fichiers au format py-percent:
- 🚀 Développez les méthode de la class
PyPercentLoader
.