1ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Copyright 2006 Google, Inc. All Rights Reserved. 2ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Licensed to PSF under a Contributor Agreement. 3ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 4ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh"""Base class for fixers (optional, but recommended).""" 5ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 6ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Python imports 7ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport logging 8ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehimport itertools 9ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 10ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh# Local imports 11ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom .patcomp import PatternCompiler 12ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom . import pygram 13ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehfrom .fixer_util import does_tree_import 14ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 15ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass BaseFix(object): 16ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 17ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Optional base class for fixers. 18ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 19ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh The subclass name must be FixFooBar where FooBar is the result of 20ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh removing underscores and capitalizing the words of the fix name. 21ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh For example, the class name for a fixer named 'has_key' should be 22ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh FixHasKey. 23ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 24ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 25ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh PATTERN = None # Most subclasses should override with a string literal 26ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh pattern = None # Compiled pattern, set by compile_pattern() 27ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh pattern_tree = None # Tree representation of the pattern 28ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh options = None # Options object passed to initializer 29ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh filename = None # The filename (set by set_filename) 30ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh logger = None # A logger (set by set_filename) 31ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh numbers = itertools.count(1) # For new_name() 32ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh used_names = set() # A set of all used NAMEs 33ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh order = "post" # Does the fixer prefer pre- or post-order traversal 34ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh explicit = False # Is this ignored by refactor.py -f all? 35ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh run_order = 5 # Fixers will be sorted by run order before execution 36ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # Lower numbers will be run first. 37ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh _accept_type = None # [Advanced and not public] This tells RefactoringTool 38ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # which node type to accept when there's not a pattern. 39ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 40ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh keep_line_order = False # For the bottom matcher: match with the 41ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # original line order 42ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh BM_compatible = False # Compatibility with the bottom matching 43ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # module; every fixer should set this 44ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # manually 45ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 46ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # Shortcut for access to Python grammar symbols 47ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh syms = pygram.python_symbols 48ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 49ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def __init__(self, options, log): 50ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Initializer. Subclass may override. 51ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 52ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Args: 53ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh options: an dict containing the options passed to RefactoringTool 54ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh that could be used to customize the fixer through the command line. 55ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh log: a list to append warnings and other messages to. 56ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 57ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.options = options 58ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.log = log 59ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.compile_pattern() 60ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 61ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def compile_pattern(self): 62ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Compiles self.PATTERN into self.pattern. 63ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 64ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Subclass may override if it doesn't want to use 65ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.{pattern,PATTERN} in .match(). 66ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 67ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if self.PATTERN is not None: 68ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh PC = PatternCompiler() 69ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.pattern, self.pattern_tree = PC.compile_pattern(self.PATTERN, 70ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh with_tree=True) 71ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 72ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def set_filename(self, filename): 73ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Set the filename, and a logger derived from it. 74ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 75ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh The main refactoring tool should call this. 76ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 77ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.filename = filename 78ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.logger = logging.getLogger(filename) 79ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 80ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def match(self, node): 81ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Returns match for a given parse tree node. 82ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 83ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Should return a true or false object (not necessarily a bool). 84ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh It may return a non-empty dict of matching sub-nodes as 85ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh returned by a matching pattern. 86ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 87ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Subclass may override. 88ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 89ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh results = {"node": node} 90ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return self.pattern.match(node, results) and results 91ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 92ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def transform(self, node, results): 93ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Returns the transformation for a given parse tree node. 94ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 95ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Args: 96ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh node: the root of the parse tree that matched the fixer. 97ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh results: a dict mapping symbolic names to part of the match. 98ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 99ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Returns: 100ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh None, or a node that is a modified copy of the 101ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh argument node. The node argument may also be modified in-place to 102ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh effect the same change. 103ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 104ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Subclass *must* override. 105ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 106ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh raise NotImplementedError() 107ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 108ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def new_name(self, template=u"xxx_todo_changeme"): 109ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Return a string suitable for use as an identifier 110ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 111ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh The new name is guaranteed not to conflict with other identifiers. 112ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 113ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh name = template 114ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh while name in self.used_names: 115ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh name = template + unicode(self.numbers.next()) 116ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.used_names.add(name) 117ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return name 118ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 119ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def log_message(self, message): 120ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if self.first_log: 121ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.first_log = False 122ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.log.append("### In file %s ###" % self.filename) 123ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.log.append(message) 124ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 125ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def cannot_convert(self, node, reason=None): 126ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Warn the user that a given chunk of code is not valid Python 3, 127ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh but that it cannot be converted automatically. 128ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 129ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh First argument is the top-level node for the code in question. 130ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Optional second argument is why it can't be converted. 131ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 132ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh lineno = node.get_lineno() 133ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for_output = node.clone() 134ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh for_output.prefix = u"" 135ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh msg = "Line %d: could not convert: %s" 136ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.log_message(msg % (lineno, for_output)) 137ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if reason: 138ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.log_message(reason) 139ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 140ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def warning(self, node, reason): 141ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Used for warning the user about possible uncertainty in the 142ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh translation. 143ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 144ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh First argument is the top-level node for the code in question. 145ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh Optional second argument is why it can't be converted. 146ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 147ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh lineno = node.get_lineno() 148ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.log_message("Line %d: %s" % (lineno, reason)) 149ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 150ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def start_tree(self, tree, filename): 151ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Some fixers need to maintain tree-wide state. 152ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh This method is called once, at the start of tree fix-up. 153ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 154ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh tree - the root node of the tree to be processed. 155ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh filename - the name of the file the tree came from. 156ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 157ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.used_names = tree.used_names 158ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.set_filename(filename) 159ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.numbers = itertools.count(1) 160ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self.first_log = True 161ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 162ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def finish_tree(self, tree, filename): 163ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """Some fixers need to maintain tree-wide state. 164ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh This method is called once, at the conclusion of tree fix-up. 165ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 166ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh tree - the root node of the tree to be processed. 167ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh filename - the name of the file the tree came from. 168ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ 169ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh pass 170ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 171ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 172ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsiehclass ConditionalFix(BaseFix): 173ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh """ Base class for fixers which not execute if an import is found. """ 174ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 175ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh # This is the name of the import which, if found, will cause the test to be skipped 176ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh skip_on = None 177ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 178ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def start_tree(self, *args): 179ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh super(ConditionalFix, self).start_tree(*args) 180ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self._should_skip = None 181ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh 182ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh def should_skip(self, node): 183ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh if self._should_skip is not None: 184ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return self._should_skip 185ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh pkg = self.skip_on.split(".") 186ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh name = pkg[-1] 187ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh pkg = ".".join(pkg[:-1]) 188ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh self._should_skip = does_tree_import(pkg, name, node) 189ffab958fd8d42ed7227d83007350e61555a1fa36Andrew Hsieh return self._should_skip 190