diff options
author | Benjamin Peterson <benjamin@python.org> | 2008-09-13 18:37:09 (GMT) |
---|---|---|
committer | Benjamin Peterson <benjamin@python.org> | 2008-09-13 18:37:09 (GMT) |
commit | 3a2fb1444ac8d8fefe0d83f4f2d680e64d21a2e2 (patch) | |
tree | 4e25d1f79339f646d49bd6fd17b6d0e057b7cc74 /Lib/lib2to3 | |
parent | c575c9064769d37ac9555b7cd2907c25ab030810 (diff) | |
download | cpython-3a2fb1444ac8d8fefe0d83f4f2d680e64d21a2e2.zip cpython-3a2fb1444ac8d8fefe0d83f4f2d680e64d21a2e2.tar.gz cpython-3a2fb1444ac8d8fefe0d83f4f2d680e64d21a2e2.tar.bz2 |
Merged revisions 66453 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
................
r66453 | benjamin.peterson | 2008-09-13 12:43:19 -0500 (Sat, 13 Sep 2008) | 24 lines
Merged revisions 66191,66418,66438,66445 via svnmerge from
svn+ssh://pythondev@svn.python.org/sandbox/trunk/2to3/lib2to3
........
r66191 | benjamin.peterson | 2008-09-03 17:00:52 -0500 (Wed, 03 Sep 2008) | 1 line
update the Grammar file after recent syntax changes
........
r66418 | benjamin.peterson | 2008-09-12 18:49:48 -0500 (Fri, 12 Sep 2008) | 1 line
a trival fix to get a few more print corner cases #2899
........
r66438 | benjamin.peterson | 2008-09-12 21:32:30 -0500 (Fri, 12 Sep 2008) | 5 lines
add Jack Diederich's fixer for metaclass syntax #2366
my contribution to this was adding a few tests and fixing a few bugs
I also reviewed it (Jack is a committer)
........
r66445 | benjamin.peterson | 2008-09-13 10:50:00 -0500 (Sat, 13 Sep 2008) | 1 line
add a few more tests concerning int literals and weird spacing
........
................
Diffstat (limited to 'Lib/lib2to3')
-rw-r--r-- | Lib/lib2to3/Grammar.txt | 4 | ||||
-rw-r--r-- | Lib/lib2to3/fixes/fix_metaclass.py | 227 | ||||
-rw-r--r-- | Lib/lib2to3/fixes/fix_print.py | 2 | ||||
-rw-r--r-- | Lib/lib2to3/tests/data/py2_test_grammar.py | 178 | ||||
-rw-r--r-- | Lib/lib2to3/tests/data/py3_test_grammar.py | 40 | ||||
-rwxr-xr-x | Lib/lib2to3/tests/test_fixers.py | 146 |
6 files changed, 519 insertions, 78 deletions
diff --git a/Lib/lib2to3/Grammar.txt b/Lib/lib2to3/Grammar.txt index abf1e15..1f4a50f 100644 --- a/Lib/lib2to3/Grammar.txt +++ b/Lib/lib2to3/Grammar.txt @@ -138,7 +138,9 @@ dictsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) | classdef: 'class' NAME ['(' [arglist] ')'] ':' suite -arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test) +arglist: (argument ',')* (argument [','] + |'*' test (',' argument)* [',' '**' test] + |'**' test) argument: test [comp_for] | test '=' test # Really [keyword '='] test comp_iter: comp_for | comp_if diff --git a/Lib/lib2to3/fixes/fix_metaclass.py b/Lib/lib2to3/fixes/fix_metaclass.py new file mode 100644 index 0000000..e291dd7 --- /dev/null +++ b/Lib/lib2to3/fixes/fix_metaclass.py @@ -0,0 +1,227 @@ +"""Fixer for __metaclass__ = X -> (metaclass=X) methods. + + The various forms of classef (inherits nothing, inherits once, inherints + many) don't parse the same in the CST so we look at ALL classes for + a __metaclass__ and if we find one normalize the inherits to all be + an arglist. + + For one-liner classes ('class X: pass') there is no indent/dedent so + we normalize those into having a suite. + + Moving the __metaclass__ into the classdef can also cause the class + body to be empty so there is some special casing for that as well. + + This fixer also tries very hard to keep original indenting and spacing + in all those corner cases. + +""" +# Author: Jack Diederich + +import os + +# Local imports +from .. import fixer_base +from ..pygram import token +from ..fixer_util import Name, syms, Node, Leaf + + +def has_metaclass(parent): + """ we have to check the cls_node without changing it. + There are two possiblities: + 1) clsdef => suite => simple_stmt => expr_stmt => Leaf('__meta') + 2) clsdef => simple_stmt => expr_stmt => Leaf('__meta') + """ + for node in parent.children: + if node.type == syms.suite: + return has_metaclass(node) + elif node.type == syms.simple_stmt and node.children: + expr_node = node.children[0] + if expr_node.type == syms.expr_stmt and expr_node.children: + leaf_node = expr_node.children[0] + if leaf_node.value == '__metaclass__': + return True + return False + + +def fixup_parse_tree(cls_node): + """ one-line classes don't get a suite in the parse tree so we add + one to normalize the tree + """ + for node in cls_node.children: + if node.type == syms.suite: + # already in the prefered format, do nothing + return + + # !%@#! oneliners have no suite node, we have to fake one up + for i, node in enumerate(cls_node.children): + if node.type == token.COLON: + break + else: + raise ValueError("No class suite and no ':'!") + + # move everything into a suite node + suite = Node(syms.suite, []) + while cls_node.children[i+1:]: + move_node = cls_node.children[i+1] + suite.append_child(move_node.clone()) + move_node.remove() + cls_node.append_child(suite) + node = suite + + +def fixup_simple_stmt(parent, i, stmt_node): + """ if there is a semi-colon all the parts count as part of the same + simple_stmt. We just want the __metaclass__ part so we move + everything efter the semi-colon into its own simple_stmt node + """ + for semi_ind, node in enumerate(stmt_node.children): + if node.type == token.SEMI: # *sigh* + break + else: + return + + node.remove() # kill the semicolon + new_expr = Node(syms.expr_stmt, []) + new_stmt = Node(syms.simple_stmt, [new_expr]) + while stmt_node.children[semi_ind:]: + move_node = stmt_node.children[semi_ind] + new_expr.append_child(move_node.clone()) + move_node.remove() + parent.insert_child(i, new_stmt) + new_leaf1 = new_stmt.children[0].children[0] + old_leaf1 = stmt_node.children[0].children[0] + new_leaf1.set_prefix(old_leaf1.get_prefix()) + + +def remove_trailing_newline(node): + if node.children and node.children[-1].type == token.NEWLINE: + node.children[-1].remove() + + +def find_metas(cls_node): + # find the suite node (Mmm, sweet nodes) + for node in cls_node.children: + if node.type == syms.suite: + break + else: + raise ValueError("No class suite!") + + # look for simple_stmt[ expr_stmt[ Leaf('__metaclass__') ] ] + for i, simple_node in list(enumerate(node.children)): + if simple_node.type == syms.simple_stmt and simple_node.children: + expr_node = simple_node.children[0] + if expr_node.type == syms.expr_stmt and expr_node.children: + leaf_node = expr_node.children[0] + if leaf_node.value == '__metaclass__': + fixup_simple_stmt(node, i, simple_node) + remove_trailing_newline(simple_node) + yield (node, i, simple_node) + + +def fixup_indent(suite): + """ If an INDENT is followed by a thing with a prefix then nuke the prefix + Otherwise we get in trouble when removing __metaclass__ at suite start + """ + kids = suite.children[::-1] + # find the first indent + while kids: + node = kids.pop() + if node.type == token.INDENT: + break + + # find the first Leaf + while kids: + node = kids.pop() + if isinstance(node, Leaf) and node.type != token.DEDENT: + if node.prefix: + node.set_prefix('') + return + else: + kids.extend(node.children[::-1]) + + +class FixMetaclass(fixer_base.BaseFix): + + PATTERN = """ + classdef<any*> + """ + + def transform(self, node, results): + if not has_metaclass(node): + return node + + fixup_parse_tree(node) + + # find metaclasses, keep the last one + last_metaclass = None + for suite, i, stmt in find_metas(node): + last_metaclass = stmt + stmt.remove() + + text_type = node.children[0].type # always Leaf(nnn, 'class') + + # figure out what kind of classdef we have + if len(node.children) == 7: + # Node(classdef, ['class', 'name', '(', arglist, ')', ':', suite]) + # 0 1 2 3 4 5 6 + if node.children[3].type == syms.arglist: + arglist = node.children[3] + # Node(classdef, ['class', 'name', '(', 'Parent', ')', ':', suite]) + elif isinstance(node.children[3], Leaf): + parent = node.children[3].clone() + arglist = Node(syms.arglist, [parent]) + node.set_child(3, arglist) + else: + raise ValueError("Unexpected class inheritance arglist") + elif len(node.children) == 6: + # Node(classdef, ['class', 'name', '(', ')', ':', suite]) + # 0 1 2 3 4 5 + arglist = Node(syms.arglist, []) + node.insert_child(3, arglist) + elif len(node.children) == 4: + # Node(classdef, ['class', 'name', ':', suite]) + # 0 1 2 3 + arglist = Node(syms.arglist, []) + node.insert_child(2, Leaf(token.RPAR, ')')) + node.insert_child(2, arglist) + node.insert_child(2, Leaf(token.LPAR, '(')) + else: + raise ValueError("Unexpected class definition") + + # now stick the metaclass in the arglist + meta_txt = last_metaclass.children[0].children[0] + meta_txt.value = 'metaclass' + orig_meta_prefix = meta_txt.get_prefix() + + if arglist.children: + arglist.append_child(Leaf(token.COMMA, ',')) + meta_txt.set_prefix(' ') + else: + meta_txt.set_prefix('') + + # compact the expression "metaclass = Meta" -> "metaclass=Meta" + expr_stmt = last_metaclass.children[0] + assert expr_stmt.type == syms.expr_stmt + expr_stmt.children[1].set_prefix('') + expr_stmt.children[2].set_prefix('') + + arglist.append_child(last_metaclass) + + fixup_indent(suite) + + # check for empty suite + if not suite.children: + # one-liner that was just __metaclass_ + suite.remove() + pass_leaf = Leaf(text_type, 'pass') + pass_leaf.set_prefix(orig_meta_prefix) + node.append_child(pass_leaf) + node.append_child(Leaf(token.NEWLINE, os.linesep)) + + elif len(suite.children) > 1 and \ + (suite.children[-2].type == token.INDENT and + suite.children[-1].type == token.DEDENT): + # there was only one line in the class body and it was __metaclass__ + pass_leaf = Leaf(text_type, 'pass') + suite.insert_child(-1, pass_leaf) + suite.insert_child(-1, Leaf(token.NEWLINE, os.linesep)) diff --git a/Lib/lib2to3/fixes/fix_print.py b/Lib/lib2to3/fixes/fix_print.py index 6d01dfd..134a972 100644 --- a/Lib/lib2to3/fixes/fix_print.py +++ b/Lib/lib2to3/fixes/fix_print.py @@ -29,7 +29,7 @@ parend_expr = patcomp.compile_pattern( class FixPrint(fixer_base.ConditionalFix): PATTERN = """ - simple_stmt< bare='print' any > | print_stmt + simple_stmt< any* bare='print' any* > | print_stmt """ skip_on = '__future__.print_function' diff --git a/Lib/lib2to3/tests/data/py2_test_grammar.py b/Lib/lib2to3/tests/data/py2_test_grammar.py index b2ff643..dc794e2 100644 --- a/Lib/lib2to3/tests/data/py2_test_grammar.py +++ b/Lib/lib2to3/tests/data/py2_test_grammar.py @@ -1,4 +1,4 @@ -# Python 2's Lib/test/test_grammar.py (r54061) +# Python 2's Lib/test/test_grammar.py (r66189) # Python test set -- part 1, grammar. # This just tests whether the parser accepts them all. @@ -30,13 +30,15 @@ class TokenTests(unittest.TestCase): def testPlainIntegers(self): self.assertEquals(0xff, 255) - self.assertEquals(0o377, 255) - self.assertEquals(2147483647, 0o17777777777) - from sys import maxsize + self.assertEquals(0377, 255) + self.assertEquals(2147483647, 017777777777) + # "0x" is not a valid literal + self.assertRaises(SyntaxError, eval, "0x") + from sys import maxint if maxint == 2147483647: - self.assertEquals(-2147483647-1, -0o20000000000) + self.assertEquals(-2147483647-1, -020000000000) # XXX -2147483648 - self.assert_(0o37777777777 > 0) + self.assert_(037777777777 > 0) self.assert_(0xffffffff > 0) for s in '2147483648', '040000000000', '0x100000000': try: @@ -44,8 +46,8 @@ class TokenTests(unittest.TestCase): except OverflowError: self.fail("OverflowError on huge integer literal %r" % s) elif maxint == 9223372036854775807: - self.assertEquals(-9223372036854775807-1, -0o1000000000000000000000) - self.assert_(0o1777777777777777777777 > 0) + self.assertEquals(-9223372036854775807-1, -01000000000000000000000) + self.assert_(01777777777777777777777 > 0) self.assert_(0xffffffffffffffff > 0) for s in '9223372036854775808', '02000000000000000000000', \ '0x10000000000000000': @@ -57,14 +59,14 @@ class TokenTests(unittest.TestCase): self.fail('Weird maxint value %r' % maxint) def testLongIntegers(self): - x = 0 - x = 0 - x = 0xffffffffffffffff - x = 0xffffffffffffffff - x = 0o77777777777777777 - x = 0o77777777777777777 - x = 123456789012345678901234567890 - x = 123456789012345678901234567890 + x = 0L + x = 0l + x = 0xffffffffffffffffL + x = 0xffffffffffffffffl + x = 077777777777777777L + x = 077777777777777777l + x = 123456789012345678901234567890L + x = 123456789012345678901234567890l def testFloats(self): x = 3.14 @@ -152,27 +154,27 @@ class GrammarTests(unittest.TestCase): f1(*(), **{}) def f2(one_argument): pass def f3(two, arguments): pass - def f4(two, xxx_todo_changeme): (compound, (argument, list)) = xxx_todo_changeme; pass - def f5(xxx_todo_changeme1, two): (compound, first) = xxx_todo_changeme1; pass - self.assertEquals(f2.__code__.co_varnames, ('one_argument',)) - self.assertEquals(f3.__code__.co_varnames, ('two', 'arguments')) + def f4(two, (compound, (argument, list))): pass + def f5((compound, first), two): pass + self.assertEquals(f2.func_code.co_varnames, ('one_argument',)) + self.assertEquals(f3.func_code.co_varnames, ('two', 'arguments')) if sys.platform.startswith('java'): - self.assertEquals(f4.__code__.co_varnames, + self.assertEquals(f4.func_code.co_varnames, ('two', '(compound, (argument, list))', 'compound', 'argument', 'list',)) - self.assertEquals(f5.__code__.co_varnames, + self.assertEquals(f5.func_code.co_varnames, ('(compound, first)', 'two', 'compound', 'first')) else: - self.assertEquals(f4.__code__.co_varnames, + self.assertEquals(f4.func_code.co_varnames, ('two', '.1', 'compound', 'argument', 'list')) - self.assertEquals(f5.__code__.co_varnames, + self.assertEquals(f5.func_code.co_varnames, ('.0', 'two', 'compound', 'first')) def a1(one_arg,): pass def a2(two, args,): pass def v0(*rest): pass def v1(a, *rest): pass def v2(a, b, *rest): pass - def v3(a, xxx_todo_changeme2, *rest): (b, c) = xxx_todo_changeme2; return a, b, c, rest + def v3(a, (b, c), *rest): return a, b, c, rest f1() f2(1) @@ -201,9 +203,9 @@ class GrammarTests(unittest.TestCase): # ceval unpacks the formal arguments into the first argcount names; # thus, the names nested inside tuples must appear after these names. if sys.platform.startswith('java'): - self.assertEquals(v3.__code__.co_varnames, ('a', '(b, c)', 'rest', 'b', 'c')) + self.assertEquals(v3.func_code.co_varnames, ('a', '(b, c)', 'rest', 'b', 'c')) else: - self.assertEquals(v3.__code__.co_varnames, ('a', '.1', 'rest', 'b', 'c')) + self.assertEquals(v3.func_code.co_varnames, ('a', '.1', 'rest', 'b', 'c')) self.assertEquals(v3(1, (2, 3), 4), (1, 2, 3, (4,))) def d01(a=1): pass d01() @@ -277,17 +279,29 @@ class GrammarTests(unittest.TestCase): d22v(*(1, 2, 3, 4)) d22v(1, 2, *(3, 4, 5)) d22v(1, *(2, 3), **{'d': 4}) - def d31v(xxx_todo_changeme3): (x) = xxx_todo_changeme3; pass + def d31v((x)): pass d31v(1) - def d32v(xxx_todo_changeme4): (x,) = xxx_todo_changeme4; pass + def d32v((x,)): pass d32v((1,)) + # keyword arguments after *arglist + def f(*args, **kwargs): + return args, kwargs + self.assertEquals(f(1, x=2, *[3, 4], y=5), ((1, 3, 4), + {'x':2, 'y':5})) + self.assertRaises(SyntaxError, eval, "f(1, *(2,3), 4)") + self.assertRaises(SyntaxError, eval, "f(1, x=2, *(3,4), x=5)") + + # Check ast errors in *args and *kwargs + check_syntax_error(self, "f(*g(1=2))") + check_syntax_error(self, "f(**g(1=2))") + def testLambdef(self): ### lambdef: 'lambda' [varargslist] ':' test l1 = lambda : 0 self.assertEquals(l1(), 0) l2 = lambda : a[d] # XXX just testing the expression - l3 = lambda : [2 < x for x in [-1, 3, 0]] + l3 = lambda : [2 < x for x in [-1, 3, 0L]] self.assertEquals(l3(), [0, 1, 0]) l4 = lambda x = lambda y = lambda z=1 : z : y() : x() self.assertEquals(l4(), 1) @@ -295,6 +309,7 @@ class GrammarTests(unittest.TestCase): self.assertEquals(l5(1, 2), 5) self.assertEquals(l5(1, 2, 3), 6) check_syntax_error(self, "lambda x: x = 2") + check_syntax_error(self, "lambda (None,): None") ### stmt: simple_stmt | compound_stmt # Tested below @@ -325,36 +340,36 @@ class GrammarTests(unittest.TestCase): def testPrintStmt(self): # 'print' (test ',')* [test] - import io + import StringIO # Can't test printing to real stdout without comparing output # which is not available in unittest. save_stdout = sys.stdout - sys.stdout = io.StringIO() + sys.stdout = StringIO.StringIO() - print(1, 2, 3) - print(1, 2, 3, end=' ') - print() - print(0 or 1, 0 or 1, end=' ') - print(0 or 1) + print 1, 2, 3 + print 1, 2, 3, + print + print 0 or 1, 0 or 1, + print 0 or 1 # 'print' '>>' test ',' - print(1, 2, 3, file=sys.stdout) - print(1, 2, 3, end=' ', file=sys.stdout) - print(file=sys.stdout) - print(0 or 1, 0 or 1, end=' ', file=sys.stdout) - print(0 or 1, file=sys.stdout) + print >> sys.stdout, 1, 2, 3 + print >> sys.stdout, 1, 2, 3, + print >> sys.stdout + print >> sys.stdout, 0 or 1, 0 or 1, + print >> sys.stdout, 0 or 1 # test printing to an instance class Gulp: def write(self, msg): pass gulp = Gulp() - print(1, 2, 3, file=gulp) - print(1, 2, 3, end=' ', file=gulp) - print(file=gulp) - print(0 or 1, 0 or 1, end=' ', file=gulp) - print(0 or 1, file=gulp) + print >> gulp, 1, 2, 3 + print >> gulp, 1, 2, 3, + print >> gulp + print >> gulp, 0 or 1, 0 or 1, + print >> gulp, 0 or 1 # test print >> None def driver(): @@ -368,13 +383,13 @@ class GrammarTests(unittest.TestCase): # we should see this once def tellme(file=sys.stdout): - print('hello world', file=file) + print >> file, 'hello world' driver() # we should not see this at all def tellme(file=None): - print('goodbye universe', file=file) + print >> file, 'goodbye universe' driver() @@ -461,7 +476,7 @@ hello world continue except: raise - if count > 2 or big_hippo != 1: + if count > 2 or big_hippo <> 1: self.fail("continue then break in try/except in loop broken!") test_inner() @@ -478,7 +493,7 @@ hello world def testRaise(self): # 'raise' test [',' test] - try: raise RuntimeError('just testing') + try: raise RuntimeError, 'just testing' except RuntimeError: pass try: raise KeyboardInterrupt except KeyboardInterrupt: pass @@ -506,33 +521,33 @@ hello world # 'exec' expr ['in' expr [',' expr]] z = None del z - exec('z=1+1\n') + exec 'z=1+1\n' if z != 2: self.fail('exec \'z=1+1\'\\n') del z - exec('z=1+1') + exec 'z=1+1' if z != 2: self.fail('exec \'z=1+1\'') z = None del z import types if hasattr(types, "UnicodeType"): - exec(r"""if 1: + exec r"""if 1: exec u'z=1+1\n' if z != 2: self.fail('exec u\'z=1+1\'\\n') del z exec u'z=1+1' - if z != 2: self.fail('exec u\'z=1+1\'')""") + if z != 2: self.fail('exec u\'z=1+1\'')""" g = {} - exec('z = 1', g) - if '__builtins__' in g: del g['__builtins__'] + exec 'z = 1' in g + if g.has_key('__builtins__'): del g['__builtins__'] if g != {'z': 1}: self.fail('exec \'z = 1\' in g') g = {} l = {} import warnings warnings.filterwarnings("ignore", "global statement", module="<string>") - exec('global a; a = 1; b = 2', g, l) - if '__builtins__' in g: del g['__builtins__'] - if '__builtins__' in l: del l['__builtins__'] + exec 'global a; a = 1; b = 2' in g, l + if g.has_key('__builtins__'): del g['__builtins__'] + if l.has_key('__builtins__'): del l['__builtins__'] if (g, l) != ({'a':1}, {'b':2}): self.fail('exec ... in g (%s), l (%s)' %(g,l)) @@ -544,7 +559,7 @@ hello world assert 1, lambda x:x+1 try: assert 0, "msg" - except AssertionError as e: + except AssertionError, e: self.assertEquals(e.args[0], "msg") else: if __debug__: @@ -572,6 +587,15 @@ hello world while 0: pass else: pass + # Issue1920: "while 0" is optimized away, + # ensure that the "else" clause is still present. + x = 0 + while 0: + x = 1 + else: + x = 2 + self.assertEquals(x, 2) + def testFor(self): # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] for i in 1, 2, 3: pass @@ -602,7 +626,7 @@ hello world def testTry(self): ### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite] ### | 'try' ':' suite 'finally' ':' suite - ### except_clause: 'except' [expr [',' expr]] + ### except_clause: 'except' [expr [('as' | ',') expr]] try: 1/0 except ZeroDivisionError: @@ -611,7 +635,7 @@ hello world pass try: 1/0 except EOFError: pass - except TypeError, msg: pass + except TypeError as msg: pass except RuntimeError, msg: pass except: pass else: pass @@ -655,7 +679,7 @@ hello world x = (1 == 1) if 1 == 1: pass if 1 != 1: pass - if 1 != 1: pass + if 1 <> 1: pass if 1 < 1: pass if 1 > 1: pass if 1 <= 1: pass @@ -664,7 +688,7 @@ hello world if 1 is not 1: pass if 1 in (): pass if 1 not in (): pass - if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 != 1 in 1 not in 1 is 1 is not 1: pass + if 1 < 1 > 1 == 1 >= 1 <= 1 <> 1 != 1 in 1 not in 1 is 1 is not 1: pass def testBinaryMaskOps(self): x = 1 & 1 @@ -747,9 +771,9 @@ hello world x = {'one': 1, 'two': 2,} x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6} - x = repr(x) - x = repr(1 or 2 or 3) - self.assertEqual(repr((1,2)), '(1, 2)') + x = `x` + x = `1 or 2 or 3` + self.assertEqual(`1,2`, '(1, 2)') x = x x = 'x' @@ -770,6 +794,16 @@ hello world def meth1(self): pass def meth2(self, arg): pass def meth3(self, a1, a2): pass + # decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE + # decorators: decorator+ + # decorated: decorators (classdef | funcdef) + def class_decorator(x): + x.decorated = True + return x + @class_decorator + class G: + pass + self.assertEqual(G.decorated, True) def testListcomps(self): # list comprehension tests @@ -837,9 +871,9 @@ hello world def testGenexps(self): # generator expression tests g = ([x for x in range(10)] for x in range(1)) - self.assertEqual(next(g), [x for x in range(10)]) + self.assertEqual(g.next(), [x for x in range(10)]) try: - next(g) + g.next() self.fail('should produce StopIteration exception') except StopIteration: pass @@ -847,7 +881,7 @@ hello world a = 1 try: g = (a for d in a) - next(g) + g.next() self.fail('should produce TypeError') except TypeError: pass @@ -892,7 +926,7 @@ hello world # Test ifelse expressions in various cases def _checkeval(msg, ret): "helper to check that evaluation of expressions is done correctly" - print(x) + print x return ret self.assertEqual([ x() for x in lambda: True, lambda: False if x() ], [True]) diff --git a/Lib/lib2to3/tests/data/py3_test_grammar.py b/Lib/lib2to3/tests/data/py3_test_grammar.py index 2fb3217..9a88463 100644 --- a/Lib/lib2to3/tests/data/py3_test_grammar.py +++ b/Lib/lib2to3/tests/data/py3_test_grammar.py @@ -8,7 +8,7 @@ # regression test, the filterwarnings() call has been added to # regrtest.py. -from test.test_support import run_unittest, check_syntax_error +from test.support import run_unittest, check_syntax_error import unittest import sys # testing import * @@ -32,8 +32,10 @@ class TokenTests(unittest.TestCase): self.assertEquals(0o377, 255) self.assertEquals(2147483647, 0o17777777777) self.assertEquals(0b1001, 9) + # "0x" is not a valid literal + self.assertRaises(SyntaxError, eval, "0x") from sys import maxsize - if maxint == 2147483647: + if maxsize == 2147483647: self.assertEquals(-2147483647-1, -0o20000000000) # XXX -2147483648 self.assert_(0o37777777777 > 0) @@ -45,7 +47,7 @@ class TokenTests(unittest.TestCase): x = eval(s) except OverflowError: self.fail("OverflowError on huge integer literal %r" % s) - elif maxint == 9223372036854775807: + elif maxsize == 9223372036854775807: self.assertEquals(-9223372036854775807-1, -0o1000000000000000000000) self.assert_(0o1777777777777777777777 > 0) self.assert_(0xffffffffffffffff > 0) @@ -58,7 +60,7 @@ class TokenTests(unittest.TestCase): except OverflowError: self.fail("OverflowError on huge integer literal %r" % s) else: - self.fail('Weird maxint value %r' % maxint) + self.fail('Weird maxsize value %r' % maxsize) def testLongIntegers(self): x = 0 @@ -263,6 +265,14 @@ class GrammarTests(unittest.TestCase): d22v(*(1, 2, 3, 4)) d22v(1, 2, *(3, 4, 5)) d22v(1, *(2, 3), **{'d': 4}) + + # keyword argument type tests + try: + str('x', **{b'foo':1 }) + except TypeError: + pass + else: + self.fail('Bytes should not work as keyword argument names') # keyword only argument tests def pos0key1(*, key): return key pos0key1(key=100) @@ -274,6 +284,14 @@ class GrammarTests(unittest.TestCase): pos2key2dict(1,2,k2=100,tokwarg1=100,tokwarg2=200) pos2key2dict(1,2,tokwarg1=100,tokwarg2=200, k2=100) + # keyword arguments after *arglist + def f(*args, **kwargs): + return args, kwargs + self.assertEquals(f(1, x=2, *[3, 4], y=5), ((1, 3, 4), + {'x':2, 'y':5})) + self.assertRaises(SyntaxError, eval, "f(1, *(2,3), 4)") + self.assertRaises(SyntaxError, eval, "f(1, x=2, *(3,4), x=5)") + # argument annotation tests def f(x) -> list: pass self.assertEquals(f.__annotations__, {'return': list}) @@ -308,6 +326,10 @@ class GrammarTests(unittest.TestCase): def f(*, k=1): return closure def f() -> int: return closure + # Check ast errors in *args and *kwargs + check_syntax_error(self, "f(*g(1=2))") + check_syntax_error(self, "f(**g(1=2))") + def testLambdef(self): ### lambdef: 'lambda' [varargslist] ':' test l1 = lambda : 0 @@ -321,6 +343,7 @@ class GrammarTests(unittest.TestCase): self.assertEquals(l5(1, 2), 5) self.assertEquals(l5(1, 2, 3), 6) check_syntax_error(self, "lambda x: x = 2") + check_syntax_error(self, "lambda (None,): None") l6 = lambda x, y, *, k=20: x+y+k self.assertEquals(l6(1,2), 1+2+20) self.assertEquals(l6(1,2,k=10), 1+2+10) @@ -498,6 +521,15 @@ class GrammarTests(unittest.TestCase): while 0: pass else: pass + # Issue1920: "while 0" is optimized away, + # ensure that the "else" clause is still present. + x = 0 + while 0: + x = 1 + else: + x = 2 + self.assertEquals(x, 2) + def testFor(self): # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite] for i in 1, 2, 3: pass diff --git a/Lib/lib2to3/tests/test_fixers.py b/Lib/lib2to3/tests/test_fixers.py index eb5a939..91d51cf 100755 --- a/Lib/lib2to3/tests/test_fixers.py +++ b/Lib/lib2to3/tests/test_fixers.py @@ -385,6 +385,16 @@ class Test_print(FixerTestCase): a = """print()""" self.check(b, a) + def test_4(self): + # from bug 3000 + b = """print whatever; print""" + a = """print(whatever); print()""" + self.check(b, a) + + def test_5(self): + b = """print; print whatever;""" + a = """print(); print(whatever);""" + def test_tuple(self): b = """print (a, b, c)""" a = """print((a, b, c))""" @@ -2379,6 +2389,15 @@ class Test_numliterals(FixerTestCase): a = """b = 0x12""" self.check(b, a) + def test_comments_and_spacing(self): + b = """b = 0x12L""" + a = """b = 0x12""" + self.check(b, a) + + b = """b = 0755 # spam""" + a = """b = 0o755 # spam""" + self.check(b, a) + def test_unchanged_int(self): s = """5""" self.unchanged(s) @@ -3430,6 +3449,133 @@ class Test_paren(FixerTestCase): s = """[i for i in m]""" self.unchanged(s) +class Test_metaclass(FixerTestCase): + + fixer = 'metaclass' + + def test_unchanged(self): + self.unchanged("class X(): pass") + self.unchanged("class X(object): pass") + self.unchanged("class X(object1, object2): pass") + self.unchanged("class X(object1, object2, object3): pass") + self.unchanged("class X(metaclass=Meta): pass") + self.unchanged("class X(b, arg=23, metclass=Meta): pass") + self.unchanged("class X(b, arg=23, metaclass=Meta, other=42): pass") + + s = """ + class X: + def __metaclass__(self): pass + """ + self.unchanged(s) + + def test_comments(self): + b = """ + class X: + # hi + __metaclass__ = AppleMeta + """ + a = """ + class X(metaclass=AppleMeta): + # hi + pass + """ + self.check(b, a) + + b = """ + class X: + __metaclass__ = Meta + # Bedtime! + """ + a = """ + class X(metaclass=Meta): + pass + # Bedtime! + """ + self.check(b, a) + + def test_meta(self): + # no-parent class, odd body + b = """ + class X(): + __metaclass__ = Q + pass + """ + a = """ + class X(metaclass=Q): + pass + """ + self.check(b, a) + + # one parent class, no body + b = """class X(object): __metaclass__ = Q""" + a = """class X(object, metaclass=Q): pass""" + self.check(b, a) + + + # one parent, simple body + b = """ + class X(object): + __metaclass__ = Meta + bar = 7 + """ + a = """ + class X(object, metaclass=Meta): + bar = 7 + """ + self.check(b, a) + + b = """ + class X: + __metaclass__ = Meta; x = 4; g = 23 + """ + a = """ + class X(metaclass=Meta): + x = 4; g = 23 + """ + self.check(b, a) + + # one parent, simple body, __metaclass__ last + b = """ + class X(object): + bar = 7 + __metaclass__ = Meta + """ + a = """ + class X(object, metaclass=Meta): + bar = 7 + """ + self.check(b, a) + + # redefining __metaclass__ + b = """ + class X(): + __metaclass__ = A + __metaclass__ = B + bar = 7 + """ + a = """ + class X(metaclass=B): + bar = 7 + """ + self.check(b, a) + + # multiple inheritance, simple body + b = """ + class X(clsA, clsB): + __metaclass__ = Meta + bar = 7 + """ + a = """ + class X(clsA, clsB, metaclass=Meta): + bar = 7 + """ + self.check(b, a) + + # keywords in the class statement + b = """class m(a, arg=23): __metaclass__ = Meta""" + a = """class m(a, arg=23, metaclass=Meta): pass""" + self.check(b, a) + if __name__ == "__main__": import __main__ |