1 | import abc |
||
2 | import os |
||
3 | from typing import Any, Dict, Optional |
||
4 | |||
5 | |||
6 | View Code Duplication | class Wrapper(metaclass=abc.ABCMeta): |
|
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
7 | """ |
||
8 | Parent class for classes that generate Python code, i.e. wrappers, for calling a stored procedures and functions. |
||
9 | """ |
||
10 | |||
11 | # ------------------------------------------------------------------------------------------------------------------ |
||
12 | def __init__(self, routine: Dict[str, Any], lob_as_string_flag: bool): |
||
13 | """ |
||
14 | Object constructor. |
||
15 | |||
16 | :param dict routine: The metadata of the stored routine. |
||
17 | :param bool lob_as_string_flag: If 'True' LOBs must be treated as strings/bytes. |
||
18 | """ |
||
19 | self._page_width: int = 120 |
||
20 | """ |
||
21 | The maximum number of columns in the source code. |
||
22 | """ |
||
23 | |||
24 | self._code: str = '' |
||
25 | """ |
||
26 | Buffer for the generated code. |
||
27 | """ |
||
28 | |||
29 | self.__indent_level: int = 1 |
||
30 | """ |
||
31 | The current level of indentation in the generated code. |
||
32 | """ |
||
33 | |||
34 | self._routine: Dict[str, Any] = routine |
||
35 | """ |
||
36 | The metadata of the stored routine. |
||
37 | """ |
||
38 | |||
39 | self._lob_as_string_flag: bool = lob_as_string_flag == 'True' |
||
40 | """ |
||
41 | If True BLOBs and CLOBs must be treated as strings. |
||
42 | """ |
||
43 | |||
44 | # ------------------------------------------------------------------------------------------------------------------ |
||
45 | def _write(self, text: str) -> None: |
||
46 | """ |
||
47 | Appends a part of code to the generated code. |
||
48 | |||
49 | :param str text: The part of code that must be appended. |
||
50 | """ |
||
51 | self._code += str(text) |
||
52 | |||
53 | # ------------------------------------------------------------------------------------------------------------------ |
||
54 | def _write_line(self, line: Optional[str] = None) -> None: |
||
55 | """ |
||
56 | Appends a line of code to the generated code and adjust the indent level of the generated code. |
||
57 | |||
58 | :param line: The line of code (with out LF) that must be appended. |
||
59 | """ |
||
60 | if line is None: |
||
61 | self._write("\n") |
||
62 | if self.__indent_level > 1: |
||
63 | self.__indent_level -= 1 |
||
64 | elif line == '': |
||
65 | self._write("\n") |
||
66 | else: |
||
67 | line = (' ' * 4 * self.__indent_level) + line |
||
68 | if line[-1:] == ':': |
||
69 | self.__indent_level += 1 |
||
70 | self._write(line + "\n") |
||
71 | |||
72 | # ------------------------------------------------------------------------------------------------------------------ |
||
73 | def _indent_level_down(self, levels: int = 1) -> None: |
||
74 | """ |
||
75 | Decrements the indent level of the generated code. |
||
76 | |||
77 | :param levels: The number of levels indent level of the generated code must be decremented. |
||
78 | """ |
||
79 | self.__indent_level -= int(levels) |
||
80 | |||
81 | # ------------------------------------------------------------------------------------------------------------------ |
||
82 | def _write_separator(self) -> None: |
||
83 | """ |
||
84 | Inserts a horizontal (commented) line tot the generated code. |
||
85 | """ |
||
86 | tmp = self._page_width - ((4 * self.__indent_level) + 2) |
||
87 | self._write_line('# ' + ('-' * tmp)) |
||
88 | |||
89 | # ------------------------------------------------------------------------------------------------------------------ |
||
90 | def is_lob_parameter(self, parameters: Dict[str, Any]) -> bool: |
||
91 | """ |
||
92 | Returns True of one of the parameters is a BLOB or CLOB. Otherwise, returns False. |
||
93 | |||
94 | :param parameters: The parameters of a stored routine. |
||
95 | |||
96 | :rtype: bool |
||
97 | """ |
||
98 | raise NotImplementedError() |
||
99 | |||
100 | # ------------------------------------------------------------------------------------------------------------------ |
||
101 | def write_routine_method(self, routine: Dict[str, Any]) -> str: |
||
102 | """ |
||
103 | Returns a complete wrapper method. |
||
104 | |||
105 | :param dict[str,*] routine: The routine metadata. |
||
106 | |||
107 | :rtype: str |
||
108 | """ |
||
109 | if self._lob_as_string_flag: |
||
110 | return self._write_routine_method_without_lob(routine) |
||
111 | else: |
||
112 | if self.is_lob_parameter(routine['parameters']): |
||
113 | return self._write_routine_method_with_lob(routine) |
||
114 | else: |
||
115 | return self._write_routine_method_without_lob(routine) |
||
116 | |||
117 | # ------------------------------------------------------------------------------------------------------------------ |
||
118 | def __write_docstring_description(self, routine: Dict[str, Any]) -> None: |
||
119 | """ |
||
120 | Writes the description part of the docstring for the wrapper method of a stored routine. |
||
121 | |||
122 | :param dict routine: The metadata of the stored routine. |
||
123 | """ |
||
124 | if routine['pydoc']['description']: |
||
125 | self._write_line(routine['pydoc']['description']) |
||
126 | |||
127 | # ------------------------------------------------------------------------------------------------------------------ |
||
128 | def _write_docstring_parameters(self, routine: Dict[str, Any]) -> None: |
||
129 | """ |
||
130 | Writes the parameters part of the docstring for the wrapper method of a stored routine. |
||
131 | |||
132 | :param dict routine: The metadata of the stored routine. |
||
133 | """ |
||
134 | if routine['pydoc']['parameters']: |
||
135 | self._write_line('') |
||
136 | |||
137 | for param in routine['pydoc']['parameters']: |
||
138 | lines = param['description'].split(os.linesep) |
||
139 | self._write_line(':param {0} {1}: {2}'.format(param['python_type'], |
||
140 | param['parameter_name'], |
||
141 | lines[0])) |
||
142 | del lines[0] |
||
143 | |||
144 | tmp = ':param {0} {1}:'.format(param['python_type'], param['parameter_name']) |
||
145 | indent = ' ' * len(tmp) |
||
146 | for line in lines: |
||
147 | self._write_line('{0} {1}'.format(indent, line)) |
||
148 | |||
149 | self._write_line('{0} {1}'.format(indent, param['data_type_descriptor'])) |
||
150 | |||
151 | # ------------------------------------------------------------------------------------------------------------------ |
||
152 | def __write_docstring_return_type(self) -> None: |
||
153 | """ |
||
154 | Writes the return type part of the docstring for the wrapper method of a stored routine. |
||
155 | """ |
||
156 | rtype = self._get_docstring_return_type() |
||
157 | if rtype: |
||
158 | self._write_line('') |
||
159 | self._write_line(':rtype: {0}'.format(rtype)) |
||
160 | |||
161 | # ------------------------------------------------------------------------------------------------------------------ |
||
162 | def __write_docstring(self, routine: Dict[str, Any]) -> None: |
||
163 | """ |
||
164 | Writes the docstring for the wrapper method of a stored routine. |
||
165 | |||
166 | :param dict routine: The metadata of the stored routine. |
||
167 | """ |
||
168 | self._write_line('"""') |
||
169 | |||
170 | self.__write_docstring_description(routine) |
||
171 | self._write_docstring_parameters(routine) |
||
172 | self.__write_docstring_return_type() |
||
173 | |||
174 | self._write_line('"""') |
||
175 | |||
176 | # ------------------------------------------------------------------------------------------------------------------ |
||
177 | @abc.abstractmethod |
||
178 | def _get_docstring_return_type(self) -> str: |
||
179 | """ |
||
180 | Returns the return type of the wrapper method the be used in the docstring. |
||
181 | |||
182 | :rtype: str |
||
183 | """ |
||
184 | |||
185 | # ------------------------------------------------------------------------------------------------------------------ |
||
186 | @abc.abstractmethod |
||
187 | def _return_type_hint(self) -> str: |
||
188 | """ |
||
189 | Returns the return type hint of the wrapper method. |
||
190 | |||
191 | :rtype: str |
||
192 | """ |
||
193 | |||
194 | # ------------------------------------------------------------------------------------------------------------------ |
||
195 | @abc.abstractmethod |
||
196 | def _write_result_handler(self, routine: Dict[str, Any]) -> None: |
||
197 | """ |
||
198 | Generates code for calling the stored routine in the wrapper method. |
||
199 | """ |
||
200 | raise NotImplementedError() |
||
201 | |||
202 | # ------------------------------------------------------------------------------------------------------------------ |
||
203 | def _write_routine_method_with_lob(self, routine: Dict[str, Any]) -> str: |
||
204 | return self._write_routine_method_without_lob(routine) |
||
205 | |||
206 | # ------------------------------------------------------------------------------------------------------------------ |
||
207 | def _write_routine_method_without_lob(self, routine: Dict[str, Any]) -> str: |
||
208 | |||
209 | self._write_line() |
||
210 | self._write_separator() |
||
211 | self._write_line('def {0!s}({1!s}) -> {2!s}:'.format(str(routine['routine_name']), |
||
212 | str(self._get_wrapper_args(routine)), |
||
213 | str(self._return_type_hint()))) |
||
214 | self.__write_docstring(routine) |
||
215 | self._write_result_handler(routine) |
||
216 | |||
217 | return self._code |
||
218 | |||
219 | # ------------------------------------------------------------------------------------------------------------------ |
||
220 | @staticmethod |
||
221 | def _get_wrapper_args(routine: Dict[str, Any]) -> str: |
||
222 | """ |
||
223 | Returns code for the parameters of the wrapper method for the stored routine. |
||
224 | |||
225 | :param dict[str,*] routine: The routine metadata. |
||
226 | |||
227 | :rtype: str |
||
228 | """ |
||
229 | ret = 'self' |
||
230 | |||
231 | for parameter_info in routine['pydoc']['parameters']: |
||
232 | if ret: |
||
233 | ret += ', ' |
||
234 | |||
235 | ret += parameter_info['parameter_name'] |
||
236 | |||
237 | if parameter_info['python_type_hint']: |
||
238 | ret += ': ' + parameter_info['python_type_hint'] |
||
239 | |||
240 | return ret |
||
241 | |||
242 | # ---------------------------------------------------------------------------------------------------------------------- |
||
243 |