Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes issue 232: Pyani throws an error when it fails to find 3rd-party tool versions (but they are installed) #276

Merged
merged 87 commits into from
Sep 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
5e0bf45
Added message about failure to retrieve a third-party tool's version
baileythegreen Jun 6, 2021
cf26886
Switch commandline for obtaining `blastall`'s version based on OS
baileythegreen Jun 6, 2021
cd3997d
add string returns to get_<PROGRAM>_version
widdowquinn Jun 16, 2021
94c71d8
add logger warning when blastall executable can't be run
widdowquinn Jun 16, 2021
57e5097
extend docstrings for third-party tool get_version()
widdowquinn Jun 16, 2021
ac99fdc
Add tests for new get_version()` checks in `anib`
baileythegreen Jul 5, 2021
2efa874
Move `test_get_version` to use `pytest.fixture` and `monkeypatch`
baileythegreen Jul 11, 2021
4fe26da
Add `test_get_version` to `tests/test_anim.py`
baileythegreen Jul 11, 2021
126b1ba
Create `tests/test_aniblastall.py` and add `test_get_version`
baileythegreen Jul 11, 2021
bfd5b97
modify test layout for get_version
widdowquinn Sep 3, 2021
e5711a8
add header info to test_aniblastall.py
widdowquinn Sep 3, 2021
165044a
modify get_version() tests in test_anim.py
widdowquinn Sep 3, 2021
cb54865
Add support for symlinks
baileythegreen Jul 5, 2021
d822626
simplify handling of symlinks while indexing genome files
widdowquinn Jul 6, 2021
765e455
Update __init__.py
baileythegreen Apr 21, 2021
edf90ac
Update pyani_script.py
baileythegreen Apr 21, 2021
97f8f66
Update __init__.py
baileythegreen Apr 21, 2021
2805769
Update __init__.py
baileythegreen Apr 21, 2021
e9216ca
anim --version added
baileythegreen Apr 21, 2021
bf77ed7
--version attempts
baileythegreen Apr 21, 2021
9c98c03
Implement subcommand parsers
baileythegreen Apr 21, 2021
3f06ee6
Removed commented lines
baileythegreen Apr 21, 2021
33a5b5a
Changed format of output
baileythegreen May 13, 2021
837d6b1
Fixed handling of global
baileythegreen May 13, 2021
081523c
Alter handling of requests
baileythegreen May 13, 2021
cba054a
limit search for version info to anim, anib, aniblastall
baileythegreen May 13, 2021
7cc5d49
Changed action taken with `--version` option to "store"
baileythegreen Jun 6, 2021
d26b487
Removed `--version` and `--citation` options from `common_parser.py`
baileythegreen Jun 6, 2021
961a38b
Removed code that enabled subcmd versions of `--version` & `--citation`
baileythegreen Jun 6, 2021
9977a68
Removed unnecessary imports I had previously added
baileythegreen Jun 6, 2021
f278e5b
Remove unnecessary `import sys`
baileythegreen Jun 6, 2021
c7687e0
Removed unnecessary import of `anim`
baileythegreen Jun 6, 2021
550743d
Removed unnecessary import of `anib`
baileythegreen Jun 6, 2021
c651678
Removed unnecessary import of `__version__`
baileythegreen Jun 6, 2021
46b9869
Added dummy `--version` and `--citation` options to `common_parser.py`
baileythegreen Jun 6, 2021
ad8c6b1
Commented out a code checking to see if `--citation` was requested
baileythegreen Jun 6, 2021
560a526
Altered `$ pyani` behaviour to mimic `$ pyani -h`, not `$ pyani --ver…
baileythegreen Jun 6, 2021
083b154
add version to common log header
widdowquinn Jun 16, 2021
a5a35e4
remove commented code that wasn't visited
widdowquinn Jun 16, 2021
c9cea49
Changed link to Bailey's staff page
baileythegreen Jun 8, 2021
558eefb
Added an option for documentation updates to PR template
baileythegreen Jun 6, 2021
cb280c0
update CircleCI config (add py39, update images)
Jun 12, 2021
1e2995d
Added note relating to use of `pyani plot` with large numbers of genomes
baileythegreen Jun 6, 2021
dfa48e4
make minor clarification/style changes to text
Jun 14, 2021
5f7d177
Notify user when ftp download fails because the file has been replaced
baileythegreen Jun 6, 2021
c7a7e4f
Changed `logger` method call to `warning`
baileythegreen Jun 16, 2021
2b36ecd
Changed naming scheme for `.md5` files to retain existing extension
baileythegreen Jun 6, 2021
cc48c59
Changed search space for hashfiles to `.md5` and `.ext.md5`
baileythegreen Jun 6, 2021
eabebe2
Renamed target files for `subcmd_index` tests for retaining original …
baileythegreen Jun 6, 2021
eac440c
Remove target files for `subcmd_index` tests without original extension
baileythegreen Jun 6, 2021
c7e9ec4
Added comments explaining which hashfiles will be sought
baileythegreen Jun 6, 2021
8eec88e
Remove `sys` import and calls
baileythegreen Jun 8, 2021
693a62d
Add logging to pyani_files.py and log warnings for missing `.md5`s
baileythegreen Jun 8, 2021
cfdbbcf
Add test for a missing hash file
baileythegreen Jun 8, 2021
c9254f2
Add a test input for which there is no corresponding hash file
baileythegreen Jun 8, 2021
9530869
Rename test input file
baileythegreen Jun 11, 2021
b7a7b09
Modified logging when checking for hashfiles
baileythegreen Jun 11, 2021
5d12690
Rename test input file
baileythegreen Jun 11, 2021
2e216ed
Changed name of (extension) test input file
baileythegreen Jun 12, 2021
69b0d12
Removed old test input file
baileythegreen Jun 12, 2021
0a3bee4
fix #287 - allows empty sys.argv if arguments passed
widdowquinn Sep 3, 2021
07c4f7e
docs: update README.md [skip ci]
allcontributors[bot] Jun 19, 2021
0db386b
docs: update .all-contributorsrc [skip ci]
allcontributors[bot] Jun 19, 2021
48e1000
Fixed explanation of `-h` option
baileythegreen Jun 16, 2021
51b6ef0
Add mention of Discussions tab to ReadTheDocs
baileythegreen Jun 16, 2021
c9efdb1
fix typo
widdowquinn Jun 17, 2021
d12c02b
fix single -> double backticks
widdowquinn Jun 17, 2021
702721e
add six citations for June 2021
widdowquinn Jun 17, 2021
663b9f6
Fix anim recover; more debugging
peterjc Aug 6, 2020
5483808
Fix recovery mode loading of pre-existing delta files
peterjc Aug 17, 2020
7da91fb
Nested folders for anim: output_dir/nucmer_output/A/A_vs_B.*
peterjc Aug 17, 2020
226733d
Logging for missing labels and classes
peterjc Aug 19, 2020
13956fc
Allow reference genome FASTA files to be symlinks
peterjc Aug 28, 2020
063affc
fix deltafile parsing error
Jun 19, 2021
f15cbb0
fix subdirectory location in anim.py
Jun 19, 2021
2e6dd21
Add notes on SGE usage to `README.md`
baileythegreen Jun 6, 2021
53913ce
Add scheduler documentation to ReadTheDocs
baileythegreen Jun 22, 2021
7467a32
Update Scheduler documentation
baileythegreen Jun 29, 2021
0bdaec9
Explicitly mention microbes in `Readme.md`.
baileythegreen Jul 15, 2021
a8ef105
add 25 new citations for August 2021
widdowquinn Aug 17, 2021
045932e
add string returns to get_<PROGRAM>_version
widdowquinn Jun 16, 2021
04a1b11
Add `test_get_version` to `tests/test_anim.py`
baileythegreen Jul 11, 2021
b7341c4
Create `tests/test_aniblastall.py` and add `test_get_version`
baileythegreen Jul 11, 2021
1091dab
catch when comparison tools are not in the $PATH
widdowquinn Sep 3, 2021
c0cda6a
fix tests that were accidentally reverted in rebase
widdowquinn Sep 3, 2021
d998cfa
fix CircleCI apt-get update test breaker
widdowquinn Sep 3, 2021
2931b4f
Merge branch 'master' into issue_232
widdowquinn Sep 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ jobs:
- run:
name: update apt-get
command: |
sudo apt-get update
sudo apt-get update --allow-releaseinfo-change
sudo apt-get upgrade
- run:
name: install third-party tools
command: |
Expand Down
28 changes: 25 additions & 3 deletions pyani/anib.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# (c) The James Hutton Institute 2016-2019
# (c) University of Strathclyde 2019-2020
# (c) University of Strathclyde 2019-2021
# Author: Leighton Pritchard
#
# Contact: leighton.pritchard@strath.ac.uk
Expand All @@ -16,7 +16,7 @@
# The MIT License
#
# Copyright (c) 2016-2019 The James Hutton Institute
# Copyright (c) 2019-2020 University of Strathclyde
# Copyright (c) 2019-2021 University of Strathclyde
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -81,6 +81,7 @@
aligned sequence identity used to calculate ANI.
"""

import os
import platform
import re
import shutil
Expand Down Expand Up @@ -114,7 +115,24 @@ def get_version(blast_exe: Path = pyani_config.BLASTN_DEFAULT) -> str:
Package: blast 2.9.0, build Jun 10 2019 09:40:53

This is concatenated with the OS name.

The following circumstances are explicitly reported as strings

- no executable at passed path
- non-executable file at passed path
- no version info returned
"""
blastn_path = Path(shutil.which(blast_exe)) # type:ignore

