Skip to content

Commit fdb0e3f

Browse files
author
benjamin.peterson
committed
Merged revisions 67900-67901,67919,67928,67984,67991-67993,68106-68108,68110 via svnmerge from
svn+ssh://[email protected]/sandbox/trunk/2to3/lib2to3 ........ r67900 | benjamin.peterson | 2008-12-22 14:02:45 -0600 (Mon, 22 Dec 2008) | 4 lines fix_execfile: wrap the open(fn).read() call in compile(), so the filename is preserved also add unittests for the fixer ........ r67901 | benjamin.peterson | 2008-12-22 14:09:55 -0600 (Mon, 22 Dec 2008) | 1 line remove unused import ........ r67919 | benjamin.peterson | 2008-12-23 13:12:22 -0600 (Tue, 23 Dec 2008) | 1 line copy permission bits from the backup to the original ........ r67928 | benjamin.peterson | 2008-12-26 20:49:30 -0600 (Fri, 26 Dec 2008) | 1 line don't be so idiot about multiple local imports in fix_import; still won't handle absolute and local imports on the same line ........ r67984 | benjamin.peterson | 2008-12-28 09:55:16 -0600 (Sun, 28 Dec 2008) | 1 line don't need loop ........ r67991 | benjamin.peterson | 2008-12-28 14:30:26 -0600 (Sun, 28 Dec 2008) | 1 line actually call finish_tree() ........ r67992 | benjamin.peterson | 2008-12-28 14:34:47 -0600 (Sun, 28 Dec 2008) | 1 line remove useless test ........ r67993 | benjamin.peterson | 2008-12-28 15:04:32 -0600 (Sun, 28 Dec 2008) | 1 line update pyk3's test grammar ........ r68106 | benjamin.peterson | 2008-12-31 11:53:58 -0600 (Wed, 31 Dec 2008) | 1 line #2734 don't convert every instance of long (eg if it's an attribute) ........ r68107 | benjamin.peterson | 2008-12-31 11:55:10 -0600 (Wed, 31 Dec 2008) | 1 line add another test ........ r68108 | benjamin.peterson | 2008-12-31 12:00:12 -0600 (Wed, 31 Dec 2008) | 1 line don't change long even if it's the only argument name ........ r68110 | benjamin.peterson | 2008-12-31 14:13:26 -0600 (Wed, 31 Dec 2008) | 1 line remove unused import ........ git-svn-id: http://svn.python.org/projects/python/trunk@68197 6015fed2-1504-0410-9fe1-9d1591cc4771
1 parent bc72fca commit fdb0e3f

9 files changed

+198
-58
lines changed

Lib/lib2to3/fixer_util.py

+23
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,29 @@ def in_special_context(node):
222222
return True
223223
return False
224224

225+
def is_probably_builtin(node):
226+
"""
227+
Check that something isn't an attribute or function name etc.
228+
"""
229+
prev = node.get_prev_sibling()
230+
if prev is not None and prev.type == token.DOT:
231+
# Attribute lookup.
232+
return False
233+
parent = node.parent
234+
if parent.type in (syms.funcdef, syms.classdef):
235+
return False
236+
if parent.type == syms.expr_stmt and parent.children[0] is node:
237+
# Assignment.
238+
return False
239+
if parent.type == syms.parameters or \
240+
(parent.type == syms.typedargslist and (
241+
(prev is not None and prev.type == token.COMMA) or
242+
parent.children[0] is node
243+
)):
244+
# The name of an argument.
245+
return False
246+
return True
247+
225248
###########################################################
226249
### The following functions are to find bindings in a suite
227250
###########################################################

