Coverage for biobb_haddock/utils/anarcii.py: 96%

50 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-13 14:55 +0000

1#!/usr/bin/env python3 

2 

3"""Module containing the Anarcii class and the command line interface.""" 

4 

5import contextlib 

6import io 

7import logging 

8from pathlib import Path 

9from typing import Optional, Union 

10 

11from biobb_common.generic.biobb_object import BiobbObject 

12from biobb_common.tools import file_utils as fu 

13from biobb_common.tools.file_utils import launchlogger 

14from anarcii import Anarcii as AnarciiModel 

15 

16 

17class Anarcii(BiobbObject): 

18 """ 

19 | biobb_haddock Anarcii 

20 | Wrapper class for the `ANARCII <https://github.com/oxpig/ANARCII>`_ antibody numbering tool. 

21 | ANARCII numbers antibody, TCR, and other immune receptor sequences. Given an input PDB structure it renumbers it (IMGT scheme by default) and writes the renumbered PDB structure. 

22 

23 Args: 

24 input_pdb_path (str): Path to the input PDB structure file. File type: input. `Sample file <https://raw.githubusercontent.com/bioexcel/biobb_haddock/master/biobb_haddock/test/data/haddock_restraints/4G6K_clean.pdb>`_. Accepted formats: pdb (edam:format_1476). 

25 output_pdb_path (str): Path to the output renumbered PDB structure file. File type: output. `Sample file <https://raw.githubusercontent.com/bioexcel/biobb_haddock/master/biobb_haddock/test/data/utils/4G6K_anarcii_imgt.pdb>`_. Accepted formats: pdb (edam:format_1476). 

26 properties (dict - Python dictionary object containing the tool parameters, not input/output files): 

27 * **seq_type** (*str*) - ("antibody") Type of sequence to number. Values: antibody, tcr, vhh, sabdab. 

28 * **mode** (*str*) - ("accuracy") Numbering mode. Values: accuracy, speed. 

29 * **verbose** (*bool*) - (True) Print verbose output during numbering. 

30 * **remove_tmp** (*bool*) - (True) [WF property] Remove temporal files. 

31 * **restart** (*bool*) - (False) [WF property] Do not execute if output files exist. 

32 * **sandbox_path** (*str*) - ("./") [WF property] Parent path to the sandbox directory. 

33 

34 Examples: 

35 This is a use example of how to use the building block from Python:: 

36 

37 from biobb_haddock.utils.anarcii import anarcii 

38 prop = { 'seq_type': 'antibody', 

39 'mode': 'accuracy' } 

40 anarcii(input_pdb_path='/path/to/structure.pdb', 

41 output_pdb_path='/path/to/renumbered.pdb', 

42 properties=prop) 

43 

44 Info: 

45 * wrapped_software: 

46 * name: ANARCII 

47 * version: 2.0.0 

48 * license: BSD-3-Clause 

49 * ontology: 

50 * name: EDAM 

51 * schema: http://edamontology.org/EDAM.owl 

52 """ 

53 

54 def __init__( 

55 self, 

56 input_pdb_path: Union[str, Path], 

57 output_pdb_path: Union[str, Path], 

58 properties: Optional[dict] = None, 

59 **kwargs, 

60 ) -> None: 

61 properties = properties or {} 

62 

63 # Call parent class constructor 

64 super().__init__(properties) 

65 self.locals_var_dict = locals().copy() 

66 

67 # Input/Output files 

68 self.io_dict = { 

69 "in": {"input_pdb_path": input_pdb_path}, 

70 "out": {"output_pdb_path": output_pdb_path}, 

71 } 

72 

73 # Properties specific for BB 

74 self.seq_type = properties.get("seq_type", "antibody") 

75 self.mode = properties.get("mode", "accuracy") 

76 self.verbose = properties.get("verbose", True) 

77 

78 # Check the properties 

79 self.check_properties(properties) 

80 self.check_arguments() 

81 

82 @launchlogger 

83 def launch(self) -> int: 

84 """Execute the :class:`Anarcii <utils.anarcii.Anarcii>` object.""" 

85 

86 # Setup Biobb 

87 if self.check_restart(): 

88 return 0 

89 self.stage_files() 

90 

91 # ANARCII appends the '.pdb' suffix to the output stem 

92 output_pdb_path = self.stage_io_dict["out"]["output_pdb_path"] 

93 pdb_out_stem = str(Path(output_pdb_path).with_suffix("")) 

94 

95 fu.log( 

96 "Numbering %s with ANARCII (seq_type=%s, mode=%s)" 

97 % (self.stage_io_dict["in"]["input_pdb_path"], self.seq_type, self.mode), 

98 self.out_log, 

99 self.global_log, 

100 ) 

101 

102 # Capture ANARCII output (stdout/stderr prints and logging) into the Biobb log 

103 log_capture = io.StringIO() 

104 log_handler = logging.StreamHandler(log_capture) 

105 root_logger = logging.getLogger() 

106 root_logger.addHandler(log_handler) 

107 try: 

108 with contextlib.redirect_stdout(log_capture), contextlib.redirect_stderr(log_capture): 

109 model = AnarciiModel( 

110 seq_type=self.seq_type, mode=self.mode, verbose=self.verbose 

111 ) 

112 # number() renumbers the structure and returns the numbering 

113 self.results = model.number( 

114 self.stage_io_dict["in"]["input_pdb_path"], pdb_out_stem=pdb_out_stem 

115 ) 

116 finally: 

117 root_logger.removeHandler(log_handler) 

118 

119 captured = log_capture.getvalue().strip() 

120 if captured: 

121 fu.log("ANARCII output:\n%s" % captured, self.out_log, self.global_log) 

122 

123 # Copy files to host 

124 self.copy_to_host() 

125 

126 # Remove temporal files 

127 self.remove_tmp_files() 

128 

129 self.check_arguments(output_files_created=True, raise_exception=True) 

130 return self.return_code 

131 

132 

133def anarcii( 

134 input_pdb_path: Union[str, Path], 

135 output_pdb_path: Union[str, Path], 

136 properties: Optional[dict] = None, 

137 **kwargs, 

138) -> int: 

139 """Create :class:`Anarcii <utils.anarcii.Anarcii>` class and 

140 execute the :meth:`launch() <utils.anarcii.Anarcii.launch>` method.""" 

141 return Anarcii(**dict(locals())).launch() 

142 

143 

144anarcii.__doc__ = Anarcii.__doc__ 

145main = Anarcii.get_main(anarcii, "Wrapper for the ANARCII antibody numbering tool.") 

146 

147 

148if __name__ == "__main__": 

149 main()