From 1a8eb0ba84e5d386741823098acf29170f6832ea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:31:55 +0000 Subject: [PATCH 1/2] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.6 → v0.16.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.6...v0.16.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f91485..b8b3c71 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.15.6 + rev: v0.16.2 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From da7a89169be9abe48e420d556175a338f17d9604 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2026 16:32:33 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- setup.py | 2 +- src/iranges/IRanges.py | 93 +++++++++++++++++++------------------- src/iranges/__init__.py | 2 +- src/iranges/irangeslist.py | 23 +++++----- src/iranges/sew_handler.py | 10 ++-- src/iranges/utils.py | 14 +++--- tests/test_coverage.py | 1 + tests/test_interrange.py | 1 + tests/test_intrarange.py | 1 + tests/test_order_ops.py | 1 + tests/test_set_ops.py | 1 + 11 files changed, 75 insertions(+), 74 deletions(-) diff --git a/setup.py b/setup.py index 94d78ce..be7c29b 100644 --- a/setup.py +++ b/setup.py @@ -73,7 +73,7 @@ def build_cmake(self, ext): ext_modules=[CMakeExtension("iranges")], cmdclass={"build_ext": build_ext}, ) - except: # noqa + except: print( "\n\nAn error occurred while building the project, " "please ensure you have the most updated version of setuptools, " diff --git a/src/iranges/IRanges.py b/src/iranges/IRanges.py index 8fcff1d..555e216 100644 --- a/src/iranges/IRanges.py +++ b/src/iranges/IRanges.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Sequence from copy import deepcopy -from typing import Any, Dict, List, Literal, Optional, Sequence, Tuple, Union +from typing import Any, Literal from warnings import warn import biocutils as ut @@ -63,11 +64,11 @@ class IRanges(ut.BiocObject): def __init__( self, - start: Union[np.ndarray, Sequence[int]] = [], - width: Union[np.ndarray, Sequence[int]] = [], - names: Optional[Union[Sequence[str], ut.Names]] = None, - mcols: Optional[BiocFrame] = None, - metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None, + start: np.ndarray | Sequence[int] = [], + width: np.ndarray | Sequence[int] = [], + names: Sequence[str] | ut.Names | None = None, + mcols: BiocFrame | None = None, + metadata: dict[str, Any] | ut.NamedList | None = None, _validate: bool = True, ): """ @@ -151,7 +152,7 @@ def _sanitize_names(self, names): def _validate_names(self): if self._names is None: - return None + return if not isinstance(self._names, ut.Names): raise ValueError("'names' should be a list of strings.") @@ -185,7 +186,7 @@ def get_start(self) -> np.ndarray: """ return self._start - def set_start(self, start: Union[np.ndarray, Sequence[int]], in_place: bool = False) -> IRanges: + def set_start(self, start: np.ndarray | Sequence[int], in_place: bool = False) -> IRanges: """Modify start positions (in-place operation). Args: @@ -222,7 +223,7 @@ def start(self) -> np.ndarray: return self.get_start() @start.setter - def start(self, start: Union[np.ndarray, Sequence[int]]): + def start(self, start: np.ndarray | Sequence[int]): """Modify start positions (in-place operation). Args: @@ -245,7 +246,7 @@ def get_width(self) -> np.ndarray: """ return self._width - def set_width(self, width: Union[np.ndarray, Sequence[int]], in_place: bool = False) -> IRanges: + def set_width(self, width: np.ndarray | Sequence[int], in_place: bool = False) -> IRanges: """ Args: width: @@ -282,7 +283,7 @@ def width(self) -> np.ndarray: return self.get_width() @width.setter - def width(self, width: Union[np.ndarray, Sequence[int]]): + def width(self, width: np.ndarray | Sequence[int]): """Set or modify width of each interval (in-place operation). Args: @@ -323,7 +324,7 @@ def end(self) -> np.ndarray: """ return self.get_end() - def get_names(self) -> Optional[ut.Names]: + def get_names(self) -> ut.Names | None: """Get range names. Returns: @@ -332,7 +333,7 @@ def get_names(self) -> Optional[ut.Names]: """ return self._names - def set_names(self, names: Optional[Union[ut.Names, Sequence[str]]], in_place: bool = False) -> IRanges: + def set_names(self, names: ut.Names | Sequence[str] | None, in_place: bool = False) -> IRanges: """ Args: names: @@ -352,7 +353,7 @@ def set_names(self, names: Optional[Union[ut.Names, Sequence[str]]], in_place: b return output @property - def names(self) -> Optional[ut.Names]: + def names(self) -> ut.Names | None: """Get names. Returns: @@ -362,7 +363,7 @@ def names(self) -> Optional[ut.Names]: return self.get_names() @names.setter - def names(self, names: Optional[Sequence[str]]): + def names(self, names: Sequence[str] | None): """Set new names (in-place operation). Args: @@ -388,7 +389,7 @@ def get_mcols(self) -> BiocFrame: """ return self._mcols - def set_mcols(self, mcols: Optional[BiocFrame], in_place: bool = False) -> IRanges: + def set_mcols(self, mcols: BiocFrame | None, in_place: bool = False) -> IRanges: """Set new metadata about ranges. Args: @@ -419,7 +420,7 @@ def mcols(self) -> BiocFrame: return self.get_mcols() @mcols.setter - def mcols(self, mcols: Optional[BiocFrame]): + def mcols(self, mcols: BiocFrame | None): """Set new metadata about ranges (in-place operation). Args: @@ -444,7 +445,7 @@ def __len__(self) -> int: """ return len(self._start) - def __getitem__(self, subset: Union[Sequence, int, str, bool, slice, range]) -> IRanges: + def __getitem__(self, subset: Sequence | int | str | bool | slice | range) -> IRanges: """Subset the IRanges. Args: @@ -465,7 +466,7 @@ def __getitem__(self, subset: Union[Sequence, int, str, bool, slice, range]) -> metadata=self._metadata, ) - def __setitem__(self, args: Union[Sequence, int, str, bool, slice, range], value: IRanges): + def __setitem__(self, args: Sequence | int | str | bool | slice | range, value: IRanges): """Add or update positions (in-place operation). Args: @@ -502,7 +503,7 @@ def __setitem__(self, args: Union[Sequence, int, str, bool, slice, range], value self.delete_nclist_index() - def get_row(self, index_or_name: Union[str, int]) -> IRanges: + def get_row(self, index_or_name: str | int) -> IRanges: """Access a row by index or row name. Args: @@ -677,8 +678,8 @@ def __deepcopy__(self, memo) -> IRanges: ############################# def shift_and_clip_ranges( - self, shift: np.ndarray, width: Union[int, None] = None, circle_length: Union[int, None] = None - ) -> Tuple[np.ndarray, np.ndarray, int, bool]: + self, shift: np.ndarray, width: int | None = None, circle_length: int | None = None + ) -> tuple[np.ndarray, np.ndarray, int, bool]: """Shift and clip interval ranges. Args: @@ -705,10 +706,10 @@ def shift_and_clip_ranges( def coverage( self, - shift: Optional[np.ndarray] = None, - width: Union[int, None] = None, - weight: Optional[np.ndarray] = None, - circle_length: Union[int, None] = None, + shift: np.ndarray | None = None, + width: int | None = None, + weight: np.ndarray | None = None, + circle_length: int | None = None, method: Literal["auto", "sort", "hash", "naive"] = "auto", ) -> np.ndarray: """Compute weighted coverage of ranges. @@ -842,7 +843,7 @@ def sort(self, decreasing: bool = False, in_place: bool = False) -> IRanges: output = self._define_output(in_place) return output[order] - def gaps(self, start: Optional[int] = None, end: Optional[int] = None) -> IRanges: + def gaps(self, start: int | None = None, end: int | None = None) -> IRanges: """Gaps returns an ``IRanges`` object representing the set of intervals that remain after the ranges are removed specified by the start and end arguments. @@ -950,7 +951,7 @@ def disjoint_bins(self) -> np.ndarray: #### intra-range methods #### ############################# - def shift(self, shift: Union[int, List[int], np.ndarray], in_place: bool = False) -> IRanges: + def shift(self, shift: int | list[int] | np.ndarray, in_place: bool = False) -> IRanges: """Shift ranges by specified amount. Args: @@ -982,9 +983,9 @@ def shift(self, shift: Union[int, List[int], np.ndarray], in_place: bool = False def narrow( self, - start: Optional[Union[int, List[int], np.ndarray]] = None, - width: Optional[Union[int, List[int], np.ndarray]] = None, - end: Optional[Union[int, List[int], np.ndarray]] = None, + start: int | list[int] | np.ndarray | None = None, + width: int | list[int] | np.ndarray | None = None, + end: int | list[int] | np.ndarray | None = None, in_place: bool = False, ) -> IRanges: """Narrow ranges. @@ -1027,8 +1028,8 @@ def narrow( def resize( self, - width: Union[int, List[int], np.ndarray], - fix: Union[Literal["start", "end", "center"], List[Literal["start", "end", "center"]]] = "start", + width: int | list[int] | np.ndarray, + fix: Literal["start", "end", "center"] | list[Literal["start", "end", "center"]] = "start", in_place: bool = False, ) -> IRanges: """Resize ranges to the specified ``width`` where either the ``start``, ``end``, or ``center`` is used as an @@ -1293,8 +1294,8 @@ def reflect(self, bounds: IRanges, in_place: bool = False) -> IRanges: def restrict( self, - start: Optional[Union[int, List[int], np.ndarray]] = None, - end: Optional[Union[int, List[int], np.ndarray]] = None, + start: int | list[int] | np.ndarray | None = None, + end: int | list[int] | np.ndarray | None = None, keep_all_ranges: bool = False, ) -> IRanges: """Restrict ranges to a given start and end positions. @@ -1370,10 +1371,10 @@ def restrict( def threebands( self, - start: Optional[Union[int, np.ndarray]] = None, - end: Optional[Union[int, np.ndarray]] = None, - width: Optional[Union[int, np.ndarray]] = None, - ) -> Dict[str, IRanges]: + start: int | np.ndarray | None = None, + end: int | np.ndarray | None = None, + width: int | np.ndarray | None = None, + ) -> dict[str, IRanges]: """Split ranges into three parts: left, middle, and right. Args: @@ -1417,7 +1418,7 @@ def threebands( "right": IRanges(right_starts, right_widths), } - def overlap_indices(self, start: Optional[int] = None, end: Optional[int] = None) -> np.ndarray: + def overlap_indices(self, start: int | None = None, end: int | None = None) -> np.ndarray: """Find overlaps with the start and end positions. Args: @@ -1868,7 +1869,7 @@ def precede( select: Literal["all", "first"] = "first", delete_index: bool = True, num_threads: int = 1, - ) -> Union[np.ndarray, BiocFrame]: + ) -> np.ndarray | BiocFrame: """Find nearest positions that are upstream/precede each query range. Args: @@ -1930,7 +1931,7 @@ def follow( select: Literal["all", "last"] = "last", delete_index: bool = True, num_threads: int = 1, - ) -> Union[np.ndarray, BiocFrame]: + ) -> np.ndarray | BiocFrame: """Find nearest positions that are downstream/follow each query range. Args: @@ -2013,7 +2014,7 @@ def nearest( adjacent_equals_overlap: bool = True, delete_index: bool = True, num_threads: int = 1, - ) -> Union[np.ndarray, BiocFrame]: + ) -> np.ndarray | BiocFrame: """Find nearest ranges in both directions. Args: @@ -2241,9 +2242,7 @@ def combine(self, *other: IRanges) -> IRanges: ######>> window methods <<###### ################################ - def tile( - self, n: Optional[Union[int, np.ndarray]] = None, width: Optional[Union[int, np.ndarray]] = None - ) -> List[IRanges]: + def tile(self, n: int | np.ndarray | None = None, width: int | np.ndarray | None = None) -> list[IRanges]: """Split ranges into either n equal parts or parts of fixed width. Args: @@ -2297,7 +2296,7 @@ def tile( return result - def sliding_windows(self, width: int, step: int = 1) -> List[IRanges]: + def sliding_windows(self, width: int, step: int = 1) -> list[IRanges]: """Create sliding windows of fixed width and step size. Args: diff --git a/src/iranges/__init__.py b/src/iranges/__init__.py index e5b3558..8a59905 100644 --- a/src/iranges/__init__.py +++ b/src/iranges/__init__.py @@ -16,5 +16,5 @@ del version, PackageNotFoundError from .IRanges import IRanges -from .utils import normalize_array from .irangeslist import CompressedIRangesList +from .utils import normalize_array diff --git a/src/iranges/irangeslist.py b/src/iranges/irangeslist.py index d314fd1..9329eb6 100644 --- a/src/iranges/irangeslist.py +++ b/src/iranges/irangeslist.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Any, Dict, List, Optional, Sequence, Union +from collections.abc import Sequence +from typing import Any import biocutils as ut from compressed_lists import CompressedList, Partitioning @@ -20,8 +21,8 @@ def __init__( self, unlist_data: IRanges, partitioning: Partitioning, - element_metadata: Optional[dict] = None, - metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None, + element_metadata: dict | None = None, + metadata: dict[str, Any] | ut.NamedList | None = None, **kwargs, ): """Initialize a CompressedIRangesList. @@ -52,9 +53,9 @@ def __init__( @classmethod def from_list( cls, - lst: List[IRanges], - names: Optional[Union[ut.Names, Sequence[str]]] = None, - metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None, + lst: list[IRanges], + names: ut.Names | Sequence[str] | None = None, + metadata: dict[str, Any] | ut.NamedList | None = None, ) -> CompressedIRangesList: """Create a `CompressedIRangesList` from a regular list. @@ -141,8 +142,8 @@ def __str__(self) -> str: output += f"partitioning: {ut.print_truncated_list(self._partitioning)}\n" - output += f"element_metadata({str(len(self._element_metadata))} rows): {ut.print_truncated_list(list(self._element_metadata.get_column_names()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" - output += f"metadata({str(len(self._metadata))}): {ut.print_truncated_list(list(self._metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" + output += f"element_metadata({len(self._element_metadata)!s} rows): {ut.print_truncated_list(list(self._element_metadata.get_column_names()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" + output += f"metadata({len(self._metadata)!s}): {ut.print_truncated_list(list(self._metadata.keys()), sep=' ', include_brackets=False, transform=lambda y: y)}\n" return output @@ -150,9 +151,9 @@ def __str__(self) -> str: @splitAsCompressedList.register def _( data: IRanges, - groups_or_partitions: Union[list, Partitioning], - names: Optional[Union[ut.Names, Sequence[str]]] = None, - metadata: Optional[dict] = None, + groups_or_partitions: list | Partitioning, + names: ut.Names | Sequence[str] | None = None, + metadata: dict | None = None, ) -> CompressedIRangesList: """Handle lists of IRanges objects.""" diff --git a/src/iranges/sew_handler.py b/src/iranges/sew_handler.py index 9a9fa20..5b4e70b 100644 --- a/src/iranges/sew_handler.py +++ b/src/iranges/sew_handler.py @@ -1,5 +1,3 @@ -from typing import Optional, Tuple, Union - import numpy as np from .utils import handle_negative_coords, normalize_array @@ -17,9 +15,9 @@ class SEWWrangler: def __init__( self, ref_widths: np.ndarray, - start: Optional[Union[int, np.ndarray]] = None, - end: Optional[Union[int, np.ndarray]] = None, - width: Optional[Union[int, np.ndarray]] = None, + start: int | np.ndarray | None = None, + end: int | np.ndarray | None = None, + width: int | np.ndarray | None = None, translate_negative: bool = True, allow_nonnarrowing: bool = False, ): @@ -79,7 +77,7 @@ def _validate_narrowing(self, starts: np.ndarray, widths: np.ndarray) -> None: f"the solved end ({int(ends[idx])}) is > refwidth" ) - def solve(self) -> Tuple[np.ndarray, np.ndarray]: + def solve(self) -> tuple[np.ndarray, np.ndarray]: """Resolve Start/End/Width parameters to concrete ranges. Returns: diff --git a/src/iranges/utils.py b/src/iranges/utils.py index b51afff..910766a 100644 --- a/src/iranges/utils.py +++ b/src/iranges/utils.py @@ -1,5 +1,3 @@ -from typing import Optional, Tuple, Union - import numpy as np __author__ = "Jayaram Kancherla" @@ -8,7 +6,7 @@ def normalize_array( - x: Optional[Union[int, float, np.number, np.ndarray]], length: int, dtype: np.dtype = np.int32 + x: float | np.number | np.ndarray | None, length: int, dtype: np.dtype = np.int32 ) -> np.ma.MaskedArray: """Normalize input to masked array with proper length and type. @@ -66,8 +64,8 @@ def handle_negative_coords(coords: np.ma.MaskedArray, ref_len: np.ndarray) -> np def clip_ranges( - starts: np.ndarray, widths: np.ndarray, min_val: Optional[int] = None, max_val: Optional[int] = None -) -> Tuple[np.ndarray, np.ndarray]: + starts: np.ndarray, widths: np.ndarray, min_val: int | None = None, max_val: int | None = None +) -> tuple[np.ndarray, np.ndarray]: """Clip ranges to specified bounds. Args: @@ -102,10 +100,10 @@ def clip_ranges( def compute_up_down( starts: np.ndarray, widths: np.ndarray, - upstream: Union[int, np.ndarray], - downstream: Union[int, np.ndarray], + upstream: int | np.ndarray, + downstream: int | np.ndarray, site: str, -) -> Tuple[np.ndarray, np.ndarray]: +) -> tuple[np.ndarray, np.ndarray]: """Helper for promoters/terminators.""" length = len(starts) diff --git a/tests/test_coverage.py b/tests/test_coverage.py index 3f6dc4a..08808c4 100644 --- a/tests/test_coverage.py +++ b/tests/test_coverage.py @@ -1,4 +1,5 @@ import numpy as np + from iranges import IRanges __author__ = "jkanche" diff --git a/tests/test_interrange.py b/tests/test_interrange.py index 2a11adf..c4eff9e 100644 --- a/tests/test_interrange.py +++ b/tests/test_interrange.py @@ -1,4 +1,5 @@ import numpy as np + from iranges import IRanges __author__ = "jkanche" diff --git a/tests/test_intrarange.py b/tests/test_intrarange.py index d6bfb6d..c970dd2 100644 --- a/tests/test_intrarange.py +++ b/tests/test_intrarange.py @@ -1,5 +1,6 @@ import numpy as np import pytest + from iranges import IRanges __author__ = "jkanche" diff --git a/tests/test_order_ops.py b/tests/test_order_ops.py index 3944fff..2f50a44 100644 --- a/tests/test_order_ops.py +++ b/tests/test_order_ops.py @@ -1,4 +1,5 @@ import numpy as np + from iranges import IRanges __author__ = "jkanche" diff --git a/tests/test_set_ops.py b/tests/test_set_ops.py index f57341e..850ea43 100644 --- a/tests/test_set_ops.py +++ b/tests/test_set_ops.py @@ -1,4 +1,5 @@ import numpy as np + from iranges import IRanges __author__ = "jkanche"