From da6d64aa520bfca98bf5f2425741cc0f10bb263c Mon Sep 17 00:00:00 2001 From: yangli Date: Sun, 9 Oct 2022 22:55:50 -0500 Subject: [PATCH] add print func --- README.md | 18 +++++- images/img.png | Bin 0 -> 51179 bytes poetry.lock | 131 +++++++++++++++++++++++++++++++++++++- pyproject.toml | 5 +- src/mssw/__init__.py | 125 ++++++++++++++++++++++++++++++++++-- src/mssw/cpp/__init__.pyi | 3 +- tests/test_aligner.py | 5 +- 7 files changed, 270 insertions(+), 17 deletions(-) create mode 100644 images/img.png diff --git a/README.md b/README.md index 9aee47c..562816b 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,14 @@ $ pip install mssw ## Benchmark -The result is tested in Linux x86_64 4 cores and 4GB of memory. +The result is tested in Linux x86_64 4 cores and 4GB of memory. ### With BioPython | Query Length | Reference Length | mssw Time (s) | bio python Time (s) | Speedup | | ------------ | ---------------- | ------------- | ------------------- | :------- | | 15 | 39 | 4.470348e-05 | 1.424551e-04 | 3.186666 | -| 150 | 390 | 2.179623e-04 | 2.270699e-03 | 10.41785 | +| 150 | 390 | 2.179623e-04 | 2.270699e-03 | 10.41785 | | 1500 | 3900 | 1.665862e-02 | 1.534623e-01 | 9.212187 | | 15000 | 39000 | 1.696888e+00 | 1.574137e+01 | 9.276609 | @@ -90,3 +90,17 @@ assert alignment.ref_end_next_best == 0 assert alignment.mismatches == 2 assert alignment.cigar_string == "4=1X4=1I5=" ``` + +### Example 5: Print Alignment Result + +```python +import mssw + +reference = "CAGCCTTTCTGACCCGGAAATCAAAATAGGCACAACAAA" +query = "CTGAGCCGGTAAATC" +aligner = mssw.Aligner(match=3, mismatch=1, gap_open=2, gap_extend=2) +alignment = aligner.align(query, reference) +alignment.print() +``` + +![img.png](images/img.png) diff --git a/images/img.png b/images/img.png new file mode 100644 index 0000000000000000000000000000000000000000..3341b6254b8bef46afcf0ea72b58108fec0fd2e3 GIT binary patch literal 51179 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.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] @@ -44,6 +45,7 @@ python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -53,6 +55,34 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "commonmark" +version = "0.9.1" +description = "Python parser for the CommonMark Markdown spec" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +test = ["flake8 (==3.7.8)", "hypothesis (==3.55.3)"] + +[[package]] +name = "importlib-metadata" +version = "5.0.0" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + [[package]] name = "iniconfig" version = "1.1.1" @@ -108,6 +138,9 @@ category = "dev" optional = false python-versions = ">=3.6" +[package.dependencies] +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} + [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] @@ -143,6 +176,17 @@ python-versions = ">=3.6" [package.extras] global = ["pybind11-global (==2.10.0)"] +[[package]] +name = "Pygments" +version = "2.13.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +plugins = ["importlib-metadata"] + [[package]] name = "pyparsing" version = "3.0.9" @@ -165,6 +209,7 @@ python-versions = ">=3.7" [package.dependencies] attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} +importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" @@ -187,6 +232,22 @@ packaging = ">=14.1" pytest = ">=2.9" termcolor = ">=1.1.0" +[[package]] +name = "rich" +version = "12.6.0" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +category = "main" +optional = false +python-versions = ">=3.6.3,<4.0.0" + +[package.dependencies] +commonmark = ">=0.9.0,<0.10.0" +pygments = ">=2.6.0,<3.0.0" +typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""} + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] + [[package]] name = "ruamel.yaml" version = "0.17.21" @@ -242,18 +303,38 @@ category = "dev" optional = false python-versions = ">=3.7" +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "typing-extensions" version = "4.4.0" description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "zipp" +version = "3.9.0" +description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false python-versions = ">=3.7" +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + [metadata] lock-version = "1.1" -python-versions = "^3.8" -content-hash = "8cd88a8fe797c2672c8808edf924e89fb905b6d17223ee81bd7ebf55bea8765a" +python-versions = "^3.7" +content-hash = "76b9c3adbe67faecdb0cdcb0b2d2ea644e932fea7159dbbe87d419a22212f962" [metadata.files] attrs = [ @@ -291,6 +372,14 @@ colorama = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] +commonmark = [ + {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, + {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, +] +importlib-metadata = [ + {file = "importlib_metadata-5.0.0-py3-none-any.whl", hash = "sha256:ddb0e35065e8938f867ed4928d0ae5bf2a53b7773871bfe6bcc7e4fcdc7dea43"}, + {file = "importlib_metadata-5.0.0.tar.gz", hash = "sha256:da31db32b304314d044d3c12c79bd59e307889b287ad12ff387b3500835fc2ab"}, +] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, @@ -327,6 +416,10 @@ pybind11 = [ {file = "pybind11-2.10.0-py3-none-any.whl", hash = "sha256:6bbc7a2f79689307f0d8d240172851955fc214b33e4cbd7fdbc9cd7176a09260"}, {file = "pybind11-2.10.0.tar.gz", hash = "sha256:18977589c10f595f65ec1be90b0a0763b43e458d25d97be9db75b958eb1f43fe"}, ] +Pygments = [ + {file = "Pygments-2.13.0-py3-none-any.whl", hash = "sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"}, + {file = "Pygments-2.13.0.tar.gz", hash = "sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1"}, +] pyparsing = [ {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, @@ -339,6 +432,10 @@ pytest-sugar = [ {file = "pytest-sugar-0.9.5.tar.gz", hash = "sha256:eea78b6f15b635277d3d90280cd386d8feea1cab0f9be75947a626e8b02b477d"}, {file = "pytest_sugar-0.9.5-py2.py3-none-any.whl", hash = "sha256:3da42de32ce4e1e95b448d61c92804433f5d4058c0a765096991c2e93d5a289f"}, ] +rich = [ + {file = "rich-12.6.0-py3-none-any.whl", hash = "sha256:a4eb26484f2c82589bd9a17c73d32a010b1e29d89f1604cd9bf3a2097b81bb5e"}, + {file = "rich-12.6.0.tar.gz", hash = "sha256:ba3a3775974105c221d31141f2c116f4fd65c5ceb0698657a11e9f295ec93fd0"}, +] "ruamel.yaml" = [ {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, @@ -387,7 +484,37 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +typed-ast = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] typing-extensions = [ {file = "typing_extensions-4.4.0-py3-none-any.whl", hash = "sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e"}, {file = "typing_extensions-4.4.0.tar.gz", hash = "sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa"}, ] +zipp = [ + {file = "zipp-3.9.0-py3-none-any.whl", hash = "sha256:972cfa31bc2fedd3fa838a51e9bc7e64b7fb725a8c00e7431554311f180e9980"}, + {file = "zipp-3.9.0.tar.gz", hash = "sha256:3a7af91c3db40ec72dd9d154ae18e008c69efe8ca88dde4f9a731bb82fe2f9eb"}, +] diff --git a/pyproject.toml b/pyproject.toml index e00cf51..2d56132 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "mssw" -version = "0.1.4" +version = "0.1.5" description = "Modern Cpp binding for complete-striped-smith-watern-library" authors = ["Yangyang Li "] license = "MIT" @@ -15,14 +15,13 @@ classifiers = [ 'Operating System :: Unix', 'Operating System :: MacOS', 'Programming Language :: Python', - 'Topic :: Communications :: Email', - 'Topic :: Software Development :: Bug Tracking', ] [tool.poetry.dependencies] python = "^3.7" pybind11 = "^2.10.0" setuptools = "^65.4.1" +rich = "^12.6.0" [tool.poetry.group.dev.dependencies] diff --git a/src/mssw/__init__.py b/src/mssw/__init__.py index f4f4f52..866f40e 100644 --- a/src/mssw/__init__.py +++ b/src/mssw/__init__.py @@ -4,17 +4,123 @@ __PACKAGE_NAME__ = "mssw" import mssw.cpp # type: ignore -from mssw.cpp import Alignment, Filter # type: ignore +from mssw.cpp import Filter # type: ignore import typing as t +from rich.console import Console +from rich.table import Table __all__ = ["Aligner", "Alignment", "Filter"] +class Alignment: + """Alignment class.""" + + def __init__(self): + """Init.""" + self._alignment = cpp.Alignment() + + @classmethod + def from_alignment(cls, alignment: "Alignment"): + """Create alignment from cpp alignment.""" + new_alignment = cls() + new_alignment._alignment = alignment + return new_alignment + + @property + def sw_score(self): + """Get sw score.""" + return self._alignment.sw_score + + @property + def sw_score_next_best(self): + """Get sw score next best.""" + return self._alignment.sw_score_next_best + + @property + def ref_begin(self): + """Get ref begin.""" + return self._alignment.ref_begin + + @property + def ref_end(self): + """Get ref end.""" + return self._alignment.ref_end + + @property + def query_begin(self): + """Get query begin.""" + return self._alignment.query_begin + + @property + def query_end(self): + """Get query end.""" + return self._alignment.query_end + + @property + def ref_end_next_best(self): + """Get ref end next best.""" + return self._alignment.ref_end_next_best + + @property + def mismatches(self): + """Get mismatch.""" + return self._alignment.mismatches + + @property + def cigar_string(self): + """Get cigar string.""" + return self._alignment.cigar_string + + @property + def cigar(self): + """Get cigar.""" + return self._alignment.cigar + + def __repr__(self): + """Get string representation.""" + return ( + f"Alignment(sw_score={self.sw_score}, sw_score_next_best={self.sw_score_next_best}, " + f"ref_begin={self.ref_begin}, ref_end={self.ref_end}, query_begin={self.query_begin}, " + f"query_end={self.query_end}, ref_end_next_best={self.ref_end_next_best}, " + f"mismatch={self.mismatch}, cigar_string={self.cigar_string}, cigar={self.cigar})" + ) + + def clear(self): + """Clear alignment.""" + self._alignment.clear() + + def print(self): + """Print alignment.""" + table = Table( + title="Alignment Result", show_header=True, header_style="bold magenta" + ) + table.add_column("Metric", justify="left", style="cyan", no_wrap=True) + table.add_column("Value", justify="right", style="magenta") + + table.add_row("sw_score", str(self.sw_score)) + table.add_row("sw_score_next_best", str(self.sw_score_next_best)) + table.add_row("ref_begin", str(self.ref_begin)) + table.add_row("ref_end", str(self.ref_end)) + table.add_row("query_begin", str(self.query_begin)) + table.add_row("query_end", str(self.query_end)) + table.add_row("ref_end_next_best", str(self.ref_end_next_best)) + table.add_row("mismatch", str(self.mismatches)) + table.add_row("cigar_string", str(self.cigar_string)) + table.add_row("cigar", str(self.cigar)) + + console = Console() + console.print(table) + + class Aligner: def __init__( self, match: int = 2, mismatch: int = 2, gap_open: int = 3, gap_extend: int = 1 ): + self.match = match + self.mismatch = mismatch + self.gap_open = gap_open + self.gap_extend = gap_extend self._aligner = cpp.Aligner(match, mismatch, gap_open, gap_extend) def align( @@ -22,16 +128,25 @@ def align( query, ref, align_filter: t.Optional[cpp.Filter] = None, - alignment: t.Optional[cpp.Alignment] = None, - ): + alignment: t.Optional[Alignment] = None, + ) -> Alignment: if align_filter is None: align_filter = cpp.Filter() mask_len = max(len(ref) / 2, 15) if alignment is None: - alignment = cpp.Alignment() + alignment = Alignment() + self._aligner.Align( - query, ref, len(ref), align_filter, alignment, int(mask_len) + query, ref, len(ref), align_filter, alignment._alignment, int(mask_len) ) return alignment + + def __repr__(self): + return ( + f"Aligner(match={self.match}, mismatch={self.mismatch}, " + f"gap_open={self.gap_open}, gap_extend={self.gap_extend})" + ) + + __str__ = __repr__ diff --git a/src/mssw/cpp/__init__.pyi b/src/mssw/cpp/__init__.pyi index 3839b7b..acaf8e7 100644 --- a/src/mssw/cpp/__init__.pyi +++ b/src/mssw/cpp/__init__.pyi @@ -1,6 +1,5 @@ """Bindings for ::StripedSmithWaterman namespace""" -from __future__ import annotations -import mssw._cpp.StripedSmithWaterman +import mssw._cpp.StripedSmithWaterman # type: ignore import typing __all__ = [ diff --git a/tests/test_aligner.py b/tests/test_aligner.py index f2b3d14..0062051 100644 --- a/tests/test_aligner.py +++ b/tests/test_aligner.py @@ -3,7 +3,7 @@ def print_alignment(alignment): print("===== SSW result =====\n") - print(f"Best Smith-Waterman score:\t{alignment.sw_score}\n") + print("Best Smith-Waterman score:\t{alignment.sw_score}\n") print(f"Next-best Smith-Waterman score:\t{alignment.sw_score_next_best}\n") print(f"Reference start:\t{alignment.ref_begin}\n") print(f"Reference end:\t{alignment.ref_end}\n") @@ -21,7 +21,6 @@ def test_aligner(): aligner = mssw.Aligner() aligner_filter = mssw.Filter() alignment = aligner.align(query, reference, aligner_filter) - print_alignment(alignment) assert alignment.sw_score == 21 assert alignment.sw_score_next_best == 2 assert alignment.ref_begin == 8 @@ -38,7 +37,6 @@ def test_aligner_default(): query = "CTGAGCCGGTAAATC" aligner = mssw.Aligner() alignment = aligner.align(query, reference) - print_alignment(alignment) assert alignment.sw_score == 21 assert alignment.sw_score_next_best == 2 assert alignment.ref_begin == 8 @@ -56,3 +54,4 @@ def test_aligner_with_custom_score(): aligner = mssw.Aligner(match=3, mismatch=10, gap_open=2, gap_extend=2) alignment = aligner.align(query, reference) assert alignment.cigar_string == "4=1I1D4=1I5=" + alignment.print()