Référence de syntaxe
Cette page est la référence canonique de mecapy.yml v1 : chaque
section décrit un bloc du manifest, son type, ses valeurs par défaut
et ses contraintes.
version: "1"Figé à "1". Toute autre valeur est rejetée. Un futur format v2
conservera la même clé en changeant le littéral — vos manifests
existants restent valides tant que MecaPy maintient le parseur v1.
package: name: bolt-sizing # requis version: 0.1.0 # requis, semver description: | # optionnel Résumé en une ligne ; multi-ligne autorisé. author: Jane Doe # optionnel license: MIT # optionnel visibility: private # optionnel, défaut "private" organization: acme-corp # requis si visibility=internal tags: [bolts, mechanics] # optionnel engine: # optionnel — uniquement pour les wrappers name: code-aster version: "17.4.0" resources: # optionnel, voir plus bas recommended: { cpu: 1, memory_mb: 512 } timeout: 60| Champ | Type | Défaut | Notes |
|---|---|---|---|
name | chaîne | — | Identifiant du package. Kebab-case recommandé. |
version | chaîne | — | Semver strict : MAJEUR.MINEUR.PATCH (tag pre-release optionnel). C’est la version du wrapper, pas celle de l’engine. |
description | chaîne | null | Texte libre. |
author | chaîne | null | Texte libre. |
license | chaîne | null | Identifiant SPDX (MIT, Apache-2.0, …). |
visibility | enum | "private" | private / internal / public. Voir visibilité. |
organization | chaîne | null | Slug de l’organisation propriétaire. Requis si visibility: internal, interdit si visibility: private, optionnel (attribution + contrôle de publication) si visibility: public. |
tags | liste[chaîne] | null | Pour la recherche/catégorisation dans la liste des packages. |
engine | objet | null | Outil tiers encapsulé ({name, version}). Les packages purement Python le laissent à null. |
resources | objet | null | Ressources par défaut héritées par chaque fonction — voir plus bas. |
engine — distinguer le wrapper de l’outil encapsulé
Section intitulée « engine — distinguer le wrapper de l’outil encapsulé »Quand un package encapsule un binaire tiers (code-aster, abaqus,
openfoam…), deux numéros de version coexistent : la version du package
MecaPy (package.version, p. ex. 0.4.1) et la version de l’outil
tiers embarquée dans l’image (p. ex. code-aster 17.4.0). Les
descriptions en texte libre perdent vite cette distinction. Déclarez
l’engine explicitement pour que la plateforme l’affiche comme un badge
distinct :
package: name: code-aster version: 0.4.1 engine: name: code-aster version: "17.4.0"| Champ | Type | Requis si présent | Notes |
|---|---|---|---|
engine.name | chaîne | oui | 1–50 caractères. Identifiant de l’engine (code-aster, abaqus, openfoam, …). |
engine.version | chaîne | oui | 1–50 caractères. Chaîne de version libre (semver, année, SHA git — ce qu’utilise l’outil tiers). |
Le bloc est tout-ou-rien — déclarez les deux champs ou omettez le
bloc entier. Le frontend l’affiche comme un badge sur la page de
détail du package, la page d’exécution (engine code-aster 17.4.0) et
l’historique des jobs (traçabilité : on peut retrouver quel build du
solveur a servi à une exécution donnée).
Le bloc est une union discriminée sur kind. Chaque kind a ses
propres champs requis :
runtime: kind: python # mode A — managé version: "3.12" # requis, "3.10" | "3.11" | "3.12" requirements: false # optionnel, installe le requirements.txt racine au buildruntime: kind: dockerfile # mode B — build custom dockerfile: Dockerfile # requis, chemin relatif à la racine du repo context: . # optionnel, défaut "."runtime: kind: image # mode C — image pré-construite image: ghcr.io/me/x:1.4 # requis, référence registry complète avec tagDétails et compromis par mode : modes de runtime.
functions
Section intitulée « functions »Un dict non vide. Chaque entrée déclare exactement un corps parmi quatre, selon le mode de runtime et le style d’écriture :
functions: my_function: # ── Mode A, code à la main ── handler: pkg.module:function # `module:function`
# ── Mode A, corps déclaratif (l'un OU l'autre, voir section dédiée) ── formula: outputs: { area: "pi * radius ** 2" } # table: # columns: { d: [6, 8, 10], As: [20.1, 36.6, 58.0] } # axes: { d: { kind: continuous, interpolation: true } }
# ── Modes B/C uniquement ── entrypoint: ["python", "/run.py", "x"] # liste argv, non vide
# ── Commun ── description: | Description en texte libre. inputs: diameter: { type: Length, required: true, description: "..." } load: { type: Force, symbol: "F_t", unit: "N" } outputs: stress: { type: Stress } resources: recommended: { cpu: 1, memory_mb: 512 } timeout: 120 testcases: tests/static_support.json # chemin optionnel vers un JSON de cas de test| Champ | Type | Requis | Notes |
|---|---|---|---|
handler | chaîne | un corps requis | module:function. Fonctions de niveau module uniquement — voir handlers. |
formula | objet | un corps requis | Corps déclaratif : expressions mathématiques, handler généré par la plateforme. Voir ci-dessous. |
table | objet | un corps requis | Corps déclaratif : table de valeurs (lookup ± interpolation 1-D). Voir ci-dessous. |
entrypoint | liste[chaîne] | un corps requis | Modes B/C. Liste argv, non vide. Le conteneur exécute entrypoint[0] entrypoint[1] .... |
description | chaîne | non | Texte libre. |
inputs | objet | non | Ports d’entrée typés. Voir ports d’E/S typés. |
outputs | objet | non | Ports de sortie typés. |
resources | objet | non | Surcharge par fonction de package.resources. |
testcases | chaîne | non | Chemin (relatif à la racine du repo) d’un fichier JSON de cas de test — exécuté après le build quand quality.validation_testcases.on_deployment vaut true. |
Les quatre corps (handler, formula, table, entrypoint) sont
mutuellement exclusifs : exactement un par fonction. Le recoupement
avec runtime.kind se fait après le parsing — les corps handler,
formula et table exigent le mode A (runtime Python), les modes B/C
exigent entrypoint.
En mode A avec handler:, vous pouvez omettre entièrement inputs: /
outputs: — MecaPy lit la signature du handler et les déduit des type
hints. Vous ne les déclarez que pour surcharger ou documenter. Avec un
corps déclaratif ou en modes B/C ils sont obligatoires : il n’y a pas de
signature Python à lire.
Corps déclaratifs — formula: et table:
Section intitulée « Corps déclaratifs — formula: et table: »Le manifeste est la source unique d’une fonction déclarative : la
plateforme génère le handler Python (handler_generated.py, nom
réservé — ne placez aucun fichier de ce nom dans le package) au moment
du build, à partir du bloc seul. L’éditeur en ligne (mode Simple) édite
ces blocs directement, et sait convertir une formule en fichier Python
(« Convertir en code ») quand vous voulez reprendre la main.
functions: preload_max: description: Précharge maximale admissible. inputs: T_max: { type: Moment, symbol: "T_{max}", unit: "N*m" } B: { type: Area, unit: "m^2" } outputs: F0_max: { type: Force, unit: "N" } formula: intermediates: # optionnel — évaluées dans l'ordre k: "T_max * 1000" outputs: # requis, non vide F0_max: "k / B"Contraintes validées à l’édition (erreur FORMULA_INVALID) et au build :
- le nom de la fonction doit être un identifiant Python (lettres,
chiffres, underscore — pas de tiret) : il devient
def <nom>(dans le handler généré ; - chaque expression est du Python (mode expression) et ne référence que
les entrées, les intermédiaires et l’allowlist mathématique
(
sin,cos,sqrt,exp,log,pi,e,abs,min,max,round, …) ; - les clés de
formula.outputset les portsoutputs:doivent coïncider exactement (l’éditeur Simple maintient cet alignement automatiquement).
Un bloc table: déclare des colonnes de même longueur ; chaque entrée
est un axe categorical (correspondance exacte, typé Literal[...])
ou continuous (avec interpolation: true pour l’interpolation
linéaire 1-D) ; les colonnes restantes sont les sorties.
resources
Section intitulée « resources »Le bloc resources déclare des ressources brutes — CPU, mémoire,
timeout. Il ne mentionne aucun tier : un tier est une notion
d’infrastructure (formes de VM, propres au fournisseur cloud), alors
qu’un manifeste doit rester stable quand l’infra évolue. Les tiers sont
calculés par la plateforme à partir de ces valeurs.
resources: recommended: # requis — ressources visées par défaut cpu: 2 memory_mb: 2048 minimal: # optionnel — plancher dur (CPU/RAM) cpu: 1 memory_mb: 512 maximal: # optionnel — plafond dur (CPU/RAM) cpu: 8 memory_mb: 16384 timeout: 300 # requis — secondes| Champ | Type | Requis | Notes |
|---|---|---|---|
recommended | objet {cpu, memory_mb} | oui | Ressources visées par défaut. Détermine le tier par défaut. |
minimal | objet {cpu, memory_mb} | non | Plancher dur de la fourchette d’override. Absent → pas de plancher. |
maximal | objet {cpu, memory_mb} | non | Plafond dur de la fourchette d’override. Absent → plafond plateforme. |
timeout | entier | oui | Secondes. Axe indépendant du couple CPU/RAM. |
cpu est un entier de cores, memory_mb un entier de Mio.
Tiers — calculés par la plateforme
Section intitulée « Tiers — calculés par la plateforme »À l’exécution, la plateforme projette recommended sur son catalogue
de tiers (le plus petit tier dont cpu et memory_mb couvrent le
recommandé devient le tier par défaut). Les pages d’exécution et
l’éditeur de workflow proposent un sélecteur de tier ; minimal /
maximal, s’ils sont déclarés, en grisent les bornes hors fourchette.
Catalogue plateforme actuel (CPU / mémoire — le timeout n’est pas porté par le tier) :
| Tier | CPU | Mémoire (Mio) |
|---|---|---|
nano | 1 | 512 |
micro | 1 | 1024 |
small | 2 | 2048 |
medium | 2 | 4096 |
large | 4 | 8192 |
xlarge | 4 | 16384 |
xlarge_2 | 4 | 32768 |
xlarge_4 | 8 | 65536 |
xlarge_8 | 16 | 131072 |
xlarge_16 | 32 | 262144 |
xlarge_32 | 64 | 524288 |
Les tiers nano → large s’exécutent sur le parc de workers courant.
Les tiers xlarge → xlarge_32 sont des gabarits haute-mémoire ; un
job qui en exige un ne s’exécute qu’une fois qu’un worker assez grand
est disponible, sinon il est rejeté avec une erreur explicite
(« tier exceeds worker capacity »).
Le plafond dur de la plateforme est xlarge_32 (64 CPU / 524288 Mio) ;
le timeout est plafonné à 86400 s. Une valeur recommended /
minimal / maximal ou un timeout au-delà déclenche une erreur de
validation, de même que l’ordre minimal ≤ recommended ≤ maximal.
Héritage
Section intitulée « Héritage »package.resources fixe le défaut. Si une fonction déclare son propre
bloc resources:, celui-ci remplace entièrement le bloc package
(pas de fusion sous-champ par sous-champ) — il doit donc être complet
(recommended + timeout). L’absence des deux blocs retombe sur le
défaut plateforme (recommended: {cpu: 2, memory_mb: 2048},
timeout: 300).
Un dict de forme libre. Son seul consommateur actuel est le contrôle par cas de test :
quality: validation_testcases: on_deployment: true # exécute les cas de test après le build fail_on_error: false # gate la version sur échec (défaut false)Quand on_deployment: true, après un build réussi le package passe à
l’état validating : les cas de test du fichier JSON testcases: de
chaque fonction sont exécutés, un job par cas. À la fin :
- tous les cas passent → la version devient
ready; - un cas échoue avec
fail_on_error: false(défaut) → la version devient quand mêmeready, les échecs étant consignés en avertissement ; - un cas échoue avec
fail_on_error: true→ la version passe àvalidation_failedet n’est pas servable.
Les résultats — réussite par fonction, cas en échec, lien vers le job de chaque cas — sont visibles sur la page de détail du package.
Cadence et bornes temporelles
Section intitulée « Cadence et bornes temporelles »L’orchestrateur de validation est polling-based, pas push-based — il n’est pas réveillé par la fin du build. Conséquences à connaître :
- Démarrage : un scheduler interne tique chaque minute et
ramasse tous les packages en
validating. Entre le moment où le build flippe le statut et le premier jobpendingvisible dans l’historique, on observe donc jusqu’à ~60 s d’attente puis 1–4 s de préparation (download du zip source S3, extract, parse des fichierstestcases:). - Plafond agrégé par tick : un seul tick d’orchestration attend les jobs au maximum 15 min. Au-delà, les jobs encore pending sont comptés comme échoués (côté validation — ils peuvent finir côté worker, mais leur résultat n’est plus injecté dans la validation, qui transitionne immédiatement le package).
- Plafond par cas : chaque job est en plus borné par le
timeoutde la fonction (par défaut 300 s, voirresources). Un cas qui excède ce timeout est tué par le worker et compte comme un échec. - Pas de boucle infinie : tant qu’un tick polle, les ticks
suivants sont muselés par un verrou advisory Postgres et ne
re-soumettent pas les cas. Le package quitte
validatingau plus tard 15 min après son ramassage initial.
Pratique : pour des cas de validation lents (FEM, mailleurs), pense à
relever resources.timeout dans le manifeste et à découper le
calcul en cas plus petits — l’orchestrateur agrège, mais reste limité
à 15 min d’attente pour l’ensemble du package.
Le bloc est volontairement non typé au niveau du modèle pour que de
nouvelles sous-clés puissent arriver sans changer le format du
manifest. Tout ce qui est hors validation_testcases est aujourd’hui
ignoré.
Ordre de validation
Section intitulée « Ordre de validation »Le parseur s’exécute dans cet ordre — le connaître aide à lire les messages d’erreur :
- Chargement YAML — les erreurs de syntaxe viennent d’ici.
- Schéma — chaque champ est typé et contrôlé selon le modèle.
- Résolution du runtime discriminé —
runtime.kindchoisit le sous-modèle qui parse le reste du blocruntime. handlerOU EXCLUSIFentrypoint— validation au niveau fonction.- Recoupement
runtime.kind↔ champ par fonction — validation au niveau manifest. - Plafonds de ressources — chaque bloc
resources:doit respecter les plafonds plateforme (64 CPU / 524288 Mio / 86400 s) et l’ordreminimal ≤ recommended ≤ maximal. - Résolution des E/S typées — chaque
type:est cherché dans le catalogue de types ; un type inconnu échoue. - Introspection de signature en mode A (au déploiement uniquement) — le handler est importé et sa signature lue pour les schemas déduits des type hints.
Les erreurs des étapes 1 à 7 sont renvoyées de façon synchrone en 4xx
par la route de déploiement. Les erreurs de l’étape 8 retombent sur un
schema permissif {"type": "object"} avec un avertissement journalisé
côté serveur, plutôt que de faire échouer le déploiement.