From c39f54a1f3c677d5e66ee61e2e40590608d7ea8d Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Wed, 25 Oct 2023 17:42:01 -0400 Subject: [PATCH] Adapt package for Python 3.8 (#10) - In addition updates github action to test the package from python 3.8 to 3.12. - Most changes deal with registering dispatchers for scipy's sparray. --- .github/workflows/pypi-test.yml | 9 +++- setup.cfg | 4 +- src/biocgenerics/colnames.py | 6 +-- src/biocgenerics/combine.py | 17 +++++--- src/biocgenerics/combine_cols.py | 75 ++++++++++++++++++++++++-------- src/biocgenerics/combine_rows.py | 70 ++++++++++++++++++++++------- src/biocgenerics/combine_seqs.py | 74 +++++++++++++++++++++++-------- src/biocgenerics/rownames.py | 6 +-- src/biocgenerics/utils.py | 19 -------- tests/test_utils.py | 17 -------- 10 files changed, 194 insertions(+), 103 deletions(-) delete mode 100644 tests/test_utils.py diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml index 94e48dd..9dc019a 100644 --- a/.github/workflows/pypi-test.yml +++ b/.github/workflows/pypi-test.yml @@ -13,13 +13,18 @@ jobs: build: runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12' ] + name: Python ${{ matrix.python-version }} steps: - uses: actions/checkout@v2 - - name: Set up Python 3.9 + - name: Setup Python uses: actions/setup-python@v2 with: - python-version: 3.9 + python-version: ${{ matrix.python-version }} + cache: 'pip' - name: Install dependencies run: | python -m pip install --upgrade pip diff --git a/setup.cfg b/setup.cfg index b79e371..7e17e48 100644 --- a/setup.cfg +++ b/setup.cfg @@ -41,7 +41,7 @@ package_dir = =src # Require a min/specific Python version (comma-separated conditions) -# python_requires = >=3.8 +python_requires = >=3.8 # Add here dependencies of your project (line-separated), e.g. requests>=2.2,<3.0. # Version specifiers like >=2.2,<3.0 avoid problems due to API changes in @@ -50,7 +50,7 @@ package_dir = install_requires = importlib-metadata; python_version<"3.8" numpy - biocutils>=0.0.2 + biocutils>=0.0.4 [options.packages.find] where = src diff --git a/src/biocgenerics/colnames.py b/src/biocgenerics/colnames.py index 96b5eaa..2fae710 100644 --- a/src/biocgenerics/colnames.py +++ b/src/biocgenerics/colnames.py @@ -1,7 +1,7 @@ from functools import singledispatch from typing import List -from .utils import _is_package_installed +from biocutils.package_utils import is_package_installed __author__ = "jkanche" __copyright__ = "jkanche" @@ -27,7 +27,7 @@ def colnames(x) -> list: raise NotImplementedError(f"`colnames` is not supported for class: '{type(x)}'.") -if _is_package_installed("pandas") is True: +if is_package_installed("pandas") is True: from pandas import DataFrame @colnames.register(DataFrame) @@ -54,7 +54,7 @@ def set_colnames(x, names: List[str]): ) -if _is_package_installed("pandas") is True: +if is_package_installed("pandas") is True: from pandas import DataFrame @set_colnames.register(DataFrame) diff --git a/src/biocgenerics/combine.py b/src/biocgenerics/combine.py index 6aef7c4..51bcd86 100644 --- a/src/biocgenerics/combine.py +++ b/src/biocgenerics/combine.py @@ -1,6 +1,7 @@ from functools import singledispatch from typing import Any +from biocutils.package_utils import is_package_installed from numpy import ndarray from .combine_rows import combine_rows @@ -9,7 +10,6 @@ _is_1d_dense_arrays, _is_1d_sparse_arrays, _is_any_element_list, - _is_package_installed, ) __author__ = "jkanche" @@ -48,7 +48,7 @@ def _combine_dense_arrays(*x: ndarray): return combine_rows(*x) -if _is_package_installed("scipy") is True: +if is_package_installed("scipy") is True: import scipy.sparse as sp def _combine_sparse(*x): @@ -60,11 +60,18 @@ def _combine_sparse(*x): return combine_rows(*x) - combine.register(sp.sparray, _combine_sparse) - combine.register(sp.spmatrix, _combine_sparse) + try: + combine.register(sp.sparray, _combine_sparse) + except Exception: + pass + try: + combine.register(sp.spmatrix, _combine_sparse) + except Exception: + pass -if _is_package_installed("pandas") is True: + +if is_package_installed("pandas") is True: from pandas import DataFrame, Series @combine.register(Series) diff --git a/src/biocgenerics/combine_cols.py b/src/biocgenerics/combine_cols.py index 38d80a7..33e36ea 100644 --- a/src/biocgenerics/combine_cols.py +++ b/src/biocgenerics/combine_cols.py @@ -3,13 +3,10 @@ from warnings import warn from biocutils import is_list_of_type +from biocutils.package_utils import is_package_installed from numpy import hstack, ndarray -from .utils import ( - _convert_sparse_to_dense, - _do_arrays_match, - _is_package_installed, -) +from .utils import _convert_sparse_to_dense, _do_arrays_match __author__ = "jkanche" __copyright__ = "jkanche" @@ -76,44 +73,84 @@ def _combine_cols_dense_arrays(*x: ndarray): raise ValueError("All elements must be 2-dimensional matrices.") -if _is_package_installed("scipy") is True: +if is_package_installed("scipy") is True: import scipy.sparse as sp - def _combine_cols_sparse_arrays(*x): - if is_list_of_type(x, (sp.sparray, sp.spmatrix)): + def _combine_cols_sparse_matrices(*x): + if is_list_of_type(x, sp.spmatrix): sp_conc = sp.hstack(x) if _do_arrays_match(x, 0) is not True: raise ValueError("1st dimension does not match across all elements.") first = x[0] - if isinstance(first, (sp.csr_matrix, sp.csr_array)): + if isinstance(first, sp.csr_matrix): return sp_conc.tocsr() - elif isinstance(first, (sp.csc_matrix, sp.csc_array)): + elif isinstance(first, sp.csc_matrix): return sp_conc.tocsc() - elif isinstance(first, (sp.bsr_matrix, sp.bsr_array)): + elif isinstance(first, sp.bsr_matrix): return sp_conc.tobsr() - elif isinstance(first, (sp.coo_matrix, sp.coo_array)): + elif isinstance(first, sp.coo_matrix): return sp_conc.tocoo() - elif isinstance(first, (sp.dia_matrix, sp.dia_array)): + elif isinstance(first, sp.dia_matrix): return sp_conc.todia() - elif isinstance(first, (sp.lil_matrix, sp.lil_array)): + elif isinstance(first, sp.lil_matrix): return sp_conc.tolil() else: return sp_conc - warn("Not all elements are scipy sparse arrays.") + warn("Not all elements are scipy sparse matrices.") - if is_list_of_type(x, (ndarray, sp.sparray, sp.spmatrix)): + if is_list_of_type(x, (ndarray, sp.spmatrix)): return _generic_combine_cols_dense_sparse(x) raise ValueError("All elements must be 2-dimensional matrices.") - combine_cols.register(sp.sparray, _combine_cols_sparse_arrays) - combine_cols.register(sp.spmatrix, _combine_cols_sparse_arrays) + try: + def _combine_cols_sparse_arrays(*x): + if is_list_of_type(x, sp.sparray): + sp_conc = sp.hstack(x) -if _is_package_installed("pandas") is True: + if _do_arrays_match(x, 0) is not True: + raise ValueError( + "1st dimension does not match across all elements." + ) + + first = x[0] + if isinstance(first, sp.csr_array): + return sp_conc.tocsr() + elif isinstance(first, sp.csc_array): + return sp_conc.tocsc() + elif isinstance(first, sp.bsr_array): + return sp_conc.tobsr() + elif isinstance(first, sp.coo_array): + return sp_conc.tocoo() + elif isinstance(first, sp.dia_array): + return sp_conc.todia() + elif isinstance(first, sp.lil_array): + return sp_conc.tolil() + else: + return sp_conc + + warn("Not all elements are scipy sparse arrays.") + + if is_list_of_type(x, (ndarray, sp.sparray, sp.spmatrix)): + return _generic_combine_cols_dense_sparse(x) + + raise ValueError("All elements must be 2-dimensional arrays.") + + combine_cols.register(sp.sparray, _combine_cols_sparse_arrays) + except Exception: + pass + + try: + combine_cols.register(sp.spmatrix, _combine_cols_sparse_matrices) + except Exception: + pass + + +if is_package_installed("pandas") is True: from pandas import DataFrame, concat @combine_cols.register(DataFrame) diff --git a/src/biocgenerics/combine_rows.py b/src/biocgenerics/combine_rows.py index 9c157f5..917a5d5 100644 --- a/src/biocgenerics/combine_rows.py +++ b/src/biocgenerics/combine_rows.py @@ -3,12 +3,12 @@ from warnings import warn from biocutils import is_list_of_type +from biocutils.package_utils import is_package_installed from numpy import concatenate, ndarray from .utils import ( _convert_sparse_to_dense, _do_arrays_match, - _is_package_installed, ) __author__ = "jkanche" @@ -76,44 +76,84 @@ def _combine_rows_dense_arrays(*x: ndarray): raise ValueError("All elements must be 2-dimensional matrices.") -if _is_package_installed("scipy") is True: +if is_package_installed("scipy") is True: import scipy.sparse as sp - def _combine_rows_sparse_arrays(*x): - if is_list_of_type(x, (sp.sparray, sp.spmatrix)): + def _combine_rows_sparse_matrices(*x): + if is_list_of_type(x, sp.spmatrix): sp_conc = sp.vstack(x) if _do_arrays_match(x, 1) is not True: raise ValueError("2nd dimension does not match across all elements.") first = x[0] - if isinstance(first, (sp.csr_matrix, sp.csr_array)): + if isinstance(first, sp.csr_matrix): return sp_conc.tocsr() - elif isinstance(first, (sp.csc_matrix, sp.csc_array)): + elif isinstance(first, sp.csc_matrix): return sp_conc.tocsc() - elif isinstance(first, (sp.bsr_matrix, sp.bsr_array)): + elif isinstance(first, sp.bsr_matrix): return sp_conc.tobsr() - elif isinstance(first, (sp.coo_matrix, sp.coo_array)): + elif isinstance(first, sp.coo_matrix): return sp_conc.tocoo() - elif isinstance(first, (sp.dia_matrix, sp.dia_array)): + elif isinstance(first, sp.dia_matrix): return sp_conc.todia() - elif isinstance(first, (sp.lil_matrix, sp.lil_array)): + elif isinstance(first, sp.lil_matrix): return sp_conc.tolil() else: return sp_conc - warn("Not all elements are scipy sparse arrays.") + warn("Not all elements are scipy sparse matrices.") - if is_list_of_type(x, (ndarray, sp.sparray, sp.spmatrix)): + if is_list_of_type(x, (ndarray, sp.spmatrix)): return _generic_combine_rows_dense_sparse(x) raise ValueError("All elements must be 2-dimensional matrices.") - combine_rows.register(sp.sparray, _combine_rows_sparse_arrays) - combine_rows.register(sp.spmatrix, _combine_rows_sparse_arrays) + try: + def _combine_rows_sparse_arrays(*x): + if is_list_of_type(x, sp.sparray): + sp_conc = sp.vstack(x) -if _is_package_installed("pandas") is True: + if _do_arrays_match(x, 1) is not True: + raise ValueError( + "2nd dimension does not match across all elements." + ) + + first = x[0] + if isinstance(first, sp.csr_array): + return sp_conc.tocsr() + elif isinstance(first, sp.csc_array): + return sp_conc.tocsc() + elif isinstance(first, sp.bsr_array): + return sp_conc.tobsr() + elif isinstance(first, sp.coo_array): + return sp_conc.tocoo() + elif isinstance(first, sp.dia_array): + return sp_conc.todia() + elif isinstance(first, sp.lil_array): + return sp_conc.tolil() + else: + return sp_conc + + warn("Not all elements are scipy sparse arrays.") + + if is_list_of_type(x, (ndarray, sp.sparray, sp.spmatrix)): + return _generic_combine_rows_dense_sparse(x) + + raise ValueError("All elements must be 2-dimensional arrays.") + + combine_rows.register(sp.sparray, _combine_rows_sparse_arrays) + except Exception: + pass + + try: + combine_rows.register(sp.spmatrix, _combine_rows_sparse_matrices) + except Exception: + pass + + +if is_package_installed("pandas") is True: from pandas import DataFrame, concat @combine_rows.register(DataFrame) diff --git a/src/biocgenerics/combine_seqs.py b/src/biocgenerics/combine_seqs.py index 62863b2..acdbafc 100644 --- a/src/biocgenerics/combine_seqs.py +++ b/src/biocgenerics/combine_seqs.py @@ -4,13 +4,13 @@ from warnings import warn from biocutils import is_list_of_type +from biocutils.package_utils import is_package_installed from numpy import concatenate, ndarray from .utils import ( _convert_1d_sparse_to_dense, _is_1d_dense_arrays, _is_1d_sparse_arrays, - _is_package_installed, ) __author__ = "jkanche" @@ -111,48 +111,86 @@ def _combine_seqs_dense_arrays(*x: ndarray): return _generic_coerce_list(x) -if _is_package_installed("scipy") is True: +if is_package_installed("scipy") is True: import scipy.sparse as sp - def _combine_seqs_sparse_arrays(*x): - print("here") - print(x) - if is_list_of_type(x, (sp.sparray, sp.spmatrix)): + def _combine_seqs_sparse_matrices(*x): + if is_list_of_type(x, sp.spmatrix): sp_conc = sp.hstack(x) if _is_1d_sparse_arrays(x) is not True: raise ValueError( - "Not all elements are 1-dimensional arrays, use `combine_rows` instead." + "Not all elements are 1-dimensional matrices, use `combine_rows` instead." ) first = x[0] - if isinstance(first, (sp.csr_matrix, sp.csr_array)): + if isinstance(first, sp.csr_matrix): return sp_conc.tocsr() - elif isinstance(first, (sp.csc_matrix, sp.csc_array)): + elif isinstance(first, sp.csc_matrix): return sp_conc.tocsc() - elif isinstance(first, (sp.bsr_matrix, sp.bsr_array)): + elif isinstance(first, sp.bsr_matrix): return sp_conc.tobsr() - elif isinstance(first, (sp.coo_matrix, sp.coo_array)): + elif isinstance(first, sp.coo_matrix): return sp_conc.tocoo() - elif isinstance(first, (sp.dia_matrix, sp.dia_array)): + elif isinstance(first, sp.dia_matrix): return sp_conc.todia() - elif isinstance(first, (sp.lil_matrix, sp.lil_array)): + elif isinstance(first, sp.lil_matrix): return sp_conc.tolil() else: return sp_conc - warn("Not all elements are scipy sparse arrays.") + warn("Not all elements are scipy sparse matrices.") - if is_list_of_type(x, (ndarray, sp.sparray, sp.spmatrix)): + if is_list_of_type(x, (ndarray, sp.spmatrix)): return _generic_combine_seqs_dense_sparse(x) return _generic_coerce_list(x) - combine_seqs.register(sp.sparray, _combine_seqs_sparse_arrays) - combine_seqs.register(sp.spmatrix, _combine_seqs_sparse_arrays) + try: + + def _combine_seqs_sparse_arrays(*x): + if is_list_of_type(x, sp.sparray): + sp_conc = sp.hstack(x) + + if _is_1d_sparse_arrays(x) is not True: + raise ValueError( + "Not all elements are 1-dimensional arrays, use `combine_rows` instead." + ) + + first = x[0] + if isinstance(first, sp.csr_array): + return sp_conc.tocsr() + elif isinstance(first, sp.csc_array): + return sp_conc.tocsc() + elif isinstance(first, sp.bsr_array): + return sp_conc.tobsr() + elif isinstance(first, sp.coo_array): + return sp_conc.tocoo() + elif isinstance(first, sp.dia_array): + return sp_conc.todia() + elif isinstance(first, sp.lil_array): + return sp_conc.tolil() + else: + return sp_conc + + warn("Not all elements are scipy sparse arrays.") + + if is_list_of_type(x, (ndarray, sp.sparray, sp.spmatrix)): + return _generic_combine_seqs_dense_sparse(x) + + return _generic_coerce_list(x) + + combine_seqs.register(sp.sparray, _combine_seqs_sparse_arrays) + except Exception: + pass + + try: + combine_seqs.register(sp.spmatrix, _combine_seqs_sparse_matrices) + except Exception: + pass -if _is_package_installed("pandas") is True: +if is_package_installed("pandas") is True: from pandas import Series, concat @combine_seqs.register(Series) diff --git a/src/biocgenerics/rownames.py b/src/biocgenerics/rownames.py index e8e0d94..1cf22b3 100644 --- a/src/biocgenerics/rownames.py +++ b/src/biocgenerics/rownames.py @@ -1,7 +1,7 @@ from functools import singledispatch from typing import Any, List -from .utils import _is_package_installed +from biocutils.package_utils import is_package_installed __author__ = "jkanche" __copyright__ = "jkanche" @@ -27,7 +27,7 @@ def rownames(x) -> List[str]: raise NotImplementedError(f"`rownames` do not exist for class: '{type(x)}'.") -if _is_package_installed("pandas") is True: +if is_package_installed("pandas") is True: from pandas import DataFrame @rownames.register(DataFrame) @@ -52,7 +52,7 @@ def set_rownames(x: Any, names: List[str]): raise NotImplementedError(f"Cannot set `rownames` for class: {type(x)}") -if _is_package_installed("pandas") is True: +if is_package_installed("pandas") is True: from pandas import DataFrame @set_rownames.register(DataFrame) diff --git a/src/biocgenerics/utils.py b/src/biocgenerics/utils.py index 6a531c5..721268c 100644 --- a/src/biocgenerics/utils.py +++ b/src/biocgenerics/utils.py @@ -72,25 +72,6 @@ def _do_arrays_match(x, dim: int) -> bool: return all(y == first for y in all_shapes) -def _is_package_installed(package_name: str) -> bool: - """Check if the package is installed. - - Args: - package_name (str): Package name. - - Returns: - bool: True if package is installed, otherwise False. - """ - _installed = False - try: - exec(f"import {package_name}") - _installed = True - except Exception: - pass - - return _installed - - def _is_any_element_list(x, target_type) -> bool: """Check if ``x`` is a list and any of the elements are of the ``target_type``. diff --git a/tests/test_utils.py b/tests/test_utils.py deleted file mode 100644 index 7afc9f9..0000000 --- a/tests/test_utils.py +++ /dev/null @@ -1,17 +0,0 @@ -from biocgenerics.utils import _is_package_installed - -__author__ = "jkanche" -__copyright__ = "jkanche" -__license__ = "MIT" - - -def test_for_pandas(): - pkg = _is_package_installed("pandas") - - assert pkg is True - - -def test_for_scipy(): - pkg = _is_package_installed("scipy") - - assert pkg is True