Skip to content

Commit

Permalink
Merge pull request #10 from refresh-bio/binaries_validation
Browse files Browse the repository at this point in the history
Binaries validation
  • Loading branch information
sebastiandeorowicz committed Jul 11, 2024
2 parents 042478a + 5ef76fc commit e8d98a2
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 22 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ For datasets containing up to 1000 viral genomes, Vclust is available at [http:/

## 2. Installation

To install Vclust you can download statically compiled binaries or compile dependencies from source.
To install Vclust you can download statically compiled binaries or compile dependencies from source. Vclust requires Python 3.7 or higher.

### Option 1: Download precompiled binaries

Expand Down
17 changes: 17 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,23 @@ def test_dir():
shutil.rmtree(TMP_DIR)


@pytest.mark.parametrize('subcommand',[
'prefilter', 'align', 'cluster',
])
def test_subcommands(subcommand):
cmd = [
f'{VCLUST.resolve()}',
f'{subcommand}',
]
p = subprocess.run(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True)
assert p.returncode == 0
assert not p.stderr
assert p.stdout


@pytest.mark.parametrize('input,params,error_msg',[
(FASTA_DIR, ['--batch-size', '4'], 'error: --batch-size'),
(FASTA_DIR, ['--min-ident', '95'], 'between 0 and 1'),
Expand Down
88 changes: 67 additions & 21 deletions vclust.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
import multiprocessing
import pathlib
import shutil
import os
import subprocess
import sys
import typing
import uuid

__version__ = '1.0.1'
__version__ = '1.0.2'

DEFAULT_THREAD_COUNT = min(multiprocessing.cpu_count(), 64)

Expand Down Expand Up @@ -524,26 +525,49 @@ def create_logger(name: str, log_level: int = logging.INFO) -> logging.Logger:

def get_uuid() -> str:
"""Returns a unique string identifier."""
return f'vclust-{str(uuid.uuid4().hex)[:16]}'
return f'vclust-{str(uuid.uuid4().hex)[:10]}'


def validate_binary(bin_path: pathlib.Path) -> pathlib.Path:
"""Verifies if a binary file exists and is executable.
"""Validates the existence and executability of a binary file.
This function checks if the provided path points to an existing binary file
and if it is executable. It also attempts to run the binary to ensure it
operates without errors.
Args:
bin_path (Path): Path to the executable binary file.
bin_path:
The path to the executable binary file.
Returns:
Path to the binary file.
pathlib.Path: The resolved path to the binary file.
Raises:
SystemExit: If the binary file is not found or not executable.
SystemExit: If the binary file does not exist, is not executable, or
if running the binary encounters an error.
"""
bin_path = bin_path.resolve()

if not bin_path.exists():
exit(f'error: executable not found: {bin_path.resolve()}')
if not shutil.which(bin_path):
exit(f'error: file not executable: {bin_path.resolve()}')
exit(f'error: Executable not found: {bin_path}')

if not bin_path.is_file() or not os.access(bin_path, os.X_OK):
exit(f'error: Binary file not executable: {bin_path}')

try:
process = subprocess.run(
[str(bin_path)],
stdout=subprocess.DEVNULL,
stderr=subprocess.PIPE,
text=True,
check=True
)
except subprocess.CalledProcessError as e:
exit(f'error: Running {bin_path} failed with message: {e.stderr}')
except OSError as e:
exit(f'error: OSError in {bin_path} - {e}')
except Exception as e:
exit(f'error: Unexpected error in binary {bin_path} - {e}')
return bin_path


Expand Down Expand Up @@ -601,23 +625,45 @@ def run(
verbose: bool,
logger: logging.Logger
) -> subprocess.CompletedProcess:
"""Runs a given command as a subprocess and handle logging.
"""Executes a given command as a subprocess and handles logging.
This function runs the specified command, logs the execution details,
and manages errors. If verbose mode is enabled, the command's standard
error output is not suppressed. Otherwise, the standard error is piped
and logged in case of failure.
Args:
cmd:
The command to run as a list of strings.
verbose:
Flag indicating whether to run the command in verbose mode.
logger:
The logger instance for logging information and errors.
Returns:
subprocess.CompletedProcess: Completed process information.
subprocess.CompletedProcess: The completed process information.
Raises:
SystemExit: If the command fails to execute or an error occurs.
"""
logger.info(f'Running: {" ".join(cmd)}')
process = subprocess.run(
cmd,
stdout=subprocess.DEVNULL,
stderr=None if verbose else subprocess.PIPE,
text=True,
)
if process.returncode:
logger.error(f'While running: {" ".join(process.args)}')
logger.error(f'Error message: {process.stderr}')
try:
process = subprocess.run(
cmd,
stdout=subprocess.DEVNULL,
stderr=None if verbose else subprocess.PIPE,
text=True,
check=True
)
except subprocess.CalledProcessError as e:
logger.error(f'Process {" ".join(cmd)} failed with message: {e.stderr}')
exit(1)
except OSError as e:
logger.error(f'OSError: {" ".join(cmd)} failed with message: {e}')
exit(1)
except Exception as e:
logger.error(f'Unexpected: {" ".join(cmd)} failed with message: {e}')
exit(1)
logger.info(f'Done')
return process

Expand Down

0 comments on commit e8d98a2

Please sign in to comment.