Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
dfbd5e5
Changed all single quotes to double.
ahanden Jul 6, 2026
610c130
Fixed FA102
ahanden Jul 6, 2026
6c3a4c3
Fixed D212
ahanden Jul 6, 2026
3be0c93
fixed ANN204
ahanden Jul 6, 2026
7265bf0
fixed TCH001
ahanden Jul 6, 2026
099281d
fixed COM812
ahanden Jul 6, 2026
6b3df4a
fixed PYI036
ahanden Jul 6, 2026
1821cc7
fixed FA102
ahanden Jul 6, 2026
257d8f9
fixed PGH003
ahanden Jul 6, 2026
d673a6f
fixed D205
ahanden Jul 6, 2026
f811cbf
fixed UP004
ahanden Jul 6, 2026
48b4817
Address EM lints
ahanden Jul 6, 2026
eab996c
linted for PTH
ahanden Jul 6, 2026
4e4fc1a
linted for ISC
ahanden Jul 6, 2026
473e993
lintedfor PERF
ahanden Jul 6, 2026
278e3e9
linted for TCH
ahanden Jul 6, 2026
bb06c7d
Removed extra paranthesis from raise statements
ahanden Jul 6, 2026
9cb4c56
Removing unnused imports
ahanden Jul 6, 2026
0d5a5df
linted for ANN
ahanden Jul 6, 2026
90de421
more linting
ahanden Jul 6, 2026
8ee2012
more linting
ahanden Jul 6, 2026
6d8fce6
more linting
ahanden Jul 6, 2026
9868340
updated ruff rules
ahanden Jul 6, 2026
7e54a98
Updated documentation
ahanden Jul 8, 2026
e9953db
Fixed comp_map. Updated test imports and error handling.
ahanden Jul 10, 2026
ce58ac2
Minor linting. Updated ruff.toml for INP001.
ahanden Jul 10, 2026
7cda124
Moved ruff config to pyproject.toml. Changed isort format.
ahanden Jul 10, 2026
2a1f620
linted Q000
ahanden Jul 10, 2026
c773148
linted PTH in test
ahanden Jul 10, 2026
50f2b97
mypy typing
ahanden Jul 10, 2026
a5081df
mypy test. fixed error catch in analyze parse_args.
ahanden Jul 10, 2026
fd56bb8
More test linting. Added new rules to pyproject.toml.
ahanden Jul 10, 2026
f466110
Updated pyproject for numpy convention. Some reditools linting
ahanden Jul 10, 2026
ac78a91
Added documentation to most test code. Some more misc linting.
ahanden Jul 13, 2026
5aa7e0d
Finished test documentation.
ahanden Jul 13, 2026
168a77b
Fixed alignment_manager test case.
ahanden Jul 13, 2026
d662a01
Tweaked temp_file_manager methods. isort.
ahanden Jul 13, 2026
37774aa
WPS linting on test
ahanden Jul 13, 2026
8e5c3df
mypy test. Updated ruff rules in pyproject.toml
ahanden Jul 13, 2026
1292d04
Fixed save file path for region file list.
ahanden Jul 13, 2026
cc9d842
Simplified file paths in json_args
ahanden Jul 13, 2026
384152f
Made new bases constant. Fixed major thread count issue.
ahanden Jul 13, 2026
20b0fcd
Mad use of Genome class in test/fasta_file. Improved error message fo…
ahanden Jul 13, 2026
1044f55
Expanded mypy workflow to include test code.
ahanden Jul 13, 2026
bc841e9
Merge branch 'main' into more_linting
ahanden Jul 13, 2026
cb2bcfd
Made unittest error handling more specific for fasta files.
ahanden Jul 13, 2026
6d415a6
Removed extra line from logger.
ahanden Jul 13, 2026
20ea6cc
Specified logger socket error. isort.
ahanden Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/mypy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ on:
push:
paths:
- 'reditools/**py'
- 'test/**py'
pull_request:
paths:
- 'reditools/**py'
- 'test/**py'
jobs:
mypy:
runs-on: ubuntu-latest
Expand All @@ -27,4 +29,4 @@ jobs:
- name: Add mypy annotator
uses: pr-annotators/mypy-pr-annotator@v1.0.0
- name: Run mypy
run: uv run mypy reditools/
run: uv run mypy reditools/ test/
23 changes: 23 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,26 @@ issues = "https://github.com/BioinfoUNIBA/REDItools3/issues"

