Skip to content

Commit

Permalink
Merge pull request #90 from paulsengroup/feature/modular-deps
Browse files Browse the repository at this point in the history
Make pandas and scipy optional deps
  • Loading branch information
robomics authored Oct 18, 2024
2 parents 778a0d4 + b490f97 commit 50cd57b
Show file tree
Hide file tree
Showing 13 changed files with 107 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/fuzzy-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ jobs:
conan remove --confirm "*"
- name: Build and install
run: pip install --verbose .
run: pip install --verbose '.[all]'

- name: Clean Conan cache (post-build)
if: steps.cache-conan.outputs.cache-hit != 'true'
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pip.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
echo 'CXX=clang++' >> $GITHUB_ENV
- name: Build and install
run: pip install --verbose '.[test]'
run: pip install --verbose '.[all,test]'

- name: Save Conan cache
uses: actions/cache/save@v4
Expand Down
29 changes: 24 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
[build-system]
requires = [
"conan>=2.0.5",
"nanobind==2.2.0", # This is required in order to run stubgen
"nanobind>=2", # This is required in order to run stubgen
"numpy",
"pandas>=2.1.0,!=2.2.0",
"pyarrow==17.0.0",
"pandas>=2.1,!=2.2.0",
"pyarrow>=14",
"scikit-build-core>=0.10",
"scipy",
"typing_extensions",
Expand All @@ -33,13 +33,32 @@ classifiers = [

dependencies = [
"numpy",
"pandas>=2.1.0,!=2.2.0",
"pyarrow>=14",
]

optional-dependencies.pandas = [
"pandas>=2.1.0,!=2.2.0",
]

optional-dependencies.scipy = [
"scipy",
]

optional-dependencies.all = [
"hictkpy[pandas,scipy]",
]

optional-dependencies.test = [
"pytest>=8.0"
"hictkpy[all]",
"pytest>=8.0",
]

optional-dependencies.dev = [
"hictkpy[all,test]",
"black>=24.10",
"clang-format>=19.1",
"isort>=5.13",
"gersemi>=0.15",
]

[tool.scikit-build]
Expand Down
4 changes: 0 additions & 4 deletions src/hictkpy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,6 @@ NB_MODULE(_hictkpy, m) {
throw std::runtime_error("failed to initialize pyarrow runtime");
}

[[maybe_unused]] auto np = nb::module_::import_("numpy");
[[maybe_unused]] auto pd = nb::module_::import_("pandas");
[[maybe_unused]] auto ss = nb::module_::import_("scipy.sparse");

m.attr("__hictk_version__") = hictk::config::version::str();

m.doc() = "Blazing fast toolkit to work with .hic and .cool files.";
Expand Down
2 changes: 1 addition & 1 deletion src/include/hictkpy/bin_table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace hictkpy {

template <typename File>
inline nanobind::object get_bins_from_file(const File &f) {
auto pd = nanobind::module_::import_("pandas");
auto pd = import_module_checked("pandas");

Dynamic1DA<std::int32_t> chrom_ids{};
Dynamic1DA<std::int32_t> starts{};
Expand Down
13 changes: 13 additions & 0 deletions src/include/hictkpy/nanobind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,16 @@ HICTKPY_DISABLE_WARNING_USELESS_CAST
#include <nanobind/stl/vector.h>
HICTKPY_DISABLE_WARNING_POP
// clang-format on

#include <string>

inline nanobind::module_ import_module_checked(const std::string& module_name) {
try {
return nanobind::module_::import_(module_name.c_str());
} catch (nanobind::python_error& e) {
// NOLINTNEXTLINE(*-pro-type-vararg)
nanobind::raise_from(e, PyExc_ModuleNotFoundError,
"To enable %s support, please install %s with: pip install 'hictkpy[%s]'",
module_name.c_str(), module_name.c_str(), module_name.c_str());
}
}
3 changes: 3 additions & 0 deletions src/pixel_selector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ nb::object PixelSelector::to_arrow(std::string_view span) const {
}

nb::object PixelSelector::to_pandas(std::string_view span) const {
import_module_checked("pandas");
return to_arrow(span).attr("to_pandas")();
}

Expand All @@ -222,6 +223,7 @@ template <typename N, typename PixelSelector>
}

nb::object PixelSelector::to_csr(std::string_view span) const {
import_module_checked("scipy");
const auto query_span = parse_span(span);

return std::visit(
Expand All @@ -237,6 +239,7 @@ nb::object PixelSelector::to_csr(std::string_view span) const {
}

nb::object PixelSelector::to_coo(std::string_view span) const {
import_module_checked("scipy");
return to_csr(span).attr("tocoo")(false);
}

Expand Down
12 changes: 11 additions & 1 deletion test/test_fetch_dense.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import pathlib

import numpy as np
import numpy.typing as npt
import pytest
from scipy.linalg import issymmetric

import hictkpy

Expand All @@ -21,6 +21,16 @@
)


def issymmetric(m: npt.NDArray) -> bool:
assert m.ndim == 2
if m.size == 0:
return True
if m.shape[0] != m.shape[1]:
return False

return np.allclose(m, m.T, atol=0.0, rtol=0.0)


class TestClass:
def test_genome_wide(self, file, resolution):
f = hictkpy.File(file, resolution)
Expand Down
10 changes: 10 additions & 0 deletions test/test_fetch_df.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
)


def pandas_avail() -> bool:
try:
import pandas
except ModuleNotFoundError:
return False

return True


@pytest.mark.skipif(not pandas_avail(), reason="pandas is not available")
class TestClass:
def test_genome_wide(self, file, resolution):
f = hictkpy.File(file, resolution)
Expand Down
10 changes: 10 additions & 0 deletions test/test_fetch_sparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@
)


def scipy_avail() -> bool:
try:
import scipy
except ModuleNotFoundError:
return False

return True


@pytest.mark.skipif(not scipy_avail(), reason="scipy is not available")
class TestClass:
def test_genome_wide(self, file, resolution):
f = hictkpy.File(file, resolution)
Expand Down
14 changes: 13 additions & 1 deletion test/test_file_accessors.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,25 @@
)


def pandas_avail() -> bool:
try:
import pandas
except ModuleNotFoundError:
return False

return True


class TestClass:
@pytest.mark.skipif(not pandas_avail(), reason="pandas is not available")
def test_attributes(self, file, resolution):
f = hictkpy.File(file, resolution)
assert f.resolution() == 100_000
# assert f.nchroms() == 8 # TODO enable after merging https://github.com/paulsengroup/hictk/pull/294
assert f.nbins() == 1380

assert "chr2L" in f.chromosomes()

assert len(f.bins()) == 1380
assert len(f.chromosomes()) == 8

Expand All @@ -47,4 +59,4 @@ def test_normalizations(self, file, resolution):

name = "weight" if f.is_cooler() else "ICE"
weights = f.weights(name)
assert len(weights) == len(f.bins())
assert len(weights) == f.nbins()
10 changes: 10 additions & 0 deletions test/test_file_creation_cool.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@
)


def pandas_avail() -> bool:
try:
import pandas
except ModuleNotFoundError:
return False

return True


@pytest.mark.skipif(not pandas_avail(), reason="pandas is not available")
class TestClass:
@staticmethod
def setup_method():
Expand Down
10 changes: 10 additions & 0 deletions test/test_file_creation_hic.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@
)


def pandas_avail() -> bool:
try:
import pandas
except ModuleNotFoundError:
return False

return True


@pytest.mark.skipif(not pandas_avail(), reason="pandas is not available")
class TestClass:
@staticmethod
def setup_method():
Expand Down

0 comments on commit 50cd57b

Please sign in to comment.