From 287169a2a4284ef2e9b3f719a037d3da54b138af Mon Sep 17 00:00:00 2001 From: Adam English Date: Tue, 23 May 2023 17:21:19 -0400 Subject: [PATCH 1/3] Making package pip installable Works on python3.9 MacOS and python3.10 Ubuntu 22.04 docker. Had to disable openmp. May be related to recent commits on WFA2_lib. Still need to figure out where to put author/email type information in the pyproject.toml (or wherever it goes now) --- LICENSE.md => LICENSE | 0 pywfa/__init__.py | 9 ++- setup.py | 137 ------------------------------------------ 3 files changed, 6 insertions(+), 140 deletions(-) rename LICENSE.md => LICENSE (100%) delete mode 100644 setup.py diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/pywfa/__init__.py b/pywfa/__init__.py index 839ad93..670a588 100644 --- a/pywfa/__init__.py +++ b/pywfa/__init__.py @@ -1,3 +1,6 @@ -from __future__ import absolute_import -from .align import WavefrontAligner, clip_cigartuples, cigartuples_to_str, elide_mismatches_from_cigar - +from pywfa.align import ( + WavefrontAligner, + clip_cigartuples, + cigartuples_to_str, + elide_mismatches_from_cigar, +) diff --git a/setup.py b/setup.py deleted file mode 100644 index a91ec35..0000000 --- a/setup.py +++ /dev/null @@ -1,137 +0,0 @@ -from Cython.Build import cythonize -from setuptools.command.build_ext import build_ext as _build_ext -from setuptools.command.install import install as _install -from setuptools import setup -import setuptools -from setuptools.extension import Extension -from subprocess import run -import os -import glob -import shutil -from distutils import ccompiler -import distutils.sysconfig - - -debug = False - -cy_options = { - 'annotate': False, - 'compiler_directives': { - 'profile': debug, - 'linetrace': debug, - 'boundscheck': debug, - 'wraparound': debug, - 'nonecheck': debug, - 'initializedcheck': debug, - 'language_level': 3 - } -} - -cfg_vars = distutils.sysconfig.get_config_vars() -for key, value in cfg_vars.items(): - if type(value) == str: - cfg_vars[key] = value.replace("-Wstrict-prototypes", "") - - -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='.c') 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 get_extra_args(flags): - compiler = ccompiler.new_compiler() - extra_compile_args = [] - for f in flags: - if has_flag(compiler, f): - extra_compile_args.append(f) - return extra_compile_args - - -extras = ["-Wno-unused-function", "-Wno-unused-result", '-Wno-ignored-qualifiers', "-Wno-deprecated-declarations"] -extras_pywfa = get_extra_args(extras) - -root = os.path.abspath(os.path.dirname(__file__)) -wfa = os.path.join(root, "pywfa/WFA2_lib") -libraries = [f"{wfa}/lib"] -library_dirs = [f"{wfa}/lib"] -include_dirs = [".", root, wfa, f"{wfa}/lib", f"{wfa}/utils", f"{wfa}/wavefront", f"{wfa}/bindings/cpp", f"{wfa}/system", - f"{wfa}/alignment", f"{root}/pywfa"] - -print("Libs", libraries) -print("Library dirs", library_dirs) -print("Include dirs", include_dirs) - -ext_modules = list() -ext_modules.append(Extension("pywfa.align", - ["pywfa/align.pyx"], - libraries=libraries, - library_dirs=library_dirs, - include_dirs=include_dirs, - extra_compile_args=extras_pywfa, - language="c", - extra_objects=[f"{wfa}/lib/libwfa.a"] - )) - -openmp_support = False - - -class MyBuild(_build_ext): - # https://stackoverflow.com/questions/1754966/how-can-i-run-a-makefile-in-setup-py - def build_extension(self, ext): - # This is a hack to automate python setup.py build_ext --inplace during install - # Note sure how robust this is - global openmp_support - compiler = ccompiler.new_compiler() - openmp_support = has_flag(compiler, '-fopenmp') - - shared = glob.glob(f"{root}/build/lib*/pywfa/*.so") + glob.glob(f"{root}/build/lib*/pywfa/*.dll") - _build_ext.build_extension(self, ext) - [shutil.copy(i, f"{root}/pywfa") for i in shared] - - -class Build_ext_first(_install): - def run(self): - # Build WFA2-lib first - # run("cd pywfa/WFA2_lib; make clean all; cd ../../", shell=True) - global openmp_support - if openmp_support: - run("cd pywfa/WFA2_lib; make clean all BUILD_WFA_PARALLEL=1 BUILD_MINIMAL=1; cd ../../", shell=True) - else: - run("pwd", shell=True) - run("cd pywfa/WFA2_lib; make clean all BUILD_WFA_PARALLEL=0 BUILD_MINIMAL=1; cd ../../", shell=True) - return setuptools.command.install.install.run(self) - -setup( - name="pywfa", - author="Kez Cleal", - author_email="clealk@cardiff.ac.uk", - url="https://github.com/kcleal/pywfa", - description="Align sequences using WFA2-lib", - license="MIT", - version='0.4.1', - python_requires='>=3.7', - install_requires=[ # runtime requires - 'cython', - ], - setup_requires=[ - 'cython', - ], - tests_require=[ - 'pysam', 'nose' - ], - packages=["pywfa", "pywfa/tests", "pywfa/WFA2_lib"], - ext_modules=cythonize(ext_modules, **cy_options), - cmdclass={'install': Build_ext_first, 'build_ext': MyBuild}, - #include_package_data=True, - zip_safe=False, - test_suite='nose.collector', -) From 6476824f668b5720c3866b2666ab3eff96e93e34 Mon Sep 17 00:00:00 2001 From: Adam English Date: Tue, 23 May 2023 17:40:20 -0400 Subject: [PATCH 2/3] Finishing toml and including build forgot the custom build --- _custom_build.py | 116 +++++++++++++++++++++++++++++++++++++++++++++++ pyproject.toml | 28 ++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 _custom_build.py create mode 100644 pyproject.toml diff --git a/_custom_build.py b/_custom_build.py new file mode 100644 index 0000000..c405edb --- /dev/null +++ b/_custom_build.py @@ -0,0 +1,116 @@ +import os +import glob +import shutil +import sysconfig +import setuptools +from subprocess import run +from distutils import ccompiler +from setuptools import Extension +from Cython.Build import cythonize +from setuptools.command.build_py import build_py as _build_py + +########### +# Helpers # +########### +debug = False + +cy_options = { + 'annotate': False, + 'compiler_directives': { + 'profile': debug, + 'linetrace': debug, + 'boundscheck': debug, + 'wraparound': debug, + 'nonecheck': debug, + 'initializedcheck': debug, + 'language_level': 3 + } +} + +cfg_vars = sysconfig.get_config_vars() +for key, value in cfg_vars.items(): + if type(value) == str: + cfg_vars[key] = value.replace("-Wstrict-prototypes", "") + +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='.c') 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 get_extra_args(flags): + compiler = ccompiler.new_compiler() + extra_compile_args = [] + for f in flags: + if has_flag(compiler, f): + extra_compile_args.append(f) + return extra_compile_args + +################## +# WFA2_lib build # +################## +extras = ["-Wno-unused-function", "-Wno-unused-result", '-Wno-ignored-qualifiers', "-Wno-deprecated-declarations"] +extras_pywfa = get_extra_args(extras) + +root = os.path.abspath(os.path.dirname(__file__)) +wfa = os.path.join(root, "pywfa/WFA2_lib") +libraries = [f"{wfa}/lib"] +library_dirs = [f"{wfa}/lib"] +include_dirs = [".", root, wfa, f"{wfa}/lib", f"{wfa}/utils", f"{wfa}/wavefront", f"{wfa}/bindings/cpp", f"{wfa}/system", + f"{wfa}/alignment", f"{root}/pywfa"] + +print("Libs", libraries) +print("Library dirs", library_dirs) +print("Include dirs", include_dirs) + +# Can't get openmp to behave. Whenever its on (1), the build fails. +# this has happened on multiple OS with/without `libomp-dev` +# compiler = ccompiler.new_compiler() +omp = 0 #1 if has_flag(compiler, "-fopenmp") else 0 +ret = run(f"cd pywfa/WFA2_lib; make clean all BUILD_WFA_PARALLEL={omp} BUILD_MINIMAL=1; cd ../../", shell=True) +if ret.returncode != 0: + print("Unable to build WFA2_lib") + print(ret) + exit(ret.returncode) + +################## +# bindings build # +################## +m_ext_module = cythonize(Extension("pywfa.align", + ["pywfa/align.pyx"], + libraries=libraries, + library_dirs=library_dirs, + include_dirs=include_dirs, + extra_compile_args=extras_pywfa, + language="c", + extra_objects=[f"{wfa}/lib/libwfa.a"] + ), + **cy_options) + +shared = glob.glob(f"{root}/build/lib*/pywfa/*.so") + glob.glob(f"{root}/build/lib*/pywfa/*.dll") +[shutil.copy(i, f"{root}/pywfa") for i in shared] + +################### +# Basic build_ext # +################### +# Thanks to https://stackoverflow.com/questions/73800736/pyproject-toml-and-cython-extension-module +class build_py(_build_py): + def run(self): + self.run_command("build_ext") + return super().run() + + def initialize_options(self): + super().initialize_options() + if self.distribution.ext_modules == None: + self.distribution.ext_modules = [] + + self.distribution.ext_modules.extend( + m_ext_module + ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..58542ba --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,28 @@ +[build-system] +requires = ["setuptools>=61.0", "cython"] +build-backend = "setuptools.build_meta" + +[project] +name="pywfa" +version='0.4.1' +description="Align sequences using WFA2-lib" +requires-python='>=3.7' +readme = "README.rst" +authors = [ + { name = "Kez Cleal", email = "clealk@cardiff.ac.uk" } +] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", +] + +[tool.setuptools] +py-modules = ["_custom_build"] +packages=["pywfa", "pywfa.tests", "pywfa.WFA2_lib"] + +[tool.setuptools.cmdclass] +build_py = "_custom_build.build_py" + +[project.urls] +Repository="https://github.com/kcleal/pywfa" From f9928ea907324c522c211f32905970d7e865aed7 Mon Sep 17 00:00:00 2001 From: Adam English Date: Tue, 23 May 2023 18:39:11 -0400 Subject: [PATCH 3/3] update readme can import objects directly from pywfa --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 7667155..6659878 100644 --- a/README.rst +++ b/README.rst @@ -29,7 +29,7 @@ Alignment of pattern and text strings can be performed by accessing WFA2-lib fun .. code-block:: python - from pywfa.align import WavefrontAligner + from pywfa import WavefrontAligner pattern = "TCTTTACTCGCGCGTTGGAGAAATACAATAGT" text = "TCTATACTGCGCGTTTGGAGAAATAAAATAGT" @@ -115,7 +115,7 @@ To configure the `WaveFrontAligner`, options can be provided during initializati .. code-block:: python - from pywfa.align import WavefrontAligner + from pywfa import WavefrontAligner a = WavefrontAligner(scope="score", distance="affine2p",