Skip to content

Commit

Permalink
Merge pull request #410 from kiyoon/feat/run-pdb2pqr
Browse files Browse the repository at this point in the history
feat: run pdb2pqr in python (no CLI)
  • Loading branch information
sobolevnrm authored Nov 30, 2024
2 parents be4e943 + 2cec21d commit ba9624c
Show file tree
Hide file tree
Showing 9 changed files with 42 additions and 17 deletions.
1 change: 1 addition & 0 deletions pdb2pqr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from ._version import __version__
from .main import main as pdb2pqr_main
from .main import run_pdb2pqr

_LOGGER = logging.getLogger(__name__)
logging.captureWarnings(capture=True)
Expand Down
26 changes: 23 additions & 3 deletions pdb2pqr/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
.. codeauthor:: Nathan Baker (et al.)
"""

from __future__ import annotations

import argparse
import logging
import sys
from collections import OrderedDict
from collections.abc import Sequence
from io import StringIO
from os import PathLike
from pathlib import Path

import propka.input as pk_in
Expand Down Expand Up @@ -224,7 +228,7 @@ def build_main_parser():
"calculation method."
),
)
pars = propka.lib.build_parser(pars)
pars: argparse.ArgumentParser = propka.lib.build_parser(pars)

# Override version flag set by PROPKA
pars.add_argument(
Expand Down Expand Up @@ -755,15 +759,14 @@ def non_trivial(args, biomolecule, ligand, definition, is_cif):
}


def main_driver(args):
def main_driver(args: argparse.Namespace):
"""Main driver for running program from the command line.
Validate inputs, launch PDB2PQR, handle output.
:param args: command-line arguments
:type args: argparse.Namespace
"""
io.setup_logger(args.output_pqr, args.log_level)
_LOGGER.debug(f"Invoked with arguments: {args}")
print_splash_screen(args)
_LOGGER.info("Checking and transforming input arguments.")
Expand Down Expand Up @@ -836,10 +839,27 @@ def main():
"""Hook for command-line usage."""
parser = build_main_parser()
args = parser.parse_args()
io.setup_logger(args.output_pqr, args.log_level)
if main_driver(args) == 1:
sys.exit(1)


def run_pdb2pqr(args: Sequence[str | PathLike]):
"""Run PDB2PQR with a list of arguments.
Logger is not set up so that it can be called multiple times.
:param args: list of command-line arguments
:type args: list
:return: results of PDB2PQR run
:rtype: tuple
"""
args_strlist = [str(arg) for arg in args]
parser = build_main_parser()
args_parsed = parser.parse_args(args_strlist)
return main_driver(args_parsed)


def dx_to_cube():
"""Convert DX file format to Cube file format.
Expand Down
1 change: 1 addition & 0 deletions ruff_essential.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ select = [
"F63",
"F7",
"F82",
"FA102", # Python 3.10-style union typing is used so we need to import `from __future__ import annotations`
]
5 changes: 4 additions & 1 deletion tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def compare_pqr(pqr1_path, pqr2_path, compare_resnames=False):
_LOGGER.info(result)


