Skip to content

Commit 80c464a

Browse files
authored
Migrating to ncls-cpp for performant interval and search operations (#152)
Bulk of the work was done in the IRanges package. Also update relevant tests & documentation.
1 parent 6249eaf commit 80c464a

12 files changed

Lines changed: 318 additions & 227 deletions

File tree

.github/workflows/publish-pypi.yml

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
# This workflow will install Python dependencies, run tests and lint with a single version of Python
2-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3-
41
name: Publish to PyPI
52

63
on:
@@ -19,10 +16,10 @@ jobs:
1916
steps:
2017
- uses: actions/checkout@v4
2118

22-
- name: Set up Python 3.11
19+
- name: Set up Python 3.12
2320
uses: actions/setup-python@v5
2421
with:
25-
python-version: 3.11
22+
python-version: 3.12
2623

2724
- name: Install dependencies
2825
run: |
@@ -33,6 +30,14 @@ jobs:
3330
run: |
3431
tox
3532
33+
- name: Build Project and Publish
34+
run: |
35+
python -m tox -e clean,build
36+
37+
# This uses the trusted publisher workflow so no token is required.
38+
- name: Publish to PyPI
39+
uses: pypa/gh-action-pypi-publish@release/v1
40+
3641
- name: Build docs
3742
run: |
3843
tox -e docs
@@ -45,11 +50,3 @@ jobs:
4550
branch: gh-pages # The branch the action should deploy to.
4651
folder: ./docs/_build/html
4752
clean: true # Automatically remove deleted files from the deploy branch
48-
49-
- name: Build Project and Publish
50-
run: |
51-
python -m tox -e clean,build
52-
53-
# This uses the trusted publisher workflow so no token is required.
54-
- name: Publish to PyPI
55-
uses: pypa/gh-action-pypi-publish@release/v1

.github/workflows/run-tests.yml

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,73 @@
1-
name: Run tests
1+
name: Test the library
22

33
on:
44
push:
5-
branches: [master]
5+
branches:
6+
- master # for legacy repos
7+
- main
68
pull_request:
9+
branches:
10+
- master # for legacy repos
11+
- main
12+
workflow_dispatch: # Allow manually triggering the workflow
13+
schedule:
14+
# Run roughly every 15 days at 00:00 UTC
15+
# (useful to check if updates on dependencies break the package)
16+
- cron: "0 0 1,16 * *"
17+
18+
permissions:
19+
contents: read
20+
21+
concurrency:
22+
group: >-
23+
${{ github.workflow }}-${{ github.ref_type }}-
24+
${{ github.event.pull_request.number || github.sha }}
25+
cancel-in-progress: true
726

827
jobs:
9-
build:
10-
runs-on: ubuntu-latest
28+
test:
1129
strategy:
1230
matrix:
13-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
14-
15-
name: Python ${{ matrix.python-version }}
31+
python: ["3.9", "3.10", "3.11", "3.12", "3.13"]
32+
platform:
33+
- ubuntu-latest
34+
- macos-latest
35+
# - windows-latest
36+
runs-on: ${{ matrix.platform }}
37+
name: Python ${{ matrix.python }}, ${{ matrix.platform }}
1638
steps:
1739
- uses: actions/checkout@v4
1840

19-
- name: Setup Python
20-
uses: actions/setup-python@v5
41+
- uses: actions/setup-python@v5
42+
id: setup-python
2143
with:
22-
python-version: ${{ matrix.python-version }}
23-
cache: "pip"
44+
python-version: ${{ matrix.python }}
2445

2546
- name: Install dependencies
2647
run: |
2748
python -m pip install --upgrade pip
28-
pip install tox
49+
pip install tox coverage
2950
30-
- name: Test with tox
31-
run: |
51+
- name: Run tests
52+
run: >-
53+
pipx run --python '${{ steps.setup-python.outputs.python-path }}'
3254
tox
55+
-- -rFEx --durations 10 --color yes --cov --cov-branch --cov-report=xml # pytest args
56+
57+
- name: Check for codecov token availability
58+
id: codecov-check
59+
shell: bash
60+
run: |
61+
if [ ${{ secrets.CODECOV_TOKEN }} != '' ]; then
62+
echo "codecov=true" >> $GITHUB_OUTPUT;
63+
else
64+
echo "codecov=false" >> $GITHUB_OUTPUT;
65+
fi
66+
67+
- name: Upload coverage reports to Codecov with GitHub Action
68+
uses: codecov/codecov-action@v5
69+
if: ${{ steps.codecov-check.outputs.codecov == 'true' }}
70+
env:
71+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
72+
slug: ${{ github.repository }}
73+
flags: ${{ matrix.platform }} - py${{ matrix.python }}

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## Version 0.7.0
4+
5+
- Changes to switch to LTLA/nclist-cpp in the iranges package for overlap and search operations.
6+
37
## Version 0.6.2 - 0.6.3
48

59
- Implement biocutil's `extract_row_names` generic.
@@ -92,7 +96,7 @@ This is a complete rewrite of both these classes following the functional paradi
9296

9397
The package does not rely on pandas anymore. While we try to provide backwards compatibility to construct a GenomicRanges object from a pandas dataframe using the `from_pandas` method, please note that the default constructor to genomic ranges does not accept a pandas data frame anymore!
9498

95-
Most range based methods have been reimplemented and the heavy lifting is done in the [IRanges package](https://github.com/BiocPy/IRanges) for interval operations. The package indirectly depends on [NCLS](https://github.com/pyranges/ncls) interval tree data structure to perform search and overlap operations.
99+
Most range based methods have been reimplemented and the heavy lifting is done in the [IRanges package](https://github.com/BiocPy/IRanges) for interval operations. The package depends on [nclist-cpp](https://github.com/LTLA/nclist-cpp) data structure to perform search and overlap operations.
96100

97101
Tests, documentation and readme has been updated to reflect these changes.
98102

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ pip install genomicranges[optional]
2424

2525
### From Bioinformatic file formats
2626

27+
> [!NOTE]
28+
> When reading genomic formats, `ends` are expected to be inclusive to be consistent with Bioconductor representations (& gff). If they are not, we recommend subtracting 1 from the `ends`.
29+
2730
#### From `biobear`
2831

2932
Although the parsing capabilities in this package are limited, the [biobear](https://github.com/wheretrue/biobear) library is designed for reading and searching various bioinformatics file formats, including FASTA, FASTQ, VCF, BAM, and GFF, or from an object store like S3. Users can esily convert these representations to `GenomicRanges` (or [read more here](https://www.wheretrue.dev/docs/exon/biobear/genomicranges-integration)):
@@ -44,8 +47,8 @@ print(len(gg), len(df))
4447
```
4548

4649
## output
47-
## 77 77
48-
50+
## 77 77> [!NOTE]
51+
> `ends` are expected to be inclusive to be consistent with Bioconductor representations. If they are not, we recommend subtracting 1 from the `ends`.
4952
5053
#### UCSC or GTF file
5154

docs/tutorial.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ kernelspec:
88

99
`GenomicRanges` is a Python package designed to handle genomic locations and facilitate genomic analysis. It is similar to Bioconductor's [GenomicRanges](https://bioconductor.org/packages/release/bioc/html/GenomicRanges.html) and uses the [IRanges](https://github.com/BiocPy/IRanges) package under the hood to manage and provide interval-based arithmetic operations.
1010

11-
An `IRanges` holds a **start** position and a **width**, and is typically used to represent coordinates along a genomic sequence. The interpretation of the **start** position depends on the application; for sequences, the **start** is usually a 1-based position, but other use cases may allow zero or even negative values, e.g., circular genomes. Ends are considered inclusive. `IRanges` uses [nested containment lists](https://github.com/pyranges/ncls) under the hood to perform fast overlap and search-based operations.
11+
An `IRanges` holds a **start** position and a **width**, and is typically used to represent coordinates along a genomic sequence. The interpretation of the **start** position depends on the application; for sequences, the **start** is usually a 1-based position, but other use cases may allow zero or even negative values, e.g., circular genomes. Ends are considered inclusive. `IRanges` uses [LTLa/nclist-cpp](https://github.com/LTLA/nclist-cpp) under the hood to perform fast overlap and search-based operations.
1212

1313
The package provides a `GenomicRanges` class to specify multiple genomic elements, typically where genes start and end. Genes are themselves made of many subregions, such as exons, and a `GenomicRangesList` enables the representation of this nested structure.
1414

pyproject.toml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,14 @@ version_scheme = "no-guess-dev"
1111
line-length = 120
1212
src = ["src"]
1313
# exclude = ["tests"]
14-
extend-ignore = ["F821"]
14+
lint.extend-ignore = ["F821"]
1515

16-
[tool.ruff.pydocstyle]
16+
[tool.ruff.lint.pydocstyle]
1717
convention = "google"
1818

1919
[tool.ruff.format]
2020
docstring-code-format = true
2121
docstring-code-line-length = 20
2222

23-
[tool.ruff.per-file-ignores]
23+
[tool.ruff.lint.per-file-ignores]
2424
"__init__.py" = ["E402", "F401"]
25-
26-
[tool.black]
27-
force-exclude = "__init__.py"

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ python_requires = >=3.9
5050
install_requires =
5151
importlib-metadata; python_version<"3.8"
5252
biocframe>=0.6.2
53-
iranges>=0.4.2
53+
iranges>=0.5.1
5454
biocutils>=0.2.1
5555
numpy
5656

0 commit comments

Comments
 (0)