pysec2pri 0.2.2__tar.gz → 0.2.4__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.
Files changed (51) hide show
  1. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/PKG-INFO +19 -10
  2. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/README.md +17 -8
  3. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/pyproject.toml +3 -3
  4. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/__init__.py +22 -0
  5. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/api.py +130 -85
  6. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/cli.py +8 -20
  7. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/ensembl.yaml +1 -2
  8. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/hgnc.yaml +1 -2
  9. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/ncbi.yaml +1 -2
  10. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/consolidate.py +54 -116
  11. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/downloads/ensembl.py +12 -4
  12. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/chebi.py +3 -4
  13. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/vgnc.py +2 -3
  14. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/version.py +1 -1
  15. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/LICENSE +0 -0
  16. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/__main__.py +0 -0
  17. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/__init__.py +0 -0
  18. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/chebi.yaml +0 -0
  19. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/hmdb_metabolites.yaml +0 -0
  20. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/hmdb_proteins.yaml +0 -0
  21. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/uniprot.yaml +0 -0
  22. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/vgnc.yaml +0 -0
  23. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/config/wikidata.yaml +0 -0
  24. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/constants.py +0 -0
  25. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/download.py +0 -0
  26. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/downloads/__init__.py +0 -0
  27. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/downloads/chebi.py +0 -0
  28. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/downloads/hgnc.py +0 -0
  29. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/downloads/hmdb.py +0 -0
  30. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/downloads/ncbi.py +0 -0
  31. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/downloads/uniprot.py +0 -0
  32. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/exports.py +0 -0
  33. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/logging.py +0 -0
  34. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/__init__.py +0 -0
  35. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/base.py +0 -0
  36. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/ensembl.py +0 -0
  37. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/hgnc.py +0 -0
  38. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/hmdb.py +0 -0
  39. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/ncbi.py +0 -0
  40. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/uniprot.py +0 -0
  41. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/parsers/wikidata.py +0 -0
  42. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/py.typed +0 -0
  43. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/queries/__init__.py +0 -0
  44. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/queries/chemical_redirects.rq +0 -0
  45. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/queries/chemical_redirects_test.rq +0 -0
  46. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/queries/gene_redirects.rq +0 -0
  47. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/queries/gene_redirects_test.rq +0 -0
  48. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/queries/protein_redirects.rq +0 -0
  49. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/queries/protein_redirects_test.rq +0 -0
  50. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/update_ids.py +0 -0
  51. {pysec2pri-0.2.2 → pysec2pri-0.2.4}/src/pysec2pri/xref.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysec2pri
3
- Version: 0.2.2
3
+ Version: 0.2.4
4
4
  Summary: Secondary to primary identifier mapping
5
5
  Keywords: snekpack,cookiecutter
6
6
  Author: Javier Millán Acosta
@@ -40,7 +40,7 @@ Requires-Dist: numpy
40
40
  Requires-Dist: rdkit
41
41
  Requires-Dist: polars-runtime-32
42
42
  Requires-Dist: sssom-schema==1.1.0a5
43
- Requires-Dist: mapkgsutils>=0.0.5
43
+ Requires-Dist: mapkgsutils==0.1.2
44
44
  Maintainer: Javier Millán Acosta
45
45
  Maintainer-email: Javier Millán Acosta <javier.millan.acosta@gmail.com>
46
46
  Requires-Python: >=3.10
@@ -182,6 +182,15 @@ pysec2pri hgnc ids # outputs hgnc_{version}_sssom.tsv
182
182
  pysec2pri update-ids gene_ex.tsv hgnc --at gene --mapping hgnc_{version}_sssom.tsv
183
183
  ```
184
184
 
185
+ In Python, `load_<datasource>` reads a written SSSOM file back into the same
186
+ `IdMappingSet`/`LabelMappingSet`:
187
+
188
+ ```python
189
+ from pysec2pri import load_hgnc, update_ids
190
+ hgnc_ms = load_hgnc("hgnc_115_sssom.tsv")
191
+ df_with_new_column = update_ids(mapping_set=hgnc_ms, ids=df, at="gene")
192
+ ```
193
+
185
194
  Ambiguous mappings (where a deprecated ID or label serves as a recommended for
186
195
  another entity) are not resolved, but flagged for users to solve them manually.
187
196
  If the input file has a column of known aliases or synonyms for each row, pass
@@ -234,7 +243,7 @@ mappings.
234
243
  ### Aliases / synonyms
235
244
 
236
245
  Alias mappings use `oboInOwl:hasExactSynonym`. The alias is the `subject_label`
237
- and the authoritative name is the `object_label`/`object_id`.
246
+ and the primary name is the `object_label`/`object_id`.
238
247
 
239
248
  ```mermaid
240
249
  flowchart LR
@@ -324,15 +333,15 @@ crosswalk("ENSG00000141510", frm="ensembl", to="symbol") # via HGNC's own cross
324
333
  ### Consolidating mapping dates across releases
325
334
 
326
335
  A single release snapshot only presents each mapping's _last-seen_ date.
