diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9437b81..f65d370 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,10 +32,18 @@ jobs: with: python-version: ${{ matrix.PYTHON_VERSION }} + - name: Install poetry + run: | + python -m pip install --upgrade pip + python -m pip install poetry==${{env.POETRY_VERSION}} + + - name: Configure poetry + shell: bash + run: poetry config virtualenvs.in-project true + - name: Set up miniconda uses: conda-incubator/setup-miniconda@v3 with: - miniforge-variant: Mambaforge miniforge-version: latest channels: conda-forge,bioconda activate-environment: prymer @@ -45,61 +53,19 @@ jobs: auto-activate-base: false python-version: ${{ matrix.PYTHON_VERSION }} - - name: Install fulcrumgenomics/bwa - shell: bash -l {0} - run: | - conda activate prymer - pushd bwa - make -j $(nproc) - cp bwa ${CONDA_PREFIX}/bin - popd - - - name: Configure poetry and check lock file - shell: bash -l {0} - run: | - conda activate prymer - poetry config virtualenvs.in-project false - poetry check --lock + - name: Install the project's dependencies + shell: bash -el {0} + run: poetry install - - name: Poetry install - shell: bash -l {0} - run: | - conda activate prymer - poetry lock --no-update - poetry install --with dev - - - name: Unit tests (with doctest and coverage) - shell: bash -l {0} - run: | - conda activate prymer - poetry run pytest --cov=prymer --cov-report=xml --cov-branch --doctest-plus --doctest-modules prymer tests + - name: Test the codebase + shell: bash -el {0} + run: poetry run pytest - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.5.0 with: token: ${{ secrets.CODECOV_TOKEN }} - - - name: Style checking - shell: bash -l {0} - run: | - conda activate prymer - poetry run ruff format --check - - - name: Run lint - shell: bash -l {0} - run: | - conda activate prymer - poetry run ruff check - - name: Run mypy - shell: bash -l {0} - run: | - conda activate prymer - poetry run mypy - - - name: Run docs - shell: bash -l {0} - run: | - conda activate prymer - set -euo pipefail - poetry run mkdocs build --strict + - name: Test building the documentation + shell: bash -el {0} + run: set -euo pipefail && poetry run mkdocs build --strict diff --git a/README.md b/README.md index 54bc433..1def53b 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,62 @@ # Python Primer Design Library [![Python Versions][language-badge]][language-link] -[![Code Style][code-style-badge]][code-style-link] -[![Type Checked][type-checking-badge]][type-checking-link] -[![PEP8][pep-8-badge]][pep-8-link] -[![Code Coverage][code-coverage-badge]][code-coverage-link] [![License][license-badge]][license-link] - ---- - -[![Install with Bioconda][bioconda-badge]][bioconda-link] -[![Bioconda][bioconda-dl-badge]][bioconda-dl-link] -[![PyPI version][pypi-badge]][pypi-link] -[![PyPI download total][pypi-downloads-badge]][pypi-downloads-link] -[![Python package][python-package-badge]][python-package-link] +[![MyPy Checked][type-checking-badge]][type-checking-link] +[![Poetry][poetry-badge]][poetry-link] +[![Ruff][ruff-badge]][ruff-link] [language-badge]: https://img.shields.io/badge/python-3.11_|_3.12-blue [language-link]: http://www.python.org/ -[code-style-badge]: https://img.shields.io/badge/code%20style-black-000000.svg -[code-style-link]: https://black.readthedocs.io/en/stable/ -[type-checking-badge]: http://www.mypy-lang.org/static/mypy_badge.svg -[type-checking-link]: http://mypy-lang.org/ -[pep-8-badge]: https://img.shields.io/badge/code%20style-pep8-brightgreen.svg -[pep-8-link]: https://www.python.org/dev/peps/pep-0008/ -[code-coverage-badge]: https://codecov.io/gh/fulcrumgenomics/prymer/branch/main/graph/badge.svg -[code-coverage-link]: https://codecov.io/gh/fulcrumgenomics/prymer [license-badge]: http://img.shields.io/badge/license-MIT-blue.svg [license-link]: https://github.com/fulcrumgenomics/prymer/blob/main/LICENSE -[bioconda-badge]: https://img.shields.io/badge/install%20with-bioconda-brightgreen.svg?style=flat +[type-checking-badge]: http://www.mypy-lang.org/static/mypy_badge.svg +[type-checking-link]: http://mypy-lang.org/ +[poetry-badge]: https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json +[poetry-link]: https://python-poetry.org/ +[ruff-badge]: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json +[ruff-link]: https://docs.astral.sh/ruff/ + +[![Install with Bioconda][bioconda-badge]][bioconda-link] +[![PyPI version][pypi-badge]][pypi-link] + +[bioconda-badge]: https://img.shields.io/badge/install%20with-bioconda-brightgreen.svg?label=Install%20with [bioconda-link]: http://bioconda.github.io/recipes/prymer/README.html -[bioconda-dl-badge]: https://img.shields.io/conda/dn/bioconda/prymer.svg?label=Bioconda -[bioconda-dl-link]: https://anaconda.org/bioconda/prymer -[pypi-badge]: https://badge.fury.io/py/prymer.svg +[pypi-badge]: https://img.shields.io/pypi/v/prymer?label=Install%20with%20PyPi [pypi-link]: https://pypi.python.org/pypi/prymer -[pypi-downloads-badge]: https://img.shields.io/pypi/dm/prymer + +[![Bioconda][bioconda-dl-badge]][bioconda-dl-link] +[![PyPI download total][pypi-downloads-badge]][pypi-downloads-link] + + +[bioconda-dl-badge]: https://img.shields.io/conda/dn/bioconda/prymer.svg?label=Bioconda%20downloads +[bioconda-dl-link]: https://anaconda.org/bioconda/prymer +[pypi-downloads-badge]: https://img.shields.io/pypi/dm/prymer.svg?label=PyPi%20downloads [pypi-downloads-link]: https://pypi.python.org/pypi/prymer -[python-package-badge]: https://github.com/fulcrumgenomics/prymer/actions/workflows/publish_prymer.yml/badge.svg -[python-package-link]: https://github.com/fulcrumgenomics/prymer/actions/workflows/publish_prymer.yml -## Quick setup +[![tests][python-tests-badge]][python-tests-link] +[![publish prymer][publish-prymer-badge]][publish-prymer-link] +[![Code Coverage][code-coverage-badge]][code-coverage-link] + +[publish-prymer-badge]: https://github.com/fulcrumgenomics/prymer/actions/workflows/publish_prymer.yml/badge.svg +[publish-prymer-link]: https://github.com/fulcrumgenomics/prymer/actions/workflows/publish_prymer.yml +[python-tests-badge]: https://github.com/fulcrumgenomics/prymer/actions/workflows/tests.yml/badge.svg +[python-tests-link]: https://github.com/fulcrumgenomics/prymer/actions/workflows/tests.yml +[code-coverage-badge]: https://codecov.io/gh/fulcrumgenomics/prymer/branch/main/graph/badge.svg +[code-coverage-link]: https://codecov.io/gh/fulcrumgenomics/prymer + +## Recommended Installation + +The package `prymer` requires installation of [Primer3](https://github.com/primer3-org/primer3) and [interactive `bwa`](https://github.com/fulcrumgenomics/bwa-aln-interactive). + +To satisfy these requirements, it is recommended to install using [bioconda](https://bioconda.github.io/): + +```console +mamba install -c bioconda prymer +``` + +## Development and Testing -See [Installation](docs/installation.md). +See the [developer's instructions][developers-instructions-link] for more information. +[developers-instructions-link]: https://prymer.readthedocs.io/en/latest/installation-and-developers-documentation.html#installation-for-development diff --git a/docs/index.md b/docs/index.md index 4b9fde1..b676f5f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -4,7 +4,7 @@ Python Primer Design Library ## Documentation Contents -* [Installation](installation.md) +* [Installation](installation-and-developers-documentation.md) * [Overview](overview.md) * [API](reference/prymer/index.md) diff --git a/docs/installation-and-developers-documentation.md b/docs/installation-and-developers-documentation.md new file mode 100644 index 0000000..2f849bd --- /dev/null +++ b/docs/installation-and-developers-documentation.md @@ -0,0 +1,93 @@ +# Installation and Developer's Documentation + +## Recommended Installation + +The package `prymer` requires installation of [Primer3](https://github.com/primer3-org/primer3) and [interactive `bwa`](https://github.com/fulcrumgenomics/bwa-aln-interactive). + +To satisfy these requirements, it is recommended to install using [bioconda](https://bioconda.github.io/): + +```console +mamba install -c bioconda prymer +``` + +## Installation for Development and Release + +1. Install the environment manager [`mamba`](https://mamba.readthedocs.io/en/latest/installation/mamba-installation.html) +2. Install the Python build tool [`poetry`](https://python-poetry.org/docs/#installing-with-the-official-installer) +3. Create an environment with Python, [Primer3](https://github.com/primer3-org/primer3), and [interactive `bwa`](https://github.com/fulcrumgenomics/bwa-aln-interactive): + + ```console + mamba env create -y -f prymer.yml + ``` + +4. Activate the environment: + + ```console + mamba activate prymer + ``` + +5. Configure `poetry` to install into pre-existing virtual environments: + + ```console + poetry config settings.virtualenvs.create false + ``` + +6. Install `prymer` into the virtual environment: + + ```console + poetry install + ``` + +# Checking the Build + +Use `poetry` to test your code. + +```console +poetry run pytest +``` + +Note that `poetry run pytest` will run `mypy` checks, `ruff` checks, `pytest` unit tests, and will provide a unit test coverage report. + +However, `pytest` will neither run the ruff formatter nor apply `ruff`'s automatic lint fixes, which can be done by calling `ruff` directly. + +```console +poetry run ruff format && poetry run ruff check --fix +``` + +# Building the Documentation + +Use `mkdocs` to build and serve the documentation. + +```console +poetry run mkdocs build && poetry run mkdocs serve +``` + +# Creating a Release on PyPi + +1. Clone the repository recursively and ensure you are on the `main` (un-dirty) branch +2. Checkout a new branch to prepare the library for release +3. Bump the version of the library to the desired SemVer with `poetry version #.#.#` +4. Commit the version bump changes with a Git commit message like `chore(release): bump to #.#.#` +5. Push the commit to the upstream remote, open a PR, ensure tests pass, and seek reviews +6. Squash merge the PR +7. Tag the new commit on the main branch of the origin repository with the new SemVer + +> [!NOTE] +> This project follows [Semantic Versioning](https://semver.org/). +> In brief: +> +> * `MAJOR` version when you make incompatible API changes +> * `MINOR` version when you add functionality in a backwards compatible manner +> * `PATCH` version when you make backwards compatible bug fixes + +GitHub Actions will take care of the remainder of the deployment and release process with: + +1. Unit tests will be run for safety-sake +2. A source distribution will be built +3. Multi-arch multi-Python binary distributions will be built +4. Assets will be deployed to PyPi with the new SemVer +5. A [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/)-aware changelog will be drafted +6. A GitHub release will be created with the new SemVer and the drafted changelog + +> [!IMPORTANT] +> Consider editing the changelog if there are any errors or necessary enhancements. diff --git a/docs/installation.md b/docs/installation.md deleted file mode 100644 index e173dac..0000000 --- a/docs/installation.md +++ /dev/null @@ -1,102 +0,0 @@ -# Installation - - -## Installing `prymer` - -The installation requires three steps: - -1. Install python and other dependencies with `conda` -2. Install the custom version of bwa -3. Install `prymer` with `poetry. - -Install the required Python version, [`poetry`](https://github.com/python-poetry/poetry), and [`primer3](https://github.com/primer3-org/primer3) into your environment manager of choice, e.g. - -```sh -$ mamba env create -y -f prymer.yml -$ conda activate prymer -``` - -Install the custom version of bwa: -```sh -$ git clone -b interactive_aln git@github.com:fulcrumgenomics/bwa.git -$ cd bwa -$ make -j 12 -$ cp bwa ${CONDA_PREFIX}/bin -``` - -Note: the `virtualenvs.create false` setting in `poetry.toml` stops poetry from creating new virtual environments and forces it to use the active conda environment instead. -This can be set once per machine/user and stored in the user's poetry configuration with: - -```sh -$ poetry config settings.virtualenvs.create false -``` - -Install the prymer with `poetry`. - -```console -$ poetry install -``` - -## Getting Setup for Development Work - -Follow the [instructions above](#installing-prymer) - -```console -$ poetry install --with dev -``` - -## Checking the Build - -Make sure that [instructions for development work](#getting-setup-for-development-work) have been followed. - -Use `poetry` to format, lint, type-check, and test your code. -Note that `poetry run pytest` will run `mypy` and `ruff` code checks in addition to `pytest` unit tests, and will provide a unit test coverage report. - -```console -$ poetry run pytest -``` - -However, `pytest` will neither run the ruff formatter nor apply `ruff`'s automatic lint fixes, which can be done by calling `ruff` directly. - -```console -$ poetry run ruff format && poetry run ruff check --fix -``` - -Static type checking is performed using `mpyp`. - -```console -poetry run mypy -``` - -## Building the Documentation - -Make sure that [instructions for development work](#getting-setup-for-development-work) have been followed. - -Use `mkdocs` to build and serve the documentation. - -```console -$ poetry install --with dev -$ poetry run mkdocs build -$ poetry run mkdocs serve -``` - -## Creating a Release on PyPi - -1. Clone the repository recursively and ensure you are on the `main` (un-dirty) branch -2. Checkout a new branch to prepare the library for release -3. Bump the version of the library to the desired SemVer with `poetry version #.#.#` -4. Commit the version bump changes with a Git commit message like `chore(release): bump to #.#.#` -5. Push the commit to the upstream remote, open a PR, ensure tests pass, and seek reviews -6. Squash merge the PR -7. Tag the new commit on the main branch of the repository with the new SemVer - -GitHub Actions will take care of the remainder of the deployment and release process with: - -1. Unit tests will be run for safety-sake -2. A source distribution will be built -3. Many multi-arch multi-Python binary distributions will be built -4. Assets will be deployed to PyPi with the new SemVer -5. A [Conventional Commit](https://www.conventionalcommits.org/en/v1.0.0/)-aware changelog will be drafted -6. A GitHub release will be created with the new SemVer and the drafted changelog - -Consider editing the changelog if there are any errors or necessary enhancements. diff --git a/prymer.yml b/prymer.yml index 44d0515..817a458 100644 --- a/prymer.yml +++ b/prymer.yml @@ -3,12 +3,6 @@ channels: - bioconda - conda-forge dependencies: - # Python - - python>=3.11.* - - ruff>=0.2.1 - - mypy>=1.8 - - pytest>=8.0.0 - - pytest-workflow=2.1.0 - - poetry=1.7.1 - - primer3=2.6.1 - - pyproject_hooks=1.0.0 + - bioconda::bwa-aln-interactive=0.7.18 + - bioconda::primer3=2.6.1 + - conda-forge::python>=3.11.* diff --git a/prymer/offtarget/bwa.py b/prymer/offtarget/bwa.py index 7c4bd47..5b15bc9 100644 --- a/prymer/offtarget/bwa.py +++ b/prymer/offtarget/bwa.py @@ -15,7 +15,13 @@ hits in the "XA" tag than the total number hits reported in the "HN". This occurs when BWA finds more hits than `max_hits` (see `bwt aln -X`). - ## Example +Use of this module requires installation of a custom version of BWA named `bwa-aln-interactive`. +See: + + - https://github.com/fulcrumgenomics/bwa/tree/interactive_aln + - https://bioconda.github.io/recipes/bwa-aln-interactive/README.html + +## Example ```python >>> from pathlib import Path @@ -193,7 +199,10 @@ class BwaAlnInteractive(ExecutableRunner): the process running and be able to send it chunks of reads periodically and get alignments back without waiting for a full batch of reads to be sent. - See: https://github.com/fulcrumgenomics/bwa/tree/interactive_aln + See: + - https://bioconda.github.io/recipes/bwa-aln-interactive/README.html + - https://github.com/fulcrumgenomics/bwa/tree/interactive_aln + Attributes: max_hits: the maximum number of hits to report - if more than this number of seed hits @@ -207,7 +216,7 @@ def __init__( self, ref: Path, max_hits: int, - executable: str | Path = "bwa", + executable: str | Path = "bwa-aln-interactive", max_mismatches: int = 3, max_mismatches_in_seed: int = 3, max_gap_opens: int = 0, @@ -222,7 +231,7 @@ def __init__( ref: the path to the reference FASTA, which must be indexed with bwa. max_hits: the maximum number of hits to report - if more than this number of seed hits are found, report only the count and not each hit. - executable: string or Path representation of the `bwa` executable path + executable: string or Path representation of the `bwa-aln-interactive` executable path max_mismatches: the maximum number of mismatches allowed in the full query sequence max_mismatches_in_seed: the maximum number of mismatches allowed in the seed region max_gap_opens: the maximum number of gap opens allowed in the full query sequence @@ -252,7 +261,7 @@ def __init__( else: message = "BWA index file does not exist:\n\t" message += "\t\n".join(f"{p}" for p in missing_aux_paths) - raise FileNotFoundError(f"{message}\nPlease index with: `bwa index {ref}`") + raise FileNotFoundError(f"{message}\nPlease index with: `{executable_path} index {ref}`") # -N = non-iterative mode: search for all n-difference hits (slooow) # -S = output SAM (run samse) diff --git a/prymer/offtarget/offtarget_detector.py b/prymer/offtarget/offtarget_detector.py index 1773042..1876ee0 100644 --- a/prymer/offtarget/offtarget_detector.py +++ b/prymer/offtarget/offtarget_detector.py @@ -123,7 +123,7 @@ class OffTargetResult: class OffTargetDetector: """A class for detecting off-target mappings of primers and primer pairs that uses a custom - version of "bwa aln". + version of "bwa aln" named "bwa-aln-interactive". The off-target detection is faster and more sensitive than traditional isPCR and in addition can correctly detect primers that are repetitive and contain many thousands or millions of mappings @@ -146,7 +146,7 @@ def __init__( threads: Optional[int] = None, keep_spans: bool = True, keep_primer_spans: bool = True, - executable: str | Path = "bwa", + executable: str | Path = "bwa-aln-interactive", ) -> None: """ Args: diff --git a/pyproject.toml b/pyproject.toml index 4ccb6cb..e4a2898 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -125,6 +125,11 @@ minversion = "7.4" addopts = [ "--ignore=docs/scripts", "--color=yes", + "--cov", + "--cov-report=xml", + "--cov-branch", + "--doctest-plus", + "--doctest-modules", "--mypy", "--ruff", "--doctest-plus",