isof 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
isof-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Colin Ferrari
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
isof-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,258 @@
1
+ Metadata-Version: 2.4
2
+ Name: isof
3
+ Version: 0.1.0
4
+ Summary: Lecteur et vérificateur du format ISOF — échange de données isotopiques
5
+ Author-email: IsoFind SAS <colin.ferrari@isofind.tech>
6
+ License: MIT
7
+ Project-URL: Homepage, https://isofind.tech
8
+ Project-URL: Repository, https://github.com/ColinFerrari/isof
9
+ Project-URL: Bug Tracker, https://github.com/ColinFerrari/isof/issues
10
+ Project-URL: Specification, https://isofind.tech/isof-spec
11
+ Keywords: isotopes,geochemistry,traceability,forensics,ISOF
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
21
+ Classifier: Topic :: Scientific/Engineering :: Physics
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: cryptography>=41.0
26
+ Provides-Extra: pandas
27
+ Requires-Dist: pandas>=1.5; extra == "pandas"
28
+ Provides-Extra: dev
29
+ Requires-Dist: pytest>=7.0; extra == "dev"
30
+ Requires-Dist: pytest-cov; extra == "dev"
31
+ Requires-Dist: pandas>=1.5; extra == "dev"
32
+ Requires-Dist: ruff; extra == "dev"
33
+ Requires-Dist: mypy; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # isof
37
+
38
+ Python reader and verifier for the **ISOF v1.0** format, an open standard for exchanging geochemical isotopic data.
39
+
40
+ This format allows exchanging in a single file the isotopic data along with all associated metadata (analytical methods used for each sample, purification yields, analysis pipeline...), while enabling traceability of modifications once files are produced and certifying the origin of the file (laboratory certification).
41
+
42
+ **Sovereignty and Confidentiality:** Signature verification is a 100% local process. No data is sent to a third-party server for validation.
43
+
44
+ ```python
45
+ import isof
46
+
47
+ report = isof.load("analyse_bolivie.isof")
48
+
49
+ if report.is_authentic():
50
+ print(f"Signed by: {report.signature.signed_by}")
51
+
52
+ df = report.to_pandas()
53
+ print(df[["sample_name", "element", "ratio", "ratio_2se"]])
54
+ ```
55
+
56
+ The ISOF format is used by [IsoFind](https://isofind.tech), but this parser is independent and can read any file compliant with the [ISOF v1.0 specification](https://isofind.tech/isof-spec).
57
+
58
+ ---
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install isof
64
+ ```
65
+
66
+ With pandas support:
67
+
68
+ ```bash
69
+ pip install isof[pandas]
70
+ ```
71
+
72
+ Requires Python ≥ 3.9.
73
+
74
+ ---
75
+
76
+ ## Usage
77
+
78
+ ### Load a file
79
+
80
+ ```python
81
+ import isof
82
+
83
+ report = isof.load("analyse.isof")
84
+ print(report)
85
+ # <ISOfDocument v1.0 — 12 échantillon(s) — IGE Grenoble>
86
+ ```
87
+
88
+ From a JSON string (API, database):
89
+
90
+ ```python
91
+ with open("analyse.isof") as f:
92
+ text = f.read()
93
+
94
+ report = isof.loads(text)
95
+ ```
96
+
97
+ ### Verify integrity
98
+
99
+ ```python
100
+ # Simple check
101
+ if report.is_authentic():
102
+ print("Data integrity confirmed")
103
+
104
+ # Detailed result
105
+ result = report.verify()
106
+ print(result.valid) # bool
107
+ print(result.level) # 1 (SHA-256) or 2 (IsoFind PKI)
108
+ print(result.signer) # organisation or certificate CN
109
+ print(result.signed_at) # ISO 8601 timestamp
110
+ print(result.reason) # None if valid, error message otherwise
111
+ ```
112
+
113
+ Two signature levels coexist in the format:
114
+
115
+ | Level | Mechanism | Guarantee |
116
+ | ----- | -------------------------- | -------------------------------------------------------------- |
117
+ | 1 | SHA-256 over the data | Integrity — file has not been modified since export |
118
+ | 2 | ECDSA P-256 + IsoFind PKI | Authenticity — signed by a laboratory certified by IsoFind |
119
+
120
+ Verification works **offline**: IsoFind certificates are embedded in the package.
121
+
122
+ ### Access data
123
+
124
+ ```python
125
+ # Sample list
126
+ for sample in report.samples:
127
+ print(sample.id, sample.name, sample.classification)
128
+ for iso in sample.isotope_data:
129
+ print(f" {iso.element} {iso.system} = {iso.ratio} ± {iso.ratio_2se}")
130
+
131
+ # Look up a sample by identifier
132
+ s = report.sample("BOL-24-01")
133
+
134
+ # Filter
135
+ sources = report.filter_samples(classification="source")
136
+ sb_samples = report.filter_samples(element="Sb")
137
+ combined = report.filter_samples(element="Pb", material_type="minerai")
138
+
139
+ # Metadata
140
+ print(report.created_by.organisation)
141
+ print(report.project.reference)
142
+ print(report.project.client)
143
+ ```
144
+
145
+ ### Purification yields
146
+
147
+ ```python
148
+ # Yields for a sample
149
+ yields = report.yields_for_sample("BOL-24-01")
150
+ for y in yields:
151
+ print(f"{y.element}: {y.value_pct}%")
152
+
153
+ # Contamination alerts (yield > 105%)
154
+ suspects = report.suspicious_yields()
155
+ for y in suspects:
156
+ print(f"Possible contamination — {y.sample_id} / {y.element}: {y.value_pct}%")
157
+ ```
158
+
159
+ ### Methods and pipelines
160
+
161
+ ```python
162
+ # Preparation methods
163
+ for key, method in report.methods.items():
164
+ print(f"{key} — {method.name} ({method.type})")
165
+ if method.yield_min_pct:
166
+ print(f" Expected yield: {method.yield_min_pct}–{method.yield_max_pct}%")
167
+
168
+ # Pipelines
169
+ for key, pipeline in report.pipelines.items():
170
+ print(f"{pipeline.name} ({pipeline.element})")
171
+ for stage in pipeline.stages:
172
+ print(f" {stage.order}. {stage.label}")
173
+ ```
174
+
175
+ ### Export to pandas
176
+
177
+ ```python
178
+ df = report.to_pandas()
179
+
180
+ # One row per isotopic measurement, sample metadata included
181
+ df[["sample_name", "element", "ratio", "ratio_2se", "instrument"]]
182
+
183
+ # Standard pandas filtering
184
+ pb_data = df[df["element"] == "Pb"]
185
+ sources = df[df["classification"] == "source"]
186
+ ```
187
+
188
+ ### CSV export
189
+
190
+ ```python
191
+ report.to_csv("export.csv")
192
+ # equivalent to report.to_pandas().to_csv("export.csv", index=False)
193
+ ```
194
+
195
+ ---
196
+
197
+ ## Error handling
198
+
199
+ ```python
200
+ from isof.exceptions import ISOfParseError, ISOfVersionError, ISOfSignatureError
201
+
202
+ try:
203
+ report = isof.load("file.isof")
204
+ except ISOfVersionError as e:
205
+ print(f"Version {e.found} not supported, please update python-isof")
206
+ except ISOfParseError as e:
207
+ print(f"Invalid file: {e}")
208
+
209
+ # Corrupted vs. absent signature — two distinct cases
210
+ result = report.verify()
211
+ if result.level == 0:
212
+ print("No signature in this file")
213
+ elif not result.valid:
214
+ print(f"Signature present but invalid: {result.reason}")
215
+ ```
216
+
217
+ ---
218
+
219
+ ## ISOF format
220
+
221
+ Structure of an `.isof` document (JSON):
222
+
223
+ ```
224
+ {
225
+ "isof_version": "1.0",
226
+ "created_at": "2025-03-10T14:32:00Z",
227
+ "created_by": { "software", "operator", "organisation" },
228
+ "project": { "name", "reference", "client", "classification" },
229
+ "samples": [ ... ], ← isotopic data per sample
230
+ "methods": { ... }, ← preparation protocols
231
+ "pipelines": { ... }, ← method sequences per element
232
+ "purification": { ... }, ← measured yields per (sample, element)
233
+ "assignments": [ ... ], ← method ↔ sample links
234
+ "signature": { ... } ← optional
235
+ }
236
+ ```
237
+
238
+ Full specification: [isofind.tech/isof-spec](https://isofind.tech/isof-spec)
239
+
240
+ ---
241
+
242
+ ## Development
243
+
244
+ ```bash
245
+ git clone https://github.com/ColinFerrari/isof
246
+ cd isof
247
+ pip install -e ".[dev]"
248
+ pytest tests/ -v
249
+ ```
250
+
251
+ ---
252
+
253
+ ## License
254
+
255
+ MIT — see [LICENSE](LICENSE).
256
+
257
+ This package is maintained by [Colin Ferrari](https://isofind.tech).
258
+ The ISOF format is an open standard — third-party contributions and implementations are welcome.
isof-0.1.0/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # isof
2
+
3
+ Python reader and verifier for the **ISOF v1.0** format, an open standard for exchanging geochemical isotopic data.
4
+
5
+ This format allows exchanging in a single file the isotopic data along with all associated metadata (analytical methods used for each sample, purification yields, analysis pipeline...), while enabling traceability of modifications once files are produced and certifying the origin of the file (laboratory certification).
6
+
7
+ **Sovereignty and Confidentiality:** Signature verification is a 100% local process. No data is sent to a third-party server for validation.
8
+
9
+ ```python
10
+ import isof
11
+
12
+ report = isof.load("analyse_bolivie.isof")
13
+
14
+ if report.is_authentic():
15
+ print(f"Signed by: {report.signature.signed_by}")
16
+
17
+ df = report.to_pandas()
18
+ print(df[["sample_name", "element", "ratio", "ratio_2se"]])
19
+ ```
20
+
21
+ The ISOF format is used by [IsoFind](https://isofind.tech), but this parser is independent and can read any file compliant with the [ISOF v1.0 specification](https://isofind.tech/isof-spec).
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install isof
29
+ ```
30
+
31
+ With pandas support:
32
+
33
+ ```bash
34
+ pip install isof[pandas]
35
+ ```
36
+
37
+ Requires Python ≥ 3.9.
38
+
39
+ ---
40
+
41
+ ## Usage
42
+
43
+ ### Load a file
44
+
45
+ ```python
46
+ import isof
47
+
48
+ report = isof.load("analyse.isof")
49
+ print(report)
50
+ # <ISOfDocument v1.0 — 12 échantillon(s) — IGE Grenoble>
51
+ ```
52
+
53
+ From a JSON string (API, database):
54
+
55
+ ```python
56
+ with open("analyse.isof") as f:
57
+ text = f.read()
58
+
59
+ report = isof.loads(text)
60
+ ```
61
+
62
+ ### Verify integrity
63
+
64
+ ```python
65
+ # Simple check
66
+ if report.is_authentic():
67
+ print("Data integrity confirmed")
68
+
69
+ # Detailed result
70
+ result = report.verify()
71
+ print(result.valid) # bool
72
+ print(result.level) # 1 (SHA-256) or 2 (IsoFind PKI)
73
+ print(result.signer) # organisation or certificate CN
74
+ print(result.signed_at) # ISO 8601 timestamp
75
+ print(result.reason) # None if valid, error message otherwise
76
+ ```
77
+
78
+ Two signature levels coexist in the format:
79
+
80
+ | Level | Mechanism | Guarantee |
81
+ | ----- | -------------------------- | -------------------------------------------------------------- |
82
+ | 1 | SHA-256 over the data | Integrity — file has not been modified since export |
83
+ | 2 | ECDSA P-256 + IsoFind PKI | Authenticity — signed by a laboratory certified by IsoFind |
84
+
85
+ Verification works **offline**: IsoFind certificates are embedded in the package.
86
+
87
+ ### Access data
88
+
89
+ ```python
90
+ # Sample list
91
+ for sample in report.samples:
92
+ print(sample.id, sample.name, sample.classification)
93
+ for iso in sample.isotope_data:
94
+ print(f" {iso.element} {iso.system} = {iso.ratio} ± {iso.ratio_2se}")
95
+
96
+ # Look up a sample by identifier
97
+ s = report.sample("BOL-24-01")
98
+
99
+ # Filter
100
+ sources = report.filter_samples(classification="source")
101
+ sb_samples = report.filter_samples(element="Sb")
102
+ combined = report.filter_samples(element="Pb", material_type="minerai")
103
+
104
+ # Metadata
105
+ print(report.created_by.organisation)
106
+ print(report.project.reference)
107
+ print(report.project.client)
108
+ ```
109
+
110
+ ### Purification yields
111
+
112
+ ```python
113
+ # Yields for a sample
114
+ yields = report.yields_for_sample("BOL-24-01")
115
+ for y in yields:
116
+ print(f"{y.element}: {y.value_pct}%")
117
+
118
+ # Contamination alerts (yield > 105%)
119
+ suspects = report.suspicious_yields()
120
+ for y in suspects:
121
+ print(f"Possible contamination — {y.sample_id} / {y.element}: {y.value_pct}%")
122
+ ```
123
+
124
+ ### Methods and pipelines
125
+
126
+ ```python
127
+ # Preparation methods
128
+ for key, method in report.methods.items():
129
+ print(f"{key} — {method.name} ({method.type})")
130
+ if method.yield_min_pct:
131
+ print(f" Expected yield: {method.yield_min_pct}–{method.yield_max_pct}%")
132
+
133
+ # Pipelines
134
+ for key, pipeline in report.pipelines.items():
135
+ print(f"{pipeline.name} ({pipeline.element})")
136
+ for stage in pipeline.stages:
137
+ print(f" {stage.order}. {stage.label}")
138
+ ```
139
+
140
+ ### Export to pandas
141
+
142
+ ```python
143
+ df = report.to_pandas()
144
+
145
+ # One row per isotopic measurement, sample metadata included
146
+ df[["sample_name", "element", "ratio", "ratio_2se", "instrument"]]
147
+
148
+ # Standard pandas filtering
149
+ pb_data = df[df["element"] == "Pb"]
150
+ sources = df[df["classification"] == "source"]
151
+ ```
152
+
153
+ ### CSV export
154
+
155
+ ```python
156
+ report.to_csv("export.csv")
157
+ # equivalent to report.to_pandas().to_csv("export.csv", index=False)
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Error handling
163
+
164
+ ```python
165
+ from isof.exceptions import ISOfParseError, ISOfVersionError, ISOfSignatureError
166
+
167
+ try:
168
+ report = isof.load("file.isof")
169
+ except ISOfVersionError as e:
170
+ print(f"Version {e.found} not supported, please update python-isof")
171
+ except ISOfParseError as e:
172
+ print(f"Invalid file: {e}")
173
+
174
+ # Corrupted vs. absent signature — two distinct cases
175
+ result = report.verify()
176
+ if result.level == 0:
177
+ print("No signature in this file")
178
+ elif not result.valid:
179
+ print(f"Signature present but invalid: {result.reason}")
180
+ ```
181
+
182
+ ---
183
+
184
+ ## ISOF format
185
+
186
+ Structure of an `.isof` document (JSON):
187
+
188
+ ```
189
+ {
190
+ "isof_version": "1.0",
191
+ "created_at": "2025-03-10T14:32:00Z",
192
+ "created_by": { "software", "operator", "organisation" },
193
+ "project": { "name", "reference", "client", "classification" },
194
+ "samples": [ ... ], ← isotopic data per sample
195
+ "methods": { ... }, ← preparation protocols
196
+ "pipelines": { ... }, ← method sequences per element
197
+ "purification": { ... }, ← measured yields per (sample, element)
198
+ "assignments": [ ... ], ← method ↔ sample links
199
+ "signature": { ... } ← optional
200
+ }
201
+ ```
202
+
203
+ Full specification: [isofind.tech/isof-spec](https://isofind.tech/isof-spec)
204
+
205
+ ---
206
+
207
+ ## Development
208
+
209
+ ```bash
210
+ git clone https://github.com/ColinFerrari/isof
211
+ cd isof
212
+ pip install -e ".[dev]"
213
+ pytest tests/ -v
214
+ ```
215
+
216
+ ---
217
+
218
+ ## License
219
+
220
+ MIT — see [LICENSE](LICENSE).
221
+
222
+ This package is maintained by [Colin Ferrari](https://isofind.tech).
223
+ The ISOF format is an open standard — third-party contributions and implementations are welcome.
@@ -0,0 +1,93 @@
1
+ """
2
+ isof — Lecteur et vérificateur du format ISOF v1.0 | ISOF v1.0 reader and verificator
3
+
4
+ Usage minimal : | minimal use :
5
+
6
+ import isof
7
+
8
+ report = isof.load("analyse_bolivie.isof")
9
+ if report.is_authentic():
10
+ print(f"Signé par : {report.signature.signed_by}")
11
+ df = report.to_pandas()
12
+
13
+ Le format ISOF est un standard ouvert pour l'échange de données isotopiques | ISOF format is an open standard for isotope data exchange
14
+ Spécification : https://isofind.tech/isof-spec
15
+ """
16
+
17
+ from .exceptions import ISOfError, ISOfParseError, ISOfSignatureError, ISOfVersionError
18
+ from .models import (
19
+ Assignment,
20
+ CreatedBy,
21
+ IsotopeRecord,
22
+ Method,
23
+ Pipeline,
24
+ Project,
25
+ PurificationYield,
26
+ Sample,
27
+ Signature,
28
+ )
29
+ from .parser import ISOfDocument, load_file, load_string
30
+ from .signature import VerificationResult
31
+
32
+ __version__ = "0.1.0"
33
+ __author__ = "Colin Ferrari"
34
+ __all__ = [
35
+ # Fonctions d'entrée principales
36
+ "load",
37
+ "loads",
38
+ # Document
39
+ "ISOfDocument",
40
+ # Modèles
41
+ "Sample",
42
+ "IsotopeRecord",
43
+ "Method",
44
+ "Pipeline",
45
+ "PurificationYield",
46
+ "Assignment",
47
+ "CreatedBy",
48
+ "Project",
49
+ "Signature",
50
+ "VerificationResult",
51
+ # Exceptions
52
+ "ISOfError",
53
+ "ISOfParseError",
54
+ "ISOfVersionError",
55
+ "ISOfSignatureError",
56
+ ]
57
+
58
+
59
+ def load(path) -> ISOfDocument:
60
+ """Charge un fichier .isof depuis le disque. | Load an .isof file from disk
61
+
62
+ Args:
63
+ path: Chemin vers le fichier, str ou pathlib.Path.
64
+
65
+ Returns:
66
+ ISOfDocument prêt à l'emploi. | Ready to use
67
+
68
+ Raises:
69
+ ISOfParseError: Fichier introuvable, JSON invalide, ou structure ISOF non reconnue. | Unfound file, invalid JSON or ISOF structure unrecognised
70
+ ISOfVersionError: Version du format non supportée par ce parser. | Format unsupported by this parser
71
+
72
+ Example: | Exemple :
73
+ >>> report = isof.load("analyse_bolivie.isof")
74
+ >>> print(report)
75
+ <ISOfDocument v1.0 — 12 échantillon(s) — IGE Grenoble>
76
+ """
77
+ _, doc = load_file(path)
78
+ return doc
79
+
80
+
81
+ def loads(text: str) -> ISOfDocument:
82
+ """Charge un document ISOF depuis une chaîne JSON. | Load a ISOF document from a JSON chain.
83
+
84
+ Utile pour tester, ou pour lire depuis une API qui retourne du ISOF. | Useful to test or read from an API returning ISOF.
85
+
86
+ Args:
87
+ text: Contenu JSON d'un document ISOF. | JSON content of an ISOF document.
88
+
89
+ Returns:
90
+ ISOfDocument prêt à l'emploi. | ISOFDocument ready to use.
91
+ """
92
+ _, doc = load_string(text)
93
+ return doc
@@ -0,0 +1,71 @@
1
+ """
2
+ Hiérarchie d'exceptions du parser ISOF.
3
+
4
+ On distingue les erreurs de parsing (fichier malformé ou incompatible)
5
+ des erreurs de signature (fichier valide structurellement mais dont
6
+ l'intégrité ne peut pas être confirmée). Cette séparation permet aux
7
+ appelants de traiter les deux cas différemment sans inspecter le message.
8
+
9
+ ISOF parser exception hierarchy.
10
+
11
+ A distinction is made between parsing errors (malformed or incompatible file)
12
+ and signature errors (structurally valid file but whose
13
+ integrity cannot be confirmed). This separation allows
14
+ callers to handle the two cases differently without inspecting the message.
15
+ """
16
+
17
+
18
+ class ISOfError(Exception):
19
+ """
20
+ Classe de base, attraper celle-ci pour gérer toutes les erreurs ISOF.
21
+ Base class, catch this one to handle all ISOF errors.
22
+ """
23
+
24
+
25
+ class ISOfParseError(ISOfError):
26
+ """
27
+ Le fichier n'est pas un document ISOF valide ou lisible.
28
+ Causes typiques : JSON malformé, champ obligatoire absent,
29
+ version du format incompatible avec ce parser.
30
+
31
+ The file is not a valid or readable ISOF document.
32
+ Typical causes: Malformed JSON, missing required field,
33
+ format version incompatible with this parser.
34
+ """
35
+
36
+
37
+ class ISOfVersionError(ISOfParseError):
38
+ """
39
+ La version du format déclarée dans le fichier n'est pas supportée.
40
+ Elle est différente d'ISOfParseError pour que les outils puissent
41
+ suggérer une mise à jour du parser plutôt qu'un message d'erreur classique.
42
+
43
+ The format version declared in the file is not supported.
44
+ It differs from ISOfParseError so that tools can
45
+ suggest a parser update rather than a standard error message.
46
+ """
47
+
48
+ def __init__(self, found: str, supported: tuple[str, ...]) -> None:
49
+ self.found = found
50
+ self.supported = supported
51
+ super().__init__(
52
+ f"Version ISOF '{found}' non supportée. "
53
+ f"Versions supportées : {', '.join(supported)}"
54
+ )
55
+
56
+
57
+ class ISOfSignatureError(ISOfError):
58
+ """
59
+ La signature est présente mais ne peut pas être vérifiée.
60
+
61
+ Distinct d'une signature invalide (is_authentic() → False) :
62
+ ici c'est le processus de vérification lui-même qui a échoué,
63
+ par exemple parce que l'algorithme est inconnu ou que le certificat
64
+ est illisible.
65
+
66
+ The signature is present but cannot be verified.
67
+
68
+ This differs from an invalid signature (is_authentic() → False):
69
+ here, the verification process itself failed, for example,
70
+ because the algorithm is unknown or the certificate is unreadable.
71
+ """