De-lint lib.py.

This commit is contained in:
Nathan Baker
2020-05-24 15:30:35 -07:00
parent 04b52d458c
commit 59a8717664
2 changed files with 180 additions and 121 deletions

View File

@@ -240,7 +240,7 @@ class Atom(object):
if self.group.titratable: if self.group.titratable:
model_pka = '%6.2f'%self.group.model_pka model_pka = '%6.2f'%self.group.model_pka
str_ = "%-6s%5d %s " % (self.type.upper(), self.numb, str_ = "%-6s%5d %s " % (self.type.upper(), self.numb,
propka.lib.makeTidyAtomLabel(self.name, self.element)) propka.lib.make_tidy_atom_label(self.name, self.element))
str_ += "%s%2s%4d%12.3lf%8.3lf%8.3lf%6s%6s \n" % (self.res_name, self.chain_id, str_ += "%s%2s%4d%12.3lf%8.3lf%8.3lf%6s%6s \n" % (self.res_name, self.chain_id,
self.res_num, self.x, self.y, self.res_num, self.x, self.y,
self.z, group, model_pka) self.z, group, model_pka)
@@ -313,7 +313,7 @@ class Atom(object):
String with PDB line. String with PDB line.
""" """
str_ = "%-6s%5d " % (self.type.upper(), self.numb) str_ = "%-6s%5d " % (self.type.upper(), self.numb)
str_ += "%s %s" % (propka.lib.makeTidyAtomLabel(self.name, self.element), str_ += "%s %s" % (propka.lib.make_tidy_atom_label(self.name, self.element),
self.res_name) self.res_name)
str_ += "%2s%4d%12.3lf%8.3lf%8.3lf%6s%6s\n" % (self.chain_id, self.res_num, str_ += "%2s%4d%12.3lf%8.3lf%8.3lf%6s%6s\n" % (self.chain_id, self.res_num,
self.x, self.y, self.z, self.x, self.y, self.z,
@@ -330,7 +330,7 @@ class Atom(object):
Returns: Returns:
String with MOL2 line. String with MOL2 line.
""" """
str_ = "%-4d %-4s " % (id_, propka.lib.makeTidyAtomLabel(self.name, str_ = "%-4d %-4s " % (id_, propka.lib.make_tidy_atom_label(self.name,
self.element)) self.element))
str_ += "%10.4f %10.4f %10.4f " % (self.x, self.y, self.z) str_ += "%10.4f %10.4f %10.4f " % (self.x, self.y, self.z)
str_ += "%6s %6d %10s %10.4f\n" % (self.sybyl_type.replace('-', ''), str_ += "%6s %6d %10s %10.4f\n" % (self.sybyl_type.replace('-', ''),
@@ -369,7 +369,7 @@ class Atom(object):
beta = self.beta beta = self.beta
str_ = "ATOM " str_ = "ATOM "
str_ += "%6d" % (numb) str_ += "%6d" % (numb)
str_ += " %s" % (propka.lib.makeTidyAtomLabel(name, self.element)) str_ += " %s" % (propka.lib.make_tidy_atom_label(name, self.element))
str_ += " %s" % (res_name) str_ += " %s" % (res_name)
str_ += "%2s" % (chain_id) str_ += "%2s" % (chain_id)
str_ += "%4d" % (res_num) str_ += "%4d" % (res_num)
@@ -388,7 +388,7 @@ class Atom(object):
Returns: Returns:
String with label""" String with label"""
return propka.lib.makeTidyAtomLabel(self.name, self.element) return propka.lib.make_tidy_atom_label(self.name, self.element)
def __str__(self): def __str__(self):
"""Return an undefined-format string version of this atom.""" """Return an undefined-format string version of this atom."""

View File

@@ -1,112 +1,142 @@
from __future__ import division """Implements many of the main functions used to call PROPKA."""
from __future__ import print_function
import sys import sys
import pkg_resources
import logging import logging
import argparse import argparse
import pkg_resources
logger = logging.getLogger("propka") _LOGGER = logging.getLogger("propka")
stdout_handler = logging.StreamHandler(sys.stdout) _STDOUT_HANDLER = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(logging.Formatter("%(message)s")) _STDOUT_HANDLER.setFormatter(logging.Formatter("%(message)s"))
logger.addHandler(stdout_handler) _LOGGER.addHandler(_STDOUT_HANDLER)
#
# file I/O
#
def open_file_for_reading(filename):
"""Open file or file-like stream *filename* for reading.
*filename* may be a string and then it is opened but if it is a def open_file_for_reading(input_file):
file-like object (such as an open :class:`file` or """Open file or file-like stream for reading.
:class:`StringIO.StringIO` --- really anything with ``next()``,
``read()``, ``readlines()``, ``readline``, ``close`` methods) then TODO - convert this to a context manager
the object is just passed through (the stream is attempted to be
reset to the beginning with ``fseek(0)``). Args:
input_file: path to file or file-like object. If file-like object,
then will attempt fseek(0).
""" """
if (hasattr(filename, 'next') or hasattr(filename, '__next__')) \ try:
and hasattr(filename, 'read') \ input_file.fseek(0)
and hasattr(filename, 'readline') and hasattr(filename, 'readlines') \ return input_file
and hasattr(filename, 'close'): except AttributeError:
# already a stream pass
try:
filename.fseek(0)
except AttributeError:
pass
return filename
try: try:
f = open(filename,'r') file_ = open(input_file, 'rt')
except: except:
raise IOError('Cannot find file %s' %filename) raise IOError('Cannot find file %s' % input_file)
return f return file_
def open_file_for_writing(filename):
"""Open file or file-like stream for writing""" def open_file_for_writing(input_file):
if hasattr(filename, 'write') and hasattr(filename, 'writeline') and hasattr(filename, 'writelines') \ """Open file or file-like stream for writing.
and hasattr(filename, 'close'):
# already a stream TODO - convert this to a context manager.
try:
mode = filename.mode Args:
except AttributeError: input_file: path to file or file-like object. If file-like object,
mode = "w" then will attempt to get file mode.
"""
try:
mode = input_file.mode
if not ("w" in mode or "a" in mode or "+" in mode): if not ("w" in mode or "a" in mode or "+" in mode):
raise IOError("File/stream not open for writing") raise IOError("File/stream not open for writing")
return filename return input_file
except AttributeError:
pass
try: try:
f = open(filename,'w') file_ = open(input_file, 'wt')
except: except FileNotFoundError:
raise Exception('Could not open %s'%filename) raise Exception('Could not open %s' % input_file)
return f return file_
#
# bookkeeping etc.
#
def conformation_sorter(conf): def conformation_sorter(conf):
"""TODO - figure out what this function does."""
model = int(conf[:-1]) model = int(conf[:-1])
altloc = conf[-1:] altloc = conf[-1:]
return model*100+ord(altloc) return model*100+ord(altloc)
def split_atoms_into_molecules(atoms): def split_atoms_into_molecules(atoms):
"""Maps atoms into molecules.
Args:
atoms: list of atoms
Returns:
list of molecules
"""
molecules = [] molecules = []
while len(atoms) > 0:
while len(atoms)>0:
initial_atom = atoms.pop() initial_atom = atoms.pop()
molecules.append( make_molecule(initial_atom,atoms)) molecules.append(make_molecule(initial_atom, atoms))
return molecules return molecules
def make_molecule(atom, atoms): def make_molecule(atom, atoms):
"""Make a molecule from atoms.
Args:
atom: one of the atoms
atoms: a list of the remaining atoms
Return:
list of atoms
"""
bonded_atoms = [a for a in atoms if atom in a.bonded_atoms] bonded_atoms = [a for a in atoms if atom in a.bonded_atoms]
res_atoms = [atom,] res_atoms = [atom,]
for bond_atom in bonded_atoms:
for ba in bonded_atoms: if bond_atom in atoms:
if ba in atoms: atoms.remove(bond_atom)
atoms.remove(ba) res_atoms.extend(make_molecule(bond_atom, atoms))
res_atoms.extend(make_molecule(ba, atoms))
return res_atoms return res_atoms
def make_grid(min,max,step): def make_grid(min_, max_, step):
x = min """Make a grid across the specified tange.
while x <= max:
TODO - figure out if this duplicates existing generators like `range` or
numpy function.
Args:
min_: minimum value of grid
max_: maximum value of grid
step: grid step size
"""
x = min_
while x <= max_:
yield x yield x
x += step x += step
return
def generate_combinations(interactions): def generate_combinations(interactions):
"""Generate combinations of interactions.
Args:
interactions: list of interactions
Returns:
list of combinations
"""
res = [[]] res = [[]]
for interaction in interactions: for interaction in interactions:
res = make_combination(res, interaction) res = make_combination(res, interaction)
res.remove([]) res.remove([])
return res return res
def make_combination(combis, interaction): def make_combination(combis, interaction):
"""Make a specific set of combinations.
Args:
combis: list of combinations
interaction: interaction to add to combinations
Returns:
list of combinations
"""
res = [] res = []
for combi in combis: for combi in combis:
res.append(combi+[interaction]) res.append(combi+[interaction])
@@ -115,10 +145,14 @@ def make_combination(combis, interaction):
def parse_res_string(res_str): def parse_res_string(res_str):
""" """Parse a residue string.
Parse the residue string, in format "chain:resnum[inscode]", and return
a tuple of (chain, resnum, inscode). Raises ValueError if the input Args:
string is invalid. res_string: residue string in format "chain:resnum[inscode]"
Returns:
a tuple of (chain, resnum, inscode).
Raises:
ValueError if the input string is invalid.
""" """
try: try:
chain, resnum_str = res_str.split(":") chain, resnum_str = res_str.split(":")
@@ -142,7 +176,7 @@ def build_parser(parser=None):
"""Build an argument parser for PROPKA. """Build an argument parser for PROPKA.
Args: Args:
parser_: existing parser. If this is not None, then the PROPKA parser will parser: existing parser. If this is not None, then the PROPKA parser will
be created as a subparser to this existing parser. Otherwise, a be created as a subparser to this existing parser. Otherwise, a
new parser will be created. new parser will be created.
Returns: Returns:
@@ -158,7 +192,6 @@ def build_parser(parser=None):
# This is duck-typing at its finest # This is duck-typing at its finest
group = parser group = parser
group.add_argument("input_pdb", help="read data from <filename>") group.add_argument("input_pdb", help="read data from <filename>")
group.add_argument("-f", "--file", action="append", dest="filenames", default=[], group.add_argument("-f", "--file", action="append", dest="filenames", default=[],
help="read data from <filename>, i.e. <filename> is added to arguments") help="read data from <filename>, i.e. <filename> is added to arguments")
group.add_argument("-r", "--reference", dest="reference", default="neutral", group.add_argument("-r", "--reference", dest="reference", default="neutral",
@@ -223,31 +256,25 @@ def build_parser(parser=None):
def loadOptions(args): def loadOptions(args):
""" """Load the arguments parser with options.
Load the arguments parser with options. Note that verbosity is set as soon
as this function is invoked. NOTE - verbosity is set as soon as this function is invoked.
Arguments: Arguments:
args: list of arguments args: list of arguments
Returns: Returns:
argparse namespace argparse namespace
""" """
# defining a 'usage' message
usage = "usage: %prog [options] filename"
# loading the parser # loading the parser
parser = build_parser() parser = build_parser()
# parsing and returning options and arguments # parsing and returning options and arguments
if len(args) == 0: if len(args) == 0:
# command line # command line
options = parser.parse_args() options = parser.parse_args()
else: else:
options = parser.parse_args(args) options = parser.parse_args(args)
# adding specified filenames to arguments # adding specified filenames to arguments
options.filenames.append(options.input_pdb) options.filenames.append(options.input_pdb)
# Convert titrate_only string to a list of (chain, resnum) items: # Convert titrate_only string to a list of (chain, resnum) items:
if options.titrate_only is not None: if options.titrate_only is not None:
res_list = [] res_list = []
@@ -255,76 +282,108 @@ def loadOptions(args):
try: try:
chain, resnum, inscode = parse_res_string(res_str) chain, resnum, inscode = parse_res_string(res_str)
except ValueError: except ValueError:
logger.critical('Invalid residue string: "%s"' % res_str) _LOGGER.critical('Invalid residue string: "%s"', res_str)
sys.exit(1) sys.exit(1)
res_list.append((chain, resnum, inscode)) res_list.append((chain, resnum, inscode))
options.titrate_only = res_list options.titrate_only = res_list
# Set the no-print variable # Set the no-print variable
level = getattr(logging, options.log_level) level = getattr(logging, options.log_level)
logger.setLevel(level) _LOGGER.setLevel(level)
# done! # done!
return options return options
def makeTidyAtomLabel(name,element): def make_tidy_atom_label(name, element):
""" """Returns a 'tidier' atom label for printing to the new PDB file.
Returns a 'tidier' atom label for printing the new pdbfile
"""
if len(name)>4:# if longer than 4, just truncate the name Args:
label=name[0:4] name: atom name
elif len(name)==4:# if lenght is 4, otherwise use the name as it is element: atom element
Returns:
string
"""
if len(name) > 4: # if longer than 4, just truncate the name
label = name[0:4]
elif len(name) == 4: # if length is 4, otherwise use the name as it is
label = name label = name
else: # if less than 4 characters long, insert white space as needed else: # if less than 4 characters long, insert white space as needed
if len(element)==1: if len(element) == 1:
label = ' %-3s'%name label = ' %-3s' % name
else: # The element shoul occupy the two first chars else: # The element should occupy the two first chars
label = '%-4s'%name label = '%-4s' % name
return label return label
def get_sorted_configurations(configuration_keys): def get_sorted_configurations(configuration_keys):
""" """Extract and sort configurations.
extract and sort configurations
Args:
configuration_keys: list of configuration keys
Returns:
list of configurations
""" """
configurations = list(configuration_keys) configurations = list(configuration_keys)
configurations.sort(key=configuration_compare) configurations.sort(key=configuration_compare)
return configurations return configurations
def configuration_compare(conf): def configuration_compare(conf):
"""TODO - figure out what this function does."""
return 100*int(conf[1:-2]) + ord(conf[-1]) return 100*int(conf[1:-2]) + ord(conf[-1])
def write_file(filename, lines):
"""Writes a new file.
Args:
def writeFile(filename, lines): filename: name of file
lines: lines to write to file
""" """
Writes a new file file_ = open_file_for_writing(filename)
"""
f = open_file_for_writing(filename)
for line in lines: for line in lines:
f.write( "%s\n" % (line) ) file_.write("%s\n" % (line))
f.close() file_.close()
def _args_to_str(arg_list): def _args_to_str(arg_list):
"""Summarize list of arguments in string.
Args:
arg_list: list of arguments
Returns:
string
"""
return " ".join(map(str, arg_list)) return " ".join(map(str, arg_list))
def info(*args): def info(*args):
"""Log a message. Level defaults to INFO unless overridden.""" """Log a message to info.
logger.info(_args_to_str(args))
Level defaults to INFO unless overridden.
Args:
args: argument list
"""
_LOGGER.info(_args_to_str(args))
def debug(*args): def debug(*args):
"""Log a message on the DEBUG level.""" """Log a message to debug.
logger.debug(_args_to_str(args))
Level defaults to DEBUG unless overridden.
Args:
args: argument list
"""
_LOGGER.debug(_args_to_str(args))
def warning(*args): def warning(*args):
"""Log a WARN message""" """Log a message to warning.
logger.warning(_args_to_str(args))
Level defaults to WARNING unless overridden.
Args:
args: argument list
"""
_LOGGER.warning(_args_to_str(args))