Skip to content

Commit 847c23c

Browse files
authored
Merge pull request #113 from galaxyproject/fix_#108
Display error notification when a code generation command fails
2 parents d39b36b + 5ebd498 commit 847c23c

File tree

6 files changed

+85
-54
lines changed

6 files changed

+85
-54
lines changed

client/src/commands.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ namespace SortDocumentParamsAttrsCommandRequest {
3232
}
3333

3434
interface GeneratedSnippetResult {
35-
snippet: string,
36-
position: Position
37-
replace_range: Range | null
35+
snippet: string;
36+
position: Position;
37+
replace_range: Range | null;
38+
error_message: string;
3839
}
3940

4041
interface ReplaceTextRangeResult {
41-
text: string,
42-
replace_range: Range
42+
text: string;
43+
replace_range: Range;
4344
}
4445

4546

@@ -136,7 +137,12 @@ async function requestInsertSnippet(client: LanguageClient, request: RequestType
136137

137138
let param = client.code2ProtocolConverter.asTextDocumentIdentifier(document);
138139
let response = await client.sendRequest(request, param);
139-
if (!response || !response.snippet) return;
140+
if (!response || !response.snippet || response.error_message) {
141+
if (response.error_message) {
142+
window.showErrorMessage(response.error_message);
143+
}
144+
return;
145+
}
140146

141147
try {
142148
const snippet = new SnippetString(response.snippet);

server/galaxyls/services/tools/generators/command.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List, Optional, Union, cast
1+
from typing import List, Optional, Tuple, Union, cast
22

33
from anytree import PreOrderIter
44
from galaxyls.services.tools.constants import (
@@ -44,22 +44,26 @@ class GalaxyToolCommandSnippetGenerator(SnippetGenerator):
4444
def __init__(self, tool_document: GalaxyToolXmlDocument, tabSize: int = 4) -> None:
4545
super().__init__(tool_document, tabSize)
4646

47-
def _build_snippet(self) -> Optional[str]:
47+
def _build_snippet(self) -> Tuple[str, bool]:
4848
"""This function tries to generate a code snippet in TextMate format with boilerplate
4949
Cheetah code extracted from the inputs and outputs of the tool.
5050
5151
Returns:
52-
Optional[str]: The code snippet in TextMate format or None if the generation failed.
52+
Tuple[str, bool]: The code snippet in TextMate format or an error message if the
53+
generation failed. The second value of the tuple indicates if it is an error.
5354
"""
54-
input_tree = self.expanded_document.analyze_inputs()
55-
outputs = self.expanded_document.get_outputs()
56-
result_snippet = self._generate_command_snippet(input_tree, outputs)
57-
command_section = self.tool_document.find_element(COMMAND)
58-
if command_section and not command_section.is_self_closed:
59-
if command_section.get_cdata_section():
60-
return result_snippet
61-
return f"<![CDATA[\n\n{result_snippet}\n\n]]>\n"
62-
return f"<{COMMAND}><![CDATA[\n\n{result_snippet}\n\n]]>\n</{COMMAND}>\n"
55+
try:
56+
input_tree = self.expanded_document.analyze_inputs()
57+
outputs = self.expanded_document.get_outputs()
58+
result_snippet = self._generate_command_snippet(input_tree, outputs)
59+
command_section = self.tool_document.find_element(COMMAND)
60+
if command_section and not command_section.is_self_closed:
61+
if command_section.get_cdata_section():
62+
return (result_snippet, False)
63+
return (f"<![CDATA[\n\n{result_snippet}\n\n]]>\n", False)
64+
return (f"<{COMMAND}><![CDATA[\n\n{result_snippet}\n\n]]>\n</{COMMAND}>\n", False)
65+
except BaseException as ex:
66+
return (f"Automatic command section generation failed with reason: {ex}", True)
6367

6468
def _find_snippet_insert_position(self) -> Union[Position, Range]:
6569
"""Returns the position inside the document where command section

server/galaxyls/services/tools/generators/snippets.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from abc import ABC, abstractmethod
2-
from typing import List, Optional, Union, cast
2+
from typing import List, Optional, Tuple, Union, cast
33

44
from galaxy.util import xml_macros
55
from galaxyls.services.tools.constants import DASH, UNDERSCORE
@@ -21,22 +21,24 @@ def __init__(self, tool_document: GalaxyToolXmlDocument, tabSize: int = 4) -> No
2121
self.indent_spaces: str = " " * tabSize
2222
super().__init__()
2323

24-
def generate_snippet(self) -> Optional[GeneratedSnippetResult]:
24+
def generate_snippet(self) -> GeneratedSnippetResult:
2525
"""Generates a code snippet using this generator."""
26-
snippet = self._build_snippet()
27-
if snippet:
28-
insert_position = self._find_snippet_insert_position()
29-
if type(insert_position) == Range:
30-
insert_position = cast(Range, insert_position)
31-
return GeneratedSnippetResult(snippet, insert_position.start, insert_position)
32-
insert_position = cast(Position, insert_position)
33-
return GeneratedSnippetResult(snippet, insert_position)
34-
return None
26+
result, is_error = self._build_snippet()
27+
if is_error:
28+
return GeneratedSnippetResult.as_error(result)
29+
insert_position = self._find_snippet_insert_position()
30+
if type(insert_position) == Range:
31+
insert_position = cast(Range, insert_position)
32+
return GeneratedSnippetResult(result, insert_position.start, insert_position)
33+
insert_position = cast(Position, insert_position)
34+
return GeneratedSnippetResult(result, insert_position)
3535

3636
@abstractmethod
37-
def _build_snippet(self) -> Optional[str]:
38-
"""This abstract function should return the generated snippet in TextMate format or None
39-
if the snippet can't be generated."""
37+
def _build_snippet(self) -> Tuple[str, bool]:
38+
"""This abstract function should return a tuple with the generated snippet text in TextMate format or
39+
an error message if the snippet can't be generated.
40+
41+
The second value of the tuple is a bool indicating if there was an error."""
4042
pass
4143

4244
@abstractmethod

server/galaxyls/services/tools/generators/tests.py

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List, Optional, Union, cast
1+
from typing import List, Optional, Tuple, Union, cast
22

33
from galaxyls.services.tools.constants import (
44
ARGUMENT,
@@ -58,20 +58,26 @@ class GalaxyToolTestSnippetGenerator(SnippetGenerator):
5858
def __init__(self, tool_document: GalaxyToolXmlDocument, tabSize: int = 4) -> None:
5959
super().__init__(tool_document, tabSize)
6060

61-
def _build_snippet(self) -> Optional[str]:
61+
def _build_snippet(self) -> Tuple[str, bool]:
6262
"""This function tries to generate a code snippet in TextMate format with all the tests cases extracted
6363
from the inputs and outputs of the tool.
6464
6565
Returns:
66-
Optional[str]: The code snippet in TextMate format or None if the generation failed.
66+
Tuple[str, bool]: The code snippet in TextMate format or an error message if the generation failed.
67+
The second value of the tuple indicates if there was an error.
6768
"""
68-
input_tree = self.expanded_document.analyze_inputs()
69-
outputs = self.expanded_document.get_outputs()
70-
result_snippet = "\n".join((self._generate_test_case_snippet(input_node, outputs) for input_node in input_tree.leaves))
71-
tests_section = self.tool_document.find_element(TESTS)
72-
if tests_section and not tests_section.is_self_closed:
73-
return result_snippet
74-
return f"\n<{TESTS}>\n{result_snippet}\n</{TESTS}>"
69+
try:
70+
input_tree = self.expanded_document.analyze_inputs()
71+
outputs = self.expanded_document.get_outputs()
72+
result_snippet = "\n".join(
73+
(self._generate_test_case_snippet(input_node, outputs) for input_node in input_tree.leaves)
74+
)
75+
tests_section = self.tool_document.find_element(TESTS)
76+
if tests_section and not tests_section.is_self_closed:
77+
return (result_snippet, False)
78+
return (f"\n<{TESTS}>\n{result_snippet}\n</{TESTS}>", False)
79+
except BaseException as ex:
80+
return (f"Automatic Test Case generation failed with reason: {ex}", True)
7581

7682
def _find_snippet_insert_position(self) -> Union[Position, Range]:
7783
"""Returns the position inside the document where new test cases
@@ -118,15 +124,12 @@ def _generate_test_case_snippet(self, input_node: InputNode, outputs: List[XmlEl
118124
Returns:
119125
str: The resulting code snippet in TextMate format.
120126
"""
121-
try:
122-
test_element = self._create_test_element()
123-
self._add_inputs_to_test_element(input_node, test_element)
124-
self._add_outputs_to_test_element(outputs, test_element)
125-
etree.indent(test_element, space=self.indent_spaces)
126-
snippet = etree.tostring(test_element, pretty_print=True, encoding=str)
127-
return cast(str, snippet)
128-
except BaseException:
129-
return ""
127+
test_element = self._create_test_element()
128+
self._add_inputs_to_test_element(input_node, test_element)
129+
self._add_outputs_to_test_element(outputs, test_element)
130+
etree.indent(test_element, space=self.indent_spaces)
131+
snippet = etree.tostring(test_element, pretty_print=True, encoding=str)
132+
return cast(str, snippet)
130133

131134
def _create_test_element(self) -> etree._Element:
132135
"""Returns a XML element representing a <test> tag with the basic information.

server/galaxyls/tests/unit/test_tools.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,10 @@ def test_build_snippet_returns_expected_result(self, tool_file: str, expected_sn
167167
tool = GalaxyToolXmlDocument(document)
168168
generator = GalaxyToolTestSnippetGenerator(tool)
169169

170-
actual_snippet = generator._build_snippet()
170+
actual_snippet, is_error = generator._build_snippet()
171171
print(actual_snippet)
172172
assert actual_snippet == expected_snippet
173+
assert not is_error
173174

174175
@pytest.mark.parametrize(
175176
"source, expected_position",
@@ -229,9 +230,10 @@ def test_build_snippet_returns_expected_result(self, tool_file: str, expected_sn
229230
tool = GalaxyToolXmlDocument(document)
230231
generator = GalaxyToolCommandSnippetGenerator(tool)
231232

232-
actual_snippet = generator._build_snippet()
233+
actual_snippet, is_error = generator._build_snippet()
233234
print(actual_snippet)
234235
assert actual_snippet == expected_snippet
236+
assert not is_error
235237

236238
@pytest.mark.parametrize(
237239
"source, expected_position",

server/galaxyls/types.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,24 @@ class GeneratedSnippetResult:
2323
text inside the given range.
2424
"""
2525

26-
def __init__(self, snippet: str, insert_position: Position, replace_range: Optional[Range] = None):
26+
def __init__(
27+
self,
28+
snippet: str,
29+
insert_position: Position,
30+
replace_range: Optional[Range] = None,
31+
error_message: Optional[str] = None,
32+
):
2733
self.snippet = snippet
2834
self.position = insert_position
2935
self.replace_range = replace_range
36+
self.error_message = error_message
37+
38+
@staticmethod
39+
def as_error(error_message: str) -> "GeneratedSnippetResult":
40+
"""Returns a GeneratedSnippetResult with empty values and the given error message.
41+
42+
The error message should be displayed or logged by the client."""
43+
return GeneratedSnippetResult("", Position(), error_message=error_message)
3044

3145

3246
class ReplaceTextRangeResult:

0 commit comments

Comments
 (0)