From 06ad73b982e17a1b5770cf70f4f5914ed8289187 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Tue, 31 Oct 2023 12:12:06 -0400 Subject: [PATCH] Update packaging metadata and config (#1). Github actions, pre commit configurations, linting etc. --- .github/workflows/pypi-publish.yml | 51 +++++++++++ .github/workflows/pypi-test.yml | 40 ++++++++ .pre-commit-config.yaml | 52 +++++++++++ AUTHORS.md | 1 + docs/conf.py | 14 ++- docs/requirements.txt | 2 + pyproject.toml | 15 +++ setup.cfg | 17 ++-- setup.py | 10 +- src/iranges/IRanges.py | 142 ++++++++++++++++------------- src/iranges/__init__.py | 2 +- tests/conftest.py | 11 +-- tests/test_IRanges.py | 105 ++++++++++----------- tests/test_skeleton.py | 25 ----- 14 files changed, 329 insertions(+), 158 deletions(-) create mode 100644 .github/workflows/pypi-publish.yml create mode 100644 .github/workflows/pypi-test.yml create mode 100644 .pre-commit-config.yaml delete mode 100644 tests/test_skeleton.py diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml new file mode 100644 index 0000000..7b591a2 --- /dev/null +++ b/.github/workflows/pypi-publish.yml @@ -0,0 +1,51 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Publish to PyPI + +on: + push: + tags: "*" + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Set up Python 3.9 + uses: actions/setup-python@v2 + with: + python-version: 3.9 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest tox + # - name: Lint with flake8 + # run: | + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with tox + run: | + tox + - name: Build docs + run: | + tox -e docs + - run: touch ./docs/_build/html/.nojekyll + - name: GH Pages Deployment + uses: JamesIves/github-pages-deploy-action@4.1.3 + with: + branch: gh-pages # The branch the action should deploy to. + folder: ./docs/_build/html + clean: true # Automatically remove deleted files from the deploy branch + - name: Build Project and Publish + run: | + python -m tox -e clean,build + - name: Publish package + uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 + with: + user: __token__ + password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml new file mode 100644 index 0000000..9dc019a --- /dev/null +++ b/.github/workflows/pypi-test.yml @@ -0,0 +1,40 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Test the library + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +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: Setup Python + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest tox + # - name: Lint with flake8 + # run: | + # # stop the build if there are Python syntax errors or undefined names + # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + # # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with tox + run: | + tox diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..3cb6861 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,52 @@ +exclude: '^docs/conf.py' + +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: check-added-large-files + - id: check-ast + - id: check-json + - id: check-merge-conflict + - id: check-xml + - id: check-yaml + - id: debug-statements + - id: end-of-file-fixer + - id: requirements-txt-fixer + - id: mixed-line-ending + args: ['--fix=auto'] # replace 'auto' with 'lf' to enforce Linux/Mac line endings or 'crlf' for Windows + +- repo: https://github.com/PyCQA/docformatter + rev: v1.7.5 + hooks: + - id: docformatter + additional_dependencies: [tomli] + args: [--in-place, --wrap-descriptions=120, --wrap-summaries=120] + # --config, ./pyproject.toml + +- repo: https://github.com/psf/black + rev: 23.10.1 + hooks: + - id: black + language_version: python3 + +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.1.3 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix] + +## If like to embrace black styles even in the docs: +# - repo: https://github.com/asottile/blacken-docs +# rev: v1.13.0 +# hooks: +# - id: blacken-docs +# additional_dependencies: [black] + +## Check for misspells in documentation files: +# - repo: https://github.com/codespell-project/codespell +# rev: v2.2.5 +# hooks: +# - id: codespell diff --git a/AUTHORS.md b/AUTHORS.md index 3e241a3..4bb189d 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,3 +1,4 @@ # Contributors * LTLA [infinite.monkeys.with.keyboards@gmail.com](mailto:infinite.monkeys.with.keyboards@gmail.com) +* jkanche [jayaram.kancherla@gmail.com](mailto:jayaram.kancherla@gmail.com) diff --git a/docs/conf.py b/docs/conf.py index d374d8e..35ff62c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -72,6 +72,7 @@ "sphinx.ext.ifconfig", "sphinx.ext.mathjax", "sphinx.ext.napoleon", + "sphinx_autodoc_typehints", ] # Add any paths that contain templates here, relative to this directory. @@ -166,12 +167,23 @@ # If this is True, todo emits a warning for each TODO entries. The default is False. todo_emit_warnings = True +autodoc_default_options = { + # 'members': 'var1, var2', + # 'member-order': 'bysource', + 'special-members': True, + 'undoc-members': True, + 'exclude-members': '__weakref__, __dict__, __str__, __module__, __init__' +} + +autosummary_generate = True +autosummary_imported_members = True + # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = "alabaster" +html_theme = "furo" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/docs/requirements.txt b/docs/requirements.txt index 0990c2a..daecbf1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,6 +1,8 @@ +furo # Requirements file for ReadTheDocs, check .readthedocs.yml. # To build the module reference correctly, make sure every external package # under `install_requires` in `setup.cfg` is also listed here! # sphinx_rtd_theme myst-parser[linkify] sphinx>=3.2.1 +sphinx-autodoc-typehints diff --git a/pyproject.toml b/pyproject.toml index 89a5bed..0514df9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,18 @@ build-backend = "setuptools.build_meta" # For smarter version schemes and other configuration options, # check out https://github.com/pypa/setuptools_scm version_scheme = "no-guess-dev" + +[tool.ruff] +line-length = 120 +src = ["src"] +exclude = ["tests"] +extend-ignore = ["F821"] + +[tool.ruff.pydocstyle] +convention = "google" + +[tool.ruff.per-file-ignores] +"__init__.py" = ["E402", "F401"] + +[tool.black] +force-exclude = "__init__.py" diff --git a/setup.cfg b/setup.cfg index f09a5fc..fe36e84 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,17 +5,17 @@ [metadata] name = IRanges -description = Add a short description here! +description = Python implementation of the [**IRanges**](https://bioconductor.org/packages/IRanges) Bioconductor package. author = LTLA author_email = infinite.monkeys.with.keyboards@gmail.com license = MIT license_files = LICENSE.txt long_description = file: README.md long_description_content_type = text/markdown; charset=UTF-8; variant=GFM -url = https://github.com/pyscaffold/pyscaffold/ +url = https://github.com/biocpy/iranges # Add here related links, for example: project_urls = - Documentation = https://pyscaffold.org/ + Documentation = https://github.com/biocpy/iranges # Source = https://github.com/pyscaffold/pyscaffold/ # Changelog = https://pyscaffold.org/en/latest/changelog.html # Tracker = https://github.com/pyscaffold/pyscaffold/issues @@ -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 @@ -49,8 +49,8 @@ package_dir = # For more information, check out https://semver.org/. install_requires = importlib-metadata; python_version<"3.8" - biocutils - biocframe + biocutils>=0.0.6 + biocframe>=0.4.0 [options.packages.find] @@ -62,12 +62,17 @@ exclude = # Add here additional requirements for extra features, to install with: # `pip install IRanges[PDF]` like: # PDF = ReportLab; RXP +optional = + numpy # Add here test requirements (semicolon/line-separated) testing = setuptools pytest pytest-cov + biocframe + biocutils + numpy [options.entry_points] # Add here console scripts like: diff --git a/setup.py b/setup.py index 4f356aa..d08937a 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,8 @@ -""" - Setup file for IRanges. - Use setup.cfg to configure your project. +"""Setup file for IRanges. Use setup.cfg to configure your project. - This file was generated with PyScaffold 4.5. - PyScaffold helps you to put up the scaffold of your new Python project. - Learn more under: https://pyscaffold.org/ +This file was generated with PyScaffold 4.5. +PyScaffold helps you to put up the scaffold of your new Python project. +Learn more under: https://pyscaffold.org/ """ from setuptools import setup diff --git a/src/iranges/IRanges.py b/src/iranges/IRanges.py index b25959b..1d7f98b 100644 --- a/src/iranges/IRanges.py +++ b/src/iranges/IRanges.py @@ -7,32 +7,31 @@ class IRanges: - """ - A collection of integer ranges, equivalent to the ``IRanges`` class from - the Bioconductor package of the same name. This holds a start position and - a width, and is most typically used to represent coordinates along some - 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. + """A collection of integer ranges, equivalent to the ``IRanges`` class from the Bioconductor package of the same + name. + + This holds a start position and a width, and is most typically used to represent coordinates along some 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. """ def __init__( - self, - start: Sequence[int], - width: Sequence[int], - names: Optional[Sequence[str]] = None, - mcols: Optional[BiocFrame] = None, - metadata: Optional[Dict] = None, + self, + start: Sequence[int], + width: Sequence[int], + names: Optional[Sequence[str]] = None, + mcols: Optional[BiocFrame] = None, + metadata: Optional[Dict] = None, validate: bool = True, ): """ Args: - start: + start: Sequence of integers containing the start position for each range. All values should fall within the range that can be represented by a 32-bit signed integer. - width: + width: Sequence of integers containing the width for each range. This should be of the same length as ``start``. All values should be non-negative and fall within the range that can be represented @@ -111,7 +110,9 @@ def _validate_mcols(self): if not isinstance(self._mcols, BiocFrame): raise TypeError("'mcols' should be a BiocFrame") if self._mcols.shape[0] != len(self._start): - raise ValueError("number of rows of 'mcols' should be equal to length of 'start'") + raise ValueError( + "number of rows of 'mcols' should be equal to length of 'start'" + ) def _sanitize_metadata(self, metadata): if metadata is None: @@ -183,14 +184,14 @@ def _define_output(self, in_place): def set_start(self, start: Sequence[int], in_place: bool = False) -> "IRanges": """ Args: - start: + start: Sequence of start positions, see the constructor for details. - in_place: + in_place: Whether to modify the object in place. Returns: - If ``in_place = False``, a new ``IRanges`` is returned with the + If ``in_place = False``, a new ``IRanges`` is returned with the modified start positions. Otherwise, the current object is directly modified and a reference to it is returned. """ @@ -203,10 +204,10 @@ def set_start(self, start: Sequence[int], in_place: bool = False) -> "IRanges": def set_width(self, width: Sequence[int], in_place: bool = False) -> "IRanges": """ Args: - width: + width: Sequence of widths, see the constructor for details. - in_place: + in_place: Whether to modify the object in place. Returns: @@ -219,13 +220,15 @@ def set_width(self, width: Sequence[int], in_place: bool = False) -> "IRanges": output._validate_width() return output - def set_names(self, names: Optional[Sequence[str]], in_place: bool = False) -> "IRanges": + def set_names( + self, names: Optional[Sequence[str]], in_place: bool = False + ) -> "IRanges": """ Args: - names: + names: Sequence of names or None, see the constructor for details. - in_place: + in_place: Whether to modify the object in place. Returns: @@ -238,14 +241,16 @@ def set_names(self, names: Optional[Sequence[str]], in_place: bool = False) -> " output._validate_names() return output - def set_mcols(self, mcols: Optional[BiocFrame], in_place: bool = False) -> "IRanges": + def set_mcols( + self, mcols: Optional[BiocFrame], in_place: bool = False + ) -> "IRanges": """ Args: - mcols: + mcols: Data frame of additional columns, see the constructor for details. - in_place: + in_place: Whether to modify the object in place. Returns: @@ -258,13 +263,15 @@ def set_mcols(self, mcols: Optional[BiocFrame], in_place: bool = False) -> "IRan output._validate_mcols() return output - def set_metadata(self, metadata: Optional[Dict], in_place: bool = False) -> "IRanges": + def set_metadata( + self, metadata: Optional[Dict], in_place: bool = False + ) -> "IRanges": """ Args: - metadata: + metadata: Additional metadata. - in_place: + in_place: Whether to modify the object in place. Returns: @@ -288,10 +295,12 @@ def __len__(self) -> int: """ return len(self._start) - def __getitem__(self, subset: Union[Sequence, int, str, bool, slice, range]) -> "IRanges": + def __getitem__( + self, subset: Union[Sequence, int, str, bool, slice, range] + ) -> "IRanges": """ Args: - subset: + subset: Integer indices, a boolean filter, or (if the current object is named) names specifying the ranges to be extracted, see :py:meth:`~biocutils.normalize_subscript.normalize_subscript`. @@ -301,17 +310,19 @@ def __getitem__(self, subset: Union[Sequence, int, str, bool, slice, range]) -> """ idx, scalar = ut.normalize_subscript(subset, len(self), self._names) return type(self)( - start = self._start[idx], - width = self._width[idx], - names = ut.subset(self._names, idx) if self._names is not None else None, - mcols = self._mcols[list(idx),:], # doesn't support ranges yet. - metadata = self._metadata + start=self._start[idx], + width=self._width[idx], + names=ut.subset(self._names, idx) if self._names is not None else None, + mcols=self._mcols[list(idx), :], # doesn't support ranges yet. + metadata=self._metadata, ) - def __setitem__(self, args: Union[Sequence, int, str, bool, slice, range], value: "IRanges"): + def __setitem__( + self, args: Union[Sequence, int, str, bool, slice, range], value: "IRanges" + ): """ Args: - subset: + subset: Integer indices, a boolean filter, or (if the current object is named) names specifying the ranges to be replaced, see :py:meth:`~biocutils.normalize_subscript.normalize_subscript`. @@ -326,16 +337,16 @@ def __setitem__(self, args: Union[Sequence, int, str, bool, slice, range], value idx, scalar = ut.normalize_subscript(args, len(self), self._names) self._start[idx] = value._start self._width[idx] = value._width -# self._mcols[list(idx),:] = value._mcols # doesn't support ranges yet. + # self._mcols[list(idx),:] = value._mcols # doesn't support ranges yet. if value._names is not None: if self._names is None: - self._names = [''] * len(self) + self._names = [""] * len(self) for i, j in enumerate(idx): self._names[j] = value._names[i] elif self._names is not None: for i, j in enumerate(idx): - self._names[j] = '' + self._names[j] = "" ################## #### Printing #### @@ -364,7 +375,16 @@ def __str__(self): nranges = len(self) nmcols = self._mcols.shape[1] # TODO: clean up later. - return "IRanges object with " + str(nranges) + " range" + ("" if nranges == 1 else "s") + " and " + str(nmcols) + " metadata column" + ("" if nmcols == 1 else "s") + return ( + "IRanges object with " + + str(nranges) + + " range" + + ("" if nranges == 1 else "s") + + " and " + + str(nmcols) + + " metadata column" + + ("" if nmcols == 1 else "s") + ) ################# #### Copying #### @@ -376,12 +396,12 @@ def __copy__(self): A shallow copy of this object. """ return type(self)( - start = self._start, - width = self._width, - names = self._names, - mcols = self._mcols, - metadata = self._metadata, - validate = False + start=self._start, + width=self._width, + names=self._names, + mcols=self._mcols, + metadata=self._metadata, + validate=False, ) def __deepcopy__(self, memo): @@ -393,12 +413,12 @@ def __deepcopy__(self, memo): A deep copy of this object. """ return type(self)( - start = deepcopy(self._start, memo), - width = deepcopy(self._width, memo), - names = deepcopy(self._names, memo), - mcols = deepcopy(self._mcols, memo), - metadata = deepcopy(self._metadata, memo), - validate = False + start=deepcopy(self._start, memo), + width=deepcopy(self._width, memo), + names=deepcopy(self._names, memo), + mcols=deepcopy(self._mcols, memo), + metadata=deepcopy(self._metadata, memo), + validate=False, ) @@ -420,10 +440,10 @@ def _combine_IRanges(*x: IRanges) -> IRanges: all_names += [""] * len(y) return IRanges( - start = combine_seqs(*[y._start for y in x]), - width = combine_seqs(*[y._width for y in x]), - names = all_names, - mcols = combine_rows(*[y._mcols for y in x]), - metadata = x[0]._metadata, - validate = False + start=combine_seqs(*[y._start for y in x]), + width=combine_seqs(*[y._width for y in x]), + names=all_names, + mcols=combine_rows(*[y._mcols for y in x]), + metadata=x[0]._metadata, + validate=False, ) diff --git a/src/iranges/__init__.py b/src/iranges/__init__.py index 041b33c..26323b6 100644 --- a/src/iranges/__init__.py +++ b/src/iranges/__init__.py @@ -15,4 +15,4 @@ finally: del version, PackageNotFoundError -from .IRanges import * +from .IRanges import IRanges diff --git a/tests/conftest.py b/tests/conftest.py index 16c2d92..50903ea 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +1,9 @@ -""" - Dummy conftest.py for iranges. +"""Dummy conftest.py for iranges. - If you don't know what this is for, just leave it empty. - Read more about conftest.py under: - - https://docs.pytest.org/en/stable/fixture.html - - https://docs.pytest.org/en/stable/writing_plugins.html +If you don't know what this is for, just leave it empty. +Read more about conftest.py under: +- https://docs.pytest.org/en/stable/fixture.html +- https://docs.pytest.org/en/stable/writing_plugins.html """ # import pytest diff --git a/tests/test_IRanges.py b/tests/test_IRanges.py index cd98f50..d20c908 100644 --- a/tests/test_IRanges.py +++ b/tests/test_IRanges.py @@ -1,21 +1,22 @@ -from iranges import IRanges -from biocframe import BiocFrame +import copy + import numpy as np import pytest -import copy +from biocframe import BiocFrame from biocgenerics import combine_seqs +from iranges import IRanges def test_IRanges_basic(): - starts = [1,2,3,4] - widths = [4,5,6,7] + starts = [1, 2, 3, 4] + widths = [4, 5, 6, 7] x = IRanges(starts, widths) assert (x.get_start() == np.array(starts)).all() assert (x.get_width() == np.array(widths)).all() - y = x.set_start([0,1,2,3]) + y = x.set_start([0, 1, 2, 3]) assert (y.get_start() == np.array([0, 1, 2, 3])).all() - y = x.set_width([10,11,12,13]) + y = x.set_width([10, 11, 12, 13]) assert (y.get_width() == np.array([10, 11, 12, 13])).all() # Works with NumPy array inputs. @@ -37,7 +38,7 @@ def test_IRanges_basic(): assert str(ex.value).find("must be non-negative") >= 0 with pytest.raises(ValueError) as ex: - IRanges([1], [2**31-1]) + IRanges([1], [2**31 - 1]) assert str(ex.value).find("should fit in a 32-bit") >= 0 # Adding names. @@ -50,13 +51,13 @@ def test_IRanges_basic(): def test_IRanges_metadata(): - starts = [1,2,3,4] - widths = [4,5,6,7] + starts = [1, 2, 3, 4] + widths = [4, 5, 6, 7] x = IRanges(starts, widths) assert x.get_mcols().shape[1] == 0 - y = x.set_mcols(BiocFrame({ "ok": [ True, False, False, True ] })) - assert y.get_mcols().column("ok") == [ True, False, False, True ] + y = x.set_mcols(BiocFrame({"ok": [True, False, False, True]})) + assert y.get_mcols().column("ok") == [True, False, False, True] y = x.set_mcols(None) assert y.get_mcols().shape[1] == 0 @@ -78,77 +79,77 @@ def test_IRanges_metadata(): def test_IRanges_getitem(): - starts = [1,2,3,4] - widths = [4,5,6,7] + starts = [1, 2, 3, 4] + widths = [4, 5, 6, 7] x = IRanges(starts, widths) y = x[1:3] assert len(y) == 2 - assert (y.get_start() == np.array([2,3])).all() - assert (y.get_width() == np.array([5,6])).all() + assert (y.get_start() == np.array([2, 3])).all() + assert (y.get_width() == np.array([5, 6])).all() - y = x.set_names(["A", "B", "C", "D"])[[0,3]] - assert (y.get_start() == np.array([1,4])).all() + y = x.set_names(["A", "B", "C", "D"])[[0, 3]] + assert (y.get_start() == np.array([1, 4])).all() assert y.get_names() == ["A", "D"] y = x.set_mcols(BiocFrame({"ok": [True, True, False, False]}))[::-1] - assert (y.get_start() == np.array([4,3,2,1])).all() + assert (y.get_start() == np.array([4, 3, 2, 1])).all() assert y.get_mcols().column("ok") == [False, False, True, True] - y = x.set_metadata({"A":"B"})[0] + y = x.set_metadata({"A": "B"})[0] assert (y.get_start() == np.array([1])).all() assert y.get_metadata() == {"A": "B"} def test_IRanges_setitem(): - starts = [1,2,3,4] - widths = [4,5,6,7] - starts2 = [10,20,30,40] + starts = [1, 2, 3, 4] + widths = [4, 5, 6, 7] + starts2 = [10, 20, 30, 40] widths2 = [50, 60, 70, 80] x = IRanges(starts, widths) y = IRanges(starts2, widths2) x[1:3] = y[1:3] - assert (x.get_start() == np.array([1,20,30,4])).all() - assert (x.get_width() == np.array([4,60,70,7])).all() - assert x.get_names() == None + assert (x.get_start() == np.array([1, 20, 30, 4])).all() + assert (x.get_width() == np.array([4, 60, 70, 7])).all() + assert x.get_names() is None -# x = IRanges(starts, widths, mcols = BiocFrame({"foo": ['a', 'b', 'c', 'd']})) -# y = IRanges(starts2, widths2, mcols = BiocFrame({"foo": ['A', 'B', 'C', 'D']})) -# x[1:3] = y[1:3] -# assert x.get_mcols().column("foo") == ['a', 'B', 'C', 'd'] + # x = IRanges(starts, widths, mcols = BiocFrame({"foo": ['a', 'b', 'c', 'd']})) + # y = IRanges(starts2, widths2, mcols = BiocFrame({"foo": ['A', 'B', 'C', 'D']})) + # x[1:3] = y[1:3] + # assert x.get_mcols().column("foo") == ['a', 'B', 'C', 'd'] x = IRanges(starts, widths, names=["a", "b", "c", "d"]) - y = IRanges(starts2, widths2, names=['A', 'B', 'C', 'D']) + y = IRanges(starts2, widths2, names=["A", "B", "C", "D"]) x[1:3] = y[1:3] - assert x.get_names() == ['a', 'B', 'C', 'd'] + assert x.get_names() == ["a", "B", "C", "d"] x = IRanges(starts, widths) - y = IRanges(starts2, widths2, names=['A', 'B', 'C', 'D']) + y = IRanges(starts2, widths2, names=["A", "B", "C", "D"]) x[1:3] = y[1:3] - assert x.get_names() == ['', 'B', 'C', ''] + assert x.get_names() == ["", "B", "C", ""] - x = IRanges(starts, widths, names=['a', 'b', 'c', 'd']) + x = IRanges(starts, widths, names=["a", "b", "c", "d"]) y = IRanges(starts2, widths2) x[1:3] = y[1:3] - assert x.get_names() == ['a', '', '', 'd'] + assert x.get_names() == ["a", "", "", "d"] def test_IRanges_print(): - starts = [1,2,3,4] - widths = [4,5,6,7] + starts = [1, 2, 3, 4] + widths = [4, 5, 6, 7] x = IRanges(starts, widths) assert repr(x).startswith("IRanges(") >= 0 assert str(x).startswith("IRanges ") >= 0 def test_IRanges_copy(): - starts = [1,2,3,4] - widths = [4,5,6,7] + starts = [1, 2, 3, 4] + widths = [4, 5, 6, 7] x = IRanges(starts, widths) shallow = copy.copy(x) - shallow.set_start([4,3,2,1], in_place=True) + shallow.set_start([4, 3, 2, 1], in_place=True) assert x.get_start()[0] == 1 assert shallow.get_start()[0] == 4 @@ -159,29 +160,29 @@ def test_IRanges_copy(): def test_IRanges_combine(): - starts = [1,2,3,4] - widths = [4,5,6,7] - starts2 = [10,20,30,40] + starts = [1, 2, 3, 4] + widths = [4, 5, 6, 7] + starts2 = [10, 20, 30, 40] widths2 = [50, 60, 70, 80] x = IRanges(starts, widths) y = IRanges(starts2, widths2) comb = combine_seqs(x, y) - assert (comb.get_start() == np.array([1,2,3,4,10,20,30,40])).all() + assert (comb.get_start() == np.array([1, 2, 3, 4, 10, 20, 30, 40])).all() assert (comb.get_width() == np.array([4, 5, 6, 7, 50, 60, 70, 80])).all() assert comb.get_names() is None - x = IRanges(starts, widths, mcols = BiocFrame({"foo": ['a', 'b', 'c', 'd']})) - y = IRanges(starts2, widths2, mcols = BiocFrame({"foo": ['A', 'B', 'C', 'D']})) + x = IRanges(starts, widths, mcols=BiocFrame({"foo": ["a", "b", "c", "d"]})) + y = IRanges(starts2, widths2, mcols=BiocFrame({"foo": ["A", "B", "C", "D"]})) comb = combine_seqs(x, y) - assert comb.get_mcols().column("foo") == [ "a", "b", "c", "d", "A", "B", "C", "D" ] + assert comb.get_mcols().column("foo") == ["a", "b", "c", "d", "A", "B", "C", "D"] x = IRanges(starts, widths, names=["a", "b", "c", "d"]) - y = IRanges(starts2, widths2, names=['A', 'B', 'C', 'D']) + y = IRanges(starts2, widths2, names=["A", "B", "C", "D"]) comb = combine_seqs(x, y) - assert comb.get_names() == [ "a", "b", "c", "d", "A", "B", "C", "D" ] + assert comb.get_names() == ["a", "b", "c", "d", "A", "B", "C", "D"] x = IRanges(starts, widths) - y = IRanges(starts2, widths2, names=['A', 'B', 'C', 'D']) + y = IRanges(starts2, widths2, names=["A", "B", "C", "D"]) comb = combine_seqs(x, y) - assert comb.get_names() == [ "", "", "", "", "A", "B", "C", "D" ] + assert comb.get_names() == ["", "", "", "", "A", "B", "C", "D"] diff --git a/tests/test_skeleton.py b/tests/test_skeleton.py deleted file mode 100644 index ad8d17c..0000000 --- a/tests/test_skeleton.py +++ /dev/null @@ -1,25 +0,0 @@ -import pytest - -from iranges.skeleton import fib, main - -__author__ = "LTLA" -__copyright__ = "LTLA" -__license__ = "MIT" - - -def test_fib(): - """API Tests""" - assert fib(1) == 1 - assert fib(2) == 1 - assert fib(7) == 13 - with pytest.raises(AssertionError): - fib(-10) - - -def test_main(capsys): - """CLI Tests""" - # capsys is a pytest fixture that allows asserts against stdout/stderr - # https://docs.pytest.org/en/stable/capture.html - main(["7"]) - captured = capsys.readouterr() - assert "The 7-th Fibonacci number is 13" in captured.out