Skip to content

Commit

Permalink
Merge pull request #177 from speleo3/mypy-2
Browse files Browse the repository at this point in the history
More static typing
  • Loading branch information
speleo3 authored Dec 19, 2023
2 parents 9d4b59c + abd3d05 commit 5a00d05
Show file tree
Hide file tree
Showing 27 changed files with 764 additions and 331 deletions.
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ include propka.cfg

include versioneer.py
include propka/_version.py

# Python type annotation declarations, see PEP-561
include propka/py.typed
92 changes: 52 additions & 40 deletions propka/atom.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import string
from typing import cast, List, NoReturn, Optional, TYPE_CHECKING
import warnings

from propka.lib import make_tidy_atom_label
from . import hybrid36
Expand Down Expand Up @@ -42,6 +43,43 @@ class Atom:
:meth:`make_input_line` and :meth:`get_input_parameters` have been
removed as reading/writing PROPKA input is no longer supported.
"""
group: Optional["Group"] = None
group_type: Optional[str] = None
cysteine_bridge: bool = False
residue: NoReturn = None # type: ignore[assignment]
conformation_container: Optional["ConformationContainer"] = None
molecular_container: Optional["MolecularContainer"] = None
is_protonated: bool = False
steric_num_lone_pairs_set: bool = False
terminal: Optional[str] = None
charge: float = 0.0
charge_set: bool = False
steric_number: int = 0
number_of_lone_pairs: int = 0
number_of_protons_to_add: int = 0
num_pi_elec_2_3_bonds: int = 0
num_pi_elec_conj_2_3_bonds: int = 0
groups_extracted: bool = False

# PDB attributes
name: str = ''
numb: int = 0
x: float = 0.0
y: float = 0.0
z: float = 0.0
res_num: int = 0
res_name: str = ''
chain_id: str = 'A'
type: str = ''
occ: str = '1.0'
beta: str = '0.0'
element: str = ''
icode: str = ''

# ligand atom types
sybyl_type = ''
sybyl_assigned = False
marvin_pka = False

def __init__(self, line: Optional[str] = None):
"""Initialize Atom object.
Expand All @@ -50,53 +88,17 @@ def __init__(self, line: Optional[str] = None):
line: Line from a PDB file to set properties of atom.
"""
self.number_of_bonded_elements: NoReturn = cast(NoReturn, {}) # FIXME unused?
self.group: Optional[Group] = None
self.group_type: Optional[str] = None
self.cysteine_bridge: bool = False
self.bonded_atoms: List[Atom] = []
self.residue = None
self.conformation_container: Optional[ConformationContainer] = None
self.molecular_container: Optional[MolecularContainer] = None
self.is_protonated = False
self.steric_num_lone_pairs_set = False
self.terminal: Optional[str] = None
self.charge = 0.0
self.charge_set = False
self.steric_number = 0
self.number_of_lone_pairs = 0
self.number_of_protons_to_add = 0
self.num_pi_elec_2_3_bonds = 0
self.num_pi_elec_conj_2_3_bonds = 0
self.groups_extracted = 0
self.set_properties(line)
fmt = "{r.name:3s}{r.res_num:>4d}{r.chain_id:>2s}"
self.residue_label = fmt.format(r=self)

# ligand atom types
self.sybyl_type = ''
self.sybyl_assigned = False
self.marvin_pka = False

def set_properties(self, line: Optional[str]):
"""Line from PDB file to set properties of atom.
Args:
line: PDB file line
"""
self.name = ''
self.numb = 0
self.x = 0.0
self.y = 0.0
self.z = 0.0
self.res_num = 0
self.res_name = ''
self.chain_id = 'A'
self.type = ''
self.occ = '1.0'
self.beta = '0.0'
self.element = ''
self.icode = ''

if line:
self.name = line[12:16].strip()
self.numb = int(hybrid36.decode(line[6:11]))
Expand Down Expand Up @@ -183,9 +185,17 @@ def is_atom_within_bond_distance(self, other_atom, max_bonds, cur_bond):
return True
return False

def set_property(self, numb=None, name=None, res_name=None, chain_id=None,
res_num=None, x=None, y=None, z=None, occ=None,
beta=None):
def set_property(self,
numb: Optional[int] = None,
name: Optional[str] = None,
res_name: Optional[str] = None,
chain_id: Optional[str] = None,
res_num: Optional[int] = None,
x: Optional[float] = None,
y: Optional[float] = None,
z: Optional[float] = None,
occ: Optional[str] = None,
beta: Optional[str] = None):
"""Set properties of the atom object.
Args:
Expand Down Expand Up @@ -303,6 +313,7 @@ def make_pdb_line2(self, numb=None, name=None, res_name=None, chain_id=None,
Returns:
String with PDB line.
"""
warnings.warn("only used by unused function")
if numb is None:
numb = self.numb
if name is None:
Expand Down Expand Up @@ -343,11 +354,12 @@ def __str__(self):
"""Return an undefined-format string version of this atom."""
return STR_FMT.format(r=self)

def set_residue(self, residue):
def set_residue(self, residue: NoReturn):
""" Makes a reference to the parent residue
Args:
residue: the parent residue
"""
raise NotImplementedError("unused")
if self.residue is None:
self.residue = residue
2 changes: 2 additions & 0 deletions propka/bonds.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def find_bonds_for_protein(self, protein):
Args:
protein: the protein to search for bonds
"""
raise NotImplementedError("unused")
_LOGGER.info('++++ Side chains ++++')
# side chains
for chain in protein.chains:
Expand Down Expand Up @@ -132,6 +133,7 @@ def check_for_cysteine_bonds(self, cys1, cys2):
cys1: one of the cysteines to check
cys1: one of the cysteines to check
"""
raise NotImplementedError("unused")
for atom1 in cys1.atoms:
if atom1.name == 'SG':
for atom2 in cys2.atoms:
Expand Down
40 changes: 26 additions & 14 deletions propka/conformation_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
"""
import logging
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.version import Version

if TYPE_CHECKING:
from propka.atom import Atom
Expand All @@ -19,10 +22,13 @@
from propka.determinants import set_backbone_determinants, set_ion_determinants
from propka.determinants import set_determinants
from propka.group import Group, is_group
from propka.parameters import Parameters


_LOGGER = logging.getLogger(__name__)

CallableGroupToGroups = Callable[[Group], List[Group]]


#: 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
Expand All @@ -44,9 +50,9 @@ class ConformationContainer:
"""

def __init__(self,
name: str = '',
parameters=None,
molecular_container: Optional["MolecularContainer"] = None):
name: str,
parameters: Parameters,
molecular_container: "MolecularContainer"):
"""Initialize conformation container.
Args:
Expand Down Expand Up @@ -145,6 +151,7 @@ def find_bonded_titratable_groups(self, atom: "Atom", num_bonds: int,
Returns:
a set of bonded atom groups
"""
assert self.parameters is not None
res: Set[Group] = set()
for bond_atom in atom.bonded_atoms:
# skip the original atom
Expand Down Expand Up @@ -187,7 +194,7 @@ def init_group(self, group: Group):

# If --titrate_only option is set, make non-specified residues
# un-titratable:
assert self.molecular_container is not None
assert self.molecular_container.options is not None
titrate_only = self.molecular_container.options.titrate_only
if titrate_only is not None:
atom = group.atom
Expand All @@ -196,7 +203,7 @@ def init_group(self, group: Group):
if group.residue_type == 'CYS':
group.exclude_cys_from_results = True

def calculate_pka(self, version, options):
def calculate_pka(self, version: Version, options: Options):
"""Calculate pKas for conformation container.
Args:
Expand Down Expand Up @@ -272,7 +279,7 @@ def coupling_effects(self):
return penalised_labels

@staticmethod
def share_determinants(groups):
def share_determinants(groups: Iterable[Group]):
"""Share sidechain, backbone, and Coloumb determinants between groups.
Args:
Expand All @@ -282,7 +289,7 @@ def share_determinants(groups):
types = ['sidechain', 'backbone', 'coulomb']
for type_ in types:
# find maximum value for each determinant
max_dets = {}
max_dets: Dict[Group, float] = {}
for group in groups:
for det in group.determinants[type_]:
# update max dets
Expand All @@ -298,7 +305,11 @@ def share_determinants(groups):
for group in groups:
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.
Args:
Expand All @@ -310,15 +321,16 @@ def get_coupled_systems(self, groups, get_coupled_groups):
groups = set(groups)
while len(groups) > 0:
# extract a system of coupled groups ...
system = set()
system: Set[Group] = set()
self.get_a_coupled_system_of_groups(
groups.pop(), system, get_coupled_groups)
# ... and remove them from the list
groups -= system
yield system

def get_a_coupled_system_of_groups(self, new_group, coupled_groups,
get_coupled_groups):
def get_a_coupled_system_of_groups(self, new_group: Group,
coupled_groups: Set[Group],
get_coupled_groups: CallableGroupToGroups):
"""Set up coupled systems of groups.
Args:
Expand Down Expand Up @@ -349,7 +361,7 @@ def calculate_folding_energy(self, ph=None, reference=None):
reference=reference)
return ddg

def calculate_charge(self, parameters, ph=None):
def calculate_charge(self, parameters: Parameters, ph: float):
"""Calculate charge for folded and unfolded states.
Args:
Expand All @@ -367,7 +379,7 @@ def calculate_charge(self, parameters, ph=None):
state='folded')
return unfolded, folded

def get_backbone_groups(self):
def get_backbone_groups(self) -> List[Group]:
"""Get backbone groups needed for the pKa calculations.
Returns:
Expand Down
13 changes: 9 additions & 4 deletions propka/coupled_groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,20 @@
"""
import logging
import itertools
from typing import Optional
import propka.lib
from propka.group import Group
from propka.output import make_interaction_map
from propka.parameters import Parameters


_LOGGER = logging.getLogger(__name__)


class NonCovalentlyCoupledGroups:
"""Groups that are coupled without covalent bonding."""
def __init__(self):
self.parameters = None
self.do_prot_stat = True
parameters: Optional[Parameters] = None
do_prot_stat = True

def is_coupled_protonation_state_probability(self, group1, group2,
energy_method,
Expand All @@ -33,6 +34,7 @@ def is_coupled_protonation_state_probability(self, group1, group2,
Returns:
dictionary describing coupling
"""
assert self.parameters is not None
# check if the interaction energy is high enough
interaction_energy = max(self.get_interaction(group1, group2),
self.get_interaction(group2, group1))
Expand Down Expand Up @@ -105,6 +107,7 @@ def get_pka_diff_factor(self, pka1, pka2):
Returns:
float value of scaling factor
"""
assert self.parameters is not None
intrinsic_pka_diff = abs(pka1-pka2)
res = 0.0
if intrinsic_pka_diff <= self.parameters.max_intrinsic_pka_diff:
Expand All @@ -122,6 +125,7 @@ def get_free_energy_diff_factor(self, energy1, energy2):
Returns:
float value of scaling factor
"""
assert self.parameters is not None
free_energy_diff = abs(energy1-energy2)
res = 0.0
if free_energy_diff <= self.parameters.max_free_energy_diff:
Expand All @@ -136,6 +140,7 @@ def get_interaction_factor(self, interaction_energy):
Returns:
float value of scaling factor
"""
assert self.parameters is not None
res = 0.0
interaction_energy = abs(interaction_energy)
if interaction_energy >= self.parameters.min_interaction_energy:
Expand Down Expand Up @@ -260,7 +265,7 @@ def print_system(self, conformation, system):
_LOGGER.info(swap_info)

@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.
Args:
Expand Down
Loading

0 comments on commit 5a00d05

Please sign in to comment.