~pythonregexp2.7/python/issue2636-19

« back to all changes in this revision

Viewing changes to Lib/lib2to3/fixer_util.py

  • Committer: Jeffrey C. "The TimeHorse" Jacobs
  • Date: 2008-09-21 13:47:31 UTC
  • mfrom: (39021.1.404 Regexp-2.7)
  • Revision ID: darklord@timehorse.com-20080921134731-rudomuzeh1b2tz1y
Merged in changes from the latest python source snapshot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""Utility functions, node construction macros, etc."""
 
2
# Author: Collin Winter
 
3
 
 
4
# Local imports
 
5
from .pgen2 import token
 
6
from .pytree import Leaf, Node
 
7
from .pygram import python_symbols as syms
 
8
from . import patcomp
 
9
 
 
10
 
 
11
###########################################################
 
12
### Common node-construction "macros"
 
13
###########################################################
 
14
 
 
15
def KeywordArg(keyword, value):
 
16
    return Node(syms.argument,
 
17
                [keyword, Leaf(token.EQUAL, '='), value])
 
18
 
 
19
def LParen():
 
20
    return Leaf(token.LPAR, "(")
 
21
 
 
22
def RParen():
 
23
    return Leaf(token.RPAR, ")")
 
24
 
 
25
def Assign(target, source):
 
26
    """Build an assignment statement"""
 
27
    if not isinstance(target, list):
 
28
        target = [target]
 
29
    if not isinstance(source, list):
 
30
        source.set_prefix(" ")
 
31
        source = [source]
 
32
 
 
33
    return Node(syms.atom,
 
34
                target + [Leaf(token.EQUAL, "=", prefix=" ")] + source)
 
35
 
 
36
def Name(name, prefix=None):
 
37
    """Return a NAME leaf"""
 
38
    return Leaf(token.NAME, name, prefix=prefix)
 
39
 
 
40
def Attr(obj, attr):
 
41
    """A node tuple for obj.attr"""
 
42
    return [obj, Node(syms.trailer, [Dot(), attr])]
 
43
 
 
44
def Comma():
 
45
    """A comma leaf"""
 
46
    return Leaf(token.COMMA, ",")
 
47
 
 
48
def Dot():
 
49
    """A period (.) leaf"""
 
50
    return Leaf(token.DOT, ".")
 
51
 
 
52
def ArgList(args, lparen=LParen(), rparen=RParen()):
 
53
    """A parenthesised argument list, used by Call()"""
 
54
    node = Node(syms.trailer, [lparen.clone(), rparen.clone()])
 
55
    if args:
 
56
        node.insert_child(1, Node(syms.arglist, args))
 
57
    return node
 
58
 
 
59
def Call(func_name, args=None, prefix=None):
 
60
    """A function call"""
 
61
    node = Node(syms.power, [func_name, ArgList(args)])
 
62
    if prefix is not None:
 
63
        node.set_prefix(prefix)
 
64
    return node
 
65
 
 
66
def Newline():
 
67
    """A newline literal"""
 
68
    return Leaf(token.NEWLINE, "\n")
 
69
 
 
70
def BlankLine():
 
71
    """A blank line"""
 
72
    return Leaf(token.NEWLINE, "")
 
73
 
 
74
def Number(n, prefix=None):
 
75
    return Leaf(token.NUMBER, n, prefix=prefix)
 
76
 
 
77
def Subscript(index_node):
 
78
    """A numeric or string subscript"""
 
79
    return Node(syms.trailer, [Leaf(token.LBRACE, '['),
 
80
                               index_node,
 
81
                               Leaf(token.RBRACE, ']')])
 
82
 
 
83
def String(string, prefix=None):
 
84
    """A string leaf"""
 
85
    return Leaf(token.STRING, string, prefix=prefix)
 
86
 
 
87
def ListComp(xp, fp, it, test=None):
 
88
    """A list comprehension of the form [xp for fp in it if test].
 
89
 
 
90
    If test is None, the "if test" part is omitted.
 
91
    """
 
92
    xp.set_prefix("")
 
93
    fp.set_prefix(" ")
 
94
    it.set_prefix(" ")
 
95
    for_leaf = Leaf(token.NAME, "for")
 
96
    for_leaf.set_prefix(" ")
 
97
    in_leaf = Leaf(token.NAME, "in")
 
98
    in_leaf.set_prefix(" ")
 
99
    inner_args = [for_leaf, fp, in_leaf, it]
 
100
    if test:
 
101
        test.set_prefix(" ")
 
102
        if_leaf = Leaf(token.NAME, "if")
 
103
        if_leaf.set_prefix(" ")
 
104
        inner_args.append(Node(syms.comp_if, [if_leaf, test]))
 
105
    inner = Node(syms.listmaker, [xp, Node(syms.comp_for, inner_args)])
 
106
    return Node(syms.atom,
 
107
                       [Leaf(token.LBRACE, "["),
 
108
                        inner,
 
109
                        Leaf(token.RBRACE, "]")])
 
110
 
 
111
def FromImport(package_name, name_leafs):
 
112
    """ Return an import statement in the form:
 
