examples/embedding/08_jupyter_integration.py
"""
Exemple d'intégration de Catnip dans Jupyter via magic commands IPython.
Montre comment :
1. Créer des magic commands pour Catnip
2. Utiliser Catnip dans des cellules Jupyter
3. Partager l'état entre Python et Catnip
4. Intégrer avec le namespace IPython
Use case : Analyse de données interactive dans notebooks Jupyter.
Installation :
%load_ext jupyter_integration
Utilisation :
%%catnip
x = 10
y = x * 2
y
"""
from catnip import Catnip
try:
from IPython.core.magic import Magics, magics_class, line_magic, cell_magic
from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring
IPYTHON_AVAILABLE = True
except ImportError:
IPYTHON_AVAILABLE = False
# Stubs pour la démonstration
def magics_class(cls):
return cls
def line_magic(func):
return func
def cell_magic(func):
return func
def magic_arguments():
def decorator(func):
return func
return decorator
def argument(*args, **kwargs):
def decorator(func):
return func
return decorator
def parse_argstring(func, line):
from types import SimpleNamespace
return SimpleNamespace(verbose=False, output=None)
class Magics:
def __init__(self, shell):
self.shell = shell
@magics_class
class CatnipMagics(Magics):
"""
Magic commands IPython pour Catnip.
Commandes disponibles :
- %catnip <code> : Évalue une ligne de code Catnip
- %%catnip : Évalue une cellule de code Catnip
- %catnip_load <file> : Charge et exécute un fichier Catnip
- %catnip_reset : Réinitialise le contexte Catnip
"""
def __init__(self, shell):
super().__init__(shell)
self._catnip_instance = Catnip()
# Partager le namespace IPython avec Catnip
self._catnip_instance.context.globals.update(self.shell.user_ns)
@line_magic
@magic_arguments()
@argument('-v', '--verbose', action='store_true', help='Afficher les détails')
def catnip(self, line):
"""
Évalue une ligne de code Catnip.
Usage:
%catnip x = 10; y = x * 2; y
%catnip -v fib = (n) => { if n <= 1 { n } else { fib(n-1) + fib(n-2) } }
"""
args = parse_argstring(self._catnip_instance, line)
code = line.replace('-v', '').replace('--verbose', '').strip()
try:
self._catnip_instance.parse(code)
result = self._catnip_instance.execute()
# Synchroniser le namespace
self._sync_namespace()
if args.verbose:
print(f"Code: {code}")
print(f"Résultat: {result}")
return result
except Exception as e:
print(f"Erreur Catnip: {e}")
return None
@cell_magic
@magic_arguments()
@argument('-o', '--output', help='Variable Python où stocker le résultat')
def catnip(self, line, cell):
"""
Évalue une cellule de code Catnip.
Usage:
%%catnip
x = 10
y = x * 2
y
%%catnip -o result
fib = (n) => { if n <= 1 { n } else { fib(n-1) + fib(n-2) } }
fib(10)
"""
args = parse_argstring(self._catnip_instance, line)
try:
self._catnip_instance.parse(cell)
result = self._catnip_instance.execute()
# Synchroniser le namespace
self._sync_namespace()
# Stocker dans variable Python si demandé
if args.output:
self.shell.user_ns[args.output] = result
return result
except Exception as e:
print(f"Erreur Catnip: {e}")
return None
@line_magic
def catnip_load(self, line):
"""
Charge et exécute un fichier Catnip.
Usage:
%catnip_load script.cat
"""
filename = line.strip()
try:
with open(filename, 'r') as f:
code = f.read()
self._catnip_instance.parse(code)
result = self._catnip_instance.execute()
# Synchroniser le namespace
self._sync_namespace()
print(f"Fichier chargé: {filename}")
return result
except FileNotFoundError:
print(f"Fichier introuvable: {filename}")
except Exception as e:
print(f"Erreur: {e}")
return None
@line_magic
def catnip_reset(self, line):
"""
Réinitialise le contexte Catnip.
Usage:
%catnip_reset
"""
self._catnip_instance = Catnip()
self._catnip_instance.context.globals.update(self.shell.user_ns)
print("Contexte Catnip réinitialisé")
@line_magic
def catnip_vars(self, line):
"""
Affiche les variables Catnip disponibles.
Usage:
%catnip_vars
"""
print("Variables Catnip:")
for key, value in self._catnip_instance.context.globals.items():
if not key.startswith('_'):
print(f" {key} = {value}")
def _sync_namespace(self):
"""
Synchronise les variables entre Catnip et IPython.
Les variables créées en Catnip sont disponibles en Python et vice-versa.
"""
# Catnip → Python
for key, value in self._catnip_instance.context.globals.items():
if not key.startswith('_') and not callable(value):
self.shell.user_ns[key] = value
def load_ipython_extension(ipython):
"""
Charge l'extension IPython.
Usage dans Jupyter:
%load_ext jupyter_integration
"""
if not IPYTHON_AVAILABLE:
print("IPython n'est pas installé. Installer avec : pip install ipython")
return
ipython.register_magics(CatnipMagics)
print("Extension Catnip chargée")
print("Commandes disponibles:")
print(" %catnip <code> - Évaluer une ligne")
print(" %%catnip - Évaluer une cellule")
print(" %catnip_load <file> - Charger un fichier")
print(" %catnip_reset - Réinitialiser")
print(" %catnip_vars - Lister les variables")
# --- Démonstration (simulation hors Jupyter) ---
if __name__ == '__main__':
print("▸ Exemple 1 : Magic command de base")
print()
print("""
Dans Jupyter, après avoir chargé l'extension :
%load_ext jupyter_integration
Vous pouvez utiliser Catnip directement :
%catnip x = 10; y = x * 2; y
# Output: 20
# La variable est disponible en Python
print(x) # 10
print(y) # 20
""")
print()
print("▸ Exemple 2 : Cellule Catnip avec calculs")
print()
print("""
%%catnip
# Fibonacci en Catnip
fib = (n) => {
if n <= 1 { n }
else { fib(n-1) + fib(n-2) }
}
# Calculer fib(10)
result = fib(10)
result
# Output: 55
""")
print()
print("▸ Exemple 3 : Partage d'état avec Python")
print()
print("""
# Créer des données en Python
import pandas as pd
df = pd.DataFrame({
'a': [1, 2, 3],
'b': [4, 5, 6]
})
# Utiliser en Catnip (df est disponible)
%%catnip -o col_sum
sum_a = 6 # df['a'].sum() simulé
sum_b = 15 # df['b'].sum() simulé
sum_a + sum_b
# Résultat stocké dans variable Python 'col_sum'
print(col_sum) # 21
""")
print()
print("▸ Exemple 4 : Charger un script externe")
print()
print("""
# Créer un fichier utils.cat
# factorial = (n) => { if n <= 1 { 1 } else { n * factorial(n-1) } }
# Charger dans Jupyter
%catnip_load utils.cat
# Utiliser la fonction
%catnip factorial(5)
# Output: 120
""")
print()
print("▸ Exemple 5 : Analyse de données interactive")
print()
print("""
import numpy as np
# Données NumPy
data = np.array([10, 20, 30, 40, 50])
%%catnip
# Calculer statistiques
count = 5
mean = 30 # data.mean() simulé
std = 15 # data.std() simulé
# Détection outliers (> 2 std)
threshold = mean + 2 * std
threshold
# Python peut réutiliser ces variables
print(f"Moyenne: {mean}")
print(f"Seuil: {threshold}")
""")
print()
print("▸ Simulation de session")
print()
# Démonstration simple avec Catnip directement (pas besoin d'IPython)
print("Note: Cette simulation ne nécessite pas IPython")
print("Pour utiliser réellement dans Jupyter:")
print(" 1. pip install ipython jupyter")
print(" 2. %load_ext jupyter_integration")
print()
# Démonstration simple
print("Démonstration simple :")
catnip_demo = Catnip()
code1 = "x = 42; y = x * 2; y"
catnip_demo.parse(code1)
result = catnip_demo.execute()
print(f"Code : {code1}")
print(f"Résultat : {result}")
print(f"Variables : x={catnip_demo.context.globals['x']}, y={catnip_demo.context.globals['y']}")
print()
code2 = "z = x + y; z"
catnip_demo.parse(code2)
result = catnip_demo.execute()
print(f"Code : {code2}")
print(f"Résultat : {result}")
print(f"Variable : z={catnip_demo.context.globals['z']}")