pysec2pri 0.2.2__tar.gz → 0.2.3__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.3}/PKG-INFO +3 -3
  2. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/README.md +1 -1
  3. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/pyproject.toml +3 -3
  4. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/api.py +5 -13
  5. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/cli.py +8 -20
  6. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/ensembl.yaml +1 -2
  7. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/hgnc.yaml +1 -2
  8. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/ncbi.yaml +1 -2
  9. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/consolidate.py +54 -116
  10. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/downloads/ensembl.py +12 -4
  11. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/chebi.py +3 -4
  12. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/vgnc.py +2 -3
  13. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/version.py +1 -1
  14. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/LICENSE +0 -0
  15. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/__init__.py +0 -0
  16. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/__main__.py +0 -0
  17. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/__init__.py +0 -0
  18. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/chebi.yaml +0 -0
  19. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/hmdb_metabolites.yaml +0 -0
  20. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/hmdb_proteins.yaml +0 -0
  21. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/uniprot.yaml +0 -0
  22. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/vgnc.yaml +0 -0
  23. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/config/wikidata.yaml +0 -0
  24. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/constants.py +0 -0
  25. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/download.py +0 -0
  26. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/downloads/__init__.py +0 -0
  27. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/downloads/chebi.py +0 -0
  28. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/downloads/hgnc.py +0 -0
  29. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/downloads/hmdb.py +0 -0
  30. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/downloads/ncbi.py +0 -0
  31. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/downloads/uniprot.py +0 -0
  32. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/exports.py +0 -0
  33. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/logging.py +0 -0
  34. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/__init__.py +0 -0
  35. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/base.py +0 -0
  36. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/ensembl.py +0 -0
  37. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/hgnc.py +0 -0
  38. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/hmdb.py +0 -0
  39. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/ncbi.py +0 -0
  40. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/uniprot.py +0 -0
  41. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/parsers/wikidata.py +0 -0
  42. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/py.typed +0 -0
  43. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/queries/__init__.py +0 -0
  44. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/queries/chemical_redirects.rq +0 -0
  45. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/queries/chemical_redirects_test.rq +0 -0
  46. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/queries/gene_redirects.rq +0 -0
  47. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/queries/gene_redirects_test.rq +0 -0
  48. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/queries/protein_redirects.rq +0 -0
  49. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/queries/protein_redirects_test.rq +0 -0
  50. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/src/pysec2pri/update_ids.py +0 -0
  51. {pysec2pri-0.2.2 → pysec2pri-0.2.3}/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.3
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.1
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
@@ -234,7 +234,7 @@ mappings.
234
234
  ### Aliases / synonyms
235
235
 
236
236
  Alias mappings use `oboInOwl:hasExactSynonym`. The alias is the `subject_label`
237
- and the authoritative name is the `object_label`/`object_id`.
237
+ and the primary name is the `object_label`/`object_id`.
238
238
 
239
239
  ```mermaid
240
240
  flowchart LR
@@ -181,7 +181,7 @@ mappings.
181
181
  ### Aliases / synonyms
182
182
 
183
183
  Alias mappings use `oboInOwl:hasExactSynonym`. The alias is the `subject_label`
184
- and the authoritative name is the `object_label`/`object_id`.
184
+ and the primary name is the `object_label`/`object_id`.
185
185
 
186
186
  ```mermaid
187
187
  flowchart LR
@@ -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.3"
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.1",
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.3"
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}",
@@ -244,8 +244,7 @@ def generate_hgnc_primary_ids(
244
244
  Only the HGNC complete set file is downloaded/read. The returned mapping
245
245
  set has an empty ``mappings`` list; its ``_primary_ids`` store is
246
246
  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.
247
+ the complete list.
249
248
 
250
249
  Args:
251
250
  input_path: Local HGNC complete set TSV. Auto-downloaded if ``None``.
@@ -1112,9 +1111,7 @@ def generate_ensembl_label_history(
1112
1111
  previous-symbol -> current-symbol transitions are derived by diffing
1113
1112
  each release's primary-label snapshot per stable ID. Delegates to
1114
1113
  :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.
1114
+ historical release (or a bounded range) and resumes on re-run.
1118
1115
 
1119
1116
  Args:
1120
1117
  species: Canonical NCBI taxon ID (default ``9606`` for human).
@@ -2015,15 +2012,10 @@ def crosswalk(
2015
2012
  ) -> dict[str, str] | pd.DataFrame:
2016
2013
  r"""Map a gene identifier from one vocabulary to another, via HGNC.
2017
2014
 
2018
- A thin convenience wrapper, not a separate resolver: ``frm="symbol"``
2019
- resolves through :func:`generate_hgnc_labels` +
2015
+ Wrapper: ``frm="symbol"`` resolves through :func:`generate_hgnc_labels` +
2020
2016
  :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.
2017
+ still resolves to its current identity (the temporal aspect) and an ambiguous
2018
+ label is left blank and reported rather than guessed.
2027
2019
 
2028
2020
  Args:
2029
2021
  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.3"
16
16
 
17
17
 
18
18
  def get_git_hash() -> str:
File without changes