[project.optional-dependencies]
wps = ["flake8", "wemake-python-styleguide"]

[tool.isort]
multi_line_output=3
include_trailing_comma=true

[tool.ruff]
line-length = 80

[tool.ruff.lint]
select = ["ALL"]
external = ["WPS"]
ignore = [
"RUF100", "PYI034", "PLR0913", "FBT001", "FBT002", "D203", "D213",
"INP001", "S311", "I",
]

[tool.ruff.lint.per-file-ignores]
"test/__main__.py" = ["F401"]
"**/__init__.py" = ["F401"]
"test/**py" = ["PT", "SLF001", "FLY002"]

[tool.ruff.lint.pydocstyle]
convention = "numpy"
1 change: 1 addition & 0 deletions reditools/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""REDItools: RNA Editing Package."""
22 changes: 12 additions & 10 deletions reditools/__main__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"""CLI entry point for REDItools."""

import sys

from reditools.tools import analyze, annotate, find_repeats, index
from reditools.tools.analyze import main as analyze
from reditools.tools.annotate import main as annotate
from reditools.tools.find_repeats import main as find_repeats
from reditools.tools.index import main as index


def usage() -> None:
"""
Print the usage information for the REDItools3 toolkit.
"""
"""Print the usage information for the REDItools3 toolkit."""
usage_str = """usage: reditools {analyze,find-repeats,index,annotate}

REDItools3
Expand All @@ -22,15 +24,15 @@ def usage() -> None:

annotate Annotate REDItools RNA output with DNA output
"""
print(usage_str) # noqa: WPS421
print(usage_str) # noqa: WPS421 T201


if __name__ == '__main__':
if __name__ == "__main__":
toolkit = {
'analyze': analyze,
'find-repeats': find_repeats,
'index': index,
'annotate': annotate,
"analyze": analyze,
"find-repeats": find_repeats,
"index": index,
"annotate": annotate,
}
if len(sys.argv) > 1:
command = sys.argv.pop(1)
Expand Down
93 changes: 46 additions & 47 deletions reditools/alignment_file.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
"""A wrapper around pysam.AlignmentFile with integrated quality control."""
from __future__ import annotations

from types import TracebackType
from typing import Any, Collection, Iterator
from typing import TYPE_CHECKING

from pysam import AlignedSegment
from pysam.libcalignmentfile import AlignmentFile as PysamAlignmentFile

from reditools.region import Region
if TYPE_CHECKING:
from types import TracebackType
from typing import Any, Collection, Iterator

from pysam import AlignedSegment

from reditools.region import Region


class ReadQC:
"""
Perform quality control checks on aligned reads.
"""Perform quality control checks on aligned reads.

Parameters
----------
Expand All @@ -21,16 +26,16 @@ class ReadQC:
excluded_read_names : Collection[str] | None
A collection of read names to be excluded.
"""
_flags_to_keep = {0, 16, 83, 99, 147, 163}

_flags_to_keep = frozenset((0, 16, 83, 99, 147, 163))

def __init__(
self,
min_quality: int,
min_length: int,
excluded_read_names: Collection[str] | None,
):
"""
Initialize the ReadQC with quality and length thresholds.
) -> None:
"""Initialize the ReadQC with quality and length thresholds.

