ยปCore Development>Code coverage>Lib/lib2to3/fixer_base.py

Python code coverage for Lib/lib2to3/fixer_base.py

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