def run_pdb2pqr(
def run_pdb2pqr_for_tests(
args,
input_pdb,
tmp_path,
Expand All @@ -212,6 +212,8 @@ def run_pdb2pqr(
compare_resnames=False,
):
"""Basic code for invoking PDB2PQR."""
from pdb2pqr import io

if output_pqr is None:
hash_str = f"{args}{input_pdb}"
hash_ = hashlib.sha1(hash_str.encode("UTF-8")).hexdigest()
Expand All @@ -220,6 +222,7 @@ def run_pdb2pqr(
_LOGGER.debug(f"Writing output to {output_pqr}")
arg_str = f"{args} {input_pdb} {output_pqr}"
args = PARSER.parse_args(arg_str.split())
io.setup_logger(args.output_pqr, args.log_level)
_ = main_driver(args)
if expected_pqr is not None:
compare_pqr(
Expand Down
14 changes: 7 additions & 7 deletions tests/core_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def test_short_pdb(input_pdb, tmp_path):
"""Non-regression tests on short list of PDB-format biomolecules."""
args = "--log-level=INFO --ff=AMBER --drop-water --apbs-input=apbs.in"
output_pqr = Path(input_pdb).stem + ".pqr"
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand All @@ -78,7 +78,7 @@ def test_basic_cif(input_pdb, tmp_path):
"""Non-regression tests on short list of CIF-format biomolecules."""
args = "--log-level=INFO --ff=AMBER --drop-water --apbs-input=apbs.in"
output_pqr = Path(input_pdb).stem + ".cif"
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand All @@ -92,7 +92,7 @@ def test_long_pdb(input_pdb, tmp_path):
"""Non-regression tests on short list of PDB-format biomolecules."""
args = "--log-level=INFO --ff=AMBER --drop-water --apbs-input=apbs.in"
output_pqr = Path(input_pdb).stem + ".pqr"
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand All @@ -106,7 +106,7 @@ def test_broken_backbone(input_pdb, tmp_path):
"""Test graceful failure of optimization with missing backbone atoms."""
args = "--log-level=INFO --ff=AMBER --drop-water"
output_pqr = Path(input_pdb).stem + ".pqr"
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand All @@ -122,7 +122,7 @@ def test_protonated_terminals(input_pdb, expected_pqr, tmp_path):
"""Tests for terminal residue protonation."""
args = "--log-level=INFO --ff=AMBER --ffout AMBER"
output_pqr = Path(input_pdb).stem + ".pqr"
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=common.DATA_DIR / input_pdb,
output_pqr=output_pqr,
Expand All @@ -146,7 +146,7 @@ def test_cyclic_peptide(input_pdb, expected_pqr, tmp_path):
"""Tests for cyclic peptide protonation."""
args = "--log-level=INFO --ff=AMBER --ffout AMBER"
output_pqr = Path(input_pdb).stem + ".pqr"
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=common.DATA_DIR / input_pdb,
output_pqr=output_pqr,
Expand All @@ -170,7 +170,7 @@ def test_ph_naming(naming_test, tmp_path):
f"--drop-water --whitespace --with-ph={naming_test['pH']} "
f"--titration-state-method=propka"
)
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=common.DATA_DIR / input_pdb,
output_pqr=output_pqr,
Expand Down
2 changes: 1 addition & 1 deletion tests/ligand_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def test_ligand_biomolecule(input_pdb, tmp_path):
args = f"--log-level=INFO --ff=AMBER --drop-water --ligand={ligand}"
output_pqr = Path(input_pdb).stem + ".pqr"
_LOGGER.debug(f"Running test in {tmp_path}")
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand Down
2 changes: 1 addition & 1 deletion tests/logging_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_log_output_in_pqr_location(
args = "--log-level=INFO --ff=AMBER"
input_path = common.DATA_DIR / input_file
output_pqr = output_file
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_path,
output_pqr=output_pqr,
Expand Down
2 changes: 1 addition & 1 deletion tests/propka_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def test_propka_apo(input_pdb, tmp_path):
"--titration-state-method=propka"
)
output_pqr = Path(input_pdb).stem + ".pqr"
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand Down
6 changes: 3 additions & 3 deletions tests/regression_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
)
def test_basic(args, input_pdb, output_pqr, expected_pqr, tmp_path):
"""Basic code to run 1AFS."""
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand Down Expand Up @@ -87,7 +87,7 @@ def test_basic(args, input_pdb, output_pqr, expected_pqr, tmp_path):
)
def test_forcefields(args, input_pdb, output_pqr, expected_pqr, tmp_path):
"""Basic code to run 1AFS with --whitespace for different forcefields."""
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand Down Expand Up @@ -153,7 +153,7 @@ def test_forcefields(args, input_pdb, output_pqr, expected_pqr, tmp_path):
)
def test_other_options(args, input_pdb, output_pqr, expected_pqr, tmp_path):
"""Basic code to run 1AFS with --whitespace."""
common.run_pdb2pqr(
common.run_pdb2pqr_for_tests(
args=args,
input_pdb=input_pdb,
output_pqr=output_pqr,
Expand Down

0 comments on commit ba9624c

Please sign in to comment.