mirror of https://github.com/python/cpython
Merged revisions 68197 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ................ r68197 | benjamin.peterson | 2009-01-03 10:34:02 -0600 (Sat, 03 Jan 2009) | 55 lines Merged revisions 67900-67901,67919,67928,67984,67991-67993,68106-68108,68110 via svnmerge from svn+ssh://pythondev@svn.python.org/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 ........ ................
This commit is contained in:
parent
627cf6a976
commit
8bcddcabd7
|
@ -222,6 +222,29 @@ def in_special_context(node):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def is_probably_builtin(node):
|
||||||
|
"""
|
||||||
|
Check that something isn't an attribute or function name etc.
|
||||||
|
"""
|
||||||
|
prev = node.get_prev_sibling()
|
||||||
|
if prev is not None and prev.type == token.DOT:
|
||||||
|
# Attribute lookup.
|
||||||
|
return False
|
||||||
|
parent = node.parent
|
||||||
|
if parent.type in (syms.funcdef, syms.classdef):
|
||||||
|
return False
|
||||||
|
if parent.type == syms.expr_stmt and parent.children[0] is node:
|
||||||
|
# Assignment.
|
||||||
|
return False
|
||||||
|
if parent.type == syms.parameters or \
|
||||||
|
(parent.type == syms.typedargslist and (
|
||||||
|
(prev is not None and prev.type == token.COMMA) or
|
||||||
|
parent.children[0] is node
|
||||||
|
)):
|
||||||
|
# The name of an argument.
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
###########################################################
|
###########################################################
|
||||||
### The following functions are to find bindings in a suite
|
### The following functions are to find bindings in a suite
|
||||||
###########################################################
|
###########################################################
|
||||||
|
|
|
@ -7,9 +7,9 @@ This converts usages of the execfile function into calls to the built-in
|
||||||
exec() function.
|
exec() function.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from .. import pytree
|
|
||||||
from .. import fixer_base
|
from .. import fixer_base
|
||||||
from ..fixer_util import Comma, Name, Call, LParen, RParen, Dot
|
from ..fixer_util import (Comma, Name, Call, LParen, RParen, Dot, Node,
|
||||||
|
ArgList, String, syms)
|
||||||
|
|
||||||
|
|
||||||
class FixExecfile(fixer_base.BaseFix):
|
class FixExecfile(fixer_base.BaseFix):
|
||||||
|
@ -22,16 +22,30 @@ class FixExecfile(fixer_base.BaseFix):
|
||||||
|
|
||||||
def transform(self, node, results):
|
def transform(self, node, results):
|
||||||
assert results
|
assert results
|
||||||
syms = self.syms
|
|
||||||
filename = results["filename"]
|
filename = results["filename"]
|
||||||
globals = results.get("globals")
|
globals = results.get("globals")
|
||||||
locals = results.get("locals")
|
locals = results.get("locals")
|
||||||
args = [Name('open'), LParen(), filename.clone(), RParen(), Dot(),
|
|
||||||
Name('read'), LParen(), RParen()]
|
# Copy over the prefix from the right parentheses end of the execfile
|
||||||
args[0].set_prefix("")
|
# call.
|
||||||
|
execfile_paren = node.children[-1].children[-1].clone()
|
||||||
|
# Construct open().read().
|
||||||
|
open_args = ArgList([filename.clone()], rparen=execfile_paren)
|
||||||
|
open_call = Node(syms.power, [Name("open"), open_args])
|
||||||
|
read = [Node(syms.trailer, [Dot(), Name('read')]),
|
||||||
|
Node(syms.trailer, [LParen(), RParen()])]
|
||||||
|
open_expr = [open_call] + read
|
||||||
|
# Wrap the open call in a compile call. This is so the filename will be
|
||||||
|
# preserved in the execed code.
|
||||||
|
filename_arg = filename.clone()
|
||||||
|
filename_arg.set_prefix(" ")
|
||||||
|
exec_str = String("'exec'", " ")
|
||||||
|
compile_args = open_expr + [Comma(), filename_arg, Comma(), exec_str]
|
||||||
|
compile_call = Call(Name("compile"), compile_args, "")
|
||||||
|
# Finally, replace the execfile call with an exec call.
|
||||||
|
args = [compile_call]
|
||||||
if globals is not None:
|
if globals is not None:
|
||||||
args.extend([Comma(), globals.clone()])
|
args.extend([Comma(), globals.clone()])
|
||||||
if locals is not None:
|
if locals is not None:
|
||||||
args.extend([Comma(), locals.clone()])
|
args.extend([Comma(), locals.clone()])
|
||||||
|
|
||||||
return Call(Name("exec"), args, prefix=node.get_prefix())
|
return Call(Name("exec"), args, prefix=node.get_prefix())
|
||||||
|
|
|
@ -13,49 +13,72 @@ Becomes:
|
||||||
# Local imports
|
# Local imports
|
||||||
from .. import fixer_base
|
from .. import fixer_base
|
||||||
from os.path import dirname, join, exists, pathsep
|
from os.path import dirname, join, exists, pathsep
|
||||||
from ..fixer_util import FromImport, syms
|
from ..fixer_util import FromImport, syms, token
|
||||||
|
|
||||||
|
|
||||||
|
def traverse_imports(names):
|
||||||
|
"""
|
||||||
|
Walks over all the names imported in a dotted_as_names node.
|
||||||
|
"""
|
||||||
|
pending = [names]
|
||||||
|
while pending:
|
||||||
|
node = pending.pop()
|
||||||
|
if node.type == token.NAME:
|
||||||
|
yield node.value
|
||||||
|
elif node.type == syms.dotted_name:
|
||||||
|
yield "".join([ch.value for ch in node.children])
|
||||||
|
elif node.type == syms.dotted_as_name:
|
||||||
|
pending.append(node.children[0])
|
||||||
|
elif node.type == syms.dotted_as_names:
|
||||||
|
pending.extend(node.children[::-2])
|
||||||
|
else:
|
||||||
|
raise AssertionError("unkown node type")
|
||||||
|
|
||||||
|
|
||||||
class FixImport(fixer_base.BaseFix):
|
class FixImport(fixer_base.BaseFix):
|
||||||
|
|
||||||
PATTERN = """
|
PATTERN = """
|
||||||
import_from< type='from' imp=any 'import' ['('] any [')'] >
|
import_from< 'from' imp=any 'import' ['('] any [')'] >
|
||||||
|
|
|
|
||||||
import_name< type='import' imp=any >
|
import_name< 'import' imp=any >
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def transform(self, node, results):
|
def transform(self, node, results):
|
||||||
imp = results['imp']
|
imp = results['imp']
|
||||||
|
|
||||||
mod_name = str(imp.children[0] if imp.type == syms.dotted_as_name \
|
if node.type == syms.import_from:
|
||||||
else imp)
|
|
||||||
|
|
||||||
if str(imp).startswith('.'):
|
|
||||||
# Already a new-style import
|
|
||||||
return
|
|
||||||
|
|
||||||
if not probably_a_local_import(str(mod_name), self.filename):
|
|
||||||
# I guess this is a global import -- skip it!
|
|
||||||
return
|
|
||||||
|
|
||||||
if results['type'].value == 'from':
|
|
||||||
# Some imps are top-level (eg: 'import ham')
|
# Some imps are top-level (eg: 'import ham')
|
||||||
# some are first level (eg: 'import ham.eggs')
|
# some are first level (eg: 'import ham.eggs')
|
||||||
# some are third level (eg: 'import ham.eggs as spam')
|
# some are third level (eg: 'import ham.eggs as spam')
|
||||||
# Hence, the loop
|
# Hence, the loop
|
||||||
while not hasattr(imp, 'value'):
|
while not hasattr(imp, 'value'):
|
||||||
imp = imp.children[0]
|
imp = imp.children[0]
|
||||||
|
if self.probably_a_local_import(imp.value):
|
||||||
imp.value = "." + imp.value
|
imp.value = "." + imp.value
|
||||||
node.changed()
|
imp.changed()
|
||||||
else:
|
|
||||||
new = FromImport('.', getattr(imp, 'content', None) or [imp])
|
|
||||||
new.set_prefix(node.get_prefix())
|
|
||||||
node = new
|
|
||||||
return node
|
return node
|
||||||
|
else:
|
||||||
|
have_local = False
|
||||||
|
have_absolute = False
|
||||||
|
for mod_name in traverse_imports(imp):
|
||||||
|
if self.probably_a_local_import(mod_name):
|
||||||
|
have_local = True
|
||||||
|
else:
|
||||||
|
have_absolute = True
|
||||||
|
if have_absolute:
|
||||||
|
if have_local:
|
||||||
|
# We won't handle both sibling and absolute imports in the
|
||||||
|
# same statement at the moment.
|
||||||
|
self.warning(node, "absolute and local imports together")
|
||||||
|
return
|
||||||
|
|
||||||
def probably_a_local_import(imp_name, file_path):
|
new = FromImport('.', [imp])
|
||||||
# Must be stripped because the right space is included by the parser
|
new.set_prefix(node.get_prefix())
|
||||||
imp_name = imp_name.split('.', 1)[0].strip()
|
return new
|
||||||
base_path = dirname(file_path)
|
|
||||||
|
def probably_a_local_import(self, imp_name):
|
||||||
|
imp_name = imp_name.split('.', 1)[0]
|
||||||
|
base_path = dirname(self.filename)
|
||||||
base_path = join(base_path, imp_name)
|
base_path = join(base_path, imp_name)
|
||||||
# If there is no __init__.py next to the file its not in a package
|
# If there is no __init__.py next to the file its not in a package
|
||||||
# so can't be a relative import.
|
# so can't be a relative import.
|
||||||
|
|
|
@ -118,7 +118,7 @@ class FixImports(fixer_base.BaseFix):
|
||||||
def transform(self, node, results):
|
def transform(self, node, results):
|
||||||
import_mod = results.get("module_name")
|
import_mod = results.get("module_name")
|
||||||
if import_mod:
|
if import_mod:
|
||||||
new_name = self.mapping[(import_mod or mod_name).value]
|
new_name = self.mapping[import_mod.value]
|
||||||
import_mod.replace(Name(new_name, prefix=import_mod.get_prefix()))
|
import_mod.replace(Name(new_name, prefix=import_mod.get_prefix()))
|
||||||
if "name_import" in results:
|
if "name_import" in results:
|
||||||
# If it's not a "from x import x, y" or "import x as y" import,
|
# If it's not a "from x import x, y" or "import x as y" import,
|
||||||
|
@ -129,10 +129,8 @@ class FixImports(fixer_base.BaseFix):
|
||||||
# line (e.g., "import StringIO, urlparse"). The problem is that I
|
# line (e.g., "import StringIO, urlparse"). The problem is that I
|
||||||
# can't figure out an easy way to make a pattern recognize the
|
# can't figure out an easy way to make a pattern recognize the
|
||||||
# keys of MAPPING randomly sprinkled in an import statement.
|
# keys of MAPPING randomly sprinkled in an import statement.
|
||||||
while True:
|
|
||||||
results = self.match(node)
|
results = self.match(node)
|
||||||
if not results:
|
if results:
|
||||||
break
|
|
||||||
self.transform(node, results)
|
self.transform(node, results)
|
||||||
else:
|
else:
|
||||||
# Replace usage of the module.
|
# Replace usage of the module.
|
||||||
|
|
|
@ -5,20 +5,18 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Local imports
|
# Local imports
|
||||||
from .. import pytree
|
|
||||||
from .. import fixer_base
|
from .. import fixer_base
|
||||||
from ..fixer_util import Name, Number
|
from ..fixer_util import Name, Number, is_probably_builtin
|
||||||
|
|
||||||
|
|
||||||
class FixLong(fixer_base.BaseFix):
|
class FixLong(fixer_base.BaseFix):
|
||||||
|
|
||||||
PATTERN = "'long'"
|
PATTERN = "'long'"
|
||||||
|
|
||||||
static_long = Name("long")
|
|
||||||
static_int = Name("int")
|
static_int = Name("int")
|
||||||
|
|
||||||
def transform(self, node, results):
|
def transform(self, node, results):
|
||||||
assert node == self.static_long, node
|
if is_probably_builtin(node):
|
||||||
new = self.static_int.clone()
|
new = self.static_int.clone()
|
||||||
new.set_prefix(node.get_prefix())
|
new.set_prefix(node.get_prefix())
|
||||||
return new
|
return new
|
||||||
|
|
|
@ -41,7 +41,7 @@ class StdoutRefactoringTool(refactor.RefactoringTool):
|
||||||
super(StdoutRefactoringTool, self).write_file(new_text,
|
super(StdoutRefactoringTool, self).write_file(new_text,
|
||||||
filename, old_text)
|
filename, old_text)
|
||||||
if not self.nobackups:
|
if not self.nobackups:
|
||||||
shutil.copymode(filename, backup)
|
shutil.copymode(backup, filename)
|
||||||
|
|
||||||
def print_output(self, lines):
|
def print_output(self, lines):
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
|
|
@ -287,17 +287,13 @@ class RefactoringTool(object):
|
||||||
Returns:
|
Returns:
|
||||||
True if the tree was modified, False otherwise.
|
True if the tree was modified, False otherwise.
|
||||||
"""
|
"""
|
||||||
# Two calls to chain are required because pre_order.values()
|
for fixer in chain(self.pre_order, self.post_order):
|
||||||
# will be a list of lists of fixers:
|
|
||||||
# [[<fixer ...>, <fixer ...>], [<fixer ...>]]
|
|
||||||
all_fixers = chain(self.pre_order, self.post_order)
|
|
||||||
for fixer in all_fixers:
|
|
||||||
fixer.start_tree(tree, name)
|
fixer.start_tree(tree, name)
|
||||||
|
|
||||||
self.traverse_by(self.pre_order_heads, tree.pre_order())
|
self.traverse_by(self.pre_order_heads, tree.pre_order())
|
||||||
self.traverse_by(self.post_order_heads, tree.post_order())
|
self.traverse_by(self.post_order_heads, tree.post_order())
|
||||||
|
|
||||||
for fixer in all_fixers:
|
for fixer in chain(self.pre_order, self.post_order):
|
||||||
fixer.finish_tree(tree, name)
|
fixer.finish_tree(tree, name)
|
||||||
return tree.was_changed
|
return tree.was_changed
|
||||||
|
|
||||||
|
|
|
@ -485,6 +485,14 @@ class GrammarTests(unittest.TestCase):
|
||||||
global a, b
|
global a, b
|
||||||
global one, two, three, four, five, six, seven, eight, nine, ten
|
global one, two, three, four, five, six, seven, eight, nine, ten
|
||||||
|
|
||||||
|
def testNonlocal(self):
|
||||||
|
# 'nonlocal' NAME (',' NAME)*
|
||||||
|
x = 0
|
||||||
|
y = 0
|
||||||
|
def f():
|
||||||
|
nonlocal x
|
||||||
|
nonlocal x, y
|
||||||
|
|
||||||
def testAssert(self):
|
def testAssert(self):
|
||||||
# assert_stmt: 'assert' test [',' test]
|
# assert_stmt: 'assert' test [',' test]
|
||||||
assert 1
|
assert 1
|
||||||
|
|
|
@ -1073,11 +1073,72 @@ class Test_long(FixerTestCase):
|
||||||
a = """z = type(x) in (int, int)"""
|
a = """z = type(x) in (int, int)"""
|
||||||
self.check(b, a)
|
self.check(b, a)
|
||||||
|
|
||||||
|
def test_unchanged(self):
|
||||||
|
s = """long = True"""
|
||||||
|
self.unchanged(s)
|
||||||
|
|
||||||
|
s = """s.long = True"""
|
||||||
|
self.unchanged(s)
|
||||||
|
|
||||||
|
s = """def long(): pass"""
|
||||||
|
self.unchanged(s)
|
||||||
|
|
||||||
|
s = """class long(): pass"""
|
||||||
|
self.unchanged(s)
|
||||||
|
|
||||||
|
s = """def f(long): pass"""
|
||||||
|
self.unchanged(s)
|
||||||
|
|
||||||
|
s = """def f(g, long): pass"""
|
||||||
|
self.unchanged(s)
|
||||||
|
|
||||||
|
s = """def f(x, long=True): pass"""
|
||||||
|
self.unchanged(s)
|
||||||
|
|
||||||
def test_prefix_preservation(self):
|
def test_prefix_preservation(self):
|
||||||
b = """x = long( x )"""
|
b = """x = long( x )"""
|
||||||
a = """x = int( x )"""
|
a = """x = int( x )"""
|
||||||
self.check(b, a)
|
self.check(b, a)
|
||||||
|
|
||||||
|
|
||||||
|
class Test_execfile(FixerTestCase):
|
||||||
|
fixer = "execfile"
|
||||||
|
|
||||||
|
def test_conversion(self):
|
||||||
|
b = """execfile("fn")"""
|
||||||
|
a = """exec(compile(open("fn").read(), "fn", 'exec'))"""
|
||||||
|
self.check(b, a)
|
||||||
|
|
||||||
|
b = """execfile("fn", glob)"""
|
||||||
|
a = """exec(compile(open("fn").read(), "fn", 'exec'), glob)"""
|
||||||
|
self.check(b, a)
|
||||||
|
|
||||||
|
b = """execfile("fn", glob, loc)"""
|
||||||
|
a = """exec(compile(open("fn").read(), "fn", 'exec'), glob, loc)"""
|
||||||
|
self.check(b, a)
|
||||||
|
|
||||||
|
b = """execfile("fn", globals=glob)"""
|
||||||
|
a = """exec(compile(open("fn").read(), "fn", 'exec'), globals=glob)"""
|
||||||
|
self.check(b, a)
|
||||||
|
|
||||||
|
b = """execfile("fn", locals=loc)"""
|
||||||
|
a = """exec(compile(open("fn").read(), "fn", 'exec'), locals=loc)"""
|
||||||
|
self.check(b, a)
|
||||||
|
|
||||||
|
b = """execfile("fn", globals=glob, locals=loc)"""
|
||||||
|
a = """exec(compile(open("fn").read(), "fn", 'exec'), globals=glob, locals=loc)"""
|
||||||
|
self.check(b, a)
|
||||||
|
|
||||||
|
def test_spacing(self):
|
||||||
|
b = """execfile( "fn" )"""
|
||||||
|
a = """exec(compile(open( "fn" ).read(), "fn", 'exec'))"""
|
||||||
|
self.check(b, a)
|
||||||
|
|
||||||
|
b = """execfile("fn", globals = glob)"""
|
||||||
|
a = """exec(compile(open("fn").read(), "fn", 'exec'), globals = glob)"""
|
||||||
|
self.check(b, a)
|
||||||
|
|
||||||
|
|
||||||
class Test_isinstance(FixerTestCase):
|
class Test_isinstance(FixerTestCase):
|
||||||
fixer = "isinstance"
|
fixer = "isinstance"
|
||||||
|
|
||||||
|
@ -3466,11 +3527,30 @@ class Test_import(FixerTestCase):
|
||||||
a = "from . import foo, bar"
|
a = "from . import foo, bar"
|
||||||
self.check_both(b, a)
|
self.check_both(b, a)
|
||||||
|
|
||||||
|
b = "import foo, bar, x"
|
||||||
|
a = "from . import foo, bar, x"
|
||||||
|
self.check_both(b, a)
|
||||||
|
|
||||||
|
b = "import x, y, z"
|
||||||
|
a = "from . import x, y, z"
|
||||||
|
self.check_both(b, a)
|
||||||
|
|
||||||
def test_import_as(self):
|
def test_import_as(self):
|
||||||
b = "import foo as x"
|
b = "import foo as x"
|
||||||
a = "from . import foo as x"
|
a = "from . import foo as x"
|
||||||
self.check_both(b, a)
|
self.check_both(b, a)
|
||||||
|
|
||||||
|
b = "import a as b, b as c, c as d"
|
||||||
|
a = "from . import a as b, b as c, c as d"
|
||||||
|
self.check_both(b, a)
|
||||||
|
|
||||||
|
def test_local_and_absolute(self):
|
||||||
|
self.always_exists = False
|
||||||
|
self.present_files = set(["foo.py", "__init__.py"])
|
||||||
|
|
||||||
|
s = "import foo, bar"
|
||||||
|
self.warns_unchanged(s, "absolute and local imports together")
|
||||||
|
|
||||||
def test_dotted_import(self):
|
def test_dotted_import(self):
|
||||||
b = "import foo.bar"
|
b = "import foo.bar"
|
||||||
a = "from . import foo.bar"
|
a = "from . import foo.bar"
|
||||||
|
|
Loading…
Reference in New Issue