mirror of
https://github.com/biopython/biopython.git
synced 2025-10-20 21:53:47 +08:00
$ ruff --fix --select PIE790 BioSQL/ Bio/ Doc/examples/ Tests/ Scripts/ Found 62 errors (62 fixed, 0 remaining). Then re-ran black.
135 lines
5.0 KiB
Python
135 lines
5.0 KiB
Python
# Copyright (C) 2011 by Brandon Invergo (b.invergo@gmail.com)
|
|
#
|
|
# This file is part of the Biopython distribution and governed by your
|
|
# choice of the "Biopython License Agreement" or the "BSD 3-Clause License".
|
|
# Please see the LICENSE file that should have been included as part of this
|
|
# package.
|
|
|
|
"""Base class for the support of PAML, Phylogenetic Analysis by Maximum Likelihood."""
|
|
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
|
|
class PamlError(EnvironmentError):
|
|
"""paml has failed.
|
|
|
|
Run with verbose=True to view the error message.
|
|
"""
|
|
|
|
|
|
class Paml:
|
|
"""Base class for wrapping PAML commands."""
|
|
|
|
def __init__(self, alignment=None, working_dir=None, out_file=None):
|
|
"""Initialize the class."""
|
|
if working_dir is None:
|
|
self.working_dir = os.getcwd()
|
|
else:
|
|
self.working_dir = working_dir
|
|
if alignment is not None:
|
|
if not os.path.exists(alignment):
|
|
raise FileNotFoundError("The specified alignment file does not exist.")
|
|
self.alignment = alignment
|
|
self.out_file = out_file
|
|
self._options = {} # will be set in subclasses
|
|
|
|
def write_ctl_file(self):
|
|
"""Write control file."""
|
|
|
|
def read_ctl_file(self):
|
|
"""Read control file."""
|
|
|
|
def print_options(self):
|
|
"""Print out all of the options and their current settings."""
|
|
for option in self._options.items():
|
|
print(f"{option[0]} = {option[1]}")
|
|
|
|
def set_options(self, **kwargs):
|
|
"""Set the value of an option.
|
|
|
|
This function abstracts the options dict to prevent the user from
|
|
adding options that do not exist or misspelling options.
|
|
"""
|
|
for option, value in kwargs.items():
|
|
if option not in self._options:
|
|
raise KeyError("Invalid option: " + option)
|
|
else:
|
|
self._options[option] = value
|
|
|
|
def get_option(self, option):
|
|
"""Return the value of an option."""
|
|
if option not in self._options:
|
|
raise KeyError("Invalid option: " + option)
|
|
else:
|
|
return self._options.get(option)
|
|
|
|
def get_all_options(self):
|
|
"""Return the values of all the options."""
|
|
return list(self._options.items())
|
|
|
|
def _set_rel_paths(self):
|
|
"""Convert all file/directory locations to paths relative to the current working directory (PRIVATE).
|
|
|
|
paml requires that all paths specified in the control file be
|
|
relative to the directory from which it is called rather than
|
|
absolute paths.
|
|
"""
|
|
if self.working_dir is not None:
|
|
self._rel_working_dir = os.path.relpath(self.working_dir)
|
|
if self.alignment is not None:
|
|
self._rel_alignment = os.path.relpath(self.alignment, self.working_dir)
|
|
if self.out_file is not None:
|
|
self._rel_out_file = os.path.relpath(self.out_file, self.working_dir)
|
|
|
|
def run(self, ctl_file, verbose, command):
|
|
"""Run a paml program using the current configuration.
|
|
|
|
Check that the class attributes exist and raise an error
|
|
if not. Then run the command and check if it succeeds with
|
|
a return code of 0, otherwise raise an error.
|
|
|
|
The arguments may be passed as either absolute or relative
|
|
paths, despite the fact that paml requires relative paths.
|
|
"""
|
|
if self.alignment is None:
|
|
raise ValueError("Alignment file not specified.")
|
|
if not os.path.exists(self.alignment):
|
|
raise FileNotFoundError("The specified alignment file does not exist.")
|
|
if self.out_file is None:
|
|
raise ValueError("Output file not specified.")
|
|
if self.working_dir is None:
|
|
raise ValueError("Working directory not specified.")
|
|
# Get the current working directory
|
|
cwd = os.getcwd()
|
|
# Move to the desired working directory
|
|
if not os.path.exists(self.working_dir):
|
|
os.mkdir(self.working_dir)
|
|
os.chdir(self.working_dir)
|
|
# If no external control file was specified...
|
|
if ctl_file is None:
|
|
# Dynamically build a control file
|
|
self.write_ctl_file()
|
|
ctl_file = self.ctl_file
|
|
else:
|
|
if not os.path.exists(ctl_file):
|
|
raise FileNotFoundError("The specified control file does not exist.")
|
|
if verbose:
|
|
result_code = subprocess.call([command, ctl_file])
|
|
else:
|
|
with open(os.devnull) as dn:
|
|
result_code = subprocess.call([command, ctl_file], stdout=dn, stderr=dn)
|
|
os.chdir(cwd)
|
|
if result_code > 0:
|
|
# If the program fails for any reason
|
|
raise PamlError(
|
|
"%s has failed (return code %i). Run with verbose = True to view error message"
|
|
% (command, result_code)
|
|
)
|
|
if result_code < 0:
|
|
# If the paml process is killed by a signal somehow
|
|
raise OSError(
|
|
"The %s process was killed (return code %i)." % (command, result_code)
|
|
)
|