327
- `consolidate` builds a first-seen-date index by walking a datasource's
328
- historical archive (or, where the parser already exposes a real per-row date
329
- such as HGNC's `date_symbol_changed`, a single fast pass) and writes it back out
330
- as a real SSSOM mapping set whose `mapping_date` is each mapping's true first
331
- appearance:
336
+ `consolidate` builds a first-seen-date index and writes it back out as a real
337
+ SSSOM mapping set whose `mapping_date` is each mapping's true first appearance.
338
+ The path is chosen automatically: datasources with a versioned archive are
339
+ walked release by release, while those without one (NCBI, VGNC) are covered in a
340
+ single fast pass over the current release:
332
341
 
333
342
  ```bash
334
- pysec2pri chebi consolidate --mode release # walks ~250 ChEBI releases; slow, run as a one-off
335
- pysec2pri hgnc consolidate --mode date # fast: uses HGNC's own per-row date
343
+ pysec2pri chebi consolidate # walks ~250 ChEBI releases (versioned archive); slow, run as a one-off
344
+ pysec2pri ncbi consolidate # single fast pass (no versioned archive)
336
345
  ```
337
346
 
338
347
  Supported for `chebi`, `ensembl`, `hgnc`, `ncbi`, `uniprot`, and `vgnc`. Ensembl
@@ -129,6 +129,15 @@ pysec2pri hgnc ids # outputs hgnc_{version}_sssom.tsv
129
129
  pysec2pri update-ids gene_ex.tsv hgnc --at gene --mapping hgnc_{version}_sssom.tsv
130
130
  ```
131
131
 
132
+ In Python, `load_<datasource>` reads a written SSSOM file back into the same
133
+ `IdMappingSet`/`LabelMappingSet`:
134
+
135
+ ```python
136
+ from pysec2pri import load_hgnc, update_ids
137
+ hgnc_ms = load_hgnc("hgnc_115_sssom.tsv")
138
+ df_with_new_column = update_ids(mapping_set=hgnc_ms, ids=df, at="gene")
139
+ ```
140
+
132
141
  Ambiguous mappings (where a deprecated ID or label serves as a recommended for
133
142
  another entity) are not resolved, but flagged for users to solve them manually.
134
143
  If the input file has a column of known aliases or synonyms for each row, pass
@@ -181,7 +190,7 @@ mappings.
181
190
  ### Aliases / synonyms
182
191
 
183
192
  Alias mappings use `oboInOwl:hasExactSynonym`. The alias is the `subject_label`
184
- and the authoritative name is the `object_label`/`object_id`.
193
+ and the primary name is the `object_label`/`object_id`.
185
194
 
186
195
  ```mermaid
187
196
  flowchart LR
@@ -271,15 +280,15 @@ crosswalk("ENSG00000141510", frm="ensembl", to="symbol") # via HGNC's own cross
271
280
  ### Consolidating mapping dates across releases
272
281
 
273
282
  A single release snapshot only presents each mapping's _last-seen_ date.
274
- `consolidate` builds a first-seen-date index by walking a datasource's
275
- historical archive (or, where the parser already exposes a real per-row date
276
- such as HGNC's `date_symbol_changed`, a single fast pass) and writes it back out
277
- as a real SSSOM mapping set whose `mapping_date` is each mapping's true first
278
- appearance:
283
+ `consolidate` builds a first-seen-date index and writes it back out as a real
284
+ SSSOM mapping set whose `mapping_date` is each mapping's true first appearance.
285
+ The path is chosen automatically: datasources with a versioned archive are
286
+ walked release by release, while those without one (NCBI, VGNC) are covered in a
287
+ single fast pass over the current release:
279
288
 
280
289
  ```bash
281
- pysec2pri chebi consolidate --mode release # walks ~250 ChEBI releases; slow, run as a one-off
282
- pysec2pri hgnc consolidate --mode date # fast: uses HGNC's own per-row date
290
+ pysec2pri chebi consolidate # walks ~250 ChEBI releases (versioned archive); slow, run as a one-off
291
+ pysec2pri ncbi consolidate # single fast pass (no versioned archive)
283
292
  ```
284
293
 
285
294
  Supported for `chebi`, `ensembl`, `hgnc`, `ncbi`, `uniprot`, and `vgnc`. Ensembl
@@ -7,7 +7,7 @@ build-backend = "uv_build"
7
7
 
8
8
  [project]
9
9
  name = "pysec2pri"
10
- version = "0.2.2"
10
+ version = "0.2.4"
11
11
  description = "Secondary to primary identifier mapping"
12
12
  readme = "README.md"
13
13
  authors = [
@@ -71,7 +71,7 @@ dependencies = [
71
71
  "rdkit", # Chemistry toolkit for SDF parsing
72
72
  "polars-runtime-32", # Polars runtime for 32-bit systems
73
73
  "sssom-schema==1.1.0a5", # SSSOM schema for validation
74
- "mapkgsutils>=0.0.5",
74
+ "mapkgsutils==0.1.2",
75
75
  ]
76
76
 
77
77
  # see https://peps.python.org/pep-0735/ and https://docs.astral.sh/uv/concepts/dependencies/#dependency-groups
@@ -255,7 +255,7 @@ known-first-party = [
255
255
  docstring-code-format = true
256
256
 
257
257
  [tool.bumpversion]
258
- current_version = "0.2.2"
258
+ current_version = "0.2.4"
259
259
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(?:-(?P<release>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?(?:\\+(?P<build>[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?"
260
260
  serialize = [
261
261
  "{major}.{minor}.{patch}-{release}+{build}",
@@ -33,6 +33,17 @@ from pysec2pri.api import (
33
33
  generate_wikidata,
34
34
  generate_wikidata_labels,
35
35
  list_versions,
36
+ load_chebi,
37
+ load_ensembl,
38
+ load_hgnc,
39
+ load_hmdb,
40
+ load_hmdb_proteins,
41
+ load_label_mapping,
42
+ load_mapping,
43
+ load_ncbi,
44
+ load_uniprot,
45
+ load_vgnc,
46
+ load_wikidata,
36
47
  resolve_ids,
37
48
  resolve_labels,
38
49
  write_json,
@@ -101,6 +112,17 @@ __all__ = [
101
112
  "generate_wikidata",
102
113
  "generate_wikidata_labels",
103
114
  "list_versions",
115
+ "load_chebi",
116
+ "load_ensembl",
117
+ "load_hgnc",
118
+ "load_hmdb",
119
+ "load_hmdb_proteins",
120
+ "load_label_mapping",
121
+ "load_mapping",
122
+ "load_ncbi",
123
+ "load_uniprot",
124
+ "load_vgnc",
125
+ "load_wikidata",
104
126
  "load_xref_mapping",
105
127
  "resolve_ambiguous_with_hints",
106
128
  "resolve_ids",
@@ -86,8 +86,17 @@ __all__ = [
86
86
  "generate_wikidata_labels",
87
87
  "generate_wikidata_labels",
88
88
  "list_versions",
89
+ "load_chebi",
90
+ "load_ensembl",
91
+ "load_hgnc",
92
+ "load_hmdb",
93
+ "load_hmdb_proteins",
89
94
  "load_label_mapping",
90
95
  "load_mapping",
96
+ "load_ncbi",
97
+ "load_uniprot",
98
+ "load_vgnc",
99
+ "load_wikidata",
91
100
  "load_xref_mapping",
92
101
  "resolve_ids",
93
102
  "resolve_labels",
@@ -244,8 +253,7 @@ def generate_hgnc_primary_ids(
244
253
  Only the HGNC complete set file is downloaded/read. The returned mapping
245
254
  set has an empty ``mappings`` list; its ``_primary_ids`` store is
246
255
  populated with every current HGNC ID so that ``to_pri_ids()`` produces
247
- the authoritative complete list, not just the subset of primaries that
248
- happen to have an associated secondary.
256
+ the complete list.
249
257
 
250
258
  Args:
251
259
  input_path: Local HGNC complete set TSV. Auto-downloaded if ``None``.
@@ -1112,9 +1120,7 @@ def generate_ensembl_label_history(
1112
1120
  previous-symbol -> current-symbol transitions are derived by diffing
1113
1121
  each release's primary-label snapshot per stable ID. Delegates to
1114
1122
  :func:`pysec2pri.consolidate.build_label_history`, which walks every
1115
- historical release (or a bounded range) and resumes on re-run. This is
1116
- a network-heavy, run-on-demand operation, not part of normal
1117
- single-release generation.
1123
+ historical release (or a bounded range) and resumes on re-run.
1118
1124
 
1119
1125
  Args:
1120
1126
  species: Canonical NCBI taxon ID (default ``9606`` for human).
@@ -1506,95 +1512,139 @@ def write_diff_output(
1506
1512
 
1507
1513
 
1508
1514
  def load_mapping(path: Path | str) -> IdMappingSet:
1509
- """Load an ID mapping set from a pysec2pri TSV file.
1515
+ """Load an ID mapping set from an SSSOM TSV file.
1510
1516
 
1511
- Accepts the ``sec2pri`` TSV format (columns ``subject_id``, ``object_id``,
1512
- ``predicate_id``, ``mapping_cardinality``) and the full SSSOM TSV format
1513
- (comment-prefixed metadata lines are skipped automatically).
1517
+ Produces the same :class:`~pysec2pri.parsers.base.IdMappingSet` a fresh
1518
+ parse would, ready to pass to :func:`resolve_ids`.
1514
1519
 
1515
1520
  Args:
1516
- path: Path to the TSV file to load.
1517
-
1518
- Returns:
1519
- An :class:`~pysec2pri.parsers.base.IdMappingSet` populated from the
1520
- file, ready to pass to :func:`resolve_ids`.
1521
+ path: Path to the SSSOM TSV file to load.
1521
1522
  """
1522
- import pandas as pd
1523
- from sssom_schema import Mapping
1523
+ from mapkgsutils.exports import read_sssom
1524
1524
 
1525
- path = Path(path)
1526
- df = pd.read_csv(path, sep="\t", dtype=str, comment="#")
1527
- ms = IdMappingSet(
1528
- mapping_set_id=str(path),
1529
- license="https://creativecommons.org/licenses/by/4.0/",
1530
- )
1531
- mappings: list[Mapping] = []
1532
- for _, row in df.iterrows():
1533
- m = Mapping(
1534
- subject_id=row.get("subject_id") or "",
1535
- object_id=row.get("object_id") or "",
1536
- predicate_id=row.get("predicate_id") or "",
1537
- mapping_justification=row.get("mapping_justification")
1538
- or "semapv:BackgroundKnowledgeBasedMatching",
1539
- mapping_cardinality=row.get("mapping_cardinality") or None,
1540
- )
1541
- mappings.append(m)
1542
- ms.mappings = mappings
1543
- return ms
1525
+ return read_sssom(path, mapping_set_class=IdMappingSet, on="id")
1544
1526
 
1545
1527
 
1546
1528
  def load_label_mapping(path: Path | str) -> LabelMappingSet:
1547
- """Load a label/label mapping set from a pysec2pri TSV file.
1529
+ """Load a label mapping set from an SSSOM TSV file.
1530
+
1531
+ Produces the same :class:`~pysec2pri.parsers.base.LabelMappingSet` a fresh
1532
+ parse would, ready to pass to :func:`resolve_labels`.
1533
+
1534
+ Args:
1535
+ path: Path to the SSSOM TSV file to load.
1536
+ """
1537
+ from mapkgsutils.exports import read_sssom
1548
1538
 
1549
- Accepts two column-name conventions:
1539
+ return read_sssom(path, mapping_set_class=LabelMappingSet, on="label")
1550
1540
 
1551
- - **New** (``label_sec2pri`` tabular output): ``secondary_id``,
1552
- ``secondary_label``, ``primary_id``, ``primary_label``,
1553
- ``predicate_id``, ``mapping_cardinality``.
1554
- - **Legacy** (SSSOM or old tabular output): ``subject_id``,
1555
- ``subject_label``, ``object_id``, ``object_label``, ``predicate_id``.
1556
1541
 
1557
- Full SSSOM TSV (comment-prefixed metadata lines) is also accepted.
1542
+ def load_chebi(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1543
+ """Load a ChEBI SSSOM file into the proper mapping set object.
1558
1544
 
1559
1545
  Args:
1560
- path: Path to the TSV file to load.
1546
+ path: Path to the SSSOM TSV file.
1547
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1548
+ """
1549
+ from pysec2pri.parsers import ChEBIParser
1561
1550
 
1562
- Returns:
1563
- A :class:`~pysec2pri.parsers.base.LabelMappingSet` populated from
1564
- the file, ready to pass to :func:`resolve_labels`.
1551
+ return ChEBIParser().load(path, mapping_type=mapping_type)
1552
+
1553
+
1554
+ def load_ensembl(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1555
+ """Load an Ensembl SSSOM file into the proper mapping set object.
1556
+
1557
+ Args:
1558
+ path: Path to the SSSOM TSV file.
1559
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1565
1560
  """
1566
- import pandas as pd
1567
- from sssom_schema import Mapping
1561
+ from pysec2pri.parsers import EnsemblParser
1568
1562
 
1569
- path = Path(path)
1570
- df = pd.read_csv(path, sep="\t", dtype=str, comment="#")
1571
- ms = LabelMappingSet(
1572
- mapping_set_id=str(path),
1573
- license="https://creativecommons.org/licenses/by/4.0/",
1574
- )
1563
+ return EnsemblParser().load(path, mapping_type=mapping_type)
1575
1564
 
1576
- def _col(row: pd.Series, *names: str) -> str | None:
1577
- for name in names:
1578
- val = row.get(name)
1579
- if val and str(val).strip():
1580
- return str(val).strip()
1581
- return None
1582
-
1583
- mappings: list[Mapping] = []
1584
- for _, row in df.iterrows():
1585
- m = Mapping(
1586
- subject_id=_col(row, "secondary_id", "subject_id") or "",
1587
- subject_label=_col(row, "secondary_label", "subject_label"),
1588
- object_id=_col(row, "primary_id", "object_id") or "",
1589
- object_label=_col(row, "primary_label", "object_label"),
1590
- predicate_id=_col(row, "predicate_id") or "",
1591
- mapping_justification=_col(row, "mapping_justification")
1592
- or "semapv:BackgroundKnowledgeBasedMatching",
1593
- mapping_cardinality=_col(row, "mapping_cardinality"),
1594
- )
1595
- mappings.append(m)
1596
- ms.mappings = mappings
1597
- return ms
1565
+
1566
+ def load_hgnc(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1567
+ """Load an HGNC SSSOM file into the proper mapping set object.
1568
+
1569
+ Args:
1570
+ path: Path to the SSSOM TSV file.
1571
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1572
+ """
1573
+ from pysec2pri.parsers import HGNCParser
1574
+
1575
+ return HGNCParser().load(path, mapping_type=mapping_type)
1576
+
1577
+
1578
+ def load_ncbi(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1579
+ """Load an NCBI SSSOM file into the proper mapping set object.
1580
+
1581
+ Args:
1582
+ path: Path to the SSSOM TSV file.
1583
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1584
+ """
1585
+ from pysec2pri.parsers import NCBIParser
1586
+
1587
+ return NCBIParser().load(path, mapping_type=mapping_type)
1588
+
1589
+
1590
+ def load_uniprot(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1591
+ """Load a UniProt SSSOM file into the proper mapping set object.
1592
+
1593
+ Args:
1594
+ path: Path to the SSSOM TSV file.
1595
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1596
+ """
1597
+ from pysec2pri.parsers import UniProtParser
1598
+
1599
+ return UniProtParser().load(path, mapping_type=mapping_type)
1600
+
1601
+
1602
+ def load_vgnc(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1603
+ """Load a VGNC SSSOM file into the proper mapping set object.
1604
+
1605
+ Args:
1606
+ path: Path to the SSSOM TSV file.
1607
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1608
+ """
1609
+ from pysec2pri.parsers import VGNCParser
1610
+
1611
+ return VGNCParser().load(path, mapping_type=mapping_type)
1612
+
1613
+
1614
+ def load_hmdb(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1615
+ """Load an HMDB metabolite SSSOM file into the proper mapping set object.
1616
+
1617
+ Args:
1618
+ path: Path to the SSSOM TSV file.
1619
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1620
+ """
1621
+ from pysec2pri.parsers import HMDBMetaboliteParser
1622
+
1623
+ return HMDBMetaboliteParser().load(path, mapping_type=mapping_type)
1624
+
1625
+
1626
+ def load_hmdb_proteins(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1627
+ """Load an HMDB protein SSSOM file into the proper mapping set object.
1628
+
1629
+ Args:
1630
+ path: Path to the SSSOM TSV file.
1631
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1632
+ """
1633
+ from pysec2pri.parsers import HMDBProteinParser
1634
+
1635
+ return HMDBProteinParser().load(path, mapping_type=mapping_type)
1636
+
1637
+
1638
+ def load_wikidata(path: Path | str, *, mapping_type: str | None = None) -> BaseMappingSet:
1639
+ """Load a Wikidata SSSOM file into the proper mapping set object.
1640
+
1641
+ Args:
1642
+ path: Path to the SSSOM TSV file.
1643
+ mapping_type: ``"id"``/``"label"`` to force the class, else inferred.
1644
+ """
1645
+ from pysec2pri.parsers import WikidataParser
1646
+
1647
+ return WikidataParser().load(path, mapping_type=mapping_type)
1598
1648
 
1599
1649
 
1600
1650
  def resolve_ids(
@@ -2015,15 +2065,10 @@ def crosswalk(
2015
2065
  ) -> dict[str, str] | pd.DataFrame:
2016
2066
  r"""Map a gene identifier from one vocabulary to another, via HGNC.
2017
2067
 
2018
- A thin convenience wrapper, not a separate resolver: ``frm="symbol"``
2019
- resolves through :func:`generate_hgnc_labels` +
2068
+ Wrapper: ``frm="symbol"`` resolves through :func:`generate_hgnc_labels` +
2020
2069
  :func:`~pysec2pri.update_ids.update_labels`, so a *previous* HGNC symbol
2021
- still resolves to its current identity (the temporal aspect) and a
2022
- genuinely ambiguous symbol is left blank and reported rather than
2023
- guessed. ``frm`` in ``"ensembl"``, ``"entrez"``, ``"refseq"``, or
2024
- ``"uniprot"`` resolves through HGNC's own cross-reference crosswalk
2025
- table, downloaded from the ``xref_sources`` declared in
2026
- ``config/hgnc.yaml`` unless *xref_mapping* is supplied explicitly.
2070
+ still resolves to its current identity (the temporal aspect) and an ambiguous
2071
+ label is left blank and reported rather than guessed.
2027
2072
 
2028
2073
  Args:
2029
2074
  input_data: A single identifier string, a list of identifier
@@ -638,7 +638,6 @@ def _make_consolidate_cmd(cfg_id: str, extra_opts: list[Callable[..., Any]]) ->
638
638
  """Return a decorated (but not yet click.command-wrapped) ``consolidate`` callable."""
639
639
 
640
640
  def _cmd(
641
- mode: str,
642
641
  mapping_sets: str,
643
642
  cache_dir: Path | None,
644
643
  force: bool,
@@ -651,12 +650,11 @@ def _make_consolidate_cmd(cfg_id: str, extra_opts: list[Callable[..., Any]]) ->
651
650
  extras = ", ".join(f"{k}={v}" for k, v in extra_kwargs.items() if v is not None)
652
651
  click.echo(
653
652
  f"Consolidating {cfg_id.upper()} mapping dates "
654
- f"(mode={mode}{', ' + extras if extras else ''}, {mapping_sets})..."
653
+ f"({extras + ', ' if extras else ''}{mapping_sets})..."
655
654
  )
656
655
  try:
657
656
  path, _ = consolidate_mapping_dates(
658
657
  cfg_id,
659
- mode=mode,
660
658
  cache_dir=cache_dir,
661
659
  mapping_sets=mapping_sets,
662
660
  show_progress=not no_progress,
@@ -669,17 +667,6 @@ def _make_consolidate_cmd(cfg_id: str, extra_opts: list[Callable[..., Any]]) ->
669
667
  click.echo(f"Wrote consolidated mapping set -> {output or _sssom_output_path(path)}")
670
668
 
671
669
  decorators: list[Callable[..., Any]] = [
672
- click.option(
673
- "--mode",
674
- default="release",
675
- show_default=True,
676
- type=click.Choice(["release", "date"]),
677
- help=(
678
- "'release': walk every historical release for the first-seen version/date. "
679
- "'date': single pass using the source's own per-row date (falls back to "
680
- "'release' with a warning if the source has none)."
681
- ),
682
- ),
683
670
  *extra_opts,
684
671
  click.option(
685
672
  "--mapping-sets",
@@ -727,12 +714,13 @@ def _register_consolidate_commands(parent: click.Group) -> None:
727
714
  "Build/update the per-mapping first-seen-date index, written as a real "
728
715
  "SSSOM mapping set whose mapping_date is each mapping's date of first "
729
716
  "appearance.\n\n"
730
- "In 'release' mode (default), walks every historical release once to "
731
- "discover the earliest release each mapping appeared in. This is slow "
732
- "and network-heavy (~250 releases for ChEBI); meant to be run as a "
733
- "manual/one-off operation, not as part of normal mapping generation. "
734
- "In 'date' mode, a single current-release pass captures the source's "
735
- "own per-row date directly (fast)."
717
+ "For datasources with a versioned archive (ChEBI, Ensembl, HGNC, "
718
+ "UniProt), walks every historical release once to discover the earliest "
719
+ "release each mapping appeared in. This is slow and network-heavy (~250 "
720
+ "releases for ChEBI); meant to be run as a manual/one-off operation, not "
721
+ "as part of normal mapping generation. For datasources without one "
722
+ "(NCBI, VGNC), a single current-release pass captures the source's own "
723
+ "per-row date directly (fast)."
736
724
  ),
737
725
  )(raw_cmd)
738
726
  )
@@ -504,5 +504,4 @@ xref_sources:
504
504
  refseq: "RefSeq IDs"
505
505
  uniprot: "UniProt ID(supplied by UniProt)"
506
506
  note:
507
- "Source of truth for cross-references varies; this is a suggested default,
508
- not authoritative."
507
+ "Source of truth for cross-references varies; this is a suggested default."
@@ -142,8 +142,7 @@ xref_sources:
142
142
  refseq: "RefSeq IDs"
143
143
  uniprot: "UniProt ID(supplied by UniProt)"
144
144
  note:
145
- "Source of truth for cross-references varies; this is a suggested default,
146
- not authoritative."
145
+ "Source of truth for cross-references varies; this is a suggested default"
147
146
 
148
147
  mapping_sets:
149
148
  ids:
@@ -165,5 +165,4 @@ xref_sources:
165
165
  refseq: "RefSeq IDs"
166
166
  uniprot: "UniProt ID(supplied by UniProt)"
167
167
  note:
168
- "Source of truth for cross-references varies; this is a suggested default,
169
- not authoritative."
168
+ "Source of truth for cross-references varies; this is a suggested default"
@@ -40,7 +40,6 @@ import json
40
40
  import os
41
41
  import shutil
42
42
  import tempfile
43
- import warnings
44
43
  from collections.abc import Callable, Iterable
45
44
  from pathlib import Path
46
45
  from typing import Any
@@ -60,7 +59,7 @@ __all__ = [
60
59
  ]
61
60
 
62
61
  # Datasources this module knows how to download+parse per release.
63
- # NCBI and VGNC have no versioned archive, so only mode="date" applies to them.
62
+ # NCBI and VGNC have no versioned archive, so they take the single-parse path.
64
63
  SUPPORTED_DATASOURCES = ("chebi", "ensembl", "hgnc", "ncbi", "uniprot", "vgnc")
65
64
 
66
65
  # mapping_sets kinds each datasource's parser actually supports.
@@ -100,7 +99,8 @@ def _uniprot_versions(**kwargs: Any) -> list[str]:
100
99
  return UniProtDownloader().list_versions()
101
100
 
102
101
 
103
- # Datasources with a versioned archive that can be walked for mode="release".
102
+ # Datasources with a versioned archive that can be walked release by release.
103
+ # Datasources absent here (NCBI, VGNC) take the single-parse path instead.
104
104
  _LIST_VERSIONS_FNS: dict[str, Callable[..., list[str]]] = {
105
105
  "chebi": _chebi_versions,
106
106
  "ensembl": _ensembl_versions,
@@ -167,53 +167,6 @@ def _save_optional_output(mapping_set: BaseMappingSet, output: Path | None) -> N
167
167
  mapping_set.save("sssom", output)
168
168
 
169
169
 
170
- def _consolidate_by_date_full(
171
- cache_path: Path,
172
- meta_path: Path,
173
- *,
174
- run_one_version: Callable[[], Any],
175
- ) -> bool:
176
- """Date mode that caches the *full* mapping set, not just dated rows.
177
-
178
- :func:`mapkgsutils.consolidate.consolidate_by_date` keeps only rows that
179
- carry a real per-row ``mapping_date`` and drops the rest, so a single
180
- parse of e.g. HGNC labels would cache only its ~12k dated previous-symbol
181
- mappings and lose the ~48k alias mappings. This builds a complete cache
182
- from one (current) parse: every mapping is kept, stamped with its own
183
- per-row date when present and left undated otherwise (exactly how the
184
- release walk treats an unresolvable date).
185
-
186
- Returns ``True`` (cache written) when the parse yields at least one real
187
- per-row date, and ``False`` (cache untouched) when it yields none -- so
188
- date-less sources (ChEBI, UniProt) still fall back to a release walk
189
- rather than caching a fully undated set.
190
- """
191
- mapping_set = run_one_version()
192
- mappings = mapping_set.mappings or []
193
- if not any(getattr(m, "mapping_date", None) for m in mappings):
194
- return False
195
-
196
- version_label = str(getattr(mapping_set, "mapping_set_version", None) or "current")
197
- records: dict[str, dict[str, str]] = {}
198
- for m in mappings:
199
- # record_id is release-scoped; its trailing 16 hex chars are the
200
- # version-independent pair hash (see mapkgsutils.parsers.base.pair_hash).
201
- pair_key = str(getattr(m, "record_id", None) or "")[-16:]
202
- if not pair_key:
203
- continue
204
- date_str = str(m.mapping_date) if getattr(m, "mapping_date", None) else ""
205
- records[pair_key] = {
206
- "first_seen_version": version_label,
207
- "first_seen_date": date_str,
208
- "last_seen_version": version_label,
209
- "last_seen_date": date_str,
210
- "fields_json": _consolidate._mapping_fields_json(m),
211
- }
212
- _write_cache(cache_path, records)
213
- _write_meta(meta_path, version_label)
214
- return True
215
-
216
-
217
170
  def load_mapping_dates(
218
171
  datasource: str,
219
172
  cache_dir: Path | None = None,
@@ -435,7 +388,6 @@ def _write_consolidated_sssom(
435
388
  def consolidate_mapping_dates(
436
389
  datasource: str,
437
390
  *,
438
- mode: str = "release",
439
391
  cache_dir: Path | None = None,
440
392
  mapping_sets: str = "ids",
441
393
  show_progress: bool = True,
@@ -445,40 +397,35 @@ def consolidate_mapping_dates(
445
397
  ) -> tuple[Path, BaseMappingSet]:
446
398
  """Build/update the first-seen-date index for *datasource*.
447
399
 
448
- Two strategies, selected via *mode*:
449
-
450
- - ``"release"``: walk every historical release of *datasource* once
451
- (oldest first, resuming from the last completed version unless
452
- *force* is set). For every mapping seen, records the version/date it
453
- first appeared and keeps bumping the version/date it was last seen.
454
- This is a slow, network-heavy operation (e.g. ~250 releases for ChEBI) and
455
- is meant to be run manually/as a one-off, not as part of normal
456
- mapping generation. Requires a versioned archive (ChEBI, HGNC,
457
- UniProt); raises :class:`ValueError` for datasources without one
458
- (e.g. NCBI).
459
- - ``"date"``: a single (current) parse, caching the *full* mapping set
460
- with whatever real per-row ``mapping_date`` the datasource's parser
461
- already produces (e.g. HGNC's ``date_symbol_changed``, NCBI's
462
- ``Discontinue_Date``); rows without such a date are still cached, just
463
- left undated. Fast -- no historical walk. If *datasource* never
464
- produces a real per-row date at all (ChEBI, UniProt), falls back to
465
- ``"release"`` mode and warns the user.
466
-
467
- Either way, alongside the internal cache file this also writes a
468
- companion real SSSOM mapping set (see :func:`_sssom_output_path`) where
469
- every row's ``mapping_date`` is its true first-seen date, rather than
470
- whichever release happened to be parsed last.
400
+ Collects every mapping using all available provenance. The walk shape is
401
+ chosen automatically by data availability, not by a requested mode:
402
+
403
+ - Datasources **with** a versioned archive (ChEBI, Ensembl, HGNC,
404
+ UniProt): walk every historical release once (oldest first, resuming
405
+ from the last completed version unless *force*). For every mapping
406
+ seen, records the version/date it first appeared and keeps bumping the
407
+ version/date it was last seen. Slow and network-heavy (~250 releases
408
+ for ChEBI); meant to be run manually/as a one-off.
409
+ - Datasources **without** one (NCBI, VGNC): a single current parse
410
+ caching the *full* mapping set — every mapping kept, stamped with its
411
+ own per-row ``mapping_date`` when the parser provides one and left
412
+ undated otherwise. Fast, no historical walk.
413
+
414
+ Alongside the internal cache file this also writes a companion real SSSOM
415
+ mapping set (see :func:`_sssom_output_path`) where each row's
416
+ ``mapping_date`` is its per-row date when present, else its first-seen
417
+ release date distinct from the first-seen release version carried in
418
+ ``subject_source_version``/``object_source_version``.
471
419
 
472
420
  Args:
473
421
  datasource: One of :data:`SUPPORTED_DATASOURCES`.
474
- mode: ``"release"`` or ``"date"``.
475
422
  cache_dir: Directory to write the cache file. Defaults to
476
423
  :func:`default_cache_dir`.
477
424
  mapping_sets: ``"ids"`` or ``"labels"``.
478
425
  show_progress: Whether to show a progress bar over releases
479
- (``"release"`` mode only).
426
+ (versioned-archive datasources only).
480
427
  force: Re-scan every release from scratch, ignoring any existing
481
- cache/resume state (``"release"`` mode only).
428
+ cache/resume state (versioned-archive datasources only).
482
429
  output: Optional path to also write the consolidated SSSOM mapping
483
430
  set to. The internal cache-adjacent copy (see
484
431
  :func:`_sssom_output_path`) is always written; when *output* is
@@ -493,13 +440,9 @@ def consolidate_mapping_dates(
493
440
  written alongside it) and the in-memory consolidated mapping set.
494
441
 
495
442
  Raises:
496
- ValueError: For an unknown *mode*, an unsupported *datasource*, an
497
- unsupported *mapping_sets* for *datasource*, or
498
- ``mode="release"`` against a datasource with no versioned
499
- archive.
443
+ ValueError: For an unsupported *datasource* or an unsupported
444
+ *mapping_sets* for *datasource*.
500
445
  """
501
- if mode not in ("release", "date"):
502
- raise ValueError(f"mode must be 'release' or 'date', got {mode!r}")
503
446
  if datasource not in SUPPORTED_DATASOURCES:
504
447
  raise ValueError(
505
448
  f"Unsupported datasource: {datasource!r}. Supported: {SUPPORTED_DATASOURCES}"
@@ -524,41 +467,36 @@ def consolidate_mapping_dates(
524
467
  cache_path = _cache_path(cache_dir, datasource, mapping_sets, **kwargs)
525
468
  meta_path = _meta_path(cache_dir, datasource, mapping_sets, **kwargs)
526
469
 
527
- if mode == "date":
528
- got_dates = _consolidate_by_date_full(
529
- cache_path,
530
- meta_path,
531
- run_one_version=lambda: _run_one_version(datasource, None, mapping_sets, **kwargs),
532
- )
533
- if got_dates:
534
- _, mapping_set = _write_consolidated_sssom(
535
- datasource, mapping_sets, cache_path, meta_path
536
- )
537
- _save_optional_output(mapping_set, output)
538
- return cache_path, mapping_set
539
- msg = (
540
- f"{datasource!r} has no parseable per-row mapping dates; "
541
- "falling back to mode='release'."
542
- )
543
- logger.warning(msg)
544
- warnings.warn(msg, UserWarning, stacklevel=2)
470
+ def _run(version: str | None) -> Any:
471
+ return _run_one_version(datasource, version, mapping_sets, **kwargs)
545
472
 
546
473
  list_versions_fn = _LIST_VERSIONS_FNS.get(datasource)
547
474
  if list_versions_fn is None:
548
- raise ValueError(f"{datasource!r} has no versioned archive; only mode='date' is supported.")
475
+ # No versioned archive (NCBI, VGNC): single current parse, full set.
476
+ _consolidate.consolidate(
477
+ cache_path,
478
+ meta_path,
479
+ label=datasource,
480
+ run_one_version=_run,
481
+ show_progress=show_progress,
482
+ force=force,
483
+ )
484
+ else:
485
+ # Versioned archive: walk every historical release for first-seen dates.
486
+ from pysec2pri.download import resolve_release_date
549
487
 
550
- from pysec2pri.download import resolve_release_date
488
+ fn = list_versions_fn
489
+ _consolidate.consolidate(
490
+ cache_path,
491
+ meta_path,
492
+ label=datasource,
493
+ run_one_version=_run,
494
+ list_versions=lambda: fn(**kwargs),
495
+ resolve_release_date=lambda v: resolve_release_date(datasource, v, **kwargs),
496
+ show_progress=show_progress,
497
+ force=force,
498
+ )
551
499
 
552
- _consolidate.consolidate_by_release(
553
- cache_path,
554
- meta_path,
555
- label=datasource,
556
- list_versions=lambda: list_versions_fn(**kwargs),
557
- run_one_version=lambda v: _run_one_version(datasource, v, mapping_sets, **kwargs),
558
- resolve_release_date=lambda v: resolve_release_date(datasource, v, **kwargs),
559
- show_progress=show_progress,
560
- force=force,
561
- )
562
500
  _, mapping_set = _write_consolidated_sssom(datasource, mapping_sets, cache_path, meta_path)
563
501
  _save_optional_output(mapping_set, output)
564
502
  return cache_path, mapping_set
@@ -632,8 +570,8 @@ def build_label_history(
632
570
  previous release's, oldest to newest. Network-heavy and resumable --
633
571
  meant to be run on demand, not as part of normal mapping generation
634
572
  (mirrors the release-walk pattern in
635
- :func:`mapkgsutils.consolidate.consolidate_by_release`, but tracks a
636
- carried label map instead of first/last-seen dates).
573
+ :func:`mapkgsutils.consolidate.consolidate`, but tracks a carried label
574
+ map instead of first/last-seen dates).
637
575
 
638
576
  Args:
639
577
  datasource: Currently only ``"ensembl"`` is supported.
@@ -709,7 +647,7 @@ def build_label_history(
709
647
  shutil.rmtree(tmpdir, ignore_errors=True)
710
648
 
711
649
  release_date = resolve_release_date(datasource, v, species=species)
712
- # Unlike consolidate_by_release's plain-dict cache, these rows become
650
+ # Unlike the consolidate walk's plain-dict cache, these rows become
713
651
  # real Mapping objects -- mapping_date must be a valid date or None,
714
652
  # never the raw (non-date-shaped) version string.
715
653
  date_str = release_date.date().isoformat() if release_date else None
@@ -375,8 +375,16 @@ def urls_and_date(
375
375
  urls = _get_ensembl_urls_for_version(version, species=species)
376
376
  logger.info("Ensembl version %s (species %s): %s", version, species, urls)
377
377
  else:
378
- release_info = check_ensembl_release()
379
- urls = release_info.files
380
- release_date = release_info.release_date
381
- logger.info("Ensembl release %s: %s", release_info.version, urls)
378
+ # The release number is global across species, but the URLs (and the
379
+ # resolved release date) are species-specific, so resolve the latest
380
+ # version and build URLs for the requested species rather than reusing
381
+ # check_ensembl_release()'s human-only URLs.
382
+ versions = EnsemblDownloader(show_progress=False).list_versions()
383
+ if not versions:
384
+ raise ValueError("Could not find Ensembl releases on the FTP server")
385
+ version = versions[-1]
386
+ urls = _get_ensembl_urls_for_version(version, species=species)
387
+ check_url = urls.get("stable_id_event")
388
+ release_date = get_file_last_modified(check_url) if check_url else None
389
+ logger.info("Ensembl release %s (species %s): %s", version, species, urls)
382
390
  return urls, release_date
@@ -190,8 +190,7 @@ class ChEBIParser(BaseParser):
190
190
  Reads ``compounds.tsv`` (TSV releases >= 245) to extract every current
191
191
  ChEBI compound ID. The returned mapping set has an empty ``mappings``
192
192
  list; its ``_primary_ids`` store is populated with every current ChEBI
193
- ID (CHEBI: prefixed) so that ``to_pri_ids()`` produces the authoritative
194
- complete list.
193
+ ID (CHEBI: prefixed) so that ``to_pri_ids()`` produces the complete list.
195
194
 
196
195
  Args:
197
196
  input_path: Path to a directory containing ``compounds.tsv``, or
@@ -496,7 +495,7 @@ def _parse_names_tsv(
496
495
 
497
496
  Format: id|compound_id|name|type|status_id|adapted|language_code|ascii_name
498
497
 
499
- The authoritative primary (canonical) name for each compound is taken from
498
+ The true primary (canonical) name for each compound is taken from
500
499
  the ``name`` column of ``compounds.tsv``: the same value displayed on the
501
500
  ChEBI website and stored as the top-level label in the ChEBI ontology.
502
501
  Every entry in ``names.tsv`` that differs from that canonical name is
@@ -526,7 +525,7 @@ def _parse_names_tsv(
526
525
  "Pass the path to compounds.tsv."
527
526
  )
528
527
 
529
- # Load canonical names from compounds.tsv (ChEBI-authoritative primary label)
528
+ # Load canonical names from compounds.tsv (ChEBI primary label)
530
529
  cpd_cols = ["id", "name", "stars"] if subset == "3star" else ["id", "name"]
531
530
  cpd_df = pl.read_csv(
532
531
  compounds_path,
@@ -25,9 +25,8 @@ This parser extracts:
25
25
  detection and ``to_pri_labels()`` stay scoped to one species' namespace
26
26
  instead of flagging cross-species homonyms as ambiguous. Passing
27
27
  :data:`ALL_SPECIES` processes every species together instead, in which
28
- case a shared symbol *is* genuinely ambiguous (there's no other context
29
- to tell the species apart) and gets flagged accordingly -- that's
30
- ``_annotate_ambiguous_mappings`` doing its job, not a bug.
28
+ case a shared symbol is really ambiguous (there's no other context
29
+ to tell the species apart) and gets flagged accordingly.
31
30
 
32
31
  Uses SSSOM-compliant MappingSet classes with cardinality computation.
33
32
  """
@@ -12,7 +12,7 @@ __all__ = [
12
12
  "get_version",
13
13
  ]
14
14
 
15
- VERSION = "0.2.2"
15
+ VERSION = "0.2.4"
16
16
 
17
17
 
18
18
  def get_git_hash() -> str:
File without changes