Lib/lib2to3/fixes/fix_execfile.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
exec() function.
88
"""
99

10-
from .. import pytree
1110
from .. import fixer_base
12-
from ..fixer_util import Comma, Name, Call, LParen, RParen, Dot
11+
from ..fixer_util import (Comma, Name, Call, LParen, RParen, Dot, Node,
12+
ArgList, String, syms)
1313

1414

1515
class FixExecfile(fixer_base.BaseFix):
@@ -22,16 +22,30 @@ class FixExecfile(fixer_base.BaseFix):
2222

2323
def transform(self, node, results):
2424
assert results
25-
syms = self.syms
2625
filename = results["filename"]
2726
globals = results.get("globals")
2827
locals = results.get("locals")
29-
args = [Name('open'), LParen(), filename.clone(), RParen(), Dot(),
30-
Name('read'), LParen(), RParen()]
31-
args[0].set_prefix("")
28+
29+
# Copy over the prefix from the right parentheses end of the execfile
30+
# call.
31+
execfile_paren = node.children[-1].children[-1].clone()
32+
# Construct open().read().
33+
open_args = ArgList([filename.clone()], rparen=execfile_paren)
34+
open_call = Node(syms.power, [Name("open"), open_args])
35+
read = [Node(syms.trailer, [Dot(), Name('read')]),
36+
Node(syms.trailer, [LParen(), RParen()])]
37+
open_expr = [open_call] + read
38+
# Wrap the open call in a compile call. This is so the filename will be
39+
# preserved in the execed code.
40+
filename_arg = filename.clone()
41+
filename_arg.set_prefix(" ")
42+
exec_str = String("'exec'", " ")
43+
compile_args = open_expr + [Comma(), filename_arg, Comma(), exec_str]
44+
compile_call = Call(Name("compile"), compile_args, "")
45+
# Finally, replace the execfile call with an exec call.
46+
args = [compile_call]
3247
if globals is not None:
3348
args.extend([Comma(), globals.clone()])
3449
if locals is not None:
3550
args.extend([Comma(), locals.clone()])
36-
3751
return Call(Name("exec"), args, prefix=node.get_prefix())

Lib/lib2to3/fixes/fix_import.py

+55-32
Original file line numberDiff line numberDiff line change
@@ -13,55 +13,78 @@
1313
# Local imports
1414
from .. import fixer_base
1515
from os.path import dirname, join, exists, pathsep
16-
from ..fixer_util import FromImport, syms
16+
from ..fixer_util import FromImport, syms, token
17+
18+
19+
def traverse_imports(names):
20+
"""
21+
Walks over all the names imported in a dotted_as_names node.
22+
"""
23+
pending = [names]
24+
while pending:
25+
node = pending.pop()
26+
if node.type == token.NAME:
27+
yield node.value
28+
elif node.type == syms.dotted_name:
29+
yield "".join([ch.value for ch in node.children])
30+
elif node.type == syms.dotted_as_name:
31+
pending.append(node.children[0])
32+
elif node.type == syms.dotted_as_names:
33+
pending.extend(node.children[::-2])
34+
else:
35+
raise AssertionError("unkown node type")
36+
1737

1838
class FixImport(fixer_base.BaseFix):
1939

2040
PATTERN = """
21-
import_from< type='from' imp=any 'import' ['('] any [')'] >
41+
import_from< 'from' imp=any 'import' ['('] any [')'] >
2242
|
23-
import_name< type='import' imp=any >
43+
import_name< 'import' imp=any >
2444
"""
2545

2646
def transform(self, node, results):
2747
imp = results['imp']
2848

29-
mod_name = unicode(imp.children[0] if imp.type == syms.dotted_as_name \
30-
else imp)
31-
32-
if mod_name.startswith('.'):
33-
# Already a new-style import
34-
return
35-
36-
if not probably_a_local_import(mod_name, self.filename):
37-
# I guess this is a global import -- skip it!
38-
return
39-
40-
if results['type'].value == 'from':
49+
if node.type == syms.import_from:
4150
# Some imps are top-level (eg: 'import ham')
4251
# some are first level (eg: 'import ham.eggs')
4352
# some are third level (eg: 'import ham.eggs as spam')
4453
# Hence, the loop
4554
while not hasattr(imp, 'value'):
4655
imp = imp.children[0]
47-
imp.value = "." + imp.value
48-
node.changed()
56+
if self.probably_a_local_import(imp.value):
57+
imp.value = "." + imp.value
58+
imp.changed()
59+
return node
4960
else:
50-
new = FromImport('.', getattr(imp, 'content', None) or [imp])
61+
have_local = False
62+
have_absolute = False
63+
for mod_name in traverse_imports(imp):
64+
if self.probably_a_local_import(mod_name):
65+
have_local = True
66+
else:
67+
have_absolute = True
68+
if have_absolute:
69+
if have_local:
70+
# We won't handle both sibling and absolute imports in the
71+
# same statement at the moment.
72+
self.warning(node, "absolute and local imports together")
73+
return
74+
75+
new = FromImport('.', [imp])
5176
new.set_prefix(node.get_prefix())
52-
node = new
53-
return node
77+
return new
5478

55-
def probably_a_local_import(imp_name, file_path):
56-
# Must be stripped because the right space is included by the parser
57-
imp_name = imp_name.split('.', 1)[0].strip()
58-
base_path = dirname(file_path)
59-
base_path = join(base_path, imp_name)
60-
# If there is no __init__.py next to the file its not in a package
61-
# so can't be a relative import.
62-
if not exists(join(dirname(base_path), '__init__.py')):
79+
def probably_a_local_import(self, imp_name):
80+
imp_name = imp_name.split('.', 1)[0]
81+
base_path = dirname(self.filename)
82+
base_path = join(base_path, imp_name)
83+
# If there is no __init__.py next to the file its not in a package
84+
# so can't be a relative import.
85+
if not exists(join(dirname(base_path), '__init__.py')):
86+
return False
87+
for ext in ['.py', pathsep, '.pyc', '.so', '.sl', '.pyd']:
88+
if exists(base_path + ext):
89+
return True
6390
return False
64-
for ext in ['.py', pathsep, '.pyc', '.so', '.sl', '.pyd']:
65-
if exists(base_path + ext):
66-
return True
67-
return False

Lib/lib2to3/fixes/fix_imports.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ def start_tree(self, tree, filename):
118118
def transform(self, node, results):
119119
import_mod = results.get("module_name")
120120
if import_mod:
121-
new_name = self.mapping[(import_mod or mod_name).value]
121+
new_name = self.mapping[import_mod.value]
122122
import_mod.replace(Name(new_name, prefix=import_mod.get_prefix()))
123123
if "name_import" in results:
124124
# If it's not a "from x import x, y" or "import x as y" import,
@@ -129,10 +129,8 @@ def transform(self, node, results):
129129
# line (e.g., "import StringIO, urlparse"). The problem is that I
130130
# can't figure out an easy way to make a pattern recognize the
131131
# keys of MAPPING randomly sprinkled in an import statement.
132-
while True:
133-
results = self.match(node)
134-
if not results:
135-
break
132+
results = self.match(node)
133+
if results:
136134
self.transform(node, results)
137135
else:
138136
# Replace usage of the module.

Lib/lib2to3/fixes/fix_long.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,18 @@
55
"""
66

