diff --git a/doc/contributing/guidelines.rst b/doc/contributing/guidelines.rst index 42c6830..180771f 100644 --- a/doc/contributing/guidelines.rst +++ b/doc/contributing/guidelines.rst @@ -109,6 +109,11 @@ Follow these rules when you add or change a computation function: 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). + - 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 diff --git a/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po b/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po index 478e8f4..bd7c1b8 100644 --- a/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po +++ b/doc/locale/fr/LC_MESSAGES/contributing/guidelines.po @@ -106,6 +106,9 @@ msgstr "Augmentez ``contract_version`` pour tout changement de sens : paramètre 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 "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 "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." 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 1eb43f4..4338078 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,10 +76,6 @@ 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." -#, python-format -msgid "**Operation contracts**: The operations whose result changed for some inputs now declare version 2 of their contract: `sigima.signal.addition`, `average`, `standard_deviation`, `product`, `difference` and `signals_to_image` (inputs of different data types, uncertainty of a product), and `sigima.image.resampling`, `calibration`, `extract_roi`, `extract_rois` and `radial_profile` (pixel grid, complex data, pixels selected by their centers). Applications that replay recorded processing (e.g. DataLab capsules) now refuse to replay calls of these operations recorded with earlier versions, instead of reporting a different result." -msgstr "**Contrats d'opérations** : les opérations dont le résultat a changé pour certaines entrées déclarent désormais la version 2 de leur contrat : `sigima.signal.addition`, `average`, `standard_deviation`, `product`, `difference` et `signals_to_image` (entrées de types de données différents, incertitude d'un produit), et `sigima.image.resampling`, `calibration`, `extract_roi`, `extract_rois` et `radial_profile` (grille de pixels, données complexes, pixels sélectionnés par leur centre). Les applications qui rejouent des traitements enregistrés (par exemple les capsules DataLab) refusent désormais de rejouer les appels de ces opérations enregistrés avec des versions antérieures, au lieu de signaler un résultat différent." - msgid "🛠️ Bug Fixes since version 1.3.0" msgstr "🛠️ Corrections de bugs depuis la version 1.3.0" diff --git a/doc/release_notes/release_1.04.md b/doc/release_notes/release_1.04.md index 0ede2e9..c06baa1 100644 --- a/doc/release_notes/release_1.04.md +++ b/doc/release_notes/release_1.04.md @@ -26,7 +26,6 @@ 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 contracts**: The operations whose result changed for some inputs now declare version 2 of their contract: `sigima.signal.addition`, `average`, `standard_deviation`, `product`, `difference` and `signals_to_image` (inputs of different data types, uncertainty of a product), and `sigima.image.resampling`, `calibration`, `extract_roi`, `extract_rois` and `radial_profile` (pixel grid, complex data, pixels selected by their centers). Applications that replay recorded processing (e.g. DataLab capsules) now refuse to replay calls of these operations recorded with earlier versions, instead of reporting a different result. ### 🛠️ Bug Fixes since version 1.3.0 ### diff --git a/sigima/proc/image/extraction.py b/sigima/proc/image/extraction.py index 35f1547..48b1865 100644 --- a/sigima/proc/image/extraction.py +++ b/sigima/proc/image/extraction.py @@ -64,8 +64,8 @@ @computation_function( operation_id="sigima.image.extract_rois", - contract_version=2, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + contract_version=1, + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def extract_rois(src: ImageObj, params: list[ROI2DParam]) -> ImageObj: """Extract multiple regions of interest from data @@ -109,8 +109,8 @@ def extract_rois(src: ImageObj, params: list[ROI2DParam]) -> ImageObj: @computation_function( operation_id="sigima.image.extract_roi", - contract_version=2, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + contract_version=1, + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def extract_roi(src: ImageObj, p: ROI2DParam) -> ImageObj: """Extract single ROI @@ -534,8 +534,8 @@ def choice_callback(self, item, value): # pylint: disable=unused-argument @computation_function( operation_id="sigima.image.radial_profile", - contract_version=2, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + contract_version=1, + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def radial_profile(src: ImageObj, p: RadialProfileParam) -> SignalObj: """Compute radial profile around the centroid diff --git a/sigima/proc/image/geometry.py b/sigima/proc/image/geometry.py index 94c8af9..80935f3 100644 --- a/sigima/proc/image/geometry.py +++ b/sigima/proc/image/geometry.py @@ -548,8 +548,8 @@ def _resampling_axis( @computation_function( operation_id="sigima.image.resampling", - contract_version=2, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + contract_version=1, + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def resampling(src: ImageObj, p: Resampling2DParam) -> ImageObj: """Resample image to new coordinate grid using interpolation @@ -731,8 +731,8 @@ class XYZCalibrateParam(gds.DataSet): @computation_function( operation_id="sigima.image.calibration", - contract_version=2, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + contract_version=1, + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def calibration(src: ImageObj, p: XYZCalibrateParam) -> ImageObj: """Compute polynomial calibration diff --git a/sigima/proc/signal/arithmetic.py b/sigima/proc/signal/arithmetic.py index 861c011..420556e 100644 --- a/sigima/proc/signal/arithmetic.py +++ b/sigima/proc/signal/arithmetic.py @@ -111,9 +111,9 @@ def __product_uncertainty(y_array: np.ndarray, dy_array: np.ndarray) -> np.ndarr @computation_function( operation_id="sigima.signal.addition", - contract_version=2, + contract_version=1, x_alignment=SMALLEST_GRID_LINEAR_HOLD, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def addition(src_list: list[SignalObj]) -> SignalObj: """Compute the element-wise sum of multiple signals. @@ -150,9 +150,9 @@ def addition(src_list: list[SignalObj]) -> SignalObj: @computation_function( operation_id="sigima.signal.average", - contract_version=2, + contract_version=1, x_alignment=SMALLEST_GRID_LINEAR_HOLD, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def average(src_list: list[SignalObj]) -> SignalObj: """Compute the element-wise average of multiple signals. @@ -190,9 +190,9 @@ def average(src_list: list[SignalObj]) -> SignalObj: @computation_function( operation_id="sigima.signal.standard_deviation", - contract_version=2, + contract_version=1, x_alignment=SMALLEST_GRID_LINEAR_HOLD, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def standard_deviation(src_list: list[SignalObj]) -> SignalObj: """Compute the element-wise standard deviation of multiple signals. @@ -225,9 +225,9 @@ def standard_deviation(src_list: list[SignalObj]) -> SignalObj: @computation_function( operation_id="sigima.signal.product", - contract_version=2, + contract_version=1, x_alignment=SMALLEST_GRID_LINEAR_HOLD, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def product(src_list: list[SignalObj]) -> SignalObj: """Compute the element-wise product of multiple signals. @@ -467,9 +467,9 @@ def arithmetic(src1: SignalObj, src2: SignalObj, p: ArithmeticParam) -> SignalOb @computation_function( operation_id="sigima.signal.difference", - contract_version=2, + contract_version=1, x_alignment=SOURCE_GRID_LINEAR, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def difference(src1: SignalObj, src2: SignalObj) -> SignalObj: """Compute the element-wise difference between two signals. @@ -583,9 +583,9 @@ def division(src1: SignalObj, src2: SignalObj) -> SignalObj: @computation_function( operation_id="sigima.signal.signals_to_image", - contract_version=2, + contract_version=1, x_alignment=SMALLEST_GRID_LINEAR_HOLD, - qualification=ReplayQualification(contract_version=2, comparison=EXACT), + qualification=ReplayQualification(contract_version=1, comparison=EXACT), ) def signals_to_image(src_list: list[SignalObj], p: SignalsToImageParam) -> ImageObj: """Combine multiple signals into an image. diff --git a/sigima/tests/common/contracts_unit_test.py b/sigima/tests/common/contracts_unit_test.py index 986e0f7..6639f33 100644 --- a/sigima/tests/common/contracts_unit_test.py +++ b/sigima/tests/common/contracts_unit_test.py @@ -354,7 +354,7 @@ def test_normalize_contract() -> None: def test_difference_contract() -> None: """The difference contract is qualified, signal 2-to-1, with X alignment.""" - contract = contracts.get_operation_contract("sigima.signal.difference", 2) + contract = contracts.get_operation_contract("sigima.signal.difference", 1) assert contract.function is sigima.proc.signal.difference assert contract.pattern == "2_to_1" and contract.qualified assert contract.inputs == ( @@ -368,33 +368,6 @@ def test_difference_contract() -> None: assert contracts.parameters_from_values(contract, {}) is None -#: Contracts whose result changed for some inputs in Sigima 1.4 (see the release -#: notes): calls recorded with version 1 must be refused, not replayed -CONTRACTS_V2 = ( - "sigima.signal.addition", - "sigima.signal.average", - "sigima.signal.standard_deviation", - "sigima.signal.product", - "sigima.signal.difference", - "sigima.signal.signals_to_image", - "sigima.image.resampling", - "sigima.image.calibration", - "sigima.image.extract_roi", - "sigima.image.extract_rois", - "sigima.image.radial_profile", -) - - -@pytest.mark.parametrize("operation_id", CONTRACTS_V2) -def test_contracts_changed_in_version_2(operation_id: str) -> None: - """Contracts whose result changed are qualified at version 2 and refuse 1.""" - contract = contracts.get_operation_contract(operation_id, 2) - assert contract.qualified - assert contract.qualification.contract_version == 2 - with pytest.raises(contracts.IncompatibleContractError): - contracts.get_operation_contract(operation_id, 1) - - def test_signature_calls_and_pickling_unchanged() -> None: """Decorated signature, both calling styles and pickling are preserved.""" func = sigima.proc.signal.normalize @@ -706,7 +679,7 @@ def test_comparison_rules() -> None: def test_contract_descriptors_pickle() -> None: """Descriptors and decorated functions with declarations are picklable.""" - contract = contracts.get_operation_contract("sigima.signal.difference", 2) + contract = contracts.get_operation_contract("sigima.signal.difference", 1) for value in ( contract.inputs, contract.outputs, diff --git a/sigima/tests/signal/alignment_unit_test.py b/sigima/tests/signal/alignment_unit_test.py index d873870..518cc19 100644 --- a/sigima/tests/signal/alignment_unit_test.py +++ b/sigima/tests/signal/alignment_unit_test.py @@ -136,7 +136,7 @@ def test_uncertainties_are_never_interpolated() -> None: def test_recorded_rule_is_checked_on_replay() -> None: """A replay accepts only a recorded, supported rule and version.""" - contract = contracts.get_operation_contract("sigima.signal.difference", 2) + contract = contracts.get_operation_contract("sigima.signal.difference", 1) source = _signal([0, 1, 2], [0, 1, 2]) operand = _signal([0, 0.5, 2], [0, 0.5, 2]) context = prepare_execution(contract, [source, operand], param=None).context