Source code for gain.genomic_resources.statistics.region_fold

"""The scan-side fold that turns a kind's regions into one statistic.

Not part of ``base_statistic`` because this fold reports through
``cli_errors``, and the line that draws is a real one: ``base_statistic``
says what a statistic IS, and stays free of anything CLI-facing.
"""
from __future__ import annotations

from collections.abc import Callable, Iterable
from typing import Any

from gain.genomic_resources.cli_errors import report_resource_failure
from gain.genomic_resources.statistics.base_statistic import (
    MergeableRegion,
    RegionFoldedStatistic,
    regions_in_genomic_order,
)


# `S` is not tied to the regions' type: the bound that would say so,
# `S: RegionFoldedStatistic[R]`, refers to an earlier type parameter and
# mypy 1.15 rejects it (`Name "R" is not defined`).  `Any` states that
# looseness instead of hiding it behind a bare `RegionFoldedStatistic`.
# Handing coverage regions to an `AlleleStatistics` therefore type
# checks; `RegionFoldedStatistic.merge`'s concrete-type gate is what
# refuses it at runtime.
[docs] def merge_regions[S: RegionFoldedStatistic[Any]]( resource_id: str, regions: Iterable[MergeableRegion | None], # `Callable[[], S]` rather than `type[S]`: the concrete statistics # take no constructor arguments, but the BOUND's `__init__` takes an # id and a description, and `type[S]` is checked against the bound. build: Callable[[], S], what: str, ) -> S | None: """Fold one kind's scanned regions into a single statistic. ``None`` for a kind that contributed no regions at all -- the caller writes no file for it, which is how "this resource has no such statistic" is said. A kind that produced nothing on SOME task is a ``None`` among the regions and is simply dropped. ``what`` names the statistic in the failure line, and is the same string :func:`~.base_statistic.refuse_unmergeable` names it by -- the two halves of what an operator reads on one failure. The failure is reported AND re-raised: reporting alone would leave the build writing a half-merged statistic. """ ordered = regions_in_genomic_order(regions) if not ordered: return None statistics = build() try: for region in ordered: statistics.fold_region(region) except ValueError as err: report_resource_failure( err, f"could not merge the {what} of", resource_id) raise return statistics