%PDF- %PDF-
Direktori : /lib/python3/dist-packages/certbot_apache/_internal/ |
Current File : //lib/python3/dist-packages/certbot_apache/_internal/dualparser.py |
""" Dual ParserNode implementation """ from certbot_apache._internal import apacheparser from certbot_apache._internal import assertions from certbot_apache._internal import augeasparser class DualNodeBase: """ Dual parser interface for in development testing. This is used as the base class for dual parser interface classes. This class handles runtime attribute value assertions.""" def save(self, msg): # pragma: no cover """ Call save for both parsers """ self.primary.save(msg) self.secondary.save(msg) def __getattr__(self, aname): """ Attribute value assertion """ firstval = getattr(self.primary, aname) secondval = getattr(self.secondary, aname) exclusions = [ # Metadata will inherently be different, as ApacheParserNode does # not have Augeas paths and so on. aname == "metadata", callable(firstval) ] if not any(exclusions): assertions.assertEqualSimple(firstval, secondval) return firstval def find_ancestors(self, name): """ Traverses the ancestor tree and returns ancestors matching name """ return self._find_helper(DualBlockNode, "find_ancestors", name) def _find_helper(self, nodeclass, findfunc, search, **kwargs): """A helper for find_* functions. The function specific attributes should be passed as keyword arguments. :param interfaces.ParserNode nodeclass: The node class for results. :param str findfunc: Name of the find function to call :param str search: The search term """ primary_res = getattr(self.primary, findfunc)(search, **kwargs) secondary_res = getattr(self.secondary, findfunc)(search, **kwargs) # The order of search results for Augeas implementation cannot be # assured. pass_primary = assertions.isPassNodeList(primary_res) pass_secondary = assertions.isPassNodeList(secondary_res) new_nodes = [] if pass_primary and pass_secondary: # Both unimplemented new_nodes.append(nodeclass(primary=primary_res[0], secondary=secondary_res[0])) # pragma: no cover elif pass_primary: for c in secondary_res: new_nodes.append(nodeclass(primary=primary_res[0], secondary=c)) elif pass_secondary: for c in primary_res: new_nodes.append(nodeclass(primary=c, secondary=secondary_res[0])) else: assert len(primary_res) == len(secondary_res) matches = self._create_matching_list(primary_res, secondary_res) for p, s in matches: new_nodes.append(nodeclass(primary=p, secondary=s)) return new_nodes class DualCommentNode(DualNodeBase): """ Dual parser implementation of CommentNode interface """ def __init__(self, **kwargs): """ This initialization implementation allows ordinary initialization of CommentNode objects as well as creating a DualCommentNode object using precreated or fetched CommentNode objects if provided as optional arguments primary and secondary. Parameters other than the following are from interfaces.CommentNode: :param CommentNode primary: Primary pre-created CommentNode, mainly used when creating new DualParser nodes using add_* methods. :param CommentNode secondary: Secondary pre-created CommentNode """ kwargs.setdefault("primary", None) kwargs.setdefault("secondary", None) primary = kwargs.pop("primary") secondary = kwargs.pop("secondary") if primary or secondary: assert primary and secondary self.primary = primary self.secondary = secondary else: self.primary = augeasparser.AugeasCommentNode(**kwargs) self.secondary = apacheparser.ApacheCommentNode(**kwargs) assertions.assertEqual(self.primary, self.secondary) class DualDirectiveNode(DualNodeBase): """ Dual parser implementation of DirectiveNode interface """ def __init__(self, **kwargs): """ This initialization implementation allows ordinary initialization of DirectiveNode objects as well as creating a DualDirectiveNode object using precreated or fetched DirectiveNode objects if provided as optional arguments primary and secondary. Parameters other than the following are from interfaces.DirectiveNode: :param DirectiveNode primary: Primary pre-created DirectiveNode, mainly used when creating new DualParser nodes using add_* methods. :param DirectiveNode secondary: Secondary pre-created DirectiveNode """ kwargs.setdefault("primary", None) kwargs.setdefault("secondary", None) primary = kwargs.pop("primary") secondary = kwargs.pop("secondary") if primary or secondary: assert primary and secondary self.primary = primary self.secondary = secondary else: self.primary = augeasparser.AugeasDirectiveNode(**kwargs) self.secondary = apacheparser.ApacheDirectiveNode(**kwargs) assertions.assertEqual(self.primary, self.secondary) def set_parameters(self, parameters): """ Sets parameters and asserts that both implementation successfully set the parameter sequence """ self.primary.set_parameters(parameters) self.secondary.set_parameters(parameters) assertions.assertEqual(self.primary, self.secondary) class DualBlockNode(DualNodeBase): """ Dual parser implementation of BlockNode interface """ def __init__(self, **kwargs): """ This initialization implementation allows ordinary initialization of BlockNode objects as well as creating a DualBlockNode object using precreated or fetched BlockNode objects if provided as optional arguments primary and secondary. Parameters other than the following are from interfaces.BlockNode: :param BlockNode primary: Primary pre-created BlockNode, mainly used when creating new DualParser nodes using add_* methods. :param BlockNode secondary: Secondary pre-created BlockNode """ kwargs.setdefault("primary", None) kwargs.setdefault("secondary", None) primary = kwargs.pop("primary") secondary = kwargs.pop("secondary") if primary or secondary: assert primary and secondary self.primary = primary self.secondary = secondary else: self.primary = augeasparser.AugeasBlockNode(**kwargs) self.secondary = apacheparser.ApacheBlockNode(**kwargs) assertions.assertEqual(self.primary, self.secondary) def add_child_block(self, name, parameters=None, position=None): """ Creates a new child BlockNode, asserts that both implementations did it in a similar way, and returns a newly created DualBlockNode object encapsulating both of the newly created objects """ primary_new = self.primary.add_child_block(name, parameters, position) secondary_new = self.secondary.add_child_block(name, parameters, position) assertions.assertEqual(primary_new, secondary_new) new_block = DualBlockNode(primary=primary_new, secondary=secondary_new) return new_block def add_child_directive(self, name, parameters=None, position=None): """ Creates a new child DirectiveNode, asserts that both implementations did it in a similar way, and returns a newly created DualDirectiveNode object encapsulating both of the newly created objects """ primary_new = self.primary.add_child_directive(name, parameters, position) secondary_new = self.secondary.add_child_directive(name, parameters, position) assertions.assertEqual(primary_new, secondary_new) new_dir = DualDirectiveNode(primary=primary_new, secondary=secondary_new) return new_dir def add_child_comment(self, comment="", position=None): """ Creates a new child CommentNode, asserts that both implementations did it in a similar way, and returns a newly created DualCommentNode object encapsulating both of the newly created objects """ primary_new = self.primary.add_child_comment(comment, position) secondary_new = self.secondary.add_child_comment(comment, position) assertions.assertEqual(primary_new, secondary_new) new_comment = DualCommentNode(primary=primary_new, secondary=secondary_new) return new_comment def _create_matching_list(self, primary_list, secondary_list): """ Matches the list of primary_list to a list of secondary_list and returns a list of tuples. This is used to create results for find_ methods. This helper function exists, because we cannot ensure that the list of search results returned by primary.find_* and secondary.find_* are ordered in a same way. The function pairs the same search results from both implementations to a list of tuples. """ matched = [] for p in primary_list: match = None for s in secondary_list: try: assertions.assertEqual(p, s) match = s break except AssertionError: continue if match: matched.append((p, match)) else: raise AssertionError("Could not find a matching node.") return matched def find_blocks(self, name, exclude=True): """ Performs a search for BlockNodes using both implementations and does simple checks for results. This is built upon the assumption that unimplemented find_* methods return a list with a single assertion passing object. After the assertion, it creates a list of newly created DualBlockNode instances that encapsulate the pairs of returned BlockNode objects. """ return self._find_helper(DualBlockNode, "find_blocks", name, exclude=exclude) def find_directives(self, name, exclude=True): """ Performs a search for DirectiveNodes using both implementations and checks the results. This is built upon the assumption that unimplemented find_* methods return a list with a single assertion passing object. After the assertion, it creates a list of newly created DualDirectiveNode instances that encapsulate the pairs of returned DirectiveNode objects. """ return self._find_helper(DualDirectiveNode, "find_directives", name, exclude=exclude) def find_comments(self, comment): """ Performs a search for CommentNodes using both implementations and checks the results. This is built upon the assumption that unimplemented find_* methods return a list with a single assertion passing object. After the assertion, it creates a list of newly created DualCommentNode instances that encapsulate the pairs of returned CommentNode objects. """ return self._find_helper(DualCommentNode, "find_comments", comment) def delete_child(self, child): """Deletes a child from the ParserNode implementations. The actual ParserNode implementations are used here directly in order to be able to match a child to the list of children.""" self.primary.delete_child(child.primary) self.secondary.delete_child(child.secondary) def unsaved_files(self): """ Fetches the list of unsaved file paths and asserts that the lists match """ primary_files = self.primary.unsaved_files() secondary_files = self.secondary.unsaved_files() assertions.assertEqualSimple(primary_files, secondary_files) return primary_files def parsed_paths(self): """ Returns a list of file paths that have currently been parsed into the parser tree. The returned list may include paths with wildcard characters, for example: ['/etc/apache2/conf.d/*.load'] This is typically called on the root node of the ParserNode tree. :returns: list of file paths of files that have been parsed """ primary_paths = self.primary.parsed_paths() secondary_paths = self.secondary.parsed_paths() assertions.assertEqualPathsList(primary_paths, secondary_paths) return primary_paths