pax_global_header00006660000000000000000000000064145072215000014506gustar00rootroot0000000000000052 comment=ffc856a5c73d74195a16673e88749e322a3c1764 pyspoa-0.2.1/000077500000000000000000000000001450722150000130215ustar00rootroot00000000000000pyspoa-0.2.1/.gitignore000066400000000000000000000000671450722150000150140ustar00rootroot00000000000000.eggs/ dist/ build/ *~ *.so *-info/ tmp/ pyspoa/ venv*/pyspoa-0.2.1/.gitlab-ci.yml000066400000000000000000000074631450722150000154670ustar00rootroot00000000000000include: - project: "epi2melabs/ci-templates" file: - "push-github.yaml" - "push-conda.yaml" - "snippets.yaml" image: ${UBUNTUIMAGE}:20.04 variables: GIT_SUBMODULE_STRATEGY: recursive GH_NAMESPACE: "nanoporetech" .prep-image: &prep-image | export DEBIAN_FRONTEND=noninteractive apt update -qq apt install -y --no-install-recommends gcc autoconf libtool automake make curl wget zlib1g-dev git .minimal-python: &minimal-python | export DEBIAN_FRONTEND=noninteractive apt-get update -qq && apt-get install -y -qq python3-all-dev python3-venv stages: - test - prerelease - release test: stage: test parallel: matrix: - PYENV_VERSION: ["3.8", "3.9", "3.10", "3.11"] script: - *prep-image - !reference [.install, pyenv] - !reference [.setup, pyenv-python] - make test deploy-checks: stage: prerelease variables: PACKAGE_NAME: pyspoa LICENSE_FILE: BSD-4-CLAUSE script: - !reference [.check, python-version-setup] - !reference [.check, changelog] - !reference [.check, license] rules: - if: '$CI_COMMIT_TAG =~ /^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$/' .conda-before-script: &conda-before-script | export CONDA_PKG=${CI_PROJECT_NAME} export CONDA_PKG_VERSION=${CI_COMMIT_TAG/v/} mkdir conda-build cd conda-build conda: extends: - .deploy-conda-linux variables: RECIPEPATH: "../conda" before_script: - *prep-image - *conda-before-script conda-arm: extends: - .deploy-conda-linux-arm variables: RECIPEPATH: "../conda" before_script: - *prep-image - *conda-before-script conda-mac: extends: - .deploy-conda-mac variables: RECIPEPATH: "../conda" before_script: - *conda-before-script conda-mac-arm: extends: - .deploy-conda-mac-arm variables: RECIPEPATH: "../conda" before_script: - *conda-before-script ### Python # The selection of wheels built here is mainly driven by the matrix # we build for medaka. bld:py-sdist: stage: prerelease script: - *prep-image - *minimal-python - make sdist - cd dist - python3 -m venv venv - . venv/bin/activate - pip install --upgrade pip - pip install *.tar.gz artifacts: paths: - dist/*.tar.gz .many-linux: stage: prerelease script: - echo "Building a Python ${PYWHEEL} wheel on manylinux_${FLAVOUR}" - ./build-wheels.sh . ${PYWHEEL} artifacts: paths: - wheelhouse-final/*.whl wheels-2014: extends: .many-linux image: "quay.io/pypa/manylinux2014_x86_64" parallel: matrix: - PYWHEEL: [7, 8, 9] FLAVOUR: ["2014"] wheels-arm-2014: extends: .many-linux tags: - arm64 image: "quay.io/pypa/manylinux2014_aarch64" parallel: matrix: - PYWHEEL: [8, 9, 10] FLAVOUR: ["2014"] wheels-2_28: extends: .many-linux image: "quay.io/pypa/manylinux_2_28_x86_64" parallel: matrix: - PYWHEEL: [8, 9, 10, 11] FLAVOUR: ["2_28"] wheels-arm-2_28: extends: .many-linux tags: - arm64 image: "quay.io/pypa/manylinux_2_28_aarch64" parallel: matrix: - PYWHEEL: [8, 9, 10, 11] FLAVOUR: ["2_28"] deploy:pypi: stage: release script: - *minimal-python - make pypi_build/bin/activate - source pypi_build/bin/activate - twine upload --non-interactive wheelhouse-final/pyspoa*.whl dist/pyspoa*.tar.gz rules: - if: '$CI_COMMIT_TAG =~ /^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+$/' pyspoa-0.2.1/.gitmodules000066400000000000000000000001101450722150000151660ustar00rootroot00000000000000[submodule "src"] path = src url = https://github.com/rvaser/spoa.git pyspoa-0.2.1/CHANGELOG.md000066400000000000000000000023031450722150000146300ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [v0.2.1] ### Changed - Default argument for `min_coverage` to `None` ## [v0.2.0] ### Changed - Updated SPOA to v4.1.2 ### Added - `min_coverage` option recently introduced in SPOA ## [v0.1.0] ### Changed - Moved CI to internal infrastructure - Added conda builds to CI - Larger selection of wheels ## [v0.0.10] ### Added - Support for aarch64 in setup.py ## [v0.0.9] ### Changed - Python support: 3.6, 3.7, removed 3.10, added 3.11 - All package requirements removed in favour of build time requirements in pyproject.toml ## [v0.0.8] ### Added - Python 3.6+ wheels and source distribution ## [v0.0.6] ### Added - Python 3.9 Support ### Changed - Update spoa to v4.0.7 ## [v0.0.5] ### Fixed - Alignment type selection, Smith-Waterman no longer hardcoded. ## [v0.0.4] ### Fixed - Portability build ## [v0.0.3] ### Changed - Upgrade to spoa 4.0.0 - Compile for portability ## [v0.0.2] ### Added - Python 3.8 support ## [v0.0.1] ### Added - PyPI builds pyspoa-0.2.1/LICENSE000066400000000000000000000032621450722150000140310ustar00rootroot00000000000000Copyright (c) 2020-, Oxford Nanopore Technologies Plc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by Oxford Nanopore Technologies Plc. * Neither the name of Oxford Nanopore Technologies Plc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY Oxford Nanopore Technologies Plc. AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Oxford Nanopore Technologies Plc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pyspoa-0.2.1/MANIFEST.in000066400000000000000000000000311450722150000145510ustar00rootroot00000000000000recursive-include src/ * pyspoa-0.2.1/Makefile000066400000000000000000000015301450722150000144600ustar00rootroot00000000000000PYTHON ?= python3 IN_VENV=. ./venv/bin/activate venv/bin/activate: test -d venv || $(PYTHON) -m venv venv ${IN_VENV} && pip install pip --upgrade .PHONY: build build: venv/bin/activate ${IN_VENV} && pip wheel . -w dist .PHONY: install install: build ${IN_VENV} && pip install dist/pyspoa*.whl .PHONY: test test: install ${IN_VENV} && python tests/test_pyspoa.py sdist: venv/bin/activate ${IN_VENV} && python setup.py sdist IN_BUILD=. ./pypi_build/bin/activate pypi_build/bin/activate: test -d pypi_build || $(PYTHON) -m venv pypi_build --prompt "(pypi) " ${IN_BUILD} && pip install pip --upgrade ${IN_BUILD} && pip install --upgrade pip setuptools twine wheel readme_renderer[md] keyrings.alt clean: rm -rf dist wheelhouse-final venv pypi_build src/build build tmp var *~ *.whl __pycache__ python setup.py clean pip uninstall -y pyspoa pyspoa-0.2.1/README.md000066400000000000000000000031241450722150000143000ustar00rootroot00000000000000# pyspoa ![test-pyspoa](https://github.com/nanoporetech/pyspoa/workflows/test-pyspoa/badge.svg) [![PyPI version](https://badge.fury.io/py/pyspoa.svg)](https://badge.fury.io/py/pyspoa) Python bindings to [spoa](https://github.com/rvaser/spoa). ## Installation ```bash $ pip install pyspoa ``` ## Usage ```python >>> from spoa import poa >>> >>> consensus, msa = poa(['AACTTATA', 'AACTTATG', 'AACTATA']) >>> consensus 'AACTTATA' >>> msa ['AACTTATA-', 'AACTTAT-G', 'AAC-TATA-'] >>> print(os.linesep.join(msa)) AACTTATA- AACTTAT-G AAC-TATA- ``` ## Developer Quick Start ```bash $ git clone --recursive https://github.com/nanoporetech/pyspoa.git $ cd pyspoa $ python3 -m venv pyspoa $ source pyspoa/bin/activate (pyspoa) $ make build ``` ### Licence and Copyright (c) 2019 Oxford Nanopore Technologies Ltd. pyspoa is distributed under the terms of the MIT License. If a copy of the License was not distributed with this file, You can obtain one at https://github.com/nanoporetech/pyspoa ### Research Release Research releases are provided as technology demonstrators to provide early access to features or stimulate Community development of tools. Support for this software will be minimal and is only provided directly by the developers. Feature requests, improvements, and discussions are welcome and can be implemented by forking and pull requests. However much as we would like to rectify every issue and piece of feedback users may have, the developers may have limited resource for support of this software. Research releases may be unstable and subject to rapid iteration by Oxford Nanopore Technologies. pyspoa-0.2.1/build-wheels.sh000077500000000000000000000040031450722150000157410ustar00rootroot00000000000000#!/bin/bash # Usage: ./build-wheels.sh ... set -eux PACKAGE_NAME=pyspoa workdir=$1 shift echo "Changing cwd to ${workdir}" cd ${workdir} # some many linux containers are centos-based, others are debian! if [ -f /etc/centos-release ]; then yum install -y wget else # https://stackoverflow.com/questions/76094428/debian-stretch-repositories-404-not-found sed -i -e 's/deb.debian.org/archive.debian.org/g' \ -e 's|security.debian.org|archive.debian.org/|g' \ -e '/stretch-updates/d' /etc/apt/sources.list apt update apt install -y wget fi # spoa wants zlib >=1.2.8, ensure we have that wget https://github.com/madler/zlib/releases/download/v1.3/zlib-1.3.tar.gz tar -xzvf zlib-1.3.tar.gz pushd zlib-1.3 ./configure make install popd mkdir -p wheelhouse echo "PYTHON VERSIONS AVAILABLE" ls /opt/python/ # Compile wheels for minor in $@; do if [[ "${minor}" == "8" ]] || [[ "${minor}" == "9" ]] || \ [[ "${minor}" == "10" ]] || [[ "${minor}" == "11" ]]; then PYBIN="/opt/python/cp3${minor}-cp3${minor}/bin" else PYBIN="/opt/python/cp3${minor}-cp3${minor}m/bin" fi "${PYBIN}/python3" -m pip install --upgrade pip "${PYBIN}/python3" -m pip install cmake==3.27.1 scikit-build "${PYBIN}"/pip wheel --no-dependencies . -w ./wheelhouse/ done ls ${PYBIN} # Bundle external shared libraries into the wheels for whl in "wheelhouse/${PACKAGE_NAME}"*.whl; do auditwheel repair "${whl}" -w ./wheelhouse/ done # Install + test packages for minor in $@; do if [[ "${minor}" == "8" ]] || [[ "${minor}" == "9" ]] || \ [[ "${minor}" == "10" ]] || [[ "${minor}" == "11" ]]; then PYBIN="/opt/python/cp3${minor}-cp3${minor}/bin" else PYBIN="/opt/python/cp3${minor}-cp3${minor}m/bin" fi "${PYBIN}"/pip install "${PACKAGE_NAME}" --no-index -f ./wheelhouse "${PYBIN}"/python3 tests/test_pyspoa.py done mkdir wheelhouse-final cp wheelhouse/${PACKAGE_NAME}*manylinux* wheelhouse-final pyspoa-0.2.1/build.py000066400000000000000000000006151450722150000144740ustar00rootroot00000000000000import os from shutil import rmtree from subprocess import run if __name__ == "__main__": bdir = "src/build" rmtree(bdir, ignore_errors=True) os.makedirs(bdir) run([ "cmake", "-D", "spoa_optimize_for_portability=ON", "-D", "CMAKE_BUILD_TYPE=Release", "-D", "CMAKE_CXX_FLAGS='-I ../vendor/cereal/include/ -fPIC '", "..", ], cwd=bdir) run("make", cwd=bdir) pyspoa-0.2.1/conda/000077500000000000000000000000001450722150000141055ustar00rootroot00000000000000pyspoa-0.2.1/conda/build.sh000066400000000000000000000001511450722150000155350ustar00rootroot00000000000000#!/bin/bash ${PYTHON} -m pip wheel . -w dist ${PYTHON} -m pip install . --no-deps --ignore-install -vv pyspoa-0.2.1/conda/meta.yaml000066400000000000000000000015731450722150000157250ustar00rootroot00000000000000package: name: {{ environ.get('CONDA_PKG') }} version: {{ environ.get('CONDA_PKG_VERSION') }} source: path: ../ build: number: {{ environ.get('CONDA_PKG_BUILD', 0) }} skip: True # [py2k] requirements: build: - {{ compiler('c') }} - {{ compiler('cxx') }} - make - cmake host: - python - pip - pybind11 - zlib - simde - libgomp # [linux] - llvm-openmp # [osx] - cpu_features # [x86] run: - python test: imports: - spoa about: home: https://github.com/nanoporetech/pyspoa license: MIT License license_file: LICENSE summary: Python binding to spoa library. doc_url: https://github.com/nanoporetech/spoa dev_url: https://github.com/nanoporetech/spoa extra: recipe-maintainers: - cjw85 - iiSeymour pyspoa-0.2.1/pyproject.toml000066400000000000000000000001771450722150000157420ustar00rootroot00000000000000[build-system] requires = ["cmake", "pybind11", "setuptools", "wheel", "scikit-build"] build-backend = "setuptools.build_meta" pyspoa-0.2.1/pyspoa.cpp000066400000000000000000000025401450722150000150410ustar00rootroot00000000000000#include "spoa.hpp" #define PYBIND11_DETAILED_ERROR_MESSAGES // for type information in casting errors #include #include using namespace pybind11::literals; // for _a in pybind def auto poa(std::vector sequences, int algorithm, bool genmsa, int m, int n, int g, int e, int q, int c, pybind11::object min_coverage) -> pybind11::tuple { // set min_coverage to the default of the SPOA CLI (-1) if None int min_cov = -1; if (min_coverage != pybind11::none()) { min_cov = min_coverage.cast(); } auto alignment_engine = spoa::AlignmentEngine::Create( static_cast(algorithm), m, n, g, e, q, c ); spoa::Graph graph{}; for (const auto& it: sequences) { auto alignment = alignment_engine->Align(it, graph); graph.AddAlignment(alignment, it); } auto consensus = graph.GenerateConsensus(min_cov); std::vector msa; if (genmsa) msa = graph.GenerateMultipleSequenceAlignment(); return pybind11::make_tuple(consensus, msa); } PYBIND11_MODULE(spoa, m) { m.def( "poa", &poa, "", "sequences"_a, "algorithm"_a=0, "genmsa"_a=true, "m"_a=5, "n"_a=-4, "g"_a=-8, "e"_a=-6, "q"_a=-10, "c"_a=-4, "min_coverage"_a=pybind11::none() ); m.attr("__version__") = VERSION_INFO; } pyspoa-0.2.1/setup.py000066400000000000000000000103331450722150000145330ustar00rootroot00000000000000import os import sys from shutil import rmtree from subprocess import run import platform import setuptools from setuptools import setup, Extension from setuptools.command.install import install from setuptools.command.build_ext import build_ext LIB_SPOA = 'src/build/lib/libspoa.a' if os.environ.get('libspoa'): LIB_SPOA = os.environ['libspoa'] class get_pybind_include(object): """ Helper class to determine the pybind11 include path The purpose of this class is to postpone importing pybind11 until it is actually installed, so that the ``get_include()`` method can be invoked. """ def __init__(self, user=False): self.user = user def __str__(self): import pybind11 return pybind11.get_include(self.user) def has_flag(compiler, flagname): """ Return a boolean indicating whether a flag name is supported on the specified compiler. """ import tempfile with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f: f.write('int main (int argc, char **argv) { return 0; }') try: compiler.compile([f.name], extra_postargs=[flagname]) except setuptools.distutils.errors.CompileError: return False return True def cpp_flag(compiler): """ Return the -std=c++[11/14/17] compiler flag. The newer version is prefered over c++11 (when it is available). """ flags = ['-std=c++14', '-std=c++11'] for flag in flags: if has_flag(compiler, flag): return flag raise RuntimeError('Unsupported compiler -- at least C++11 support is needed!') def build_spoa(): bdir = "src/build" rmtree(bdir, ignore_errors=True) os.makedirs(bdir) # x86 -- builds with -msse4.1 instead of -march=native extra_flags = ["-D", "spoa_optimize_for_portability=ON"] if platform.machine() in {"aarch64", "arm64"}: extra_flags = [ "-D", "spoa_use_simde=ON", "-D", "spoa_use_simde_nonvec=ON", "-D", "spoa_use_simde_openmp=ON"] run( ["cmake"] + extra_flags + [ "-D", "CMAKE_BUILD_TYPE=Release", "-D", "CMAKE_CXX_FLAGS='-I ../vendor/cereal/include/ -fPIC '", ".."], cwd=bdir) run("make", cwd=bdir) class BuildExt(build_ext): """ A custom build extension for adding compiler-specific options. """ c_opts = { 'msvc': ['/EHsc'], 'unix': [], } l_opts = { 'msvc': [], 'unix': [], } if sys.platform == 'darwin': darwin_opts = ['-stdlib=libc++', '-mmacosx-version-min=10.7'] c_opts['unix'] += darwin_opts l_opts['unix'] += darwin_opts def build_extensions(self): if not os.environ.get('libspoa'): build_spoa() ct = self.compiler.compiler_type opts = self.c_opts.get(ct, []) link_opts = self.l_opts.get(ct, []) if ct == 'unix': opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version()) opts.append(cpp_flag(self.compiler)) if has_flag(self.compiler, '-fvisibility=hidden'): opts.append('-fvisibility=hidden') elif ct == 'msvc': opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()) for ext in self.extensions: ext.extra_compile_args = opts ext.extra_link_args = link_opts build_ext.build_extensions(self) ext_modules = [ Extension( 'spoa', ['pyspoa.cpp'], include_dirs=[ 'src/include/spoa', 'src/vendor/cereal/include', get_pybind_include(), get_pybind_include(user=True), ], language='c++', extra_objects=[ LIB_SPOA ], ), ] with open('README.md', encoding='utf-8') as f: long_description = f.read() setup( name='pyspoa', version='0.2.1', author='Oxford Nanoporetech Technologies, Ltd.', author_email='support@nanoporetech.com', url='https://github.com/nanoporetech/pyspoa', description='Python bindings to spoa', long_description=long_description, long_description_content_type='text/markdown', ext_modules=ext_modules, cmdclass={ 'build_ext': BuildExt, }, zip_safe=False, ) pyspoa-0.2.1/src/000077500000000000000000000000001450722150000136105ustar00rootroot00000000000000pyspoa-0.2.1/tests/000077500000000000000000000000001450722150000141635ustar00rootroot00000000000000pyspoa-0.2.1/tests/test_pyspoa.py000066400000000000000000000032631450722150000171130ustar00rootroot00000000000000#!/usr/env/bin python3 from spoa import poa from unittest import TestCase, main class Tests(TestCase): def test_bindings(self): """ simple poa to check bindings""" consensus, msa = poa(['AACTTATA', 'AACTTATG', 'AACTATA']) self.assertEqual(consensus, 'AACTTATA') self.assertEqual(len(msa), 3) def test_bindings_no_msa(self): """ simple poa to check bindings with msa generation""" consensus, msa = poa(['AACTTATA', 'AACTTATG', 'AACTATA'], genmsa=False) self.assertEqual(consensus, 'AACTTATA') self.assertEqual(len(msa), 0) def test_bindings_min_coverage(self): """ simple poa to check bindings with `min_coverage` param""" consensus, msa = poa(['AACTTATA', 'AACTTATG', 'AACTATA'], min_coverage=3) self.assertEqual(consensus, 'AACTAT') self.assertEqual(len(msa), 3) def test_bindings_min_coverage_None(self): """ simple poa to check bindings with `min_coverage=None`""" consensus, msa = poa(['AACTTATA', 'AACTTATG', 'AACTATA'], min_coverage=None) self.assertEqual(consensus, 'AACTTATA') self.assertEqual(len(msa), 3) def test_bindings_min_coverage_wrong_type_string(self): """ check that bindings with `min_coverage=str` throw error""" self.assertRaises( RuntimeError, poa, ["AACTTATA", "AACTTATG", "AACTATA"], min_coverage="x" ) def test_bindings_min_coverage_wrong_type_float(self): """ check that bindings with `min_coverage=float` throw error""" self.assertRaises( RuntimeError, poa, ["AACTTATA", "AACTTATG", "AACTATA"], min_coverage=1.5 ) if __name__ == '__main__': main()