From a09f64591ec46b4b5f8cfe9b3e9f63305e965710 Mon Sep 17 00:00:00 2001 From: Marcus Fedarko Date: Tue, 19 Apr 2022 19:52:21 -0700 Subject: [PATCH 1/6] BUG: Fix single-node-tree check Reliant now on the topology rather than on the string, since relying on the presence of "," yields false positives in the case of a tree with multiple nodes in a single linear chain. --- bp/_io.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bp/_io.pyx b/bp/_io.pyx index c445be3..ec559b6 100644 --- a/bp/_io.pyx +++ b/bp/_io.pyx @@ -148,9 +148,6 @@ cpdef parse_newick(unicode data): np.ndarray[np.double_t, ndim=1] lengths np.ndarray[np.int32_t, ndim=1] edges - if data.count(',') == 0: - raise ValueError("Only trees with more than 1 node supported") - data = data.strip() if not data.endswith(';'): raise ValueError("Newick does not appear terminated with a semicolon") @@ -158,6 +155,9 @@ cpdef parse_newick(unicode data): datalen = len(data) topology = _newick_to_bp(data) + if len(topology.B) <= 1: + raise ValueError("Only trees with more than 1 node supported") + names = np.full(len(topology.B), None, dtype=object) lengths = np.zeros(len(topology.B), dtype=np.double) edges = np.full(len(topology.B), 0, dtype=np.int32) From ff614c178148184c5f88de749998c0d08bfa86a6 Mon Sep 17 00:00:00 2001 From: Marcus Fedarko Date: Tue, 19 Apr 2022 20:13:17 -0700 Subject: [PATCH 2/6] TST: fix tests (new BitArray location) --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 532ec0c..974fa70 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ libbitarr.a: - cd BitArray && $(MAKE) - make -C BitArray -f Makefile + cd bp/BitArray && $(MAKE) + make -C bp/BitArray -f Makefile build: libbitarr.a python setup.py build_ext --inplace From 692aaebcd5ac03e4bc7fc06711b28f6607122b28 Mon Sep 17 00:00:00 2001 From: Marcus Fedarko Date: Tue, 19 Apr 2022 20:36:26 -0700 Subject: [PATCH 3/6] STY/TST: fix flake8 issues; add linear tree test --- bp/GPL/tests/test_insert.py | 1 - bp/_version.py | 13 +++++++++---- bp/tests/test_conv.py | 2 +- bp/tests/test_insert.py | 6 +++++- bp/tests/test_io.py | 21 ++++++++++++++++++--- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/bp/GPL/tests/test_insert.py b/bp/GPL/tests/test_insert.py index b4080aa..1d95b07 100644 --- a/bp/GPL/tests/test_insert.py +++ b/bp/GPL/tests/test_insert.py @@ -31,4 +31,3 @@ def test_insert_multifurcating(self): if __name__ == '__main__': unittest.main() - diff --git a/bp/_version.py b/bp/_version.py index be93199..5f1071e 100644 --- a/bp/_version.py +++ b/bp/_version.py @@ -1,4 +1,3 @@ - # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -339,7 +338,9 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, runner=run_command): pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = runner(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[0].strip() + date = runner( + GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root + )[0].strip() # Use only the last line. Previous lines may contain GPG signature # information. date = date.splitlines()[-1] @@ -429,10 +430,14 @@ def render_pep440_pre(pieces): if pieces["closest-tag"]: if pieces["distance"]: # update the post release segment - tag_version, post_version = pep440_split_post(pieces["closest-tag"]) + tag_version, post_version = pep440_split_post( + pieces["closest-tag"] + ) rendered = tag_version if post_version is not None: - rendered += ".post%d.dev%d" % (post_version+1, pieces["distance"]) + rendered += ".post%d.dev%d" % ( + post_version+1, pieces["distance"] + ) else: rendered += ".post0.dev%d" % (pieces["distance"]) else: diff --git a/bp/tests/test_conv.py b/bp/tests/test_conv.py index cfcd0a2..893bd8d 100644 --- a/bp/tests/test_conv.py +++ b/bp/tests/test_conv.py @@ -16,7 +16,7 @@ def setUp(self): self.sktn = skbio.TreeNode.read(StringIO(self.tstr)) def test_to_skbio_treenode_with_edge_numbers(self): - # from https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0031009 + # from https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0031009 # noqa # but without edge labels # 0 1 2 3 4 5 6 7 8 9 # 1 1 1 0 1 0 0 1 0 0 diff --git a/bp/tests/test_insert.py b/bp/tests/test_insert.py index c159b9d..eb3eedd 100644 --- a/bp/tests/test_insert.py +++ b/bp/tests/test_insert.py @@ -7,11 +7,14 @@ class InsertTests(unittest.TestCase): package = 'bp.tests' + def setUp(self): self.jplacedata_multiple = \ open(self.get_data_path('300/placement_mul.jplace')).read() self.final_multiple_fully_resolved = \ - skbio.TreeNode.read(self.get_data_path('300/placement.full_resolve.newick')) + skbio.TreeNode.read( + self.get_data_path('300/placement.full_resolve.newick') + ) def get_data_path(self, filename): # adapted from qiime2.plugin.testing.TestPluginBase @@ -51,5 +54,6 @@ def test_insert_fully_resolved_multiple_placements(self): self.assertEqual(obs.compare_rfd(exp), 0) self.assertAlmostEqual(obs.compare_tip_distances(exp), 0) + if __name__ == '__main__': unittest.main() diff --git a/bp/tests/test_io.py b/bp/tests/test_io.py index 9954225..dd4a62e 100644 --- a/bp/tests/test_io.py +++ b/bp/tests/test_io.py @@ -13,12 +13,13 @@ class NewickTests(TestCase): def test_parse_newick_simple_edge_numbers(self): - # from https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0031009 + # from https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0031009 # noqa # but without edge labels # 0 1 2 3 4 5 6 7 8 9 # 1 1 1 0 1 0 0 1 0 0 in_ = '((A:.01{0}, B:.01{1})D:.01{3}, C:.01{4}) {5};' - exp_sk = '((A:.01, B:.01)D:.01, C:.01);' # skbio doesn't know about edge numbers + # skbio doesn't know about edge numbers + exp_sk = '((A:.01, B:.01)D:.01, C:.01);' obs = parse_newick(in_) obs_sk = to_skbio_treenode(obs) exp_sk = skbio.TreeNode.read([exp_sk]) @@ -82,6 +83,20 @@ def test_parse_newick_singlenode_bug(self): with self.assertRaises(ValueError): parse_newick(test) + def test_parse_newick_linear_tree(self): + # https://github.com/biocore/improved-octo-waddle/pull/48 + test = '((b:3)a:2)root:1;' + + # Test that we can parse these edge cases successfully, without + # mistaking them for single-node trees + topology = parse_newick(test) + + # Convert the tree to a skbio TreeNode to make checking easier + skbio_tree = to_skbio_treenode(topology) + self.assertEqual(skbio_tree.name, "root") + self.assertEqual([n.name for n in skbio_tree.children], ["a"]) + self.assertEqual([n.name for n in skbio_tree.tips()], ["b"]) + def test_parse_newick_no_semicolon_bug(self): # https://github.com/wasade/improved-octo-waddle/issues/26 test = "((h:1, i:1, j:1, k:1, l: 1),(e:1,f:1),(n:1,o:1,p:1))a:1" @@ -96,7 +111,7 @@ def test_parse_newick_no_semicolon_bug(self): def test_write_newick_underscore_bug(self): test = "(((a)b)'c_foo',((d)e)f)r;" buf = io.StringIO() - obs = write_newick(parse_newick(test), buf, False) + write_newick(parse_newick(test), buf, False) buf.seek(0) self.assertIn("'c_foo'", test) From 60e5f6d98dc25a273f8827a511b1234a2b27c755 Mon Sep 17 00:00:00 2001 From: Marcus Fedarko Date: Tue, 19 Apr 2022 21:03:54 -0700 Subject: [PATCH 4/6] TST: Explicitly verify that the only non-tip is a Just a small check that linear trees somehow aren't parsed as having *more* nodes than they should. IDK how that would even happen, but this ensures it shouldn't at least... --- bp/tests/test_io.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bp/tests/test_io.py b/bp/tests/test_io.py index dd4a62e..998d535 100644 --- a/bp/tests/test_io.py +++ b/bp/tests/test_io.py @@ -95,6 +95,7 @@ def test_parse_newick_linear_tree(self): skbio_tree = to_skbio_treenode(topology) self.assertEqual(skbio_tree.name, "root") self.assertEqual([n.name for n in skbio_tree.children], ["a"]) + self.assertEqual([n.name for n in skbio_tree.non_tips()], ["a"]) self.assertEqual([n.name for n in skbio_tree.tips()], ["b"]) def test_parse_newick_no_semicolon_bug(self): From 551a679c2216106d24fd71b7d82ebc6f3ffda57d Mon Sep 17 00:00:00 2001 From: Marcus Fedarko Date: Wed, 20 Apr 2022 16:06:30 -0700 Subject: [PATCH 5/6] BLD: remove travis To avoid confusion; the CI for iow is now on github actions --- .travis.yml | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 64408cb..0000000 --- a/.travis.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Check on http://lint.travis-ci.org/ after modifying it! Originally -# modified from https://gist.github.com/dan-blanchard/7045057 -sudo: false -language: python -env: - - PYTHON_VERSION=3.6 -before_install: - - wget http://repo.continuum.io/miniconda/Miniconda3-3.7.3-Linux-x86_64.sh -O miniconda.sh - - chmod +x miniconda.sh - - ./miniconda.sh -b - - export PATH=/home/travis/miniconda3/bin:$PATH - # Update conda itself - - conda update --yes conda -install: - - conda create --yes -n env_name python=$PYTHON_VERSION --file ci/conda_requirements.txt - - source activate env_name - - pip install . --no-deps -script: - - flake8 bp - - make test -after_success: - - coveralls From 3a13e3a688271e7a6dbc7761c28c9fe988969f28 Mon Sep 17 00:00:00 2001 From: Marcus Fedarko Date: Wed, 20 Apr 2022 16:17:14 -0700 Subject: [PATCH 6/6] DOC/BLD: add the (obligatory) fancy badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6f65eb6..ce4f3a1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ Improved Octo Waddle -------------------- +[![Improved Octo Waddle CI](https://github.com/biocore/improved-octo-waddle/actions/workflows/python-package-conda.yml/badge.svg)](https://github.com/biocore/improved-octo-waddle/actions/workflows/python-package-conda.yml) + An implementation of the balanced parentheses tree structure as described by [Cordova and Navarro](http://www.dcc.uchile.cl/~gnavarro/ps/tcs16.2.pdf).