From 61cebaaed8d0a6458c8447544f3ba055789b8310 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut <1311787+PierreRaybaut@users.noreply.github.com> Date: Sun, 25 Oct 2026 10:31:34 +0100 Subject: [PATCH] docs(contracts): increase versions on any change of result, independently of applications The guidelines stated that contract versions are not increased until a stable application records contracts (DataLab 1.4). This tied the versioning of Sigima to the releases of an application, while Sigima is a reference of its own. The rule is now: the version of a contract is increased whenever a change of a computation function changes the result it returns, in the change itself, whatever the releases of the applications using Sigima. It is not increased when previously refused inputs are now accepted, when the change has no effect on the result, or when the change is entirely covered by a versioned rule of the execution context (whose version is increased instead); inputs previously accepted and now refused require an increase. Keeping version 1 from 1.3.0 to 1.4.0 is recorded as a one-off exception (contracts just published, not used yet) that does not apply to later changes. The rule, with a checklist, is documented in the contributing guidelines, in a new user guide page "Operation contracts and versions", in a 1.4 release note, in the instructions for AI agents (contracts section, corrected example, checklist for modifying a computation function) and in the docstrings of sigima.proc.contracts and computation_function, with French translations. Closes #96 Assisted-by: Claude Opus 5.5 --- .github/copilot-instructions.md | 77 +++++++++++++++- doc/contributing/guidelines.rst | 50 ++++++---- .../fr/LC_MESSAGES/contributing/guidelines.po | 19 ++-- .../LC_MESSAGES/release_notes/release_1.04.po | 3 + .../fr/LC_MESSAGES/user_guide/contracts.po | 76 +++++++++++++++ doc/locale/fr/LC_MESSAGES/user_guide/index.po | 6 ++ doc/release_notes/release_1.04.md | 1 + doc/user_guide/contracts.rst | 92 +++++++++++++++++++ doc/user_guide/index.rst | 7 ++ sigima/proc/contracts/__init__.py | 6 +- sigima/proc/decorator.py | 5 +- 11 files changed, 310 insertions(+), 32 deletions(-) create mode 100644 doc/locale/fr/LC_MESSAGES/user_guide/contracts.po create mode 100644 doc/user_guide/contracts.rst diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ba67febc..33ed09e2 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -182,6 +182,47 @@ result = my_processing(src, param1=10, param2="value") - Parameter class MUST be a `guidata.dataset.DataSet` subclass - Always re-export parameter classes in `sigima.params` module - Export computation functions in `__all__` of their module AND in `sigima/proc/{signal|image}/__init__.py` +- Every public computation function declares an operation contract (see below) + +### 1b. Operation Contracts (Versioned, Replayable Identity) + +Every public computation function declares its contract next to it: + +```python +from sigima.proc.contracts import EXACT, ReplayQualification + + +@computation_function( + operation_id="sigima.signal.my_processing", # stable, written in clear + contract_version=1, # 1 at the first declaration + qualification=ReplayQualification(contract_version=1, comparison=EXACT), +) +def my_processing(src: SignalObj, p: MyParam) -> SignalObj: ... +``` + +Applications (DataLab, DataLab-Web, capsules) record `(operation_id, contract_version)` with each +computation and replay it later, possibly with another Sigima version: a replay requires the same +identifier and version, otherwise it is refused explicitly. + +**Versioning rule (mandatory)**: +- **Increase `contract_version` (and the version of its `ReplayQualification`) in the same change + whenever a modification of a computation function changes the result it returns**: new, removed + or renamed parameters, different defaults, different outputs, a different result for the same + inputs (even for unusual inputs only, e.g. mixed real/complex or `float32`/`float64` data), or + inputs that were accepted and are now refused. +- This is **independent of the releases of DataLab or of any other application**: Sigima is a + reference of its own. Never postpone or skip an increase because an application is not released + yet. +- Do **not** increase it only when the result cannot change: inputs previously refused (error) are + now accepted; refactoring, performance or messages without effect on the result; or the change is + entirely covered by a versioned rule of the execution context (`IMAGE_ROI_CONVENTION`, X-alignment + policies), whose version is increased instead. +- Historical one-off exception: Sigima 1.4.0 kept version 1 although results changed since 1.3.0 + (contracts just published, not used yet). It does **not** apply to later changes. +- Keep the identifier when renaming or moving a function (use `aliases` if it must change). + +Full rules: `doc/contributing/guidelines.rst` (section "Operation contracts"); user documentation: +`doc/user_guide/contracts.rst`. ### 2. Object Model: `SignalObj` and `ImageObj` @@ -303,11 +344,16 @@ result.roi = rois # Attach to result 1. **Implement in `sigima/proc/signal/processing.py` (or appropriate module)**: ```python +from sigima.proc.contracts import EXACT, ReplayQualification from sigima.proc.decorator import computation_function import sigima.params -@computation_function() +@computation_function( + operation_id="sigima.signal.my_feature", + contract_version=1, + qualification=ReplayQualification(contract_version=1, comparison=EXACT), +) def my_feature(src: SignalObj, p: MyFeatureParam) -> SignalObj: """Apply my feature to signal. @@ -391,6 +437,28 @@ def test_my_feature(): # Add usage example in doc/examples/ if complex ``` +7. **Prove the replay qualification**: the `@pytest.mark.validation` test is required; run + `python scripts/run_with_env.py python -m pytest --qualification-report=report.json` (fails if a + declared qualification is not proven). Declare a tolerance only when justified (see guidelines). + +### Modifying an Existing Computation Function + +Checklist (see "Operation Contracts" above): + +1. **Can the result change?** (values, outputs, defaults, parameters, accepted inputs) If yes: +2. **Increase `contract_version`** and the `contract_version` of its `ReplayQualification` in the + same change, whatever the releases of DataLab or other applications. +3. **Update reference cases deliberately** and explain the scientific reason (never to make a test + pass). +4. **Run the qualification report** (`--qualification-report=report.json`). +5. **Release notes**: describe the change and list the contracts whose version was increased. +6. **Downstream**: provenance tests of applications that check a contract version (e.g. DataLab + `datalab/tests/features/common/provenance_*_test.py`) must follow. + +No increase when the result cannot change (previously refused inputs now accepted, refactoring +without effect) or when the change is entirely covered by a versioned rule of the execution context +(increase that rule's version instead). + ### Adding Low-Level NumPy Functions to `tools` **When to use `tools` vs `proc`**: @@ -559,6 +627,8 @@ Sigima functions are **consumed by DataLab processors**: | `sigima/objects/signal/object.py` | `SignalObj` implementation | | `sigima/objects/image/object.py` | `ImageObj` implementation | | `sigima/proc/decorator.py` | `@computation_function` decorator system | +| `sigima/proc/contracts/` | Operation contracts: registry, codec, execution, replay qualification | +| `doc/contributing/guidelines.rst` | Contract rules (identifiers, version increases, qualification) | | `sigima/proc/signal/processing.py` | Signal processing functions (normalize, calibrate, etc.) | | `sigima/proc/image/detection.py` | Image detection (blobs, peaks, contours) | | `sigima/tools/checks.py` | Input validation (`check_1d_array`, `check_2d_array`) | @@ -585,6 +655,9 @@ Sigima functions are **consumed by DataLab processors**: - Entirely new computation type (e.g., new parametric images) - New analysis methods +**Contract versions are independent of this classification**: a bug fix that changes the result +of a computation function increases its `contract_version` too. + ## Getting Help - **Documentation**: https://sigima.readthedocs.io/ @@ -593,4 +666,4 @@ Sigima functions are **consumed by DataLab processors**: --- -**Remember**: Always use `scripts/run_with_env.py`, test headlessly with pytest, and export all parameters through `sigima.params`. +**Remember**: Always use `scripts/run_with_env.py`, test headlessly with pytest, export all parameters through `sigima.params`, and increase `contract_version` whenever a change modifies the result of a computation function. diff --git a/doc/contributing/guidelines.rst b/doc/contributing/guidelines.rst index 180771f4..49dcc746 100644 --- a/doc/contributing/guidelines.rst +++ b/doc/contributing/guidelines.rst @@ -96,30 +96,40 @@ Follow these rules when you add or change a computation function: - If the identifier itself must change, keep the previous one in ``aliases``. -- Increase ``contract_version`` for any change of meaning: new, removed or - renamed parameters, a different default behavior, different outputs or a - different scientific result for the same inputs. - -- In particular, increase it whenever a call that succeeded may now give - another result, even for unusual inputs only (e.g. mixed real and complex, or - ``float32`` and ``float64``, data): a replay of an activity recorded with the - earlier version is then refused explicitly instead of being reported as - different. Do not increase it when only inputs that were refused are now - accepted, or when inputs that were accepted are now refused (the replay then - fails explicitly), nor when the change is entirely covered by a rule recorded - in the execution context (e.g. the X-alignment policy). - -- Version 1 of the contracts is settled by the first stable release of an - application recording them (DataLab 1.4): until then, no recorded activity - has to be refused, and contract versions are not increased (the changes of - Sigima 1.4 are part of version 1). +- Increase ``contract_version`` whenever a change of the computation function + changes the result it returns: new, removed or renamed parameters, a + different default behavior, different outputs or a different scientific + result for the same inputs, even for unusual inputs only (e.g. mixed real and + complex, or ``float32`` and ``float64``, data), and inputs that were accepted + and are now refused. Do it in the change itself, whatever the releases of + DataLab or of any other application using Sigima: Sigima is a reference of + its own, and a replay of an activity recorded with the earlier version is + then refused explicitly instead of giving another result. + +- Do not increase it when the result cannot change: only inputs that were + refused (with an error) are now accepted, the change has no effect on the + result (refactoring, performance, messages), or the change is entirely + covered by a versioned rule of the execution context (see below), whose + version is increased instead. + +- Checklist for any change of an existing computation function: decide whether + the result can change; if so, increase ``contract_version`` and the version + of its ``ReplayQualification`` (after proving it, see below), update the + reference cases deliberately, run ``pytest --qualification-report``, list the + increased contracts in the release notes, and update the provenance tests of + the applications that check a contract version (e.g. DataLab). + +- Historical exception: the contracts were published with Sigima 1.3.0 and + kept version 1 in Sigima 1.4.0 although several results changed, because they + had just been published and were not used yet. This one-off decision does not + apply to later changes. - Rules that apply to every operation are recorded in the execution context rather than in each contract: the selection of image pixels by regions of interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an - input image carries regions of interest). Increase the version of such a rule, - not the contract versions, when it changes: a replay refuses the calls - recorded with another version. + input image carries regions of interest) and the X-alignment policies (see + below). Increase the version of such a rule, not the contract versions, when + it changes: a replay refuses the calls recorded with another version. - The structure of the contract (input roles, parameter arguments, outputs) is inferred from the annotations. Annotate every argument and the return value diff --git a/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po b/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po index bd7c1b81..75f63945 100644 --- a/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po +++ b/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po @@ -100,17 +100,20 @@ msgstr "Écrivez l'identifiant en clair auprès de la fonction : ``sigima.signal msgid "If the identifier itself must change, keep the previous one in ``aliases``." msgstr "Si l'identifiant lui-même doit changer, conservez le précédent dans ``aliases``." -msgid "Increase ``contract_version`` for any change of meaning: new, removed or renamed parameters, a different default behavior, different outputs or a different scientific result for the same inputs." -msgstr "Augmentez ``contract_version`` pour tout changement de sens : paramètres ajoutés, supprimés ou renommés, comportement par défaut différent, sorties différentes ou résultat scientifique différent pour les mêmes entrées." +msgid "Increase ``contract_version`` whenever a change of the computation function changes the result it returns: new, removed or renamed parameters, a different default behavior, different outputs or a different scientific result for the same inputs, even for unusual inputs only (e.g. mixed real and complex, or ``float32`` and ``float64``, data), and inputs that were accepted and are now refused. Do it in the change itself, whatever the releases of DataLab or of any other application using Sigima: Sigima is a reference of its own, and a replay of an activity recorded with the earlier version is then refused explicitly instead of giving another result." +msgstr "Augmentez ``contract_version`` dès qu'une modification de la fonction de calcul change le résultat qu'elle renvoie : paramètres ajoutés, supprimés ou renommés, comportement par défaut différent, sorties différentes ou résultat scientifique différent pour les mêmes entrées, même pour des entrées inhabituelles seulement (par exemple des données mêlant réels et complexes, ou ``float32`` et ``float64``), ainsi que des entrées acceptées qui sont désormais refusées. Faites-le dans la modification elle-même, indépendamment des publications de DataLab ou de toute autre application utilisant Sigima : Sigima est une référence en soi, et le rejeu d'une activité enregistrée avec la version précédente est alors refusé explicitement au lieu de donner un autre résultat." -msgid "In particular, increase it whenever a call that succeeded may now give another result, even for unusual inputs only (e.g. mixed real and complex, or ``float32`` and ``float64``, data): a replay of an activity recorded with the earlier version is then refused explicitly instead of being reported as different. Do not increase it when only inputs that were refused are now accepted, or when inputs that were accepted are now refused (the replay then fails explicitly), nor when the change is entirely covered by a rule recorded in the execution context (e.g. the X-alignment policy)." -msgstr "En particulier, augmentez-la dès qu'un appel qui réussissait peut désormais donner un autre résultat, même pour des entrées inhabituelles seulement (par exemple des données mêlant réels et complexes, ou ``float32`` et ``float64``) : le rejeu d'une activité enregistrée avec la version précédente est alors refusé explicitement au lieu d'être signalé comme différent. Ne l'augmentez pas lorsque seules des entrées auparavant refusées sont désormais acceptées, ou lorsque des entrées auparavant acceptées sont désormais refusées (le rejeu échoue alors explicitement), ni lorsque le changement est entièrement couvert par une règle enregistrée dans le contexte d'exécution (par exemple la politique d'alignement en X)." +msgid "Do not increase it when the result cannot change: only inputs that were refused (with an error) are now accepted, the change has no effect on the result (refactoring, performance, messages), or the change is entirely covered by a versioned rule of the execution context (see below), whose version is increased instead." +msgstr "Ne l'augmentez pas lorsque le résultat ne peut pas changer : seules des entrées auparavant refusées (avec une erreur) sont désormais acceptées, la modification est sans effet sur le résultat (refactorisation, performances, messages), ou elle est entièrement couverte par une règle versionnée du contexte d'exécution (voir ci-dessous), dont la version est augmentée à la place." -msgid "Version 1 of the contracts is settled by the first stable release of an application recording them (DataLab 1.4): until then, no recorded activity has to be refused, and contract versions are not increased (the changes of Sigima 1.4 are part of version 1)." -msgstr "La version 1 des contrats est fixée par la première version stable d'une application qui les enregistre (DataLab 1.4) : d'ici là, aucune activité enregistrée ne doit être refusée, et les versions des contrats ne sont pas augmentées (les changements de Sigima 1.4 font partie de la version 1)." +msgid "Checklist for any change of an existing computation function: decide whether the result can change; if so, increase ``contract_version`` and the version of its ``ReplayQualification`` (after proving it, see below), update the reference cases deliberately, run ``pytest --qualification-report``, list the increased contracts in the release notes, and update the provenance tests of the applications that check a contract version (e.g. DataLab)." +msgstr "Liste de contrôle pour toute modification d'une fonction de calcul existante : déterminez si le résultat peut changer ; si c'est le cas, augmentez ``contract_version`` et la version de sa ``ReplayQualification`` (après l'avoir prouvée, voir ci-dessous), mettez à jour délibérément les cas de référence, exécutez ``pytest --qualification-report``, listez les contrats dont la version a augmenté dans les notes de version, et mettez à jour les tests de provenance des applications qui vérifient une version de contrat (par exemple DataLab)." -msgid "Rules that apply to every operation are recorded in the execution context rather than in each contract: the selection of image pixels by regions of interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an input image carries regions of interest). Increase the version of such a rule, not the contract versions, when it changes: a replay refuses the calls recorded with another version." -msgstr "Les règles qui s'appliquent à toutes les opérations sont enregistrées dans le contexte d'exécution plutôt que dans chaque contrat : la sélection des pixels d'une image par les régions d'intérêt (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, enregistrée lorsqu'une image d'entrée porte des régions d'intérêt). Augmentez la version d'une telle règle, et non les versions des contrats, lorsqu'elle change : un rejeu refuse les appels enregistrés avec une autre version." +msgid "Historical exception: the contracts were published with Sigima 1.3.0 and kept version 1 in Sigima 1.4.0 although several results changed, because they had just been published and were not used yet. This one-off decision does not apply to later changes." +msgstr "Exception historique : les contrats, publiés avec Sigima 1.3.0, ont conservé la version 1 dans Sigima 1.4.0 bien que plusieurs résultats aient changé, car ils venaient d'être publiés et n'étaient pas encore exploités. Cette décision ponctuelle ne s'applique pas aux modifications ultérieures." + +msgid "Rules that apply to every operation are recorded in the execution context rather than in each contract: the selection of image pixels by regions of interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an input image carries regions of interest) and the X-alignment policies (see below). Increase the version of such a rule, not the contract versions, when it changes: a replay refuses the calls recorded with another version." +msgstr "Les règles qui s'appliquent à toutes les opérations sont enregistrées dans le contexte d'exécution plutôt que dans chaque contrat : la sélection des pixels d'une image par les régions d'intérêt (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, enregistrée lorsqu'une image d'entrée porte des régions d'intérêt) et les politiques d'alignement en X (voir ci-dessous). Augmentez la version d'une telle règle, et non les versions des contrats, lorsqu'elle change : un rejeu refuse les appels enregistrés avec une autre version." msgid "The structure of the contract (input roles, parameter arguments, outputs) is inferred from the annotations. Annotate every argument and the return value with names imported at runtime (not only under ``TYPE_CHECKING``). When roles cannot be inferred (more than two input objects), declare them with ``roles=(...)``." msgstr "La structure du contrat (rôles des entrées, arguments de paramètres, sorties) est déduite des annotations. Annotez chaque argument et la valeur de retour avec des noms importés à l'exécution (pas seulement sous ``TYPE_CHECKING``). Lorsque les rôles ne peuvent pas être déduits (plus de deux objets en entrée), déclarez-les avec ``roles=(...)``." diff --git a/doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po b/doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po index 43380789..65b58a38 100644 --- a/doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po +++ b/doc/locale/fr/LC_MESSAGES/release_notes/release_1.04.po @@ -76,6 +76,9 @@ msgstr "**Centre de l'image** : `ImageObj.xc` et `ImageObj.yc` sont désormais l msgid "**Ellipse coordinates**: `EllipseCoordinates` (`sigima.objects.shape`) now holds the 5 values `(x, y, a, b, theta)` of ellipse geometry results, which can thus be transformed." msgstr "**Coordonnées d'ellipse** : `EllipseCoordinates` (`sigima.objects.shape`) contient désormais les 5 valeurs `(x, y, a, b, theta)` des résultats géométriques d'ellipse, qui peuvent ainsi être transformés." +msgid "**Operation contract versions**: The operation contracts keep version 1 in this release, although the result of some operations changed since 1.3.0 (for example `resampling`, `extract_roi` and the product of signals): the contracts had just been published with Sigima 1.3.0 and were not used yet. This one-off exception does not apply to later releases: from now on, the version of a contract is increased whenever a change of the computation changes its result, whatever the releases of the applications using Sigima, and calls recorded with an earlier version are refused instead of being replayed (see [Operation contracts and versions](../user_guide/contracts.rst))." +msgstr "**Versions des contrats d'opération** : les contrats d'opération conservent la version 1 dans cette version, bien que le résultat de certaines opérations ait changé depuis la 1.3.0 (par exemple `resampling`, `extract_roi` et le produit de signaux) : les contrats venaient d'être publiés avec Sigima 1.3.0 et n'étaient pas encore exploités. Cette exception ponctuelle ne s'applique pas aux versions ultérieures : désormais, la version d'un contrat est augmentée dès qu'une modification du calcul change son résultat, indépendamment des publications des applications utilisant Sigima, et les appels enregistrés avec une version antérieure sont refusés au lieu d'être rejoués (voir [Contrats d'opération et versions](../user_guide/contracts.rst))." + msgid "🛠️ Bug Fixes since version 1.3.0" msgstr "🛠️ Corrections de bugs depuis la version 1.3.0" diff --git a/doc/locale/fr/LC_MESSAGES/user_guide/contracts.po b/doc/locale/fr/LC_MESSAGES/user_guide/contracts.po new file mode 100644 index 00000000..bf48f4ea --- /dev/null +++ b/doc/locale/fr/LC_MESSAGES/user_guide/contracts.po @@ -0,0 +1,76 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) 2025, DataLab Platform Developers +# This file is distributed under the same license as the Sigima package. +# FIRST AUTHOR , 2025. +# +#, fuzzy +msgid "" +msgstr "" +"Language: fr\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" + +msgid "Operation contracts and versions" +msgstr "Contrats d'opération et versions" + +msgid "Every public computation function of Sigima declares an **operation contract**: a stable identifier (e.g. ``sigima.signal.normalize``) and a version, the roles of its inputs, its parameters and outputs, and the rules its result depends on besides them. Applications such as DataLab record the identifier and the version of each computation with the processed data (provenance), so that the computation can be replayed later, possibly with another Sigima version, and compared with the recorded result." +msgstr "Chaque fonction de calcul publique de Sigima déclare un **contrat d'opération** : un identifiant stable (par exemple ``sigima.signal.normalize``) et une version, les rôles de ses entrées, ses paramètres et ses sorties, et les règles dont son résultat dépend en plus de ceux-ci. Les applications comme DataLab enregistrent l'identifiant et la version de chaque calcul avec les données traitées (provenance), afin que le calcul puisse être rejoué plus tard, éventuellement avec une autre version de Sigima, et comparé au résultat enregistré." + +msgid "What a version guarantees" +msgstr "Ce que garantit une version" + +msgid "For a given identifier and version, the same inputs, parameters and execution context give the same result, compared under the rule declared by the replay qualification of the contract: exact by default, or within a justified tolerance for some computations (transforms, fits, reductions...) whose results differ slightly between numerical libraries or platforms. Contracts that are not qualified keep their identity but are never replayed." +msgstr "Pour un identifiant et une version donnés, les mêmes entrées, paramètres et contexte d'exécution donnent le même résultat, comparé selon la règle déclarée par la qualification de rejeu du contrat : exacte par défaut, ou dans une tolérance justifiée pour certains calculs (transformées, ajustements, réductions…) dont les résultats diffèrent légèrement d'une bibliothèque numérique ou d'une plateforme à l'autre. Les contrats non qualifiés conservent leur identité mais ne sont jamais rejoués." + +msgid "When a version is increased" +msgstr "Quand une version est augmentée" + +msgid "Sigima increases the version of a contract whenever a change of the computation function changes the result it returns: new, removed or renamed parameters, a different default behavior, different outputs or a different result for the same inputs (even for unusual inputs only), or inputs that were accepted and are now refused. This is done in the Sigima release that brings the change, whatever the releases of the applications using Sigima: Sigima is a reference of its own." +msgstr "Sigima augmente la version d'un contrat dès qu'une modification de la fonction de calcul change le résultat qu'elle renvoie : paramètres ajoutés, supprimés ou renommés, comportement par défaut différent, sorties différentes ou résultat différent pour les mêmes entrées (même pour des entrées inhabituelles seulement), ou entrées acceptées qui sont désormais refusées. Cela est fait dans la version de Sigima qui apporte la modification, indépendamment des publications des applications qui utilisent Sigima : Sigima est une référence en soi." + +msgid "The version is not increased when the result cannot change:" +msgstr "La version n'est pas augmentée lorsque le résultat ne peut pas changer :" + +msgid "inputs that were refused (with an error) are now accepted;" +msgstr "des entrées auparavant refusées (avec une erreur) sont désormais acceptées ;" + +msgid "the change has no effect on the result (refactoring, performance, messages);" +msgstr "la modification est sans effet sur le résultat (refactorisation, performances, messages) ;" + +msgid "the change is entirely covered by a versioned rule of the execution context (see below), whose version is increased instead." +msgstr "la modification est entièrement couverte par une règle versionnée du contexte d'exécution (voir ci-dessous), dont la version est augmentée à la place." + +msgid "The release notes list the contracts whose version was increased." +msgstr "Les notes de version listent les contrats dont la version a été augmentée." + +msgid "Rules of the execution context" +msgstr "Règles du contexte d'exécution" + +msgid "Some rules apply to every operation, or to a family of operations, and are recorded with each call rather than in each contract, with their own identifier and version:" +msgstr "Certaines règles s'appliquent à toutes les opérations, ou à une famille d'opérations, et sont enregistrées avec chaque appel plutôt que dans chaque contrat, avec leurs propres identifiant et version :" + +msgid "the selection of image pixels by regions of interest (``sigima.image.roi.pixel_centers``, :data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`), recorded when an input image carries regions of interest;" +msgstr "la sélection des pixels d'une image par les régions d'intérêt (``sigima.image.roi.pixel_centers``, :data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`), enregistrée lorsqu'une image d'entrée porte des régions d'intérêt ;" + +msgid "the alignment of signals on a common X grid, for operations combining several signals (``sigima.signal.x_alignment.*`` policies);" +msgstr "l'alignement des signaux sur une grille X commune, pour les opérations combinant plusieurs signaux (politiques ``sigima.signal.x_alignment.*``) ;" + +msgid "the global options read by a computation and the metadata of its inputs it reads." +msgstr "les options globales lues par un calcul et les métadonnées de ses entrées qu'il lit." + +msgid "When such a rule changes, its version is increased and the contracts keep theirs." +msgstr "Lorsqu'une telle règle change, sa version est augmentée et les contrats conservent la leur." + +msgid "Consequences for replay" +msgstr "Conséquences pour le rejeu" + +msgid "An application replays a recorded call only with a local contract of the same identifier and version, qualified for that version, and with the recorded execution context. A call recorded with another version of the contract, or with a rule of the context that Sigima no longer supports, is refused explicitly (unsupported contract or unsupported context): it is never replayed to give a silently different result. To obtain a record with the current version, compute the result again." +msgstr "Une application ne rejoue un appel enregistré qu'avec un contrat local de même identifiant et de même version, qualifié pour cette version, et avec le contexte d'exécution enregistré. Un appel enregistré avec une autre version du contrat, ou avec une règle du contexte que Sigima ne prend plus en charge, est refusé explicitement (contrat ou contexte non pris en charge) : il n'est jamais rejoué pour donner silencieusement un résultat différent. Pour obtenir un enregistrement avec la version actuelle, recalculez le résultat." + +msgid "The contracts were published with Sigima 1.3.0 and kept version 1 in Sigima 1.4.0 although several results changed, because they had just been published and were not used yet. This one-off exception does not apply to later releases." +msgstr "Les contrats, publiés avec Sigima 1.3.0, ont conservé la version 1 dans Sigima 1.4.0 bien que plusieurs résultats aient changé, car ils venaient d'être publiés et n'étaient pas encore exploités. Cette exception ponctuelle ne s'applique pas aux versions ultérieures." + +msgid "See also the rules followed by contributors (:doc:`/contributing/guidelines`, section \"Operation contracts\") and the API of :mod:`sigima.proc.contracts`." +msgstr "Voir aussi les règles suivies par les contributeurs (:doc:`/contributing/guidelines`, section « Operation contracts ») et l'API de :mod:`sigima.proc.contracts`." diff --git a/doc/locale/fr/LC_MESSAGES/user_guide/index.po b/doc/locale/fr/LC_MESSAGES/user_guide/index.po index 0c7dadda..42499ef2 100644 --- a/doc/locale/fr/LC_MESSAGES/user_guide/index.po +++ b/doc/locale/fr/LC_MESSAGES/user_guide/index.po @@ -41,3 +41,9 @@ msgstr "Fonctionnalités" msgid "Key functionalities" msgstr "Fonctionnalités clés" + +msgid " Contracts" +msgstr "Contrats" + +msgid "Operation contracts and versions" +msgstr "Contrats d'opération et versions" diff --git a/doc/release_notes/release_1.04.md b/doc/release_notes/release_1.04.md index c06baa1f..305839be 100644 --- a/doc/release_notes/release_1.04.md +++ b/doc/release_notes/release_1.04.md @@ -26,6 +26,7 @@ The [user guide](../user_guide/overview.rst) shows how readers and connectors at * **Replay of processing in image ROIs**: The selection of image pixels by regions of interest is now recorded with each call on images carrying regions of interest (rule `sigima.image.roi.pixel_centers`, `sigima.proc.contracts.IMAGE_ROI_CONVENTION`, in the execution context described by `describe_call` and `prepare_execution`). Applications that replay recorded processing (e.g. DataLab capsules) refuse to replay such calls recorded with earlier versions, which selected other pixels, instead of reporting a different result. Calls on images without regions of interest are not concerned. * **Image center**: `ImageObj.xc` and `ImageObj.yc` are now the center of the pixel grid (halfway between the centers of the first and last pixels), for uniform and non-uniform coordinates: they were half a pixel off for uniform coordinates. `width` and `height` are unchanged. * **Ellipse coordinates**: `EllipseCoordinates` (`sigima.objects.shape`) now holds the 5 values `(x, y, a, b, theta)` of ellipse geometry results, which can thus be transformed. +* **Operation contract versions**: The operation contracts keep version 1 in this release, although the result of some operations changed since 1.3.0 (for example `resampling`, `extract_roi` and the product of signals): the contracts had just been published with Sigima 1.3.0 and were not used yet. This one-off exception does not apply to later releases: from now on, the version of a contract is increased whenever a change of the computation changes its result, whatever the releases of the applications using Sigima, and calls recorded with an earlier version are refused instead of being replayed (see [Operation contracts and versions](../user_guide/contracts.rst)). ### 🛠️ Bug Fixes since version 1.3.0 ### diff --git a/doc/user_guide/contracts.rst b/doc/user_guide/contracts.rst new file mode 100644 index 00000000..d76e94cb --- /dev/null +++ b/doc/user_guide/contracts.rst @@ -0,0 +1,92 @@ +.. _contracts: + +Operation contracts and versions +================================ + +Every public computation function of Sigima declares an **operation contract**: +a stable identifier (e.g. ``sigima.signal.normalize``) and a version, the roles +of its inputs, its parameters and outputs, and the rules its result depends on +besides them. Applications such as DataLab record the identifier and the +version of each computation with the processed data (provenance), so that the +computation can be replayed later, possibly with another Sigima version, and +compared with the recorded result. + +.. code-block:: python + + from sigima.proc import contracts + import sigima.proc.signal as sips + + contract = contracts.contract_for_function(sips.normalize) + print(contract.operation_id, contract.contract_version) + # sigima.signal.normalize 1 + +What a version guarantees +------------------------- + +For a given identifier and version, the same inputs, parameters and execution +context give the same result, compared under the rule declared by the replay +qualification of the contract: exact by default, or within a justified +tolerance for some computations (transforms, fits, reductions...) whose results +differ slightly between numerical libraries or platforms. Contracts that are not +qualified keep their identity but are never replayed. + +When a version is increased +--------------------------- + +Sigima increases the version of a contract whenever a change of the computation +function changes the result it returns: new, removed or renamed parameters, a +different default behavior, different outputs or a different result for the +same inputs (even for unusual inputs only), or inputs that were accepted and +are now refused. This is done in the Sigima release that brings the change, +whatever the releases of the applications using Sigima: Sigima is a reference +of its own. + +The version is not increased when the result cannot change: + +- inputs that were refused (with an error) are now accepted; +- the change has no effect on the result (refactoring, performance, messages); +- the change is entirely covered by a versioned rule of the execution context + (see below), whose version is increased instead. + +The release notes list the contracts whose version was increased. + +Rules of the execution context +------------------------------ + +Some rules apply to every operation, or to a family of operations, and are +recorded with each call rather than in each contract, with their own identifier +and version: + +- the selection of image pixels by regions of interest + (``sigima.image.roi.pixel_centers``, + :data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`), recorded when an input + image carries regions of interest; +- the alignment of signals on a common X grid, for operations combining several + signals (``sigima.signal.x_alignment.*`` policies); +- the global options read by a computation and the metadata of its inputs it + reads. + +When such a rule changes, its version is increased and the contracts keep +theirs. + +Consequences for replay +----------------------- + +An application replays a recorded call only with a local contract of the same +identifier and version, qualified for that version, and with the recorded +execution context. A call recorded with another version of the contract, or +with a rule of the context that Sigima no longer supports, is refused +explicitly (unsupported contract or unsupported context): it is never replayed +to give a silently different result. To obtain a record with the current +version, compute the result again. + +.. note:: + + The contracts were published with Sigima 1.3.0 and kept version 1 in + Sigima 1.4.0 although several results changed, because they had just been + published and were not used yet. This one-off exception does not apply to + later releases. + +See also the rules followed by contributors +(:doc:`/contributing/guidelines`, section "Operation contracts") and the API of +:mod:`sigima.proc.contracts`. diff --git a/doc/user_guide/index.rst b/doc/user_guide/index.rst index 55f4e0d3..83b947da 100644 --- a/doc/user_guide/index.rst +++ b/doc/user_guide/index.rst @@ -30,6 +30,12 @@ User Guide Key functionalities + .. grid-item-card:: :octicon:`versions;1em;sd-text-info` Contracts + :link: contracts + :link-type: doc + + Operation contracts and versions + .. toctree:: :maxdepth: 2 :caption: Contents: @@ -38,3 +44,4 @@ User Guide getting_started overview features + contracts diff --git a/sigima/proc/contracts/__init__.py b/sigima/proc/contracts/__init__.py index 722f107e..c7352fd0 100644 --- a/sigima/proc/contracts/__init__.py +++ b/sigima/proc/contracts/__init__.py @@ -10,7 +10,11 @@ replay qualification. The structure of the contract (input roles, parameter arguments, outputs) is inferred from the annotations of the function. Applications use contracts to record what was computed and to replay it later, -possibly with another Sigima version, with the same meaning. +possibly with another Sigima version, with the same meaning. The version of a +contract is increased whenever a change of the function changes the result it +returns, whatever the releases of the applications using Sigima; a call recorded +with another version is refused instead of being replayed (see the user guide, +"Operation contracts and versions", and the contributing guidelines). This package is the public API of contracts; import from it rather than from its modules: diff --git a/sigima/proc/decorator.py b/sigima/proc/decorator.py index c9fcf18a..616b0f22 100644 --- a/sigima/proc/decorator.py +++ b/sigima/proc/decorator.py @@ -418,7 +418,10 @@ def computation_function( description: Optional custom description or docstring. operation_id: Optional stable operation identifier (see :mod:`sigima.proc.contracts`). Never derived from the Python name. - contract_version: Contract version, required with ``operation_id``. + contract_version: Contract version, required with ``operation_id``: 1 at + the first declaration, increased whenever a change of the function changes + the result it returns, whatever the releases of the applications using + Sigima (see the contributing guidelines, "Operation contracts"). aliases: Earlier operation identifiers, only with ``operation_id``. qualification: Replay qualification of the tested contract version (see :class:`sigima.proc.contracts.declarations.ReplayQualification`), only