Repository: haddocking/arctic3d

Issue:
improve filtering on input interface_file
the program should handle the following situations while reading the `interface_file`

1. empty line in interface file -> skip it
2. only a single value (possibly the name of the interface) -> abort the execution and throw an exception
3. invalid residue numbers (es 19A,28-38) -> abort the execution and throw an exception

Comments:

Patch:
diff --git a/src/arctic3d/modules/interface.py b/src/arctic3d/modules/interface.py
index f9f6049..8fd15cc 100644
--- a/src/arctic3d/modules/interface.py
+++ b/src/arctic3d/modules/interface.py
@@ -84,6 +84,46 @@ def parse_out_pdb(out_pdb_string):
     return set(out_pdb_list)
 
 
+def parse_interface_line(int_line, ln_num):
+    """
+    Parses the input interface line according to the following format:
+
+    int_name 1,2,3,6,7
+
+    Parameters
+    ----------
+    int_line : str
+        interface_line
+    ln_num : int
+        line number
+
+    Returns
+    -------
+    int_name : str
+        name of the interface
+    residue_list : list
+        list of residues
+    """
+    splt_ln = int_line.strip().split()
+    int_name = splt_ln[0]
+    # checking malformed interface
+    if len(splt_ln) != 2:
+        raise Exception(
+            f"Found uncompatible interface at line {ln_num} in interface_file."
+        )
+    residues_str_list = splt_ln[1].split(",")
+    residues_int_list = []
+    # checking they are all integers
+    for resid_string in residues_str_list:
+        if resid_string.isdigit():
+            residues_int_list.append(int(resid_string))
+        else:
+            raise Exception(
+                f"Malformed residue {resid_string} at line {ln_num} in interface_file."
+            )
+    return int_name, residues_int_list
+
+
 def read_interface_residues(interface_file):
     """
     Parameters
@@ -105,14 +145,12 @@ def read_interface_residues(interface_file):
     interface_dict = {}
     if os.path.exists(interface_file):
         with open(interface_file, "r") as ifile:
+            ln_num = 0  # keep track of line number
             for ln in ifile:
+                ln_num += 1
                 if ln != os.linesep:
-                    splt_ln = ln.split()
-                    try:
-                        residues = [int(resid) for resid in splt_ln[1].split(",")]
-                        interface_dict[splt_ln[0]] = residues
-                    except Exception as e:
-                        log.exception(e)
+                    int_name, residue_list = parse_interface_line(ln, ln_num)
+                    interface_dict[int_name] = residue_list
     else:
         raise Exception(f"interface_file {interface_file} does not exist")
     return interface_dict


Test patch:
diff --git a/tests/test_interface.py b/tests/test_interface.py
index ca503cb..a980505 100644
--- a/tests/test_interface.py
+++ b/tests/test_interface.py
@@ -3,6 +3,7 @@ from pathlib import Path
 import pytest
 
 from arctic3d.modules.interface import (
+    parse_interface_line,
     parse_out_pdb,
     parse_out_uniprot,
     read_interface_residues,
@@ -32,6 +33,20 @@ def test_read_int_file():
     assert obs_interface_dict == exp_interface_dict
 
 
+def test_parse_interface_line():
+    """Test parse_interface_line function."""
+    interface_lines = ["P00767 1,2,3", "P00767", "P00767 1-3,4"]
+    # first string is correct
+    exp_interface = "P00767", [1, 2, 3]
+    obs_interface = parse_interface_line(interface_lines[0], 0)
+    assert exp_interface == obs_interface
+    # the other two should throw an exception
+    with pytest.raises(Exception):
+        parse_interface_line(interface_lines[1], 1)
+    with pytest.raises(Exception):
+        parse_interface_line(interface_lines[2], 2)
+
+
 def test_parse_out_uniprot():
     uniprot_strings = [None, "P00760", "P00760,P00974"]
     expected_uniprot_strings = [set([]), set(["P00760"]), set(["P00760", "P00974"])]