113
        from package import name_leafs"""
 
114
    # XXX: May not handle dotted imports properly (eg, package_name='foo.bar')
 
115
    #assert package_name == '.' or '.' not in package_name, "FromImport has "\
 
116
    #       "not been tested with dotted package names -- use at your own "\
 
117
    #       "peril!"
 
118
 
 
119
    for leaf in name_leafs:
 
120
        # Pull the leaves out of their old tree
 
121
        leaf.remove()
 
122
 
 
123
    children = [Leaf(token.NAME, 'from'),
 
124
                Leaf(token.NAME, package_name, prefix=" "),
 
125
                Leaf(token.NAME, 'import', prefix=" "),
 
126
                Node(syms.import_as_names, name_leafs)]
 
127
    imp = Node(syms.import_from, children)
 
128
    return imp
 
129
 
 
130
 
 
131
###########################################################
 
132
### Determine whether a node represents a given literal
 
133
###########################################################
 
134
 
 
135
def is_tuple(node):
 
136
    """Does the node represent a tuple literal?"""
 
137
    if isinstance(node, Node) and node.children == [LParen(), RParen()]:
 
138
        return True
 
139
    return (isinstance(node, Node)
 
140
            and len(node.children) == 3
 
141
            and isinstance(node.children[0], Leaf)
 
142
            and isinstance(node.children[1], Node)
 
143
            and isinstance(node.children[2], Leaf)
 
144
            and node.children[0].value == "("
 
145
            and node.children[2].value == ")")
 
146
 
 
147
def is_list(node):
 
148
    """Does the node represent a list literal?"""
 
149
    return (isinstance(node, Node)
 
150
            and len(node.children) > 1
 
151
            and isinstance(node.children[0], Leaf)
 
152
            and isinstance(node.children[-1], Leaf)
 
153
            and node.children[0].value == "["
 
154
            and node.children[-1].value == "]")
 
155
 
 
156
###########################################################
 
157
### Common portability code. This allows fixers to do, eg,
 
158
###  "from .util import set" and forget about it.
 
159
###########################################################
 
160
 
 
161
try:
 
162
    any = any
 
163
except NameError:
 
164
    def any(l):
 
165
        for o in l:
 
166
            if o:
 
167
                return True
 
168
        return False
 
169
 
 
170
try:
 
171
    set = set
 
172
except NameError:
 
173
    from sets import Set as set
 
174
 
 
175
try:
 
176
    reversed = reversed
 
177
except NameError:
 
178
    def reversed(l):
 
179
        return l[::-1]
 
180
 
 
181
###########################################################
 
182
### Misc
 
183
###########################################################
 
184
 
 
185
 
 
186
consuming_calls = set(["sorted", "list", "set", "any", "all", "tuple", "sum",
 
187
                       "min", "max"])
 
188
 
 
189
def attr_chain(obj, attr):
 
190
    """Follow an attribute chain.
 
191
 
 
192
    If you have a chain of objects where a.foo -> b, b.foo-> c, etc,
 
193
    use this to iterate over all objects in the chain. Iteration is
 
194
    terminated by getattr(x, attr) is None.
 
195
 
 
196
    Args:
 
197
        obj: the starting object
 
198
        attr: the name of the chaining attribute
 
199
 
 
200
    Yields:
 
201
        Each successive object in the chain.
 
202
    """
 
203
    next = getattr(obj, attr)
 
204
    while next:
 
205
        yield next
 
206
        next = getattr(next, attr)
 
207
 
 
208
p0 = """for_stmt< 'for' any 'in' node=any ':' any* >
 
209
        | comp_for< 'for' any 'in' node=any any* >
 
210
     """
 
211
p1 = """
 
212
power<
 
213
    ( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' |
 
214
      'any' | 'all' | (any* trailer< '.' 'join' >) )
 
215
    trailer< '(' node=any ')' >
 
216
    any*
 
217
>
 
218
"""
 
219
p2 = """
 
220
power<
 
221
    'sorted'
 
222
    trailer< '(' arglist<node=any any*> ')' >
 
223
    any*
 
224
>
 
225
"""
 
226
pats_built = False
 
227
def in_special_context(node):
 
228
    """ Returns true if node is in an environment where all that is required
 
229
        of it is being itterable (ie, it doesn't matter if it returns a list
 
230
        or an itterator).
 
231
        See test_map_nochange in test_fixers.py for some examples and tests.
 
232
        """
 
233
    global p0, p1, p2, pats_built
 
234
    if not pats_built:
 
235
        p1 = patcomp.compile_pattern(p1)
 
236
        p0 = patcomp.compile_pattern(p0)
 
237
        p2 = patcomp.compile_pattern(p2)
 
238
        pats_built = True
 
239
    patterns = [p0, p1, p2]
 
240
    for pattern, parent in zip(patterns, attr_chain(node, "parent")):
 
241
        results = {}
 
242
        if pattern.match(parent, results) and results["node"] is node:
 
243
            return True
 
244
    return False
 
245
 
 
246
###########################################################
 
247
### The following functions are to find bindings in a suite
 
248
###########################################################
 
249
 
 
250
def make_suite(node):
 
251
    if node.type == syms.suite:
 
252
        return node
 
253
    node = node.clone()
 
254
    parent, node.parent = node.parent, None
 
255
    suite = Node(syms.suite, [node])
 
256
    suite.parent = parent
 
257
    return suite
 
258
 
 
259
def does_tree_import(package, name, node):
 
260
    """ Returns true if name is imported from package at the
 
