diff --git a/README.md b/README.md index a60a827..1d7b66c 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,7 @@ Each tool in the BuddySuite has been extensively documented in the [wiki](https: All of the individual Buddy toolkits are located in the 'buddysuite' directory and the ['develop' branch](https://github.com/biologyguy/BuddySuite/tree/develop) is where all new features have been implemented. If you're interested in contributing, please refer to the - [developer page](https://github.com/biologyguy/BuddySuite/wiki/Developers) for further information on dependencies - and instructions. + [developer page](https://github.com/biologyguy/BuddySuite/wiki/Developers) for further information. ## Citation We are currently working on a [peer reviewed manuscript](https://github.com/biologyguy/BuddySuite/tree/develop/manuscript), but until then diff --git a/README.rst b/README.rst index c1755ba..fa5a318 100644 --- a/README.rst +++ b/README.rst @@ -60,7 +60,7 @@ branch `__ is where all new features have been implemented. If you're interested in contributing, please refer to the `developer page `__ for -further information on dependencies and instructions. +further information. Citation -------- diff --git a/buddysuite/AlignBuddy.py b/buddysuite/AlignBuddy.py index 5caf10a..98c4959 100755 --- a/buddysuite/AlignBuddy.py +++ b/buddysuite/AlignBuddy.py @@ -28,11 +28,15 @@ # BuddySuite specific try: - from . import buddy_resources as br - from . import SeqBuddy as Sb -except SystemError: import buddy_resources as br import SeqBuddy as Sb +except ImportError: + try: + import buddysuite.buddy_resources as br + import buddysuite.SeqBuddy as Sb + except AttributeError: + from . import buddy_resources as br + from . import SeqBuddy as Sb # Standard library import sys @@ -65,7 +69,7 @@ # ################################################ GLOBALS ###################################################### # GAP_CHARS = ["-", ".", " "] -VERSION = br.Version("AlignBuddy", 1, "2b6", br.contributors, {"year": 2016, "month": 10, "day": 3}) +VERSION = br.Version("AlignBuddy", 1, "2b7", br.contributors, {"year": 2016, "month": 10, "day": 19}) # #################################################### ALIGNBUDDY #################################################### # diff --git a/buddysuite/BuddySuite.py b/buddysuite/BuddySuite.py index 15b6265..b9e0c46 100755 --- a/buddysuite/BuddySuite.py +++ b/buddysuite/BuddySuite.py @@ -24,12 +24,21 @@ from configparser import ConfigParser, NoOptionError import os import re -import buddysuite -import buddysuite.buddy_resources as br import shutil import random import string +try: + import buddysuite + import buddy_resources as br +except ImportError: + try: + import buddysuite + import buddysuite.buddy_resources as br + except AttributeError: + from . import buddysuite + from . import buddy_resources as br + def setup(): # ToDo: Check permissions? print("\033[1mWelcome to BuddySuite!\033[m\nLet's configure your installation:\n") diff --git a/buddysuite/DatabaseBuddy.py b/buddysuite/DatabaseBuddy.py index d10f399..a0e2b10 100755 --- a/buddysuite/DatabaseBuddy.py +++ b/buddysuite/DatabaseBuddy.py @@ -30,9 +30,12 @@ # BuddySuite specific try: - from . import buddy_resources as br -except SystemError: import buddy_resources as br +except ImportError: + try: + import buddysuite.buddy_resources as br + except AttributeError: + from . import buddy_resources as br # Standard library import sys @@ -77,7 +80,7 @@ "fastq-solexa", "fastq-illumina", "genbank", "gb", "imgt", "nexus", "phd", "phylip", "seqxml", "stockholm", "tab", "qual"] CONFIG = br.config_values() -VERSION = br.Version("DatabaseBuddy", 1, "2b6", br.contributors, {"year": 2016, "month": 10, "day": 3}) +VERSION = br.Version("DatabaseBuddy", 1, "2b7", br.contributors, {"year": 2016, "month": 10, "day": 19}) GREY = "\033[90m" RED = "\033[91m" diff --git a/buddysuite/PhyloBuddy.py b/buddysuite/PhyloBuddy.py index a729716..11a8342 100755 --- a/buddysuite/PhyloBuddy.py +++ b/buddysuite/PhyloBuddy.py @@ -27,11 +27,15 @@ # BuddySuite specific try: - from . import buddy_resources as br - from . import AlignBuddy as Alb -except SystemError: import buddy_resources as br import AlignBuddy as Alb +except ImportError: + try: + import buddysuite.buddy_resources as br + import buddysuite.AlignBuddy as Alb + except AttributeError: + from . import buddy_resources as br + from . import AlignBuddy as Alb # Standard library import sys @@ -143,7 +147,7 @@ def ascending_order(phylobuddy): # ##################################################### GLOBALS ###################################################### # CONFIG = br.config_values() -VERSION = br.Version("PhyloBuddy", 1, "2b6", br.contributors, {"year": 2016, "month": 10, "day": 3}) +VERSION = br.Version("PhyloBuddy", 1, "2b7", br.contributors, {"year": 2016, "month": 10, "day": 19}) OUTPUT_FORMATS = ["newick", "nexus", "nexml"] PHYLO_INFERENCE_TOOLS = ["raxml", "phyml", "fasttree"] diff --git a/buddysuite/SeqBuddy.py b/buddysuite/SeqBuddy.py index 5b811f1..85e9f06 100755 --- a/buddysuite/SeqBuddy.py +++ b/buddysuite/SeqBuddy.py @@ -31,11 +31,15 @@ # BuddySuite specific try: - from . import buddy_resources as br - from . import AlignBuddy as Alb -except SystemError: import buddy_resources as br import AlignBuddy as Alb +except ImportError: + try: + import buddysuite.buddy_resources as br + import buddysuite.AlignBuddy as Alb + except AttributeError: + from . import buddy_resources as br + from . import AlignBuddy as Alb # Standard library import sys @@ -153,7 +157,7 @@ def incremental_rename(query, replace): # - Try to speed things up by reading in all sequence data only when necessary # ###################################################### GLOBALS ##################################################### # -VERSION = br.Version("SeqBuddy", 1, "2b6", br.contributors, {"year": 2016, "month": 10, "day": 3}) +VERSION = br.Version("SeqBuddy", 1, "2b7", br.contributors, {"year": 2016, "month": 10, "day": 19}) OUTPUT_FORMATS = ["ids", "accessions", "summary", "full-summary", "clustal", "embl", "fasta", "fastq", "fastq-sanger", "fastq-solexa", "fastq-illumina", "genbank", "gb", "imgt", "nexus", "phd", "phylip", "phylip-relaxed", "phylipss", "phylipsr", "raw", "seqxml", "sff", "stockholm", "tab", "qual"] @@ -1355,11 +1359,11 @@ def count_residues(seqbuddy): neut = len(re.findall("[GAVLIPFYWSTNQM]", seq)) resid_count['% Uncharged'] = round(100 * neut / seq_len, 2) - hyrdophobic = len(re.findall("[AVLIPYFWMC]", seq)) - resid_count['% Hyrdophobic'] = round(100 * hyrdophobic / seq_len, 2) + hydrophobic = len(re.findall("[AVLIPYFWMC]", seq)) + resid_count['% Hydrophobic'] = round(100 * hydrophobic / seq_len, 2) - hyrdophilic = len(re.findall("[NQSTKRHDE]", seq)) - resid_count['% Hyrdophilic'] = round(100 * hyrdophilic / seq_len, 2) + hydrophilic = len(re.findall("[NQSTKRHDE]", seq)) + resid_count['% Hydrophilic'] = round(100 * hydrophilic / seq_len, 2) for residue in ["A", "C", "D", "E", "F", "G", "H", "I", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "V", "W", "Y"]: diff --git a/buddysuite/buddy_resources.py b/buddysuite/buddy_resources.py index 030b862..e0c704d 100755 --- a/buddysuite/buddy_resources.py +++ b/buddysuite/buddy_resources.py @@ -23,9 +23,8 @@ """ from __future__ import print_function import sys -if float("%s.%s" % (sys.version_info[0], sys.version_info[1])) < 3.5: - print("Error: Attempting to run BuddySuite with Python %s.%s. Python 3.5+ required." % - (sys.version_info[0], sys.version_info[1])) +if int(sys.version_info[0]) < 3: + print("Error: Attempting to run BuddySuite with Python %s. Python 3+ required." % sys.version_info[0]) sys.exit() import argparse import datetime diff --git a/buddysuite/tests/__init__.py b/buddysuite/tests/__init__.py index 0b308b4..a6f0b09 100644 --- a/buddysuite/tests/__init__.py +++ b/buddysuite/tests/__init__.py @@ -1,13 +1,17 @@ # coding=utf-8 import os +import sys from copy import deepcopy from hashlib import md5 from collections import OrderedDict -from .. import AlignBuddy as Alb -from .. import SeqBuddy as Sb -from .. import PhyloBuddy as Pb -from .. import DatabaseBuddy as Db +DIRECTORY_SCRIPT = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, "%s%s.." % (DIRECTORY_SCRIPT, os.sep)) + +import AlignBuddy as Alb +import SeqBuddy as Sb +import PhyloBuddy as Pb +import DatabaseBuddy as Db # This file (conftest.py) must be in the same directory as unit_test_resources RESOURCE_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'unit_test_resources') + os.path.sep @@ -21,7 +25,7 @@ def __init__(self): @staticmethod def buddy2hash(buddy): if type(buddy) not in [Sb.SeqBuddy, Alb.AlignBuddy, Pb.PhyloBuddy, Db.DbBuddy]: - raise AttributeError("Buddy object required") + raise AttributeError("Buddy object required, not %s" % type(buddy)) _hash = md5("{0}".format(str(buddy)).encode("utf-8")).hexdigest() return _hash @@ -320,7 +324,7 @@ def get_one(self, code, mode="objs"): if len(code.split()) != 3: raise AttributeError("Only explicit three-component codes are accepted") output = self.get_list(code, mode) - return None if not output or len(output) > 1else output[0] + return None if not output or len(output) > 1 else output[0] # ################################ - PhyloBuddy - ################################# # diff --git a/buddysuite/tests/conftest.py b/buddysuite/tests/conftest.py index b6a056c..4d02407 100755 --- a/buddysuite/tests/conftest.py +++ b/buddysuite/tests/conftest.py @@ -33,19 +33,19 @@ def sb_resources(): def sb_odd_resources(): # A dict of invalid file resources resource_list = {file_format: name.format(path=RESOURCE_PATH) for file_format, name in [ - ('blank', '{path}/blank.fa'), - ('figtree', '{path}/figtree.nexus'), - ('unrecognizable', '{path}/unrecognizable.phy'), - ('gibberish', '{path}/gibberish.fa'), - ('phylipss_cols', '{path}/malformed_phylip_columns.physs'), - ('duplicate', '{path}/Duplicate_seqs.fa'), - ('ambiguous_dna', '{path}/ambiguous_dna.fa'), - ('ambiguous_rna', '{path}/ambiguous_rna.fa'), - ('blastn', '{path}/blast/Mnemiopsis_cds.n'), - ('blastp', '{path}/blast/Mnemiopsis_pep.p'), - ('dummy_feats', '{path}/Mnemiopsis_cds_dummy_features.gb'), - ('cnidaria_pep', '{path}/Cnidaria_pep.nexus'), - ('mixed', '{path}/mixed_alpha.fa') + ('blank', '{path}blank.fa'), + ('figtree', '{path}figtree.nexus'), + ('unrecognizable', '{path}unrecognizable.phy'), + ('gibberish', '{path}gibberish.fa'), + ('phylipss_cols', '{path}malformed_phylip_columns.physs'), + ('duplicate', '{path}Duplicate_seqs.fa'), + ('ambiguous_dna', '{path}ambiguous_dna.fa'), + ('ambiguous_rna', '{path}ambiguous_rna.fa'), + ('blastn', '{path}blast/Mnemiopsis_cds.n'), + ('blastp', '{path}blast/Mnemiopsis_pep.p'), + ('dummy_feats', '{path}Mnemiopsis_cds_dummy_features.gb'), + ('cnidaria_pep', '{path}Cnidaria_pep.nexus'), + ('mixed', '{path}mixed_alpha.fa') ]} return resource_list @@ -66,16 +66,16 @@ def alb_odd_resources(): resource_list = { 'dna': { 'single': {file_format: name.format(path=RESOURCE_PATH) for file_format, name in [ - ('fasta', '{path}/gibberish.fa'), - ('phylipss_recs', '{path}/malformed_phylip_records.physs'), - ('phylipss_cols', '{path}/malformed_phylip_columns.physs'), - ('ambiguous', '{path}/ambiguous_dna_alignment.fa')]} + ('fasta', '{path}gibberish.fa'), + ('phylipss_recs', '{path}malformed_phylip_records.physs'), + ('phylipss_cols', '{path}malformed_phylip_columns.physs'), + ('ambiguous', '{path}ambiguous_dna_alignment.fa')]} }, 'protein': { 'single': {file_format: name.format(path=RESOURCE_PATH) for file_format, name in [ - ('phylip', '{path}/unrecognizable.phy')]} + ('phylip', '{path}unrecognizable.phy')]} }, - 'blank': "%s/blank.fa" % RESOURCE_PATH + 'blank': "%sblank.fa" % RESOURCE_PATH } return resource_list @@ -94,13 +94,13 @@ def pb_resources(): def pb_odd_resources(): # A dict of invalid file resources resource_list = {file_format: name.format(path=RESOURCE_PATH) for file_format, name in [ - ('blank', '{path}/blank.fa'), - ('unrecognizable', '{path}/unrecognizable.phy'), - ('figtree', '{path}/figtree.nexus'), - ('compare', '{path}/compare_trees.newick'), - ('node_lables', '{path}/tree_with_node_lables.nwk'), - ('lengths', '{path}/Mnemiopsis_pep.newick'), - ('bootstraps', '{path}/Mnemiopsis_pep_bootstraps.newick'), - ('support', '{path}/Mnemiopsis_pep_support.newick') + ('blank', '{path}blank.fa'), + ('unrecognizable', '{path}unrecognizable.phy'), + ('figtree', '{path}figtree.nexus'), + ('compare', '{path}compare_trees.newick'), + ('node_lables', '{path}tree_with_node_lables.nwk'), + ('lengths', '{path}Mnemiopsis_pep.newick'), + ('bootstraps', '{path}Mnemiopsis_pep_bootstraps.newick'), + ('support', '{path}Mnemiopsis_pep_support.newick') ]} return resource_list diff --git a/buddysuite/tests/test_alignbuddy/test_alb_3rd_party.py b/buddysuite/tests/test_alignbuddy/test_alb_3rd_party.py index 5ee4dc9..ae0c0f3 100644 --- a/buddysuite/tests/test_alignbuddy/test_alb_3rd_party.py +++ b/buddysuite/tests/test_alignbuddy/test_alb_3rd_party.py @@ -12,9 +12,9 @@ from unittest import mock from shutil import which from subprocess import Popen, PIPE -from ... import AlignBuddy as Alb -from ... import SeqBuddy as Sb -from ... import buddy_resources as br +import AlignBuddy as Alb +import SeqBuddy as Sb +import buddy_resources as br # ########################################### 'ga', '--generate_alignment' ########################################## # @@ -237,17 +237,11 @@ def ask_true(*ask_args): pass return True - try: - monkeypatch.setattr("buddysuite.buddy_resources.ask", ask_false) - except ImportError: - monkeypatch.setattr("buddy_resources.ask", ask_false) + monkeypatch.setattr(br, "ask", ask_false) with pytest.raises(SystemExit): Alb.generate_msa(tester, clustalo_bin, keep_temp="%s%sga_temp_files" % (temp_dir.path, os.sep)) - try: - monkeypatch.setattr("buddysuite.buddy_resources.ask", ask_true) - except ImportError: - monkeypatch.setattr("buddy_resources.ask", ask_true) + monkeypatch.setattr(br, "ask", ask_true) Alb.generate_msa(tester, clustalo_bin, keep_temp="%s%sga_temp_files" % (temp_dir.path, os.sep)) assert os.path.isfile("{0}{1}ga_temp_files{1}result".format(temp_dir.path, os.sep)) assert os.path.isfile("{0}{1}ga_temp_files{1}tmp.fa".format(temp_dir.path, os.sep)) diff --git a/buddysuite/tests/test_alignbuddy/test_alb_api.py b/buddysuite/tests/test_alignbuddy/test_alb_api.py index 149f7ba..9d7d75c 100644 --- a/buddysuite/tests/test_alignbuddy/test_alb_api.py +++ b/buddysuite/tests/test_alignbuddy/test_alb_api.py @@ -9,9 +9,9 @@ from Bio.SeqRecord import SeqRecord from Bio.AlignIO import MultipleSeqAlignment from Bio.Alphabet import IUPAC -from ... import AlignBuddy as Alb -from ... import SeqBuddy as Sb -from ... import buddy_resources as br +import AlignBuddy as Alb +import SeqBuddy as Sb +import buddy_resources as br from .. import __init__ from collections import OrderedDict diff --git a/buddysuite/tests/test_alignbuddy/test_alb_class_and_helpers.py b/buddysuite/tests/test_alignbuddy/test_alb_class_and_helpers.py index 52d0e8f..715fdbe 100755 --- a/buddysuite/tests/test_alignbuddy/test_alb_class_and_helpers.py +++ b/buddysuite/tests/test_alignbuddy/test_alb_class_and_helpers.py @@ -9,9 +9,9 @@ from Bio.Alphabet import IUPAC from Bio import AlignIO -from ... import buddy_resources as br -from ...AlignBuddy import AlignBuddy, guess_alphabet, guess_format, make_copy -from ...buddy_resources import GuessError, parse_format +import buddy_resources as br +from AlignBuddy import AlignBuddy, guess_alphabet, guess_format, make_copy +from buddy_resources import GuessError, parse_format def mock_valueerror(*args, **kwargs): diff --git a/buddysuite/tests/test_alignbuddy/test_alb_ui.py b/buddysuite/tests/test_alignbuddy/test_alb_ui.py index cd268a5..279d4e2 100644 --- a/buddysuite/tests/test_alignbuddy/test_alb_ui.py +++ b/buddysuite/tests/test_alignbuddy/test_alb_ui.py @@ -29,9 +29,9 @@ import argparse from copy import deepcopy -from ... import AlignBuddy as Alb -from ... import SeqBuddy as Sb -from ... import buddy_resources as br +import AlignBuddy as Alb +import SeqBuddy as Sb +import buddy_resources as br TEMP_DIR = br.TempDir() VERSION = Sb.VERSION diff --git a/buddysuite/tests/test_buddy_resources/test_buddy_resources.py b/buddysuite/tests/test_buddy_resources/test_buddy_resources.py index 208e046..41c9258 100644 --- a/buddysuite/tests/test_buddy_resources/test_buddy_resources.py +++ b/buddysuite/tests/test_buddy_resources/test_buddy_resources.py @@ -15,8 +15,8 @@ from time import sleep import datetime from unittest import mock -from ... import AlignBuddy as Alb -from ... import buddy_resources as br +import AlignBuddy as Alb +import buddy_resources as br from pkg_resources import DistributionNotFound from configparser import ConfigParser if os.name == "nt": @@ -673,8 +673,8 @@ def storlines(*args, **kwargs): File "sb", line 4612, in command_line_ui 1/0 """ - error_hash = b'44f77d4602b7213f958ed80ef0301365' - fake_raw_output = io.BytesIO(b'{\"%b\": [1.1, 1.2]}' % error_hash) + error_hash = b'{"44f77d4602b7213f958ed80ef0301365": [1.1, 1.2]}' + fake_raw_output = io.BytesIO(error_hash) monkeypatch.setattr(urllib.request, "urlopen", lambda *_, **__: fake_raw_output) monkeypatch.setattr(br, "FTP", FakeFTP) @@ -683,14 +683,14 @@ def storlines(*args, **kwargs): br.error_report(fake_error, True) # Known bug - error_hash = b'54f77d4602b7213f958ed80ef0301365' - fake_raw_output = io.BytesIO(b'{\"%b\": [1.1, 1.2]}' % error_hash) # Needs to be reset every time + error_hash = b'{"54f77d4602b7213f958ed80ef0301365": [1.1, 1.2]}' + fake_raw_output = io.BytesIO(error_hash) # Needs to be reset every time monkeypatch.setattr(urllib.request, "urlopen", lambda *_, **__: fake_raw_output) with pytest.raises(RuntimeError): # Unknown error, diagnostics true br.error_report(fake_error, True) - fake_raw_output = io.BytesIO(b'{\"%b\": [1.1, 1.2]}' % error_hash) + fake_raw_output = io.BytesIO(error_hash) monkeypatch.setattr(urllib.request, "urlopen", lambda *_, **__: fake_raw_output) def raise_ftp_errors(*args, **kwargs): @@ -874,7 +874,11 @@ def test_send_traceback(capsys, monkeypatch): # Function: FailedFunc""" in out assert """\ -# User: hashless +# TestBuddy: 1.2 +# Function: FailedFunc +# Python:""" in out + + assert """\ # Date: %s RuntimeError: Something broke! @@ -900,7 +904,7 @@ def test_shift_features(sb_resources, hf): features = buddy.records[0].features shifted_features = br.shift_features(features, 10, len(buddy.records[0])) buddy.records[0].features = shifted_features - assert hf.buddy2hash(buddy) == "e494bf6dc9c6c73c509e66ffc3db57a9" + assert hf.buddy2hash(buddy) == "e494bf6dc9c6c73c509e66ffc3db57a9", print(buddy) buddy = sb_resources.get_one("d g") buddy.records = [buddy.records[0]] diff --git a/buddysuite/tests/test_buddysuite/test_buddysuite.py b/buddysuite/tests/test_buddysuite/test_buddysuite.py index e9c0dd1..1b8ab87 100644 --- a/buddysuite/tests/test_buddysuite/test_buddysuite.py +++ b/buddysuite/tests/test_buddysuite/test_buddysuite.py @@ -1,13 +1,12 @@ #!/use/bin/env python3 # coding=utf-8 -import pytest import sys import re import os import shutil -from ... import BuddySuite as Bs -from ... import buddy_resources as br +import BuddySuite as Bs +import buddy_resources as br def test_version(capsys): diff --git a/buddysuite/tests/test_databasebuddy/test_db_class_and_helpers.py b/buddysuite/tests/test_databasebuddy/test_db_class_and_helpers.py index 615b9db..0d5c05e 100644 --- a/buddysuite/tests/test_databasebuddy/test_db_class_and_helpers.py +++ b/buddysuite/tests/test_databasebuddy/test_db_class_and_helpers.py @@ -5,8 +5,8 @@ import random import re -from ... import buddy_resources as br -from ... import DatabaseBuddy as Db +import buddy_resources as br +import DatabaseBuddy as Db # A few real accession numbers to test things out with ACCNS = ["NP_001287575.1", "ADH10263.1", "XP_005165403.2", "A0A087WX72", "A0A096MTH0", "A0A0A9YFB0", diff --git a/buddysuite/tests/test_databasebuddy/test_db_clients.py b/buddysuite/tests/test_databasebuddy/test_db_clients.py index 4f90ce4..8989368 100644 --- a/buddysuite/tests/test_databasebuddy/test_db_clients.py +++ b/buddysuite/tests/test_databasebuddy/test_db_clients.py @@ -6,8 +6,9 @@ import re import json import os -from ... import buddy_resources as br -from ... import DatabaseBuddy as Db +import buddy_resources as br +import DatabaseBuddy as Db +from io import StringIO def patched_close(self): # This suppresses an 'ignored' exception @@ -83,12 +84,12 @@ def mock_urlopen_uniprot_summary(*args, **kwargs): def mock_raise_httperror(*args, **kwargs): print("mock_raise_httperror\nargs: %s\nkwargs: %s" % (args, kwargs)) - raise HTTPError(url="http://fake.come", code=101, msg="Fake HTTPError from Mock", hdrs="Foo", fp="Bar") + raise HTTPError(url="http://fake.come", code=101, msg="Fake HTTPError from Mock", hdrs="Foo", fp=StringIO("Bar")) def mock_raise_503_httperror(*args, **kwargs): print("mock_raise_httperror\nargs: %s\nkwargs: %s" % (args, kwargs)) - raise HTTPError(url="http://fake.come", code=503, msg="Service unavailable", hdrs="Foo", fp="Bar") + raise HTTPError(url="http://fake.come", code=503, msg="Service unavailable", hdrs="Foo", fp=StringIO("Bar")) def mock_raise_urlerror(*args, **kwargs): @@ -135,7 +136,7 @@ def test_client_parse_error_file(): assert not client.parse_error_file() assert not dbbuddy.failures - client.http_errors_file.write("Casp9\n%s\n//\n" % HTTPError("101", "Fake HTTPError from Mock", "Foo", "Bar", "Baz")) + client.http_errors_file.write("Casp9\n%s\n//\n" % HTTPError("101", "Fake HTTPError from Mock", "Foo", "Bar", StringIO("Baz"))) client.http_errors_file.write("Inx1\n%s\n//\n" % URLError("Fake URLError from Mock")) assert client.parse_error_file() == '''Casp9 @@ -176,11 +177,11 @@ def test_uniprotrestclient_init(): assert client.max_url == 1000 -def test_uniprotrestclient_query_uniprot(capsys): +def test_uniprotrestclient_query_uniprot(capsys, monkeypatch): dbbuddy = Db.DbBuddy() client = Db.UniProtRestClient(dbbuddy) - with mock.patch('buddysuite.DatabaseBuddy.urlopen', mock_urlopen_handle_uniprot_ids): - client.query_uniprot("inx15", {"format": "list"}) + monkeypatch.setattr(Db, 'urlopen', mock_urlopen_handle_uniprot_ids) + client.query_uniprot("inx15", {"format": "list"}) assert client.results_file.read() == '''# Search: inx15 A8XEF9 @@ -189,25 +190,25 @@ def test_uniprotrestclient_query_uniprot(capsys): // ''' # Also make sure request_params can come in as a list - with mock.patch('buddysuite.DatabaseBuddy.urlopen', mock_urlopen_handle_uniprot_ids): - client.query_uniprot("inx15", [{"format": "list"}]) + monkeypatch.setattr(Db, 'urlopen', mock_urlopen_handle_uniprot_ids) + client.query_uniprot("inx15", [{"format": "list"}]) # Errors - with mock.patch('buddysuite.DatabaseBuddy.urlopen', mock_raise_httperror): - client.query_uniprot("inx15", [{"format": "list"}]) + monkeypatch.setattr(Db, 'urlopen', mock_raise_httperror) + client.query_uniprot("inx15", [{"format": "list"}]) assert client.http_errors_file.read() == "Uniprot search failed for 'inx15'\nHTTP Error 101: " \ "Fake HTTPError from Mock\n//\n" - with mock.patch('buddysuite.DatabaseBuddy.urlopen', mock_raise_urlerror_8): - client.query_uniprot("inx15", [{"format": "list"}]) + monkeypatch.setattr(Db, 'urlopen', mock_raise_urlerror_8) + client.query_uniprot("inx15", [{"format": "list"}]) assert "Uniprot request failed, are you connected to the internet?" in client.http_errors_file.read() - with mock.patch('buddysuite.DatabaseBuddy.urlopen', mock_raise_urlerror): - client.query_uniprot("inx15", [{"format": "list"}]) + monkeypatch.setattr(Db, 'urlopen', mock_raise_urlerror) + client.query_uniprot("inx15", [{"format": "list"}]) assert "" in client.http_errors_file.read() - with mock.patch('buddysuite.DatabaseBuddy.urlopen', mock_raise_keyboardinterrupt): - client.query_uniprot("inx15", [{"format": "list"}]) + monkeypatch.setattr(Db, 'urlopen', mock_raise_keyboardinterrupt) + client.query_uniprot("inx15", [{"format": "list"}]) out, err = capsys.readouterr() assert "\n\tUniProt query interrupted by user\n" in err @@ -215,19 +216,19 @@ def test_uniprotrestclient_query_uniprot(capsys): client.query_uniprot("ABXEF9", params) -def test_uniprotrestclient_count_hits(): +def test_uniprotrestclient_count_hits(monkeypatch): dbbuddy = Db.DbBuddy("inx15,inx16") client = Db.UniProtRestClient(dbbuddy) - with mock.patch('buddysuite.DatabaseBuddy.urlopen', mock_urlopen_uniprot_count_hits): - assert client.count_hits() == 10 + monkeypatch.setattr(Db, 'urlopen', mock_urlopen_uniprot_count_hits) + assert client.count_hits() == 10 - for indx in range(10): - client.dbbuddy.search_terms.append("a" * 110) - assert client.count_hits() == 20 + for indx in range(10): + client.dbbuddy.search_terms.append("a" * 110) + assert client.count_hits() == 20 - with mock.patch('buddysuite.DatabaseBuddy.urlopen', mock_raise_httperror): - assert client.count_hits() == 0 - assert "d3b8e6bb4b9094117b7555b01dc85f64" in client.dbbuddy.failures + monkeypatch.setattr(Db, 'urlopen', mock_raise_httperror) + assert client.count_hits() == 0 + assert "d3b8e6bb4b9094117b7555b01dc85f64" in client.dbbuddy.failures with pytest.raises(ValueError) as err: client.dbbuddy.search_terms[0] = "a" * 1001 @@ -671,9 +672,11 @@ def patch_ensembl_urlopen(*args, **kwargs): with open("%s/ensembl_sequence.seqxml" % test_files, "r") as ifile: outfile.write(ifile.read().encode()) elif "error400" in args[0].full_url: - raise HTTPError(url="http://fake.come", code=400, msg="Bad request", hdrs="Foo", fp="Bar") + raise HTTPError(url="http://fake.come", code=400, msg="Bad request", + hdrs="Foo", fp=StringIO("Bar")) elif "error429" in args[0].full_url: - raise HTTPError(url="http://fake.come", code=429, msg="Server busy", hdrs={'Retry-After': 0}, fp="Bar") + raise HTTPError(url="http://fake.come", code=429, msg="Server busy", + hdrs={'Retry-After': 0}, fp=StringIO("Bar")) return outfile.get_handle("r") outfile = br.TempFile(byte_mode=True) diff --git a/buddysuite/tests/test_databasebuddy/test_db_ui.py b/buddysuite/tests/test_databasebuddy/test_db_ui.py index 64143de..d7860bc 100644 --- a/buddysuite/tests/test_databasebuddy/test_db_ui.py +++ b/buddysuite/tests/test_databasebuddy/test_db_ui.py @@ -5,8 +5,8 @@ import argparse from copy import deepcopy -from ... import buddy_resources as br -from ... import DatabaseBuddy as Db +import buddy_resources as br +import DatabaseBuddy as Db def fmt(prog): @@ -648,7 +648,7 @@ def test_liveshell_do_save(monkeypatch, capsys): assert "Live session saved\n\n" in out assert os.path.isfile("%s/save_dir/save_file1.db" % tmp_dir.path) with open("%s/save_dir/save_file1.db" % tmp_dir.path, "rb") as ifile: - assert len(ifile.read()) == 290 + assert len(ifile.read()) in [279, 281] # Different versions of python give different file sizes # File exists, abort monkeypatch.setattr(br, "ask", lambda _, **kwargs: False) diff --git a/buddysuite/tests/test_fixtures.py b/buddysuite/tests/test_fixtures.py index bd8bbee..4740fce 100644 --- a/buddysuite/tests/test_fixtures.py +++ b/buddysuite/tests/test_fixtures.py @@ -5,21 +5,27 @@ """ import os import pytest -from buddysuite import SeqBuddy, AlignBuddy, PhyloBuddy, DatabaseBuddy +from .__init__ import Sb, Alb, Db, Pb +''' +from .. import SeqBuddy +from .. import AlignBuddy +from .. import PhyloBuddy +from .. import DatabaseBuddy +''' # ################################# - Helper functions - ################################## # def test_hf_buddy2hash(hf): - seqbuddy = SeqBuddy.SeqBuddy(">Foo\nATGCGCGCATGCTA") + seqbuddy = Sb.SeqBuddy(">Foo\nATGCGCGCATGCTA") assert hf.buddy2hash(seqbuddy) == "0b7482f09f950144574337d95376d644" - alignbuddy = AlignBuddy.AlignBuddy(">Foo1\nATGCGCGCATGCTA>Foo2\nATGGGCGAATTTTA") + alignbuddy = Alb.AlignBuddy(">Foo1\nATGCGCGCATGCTA>Foo2\nATGGGCGAATTTTA") assert hf.buddy2hash(alignbuddy) == "71b794705f9817006cd053bd20fb1481" - phylobuddy = PhyloBuddy.PhyloBuddy("(((A, B), C),(D, E));") + phylobuddy = Pb.PhyloBuddy("(((A, B), C),(D, E));") assert hf.buddy2hash(phylobuddy) == "20f81e9f99c673e6cde3afb4b30cc6da" - dbbuddy = DatabaseBuddy.DbBuddy("Casp9,Inx1") + dbbuddy = Db.DbBuddy("Casp9,Inx1") assert hf.buddy2hash(dbbuddy) == "058055f7f09d0e8bcf8ae75d3ed73a1f" with pytest.raises(AttributeError) as err: @@ -44,7 +50,7 @@ def test_sb_resources_init(hf, sb_resources, capsys): assert molecule[0] in sb_resources.sb_objs assert sb_resources.resources['dna']["clustal"] == "%sMnemiopsis_cds.clus" % hf.resource_path - assert type(sb_resources.sb_objs['dna']["clustal"]) == SeqBuddy.SeqBuddy + assert type(sb_resources.sb_objs['dna']["clustal"]) == Sb.SeqBuddy for key in ["molecule", "format"]: assert key in sb_resources.code_dict @@ -95,8 +101,8 @@ def test_sb_resources_get_key(sb_resources): def test_sb_resources_get(sb_resources): objs = sb_resources.get("d p pss") assert len(objs) == 2 - assert type(objs["d pss"]) == SeqBuddy.SeqBuddy - assert type(objs["p pss"]) == SeqBuddy.SeqBuddy + assert type(objs["d pss"]) == Sb.SeqBuddy + assert type(objs["p pss"]) == Sb.SeqBuddy objs = sb_resources.get("d p pss", mode="paths") assert len(objs) == 2 @@ -117,8 +123,8 @@ def test_sb_resources_get(sb_resources): def test_sb_resources_get_list(sb_resources): objs = sb_resources.get_list("d p pss") assert len(objs) == 2 - assert type(objs[0]) == SeqBuddy.SeqBuddy - assert type(objs[1]) == SeqBuddy.SeqBuddy + assert type(objs[0]) == Sb.SeqBuddy + assert type(objs[1]) == Sb.SeqBuddy objs = sb_resources.get_list("d p pss", mode="paths") assert len(objs) == 2 @@ -127,7 +133,7 @@ def test_sb_resources_get_list(sb_resources): objs = sb_resources.get_list("d z pss", mode="objs") assert len(objs) == 1 - assert type(objs[0]) == SeqBuddy.SeqBuddy + assert type(objs[0]) == Sb.SeqBuddy with pytest.raises(ValueError) as err: sb_resources.get_list('d p pss', mode="foo") @@ -139,7 +145,7 @@ def test_sb_resources_get_one(sb_resources): assert not sb_resources.get_one("z pss") objs = sb_resources.get_one("d pss") - assert type(objs) == SeqBuddy.SeqBuddy + assert type(objs) == Sb.SeqBuddy objs = sb_resources.get_one("p pss", mode="paths") assert "Mnemiopsis_pep.physs" in objs @@ -168,7 +174,7 @@ def test_alb_resources_init(hf, alb_resources): assert len(alb_resources.resources[files[0]][files[1]]) == files[2] assert alb_resources.resources['dna']['single']['clustal'] == "%sMnemiopsis_cds.clus" % hf.resource_path - assert type(alb_resources.alb_objs['dna']['single']['clustal']) == AlignBuddy.AlignBuddy + assert type(alb_resources.alb_objs['dna']['single']['clustal']) == Alb.AlignBuddy for key in ["molecule", "format", "num_aligns"]: assert key in alb_resources.code_dict @@ -232,10 +238,10 @@ def test_alb_resources_get_key(alb_resources): def test_alb_resources_get(alb_resources): objs = alb_resources.get("d p pss") assert len(objs) == 4 - assert type(objs["o d pss"]) == AlignBuddy.AlignBuddy - assert type(objs["o p pss"]) == AlignBuddy.AlignBuddy - assert type(objs["m d pss"]) == AlignBuddy.AlignBuddy - assert type(objs["m p pss"]) == AlignBuddy.AlignBuddy + assert type(objs["o d pss"]) == Alb.AlignBuddy + assert type(objs["o p pss"]) == Alb.AlignBuddy + assert type(objs["m d pss"]) == Alb.AlignBuddy + assert type(objs["m p pss"]) == Alb.AlignBuddy objs = alb_resources.get("d p o pss", mode="paths") assert len(objs) == 2 @@ -257,8 +263,8 @@ def test_alb_resources_get(alb_resources): def test_alb_resources_get_list(alb_resources): objs = alb_resources.get_list("d p pss") assert len(objs) == 4 - assert type(objs[0]) == AlignBuddy.AlignBuddy - assert type(objs[2]) == AlignBuddy.AlignBuddy + assert type(objs[0]) == Alb.AlignBuddy + assert type(objs[2]) == Alb.AlignBuddy objs = alb_resources.get_list("d p o pss", mode="paths") assert len(objs) == 2 @@ -267,8 +273,8 @@ def test_alb_resources_get_list(alb_resources): objs = alb_resources.get_list("d z pss", mode="objs") assert len(objs) == 2 - assert type(objs[0]) == AlignBuddy.AlignBuddy - assert type(objs[1]) == AlignBuddy.AlignBuddy + assert type(objs[0]) == Alb.AlignBuddy + assert type(objs[1]) == Alb.AlignBuddy with pytest.raises(ValueError) as err: alb_resources.get_list('d p pss', mode="foo") @@ -280,7 +286,7 @@ def test_alb_resources_get_one(alb_resources): assert not alb_resources.get_one("d z pss") objs = alb_resources.get_one("d m pss") - assert type(objs) == AlignBuddy.AlignBuddy + assert type(objs) == Alb.AlignBuddy objs = alb_resources.get_one("p m pss", mode="paths") assert "Alignments_pep.physs" in objs @@ -322,7 +328,7 @@ def test_pb_resources_init(hf, pb_resources): assert len(pb_resources.resources['multi']) == 3 assert pb_resources.resources['single']['newick'] == "%ssingle_tree.newick" % hf.resource_path - assert type(pb_resources.pb_objs['single']['newick']) == PhyloBuddy.PhyloBuddy + assert type(pb_resources.pb_objs['single']['newick']) == Pb.PhyloBuddy for key in ["format", "num_trees"]: assert key in pb_resources.code_dict @@ -374,10 +380,10 @@ def test_pb_resources_get_key(pb_resources): def test_pb_resources_get(pb_resources): objs = pb_resources.get("o m k l") assert len(objs) == 4 - assert type(objs["o l"]) == PhyloBuddy.PhyloBuddy - assert type(objs["o k"]) == PhyloBuddy.PhyloBuddy - assert type(objs["m l"]) == PhyloBuddy.PhyloBuddy - assert type(objs["m k"]) == PhyloBuddy.PhyloBuddy + assert type(objs["o l"]) == Pb.PhyloBuddy + assert type(objs["o k"]) == Pb.PhyloBuddy + assert type(objs["m l"]) == Pb.PhyloBuddy + assert type(objs["m k"]) == Pb.PhyloBuddy objs = pb_resources.get("o", mode="paths") assert len(objs) == 3 @@ -399,8 +405,8 @@ def test_pb_resources_get(pb_resources): def test_pb_resources_get_list(pb_resources): objs = pb_resources.get_list("m") assert len(objs) == 3 - assert type(objs[0]) == PhyloBuddy.PhyloBuddy - assert type(objs[2]) == PhyloBuddy.PhyloBuddy + assert type(objs[0]) == Pb.PhyloBuddy + assert type(objs[2]) == Pb.PhyloBuddy objs = pb_resources.get_list("m o l", mode="paths") assert len(objs) == 2 @@ -409,7 +415,7 @@ def test_pb_resources_get_list(pb_resources): objs = pb_resources.get_list("o z k", mode="objs") assert len(objs) == 1 - assert type(objs[0]) == PhyloBuddy.PhyloBuddy + assert type(objs[0]) == Pb.PhyloBuddy with pytest.raises(ValueError) as err: pb_resources.get_list('o k', mode="foo") @@ -420,7 +426,7 @@ def test_pb_resources_get_one(pb_resources): assert not pb_resources.get_one("k z") objs = pb_resources.get_one("m k") - assert type(objs) == PhyloBuddy.PhyloBuddy + assert type(objs) == Pb.PhyloBuddy objs = pb_resources.get_one("m k", mode="paths") assert "multi_tree.newick" in objs diff --git a/buddysuite/tests/test_phylobuddy/test_pb_3rd_party.py b/buddysuite/tests/test_phylobuddy/test_pb_3rd_party.py index c53aced..05c2464 100755 --- a/buddysuite/tests/test_phylobuddy/test_pb_3rd_party.py +++ b/buddysuite/tests/test_phylobuddy/test_pb_3rd_party.py @@ -11,9 +11,9 @@ from subprocess import Popen, PIPE import re -from ... import AlignBuddy as Alb -from ... import PhyloBuddy as Pb -from ... import buddy_resources as br +import AlignBuddy as Alb +import PhyloBuddy as Pb +import buddy_resources as br # ###################### 'gt', '--generate_trees' ###################### # diff --git a/buddysuite/tests/test_phylobuddy/test_pb_api.py b/buddysuite/tests/test_phylobuddy/test_pb_api.py index 597f820..3c4f069 100644 --- a/buddysuite/tests/test_phylobuddy/test_pb_api.py +++ b/buddysuite/tests/test_phylobuddy/test_pb_api.py @@ -3,8 +3,8 @@ """ Tests PhyloBuddy API functions """ import pytest -from ... import PhyloBuddy as Pb -from ... import buddy_resources as br +import PhyloBuddy as Pb +import buddy_resources as br from .. import __init__ from unittest import mock diff --git a/buddysuite/tests/test_phylobuddy/test_pb_class_and_helpers.py b/buddysuite/tests/test_phylobuddy/test_pb_class_and_helpers.py index b151aa6..c9203ed 100644 --- a/buddysuite/tests/test_phylobuddy/test_pb_class_and_helpers.py +++ b/buddysuite/tests/test_phylobuddy/test_pb_class_and_helpers.py @@ -4,8 +4,8 @@ import pytest import os -from ...PhyloBuddy import PhyloBuddy, _convert_to_ete, _guess_format -from ... import buddy_resources as br +from PhyloBuddy import PhyloBuddy, _convert_to_ete, _guess_format +import buddy_resources as br import ete3 diff --git a/buddysuite/tests/test_phylobuddy/test_pb_ui.py b/buddysuite/tests/test_phylobuddy/test_pb_ui.py index 2a4f5fd..637cae8 100644 --- a/buddysuite/tests/test_phylobuddy/test_pb_ui.py +++ b/buddysuite/tests/test_phylobuddy/test_pb_ui.py @@ -31,8 +31,8 @@ import ete3 import shutil -from ... import buddy_resources as br -from ... import PhyloBuddy as Pb +import buddy_resources as br +import PhyloBuddy as Pb VERSION = Pb.VERSION WRITE_FILE = br.TempFile() diff --git a/buddysuite/tests/test_seqbuddy/test_sb_3rd_party.py b/buddysuite/tests/test_seqbuddy/test_sb_3rd_party.py index 4f4576a..38cedc9 100644 --- a/buddysuite/tests/test_seqbuddy/test_sb_3rd_party.py +++ b/buddysuite/tests/test_seqbuddy/test_sb_3rd_party.py @@ -9,8 +9,8 @@ from unittest import mock from subprocess import Popen, PIPE import re -from ... import SeqBuddy as Sb -from ... import buddy_resources as br +import SeqBuddy as Sb +import buddy_resources as br blast_version = Popen("blastn -version", shell=True, stdout=PIPE).communicate()[0].decode() blast_version = re.search("[0-9]+\.[0-9]+\.[0-9]+", blast_version).group(0) @@ -31,32 +31,27 @@ def test_bl2seq(sb_resources, hf): # ###################### '-bl', '--blast' ###################### # -def test_blastn(sb_resources, sb_odd_resources, hf): +def test_blastn(sb_resources, sb_odd_resources, hf, monkeypatch): tester = Sb.pull_recs(sb_resources.get_one("d f"), '8', True) tester = Sb.blast(tester, sb_odd_resources["blastn"]) assert hf.buddy2hash(tester) == "95c417b6c2846d1b7a1a07f50c62ff8a" + tester = Sb.SeqBuddy(">Seq1\nATGCGCGCTACGCTAGCTAGCTAGCTCGCATGCAT") + tester = Sb.blast(tester, sb_odd_resources["blastn"]) + assert len(tester.records) == 0 + with pytest.raises(RuntimeError) as e: tester = sb_resources.get_one("d f") Sb.blast(tester, "Mnemiopsis_cds.nhr") assert "The .nhr file of your blast database was not found" in str(e.value) - try: - with mock.patch("buddysuite.SeqBuddy._check_for_blast_bin", return_value=False): - with pytest.raises(SystemError) as e: - Sb.blast(tester, sb_odd_resources["blastn"]) - assert 'blastn not found in system path' in str(e.value) - except ImportError: - with mock.patch("SeqBuddy._check_for_blast_bin", return_value=False): - with pytest.raises(SystemError) as e: - Sb.blast(tester, sb_odd_resources["blastn"]) - assert 'blastn not found in system path' in str(e.value) - tester = Sb.SeqBuddy(">Seq1\nATGCGCGCTACGCTAGCTAGCTAGCTCGCATGCAT") - tester = Sb.blast(tester, sb_odd_resources["blastn"]) - assert len(tester.records) == 0 + monkeypatch.setattr(Sb, "_check_for_blast_bin", lambda *_: False) + with pytest.raises(SystemError) as e: + Sb.blast(tester, sb_odd_resources["blastn"]) + assert 'blastn not found in system path' in str(e.value) -def test_blastp(sb_resources, sb_odd_resources, hf): +def test_blastp(sb_resources, sb_odd_resources, hf, monkeypatch): seqbuddy = Sb.pull_recs(sb_resources.get_one('p f'), '8', True) tester = Sb.blast(seqbuddy, sb_odd_resources["blastp"]) assert hf.buddy2hash(tester) in ["4237c79672c1cf1d4a9bdb160a53a4b9", "118d4f412e2a362b9d16130abbf395c5"] @@ -66,16 +61,10 @@ def test_blastp(sb_resources, sb_odd_resources, hf): Sb.blast(tester, "Mnemiopsis_pep.phr") assert "The .phr file of your blast database was not found" in str(e.value) - try: - with mock.patch("buddysuite.SeqBuddy._check_for_blast_bin", return_value=False): - with pytest.raises(SystemError) as e: - Sb.blast(tester, sb_odd_resources["blastp"]) - assert 'blastp not found in system path' in str(e.value) - except ImportError: - with mock.patch("SeqBuddy._check_for_blast_bin", return_value=False): - with pytest.raises(SystemError) as e: - Sb.blast(tester, sb_odd_resources["blastp"]) - assert 'blastp not found in system path' in str(e.value) + monkeypatch.setattr(Sb, "_check_for_blast_bin", lambda *_: False) + with pytest.raises(SystemError) as e: + Sb.blast(tester, sb_odd_resources["blastp"]) + assert 'blastp not found in system path' in str(e.value) def test_makeblastdb(monkeypatch, sb_resources, hf): diff --git a/buddysuite/tests/test_seqbuddy/test_sb_api.py b/buddysuite/tests/test_seqbuddy/test_sb_api.py index 5f8ef14..1ce25a9 100644 --- a/buddysuite/tests/test_seqbuddy/test_sb_api.py +++ b/buddysuite/tests/test_seqbuddy/test_sb_api.py @@ -13,8 +13,8 @@ import time from collections import OrderedDict -from ... import SeqBuddy as Sb -from ... import buddy_resources as br +import SeqBuddy as Sb +import buddy_resources as br TEMPDIR = br.TempDir() @@ -379,8 +379,8 @@ def test_count_residues_prot(sb_resources): assert res_count["% Positive"] == 12.23 assert res_count["% Negative"] == 12.71 assert res_count["% Uncharged"] == 73.62 - assert res_count["% Hyrdophilic"] == 36.93 - assert res_count["% Hyrdophobic"] == 55.4 + assert res_count["% Hydrophilic"] == 36.93 + assert res_count["% Hydrophobic"] == 55.4 # ###################### '-dgn' '--degenerate_sequence'################### # diff --git a/buddysuite/tests/test_seqbuddy/test_sb_class_and_helpers.py b/buddysuite/tests/test_seqbuddy/test_sb_class_and_helpers.py index 2dcda2f..ddca695 100644 --- a/buddysuite/tests/test_seqbuddy/test_sb_class_and_helpers.py +++ b/buddysuite/tests/test_seqbuddy/test_sb_class_and_helpers.py @@ -5,8 +5,8 @@ from Bio.Alphabet import IUPAC from collections import OrderedDict import os -from ... import buddy_resources as br -from ... import SeqBuddy as Sb +import buddy_resources as br +import SeqBuddy as Sb def test_instantiate_seqbuddy_from_file(sb_resources): diff --git a/buddysuite/tests/test_seqbuddy/test_sb_ui.py b/buddysuite/tests/test_seqbuddy/test_sb_ui.py index 8206ac6..ccec8e3 100644 --- a/buddysuite/tests/test_seqbuddy/test_sb_ui.py +++ b/buddysuite/tests/test_seqbuddy/test_sb_ui.py @@ -34,8 +34,8 @@ import sys from collections import OrderedDict -from ... import SeqBuddy as Sb -from ... import buddy_resources as br +import SeqBuddy as Sb +import buddy_resources as br TEMP_DIR = br.TempDir() VERSION = Sb.VERSION @@ -324,7 +324,7 @@ def test_count_residues_ui(capsys, sb_resources, hf): test_in_args.count_residues = ["conc"] Sb.command_line_ui(test_in_args, sb_resources.get_one('p f'), True) out, err = capsys.readouterr() - assert hf.string2hash(out) == "c7062408f939f4b310f2f97c3e94eb37" + assert hf.string2hash(out) == "051d98f6e2ba6159f5f282562d3627b4" # ###################### '-dgn' '--degenerate_sequence'################### # diff --git a/manuscript/bmc_article.tex b/manuscript/bmc_article.tex index 6cb150b..4b067bc 100644 --- a/manuscript/bmc_article.tex +++ b/manuscript/bmc_article.tex @@ -179,17 +179,15 @@ \begin{abstract} % abstract \parttitle{Background} % Required -BuddySuite is a collection of four independent yet interrelated command-line programs that facilitate each step in the workflow of sequence discovery, curation, alignment, and phylogenetic reconstruction. Common sequence, alignment, and tree file formats are automatically detected and parsed, and nearly 100 routine tasks have been combined into this comprehensive suite of toolkits. +The ability to manipulate sequence, alignment, and phylogenetic tree files has become an increasingly important skill in the life sciences, whether it be to generate summary information about those files or to prepare them for further downstream analysis. The command-line is generally the most powerful environment for interacting with these resources, especially as the files become even moderately large, but there has been little focus on developing or maintaining general purpose toolkits in recent years. \parttitle{Results} % Required -The project has been implemented in Python 3 for use on UNIX-based systems. Installation is performed using a dedicated graphical installer or by cloning the development Git repository. All source code is freely available. +BuddySuite is a collection of four independent yet interrelated command-line programs that facilitate each step in the workflow of sequence discovery, curation, alignment, and phylogenetic reconstruction. Common sequence, alignment, and tree file formats are automatically detected and parsed, and over 100 routine tasks have been implemented in this comprehensive suite of toolkits. The project has been engineered to easily accommodate the addition of new tools, written in the popular programming language Python, and hosted on the Python Package Index and GitHub to maximize accessibility. Documentation for each BuddySuite tool, including usage examples, is available at http://tiny.cc/buddysuite\_wiki. \parttitle{Conclusions} % Required +All software is open source and freely available without restriction. http://research.nhgri.nih.gov/software/BuddySuite -\parttitle{Supplementary} -Documentation for each BuddySuite tool is available at http://tiny.cc/buddysuite\_wiki - \end{abstract} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -201,9 +199,13 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \begin{keyword} % Three to ten keywords -\kwd{sample} -\kwd{article} -\kwd{author} +\kwd{software} +\kwd{command line} +\kwd{sequence} +\kwd{alignment} +\kwd{phylogenetic tree} +\kwd{python} +\kwd{toolkits} \end{keyword} % MSC classifications codes, if any @@ -215,7 +217,7 @@ \end{abstractbox} % -\end{fmbox}% uncomment this for twcolumn layout +\end{fmbox} % uncomment this for twcolumn layout \end{frontmatter} @@ -246,36 +248,37 @@ %% Background %% %% \section*{Background} -Manipulation of biological sequence data is now a routine task within the life sciences, not just by bioinformaticians, but also by `bench biologists' who are becoming increasingly savvy in applying computational methods to their own work. While there are excellent graphical platforms for organizing, visualizing, and manipulating these forms of data, it is often advantageous to interact with text files directly from the command line, especially when the size of datasets become even moderately large. Most common tasks can be accomplished with existing open source software, but it is usually necessary to bring together many different standalone tools to build a particular workflow. Such tools may be dependent on pre-defined file format specifications, have non-trivial installation requirements, and/or be difficult to extend or modify. While each of these issues is surmountable, particularly if one can write custom programs in any of the popular scripting languages (e.g., Perl, R, or Python), they do impose an entry barrier to those without a basic background in computer science. Furthermore, finding available tools can be non-trivial, as specialized programs are not generally well advertised or highly ranked by search engines. To address these issues we have developed BuddySuite, a unified set of command-line data manipulation tools that are easy to install, intuitively organized, and implemented in the popular programming language Python. The target audience for this software is those with a basic working knowledge of the standard POSIX shell (e.g., command-line terminals in Linux or Mac OS X) who routinely interact with sequence, alignment, or phylogenetic tree files. +Manipulation of biological sequence data is now a routine task within the life sciences, not just by bioinformaticians, but also by `bench biologists' who are becoming increasingly savvy in applying computational methods to their own work. While there are excellent graphical platforms for organizing, visualizing, and manipulating these forms of data, it is often advantageous to interact with text files directly from the command line, especially when the size of datasets become even moderately large. Most common tasks can be accomplished with existing open source software, but it is usually necessary to bring together many different standalone tools to build a particular workflow. Such tools may be dependent on pre-defined file format specifications, have non-trivial installation requirements, and/or be difficult to extend or modify. While each of these issues is surmountable, particularly if one can write custom programs in any of the popular scripting languages (e.g., bash, Perl, R, or Python), they do impose an entry barrier to those without a basic background in computer science. Furthermore, finding available tools can be non-trivial, as specialized programs are not generally well advertised or highly ranked by search engines. To address these issues we have developed BuddySuite, a unified set of command-line data manipulation tools that are easy to install, intuitively organized, and implemented in the popular programming language Python. The target audience for this software is those with a basic working knowledge of the standard POSIX shell (e.g., command-line terminals in Linux or Mac OS X) who routinely interact with sequence, alignment, or phylogenetic tree files. % \cite{koon,oreg,khar,zvai,xjon,schn,pond,smith,marg,hunn,advi,koha,mouse} \section*{Implementation} -ADD: Table of tools - -ADD: Installation via BioConda and PyPI +BuddySuite is a Python 3.5+ library developed for use on all major operating systems (Windows Vista+, Mac OSX, and Linux), providing and API and command-line tools for manipulating and analyzing sequence and tree data in a range of standardized file formats. The project is free and open-source, hosted on GitHub to facilitate community contribution, and distributed through the Python Package Index for easy installation over the internet using the package manager `pip'. In anticipation of continued development and support of this project, a custom error and usage reporting system has been built in. -ADD: Full list of wrapped software \subsection*{Command line user interface} -BuddySuite is implemented in pure Python and includes four core command line programs: SeqBuddy, AlignBuddy, PhyloBuddy, and DatabaseBuddy. The first three accept sequence, alignment, or phylogenetic tree data as input, respectively, and flags are used to specify which tool to run. DatabaseBuddy, on the other hand, is intended to run primarily as a `live shell', allowing the user to interactively search for and download sequence data stored in public databases (e.g., NCBI, UniProt, and Ensembl). The BuddySuite programs collectively contain 103 individual tools at the time of this writing, each with extended help and usage examples on the BuddySuite wiki (http://tiny.cc/buddysuite\_wiki). +The four command line programs distributed with BuddySuite are SeqBuddy, AlignBuddy, PhyloBuddy, and DatabaseBuddy. The first three accept sequence, alignment, or phylogenetic tree data as input, respectively, using flags to switch among the tools available in each program. DatabaseBuddy, on the other hand, is intended to run primarily as a `live shell', allowing the user to interactively search and download sequence data stored in the NCBI, UniProt, and Ensembl public databases. The BuddySuite programs collectively contain 103 individual tools at the time of this writing, each with extended help and usage examples on the BuddySuite wiki (http://tiny.cc/buddysuite\_wiki). -BuddySuite commands can be `daisy-chained' together with the pipe character ($\vert$) to create more complex workflows as a single line in the terminal. +All output is printed directly to the terminal window by default and each module adheres to the UNIX convention of accepting piped data, allowing individual tools to be `daisy-chained' into more complex workflows. In an effort to minimize the number of keystrokes required to perform any given task; \subsection*{Application programing interface (API)} +Each command-line tool available in the BuddySuite has an associated function that can be accessed by third party Python programs. Each that can be accessed directly from the command line or as an application programming interface (API) For those interested in integrating BuddySuite functions into their own Python 3 scripts, the process is simplified by base classes in each module that handle many forms of input (including plain text, file paths or handles, and BioPython objects), then automate format detection and pre-processing. An object invoked from one of these base classes is the first parameter of all BuddySuite functions and is also the output in most cases. \subsection*{File format parsing} -File format detection is automated, and most of the formats with BioPython parsers are supported \cite{Cock:2009hj}. +File format detection is automated, and most of the formats with BioPython parsers are supported \cite{Cock:2009hj}. Another key feature of BuddySuite is robust sequence annotation management. Flat file formats such as GenBank and EMBL allow for rich annotation of sequence features, and these will be retained and/or adjusted by SeqBuddy and AlignBuddy tools as necessary. As an example, the AlignBuddy `generate\_alignment' tool can be used to invoke popular third party alignment programs such as MAFFT \cite{Katoh:2013hm} on an annotated GenBank file; after completion, the new alignment will be returned in GenBank format with all original features re-mapped to account for newly introduced gaps. \subsection*{Installation} -Users of BuddySuite have several options for installing and updating the software. Stable release versions are available from the Python Package Index \cite{pypi} (http://tiny.cc/buddysuite\_pypi) and BioConda \cite{bioconda} (http://tiny.cc/buddysuite\_bioconda), allowing for automated installation with the programs `pip' or `conda', respectively. The project is also hosted on a public GitHub \cite{github} repository (http://tiny.cc/buddysuite\_github) with an active development branch for continuous integration, allowing immediate access to all new features as they are built. Unit tests have been written to cover \textgreater 95\% of the codebase and continuous integration is monitored with Travis CI \cite{travisci} (http://tiny.cc/buddysuite\_travisci). +Users of BuddySuite have several options for installing and updating the software. Stable release versions are available from the Python Package Index \cite{pypi} (http://tiny.cc/buddysuite\_pypi), allowing for automated installation with the program `pip'. The project is also hosted on a public GitHub \cite{github} repository (http://tiny.cc/buddysuite\_github) with an active development branch for continuous integration, allowing immediate access to all new features as they are built. Unit tests have been written to cover \textgreater 95\% of the codebase and continuous integration is monitored with Travis CI \cite{travisci} (http://tiny.cc/buddysuite\_travisci). \subsection*{Dependencies} Python standard library packages have been used where possible to minimize licensing and version incompatibility issues, although the suite does depend heavily on BioPython \cite{Cock:2009hj}. Furthermore, PhyloBuddy uses DendroPy \cite{Sukumaran:2010id}. for much of its tree manipulation functionality and the ETE toolkit \cite{HuertaCepas:2010fd} to graphically display trees. A number of optional programs are also used by individual functions within the BuddySuite, such as BLAST \cite{Camacho2009}, MAFFT \cite{Katoh:2013hm}, and RAxML \cite{Stamatakis:2006de}. These programs are not distributed with BuddySuite, so the user is responsible for their installation if they wish to use the functions that rely on them. +ADD: Full list of wrapped software + + \subsection*{Error/usage reporting and contribution} Looking forward, the modular nature of BuddySuite makes it particularly well suited for continued growth. New tools are easily added to each existing module and new modules may be added to the suite. Instead of relying exclusively on active community input to identify bugs and drive future development, we have implemented an optional passive data collection program to monitor usage and crash reporting. Personally identifiable information is stripped before any data is transmitted to our FTP server, and a randomly generated identifier is assigned to new systems when BuddySuite is installed to estimate attrition rates. @@ -291,7 +294,7 @@ \subsection*{Performance} \subsection*{Similar bioinformatics toolkits} Describe the state of EMBOSS and BioPieces -To keep the learning curve as shallow as possible, care has been taken to minimize the dependence of each tool on additional parameters and to infer user intent where possible. For example, the SeqBuddy 'find\_restriction\_sites' function is one of the most flexible in the Suite. It can accept three different types of arguments that control which enzymes are included in the search and how the output is formatted; all of these arguments are optional and they can be passed to the tool in any order. This flexibility is in contrast to the paradigm implemented in EMBOSS and BioPieces, which require extra flags to explicitly set all parameters. In cases such as this, where the argument type (e.g., integer or string) unambiguously identifies how it should be used by the tool, it is counter-productive to require that the user remember additional flags to explicitly mark each input. +To keep the learning curve as shallow as possible, care has been taken to minimize the dependence of each tool on additional parameters and to infer user intent where possible. For example, the SeqBuddy 'find\_restriction\_sites' function is one of the most flexible in the Suite. It can accept three different types of arguments that control which enzymes are included in the search and how the output is formatted; all of these arguments are optional and they can be passed to the tool in any order. This flexibility is in contrast to the paradigm implemented in EMBOSS and BioPieces, which require extra flags to explicitly set all parameters. In cases such as this, where the argument type (e.g., integer or string) unambiguously identifies how it should be used by the tool, it is counter-productive to require that the user remember additional flags to explicitly mark each input. \section*{Conclusions} @@ -318,10 +321,12 @@ \section*{Competing interests} The authors declare that they have no competing interests. \section*{Author's contributions} - Text for this section \ldots + SRB is the lead developer of BuddySuite and wrote the manuscript, KEK contributed significantly to the code base, SNB generated the performance statistics for FIG.X, ADB participated in the design coordination of the project. All authors read and approved the final manuscript. \section*{Acknowledgements} - Text for this section \ldots + We would like to thank the community members who contributed code to this project, big or small. It takes a village. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% The Bibliography %% %% %% diff --git a/setup.py b/setup.py index e1c33f4..4606124 100755 --- a/setup.py +++ b/setup.py @@ -75,11 +75,12 @@ def run(): } setup(name='buddysuite', - version='1.2b.6', + version='1.2b.7', description='BuddySuite is a collection of command line utilities written in Python for ' 'working with biological data.', long_description=open(os.path.join(os.path.dirname(__file__), 'README.rst'), encoding="utf-8").read(), author='Stephen Bond', + maintainer='Stephen Bond', author_email='steve.bond@gmail.com', url='https://github.com/biologyguy/BuddySuite', packages=PACKAGES,