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