77
# Local imports
8-
from .. import pytree
98
from .. import fixer_base
10-
from ..fixer_util import Name, Number
9+
from ..fixer_util import Name, Number, is_probably_builtin
1110

1211

1312
class FixLong(fixer_base.BaseFix):
1413

1514
PATTERN = "'long'"
1615

17-
static_long = Name("long")
1816
static_int = Name("int")
1917

2018
def transform(self, node, results):
21-
assert node == self.static_long, node
22-
new = self.static_int.clone()
23-
new.set_prefix(node.get_prefix())
24-
return new
19+
if is_probably_builtin(node):
20+
new = self.static_int.clone()
21+
new.set_prefix(node.get_prefix())
22+
return new

Lib/lib2to3/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def write_file(self, new_text, filename, old_text):
4141
super(StdoutRefactoringTool, self).write_file(new_text,
4242
filename, old_text)
4343
if not self.nobackups:
44-
shutil.copymode(filename, backup)
44+
shutil.copymode(backup, filename)
4545

4646
def print_output(self, lines):
4747
for line in lines:

Lib/lib2to3/refactor.py

+2-6
Original file line numberDiff line numberDiff line change
@@ -287,17 +287,13 @@ def refactor_tree(self, tree, name):
287287
Returns:
288288
True if the tree was modified, False otherwise.
289289
"""
290-
# Two calls to chain are required because pre_order.values()
291-
# will be a list of lists of fixers:
292-
# [[<fixer ...>, <fixer ...>], [<fixer ...>]]
293-
all_fixers = chain(self.pre_order, self.post_order)
294-
for fixer in all_fixers:
290+
for fixer in chain(self.pre_order, self.post_order):
295291
fixer.start_tree(tree, name)
296292

297293
self.traverse_by(self.pre_order_heads, tree.pre_order())
298294
self.traverse_by(self.post_order_heads, tree.post_order())
299295

300-
for fixer in all_fixers:
296+
for fixer in chain(self.pre_order, self.post_order):
301297
fixer.finish_tree(tree, name)
302298
return tree.was_changed
303299

Lib/lib2to3/tests/data/py3_test_grammar.py

+8
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,14 @@ def testGlobal(self):
485485
global a, b
486486
global one, two, three, four, five, six, seven, eight, nine, ten
487487

488+
def testNonlocal(self):
489+
# 'nonlocal' NAME (',' NAME)*
490+
x = 0
491+
y = 0
492+
def f():
493+
nonlocal x
494+
nonlocal x, y
495+
488496
def testAssert(self):
489497
# assert_stmt: 'assert' test [',' test]
490498
assert 1

Lib/lib2to3/tests/test_fixers.py

+80
Original file line numberDiff line numberDiff line change
@@ -1073,11 +1073,72 @@ def test_3(self):
10731073
a = """z = type(x) in (int, int)"""
10741074
self.check(b, a)
10751075

1076+
def test_unchanged(self):
1077+
s = """long = True"""
1078+
self.unchanged(s)
1079+
1080+
s = """s.long = True"""
1081+
self.unchanged(s)
1082+
1083+
s = """def long(): pass"""
1084+
self.unchanged(s)
1085+
1086+
s = """class long(): pass"""
1087+
self.unchanged(s)
1088+
1089+
s = """def f(long): pass"""
1090+
self.unchanged(s)
1091+
1092+
s = """def f(g, long): pass"""
1093+
self.unchanged(s)
1094+
1095+
s = """def f(x, long=True): pass"""
1096+
self.unchanged(s)
1097+
10761098
def test_prefix_preservation(self):
10771099
b = """x = long( x )"""
10781100
a = """x = int( x )"""
10791101
self.check(b, a)
10801102

1103+
1104+
class Test_execfile(FixerTestCase):
1105+
fixer = "execfile"
1106+
1107+
def test_conversion(self):
1108+
b = """execfile("fn")"""
1109+
a = """exec(compile(open("fn").read(), "fn", 'exec'))"""
1110+
self.check(b, a)
1111+
1112+
b = """execfile("fn", glob)"""
1113+
a = """exec(compile(open("fn").read(), "fn", 'exec'), glob)"""
1114+
self.check(b, a)
1115+
1116+
b = """execfile("fn", glob, loc)"""
1117+
a = """exec(compile(open("fn").read(), "fn", 'exec'), glob, loc)"""
1118+
self.check(b, a)
1119+
1120+
b = """execfile("fn", globals=glob)"""
1121+
a = """exec(compile(open("fn").read(), "fn", 'exec'), globals=glob)"""
1122+
self.check(b, a)
1123+
1124+
b = """execfile("fn", locals=loc)"""
1125+
a = """exec(compile(open("fn").read(), "fn", 'exec'), locals=loc)"""
1126+
self.check(b, a)
1127+
1128+
b = """execfile("fn", globals=glob, locals=loc)"""
1129+
a = """exec(compile(open("fn").read(), "fn", 'exec'), globals=glob, locals=loc)"""
1130+
self.check(b, a)
1131+
1132+
def test_spacing(self):
1133+
b = """execfile( "fn" )"""
1134+
a = """exec(compile(open( "fn" ).read(), "fn", 'exec'))"""
1135+
self.check(b, a)
1136+
1137+
b = """execfile("fn", globals = glob)"""
1138+
a = """exec(compile(open("fn").read(), "fn", 'exec'), globals = glob)"""
1139+
self.check(b, a)
1140+
1141+
10811142
class Test_isinstance(FixerTestCase):
10821143
fixer = "isinstance"
10831144

@@ -3466,11 +3527,30 @@ def test_import(self):
34663527
a = "from . import foo, bar"
34673528
self.check_both(b, a)
34683529

3530+
b = "import foo, bar, x"
3531+
a = "from . import foo, bar, x"
3532+
self.check_both(b, a)
3533+
3534+
b = "import x, y, z"
3535+
a = "from . import x, y, z"
3536+
self.check_both(b, a)
3537+
34693538
def test_import_as(self):
34703539
b = "import foo as x"
34713540
a = "from . import foo as x"
34723541
self.check_both(b, a)
34733542

3543+
b = "import a as b, b as c, c as d"
3544+
a = "from . import a as b, b as c, c as d"
3545+
self.check_both(b, a)
3546+
3547+
def test_local_and_absolute(self):
3548+
self.always_exists = False
3549+
self.present_files = set(["foo.py", "__init__.py"])
3550+
3551+
s = "import foo, bar"
3552+
self.warns_unchanged(s, "absolute and local imports together")
3553+
34743554
def test_dotted_import(self):
34753555
b = "import foo.bar"
34763556
a = "from . import foo.bar"

0 commit comments

Comments
 (0)