Parameters
----------
Expand All @@ -43,20 +48,20 @@ def __init__(
"""
self.min_quality = min_quality
self.min_length = min_length
self.excluded_read_names = excluded_read_names

self.check_list = [self.check_baseline]
if self.min_quality > 0:
self.check_list.append(self.check_quality)
if self.min_length > 0:
self.check_list.append(self.check_length)
if self.excluded_read_names:
self.excluded_read_names = set(self.excluded_read_names)
if excluded_read_names:
self.excluded_read_names = set(excluded_read_names)
self.check_list.append(self.check_excluded_read_names)
else:
self.excluded_read_names = set()

def check_baseline(self, read: AlignedSegment) -> bool:
"""
Check if the read passes baseline flag and tag requirements.
"""Check if the read passes baseline flag and tag requirements.

Parameters
----------
Expand All @@ -68,11 +73,10 @@ def check_baseline(self, read: AlignedSegment) -> bool:
bool
True if the read passes, False otherwise.
"""
return read.flag in self._flags_to_keep and not read.has_tag('SA')
return read.flag in self._flags_to_keep and not read.has_tag("SA")

def check_quality(self, read: AlignedSegment) -> bool:
"""
Check if the read passes the minimum mapping quality threshold.
"""Check if the read passes the minimum mapping quality threshold.

Parameters
----------
Expand All @@ -87,8 +91,7 @@ def check_quality(self, read: AlignedSegment) -> bool:
return read.mapping_quality >= self.min_quality

def check_length(self, read: AlignedSegment) -> bool:
"""
Check if the read passes the minimum length threshold.
"""Check if the read passes the minimum length threshold.

Parameters
----------
Expand All @@ -103,8 +106,7 @@ def check_length(self, read: AlignedSegment) -> bool:
return read.query_length >= self.min_length

def check_excluded_read_names(self, read: AlignedSegment) -> bool:
"""
Check if the read name is not in the excluded list.
"""Check if the read name is not in the excluded list.

Parameters
----------
Expand All @@ -116,11 +118,10 @@ def check_excluded_read_names(self, read: AlignedSegment) -> bool:
bool
True if the read name is not excluded, False otherwise.
"""
return read.query_name not in self.excluded_read_names # type: ignore
return read.query_name not in self.excluded_read_names

def run_check(self, read: AlignedSegment) -> bool:
"""
Run all configured quality control checks on the read.
"""Run all configured quality control checks on the read.

Parameters
----------
Expand All @@ -136,8 +137,7 @@ def run_check(self, read: AlignedSegment) -> bool:


class RTAlignmentFile:
"""
A wrapper around pysam.AlignmentFile with integrated quality control.
"""A wrapper around pysam.AlignmentFile with integrated quality control.

Parameters
----------
Expand All @@ -159,10 +159,9 @@ def __init__(
min_quality: int=0,
min_length: int=0,
excluded_read_names: Collection[str] | None=None,
**kwargs: Any,
**kwargs: Any, # noqa: ANN401
) -> None:
"""
Initialize the RTAlignmentFile.
"""Initialize the RTAlignmentFile.

Parameters
----------
Expand All @@ -177,14 +176,17 @@ def __init__(
**kwargs
Keyword arguments passed to pysam.AlignmentFile.
"""
kwargs['ignore_truncation'] = True
kwargs["ignore_truncation"] = True
self.alignment_file = PysamAlignmentFile(filename, **kwargs)
self.alignment_file.check_index()
self.readqc = ReadQC(min_quality, min_length, excluded_read_names)
if excluded_read_names is None:
excluded_names: Collection[str] = []
else:
excluded_names = excluded_read_names
self.readqc = ReadQC(min_quality, min_length, excluded_names)

def __enter__(self): # type: ignore
"""
Enter the runtime context related to this object.
def __enter__(self) -> RTAlignmentFile:
"""Enter the runtime context related to this object.

Returns
-------
Expand All @@ -195,20 +197,19 @@ def __enter__(self): # type: ignore

def __exit__(
self,
exc_type: type,
exc_value: Exception,
traceback: TracebackType,
typ: type[BaseException] | None,
exc: BaseException | None,
tb: TracebackType | None,
) -> None:
"""
Exit the runtime context related to this object.
"""Exit the runtime context related to this object.

Parameters
----------
exc_type : type | None
typ : type[BaseException] | None
The exception type.
exc_value : Exception | None
exc : BaseException | None
The exception value.
traceback : TracebackType | None
tb : TracebackType | None
The traceback.
"""
self.alignment_file.close()
Expand All @@ -217,9 +218,7 @@ def fetch_by_position(
self,
region: Region | str,
) -> Iterator[list[AlignedSegment]]:
"""
Fetch reads from the alignment file grouped by their reference start
position.
"""Fetch reads from the alignment file grouped by reference start.

Parameters
----------
Expand Down
Loading
Loading