Typing: Options
This commit is contained in:
@@ -6,7 +6,8 @@ Container data structure for molecular conformations.
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import functools
|
import functools
|
||||||
from typing import Iterable, List, NoReturn, Optional, TYPE_CHECKING, Set
|
from typing import Callable, Dict, Iterable, Iterator, List, NoReturn, Optional, TYPE_CHECKING, Set
|
||||||
|
|
||||||
from propka.lib import Options
|
from propka.lib import Options
|
||||||
from propka.version import Version
|
from propka.version import Version
|
||||||
|
|
||||||
@@ -26,6 +27,8 @@ from propka.parameters import Parameters
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CallableGroupToGroups = Callable[[Group], List[Group]]
|
||||||
|
|
||||||
|
|
||||||
#: A large number that gets multipled with the integer obtained from applying
|
#: A large number that gets multipled with the integer obtained from applying
|
||||||
#: :func:`ord` to the atom chain ID. Used in calculating atom keys for
|
#: :func:`ord` to the atom chain ID. Used in calculating atom keys for
|
||||||
@@ -276,7 +279,7 @@ class ConformationContainer:
|
|||||||
return penalised_labels
|
return penalised_labels
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def share_determinants(groups):
|
def share_determinants(groups: Iterable[Group]):
|
||||||
"""Share sidechain, backbone, and Coloumb determinants between groups.
|
"""Share sidechain, backbone, and Coloumb determinants between groups.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -286,7 +289,7 @@ class ConformationContainer:
|
|||||||
types = ['sidechain', 'backbone', 'coulomb']
|
types = ['sidechain', 'backbone', 'coulomb']
|
||||||
for type_ in types:
|
for type_ in types:
|
||||||
# find maximum value for each determinant
|
# find maximum value for each determinant
|
||||||
max_dets = {}
|
max_dets: Dict[Group, float] = {}
|
||||||
for group in groups:
|
for group in groups:
|
||||||
for det in group.determinants[type_]:
|
for det in group.determinants[type_]:
|
||||||
# update max dets
|
# update max dets
|
||||||
@@ -302,7 +305,11 @@ class ConformationContainer:
|
|||||||
for group in groups:
|
for group in groups:
|
||||||
group.set_determinant(new_determinant, type_)
|
group.set_determinant(new_determinant, type_)
|
||||||
|
|
||||||
def get_coupled_systems(self, groups, get_coupled_groups):
|
def get_coupled_systems(
|
||||||
|
self,
|
||||||
|
groups: Iterable[Group],
|
||||||
|
get_coupled_groups: CallableGroupToGroups,
|
||||||
|
) -> Iterator[Set[Group]]:
|
||||||
"""A generator that yields covalently coupled systems.
|
"""A generator that yields covalently coupled systems.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -314,15 +321,16 @@ class ConformationContainer:
|
|||||||
groups = set(groups)
|
groups = set(groups)
|
||||||
while len(groups) > 0:
|
while len(groups) > 0:
|
||||||
# extract a system of coupled groups ...
|
# extract a system of coupled groups ...
|
||||||
system = set()
|
system: Set[Group] = set()
|
||||||
self.get_a_coupled_system_of_groups(
|
self.get_a_coupled_system_of_groups(
|
||||||
groups.pop(), system, get_coupled_groups)
|
groups.pop(), system, get_coupled_groups)
|
||||||
# ... and remove them from the list
|
# ... and remove them from the list
|
||||||
groups -= system
|
groups -= system
|
||||||
yield system
|
yield system
|
||||||
|
|
||||||
def get_a_coupled_system_of_groups(self, new_group, coupled_groups,
|
def get_a_coupled_system_of_groups(self, new_group: Group,
|
||||||
get_coupled_groups):
|
coupled_groups: Set[Group],
|
||||||
|
get_coupled_groups: CallableGroupToGroups):
|
||||||
"""Set up coupled systems of groups.
|
"""Set up coupled systems of groups.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -353,7 +361,7 @@ class ConformationContainer:
|
|||||||
reference=reference)
|
reference=reference)
|
||||||
return ddg
|
return ddg
|
||||||
|
|
||||||
def calculate_charge(self, parameters, ph: float):
|
def calculate_charge(self, parameters: Parameters, ph: float):
|
||||||
"""Calculate charge for folded and unfolded states.
|
"""Calculate charge for folded and unfolded states.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -371,7 +379,7 @@ class ConformationContainer:
|
|||||||
state='folded')
|
state='folded')
|
||||||
return unfolded, folded
|
return unfolded, folded
|
||||||
|
|
||||||
def get_backbone_groups(self):
|
def get_backbone_groups(self) -> List[Group]:
|
||||||
"""Get backbone groups needed for the pKa calculations.
|
"""Get backbone groups needed for the pKa calculations.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|||||||
@@ -6,9 +6,11 @@ Describe and analyze energetic coupling between groups.
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import itertools
|
import itertools
|
||||||
|
from typing import Optional
|
||||||
import propka.lib
|
import propka.lib
|
||||||
from propka.group import Group
|
from propka.group import Group
|
||||||
from propka.output import make_interaction_map
|
from propka.output import make_interaction_map
|
||||||
|
from propka.parameters import Parameters
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -16,9 +18,8 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class NonCovalentlyCoupledGroups:
|
class NonCovalentlyCoupledGroups:
|
||||||
"""Groups that are coupled without covalent bonding."""
|
"""Groups that are coupled without covalent bonding."""
|
||||||
def __init__(self):
|
parameters: Optional[Parameters] = None
|
||||||
self.parameters = None
|
do_prot_stat = True
|
||||||
self.do_prot_stat = True
|
|
||||||
|
|
||||||
def is_coupled_protonation_state_probability(self, group1, group2,
|
def is_coupled_protonation_state_probability(self, group1, group2,
|
||||||
energy_method,
|
energy_method,
|
||||||
@@ -264,7 +265,7 @@ class NonCovalentlyCoupledGroups:
|
|||||||
_LOGGER.info(swap_info)
|
_LOGGER.info(swap_info)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_interaction(group1, group2, include_side_chain_hbs=True):
|
def get_interaction(group1: Group, group2: Group, include_side_chain_hbs=True):
|
||||||
"""Get interaction energy between two groups.
|
"""Get interaction energy between two groups.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ Provides the :class:`Determinant` class.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from propka.group import Group
|
||||||
|
|
||||||
|
|
||||||
class Determinant:
|
class Determinant:
|
||||||
"""Determinant class.
|
"""Determinant class.
|
||||||
@@ -25,7 +30,7 @@ class Determinant:
|
|||||||
TODO - figure out what this class does.
|
TODO - figure out what this class does.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, group, value):
|
def __init__(self, group: "Group", value: float):
|
||||||
"""Initialize the object.
|
"""Initialize the object.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -36,7 +41,7 @@ class Determinant:
|
|||||||
self.label = group.label
|
self.label = group.label
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
||||||
def add(self, value):
|
def add(self, value: float):
|
||||||
"""Increment determinant value.
|
"""Increment determinant value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ Input routines.
|
|||||||
Methods to read PROPKA input files (:func:`read_propka` and
|
Methods to read PROPKA input files (:func:`read_propka` and
|
||||||
:func:`get_atom_lines_from_input`) have been removed.
|
:func:`get_atom_lines_from_input`) have been removed.
|
||||||
"""
|
"""
|
||||||
import typing
|
from typing import IO, ContextManager, Dict, Iterable, Iterator, Optional, Tuple
|
||||||
from typing import Iterator, Tuple, Union
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import io
|
import io
|
||||||
import zipfile
|
import zipfile
|
||||||
@@ -19,19 +18,18 @@ from propka.lib import protein_precheck
|
|||||||
from propka.atom import Atom
|
from propka.atom import Atom
|
||||||
from propka.conformation_container import ConformationContainer
|
from propka.conformation_container import ConformationContainer
|
||||||
from propka.molecular_container import MolecularContainer
|
from propka.molecular_container import MolecularContainer
|
||||||
|
from propka.output import _PathArg, _PathLikeTypes, _TextIOSource
|
||||||
from propka.parameters import Parameters
|
from propka.parameters import Parameters
|
||||||
|
|
||||||
|
|
||||||
def open_file_for_reading(
|
def open_file_for_reading(input_file: _TextIOSource) -> ContextManager[IO[str]]:
|
||||||
input_file: typing.Union[str, Path, typing.TextIO]
|
|
||||||
) -> typing.ContextManager[typing.TextIO]:
|
|
||||||
"""Open file or file-like stream for reading.
|
"""Open file or file-like stream for reading.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
input_file: path to file or file-like object. If file-like object,
|
input_file: path to file or file-like object. If file-like object,
|
||||||
then will attempt seek(0).
|
then will attempt seek(0).
|
||||||
"""
|
"""
|
||||||
if not isinstance(input_file, (str, Path)):
|
if not isinstance(input_file, _PathLikeTypes):
|
||||||
input_file.seek(0)
|
input_file.seek(0)
|
||||||
return contextlib.nullcontext(input_file)
|
return contextlib.nullcontext(input_file)
|
||||||
|
|
||||||
@@ -48,7 +46,11 @@ def open_file_for_reading(
|
|||||||
return contextlib.closing(open(input_file, 'rt'))
|
return contextlib.closing(open(input_file, 'rt'))
|
||||||
|
|
||||||
|
|
||||||
def read_molecule_file(filename: str, mol_container: MolecularContainer, stream=None) -> MolecularContainer:
|
def read_molecule_file(
|
||||||
|
filename: _PathArg,
|
||||||
|
mol_container: MolecularContainer,
|
||||||
|
stream: Optional[IO[str]] = None,
|
||||||
|
) -> MolecularContainer:
|
||||||
"""Read input file or stream (PDB or PROPKA) for a molecular container
|
"""Read input file or stream (PDB or PROPKA) for a molecular container
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -96,11 +98,7 @@ def read_molecule_file(filename: str, mol_container: MolecularContainer, stream=
|
|||||||
input_path = Path(filename)
|
input_path = Path(filename)
|
||||||
mol_container.name = input_path.stem
|
mol_container.name = input_path.stem
|
||||||
input_file_extension = input_path.suffix
|
input_file_extension = input_path.suffix
|
||||||
|
input_file = filename if stream is None else stream
|
||||||
if stream is not None:
|
|
||||||
input_file = stream
|
|
||||||
else:
|
|
||||||
input_file = filename
|
|
||||||
|
|
||||||
if input_file_extension.lower() == '.pdb':
|
if input_file_extension.lower() == '.pdb':
|
||||||
# input is a pdb file. read in atoms and top up containers to make
|
# input is a pdb file. read in atoms and top up containers to make
|
||||||
@@ -133,7 +131,7 @@ def read_molecule_file(filename: str, mol_container: MolecularContainer, stream=
|
|||||||
return mol_container
|
return mol_container
|
||||||
|
|
||||||
|
|
||||||
def read_parameter_file(input_file: Union[Path, str], parameters: Parameters) -> Parameters:
|
def read_parameter_file(input_file: _PathArg, parameters: Parameters) -> Parameters:
|
||||||
"""Read a parameter file.
|
"""Read a parameter file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -161,8 +159,13 @@ def conformation_sorter(conf: str) -> int:
|
|||||||
return model*100+ord(altloc)
|
return model*100+ord(altloc)
|
||||||
|
|
||||||
|
|
||||||
def get_atom_lines_from_pdb(pdb_file, ignore_residues=[], keep_protons=False,
|
def get_atom_lines_from_pdb(
|
||||||
tags=['ATOM ', 'HETATM'], chains=None) -> Iterator[Tuple[str, Atom]]:
|
pdb_file: _TextIOSource,
|
||||||
|
ignore_residues: Iterable[str] = (),
|
||||||
|
keep_protons: bool = False,
|
||||||
|
tags: Iterable[str] = ('ATOM ', 'HETATM'),
|
||||||
|
chains: Optional[Iterable[str]] = None,
|
||||||
|
) -> Iterator[Tuple[str, Atom]]:
|
||||||
"""Get atom lines from PDB file.
|
"""Get atom lines from PDB file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -228,7 +231,8 @@ def get_atom_lines_from_pdb(pdb_file, ignore_residues=[], keep_protons=False,
|
|||||||
terminal = None
|
terminal = None
|
||||||
|
|
||||||
|
|
||||||
def read_pdb(pdb_file, parameters, molecule):
|
def read_pdb(pdb_file: _TextIOSource, parameters: Parameters,
|
||||||
|
molecule: MolecularContainer):
|
||||||
"""Parse a PDB file.
|
"""Parse a PDB file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -240,7 +244,7 @@ def read_pdb(pdb_file, parameters, molecule):
|
|||||||
1. list of conformations
|
1. list of conformations
|
||||||
2. list of names
|
2. list of names
|
||||||
"""
|
"""
|
||||||
conformations = {}
|
conformations: Dict[str, ConformationContainer] = {}
|
||||||
# read in all atoms in the file
|
# read in all atoms in the file
|
||||||
lines = get_atom_lines_from_pdb(
|
lines = get_atom_lines_from_pdb(
|
||||||
pdb_file, ignore_residues=parameters.ignore_residues,
|
pdb_file, ignore_residues=parameters.ignore_residues,
|
||||||
@@ -253,4 +257,4 @@ def read_pdb(pdb_file, parameters, molecule):
|
|||||||
conformations[name].add_atom(atom)
|
conformations[name].add_atom(atom)
|
||||||
# make a sorted list of conformation names
|
# make a sorted list of conformation names
|
||||||
names = sorted(conformations.keys(), key=conformation_sorter)
|
names = sorted(conformations.keys(), key=conformation_sorter)
|
||||||
return [conformations, names]
|
return conformations, names
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ def add_determinants(iterative_interactions: List[Interaction], version: Version
|
|||||||
for itres in iteratives:
|
for itres in iteratives:
|
||||||
for type_ in ['sidechain', 'backbone', 'coulomb']:
|
for type_ in ['sidechain', 'backbone', 'coulomb']:
|
||||||
for interaction in itres.determinants[type_]:
|
for interaction in itres.determinants[type_]:
|
||||||
value = interaction[1]
|
value: float = interaction[1]
|
||||||
if value > UNK_MIN_VALUE or value < -UNK_MIN_VALUE:
|
if value > UNK_MIN_VALUE or value < -UNK_MIN_VALUE:
|
||||||
group = interaction[0]
|
group = interaction[0]
|
||||||
new_det = Determinant(group, value)
|
new_det = Determinant(group, value)
|
||||||
|
|||||||
@@ -5,16 +5,20 @@ Set-up of a PROPKA calculation
|
|||||||
Implements many of the main functions used to call PROPKA.
|
Implements many of the main functions used to call PROPKA.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
|
||||||
import logging
|
import logging
|
||||||
import argparse
|
import argparse
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, TYPE_CHECKING
|
from typing import Iterable, Iterator, List, TYPE_CHECKING, NoReturn, Optional, Tuple, TypeVar
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from propka.atom import Atom
|
from propka.atom import Atom
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
Number = TypeVar("Number", int, float)
|
||||||
|
|
||||||
|
_T_RESIDUE_TUPLE = Tuple[str, int, str]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +27,28 @@ EXPECTED_ATOM_NUMBERS = {'ALA': 5, 'ARG': 11, 'ASN': 8, 'ASP': 8, 'CYS': 6,
|
|||||||
'LEU': 8, 'LYS': 9, 'MET': 8, 'PHE': 11, 'PRO': 7,
|
'LEU': 8, 'LYS': 9, 'MET': 8, 'PHE': 11, 'PRO': 7,
|
||||||
'SER': 6, 'THR': 7, 'TRP': 14, 'TYR': 12, 'VAL': 7}
|
'SER': 6, 'THR': 7, 'TRP': 14, 'TYR': 12, 'VAL': 7}
|
||||||
|
|
||||||
Options = argparse.Namespace
|
|
||||||
|
class Options:
|
||||||
|
# Note: All the "NoReturn" members appear to be unused
|
||||||
|
alignment: NoReturn # Optional[List[str]]
|
||||||
|
chains: Optional[List[str]]
|
||||||
|
display_coupled_residues: bool = False
|
||||||
|
filenames: List[str] # List[Path]?
|
||||||
|
grid: Tuple[float, float, float] = (0.0, 14.0, 0.1)
|
||||||
|
input_pdb: str # Path?
|
||||||
|
keep_protons: bool = False
|
||||||
|
log_level: str = 'INFO'
|
||||||
|
mutations: NoReturn # Optional[List[str]]
|
||||||
|
mutator: NoReturn # Optional[str] # alignment/scwrl/jackal
|
||||||
|
mutator_options: NoReturn # Optional[List[str]]
|
||||||
|
pH: NoReturn # float = 7.0
|
||||||
|
parameters: Path
|
||||||
|
protonate_all: bool = False
|
||||||
|
reference: NoReturn # str = 'neutral'
|
||||||
|
reuse_ligand_mol2_file: bool = False # only used by unused function
|
||||||
|
thermophiles: NoReturn # Optional[List[str]]
|
||||||
|
titrate_only: Optional[List[_T_RESIDUE_TUPLE]]
|
||||||
|
window: Tuple[float, float, float] = (0.0, 14.0, 1.0)
|
||||||
|
|
||||||
|
|
||||||
def protein_precheck(conformations, names):
|
def protein_precheck(conformations, names):
|
||||||
@@ -112,11 +137,11 @@ def make_molecule(atom: "Atom", atoms: List["Atom"]):
|
|||||||
return res_atoms
|
return res_atoms
|
||||||
|
|
||||||
|
|
||||||
def make_grid(min_, max_, step):
|
def make_grid(min_: Number, max_: Number, step: Number) -> Iterator[Number]:
|
||||||
"""Make a grid across the specified tange.
|
"""Make a grid across the specified tange.
|
||||||
|
|
||||||
TODO - figure out if this duplicates existing generators like `range` or
|
Like range() for integers or numpy.arange() for floats, except that `max_`
|
||||||
numpy function.
|
is not excluded from the range.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
min_: minimum value of grid
|
min_: minimum value of grid
|
||||||
@@ -129,7 +154,7 @@ def make_grid(min_, max_, step):
|
|||||||
x += step
|
x += step
|
||||||
|
|
||||||
|
|
||||||
def generate_combinations(interactions):
|
def generate_combinations(interactions: Iterable[T]) -> List[List[T]]:
|
||||||
"""Generate combinations of interactions.
|
"""Generate combinations of interactions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -137,14 +162,14 @@ def generate_combinations(interactions):
|
|||||||
Returns:
|
Returns:
|
||||||
list of combinations
|
list of combinations
|
||||||
"""
|
"""
|
||||||
res = [[]]
|
res: List[List[T]] = [[]]
|
||||||
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: List[List[T]], interaction: T) -> List[List[T]]:
|
||||||
"""Make a specific set of combinations.
|
"""Make a specific set of combinations.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -160,7 +185,7 @@ def make_combination(combis, interaction):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def parse_res_string(res_str):
|
def parse_res_string(res_str: str) -> _T_RESIDUE_TUPLE:
|
||||||
"""Parse a residue string.
|
"""Parse a residue string.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -189,6 +214,16 @@ def parse_res_string(res_str):
|
|||||||
return chain, resnum, inscode
|
return chain, resnum, inscode
|
||||||
|
|
||||||
|
|
||||||
|
def parse_res_list(titrate_only: str):
|
||||||
|
res_list: List[_T_RESIDUE_TUPLE] = []
|
||||||
|
for res_str in titrate_only.split(','):
|
||||||
|
try:
|
||||||
|
res_list.append(parse_res_string(res_str))
|
||||||
|
except ValueError as ex:
|
||||||
|
raise argparse.ArgumentTypeError(f'{ex}: "{res_str:s}"')
|
||||||
|
return res_list
|
||||||
|
|
||||||
|
|
||||||
def build_parser(parser=None):
|
def build_parser(parser=None):
|
||||||
"""Build an argument parser for PROPKA.
|
"""Build an argument parser for PROPKA.
|
||||||
|
|
||||||
@@ -233,6 +268,7 @@ def build_parser(parser=None):
|
|||||||
'" " for chains without ID [all]'))
|
'" " for chains without ID [all]'))
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"-i", "--titrate_only", dest="titrate_only",
|
"-i", "--titrate_only", dest="titrate_only",
|
||||||
|
type=parse_res_list,
|
||||||
help=('Treat only the specified residues as titratable. Value should '
|
help=('Treat only the specified residues as titratable. Value should '
|
||||||
'be a comma-separated list of "chain:resnum" values; for '
|
'be a comma-separated list of "chain:resnum" values; for '
|
||||||
'example: -i "A:10,A:11"'))
|
'example: -i "A:10,A:11"'))
|
||||||
@@ -252,8 +288,8 @@ def build_parser(parser=None):
|
|||||||
"--version", action="version", version=f"%(prog)s {propka.__version__}")
|
"--version", action="version", version=f"%(prog)s {propka.__version__}")
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"-p", "--parameters", dest="parameters",
|
"-p", "--parameters", dest="parameters",
|
||||||
default=str(Path(__file__).parent / "propka.cfg"),
|
type=Path, default=Path(__file__).parent / "propka.cfg",
|
||||||
help="set the parameter file [{default:s}]")
|
help="set the parameter file")
|
||||||
try:
|
try:
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
"--log-level",
|
"--log-level",
|
||||||
@@ -325,18 +361,6 @@ def loadOptions(args=None) -> Options:
|
|||||||
|
|
||||||
# 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:
|
|
||||||
if options.titrate_only is not None:
|
|
||||||
res_list = []
|
|
||||||
for res_str in options.titrate_only.split(','):
|
|
||||||
try:
|
|
||||||
chain, resnum, inscode = parse_res_string(res_str)
|
|
||||||
except ValueError:
|
|
||||||
_LOGGER.critical(
|
|
||||||
'Invalid residue string: "{0:s}"'.format(res_str))
|
|
||||||
sys.exit(1)
|
|
||||||
res_list.append((chain, resnum, inscode))
|
|
||||||
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)
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ class MolecularContainer:
|
|||||||
conformation='AVR', reference=reference)
|
conformation='AVR', reference=reference)
|
||||||
|
|
||||||
def get_folding_profile(self, conformation='AVR', reference="neutral",
|
def get_folding_profile(self, conformation='AVR', reference="neutral",
|
||||||
grid=[0., 14., 0.1]):
|
grid: Tuple[float, float, float] = (0., 14., 0.1)):
|
||||||
"""Get a folding profile.
|
"""Get a folding profile.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -174,25 +174,25 @@ class MolecularContainer:
|
|||||||
4. stability_range
|
4. stability_range
|
||||||
"""
|
"""
|
||||||
# calculate stability profile
|
# calculate stability profile
|
||||||
profile = []
|
profile: List[Tuple[float, float]] = []
|
||||||
for ph in make_grid(*grid):
|
for ph in make_grid(*grid):
|
||||||
conf = self.conformations[conformation]
|
conf = self.conformations[conformation]
|
||||||
ddg = conf.calculate_folding_energy(ph=ph, reference=reference)
|
ddg = conf.calculate_folding_energy(ph=ph, reference=reference)
|
||||||
profile.append([ph, ddg])
|
profile.append((ph, ddg))
|
||||||
# find optimum
|
# find optimum
|
||||||
opt = [None, 1e6]
|
opt: Tuple[Optional[float], float] = (None, 1e6)
|
||||||
for point in profile:
|
for point in profile:
|
||||||
opt = min(opt, point, key=lambda v: v[1])
|
opt = min(opt, point, key=lambda v: v[1])
|
||||||
# find values within 80 % of optimum
|
# find values within 80 % of optimum
|
||||||
range_80pct = [None, None]
|
range_80pct: Tuple[Optional[float], Optional[float]] = (None, None)
|
||||||
values_within_80pct = [p[0] for p in profile if p[1] < 0.8*opt[1]]
|
values_within_80pct = [p[0] for p in profile if p[1] < 0.8*opt[1]]
|
||||||
if len(values_within_80pct) > 0:
|
if len(values_within_80pct) > 0:
|
||||||
range_80pct = [min(values_within_80pct), max(values_within_80pct)]
|
range_80pct = (min(values_within_80pct), max(values_within_80pct))
|
||||||
# find stability range
|
# find stability range
|
||||||
stability_range = [None, None]
|
stability_range: Tuple[Optional[float], Optional[float]] = (None, None)
|
||||||
stable_values = [p[0] for p in profile if p[1] < 0.0]
|
stable_values = [p[0] for p in profile if p[1] < 0.0]
|
||||||
if len(stable_values) > 0:
|
if len(stable_values) > 0:
|
||||||
stability_range = [min(stable_values), max(stable_values)]
|
stability_range = (min(stable_values), max(stable_values))
|
||||||
return profile, opt, range_80pct, stability_range
|
return profile, opt, range_80pct, stability_range
|
||||||
|
|
||||||
def get_charge_profile(self, conformation: str = 'AVR', grid=[0., 14., .1]):
|
def get_charge_profile(self, conformation: str = 'AVR', grid=[0., 14., .1]):
|
||||||
|
|||||||
@@ -12,13 +12,29 @@ Output routines.
|
|||||||
import logging
|
import logging
|
||||||
from datetime import date
|
from datetime import date
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from os import PathLike
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import IO, AnyStr, List, NoReturn, Optional, Union, TYPE_CHECKING
|
||||||
|
import warnings
|
||||||
|
|
||||||
|
from .parameters import Parameters
|
||||||
from . import __version__
|
from . import __version__
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .atom import Atom
|
||||||
|
from .conformation_container import ConformationContainer
|
||||||
|
from .molecular_container import MolecularContainer
|
||||||
|
|
||||||
|
# https://docs.python.org/3/glossary.html#term-path-like-object
|
||||||
|
_PathLikeTypes = (PathLike, str)
|
||||||
|
_PathArg = Union[PathLike, str]
|
||||||
|
_IOSource = Union[IO[AnyStr], PathLike, str]
|
||||||
|
_TextIOSource = _IOSource[str]
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def open_file_for_writing(input_file):
|
def open_file_for_writing(input_file: _TextIOSource) -> IO[str]:
|
||||||
"""Open file or file-like stream for writing.
|
"""Open file or file-like stream for writing.
|
||||||
|
|
||||||
TODO - convert this to a context manager.
|
TODO - convert this to a context manager.
|
||||||
@@ -27,17 +43,13 @@ def open_file_for_writing(input_file):
|
|||||||
input_file: path to file or file-like object. If file-like object,
|
input_file: path to file or file-like object. If file-like object,
|
||||||
then will attempt to get file mode.
|
then will attempt to get file mode.
|
||||||
"""
|
"""
|
||||||
try:
|
if isinstance(input_file, _PathLikeTypes):
|
||||||
if not input_file.writable():
|
return open(input_file, 'wt')
|
||||||
raise IOError("File/stream not open for writing")
|
|
||||||
return input_file
|
if not input_file.writable():
|
||||||
except AttributeError:
|
raise IOError("File/stream not open for writing")
|
||||||
pass
|
|
||||||
try:
|
return input_file
|
||||||
file_ = open(input_file, 'wt')
|
|
||||||
except FileNotFoundError:
|
|
||||||
raise Exception('Could not open {0:s}'.format(input_file))
|
|
||||||
return file_
|
|
||||||
|
|
||||||
|
|
||||||
def write_file(filename, lines):
|
def write_file(filename, lines):
|
||||||
@@ -47,6 +59,7 @@ def write_file(filename, lines):
|
|||||||
filename: name of file
|
filename: name of file
|
||||||
lines: lines to write to file
|
lines: lines to write to file
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("unused and untested by propka")
|
||||||
file_ = open_file_for_writing(filename)
|
file_ = open_file_for_writing(filename)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
file_.write("{0:s}\n".format(line))
|
file_.write("{0:s}\n".format(line))
|
||||||
@@ -101,17 +114,22 @@ def write_pdb_for_protein(
|
|||||||
pdbfile.close()
|
pdbfile.close()
|
||||||
|
|
||||||
|
|
||||||
def write_pdb_for_conformation(conformation, filename):
|
def write_pdb_for_conformation(conformation: "ConformationContainer",
|
||||||
|
filename: _PathArg):
|
||||||
"""Write PDB conformation to a file.
|
"""Write PDB conformation to a file.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
conformation: conformation container
|
conformation: conformation container
|
||||||
filename: filename for output
|
filename: filename for output
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("unused and untested by propka")
|
||||||
write_pdb_for_atoms(conformation.atoms, filename)
|
write_pdb_for_atoms(conformation.atoms, filename)
|
||||||
|
|
||||||
|
|
||||||
def write_pka(protein, parameters, filename=None, conformation='1A',
|
def write_pka(protein: "MolecularContainer",
|
||||||
|
parameters: Parameters,
|
||||||
|
filename: Optional[_PathArg] = None,
|
||||||
|
conformation='1A',
|
||||||
reference="neutral", _="folding", verbose=False,
|
reference="neutral", _="folding", verbose=False,
|
||||||
__=None):
|
__=None):
|
||||||
"""Write the pKa-file based on the given protein.
|
"""Write the pKa-file based on the given protein.
|
||||||
@@ -129,8 +147,6 @@ def write_pka(protein, parameters, filename=None, conformation='1A',
|
|||||||
verbose = True
|
verbose = True
|
||||||
if filename is None:
|
if filename is None:
|
||||||
filename = "{0:s}.pka".format(protein.name)
|
filename = "{0:s}.pka".format(protein.name)
|
||||||
# TODO - this would be much better with a context manager
|
|
||||||
file_ = open(filename, 'w')
|
|
||||||
if verbose:
|
if verbose:
|
||||||
_LOGGER.info("Writing {0:s}".format(filename))
|
_LOGGER.info("Writing {0:s}".format(filename))
|
||||||
# writing propka header
|
# writing propka header
|
||||||
@@ -149,11 +165,10 @@ def write_pka(protein, parameters, filename=None, conformation='1A',
|
|||||||
# printing Protein Charge Profile
|
# printing Protein Charge Profile
|
||||||
str_ += get_charge_profile_section(protein, conformation=conformation)
|
str_ += get_charge_profile_section(protein, conformation=conformation)
|
||||||
# now, writing the pka text to file
|
# now, writing the pka text to file
|
||||||
file_.write(str_)
|
Path(filename).write_text(str_, encoding="utf-8")
|
||||||
file_.close()
|
|
||||||
|
|
||||||
|
|
||||||
def print_tm_profile(protein, reference="neutral", window=[0., 14., 1.],
|
def print_tm_profile(protein: NoReturn, reference="neutral", window=[0., 14., 1.],
|
||||||
__=[0., 0.], tms=None, ref=None, _=False,
|
__=[0., 0.], tms=None, ref=None, _=False,
|
||||||
options=None):
|
options=None):
|
||||||
"""Print Tm profile.
|
"""Print Tm profile.
|
||||||
@@ -186,7 +201,7 @@ def print_tm_profile(protein, reference="neutral", window=[0., 14., 1.],
|
|||||||
_LOGGER.info(str_)
|
_LOGGER.info(str_)
|
||||||
|
|
||||||
|
|
||||||
def print_result(protein, conformation, parameters):
|
def print_result(protein: "MolecularContainer", conformation: str, parameters: Parameters):
|
||||||
"""Prints all resulting output from determinants and down.
|
"""Prints all resulting output from determinants and down.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -197,7 +212,7 @@ def print_result(protein, conformation, parameters):
|
|||||||
print_pka_section(protein, conformation, parameters)
|
print_pka_section(protein, conformation, parameters)
|
||||||
|
|
||||||
|
|
||||||
def print_pka_section(protein, conformation, parameters):
|
def print_pka_section(protein: "MolecularContainer", conformation: str, parameters: Parameters):
|
||||||
"""Prints out pKa section of results.
|
"""Prints out pKa section of results.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -212,7 +227,7 @@ def print_pka_section(protein, conformation, parameters):
|
|||||||
_LOGGER.info("pKa summary:\n%s", str_)
|
_LOGGER.info("pKa summary:\n%s", str_)
|
||||||
|
|
||||||
|
|
||||||
def get_determinant_section(protein, conformation, parameters):
|
def get_determinant_section(protein: "MolecularContainer", conformation: str, parameters: Parameters):
|
||||||
"""Returns string with determinant section of results.
|
"""Returns string with determinant section of results.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -244,7 +259,8 @@ def get_determinant_section(protein, conformation, parameters):
|
|||||||
return str_
|
return str_
|
||||||
|
|
||||||
|
|
||||||
def get_summary_section(protein, conformation, parameters):
|
def get_summary_section(protein: "MolecularContainer", conformation: str,
|
||||||
|
parameters: Parameters):
|
||||||
"""Returns string with summary section of the results.
|
"""Returns string with summary section of the results.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -266,7 +282,8 @@ def get_summary_section(protein, conformation, parameters):
|
|||||||
|
|
||||||
|
|
||||||
def get_folding_profile_section(
|
def get_folding_profile_section(
|
||||||
protein, conformation='AVR', direction="folding", reference="neutral",
|
protein: "MolecularContainer",
|
||||||
|
conformation='AVR', direction="folding", reference="neutral",
|
||||||
window=[0., 14., 1.0], _=False, __=None):
|
window=[0., 14., 1.0], _=False, __=None):
|
||||||
"""Returns string with the folding profile section of the results.
|
"""Returns string with the folding profile section of the results.
|
||||||
|
|
||||||
@@ -373,6 +390,7 @@ def write_scwrl_sequence_file(sequence, filename="x-ray.seq", _=None):
|
|||||||
|
|
||||||
TODO - figure out what this is
|
TODO - figure out what this is
|
||||||
"""
|
"""
|
||||||
|
warnings.warn("unused and untested by propka")
|
||||||
with open(filename, 'w') as file_:
|
with open(filename, 'w') as file_:
|
||||||
start = 0
|
start = 0
|
||||||
while len(sequence[start:]) > 60:
|
while len(sequence[start:]) > 60:
|
||||||
@@ -536,7 +554,7 @@ def make_interaction_map(name, list_, interaction):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def write_pdb_for_atoms(atoms, filename, make_conect_section=False):
|
def write_pdb_for_atoms(atoms: List["Atom"], filename: _PathArg, make_conect_section=False):
|
||||||
"""Write out PDB file for atoms.
|
"""Write out PDB file for atoms.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -12,10 +12,12 @@ function. If similar functionality is desired from a Python script
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
|
from typing import IO, Iterable, Optional
|
||||||
from propka.lib import loadOptions
|
from propka.lib import loadOptions
|
||||||
from propka.input import read_parameter_file, read_molecule_file
|
from propka.input import read_parameter_file, read_molecule_file
|
||||||
from propka.parameters import Parameters
|
from propka.parameters import Parameters
|
||||||
from propka.molecular_container import MolecularContainer
|
from propka.molecular_container import MolecularContainer
|
||||||
|
from propka.output import _PathArg
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -44,7 +46,9 @@ def main(optargs=None):
|
|||||||
my_molecule.write_pka()
|
my_molecule.write_pka()
|
||||||
|
|
||||||
|
|
||||||
def single(filename: str, optargs: tuple = (), stream=None,
|
def single(filename: _PathArg,
|
||||||
|
optargs: Iterable[str] = (),
|
||||||
|
stream: Optional[IO[str]] = None,
|
||||||
write_pka: bool = True):
|
write_pka: bool = True):
|
||||||
"""Run a single PROPKA calculation using ``filename`` as input.
|
"""Run a single PROPKA calculation using ``filename`` as input.
|
||||||
|
|
||||||
@@ -105,6 +109,7 @@ def single(filename: str, optargs: tuple = (), stream=None,
|
|||||||
.. versionchanged:: 3.4.0
|
.. versionchanged:: 3.4.0
|
||||||
Removed ability to write out PROPKA input files.
|
Removed ability to write out PROPKA input files.
|
||||||
"""
|
"""
|
||||||
|
filename = str(filename)
|
||||||
# Deal with input optarg options
|
# Deal with input optarg options
|
||||||
optargs = tuple(optargs)
|
optargs = tuple(optargs)
|
||||||
optargs += (filename,)
|
optargs += (filename,)
|
||||||
|
|||||||
27
tests/test_lib.py
Normal file
27
tests/test_lib.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import propka.lib as m
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_res_string():
|
||||||
|
assert m.parse_res_string("C:123") == ("C", 123, " ")
|
||||||
|
assert m.parse_res_string("C:123B") == ("C", 123, "B")
|
||||||
|
assert m.parse_res_string("ABC:123x") == ("ABC", 123, "x")
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
m.parse_res_string("C:B123")
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
m.parse_res_string("123B")
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
m.parse_res_string("C:123:B")
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_res_list():
|
||||||
|
assert m.parse_res_list("C:123") == [("C", 123, " ")]
|
||||||
|
assert m.parse_res_list("ABC:123,D:4,F:56X") == [
|
||||||
|
("ABC", 123, " "),
|
||||||
|
("D", 4, " "),
|
||||||
|
("F", 56, "X"),
|
||||||
|
]
|
||||||
|
with pytest.raises(argparse.ArgumentTypeError):
|
||||||
|
m.parse_res_list("C:B123")
|
||||||
Reference in New Issue
Block a user