pyannotators-entityfishing 1.6.87__tar.gz → 1.6.90__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.
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/PKG-INFO +1 -1
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/src/pyannotators_entityfishing/__init__.py +1 -1
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/.claude-plugin/plugin.json +1 -1
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/resources/Taskfile.yml +7 -4
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/skills/new-plugin/SKILL.md +9 -1
- pyannotators_entityfishing-1.6.90/submodules/python-archetype/skills/new-plugin/references/converter.md +66 -0
- pyannotators_entityfishing-1.6.90/submodules/python-archetype/skills/new-plugin/references/formatter.md +54 -0
- pyannotators_entityfishing-1.6.90/submodules/python-archetype/skills/new-plugin/references/segmenter.md +55 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/skills/new-plugin/references/types.md +39 -14
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/vars/pythonPipeline.groovy +43 -3
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/.claude/skills/ef-mapping/SKILL.md +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/.gitignore +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/.gitmodules +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/.python-version +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/README.md +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/Taskfile.yml +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/pyproject.toml +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/src/pyannotators_entityfishing/candidates.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/src/pyannotators_entityfishing/ef_client.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/src/pyannotators_entityfishing/entityfishing.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/.claude-plugin/marketplace.json +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/HOWTO.md +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/README.md +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/resources/Stages.yml +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/skills/new-plugin/references/annotator.md +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/skills/new-plugin/references/importer.md +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/skills/new-plugin/references/processor.md +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/submodules/python-archetype/tools/check-marketplace.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/build_rerank_data.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/check_rerank_endpoint.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/el_jev.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/eval_linking.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/eval_mappings.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/eval_rerank.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/finetune_rerank.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/merge_rerank.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/recalibrate_linking.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/review_linking.html +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/review_linking.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/sherpa_plan_project.py +0 -0
- {pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/wdfilter.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: pyannotators-entityfishing
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.90
|
|
4
4
|
Summary: Annotator based on entity-fishing
|
|
5
5
|
Project-URL: Homepage, https://github.com/oterrier/pyannotators_entityfishing/
|
|
6
6
|
Author-email: Olivier Terrier <olivier.terrier@kairntech.com>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "python-archetype",
|
|
4
4
|
"displayName": "Kairntech python-archetype",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.2.0",
|
|
6
6
|
"description": "Cree et entretient les depots Python de Kairntech qui consomment python-archetype : creation d'un plugin a partir de sa graine, et mesure de la derive d'un depot par rapport a elle.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "Kairntech",
|
|
@@ -169,11 +169,14 @@ tasks:
|
|
|
169
169
|
cmds:
|
|
170
170
|
- task: sync
|
|
171
171
|
vars: { EXTRAS: '{{.SYNC_EXTRAS}} sbom' }
|
|
172
|
-
#
|
|
173
|
-
#
|
|
174
|
-
# échouerait, empêchant le stage suivant d'exister. Même principe que le
|
|
172
|
+
# pip-audit sort non-zéro dès qu'il trouve quelque chose, et cette task ne rend
|
|
173
|
+
# pas le verdict : son échec ne doit pas l'arrêter. Même principe que le
|
|
175
174
|
# vuln-report.py de docker-image-pipeline, qui n'utilise pas le --exit-code de
|
|
176
175
|
# trivy pour garantir que le rapport est écrit AVANT l'échec.
|
|
176
|
+
# '&& echo 0 || echo $?' et PAS '; echo $?' : Task exécute les cmds sous set -e,
|
|
177
|
+
# donc avec ';' le shell s'arrête sur l'échec de pip-audit avant d'écrire le code
|
|
178
|
+
# -- la task échouait, sans rapport, dans le seul cas où il sert. Une liste && ||
|
|
179
|
+
# n'est pas interrompue par set -e, et $? y est encore celui de pip-audit.
|
|
177
180
|
# --skip-editable : les paquets installes en editable sont les membres du
|
|
178
181
|
# workspace, qui ne peuvent figurer dans aucune base d'avis. pip-audit les
|
|
179
182
|
# ignorait DEJA, faute de les trouver sur PyPI, mais en le disant -- le rapport
|
|
@@ -182,7 +185,7 @@ tasks:
|
|
|
182
185
|
# Le code de sortie est mémorisé : c'est le verdict, et la sortie markdown seule
|
|
183
186
|
# ne le porte pas.
|
|
184
187
|
- task: uv
|
|
185
|
-
vars: { ARGS: 'run pip-audit {{.AUDIT_ARGS}} --skip-editable --format markdown --output {{.AUDIT_REPORT}}.tmp
|
|
188
|
+
vars: { ARGS: 'run pip-audit {{.AUDIT_ARGS}} --skip-editable --format markdown --output {{.AUDIT_REPORT}}.tmp && echo 0 > {{.AUDIT_REPORT}}.rc || echo $? > {{.AUDIT_REPORT}}.rc' }
|
|
186
189
|
# Le verdict est ECRIT par nous, parce que pip-audit ne le dit jamais en markdown :
|
|
187
190
|
# sans trouvaille il n'écrit aucun fichier, et sans trouvaille mais avec des paquets
|
|
188
191
|
# ignorés il n'écrit QUE leur table « Skip Reason ». Dans les deux cas le lecteur ne
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: new-plugin
|
|
3
|
-
description: Créer un plugin Sherpa — importeur (pyimporters), processeur
|
|
3
|
+
description: Créer un plugin Sherpa — importeur (pyimporters), ou processeur, annotateur, convertisseur, formateur ou segmenteur (pymultirole) — à partir d'un format ou d'un besoin : clone la graine du contrat, écrit la classe et ses paramètres, les tests et leurs fixtures, la table du README, et câble le plugin dans le paquet de flavor du serveur. À utiliser quand on demande un nouvel importeur, un nouveau format de thésaurus, un nouveau processeur, annotateur, convertisseur (un format de fichier d'entrée), formateur (un format de sortie) ou segmenteur, ou un nouveau plugin pyimporters / pymultirole.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Créer un plugin Sherpa
|
|
@@ -65,6 +65,9 @@ d'un type à l'autre.
|
|
|
65
65
|
- [`references/importer.md`](references/importer.md)
|
|
66
66
|
- [`references/processor.md`](references/processor.md)
|
|
67
67
|
- [`references/annotator.md`](references/annotator.md)
|
|
68
|
+
- [`references/converter.md`](references/converter.md)
|
|
69
|
+
- [`references/formatter.md`](references/formatter.md)
|
|
70
|
+
- [`references/segmenter.md`](references/segmenter.md)
|
|
68
71
|
|
|
69
72
|
Deux règles valent pour tous les types :
|
|
70
73
|
|
|
@@ -84,6 +87,11 @@ paquet de flavor. Un plugin non câblé ne remonte jamais dans l'API.
|
|
|
84
87
|
(`references/<type>.md` dit lequel) ;
|
|
85
88
|
- aligner la borne de `pyimporters_plugins` ou `pymultirole-plugins` sur celle des
|
|
86
89
|
dépôts frères, ne pas l'élargir ;
|
|
90
|
+
- **un plugin destiné à un client peut-il aller sur PyPI, qui est public ?**
|
|
91
|
+
Demander, ne pas supposer. Si non : `publish: false` et `archiveWheel: true` dans
|
|
92
|
+
le Jenkinsfile, et la roue rejoint `pymultirole_server/deps/` au lieu des
|
|
93
|
+
`dependencies` du paquet de flavor (`references/types.md`, « Un plugin propre à
|
|
94
|
+
un client ») ;
|
|
87
95
|
- le dépôt Bitbucket `kairntech/<prefixe>_<nom>` (**underscore** : seul le nom de
|
|
88
96
|
distribution prend le tiret) est créé par une personne, avec le script de
|
|
89
97
|
création de dépôt, qui crée aussi le webhook et le job Jenkins. Il demande des
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Converter — le contenu à écrire
|
|
2
|
+
|
|
3
|
+
Graine `kairntech/pyconverters_seed`, layout `src/pyconverters_<nom>/`, fichier
|
|
4
|
+
`src/pyconverters_<nom>/<nom>.py`. Modèles vivants : `pyconverters_inscriptis`
|
|
5
|
+
(HTML → texte et phrases), `pyconverters_newsml` (un fichier, un document par `newsItem`).
|
|
6
|
+
|
|
7
|
+
## À demander en plus
|
|
8
|
+
|
|
9
|
+
- **le format d'entrée** et **un fichier d'exemple réel** : c'est la fixture des
|
|
10
|
+
tests, dans `tests/data/`. Un convertisseur testé sur un fichier fabriqué pour
|
|
11
|
+
l'occasion passe, puis casse sur le premier vrai document ;
|
|
12
|
+
- **un document ou plusieurs par fichier** : une archive, un classeur, un flux
|
|
13
|
+
NewsML en donnent plusieurs ; que devient alors `identifier` ;
|
|
14
|
+
- **ce qui doit sortir en plus du texte** : `title`, `metadata` (langue, dates),
|
|
15
|
+
`properties`, `sentences` ou `boundaries` si le format les porte déjà ;
|
|
16
|
+
- **un service externe ?** (OCR, API de transcription) Son URL par défaut, la
|
|
17
|
+
variable d'environnement qui la surcharge, la clé et l'endroit où elle vit
|
|
18
|
+
(`tests/.env`, jamais versionné).
|
|
19
|
+
|
|
20
|
+
## Le convertisseur
|
|
21
|
+
|
|
22
|
+
Remplacer le corps de `src/pyconverters_<nom>/<nom>.py`, **garder la forme** :
|
|
23
|
+
|
|
24
|
+
- les paramètres dérivent de `ConverterParameters`, qui est un `FormDataModel` :
|
|
25
|
+
le serveur les reçoit en JSON dans un champ de formulaire multipart, et le
|
|
26
|
+
modèle les décode lui-même. **Ne pas changer de classe de base** ;
|
|
27
|
+
`Field(défaut, description=...)` et `json_schema_extra={"extra": ...}` comme
|
|
28
|
+
pour un processeur (`advanced`, `internal`…) ;
|
|
29
|
+
- `convert(source, parameters)` reçoit un `UploadFile` de Starlette et rend une
|
|
30
|
+
**liste** de `Document`, même pour un seul. `source.file` est un flux binaire
|
|
31
|
+
côté serveur : décoder soi-même, avec l'encodage des paramètres ou celui que le
|
|
32
|
+
format déclare ; `source.filename` donne le nom ;
|
|
33
|
+
- `identifier` porte le nom du fichier (suffixé pour plusieurs documents), et
|
|
34
|
+
`properties["fileName"]` aussi, comme les convertisseurs frères : c'est ce qui
|
|
35
|
+
rattache le document à sa source dans Sherpa ;
|
|
36
|
+
- `ConverterBase` a pour métaclasse `ABCSingleton` : un modèle lourd (OCR…) se
|
|
37
|
+
charge une fois et se met en cache par paramètres, pas dans `self` à chaque
|
|
38
|
+
appel ;
|
|
39
|
+
- `get_model()` renvoie la classe de paramètres.
|
|
40
|
+
|
|
41
|
+
## Les tests
|
|
42
|
+
|
|
43
|
+
- le test du modèle, le test des paramètres reçus en formulaire et le test de
|
|
44
|
+
découverte par entry-point de la graine, gardés ;
|
|
45
|
+
- un test par fixture de `tests/data/`, qui vérifie le **texte produit** (contre un
|
|
46
|
+
`.txt` attendu à côté quand il est long), `identifier` et ce que le format
|
|
47
|
+
apporte en plus (titre, phrases, métadonnées) ;
|
|
48
|
+
- un fichier dégradé ou vide, qui doit donner un document vide ou une erreur
|
|
49
|
+
claire, pas une trace illisible ;
|
|
50
|
+
- si le convertisseur appelle un service : aucun appel réseau par défaut, tests
|
|
51
|
+
live derrière `skipif` sur la clé ou un marqueur `integration`. Un dépôt dont
|
|
52
|
+
tous les tests sont sautés sans clé est **vert sans rien tester** : garder au
|
|
53
|
+
moins un test hors ligne.
|
|
54
|
+
|
|
55
|
+
## Le README
|
|
56
|
+
|
|
57
|
+
Compléter la table des paramètres, et dire quels formats et quels types MIME le
|
|
58
|
+
convertisseur accepte.
|
|
59
|
+
|
|
60
|
+
## Le câblage
|
|
61
|
+
|
|
62
|
+
`"pyconverters-<nom>>=1.6.0,<1.7.0"` dans les `dependencies` du paquet de flavor de
|
|
63
|
+
`pymultirole_server/packages/` : `pymultirole-builtins` pour un format générique,
|
|
64
|
+
la flavor du client sinon (`references/types.md`). Un paquet système (`ffmpeg`,
|
|
65
|
+
`libicu-dev`…) doit aussi être présent sur l'agent Jenkins, qui n'a pas de
|
|
66
|
+
conteneur : le dire à qui gère l'agent.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Formatter — le contenu à écrire
|
|
2
|
+
|
|
3
|
+
Graine `kairntech/pyformatters_seed`, layout `src/pyformatters_<nom>/`, fichier
|
|
4
|
+
`src/pyformatters_<nom>/<nom>.py`. Modèles vivants : `pyformatters_json` (JSON),
|
|
5
|
+
`pyformatters_tabular` (CSV ou Excel selon un paramètre).
|
|
6
|
+
|
|
7
|
+
## À demander en plus
|
|
8
|
+
|
|
9
|
+
- **le format de sortie** et son type MIME, et **un exemple du fichier attendu** :
|
|
10
|
+
c'est la fixture des tests ;
|
|
11
|
+
- **un document d'exemple réaliste**, annotations et catégories comprises : un
|
|
12
|
+
formateur rend ce que les plugins précédents ont produit, et un document nu ne
|
|
13
|
+
teste rien ;
|
|
14
|
+
- **ce qui entre dans la sortie** : le texte, les annotations (et leurs `terms`),
|
|
15
|
+
les catégories, les phrases, les métadonnées — et dans quel ordre.
|
|
16
|
+
|
|
17
|
+
## Le formateur
|
|
18
|
+
|
|
19
|
+
Remplacer le corps de `src/pyformatters_<nom>/<nom>.py`, **garder la forme** :
|
|
20
|
+
|
|
21
|
+
- les paramètres dérivent de `FormatterParameters` et se déclarent en
|
|
22
|
+
`Field(défaut, description=...)` ; `json_schema_extra={"extra": ...}` comme pour
|
|
23
|
+
un processeur ;
|
|
24
|
+
- `format(document, parameters)` reçoit **un** document, pas un lot, et rend une
|
|
25
|
+
`Response` de Starlette, que le serveur transmet telle quelle au client. Poser le
|
|
26
|
+
`media_type`, et un `Content-Disposition: attachment; filename=...` comme les
|
|
27
|
+
formateurs frères, pour que le navigateur enregistre un fichier au bon nom ;
|
|
28
|
+
- lire les offsets tels quels : `document.text[a.start:a.end]` est le texte d'une
|
|
29
|
+
annotation (fin exclue). Ne rien recalculer, ne rien modifier du document ;
|
|
30
|
+
- `FormatterBase` a pour métaclasse `ABCSingleton` : rien dans `self` qui dépende
|
|
31
|
+
d'un appel ;
|
|
32
|
+
- `get_model()` renvoie la classe de paramètres.
|
|
33
|
+
|
|
34
|
+
## Les tests
|
|
35
|
+
|
|
36
|
+
- le test du modèle et le test de découverte par entry-point de la graine, gardés ;
|
|
37
|
+
- un test de formatage qui relit la sortie (`json.loads(resp.body)`, un lecteur CSV,
|
|
38
|
+
`openpyxl` pour un classeur) et vérifie son **contenu**, pas seulement le code
|
|
39
|
+
200 : `status_code`, `media_type` et `Content-Disposition` compris ;
|
|
40
|
+
- un document sans annotation, qui doit donner une sortie valide et vide ;
|
|
41
|
+
- attention au renommage de `task init` : il remplace le mot `seed` **partout**, y
|
|
42
|
+
compris dans les textes des tests. Un offset écrit en dur sur un texte qui
|
|
43
|
+
contient ce mot se décale après l'`init`.
|
|
44
|
+
|
|
45
|
+
## Le README
|
|
46
|
+
|
|
47
|
+
Compléter la table des paramètres, et décrire la sortie : type MIME, structure,
|
|
48
|
+
ce qui y figure.
|
|
49
|
+
|
|
50
|
+
## Le câblage
|
|
51
|
+
|
|
52
|
+
`"pyformatters-<nom>>=1.6.0,<1.7.0"` dans les `dependencies` du paquet de flavor de
|
|
53
|
+
`pymultirole_server/packages/` : `pymultirole-builtins` pour un format générique,
|
|
54
|
+
la flavor du client sinon (`references/types.md`).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Segmenter — le contenu à écrire
|
|
2
|
+
|
|
3
|
+
Graine `kairntech/pysegmenters_seed`, layout `src/pysegmenters_<nom>/`, fichier
|
|
4
|
+
`src/pysegmenters_<nom>/<nom>.py`. Modèles vivants : `pysegmenters_syntok` et
|
|
5
|
+
`pysegmenters_blingfire` (phrases, selon la langue), `pysegmenters_md_splitter`
|
|
6
|
+
(sections d'un texte Markdown).
|
|
7
|
+
|
|
8
|
+
## À demander en plus
|
|
9
|
+
|
|
10
|
+
- **l'unité produite** : phrases, paragraphes, sections, fenêtres de N caractères ;
|
|
11
|
+
- **les langues** à couvrir, et d'où vient la langue d'un document
|
|
12
|
+
(`document.metadata["language"]`, un paramètre, une détection) ;
|
|
13
|
+
- **des textes d'exemple réels** et le découpage attendu, avec ses cas difficiles :
|
|
14
|
+
abréviations, listes, titres, lignes vides, texte sans ponctuation.
|
|
15
|
+
|
|
16
|
+
## Le segmenteur
|
|
17
|
+
|
|
18
|
+
Remplacer le corps de `src/pysegmenters_<nom>/<nom>.py`, **garder la forme** :
|
|
19
|
+
|
|
20
|
+
- les paramètres dérivent de `SegmenterParameters` et se déclarent en
|
|
21
|
+
`Field(défaut, description=...)` ; `json_schema_extra={"extra": ...}` comme pour
|
|
22
|
+
un processeur ;
|
|
23
|
+
- `segment(documents, parameters)` reçoit et rend **tout le lot**, sans perdre ni
|
|
24
|
+
réordonner de document ;
|
|
25
|
+
- il **remplace** `document.sentences` : un segmenteur définit le découpage, il ne
|
|
26
|
+
l'enrichit pas, et segmenter deux fois doit donner le même résultat ;
|
|
27
|
+
- une `Sentence` a `start` et `end` en **offsets de caractères dans
|
|
28
|
+
`document.text`, fin exclue**, sans les blancs de bord ; `metadata` et
|
|
29
|
+
`categories` sont optionnels ;
|
|
30
|
+
- les phrases couvrent le texte **sans trou ni chevauchement** hors blancs : un
|
|
31
|
+
annotateur ou un processeur en aval travaille phrase par phrase, et un morceau
|
|
32
|
+
de texte hors de toute phrase n'est jamais vu ;
|
|
33
|
+
- `SegmenterBase` a pour métaclasse `ABCSingleton` : un modèle de langue se charge
|
|
34
|
+
une fois, pas à chaque appel ;
|
|
35
|
+
- `get_model()` renvoie la classe de paramètres.
|
|
36
|
+
|
|
37
|
+
## Les tests
|
|
38
|
+
|
|
39
|
+
- le test du modèle et le test de découverte par entry-point de la graine, gardés ;
|
|
40
|
+
- un test de segmentation qui vérifie `text[s.start:s.end]` pour chaque phrase,
|
|
41
|
+
contre la liste attendue : c'est ce qui attrape un décalage d'un caractère ou une
|
|
42
|
+
confusion octets/caractères ;
|
|
43
|
+
- un test d'idempotence (segmenter deux fois), et un document au texte vide ;
|
|
44
|
+
- un test par langue couverte, sur un texte réel.
|
|
45
|
+
|
|
46
|
+
## Le README
|
|
47
|
+
|
|
48
|
+
Compléter la table des paramètres, et dire quelle unité le segmenteur produit et
|
|
49
|
+
pour quelles langues.
|
|
50
|
+
|
|
51
|
+
## Le câblage
|
|
52
|
+
|
|
53
|
+
`"pysegmenters-<nom>>=1.6.0,<1.7.0"` dans les `dependencies` du paquet de flavor de
|
|
54
|
+
`pymultirole_server/packages/` : `pymultirole-builtins` pour un segmenteur
|
|
55
|
+
générique, la flavor du client sinon (`references/types.md`).
|
|
@@ -8,11 +8,11 @@ skill.
|
|
|
8
8
|
| type | graine | groupe d'entry-points | classe de base | paquet de flavor | graine prête ? |
|
|
9
9
|
|------|--------|-----------------------|----------------|------------------|----------------|
|
|
10
10
|
| `importer` | `kairntech/pyimporters_seed` | `pyimporters.plugins` | `KnowledgeParserBase` (`pyimporters_plugins.base`) | `pyimporters_server/packages/pyimporters-builtins` | **oui** |
|
|
11
|
-
| `converter` | `kairntech/pyconverters_seed` | `pyconverters.plugins` | `ConverterBase` (`pymultirole_plugins.v1.converter`) | `pymultirole_server/packages/pymultirole-builtins` |
|
|
11
|
+
| `converter` | `kairntech/pyconverters_seed` | `pyconverters.plugins` | `ConverterBase` (`pymultirole_plugins.v1.converter`) | `pymultirole_server/packages/pymultirole-builtins` | **oui** |
|
|
12
12
|
| `processor` | `kairntech/pyprocessors_seed` | `pyprocessors.plugins` | `ProcessorBase` (`pymultirole_plugins.v1.processor`) | `pymultirole-builtins` ou une flavor dédiée | **oui** |
|
|
13
13
|
| `annotator` | `kairntech/pyannotators_seed` | `pyannotators.plugins` | `AnnotatorBase` (`pymultirole_plugins.v1.annotator`) | `pymultirole-builtins` ou une flavor dédiée | **oui** |
|
|
14
|
-
| `formatter` | `kairntech/pyformatters_seed` | `pyformatters.plugins` | `FormatterBase` (`pymultirole_plugins.v1.formatter`) | `pymultirole-builtins` |
|
|
15
|
-
| `segmenter` | `kairntech/pysegmenters_seed` | `pysegmenters.plugins` | `SegmenterBase` (`pymultirole_plugins.v1.segmenter`) | `pymultirole-builtins` |
|
|
14
|
+
| `formatter` | `kairntech/pyformatters_seed` | `pyformatters.plugins` | `FormatterBase` (`pymultirole_plugins.v1.formatter`) | `pymultirole-builtins` | **oui** |
|
|
15
|
+
| `segmenter` | `kairntech/pysegmenters_seed` | `pysegmenters.plugins` | `SegmenterBase` (`pymultirole_plugins.v1.segmenter`) | `pymultirole-builtins` | **oui** |
|
|
16
16
|
|
|
17
17
|
## Ce que chaque graine prête produit
|
|
18
18
|
|
|
@@ -21,13 +21,19 @@ skill.
|
|
|
21
21
|
| `importer` | à plat : `pyimporters_<nom>/` | `pyimporters_<nom>` | `CSV` → `CSVKnowledgeParser`, `CSVOptions` | « Sherpa FOO knowledge import plugin » | [`importer.md`](importer.md) |
|
|
22
22
|
| `processor` | `src/pyprocessors_<nom>/` | `src/pyprocessors_<nom>` | `Foo` → `FooProcessor`, `FooParameters` | « Processor based on foo » | [`processor.md`](processor.md) |
|
|
23
23
|
| `annotator` | `src/pyannotators_<nom>/` | `src/pyannotators_<nom>` | `Foo` → `FooAnnotator`, `FooParameters` | « Annotator based on foo » | [`annotator.md`](annotator.md) |
|
|
24
|
+
| `converter` | `src/pyconverters_<nom>/` | `src/pyconverters_<nom>` | `Foo` → `FooConverter`, `FooParameters` | « Converter based on foo » | [`converter.md`](converter.md) |
|
|
25
|
+
| `formatter` | `src/pyformatters_<nom>/` | `src/pyformatters_<nom>` | `Foo` → `FooFormatter`, `FooParameters` | « Formatter based on foo » | [`formatter.md`](formatter.md) |
|
|
26
|
+
| `segmenter` | `src/pysegmenters_<nom>/` | `src/pysegmenters_<nom>` | `Foo` → `FooSegmenter`, `FooParameters` | « Segmenter based on foo » | [`segmenter.md`](segmenter.md) |
|
|
24
27
|
|
|
25
28
|
Le layout suit la famille, pas une préférence : les dépôts `pyimporters_*` ont leur
|
|
26
29
|
paquet à plat, les dépôts `pymultirole` sont en `src/` — dont `pyprocessors_jev` et
|
|
27
30
|
`pyannotators_entityfishing`, déjà passés sous `python-archetype`, sur lesquels les
|
|
28
|
-
graines `pymultirole` sont calquées (ruff à 256 colonnes compris).
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
graines `pymultirole` sont calquées (ruff à 256 colonnes compris). Les cinq graines
|
|
32
|
+
`pymultirole` partagent la même enveloppe au type près : après `task init`, le Taskfile
|
|
33
|
+
d'un plugin est identique octet pour octet à celui des dépôts frères de sa famille
|
|
34
|
+
(`pyprocessors_jev`, `pyannotators_entityfishing`, et les dépôts migrés de chaque
|
|
35
|
+
famille). Quelques dépôts frères plus anciens ont leur paquet à plat
|
|
36
|
+
(`pyformatters_json`, `pyprocessors_deepl`…) : un plugin neuf est en `src/`.
|
|
31
37
|
|
|
32
38
|
Les paquets de flavor de `pymultirole_server/packages/` : `pymultirole-builtins`,
|
|
33
39
|
`pymultirole-afp`, `pymultirole-cairn`, `pymultirole-escrim`, `pymultirole-grouperf`,
|
|
@@ -35,19 +41,38 @@ Les paquets de flavor de `pymultirole_server/packages/` : `pymultirole-builtins`
|
|
|
35
41
|
serveur, pas une flavor). Un plugin propre à un client va dans la flavor de ce
|
|
36
42
|
client ; demander, ne pas deviner.
|
|
37
43
|
|
|
38
|
-
##
|
|
44
|
+
## Les six graines existent
|
|
39
45
|
|
|
40
|
-
`pyimporters_seed`, `
|
|
41
|
-
|
|
42
|
-
famille `pymultirole`
|
|
43
|
-
|
|
46
|
+
`pyimporters_seed`, et pour `pymultirole` `pyprocessors_seed`, `pyannotators_seed`,
|
|
47
|
+
`pyconverters_seed`, `pyformatters_seed` et `pysegmenters_seed`. Elles ne publient
|
|
48
|
+
rien (`publish: false`). La famille `pymultirole` est passée sous `python-archetype` ;
|
|
49
|
+
les dépôts restés en flit portent encore la sortie de `cookiecutter-pymultirole`
|
|
44
50
|
(`bumpversion.py`, `AUTHORS.md`, `CHANGELOG.md`, `RELEASE.md`, `mypy.ini`, `docs/`).
|
|
45
51
|
|
|
46
|
-
Si on demande un type
|
|
52
|
+
Si on demande un type qui n'est pas dans la table : **le dire et s'arrêter.**
|
|
47
53
|
Improviser l'enveloppe reviendrait à recréer à la main ce que la graine garantit,
|
|
48
54
|
sans la garantie — et le dépôt produit devrait être migré comme les autres. La
|
|
49
|
-
bonne réponse est de créer d'abord la graine du type, sur le modèle des
|
|
50
|
-
|
|
55
|
+
bonne réponse est de créer d'abord la graine du type, sur le modèle des existantes.
|
|
56
|
+
|
|
57
|
+
## Un plugin propre à un client : construit, jamais publié
|
|
58
|
+
|
|
59
|
+
PyPI est public. Un plugin écrit pour un client (`pyprocessors_escrim_reconciliation`,
|
|
60
|
+
`pyformatters_json_rem`) ne doit pas y aller : **demander**, pour tout plugin destiné à
|
|
61
|
+
une flavor client, s'il peut être publié. Si non, remplacer dans le Jenkinsfile le
|
|
62
|
+
`publishBranches` seul par :
|
|
63
|
+
|
|
64
|
+
```groovy
|
|
65
|
+
publish: false,
|
|
66
|
+
archiveWheel: true,
|
|
67
|
+
publishBranches: '^master$',
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`publish: false` est la garantie, et elle ne dépend de rien d'autre. `archiveWheel`
|
|
71
|
+
demande en plus le stage `version` et l'archivage de la roue dans le build Jenkins,
|
|
72
|
+
d'où elle rejoint `pymultirole_server/deps/` ; le Dockerfile de la flavor l'y prend
|
|
73
|
+
(`find deps -name '<distribution>-*.whl' | sort -V | tail -n 1`). Aucun index ne la
|
|
74
|
+
servant, **ne pas** l'ajouter aux `dependencies` du paquet de flavor : la résolution y
|
|
75
|
+
chercherait une version publiée, qui n'existe pas ou qui est ancienne.
|
|
51
76
|
|
|
52
77
|
## Pourquoi une graine par contrat, et un seul skill
|
|
53
78
|
|
|
@@ -33,6 +33,12 @@
|
|
|
33
33
|
* @param throttleCategory Throttle category shared with the sibling repositories. Omit for none.
|
|
34
34
|
* @param publish Publish the wheel to PyPI. Default true; never on a pull request,
|
|
35
35
|
* and never off a publishBranches branch.
|
|
36
|
+
* @param archiveWheel Archive the wheel in the build, with the version bump that goes with
|
|
37
|
+
* it. Default false. For a client-specific plugin that must never reach
|
|
38
|
+
* PyPI, which is public: 'publish: false, archiveWheel: true' versions,
|
|
39
|
+
* builds and archives, and sends nothing out. 'publish: false' alone is
|
|
40
|
+
* the guarantee -- an older copy of this file ignores archiveWheel and
|
|
41
|
+
* still publishes nothing. Same branch and pull-request rules as publish.
|
|
36
42
|
* @param publishBranches Regex the branch must FULLY match for 'version' and 'py:publish'
|
|
37
43
|
* to run, e.g. '^(master|py38)$'. Falls back to the
|
|
38
44
|
* CI_PUBLISH_BRANCHES global environment variable, then '^master$'.
|
|
@@ -90,6 +96,9 @@ def call(Map config = [:]) {
|
|
|
90
96
|
env._TIMEOUT_TIME = buildTimeout.time as String
|
|
91
97
|
env._TIMEOUT_UNIT = buildTimeout.unit ?: 'HOURS'
|
|
92
98
|
env._PUBLISH = config.get('publish', true) ? 'true' : 'false'
|
|
99
|
+
// Versionner et archiver la roue sans la publier : un plugin propre a un client est
|
|
100
|
+
// construit ici, mais ne doit jamais partir sur PyPI, qui est public.
|
|
101
|
+
env._ARCHIVE_WHEEL = config.get('archiveWheel', false) ? 'true' : 'false'
|
|
93
102
|
// Les branches depuis lesquelles publier. Une branche de feature ne doit RIEN
|
|
94
103
|
// publier : PyPI est immuable et son espace de noms est commun a toutes les
|
|
95
104
|
// branches, donc une branche neuve dont le compteur de build repasse sur un numero
|
|
@@ -168,7 +177,8 @@ def call(Map config = [:]) {
|
|
|
168
177
|
checkStageDrift()
|
|
169
178
|
// Dire pourquoi 'version' et 'py:publish' seront joues ou non :
|
|
170
179
|
// un stage grise sans explication laisse chercher.
|
|
171
|
-
String state = publishAllowed() ? 'activee' :
|
|
180
|
+
String state = publishAllowed() ? 'activee' :
|
|
181
|
+
(releaseAllowed() ? 'desactivee, roue archivee sans publication' : 'desactivee')
|
|
172
182
|
// Le '+' DOIT rester en fin de ligne. Place en tete de la ligne
|
|
173
183
|
// suivante, Groovy termine l'instruction 'echo' et lit '+ "..."'
|
|
174
184
|
// comme un plus UNAIRE sur une String : String.positive(), qui
|
|
@@ -280,7 +290,7 @@ def call(Map config = [:]) {
|
|
|
280
290
|
when {
|
|
281
291
|
allOf {
|
|
282
292
|
not { changeRequest() }
|
|
283
|
-
expression { return
|
|
293
|
+
expression { return releaseAllowed() }
|
|
284
294
|
}
|
|
285
295
|
}
|
|
286
296
|
steps {
|
|
@@ -297,11 +307,13 @@ def call(Map config = [:]) {
|
|
|
297
307
|
}
|
|
298
308
|
}
|
|
299
309
|
|
|
310
|
+
// Publie sur PyPI, ou archive seulement la roue d'un plugin confidentiel
|
|
311
|
+
// (archiveWheel) : les deux partagent la garde de version de publishWheel().
|
|
300
312
|
stage('py:publish') {
|
|
301
313
|
when {
|
|
302
314
|
allOf {
|
|
303
315
|
not { changeRequest() }
|
|
304
|
-
expression { return
|
|
316
|
+
expression { return releaseAllowed() }
|
|
305
317
|
}
|
|
306
318
|
}
|
|
307
319
|
steps {
|
|
@@ -379,6 +391,22 @@ private boolean publishAllowed() {
|
|
|
379
391
|
if (env._PUBLISH != 'true') {
|
|
380
392
|
return false
|
|
381
393
|
}
|
|
394
|
+
return onPublishBranch()
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* True when this build produces a release: a version commit and a wheel that leaves the build,
|
|
399
|
+
* whether on PyPI (publish) or only as a build artifact (archiveWheel). Same branch rule as
|
|
400
|
+
* publishAllowed(): a feature branch must not push version commits either.
|
|
401
|
+
*/
|
|
402
|
+
private boolean releaseAllowed() {
|
|
403
|
+
if (env._PUBLISH != 'true' && env._ARCHIVE_WHEEL != 'true') {
|
|
404
|
+
return false
|
|
405
|
+
}
|
|
406
|
+
return onPublishBranch()
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private boolean onPublishBranch() {
|
|
382
410
|
String branch = currentBranch()
|
|
383
411
|
return branch && (branch ==~ env._PUBLISH_BRANCHES)
|
|
384
412
|
}
|
|
@@ -617,6 +645,18 @@ private void publishWheel() {
|
|
|
617
645
|
return
|
|
618
646
|
}
|
|
619
647
|
|
|
648
|
+
if (env._ARCHIVE_WHEEL == 'true') {
|
|
649
|
+
// Le nom EXACT de la roue de ce build, pas 'dist/*.whl' : dist/ vit dans le workspace
|
|
650
|
+
// persistant et garde les roues des builds precedents. Aucune roue a ce nom est une
|
|
651
|
+
// erreur -- un build qui annonce une roue archivee et n'en archive aucune ment.
|
|
652
|
+
archiveArtifacts(artifacts: "dist/*-${version}-*.whl", fingerprint: true)
|
|
653
|
+
}
|
|
654
|
+
// Test STRICT de 'true' : la publication n'a lieu que si elle est demandee, jamais par
|
|
655
|
+
// defaut d'une valeur inattendue.
|
|
656
|
+
if (env._PUBLISH != 'true') {
|
|
657
|
+
echo "publish: false -- nothing sent to PyPI."
|
|
658
|
+
return
|
|
659
|
+
}
|
|
620
660
|
withEnv([
|
|
621
661
|
"UV_PUBLISH_USERNAME=${credentialFrom(env._PYPI_FILE, 'FLIT_USERNAME')}",
|
|
622
662
|
"UV_PUBLISH_PASSWORD=${credentialFrom(env._PYPI_FILE, 'FLIT_PASSWORD')}",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/build_rerank_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/eval_linking.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/eval_mappings.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/eval_rerank.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/finetune_rerank.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/merge_rerank.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/recalibrate_linking.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/review_linking.html
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/review_linking.py
RENAMED
|
File without changes
|
{pyannotators_entityfishing-1.6.87 → pyannotators_entityfishing-1.6.90}/tools/sherpa_plan_project.py
RENAMED
|
File without changes
|
|
File without changes
|