diff --git a/pdb2pqr/__init__.py b/pdb2pqr/__init__.py index f9c2c1a8..f9494733 100644 --- a/pdb2pqr/__init__.py +++ b/pdb2pqr/__init__.py @@ -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) diff --git a/pdb2pqr/main.py b/pdb2pqr/main.py index a9a1a93a..a4736371 100644 --- a/pdb2pqr/main.py +++ b/pdb2pqr/main.py @@ -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 @@ -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( @@ -755,7 +759,7 @@ 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. @@ -763,7 +767,6 @@ def main_driver(args): :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.") @@ -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. diff --git a/tests/common.py b/tests/common.py index ebb1f558..8dc02f2a 100644 --- a/tests/common.py +++ b/tests/common.py @@ -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, diff --git a/tests/core_test.py b/tests/core_test.py index a01f1cce..b3c4a8df 100644 --- a/tests/core_test.py +++ b/tests/core_test.py @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, @@ -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, diff --git a/tests/ligand_test.py b/tests/ligand_test.py index 0938f65f..22afffe1 100644 --- a/tests/ligand_test.py +++ b/tests/ligand_test.py @@ -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, diff --git a/tests/logging_test.py b/tests/logging_test.py index 09e6437c..4cf54bd1 100644 --- a/tests/logging_test.py +++ b/tests/logging_test.py @@ -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, diff --git a/tests/propka_test.py b/tests/propka_test.py index 46c2c178..1a1949dc 100644 --- a/tests/propka_test.py +++ b/tests/propka_test.py @@ -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, diff --git a/tests/regression_test.py b/tests/regression_test.py index 214bbf94..310cb1b4 100644 --- a/tests/regression_test.py +++ b/tests/regression_test.py @@ -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, @@ -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, @@ -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,