examples/module-loading/06_custom_functions.py
#!/usr/bin/env python3
"""
Exemples pratiques d'extension de Catnip avec des fonctions personnalisées.
"""
from catnip import Catnip, Context, pass_context
# Exemple 1 : Calculatrice avec historique
def example_calculator():
"""Calculatrice qui garde l'historique des opérations."""
print("\n⇒ Exemple 1 : Calculatrice avec historique\n")
class Calculator:
def __init__(self):
self.history = []
def add_to_history(self, operation, result):
self.history.append({'operation': operation, 'result': result})
return result
def show_history(self):
return self.history
def clear_history(self):
self.history = []
return "History cleared"
calc = Calculator()
@pass_context
def calculate(ctx, expr):
"""Évalue une expression et l'ajoute à l'historique."""
result = ctx.result
calc.add_to_history(expr, result)
return result
ctx = Context()
ctx.globals['calc'] = calc
ctx.globals['record'] = calculate
catnip = Catnip(context=ctx)
# Utilisation - opérations simples
operations = [
'x = 10 + 5',
'y = x * 2',
'z = y - 3',
'z',
]
for op in operations:
catnip.parse(op)
result = catnip.execute()
print(f"{op:20} = {result}")
print(f"\nNote: Les variables sont stockées dans le context local")
# Exemple 2 : Validation de données
def example_validation():
"""Système de validation de données avec règles personnalisées."""
print("\n⇒ Exemple 2 : Validation de données\n")
class Validator:
def __init__(self):
self.errors = []
def is_email(self, value):
"""Vérifie si c'est un email valide (simplifié)."""
return '@' in value and '.' in value.split('@')[1]
def is_between(self, value, min_val, max_val):
"""Vérifie si la valeur est dans la plage."""
return min_val <= value <= max_val
def is_not_empty(self, value):
"""Vérifie que la valeur n'est pas vide."""
return bool(value and str(value).strip())
def add_error(self, field, message):
"""Ajoute une erreur."""
self.errors.append({'field': field, 'message': message})
def is_valid(self):
"""Retourne True si aucune erreur."""
return len(self.errors) == 0
def get_errors(self):
"""Retourne la liste des erreurs."""
return self.errors
def reset(self):
"""Réinitialise les erreurs."""
self.errors = []
validator = Validator()
ctx = Context()
ctx.globals['v'] = validator
catnip = Catnip(context=ctx)
# Données à valider
user_data = {'email': 'test@example.com', 'age': 25, 'name': 'Alice'}
ctx.globals['user'] = user_data
# Règles de validation en Catnip
validation_rules = '''
v.is_email(user.email) == False and v.add_error("email", "Email invalide")
v.is_between(user.age, 18, 120) == False and v.add_error("age", "Age invalide")
v.is_not_empty(user.name) == False and v.add_error("name", "Nom requis")
'''
catnip.parse(validation_rules)
catnip.execute()
if validator.is_valid():
print("✓ Validation réussie !")
print(f" Données : {user_data}")
else:
print("✗ Erreurs de validation :")
for error in validator.get_errors():
print(f" - {error['field']}: {error['message']}")
# Test avec des données invalides
print("\nTest avec données invalides :")
validator.reset()
user_data_invalid = {'email': 'invalid-email', 'age': 15, 'name': ''}
ctx.globals['user'] = user_data_invalid
catnip.parse(validation_rules)
catnip.execute()
if validator.is_valid():
print("✓ Validation réussie !")
else:
print("✗ Erreurs de validation :")
for error in validator.get_errors():
print(f" - {error['field']}: {error['message']}")
# Exemple 3 : Système de templates
def example_templates():
"""Système simple de templates avec variables."""
print("\n⇒ Exemple 3 : Système de templates\n")
def render(template, **variables):
"""Rend un template avec les variables fournies."""
result = template
for key, value in variables.items():
result = result.replace(f'{{{key}}}', str(value))
return result
def upper(text):
"""Convertit en majuscules."""
return str(text).upper()
def lower(text):
"""Convertit en minuscules."""
return str(text).lower()
def capitalize(text):
"""Met la première lettre en majuscule."""
return str(text).capitalize()
ctx = Context()
ctx.globals['render'] = render
ctx.globals['upper'] = upper
ctx.globals['lower'] = lower
ctx.globals['capitalize'] = capitalize
catnip = Catnip(context=ctx)
# Template
template = "Hello {name}, you have {count} new messages!"
# Rendu avec variables
code = '''
name = "Alice"
count = 5
render(template, name=upper(name), count=count)
'''
ctx.globals['template'] = template
catnip.parse(code)
result = catnip.execute()
print(f"Template: {template}")
print(f"Rendu: {result}")
# Exemple 4 : État partagé entre exécutions
def example_shared_state():
"""Démontre comment partager un état entre plusieurs exécutions."""
print("\n⇒ Exemple 4 : État partagé\n")
class Counter:
def __init__(self):
self.value = 0
def increment(self, step=1):
self.value += step
return self.value
def decrement(self, step=1):
self.value -= step
return self.value
def reset(self):
self.value = 0
return self.value
def get(self):
return self.value
counter = Counter()
ctx = Context()
ctx.globals['counter'] = counter
catnip = Catnip(context=ctx)
# Suite d'opérations
operations = [
'counter.increment(5)',
'counter.increment(3)',
'counter.decrement(2)',
'counter.get()',
]
print("Opérations sur le compteur :")
for op in operations:
catnip.parse(op)
result = catnip.execute()
print(f" {op:30} → {result}")
# Exemple 5 : API fluente
def example_fluent_api():
"""Démontre une API fluente (method chaining)."""
print("\n⇒ Exemple 5 : API fluente\n")
class Query:
def __init__(self):
self.filters = []
self.sort_field = None
self.limit_value = None
def where(self, field, operator, value):
"""Ajoute un filtre."""
self.filters.append({'field': field, 'op': operator, 'value': value})
return self
def sort_by(self, field):
"""Définit le tri."""
self.sort_field = field
return self
def limit(self, count):
"""Limite les résultats."""
self.limit_value = count
return self
def execute(self):
"""Exécute la requête."""
return {'filters': self.filters, 'sort': self.sort_field, 'limit': self.limit_value}
def query():
"""Crée une nouvelle requête."""
return Query()
ctx = Context()
ctx.globals['query'] = query
catnip = Catnip(context=ctx)
# Construire une requête fluente
code = '''
query()
.where("age", ">", 18)
.where("status", "==", "active")
.sort_by("name")
.limit(10)
.execute()
'''
catnip.parse(code)
result = catnip.execute()
print("Requête construite :")
print(f" Filtres : {result['filters']}")
print(f" Tri : {result['sort']}")
print(f" Limite : {result['limit']}")
# Exemple 6 : Expressions mathématiques avancées
def example_math_extensions():
"""Ajoute des fonctions mathématiques avancées."""
print("\n⇒ Exemple 6 : Extensions mathématiques\n")
import math
def factorial(n):
"""Calcule la factorielle."""
if n <= 1:
return 1
return n * factorial(n - 1)
def fibonacci(n):
"""Calcule le nième nombre de Fibonacci."""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
def is_prime(n):
"""Vérifie si un nombre est premier."""
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
ctx = Context()
ctx.globals['factorial'] = factorial
ctx.globals['fib'] = fibonacci
ctx.globals['is_prime'] = is_prime
ctx.globals['sqrt'] = math.sqrt
ctx.globals['pow'] = math.pow
catnip = Catnip(context=ctx)
# Tests
tests = [
('factorial(5)', 120),
('fib(10)', 55),
('is_prime(17)', True),
('sqrt(16)', 4.0),
('pow(2, 8)', 256.0),
]
print("Tests de fonctions mathématiques :")
for expr, expected in tests:
catnip.parse(expr)
result = catnip.execute()
status = "✓" if result == expected else "✗"
print(f" {status} {expr:20} = {result:10} (attendu: {expected})")
# Main
if __name__ == '__main__':
print("=" * 70)
print("EXEMPLES D'EXTENSION DE CATNIP")
print("=" * 70)
example_calculator()
example_validation()
example_templates()
example_shared_state()
example_fluent_api()
example_math_extensions()
print("\n" + "=" * 70)
print("Tous les exemples sont terminés !")
print("=" * 70)