Idées intéressantes repérées dans Observable Framework
(simonwillison.net)- Observable Framework est un outil open source qui va au-delà du modèle de notebook, très adapté à l’exploration de données ad hoc, pour déployer sous forme de sites statiques des data apps, dashboards et rapports à chargement rapide
- Les blocs de code
jset les expressions inline dans Markdown s’exécutent dans le navigateur, et lorsque des valeurs réactives commenowchangent, les affichages associés se mettent aussi automatiquement à jour - Framework conserve la réactivité d’Observable Notebooks tout en offrant un fichier Markdown unique, du JavaScript standard et un workflow compatible avec Git
- Les bibliothèques comme
Inputs,d3etPlotsont chargées paresseusement pendant le développement, et lors du build et du déploiement, seul le code référencé est automatiquement chargé depuis le CDN jsdelivr - Avec un Data loader, on peut préparer les données au moment du build dans n’importe quel langage et les empaqueter sous forme de fichiers statiques comme JSON ou CSV, ce qui permet de déployer des dashboards moins dépendants d’un backend
Un générateur de sites statiques pour data apps
- Observable Framework est un générateur de sites statiques qui compile du Markdown, du JavaScript et, si nécessaire, d’autres langages en pages interactives
- Il inclut un serveur avec hot reloading complet : quand vous modifiez et enregistrez un fichier dans votre éditeur, les changements apparaissent immédiatement dans le navigateur
- Une fois le travail terminé, une commande de build permet de produire un ensemble de fichiers statiques
- Ces fichiers peuvent être déployés sur un serveur
- Ils peuvent aussi être déployés directement sur la plateforme de partage authentifiée d’Observable avec
npm run deploy
JavaScript exécuté dans Markdown
- Le principe central de Framework consiste à insérer du JavaScript dans un document Markdown pour créer un document interactif
- Les blocs de code Markdown étiquetés
jss’exécutent comme du JavaScript dans le navigateur de l’utilisateur - Des expressions inline sont également possibles, par exemple pour afficher l’heure actuelle sous forme de chaîne avec
${new Date(now)} nowest une variable spéciale qui fournit l’heure courante en millisecondes depuis l’epoch et se met à jour en continu- Quand
nowchange, les cellules et expressions inline qui y font référence se mettent aussi à jour
- Quand
- Dans Observable Notebooks, le code et le Markdown sont écrits dans des cellules séparées, alors que dans Framework les deux se trouvent dans un seul document texte
- Les expressions inline et les blocs
jspeuvent avoir des modes d’affichage différents- Les expressions inline utilisent la représentation textuelle par défaut des objets JavaScript
- Les blocs
jsutilisent la fonctiondisplay()d’Observable, dont les règles d’affichage se trouvent dans inspect/src/inspect.js
Conservation du modèle d’exécution réactif
- La réactivité, fonctionnalité centrale d’Observable Notebooks, est conservée dans les documents JavaScript Markdown de Framework
- Lorsqu’une cellule change, les autres cellules qui en dépendent sont automatiquement réévaluées
- C’est une différence majeure avec les Jupyter notebooks, et aussi l’une des fonctionnalités emblématiques de marimo, un outil de notebooks Python
- L’effet est particulièrement fort avec les champs de formulaire
- En ajoutant une entrée à la page puis en référençant sa valeur ailleurs dans le document, on peut facilement créer des interactions en temps réel
Exemple de dashboard de téléchargements PyPI
- Le dashboard d’exemple affiche les statistiques de téléchargement PyPI par package Python, et la version Observable Framework tient dans un document Markdown de 57 lignes
- L’utilisateur choisit un package dans le tableau
packagesavecInputs.select()Inputs.select()est une méthode incluse dans Framework et documentée dans Observable Inputs- La fonction
view()est une nouveauté de Framework qui permet aux changements de sélection d’une entrée d’être reflétés dans les autres blocs de code du document
packageNameest défini avecconstet peut être utilisé dans les autres blocsjsde la page- Les données sont récupérées avec
d3.json()- Dans Framework, D3 est disponible dans son intégralité
- L’URL inclut le nom du package sélectionné
- La source de données est l’API JSON de Datasette
- La table SQLite se trouve sur datasette.io/content/stats et est mise à jour une fois par jour avec les dernières statistiques des packages PyPI
- Le workflow GitHub Actions associé avait été abordé dans un précédent billet sur les baked data
- Ajouter
.jsonà l’URL renvoie du JSON- Seules les lignes correspondant à un package précis sont demandées
- Elles sont triées par date décroissante
- Jusqu’à 1 000 lignes sont reçues sous forme de tableau d’objets
- Les dates SQLite sous forme de chaînes sont converties en objets JavaScript
Dateavecd3.timeParse("%Y-%m-%d") - Le graphique est rendu avec Observable Plot, empaqueté avec Framework
- La liste des packages est obtenue en exécutant directement une requête SQL sur la base
/contentde Datasette- La requête est
select package from stats group by package order by max(downloads) desc _shape=arrayfirstest un raccourci qui permet de recevoir la première colonne des lignes de résultat sous forme de tableau JSON
- La requête est
N’inclure que le code utilisé
- Le dashboard d’exemple utilise des bibliothèques supplémentaires comme
Inputs,d3etPlot - En mode développement, un chargement paresseux est appliqué
- Le code n’est chargé que lorsqu’une cellule tente de l’utiliser pour la première fois
- Quand l’application est buildée et déployée, Framework charge automatiquement uniquement le code des bibliothèques référencées depuis le CDN jsdelivr
Mise en cache des données au moment du build
- Le Data loader de Framework est une fonctionnalité qui prépare les données du dashboard au moment du build
- Un dashboard Framework peut utiliser
fetch()ou ses wrappers à l’exécution pour récupérer des données depuis n’importe où- Observable Notebooks fonctionne de la même manière
- Avec cette approche, les performances du dashboard dépendent du backend auquel il est connecté
- Framework recommande un modèle dans lequel les données destinées au dashboard sont créées au moment du déploiement, puis seuls les sous-ensembles nécessaires sont empaquetés comme fichiers statiques
- Les fichiers de données statiques peuvent être servis rapidement depuis le même hébergement statique que le code du dashboard
- Un Data loader est un script écrit dans n’importe quel langage de programmation
- Au moment du build, Framework exécute le script
- Le résultat de la sortie standard du script est enregistré dans un fichier
- L’exemple consiste à placer
curl https://earthquake.usgs.gov/earthquakes/feed/…dans un fichierquakes.json.sh- Lors du build, le nom du fichier indique à Framework que le fichier de destination est
quakes.jsonet que le loader à exécuter est en.sh
- Lors du build, le nom du fichier indique à Framework que le fichier de destination est
- Tant qu’une technologie peut écrire du JSON, du CSV ou un format utile sur la sortie standard, elle peut servir à récupérer les données
Différences avec Observable Notebooks
- Observable Framework réutilise beaucoup d’idées et de code d’Observable Notebooks, mais diffère fortement par son format de fichier et son environnement d’exécution
- Les Observable Notebooks existants présentent les caractéristiques suivantes par rapport aux Jupyter Notebooks
- Ils utilisent JavaScript, et non Python
- L’éditeur de notebooks lui-même n’est pas open source ; c’est un produit hébergé proposé sur observablehq.com
- Les notebooks peuvent être exportés en fichiers statiques et exécutés n’importe où, mais l’éditeur est propriétaire
- Les cellules sont réactives : lorsqu’une cellule change, les autres cellules qui en dépendent sont automatiquement réévaluées, comme dans Excel
- Pour prendre en charge le modèle de réactivité, un mot-clé personnalisé appelé
viewofa été créé, ce qui signifie que la syntaxe JavaScript n’est pas entièrement standard - Les notebooks modifiables utilisent un format de fichier propriétaire complexe, qui s’intègre mal avec des outils comme Git ; Observable a donc implémenté son propre système de gestion de versions et de collaboration
- Observable Framework transpose ce modèle vers un format de fichier plus simple et un environnement d’exécution open source
- Un document est un fichier Markdown unique contenant des blocs JavaScript
- Il reste réactif, mais peut être édité avec n’importe quel éditeur de texte et placé dans Git
- L’ensemble est open source sous licence ISC, et toute la pile d’édition peut s’exécuter sur une machine locale
- Il utilise uniquement du JavaScript standard, sans syntaxe personnalisée
Changement de cap d’Observable
- Observable Framework semble marquer une évolution d’Observable, qui s’éloigne de son outil de collaboration centré sur l’éditeur propriétaire Observable Notebook pour se rapprocher des outils pour développeurs
- La bio Twitter d’Observable est : “The end-to-end solution for developers who want to build and host dashboards that don’t suck”
- Une copie de l’Internet Archive du 3 octobre 2023 indiquait : “Build data visualizations, dashboards, and data apps that impact your business — faster.”
- L’usage d’Observable Notebooks peut être limité par le caractère propriétaire de la plateforme et les restrictions des comptes gratuits, notamment l’absence de notebooks privés gratuits
- Les bibliothèques open source comme Observable Plot sont déjà considérées comme des technologies que l’on peut utiliser activement
- Observable Framework réimplémente les idées qui rendaient Observable Notebooks attractif dans un modèle open source, en JavaScript standard, avec un fichier texte unique et un déploiement statique
1 commentaires
Avis sur Hacker News
À certains égards, Observable Framework ressemble à Avengers: Endgame dans l’univers cinématographique de Mike Bostock
Il rassemble d3, Observable, Observable Plot et HTL, tout en y ajoutant pas mal d’idées nouvelles
Observable a déjà une intégration IA, et cela ressemble à un wrapper qui permet à l’IA de composer et réutiliser plus facilement ses briques. L’évaluation de la stratégie sans l’IA m’a paru un peu maladroite
Aujourd’hui, j’ai commencé à regarder comment héberger des Jupyter Notebooks statiques ou les rendre interactifs avec du WASM, et pour la plupart des usages, Observable Framework semble mieux adapté
Le problème d’Observable, c’est que même si cela ressemble à une galerie d’exemples d3, le code est conçu pour s’exécuter dans ce framework, donc on ne peut pas simplement le copier-coller
d3 n’est déjà pas particulièrement simple à utiliser sans exemples, surtout avec des changements parfois incompatibles entre versions. Cela dit, le site contient énormément de graphismes impressionnants
[0] https://observablehq.com/@d3/gallery
C’est suffisamment proche du langage de base pour qu’on ait pu simplement utiliser du JavaScript, en ajoutant juste un peu d’API pour l’affichage graphique
L’essentiel du travail consiste à réécrire les définitions de cellules de premier niveau
[0]: https://observablehq.com/@bumbeishvili/convert-observable-co...
Ce reproche revenait particulièrement souvent pour les notebooks ObservableHQ. Ils finissent par être à la fois d’excellents exemples et des ressources inutilisables. Cela dit, ce Framework a l’air plus ouvert, et au moins l’auto-hébergement est possible, donc je continue de surveiller ça
En plus, l’article explique justement que le nouveau Observable Framework élimine une partie des problèmes des anciens notebooks Observable. L’observation est donc un peu à côté du sujet, puisque désormais c’est plutôt « tout est en JavaScript standard, sans syntaxe personnalisée »
Le Framework est aussi très facile à déployer sur GitHub Pages
Voici les étapes et un exemple de GitHub Action : https://notes.billmill.org/programming/observable_framework/...
L’auteur a mis le doigt juste à propos du Framework
J’ai créé un petit graphique interactif avec Observable Framework (https://github.com/willmeyers/observable-ssta), et la configuration comme le rendu des données ont été d’une facilité presque incroyable. Mon seul reproche, c’est que j’aimerais pouvoir configurer le chargeur de données Python pour qu’il utilise un virtualenv
Il suffit de créer un projet Python, puis, au moment de lancer le serveur de développement, d’exécuter
poetry run yarn run devau lieu deyarn run dev, et Python s’exécutera dans le virtualenv. Cette configuration permet aussi de définir du code réutilisable et testable unitairement dans des paquets Python personnalisés, puis de l’importer depuis des fichiers*.json.pypour qu’ils restent très simplesnode, les outils comme npm/yarn et le JavaScript restent ainsi eux aussi dans le venv
.shet le faire pointer vers le chemin complet debin/pythondans le répertoire de l’environnement virtuel ?J’ai récemment bouclé mon premier projet « en conditions réelles » avec des notebooks Observable
Cela incluait l’apprentissage d’Observable Plot, d’Arquero, une remise à niveau sur une partie de JavaScript, et l’intégration avec un simulateur en Rust qui générait les données. Franchement, c’était excellent. Il a fallu investir pas mal d’énergie pour apprendre les outils, et la possibilité de paramétrer le générateur de données reste encore limitée, mais le notebook final est magnifique et fonctionne très bien
Avec le Markdown et la réactivité, ce type de notebook me paraît enfin réellement utilisable. Le format personnalisé de Jupyter complique énormément le versioning, et sans réactivité, les notebooks conçus de façon itérative finissent vite en chaos à état interne, agréable à écrire mais pénible à relire. J’ai aussi essayé Quarto et son intégration Observable, mais cela ressemblait à du bricolage temporaire
Honnêtement, c’est la première fois que rédiger un notebook et le partager avec d’autres m’a paru plaisant et enthousiasmant. Il y aura encore des aspérités, mais depuis ce projet, c’est devenu mon premier choix parmi les outils de notebook
[0]: https://living-papers.vercel.app/
Si vous voulez essayer rapidement le Framework et le manipuler dans le navigateur, un Codespace devcontainer qui configure automatiquement les environnements Node et Python a été préparé.
[0]: https://github.com/dleeftink/observable-codespace
Faut-il passer de Jupyter Notebook à Observable ? Ou bien est-ce une mauvaise manière de poser le débat que de les opposer ainsi ?
Pour reformuler le contenu de l’article, tout ce qui se trouve dans un bloc de code avec l’indication de contenu
jss’exécute immédiatement dans le navigateur de l’utilisateur.Pour afficher le code, il faut utiliser l’indication
js echo. En pensant à la rétrocompatibilité, l’inverse n’aurait-il pas été préférable ? Par exemple, laisser le marqueurjslargement utilisé tel quel, et faire du code exécuté dans le navigateur un comportement optionnel via une indication de typejs exec. Dans la structure actuelle, l’intégration du moteur de rendu dans une application existante oblige à gérer séparément les endroits où l’exécution est autoriséeCela pourra se faire par page via le front matter, ou à l’échelle de tout le projet via la configuration du projet. On pourra alors mettre
js run=falsepar défaut, puis n’activer le code vivant qu’avecjs runquand on le souhaite. Cela dit, notre principal cas d’usage reste le code live, c’est pourquoi nous avons choisi ce comportement par défautDésormais, il n’est plus possible d’afficher simplement un bloc de code Mermaid sans retirer l’annotation de langage
J’ai passé une nuit entière absorbé par Observable Framework, et c’était excellent.
C’était très peu intrusif, et j’ai pu visualiser et explorer en détail mon historique Google Maps. La partie liée à l’environnement des data loaders n’était pas totalement claire, mais le fait que Python s’exécute dans un environnement poetry a réglé le problème.
Comme j’aime Kotlin, j’ai aussi essayé de créer un data loader pour les scripts Kotlin, mais il y avait quelques aspérités. Kotlin s’attend à ce que le nom du fichier de script soit
foo.main.kts, tandis qu’Observable attend l’extensionfoo.exepour un loader exécutable avec shebang. J’ai donc créé un script proxy exe qui invoque le script Kotlin, mais dans ce cas le rechargement automatique des données ne se déclenche pas.Un léger inconfort par rapport à marimo ou Jupyter concerne l’usage de variables entre le data loader et le notebook. Par exemple, si l’on veut modifier la plage de données récupérée par le loader à l’aide d’un composant de vue de sélection de date, la manière de faire n’est pas claire. Cela ralentit un peu l’analyse exploratoire. Je sais bien que cela va à l’encontre du paradigme, mais je voulais le signaler. Au final, pendant l’exploration, on peut être amené à déplacer une part importante de la transformation des données dans le notebook, ce qui n’est pas idéal du point de vue des performances.
Enfin, il serait bien de pouvoir définir les data loaders en ligne. J’aime les fichiers uniques, donc si le Framework pouvait extraire un bloc de code Python en fichier, ce serait un petit gain appréciable en qualité de vie. C’est encore le début, mais le Framework semble prometteur. J’aimerais pouvoir y mettre toutes mes notes Markdown pour obtenir un environnement de type org-mode sans aller jusqu’à un Emacs complet
.shou.exe.Elle permettra de spécifier l’interpréteur associé à une extension de fichier donnée. Par exemple,
.ktspour Kotlin. https://github.com/observablehq/framework/pull/935Le fonctionnement des data loaders à partir d’entrées va un peu à l’encontre de l’orientation du Framework, qui préfère des instantanés de données statiques. L’objectif est d’avoir un site généré autonome et performant. Cela dit, une technique qui fonctionne bien consiste à générer dans le data loader un sur-ensemble des données à explorer sous forme de fichier Parquet, puis à extraire côté client, avec DuckDB/SQL, le sous-ensemble à visualiser. Les performances sont généralement bonnes, mais cela dépend évidemment de la taille du sur-ensemble que l’on veut manipuler
Observable s’intègre très bien à ClickHouse via une API REST.
Voici un exemple : https://observablehq.com/@stas-sl/github-issues-survival-ana...
Je n’ai pas encore essayé le nouveau Observable Framework, mais il serait intéressant de voir un exemple similaire qui interroge une base de données en temps réel. J’espère que l’architecture ne se limite pas à précharger et mettre en cache toutes les données. Comme ce type d’application doit être interactif, l’idéal serait même d’exposer l’édition live du SQL
Cette démo charge les données à l’exécution avec
fetch(): https://simonw.github.io/observable-framework-experiments/pa...