def _find_node(self, text): goal = text if not isinstance(text, (tuple, list)): goal = [text] class Search(object): result = None def __call__(self, node): for text in goal: if sys.version_info >= (3, 8) and text in [ 'Num', 'Str', 'NameConstant', 'Ellipsis' ]: text = 'Constant' if str(node).startswith(text): self.result = node break if node.__class__.__name__.startswith(text): self.result = node break return self.result is not None search = Search() ast.call_for_nodes(self.ast, search, recursive=True) return search.result
def _handle(self, node, base_children, eat_parens=False, eat_spaces=False): if hasattr(node, 'region'): # ???: The same node was seen twice; what should we do? warnings.warn( 'Node <%s> has been already patched; please report!' % node.__class__.__name__, RuntimeWarning) return base_children = collections.deque(base_children) self.children_stack.append(base_children) children = collections.deque() formats = [] suspected_start = self.source.offset start = suspected_start first_token = True while base_children: child = base_children.popleft() if child is None: continue offset = self.source.offset if isinstance(child, ast.AST): ast.call_for_nodes(child, self) token_start = child.region[0] else: if child is self.String: region = self.source.consume_string( end=self._find_next_statement_start()) elif child is self.Number: region = self.source.consume_number() elif child == '!=': # INFO: This has been added to handle deprecated ``<>`` region = self.source.consume_not_equal() else: region = self.source.consume(child) child = self.source[region[0]:region[1]] token_start = region[0] if not first_token: formats.append(self.source[offset:token_start]) if self.children: children.append(self.source[offset:token_start]) else: first_token = False start = token_start if self.children: children.append(child) start = self._handle_parens(children, start, formats) if eat_parens: start = self._eat_surrounding_parens( children, suspected_start, start) if eat_spaces: if self.children: children.appendleft(self.source[0:start]) end_spaces = self.source[self.source.offset:] self.source.consume(end_spaces) if self.children: children.append(end_spaces) start = 0 if self.children: node.sorted_children = children node.region = (start, self.source.offset) self.children_stack.pop()
def _handle(self, node, base_children, eat_parens=False, eat_spaces=False): if hasattr(node, 'region'): # ???: The same node was seen twice; what should we do? warnings.warn( 'Node <%s> has been already patched; please report!' % node.__class__.__name__, RuntimeWarning) return base_children = collections.deque(base_children) self.children_stack.append(base_children) children = collections.deque() formats = [] suspected_start = self.source.offset start = suspected_start first_token = True while base_children: child = base_children.popleft() if child is None: continue offset = self.source.offset if isinstance(child, ast.AST): ast.call_for_nodes(child, self) token_start = child.region[0] else: if child is self.String: region = self.source.consume_string( end=self._find_next_statement_start()) elif child is self.Number: region = self.source.consume_number() elif child == '!=': # INFO: This has been added to handle deprecated ``<>`` region = self.source.consume_not_equal() else: region = self.source.consume(child) child = self.source[region[0]:region[1]] token_start = region[0] if not first_token: formats.append(self.source[offset:token_start]) if self.children: children.append(self.source[offset:token_start]) else: first_token = False start = token_start if self.children: children.append(child) start = self._handle_parens(children, start, formats) if eat_parens: start = self._eat_surrounding_parens(children, suspected_start, start) if eat_spaces: if self.children: children.appendleft(self.source[0:start]) end_spaces = self.source[self.source.offset:] self.source.consume(end_spaces) if self.children: children.append(end_spaces) start = 0 if self.children: node.sorted_children = children node.region = (start, self.source.offset) self.children_stack.pop()
def patch_ast(node, source, sorted_children=False): """Patches the given node After calling, each node in `node` will have a new field named `region` that is a tuple containing the start and end offsets of the code that generated it. If `sorted_children` is true, a `sorted_children` field will be created for each node, too. It is a list containing child nodes as well as whitespaces and comments that occur between them. """ if hasattr(node, 'region'): return node walker = _PatchingASTWalker(source, children=sorted_children) ast.call_for_nodes(node, walker) return node
def _find_node(self, text): goal = text if not isinstance(text, (tuple, list)): goal = [text] class Search(object): result = None def __call__(self, node): for text in goal: if str(node).startswith(text): self.result = node break if node.__class__.__name__.startswith(text): self.result = node break return self.result is not None search = Search() ast.call_for_nodes(self.ast, search, recursive=True) return search.result
def find_matches(self): if self.matches is None: self.matches = [] ast.call_for_nodes(self.body, self._check_node, recursive=True) return self.matches
def _handle(self, node, base_children, eat_parens=False, eat_spaces=False): if hasattr(node, "region"): # ???: The same node was seen twice; what should we do? warnings.warn( "Node <%s> has been already patched; please report!" % node.__class__.__name__, RuntimeWarning, ) return base_children = collections.deque(base_children) self.children_stack.append(base_children) children = collections.deque() formats = [] suspected_start = self.source.offset start = suspected_start first_token = True while base_children: child = base_children.popleft() if child is None: continue offset = self.source.offset if isinstance(child, ast.AST): ast.call_for_nodes(child, self) token_start = child.region[0] else: if child is self.String: region = self.source.consume_string( end=self._find_next_statement_start() ) elif child is self.Number: region = self.source.consume_number() elif child == "!=": # INFO: This has been added to handle deprecated ``<>`` region = self.source.consume_not_equal() elif child == self.semicolon_or_as_in_except: # INFO: This has been added to handle deprecated # semicolon in except region = self.source.consume_except_as_or_semicolon() elif child == self.exec_open_paren_or_space: # These three cases handle the differences between # the deprecated exec statement and the exec # function. region = self.source.consume_exec_open_paren_or_space() elif child == self.exec_in_or_comma: region = self.source.consume_exec_in_or_comma() elif child == self.exec_close_paren_or_space: region = self.source.consume_exec_close_paren_or_space() elif child == self.with_or_comma_context_manager: region = self.source.consume_with_or_comma_context_manager() else: if hasattr(ast, "JoinedStr") and isinstance( node, (ast.JoinedStr, ast.FormattedValue) ): region = self.source.consume_joined_string(child) else: region = self.source.consume(child) child = self.source[region[0] : region[1]] token_start = region[0] if not first_token: formats.append(self.source[offset:token_start]) if self.children: children.append(self.source[offset:token_start]) else: first_token = False start = token_start if self.children: children.append(child) start = self._handle_parens(children, start, formats) if eat_parens: start = self._eat_surrounding_parens(children, suspected_start, start) if eat_spaces: if self.children: children.appendleft(self.source[0:start]) end_spaces = self.source[self.source.offset :] self.source.consume(end_spaces) if self.children: children.append(end_spaces) start = 0 if self.children: node.sorted_children = children node.region = (start, self.source.offset) self.children_stack.pop()