def test_find_tests_section_without_section_returns_none(self) -> None: document = TestUtils.to_document("<tool></tool>") tool = GalaxyToolXmlDocument(document) actual = tool.find_element("notexistent") assert actual is None
def test_get_element_content_range_of_element_returns_expected(self, source: str, element: str, expected: Range) -> None: document = TestUtils.to_document(source) tool = GalaxyToolXmlDocument(document) node = tool.find_element(element) actual = tool.get_element_content_range(node) assert actual == expected
def test_get_element_content_range_of_unknown_element_returns_none(self) -> None: document = TestUtils.to_document("<tool><tests></tests></tool>") tool = GalaxyToolXmlDocument(document) node = tool.find_element("unknown") actual = tool.get_element_content_range(node) assert actual is None
def test_find_tests_section_returns_expected(self) -> None: document = TestUtils.to_document("<tool><tests></tests></tool>") tool = GalaxyToolXmlDocument(document) actual = tool.find_element("tests") assert actual assert actual.name == "tests"
def test_get_position_after_element_returns_expected_position( self, source: str, element_name: str, expected_position: Position ) -> None: document = TestUtils.to_document(source) tool = GalaxyToolXmlDocument(document) element = tool.find_element(element_name, maxlevel=4) assert element is not None actual_position = tool.get_position_after(element) assert actual_position == expected_position
def get_macro_names(self, tool_xml: XmlDocument) -> Set[str]: tool = GalaxyToolXmlDocument.from_xml_document(tool_xml) macros = self._get_macro_definitions(tool_xml) imported_macro_files = self._get_imported_macro_files_from_tool(tool) for file in imported_macro_files.values(): macros.update(file.macros) return set(macros.keys())
def validate_document(self, xml_document: XmlDocument) -> List[Diagnostic]: """Validates the XML document and returns a list of diagnostics if there are any problems. Args: xml_document (XmlDocument): The XML document. Can be a tool wrapper or macro definition file. Returns: List[Diagnostic]: The list of issues found in the document. """ syntax_errors = self._check_syntax(xml_document.document) if syntax_errors: return syntax_errors if not xml_document.is_tool_file: return [] tool = GalaxyToolXmlDocument.from_xml_document(xml_document) try: xml_tree = etree.fromstring(tool.source) return self._validate_tree(xml_tree) except ExpandMacrosFoundException: result = self._validate_expanded(tool) return result except etree.XMLSyntaxError as e: return self._build_diagnostics_from_syntax_error(e)
def _build_diagnostics_for_expanded_macros( self, tool: GalaxyToolXmlDocument, invalid_document_error) -> List[Diagnostic]: virtual_uri = tool.xml_document.document.uri.replace( "file", "gls-expand") diagnostics = [ Diagnostic( range=tool.get_macros_range(), message=error.message, source=self.server_name, code=DiagnosticCodes.INVALID_EXPANDED_TOOL, related_information=[ DiagnosticRelatedInformation( message= ("The validation error ocurred on the expanded version of " "the document, i.e. after replacing macros. " "Click here to preview the expanded document."), location=Location( uri=f"{virtual_uri}{EXPAND_DOCUMENT_URI_SUFFIX}", range=Range( start=Position(line=error.line - 1, character=error.column), end=Position(line=error.line - 1, character=error.column), ), ), ) ], ) for error in invalid_document_error.error_log.filter_from_errors() ] return diagnostics
def generate_command( self, document: Document) -> Optional[GeneratedSnippetResult]: """Generates a boilerplate Cheetah code snippet based on the current inputs and outputs of this tool wrapper.""" tool = GalaxyToolXmlDocument(document) generator = GalaxyToolCommandSnippetGenerator(tool) return generator.generate_snippet()
def generate_tests(self, document: Document) -> Optional[GeneratedSnippetResult]: """Generates a code snippet with some tests for the current inputs and outputs of this tool wrapper.""" tool = GalaxyToolXmlDocument(document) generator = GalaxyToolTestSnippetGenerator(tool) return generator.generate_snippet()
def _get_test_suite_from_document( self, xml_document: XmlDocument) -> Optional[TestSuiteInfoResult]: tool = GalaxyToolXmlDocument.from_xml_document(xml_document) tool_id = tool.get_tool_id() tests_range = tool.get_tests_range() if tool_id and tests_range: tests = tool.get_tests() test_cases: List[TestInfoResult] = [] id = 1 for test in tests: range = xml_document.get_full_range(test) if range: test_cases.append( TestInfoResult( tool_id=tool_id, test_id=str(id), uri=xml_document.document.uri, range=range, ), ) id += 1 return TestSuiteInfoResult( tool_id=tool_id, uri=xml_document.document.uri, range=tests_range, children=test_cases, ) return None
def test_generate_snippet_with_tests_section_returns_snippet_only(self) -> None: document = TestUtils.to_document("<tool><tests></tests></tool>") tool = GalaxyToolXmlDocument(document) generator = GalaxyToolTestSnippetGenerator(tool) result = generator.generate_snippet() assert "<tests>" not in result.snippet
def test_find_snippet_position_returns_expected_result(self, source: str, expected_position: Position) -> None: document = TestUtils.to_document(source) tool = GalaxyToolXmlDocument(document) generator = GalaxyToolCommandSnippetGenerator(tool) actual_position = generator._find_snippet_insert_position() assert actual_position == expected_position
def test_generate_snippet_with_command_with_cdata_returns_snippet_only(self) -> None: document = TestUtils.to_document("<tool><command><![CDATA[]]></command></tool>") tool = GalaxyToolXmlDocument(document) generator = GalaxyToolCommandSnippetGenerator(tool) result = generator.generate_snippet() assert "<command" not in result.snippet assert "<![CDATA[" not in result.snippet
def test_build_snippet_returns_expected_result(self, tool_file: str, expected_snippet_file: str) -> None: document = TestUtils.get_test_document_from_file(tool_file) expected_snippet = TestUtils.get_test_file_contents(expected_snippet_file) tool = GalaxyToolXmlDocument(document) generator = GalaxyToolCommandSnippetGenerator(tool) actual_snippet = generator._build_snippet() assert actual_snippet == expected_snippet
def _edit_create_import_macros_section(self, tool: GalaxyToolXmlDocument, macros_file_name: str) -> TextEdit: """Returns the TextEdit operation that will add a macros file <import> definition to the existing <macros> section of a tool wrapper or also create the <macros> section if it doesn't exists.""" macros_element = tool.find_element(MACROS) if macros_element: insert_position = tool.get_position_before_first_child( macros_element) macro_xml = f"<import>{macros_file_name}</import>" else: insert_position = self._find_macros_insert_position(tool) macro_xml = f"<macros>\n<import>{macros_file_name}</import>\n</macros>" insert_range = Range(start=insert_position, end=insert_position) final_macro_xml = self._adapt_format(tool.xml_document, insert_range, macro_xml) return TextEdit( range=insert_range, new_text=final_macro_xml, )
def _edit_add_macro_to_macros_section(self, tool: GalaxyToolXmlDocument, macros_element: XmlElement, macro: MacroData) -> TextEdit: """Returns the TextEdit operation that will add a macro definition to the <macros> section of a tool wrapper.""" insert_position = tool.get_position_after_last_child(macros_element) insert_range = Range(start=insert_position, end=insert_position) macro_xml = f'<xml name="{macro.name}">\n{macro.content}\n</xml>' final_macro_xml = self._adapt_format(tool.xml_document, insert_range, macro_xml) return TextEdit( range=insert_range, new_text=final_macro_xml, )
def _get_expanded_tool_document(self, tool_document: GalaxyToolXmlDocument) -> GalaxyToolXmlDocument: """If the given tool document uses macros, a new tool document with the expanded macros is returned, otherwise, the same document is returned. """ if tool_document.uses_macros: try: document = tool_document.document expanded_tool_tree, _ = xml_macros.load_with_references(document.path) expanded_tool_tree = cast(etree._ElementTree, expanded_tool_tree) expanded_source = etree.tostring(expanded_tool_tree, encoding=str) expanded_document = Document(uri=document.uri, source=expanded_source, version=document.version) return GalaxyToolXmlDocument(expanded_document) except BaseException: return tool_document return tool_document
def load_macro_definitions(self, tool_xml: XmlDocument) -> ToolMacroDefinitions: tool = GalaxyToolXmlDocument.from_xml_document(tool_xml) tokens = self._get_token_definitions(tool_xml) macros = self._get_macro_definitions(tool_xml) imported_macro_files = self._get_imported_macro_files_from_tool(tool) for file in imported_macro_files.values(): tokens.update(file.tokens) macros.update(file.macros) return ToolMacroDefinitions( tool_document=tool_xml, imported_macros=imported_macro_files, tokens=tokens, macros=macros, )
def _get_imported_macro_files_from_tool( self, tool: GalaxyToolXmlDocument) -> Dict[str, ImportedMacrosFile]: macro_files = {} uris_dict = tool.get_macro_import_uris() for file_name, file_uri in uris_dict.items(): macros_document = self._load_macros_document(file_uri) tokens = self._get_token_definitions(macros_document) macros = self._get_macro_definitions(macros_document) macro_files[file_name] = ImportedMacrosFile( file_name=file_name, file_uri=file_uri, document=macros_document, tokens=tokens, macros=macros, ) return macro_files
def _calculate_local_changes_for_macro( self, tool: GalaxyToolXmlDocument, macro: MacroData, params: CodeActionParams) -> Dict[str, List[TextEdit]]: """Returns a dictionary with the file uri and the TextEdit operations that will add a macro definition to the <macros> section of a tool wrapper. If the <macros> section doesn't exists it will also be created.""" macros_element = tool.get_macros_element() edits: List[TextEdit] = [] if macros_element is None: edits.append(self._edit_create_with_macros_section(tool, macro)) else: edits.append( self._edit_add_macro_to_macros_section(tool, macros_element, macro)) edits.append( self._edit_replace_range_with_macro_expand(tool, macro, params.range)) changes = {params.text_document.uri: edits} return changes
def get_available_refactoring_actions( self, xml_document: XmlDocument, params: CodeActionParams) -> List[CodeAction]: """Gets a collection of possible refactoring code actions on a selected chunk of the document.""" code_actions = [] text_in_range = xml_document.get_text_in_range(params.range) target_element_tag = self._get_valid_full_element_tag(text_in_range) if target_element_tag is not None: macro = MacroData(name=target_element_tag, content=text_in_range.strip()) macro_definitions = self.macros.definitions_provider.load_macro_definitions( xml_document) tool = GalaxyToolXmlDocument.from_xml_document(xml_document) code_actions.extend( self.macros.create_extract_to_macros_file_actions( tool, macro_definitions, macro, params)) code_actions.extend( self.macros.create_extract_to_local_macro_actions( tool, macro, params)) return code_actions
def _build_diagnostics_for_macros_file_syntax_error( self, tool: GalaxyToolXmlDocument, syntax_error) -> List[Diagnostic]: result = Diagnostic( range=tool.get_import_macro_file_range(syntax_error.filename), message=syntax_error.msg, source=self.server_name, related_information=[ DiagnosticRelatedInformation( message="Syntax error found on imported file.", location=Location( uri=Path(syntax_error.filename).as_uri(), range=Range( start=Position(line=syntax_error.lineno - 1, character=syntax_error.offset), end=Position(line=syntax_error.lineno - 1, character=syntax_error.offset), ), ), ) ], ) return [result]
def _find_macros_insert_position(self, tool: GalaxyToolXmlDocument) -> Position: """Returns the position inside the document where the macros section can be inserted. Returns: Range: The position where the macros section can be inserted in the document. """ section = tool.find_element(XREF) if section: return tool.get_position_after(section) section = tool.find_element(DESCRIPTION) if section: return tool.get_position_after(section) root = tool.find_element(TOOL) content_range = tool.get_content_range(root) if content_range: return content_range.start return Position(line=0, character=0)
def test_analyze_inputs_returns_expected_number_of_leaves(self) -> None: tool = GalaxyToolXmlDocument(TEST_TOOL_WITH_INPUTS_DOCUMENT) result = tool.analyze_inputs() assert len(result.leaves) == 3
def test_init_sets_properties(self) -> None: document = TestUtils.to_document("<tool></tool>") tool = GalaxyToolXmlDocument(document) assert not tool.xml_document.is_empty
def test_uses_macros_returns_expected(self, source: str, expected: bool) -> None: document = TestUtils.to_document(source) tool = GalaxyToolXmlDocument(document) assert tool.uses_macros == expected
def test_is_valid(self, source: str, expected: bool) -> None: document = TestUtils.to_document(source) tool = GalaxyToolXmlDocument(document) assert tool.is_valid == expected