pax_global_header00006660000000000000000000000064144646604740014531gustar00rootroot0000000000000052 comment=52394b0060d2faf6dc1d2c6b2dd9cada19ad7ab8 pyspoa-0.0.10/000077500000000000000000000000001446466047400131225ustar00rootroot00000000000000pyspoa-0.0.10/.github/000077500000000000000000000000001446466047400144625ustar00rootroot00000000000000pyspoa-0.0.10/.github/workflows/000077500000000000000000000000001446466047400165175ustar00rootroot00000000000000pyspoa-0.0.10/.github/workflows/build-wheels.sh000077500000000000000000000007201446466047400214410ustar00rootroot00000000000000#!/bin/bash set -e -x for VER in cp38-cp38 cp39-cp39 cp310-cp310 cp311-cp311; do PYBIN=/opt/python/${VER}/bin rm -rf venv3 "${PYBIN}/python3" -m venv venv3 source venv3/bin/activate "${PYBIN}/python3" -m pip install --upgrade pip "${PYBIN}/python3" -m pip install cmake==3.27.1 scikit-build "${PYBIN}/python3" -m pip wheel . -w dist make clean deactivate done for wheel in dist/*.whl; do auditwheel repair "${wheel}" done pyspoa-0.0.10/.github/workflows/publish.yml000066400000000000000000000017651446466047400207210ustar00rootroot00000000000000name: publish-pyspoa on: push: tags: - 'v*' jobs: linux-wheels: runs-on: ubuntu-latest container: quay.io/pypa/manylinux_2_28_x86_64 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Build wheels run: | bash .github/workflows/build-wheels.sh - uses: actions/upload-artifact@v3 with: name: linux-wheels path: wheelhouse/ osx-wheels: runs-on: macos-latest strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v3 with: submodules: recursive - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Build wheels run: | python3 -m venv venv3 source venv3/bin/activate make build - uses: actions/upload-artifact@v3 with: name: osx-${{ matrix.python-version }}-wheel path: dist/ pyspoa-0.0.10/.github/workflows/test.yml000066400000000000000000000022351446466047400202230ustar00rootroot00000000000000name: test-pyspoa on: [push] jobs: test: strategy: matrix: os: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 with: submodules: recursive - uses: actions/setup-python@v4 with: python-version: '3.9' - name: Test ${{ matrix.os }} binding run: | python3 -m venv venv3 source venv3/bin/activate make test linux-wheels: runs-on: ubuntu-latest container: quay.io/pypa/manylinux_2_28_x86_64 steps: - uses: actions/checkout@v3 with: submodules: recursive - name: Build wheels run: | bash .github/workflows/build-wheels.sh osx-wheels: runs-on: macos-latest strategy: matrix: python-version: ['3.8', '3.9', '3.10', '3.11'] steps: - uses: actions/checkout@v3 with: submodules: recursive - uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Build wheels run: | python3 -m venv venv3 source venv3/bin/activate make test pyspoa-0.0.10/.gitignore000066400000000000000000000000671446466047400151150ustar00rootroot00000000000000.eggs/ dist/ build/ *~ *.so *-info/ tmp/ pyspoa/ venv*/pyspoa-0.0.10/.gitmodules000066400000000000000000000001101446466047400152670ustar00rootroot00000000000000[submodule "src"] path = src url = https://github.com/rvaser/spoa.git pyspoa-0.0.10/LICENSE000066400000000000000000000021031446466047400141230ustar00rootroot00000000000000MIT License Copyright (c) 2020 Oxford Nanopore Technologies, Ltd. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pyspoa-0.0.10/MANIFEST.in000066400000000000000000000000311446466047400146520ustar00rootroot00000000000000recursive-include src/ * pyspoa-0.0.10/Makefile000066400000000000000000000003441446466047400145630ustar00rootroot00000000000000build: pip wheel . -w dist install: build pip install dist/pyspoa*.whl test: install python3 tests/test_pyspoa.py clean: rm -rf src/build build tmp var *~ *.whl __pycache__ python setup.py clean pip uninstall -y pyspoa pyspoa-0.0.10/README.md000066400000000000000000000031241446466047400144010ustar00rootroot00000000000000# 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.0.10/build.py000066400000000000000000000006151446466047400145750ustar00rootroot00000000000000import 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.0.10/pyproject.toml000066400000000000000000000001771446466047400160430ustar00rootroot00000000000000[build-system] requires = ["cmake", "pybind11", "setuptools", "wheel", "scikit-build"] build-backend = "setuptools.build_meta" pyspoa-0.0.10/pyspoa.cpp000066400000000000000000000020031446466047400151340ustar00rootroot00000000000000#include "spoa.hpp" #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::tuple { 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(); 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 ); m.attr("__version__") = VERSION_INFO; } pyspoa-0.0.10/setup.py000066400000000000000000000103351446466047400146360ustar00rootroot00000000000000import 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.0.10', 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.0.10/src/000077500000000000000000000000001446466047400137115ustar00rootroot00000000000000pyspoa-0.0.10/tests/000077500000000000000000000000001446466047400142645ustar00rootroot00000000000000pyspoa-0.0.10/tests/test_pyspoa.py000066400000000000000000000011771446466047400172160ustar00rootroot00000000000000#!/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) if __name__ == '__main__': main()