diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f91485..be68631 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.1 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] diff --git a/perf/genomicranges.ipynb b/perf/genomicranges.ipynb index 4486a48..211d7ae 100644 --- a/perf/genomicranges.ipynb +++ b/perf/genomicranges.ipynb @@ -9,7 +9,6 @@ "source": [ "import biobear as bb\n", "\n", - "\n", "session = bb.new_session()\n", "\n", "bed = session.read_bed_file(\"consensus_peaks_bicnn.bed\", bb.BEDReadOptions(n_fields=4))" diff --git a/setup.py b/setup.py index dae5e5c..ef5a5a6 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ if __name__ == "__main__": try: setup(use_scm_version={"version_scheme": "no-guess-dev"}) - 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/genomicranges/GenomicRanges.py b/src/genomicranges/GenomicRanges.py index 17a1373..9f998ce 100644 --- a/src/genomicranges/GenomicRanges.py +++ b/src/genomicranges/GenomicRanges.py @@ -1,8 +1,9 @@ from __future__ import annotations from collections import defaultdict +from collections.abc import Sequence from multiprocessing import Pool, cpu_count -from typing import Any, Dict, List, Literal, Optional, Sequence, Tuple, Union +from typing import Any, Literal from warnings import warn import biocutils as ut @@ -129,11 +130,11 @@ def __init__( self, seqnames: Sequence[str], ranges: IRanges, - strand: Optional[Union[Sequence[str], Sequence[int], np.ndarray]] = None, - names: Optional[Union[ut.Names, Sequence[str]]] = None, - mcols: Optional[BiocFrame] = None, - seqinfo: Optional[SeqInfo] = None, - metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None, + strand: Sequence[str] | Sequence[int] | np.ndarray | None = None, + names: ut.Names | Sequence[str] | None = None, + mcols: BiocFrame | None = None, + seqinfo: SeqInfo | None = None, + metadata: dict[str, Any] | ut.NamedList | None = None, _validate: bool = True, ): """Initialize a ``GenomicRanges`` object. @@ -437,7 +438,7 @@ def __str__(self) -> str: ######>> seqnames <<###### ########################## - def get_seqnames(self, as_type: Literal["factor", "list"] = "list") -> Union[ut.Factor, List[str]]: + def get_seqnames(self, as_type: Literal["factor", "list"] = "list") -> ut.Factor | list[str]: """Access sequence names. Args: @@ -459,7 +460,7 @@ def get_seqnames(self, as_type: Literal["factor", "list"] = "list") -> Union[ut. else: raise ValueError("Argument 'as_type' must be 'factor' or 'list'.") - def set_seqnames(self, seqnames: Union[Sequence[str], np.ndarray], in_place: bool = False) -> GenomicRanges: + def set_seqnames(self, seqnames: Sequence[str] | np.ndarray, in_place: bool = False) -> GenomicRanges: """Set new sequence names. Args: @@ -486,12 +487,12 @@ def set_seqnames(self, seqnames: Union[Sequence[str], np.ndarray], in_place: boo return output @property - def seqnames(self) -> Union[Union[np.ndarray, List[str]], np.ndarray]: + def seqnames(self) -> np.ndarray | list[str]: """Alias for :py:meth:`~get_seqnames`.""" return self.get_seqnames() @seqnames.setter - def seqnames(self, seqnames: Union[Sequence[str], np.ndarray]): + def seqnames(self, seqnames: Sequence[str] | np.ndarray): """Alias for :py:meth:`~set_seqnames` with ``in_place = True``. As this mutates the original object, a warning is raised. @@ -556,9 +557,7 @@ def ranges(self, ranges: IRanges): ######>> strand <<###### ######################## - def get_strand( - self, as_type: Literal["numpy", "factor", "list"] = "numpy" - ) -> Union[Tuple[np.ndarray, dict], List[str]]: + def get_strand(self, as_type: Literal["numpy", "factor", "list"] = "numpy") -> tuple[np.ndarray, dict] | list[str]: """Access strand information. Args: @@ -598,7 +597,7 @@ def get_strand( raise ValueError("Argument 'as_type' must be 'factor' or 'list'.") def set_strand( - self, strand: Optional[Union[Sequence[str], Sequence[int], np.ndarray]], in_place: bool = False + self, strand: Sequence[str] | Sequence[int] | np.ndarray | None, in_place: bool = False ) -> GenomicRanges: """Set new strand information. @@ -635,7 +634,7 @@ def strand(self) -> np.ndarray: return self.get_strand() @strand.setter - def strand(self, strand: Optional[Union[Sequence[str], Sequence[int], np.ndarray]]): + def strand(self, strand: Sequence[str] | Sequence[int] | np.ndarray | None): """Alias for :py:meth:`~set_strand` with ``in_place = True``. As this mutates the original object, a warning is raised. @@ -657,7 +656,7 @@ def get_names(self) -> ut.Names: """ return self._names - def set_names(self, names: Optional[Union[ut.Names, Sequence[str]]], in_place: bool = False) -> GenomicRanges: + def set_names(self, names: ut.Names | Sequence[str] | None, in_place: bool = False) -> GenomicRanges: """Set new names. Args: @@ -688,7 +687,7 @@ def names(self) -> ut.Names: return self.get_names() @names.setter - def names(self, names: Optional[Union[ut.Names, Sequence[str]]]): + def names(self, names: ut.Names | Sequence[str] | None): """Alias for :py:meth:`~set_names` with ``in_place = True``. As this mutates the original object, a warning is raised. @@ -710,7 +709,7 @@ def get_mcols(self) -> BiocFrame: """ return self._mcols - def set_mcols(self, mcols: Optional[BiocFrame], in_place: bool = False) -> GenomicRanges: + def set_mcols(self, mcols: BiocFrame | None, in_place: bool = False) -> GenomicRanges: """Set new range metadata. Args: @@ -743,7 +742,7 @@ def mcols(self) -> BiocFrame: return self.get_mcols() @mcols.setter - def mcols(self, mcols: Optional[BiocFrame]): + def mcols(self, mcols: BiocFrame | None): """Alias for :py:meth:`~set_mcols` with ``in_place = True``. As this mutates the original object, a warning is raised. @@ -765,7 +764,7 @@ def get_seqinfo(self) -> SeqInfo: """ return self._seqinfo - def set_seqinfo(self, seqinfo: Optional[SeqInfo], in_place: bool = False) -> GenomicRanges: + def set_seqinfo(self, seqinfo: SeqInfo | None, in_place: bool = False) -> GenomicRanges: """Set new sequence information. Args: @@ -806,7 +805,7 @@ def seqinfo(self) -> np.ndarray: @seqinfo.setter def seqinfo( self, - seqinfo: Optional[SeqInfo], + seqinfo: SeqInfo | None, ): """Alias for :py:meth:`~set_seqinfo` with ``in_place = True``. @@ -877,7 +876,7 @@ def get_seqlengths(self) -> np.ndarray: ######>> Slicers <<###### ######################### - def get_subset(self, subset: Union[str, int, bool, Sequence]) -> GenomicRanges: + def get_subset(self, subset: str | int | bool | Sequence) -> GenomicRanges: """Subset ``GenomicRanges``, based on their indices or names. Args: @@ -910,13 +909,13 @@ def get_subset(self, subset: Union[str, int, bool, Sequence]) -> GenomicRanges: metadata=self._metadata, ) - def __getitem__(self, subset: Union[str, int, bool, Sequence]) -> GenomicRanges: + def __getitem__(self, subset: str | int | bool | Sequence) -> GenomicRanges: """Alias to :py:attr:`~get_subset`.""" return self.get_subset(subset) def set_subset( self, - args: Union[Sequence, int, str, bool, slice, range], + args: Sequence | int | str | bool | slice | range, value: GenomicRanges, in_place: bool = False, ) -> GenomicRanges: @@ -975,7 +974,7 @@ def set_subset( def __setitem__( self, - args: Union[Sequence, int, str, bool, slice, range], + args: Sequence | int | str | bool | slice | range, value: GenomicRanges, ) -> GenomicRanges: """Alias to :py:attr:`~set_subset`. @@ -1158,7 +1157,7 @@ def from_polars(cls, input) -> GenomicRanges: def flank( self, width: int, - start: Union[bool, np.ndarray, List[bool]] = True, + start: bool | np.ndarray | list[bool] = True, both: bool = False, ignore_strand: bool = False, in_place: bool = False, @@ -1238,8 +1237,8 @@ def flank( 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", ignore_strand: bool = False, in_place: bool = False, ) -> GenomicRanges: @@ -1299,7 +1298,7 @@ def resize( output._ranges = self._ranges.resize(width=width, fix=fix_arr) return output - def shift(self, shift: Union[int, List[int], np.ndarray] = 0, in_place: bool = False) -> GenomicRanges: + def shift(self, shift: int | list[int] | np.ndarray = 0, in_place: bool = False) -> GenomicRanges: """Shift all intervals. Args: @@ -1383,8 +1382,8 @@ def terminators(self, upstream: int = 2000, downstream: int = 200, in_place: boo def restrict( self, - start: Optional[Union[int, Dict[str, int], np.ndarray]] = None, - end: Optional[Union[int, Dict[str, int], np.ndarray]] = None, + start: int | dict[str, int] | np.ndarray | None = None, + end: int | dict[str, int] | np.ndarray | None = None, keep_all_ranges: bool = False, ) -> GenomicRanges: """Restrict ranges to a given start and end positions. @@ -1565,9 +1564,9 @@ def trim(self, in_place: bool = False) -> GenomicRanges: 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, ) -> GenomicRanges: """Narrow genomic positions by provided ``start``, ``width`` and ``end`` parameters. @@ -1742,7 +1741,7 @@ def range(self, with_reverse_map: bool = False, ignore_strand: bool = False) -> def gaps( self, start: int = 1, - end: Optional[Union[int, Dict[str, int]]] = None, + end: int | dict[str, int] | None = None, ignore_strand: bool = False, ) -> GenomicRanges: """Identify complemented ranges for each distinct (seqname, strand) pair. @@ -1899,8 +1898,8 @@ def disjoint_bins(self, ignore_strand: bool = False) -> np.ndarray: return binned_results def coverage( - self, shift: int = 0, width: Optional[int] = None, weight: int = 1, ignore_strand: bool = True - ) -> Dict[str, np.ndarray]: + self, shift: int = 0, width: int | None = None, weight: int = 1, ignore_strand: bool = True + ) -> dict[str, np.ndarray]: """ Calculate coverage for each chromosome. For each position, this method counts the number of ranges that cover it. @@ -2151,7 +2150,7 @@ def extract_groups_by_seqnames(self): groups.append(idx) return groups - def _get_query_common_groups(self, query: GenomicRanges) -> Tuple[np.ndarray, np.ndarray]: + def _get_query_common_groups(self, query: GenomicRanges) -> tuple[np.ndarray, np.ndarray]: # smerged = merge_SeqInfo([self._seqinfo, query._seqinfo]) common_seqlevels = set(self._seqinfo._seqnames).intersection(query._seqinfo._seqnames) q_group_idx = [self._seqinfo._seqnames.index(i) for i in common_seqlevels] @@ -2415,7 +2414,7 @@ def nearest( ignore_strand: bool = False, num_threads: int = 1, adjacent_equals_overlap: bool = True, - ) -> Union[np.ndarray, BiocFrame]: + ) -> np.ndarray | BiocFrame: """Search nearest positions both upstream and downstream that overlap with each range in ``query``. Args: @@ -2505,7 +2504,7 @@ def precede( select: Literal["all", "first"] = "first", ignore_strand: bool = False, num_threads: int = 1, - ) -> Union[np.ndarray, BiocFrame]: + ) -> np.ndarray | BiocFrame: """Search nearest positions only downstream that overlap with each range in ``query``. Args: @@ -2585,7 +2584,7 @@ def follow( select: Literal["all", "last"] = "last", ignore_strand: bool = False, num_threads: int = 1, - ) -> Union[np.ndarray, BiocFrame]: + ) -> np.ndarray | BiocFrame: """Search nearest positions only upstream that overlap with each range in ``query``. Args: @@ -2656,7 +2655,7 @@ def follow( else: return BiocFrame({"query_hits": final_qhits, "self_hits": final_shits}) - def distance(self, query: Union[GenomicRanges, IRanges]) -> np.ndarray: + def distance(self, query: GenomicRanges | IRanges) -> np.ndarray: """Compute the pair-wise distance with intervals in query. Args: @@ -2723,7 +2722,7 @@ def match(self, query: GenomicRanges, ignore_strand: bool = False) -> np.ndarray return result - def _get_ranges_as_list(self) -> List[Tuple[int, int, int]]: + def _get_ranges_as_list(self) -> list[tuple[int, int, int]]: """Internal method to get ranges as a list of tuples. Returns: @@ -2786,7 +2785,7 @@ def sort(self, decreasing: bool = False, in_place: bool = False) -> GenomicRange output = self._define_output(in_place) return output[list(order)] - def rank(self) -> List[int]: + def rank(self) -> list[int]: """Get rank of the ``GenomicRanges`` object. For each range identifies its position is a sorted order. @@ -2846,7 +2845,7 @@ def invert_strand(self, in_place: bool = False) -> GenomicRanges: ######>> window methods <<###### ################################ - def tile(self, n: Optional[int] = None, width: Optional[int] = None) -> List[GenomicRanges]: + def tile(self, n: int | None = None, width: int | None = None) -> list[GenomicRanges]: """Split each interval by ``n`` (number of sub intervals) or ``width`` (intervals with equal width). Note: Either ``n`` or ``width`` must be provided but not both. @@ -2891,7 +2890,7 @@ def tile(self, n: Optional[int] = None, width: Optional[int] = None) -> List[Gen return result - def sliding_windows(self, width: int, step: int = 1) -> List[GenomicRanges]: + def sliding_windows(self, width: int, step: int = 1) -> list[GenomicRanges]: """Slide along each range by ``width`` (intervals with equal ``width``) and ``step``. Also, checkout :py:func:`~genomicranges.io.tiling.tile_genome` for splitting @@ -2932,9 +2931,9 @@ def sliding_windows(self, width: int, step: int = 1) -> List[GenomicRanges]: @classmethod def tile_genome( cls, - seqlengths: Dict[str, int], - ntile: Optional[int] = None, - tilewidth: Optional[int] = None, + seqlengths: dict[str, int], + ntile: int | None = None, + tilewidth: int | None = None, cut_last_tile_in_chrom: bool = False, ) -> GenomicRanges: """Tile genome into approximately equal-sized regions. @@ -3123,7 +3122,7 @@ def binned_average( ######>> split <<###### ####################### - def split(self, groups: list) -> "CompressedGenomicRangesList": + def split(self, groups: list) -> CompressedGenomicRangesList: """Split the `GenomicRanges` object into a :py:class:`~genomicranges.grangeslist.CompressedGenomicRangesList`. Args: @@ -3173,7 +3172,7 @@ def empty(cls): def subtract( self, other: GenomicRanges, min_overlap: int = 1, ignore_strand: bool = False - ) -> "CompressedGenomicRangesList": + ) -> CompressedGenomicRangesList: """Subtract searches for features in ``x`` that overlap ``self`` by at least the number of base pairs given by ``min_overlap``. diff --git a/src/genomicranges/grangeslist.py b/src/genomicranges/grangeslist.py index d04b9fe..5c511b8 100644 --- a/src/genomicranges/grangeslist.py +++ b/src/genomicranges/grangeslist.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 import numpy as np @@ -120,8 +121,8 @@ def __init__( self, unlist_data: GenomicRanges, 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. @@ -152,9 +153,9 @@ def __init__( @classmethod def from_list( cls, - lst: List[GenomicRanges], - names: Optional[Union[ut.Names, Sequence[str]]] = None, - metadata: Optional[Union[Dict[str, Any], ut.NamedList]] = None, + lst: list[GenomicRanges], + names: ut.Names | Sequence[str] | None = None, + metadata: dict[str, Any] | ut.NamedList | None = None, ) -> CompressedGenomicRangesList: """Create a `CompressedIRangesList` from a regular list. @@ -241,8 +242,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 @@ -356,9 +357,9 @@ def empty(cls, n: int): @splitAsCompressedList.register def _( data: GenomicRanges, - 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, ) -> CompressedGenomicRangesList: """Handle lists of IRanges objects.""" diff --git a/src/genomicranges/io/gtf.py b/src/genomicranges/io/gtf.py index 3fc0580..3827f14 100644 --- a/src/genomicranges/io/gtf.py +++ b/src/genomicranges/io/gtf.py @@ -1,5 +1,4 @@ import logging -from typing import Dict, List, Union # Variation of https://github.com/epiviz/epivizfileserver/src/epivizfileserver/cli.py @@ -8,7 +7,7 @@ __license__ = "MIT" -def _parse_all_attribute(row: str) -> Dict: +def _parse_all_attribute(row: str) -> dict: """Extract all keys from the gtf/gff attribute string. Args: @@ -32,7 +31,7 @@ def _parse_all_attribute(row: str) -> Dict: def parse_gtf( path: str, compressed: bool, - skiprows: Union[int, List[int]] = None, + skiprows: int | list[int] = None, comment: str = "#", ): """Read a GTF file as :py:class:`~pandas.DataFrame`. @@ -107,7 +106,7 @@ def parse_gtf( def read_gtf( file: str, - skiprows: Union[int, List[int]] = None, + skiprows: int | list[int] = None, comment: str = "#", ) -> "GenomicRanges": """Read a GTF file as :py:class:`~genomicranges.GenomicRanges.GenomicRanges`. diff --git a/src/genomicranges/sequence_info.py b/src/genomicranges/sequence_info.py index abb23f8..2e5f05d 100644 --- a/src/genomicranges/sequence_info.py +++ b/src/genomicranges/sequence_info.py @@ -1,4 +1,4 @@ -from typing import Dict, List, Optional, Sequence, Union +from collections.abc import Sequence from warnings import warn import biocutils as ut @@ -81,9 +81,9 @@ class SeqInfo: def __init__( self, seqnames: Sequence[str], - seqlengths: Optional[Union[int, Sequence[int], Dict[str, int]]] = None, - is_circular: Optional[Union[bool, Sequence[bool], Dict[str, bool]]] = None, - genome: Optional[Union[str, Sequence[str], Dict[str, str]]] = None, + seqlengths: int | Sequence[int] | dict[str, int] | None = None, + is_circular: bool | Sequence[bool] | dict[str, bool] | None = None, + genome: str | Sequence[str] | dict[str, str] | None = None, validate: bool = True, ) -> None: """ @@ -163,7 +163,7 @@ def _populate_reverse_seqnames_index(self): def _wipe_reverse_seqnames_index(self): self._reverse_seqnames = None - def _flatten_incoming(self, values, expected) -> List: + def _flatten_incoming(self, values, expected) -> list: if values is None or isinstance(values, expected): return [values] * len(self) @@ -313,7 +313,7 @@ def __str__(self) -> str: ######>> seqnames <<###### ########################## - def get_seqnames(self) -> List[str]: + def get_seqnames(self) -> list[str]: """ Returns: List of all chromosome names. @@ -342,7 +342,7 @@ def set_seqnames(self, seqnames: Sequence[str], in_place: bool = False) -> "SeqI return output @property - def seqnames(self) -> List[str]: + def seqnames(self) -> list[str]: warn("'seqnames' is deprecated, use 'get_seqnames' instead", UserWarning) return self.get_seqnames() @@ -359,7 +359,7 @@ def seqnames(self, seqnames: Sequence[str]): ######>> seqlengths <<###### ############################ - def get_seqlengths(self) -> List[int]: + def get_seqlengths(self) -> list[int]: """ Returns: A list of integers is returned containing the lengths of all @@ -370,7 +370,7 @@ def get_seqlengths(self) -> List[int]: def set_seqlengths( self, - seqlengths: Optional[Union[int, Sequence[int], Dict[str, int]]], + seqlengths: int | Sequence[int] | dict[str, int] | None, in_place: bool = False, ) -> "SeqInfo": """ @@ -399,7 +399,7 @@ def set_seqlengths( return output @property - def seqlengths(self) -> List[int]: + def seqlengths(self) -> list[int]: warn( "'seqlengths' is deprecated, use 'get_seqlengths' instead", UserWarning, @@ -407,7 +407,7 @@ def seqlengths(self) -> List[int]: return self.get_seqlengths() @seqlengths.setter - def seqlengths(self, seqlengths: Optional[Union[int, Sequence[int], Dict[str, int]]]): + def seqlengths(self, seqlengths: int | Sequence[int] | dict[str, int] | None): warn( "Setting property 'seqlengths' is an in-place operation, use 'set_seqlengths' instead", UserWarning, @@ -419,7 +419,7 @@ def seqlengths(self, seqlengths: Optional[Union[int, Sequence[int], Dict[str, in ######>> is-circular <<###### ############################# - def get_is_circular(self) -> List[bool]: + def get_is_circular(self) -> list[bool]: """ Returns: A list of booleans is returned specifying whether each sequence @@ -429,7 +429,7 @@ def get_is_circular(self) -> List[bool]: def set_is_circular( self, - is_circular: Optional[Union[bool, Sequence[bool], Dict[str, bool]]], + is_circular: bool | Sequence[bool] | dict[str, bool] | None, in_place: bool = False, ) -> "SeqInfo": """ @@ -459,7 +459,7 @@ def set_is_circular( return output @property - def is_circular(self) -> List[bool]: + def is_circular(self) -> list[bool]: warn( "'is_circular' is deprecated, use 'get_is_circular' instead", UserWarning, @@ -467,7 +467,7 @@ def is_circular(self) -> List[bool]: return self.get_is_circular() @is_circular.setter - def is_circular(self, is_circular: Optional[Union[bool, Sequence[bool], Dict[str, bool]]]): + def is_circular(self, is_circular: bool | Sequence[bool] | dict[str, bool] | None): warn( "Setting property 'is_circular' is an in-place operation, use 'set_is_circular' instead", UserWarning, @@ -479,7 +479,7 @@ def is_circular(self, is_circular: Optional[Union[bool, Sequence[bool], Dict[str ######>> genome <<###### ######################## - def get_genome(self) -> List[str]: + def get_genome(self) -> list[str]: """ Returns: A list of strings is returned containing the genome identity for @@ -489,7 +489,7 @@ def get_genome(self) -> List[str]: def set_genome( self, - genome: Optional[Union[str, Sequence[str], Dict[str, str]]], + genome: str | Sequence[str] | dict[str, str] | None, in_place: bool = False, ) -> "SeqInfo": """ @@ -514,12 +514,12 @@ def set_genome( return output @property - def genome(self) -> List[str]: + def genome(self) -> list[str]: warn("'genome' is deprecated, use 'get_genome' instead", UserWarning) return self.get_genome() @genome.setter - def genome(self, genome: Optional[Union[bool, Sequence[bool], Dict[str, bool]]]): + def genome(self, genome: bool | Sequence[bool] | dict[str, bool] | None): warn( "Setting property 'genome' is an in-place operation, use 'set_genome' instead", UserWarning, @@ -546,7 +546,7 @@ def __iter__(self) -> SeqInfoIterator: ######>> Slicers <<###### ######################### - def get_subset(self, subset: Union[str, int, bool, Sequence]) -> "SeqInfo": + def get_subset(self, subset: str | int | bool | Sequence) -> "SeqInfo": """Subset ``SeqInfo``, based on their indices or seqnames. Args: @@ -576,7 +576,7 @@ def get_subset(self, subset: Union[str, int, bool, Sequence]) -> "SeqInfo": genome=ut.subset_sequence(self._genome, idx), ) - def __getitem__(self, subset: Union[str, int, bool, Sequence]) -> "SeqInfo": + def __getitem__(self, subset: str | int | bool | Sequence) -> "SeqInfo": """Alias to :py:attr:`~get_subset`.""" return self.get_subset(subset) @@ -595,7 +595,7 @@ def _combine_SeqInfo(*x: SeqInfo) -> SeqInfo: return merge_SeqInfo(x) -def merge_SeqInfo(objects: List[SeqInfo]) -> SeqInfo: +def merge_SeqInfo(objects: list[SeqInfo]) -> SeqInfo: """Merge multiple :py:class:`~SeqInfo` objects, taking the union of all reference sequences. If the same reference sequence is present with the same details across ``objects``, only a single instance is present in the final object; if details are contradictory, they are replaced with None. diff --git a/src/genomicranges/utils.py b/src/genomicranges/utils.py index 1f4e1b3..dcea743 100644 --- a/src/genomicranges/utils.py +++ b/src/genomicranges/utils.py @@ -1,5 +1,5 @@ +from collections.abc import Sequence from itertools import groupby -from typing import List, Sequence, Union import biocutils as ut import numpy as np @@ -12,7 +12,7 @@ REV_STRAND_MAP = {"1": "+", "-1": "-", "0": "*"} -def sanitize_strand_vector(strand: Union[Sequence[str], Sequence[int], np.ndarray]) -> np.ndarray: +def sanitize_strand_vector(strand: Sequence[str] | Sequence[int] | np.ndarray) -> np.ndarray: """Create a numpy representation for ``strand``. Mapping: 1 for "+" (forward strand), 0 for "*" (any strand) and -1 for "-" (reverse strand). @@ -79,16 +79,12 @@ def _sanitize_strand_search_ops(query_strand, subject_strand): elif query_strand == "-": if subject_strand == "+": out = None - elif subject_strand == "-": - out = "-" - elif subject_strand == "*": + elif subject_strand == "-" or subject_strand == "*": out = "-" elif query_strand == "*": if subject_strand == "*": out = "+" - elif subject_strand == "-": - out = "-" - elif subject_strand == "*": + elif subject_strand == "-" or subject_strand == "*": out = "-" if out is None: @@ -97,7 +93,7 @@ def _sanitize_strand_search_ops(query_strand, subject_strand): return STRAND_MAP[out] -def split_intervals(start: int, end: int, step: int) -> List: +def split_intervals(start: int, end: int, step: int) -> list: """Split an interval range into equal bins. Args: @@ -120,7 +116,7 @@ def split_intervals(start: int, end: int, step: int) -> List: return bins -def slide_intervals(start: int, end: int, width: int, step: int) -> List: +def slide_intervals(start: int, end: int, width: int, step: int) -> list: """Sliding intervals. Args: diff --git a/tests/test_gr_basic.py b/tests/test_gr_basic.py index 1e905e8..04e1dd3 100644 --- a/tests/test_gr_basic.py +++ b/tests/test_gr_basic.py @@ -28,7 +28,7 @@ strand=["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), @@ -61,7 +61,7 @@ def test_slices(): strand=["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), @@ -137,7 +137,7 @@ def test_combine(): strand=["*", "-", "-", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), diff --git a/tests/test_gr_binnedAvg.py b/tests/test_gr_binnedAvg.py index db4c371..7dd0998 100644 --- a/tests/test_gr_binnedAvg.py +++ b/tests/test_gr_binnedAvg.py @@ -1,7 +1,9 @@ -from genomicranges import GenomicRanges +from random import random + from biocframe import BiocFrame from iranges import IRanges -from random import random + +from genomicranges import GenomicRanges __author__ = "jkanche" __copyright__ = "jkanche" @@ -26,7 +28,7 @@ strand=["*", "-", "-", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), diff --git a/tests/test_gr_coverage.py b/tests/test_gr_coverage.py index d1cc86d..23c2390 100644 --- a/tests/test_gr_coverage.py +++ b/tests/test_gr_coverage.py @@ -1,8 +1,10 @@ -from genomicranges.GenomicRanges import GenomicRanges -from iranges import IRanges -from biocframe import BiocFrame from random import random + import numpy as np +from biocframe import BiocFrame +from iranges import IRanges + +from genomicranges.GenomicRanges import GenomicRanges __author__ = "jkanche" __copyright__ = "jkanche" diff --git a/tests/test_gr_initialize.py b/tests/test_gr_initialize.py index 67bea71..a352d74 100644 --- a/tests/test_gr_initialize.py +++ b/tests/test_gr_initialize.py @@ -30,7 +30,7 @@ def test_create_gr(): strand=["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), @@ -62,7 +62,7 @@ def test_create_gr_with_seqnames(): gr16 = GenomicRanges( seqnames=[f"chr{i}" for i in range(500)], - ranges=IRanges(start=range(0, 500), width=range(10, 510)), + ranges=IRanges(start=range(500), width=range(10, 510)), ) assert gr16 is not None @@ -70,7 +70,7 @@ def test_create_gr_with_seqnames(): gr32 = GenomicRanges( seqnames=[f"chr{i}" for i in range(2**16 + 1)], - ranges=IRanges(start=range(0, 2**16 + 1), width=range(10, 2**16 + 11)), + ranges=IRanges(start=range(2**16 + 1), width=range(10, 2**16 + 11)), ) assert gr32 is not None diff --git a/tests/test_gr_initialize_pandas.py b/tests/test_gr_initialize_pandas.py index 4adaa40..f6858b9 100644 --- a/tests/test_gr_initialize_pandas.py +++ b/tests/test_gr_initialize_pandas.py @@ -1,8 +1,10 @@ -import pytest -from genomicranges import GenomicRanges -from biocframe import BiocFrame from random import random + import pandas as pd +import pytest +from biocframe import BiocFrame + +from genomicranges import GenomicRanges __author__ = "jkanche" __copyright__ = "jkanche" @@ -35,7 +37,7 @@ def test_from_pandas_should_fail(): "starts": range(100, 110), "ends": range(110, 120), "strand": ["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"], - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ) diff --git a/tests/test_gr_initialize_polars.py b/tests/test_gr_initialize_polars.py index 2cec9f8..237c4e0 100644 --- a/tests/test_gr_initialize_polars.py +++ b/tests/test_gr_initialize_polars.py @@ -1,9 +1,11 @@ -import pytest -from genomicranges import GenomicRanges -from iranges import IRanges -from biocframe import BiocFrame from random import random + import polars as pl +import pytest +from biocframe import BiocFrame +from iranges import IRanges + +from genomicranges import GenomicRanges __author__ = "jkanche" __copyright__ = "jkanche" @@ -36,7 +38,7 @@ def test_from_polars_should_fail(): "starts": range(100, 110), "ends": range(110, 120), "strand": ["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"], - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ) @@ -79,7 +81,7 @@ def test_to_polars_complex(): strand=["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), diff --git a/tests/test_gr_interrange.py b/tests/test_gr_interrange.py index 98a5878..557a3fb 100644 --- a/tests/test_gr_interrange.py +++ b/tests/test_gr_interrange.py @@ -56,7 +56,7 @@ def test_reduce_with_contigs(): strand=["*", "-", "*", "+", "-"], mcols=BiocFrame( { - "score": range(0, 5), + "score": range(5), "GC": [random() for _ in range(5)], } ), diff --git a/tests/test_gr_misc.py b/tests/test_gr_misc.py index 9ec5a45..d0eedff 100644 --- a/tests/test_gr_misc.py +++ b/tests/test_gr_misc.py @@ -1,7 +1,9 @@ -from genomicranges.GenomicRanges import GenomicRanges from random import random -from iranges import IRanges + from biocframe import BiocFrame +from iranges import IRanges + +from genomicranges.GenomicRanges import GenomicRanges __author__ = "jkanche" __copyright__ = "jkanche" @@ -19,7 +21,7 @@ strand=["*", "-", "*", "+", "-"], mcols=BiocFrame( { - "score": range(0, 5), + "score": range(5), "GC": [random() for _ in range(5)], } ), diff --git a/tests/test_gr_other.py b/tests/test_gr_other.py index ca9dc8d..7e35013 100644 --- a/tests/test_gr_other.py +++ b/tests/test_gr_other.py @@ -1,11 +1,11 @@ from random import random import numpy as np +import pytest from biocframe import BiocFrame from iranges import IRanges from genomicranges.GenomicRanges import GenomicRanges -import pytest __author__ = "jkanche" __copyright__ = "jkanche" diff --git a/tests/test_gr_overlaps.py b/tests/test_gr_overlaps.py index b0752ea..488b7b3 100644 --- a/tests/test_gr_overlaps.py +++ b/tests/test_gr_overlaps.py @@ -27,7 +27,7 @@ strand=["-", "+", "+", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), diff --git a/tests/test_gr_seqInfo_trim.py b/tests/test_gr_seqInfo_trim.py index d6b507e..4416379 100644 --- a/tests/test_gr_seqInfo_trim.py +++ b/tests/test_gr_seqInfo_trim.py @@ -1,9 +1,11 @@ -from genomicranges.sequence_info import SeqInfo from random import random -from genomicranges.GenomicRanges import GenomicRanges -from iranges import IRanges -from biocframe import BiocFrame + import numpy as np +from biocframe import BiocFrame +from iranges import IRanges + +from genomicranges.GenomicRanges import GenomicRanges +from genomicranges.sequence_info import SeqInfo __author__ = "jkanche" __copyright__ = "jkanche" @@ -22,7 +24,7 @@ strand=["*", "-", "*", "+", "-"], mcols=BiocFrame( { - "score": range(0, 5), + "score": range(5), "GC": [random() for _ in range(5)], } ), diff --git a/tests/test_gr_set_ops.py b/tests/test_gr_set_ops.py index 2722df8..dc03a25 100644 --- a/tests/test_gr_set_ops.py +++ b/tests/test_gr_set_ops.py @@ -1,8 +1,10 @@ -from genomicranges.GenomicRanges import GenomicRanges from random import random -from iranges import IRanges -from biocframe import BiocFrame + import numpy as np +from biocframe import BiocFrame +from iranges import IRanges + +from genomicranges.GenomicRanges import GenomicRanges __author__ = "jkanche" __copyright__ = "jkanche" @@ -110,7 +112,7 @@ def test_intersect_complex(): strand=["*", "-", "-", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), diff --git a/tests/test_gr_subtract.py b/tests/test_gr_subtract.py index 7f24f32..4eceec8 100644 --- a/tests/test_gr_subtract.py +++ b/tests/test_gr_subtract.py @@ -1,7 +1,7 @@ import numpy as np from iranges import IRanges -from genomicranges import GenomicRanges, CompressedGenomicRangesList +from genomicranges import CompressedGenomicRangesList, GenomicRanges __author__ = "jkanche" __copyright__ = "jkanche" diff --git a/tests/test_gr_to_grl.py b/tests/test_gr_to_grl.py index d964acf..361bfcc 100644 --- a/tests/test_gr_to_grl.py +++ b/tests/test_gr_to_grl.py @@ -26,7 +26,7 @@ strand=["*", "-", "-", "*", "*", "+", "+", "+", "-", "-"], mcols=BiocFrame( { - "score": range(0, 10), + "score": range(10), "GC": [random() for _ in range(10)], } ), diff --git a/tests/test_ucsc.py b/tests/test_ucsc.py index 673a0dc..24949a8 100644 --- a/tests/test_ucsc.py +++ b/tests/test_ucsc.py @@ -1,4 +1,5 @@ import pytest + import genomicranges __author__ = "jkanche"