Aller au contenu

Handlers et points d'entrée

Chaque fonction sous functions: déclare un seul des deux champs suivants :

  • handler: — chaîne, mode A uniquement. Un chemin d’import Python qui résout vers un appelable. Le runner MecaPy importe le module et l’appelle.
  • entrypoint: — liste de chaînes, modes B/C uniquement. La commande argv que le worker lance dans votre conteneur après la préparation de /workspace/in/.

Déclarer les deux, aucun, ou un entrypoint: [] est refusé à la lecture du manifest.

Le handler est une chaîne unique de la forme module:function. Le module est importé relativement à la racine de votre dépôt ; la fonction est récupérée par attribut sur ce module. Seules les fonctions de niveau module sont supportéesmodule:Classe.methode, les classes appelables ou les applications partielles ne sont pas acceptées par le parseur.

functions:
size:
handler: bolts:size
def size(diameter: float, load: float) -> dict:
return {"stress": load / area(diameter)}

Le runner appelle size(**inputs)inputs est la fusion de in/data.json et des fichiers d’entrée (chaque File arrive comme pathlib.Path).

handler: pkg.subpkg.module:my_function

Le préfixe pointé est le chemin d’import, le : le sépare du nom d’attribut. Le runner ajoute /workspace/ à sys.path, donc tout ce que vous expédiez y est importable.

Si la logique vit dans une classe, exposez une fonction de niveau module qui instancie et délègue :

class Vis:
def __init__(self, designation: str): ...
def compute_stress(self, force: float) -> float: ...
# C'est ce que le manifest référence :
def stress_for(designation: str, force: float) -> float:
return Vis(designation).compute_stress(force)
handler: bolts:stress_for

Cela garde la distinction fonction/méthode hors de la plateforme — le manifest ne voit que des fonctions de niveau module, et la classe reste un détail d’implémentation interne.

En mode A, la signature annotée du handler nourrit l’introspection qui génère le formulaire d’exécution (AutoForm) :

  • Paramètres simples → champs typés (un par paramètre, nom = nom du paramètre).
  • Paramètres pathlib.Path → champs d’upload de fichier.
  • Retour dict ou TypedDict → plusieurs sorties.

Si l’introspection échoue (annotations manquantes, type de retour ambigu, etc.), le formulaire retombe sur un schéma permissif et un avertissement est émis. Annotez toujours vos paramètres — ils pilotent l’AutoForm et servent de référence pour vos lecteurs.

Pour le typage côté workflow, l’introspection ne suffit pas : il faut déclarer explicitement inputs: / outputs: avec les types du catalogue canonique (Length, Force, Stress, …). C’est ce qui permet à l’éditeur de workflow de valider les connexions au tracé. Voir ports d’E/S typés.

En modes B/C, le worker n’importe aucun Python utilisateur — il lance simplement l’entrypoint dans votre conteneur, /workspace/ étant déjà préparé.

functions:
static_support:
entrypoint: ["python3", "/workspace/_runner/wrapper.py"]

La liste est l’argv. L’élément 0 est le programme ; les suivants sont les arguments. La liste doit être non vide ; sinon le worker échoue à la validation. Les ENTRYPOINT et CMD propres à l’image sont ignorés — le worker invoque l’argv directement.

L’entrypoint est responsable du contrat de runtime complet :

  1. Lire /workspace/in/data.json et /workspace/in/files/*.
  2. Lancer le calcul.
  3. Écrire /workspace/out/data.json (un objet JSON).
  4. Écrire les fichiers de sortie déclarés à /workspace/out/files/<nom>.<ext>.
  5. Éventuellement déposer des artifacts libres dans /workspace/out/artifacts/.
  6. Éventuellement ajouter des lignes de progression à /workspace/out/progress.jsonl.
  7. En cas d’échec, écrire /workspace/out/_error.json et sortir avec un code de retour non nul.

Plusieurs fonctions du même package partagent une seule image (construite ou tirée une seule fois). Elles se distinguent par leur argv :

runtime:
kind: image
image: ghcr.io/me/solver:1.4
functions:
thermal:
entrypoint: ["/opt/run.sh", "--mode=thermal"]
mechanical:
entrypoint: ["/opt/run.sh", "--mode=mechanical"]
fatigue:
entrypoint: ["/opt/run.sh", "--mode=fatigue"]

L’image est tirée (mode C) ou construite (mode B) une fois et mise en cache ; chaque fonction la réutilise avec un argv différent.

Les surcharges resources: au niveau de chaque fonction marchent en modes B/C comme en mode A — elles changent les limites cgroup et le timeout pour les runs de cette fonction :

functions:
thermal:
entrypoint: ["/opt/run.sh", "--mode=thermal"]
resources:
recommended: { cpu: 2, memory_mb: 2048 }
timeout: 300
mechanical:
entrypoint: ["/opt/run.sh", "--mode=mechanical"]
resources:
recommended: { cpu: 4, memory_mb: 8192 } # plus de mémoire pour mécanique
timeout: 1800
QuestionRéponse
Python pur avec dépendances PyPI ?Mode A. Pas de Dockerfile à écrire.
Solveur natif, paquet système, GPU ?Mode B. Fournissez un Dockerfile.
Image déjà publiée, intouchable ?Mode C. Référencez-la par tag ou digest.
Plusieurs fonctions partageant des dépendances natives ?Un package, plusieurs fonctions, mode B ou C.
Fonctions sans rapport entre elles ?Un package par fonction — le versionnement reste indépendant.

Le typage côté workflow fonctionne identiquement dans les trois modes — seul le chemin de préparation des entrées change pour le worker.