if blastn_path is None:
return f"{blast_exe} is not found in $PATH"

if not blastn_path.is_file(): # no executable
return f"No blastn executable at {blastn_path}"

if not os.access(blastn_path, os.X_OK): # file exists but not executable
return f"blastn exists at {blastn_path} but not executable"

cmdline = [blast_exe, "-version"]
result = subprocess.run(
cmdline, # type: ignore
Expand All @@ -126,7 +144,11 @@ def get_version(blast_exe: Path = pyani_config.BLASTN_DEFAULT) -> str:
version = re.search( # type: ignore
r"(?<=blastn:\s)[0-9\.]*\+", str(result.stdout, "utf-8")
).group()
return f"{platform.system()}_{version}"

if 0 == len(version.strip()):
return f"blastn exists at {blastn_path} but could not retrieve version"

return f"{platform.system()}_{version} ({blastn_path})"


# Divide input FASTA sequences into fragments
Expand Down
64 changes: 49 additions & 15 deletions pyani/aniblastall.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# (c) University of Strathclyde 2020
# (c) University of Strathclyde 2021
# Author: Leighton Pritchard
#
# Contact: leighton.pritchard@strath.ac.uk
Expand All @@ -14,7 +14,7 @@
#
# The MIT License
#
# Copyright (c) 2020 University of Strathclyde
# Copyright (c) 2021 University of Strathclyde
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand All @@ -35,11 +35,13 @@
# THE SOFTWARE.
"""Code to implement the ANIblastall average nucleotide identity method."""

import logging
import os
import platform
import re
import shutil
import subprocess


from pathlib import Path

from . import pyani_config
Expand All @@ -59,16 +61,48 @@ def get_version(blast_exe: Path = pyani_config.BLASTALL_DEFAULT) -> str:
one-line descriptions for (V) [ersion] is bad or out of range [? to ?]

This is concatenated with the OS name.

The following circumstances are explicitly reported as strings

- no executable at passed path
- non-executable file at passed path
- no version info returned
- executable cannot be run on this OS
"""
cmdline = [blast_exe, "-version"]
result = subprocess.run(
cmdline, # type: ignore
shell=False,
stdout=subprocess.PIPE, # type: ignore
stderr=subprocess.PIPE,
check=False, # blastall doesn't return 0
)
version = re.search( # type: ignore
r"(?<=blastall\s)[0-9\.]*", str(result.stderr, "utf-8")
).group()
return f"{platform.system()}_{version}"
logger = logging.getLogger(__name__)

blastall_path = Path(shutil.which(blast_exe)) # type:ignore

if blastall_path is None:
return f"{blast_exe} is not found in $PATH"

if not blastall_path.is_file(): # no executable
return f"No blastall at {blastall_path}"

if not os.access(blastall_path, os.X_OK): # file exists but not executable
return f"blastall exists at {blastall_path} but not executable"

if platform.system() == "Darwin":
cmdline = [blast_exe, "-version"]
else:
cmdline = [blast_exe]

try:
result = subprocess.run(
cmdline, # type: ignore
shell=False,
stdout=subprocess.PIPE, # type: ignore
stderr=subprocess.PIPE,
check=False, # blastall doesn't return 0
)
version = re.search( # type: ignore
r"(?<=blastall\s)[0-9\.]*", str(result.stderr, "utf-8")
).group()
except OSError:
logger.warning("blastall executable will not run", exc_info=True)
return f"blastall exists at {blastall_path} but could not be executed"

if 0 == len(version.strip()):
return f"blastall exists at {blastall_path} but could not retrieve version"

return f"{platform.system()}_{version} ({blastall_path})"
29 changes: 26 additions & 3 deletions pyani/anim.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
# (c) The James Hutton Institute 2016-2019
# (c) University of Strathclyde 2019
# (c) University of Strathclyde 2019-2021
# Author: Leighton Pritchard
#
# Contact:
Expand All @@ -17,7 +17,7 @@
# The MIT License
#
# Copyright (c) 2016-2019 The James Hutton Institute
# Copyright (c) 2019 University of Strathclyde
# Copyright (c) 2019-2021 University of Strathclyde
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand Down Expand Up @@ -53,8 +53,10 @@
"""

import logging
import os
import platform
import re
import shutil
import subprocess
import sys

Expand Down Expand Up @@ -101,14 +103,35 @@ def get_version(nucmer_exe: Path = pyani_config.NUCMER_DEFAULT) -> str:
NUCmer (NUCleotide MUMmer) version 3.1

we concatenate this with the OS name.

The following circumstances are explicitly reported as strings

- no executable at passed path
- non-executable file at passed path
- no version info returned
"""
nucmer_path = Path(shutil.which(nucmer_exe)) # type:ignore

if nucmer_path is None:
return f"{nucmer_exe} is not found in $PATH"

if not nucmer_path.is_file(): # no executable
return f"No nucmer at {nucmer_path}"

if not os.access(nucmer_path, os.X_OK): # file exists but not executable
return f"nucmer exists at {nucmer_path} but not executable"

cmdline = [nucmer_exe, "-V"] # type: List
result = subprocess.run(
cmdline, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
)
match = re.search(r"(?<=version\s)[0-9\.]*", str(result.stderr, "utf-8"))
version = match.group() # type: ignore
return f"{platform.system()}_{version}"

if 0 == len(version.strip()):
return f"nucmer exists at {nucmer_path} but could not retrieve version"

return f"{platform.system()}_{version} ({nucmer_path})"


# Generate list of Job objects, one per NUCmer run
Expand Down
21 changes: 15 additions & 6 deletions pyani/scripts/pyani_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@

CITATION_INFO = [
termcolor(
"If you use pyani in your work, please cite the following publication:",
"green",
"If you use pyani in your work, please cite the following publication:", "green"
),
termcolor(
"\tPritchard, L., Glover, R. H., Humphris, S., Elphinstone, J. G.,", "yellow",
Expand All @@ -69,8 +68,7 @@
"\tfood security: soft-rotting enterobacterial plant pathogens.'", "yellow"
),
termcolor(
"\tAnalytical Methods, 8(1), 12–24. http://doi.org/10.1039/C5AY02550H",
"yellow",
"\tAnalytical Methods, 8(1), 12–24. http://doi.org/10.1039/C5AY02550H", "yellow"
),
]

Expand All @@ -81,14 +79,25 @@ def run_main(argv: Optional[List[str]] = None) -> int:

:param argv:
"""
# We need to catch the case with no arguments here, otherwise
# argparse will infer -h as an implied argument
if len(sys.argv) == 1 and argv is None:
sys.stderr.write(f"{VERSION_INFO}\n")
return 0

# If we need to (i.e. a namespace isn't passed), parse the command-line
if argv is None:
args = parse_cmdline()
else:
args = parse_cmdline(argv)

# Catch execution with no arguments
if len(sys.argv) == 1 and argv is None:
# Catch requests for citation and version information.
# This should stop any subcommands from running, also.
if args.citation:
sys.stderr.write(f"{VERSION_INFO}\n")
sys.stderr.write("\n".join(CITATION_INFO) + "\n")
return 0
if args.version:
sys.stderr.write(f"{VERSION_INFO}\n")
return 0

Expand Down
Loading