261
        top level of the tree which node belongs to.
 
262
        To cover the case of an import like 'import foo', use
 
263
        Null for the package and 'foo' for the name. """
 
264
    # Scamper up to the top level namespace
 
265
    while node.type != syms.file_input:
 
266
        assert node.parent, "Tree is insane! root found before "\
 
267
                           "file_input node was found."
 
268
        node = node.parent
 
269
 
 
270
    binding = find_binding(name, node, package)
 
271
    return bool(binding)
 
272
 
 
273
_def_syms = set([syms.classdef, syms.funcdef])
 
274
def find_binding(name, node, package=None):
 
275
    """ Returns the node which binds variable name, otherwise None.
 
276
        If optional argument package is supplied, only imports will
 
277
        be returned.
 
278
        See test cases for examples."""
 
279
    for child in node.children:
 
280
        ret = None
 
281
        if child.type == syms.for_stmt:
 
282
            if _find(name, child.children[1]):
 
283
                return child
 
284
            n = find_binding(name, make_suite(child.children[-1]), package)
 
285
            if n: ret = n
 
286
        elif child.type in (syms.if_stmt, syms.while_stmt):
 
287
            n = find_binding(name, make_suite(child.children[-1]), package)
 
288
            if n: ret = n
 
289
        elif child.type == syms.try_stmt:
 
290
            n = find_binding(name, make_suite(child.children[2]), package)
 
291
            if n:
 
292
                ret = n
 
293
            else:
 
294
                for i, kid in enumerate(child.children[3:]):
 
295
                    if kid.type == token.COLON and kid.value == ":":
 
296
                        # i+3 is the colon, i+4 is the suite
 
297
                        n = find_binding(name, make_suite(child.children[i+4]), package)
 
298
                        if n: ret = n
 
299
        elif child.type in _def_syms and child.children[1].value == name:
 
300
            ret = child
 
301
        elif _is_import_binding(child, name, package):
 
302
            ret = child
 
303
        elif child.type == syms.simple_stmt:
 
304
            ret = find_binding(name, child, package)
 
305
        elif child.type == syms.expr_stmt:
 
306
            if _find(name, child.children[0]):
 
307
                ret = child
 
308
 
 
309
        if ret:
 
310
            if not package:
 
311
                return ret
 
312
            if ret.type in (syms.import_name, syms.import_from):
 
313
                return ret
 
314
    return None
 
315
 
 
316
_block_syms = set([syms.funcdef, syms.classdef, syms.trailer])
 
317
def _find(name, node):
 
318
    nodes = [node]
 
319
    while nodes:
 
320
        node = nodes.pop()
 
321
        if node.type > 256 and node.type not in _block_syms:
 
322
            nodes.extend(node.children)
 
323
        elif node.type == token.NAME and node.value == name:
 
324
            return node
 
325
    return None
 
326
 
 
327
def _is_import_binding(node, name, package=None):
 
328
    """ Will reuturn node if node will import name, or node
 
329
        will import * from package.  None is returned otherwise.
 
330
        See test cases for examples. """
 
331
 
 
332
    if node.type == syms.import_name and not package:
 
333
        imp = node.children[1]
 
334
        if imp.type == syms.dotted_as_names:
 
335
            for child in imp.children:
 
336
                if child.type == syms.dotted_as_name:
 
337
                    if child.children[2].value == name:
 
338
                        return node
 
339
                elif child.type == token.NAME and child.value == name:
 
340
                    return node
 
341
        elif imp.type == syms.dotted_as_name:
 
342
            last = imp.children[-1]
 
343
            if last.type == token.NAME and last.value == name:
 
344
                return node
 
345
        elif imp.type == token.NAME and imp.value == name:
 
346
            return node
 
347
    elif node.type == syms.import_from:
 
348
        # unicode(...) is used to make life easier here, because
 
349
        # from a.b import parses to ['import', ['a', '.', 'b'], ...]
 
350
        if package and unicode(node.children[1]).strip() != package:
 
351
            return None
 
352
        n = node.children[3]
 
353
        if package and _find('as', n):
 
354
            # See test_from_import_as for explanation
 
355
            return None
 
356
        elif n.type == syms.import_as_names and _find(name, n):
 
357
            return node
 
358
        elif n.type == syms.import_as_name:
 
359
            child = n.children[2]
 
360
            if child.type == token.NAME and child.value == name:
 
361
                return node
 
362
        elif n.type == token.NAME and n.value == name:
 
363
            return node
 
364
        elif package and n.type == token.STAR:
 
365
            return node
 
366
    return None