summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin v. Löwis <martin@v.loewis.de>2008-03-19 05:04:44 (GMT)
committerMartin v. Löwis <martin@v.loewis.de>2008-03-19 05:04:44 (GMT)
commitef04c44e29a8276a484f58d03a75a2dec516302d (patch)
tree6231aa6bb789345a6a86c60b0f547a7bfa19927f
parentc42bcbb1f07723476cccd352eb0ae98ad2d1a809 (diff)
downloadcpython-ef04c44e29a8276a484f58d03a75a2dec516302d.zip
cpython-ef04c44e29a8276a484f58d03a75a2dec516302d.tar.gz
cpython-ef04c44e29a8276a484f58d03a75a2dec516302d.tar.bz2
Merged revisions 61596-61597 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r61596 | martin.v.loewis | 2008-03-18 23:43:46 -0500 (Di, 18 Mär 2008) | 2 lines Import lib2to3. ........ r61597 | martin.v.loewis | 2008-03-18 23:58:04 -0500 (Di, 18 Mär 2008) | 3 lines Initialized merge tracking via "svnmerge" with revisions "1-61595" from svn+ssh://pythondev@svn.python.org/sandbox/trunk/2to3/lib2to3 ........
-rw-r--r--Lib/lib2to3/Grammar.txt153
-rw-r--r--Lib/lib2to3/PatternGrammar.txt28
-rw-r--r--Lib/lib2to3/__init__.py1
-rw-r--r--Lib/lib2to3/fixes/__init__.py1
-rw-r--r--Lib/lib2to3/fixes/basefix.py165
-rw-r--r--Lib/lib2to3/fixes/fix_apply.py58
-rw-r--r--Lib/lib2to3/fixes/fix_basestring.py13
-rw-r--r--Lib/lib2to3/fixes/fix_buffer.py21
-rw-r--r--Lib/lib2to3/fixes/fix_callable.py31
-rw-r--r--Lib/lib2to3/fixes/fix_dict.py99
-rw-r--r--Lib/lib2to3/fixes/fix_except.py89
-rw-r--r--Lib/lib2to3/fixes/fix_exec.py39
-rw-r--r--Lib/lib2to3/fixes/fix_execfile.py37
-rw-r--r--Lib/lib2to3/fixes/fix_filter.py119
-rw-r--r--Lib/lib2to3/fixes/fix_funcattrs.py19
-rw-r--r--Lib/lib2to3/fixes/fix_future.py16
-rw-r--r--Lib/lib2to3/fixes/fix_has_key.py109
-rw-r--r--Lib/lib2to3/fixes/fix_idioms.py134
-rw-r--r--Lib/lib2to3/fixes/fix_imports.py89
-rw-r--r--Lib/lib2to3/fixes/fix_input.py26
-rw-r--r--Lib/lib2to3/fixes/fix_intern.py43
-rw-r--r--Lib/lib2to3/fixes/fix_itertools.py36
-rw-r--r--Lib/lib2to3/fixes/fix_long.py35
-rw-r--r--Lib/lib2to3/fixes/fix_map.py126
-rw-r--r--Lib/lib2to3/fixes/fix_methodattrs.py23
-rw-r--r--Lib/lib2to3/fixes/fix_ne.py22
-rw-r--r--Lib/lib2to3/fixes/fix_next.py104
-rw-r--r--Lib/lib2to3/fixes/fix_nonzero.py20
-rw-r--r--Lib/lib2to3/fixes/fix_numliterals.py27
-rw-r--r--Lib/lib2to3/fixes/fix_print.py81
-rw-r--r--Lib/lib2to3/fixes/fix_raise.py82
-rw-r--r--Lib/lib2to3/fixes/fix_raw_input.py16
-rw-r--r--Lib/lib2to3/fixes/fix_renames.py70
-rw-r--r--Lib/lib2to3/fixes/fix_repr.py22
-rw-r--r--Lib/lib2to3/fixes/fix_standarderror.py18
-rw-r--r--Lib/lib2to3/fixes/fix_throw.py56
-rw-r--r--Lib/lib2to3/fixes/fix_tuple_params.py169
-rw-r--r--Lib/lib2to3/fixes/fix_types.py62
-rw-r--r--Lib/lib2to3/fixes/fix_unicode.py28
-rw-r--r--Lib/lib2to3/fixes/fix_ws_comma.py39
-rw-r--r--Lib/lib2to3/fixes/fix_xrange.py18
-rw-r--r--Lib/lib2to3/fixes/fix_xreadlines.py24
-rw-r--r--Lib/lib2to3/fixes/util.py303
-rw-r--r--Lib/lib2to3/patcomp.py186
-rw-r--r--Lib/lib2to3/pgen2/__init__.py4
-rw-r--r--Lib/lib2to3/pgen2/conv.py257
-rw-r--r--Lib/lib2to3/pgen2/driver.py143
-rw-r--r--Lib/lib2to3/pgen2/grammar.py171
-rw-r--r--Lib/lib2to3/pgen2/literals.py60
-rw-r--r--Lib/lib2to3/pgen2/parse.py207
-rw-r--r--Lib/lib2to3/pgen2/pgen.py384
-rwxr-xr-xLib/lib2to3/pgen2/token.py82
-rw-r--r--Lib/lib2to3/pgen2/tokenize.py405
-rw-r--r--Lib/lib2to3/pygram.py38
-rw-r--r--Lib/lib2to3/pytree.py712
-rwxr-xr-xLib/lib2to3/refactor.py526
-rw-r--r--Lib/lib2to3/tests/__init__.py24
-rw-r--r--Lib/lib2to3/tests/benchmark.py58
-rw-r--r--Lib/lib2to3/tests/data/py2_test_grammar.py922
-rw-r--r--Lib/lib2to3/tests/data/py3_test_grammar.py863
-rwxr-xr-xLib/lib2to3/tests/pytree_idempotency.py92
-rw-r--r--Lib/lib2to3/tests/support.py47
-rw-r--r--Lib/lib2to3/tests/test_all_fixers.py42
-rwxr-xr-xLib/lib2to3/tests/test_fixers.py2918
-rw-r--r--Lib/lib2to3/tests/test_parser.py202
-rwxr-xr-xLib/lib2to3/tests/test_pytree.py440
-rw-r--r--Lib/lib2to3/tests/test_util.py536
67 files changed, 11990 insertions, 0 deletions
diff --git a/Lib/lib2to3/Grammar.txt b/Lib/lib2to3/Grammar.txt
new file mode 100644
index 0000000..abf1e15
--- /dev/null
+++ b/Lib/lib2to3/Grammar.txt
@@ -0,0 +1,153 @@
+# Grammar for Python
+
+# Note: Changing the grammar specified in this file will most likely
+# require corresponding changes in the parser module
+# (../Modules/parsermodule.c). If you can't make the changes to
+# that module yourself, please co-ordinate the required changes
+# with someone who can; ask around on python-dev for help. Fred
+# Drake <fdrake@acm.org> will probably be listening there.
+
+# NOTE WELL: You should also follow all the steps listed in PEP 306,
+# "How to Change Python's Grammar"
+
+# Commands for Kees Blom's railroad program
+#diagram:token NAME
+#diagram:token NUMBER
+#diagram:token STRING
+#diagram:token NEWLINE
+#diagram:token ENDMARKER
+#diagram:token INDENT
+#diagram:output\input python.bla
+#diagram:token DEDENT
+#diagram:output\textwidth 20.04cm\oddsidemargin 0.0cm\evensidemargin 0.0cm
+#diagram:rules
+
+# Start symbols for the grammar:
+# file_input is a module or sequence of commands read from an input file;
+# single_input is a single interactive statement;
+# eval_input is the input for the eval() and input() functions.
+# NB: compound_stmt in single_input is followed by extra NEWLINE!
+file_input: (NEWLINE | stmt)* ENDMARKER
+single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
+eval_input: testlist NEWLINE* ENDMARKER
+
+decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
+decorators: decorator+
+decorated: decorators (classdef | funcdef)
+funcdef: 'def' NAME parameters ['->' test] ':' suite
+parameters: '(' [typedargslist] ')'
+typedargslist: ((tfpdef ['=' test] ',')*
+ ('*' [tname] (',' tname ['=' test])* [',' '**' tname] | '**' tname)
+ | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
+tname: NAME [':' test]
+tfpdef: tname | '(' tfplist ')'
+tfplist: tfpdef (',' tfpdef)* [',']
+varargslist: ((vfpdef ['=' test] ',')*
+ ('*' [vname] (',' vname ['=' test])* [',' '**' vname] | '**' vname)
+ | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
+vname: NAME
+vfpdef: vname | '(' vfplist ')'
+vfplist: vfpdef (',' vfpdef)* [',']
+
+stmt: simple_stmt | compound_stmt
+simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
+small_stmt: (expr_stmt | print_stmt | del_stmt | pass_stmt | flow_stmt |
+ import_stmt | global_stmt | exec_stmt | assert_stmt)
+expr_stmt: testlist (augassign (yield_expr|testlist) |
+ ('=' (yield_expr|testlist))*)
+augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
+ '<<=' | '>>=' | '**=' | '//=')
+# For normal assignments, additional restrictions enforced by the interpreter
+print_stmt: 'print' ( [ test (',' test)* [','] ] |
+ '>>' test [ (',' test)+ [','] ] )
+del_stmt: 'del' exprlist
+pass_stmt: 'pass'
+flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
+break_stmt: 'break'
+continue_stmt: 'continue'
+return_stmt: 'return' [testlist]
+yield_stmt: yield_expr
+raise_stmt: 'raise' [test ['from' test | ',' test [',' test]]]
+import_stmt: import_name | import_from
+import_name: 'import' dotted_as_names
+import_from: ('from' ('.'* dotted_name | '.'+)
+ 'import' ('*' | '(' import_as_names ')' | import_as_names))
+import_as_name: NAME ['as' NAME]
+dotted_as_name: dotted_name ['as' NAME]
+import_as_names: import_as_name (',' import_as_name)* [',']
+dotted_as_names: dotted_as_name (',' dotted_as_name)*
+dotted_name: NAME ('.' NAME)*
+global_stmt: ('global' | 'nonlocal') NAME (',' NAME)*
+exec_stmt: 'exec' expr ['in' test [',' test]]
+assert_stmt: 'assert' test [',' test]
+
+compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated
+if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
+while_stmt: 'while' test ':' suite ['else' ':' suite]
+for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
+try_stmt: ('try' ':' suite
+ ((except_clause ':' suite)+
+ ['else' ':' suite]
+ ['finally' ':' suite] |
+ 'finally' ':' suite))
+with_stmt: 'with' test [ with_var ] ':' suite
+with_var: 'as' expr
+# NB compile.c makes sure that the default except clause is last
+except_clause: 'except' [test [(',' | 'as') test]]
+suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
+
+# Backward compatibility cruft to support:
+# [ x for x in lambda: True, lambda: False if x() ]
+# even while also allowing:
+# lambda x: 5 if x else 2
+# (But not a mix of the two)
+testlist_safe: old_test [(',' old_test)+ [',']]
+old_test: or_test | old_lambdef
+old_lambdef: 'lambda' [varargslist] ':' old_test
+
+test: or_test ['if' or_test 'else' test] | lambdef
+or_test: and_test ('or' and_test)*
+and_test: not_test ('and' not_test)*
+not_test: 'not' not_test | comparison
+comparison: expr (comp_op expr)*
+comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+expr: xor_expr ('|' xor_expr)*
+xor_expr: and_expr ('^' and_expr)*
+and_expr: shift_expr ('&' shift_expr)*
+shift_expr: arith_expr (('<<'|'>>') arith_expr)*
+arith_expr: term (('+'|'-') term)*
+term: factor (('*'|'/'|'%'|'//') factor)*
+factor: ('+'|'-'|'~') factor | power
+power: atom trailer* ['**' factor]
+atom: ('(' [yield_expr|testlist_gexp] ')' |
+ '[' [listmaker] ']' |
+ '{' [dictsetmaker] '}' |
+ '`' testlist1 '`' |
+ NAME | NUMBER | STRING+ | '.' '.' '.')
+listmaker: test ( comp_for | (',' test)* [','] )
+testlist_gexp: test ( comp_for | (',' test)* [','] )
+lambdef: 'lambda' [varargslist] ':' test
+trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
+subscriptlist: subscript (',' subscript)* [',']
+subscript: test | [test] ':' [test] [sliceop]
+sliceop: ':' [test]
+exprlist: expr (',' expr)* [',']
+testlist: test (',' test)* [',']
+dictsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* [','])) |
+ (test (comp_for | (',' test)* [','])) )
+
+classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
+
+arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test)
+argument: test [comp_for] | test '=' test # Really [keyword '='] test
+
+comp_iter: comp_for | comp_if
+comp_for: 'for' exprlist 'in' testlist_safe [comp_iter]
+comp_if: 'if' old_test [comp_iter]
+
+testlist1: test (',' test)*
+
+# not used in grammar, but may appear in "node" passed from Parser to Compiler
+encoding_decl: NAME
+
+yield_expr: 'yield' [testlist]
diff --git a/Lib/lib2to3/PatternGrammar.txt b/Lib/lib2to3/PatternGrammar.txt
new file mode 100644
index 0000000..36bf814
--- /dev/null
+++ b/Lib/lib2to3/PatternGrammar.txt
@@ -0,0 +1,28 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+# A grammar to describe tree matching patterns.
+# Not shown here:
+# - 'TOKEN' stands for any token (leaf node)
+# - 'any' stands for any node (leaf or interior)
+# With 'any' we can still specify the sub-structure.
+
+# The start symbol is 'Matcher'.
+
+Matcher: Alternatives ENDMARKER
+
+Alternatives: Alternative ('|' Alternative)*
+
+Alternative: (Unit | NegatedUnit)+
+
+Unit: [NAME '='] ( STRING [Repeater]
+ | NAME [Details] [Repeater]
+ | '(' Alternatives ')' [Repeater]
+ | '[' Alternatives ']'
+ )
+
+NegatedUnit: 'not' (STRING | NAME [Details] | '(' Alternatives ')')
+
+Repeater: '*' | '+' | '{' NUMBER [',' NUMBER] '}'
+
+Details: '<' Alternatives '>'
diff --git a/Lib/lib2to3/__init__.py b/Lib/lib2to3/__init__.py
new file mode 100644
index 0000000..ea30561
--- /dev/null
+++ b/Lib/lib2to3/__init__.py
@@ -0,0 +1 @@
+#empty
diff --git a/Lib/lib2to3/fixes/__init__.py b/Lib/lib2to3/fixes/__init__.py
new file mode 100644
index 0000000..b93054b
--- /dev/null
+++ b/Lib/lib2to3/fixes/__init__.py
@@ -0,0 +1 @@
+# Dummy file to make this directory a package.
diff --git a/Lib/lib2to3/fixes/basefix.py b/Lib/lib2to3/fixes/basefix.py
new file mode 100644
index 0000000..8eb4278
--- /dev/null
+++ b/Lib/lib2to3/fixes/basefix.py
@@ -0,0 +1,165 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Base class for fixers (optional, but recommended)."""
+
+# Python imports
+import logging
+import itertools
+
+# Get a usable 'set' constructor
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+# Local imports
+from ..patcomp import PatternCompiler
+from .. import pygram
+
+class BaseFix(object):
+
+ """Optional base class for fixers.
+
+ The subclass name must be FixFooBar where FooBar is the result of
+ removing underscores and capitalizing the words of the fix name.
+ For example, the class name for a fixer named 'has_key' should be
+ FixHasKey.
+ """
+
+ PATTERN = None # Most subclasses should override with a string literal
+ pattern = None # Compiled pattern, set by compile_pattern()
+ options = None # Options object passed to initializer
+ filename = None # The filename (set by set_filename)
+ logger = None # A logger (set by set_filename)
+ numbers = itertools.count(1) # For new_name()
+ used_names = set() # A set of all used NAMEs
+ order = "post" # Does the fixer prefer pre- or post-order traversal
+ explicit = False # Is this ignored by refactor.py -f all?
+
+ # Shortcut for access to Python grammar symbols
+ syms = pygram.python_symbols
+
+ def __init__(self, options, log):
+ """Initializer. Subclass may override.
+
+ Args:
+ options: an optparse.Values instance which can be used
+ to inspect the command line options.
+ log: a list to append warnings and other messages to.
+ """
+ self.options = options
+ self.log = log
+ self.compile_pattern()
+
+ def compile_pattern(self):
+ """Compiles self.PATTERN into self.pattern.
+
+ Subclass may override if it doesn't want to use
+ self.{pattern,PATTERN} in .match().
+ """
+ if self.PATTERN is not None:
+ self.pattern = PatternCompiler().compile_pattern(self.PATTERN)
+
+ def set_filename(self, filename):
+ """Set the filename, and a logger derived from it.
+
+ The main refactoring tool should call this.
+ """
+ self.filename = filename
+ self.logger = logging.getLogger(filename)
+
+ def match(self, node):
+ """Returns match for a given parse tree node.
+
+ Should return a true or false object (not necessarily a bool).
+ It may return a non-empty dict of matching sub-nodes as
+ returned by a matching pattern.
+
+ Subclass may override.
+ """
+ results = {"node": node}
+ return self.pattern.match(node, results) and results
+
+ def transform(self, node, results):
+ """Returns the transformation for a given parse tree node.
+
+ Args:
+ node: the root of the parse tree that matched the fixer.
+ results: a dict mapping symbolic names to part of the match.
+
+ Returns:
+ None, or a node that is a modified copy of the
+ argument node. The node argument may also be modified in-place to
+ effect the same change.
+
+ Subclass *must* override.
+ """
+ raise NotImplementedError()
+
+ def parenthesize(self, node):
+ """Wrapper around pygram.parenthesize()."""
+ return pygram.parenthesize(node)
+
+ def new_name(self, template="xxx_todo_changeme"):
+ """Return a string suitable for use as an identifier
+
+ The new name is guaranteed not to conflict with other identifiers.
+ """
+ name = template
+ while name in self.used_names:
+ name = template + str(self.numbers.next())
+ self.used_names.add(name)
+ return name
+
+ def log_message(self, message):
+ if self.first_log:
+ self.first_log = False
+ self.log.append("### In file %s ###" % self.filename)
+ self.log.append(message)
+
+ def cannot_convert(self, node, reason=None):
+ """Warn the user that a given chunk of code is not valid Python 3,
+ but that it cannot be converted automatically.
+
+ First argument is the top-level node for the code in question.
+ Optional second argument is why it can't be converted.
+ """
+ lineno = node.get_lineno()
+ for_output = node.clone()
+ for_output.set_prefix("")
+ msg = "Line %d: could not convert: %s"
+ self.log_message(msg % (lineno, for_output))
+ if reason:
+ self.log_message(reason)
+
+ def warning(self, node, reason):
+ """Used for warning the user about possible uncertainty in the
+ translation.
+
+ First argument is the top-level node for the code in question.
+ Optional second argument is why it can't be converted.
+ """
+ lineno = node.get_lineno()
+ self.log_message("Line %d: %s" % (lineno, reason))
+
+ def start_tree(self, tree, filename):
+ """Some fixers need to maintain tree-wide state.
+ This method is called once, at the start of tree fix-up.
+
+ tree - the root node of the tree to be processed.
+ filename - the name of the file the tree came from.
+ """
+ self.used_names = tree.used_names
+ self.set_filename(filename)
+ self.numbers = itertools.count(1)
+ self.first_log = True
+
+ def finish_tree(self, tree, filename):
+ """Some fixers need to maintain tree-wide state.
+ This method is called once, at the conclusion of tree fix-up.
+
+ tree - the root node of the tree to be processed.
+ filename - the name of the file the tree came from.
+ """
+ pass
diff --git a/Lib/lib2to3/fixes/fix_apply.py b/Lib/lib2to3/fixes/fix_apply.py
new file mode 100644
index 0000000..f233224
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_apply.py
@@ -0,0 +1,58 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for apply().
+
+This converts apply(func, v, k) into (func)(*v, **k)."""
+
+# Local imports
+from .. import pytree
+from ..pgen2 import token
+from . import basefix
+from .util import Call, Comma
+
+class FixApply(basefix.BaseFix):
+
+ PATTERN = """
+ power< 'apply'
+ trailer<
+ '('
+ arglist<
+ (not argument<NAME '=' any>) func=any ','
+ (not argument<NAME '=' any>) args=any [','
+ (not argument<NAME '=' any>) kwds=any] [',']
+ >
+ ')'
+ >
+ >
+ """
+
+ def transform(self, node, results):
+ syms = self.syms
+ assert results
+ func = results["func"]
+ args = results["args"]
+ kwds = results.get("kwds")
+ prefix = node.get_prefix()
+ func = func.clone()
+ if (func.type not in (token.NAME, syms.atom) and
+ (func.type != syms.power or
+ func.children[-2].type == token.DOUBLESTAR)):
+ # Need to parenthesize
+ func = self.parenthesize(func)
+ func.set_prefix("")
+ args = args.clone()
+ args.set_prefix("")
+ if kwds is not None:
+ kwds = kwds.clone()
+ kwds.set_prefix("")
+ l_newargs = [pytree.Leaf(token.STAR, "*"), args]
+ if kwds is not None:
+ l_newargs.extend([Comma(),
+ pytree.Leaf(token.DOUBLESTAR, "**"),
+ kwds])
+ l_newargs[-2].set_prefix(" ") # that's the ** token
+ # XXX Sometimes we could be cleverer, e.g. apply(f, (x, y) + t)
+ # can be translated into f(x, y, *t) instead of f(*(x, y) + t)
+ #new = pytree.Node(syms.power, (func, ArgList(l_newargs)))
+ return Call(func, l_newargs, prefix=prefix)
diff --git a/Lib/lib2to3/fixes/fix_basestring.py b/Lib/lib2to3/fixes/fix_basestring.py
new file mode 100644
index 0000000..6d753d8
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_basestring.py
@@ -0,0 +1,13 @@
+"""Fixer for basestring -> str."""
+# Author: Christian Heimes
+
+# Local imports
+from . import basefix
+from .util import Name
+
+class FixBasestring(basefix.BaseFix):
+
+ PATTERN = "'basestring'"
+
+ def transform(self, node, results):
+ return Name("str", prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_buffer.py b/Lib/lib2to3/fixes/fix_buffer.py
new file mode 100644
index 0000000..13168d6
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_buffer.py
@@ -0,0 +1,21 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer that changes buffer(...) into memoryview(...)."""
+
+# Local imports
+from . import basefix
+from .util import Name
+
+
+class FixBuffer(basefix.BaseFix):
+
+ explicit = True # The user must ask for this fixer
+
+ PATTERN = """
+ power< name='buffer' trailer< '(' [any] ')' > >
+ """
+
+ def transform(self, node, results):
+ name = results["name"]
+ name.replace(Name("memoryview", prefix=name.get_prefix()))
diff --git a/Lib/lib2to3/fixes/fix_callable.py b/Lib/lib2to3/fixes/fix_callable.py
new file mode 100644
index 0000000..90b3515
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_callable.py
@@ -0,0 +1,31 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for callable().
+
+This converts callable(obj) into hasattr(obj, '__call__')."""
+
+# Local imports
+from .. import pytree
+from . import basefix
+from .util import Call, Name, String
+
+class FixCallable(basefix.BaseFix):
+
+ # Ignore callable(*args) or use of keywords.
+ # Either could be a hint that the builtin callable() is not being used.
+ PATTERN = """
+ power< 'callable'
+ trailer< lpar='('
+ ( not(arglist | argument<any '=' any>) func=any
+ | func=arglist<(not argument<any '=' any>) any ','> )
+ rpar=')' >
+ after=any*
+ >
+ """
+
+ def transform(self, node, results):
+ func = results["func"]
+
+ args = [func.clone(), String(', '), String("'__call__'")]
+ return Call(Name("hasattr"), args, prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_dict.py b/Lib/lib2to3/fixes/fix_dict.py
new file mode 100644
index 0000000..f76ceb4
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_dict.py
@@ -0,0 +1,99 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for dict methods.
+
+d.keys() -> list(d.keys())
+d.items() -> list(d.items())
+d.values() -> list(d.values())
+
+d.iterkeys() -> iter(d.keys())
+d.iteritems() -> iter(d.items())
+d.itervalues() -> iter(d.values())
+
+Except in certain very specific contexts: the iter() can be dropped
+when the context is list(), sorted(), iter() or for...in; the list()
+can be dropped when the context is list() or sorted() (but not iter()
+or for...in!). Special contexts that apply to both: list(), sorted(), tuple()
+set(), any(), all(), sum().
+
+Note: iter(d.keys()) could be written as iter(d) but since the
+original d.iterkeys() was also redundant we don't fix this. And there
+are (rare) contexts where it makes a difference (e.g. when passing it
+as an argument to a function that introspects the argument).
+"""
+
+# Local imports
+from .. import pytree
+from .. import patcomp
+from ..pgen2 import token
+from . import basefix
+from .util import Name, Call, LParen, RParen, ArgList, Dot, set
+
+
+exempt = set(["sorted", "list", "set", "any", "all", "tuple", "sum"])
+iter_exempt = exempt | set(["iter"])
+
+
+class FixDict(basefix.BaseFix):
+ PATTERN = """
+ power< head=any+
+ trailer< '.' method=('keys'|'items'|'values'|
+ 'iterkeys'|'iteritems'|'itervalues') >
+ parens=trailer< '(' ')' >
+ tail=any*
+ >
+ """
+
+ def transform(self, node, results):
+ head = results["head"]
+ method = results["method"][0] # Extract node for method name
+ tail = results["tail"]
+ syms = self.syms
+ method_name = method.value
+ isiter = method_name.startswith("iter")
+ if isiter:
+ method_name = method_name[4:]
+ assert method_name in ("keys", "items", "values"), repr(method)
+ head = [n.clone() for n in head]
+ tail = [n.clone() for n in tail]
+ special = not tail and self.in_special_context(node, isiter)
+ args = head + [pytree.Node(syms.trailer,
+ [Dot(),
+ Name(method_name,
+ prefix=method.get_prefix())]),
+ results["parens"].clone()]
+ new = pytree.Node(syms.power, args)
+ if not special:
+ new.set_prefix("")
+ new = Call(Name(isiter and "iter" or "list"), [new])
+ if tail:
+ new = pytree.Node(syms.power, [new] + tail)
+ new.set_prefix(node.get_prefix())
+ return new
+
+ P1 = "power< func=NAME trailer< '(' node=any ')' > any* >"
+ p1 = patcomp.compile_pattern(P1)
+
+ P2 = """for_stmt< 'for' any 'in' node=any ':' any* >
+ | comp_for< 'for' any 'in' node=any any* >
+ """
+ p2 = patcomp.compile_pattern(P2)
+
+ def in_special_context(self, node, isiter):
+ if node.parent is None:
+ return False
+ results = {}
+ if (node.parent.parent is not None and
+ self.p1.match(node.parent.parent, results) and
+ results["node"] is node):
+ if isiter:
+ # iter(d.iterkeys()) -> iter(d.keys()), etc.
+ return results["func"].value in iter_exempt
+ else:
+ # list(d.keys()) -> list(d.keys()), etc.
+ return results["func"].value in exempt
+ if not isiter:
+ return False
+ # for ... in d.iterkeys() -> for ... in d.keys(), etc.
+ return self.p2.match(node.parent, results) and results["node"] is node
diff --git a/Lib/lib2to3/fixes/fix_except.py b/Lib/lib2to3/fixes/fix_except.py
new file mode 100644
index 0000000..340575a
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_except.py
@@ -0,0 +1,89 @@
+"""Fixer for except statements with named exceptions.
+
+The following cases will be converted:
+
+- "except E, T:" where T is a name:
+
+ except E as T:
+
+- "except E, T:" where T is not a name, tuple or list:
+
+ except E as t:
+ T = t
+
+ This is done because the target of an "except" clause must be a
+ name.
+
+- "except E, T:" where T is a tuple or list literal:
+
+ except E as t:
+ T = t.args
+"""
+# Author: Collin Winter
+
+# Local imports
+from .. import pytree
+from ..pgen2 import token
+from . import basefix
+from .util import Assign, Attr, Name, is_tuple, is_list, reversed
+
+def find_excepts(nodes):
+ for i, n in enumerate(nodes):
+ if isinstance(n, pytree.Node):
+ if n.children[0].value == 'except':
+ yield (n, nodes[i+2])
+
+class FixExcept(basefix.BaseFix):
+
+ PATTERN = """
+ try_stmt< 'try' ':' suite
+ cleanup=((except_clause ':' suite)+ ['else' ':' suite]
+ ['finally' ':' suite]
+ | 'finally' ':' suite) >
+ """
+
+ def transform(self, node, results):
+ syms = self.syms
+
+ try_cleanup = [ch.clone() for ch in results['cleanup']]
+ for except_clause, e_suite in find_excepts(try_cleanup):
+ if len(except_clause.children) == 4:
+ (E, comma, N) = except_clause.children[1:4]
+ comma.replace(Name("as", prefix=" "))
+
+ if N.type != token.NAME:
+ # Generate a new N for the except clause
+ new_N = Name(self.new_name(), prefix=" ")
+ target = N.clone()
+ target.set_prefix("")
+ N.replace(new_N)
+ new_N = new_N.clone()
+
+ # Insert "old_N = new_N" as the first statement in
+ # the except body. This loop skips leading whitespace
+ # and indents
+ #TODO(cwinter) suite-cleanup
+ suite_stmts = e_suite.children
+ for i, stmt in enumerate(suite_stmts):
+ if isinstance(stmt, pytree.Node):
+ break
+
+ # The assignment is different if old_N is a tuple or list
+ # In that case, the assignment is old_N = new_N.args
+ if is_tuple(N) or is_list(N):
+ assign = Assign(target, Attr(new_N, Name('args')))
+ else:
+ assign = Assign(target, new_N)
+
+ #TODO(cwinter) stopgap until children becomes a smart list
+ for child in reversed(suite_stmts[:i]):
+ e_suite.insert_child(0, child)
+ e_suite.insert_child(i, assign)
+ elif N.get_prefix() == "":
+ # No space after a comma is legal; no space after "as",
+ # not so much.
+ N.set_prefix(" ")
+
+ #TODO(cwinter) fix this when children becomes a smart list
+ children = [c.clone() for c in node.children[:3]] + try_cleanup
+ return pytree.Node(node.type, children)
diff --git a/Lib/lib2to3/fixes/fix_exec.py b/Lib/lib2to3/fixes/fix_exec.py
new file mode 100644
index 0000000..2e45cb6
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_exec.py
@@ -0,0 +1,39 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for exec.
+
+This converts usages of the exec statement into calls to a built-in
+exec() function.
+
+exec code in ns1, ns2 -> exec(code, ns1, ns2)
+"""
+
+# Local imports
+from .. import pytree
+from . import basefix
+from .util import Comma, Name, Call
+
+
+class FixExec(basefix.BaseFix):
+
+ PATTERN = """
+ exec_stmt< 'exec' a=any 'in' b=any [',' c=any] >
+ |
+ exec_stmt< 'exec' (not atom<'(' [any] ')'>) a=any >
+ """
+
+ def transform(self, node, results):
+ assert results
+ syms = self.syms
+ a = results["a"]
+ b = results.get("b")
+ c = results.get("c")
+ args = [a.clone()]
+ args[0].set_prefix("")
+ if b is not None:
+ args.extend([Comma(), b.clone()])
+ if c is not None:
+ args.extend([Comma(), c.clone()])
+
+ return Call(Name("exec"), args, prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_execfile.py b/Lib/lib2to3/fixes/fix_execfile.py
new file mode 100644
index 0000000..0e67f09
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_execfile.py
@@ -0,0 +1,37 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for execfile.
+
+This converts usages of the execfile function into calls to the built-in
+exec() function.
+"""
+
+from .. import pytree
+from . import basefix
+from .util import Comma, Name, Call, LParen, RParen, Dot
+
+
+class FixExecfile(basefix.BaseFix):
+
+ PATTERN = """
+ power< 'execfile' trailer< '(' arglist< filename=any [',' globals=any [',' locals=any ] ] > ')' > >
+ |
+ power< 'execfile' trailer< '(' filename=any ')' > >
+ """
+
+ def transform(self, node, results):
+ assert results
+ syms = self.syms
+ filename = results["filename"]
+ globals = results.get("globals")
+ locals = results.get("locals")
+ args = [Name('open'), LParen(), filename.clone(), RParen(), Dot(),
+ Name('read'), LParen(), RParen()]
+ args[0].set_prefix("")
+ if globals is not None:
+ args.extend([Comma(), globals.clone()])
+ if locals is not None:
+ args.extend([Comma(), locals.clone()])
+
+ return Call(Name("exec"), args, prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_filter.py b/Lib/lib2to3/fixes/fix_filter.py
new file mode 100644
index 0000000..096b9c8
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_filter.py
@@ -0,0 +1,119 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer that changes filter(F, X) into list(filter(F, X)).
+
+We avoid the transformation if the filter() call is directly contained
+in iter(<>), list(<>), tuple(<>), sorted(<>), ...join(<>), or
+for V in <>:.
+
+NOTE: This is still not correct if the original code was depending on
+filter(F, X) to return a string if X is a string and a tuple if X is a
+tuple. That would require type inference, which we don't do. Let
+Python 2.6 figure it out.
+"""
+
+# Local imports
+from .. import pytree
+from .. import patcomp
+from ..pgen2 import token
+from . import basefix
+from .util import Name, Call, ListComp, attr_chain, does_tree_import
+
+class FixFilter(basefix.BaseFix):
+
+ PATTERN = """
+ filter_lambda=power<
+ 'filter'
+ trailer<
+ '('
+ arglist<
+ lambdef< 'lambda'
+ (fp=NAME | vfpdef< '(' fp=NAME ')'> ) ':' xp=any
+ >
+ ','
+ it=any
+ >
+ ')'
+ >
+ >
+ |
+ power<
+ 'filter'
+ trailer< '(' arglist< none='None' ',' seq=any > ')' >
+ >
+ |
+ power<
+ 'filter'
+ args=trailer< '(' [any] ')' >
+ >
+ """
+
+ def start_tree(self, *args):
+ super(FixFilter, self).start_tree(*args)
+ self._new_filter = None
+
+ def has_new_filter(self, node):
+ if self._new_filter is not None:
+ return self._new_filter
+ self._new_filter = does_tree_import('future_builtins', 'filter', node)
+ return self._new_filter
+
+ def transform(self, node, results):
+ if self.has_new_filter(node):
+ # If filter is imported from future_builtins, we don't want to
+ # do anything here.
+ return
+
+ if "filter_lambda" in results:
+ new = ListComp(results.get("fp").clone(),
+ results.get("fp").clone(),
+ results.get("it").clone(),
+ results.get("xp").clone())
+
+ elif "none" in results:
+ new = ListComp(Name("_f"),
+ Name("_f"),
+ results["seq"].clone(),
+ Name("_f"))
+
+ else:
+ if in_special_context(node):
+ return None
+ new = node.clone()
+ new.set_prefix("")
+ new = Call(Name("list"), [new])
+ new.set_prefix(node.get_prefix())
+ return new
+
+P0 = """for_stmt< 'for' any 'in' node=any ':' any* >
+ | comp_for< 'for' any 'in' node=any any* >
+ """
+p0 = patcomp.compile_pattern(P0)
+
+P1 = """
+power<
+ ( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' |
+ 'any' | 'all' | (any* trailer< '.' 'join' >) )
+ trailer< '(' node=any ')' >
+ any*
+>
+"""
+p1 = patcomp.compile_pattern(P1)
+
+P2 = """
+power<
+ 'sorted'
+ trailer< '(' arglist<node=any any*> ')' >
+ any*
+>
+"""
+p2 = patcomp.compile_pattern(P2)
+
+def in_special_context(node):
+ patterns = [p0, p1, p2]
+ for pattern, parent in zip(patterns, attr_chain(node, "parent")):
+ results = {}
+ if pattern.match(parent, results) and results["node"] is node:
+ return True
+ return False
diff --git a/Lib/lib2to3/fixes/fix_funcattrs.py b/Lib/lib2to3/fixes/fix_funcattrs.py
new file mode 100644
index 0000000..a9ba125
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_funcattrs.py
@@ -0,0 +1,19 @@
+"""Fix function attribute names (f.func_x -> f.__x__)."""
+# Author: Collin Winter
+
+# Local imports
+from . import basefix
+from .util import Name
+
+
+class FixFuncattrs(basefix.BaseFix):
+ PATTERN = """
+ power< any+ trailer< '.' attr=('func_closure' | 'func_doc' | 'func_globals'
+ | 'func_name' | 'func_defaults' | 'func_code'
+ | 'func_dict') > any* >
+ """
+
+ def transform(self, node, results):
+ attr = results["attr"][0]
+ attr.replace(Name(("__%s__" % attr.value[5:]),
+ prefix=attr.get_prefix()))
diff --git a/Lib/lib2to3/fixes/fix_future.py b/Lib/lib2to3/fixes/fix_future.py
new file mode 100644
index 0000000..5476df4
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_future.py
@@ -0,0 +1,16 @@
+"""Remove __future__ imports
+
+from __future__ import foo is replaced with an empty line.
+"""
+# Author: Christian Heimes
+
+# Local imports
+from . import basefix
+from .util import BlankLine
+
+class FixFuture(basefix.BaseFix):
+ PATTERN = """import_from< 'from' module_name="__future__" 'import' any >"""
+
+ def transform(self, node, results):
+ return BlankLine()
+
diff --git a/Lib/lib2to3/fixes/fix_has_key.py b/Lib/lib2to3/fixes/fix_has_key.py
new file mode 100644
index 0000000..ea7bfc5
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_has_key.py
@@ -0,0 +1,109 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for has_key().
+
+Calls to .has_key() methods are expressed in terms of the 'in'
+operator:
+
+ d.has_key(k) -> k in d
+
+CAVEATS:
+1) While the primary target of this fixer is dict.has_key(), the
+ fixer will change any has_key() method call, regardless of its
+ class.
+
+2) Cases like this will not be converted:
+
+ m = d.has_key
+ if m(k):
+ ...
+
+ Only *calls* to has_key() are converted. While it is possible to
+ convert the above to something like
+
+ m = d.__contains__
+ if m(k):
+ ...
+
+ this is currently not done.
+"""
+
+# Local imports
+from .. import pytree
+from ..pgen2 import token
+from . import basefix
+from .util import Name
+
+
+class FixHasKey(basefix.BaseFix):
+
+ PATTERN = """
+ anchor=power<
+ before=any+
+ trailer< '.' 'has_key' >
+ trailer<
+ '('
+ ( not(arglist | argument<any '=' any>) arg=any
+ | arglist<(not argument<any '=' any>) arg=any ','>
+ )
+ ')'
+ >
+ after=any*
+ >
+ |
+ negation=not_test<
+ 'not'
+ anchor=power<
+ before=any+
+ trailer< '.' 'has_key' >
+ trailer<
+ '('
+ ( not(arglist | argument<any '=' any>) arg=any
+ | arglist<(not argument<any '=' any>) arg=any ','>
+ )
+ ')'
+ >
+ >
+ >
+ """
+
+ def transform(self, node, results):
+ assert results
+ syms = self.syms
+ if (node.parent.type == syms.not_test and
+ self.pattern.match(node.parent)):
+ # Don't transform a node matching the first alternative of the
+ # pattern when its parent matches the second alternative
+ return None
+ negation = results.get("negation")
+ anchor = results["anchor"]
+ prefix = node.get_prefix()
+ before = [n.clone() for n in results["before"]]
+ arg = results["arg"].clone()
+ after = results.get("after")
+ if after:
+ after = [n.clone() for n in after]
+ if arg.type in (syms.comparison, syms.not_test, syms.and_test,
+ syms.or_test, syms.test, syms.lambdef, syms.argument):
+ arg = self.parenthesize(arg)
+ if len(before) == 1:
+ before = before[0]
+ else:
+ before = pytree.Node(syms.power, before)
+ before.set_prefix(" ")
+ n_op = Name("in", prefix=" ")
+ if negation:
+ n_not = Name("not", prefix=" ")
+ n_op = pytree.Node(syms.comp_op, (n_not, n_op))
+ new = pytree.Node(syms.comparison, (arg, n_op, before))
+ if after:
+ new = self.parenthesize(new)
+ new = pytree.Node(syms.power, (new,) + tuple(after))
+ if node.parent.type in (syms.comparison, syms.expr, syms.xor_expr,
+ syms.and_expr, syms.shift_expr,
+ syms.arith_expr, syms.term,
+ syms.factor, syms.power):
+ new = self.parenthesize(new)
+ new.set_prefix(prefix)
+ return new
diff --git a/Lib/lib2to3/fixes/fix_idioms.py b/Lib/lib2to3/fixes/fix_idioms.py
new file mode 100644
index 0000000..e2f937b
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_idioms.py
@@ -0,0 +1,134 @@
+"""Adjust some old Python 2 idioms to their modern counterparts.
+
+* Change some type comparisons to isinstance() calls:
+ type(x) == T -> isinstance(x, T)
+ type(x) is T -> isinstance(x, T)
+ type(x) != T -> not isinstance(x, T)
+ type(x) is not T -> not isinstance(x, T)
+
+* Change "while 1:" into "while True:".
+
+* Change both
+
+ v = list(EXPR)
+ v.sort()
+ foo(v)
+
+and the more general
+
+ v = EXPR
+ v.sort()
+ foo(v)
+
+into
+
+ v = sorted(EXPR)
+ foo(v)
+"""
+# Author: Jacques Frechet, Collin Winter
+
+# Local imports
+from . import basefix
+from .util import Call, Comma, Name, Node, syms
+
+CMP = "(n='!=' | '==' | 'is' | n=comp_op< 'is' 'not' >)"
+TYPE = "power< 'type' trailer< '(' x=any ')' > >"
+
+class FixIdioms(basefix.BaseFix):
+
+ explicit = True # The user must ask for this fixer
+
+ PATTERN = r"""
+ isinstance=comparison< %s %s T=any >
+ |
+ isinstance=comparison< T=any %s %s >
+ |
+ while_stmt< 'while' while='1' ':' any+ >
+ |
+ sorted=any<
+ any*
+ simple_stmt<
+ expr_stmt< id1=any '='
+ power< list='list' trailer< '(' (not arglist<any+>) any ')' > >
+ >
+ '\n'
+ >
+ sort=
+ simple_stmt<
+ power< id2=any
+ trailer< '.' 'sort' > trailer< '(' ')' >
+ >
+ '\n'
+ >
+ next=any*
+ >
+ |
+ sorted=any<
+ any*
+ simple_stmt< expr_stmt< id1=any '=' expr=any > '\n' >
+ sort=
+ simple_stmt<
+ power< id2=any
+ trailer< '.' 'sort' > trailer< '(' ')' >
+ >
+ '\n'
+ >
+ next=any*
+ >
+ """ % (TYPE, CMP, CMP, TYPE)
+
+ def match(self, node):
+ r = super(FixIdioms, self).match(node)
+ # If we've matched one of the sort/sorted subpatterns above, we
+ # want to reject matches where the initial assignment and the
+ # subsequent .sort() call involve different identifiers.
+ if r and "sorted" in r:
+ if r["id1"] == r["id2"]:
+ return r
+ return None
+ return r
+
+ def transform(self, node, results):
+ if "isinstance" in results:
+ return self.transform_isinstance(node, results)
+ elif "while" in results:
+ return self.transform_while(node, results)
+ elif "sorted" in results:
+ return self.transform_sort(node, results)
+ else:
+ raise RuntimeError("Invalid match")
+
+ def transform_isinstance(self, node, results):
+ x = results["x"].clone() # The thing inside of type()
+ T = results["T"].clone() # The type being compared against
+ x.set_prefix("")
+ T.set_prefix(" ")
+ test = Call(Name("isinstance"), [x, Comma(), T])
+ if "n" in results:
+ test.set_prefix(" ")
+ test = Node(syms.not_test, [Name("not"), test])
+ test.set_prefix(node.get_prefix())
+ return test
+
+ def transform_while(self, node, results):
+ one = results["while"]
+ one.replace(Name("True", prefix=one.get_prefix()))
+
+ def transform_sort(self, node, results):
+ sort_stmt = results["sort"]
+ next_stmt = results["next"]
+ list_call = results.get("list")
+ simple_expr = results.get("expr")
+
+ if list_call:
+ list_call.replace(Name("sorted", prefix=list_call.get_prefix()))
+ elif simple_expr:
+ new = simple_expr.clone()
+ new.set_prefix("")
+ simple_expr.replace(Call(Name("sorted"), [new],
+ prefix=simple_expr.get_prefix()))
+ else:
+ raise RuntimeError("should not have reached here")
+ sort_stmt.remove()
+ if next_stmt:
+ next_stmt[0].set_prefix(sort_stmt.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_imports.py b/Lib/lib2to3/fixes/fix_imports.py
new file mode 100644
index 0000000..1ad672b
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_imports.py
@@ -0,0 +1,89 @@
+"""Fix incompatible imports and module references.
+
+Fixes:
+ * StringIO -> io
+ * cStringIO -> io
+ * md5 -> hashlib
+"""
+# Author: Collin Winter
+
+# Local imports
+from . import basefix
+from .util import Name, attr_chain, any, set
+import __builtin__
+builtin_names = [name for name in dir(__builtin__)
+ if name not in ("__name__", "__doc__")]
+
+MAPPING = {"StringIO": ("io", ["StringIO"]),
+ "cStringIO": ("io", ["StringIO"]),
+ "__builtin__" : ("builtins", builtin_names),
+ }
+
+
+def alternates(members):
+ return "(" + "|".join(map(repr, members)) + ")"
+
+
+def build_pattern():
+ bare = set()
+ for old_module, (new_module, members) in MAPPING.items():
+ bare.add(old_module)
+ bare.update(members)
+ members = alternates(members)
+ yield """import_name< 'import' (module=%r
+ | dotted_as_names< any* module=%r any* >) >
+ """ % (old_module, old_module)
+ yield """import_from< 'from' module_name=%r 'import'
+ ( %s | import_as_name< %s 'as' any >) >
+ """ % (old_module, members, members)
+ yield """import_from< 'from' module_name=%r 'import' star='*' >
+ """ % old_module
+ yield """import_name< 'import'
+ dotted_as_name< module_name=%r 'as' any > >
+ """ % old_module
+ yield """power< module_name=%r trailer< '.' %s > any* >
+ """ % (old_module, members)
+ yield """bare_name=%s""" % alternates(bare)
+
+
+class FixImports(basefix.BaseFix):
+ PATTERN = "|".join(build_pattern())
+
+ order = "pre" # Pre-order tree traversal
+
+ # Don't match the node if it's within another match
+ def match(self, node):
+ match = super(FixImports, self).match
+ results = match(node)
+ if results:
+ if any([match(obj) for obj in attr_chain(node, "parent")]):
+ return False
+ return results
+ return False
+
+ def start_tree(self, tree, filename):
+ super(FixImports, self).start_tree(tree, filename)
+ self.replace = {}
+
+ def transform(self, node, results):
+ import_mod = results.get("module")
+ mod_name = results.get("module_name")
+ bare_name = results.get("bare_name")
+ star = results.get("star")
+
+ if import_mod or mod_name:
+ new_name, members = MAPPING[(import_mod or mod_name).value]
+
+ if import_mod:
+ self.replace[import_mod.value] = new_name
+ import_mod.replace(Name(new_name, prefix=import_mod.get_prefix()))
+ elif mod_name:
+ if star:
+ self.cannot_convert(node, "Cannot handle star imports.")
+ else:
+ mod_name.replace(Name(new_name, prefix=mod_name.get_prefix()))
+ elif bare_name:
+ bare_name = bare_name[0]
+ new_name = self.replace.get(bare_name.value)
+ if new_name:
+ bare_name.replace(Name(new_name, prefix=bare_name.get_prefix()))
diff --git a/Lib/lib2to3/fixes/fix_input.py b/Lib/lib2to3/fixes/fix_input.py
new file mode 100644
index 0000000..5b88f3a
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_input.py
@@ -0,0 +1,26 @@
+"""Fixer that changes input(...) into eval(input(...))."""
+# Author: Andre Roberge
+
+# Local imports
+from . import basefix
+from .util import Call, Name
+from .. import patcomp
+
+
+context = patcomp.compile_pattern("power< 'eval' trailer< '(' any ')' > >")
+
+
+class FixInput(basefix.BaseFix):
+
+ PATTERN = """
+ power< 'input' args=trailer< '(' [any] ')' > >
+ """
+
+ def transform(self, node, results):
+ # If we're already wrapped in a eval() call, we're done.
+ if context.match(node.parent.parent):
+ return
+
+ new = node.clone()
+ new.set_prefix("")
+ return Call(Name("eval"), [new], prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_intern.py b/Lib/lib2to3/fixes/fix_intern.py
new file mode 100644
index 0000000..6d33f8c
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_intern.py
@@ -0,0 +1,43 @@
+# Copyright 2006 Georg Brandl.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for intern().
+
+intern(s) -> sys.intern(s)"""
+
+# Local imports
+from .. import pytree
+from . import basefix
+from .util import Name, Attr
+
+
+class FixIntern(basefix.BaseFix):
+
+ PATTERN = """
+ power< 'intern'
+ trailer< lpar='('
+ ( not(arglist | argument<any '=' any>) obj=any
+ | obj=arglist<(not argument<any '=' any>) any ','> )
+ rpar=')' >
+ after=any*
+ >
+ """
+
+ def transform(self, node, results):
+ syms = self.syms
+ obj = results["obj"].clone()
+ if obj.type == syms.arglist:
+ newarglist = obj.clone()
+ else:
+ newarglist = pytree.Node(syms.arglist, [obj.clone()])
+ after = results["after"]
+ if after:
+ after = [n.clone() for n in after]
+ new = pytree.Node(syms.power,
+ Attr(Name("sys"), Name("intern")) +
+ [pytree.Node(syms.trailer,
+ [results["lpar"].clone(),
+ newarglist,
+ results["rpar"].clone()])] + after)
+ new.set_prefix(node.get_prefix())
+ return new
diff --git a/Lib/lib2to3/fixes/fix_itertools.py b/Lib/lib2to3/fixes/fix_itertools.py
new file mode 100644
index 0000000..af49270
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_itertools.py
@@ -0,0 +1,36 @@
+""" Fixer for itertools.(imap|ifilter|izip) --> (map|filter|zip) and
+ itertools.ifilterfalse --> itertools.filterfalse (bugs 2360-2363)
+
+ If itertools is imported as something else (ie: import itertools as it;
+ it.izip(spam, eggs)) method calls will not get fixed.
+ """
+
+# Local imports
+from . import basefix
+from .util import Name
+
+class FixItertools(basefix.BaseFix):
+ it_funcs = "('imap'|'ifilter'|'izip'|'ifilterfalse')"
+ PATTERN = """
+ power< it='itertools'
+ trailer<
+ dot='.' func=%(it_funcs)s > trailer< '(' [any] ')' > >
+ |
+ power< func=%(it_funcs)s trailer< '(' [any] ')' > >
+ """ %(locals())
+
+ def transform(self, node, results):
+ prefix = None
+ func = results['func'][0]
+ if 'it' in results and func.value != 'ifilterfalse':
+ dot, it = (results['dot'], results['it'])
+ # Remove the 'itertools'
+ prefix = it.get_prefix()
+ it.remove()
+ # Replace the node wich contains ('.', 'function') with the
+ # function (to be consistant with the second part of the pattern)
+ dot.remove()
+ func.parent.replace(func)
+
+ prefix = prefix or func.get_prefix()
+ func.replace(Name(func.value[1:], prefix=prefix))
diff --git a/Lib/lib2to3/fixes/fix_long.py b/Lib/lib2to3/fixes/fix_long.py
new file mode 100644
index 0000000..1987e96
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_long.py
@@ -0,0 +1,35 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer that turns 'long' into 'int' everywhere.
+
+This also strips the trailing 'L' or 'l' from long loterals.
+"""
+
+# Local imports
+from .. import pytree
+from . import basefix
+from .util import Name, Number
+
+
+class FixLong(basefix.BaseFix):
+
+ PATTERN = """
+ (long_type = 'long' | number = NUMBER)
+ """
+
+ static_long = Name("long")
+ static_int = Name("int")
+
+ def transform(self, node, results):
+ long_type = results.get("long_type")
+ number = results.get("number")
+ new = None
+ if long_type:
+ assert node == self.static_long, node
+ new = self.static_int.clone()
+ if number and node.value[-1] in ("l", "L"):
+ new = Number(node.value[:-1])
+ if new is not None:
+ new.set_prefix(node.get_prefix())
+ return new
diff --git a/Lib/lib2to3/fixes/fix_map.py b/Lib/lib2to3/fixes/fix_map.py
new file mode 100644
index 0000000..b5dcaa2
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_map.py
@@ -0,0 +1,126 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer that changes map(F, ...) into list(map(F, ...)) unless there
+exists a 'from future_builtins import map' statement in the top-level
+namespace.
+
+As a special case, map(None, X) is changed into list(X). (This is
+necessary because the semantics are changed in this case -- the new
+map(None, X) is equivalent to [(x,) for x in X].)
+
+We avoid the transformation (except for the special case mentioned
+above) if the map() call is directly contained in iter(<>), list(<>),
+tuple(<>), sorted(<>), ...join(<>), or for V in <>:.
+
+NOTE: This is still not correct if the original code was depending on
+map(F, X, Y, ...) to go on until the longest argument is exhausted,
+substituting None for missing values -- like zip(), it now stops as
+soon as the shortest argument is exhausted.
+"""
+
+# Local imports
+from .. import pytree
+from .. import patcomp
+from ..pgen2 import token
+from . import basefix
+from .util import Name, Call, ListComp, attr_chain, does_tree_import
+from ..pygram import python_symbols as syms
+
+class FixMap(basefix.BaseFix):
+
+ PATTERN = """
+ map_none=power<
+ 'map'
+ trailer< '(' arglist< 'None' ',' arg=any [','] > ')' >
+ >
+ |
+ map_lambda=power<
+ 'map'
+ trailer<
+ '('
+ arglist<
+ lambdef< 'lambda'
+ (fp=NAME | vfpdef< '(' fp=NAME ')'> ) ':' xp=any
+ >
+ ','
+ it=any
+ >
+ ')'
+ >
+ >
+ |
+ power<
+ 'map'
+ args=trailer< '(' [any] ')' >
+ >
+ """
+
+ def start_tree(self, *args):
+ super(FixMap, self).start_tree(*args)
+ self._future_map_found = None
+
+ def has_future_map(self, node):
+ if self._future_map_found is not None:
+ return self._future_map_found
+ self._future_map_found = does_tree_import('future_builtins', 'map', node)
+ return self._future_map_found
+
+ def transform(self, node, results):
+ if self.has_future_map(node):
+ # If a future map has been imported for this file, we won't
+ # be making any modifications
+ return
+
+ if node.parent.type == syms.simple_stmt:
+ self.warning(node, "You should use a for loop here")
+ new = node.clone()
+ new.set_prefix("")
+ new = Call(Name("list"), [new])
+ elif "map_lambda" in results:
+ new = ListComp(results.get("xp").clone(),
+ results.get("fp").clone(),
+ results.get("it").clone())
+ else:
+ if "map_none" in results:
+ new = results["arg"].clone()
+ else:
+ if in_special_context(node):
+ return None
+ new = node.clone()
+ new.set_prefix("")
+ new = Call(Name("list"), [new])
+ new.set_prefix(node.get_prefix())
+ return new
+
+P0 = """for_stmt< 'for' any 'in' node=any ':' any* >
+ | comp_for< 'for' any 'in' node=any any* >
+ """
+p0 = patcomp.compile_pattern(P0)
+
+P1 = """
+power<
+ ( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' |
+ 'any' | 'all' | (any* trailer< '.' 'join' >) )
+ trailer< '(' node=any ')' >
+ any*
+>
+"""
+p1 = patcomp.compile_pattern(P1)
+
+P2 = """
+power<
+ 'sorted'
+ trailer< '(' arglist<node=any any*> ')' >
+ any*
+>
+"""
+p2 = patcomp.compile_pattern(P2)
+
+def in_special_context(node):
+ patterns = [p0, p1, p2]
+ for pattern, parent in zip(patterns, attr_chain(node, "parent")):
+ results = {}
+ if pattern.match(parent, results) and results["node"] is node:
+ return True
+ return False
diff --git a/Lib/lib2to3/fixes/fix_methodattrs.py b/Lib/lib2to3/fixes/fix_methodattrs.py
new file mode 100644
index 0000000..3d0d7d7
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_methodattrs.py
@@ -0,0 +1,23 @@
+"""Fix bound method attributes (method.im_? -> method.__?__).
+"""
+# Author: Christian Heimes
+
+# Local imports
+from . import basefix
+from .util import Name
+
+MAP = {
+ "im_func" : "__func__",
+ "im_self" : "__self__",
+ "im_class" : "__self__.__class__"
+ }
+
+class FixMethodattrs(basefix.BaseFix):
+ PATTERN = """
+ power< any+ trailer< '.' attr=('im_func' | 'im_self' | 'im_class') > any* >
+ """
+
+ def transform(self, node, results):
+ attr = results["attr"][0]
+ new = MAP[attr.value]
+ attr.replace(Name(new, prefix=attr.get_prefix()))
diff --git a/Lib/lib2to3/fixes/fix_ne.py b/Lib/lib2to3/fixes/fix_ne.py
new file mode 100644
index 0000000..ecd18e0
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_ne.py
@@ -0,0 +1,22 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer that turns <> into !=."""
+
+# Local imports
+from .. import pytree
+from ..pgen2 import token
+from . import basefix
+
+
+class FixNe(basefix.BaseFix):
+ # This is so simple that we don't need the pattern compiler.
+
+ def match(self, node):
+ # Override
+ return node.type == token.NOTEQUAL and node.value == "<>"
+
+ def transform(self, node, results):
+ new = pytree.Leaf(token.NOTEQUAL, "!=")
+ new.set_prefix(node.get_prefix())
+ return new
diff --git a/Lib/lib2to3/fixes/fix_next.py b/Lib/lib2to3/fixes/fix_next.py
new file mode 100644
index 0000000..adbc962
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_next.py
@@ -0,0 +1,104 @@
+"""Fixer for it.next() -> next(it), per PEP 3114."""
+# Author: Collin Winter
+
+# Things that currently aren't covered:
+# - listcomp "next" names aren't warned
+# - "with" statement targets aren't checked
+
+# Local imports
+from ..pgen2 import token
+from ..pygram import python_symbols as syms
+from . import basefix
+from .util import Name, Call, find_binding, any
+
+bind_warning = "Calls to builtin next() possibly shadowed by global binding"
+
+
+class FixNext(basefix.BaseFix):
+ PATTERN = """
+ power< base=any+ trailer< '.' attr='next' > trailer< '(' ')' > >
+ |
+ power< head=any+ trailer< '.' attr='next' > not trailer< '(' ')' > >
+ |
+ classdef< 'class' any+ ':'
+ suite< any*
+ funcdef< 'def'
+ name='next'
+ parameters< '(' NAME ')' > any+ >
+ any* > >
+ |
+ global=global_stmt< 'global' any* 'next' any* >
+ |
+ mod=file_input< any+ >
+ """
+
+ order = "pre" # Pre-order tree traversal
+
+ def start_tree(self, tree, filename):
+ super(FixNext, self).start_tree(tree, filename)
+ self.shadowed_next = False
+
+ def transform(self, node, results):
+ assert results
+
+ base = results.get("base")
+ attr = results.get("attr")
+ name = results.get("name")
+ mod = results.get("mod")
+
+ if base:
+ if self.shadowed_next:
+ attr.replace(Name("__next__", prefix=attr.get_prefix()))
+ else:
+ base = [n.clone() for n in base]
+ base[0].set_prefix("")
+ node.replace(Call(Name("next", prefix=node.get_prefix()), base))
+ elif name:
+ n = Name("__next__", prefix=name.get_prefix())
+ name.replace(n)
+ elif attr:
+ # We don't do this transformation if we're assigning to "x.next".
+ # Unfortunately, it doesn't seem possible to do this in PATTERN,
+ # so it's being done here.
+ if is_assign_target(node):
+ head = results["head"]
+ if "".join([str(n) for n in head]).strip() == '__builtin__':
+ self.warning(node, bind_warning)
+ return
+ attr.replace(Name("__next__"))
+ elif "global" in results:
+ self.warning(node, bind_warning)
+ self.shadowed_next = True
+ elif mod:
+ n = find_binding('next', mod)
+ if n:
+ self.warning(n, bind_warning)
+ self.shadowed_next = True
+
+
+### The following functions help test if node is part of an assignment
+### target.
+
+def is_assign_target(node):
+ assign = find_assign(node)
+ if assign is None:
+ return False
+
+ for child in assign.children:
+ if child.type == token.EQUAL:
+ return False
+ elif is_subtree(child, node):
+ return True
+ return False
+
+def find_assign(node):
+ if node.type == syms.expr_stmt:
+ return node
+ if node.type == syms.simple_stmt or node.parent is None:
+ return None
+ return find_assign(node.parent)
+
+def is_subtree(root, node):
+ if root == node:
+ return True
+ return any([is_subtree(c, node) for c in root.children])
diff --git a/Lib/lib2to3/fixes/fix_nonzero.py b/Lib/lib2to3/fixes/fix_nonzero.py
new file mode 100644
index 0000000..4cf6875
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_nonzero.py
@@ -0,0 +1,20 @@
+"""Fixer for __nonzero__ -> __bool__ methods."""
+# Author: Collin Winter
+
+# Local imports
+from .import basefix
+from .util import Name, syms
+
+class FixNonzero(basefix.BaseFix):
+ PATTERN = """
+ classdef< 'class' any+ ':'
+ suite< any*
+ funcdef< 'def' name='__nonzero__'
+ parameters< '(' NAME ')' > any+ >
+ any* > >
+ """
+
+ def transform(self, node, results):
+ name = results["name"]
+ new = Name("__bool__", prefix=name.get_prefix())
+ name.replace(new)
diff --git a/Lib/lib2to3/fixes/fix_numliterals.py b/Lib/lib2to3/fixes/fix_numliterals.py
new file mode 100644
index 0000000..f88be60
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_numliterals.py
@@ -0,0 +1,27 @@
+"""Fixer that turns 1L into 1, 0755 into 0o755.
+"""
+# Copyright 2007 Georg Brandl.
+# Licensed to PSF under a Contributor Agreement.
+
+# Local imports
+from ..pgen2 import token
+from .import basefix
+from .util import Number, set
+
+
+class FixNumliterals(basefix.BaseFix):
+ # This is so simple that we don't need the pattern compiler.
+
+ def match(self, node):
+ # Override
+ return (node.type == token.NUMBER and
+ (node.value.startswith("0") or node.value[-1] in "Ll"))
+
+ def transform(self, node, results):
+ val = node.value
+ if val[-1] in 'Ll':
+ val = val[:-1]
+ elif val.startswith('0') and val.isdigit() and len(set(val)) > 1:
+ val = "0o" + val[1:]
+
+ return Number(val, prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_print.py b/Lib/lib2to3/fixes/fix_print.py
new file mode 100644
index 0000000..aa5c60a
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_print.py
@@ -0,0 +1,81 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for print.
+
+Change:
+ 'print' into 'print()'
+ 'print ...' into 'print(...)'
+ 'print ... ,' into 'print(..., end=" ")'
+ 'print >>x, ...' into 'print(..., file=x)'
+"""
+
+# Local imports
+from .. import patcomp
+from .. import pytree
+from ..pgen2 import token
+from .import basefix
+from .util import Name, Call, Comma, String, is_tuple
+
+
+parend_expr = patcomp.compile_pattern(
+ """atom< '(' [atom|STRING|NAME] ')' >"""
+ )
+
+
+class FixPrint(basefix.BaseFix):
+
+ PATTERN = """
+ simple_stmt< bare='print' any > | print_stmt
+ """
+
+ def transform(self, node, results):
+ assert results
+ bare_print = results.get("bare")
+
+ if bare_print:
+ # Special-case print all by itself
+ bare_print.replace(Call(Name("print"), [],
+ prefix=bare_print.get_prefix()))
+ return
+ assert node.children[0] == Name("print")
+ args = node.children[1:]
+ if len(args) == 1 and parend_expr.match(args[0]):
+ # We don't want to keep sticking parens around an
+ # already-parenthesised expression.
+ return
+
+ sep = end = file = None
+ if args and args[-1] == Comma():
+ args = args[:-1]
+ end = " "
+ if args and args[0] == pytree.Leaf(token.RIGHTSHIFT, ">>"):
+ assert len(args) >= 2
+ file = args[1].clone()
+ args = args[3:] # Strip a possible comma after the file expression
+ # Now synthesize a print(args, sep=..., end=..., file=...) node.
+ l_args = [arg.clone() for arg in args]
+ if l_args:
+ l_args[0].set_prefix("")
+ if sep is not None or end is not None or file is not None:
+ if sep is not None:
+ self.add_kwarg(l_args, "sep", String(repr(sep)))
+ if end is not None:
+ self.add_kwarg(l_args, "end", String(repr(end)))
+ if file is not None:
+ self.add_kwarg(l_args, "file", file)
+ n_stmt = Call(Name("print"), l_args)
+ n_stmt.set_prefix(node.get_prefix())
+ return n_stmt
+
+ def add_kwarg(self, l_nodes, s_kwd, n_expr):
+ # XXX All this prefix-setting may lose comments (though rarely)
+ n_expr.set_prefix("")
+ n_argument = pytree.Node(self.syms.argument,
+ (Name(s_kwd),
+ pytree.Leaf(token.EQUAL, "="),
+ n_expr))
+ if l_nodes:
+ l_nodes.append(Comma())
+ n_argument.set_prefix(" ")
+ l_nodes.append(n_argument)
diff --git a/Lib/lib2to3/fixes/fix_raise.py b/Lib/lib2to3/fixes/fix_raise.py
new file mode 100644
index 0000000..2e9ffbc
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_raise.py
@@ -0,0 +1,82 @@
+"""Fixer for 'raise E, V, T'
+
+raise -> raise
+raise E -> raise E
+raise E, V -> raise E(V)
+raise E, V, T -> raise E(V).with_traceback(T)
+
+raise (((E, E'), E''), E'''), V -> raise E(V)
+raise "foo", V, T -> warns about string exceptions
+
+
+CAVEATS:
+1) "raise E, V" will be incorrectly translated if V is an exception
+ instance. The correct Python 3 idiom is
+
+ raise E from V
+
+ but since we can't detect instance-hood by syntax alone and since
+ any client code would have to be changed as well, we don't automate
+ this.
+"""
+# Author: Collin Winter
+
+# Local imports
+from .. import pytree
+from ..pgen2 import token
+from .import basefix
+from .util import Name, Call, Attr, ArgList, is_tuple
+
+class FixRaise(basefix.BaseFix):
+
+ PATTERN = """
+ raise_stmt< 'raise' exc=any [',' val=any [',' tb=any]] >
+ """
+
+ def transform(self, node, results):
+ syms = self.syms
+
+ exc = results["exc"].clone()
+ if exc.type is token.STRING:
+ self.cannot_convert(node, "Python 3 does not support string exceptions")
+ return
+
+ # Python 2 supports
+ # raise ((((E1, E2), E3), E4), E5), V
+ # as a synonym for
+ # raise E1, V
+ # Since Python 3 will not support this, we recurse down any tuple
+ # literals, always taking the first element.
+ if is_tuple(exc):
+ while is_tuple(exc):
+ # exc.children[1:-1] is the unparenthesized tuple
+ # exc.children[1].children[0] is the first element of the tuple
+ exc = exc.children[1].children[0].clone()
+ exc.set_prefix(" ")
+
+ if "val" not in results:
+ # One-argument raise
+ new = pytree.Node(syms.raise_stmt, [Name("raise"), exc])
+ new.set_prefix(node.get_prefix())
+ return new
+
+ val = results["val"].clone()
+ if is_tuple(val):
+ args = [c.clone() for c in val.children[1:-1]]
+ else:
+ val.set_prefix("")
+ args = [val]
+
+ if "tb" in results:
+ tb = results["tb"].clone()
+ tb.set_prefix("")
+
+ e = Call(exc, args)
+ with_tb = Attr(e, Name('with_traceback')) + [ArgList([tb])]
+ new = pytree.Node(syms.simple_stmt, [Name("raise")] + with_tb)
+ new.set_prefix(node.get_prefix())
+ return new
+ else:
+ return pytree.Node(syms.raise_stmt,
+ [Name("raise"), Call(exc, args)],
+ prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_raw_input.py b/Lib/lib2to3/fixes/fix_raw_input.py
new file mode 100644
index 0000000..e746255
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_raw_input.py
@@ -0,0 +1,16 @@
+"""Fixer that changes raw_input(...) into input(...)."""
+# Author: Andre Roberge
+
+# Local imports
+from .import basefix
+from .util import Name
+
+class FixRawInput(basefix.BaseFix):
+
+ PATTERN = """
+ power< name='raw_input' trailer< '(' [any] ')' > >
+ """
+
+ def transform(self, node, results):
+ name = results["name"]
+ name.replace(Name("input", prefix=name.get_prefix()))
diff --git a/Lib/lib2to3/fixes/fix_renames.py b/Lib/lib2to3/fixes/fix_renames.py
new file mode 100644
index 0000000..336654a
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_renames.py
@@ -0,0 +1,70 @@
+"""Fix incompatible renames
+
+Fixes:
+ * sys.maxint -> sys.maxsize
+"""
+# Author: Christian Heimes
+# based on Collin Winter's fix_import
+
+# Local imports
+from .import basefix
+from .util import Name, attr_chain, any, set
+
+MAPPING = {"sys": {"maxint" : "maxsize"},
+ }
+LOOKUP = {}
+
+def alternates(members):
+ return "(" + "|".join(map(repr, members)) + ")"
+
+
+def build_pattern():
+ #bare = set()
+ for module, replace in MAPPING.items():
+ for old_attr, new_attr in replace.items():
+ LOOKUP[(module, old_attr)] = new_attr
+ #bare.add(module)
+ #bare.add(old_attr)
+ #yield """
+ # import_name< 'import' (module=%r
+ # | dotted_as_names< any* module=%r any* >) >
+ # """ % (module, module)
+ yield """
+ import_from< 'from' module_name=%r 'import'
+ ( attr_name=%r | import_as_name< attr_name=%r 'as' any >) >
+ """ % (module, old_attr, old_attr)
+ yield """
+ power< module_name=%r trailer< '.' attr_name=%r > any* >
+ """ % (module, old_attr)
+ #yield """bare_name=%s""" % alternates(bare)
+
+
+class FixRenames(basefix.BaseFix):
+ PATTERN = "|".join(build_pattern())
+
+ order = "pre" # Pre-order tree traversal
+
+ # Don't match the node if it's within another match
+ def match(self, node):
+ match = super(FixRenames, self).match
+ results = match(node)
+ if results:
+ if any([match(obj) for obj in attr_chain(node, "parent")]):
+ return False
+ return results
+ return False
+
+ #def start_tree(self, tree, filename):
+ # super(FixRenames, self).start_tree(tree, filename)
+ # self.replace = {}
+
+ def transform(self, node, results):
+ mod_name = results.get("module_name")
+ attr_name = results.get("attr_name")
+ #bare_name = results.get("bare_name")
+ #import_mod = results.get("module")
+
+ if mod_name and attr_name:
+ new_attr = LOOKUP[(mod_name.value, attr_name.value)]
+ attr_name.replace(Name(new_attr, prefix=attr_name.get_prefix()))
+
diff --git a/Lib/lib2to3/fixes/fix_repr.py b/Lib/lib2to3/fixes/fix_repr.py
new file mode 100644
index 0000000..9917ad5
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_repr.py
@@ -0,0 +1,22 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer that transforms `xyzzy` into repr(xyzzy)."""
+
+# Local imports
+from .import basefix
+from .util import Call, Name
+
+
+class FixRepr(basefix.BaseFix):
+
+ PATTERN = """
+ atom < '`' expr=any '`' >
+ """
+
+ def transform(self, node, results):
+ expr = results["expr"].clone()
+
+ if expr.type == self.syms.testlist1:
+ expr = self.parenthesize(expr)
+ return Call(Name("repr"), [expr], prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_standarderror.py b/Lib/lib2to3/fixes/fix_standarderror.py
new file mode 100644
index 0000000..61789d0
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_standarderror.py
@@ -0,0 +1,18 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for StandardError -> Exception."""
+
+# Local imports
+from .import basefix
+from .util import Name
+
+
+class FixStandarderror(basefix.BaseFix):
+
+ PATTERN = """
+ 'StandardError'
+ """
+
+ def transform(self, node, results):
+ return Name("Exception", prefix=node.get_prefix())
diff --git a/Lib/lib2to3/fixes/fix_throw.py b/Lib/lib2to3/fixes/fix_throw.py
new file mode 100644
index 0000000..ef120bd
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_throw.py
@@ -0,0 +1,56 @@
+"""Fixer for generator.throw(E, V, T).
+
+g.throw(E) -> g.throw(E)
+g.throw(E, V) -> g.throw(E(V))
+g.throw(E, V, T) -> g.throw(E(V).with_traceback(T))
+
+g.throw("foo"[, V[, T]]) will warn about string exceptions."""
+# Author: Collin Winter
+
+# Local imports
+from .. import pytree
+from ..pgen2 import token
+from .import basefix
+from .util import Name, Call, ArgList, Attr, is_tuple
+
+class FixThrow(basefix.BaseFix):
+
+ PATTERN = """
+ power< any trailer< '.' 'throw' >
+ trailer< '(' args=arglist< exc=any ',' val=any [',' tb=any] > ')' >
+ >
+ |
+ power< any trailer< '.' 'throw' > trailer< '(' exc=any ')' > >
+ """
+
+ def transform(self, node, results):
+ syms = self.syms
+
+ exc = results["exc"].clone()
+ if exc.type is token.STRING:
+ self.cannot_convert(node, "Python 3 does not support string exceptions")
+ return
+
+ # Leave "g.throw(E)" alone
+ val = results.get("val")
+ if val is None:
+ return
+
+ val = val.clone()
+ if is_tuple(val):
+ args = [c.clone() for c in val.children[1:-1]]
+ else:
+ val.set_prefix("")
+ args = [val]
+
+ throw_args = results["args"]
+
+ if "tb" in results:
+ tb = results["tb"].clone()
+ tb.set_prefix("")
+
+ e = Call(exc, args)
+ with_tb = Attr(e, Name('with_traceback')) + [ArgList([tb])]
+ throw_args.replace(pytree.Node(syms.power, with_tb))
+ else:
+ throw_args.replace(Call(exc, args))
diff --git a/Lib/lib2to3/fixes/fix_tuple_params.py b/Lib/lib2to3/fixes/fix_tuple_params.py
new file mode 100644
index 0000000..199f6e0
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_tuple_params.py
@@ -0,0 +1,169 @@
+"""Fixer for function definitions with tuple parameters.
+
+def func(((a, b), c), d):
+ ...
+
+ ->
+
+def func(x, d):
+ ((a, b), c) = x
+ ...
+
+It will also support lambdas:
+
+ lambda (x, y): x + y -> lambda t: t[0] + t[1]
+
+ # The parens are a syntax error in Python 3
+ lambda (x): x + y -> lambda x: x + y
+"""
+# Author: Collin Winter
+
+# Local imports
+from .. import pytree
+from ..pgen2 import token
+from .import basefix
+from .util import Assign, Name, Newline, Number, Subscript, syms
+
+def is_docstring(stmt):
+ return isinstance(stmt, pytree.Node) and \
+ stmt.children[0].type == token.STRING
+
+class FixTupleParams(basefix.BaseFix):
+ PATTERN = """
+ funcdef< 'def' any parameters< '(' args=any ')' >
+ ['->' any] ':' suite=any+ >
+ |
+ lambda=
+ lambdef< 'lambda' args=vfpdef< '(' inner=any ')' >
+ ':' body=any
+ >
+ """
+
+ def transform(self, node, results):
+ if "lambda" in results:
+ return self.transform_lambda(node, results)
+
+ new_lines = []
+ suite = results["suite"]
+ args = results["args"]
+ # This crap is so "def foo(...): x = 5; y = 7" is handled correctly.
+ # TODO(cwinter): suite-cleanup
+ if suite[0].children[1].type == token.INDENT:
+ start = 2
+ indent = suite[0].children[1].value
+ end = Newline()
+ else:
+ start = 0
+ indent = "; "
+ end = pytree.Leaf(token.INDENT, "")
+
+ # We need access to self for new_name(), and making this a method
+ # doesn't feel right. Closing over self and new_lines makes the
+ # code below cleaner.
+ def handle_tuple(tuple_arg, add_prefix=False):
+ n = Name(self.new_name())
+ arg = tuple_arg.clone()
+ arg.set_prefix("")
+ stmt = Assign(arg, n.clone())
+ if add_prefix:
+ n.set_prefix(" ")
+ tuple_arg.replace(n)
+ new_lines.append(pytree.Node(syms.simple_stmt,
+ [stmt, end.clone()]))
+
+ if args.type == syms.tfpdef:
+ handle_tuple(args)
+ elif args.type == syms.typedargslist:
+ for i, arg in enumerate(args.children):
+ if arg.type == syms.tfpdef:
+ # Without add_prefix, the emitted code is correct,
+ # just ugly.
+ handle_tuple(arg, add_prefix=(i > 0))
+
+ if not new_lines:
+ return node
+
+ # This isn't strictly necessary, but it plays nicely with other fixers.
+ # TODO(cwinter) get rid of this when children becomes a smart list
+ for line in new_lines:
+ line.parent = suite[0]
+
+ # TODO(cwinter) suite-cleanup
+ after = start
+ if start == 0:
+ new_lines[0].set_prefix(" ")
+ elif is_docstring(suite[0].children[start]):
+ new_lines[0].set_prefix(indent)
+ after = start + 1
+
+ suite[0].children[after:after] = new_lines
+ for i in range(after+1, after+len(new_lines)+1):
+ suite[0].children[i].set_prefix(indent)
+ suite[0].changed()
+
+ def transform_lambda(self, node, results):
+ args = results["args"]
+ body = results["body"]
+ inner = simplify_args(results["inner"])
+
+ # Replace lambda ((((x)))): x with lambda x: x
+ if inner.type == token.NAME:
+ inner = inner.clone()
+ inner.set_prefix(" ")
+ args.replace(inner)
+ return
+
+ params = find_params(args)
+ to_index = map_to_index(params)
+ tup_name = self.new_name(tuple_name(params))
+
+ new_param = Name(tup_name, prefix=" ")
+ args.replace(new_param.clone())
+ for n in body.post_order():
+ if n.type == token.NAME and n.value in to_index:
+ subscripts = [c.clone() for c in to_index[n.value]]
+ new = pytree.Node(syms.power,
+ [new_param.clone()] + subscripts)
+ new.set_prefix(n.get_prefix())
+ n.replace(new)
+
+
+### Helper functions for transform_lambda()
+
+def simplify_args(node):
+ if node.type in (syms.vfplist, token.NAME):
+ return node
+ elif node.type == syms.vfpdef:
+ # These look like vfpdef< '(' x ')' > where x is NAME
+ # or another vfpdef instance (leading to recursion).
+ while node.type == syms.vfpdef:
+ node = node.children[1]
+ return node
+ raise RuntimeError("Received unexpected node %s" % node)
+
+def find_params(node):
+ if node.type == syms.vfpdef:
+ return find_params(node.children[1])
+ elif node.type == token.NAME:
+ return node.value
+ return [find_params(c) for c in node.children if c.type != token.COMMA]
+
+def map_to_index(param_list, prefix=[], d=None):
+ if d is None:
+ d = {}
+ for i, obj in enumerate(param_list):
+ trailer = [Subscript(Number(i))]
+ if isinstance(obj, list):
+ map_to_index(obj, trailer, d=d)
+ else:
+ d[obj] = prefix + trailer
+ return d
+
+def tuple_name(param_list):
+ l = []
+ for obj in param_list:
+ if isinstance(obj, list):
+ l.append(tuple_name(obj))
+ else:
+ l.append(obj)
+ return "_".join(l)
diff --git a/Lib/lib2to3/fixes/fix_types.py b/Lib/lib2to3/fixes/fix_types.py
new file mode 100644
index 0000000..fe7880a
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_types.py
@@ -0,0 +1,62 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer for removing uses of the types module.
+
+These work for only the known names in the types module. The forms above
+can include types. or not. ie, It is assumed the module is imported either as:
+
+ import types
+ from types import ... # either * or specific types
+
+The import statements are not modified.
+
+There should be another fixer that handles at least the following constants:
+
+ type([]) -> list
+ type(()) -> tuple
+ type('') -> str
+
+"""
+
+# Local imports
+from ..pgen2 import token
+from .import basefix
+from .util import Name
+
+_TYPE_MAPPING = {
+ 'BooleanType' : 'bool',
+ 'BufferType' : 'memoryview',
+ 'ClassType' : 'type',
+ 'ComplexType' : 'complex',
+ 'DictType': 'dict',
+ 'DictionaryType' : 'dict',
+ 'EllipsisType' : 'type(Ellipsis)',
+ #'FileType' : 'io.IOBase',
+ 'FloatType': 'float',
+ 'IntType': 'int',
+ 'ListType': 'list',
+ 'LongType': 'int',
+ 'ObjectType' : 'object',
+ 'NoneType': 'type(None)',
+ 'NotImplementedType' : 'type(NotImplemented)',
+ 'SliceType' : 'slice',
+ 'StringType': 'bytes', # XXX ?
+ 'StringTypes' : 'str', # XXX ?
+ 'TupleType': 'tuple',
+ 'TypeType' : 'type',
+ 'UnicodeType': 'str',
+ 'XRangeType' : 'range',
+ }
+
+_pats = ["power< 'types' trailer< '.' name='%s' > >" % t for t in _TYPE_MAPPING]
+
+class FixTypes(basefix.BaseFix):
+
+ PATTERN = '|'.join(_pats)
+
+ def transform(self, node, results):
+ new_value = _TYPE_MAPPING.get(results["name"].value)
+ if new_value:
+ return Name(new_value, prefix=node.get_prefix())
+ return None
diff --git a/Lib/lib2to3/fixes/fix_unicode.py b/Lib/lib2to3/fixes/fix_unicode.py
new file mode 100644
index 0000000..380f241
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_unicode.py
@@ -0,0 +1,28 @@
+"""Fixer that changes unicode to str, unichr to chr, and u"..." into "...".
+
+"""
+
+import re
+from ..pgen2 import token
+from .import basefix
+
+class FixUnicode(basefix.BaseFix):
+
+ PATTERN = "STRING | NAME<'unicode' | 'unichr'>"
+
+ def transform(self, node, results):
+ if node.type == token.NAME:
+ if node.value == "unicode":
+ new = node.clone()
+ new.value = "str"
+ return new
+ if node.value == "unichr":
+ new = node.clone()
+ new.value = "chr"
+ return new
+ # XXX Warn when __unicode__ found?
+ elif node.type == token.STRING:
+ if re.match(r"[uU][rR]?[\'\"]", node.value):
+ new = node.clone()
+ new.value = new.value[1:]
+ return new
diff --git a/Lib/lib2to3/fixes/fix_ws_comma.py b/Lib/lib2to3/fixes/fix_ws_comma.py
new file mode 100644
index 0000000..2ba1b49
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_ws_comma.py
@@ -0,0 +1,39 @@
+"""Fixer that changes 'a ,b' into 'a, b'.
+
+This also changes '{a :b}' into '{a: b}', but does not touch other
+uses of colons. It does not touch other uses of whitespace.
+
+"""
+
+from .. import pytree
+from ..pgen2 import token
+from .import basefix
+
+class FixWsComma(basefix.BaseFix):
+
+ explicit = True # The user must ask for this fixers
+
+ PATTERN = """
+ any<(not(',') any)+ ',' ((not(',') any)+ ',')* [not(',') any]>
+ """
+
+ COMMA = pytree.Leaf(token.COMMA, ",")
+ COLON = pytree.Leaf(token.COLON, ":")
+ SEPS = (COMMA, COLON)
+
+ def transform(self, node, results):
+ new = node.clone()
+ comma = False
+ for child in new.children:
+ if child in self.SEPS:
+ prefix = child.get_prefix()
+ if prefix.isspace() and "\n" not in prefix:
+ child.set_prefix("")
+ comma = True
+ else:
+ if comma:
+ prefix = child.get_prefix()
+ if not prefix:
+ child.set_prefix(" ")
+ comma = False
+ return new
diff --git a/Lib/lib2to3/fixes/fix_xrange.py b/Lib/lib2to3/fixes/fix_xrange.py
new file mode 100644
index 0000000..410e601
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_xrange.py
@@ -0,0 +1,18 @@
+# Copyright 2007 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Fixer that changes xrange(...) into range(...)."""
+
+# Local imports
+from .import basefix
+from .util import Name
+
+class FixXrange(basefix.BaseFix):
+
+ PATTERN = """
+ power< name='xrange' trailer< '(' [any] ')' > >
+ """
+
+ def transform(self, node, results):
+ name = results["name"]
+ name.replace(Name("range", prefix=name.get_prefix()))
diff --git a/Lib/lib2to3/fixes/fix_xreadlines.py b/Lib/lib2to3/fixes/fix_xreadlines.py
new file mode 100644
index 0000000..8857759
--- /dev/null
+++ b/Lib/lib2to3/fixes/fix_xreadlines.py
@@ -0,0 +1,24 @@
+"""Fix "for x in f.xreadlines()" -> "for x in f".
+
+This fixer will also convert g(f.xreadlines) into g(f.__iter__)."""
+# Author: Collin Winter
+
+# Local imports
+from .import basefix
+from .util import Name
+
+
+class FixXreadlines(basefix.BaseFix):
+ PATTERN = """
+ power< call=any+ trailer< '.' 'xreadlines' > trailer< '(' ')' > >
+ |
+ power< any+ trailer< '.' no_call='xreadlines' > >
+ """
+
+ def transform(self, node, results):
+ no_call = results.get("no_call")
+
+ if no_call:
+ no_call.replace(Name("__iter__", prefix=no_call.get_prefix()))
+ else:
+ node.replace([x.clone() for x in results["call"]])
diff --git a/Lib/lib2to3/fixes/util.py b/Lib/lib2to3/fixes/util.py
new file mode 100644
index 0000000..8b7ad9f
--- /dev/null
+++ b/Lib/lib2to3/fixes/util.py
@@ -0,0 +1,303 @@
+"""Utility functions, node construction macros, etc."""
+# Author: Collin Winter
+
+# Local imports
+from ..pgen2 import token
+from ..pytree import Leaf, Node
+from ..pygram import python_symbols as syms
+
+
+###########################################################
+### Common node-construction "macros"
+###########################################################
+
+def KeywordArg(keyword, value):
+ return Node(syms.argument,
+ [keyword, Leaf(token.EQUAL, '='), value])
+
+def LParen():
+ return Leaf(token.LPAR, "(")
+
+def RParen():
+ return Leaf(token.RPAR, ")")
+
+def Assign(target, source):
+ """Build an assignment statement"""
+ if not isinstance(target, list):
+ target = [target]
+ if not isinstance(source, list):
+ source.set_prefix(" ")
+ source = [source]
+
+ return Node(syms.atom,
+ target + [Leaf(token.EQUAL, "=", prefix=" ")] + source)
+
+def Name(name, prefix=None):
+ """Return a NAME leaf"""
+ return Leaf(token.NAME, name, prefix=prefix)
+
+def Attr(obj, attr):
+ """A node tuple for obj.attr"""
+ return [obj, Node(syms.trailer, [Dot(), attr])]
+
+def Comma():
+ """A comma leaf"""
+ return Leaf(token.COMMA, ",")
+
+def Dot():
+ """A period (.) leaf"""
+ return Leaf(token.DOT, ".")
+
+def ArgList(args, lparen=LParen(), rparen=RParen()):
+ """A parenthesised argument list, used by Call()"""
+ return Node(syms.trailer,
+ [lparen.clone(),
+ Node(syms.arglist, args),
+ rparen.clone()])
+
+def Call(func_name, args, prefix=None):
+ """A function call"""
+ node = Node(syms.power, [func_name, ArgList(args)])
+ if prefix is not None:
+ node.set_prefix(prefix)
+ return node
+
+def Newline():
+ """A newline literal"""
+ return Leaf(token.NEWLINE, "\n")
+
+def BlankLine():
+ """A blank line"""
+ return Leaf(token.NEWLINE, "")
+
+def Number(n, prefix=None):
+ return Leaf(token.NUMBER, n, prefix=prefix)
+
+def Subscript(index_node):
+ """A numeric or string subscript"""
+ return Node(syms.trailer, [Leaf(token.LBRACE, '['),
+ index_node,
+ Leaf(token.RBRACE, ']')])
+
+def String(string, prefix=None):
+ """A string leaf"""
+ return Leaf(token.STRING, string, prefix=prefix)
+
+def ListComp(xp, fp, it, test=None):
+ """A list comprehension of the form [xp for fp in it if test].
+
+ If test is None, the "if test" part is omitted.
+ """
+ xp.set_prefix("")
+ fp.set_prefix(" ")
+ it.set_prefix(" ")
+ for_leaf = Leaf(token.NAME, "for")
+ for_leaf.set_prefix(" ")
+ in_leaf = Leaf(token.NAME, "in")
+ in_leaf.set_prefix(" ")
+ inner_args = [for_leaf, fp, in_leaf, it]
+ if test:
+ test.set_prefix(" ")
+ if_leaf = Leaf(token.NAME, "if")
+ if_leaf.set_prefix(" ")
+ inner_args.append(Node(syms.comp_if, [if_leaf, test]))
+ inner = Node(syms.listmaker, [xp, Node(syms.comp_for, inner_args)])
+ return Node(syms.atom,
+ [Leaf(token.LBRACE, "["),
+ inner,
+ Leaf(token.RBRACE, "]")])
+
+###########################################################
+### Determine whether a node represents a given literal
+###########################################################
+
+def is_tuple(node):
+ """Does the node represent a tuple literal?"""
+ if isinstance(node, Node) and node.children == [LParen(), RParen()]:
+ return True
+ return (isinstance(node, Node)
+ and len(node.children) == 3
+ and isinstance(node.children[0], Leaf)
+ and isinstance(node.children[1], Node)
+ and isinstance(node.children[2], Leaf)
+ and node.children[0].value == "("
+ and node.children[2].value == ")")
+
+def is_list(node):
+ """Does the node represent a list literal?"""
+ return (isinstance(node, Node)
+ and len(node.children) > 1
+ and isinstance(node.children[0], Leaf)
+ and isinstance(node.children[-1], Leaf)
+ and node.children[0].value == "["
+ and node.children[-1].value == "]")
+
+###########################################################
+### Common portability code. This allows fixers to do, eg,
+### "from .util import set" and forget about it.
+###########################################################
+
+try:
+ any = any
+except NameError:
+ def any(l):
+ for o in l:
+ if o:
+ return True
+ return False
+
+try:
+ set = set
+except NameError:
+ from sets import Set as set
+
+try:
+ reversed = reversed
+except NameError:
+ def reversed(l):
+ return l[::-1]
+
+###########################################################
+### Misc
+###########################################################
+
+def attr_chain(obj, attr):
+ """Follow an attribute chain.
+
+ If you have a chain of objects where a.foo -> b, b.foo-> c, etc,
+ use this to iterate over all objects in the chain. Iteration is
+ terminated by getattr(x, attr) is None.
+
+ Args:
+ obj: the starting object
+ attr: the name of the chaining attribute
+
+ Yields:
+ Each successive object in the chain.
+ """
+ next = getattr(obj, attr)
+ while next:
+ yield next
+ next = getattr(next, attr)
+
+###########################################################
+### The following functions are to find bindings in a suite
+###########################################################
+
+def make_suite(node):
+ if node.type == syms.suite:
+ return node
+ node = node.clone()
+ parent, node.parent = node.parent, None
+ suite = Node(syms.suite, [node])
+ suite.parent = parent
+ return suite
+
+def does_tree_import(package, name, node):
+ """ Returns true if name is imported from package at the
+ top level of the tree which node belongs to.
+ To cover the case of an import like 'import foo', use
+ Null for the package and 'foo' for the name. """
+ # Scamper up to the top level namespace
+ while node.type != syms.file_input:
+ assert node.parent, "Tree is insane! root found before "\
+ "file_input node was found."
+ node = node.parent
+
+ binding = find_binding(name, node, package)
+ return bool(binding)
+
+_def_syms = set([syms.classdef, syms.funcdef])
+def find_binding(name, node, package=None):
+ """ Returns the node which binds variable name, otherwise None.
+ If optional argument package is supplied, only imports will
+ be returned.
+ See test cases for examples."""
+ for child in node.children:
+ ret = None
+ if child.type == syms.for_stmt:
+ if _find(name, child.children[1]):
+ return child
+ n = find_binding(name, make_suite(child.children[-1]), package)
+ if n: ret = n
+ elif child.type in (syms.if_stmt, syms.while_stmt):
+ n = find_binding(name, make_suite(child.children[-1]), package)
+ if n: ret = n
+ elif child.type == syms.try_stmt:
+ n = find_binding(name, make_suite(child.children[2]), package)
+ if n:
+ ret = n
+ else:
+ for i, kid in enumerate(child.children[3:]):
+ if kid.type == token.COLON and kid.value == ":":
+ # i+3 is the colon, i+4 is the suite
+ n = find_binding(name, make_suite(child.children[i+4]), package)
+ if n: ret = n
+ elif child.type in _def_syms and child.children[1].value == name:
+ ret = child
+ elif _is_import_binding(child, name, package):
+ ret = child
+ elif child.type == syms.simple_stmt:
+ ret = find_binding(name, child, package)
+ elif child.type == syms.expr_stmt:
+ if _find(name, child.children[0]):
+ ret = child
+
+ if ret:
+ if not package:
+ return ret
+ if ret.type in (syms.import_name, syms.import_from):
+ return ret
+ return None
+
+_block_syms = set([syms.funcdef, syms.classdef, syms.trailer])
+def _find(name, node):
+ nodes = [node]
+ while nodes:
+ node = nodes.pop()
+ if node.type > 256 and node.type not in _block_syms:
+ nodes.extend(node.children)
+ elif node.type == token.NAME and node.value == name:
+ return node
+ return None
+
+def _is_import_binding(node, name, package=None):
+ """ Will reuturn node if node will import name, or node
+ will import * from package. None is returned otherwise.
+ See test cases for examples. """
+
+ if node.type == syms.import_name and not package:
+ imp = node.children[1]
+ if imp.type == syms.dotted_as_names:
+ for child in imp.children:
+ if child.type == syms.dotted_as_name:
+ if child.children[2].value == name:
+ return node
+ elif child.type == token.NAME and child.value == name:
+ return node
+ elif imp.type == syms.dotted_as_name:
+ last = imp.children[-1]
+ if last.type == token.NAME and last.value == name:
+ return node
+ elif imp.type == token.NAME and imp.value == name:
+ return node
+ elif node.type == syms.import_from:
+ # unicode(...) is used to make life easier here, because
+ # from a.b import parses to ['import', ['a', '.', 'b'], ...]
+ if package and unicode(node.children[1]).strip() != package:
+ return None
+ n = node.children[3]
+ if package and _find('as', n):
+ # See test_from_import_as for explanation
+ return None
+ elif n.type == syms.import_as_names and _find(name, n):
+ return node
+ elif n.type == syms.import_as_name:
+ child = n.children[2]
+ if child.type == token.NAME and child.value == name:
+ return node
+ elif n.type == token.NAME and n.value == name:
+ return node
+ elif package and n.type == token.STAR:
+ return node
+ return None
diff --git a/Lib/lib2to3/patcomp.py b/Lib/lib2to3/patcomp.py
new file mode 100644
index 0000000..353b960
--- /dev/null
+++ b/Lib/lib2to3/patcomp.py
@@ -0,0 +1,186 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Pattern compiler.
+
+The grammer is taken from PatternGrammar.txt.
+
+The compiler compiles a pattern to a pytree.*Pattern instance.
+"""
+
+__author__ = "Guido van Rossum <guido@python.org>"
+
+# Python imports
+import os
+
+# Fairly local imports
+from .pgen2 import driver
+from .pgen2 import literals
+from .pgen2 import token
+from .pgen2 import tokenize
+
+# Really local imports
+from . import pytree
+from . import pygram
+
+# The pattern grammar file
+_PATTERN_GRAMMAR_FILE = os.path.join(os.path.dirname(__file__),
+ "PatternGrammar.txt")
+
+
+def tokenize_wrapper(input):
+ """Tokenizes a string suppressing significant whitespace."""
+ skip = (token.NEWLINE, token.INDENT, token.DEDENT)
+ tokens = tokenize.generate_tokens(driver.generate_lines(input).next)
+ for quintuple in tokens:
+ type, value, start, end, line_text = quintuple
+ if type not in skip:
+ yield quintuple
+
+
+class PatternCompiler(object):
+
+ def __init__(self, grammar_file=_PATTERN_GRAMMAR_FILE):
+ """Initializer.
+
+ Takes an optional alternative filename for the pattern grammar.
+ """
+ self.grammar = driver.load_grammar(grammar_file)
+ self.syms = pygram.Symbols(self.grammar)
+ self.pygrammar = pygram.python_grammar
+ self.pysyms = pygram.python_symbols
+ self.driver = driver.Driver(self.grammar, convert=pattern_convert)
+
+ def compile_pattern(self, input, debug=False):
+ """Compiles a pattern string to a nested pytree.*Pattern object."""
+ tokens = tokenize_wrapper(input)
+ root = self.driver.parse_tokens(tokens, debug=debug)
+ return self.compile_node(root)
+
+ def compile_node(self, node):
+ """Compiles a node, recursively.
+
+ This is one big switch on the node type.
+ """
+ # XXX Optimize certain Wildcard-containing-Wildcard patterns
+ # that can be merged
+ if node.type == self.syms.Matcher:
+ node = node.children[0] # Avoid unneeded recursion
+
+ if node.type == self.syms.Alternatives:
+ # Skip the odd children since they are just '|' tokens
+ alts = [self.compile_node(ch) for ch in node.children[::2]]
+ if len(alts) == 1:
+ return alts[0]
+ p = pytree.WildcardPattern([[a] for a in alts], min=1, max=1)
+ return p.optimize()
+
+ if node.type == self.syms.Alternative:
+ units = [self.compile_node(ch) for ch in node.children]
+ if len(units) == 1:
+ return units[0]
+ p = pytree.WildcardPattern([units], min=1, max=1)
+ return p.optimize()
+
+ if node.type == self.syms.NegatedUnit:
+ pattern = self.compile_basic(node.children[1:])
+ p = pytree.NegatedPattern(pattern)
+ return p.optimize()
+
+ assert node.type == self.syms.Unit
+
+ name = None
+ nodes = node.children
+ if len(nodes) >= 3 and nodes[1].type == token.EQUAL:
+ name = nodes[0].value
+ nodes = nodes[2:]
+ repeat = None
+ if len(nodes) >= 2 and nodes[-1].type == self.syms.Repeater:
+ repeat = nodes[-1]
+ nodes = nodes[:-1]
+
+ # Now we've reduced it to: STRING | NAME [Details] | (...) | [...]
+ pattern = self.compile_basic(nodes, repeat)
+
+ if repeat is not None:
+ assert repeat.type == self.syms.Repeater
+ children = repeat.children
+ child = children[0]
+ if child.type == token.STAR:
+ min = 0
+ max = pytree.HUGE
+ elif child.type == token.PLUS:
+ min = 1
+ max = pytree.HUGE
+ elif child.type == token.LBRACE:
+ assert children[-1].type == token.RBRACE
+ assert len(children) in (3, 5)
+ min = max = self.get_int(children[1])
+ if len(children) == 5:
+ max = self.get_int(children[3])
+ else:
+ assert False
+ if min != 1 or max != 1:
+ pattern = pattern.optimize()
+ pattern = pytree.WildcardPattern([[pattern]], min=min, max=max)
+
+ if name is not None:
+ pattern.name = name
+ return pattern.optimize()
+
+ def compile_basic(self, nodes, repeat=None):
+ # Compile STRING | NAME [Details] | (...) | [...]
+ assert len(nodes) >= 1
+ node = nodes[0]
+ if node.type == token.STRING:
+ value = literals.evalString(node.value)
+ return pytree.LeafPattern(content=value)
+ elif node.type == token.NAME:
+ value = node.value
+ if value.isupper():
+ if value not in TOKEN_MAP:
+ raise SyntaxError("Invalid token: %r" % value)
+ return pytree.LeafPattern(TOKEN_MAP[value])
+ else:
+ if value == "any":
+ type = None
+ elif not value.startswith("_"):
+ type = getattr(self.pysyms, value, None)
+ if type is None:
+ raise SyntaxError("Invalid symbol: %r" % value)
+ if nodes[1:]: # Details present
+ content = [self.compile_node(nodes[1].children[1])]
+ else:
+ content = None
+ return pytree.NodePattern(type, content)
+ elif node.value == "(":
+ return self.compile_node(nodes[1])
+ elif node.value == "[":
+ assert repeat is None
+ subpattern = self.compile_node(nodes[1])
+ return pytree.WildcardPattern([[subpattern]], min=0, max=1)
+ assert False, node
+
+ def get_int(self, node):
+ assert node.type == token.NUMBER
+ return int(node.value)
+
+
+# Map named tokens to the type value for a LeafPattern
+TOKEN_MAP = {"NAME": token.NAME,
+ "STRING": token.STRING,
+ "NUMBER": token.NUMBER,
+ "TOKEN": None}
+
+
+def pattern_convert(grammar, raw_node_info):
+ """Converts raw node information to a Node or Leaf instance."""
+ type, value, context, children = raw_node_info
+ if children or type in grammar.number2symbol:
+ return pytree.Node(type, children, context=context)
+ else:
+ return pytree.Leaf(type, value, context=context)
+
+
+def compile_pattern(pattern):
+ return PatternCompiler().compile_pattern(pattern)
diff --git a/Lib/lib2to3/pgen2/__init__.py b/Lib/lib2to3/pgen2/__init__.py
new file mode 100644
index 0000000..af39048
--- /dev/null
+++ b/Lib/lib2to3/pgen2/__init__.py
@@ -0,0 +1,4 @@
+# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""The pgen2 package."""
diff --git a/Lib/lib2to3/pgen2/conv.py b/Lib/lib2to3/pgen2/conv.py
new file mode 100644
index 0000000..5d788a1
--- /dev/null
+++ b/Lib/lib2to3/pgen2/conv.py
@@ -0,0 +1,257 @@
+# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Convert graminit.[ch] spit out by pgen to Python code.
+
+Pgen is the Python parser generator. It is useful to quickly create a
+parser from a grammar file in Python's grammar notation. But I don't
+want my parsers to be written in C (yet), so I'm translating the
+parsing tables to Python data structures and writing a Python parse
+engine.
+
+Note that the token numbers are constants determined by the standard
+Python tokenizer. The standard token module defines these numbers and
+their names (the names are not used much). The token numbers are
+hardcoded into the Python tokenizer and into pgen. A Python
+implementation of the Python tokenizer is also available, in the
+standard tokenize module.
+
+On the other hand, symbol numbers (representing the grammar's
+non-terminals) are assigned by pgen based on the actual grammar
+input.
+
+Note: this module is pretty much obsolete; the pgen module generates
+equivalent grammar tables directly from the Grammar.txt input file
+without having to invoke the Python pgen C program.
+
+"""
+
+# Python imports
+import re
+
+# Local imports
+from pgen2 import grammar, token
+
+
+class Converter(grammar.Grammar):
+ """Grammar subclass that reads classic pgen output files.
+
+ The run() method reads the tables as produced by the pgen parser
+ generator, typically contained in two C files, graminit.h and
+ graminit.c. The other methods are for internal use only.
+
+ See the base class for more documentation.
+
+ """
+
+ def run(self, graminit_h, graminit_c):
+ """Load the grammar tables from the text files written by pgen."""
+ self.parse_graminit_h(graminit_h)
+ self.parse_graminit_c(graminit_c)
+ self.finish_off()
+
+ def parse_graminit_h(self, filename):
+ """Parse the .h file writen by pgen. (Internal)
+
+ This file is a sequence of #define statements defining the
+ nonterminals of the grammar as numbers. We build two tables
+ mapping the numbers to names and back.
+
+ """
+ try:
+ f = open(filename)
+ except IOError, err:
+ print "Can't open %s: %s" % (filename, err)
+ return False
+ self.symbol2number = {}
+ self.number2symbol = {}
+ lineno = 0
+ for line in f:
+ lineno += 1
+ mo = re.match(r"^#define\s+(\w+)\s+(\d+)$", line)
+ if not mo and line.strip():
+ print "%s(%s): can't parse %s" % (filename, lineno,
+ line.strip())
+ else:
+ symbol, number = mo.groups()
+ number = int(number)
+ assert symbol not in self.symbol2number
+ assert number not in self.number2symbol
+ self.symbol2number[symbol] = number
+ self.number2symbol[number] = symbol
+ return True
+
+ def parse_graminit_c(self, filename):
+ """Parse the .c file writen by pgen. (Internal)
+
+ The file looks as follows. The first two lines are always this:
+
+ #include "pgenheaders.h"
+ #include "grammar.h"
+
+ After that come four blocks:
+
+ 1) one or more state definitions
+ 2) a table defining dfas
+ 3) a table defining labels
+ 4) a struct defining the grammar
+
+ A state definition has the following form:
+ - one or more arc arrays, each of the form:
+ static arc arcs_<n>_<m>[<k>] = {
+ {<i>, <j>},
+ ...
+ };
+ - followed by a state array, of the form:
+ static state states_<s>[<t>] = {
+ {<k>, arcs_<n>_<m>},
+ ...
+ };
+
+ """
+ try:
+ f = open(filename)
+ except IOError, err:
+ print "Can't open %s: %s" % (filename, err)
+ return False
+ # The code below essentially uses f's iterator-ness!
+ lineno = 0
+
+ # Expect the two #include lines
+ lineno, line = lineno+1, f.next()
+ assert line == '#include "pgenheaders.h"\n', (lineno, line)
+ lineno, line = lineno+1, f.next()
+ assert line == '#include "grammar.h"\n', (lineno, line)
+
+ # Parse the state definitions
+ lineno, line = lineno+1, f.next()
+ allarcs = {}
+ states = []
+ while line.startswith("static arc "):
+ while line.startswith("static arc "):
+ mo = re.match(r"static arc arcs_(\d+)_(\d+)\[(\d+)\] = {$",
+ line)
+ assert mo, (lineno, line)
+ n, m, k = map(int, mo.groups())
+ arcs = []
+ for _ in range(k):
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r"\s+{(\d+), (\d+)},$", line)
+ assert mo, (lineno, line)
+ i, j = map(int, mo.groups())
+ arcs.append((i, j))
+ lineno, line = lineno+1, f.next()
+ assert line == "};\n", (lineno, line)
+ allarcs[(n, m)] = arcs
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r"static state states_(\d+)\[(\d+)\] = {$", line)
+ assert mo, (lineno, line)
+ s, t = map(int, mo.groups())
+ assert s == len(states), (lineno, line)
+ state = []
+ for _ in range(t):
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r"\s+{(\d+), arcs_(\d+)_(\d+)},$", line)
+ assert mo, (lineno, line)
+ k, n, m = map(int, mo.groups())
+ arcs = allarcs[n, m]
+ assert k == len(arcs), (lineno, line)
+ state.append(arcs)
+ states.append(state)
+ lineno, line = lineno+1, f.next()
+ assert line == "};\n", (lineno, line)
+ lineno, line = lineno+1, f.next()
+ self.states = states
+
+ # Parse the dfas
+ dfas = {}
+ mo = re.match(r"static dfa dfas\[(\d+)\] = {$", line)
+ assert mo, (lineno, line)
+ ndfas = int(mo.group(1))
+ for i in range(ndfas):
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r'\s+{(\d+), "(\w+)", (\d+), (\d+), states_(\d+),$',
+ line)
+ assert mo, (lineno, line)
+ symbol = mo.group(2)
+ number, x, y, z = map(int, mo.group(1, 3, 4, 5))
+ assert self.symbol2number[symbol] == number, (lineno, line)
+ assert self.number2symbol[number] == symbol, (lineno, line)
+ assert x == 0, (lineno, line)
+ state = states[z]
+ assert y == len(state), (lineno, line)
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r'\s+("(?:\\\d\d\d)*")},$', line)
+ assert mo, (lineno, line)
+ first = {}
+ rawbitset = eval(mo.group(1))
+ for i, c in enumerate(rawbitset):
+ byte = ord(c)
+ for j in range(8):
+ if byte & (1<<j):
+ first[i*8 + j] = 1
+ dfas[number] = (state, first)
+ lineno, line = lineno+1, f.next()
+ assert line == "};\n", (lineno, line)
+ self.dfas = dfas
+
+ # Parse the labels
+ labels = []
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r"static label labels\[(\d+)\] = {$", line)
+ assert mo, (lineno, line)
+ nlabels = int(mo.group(1))
+ for i in range(nlabels):
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r'\s+{(\d+), (0|"\w+")},$', line)
+ assert mo, (lineno, line)
+ x, y = mo.groups()
+ x = int(x)
+ if y == "0":
+ y = None
+ else:
+ y = eval(y)
+ labels.append((x, y))
+ lineno, line = lineno+1, f.next()
+ assert line == "};\n", (lineno, line)
+ self.labels = labels
+
+ # Parse the grammar struct
+ lineno, line = lineno+1, f.next()
+ assert line == "grammar _PyParser_Grammar = {\n", (lineno, line)
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r"\s+(\d+),$", line)
+ assert mo, (lineno, line)
+ ndfas = int(mo.group(1))
+ assert ndfas == len(self.dfas)
+ lineno, line = lineno+1, f.next()
+ assert line == "\tdfas,\n", (lineno, line)
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r"\s+{(\d+), labels},$", line)
+ assert mo, (lineno, line)
+ nlabels = int(mo.group(1))
+ assert nlabels == len(self.labels), (lineno, line)
+ lineno, line = lineno+1, f.next()
+ mo = re.match(r"\s+(\d+)$", line)
+ assert mo, (lineno, line)
+ start = int(mo.group(1))
+ assert start in self.number2symbol, (lineno, line)
+ self.start = start
+ lineno, line = lineno+1, f.next()
+ assert line == "};\n", (lineno, line)
+ try:
+ lineno, line = lineno+1, f.next()
+ except StopIteration:
+ pass
+ else:
+ assert 0, (lineno, line)
+
+ def finish_off(self):
+ """Create additional useful structures. (Internal)."""
+ self.keywords = {} # map from keyword strings to arc labels
+ self.tokens = {} # map from numeric token values to arc labels
+ for ilabel, (type, value) in enumerate(self.labels):
+ if type == token.NAME and value is not None:
+ self.keywords[value] = ilabel
+ elif value is None:
+ self.tokens[type] = ilabel
diff --git a/Lib/lib2to3/pgen2/driver.py b/Lib/lib2to3/pgen2/driver.py
new file mode 100644
index 0000000..77e2a40
--- /dev/null
+++ b/Lib/lib2to3/pgen2/driver.py
@@ -0,0 +1,143 @@
+# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+# Modifications:
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Parser driver.
+
+This provides a high-level interface to parse a file into a syntax tree.
+
+"""
+
+__author__ = "Guido van Rossum <guido@python.org>"
+
+__all__ = ["Driver", "load_grammar"]
+
+# Python imports
+import os
+import logging
+import sys
+
+# Pgen imports
+from . import grammar, parse, token, tokenize
+
+
+class Driver(object):
+
+ def __init__(self, grammar, convert=None, logger=None):
+ self.grammar = grammar
+ if logger is None:
+ logger = logging.getLogger()
+ self.logger = logger
+ self.convert = convert
+
+ def parse_tokens(self, tokens, debug=False):
+ """Parse a series of tokens and return the syntax tree."""
+ # XXX Move the prefix computation into a wrapper around tokenize.
+ p = parse.Parser(self.grammar, self.convert)
+ p.setup()
+ lineno = 1
+ column = 0
+ type = value = start = end = line_text = None
+ prefix = ""
+ for quintuple in tokens:
+ type, value, start, end, line_text = quintuple
+ if start != (lineno, column):
+ assert (lineno, column) <= start, ((lineno, column), start)
+ s_lineno, s_column = start
+ if lineno < s_lineno:
+ prefix += "\n" * (s_lineno - lineno)
+ lineno = s_lineno
+ column = 0
+ if column < s_column:
+ prefix += line_text[column:s_column]
+ column = s_column
+ if type in (tokenize.COMMENT, tokenize.NL):
+ prefix += value
+ lineno, column = end
+ if value.endswith("\n"):
+ lineno += 1
+ column = 0
+ continue
+ if type == token.OP:
+ type = grammar.opmap[value]
+ if debug:
+ self.logger.debug("%s %r (prefix=%r)",
+ token.tok_name[type], value, prefix)
+ if p.addtoken(type, value, (prefix, start)):
+ if debug:
+ self.logger.debug("Stop.")
+ break
+ prefix = ""
+ lineno, column = end
+ if value.endswith("\n"):
+ lineno += 1
+ column = 0
+ else:
+ # We never broke out -- EOF is too soon (how can this happen???)
+ raise parse.ParseError("incomplete input", t, v, x)
+ return p.rootnode
+
+ def parse_stream_raw(self, stream, debug=False):
+ """Parse a stream and return the syntax tree."""
+ tokens = tokenize.generate_tokens(stream.readline)
+ return self.parse_tokens(tokens, debug)
+
+ def parse_stream(self, stream, debug=False):
+ """Parse a stream and return the syntax tree."""
+ return self.parse_stream_raw(stream, debug)
+
+ def parse_file(self, filename, debug=False):
+ """Parse a file and return the syntax tree."""
+ stream = open(filename)
+ try:
+ return self.parse_stream(stream, debug)
+ finally:
+ stream.close()
+
+ def parse_string(self, text, debug=False):
+ """Parse a string and return the syntax tree."""
+ tokens = tokenize.generate_tokens(generate_lines(text).next)
+ return self.parse_tokens(tokens, debug)
+
+
+def generate_lines(text):
+ """Generator that behaves like readline without using StringIO."""
+ for line in text.splitlines(True):
+ yield line
+ while True:
+ yield ""
+
+
+def load_grammar(gt="Grammar.txt", gp=None,
+ save=True, force=False, logger=None):
+ """Load the grammar (maybe from a pickle)."""
+ if logger is None:
+ logger = logging.getLogger()
+ if gp is None:
+ head, tail = os.path.splitext(gt)
+ if tail == ".txt":
+ tail = ""
+ gp = head + tail + ".".join(map(str, sys.version_info)) + ".pickle"
+ if force or not _newer(gp, gt):
+ logger.info("Generating grammar tables from %s", gt)
+ from pgen2 import pgen
+ g = pgen.generate_grammar(gt)
+ if save:
+ logger.info("Writing grammar tables to %s", gp)
+ g.dump(gp)
+ else:
+ g = grammar.Grammar()
+ g.load(gp)
+ return g
+
+
+def _newer(a, b):
+ """Inquire whether file a was written since file b."""
+ if not os.path.exists(a):
+ return False
+ if not os.path.exists(b):
+ return True
+ return os.path.getmtime(a) >= os.path.getmtime(b)
diff --git a/Lib/lib2to3/pgen2/grammar.py b/Lib/lib2to3/pgen2/grammar.py
new file mode 100644
index 0000000..7818c24
--- /dev/null
+++ b/Lib/lib2to3/pgen2/grammar.py
@@ -0,0 +1,171 @@
+# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""This module defines the data structures used to represent a grammar.
+
+These are a bit arcane because they are derived from the data
+structures used by Python's 'pgen' parser generator.
+
+There's also a table here mapping operators to their names in the
+token module; the Python tokenize module reports all operators as the
+fallback token code OP, but the parser needs the actual token code.
+
+"""
+
+# Python imports
+import pickle
+
+# Local imports
+from . import token, tokenize
+
+
+class Grammar(object):
+ """Pgen parsing tables tables conversion class.
+
+ Once initialized, this class supplies the grammar tables for the
+ parsing engine implemented by parse.py. The parsing engine
+ accesses the instance variables directly. The class here does not
+ provide initialization of the tables; several subclasses exist to
+ do this (see the conv and pgen modules).
+
+ The load() method reads the tables from a pickle file, which is
+ much faster than the other ways offered by subclasses. The pickle
+ file is written by calling dump() (after loading the grammar
+ tables using a subclass). The report() method prints a readable
+ representation of the tables to stdout, for debugging.
+
+ The instance variables are as follows:
+
+ symbol2number -- a dict mapping symbol names to numbers. Symbol
+ numbers are always 256 or higher, to distinguish
+ them from token numbers, which are between 0 and
+ 255 (inclusive).
+
+ number2symbol -- a dict mapping numbers to symbol names;
+ these two are each other's inverse.
+
+ states -- a list of DFAs, where each DFA is a list of
+ states, each state is is a list of arcs, and each
+ arc is a (i, j) pair where i is a label and j is
+ a state number. The DFA number is the index into
+ this list. (This name is slightly confusing.)
+ Final states are represented by a special arc of
+ the form (0, j) where j is its own state number.
+
+ dfas -- a dict mapping symbol numbers to (DFA, first)
+ pairs, where DFA is an item from the states list
+ above, and first is a set of tokens that can
+ begin this grammar rule (represented by a dict
+ whose values are always 1).
+
+ labels -- a list of (x, y) pairs where x is either a token
+ number or a symbol number, and y is either None
+ or a string; the strings are keywords. The label
+ number is the index in this list; label numbers
+ are used to mark state transitions (arcs) in the
+ DFAs.
+
+ start -- the number of the grammar's start symbol.
+
+ keywords -- a dict mapping keyword strings to arc labels.
+
+ tokens -- a dict mapping token numbers to arc labels.
+
+ """
+
+ def __init__(self):
+ self.symbol2number = {}
+ self.number2symbol = {}
+ self.states = []
+ self.dfas = {}
+ self.labels = [(0, "EMPTY")]
+ self.keywords = {}
+ self.tokens = {}
+ self.symbol2label = {}
+ self.start = 256
+
+ def dump(self, filename):
+ """Dump the grammar tables to a pickle file."""
+ f = open(filename, "wb")
+ pickle.dump(self.__dict__, f, 2)
+ f.close()
+
+ def load(self, filename):
+ """Load the grammar tables from a pickle file."""
+ f = open(filename, "rb")
+ d = pickle.load(f)
+ f.close()
+ self.__dict__.update(d)
+
+ def report(self):
+ """Dump the grammar tables to standard output, for debugging."""
+ from pprint import pprint
+ print "s2n"
+ pprint(self.symbol2number)
+ print "n2s"
+ pprint(self.number2symbol)
+ print "states"
+ pprint(self.states)
+ print "dfas"
+ pprint(self.dfas)
+ print "labels"
+ pprint(self.labels)
+ print "start", self.start
+
+
+# Map from operator to number (since tokenize doesn't do this)
+
+opmap_raw = """
+( LPAR
+) RPAR
+[ LSQB
+] RSQB
+: COLON
+, COMMA
+; SEMI
++ PLUS
+- MINUS
+* STAR
+/ SLASH
+| VBAR
+& AMPER
+< LESS
+> GREATER
+= EQUAL
+. DOT
+% PERCENT
+` BACKQUOTE
+{ LBRACE
+} RBRACE
+@ AT
+== EQEQUAL
+!= NOTEQUAL
+<> NOTEQUAL
+<= LESSEQUAL
+>= GREATEREQUAL
+~ TILDE
+^ CIRCUMFLEX
+<< LEFTSHIFT
+>> RIGHTSHIFT
+** DOUBLESTAR
++= PLUSEQUAL
+-= MINEQUAL
+*= STAREQUAL
+/= SLASHEQUAL
+%= PERCENTEQUAL
+&= AMPEREQUAL
+|= VBAREQUAL
+^= CIRCUMFLEXEQUAL
+<<= LEFTSHIFTEQUAL
+>>= RIGHTSHIFTEQUAL
+**= DOUBLESTAREQUAL
+// DOUBLESLASH
+//= DOUBLESLASHEQUAL
+-> RARROW
+"""
+
+opmap = {}
+for line in opmap_raw.splitlines():
+ if line:
+ op, name = line.split()
+ opmap[op] = getattr(token, name)
diff --git a/Lib/lib2to3/pgen2/literals.py b/Lib/lib2to3/pgen2/literals.py
new file mode 100644
index 0000000..0b3948a
--- /dev/null
+++ b/Lib/lib2to3/pgen2/literals.py
@@ -0,0 +1,60 @@
+# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Safely evaluate Python string literals without using eval()."""
+
+import re
+
+simple_escapes = {"a": "\a",
+ "b": "\b",
+ "f": "\f",
+ "n": "\n",
+ "r": "\r",
+ "t": "\t",
+ "v": "\v",
+ "'": "'",
+ '"': '"',
+ "\\": "\\"}
+
+def escape(m):
+ all, tail = m.group(0, 1)
+ assert all.startswith("\\")
+ esc = simple_escapes.get(tail)
+ if esc is not None:
+ return esc
+ if tail.startswith("x"):
+ hexes = tail[1:]
+ if len(hexes) < 2:
+ raise ValueError("invalid hex string escape ('\\%s')" % tail)
+ try:
+ i = int(hexes, 16)
+ except ValueError:
+ raise ValueError("invalid hex string escape ('\\%s')" % tail)
+ else:
+ try:
+ i = int(tail, 8)
+ except ValueError:
+ raise ValueError("invalid octal string escape ('\\%s')" % tail)
+ return chr(i)
+
+def evalString(s):
+ assert s.startswith("'") or s.startswith('"'), repr(s[:1])
+ q = s[0]
+ if s[:3] == q*3:
+ q = q*3
+ assert s.endswith(q), repr(s[-len(q):])
+ assert len(s) >= 2*len(q)
+ s = s[len(q):-len(q)]
+ return re.sub(r"\\(\'|\"|\\|[abfnrtv]|x.{0,2}|[0-7]{1,3})", escape, s)
+
+def test():
+ for i in range(256):
+ c = chr(i)
+ s = repr(c)
+ e = evalString(s)
+ if e != c:
+ print i, c, s, e
+
+
+if __name__ == "__main__":
+ test()
diff --git a/Lib/lib2to3/pgen2/parse.py b/Lib/lib2to3/pgen2/parse.py
new file mode 100644
index 0000000..9b1ac2a
--- /dev/null
+++ b/Lib/lib2to3/pgen2/parse.py
@@ -0,0 +1,207 @@
+# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Parser engine for the grammar tables generated by pgen.
+
+The grammar table must be loaded first.
+
+See Parser/parser.c in the Python distribution for additional info on
+how this parsing engine works.
+
+"""
+
+# Get a usable 'set' constructor
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+# Local imports
+from . import token
+
+class ParseError(Exception):
+ """Exception to signal the parser is stuck."""
+
+ def __init__(self, msg, type, value, context):
+ Exception.__init__(self, "%s: type=%r, value=%r, context=%r" %
+ (msg, type, value, context))
+ self.msg = msg
+ self.type = type
+ self.value = value
+ self.context = context
+
+class Parser(object):
+ """Parser engine.
+
+ The proper usage sequence is:
+
+ p = Parser(grammar, [converter]) # create instance
+ p.setup([start]) # prepare for parsing
+ <for each input token>:
+ if p.addtoken(...): # parse a token; may raise ParseError
+ break
+ root = p.rootnode # root of abstract syntax tree
+
+ A Parser instance may be reused by calling setup() repeatedly.
+
+ A Parser instance contains state pertaining to the current token
+ sequence, and should not be used concurrently by different threads
+ to parse separate token sequences.
+
+ See driver.py for how to get input tokens by tokenizing a file or
+ string.
+
+ Parsing is complete when addtoken() returns True; the root of the
+ abstract syntax tree can then be retrieved from the rootnode
+ instance variable. When a syntax error occurs, addtoken() raises
+ the ParseError exception. There is no error recovery; the parser
+ cannot be used after a syntax error was reported (but it can be
+ reinitialized by calling setup()).
+
+ """
+
+ def __init__(self, grammar, convert=None):
+ """Constructor.
+
+ The grammar argument is a grammar.Grammar instance; see the
+ grammar module for more information.
+
+ The parser is not ready yet for parsing; you must call the
+ setup() method to get it started.
+
+ The optional convert argument is a function mapping concrete
+ syntax tree nodes to abstract syntax tree nodes. If not
+ given, no conversion is done and the syntax tree produced is
+ the concrete syntax tree. If given, it must be a function of
+ two arguments, the first being the grammar (a grammar.Grammar
+ instance), and the second being the concrete syntax tree node
+ to be converted. The syntax tree is converted from the bottom
+ up.
+
+ A concrete syntax tree node is a (type, value, context, nodes)
+ tuple, where type is the node type (a token or symbol number),
+ value is None for symbols and a string for tokens, context is
+ None or an opaque value used for error reporting (typically a
+ (lineno, offset) pair), and nodes is a list of children for
+ symbols, and None for tokens.
+
+ An abstract syntax tree node may be anything; this is entirely
+ up to the converter function.
+
+ """
+ self.grammar = grammar
+ self.convert = convert or (lambda grammar, node: node)
+
+ def setup(self, start=None):
+ """Prepare for parsing.
+
+ This *must* be called before starting to parse.
+
+ The optional argument is an alternative start symbol; it
+ defaults to the grammar's start symbol.
+
+ You can use a Parser instance to parse any number of programs;
+ each time you call setup() the parser is reset to an initial
+ state determined by the (implicit or explicit) start symbol.
+
+ """
+ if start is None:
+ start = self.grammar.start
+ # Each stack entry is a tuple: (dfa, state, node).
+ # A node is a tuple: (type, value, context, children),
+ # where children is a list of nodes or None, and context may be None.
+ newnode = (start, None, None, [])
+ stackentry = (self.grammar.dfas[start], 0, newnode)
+ self.stack = [stackentry]
+ self.rootnode = None
+ self.used_names = set() # Aliased to self.rootnode.used_names in pop()
+
+ def addtoken(self, type, value, context):
+ """Add a token; return True iff this is the end of the program."""
+ # Map from token to label
+ ilabel = self.classify(type, value, context)
+ # Loop until the token is shifted; may raise exceptions
+ while True:
+ dfa, state, node = self.stack[-1]
+ states, first = dfa
+ arcs = states[state]
+ # Look for a state with this label
+ for i, newstate in arcs:
+ t, v = self.grammar.labels[i]
+ if ilabel == i:
+ # Look it up in the list of labels
+ assert t < 256
+ # Shift a token; we're done with it
+ self.shift(type, value, newstate, context)
+ # Pop while we are in an accept-only state
+ state = newstate
+ while states[state] == [(0, state)]:
+ self.pop()
+ if not self.stack:
+ # Done parsing!
+ return True
+ dfa, state, node = self.stack[-1]
+ states, first = dfa
+ # Done with this token
+ return False
+ elif t >= 256:
+ # See if it's a symbol and if we're in its first set
+ itsdfa = self.grammar.dfas[t]
+ itsstates, itsfirst = itsdfa
+ if ilabel in itsfirst:
+ # Push a symbol
+ self.push(t, self.grammar.dfas[t], newstate, context)
+ break # To continue the outer while loop
+ else:
+ if (0, state) in arcs:
+ # An accepting state, pop it and try something else
+ self.pop()
+ if not self.stack:
+ # Done parsing, but another token is input
+ raise ParseError("too much input",
+ type, value, context)
+ else:
+ # No success finding a transition
+ raise ParseError("bad input", type, value, context)
+
+ def classify(self, type, value, context):
+ """Turn a token into a label. (Internal)"""
+ if type == token.NAME:
+ # Keep a listing of all used names
+ self.used_names.add(value)
+ # Check for reserved words
+ ilabel = self.grammar.keywords.get(value)
+ if ilabel is not None:
+ return ilabel
+ ilabel = self.grammar.tokens.get(type)
+ if ilabel is None:
+ raise ParseError("bad token", type, value, context)
+ return ilabel
+
+ def shift(self, type, value, newstate, context):
+ """Shift a token. (Internal)"""
+ dfa, state, node = self.stack[-1]
+ newnode = (type, value, context, None)
+ newnode = self.convert(self.grammar, newnode)
+ if newnode is not None:
+ node[-1].append(newnode)
+ self.stack[-1] = (dfa, newstate, node)
+
+ def push(self, type, newdfa, newstate, context):
+ """Push a nonterminal. (Internal)"""
+ dfa, state, node = self.stack[-1]
+ newnode = (type, None, context, [])
+ self.stack[-1] = (dfa, newstate, node)
+ self.stack.append((newdfa, 0, newnode))
+
+ def pop(self):
+ """Pop a nonterminal. (Internal)"""
+ popdfa, popstate, popnode = self.stack.pop()
+ newnode = self.convert(self.grammar, popnode)
+ if newnode is not None:
+ if self.stack:
+ dfa, state, node = self.stack[-1]
+ node[-1].append(newnode)
+ else:
+ self.rootnode = newnode
+ self.rootnode.used_names = self.used_names
diff --git a/Lib/lib2to3/pgen2/pgen.py b/Lib/lib2to3/pgen2/pgen.py
new file mode 100644
index 0000000..220a71b
--- /dev/null
+++ b/Lib/lib2to3/pgen2/pgen.py
@@ -0,0 +1,384 @@
+# Copyright 2004-2005 Elemental Security, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+# Pgen imports
+from pgen2 import grammar, token, tokenize
+
+class PgenGrammar(grammar.Grammar):
+ pass
+
+class ParserGenerator(object):
+
+ def __init__(self, filename, stream=None):
+ close_stream = None
+ if stream is None:
+ stream = open(filename)
+ close_stream = stream.close
+ self.filename = filename
+ self.stream = stream
+ self.generator = tokenize.generate_tokens(stream.readline)
+ self.gettoken() # Initialize lookahead
+ self.dfas, self.startsymbol = self.parse()
+ if close_stream is not None:
+ close_stream()
+ self.first = {} # map from symbol name to set of tokens
+ self.addfirstsets()
+
+ def make_grammar(self):
+ c = PgenGrammar()
+ names = self.dfas.keys()
+ names.sort()
+ names.remove(self.startsymbol)
+ names.insert(0, self.startsymbol)
+ for name in names:
+ i = 256 + len(c.symbol2number)
+ c.symbol2number[name] = i
+ c.number2symbol[i] = name
+ for name in names:
+ dfa = self.dfas[name]
+ states = []
+ for state in dfa:
+ arcs = []
+ for label, next in state.arcs.iteritems():
+ arcs.append((self.make_label(c, label), dfa.index(next)))
+ if state.isfinal:
+ arcs.append((0, dfa.index(state)))
+ states.append(arcs)
+ c.states.append(states)
+ c.dfas[c.symbol2number[name]] = (states, self.make_first(c, name))
+ c.start = c.symbol2number[self.startsymbol]
+ return c
+
+ def make_first(self, c, name):
+ rawfirst = self.first[name]
+ first = {}
+ for label in rawfirst:
+ ilabel = self.make_label(c, label)
+ ##assert ilabel not in first # XXX failed on <> ... !=
+ first[ilabel] = 1
+ return first
+
+ def make_label(self, c, label):
+ # XXX Maybe this should be a method on a subclass of converter?
+ ilabel = len(c.labels)
+ if label[0].isalpha():
+ # Either a symbol name or a named token
+ if label in c.symbol2number:
+ # A symbol name (a non-terminal)
+ if label in c.symbol2label:
+ return c.symbol2label[label]
+ else:
+ c.labels.append((c.symbol2number[label], None))
+ c.symbol2label[label] = ilabel
+ return ilabel
+ else:
+ # A named token (NAME, NUMBER, STRING)
+ itoken = getattr(token, label, None)
+ assert isinstance(itoken, int), label
+ assert itoken in token.tok_name, label
+ if itoken in c.tokens:
+ return c.tokens[itoken]
+ else:
+ c.labels.append((itoken, None))
+ c.tokens[itoken] = ilabel
+ return ilabel
+ else:
+ # Either a keyword or an operator
+ assert label[0] in ('"', "'"), label
+ value = eval(label)
+ if value[0].isalpha():
+ # A keyword
+ if value in c.keywords:
+ return c.keywords[value]
+ else:
+ c.labels.append((token.NAME, value))
+ c.keywords[value] = ilabel
+ return ilabel
+ else:
+ # An operator (any non-numeric token)
+ itoken = grammar.opmap[value] # Fails if unknown token
+ if itoken in c.tokens:
+ return c.tokens[itoken]
+ else:
+ c.labels.append((itoken, None))
+ c.tokens[itoken] = ilabel
+ return ilabel
+
+ def addfirstsets(self):
+ names = self.dfas.keys()
+ names.sort()
+ for name in names:
+ if name not in self.first:
+ self.calcfirst(name)
+ #print name, self.first[name].keys()
+
+ def calcfirst(self, name):
+ dfa = self.dfas[name]
+ self.first[name] = None # dummy to detect left recursion
+ state = dfa[0]
+ totalset = {}
+ overlapcheck = {}
+ for label, next in state.arcs.iteritems():
+ if label in self.dfas:
+ if label in self.first:
+ fset = self.first[label]
+ if fset is None:
+ raise ValueError("recursion for rule %r" % name)
+ else:
+ self.calcfirst(label)
+ fset = self.first[label]
+ totalset.update(fset)
+ overlapcheck[label] = fset
+ else:
+ totalset[label] = 1
+ overlapcheck[label] = {label: 1}
+ inverse = {}
+ for label, itsfirst in overlapcheck.iteritems():
+ for symbol in itsfirst:
+ if symbol in inverse:
+ raise ValueError("rule %s is ambiguous; %s is in the"
+ " first sets of %s as well as %s" %
+ (name, symbol, label, inverse[symbol]))
+ inverse[symbol] = label
+ self.first[name] = totalset
+
+ def parse(self):
+ dfas = {}
+ startsymbol = None
+ # MSTART: (NEWLINE | RULE)* ENDMARKER
+ while self.type != token.ENDMARKER:
+ while self.type == token.NEWLINE:
+ self.gettoken()
+ # RULE: NAME ':' RHS NEWLINE
+ name = self.expect(token.NAME)
+ self.expect(token.OP, ":")
+ a, z = self.parse_rhs()
+ self.expect(token.NEWLINE)
+ #self.dump_nfa(name, a, z)
+ dfa = self.make_dfa(a, z)
+ #self.dump_dfa(name, dfa)
+ oldlen = len(dfa)
+ self.simplify_dfa(dfa)
+ newlen = len(dfa)
+ dfas[name] = dfa
+ #print name, oldlen, newlen
+ if startsymbol is None:
+ startsymbol = name
+ return dfas, startsymbol
+
+ def make_dfa(self, start, finish):
+ # To turn an NFA into a DFA, we define the states of the DFA
+ # to correspond to *sets* of states of the NFA. Then do some
+ # state reduction. Let's represent sets as dicts with 1 for
+ # values.
+ assert isinstance(start, NFAState)
+ assert isinstance(finish, NFAState)
+ def closure(state):
+ base = {}
+ addclosure(state, base)
+ return base
+ def addclosure(state, base):
+ assert isinstance(state, NFAState)
+ if state in base:
+ return
+ base[state] = 1
+ for label, next in state.arcs:
+ if label is None:
+ addclosure(next, base)
+ states = [DFAState(closure(start), finish)]
+ for state in states: # NB states grows while we're iterating
+ arcs = {}
+ for nfastate in state.nfaset:
+ for label, next in nfastate.arcs:
+ if label is not None:
+ addclosure(next, arcs.setdefault(label, {}))
+ for label, nfaset in arcs.iteritems():
+ for st in states:
+ if st.nfaset == nfaset:
+ break
+ else:
+ st = DFAState(nfaset, finish)
+ states.append(st)
+ state.addarc(st, label)
+ return states # List of DFAState instances; first one is start
+
+ def dump_nfa(self, name, start, finish):
+ print "Dump of NFA for", name
+ todo = [start]
+ for i, state in enumerate(todo):
+ print " State", i, state is finish and "(final)" or ""
+ for label, next in state.arcs:
+ if next in todo:
+ j = todo.index(next)
+ else:
+ j = len(todo)
+ todo.append(next)
+ if label is None:
+ print " -> %d" % j
+ else:
+ print " %s -> %d" % (label, j)
+
+ def dump_dfa(self, name, dfa):
+ print "Dump of DFA for", name
+ for i, state in enumerate(dfa):
+ print " State", i, state.isfinal and "(final)" or ""
+ for label, next in state.arcs.iteritems():
+ print " %s -> %d" % (label, dfa.index(next))
+
+ def simplify_dfa(self, dfa):
+ # This is not theoretically optimal, but works well enough.
+ # Algorithm: repeatedly look for two states that have the same
+ # set of arcs (same labels pointing to the same nodes) and
+ # unify them, until things stop changing.
+
+ # dfa is a list of DFAState instances
+ changes = True
+ while changes:
+ changes = False
+ for i, state_i in enumerate(dfa):
+ for j in range(i+1, len(dfa)):
+ state_j = dfa[j]
+ if state_i == state_j:
+ #print " unify", i, j
+ del dfa[j]
+ for state in dfa:
+ state.unifystate(state_j, state_i)
+ changes = True
+ break
+
+ def parse_rhs(self):
+ # RHS: ALT ('|' ALT)*
+ a, z = self.parse_alt()
+ if self.value != "|":
+ return a, z
+ else:
+ aa = NFAState()
+ zz = NFAState()
+ aa.addarc(a)
+ z.addarc(zz)
+ while self.value == "|":
+ self.gettoken()
+ a, z = self.parse_alt()
+ aa.addarc(a)
+ z.addarc(zz)
+ return aa, zz
+
+ def parse_alt(self):
+ # ALT: ITEM+
+ a, b = self.parse_item()
+ while (self.value in ("(", "[") or
+ self.type in (token.NAME, token.STRING)):
+ c, d = self.parse_item()
+ b.addarc(c)
+ b = d
+ return a, b
+
+ def parse_item(self):
+ # ITEM: '[' RHS ']' | ATOM ['+' | '*']
+ if self.value == "[":
+ self.gettoken()
+ a, z = self.parse_rhs()
+ self.expect(token.OP, "]")
+ a.addarc(z)
+ return a, z
+ else:
+ a, z = self.parse_atom()
+ value = self.value
+ if value not in ("+", "*"):
+ return a, z
+ self.gettoken()
+ z.addarc(a)
+ if value == "+":
+ return a, z
+ else:
+ return a, a
+
+ def parse_atom(self):
+ # ATOM: '(' RHS ')' | NAME | STRING
+ if self.value == "(":
+ self.gettoken()
+ a, z = self.parse_rhs()
+ self.expect(token.OP, ")")
+ return a, z
+ elif self.type in (token.NAME, token.STRING):
+ a = NFAState()
+ z = NFAState()
+ a.addarc(z, self.value)
+ self.gettoken()
+ return a, z
+ else:
+ self.raise_error("expected (...) or NAME or STRING, got %s/%s",
+ self.type, self.value)
+
+ def expect(self, type, value=None):
+ if self.type != type or (value is not None and self.value != value):
+ self.raise_error("expected %s/%s, got %s/%s",
+ type, value, self.type, self.value)
+ value = self.value
+ self.gettoken()
+ return value
+
+ def gettoken(self):
+ tup = self.generator.next()
+ while tup[0] in (tokenize.COMMENT, tokenize.NL):
+ tup = self.generator.next()
+ self.type, self.value, self.begin, self.end, self.line = tup
+ #print token.tok_name[self.type], repr(self.value)
+
+ def raise_error(self, msg, *args):
+ if args:
+ try:
+ msg = msg % args
+ except:
+ msg = " ".join([msg] + map(str, args))
+ raise SyntaxError(msg, (self.filename, self.end[0],
+ self.end[1], self.line))
+
+class NFAState(object):
+
+ def __init__(self):
+ self.arcs = [] # list of (label, NFAState) pairs
+
+ def addarc(self, next, label=None):
+ assert label is None or isinstance(label, str)
+ assert isinstance(next, NFAState)
+ self.arcs.append((label, next))
+
+class DFAState(object):
+
+ def __init__(self, nfaset, final):
+ assert isinstance(nfaset, dict)
+ assert isinstance(iter(nfaset).next(), NFAState)
+ assert isinstance(final, NFAState)
+ self.nfaset = nfaset
+ self.isfinal = final in nfaset
+ self.arcs = {} # map from label to DFAState
+
+ def addarc(self, next, label):
+ assert isinstance(label, str)
+ assert label not in self.arcs
+ assert isinstance(next, DFAState)
+ self.arcs[label] = next
+
+ def unifystate(self, old, new):
+ for label, next in self.arcs.iteritems():
+ if next is old:
+ self.arcs[label] = new
+
+ def __eq__(self, other):
+ # Equality test -- ignore the nfaset instance variable
+ assert isinstance(other, DFAState)
+ if self.isfinal != other.isfinal:
+ return False
+ # Can't just return self.arcs == other.arcs, because that
+ # would invoke this method recursively, with cycles...
+ if len(self.arcs) != len(other.arcs):
+ return False
+ for label, next in self.arcs.iteritems():
+ if next is not other.arcs.get(label):
+ return False
+ return True
+
+def generate_grammar(filename="Grammar.txt"):
+ p = ParserGenerator(filename)
+ return p.make_grammar()
diff --git a/Lib/lib2to3/pgen2/token.py b/Lib/lib2to3/pgen2/token.py
new file mode 100755
index 0000000..61468b3
--- /dev/null
+++ b/Lib/lib2to3/pgen2/token.py
@@ -0,0 +1,82 @@
+#! /usr/bin/env python
+
+"""Token constants (from "token.h")."""
+
+# Taken from Python (r53757) and modified to include some tokens
+# originally monkeypatched in by pgen2.tokenize
+
+#--start constants--
+ENDMARKER = 0
+NAME = 1
+NUMBER = 2
+STRING = 3
+NEWLINE = 4
+INDENT = 5
+DEDENT = 6
+LPAR = 7
+RPAR = 8
+LSQB = 9
+RSQB = 10
+COLON = 11
+COMMA = 12
+SEMI = 13
+PLUS = 14
+MINUS = 15
+STAR = 16
+SLASH = 17
+VBAR = 18
+AMPER = 19
+LESS = 20
+GREATER = 21
+EQUAL = 22
+DOT = 23
+PERCENT = 24
+BACKQUOTE = 25
+LBRACE = 26
+RBRACE = 27
+EQEQUAL = 28
+NOTEQUAL = 29
+LESSEQUAL = 30
+GREATEREQUAL = 31
+TILDE = 32
+CIRCUMFLEX = 33
+LEFTSHIFT = 34
+RIGHTSHIFT = 35
+DOUBLESTAR = 36
+PLUSEQUAL = 37
+MINEQUAL = 38
+STAREQUAL = 39
+SLASHEQUAL = 40
+PERCENTEQUAL = 41
+AMPEREQUAL = 42
+VBAREQUAL = 43
+CIRCUMFLEXEQUAL = 44
+LEFTSHIFTEQUAL = 45
+RIGHTSHIFTEQUAL = 46
+DOUBLESTAREQUAL = 47
+DOUBLESLASH = 48
+DOUBLESLASHEQUAL = 49
+AT = 50
+OP = 51
+COMMENT = 52
+NL = 53
+RARROW = 54
+ERRORTOKEN = 55
+N_TOKENS = 56
+NT_OFFSET = 256
+#--end constants--
+
+tok_name = {}
+for _name, _value in globals().items():
+ if type(_value) is type(0):
+ tok_name[_value] = _name
+
+
+def ISTERMINAL(x):
+ return x < NT_OFFSET
+
+def ISNONTERMINAL(x):
+ return x >= NT_OFFSET
+
+def ISEOF(x):
+ return x == ENDMARKER
diff --git a/Lib/lib2to3/pgen2/tokenize.py b/Lib/lib2to3/pgen2/tokenize.py
new file mode 100644
index 0000000..c31d549
--- /dev/null
+++ b/Lib/lib2to3/pgen2/tokenize.py
@@ -0,0 +1,405 @@
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation.
+# All rights reserved.
+
+"""Tokenization help for Python programs.
+
+generate_tokens(readline) is a generator that breaks a stream of
+text into Python tokens. It accepts a readline-like method which is called
+repeatedly to get the next line of input (or "" for EOF). It generates
+5-tuples with these members:
+
+ the token type (see token.py)
+ the token (a string)
+ the starting (row, column) indices of the token (a 2-tuple of ints)
+ the ending (row, column) indices of the token (a 2-tuple of ints)
+ the original line (string)
+
+It is designed to match the working of the Python tokenizer exactly, except
+that it produces COMMENT tokens for comments and gives type OP for all
+operators
+
+Older entry points
+ tokenize_loop(readline, tokeneater)
+ tokenize(readline, tokeneater=printtoken)
+are the same, except instead of generating tokens, tokeneater is a callback
+function to which the 5 fields described above are passed as 5 arguments,
+each time a new token is found."""
+
+__author__ = 'Ka-Ping Yee <ping@lfw.org>'
+__credits__ = \
+ 'GvR, ESR, Tim Peters, Thomas Wouters, Fred Drake, Skip Montanaro'
+
+import string, re
+from lib2to3.pgen2.token import *
+
+from . import token
+__all__ = [x for x in dir(token) if x[0] != '_'] + ["tokenize",
+ "generate_tokens", "untokenize"]
+del token
+
+def group(*choices): return '(' + '|'.join(choices) + ')'
+def any(*choices): return group(*choices) + '*'
+def maybe(*choices): return group(*choices) + '?'
+
+Whitespace = r'[ \f\t]*'
+Comment = r'#[^\r\n]*'
+Ignore = Whitespace + any(r'\\\r?\n' + Whitespace) + maybe(Comment)
+Name = r'[a-zA-Z_]\w*'
+
+Binnumber = r'0[bB][01]*'
+Hexnumber = r'0[xX][\da-fA-F]*[lL]?'
+Octnumber = r'0[oO]?[0-7]*[lL]?'
+Decnumber = r'[1-9]\d*[lL]?'
+Intnumber = group(Binnumber, Hexnumber, Octnumber, Decnumber)
+Exponent = r'[eE][-+]?\d+'
+Pointfloat = group(r'\d+\.\d*', r'\.\d+') + maybe(Exponent)
+Expfloat = r'\d+' + Exponent
+Floatnumber = group(Pointfloat, Expfloat)
+Imagnumber = group(r'\d+[jJ]', Floatnumber + r'[jJ]')
+Number = group(Imagnumber, Floatnumber, Intnumber)
+
+# Tail end of ' string.
+Single = r"[^'\\]*(?:\\.[^'\\]*)*'"
+# Tail end of " string.
+Double = r'[^"\\]*(?:\\.[^"\\]*)*"'
+# Tail end of ''' string.
+Single3 = r"[^'\\]*(?:(?:\\.|'(?!''))[^'\\]*)*'''"
+# Tail end of """ string.
+Double3 = r'[^"\\]*(?:(?:\\.|"(?!""))[^"\\]*)*"""'
+Triple = group("[ubUB]?[rR]?'''", '[ubUB]?[rR]?"""')
+# Single-line ' or " string.
+String = group(r"[uU]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*'",
+ r'[uU]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*"')
+
+# Because of leftmost-then-longest match semantics, be sure to put the
+# longest operators first (e.g., if = came before ==, == would get
+# recognized as two instances of =).
+Operator = group(r"\*\*=?", r">>=?", r"<<=?", r"<>", r"!=",
+ r"//=?", r"->",
+ r"[+\-*/%&|^=<>]=?",
+ r"~")
+
+Bracket = '[][(){}]'
+Special = group(r'\r?\n', r'[:;.,`@]')
+Funny = group(Operator, Bracket, Special)
+
+PlainToken = group(Number, Funny, String, Name)
+Token = Ignore + PlainToken
+
+# First (or only) line of ' or " string.
+ContStr = group(r"[uUbB]?[rR]?'[^\n'\\]*(?:\\.[^\n'\\]*)*" +
+ group("'", r'\\\r?\n'),
+ r'[uUbB]?[rR]?"[^\n"\\]*(?:\\.[^\n"\\]*)*' +
+ group('"', r'\\\r?\n'))
+PseudoExtras = group(r'\\\r?\n', Comment, Triple)
+PseudoToken = Whitespace + group(PseudoExtras, Number, Funny, ContStr, Name)
+
+tokenprog, pseudoprog, single3prog, double3prog = map(
+ re.compile, (Token, PseudoToken, Single3, Double3))
+endprogs = {"'": re.compile(Single), '"': re.compile(Double),
+ "'''": single3prog, '"""': double3prog,
+ "r'''": single3prog, 'r"""': double3prog,
+ "u'''": single3prog, 'u"""': double3prog,
+ "b'''": single3prog, 'b"""': double3prog,
+ "ur'''": single3prog, 'ur"""': double3prog,
+ "br'''": single3prog, 'br"""': double3prog,
+ "R'''": single3prog, 'R"""': double3prog,
+ "U'''": single3prog, 'U"""': double3prog,
+ "B'''": single3prog, 'B"""': double3prog,
+ "uR'''": single3prog, 'uR"""': double3prog,
+ "Ur'''": single3prog, 'Ur"""': double3prog,
+ "UR'''": single3prog, 'UR"""': double3prog,
+ "bR'''": single3prog, 'bR"""': double3prog,
+ "Br'''": single3prog, 'Br"""': double3prog,
+ "BR'''": single3prog, 'BR"""': double3prog,
+ 'r': None, 'R': None,
+ 'u': None, 'U': None,
+ 'b': None, 'B': None}
+
+triple_quoted = {}
+for t in ("'''", '"""',
+ "r'''", 'r"""', "R'''", 'R"""',
+ "u'''", 'u"""', "U'''", 'U"""',
+ "b'''", 'b"""', "B'''", 'B"""',
+ "ur'''", 'ur"""', "Ur'''", 'Ur"""',
+ "uR'''", 'uR"""', "UR'''", 'UR"""',
+ "br'''", 'br"""', "Br'''", 'Br"""',
+ "bR'''", 'bR"""', "BR'''", 'BR"""',):
+ triple_quoted[t] = t
+single_quoted = {}
+for t in ("'", '"',
+ "r'", 'r"', "R'", 'R"',
+ "u'", 'u"', "U'", 'U"',
+ "b'", 'b"', "B'", 'B"',
+ "ur'", 'ur"', "Ur'", 'Ur"',
+ "uR'", 'uR"', "UR'", 'UR"',
+ "br'", 'br"', "Br'", 'Br"',
+ "bR'", 'bR"', "BR'", 'BR"', ):
+ single_quoted[t] = t
+
+tabsize = 8
+
+class TokenError(Exception): pass
+
+class StopTokenizing(Exception): pass
+
+def printtoken(type, token, (srow, scol), (erow, ecol), line): # for testing
+ print "%d,%d-%d,%d:\t%s\t%s" % \
+ (srow, scol, erow, ecol, tok_name[type], repr(token))
+
+def tokenize(readline, tokeneater=printtoken):
+ """
+ The tokenize() function accepts two parameters: one representing the
+ input stream, and one providing an output mechanism for tokenize().
+
+ The first parameter, readline, must be a callable object which provides
+ the same interface as the readline() method of built-in file objects.
+ Each call to the function should return one line of input as a string.
+
+ The second parameter, tokeneater, must also be a callable object. It is
+ called once for each token, with five arguments, corresponding to the
+ tuples generated by generate_tokens().
+ """
+ try:
+ tokenize_loop(readline, tokeneater)
+ except StopTokenizing:
+ pass
+
+# backwards compatible interface
+def tokenize_loop(readline, tokeneater):
+ for token_info in generate_tokens(readline):
+ tokeneater(*token_info)
+
+class Untokenizer:
+
+ def __init__(self):
+ self.tokens = []
+ self.prev_row = 1
+ self.prev_col = 0
+
+ def add_whitespace(self, start):
+ row, col = start
+ assert row <= self.prev_row
+ col_offset = col - self.prev_col
+ if col_offset:
+ self.tokens.append(" " * col_offset)
+
+ def untokenize(self, iterable):
+ for t in iterable:
+ if len(t) == 2:
+ self.compat(t, iterable)
+ break
+ tok_type, token, start, end, line = t
+ self.add_whitespace(start)
+ self.tokens.append(token)
+ self.prev_row, self.prev_col = end
+ if tok_type in (NEWLINE, NL):
+ self.prev_row += 1
+ self.prev_col = 0
+ return "".join(self.tokens)
+
+ def compat(self, token, iterable):
+ startline = False
+ indents = []
+ toks_append = self.tokens.append
+ toknum, tokval = token
+ if toknum in (NAME, NUMBER):
+ tokval += ' '
+ if toknum in (NEWLINE, NL):
+ startline = True
+ for tok in iterable:
+ toknum, tokval = tok[:2]
+
+ if toknum in (NAME, NUMBER):
+ tokval += ' '
+
+ if toknum == INDENT:
+ indents.append(tokval)
+ continue
+ elif toknum == DEDENT:
+ indents.pop()
+ continue
+ elif toknum in (NEWLINE, NL):
+ startline = True
+ elif startline and indents:
+ toks_append(indents[-1])
+ startline = False
+ toks_append(tokval)
+
+def untokenize(iterable):
+ """Transform tokens back into Python source code.
+
+ Each element returned by the iterable must be a token sequence
+ with at least two elements, a token number and token value. If
+ only two tokens are passed, the resulting output is poor.
+
+ Round-trip invariant for full input:
+ Untokenized source will match input source exactly
+
+ Round-trip invariant for limited intput:
+ # Output text will tokenize the back to the input
+ t1 = [tok[:2] for tok in generate_tokens(f.readline)]
+ newcode = untokenize(t1)
+ readline = iter(newcode.splitlines(1)).next
+ t2 = [tok[:2] for tokin generate_tokens(readline)]
+ assert t1 == t2
+ """
+ ut = Untokenizer()
+ return ut.untokenize(iterable)
+
+def generate_tokens(readline):
+ """
+ The generate_tokens() generator requires one argment, readline, which
+ must be a callable object which provides the same interface as the
+ readline() method of built-in file objects. Each call to the function
+ should return one line of input as a string. Alternately, readline
+ can be a callable function terminating with StopIteration:
+ readline = open(myfile).next # Example of alternate readline
+
+ The generator produces 5-tuples with these members: the token type; the
+ token string; a 2-tuple (srow, scol) of ints specifying the row and
+ column where the token begins in the source; a 2-tuple (erow, ecol) of
+ ints specifying the row and column where the token ends in the source;
+ and the line on which the token was found. The line passed is the
+ logical line; continuation lines are included.
+ """
+ lnum = parenlev = continued = 0
+ namechars, numchars = string.ascii_letters + '_', '0123456789'
+ contstr, needcont = '', 0
+ contline = None
+ indents = [0]
+
+ while 1: # loop over lines in stream
+ try:
+ line = readline()
+ except StopIteration:
+ line = ''
+ lnum = lnum + 1
+ pos, max = 0, len(line)
+
+ if contstr: # continued string
+ if not line:
+ raise TokenError, ("EOF in multi-line string", strstart)
+ endmatch = endprog.match(line)
+ if endmatch:
+ pos = end = endmatch.end(0)
+ yield (STRING, contstr + line[:end],
+ strstart, (lnum, end), contline + line)
+ contstr, needcont = '', 0
+ contline = None
+ elif needcont and line[-2:] != '\\\n' and line[-3:] != '\\\r\n':
+ yield (ERRORTOKEN, contstr + line,
+ strstart, (lnum, len(line)), contline)
+ contstr = ''
+ contline = None
+ continue
+ else:
+ contstr = contstr + line
+ contline = contline + line
+ continue
+
+ elif parenlev == 0 and not continued: # new statement
+ if not line: break
+ column = 0
+ while pos < max: # measure leading whitespace
+ if line[pos] == ' ': column = column + 1
+ elif line[pos] == '\t': column = (column/tabsize + 1)*tabsize
+ elif line[pos] == '\f': column = 0
+ else: break
+ pos = pos + 1
+ if pos == max: break
+
+ if line[pos] in '#\r\n': # skip comments or blank lines
+ if line[pos] == '#':
+ comment_token = line[pos:].rstrip('\r\n')
+ nl_pos = pos + len(comment_token)
+ yield (COMMENT, comment_token,
+ (lnum, pos), (lnum, pos + len(comment_token)), line)
+ yield (NL, line[nl_pos:],
+ (lnum, nl_pos), (lnum, len(line)), line)
+ else:
+ yield ((NL, COMMENT)[line[pos] == '#'], line[pos:],
+ (lnum, pos), (lnum, len(line)), line)
+ continue
+
+ if column > indents[-1]: # count indents or dedents
+ indents.append(column)
+ yield (INDENT, line[:pos], (lnum, 0), (lnum, pos), line)
+ while column < indents[-1]:
+ if column not in indents:
+ raise IndentationError(
+ "unindent does not match any outer indentation level",
+ ("<tokenize>", lnum, pos, line))
+ indents = indents[:-1]
+ yield (DEDENT, '', (lnum, pos), (lnum, pos), line)
+
+ else: # continued statement
+ if not line:
+ raise TokenError, ("EOF in multi-line statement", (lnum, 0))
+ continued = 0
+
+ while pos < max:
+ pseudomatch = pseudoprog.match(line, pos)
+ if pseudomatch: # scan for tokens
+ start, end = pseudomatch.span(1)
+ spos, epos, pos = (lnum, start), (lnum, end), end
+ token, initial = line[start:end], line[start]
+
+ if initial in numchars or \
+ (initial == '.' and token != '.'): # ordinary number
+ yield (NUMBER, token, spos, epos, line)
+ elif initial in '\r\n':
+ newline = NEWLINE
+ if parenlev > 0:
+ newline = NL
+ yield (newline, token, spos, epos, line)
+ elif initial == '#':
+ assert not token.endswith("\n")
+ yield (COMMENT, token, spos, epos, line)
+ elif token in triple_quoted:
+ endprog = endprogs[token]
+ endmatch = endprog.match(line, pos)
+ if endmatch: # all on one line
+ pos = endmatch.end(0)
+ token = line[start:pos]
+ yield (STRING, token, spos, (lnum, pos), line)
+ else:
+ strstart = (lnum, start) # multiple lines
+ contstr = line[start:]
+ contline = line
+ break
+ elif initial in single_quoted or \
+ token[:2] in single_quoted or \
+ token[:3] in single_quoted:
+ if token[-1] == '\n': # continued string
+ strstart = (lnum, start)
+ endprog = (endprogs[initial] or endprogs[token[1]] or
+ endprogs[token[2]])
+ contstr, needcont = line[start:], 1
+ contline = line
+ break
+ else: # ordinary string
+ yield (STRING, token, spos, epos, line)
+ elif initial in namechars: # ordinary name
+ yield (NAME, token, spos, epos, line)
+ elif initial == '\\': # continued stmt
+ # This yield is new; needed for better idempotency:
+ yield (NL, token, spos, (lnum, pos), line)
+ continued = 1
+ else:
+ if initial in '([{': parenlev = parenlev + 1
+ elif initial in ')]}': parenlev = parenlev - 1
+ yield (OP, token, spos, epos, line)
+ else:
+ yield (ERRORTOKEN, line[pos],
+ (lnum, pos), (lnum, pos+1), line)
+ pos = pos + 1
+
+ for indent in indents[1:]: # pop remaining indent levels
+ yield (DEDENT, '', (lnum, 0), (lnum, 0), '')
+ yield (ENDMARKER, '', (lnum, 0), (lnum, 0), '')
+
+if __name__ == '__main__': # testing
+ import sys
+ if len(sys.argv) > 1: tokenize(open(sys.argv[1]).readline)
+ else: tokenize(sys.stdin.readline)
diff --git a/Lib/lib2to3/pygram.py b/Lib/lib2to3/pygram.py
new file mode 100644
index 0000000..24f1bcf
--- /dev/null
+++ b/Lib/lib2to3/pygram.py
@@ -0,0 +1,38 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Export the Python grammar and symbols."""
+
+# Python imports
+import os
+
+# Local imports
+from .pgen2 import token
+from .pgen2 import driver
+from . import pytree
+
+# The grammar file
+_GRAMMAR_FILE = os.path.join(os.path.dirname(__file__), "Grammar.txt")
+
+
+class Symbols(object):
+
+ def __init__(self, grammar):
+ """Initializer.
+
+ Creates an attribute for each grammar symbol (nonterminal),
+ whose value is the symbol's type (an int >= 256).
+ """
+ for name, symbol in grammar.symbol2number.iteritems():
+ setattr(self, name, symbol)
+
+
+python_grammar = driver.load_grammar(_GRAMMAR_FILE)
+python_symbols = Symbols(python_grammar)
+
+
+def parenthesize(node):
+ return pytree.Node(python_symbols.atom,
+ (pytree.Leaf(token.LPAR, "("),
+ node,
+ pytree.Leaf(token.RPAR, ")")))
diff --git a/Lib/lib2to3/pytree.py b/Lib/lib2to3/pytree.py
new file mode 100644
index 0000000..7584f71
--- /dev/null
+++ b/Lib/lib2to3/pytree.py
@@ -0,0 +1,712 @@
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Python parse tree definitions.
+
+This is a very concrete parse tree; we need to keep every token and
+even the comments and whitespace between tokens.
+
+There's also a pattern matching implementation here.
+"""
+
+__author__ = "Guido van Rossum <guido@python.org>"
+
+
+HUGE = 0x7FFFFFFF # maximum repeat count, default max
+
+
+class Base(object):
+
+ """Abstract base class for Node and Leaf.
+
+ This provides some default functionality and boilerplate using the
+ template pattern.
+
+ A node may be a subnode of at most one parent.
+ """
+
+ # Default values for instance variables
+ type = None # int: token number (< 256) or symbol number (>= 256)
+ parent = None # Parent node pointer, or None
+ children = () # Tuple of subnodes
+ was_changed = False
+
+ def __new__(cls, *args, **kwds):
+ """Constructor that prevents Base from being instantiated."""
+ assert cls is not Base, "Cannot instantiate Base"
+ return object.__new__(cls)
+
+ def __eq__(self, other):
+ """Compares two nodes for equality.
+
+ This calls the method _eq().
+ """
+ if self.__class__ is not other.__class__:
+ return NotImplemented
+ return self._eq(other)
+
+ def __ne__(self, other):
+ """Compares two nodes for inequality.
+
+ This calls the method _eq().
+ """
+ if self.__class__ is not other.__class__:
+ return NotImplemented
+ return not self._eq(other)
+
+ def _eq(self, other):
+ """Compares two nodes for equality.
+
+ This is called by __eq__ and __ne__. It is only called if the
+ two nodes have the same type. This must be implemented by the
+ concrete subclass. Nodes should be considered equal if they
+ have the same structure, ignoring the prefix string and other
+ context information.
+ """
+ raise NotImplementedError
+
+ def clone(self):
+ """Returns a cloned (deep) copy of self.
+
+ This must be implemented by the concrete subclass.
+ """
+ raise NotImplementedError
+
+ def post_order(self):
+ """Returns a post-order iterator for the tree.
+
+ This must be implemented by the concrete subclass.
+ """
+ raise NotImplementedError
+
+ def pre_order(self):
+ """Returns a pre-order iterator for the tree.
+
+ This must be implemented by the concrete subclass.
+ """
+ raise NotImplementedError
+
+ def set_prefix(self, prefix):
+ """Sets the prefix for the node (see Leaf class).
+
+ This must be implemented by the concrete subclass.
+ """
+ raise NotImplementedError
+
+ def get_prefix(self):
+ """Returns the prefix for the node (see Leaf class).
+
+ This must be implemented by the concrete subclass.
+ """
+ raise NotImplementedError
+
+ def replace(self, new):
+ """Replaces this node with a new one in the parent."""
+ assert self.parent is not None, str(self)
+ assert new is not None
+ if not isinstance(new, list):
+ new = [new]
+ l_children = []
+ found = False
+ for ch in self.parent.children:
+ if ch is self:
+ assert not found, (self.parent.children, self, new)
+ if new is not None:
+ l_children.extend(new)
+ found = True
+ else:
+ l_children.append(ch)
+ assert found, (self.children, self, new)
+ self.parent.changed()
+ self.parent.children = l_children
+ for x in new:
+ x.parent = self.parent
+ self.parent = None
+
+ def get_lineno(self):
+ """Returns the line number which generated the invocant node."""
+ node = self
+ while not isinstance(node, Leaf):
+ if not node.children:
+ return
+ node = node.children[0]
+ return node.lineno
+
+ def changed(self):
+ if self.parent:
+ self.parent.changed()
+ self.was_changed = True
+
+ def remove(self):
+ """Remove the node from the tree. Returns the position of the node
+ in its parent's children before it was removed."""
+ if self.parent:
+ for i, node in enumerate(self.parent.children):
+ if node is self:
+ self.parent.changed()
+ del self.parent.children[i]
+ self.parent = None
+ return i
+
+ def get_next_sibling(self):
+ """Return the node immediately following the invocant in their
+ parent's children list. If the invocant does not have a next
+ sibling, return None."""
+ if self.parent is None:
+ return None
+
+ # Can't use index(); we need to test by identity
+ for i, sibling in enumerate(self.parent.children):
+ if sibling is self:
+ try:
+ return self.parent.children[i+1]
+ except IndexError:
+ return None
+
+ def get_suffix(self):
+ """Return the string immediately following the invocant node. This
+ is effectively equivalent to node.get_next_sibling().get_prefix()"""
+ next_sib = self.get_next_sibling()
+ if next_sib is None:
+ return ""
+ return next_sib.get_prefix()
+
+
+class Node(Base):
+
+ """Concrete implementation for interior nodes."""
+
+ def __init__(self, type, children, context=None, prefix=None):
+ """Initializer.
+
+ Takes a type constant (a symbol number >= 256), a sequence of
+ child nodes, and an optional context keyword argument.
+
+ As a side effect, the parent pointers of the children are updated.
+ """
+ assert type >= 256, type
+ self.type = type
+ self.children = list(children)
+ for ch in self.children:
+ assert ch.parent is None, repr(ch)
+ ch.parent = self
+ if prefix is not None:
+ self.set_prefix(prefix)
+
+ def __repr__(self):
+ """Returns a canonical string representation."""
+ return "%s(%r, %r)" % (self.__class__.__name__,
+ self.type,
+ self.children)
+
+ def __str__(self):
+ """Returns a pretty string representation.
+
+ This reproduces the input source exactly.
+ """
+ return "".join(map(str, self.children))
+
+ def _eq(self, other):
+ """Compares two nodes for equality."""
+ return (self.type, self.children) == (other.type, other.children)
+
+ def clone(self):
+ """Returns a cloned (deep) copy of self."""
+ return Node(self.type, [ch.clone() for ch in self.children])
+
+ def post_order(self):
+ """Returns a post-order iterator for the tree."""
+ for child in self.children:
+ for node in child.post_order():
+ yield node
+ yield self
+
+ def pre_order(self):
+ """Returns a pre-order iterator for the tree."""
+ yield self
+ for child in self.children:
+ for node in child.post_order():
+ yield node
+
+ def set_prefix(self, prefix):
+ """Sets the prefix for the node.
+
+ This passes the responsibility on to the first child.
+ """
+ if self.children:
+ self.children[0].set_prefix(prefix)
+
+ def get_prefix(self):
+ """Returns the prefix for the node.
+
+ This passes the call on to the first child.
+ """
+ if not self.children:
+ return ""
+ return self.children[0].get_prefix()
+
+ def set_child(self, i, child):
+ """Equivalent to 'node.children[i] = child'. This method also sets the
+ child's parent attribute appropriately."""
+ child.parent = self
+ self.children[i].parent = None
+ self.children[i] = child
+
+ def insert_child(self, i, child):
+ """Equivalent to 'node.children.insert(i, child)'. This method also
+ sets the child's parent attribute appropriately."""
+ child.parent = self
+ self.children.insert(i, child)
+
+ def append_child(self, child):
+ """Equivalent to 'node.children.append(child)'. This method also
+ sets the child's parent attribute appropriately."""
+ child.parent = self
+ self.children.append(child)
+
+
+class Leaf(Base):
+
+ """Concrete implementation for leaf nodes."""
+
+ # Default values for instance variables
+ prefix = "" # Whitespace and comments preceding this token in the input
+ lineno = 0 # Line where this token starts in the input
+ column = 0 # Column where this token tarts in the input
+
+ def __init__(self, type, value, context=None, prefix=None):
+ """Initializer.
+
+ Takes a type constant (a token number < 256), a string value,
+ and an optional context keyword argument.
+ """
+ assert 0 <= type < 256, type
+ if context is not None:
+ self.prefix, (self.lineno, self.column) = context
+ self.type = type
+ self.value = value
+ if prefix is not None:
+ self.prefix = prefix
+
+ def __repr__(self):
+ """Returns a canonical string representation."""
+ return "%s(%r, %r)" % (self.__class__.__name__,
+ self.type,
+ self.value)
+
+ def __str__(self):
+ """Returns a pretty string representation.
+
+ This reproduces the input source exactly.
+ """
+ return self.prefix + str(self.value)
+
+ def _eq(self, other):
+ """Compares two nodes for equality."""
+ return (self.type, self.value) == (other.type, other.value)
+
+ def clone(self):
+ """Returns a cloned (deep) copy of self."""
+ return Leaf(self.type, self.value,
+ (self.prefix, (self.lineno, self.column)))
+
+ def post_order(self):
+ """Returns a post-order iterator for the tree."""
+ yield self
+
+ def pre_order(self):
+ """Returns a pre-order iterator for the tree."""
+ yield self
+
+ def set_prefix(self, prefix):
+ """Sets the prefix for the node."""
+ self.changed()
+ self.prefix = prefix
+
+ def get_prefix(self):
+ """Returns the prefix for the node."""
+ return self.prefix
+
+
+def convert(gr, raw_node):
+ """Converts raw node information to a Node or Leaf instance.
+
+ This is passed to the parser driver which calls it whenever a
+ reduction of a grammar rule produces a new complete node, so that
+ the tree is build strictly bottom-up.
+ """
+ type, value, context, children = raw_node
+ if children or type in gr.number2symbol:
+ # If there's exactly one child, return that child instead of
+ # creating a new node.
+ if len(children) == 1:
+ return children[0]
+ return Node(type, children, context=context)
+ else:
+ return Leaf(type, value, context=context)
+
+
+class BasePattern(object):
+
+ """A pattern is a tree matching pattern.
+
+ It looks for a specific node type (token or symbol), and
+ optionally for a specific content.
+
+ This is an abstract base class. There are three concrete
+ subclasses:
+
+ - LeafPattern matches a single leaf node;
+ - NodePattern matches a single node (usually non-leaf);
+ - WildcardPattern matches a sequence of nodes of variable length.
+ """
+
+ # Defaults for instance variables
+ type = None # Node type (token if < 256, symbol if >= 256)
+ content = None # Optional content matching pattern
+ name = None # Optional name used to store match in results dict
+
+ def __new__(cls, *args, **kwds):
+ """Constructor that prevents BasePattern from being instantiated."""
+ assert cls is not BasePattern, "Cannot instantiate BasePattern"
+ return object.__new__(cls)
+
+ def __repr__(self):
+ args = [self.type, self.content, self.name]
+ while args and args[-1] is None:
+ del args[-1]
+ return "%s(%s)" % (self.__class__.__name__, ", ".join(map(repr, args)))
+
+ def optimize(self):
+ """A subclass can define this as a hook for optimizations.
+
+ Returns either self or another node with the same effect.
+ """
+ return self
+
+ def match(self, node, results=None):
+ """Does this pattern exactly match a node?
+
+ Returns True if it matches, False if not.
+
+ If results is not None, it must be a dict which will be
+ updated with the nodes matching named subpatterns.
+
+ Default implementation for non-wildcard patterns.
+ """
+ if self.type is not None and node.type != self.type:
+ return False
+ if self.content is not None:
+ r = None
+ if results is not None:
+ r = {}
+ if not self._submatch(node, r):
+ return False
+ if r:
+ results.update(r)
+ if results is not None and self.name:
+ results[self.name] = node
+ return True
+
+ def match_seq(self, nodes, results=None):
+ """Does this pattern exactly match a sequence of nodes?
+
+ Default implementation for non-wildcard patterns.
+ """
+ if len(nodes) != 1:
+ return False
+ return self.match(nodes[0], results)
+
+ def generate_matches(self, nodes):
+ """Generator yielding all matches for this pattern.
+
+ Default implementation for non-wildcard patterns.
+ """
+ r = {}
+ if nodes and self.match(nodes[0], r):
+ yield 1, r
+
+
+class LeafPattern(BasePattern):
+
+ def __init__(self, type=None, content=None, name=None):
+ """Initializer. Takes optional type, content, and name.
+
+ The type, if given must be a token type (< 256). If not given,
+ this matches any *leaf* node; the content may still be required.
+
+ The content, if given, must be a string.
+
+ If a name is given, the matching node is stored in the results
+ dict under that key.
+ """
+ if type is not None:
+ assert 0 <= type < 256, type
+ if content is not None:
+ assert isinstance(content, basestring), repr(content)
+ self.type = type
+ self.content = content
+ self.name = name
+
+ def match(self, node, results=None):
+ """Override match() to insist on a leaf node."""
+ if not isinstance(node, Leaf):
+ return False
+ return BasePattern.match(self, node, results)
+
+ def _submatch(self, node, results=None):
+ """Match the pattern's content to the node's children.
+
+ This assumes the node type matches and self.content is not None.
+
+ Returns True if it matches, False if not.
+
+ If results is not None, it must be a dict which will be
+ updated with the nodes matching named subpatterns.
+
+ When returning False, the results dict may still be updated.
+ """
+ return self.content == node.value
+
+
+class NodePattern(BasePattern):
+
+ wildcards = False
+
+ def __init__(self, type=None, content=None, name=None):
+ """Initializer. Takes optional type, content, and name.
+
+ The type, if given, must be a symbol type (>= 256). If the
+ type is None this matches *any* single node (leaf or not),
+ except if content is not None, in which it only matches
+ non-leaf nodes that also match the content pattern.
+
+ The content, if not None, must be a sequence of Patterns that
+ must match the node's children exactly. If the content is
+ given, the type must not be None.
+
+ If a name is given, the matching node is stored in the results
+ dict under that key.
+ """
+ if type is not None:
+ assert type >= 256, type
+ if content is not None:
+ assert not isinstance(content, basestring), repr(content)
+ content = list(content)
+ for i, item in enumerate(content):
+ assert isinstance(item, BasePattern), (i, item)
+ if isinstance(item, WildcardPattern):
+ self.wildcards = True
+ self.type = type
+ self.content = content
+ self.name = name
+
+ def _submatch(self, node, results=None):
+ """Match the pattern's content to the node's children.
+
+ This assumes the node type matches and self.content is not None.
+
+ Returns True if it matches, False if not.
+
+ If results is not None, it must be a dict which will be
+ updated with the nodes matching named subpatterns.
+
+ When returning False, the results dict may still be updated.
+ """
+ if self.wildcards:
+ for c, r in generate_matches(self.content, node.children):
+ if c == len(node.children):
+ if results is not None:
+ results.update(r)
+ return True
+ return False
+ if len(self.content) != len(node.children):
+ return False
+ for subpattern, child in zip(self.content, node.children):
+ if not subpattern.match(child, results):
+ return False
+ return True
+
+
+class WildcardPattern(BasePattern):
+
+ """A wildcard pattern can match zero or more nodes.
+
+ This has all the flexibility needed to implement patterns like:
+
+ .* .+ .? .{m,n}
+ (a b c | d e | f)
+ (...)* (...)+ (...)? (...){m,n}
+
+ except it always uses non-greedy matching.
+ """
+
+ def __init__(self, content=None, min=0, max=HUGE, name=None):
+ """Initializer.
+
+ Args:
+ content: optional sequence of subsequences of patterns;
+ if absent, matches one node;
+ if present, each subsequence is an alternative [*]
+ min: optinal minumum number of times to match, default 0
+ max: optional maximum number of times tro match, default HUGE
+ name: optional name assigned to this match
+
+ [*] Thus, if content is [[a, b, c], [d, e], [f, g, h]] this is
+ equivalent to (a b c | d e | f g h); if content is None,
+ this is equivalent to '.' in regular expression terms.
+ The min and max parameters work as follows:
+ min=0, max=maxint: .*
+ min=1, max=maxint: .+
+ min=0, max=1: .?
+ min=1, max=1: .
+ If content is not None, replace the dot with the parenthesized
+ list of alternatives, e.g. (a b c | d e | f g h)*
+ """
+ assert 0 <= min <= max <= HUGE, (min, max)
+ if content is not None:
+ content = tuple(map(tuple, content)) # Protect against alterations
+ # Check sanity of alternatives
+ assert len(content), repr(content) # Can't have zero alternatives
+ for alt in content:
+ assert len(alt), repr(alt) # Can have empty alternatives
+ self.content = content
+ self.min = min
+ self.max = max
+ self.name = name
+
+ def optimize(self):
+ """Optimize certain stacked wildcard patterns."""
+ subpattern = None
+ if (self.content is not None and
+ len(self.content) == 1 and len(self.content[0]) == 1):
+ subpattern = self.content[0][0]
+ if self.min == 1 and self.max == 1:
+ if self.content is None:
+ return NodePattern(name=self.name)
+ if subpattern is not None and self.name == subpattern.name:
+ return subpattern.optimize()
+ if (self.min <= 1 and isinstance(subpattern, WildcardPattern) and
+ subpattern.min <= 1 and self.name == subpattern.name):
+ return WildcardPattern(subpattern.content,
+ self.min*subpattern.min,
+ self.max*subpattern.max,
+ subpattern.name)
+ return self
+
+ def match(self, node, results=None):
+ """Does this pattern exactly match a node?"""
+ return self.match_seq([node], results)
+
+ def match_seq(self, nodes, results=None):
+ """Does this pattern exactly match a sequence of nodes?"""
+ for c, r in self.generate_matches(nodes):
+ if c == len(nodes):
+ if results is not None:
+ results.update(r)
+ if self.name:
+ results[self.name] = list(nodes)
+ return True
+ return False
+
+ def generate_matches(self, nodes):
+ """Generator yielding matches for a sequence of nodes.
+
+ Args:
+ nodes: sequence of nodes
+
+ Yields:
+ (count, results) tuples where:
+ count: the match comprises nodes[:count];
+ results: dict containing named submatches.
+ """
+ if self.content is None:
+ # Shortcut for special case (see __init__.__doc__)
+ for count in xrange(self.min, 1 + min(len(nodes), self.max)):
+ r = {}
+ if self.name:
+ r[self.name] = nodes[:count]
+ yield count, r
+ else:
+ for count, r in self._recursive_matches(nodes, 0):
+ if self.name:
+ r[self.name] = nodes[:count]
+ yield count, r
+
+ def _recursive_matches(self, nodes, count):
+ """Helper to recursively yield the matches."""
+ assert self.content is not None
+ if count >= self.min:
+ r = {}
+ if self.name:
+ r[self.name] = nodes[:0]
+ yield 0, r
+ if count < self.max:
+ for alt in self.content:
+ for c0, r0 in generate_matches(alt, nodes):
+ for c1, r1 in self._recursive_matches(nodes[c0:], count+1):
+ r = {}
+ r.update(r0)
+ r.update(r1)
+ yield c0 + c1, r
+
+
+class NegatedPattern(BasePattern):
+
+ def __init__(self, content=None):
+ """Initializer.
+
+ The argument is either a pattern or None. If it is None, this
+ only matches an empty sequence (effectively '$' in regex
+ lingo). If it is not None, this matches whenever the argument
+ pattern doesn't have any matches.
+ """
+ if content is not None:
+ assert isinstance(content, BasePattern), repr(content)
+ self.content = content
+
+ def match(self, node):
+ # We never match a node in its entirety
+ return False
+
+ def match_seq(self, nodes):
+ # We only match an empty sequence of nodes in its entirety
+ return len(nodes) == 0
+
+ def generate_matches(self, nodes):
+ if self.content is None:
+ # Return a match if there is an empty sequence
+ if len(nodes) == 0:
+ yield 0, {}
+ else:
+ # Return a match if the argument pattern has no matches
+ for c, r in self.content.generate_matches(nodes):
+ return
+ yield 0, {}
+
+
+def generate_matches(patterns, nodes):
+ """Generator yielding matches for a sequence of patterns and nodes.
+
+ Args:
+ patterns: a sequence of patterns
+ nodes: a sequence of nodes
+
+ Yields:
+ (count, results) tuples where:
+ count: the entire sequence of patterns matches nodes[:count];
+ results: dict containing named submatches.
+ """
+ if not patterns:
+ yield 0, {}
+ else:
+ p, rest = patterns[0], patterns[1:]
+ for c0, r0 in p.generate_matches(nodes):
+ if not rest:
+ yield c0, r0
+ else:
+ for c1, r1 in generate_matches(rest, nodes[c0:]):
+ r = {}
+ r.update(r0)
+ r.update(r1)
+ yield c0 + c1, r
diff --git a/Lib/lib2to3/refactor.py b/Lib/lib2to3/refactor.py
new file mode 100755
index 0000000..f7a3b15
--- /dev/null
+++ b/Lib/lib2to3/refactor.py
@@ -0,0 +1,526 @@
+#!/usr/bin/env python2.5
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Refactoring framework.
+
+Used as a main program, this can refactor any number of files and/or
+recursively descend down directories. Imported as a module, this
+provides infrastructure to write your own refactoring tool.
+"""
+
+__author__ = "Guido van Rossum <guido@python.org>"
+
+
+# Python imports
+import os
+import sys
+import difflib
+import optparse
+import logging
+
+# Local imports
+from .pgen2 import driver
+from .pgen2 import tokenize
+
+from . import pytree
+from . import patcomp
+from . import fixes
+from . import pygram
+
+if sys.version_info < (2, 4):
+ hdlr = logging.StreamHandler()
+ fmt = logging.Formatter('%(name)s: %(message)s')
+ hdlr.setFormatter(fmt)
+ logging.root.addHandler(hdlr)
+else:
+ logging.basicConfig(format='%(name)s: %(message)s', level=logging.INFO)
+
+
+def main(args=None):
+ """Main program.
+
+ Call without arguments to use sys.argv[1:] as the arguments; or
+ call with a list of arguments (excluding sys.argv[0]).
+
+ Returns a suggested exit status (0, 1, 2).
+ """
+ # Set up option parser
+ parser = optparse.OptionParser(usage="refactor.py [options] file|dir ...")
+ parser.add_option("-d", "--doctests_only", action="store_true",
+ help="Fix up doctests only")
+ parser.add_option("-f", "--fix", action="append", default=[],
+ help="Each FIX specifies a transformation; default all")
+ parser.add_option("-l", "--list-fixes", action="store_true",
+ help="List available transformations (fixes/fix_*.py)")
+ parser.add_option("-p", "--print-function", action="store_true",
+ help="Modify the grammar so that print() is a function")
+ parser.add_option("-v", "--verbose", action="store_true",
+ help="More verbose logging")
+ parser.add_option("-w", "--write", action="store_true",
+ help="Write back modified files")
+
+ # Parse command line arguments
+ options, args = parser.parse_args(args)
+ if options.list_fixes:
+ print "Available transformations for the -f/--fix option:"
+ for fixname in get_all_fix_names():
+ print fixname
+ if not args:
+ return 0
+ if not args:
+ print >>sys.stderr, "At least one file or directory argument required."
+ print >>sys.stderr, "Use --help to show usage."
+ return 2
+
+ # Initialize the refactoring tool
+ rt = RefactoringTool(options)
+
+ # Refactor all files and directories passed as arguments
+ if not rt.errors:
+ rt.refactor_args(args)
+ rt.summarize()
+
+ # Return error status (0 if rt.errors is zero)
+ return int(bool(rt.errors))
+
+
+def get_all_fix_names():
+ """Return a sorted list of all available fix names."""
+ fix_names = []
+ names = os.listdir(os.path.dirname(fixes.__file__))
+ names.sort()
+ for name in names:
+ if name.startswith("fix_") and name.endswith(".py"):
+ fix_names.append(name[4:-3])
+ fix_names.sort()
+ return fix_names
+
+
+class RefactoringTool(object):
+
+ def __init__(self, options):
+ """Initializer.
+
+ The argument is an optparse.Values instance.
+ """
+ self.options = options
+ self.errors = []
+ self.logger = logging.getLogger("RefactoringTool")
+ self.fixer_log = []
+ if self.options.print_function:
+ del pygram.python_grammar.keywords["print"]
+ self.driver = driver.Driver(pygram.python_grammar,
+ convert=pytree.convert,
+ logger=self.logger)
+ self.pre_order, self.post_order = self.get_fixers()
+ self.files = [] # List of files that were or should be modified
+
+ def get_fixers(self):
+ """Inspects the options to load the requested patterns and handlers.
+
+ Returns:
+ (pre_order, post_order), where pre_order is the list of fixers that
+ want a pre-order AST traversal, and post_order is the list that want
+ post-order traversal.
+ """
+ pre_order_fixers = []
+ post_order_fixers = []
+ fix_names = self.options.fix
+ if not fix_names or "all" in fix_names:
+ fix_names = get_all_fix_names()
+ for fix_name in fix_names:
+ try:
+ mod = __import__("lib2to3.fixes.fix_" + fix_name, {}, {}, ["*"])
+ except ImportError:
+ self.log_error("Can't find transformation %s", fix_name)
+ continue
+ parts = fix_name.split("_")
+ class_name = "Fix" + "".join([p.title() for p in parts])
+ try:
+ fix_class = getattr(mod, class_name)
+ except AttributeError:
+ self.log_error("Can't find fixes.fix_%s.%s",
+ fix_name, class_name)
+ continue
+ try:
+ fixer = fix_class(self.options, self.fixer_log)
+ except Exception, err:
+ self.log_error("Can't instantiate fixes.fix_%s.%s()",
+ fix_name, class_name, exc_info=True)
+ continue
+ if fixer.explicit and fix_name not in self.options.fix:
+ self.log_message("Skipping implicit fixer: %s", fix_name)
+ continue
+
+ if self.options.verbose:
+ self.log_message("Adding transformation: %s", fix_name)
+ if fixer.order == "pre":
+ pre_order_fixers.append(fixer)
+ elif fixer.order == "post":
+ post_order_fixers.append(fixer)
+ else:
+ raise ValueError("Illegal fixer order: %r" % fixer.order)
+ return (pre_order_fixers, post_order_fixers)
+
+ def log_error(self, msg, *args, **kwds):
+ """Increments error count and log a message."""
+ self.errors.append((msg, args, kwds))
+ self.logger.error(msg, *args, **kwds)
+
+ def log_message(self, msg, *args):
+ """Hook to log a message."""
+ if args:
+ msg = msg % args
+ self.logger.info(msg)
+
+ def refactor_args(self, args):
+ """Refactors files and directories from an argument list."""
+ for arg in args:
+ if arg == "-":
+ self.refactor_stdin()
+ elif os.path.isdir(arg):
+ self.refactor_dir(arg)
+ else:
+ self.refactor_file(arg)
+
+ def refactor_dir(self, arg):
+ """Descends down a directory and refactor every Python file found.
+
+ Python files are assumed to have a .py extension.
+
+ Files and subdirectories starting with '.' are skipped.
+ """
+ for dirpath, dirnames, filenames in os.walk(arg):
+ if self.options.verbose:
+ self.log_message("Descending into %s", dirpath)
+ dirnames.sort()
+ filenames.sort()
+ for name in filenames:
+ if not name.startswith(".") and name.endswith("py"):
+ fullname = os.path.join(dirpath, name)
+ self.refactor_file(fullname)
+ # Modify dirnames in-place to remove subdirs with leading dots
+ dirnames[:] = [dn for dn in dirnames if not dn.startswith(".")]
+
+ def refactor_file(self, filename):
+ """Refactors a file."""
+ try:
+ f = open(filename)
+ except IOError, err:
+ self.log_error("Can't open %s: %s", filename, err)
+ return
+ try:
+ input = f.read() + "\n" # Silence certain parse errors
+ finally:
+ f.close()
+ if self.options.doctests_only:
+ if self.options.verbose:
+ self.log_message("Refactoring doctests in %s", filename)
+ output = self.refactor_docstring(input, filename)
+ if output != input:
+ self.write_file(output, filename, input)
+ elif self.options.verbose:
+ self.log_message("No doctest changes in %s", filename)
+ else:
+ tree = self.refactor_string(input, filename)
+ if tree and tree.was_changed:
+ # The [:-1] is to take off the \n we added earlier
+ self.write_file(str(tree)[:-1], filename)
+ elif self.options.verbose:
+ self.log_message("No changes in %s", filename)
+
+ def refactor_string(self, data, name):
+ """Refactor a given input string.
+
+ Args:
+ data: a string holding the code to be refactored.
+ name: a human-readable name for use in error/log messages.
+
+ Returns:
+ An AST corresponding to the refactored input stream; None if
+ there were errors during the parse.
+ """
+ try:
+ tree = self.driver.parse_string(data,1)
+ except Exception, err:
+ self.log_error("Can't parse %s: %s: %s",
+ name, err.__class__.__name__, err)
+ return
+ if self.options.verbose:
+ self.log_message("Refactoring %s", name)
+ self.refactor_tree(tree, name)
+ return tree
+
+ def refactor_stdin(self):
+ if self.options.write:
+ self.log_error("Can't write changes back to stdin")
+ return
+ input = sys.stdin.read()
+ if self.options.doctests_only:
+ if self.options.verbose:
+ self.log_message("Refactoring doctests in stdin")
+ output = self.refactor_docstring(input, "<stdin>")
+ if output != input:
+ self.write_file(output, "<stdin>", input)
+ elif self.options.verbose:
+ self.log_message("No doctest changes in stdin")
+ else:
+ tree = self.refactor_string(input, "<stdin>")
+ if tree and tree.was_changed:
+ self.write_file(str(tree), "<stdin>", input)
+ elif self.options.verbose:
+ self.log_message("No changes in stdin")
+
+ def refactor_tree(self, tree, name):
+ """Refactors a parse tree (modifying the tree in place).
+
+ Args:
+ tree: a pytree.Node instance representing the root of the tree
+ to be refactored.
+ name: a human-readable name for this tree.
+
+ Returns:
+ True if the tree was modified, False otherwise.
+ """
+ all_fixers = self.pre_order + self.post_order
+ for fixer in all_fixers:
+ fixer.start_tree(tree, name)
+
+ self.traverse_by(self.pre_order, tree.pre_order())
+ self.traverse_by(self.post_order, tree.post_order())
+
+ for fixer in all_fixers:
+ fixer.finish_tree(tree, name)
+ return tree.was_changed
+
+ def traverse_by(self, fixers, traversal):
+ """Traverse an AST, applying a set of fixers to each node.
+
+ This is a helper method for refactor_tree().
+
+ Args:
+ fixers: a list of fixer instances.
+ traversal: a generator that yields AST nodes.
+
+ Returns:
+ None
+ """
+ if not fixers:
+ return
+ for node in traversal:
+ for fixer in fixers:
+ results = fixer.match(node)
+ if results:
+ new = fixer.transform(node, results)
+ if new is not None and (new != node or
+ str(new) != str(node)):
+ node.replace(new)
+ node = new
+
+ def write_file(self, new_text, filename, old_text=None):
+ """Writes a string to a file.
+
+ If there are no changes, this is a no-op.
+
+ Otherwise, it first shows a unified diff between the old text
+ and the new text, and then rewrites the file; the latter is
+ only done if the write option is set.
+ """
+ self.files.append(filename)
+ if old_text is None:
+ try:
+ f = open(filename, "r")
+ except IOError, err:
+ self.log_error("Can't read %s: %s", filename, err)
+ return
+ try:
+ old_text = f.read()
+ finally:
+ f.close()
+ if old_text == new_text:
+ if self.options.verbose:
+ self.log_message("No changes to %s", filename)
+ return
+ diff_texts(old_text, new_text, filename)
+ if not self.options.write:
+ if self.options.verbose:
+ self.log_message("Not writing changes to %s", filename)
+ return
+ backup = filename + ".bak"
+ if os.path.lexists(backup):
+ try:
+ os.remove(backup)
+ except os.error, err:
+ self.log_message("Can't remove backup %s", backup)
+ try:
+ os.rename(filename, backup)
+ except os.error, err:
+ self.log_message("Can't rename %s to %s", filename, backup)
+ try:
+ f = open(filename, "w")
+ except os.error, err:
+ self.log_error("Can't create %s: %s", filename, err)
+ return
+ try:
+ try:
+ f.write(new_text)
+ except os.error, err:
+ self.log_error("Can't write %s: %s", filename, err)
+ finally:
+ f.close()
+ if self.options.verbose:
+ self.log_message("Wrote changes to %s", filename)
+
+ PS1 = ">>> "
+ PS2 = "... "
+
+ def refactor_docstring(self, input, filename):
+ """Refactors a docstring, looking for doctests.
+
+ This returns a modified version of the input string. It looks
+ for doctests, which start with a ">>>" prompt, and may be
+ continued with "..." prompts, as long as the "..." is indented
+ the same as the ">>>".
+
+ (Unfortunately we can't use the doctest module's parser,
+ since, like most parsers, it is not geared towards preserving
+ the original source.)
+ """
+ result = []
+ block = None
+ block_lineno = None
+ indent = None
+ lineno = 0
+ for line in input.splitlines(True):
+ lineno += 1
+ if line.lstrip().startswith(self.PS1):
+ if block is not None:
+ result.extend(self.refactor_doctest(block, block_lineno,
+ indent, filename))
+ block_lineno = lineno
+ block = [line]
+ i = line.find(self.PS1)
+ indent = line[:i]
+ elif (indent is not None and
+ (line.startswith(indent + self.PS2) or
+ line == indent + self.PS2.rstrip() + "\n")):
+ block.append(line)
+ else:
+ if block is not None:
+ result.extend(self.refactor_doctest(block, block_lineno,
+ indent, filename))
+ block = None
+ indent = None
+ result.append(line)
+ if block is not None:
+ result.extend(self.refactor_doctest(block, block_lineno,
+ indent, filename))
+ return "".join(result)
+
+ def refactor_doctest(self, block, lineno, indent, filename):
+ """Refactors one doctest.
+
+ A doctest is given as a block of lines, the first of which starts
+ with ">>>" (possibly indented), while the remaining lines start
+ with "..." (identically indented).
+
+ """
+ try:
+ tree = self.parse_block(block, lineno, indent)
+ except Exception, err:
+ if self.options.verbose:
+ for line in block:
+ self.log_message("Source: %s", line.rstrip("\n"))
+ self.log_error("Can't parse docstring in %s line %s: %s: %s",
+ filename, lineno, err.__class__.__name__, err)
+ return block
+ if self.refactor_tree(tree, filename):
+ new = str(tree).splitlines(True)
+ # Undo the adjustment of the line numbers in wrap_toks() below.
+ clipped, new = new[:lineno-1], new[lineno-1:]
+ assert clipped == ["\n"] * (lineno-1), clipped
+ if not new[-1].endswith("\n"):
+ new[-1] += "\n"
+ block = [indent + self.PS1 + new.pop(0)]
+ if new:
+ block += [indent + self.PS2 + line for line in new]
+ return block
+
+ def summarize(self):
+ if self.options.write:
+ were = "were"
+ else:
+ were = "need to be"
+ if not self.files:
+ self.log_message("No files %s modified.", were)
+ else:
+ self.log_message("Files that %s modified:", were)
+ for file in self.files:
+ self.log_message(file)
+ if self.fixer_log:
+ self.log_message("Warnings/messages while refactoring:")
+ for message in self.fixer_log:
+ self.log_message(message)
+ if self.errors:
+ if len(self.errors) == 1:
+ self.log_message("There was 1 error:")
+ else:
+ self.log_message("There were %d errors:", len(self.errors))
+ for msg, args, kwds in self.errors:
+ self.log_message(msg, *args, **kwds)
+
+ def parse_block(self, block, lineno, indent):
+ """Parses a block into a tree.
+
+ This is necessary to get correct line number / offset information
+ in the parser diagnostics and embedded into the parse tree.
+ """
+ return self.driver.parse_tokens(self.wrap_toks(block, lineno, indent))
+
+ def wrap_toks(self, block, lineno, indent):
+ """Wraps a tokenize stream to systematically modify start/end."""
+ tokens = tokenize.generate_tokens(self.gen_lines(block, indent).next)
+ for type, value, (line0, col0), (line1, col1), line_text in tokens:
+ line0 += lineno - 1
+ line1 += lineno - 1
+ # Don't bother updating the columns; this is too complicated
+ # since line_text would also have to be updated and it would
+ # still break for tokens spanning lines. Let the user guess
+ # that the column numbers for doctests are relative to the
+ # end of the prompt string (PS1 or PS2).
+ yield type, value, (line0, col0), (line1, col1), line_text
+
+
+ def gen_lines(self, block, indent):
+ """Generates lines as expected by tokenize from a list of lines.
+
+ This strips the first len(indent + self.PS1) characters off each line.
+ """
+ prefix1 = indent + self.PS1
+ prefix2 = indent + self.PS2
+ prefix = prefix1
+ for line in block:
+ if line.startswith(prefix):
+ yield line[len(prefix):]
+ elif line == prefix.rstrip() + "\n":
+ yield "\n"
+ else:
+ raise AssertionError("line=%r, prefix=%r" % (line, prefix))
+ prefix = prefix2
+ while True:
+ yield ""
+
+
+def diff_texts(a, b, filename):
+ """Prints a unified diff of two strings."""
+ a = a.splitlines()
+ b = b.splitlines()
+ for line in difflib.unified_diff(a, b, filename, filename,
+ "(original)", "(refactored)",
+ lineterm=""):
+ print line
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/Lib/lib2to3/tests/__init__.py b/Lib/lib2to3/tests/__init__.py
new file mode 100644
index 0000000..cfaea0d
--- /dev/null
+++ b/Lib/lib2to3/tests/__init__.py
@@ -0,0 +1,24 @@
+"""Make tests/ into a package. This allows us to "import tests" and
+have tests.all_tests be a TestSuite representing all test cases
+from all test_*.py files in tests/."""
+# Author: Collin Winter
+
+import os
+import os.path
+import unittest
+import types
+
+from . import support
+
+all_tests = unittest.TestSuite()
+
+tests_dir = os.path.join(os.path.dirname(__file__), '..', 'tests')
+tests = [t[0:-3] for t in os.listdir(tests_dir)
+ if t.startswith('test_') and t.endswith('.py')]
+
+loader = unittest.TestLoader()
+
+for t in tests:
+ __import__("",globals(),locals(),[t],level=1)
+ mod = globals()[t]
+ all_tests.addTests(loader.loadTestsFromModule(mod))
diff --git a/Lib/lib2to3/tests/benchmark.py b/Lib/lib2to3/tests/benchmark.py
new file mode 100644
index 0000000..7ccdd17
--- /dev/null
+++ b/Lib/lib2to3/tests/benchmark.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python2.5
+"""
+This is a benchmarking script to test the speed of 2to3's pattern matching
+system. It's equivalent to "refactor.py -f all" for every Python module
+in sys.modules, but without engaging the actual transformations.
+"""
+
+__author__ = "Collin Winter <collinw at gmail.com>"
+
+# Python imports
+import os.path
+import sys
+from time import time
+
+# Test imports
+from support import adjust_path
+adjust_path()
+
+# Local imports
+from .. import refactor
+
+### Mock code for refactor.py and the fixers
+###############################################################################
+class Options:
+ def __init__(self, **kwargs):
+ for k, v in kwargs.items():
+ setattr(self, k, v)
+
+ self.verbose = False
+
+def dummy_transform(*args, **kwargs):
+ pass
+
+### Collect list of modules to match against
+###############################################################################
+files = []
+for mod in sys.modules.values():
+ if mod is None or not hasattr(mod, '__file__'):
+ continue
+ f = mod.__file__
+ if f.endswith('.pyc'):
+ f = f[:-1]
+ if f.endswith('.py'):
+ files.append(f)
+
+### Set up refactor and run the benchmark
+###############################################################################
+options = Options(fix=["all"], print_function=False, doctests_only=False)
+refactor = refactor.RefactoringTool(options)
+for fixer in refactor.fixers:
+ # We don't want them to actually fix the tree, just match against it.
+ fixer.transform = dummy_transform
+
+t = time()
+for f in files:
+ print "Matching", f
+ refactor.refactor_file(f)
+print "%d seconds to match %d files" % (time() - t, len(sys.modules))
diff --git a/Lib/lib2to3/tests/data/py2_test_grammar.py b/Lib/lib2to3/tests/data/py2_test_grammar.py
new file mode 100644
index 0000000..e5d8a0f
--- /dev/null
+++ b/Lib/lib2to3/tests/data/py2_test_grammar.py
@@ -0,0 +1,922 @@
+# Python 2's Lib/test/test_grammar.py (r54061)
+
+# Python test set -- part 1, grammar.
+# This just tests whether the parser accepts them all.
+
+# NOTE: When you run this test as a script from the command line, you
+# get warnings about certain hex/oct constants. Since those are
+# issued by the parser, you can't suppress them by adding a
+# filterwarnings() call to this module. Therefore, to shut up the
+# regression test, the filterwarnings() call has been added to
+# regrtest.py.
+
+from test.test_support import run_unittest, check_syntax_error
+import unittest
+import sys
+# testing import *
+from sys import *
+
+class TokenTests(unittest.TestCase):
+
+ def testBackslash(self):
+ # Backslash means line continuation:
+ x = 1 \
+ + 1
+ self.assertEquals(x, 2, 'backslash for line continuation')
+
+ # Backslash does not means continuation in comments :\
+ x = 0
+ self.assertEquals(x, 0, 'backslash ending comment')
+
+ def testPlainIntegers(self):
+ self.assertEquals(0xff, 255)
+ self.assertEquals(0377, 255)
+ self.assertEquals(2147483647, 017777777777)
+ from sys import maxint
+ if maxint == 2147483647:
+ self.assertEquals(-2147483647-1, -020000000000)
+ # XXX -2147483648
+ self.assert_(037777777777 > 0)
+ self.assert_(0xffffffff > 0)
+ for s in '2147483648', '040000000000', '0x100000000':
+ try:
+ x = eval(s)
+ except OverflowError:
+ self.fail("OverflowError on huge integer literal %r" % s)
+ elif maxint == 9223372036854775807:
+ self.assertEquals(-9223372036854775807-1, -01000000000000000000000)
+ self.assert_(01777777777777777777777 > 0)
+ self.assert_(0xffffffffffffffff > 0)
+ for s in '9223372036854775808', '02000000000000000000000', \
+ '0x10000000000000000':
+ try:
+ x = eval(s)
+ except OverflowError:
+ self.fail("OverflowError on huge integer literal %r" % s)
+ else:
+ self.fail('Weird maxint value %r' % maxint)
+
+ def testLongIntegers(self):
+ x = 0L
+ x = 0l
+ x = 0xffffffffffffffffL
+ x = 0xffffffffffffffffl
+ x = 077777777777777777L
+ x = 077777777777777777l
+ x = 123456789012345678901234567890L
+ x = 123456789012345678901234567890l
+
+ def testFloats(self):
+ x = 3.14
+ x = 314.
+ x = 0.314
+ # XXX x = 000.314
+ x = .314
+ x = 3e14
+ x = 3E14
+ x = 3e-14
+ x = 3e+14
+ x = 3.e14
+ x = .3e14
+ x = 3.1e4
+
+ def testStringLiterals(self):
+ x = ''; y = ""; self.assert_(len(x) == 0 and x == y)
+ x = '\''; y = "'"; self.assert_(len(x) == 1 and x == y and ord(x) == 39)
+ x = '"'; y = "\""; self.assert_(len(x) == 1 and x == y and ord(x) == 34)
+ x = "doesn't \"shrink\" does it"
+ y = 'doesn\'t "shrink" does it'
+ self.assert_(len(x) == 24 and x == y)
+ x = "does \"shrink\" doesn't it"
+ y = 'does "shrink" doesn\'t it'
+ self.assert_(len(x) == 24 and x == y)
+ x = """
+The "quick"
+brown fox
+jumps over
+the 'lazy' dog.
+"""
+ y = '\nThe "quick"\nbrown fox\njumps over\nthe \'lazy\' dog.\n'
+ self.assertEquals(x, y)
+ y = '''
+The "quick"
+brown fox
+jumps over
+the 'lazy' dog.
+'''
+ self.assertEquals(x, y)
+ y = "\n\
+The \"quick\"\n\
+brown fox\n\
+jumps over\n\
+the 'lazy' dog.\n\
+"
+ self.assertEquals(x, y)
+ y = '\n\
+The \"quick\"\n\
+brown fox\n\
+jumps over\n\
+the \'lazy\' dog.\n\
+'
+ self.assertEquals(x, y)
+
+
+class GrammarTests(unittest.TestCase):
+
+ # single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
+ # XXX can't test in a script -- this rule is only used when interactive
+
+ # file_input: (NEWLINE | stmt)* ENDMARKER
+ # Being tested as this very moment this very module
+
+ # expr_input: testlist NEWLINE
+ # XXX Hard to test -- used only in calls to input()
+
+ def testEvalInput(self):
+ # testlist ENDMARKER
+ x = eval('1, 0 or 1')
+
+ def testFuncdef(self):
+ ### 'def' NAME parameters ':' suite
+ ### parameters: '(' [varargslist] ')'
+ ### varargslist: (fpdef ['=' test] ',')* ('*' NAME [',' ('**'|'*' '*') NAME]
+ ### | ('**'|'*' '*') NAME)
+ ### | fpdef ['=' test] (',' fpdef ['=' test])* [',']
+ ### fpdef: NAME | '(' fplist ')'
+ ### fplist: fpdef (',' fpdef)* [',']
+ ### arglist: (argument ',')* (argument | *' test [',' '**' test] | '**' test)
+ ### argument: [test '='] test # Really [keyword '='] test
+ def f1(): pass
+ f1()
+ f1(*())
+ f1(*(), **{})
+ def f2(one_argument): pass
+ def f3(two, arguments): pass
+ 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.func_code.co_varnames,
+ ('two', '(compound, (argument, list))', 'compound', 'argument',
+ 'list',))
+ self.assertEquals(f5.func_code.co_varnames,
+ ('(compound, first)', 'two', 'compound', 'first'))
+ else:
+ self.assertEquals(f4.func_code.co_varnames,
+ ('two', '.1', 'compound', 'argument', 'list'))
+ 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, (b, c), *rest): return a, b, c, rest
+
+ f1()
+ f2(1)
+ f2(1,)
+ f3(1, 2)
+ f3(1, 2,)
+ f4(1, (2, (3, 4)))
+ v0()
+ v0(1)
+ v0(1,)
+ v0(1,2)
+ v0(1,2,3,4,5,6,7,8,9,0)
+ v1(1)
+ v1(1,)
+ v1(1,2)
+ v1(1,2,3)
+ v1(1,2,3,4,5,6,7,8,9,0)
+ v2(1,2)
+ v2(1,2,3)
+ v2(1,2,3,4)
+ v2(1,2,3,4,5,6,7,8,9,0)
+ v3(1,(2,3))
+ v3(1,(2,3),4)
+ v3(1,(2,3),4,5,6,7,8,9,0)
+
+ # 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.func_code.co_varnames, ('a', '(b, c)', 'rest', 'b', 'c'))
+ else:
+ 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()
+ d01(1)
+ d01(*(1,))
+ d01(**{'a':2})
+ def d11(a, b=1): pass
+ d11(1)
+ d11(1, 2)
+ d11(1, **{'b':2})
+ def d21(a, b, c=1): pass
+ d21(1, 2)
+ d21(1, 2, 3)
+ d21(*(1, 2, 3))
+ d21(1, *(2, 3))
+ d21(1, 2, *(3,))
+ d21(1, 2, **{'c':3})
+ def d02(a=1, b=2): pass
+ d02()
+ d02(1)
+ d02(1, 2)
+ d02(*(1, 2))
+ d02(1, *(2,))
+ d02(1, **{'b':2})
+ d02(**{'a': 1, 'b': 2})
+ def d12(a, b=1, c=2): pass
+ d12(1)
+ d12(1, 2)
+ d12(1, 2, 3)
+ def d22(a, b, c=1, d=2): pass
+ d22(1, 2)
+ d22(1, 2, 3)
+ d22(1, 2, 3, 4)
+ def d01v(a=1, *rest): pass
+ d01v()
+ d01v(1)
+ d01v(1, 2)
+ d01v(*(1, 2, 3, 4))
+ d01v(*(1,))
+ d01v(**{'a':2})
+ def d11v(a, b=1, *rest): pass
+ d11v(1)
+ d11v(1, 2)
+ d11v(1, 2, 3)
+ def d21v(a, b, c=1, *rest): pass
+ d21v(1, 2)
+ d21v(1, 2, 3)
+ d21v(1, 2, 3, 4)
+ d21v(*(1, 2, 3, 4))
+ d21v(1, 2, **{'c': 3})
+ def d02v(a=1, b=2, *rest): pass
+ d02v()
+ d02v(1)
+ d02v(1, 2)
+ d02v(1, 2, 3)
+ d02v(1, *(2, 3, 4))
+ d02v(**{'a': 1, 'b': 2})
+ def d12v(a, b=1, c=2, *rest): pass
+ d12v(1)
+ d12v(1, 2)
+ d12v(1, 2, 3)
+ d12v(1, 2, 3, 4)
+ d12v(*(1, 2, 3, 4))
+ d12v(1, 2, *(3, 4, 5))
+ d12v(1, *(2,), **{'c': 3})
+ def d22v(a, b, c=1, d=2, *rest): pass
+ d22v(1, 2)
+ d22v(1, 2, 3)
+ d22v(1, 2, 3, 4)
+ d22v(1, 2, 3, 4, 5)
+ d22v(*(1, 2, 3, 4))
+ d22v(1, 2, *(3, 4, 5))
+ d22v(1, *(2, 3), **{'d': 4})
+ def d31v((x)): pass
+ d31v(1)
+ def d32v((x,)): pass
+ d32v((1,))
+
+ 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, 0L]]
+ self.assertEquals(l3(), [0, 1, 0])
+ l4 = lambda x = lambda y = lambda z=1 : z : y() : x()
+ self.assertEquals(l4(), 1)
+ l5 = lambda x, y, z=2: x + y + z
+ self.assertEquals(l5(1, 2), 5)
+ self.assertEquals(l5(1, 2, 3), 6)
+ check_syntax_error(self, "lambda x: x = 2")
+
+ ### stmt: simple_stmt | compound_stmt
+ # Tested below
+
+ def testSimpleStmt(self):
+ ### simple_stmt: small_stmt (';' small_stmt)* [';']
+ x = 1; pass; del x
+ def foo():
+ # verify statments that end with semi-colons
+ x = 1; pass; del x;
+ foo()
+
+ ### small_stmt: expr_stmt | print_stmt | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt | exec_stmt
+ # Tested below
+
+ def testExprStmt(self):
+ # (exprlist '=')* exprlist
+ 1
+ 1, 2, 3
+ x = 1
+ x = 1, 2, 3
+ x = y = z = 1, 2, 3
+ x, y, z = 1, 2, 3
+ abc = a, b, c = x, y, z = xyz = 1, 2, (3, 4)
+
+ check_syntax_error(self, "x + 1 = 1")
+ check_syntax_error(self, "a + 1 = b + 2")
+
+ def testPrintStmt(self):
+ # 'print' (test ',')* [test]
+ import StringIO
+
+ # Can't test printing to real stdout without comparing output
+ # which is not available in unittest.
+ save_stdout = sys.stdout
+ sys.stdout = StringIO.StringIO()
+
+ print 1, 2, 3
+ print 1, 2, 3,
+ print
+ print 0 or 1, 0 or 1,
+ print 0 or 1
+
+ # 'print' '>>' test ','
+ 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 >> 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():
+ oldstdout = sys.stdout
+ sys.stdout = Gulp()
+ try:
+ tellme(Gulp())
+ tellme()
+ finally:
+ sys.stdout = oldstdout
+
+ # we should see this once
+ def tellme(file=sys.stdout):
+ print >> file, 'hello world'
+
+ driver()
+
+ # we should not see this at all
+ def tellme(file=None):
+ print >> file, 'goodbye universe'
+
+ driver()
+
+ self.assertEqual(sys.stdout.getvalue(), '''\
+1 2 3
+1 2 3
+1 1 1
+1 2 3
+1 2 3
+1 1 1
+hello world
+''')
+ sys.stdout = save_stdout
+
+ # syntax errors
+ check_syntax_error(self, 'print ,')
+ check_syntax_error(self, 'print >> x,')
+
+ def testDelStmt(self):
+ # 'del' exprlist
+ abc = [1,2,3]
+ x, y, z = abc
+ xyz = x, y, z
+
+ del abc
+ del x, y, (z, xyz)
+
+ def testPassStmt(self):
+ # 'pass'
+ pass
+
+ # flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt
+ # Tested below
+
+ def testBreakStmt(self):
+ # 'break'
+ while 1: break
+
+ def testContinueStmt(self):
+ # 'continue'
+ i = 1
+ while i: i = 0; continue
+
+ msg = ""
+ while not msg:
+ msg = "ok"
+ try:
+ continue
+ msg = "continue failed to continue inside try"
+ except:
+ msg = "continue inside try called except block"
+ if msg != "ok":
+ self.fail(msg)
+
+ msg = ""
+ while not msg:
+ msg = "finally block not called"
+ try:
+ continue
+ finally:
+ msg = "ok"
+ if msg != "ok":
+ self.fail(msg)
+
+ def test_break_continue_loop(self):
+ # This test warrants an explanation. It is a test specifically for SF bugs
+ # #463359 and #462937. The bug is that a 'break' statement executed or
+ # exception raised inside a try/except inside a loop, *after* a continue
+ # statement has been executed in that loop, will cause the wrong number of
+ # arguments to be popped off the stack and the instruction pointer reset to
+ # a very small number (usually 0.) Because of this, the following test
+ # *must* written as a function, and the tracking vars *must* be function
+ # arguments with default values. Otherwise, the test will loop and loop.
+
+ def test_inner(extra_burning_oil = 1, count=0):
+ big_hippo = 2
+ while big_hippo:
+ count += 1
+ try:
+ if extra_burning_oil and big_hippo == 1:
+ extra_burning_oil -= 1
+ break
+ big_hippo -= 1
+ continue
+ except:
+ raise
+ if count > 2 or big_hippo <> 1:
+ self.fail("continue then break in try/except in loop broken!")
+ test_inner()
+
+ def testReturn(self):
+ # 'return' [testlist]
+ def g1(): return
+ def g2(): return 1
+ g1()
+ x = g2()
+ check_syntax_error(self, "class foo:return 1")
+
+ def testYield(self):
+ check_syntax_error(self, "class foo:yield 1")
+
+ def testRaise(self):
+ # 'raise' test [',' test]
+ try: raise RuntimeError, 'just testing'
+ except RuntimeError: pass
+ try: raise KeyboardInterrupt
+ except KeyboardInterrupt: pass
+
+ def testImport(self):
+ # 'import' dotted_as_names
+ import sys
+ import time, sys
+ # 'from' dotted_name 'import' ('*' | '(' import_as_names ')' | import_as_names)
+ from time import time
+ from time import (time)
+ # not testable inside a function, but already done at top of the module
+ # from sys import *
+ from sys import path, argv
+ from sys import (path, argv)
+ from sys import (path, argv,)
+
+ def testGlobal(self):
+ # 'global' NAME (',' NAME)*
+ global a
+ global a, b
+ global one, two, three, four, five, six, seven, eight, nine, ten
+
+ def testExec(self):
+ # 'exec' expr ['in' expr [',' expr]]
+ z = None
+ del z
+ exec 'z=1+1\n'
+ if z != 2: self.fail('exec \'z=1+1\'\\n')
+ del z
+ 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 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\'')"""
+ g = {}
+ 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' 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))
+
+ def testAssert(self):
+ # assert_stmt: 'assert' test [',' test]
+ assert 1
+ assert 1, 1
+ assert lambda x:x
+ assert 1, lambda x:x+1
+ try:
+ assert 0, "msg"
+ except AssertionError, e:
+ self.assertEquals(e.args[0], "msg")
+ else:
+ if __debug__:
+ self.fail("AssertionError not raised by assert 0")
+
+ ### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
+ # Tested below
+
+ def testIf(self):
+ # 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
+ if 1: pass
+ if 1: pass
+ else: pass
+ if 0: pass
+ elif 0: pass
+ if 0: pass
+ elif 0: pass
+ elif 0: pass
+ elif 0: pass
+ else: pass
+
+ def testWhile(self):
+ # 'while' test ':' suite ['else' ':' suite]
+ while 0: pass
+ while 0: pass
+ else: pass
+
+ def testFor(self):
+ # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
+ for i in 1, 2, 3: pass
+ for i, j, k in (): pass
+ else: pass
+ class Squares:
+ def __init__(self, max):
+ self.max = max
+ self.sofar = []
+ def __len__(self): return len(self.sofar)
+ def __getitem__(self, i):
+ if not 0 <= i < self.max: raise IndexError
+ n = len(self.sofar)
+ while n <= i:
+ self.sofar.append(n*n)
+ n = n+1
+ return self.sofar[i]
+ n = 0
+ for x in Squares(10): n = n+x
+ if n != 285:
+ self.fail('for over growing sequence')
+
+ result = []
+ for x, in [(1,), (2,), (3,)]:
+ result.append(x)
+ self.assertEqual(result, [1, 2, 3])
+
+ def testTry(self):
+ ### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
+ ### | 'try' ':' suite 'finally' ':' suite
+ ### except_clause: 'except' [expr [',' expr]]
+ try:
+ 1/0
+ except ZeroDivisionError:
+ pass
+ else:
+ pass
+ try: 1/0
+ except EOFError: pass
+ except TypeError, msg: pass
+ except RuntimeError, msg: pass
+ except: pass
+ else: pass
+ try: 1/0
+ except (EOFError, TypeError, ZeroDivisionError): pass
+ try: 1/0
+ except (EOFError, TypeError, ZeroDivisionError), msg: pass
+ try: pass
+ finally: pass
+
+ def testSuite(self):
+ # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
+ if 1: pass
+ if 1:
+ pass
+ if 1:
+ #
+ #
+ #
+ pass
+ pass
+ #
+ pass
+ #
+
+ def testTest(self):
+ ### and_test ('or' and_test)*
+ ### and_test: not_test ('and' not_test)*
+ ### not_test: 'not' not_test | comparison
+ if not 1: pass
+ if 1 and 1: pass
+ if 1 or 1: pass
+ if not not not 1: pass
+ if not 1 and 1 and 1: pass
+ if 1 and 1 or 1 and 1 and 1 or not 1 and 1: pass
+
+ def testComparison(self):
+ ### comparison: expr (comp_op expr)*
+ ### comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+ if 1: pass
+ 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
+ if 1 is 1: pass
+ 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
+
+ def testBinaryMaskOps(self):
+ x = 1 & 1
+ x = 1 ^ 1
+ x = 1 | 1
+
+ def testShiftOps(self):
+ x = 1 << 1
+ x = 1 >> 1
+ x = 1 << 1 >> 1
+
+ def testAdditiveOps(self):
+ x = 1
+ x = 1 + 1
+ x = 1 - 1 - 1
+ x = 1 - 1 + 1 - 1 + 1
+
+ def testMultiplicativeOps(self):
+ x = 1 * 1
+ x = 1 / 1
+ x = 1 % 1
+ x = 1 / 1 * 1 % 1
+
+ def testUnaryOps(self):
+ x = +1
+ x = -1
+ x = ~1
+ x = ~1 ^ 1 & 1 | 1 & 1 ^ -1
+ x = -1*1/1 + 1*1 - ---1*1
+
+ def testSelectors(self):
+ ### trailer: '(' [testlist] ')' | '[' subscript ']' | '.' NAME
+ ### subscript: expr | [expr] ':' [expr]
+
+ import sys, time
+ c = sys.path[0]
+ x = time.time()
+ x = sys.modules['time'].time()
+ a = '01234'
+ c = a[0]
+ c = a[-1]
+ s = a[0:5]
+ s = a[:5]
+ s = a[0:]
+ s = a[:]
+ s = a[-5:]
+ s = a[:-1]
+ s = a[-4:-3]
+ # A rough test of SF bug 1333982. http://python.org/sf/1333982
+ # The testing here is fairly incomplete.
+ # Test cases should include: commas with 1 and 2 colons
+ d = {}
+ d[1] = 1
+ d[1,] = 2
+ d[1,2] = 3
+ d[1,2,3] = 4
+ L = list(d)
+ L.sort()
+ self.assertEquals(str(L), '[1, (1,), (1, 2), (1, 2, 3)]')
+
+ def testAtoms(self):
+ ### atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' [dictmaker] '}' | '`' testlist '`' | NAME | NUMBER | STRING
+ ### dictmaker: test ':' test (',' test ':' test)* [',']
+
+ x = (1)
+ x = (1 or 2 or 3)
+ x = (1 or 2 or 3, 2, 3)
+
+ x = []
+ x = [1]
+ x = [1 or 2 or 3]
+ x = [1 or 2 or 3, 2, 3]
+ x = []
+
+ x = {}
+ x = {'one': 1}
+ x = {'one': 1,}
+ x = {'one' or 'two': 1 or 2}
+ x = {'one': 1, 'two': 2}
+ x = {'one': 1, 'two': 2,}
+ x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6}
+
+ x = `x`
+ x = `1 or 2 or 3`
+ self.assertEqual(`1,2`, '(1, 2)')
+
+ x = x
+ x = 'x'
+ x = 123
+
+ ### exprlist: expr (',' expr)* [',']
+ ### testlist: test (',' test)* [',']
+ # These have been exercised enough above
+
+ def testClassdef(self):
+ # 'class' NAME ['(' [testlist] ')'] ':' suite
+ class B: pass
+ class B2(): pass
+ class C1(B): pass
+ class C2(B): pass
+ class D(C1, C2, B): pass
+ class C:
+ def meth1(self): pass
+ def meth2(self, arg): pass
+ def meth3(self, a1, a2): pass
+
+ def testListcomps(self):
+ # list comprehension tests
+ nums = [1, 2, 3, 4, 5]
+ strs = ["Apple", "Banana", "Coconut"]
+ spcs = [" Apple", " Banana ", "Coco nut "]
+
+ self.assertEqual([s.strip() for s in spcs], ['Apple', 'Banana', 'Coco nut'])
+ self.assertEqual([3 * x for x in nums], [3, 6, 9, 12, 15])
+ self.assertEqual([x for x in nums if x > 2], [3, 4, 5])
+ self.assertEqual([(i, s) for i in nums for s in strs],
+ [(1, 'Apple'), (1, 'Banana'), (1, 'Coconut'),
+ (2, 'Apple'), (2, 'Banana'), (2, 'Coconut'),
+ (3, 'Apple'), (3, 'Banana'), (3, 'Coconut'),
+ (4, 'Apple'), (4, 'Banana'), (4, 'Coconut'),
+ (5, 'Apple'), (5, 'Banana'), (5, 'Coconut')])
+ self.assertEqual([(i, s) for i in nums for s in [f for f in strs if "n" in f]],
+ [(1, 'Banana'), (1, 'Coconut'), (2, 'Banana'), (2, 'Coconut'),
+ (3, 'Banana'), (3, 'Coconut'), (4, 'Banana'), (4, 'Coconut'),
+ (5, 'Banana'), (5, 'Coconut')])
+ self.assertEqual([(lambda a:[a**i for i in range(a+1)])(j) for j in range(5)],
+ [[1], [1, 1], [1, 2, 4], [1, 3, 9, 27], [1, 4, 16, 64, 256]])
+
+ def test_in_func(l):
+ return [None < x < 3 for x in l if x > 2]
+
+ self.assertEqual(test_in_func(nums), [False, False, False])
+
+ def test_nested_front():
+ self.assertEqual([[y for y in [x, x + 1]] for x in [1,3,5]],
+ [[1, 2], [3, 4], [5, 6]])
+
+ test_nested_front()
+
+ check_syntax_error(self, "[i, s for i in nums for s in strs]")
+ check_syntax_error(self, "[x if y]")
+
+ suppliers = [
+ (1, "Boeing"),
+ (2, "Ford"),
+ (3, "Macdonalds")
+ ]
+
+ parts = [
+ (10, "Airliner"),
+ (20, "Engine"),
+ (30, "Cheeseburger")
+ ]
+
+ suppart = [
+ (1, 10), (1, 20), (2, 20), (3, 30)
+ ]
+
+ x = [
+ (sname, pname)
+ for (sno, sname) in suppliers
+ for (pno, pname) in parts
+ for (sp_sno, sp_pno) in suppart
+ if sno == sp_sno and pno == sp_pno
+ ]
+
+ self.assertEqual(x, [('Boeing', 'Airliner'), ('Boeing', 'Engine'), ('Ford', 'Engine'),
+ ('Macdonalds', 'Cheeseburger')])
+
+ def testGenexps(self):
+ # generator expression tests
+ g = ([x for x in range(10)] for x in range(1))
+ self.assertEqual(g.next(), [x for x in range(10)])
+ try:
+ g.next()
+ self.fail('should produce StopIteration exception')
+ except StopIteration:
+ pass
+
+ a = 1
+ try:
+ g = (a for d in a)
+ g.next()
+ self.fail('should produce TypeError')
+ except TypeError:
+ pass
+
+ self.assertEqual(list((x, y) for x in 'abcd' for y in 'abcd'), [(x, y) for x in 'abcd' for y in 'abcd'])
+ self.assertEqual(list((x, y) for x in 'ab' for y in 'xy'), [(x, y) for x in 'ab' for y in 'xy'])
+
+ a = [x for x in range(10)]
+ b = (x for x in (y for y in a))
+ self.assertEqual(sum(b), sum([x for x in range(10)]))
+
+ self.assertEqual(sum(x**2 for x in range(10)), sum([x**2 for x in range(10)]))
+ self.assertEqual(sum(x*x for x in range(10) if x%2), sum([x*x for x in range(10) if x%2]))
+ self.assertEqual(sum(x for x in (y for y in range(10))), sum([x for x in range(10)]))
+ self.assertEqual(sum(x for x in (y for y in (z for z in range(10)))), sum([x for x in range(10)]))
+ self.assertEqual(sum(x for x in [y for y in (z for z in range(10))]), sum([x for x in range(10)]))
+ self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True)) if True), sum([x for x in range(10)]))
+ self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True) if False) if True), 0)
+ check_syntax_error(self, "foo(x for x in range(10), 100)")
+ check_syntax_error(self, "foo(100, x for x in range(10))")
+
+ def testComprehensionSpecials(self):
+ # test for outmost iterable precomputation
+ x = 10; g = (i for i in range(x)); x = 5
+ self.assertEqual(len(list(g)), 10)
+
+ # This should hold, since we're only precomputing outmost iterable.
+ x = 10; t = False; g = ((i,j) for i in range(x) if t for j in range(x))
+ x = 5; t = True;
+ self.assertEqual([(i,j) for i in range(10) for j in range(5)], list(g))
+
+ # Grammar allows multiple adjacent 'if's in listcomps and genexps,
+ # even though it's silly. Make sure it works (ifelse broke this.)
+ self.assertEqual([ x for x in range(10) if x % 2 if x % 3 ], [1, 5, 7])
+ self.assertEqual(list(x for x in range(10) if x % 2 if x % 3), [1, 5, 7])
+
+ # verify unpacking single element tuples in listcomp/genexp.
+ self.assertEqual([x for x, in [(4,), (5,), (6,)]], [4, 5, 6])
+ self.assertEqual(list(x for x, in [(7,), (8,), (9,)]), [7, 8, 9])
+
+ def testIfElseExpr(self):
+ # Test ifelse expressions in various cases
+ def _checkeval(msg, ret):
+ "helper to check that evaluation of expressions is done correctly"
+ print x
+ return ret
+
+ self.assertEqual([ x() for x in lambda: True, lambda: False if x() ], [True])
+ self.assertEqual([ x() for x in (lambda: True, lambda: False) if x() ], [True])
+ self.assertEqual([ x(False) for x in (lambda x: False if x else True, lambda x: True if x else False) if x(False) ], [True])
+ self.assertEqual((5 if 1 else _checkeval("check 1", 0)), 5)
+ self.assertEqual((_checkeval("check 2", 0) if 0 else 5), 5)
+ self.assertEqual((5 and 6 if 0 else 1), 1)
+ self.assertEqual(((5 and 6) if 0 else 1), 1)
+ self.assertEqual((5 and (6 if 1 else 1)), 6)
+ self.assertEqual((0 or _checkeval("check 3", 2) if 0 else 3), 3)
+ self.assertEqual((1 or _checkeval("check 4", 2) if 1 else _checkeval("check 5", 3)), 1)
+ self.assertEqual((0 or 5 if 1 else _checkeval("check 6", 3)), 5)
+ self.assertEqual((not 5 if 1 else 1), False)
+ self.assertEqual((not 5 if 0 else 1), 1)
+ self.assertEqual((6 + 1 if 1 else 2), 7)
+ self.assertEqual((6 - 1 if 1 else 2), 5)
+ self.assertEqual((6 * 2 if 1 else 4), 12)
+ self.assertEqual((6 / 2 if 1 else 3), 3)
+ self.assertEqual((6 < 4 if 0 else 2), 2)
+
+
+def test_main():
+ run_unittest(TokenTests, GrammarTests)
+
+if __name__ == '__main__':
+ test_main()
diff --git a/Lib/lib2to3/tests/data/py3_test_grammar.py b/Lib/lib2to3/tests/data/py3_test_grammar.py
new file mode 100644
index 0000000..bfc77fe
--- /dev/null
+++ b/Lib/lib2to3/tests/data/py3_test_grammar.py
@@ -0,0 +1,863 @@
+# Python test set -- part 1, grammar.
+# This just tests whether the parser accepts them all.
+
+# NOTE: When you run this test as a script from the command line, you
+# get warnings about certain hex/oct constants. Since those are
+# issued by the parser, you can't suppress them by adding a
+# filterwarnings() call to this module. Therefore, to shut up the
+# regression test, the filterwarnings() call has been added to
+# regrtest.py.
+
+from test.test_support import run_unittest, check_syntax_error
+import unittest
+import sys
+# testing import *
+from sys import *
+
+class TokenTests(unittest.TestCase):
+
+ def testBackslash(self):
+ # Backslash means line continuation:
+ x = 1 \
+ + 1
+ self.assertEquals(x, 2, 'backslash for line continuation')
+
+ # Backslash does not means continuation in comments :\
+ x = 0
+ self.assertEquals(x, 0, 'backslash ending comment')
+
+ def testPlainIntegers(self):
+ self.assertEquals(type(000), type(0))
+ self.assertEquals(0xff, 255)
+ self.assertEquals(0o377, 255)
+ self.assertEquals(2147483647, 0o17777777777)
+ self.assertEquals(0b1001, 9)
+ from sys import maxint
+ if maxint == 2147483647:
+ self.assertEquals(-2147483647-1, -0o20000000000)
+ # XXX -2147483648
+ self.assert_(0o37777777777 > 0)
+ self.assert_(0xffffffff > 0)
+ self.assert_(0b1111111111111111111111111111111 > 0)
+ for s in ('2147483648', '0o40000000000', '0x100000000',
+ '0b10000000000000000000000000000000'):
+ try:
+ x = eval(s)
+ except OverflowError:
+ self.fail("OverflowError on huge integer literal %r" % s)
+ elif maxint == 9223372036854775807:
+ self.assertEquals(-9223372036854775807-1, -0o1000000000000000000000)
+ self.assert_(0o1777777777777777777777 > 0)
+ self.assert_(0xffffffffffffffff > 0)
+ self.assert_(0b11111111111111111111111111111111111111111111111111111111111111 > 0)
+ for s in '9223372036854775808', '0o2000000000000000000000', \
+ '0x10000000000000000', \
+ '0b100000000000000000000000000000000000000000000000000000000000000':
+ try:
+ x = eval(s)
+ except OverflowError:
+ self.fail("OverflowError on huge integer literal %r" % s)
+ else:
+ self.fail('Weird maxint value %r' % maxint)
+
+ def testLongIntegers(self):
+ x = 0
+ x = 0xffffffffffffffff
+ x = 0Xffffffffffffffff
+ x = 0o77777777777777777
+ x = 0O77777777777777777
+ x = 123456789012345678901234567890
+ x = 0b100000000000000000000000000000000000000000000000000000000000000000000
+ x = 0B111111111111111111111111111111111111111111111111111111111111111111111
+
+ def testFloats(self):
+ x = 3.14
+ x = 314.
+ x = 0.314
+ # XXX x = 000.314
+ x = .314
+ x = 3e14
+ x = 3E14
+ x = 3e-14
+ x = 3e+14
+ x = 3.e14
+ x = .3e14
+ x = 3.1e4
+
+ def testStringLiterals(self):
+ x = ''; y = ""; self.assert_(len(x) == 0 and x == y)
+ x = '\''; y = "'"; self.assert_(len(x) == 1 and x == y and ord(x) == 39)
+ x = '"'; y = "\""; self.assert_(len(x) == 1 and x == y and ord(x) == 34)
+ x = "doesn't \"shrink\" does it"
+ y = 'doesn\'t "shrink" does it'
+ self.assert_(len(x) == 24 and x == y)
+ x = "does \"shrink\" doesn't it"
+ y = 'does "shrink" doesn\'t it'
+ self.assert_(len(x) == 24 and x == y)
+ x = """
+The "quick"
+brown fox
+jumps over
+the 'lazy' dog.
+"""
+ y = '\nThe "quick"\nbrown fox\njumps over\nthe \'lazy\' dog.\n'
+ self.assertEquals(x, y)
+ y = '''
+The "quick"
+brown fox
+jumps over
+the 'lazy' dog.
+'''
+ self.assertEquals(x, y)
+ y = "\n\
+The \"quick\"\n\
+brown fox\n\
+jumps over\n\
+the 'lazy' dog.\n\
+"
+ self.assertEquals(x, y)
+ y = '\n\
+The \"quick\"\n\
+brown fox\n\
+jumps over\n\
+the \'lazy\' dog.\n\
+'
+ self.assertEquals(x, y)
+
+ def testEllipsis(self):
+ x = ...
+ self.assert_(x is Ellipsis)
+ self.assertRaises(SyntaxError, eval, ".. .")
+
+class GrammarTests(unittest.TestCase):
+
+ # single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE
+ # XXX can't test in a script -- this rule is only used when interactive
+
+ # file_input: (NEWLINE | stmt)* ENDMARKER
+ # Being tested as this very moment this very module
+
+ # expr_input: testlist NEWLINE
+ # XXX Hard to test -- used only in calls to input()
+
+ def testEvalInput(self):
+ # testlist ENDMARKER
+ x = eval('1, 0 or 1')
+
+ def testFuncdef(self):
+ ### [decorators] 'def' NAME parameters ['->' test] ':' suite
+ ### decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
+ ### decorators: decorator+
+ ### parameters: '(' [typedargslist] ')'
+ ### typedargslist: ((tfpdef ['=' test] ',')*
+ ### ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef)
+ ### | tfpdef ['=' test] (',' tfpdef ['=' test])* [','])
+ ### tfpdef: NAME [':' test]
+ ### varargslist: ((vfpdef ['=' test] ',')*
+ ### ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef)
+ ### | vfpdef ['=' test] (',' vfpdef ['=' test])* [','])
+ ### vfpdef: NAME
+ def f1(): pass
+ f1()
+ f1(*())
+ f1(*(), **{})
+ def f2(one_argument): pass
+ def f3(two, arguments): pass
+ self.assertEquals(f2.__code__.co_varnames, ('one_argument',))
+ self.assertEquals(f3.__code__.co_varnames, ('two', 'arguments'))
+ 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
+
+ f1()
+ f2(1)
+ f2(1,)
+ f3(1, 2)
+ f3(1, 2,)
+ v0()
+ v0(1)
+ v0(1,)
+ v0(1,2)
+ v0(1,2,3,4,5,6,7,8,9,0)
+ v1(1)
+ v1(1,)
+ v1(1,2)
+ v1(1,2,3)
+ v1(1,2,3,4,5,6,7,8,9,0)
+ v2(1,2)
+ v2(1,2,3)
+ v2(1,2,3,4)
+ v2(1,2,3,4,5,6,7,8,9,0)
+
+ def d01(a=1): pass
+ d01()
+ d01(1)
+ d01(*(1,))
+ d01(**{'a':2})
+ def d11(a, b=1): pass
+ d11(1)
+ d11(1, 2)
+ d11(1, **{'b':2})
+ def d21(a, b, c=1): pass
+ d21(1, 2)
+ d21(1, 2, 3)
+ d21(*(1, 2, 3))
+ d21(1, *(2, 3))
+ d21(1, 2, *(3,))
+ d21(1, 2, **{'c':3})
+ def d02(a=1, b=2): pass
+ d02()
+ d02(1)
+ d02(1, 2)
+ d02(*(1, 2))
+ d02(1, *(2,))
+ d02(1, **{'b':2})
+ d02(**{'a': 1, 'b': 2})
+ def d12(a, b=1, c=2): pass
+ d12(1)
+ d12(1, 2)
+ d12(1, 2, 3)
+ def d22(a, b, c=1, d=2): pass
+ d22(1, 2)
+ d22(1, 2, 3)
+ d22(1, 2, 3, 4)
+ def d01v(a=1, *rest): pass
+ d01v()
+ d01v(1)
+ d01v(1, 2)
+ d01v(*(1, 2, 3, 4))
+ d01v(*(1,))
+ d01v(**{'a':2})
+ def d11v(a, b=1, *rest): pass
+ d11v(1)
+ d11v(1, 2)
+ d11v(1, 2, 3)
+ def d21v(a, b, c=1, *rest): pass
+ d21v(1, 2)
+ d21v(1, 2, 3)
+ d21v(1, 2, 3, 4)
+ d21v(*(1, 2, 3, 4))
+ d21v(1, 2, **{'c': 3})
+ def d02v(a=1, b=2, *rest): pass
+ d02v()
+ d02v(1)
+ d02v(1, 2)
+ d02v(1, 2, 3)
+ d02v(1, *(2, 3, 4))
+ d02v(**{'a': 1, 'b': 2})
+ def d12v(a, b=1, c=2, *rest): pass
+ d12v(1)
+ d12v(1, 2)
+ d12v(1, 2, 3)
+ d12v(1, 2, 3, 4)
+ d12v(*(1, 2, 3, 4))
+ d12v(1, 2, *(3, 4, 5))
+ d12v(1, *(2,), **{'c': 3})
+ def d22v(a, b, c=1, d=2, *rest): pass
+ d22v(1, 2)
+ d22v(1, 2, 3)
+ d22v(1, 2, 3, 4)
+ d22v(1, 2, 3, 4, 5)
+ d22v(*(1, 2, 3, 4))
+ d22v(1, 2, *(3, 4, 5))
+ d22v(1, *(2, 3), **{'d': 4})
+ # keyword only argument tests
+ def pos0key1(*, key): return key
+ pos0key1(key=100)
+ def pos2key2(p1, p2, *, k1, k2=100): return p1,p2,k1,k2
+ pos2key2(1, 2, k1=100)
+ pos2key2(1, 2, k1=100, k2=200)
+ pos2key2(1, 2, k2=100, k1=200)
+ def pos2key2dict(p1, p2, *, k1=100, k2, **kwarg): return p1,p2,k1,k2,kwarg
+ pos2key2dict(1,2,k2=100,tokwarg1=100,tokwarg2=200)
+ pos2key2dict(1,2,tokwarg1=100,tokwarg2=200, k2=100)
+
+ # argument annotation tests
+ def f(x) -> list: pass
+ self.assertEquals(f.__annotations__, {'return': list})
+ def f(x:int): pass
+ self.assertEquals(f.__annotations__, {'x': int})
+ def f(*x:str): pass
+ self.assertEquals(f.__annotations__, {'x': str})
+ def f(**x:float): pass
+ self.assertEquals(f.__annotations__, {'x': float})
+ def f(x, y:1+2): pass
+ self.assertEquals(f.__annotations__, {'y': 3})
+ def f(a, b:1, c:2, d): pass
+ self.assertEquals(f.__annotations__, {'b': 1, 'c': 2})
+ def f(a, b:1, c:2, d, e:3=4, f=5, *g:6): pass
+ self.assertEquals(f.__annotations__,
+ {'b': 1, 'c': 2, 'e': 3, 'g': 6})
+ def f(a, b:1, c:2, d, e:3=4, f=5, *g:6, h:7, i=8, j:9=10,
+ **k:11) -> 12: pass
+ self.assertEquals(f.__annotations__,
+ {'b': 1, 'c': 2, 'e': 3, 'g': 6, 'h': 7, 'j': 9,
+ 'k': 11, 'return': 12})
+ # Check for SF Bug #1697248 - mixing decorators and a return annotation
+ def null(x): return x
+ @null
+ def f(x) -> list: pass
+ self.assertEquals(f.__annotations__, {'return': list})
+
+ # test MAKE_CLOSURE with a variety of oparg's
+ closure = 1
+ def f(): return closure
+ def f(x=1): return closure
+ def f(*, k=1): return closure
+ def f() -> int: return closure
+
+ 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]]
+ self.assertEquals(l3(), [0, 1, 0])
+ l4 = lambda x = lambda y = lambda z=1 : z : y() : x()
+ self.assertEquals(l4(), 1)
+ l5 = lambda x, y, z=2: x + y + z
+ self.assertEquals(l5(1, 2), 5)
+ self.assertEquals(l5(1, 2, 3), 6)
+ check_syntax_error(self, "lambda x: x = 2")
+ 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)
+
+
+ ### stmt: simple_stmt | compound_stmt
+ # Tested below
+
+ def testSimpleStmt(self):
+ ### simple_stmt: small_stmt (';' small_stmt)* [';']
+ x = 1; pass; del x
+ def foo():
+ # verify statments that end with semi-colons
+ x = 1; pass; del x;
+ foo()
+
+ ### small_stmt: expr_stmt | pass_stmt | del_stmt | flow_stmt | import_stmt | global_stmt | access_stmt
+ # Tested below
+
+ def testExprStmt(self):
+ # (exprlist '=')* exprlist
+ 1
+ 1, 2, 3
+ x = 1
+ x = 1, 2, 3
+ x = y = z = 1, 2, 3
+ x, y, z = 1, 2, 3
+ abc = a, b, c = x, y, z = xyz = 1, 2, (3, 4)
+
+ check_syntax_error(self, "x + 1 = 1")
+ check_syntax_error(self, "a + 1 = b + 2")
+
+ def testDelStmt(self):
+ # 'del' exprlist
+ abc = [1,2,3]
+ x, y, z = abc
+ xyz = x, y, z
+
+ del abc
+ del x, y, (z, xyz)
+
+ def testPassStmt(self):
+ # 'pass'
+ pass
+
+ # flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt
+ # Tested below
+
+ def testBreakStmt(self):
+ # 'break'
+ while 1: break
+
+ def testContinueStmt(self):
+ # 'continue'
+ i = 1
+ while i: i = 0; continue
+
+ msg = ""
+ while not msg:
+ msg = "ok"
+ try:
+ continue
+ msg = "continue failed to continue inside try"
+ except:
+ msg = "continue inside try called except block"
+ if msg != "ok":
+ self.fail(msg)
+
+ msg = ""
+ while not msg:
+ msg = "finally block not called"
+ try:
+ continue
+ finally:
+ msg = "ok"
+ if msg != "ok":
+ self.fail(msg)
+
+ def test_break_continue_loop(self):
+ # This test warrants an explanation. It is a test specifically for SF bugs
+ # #463359 and #462937. The bug is that a 'break' statement executed or
+ # exception raised inside a try/except inside a loop, *after* a continue
+ # statement has been executed in that loop, will cause the wrong number of
+ # arguments to be popped off the stack and the instruction pointer reset to
+ # a very small number (usually 0.) Because of this, the following test
+ # *must* written as a function, and the tracking vars *must* be function
+ # arguments with default values. Otherwise, the test will loop and loop.
+
+ def test_inner(extra_burning_oil = 1, count=0):
+ big_hippo = 2
+ while big_hippo:
+ count += 1
+ try:
+ if extra_burning_oil and big_hippo == 1:
+ extra_burning_oil -= 1
+ break
+ big_hippo -= 1
+ continue
+ except:
+ raise
+ if count > 2 or big_hippo != 1:
+ self.fail("continue then break in try/except in loop broken!")
+ test_inner()
+
+ def testReturn(self):
+ # 'return' [testlist]
+ def g1(): return
+ def g2(): return 1
+ g1()
+ x = g2()
+ check_syntax_error(self, "class foo:return 1")
+
+ def testYield(self):
+ check_syntax_error(self, "class foo:yield 1")
+
+ def testRaise(self):
+ # 'raise' test [',' test]
+ try: raise RuntimeError, 'just testing'
+ except RuntimeError: pass
+ try: raise KeyboardInterrupt
+ except KeyboardInterrupt: pass
+
+ def testImport(self):
+ # 'import' dotted_as_names
+ import sys
+ import time, sys
+ # 'from' dotted_name 'import' ('*' | '(' import_as_names ')' | import_as_names)
+ from time import time
+ from time import (time)
+ # not testable inside a function, but already done at top of the module
+ # from sys import *
+ from sys import path, argv
+ from sys import (path, argv)
+ from sys import (path, argv,)
+
+ def testGlobal(self):
+ # 'global' NAME (',' NAME)*
+ global a
+ global a, b
+ global one, two, three, four, five, six, seven, eight, nine, ten
+
+ def testAssert(self):
+ # assert_stmt: 'assert' test [',' test]
+ assert 1
+ assert 1, 1
+ assert lambda x:x
+ assert 1, lambda x:x+1
+ try:
+ assert 0, "msg"
+ except AssertionError as e:
+ self.assertEquals(e.args[0], "msg")
+ else:
+ if __debug__:
+ self.fail("AssertionError not raised by assert 0")
+
+ ### compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | funcdef | classdef
+ # Tested below
+
+ def testIf(self):
+ # 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
+ if 1: pass
+ if 1: pass
+ else: pass
+ if 0: pass
+ elif 0: pass
+ if 0: pass
+ elif 0: pass
+ elif 0: pass
+ elif 0: pass
+ else: pass
+
+ def testWhile(self):
+ # 'while' test ':' suite ['else' ':' suite]
+ while 0: pass
+ while 0: pass
+ else: pass
+
+ def testFor(self):
+ # 'for' exprlist 'in' exprlist ':' suite ['else' ':' suite]
+ for i in 1, 2, 3: pass
+ for i, j, k in (): pass
+ else: pass
+ class Squares:
+ def __init__(self, max):
+ self.max = max
+ self.sofar = []
+ def __len__(self): return len(self.sofar)
+ def __getitem__(self, i):
+ if not 0 <= i < self.max: raise IndexError
+ n = len(self.sofar)
+ while n <= i:
+ self.sofar.append(n*n)
+ n = n+1
+ return self.sofar[i]
+ n = 0
+ for x in Squares(10): n = n+x
+ if n != 285:
+ self.fail('for over growing sequence')
+
+ result = []
+ for x, in [(1,), (2,), (3,)]:
+ result.append(x)
+ self.assertEqual(result, [1, 2, 3])
+
+ def testTry(self):
+ ### try_stmt: 'try' ':' suite (except_clause ':' suite)+ ['else' ':' suite]
+ ### | 'try' ':' suite 'finally' ':' suite
+ ### except_clause: 'except' [expr ['as' expr]]
+ try:
+ 1/0
+ except ZeroDivisionError:
+ pass
+ else:
+ pass
+ try: 1/0
+ except EOFError: pass
+ except TypeError as msg: pass
+ except RuntimeError as msg: pass
+ except: pass
+ else: pass
+ try: 1/0
+ except (EOFError, TypeError, ZeroDivisionError): pass
+ try: 1/0
+ except (EOFError, TypeError, ZeroDivisionError) as msg: pass
+ try: pass
+ finally: pass
+
+ def testSuite(self):
+ # simple_stmt | NEWLINE INDENT NEWLINE* (stmt NEWLINE*)+ DEDENT
+ if 1: pass
+ if 1:
+ pass
+ if 1:
+ #
+ #
+ #
+ pass
+ pass
+ #
+ pass
+ #
+
+ def testTest(self):
+ ### and_test ('or' and_test)*
+ ### and_test: not_test ('and' not_test)*
+ ### not_test: 'not' not_test | comparison
+ if not 1: pass
+ if 1 and 1: pass
+ if 1 or 1: pass
+ if not not not 1: pass
+ if not 1 and 1 and 1: pass
+ if 1 and 1 or 1 and 1 and 1 or not 1 and 1: pass
+
+ def testComparison(self):
+ ### comparison: expr (comp_op expr)*
+ ### comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'in'|'not' 'in'|'is'|'is' 'not'
+ if 1: pass
+ 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 is 1: pass
+ if 1 is not 1: pass
+ if 1 in (): pass
+ if 1 not in (): pass
+ if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in 1 is 1 is not 1: pass
+
+ def testBinaryMaskOps(self):
+ x = 1 & 1
+ x = 1 ^ 1
+ x = 1 | 1
+
+ def testShiftOps(self):
+ x = 1 << 1
+ x = 1 >> 1
+ x = 1 << 1 >> 1
+
+ def testAdditiveOps(self):
+ x = 1
+ x = 1 + 1
+ x = 1 - 1 - 1
+ x = 1 - 1 + 1 - 1 + 1
+
+ def testMultiplicativeOps(self):
+ x = 1 * 1
+ x = 1 / 1
+ x = 1 % 1
+ x = 1 / 1 * 1 % 1
+
+ def testUnaryOps(self):
+ x = +1
+ x = -1
+ x = ~1
+ x = ~1 ^ 1 & 1 | 1 & 1 ^ -1
+ x = -1*1/1 + 1*1 - ---1*1
+
+ def testSelectors(self):
+ ### trailer: '(' [testlist] ')' | '[' subscript ']' | '.' NAME
+ ### subscript: expr | [expr] ':' [expr]
+
+ import sys, time
+ c = sys.path[0]
+ x = time.time()
+ x = sys.modules['time'].time()
+ a = '01234'
+ c = a[0]
+ c = a[-1]
+ s = a[0:5]
+ s = a[:5]
+ s = a[0:]
+ s = a[:]
+ s = a[-5:]
+ s = a[:-1]
+ s = a[-4:-3]
+ # A rough test of SF bug 1333982. http://python.org/sf/1333982
+ # The testing here is fairly incomplete.
+ # Test cases should include: commas with 1 and 2 colons
+ d = {}
+ d[1] = 1
+ d[1,] = 2
+ d[1,2] = 3
+ d[1,2,3] = 4
+ L = list(d)
+ L.sort(key=lambda x: x if isinstance(x, tuple) else ())
+ self.assertEquals(str(L), '[1, (1,), (1, 2), (1, 2, 3)]')
+
+ def testAtoms(self):
+ ### atom: '(' [testlist] ')' | '[' [testlist] ']' | '{' [dictsetmaker] '}' | NAME | NUMBER | STRING
+ ### dictsetmaker: (test ':' test (',' test ':' test)* [',']) | (test (',' test)* [','])
+
+ x = (1)
+ x = (1 or 2 or 3)
+ x = (1 or 2 or 3, 2, 3)
+
+ x = []
+ x = [1]
+ x = [1 or 2 or 3]
+ x = [1 or 2 or 3, 2, 3]
+ x = []
+
+ x = {}
+ x = {'one': 1}
+ x = {'one': 1,}
+ x = {'one' or 'two': 1 or 2}
+ x = {'one': 1, 'two': 2}
+ x = {'one': 1, 'two': 2,}
+ x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6}
+
+ x = {'one'}
+ x = {'one', 1,}
+ x = {'one', 'two', 'three'}
+ x = {2, 3, 4,}
+
+ x = x
+ x = 'x'
+ x = 123
+
+ ### exprlist: expr (',' expr)* [',']
+ ### testlist: test (',' test)* [',']
+ # These have been exercised enough above
+
+ def testClassdef(self):
+ # 'class' NAME ['(' [testlist] ')'] ':' suite
+ class B: pass
+ class B2(): pass
+ class C1(B): pass
+ class C2(B): pass
+ class D(C1, C2, B): pass
+ class C:
+ 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): return x
+ @class_decorator
+ class G: pass
+
+ def testDictcomps(self):
+ # dictorsetmaker: ( (test ':' test (comp_for |
+ # (',' test ':' test)* [','])) |
+ # (test (comp_for | (',' test)* [','])) )
+ nums = [1, 2, 3]
+ self.assertEqual({i:i+1 for i in nums}, {1: 2, 2: 3, 3: 4})
+
+ def testListcomps(self):
+ # list comprehension tests
+ nums = [1, 2, 3, 4, 5]
+ strs = ["Apple", "Banana", "Coconut"]
+ spcs = [" Apple", " Banana ", "Coco nut "]
+
+ self.assertEqual([s.strip() for s in spcs], ['Apple', 'Banana', 'Coco nut'])
+ self.assertEqual([3 * x for x in nums], [3, 6, 9, 12, 15])
+ self.assertEqual([x for x in nums if x > 2], [3, 4, 5])
+ self.assertEqual([(i, s) for i in nums for s in strs],
+ [(1, 'Apple'), (1, 'Banana'), (1, 'Coconut'),
+ (2, 'Apple'), (2, 'Banana'), (2, 'Coconut'),
+ (3, 'Apple'), (3, 'Banana'), (3, 'Coconut'),
+ (4, 'Apple'), (4, 'Banana'), (4, 'Coconut'),
+ (5, 'Apple'), (5, 'Banana'), (5, 'Coconut')])
+ self.assertEqual([(i, s) for i in nums for s in [f for f in strs if "n" in f]],
+ [(1, 'Banana'), (1, 'Coconut'), (2, 'Banana'), (2, 'Coconut'),
+ (3, 'Banana'), (3, 'Coconut'), (4, 'Banana'), (4, 'Coconut'),
+ (5, 'Banana'), (5, 'Coconut')])
+ self.assertEqual([(lambda a:[a**i for i in range(a+1)])(j) for j in range(5)],
+ [[1], [1, 1], [1, 2, 4], [1, 3, 9, 27], [1, 4, 16, 64, 256]])
+
+ def test_in_func(l):
+ return [0 < x < 3 for x in l if x > 2]
+
+ self.assertEqual(test_in_func(nums), [False, False, False])
+
+ def test_nested_front():
+ self.assertEqual([[y for y in [x, x + 1]] for x in [1,3,5]],
+ [[1, 2], [3, 4], [5, 6]])
+
+ test_nested_front()
+
+ check_syntax_error(self, "[i, s for i in nums for s in strs]")
+ check_syntax_error(self, "[x if y]")
+
+ suppliers = [
+ (1, "Boeing"),
+ (2, "Ford"),
+ (3, "Macdonalds")
+ ]
+
+ parts = [
+ (10, "Airliner"),
+ (20, "Engine"),
+ (30, "Cheeseburger")
+ ]
+
+ suppart = [
+ (1, 10), (1, 20), (2, 20), (3, 30)
+ ]
+
+ x = [
+ (sname, pname)
+ for (sno, sname) in suppliers
+ for (pno, pname) in parts
+ for (sp_sno, sp_pno) in suppart
+ if sno == sp_sno and pno == sp_pno
+ ]
+
+ self.assertEqual(x, [('Boeing', 'Airliner'), ('Boeing', 'Engine'), ('Ford', 'Engine'),
+ ('Macdonalds', 'Cheeseburger')])
+
+ 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)])
+ try:
+ next(g)
+ self.fail('should produce StopIteration exception')
+ except StopIteration:
+ pass
+
+ a = 1
+ try:
+ g = (a for d in a)
+ next(g)
+ self.fail('should produce TypeError')
+ except TypeError:
+ pass
+
+ self.assertEqual(list((x, y) for x in 'abcd' for y in 'abcd'), [(x, y) for x in 'abcd' for y in 'abcd'])
+ self.assertEqual(list((x, y) for x in 'ab' for y in 'xy'), [(x, y) for x in 'ab' for y in 'xy'])
+
+ a = [x for x in range(10)]
+ b = (x for x in (y for y in a))
+ self.assertEqual(sum(b), sum([x for x in range(10)]))
+
+ self.assertEqual(sum(x**2 for x in range(10)), sum([x**2 for x in range(10)]))
+ self.assertEqual(sum(x*x for x in range(10) if x%2), sum([x*x for x in range(10) if x%2]))
+ self.assertEqual(sum(x for x in (y for y in range(10))), sum([x for x in range(10)]))
+ self.assertEqual(sum(x for x in (y for y in (z for z in range(10)))), sum([x for x in range(10)]))
+ self.assertEqual(sum(x for x in [y for y in (z for z in range(10))]), sum([x for x in range(10)]))
+ self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True)) if True), sum([x for x in range(10)]))
+ self.assertEqual(sum(x for x in (y for y in (z for z in range(10) if True) if False) if True), 0)
+ check_syntax_error(self, "foo(x for x in range(10), 100)")
+ check_syntax_error(self, "foo(100, x for x in range(10))")
+
+ def testComprehensionSpecials(self):
+ # test for outmost iterable precomputation
+ x = 10; g = (i for i in range(x)); x = 5
+ self.assertEqual(len(list(g)), 10)
+
+ # This should hold, since we're only precomputing outmost iterable.
+ x = 10; t = False; g = ((i,j) for i in range(x) if t for j in range(x))
+ x = 5; t = True;
+ self.assertEqual([(i,j) for i in range(10) for j in range(5)], list(g))
+
+ # Grammar allows multiple adjacent 'if's in listcomps and genexps,
+ # even though it's silly. Make sure it works (ifelse broke this.)
+ self.assertEqual([ x for x in range(10) if x % 2 if x % 3 ], [1, 5, 7])
+ self.assertEqual(list(x for x in range(10) if x % 2 if x % 3), [1, 5, 7])
+
+ # verify unpacking single element tuples in listcomp/genexp.
+ self.assertEqual([x for x, in [(4,), (5,), (6,)]], [4, 5, 6])
+ self.assertEqual(list(x for x, in [(7,), (8,), (9,)]), [7, 8, 9])
+
+ def testIfElseExpr(self):
+ # Test ifelse expressions in various cases
+ def _checkeval(msg, ret):
+ "helper to check that evaluation of expressions is done correctly"
+ print(x)
+ return ret
+
+ # the next line is not allowed anymore
+ #self.assertEqual([ x() for x in lambda: True, lambda: False if x() ], [True])
+ self.assertEqual([ x() for x in (lambda: True, lambda: False) if x() ], [True])
+ self.assertEqual([ x(False) for x in (lambda x: False if x else True, lambda x: True if x else False) if x(False) ], [True])
+ self.assertEqual((5 if 1 else _checkeval("check 1", 0)), 5)
+ self.assertEqual((_checkeval("check 2", 0) if 0 else 5), 5)
+ self.assertEqual((5 and 6 if 0 else 1), 1)
+ self.assertEqual(((5 and 6) if 0 else 1), 1)
+ self.assertEqual((5 and (6 if 1 else 1)), 6)
+ self.assertEqual((0 or _checkeval("check 3", 2) if 0 else 3), 3)
+ self.assertEqual((1 or _checkeval("check 4", 2) if 1 else _checkeval("check 5", 3)), 1)
+ self.assertEqual((0 or 5 if 1 else _checkeval("check 6", 3)), 5)
+ self.assertEqual((not 5 if 1 else 1), False)
+ self.assertEqual((not 5 if 0 else 1), 1)
+ self.assertEqual((6 + 1 if 1 else 2), 7)
+ self.assertEqual((6 - 1 if 1 else 2), 5)
+ self.assertEqual((6 * 2 if 1 else 4), 12)
+ self.assertEqual((6 / 2 if 1 else 3), 3)
+ self.assertEqual((6 < 4 if 0 else 2), 2)
+
+
+def test_main():
+ run_unittest(TokenTests, GrammarTests)
+
+if __name__ == '__main__':
+ test_main()
diff --git a/Lib/lib2to3/tests/pytree_idempotency.py b/Lib/lib2to3/tests/pytree_idempotency.py
new file mode 100755
index 0000000..d83f5cc
--- /dev/null
+++ b/Lib/lib2to3/tests/pytree_idempotency.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python2.5
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Main program for testing the infrastructure."""
+
+__author__ = "Guido van Rossum <guido@python.org>"
+
+# Support imports (need to be imported first)
+import support
+
+# Python imports
+import os
+import sys
+import logging
+
+# Local imports
+from .. import pytree
+import pgen2
+from pgen2 import driver
+
+logging.basicConfig()
+
+def main():
+ gr = driver.load_grammar("Grammar.txt")
+ dr = driver.Driver(gr, convert=pytree.convert)
+
+ fn = "example.py"
+ tree = dr.parse_file(fn, debug=True)
+ if not diff(fn, tree):
+ print "No diffs."
+ if not sys.argv[1:]:
+ return # Pass a dummy argument to run the complete test suite below
+
+ problems = []
+
+ # Process every imported module
+ for name in sys.modules:
+ mod = sys.modules[name]
+ if mod is None or not hasattr(mod, "__file__"):
+ continue
+ fn = mod.__file__
+ if fn.endswith(".pyc"):
+ fn = fn[:-1]
+ if not fn.endswith(".py"):
+ continue
+ print >>sys.stderr, "Parsing", fn
+ tree = dr.parse_file(fn, debug=True)
+ if diff(fn, tree):
+ problems.append(fn)
+
+ # Process every single module on sys.path (but not in packages)
+ for dir in sys.path:
+ try:
+ names = os.listdir(dir)
+ except os.error:
+ continue
+ print >>sys.stderr, "Scanning", dir, "..."
+ for name in names:
+ if not name.endswith(".py"):
+ continue
+ print >>sys.stderr, "Parsing", name
+ fn = os.path.join(dir, name)
+ try:
+ tree = dr.parse_file(fn, debug=True)
+ except pgen2.parse.ParseError, err:
+ print "ParseError:", err
+ else:
+ if diff(fn, tree):
+ problems.append(fn)
+
+ # Show summary of problem files
+ if not problems:
+ print "No problems. Congratulations!"
+ else:
+ print "Problems in following files:"
+ for fn in problems:
+ print "***", fn
+
+def diff(fn, tree):
+ f = open("@", "w")
+ try:
+ f.write(str(tree))
+ finally:
+ f.close()
+ try:
+ return os.system("diff -u %s @" % fn)
+ finally:
+ os.remove("@")
+
+if __name__ == "__main__":
+ main()
diff --git a/Lib/lib2to3/tests/support.py b/Lib/lib2to3/tests/support.py
new file mode 100644
index 0000000..7789033
--- /dev/null
+++ b/Lib/lib2to3/tests/support.py
@@ -0,0 +1,47 @@
+"""Support code for test_*.py files"""
+# Author: Collin Winter
+
+# Python imports
+import unittest
+import sys
+import os
+import os.path
+import re
+from textwrap import dedent
+
+#sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
+
+# Local imports
+from .. import pytree
+from ..pgen2 import driver
+
+test_dir = os.path.dirname(__file__)
+proj_dir = os.path.normpath(os.path.join(test_dir, ".."))
+grammar_path = os.path.join(test_dir, "..", "Grammar.txt")
+grammar = driver.load_grammar(grammar_path)
+driver = driver.Driver(grammar, convert=pytree.convert)
+
+def parse_string(string):
+ return driver.parse_string(reformat(string), debug=True)
+
+# Python 2.3's TestSuite is not iter()-able
+if sys.version_info < (2, 4):
+ def TestSuite_iter(self):
+ return iter(self._tests)
+ unittest.TestSuite.__iter__ = TestSuite_iter
+
+def run_all_tests(test_mod=None, tests=None):
+ if tests is None:
+ tests = unittest.TestLoader().loadTestsFromModule(test_mod)
+ unittest.TextTestRunner(verbosity=2).run(tests)
+
+def reformat(string):
+ return dedent(string) + "\n\n"
+
+def all_project_files():
+ for dirpath, dirnames, filenames in os.walk(proj_dir):
+ for filename in filenames:
+ if filename.endswith(".py"):
+ yield os.path.join(dirpath, filename)
+
+TestCase = unittest.TestCase
diff --git a/Lib/lib2to3/tests/test_all_fixers.py b/Lib/lib2to3/tests/test_all_fixers.py
new file mode 100644
index 0000000..e8df320
--- /dev/null
+++ b/Lib/lib2to3/tests/test_all_fixers.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python2.5
+"""Tests that run all fixer modules over an input stream.
+
+This has been broken out into its own test module because of its
+running time.
+"""
+# Author: Collin Winter
+
+# Testing imports
+try:
+ from . import support
+except ImportError:
+ import support
+
+# Python imports
+import unittest
+
+# Local imports
+from .. import pytree
+from .. import refactor
+
+class Options:
+ def __init__(self, **kwargs):
+ for k, v in kwargs.items():
+ setattr(self, k, v)
+ self.verbose = False
+
+class Test_all(support.TestCase):
+ def setUp(self):
+ options = Options(fix=["all", "idioms", "ws_comma"],
+ print_function=False)
+ self.refactor = refactor.RefactoringTool(options)
+
+ def test_all_project_files(self):
+ for filepath in support.all_project_files():
+ print "Fixing %s..." % filepath
+ self.refactor.refactor_string(open(filepath).read(), filepath)
+
+
+if __name__ == "__main__":
+ import __main__
+ support.run_all_tests(__main__)
diff --git a/Lib/lib2to3/tests/test_fixers.py b/Lib/lib2to3/tests/test_fixers.py
new file mode 100755
index 0000000..96c3f59
--- /dev/null
+++ b/Lib/lib2to3/tests/test_fixers.py
@@ -0,0 +1,2918 @@
+#!/usr/bin/env python2.5
+""" Test suite for the fixer modules """
+# Author: Collin Winter
+
+# Testing imports
+try:
+ from tests import support
+except ImportError:
+ import support
+
+# Python imports
+import unittest
+
+# Local imports
+from .. import pygram
+from .. import pytree
+from .. import refactor
+
+class Options:
+ def __init__(self, **kwargs):
+ for k, v in kwargs.items():
+ setattr(self, k, v)
+
+ self.verbose = False
+
+class FixerTestCase(support.TestCase):
+ def setUp(self):
+ options = Options(fix=[self.fixer], print_function=False)
+ self.refactor = refactor.RefactoringTool(options)
+ self.fixer_log = []
+
+ for order in (self.refactor.pre_order, self.refactor.post_order):
+ for fixer in order:
+ fixer.log = self.fixer_log
+
+ def _check(self, before, after):
+ before = support.reformat(before)
+ after = support.reformat(after)
+ tree = self.refactor.refactor_string(before, "<string>")
+ self.failUnlessEqual(after, str(tree))
+ return tree
+
+ def check(self, before, after, ignore_warnings=False):
+ tree = self._check(before, after)
+ self.failUnless(tree.was_changed)
+ if not ignore_warnings:
+ self.failUnlessEqual(self.fixer_log, [])
+
+ def warns(self, before, after, message, unchanged=False):
+ tree = self._check(before, after)
+ self.failUnless(message in "".join(self.fixer_log))
+ if not unchanged:
+ self.failUnless(tree.was_changed)
+
+ def warns_unchanged(self, before, message):
+ self.warns(before, before, message, unchanged=True)
+
+ def unchanged(self, before, ignore_warnings=False):
+ self._check(before, before)
+ if not ignore_warnings:
+ self.failUnlessEqual(self.fixer_log, [])
+
+
+class Test_ne(FixerTestCase):
+ fixer = "ne"
+
+ def test_basic(self):
+ b = """if x <> y:
+ pass"""
+
+ a = """if x != y:
+ pass"""
+ self.check(b, a)
+
+ def test_no_spaces(self):
+ b = """if x<>y:
+ pass"""
+
+ a = """if x!=y:
+ pass"""
+ self.check(b, a)
+
+ def test_chained(self):
+ b = """if x<>y<>z:
+ pass"""
+
+ a = """if x!=y!=z:
+ pass"""
+ self.check(b, a)
+
+class Test_has_key(FixerTestCase):
+ fixer = "has_key"
+
+ def test_1(self):
+ b = """x = d.has_key("x") or d.has_key("y")"""
+ a = """x = "x" in d or "y" in d"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """x = a.b.c.d.has_key("x") ** 3"""
+ a = """x = ("x" in a.b.c.d) ** 3"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """x = a.b.has_key(1 + 2).__repr__()"""
+ a = """x = (1 + 2 in a.b).__repr__()"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """x = a.b.has_key(1 + 2).__repr__() ** -3 ** 4"""
+ a = """x = (1 + 2 in a.b).__repr__() ** -3 ** 4"""
+ self.check(b, a)
+
+ def test_5(self):
+ b = """x = a.has_key(f or g)"""
+ a = """x = (f or g) in a"""
+ self.check(b, a)
+
+ def test_6(self):
+ b = """x = a + b.has_key(c)"""
+ a = """x = a + (c in b)"""
+ self.check(b, a)
+
+ def test_7(self):
+ b = """x = a.has_key(lambda: 12)"""
+ a = """x = (lambda: 12) in a"""
+ self.check(b, a)
+
+ def test_8(self):
+ b = """x = a.has_key(a for a in b)"""
+ a = """x = (a for a in b) in a"""
+ self.check(b, a)
+
+ def test_9(self):
+ b = """if not a.has_key(b): pass"""
+ a = """if b not in a: pass"""
+ self.check(b, a)
+
+ def test_10(self):
+ b = """if not a.has_key(b).__repr__(): pass"""
+ a = """if not (b in a).__repr__(): pass"""
+ self.check(b, a)
+
+ def test_11(self):
+ b = """if not a.has_key(b) ** 2: pass"""
+ a = """if not (b in a) ** 2: pass"""
+ self.check(b, a)
+
+class Test_apply(FixerTestCase):
+ fixer = "apply"
+
+ def test_1(self):
+ b = """x = apply(f, g + h)"""
+ a = """x = f(*g + h)"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """y = apply(f, g, h)"""
+ a = """y = f(*g, **h)"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """z = apply(fs[0], g or h, h or g)"""
+ a = """z = fs[0](*g or h, **h or g)"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """apply(f, (x, y) + t)"""
+ a = """f(*(x, y) + t)"""
+ self.check(b, a)
+
+ def test_5(self):
+ b = """apply(f, args,)"""
+ a = """f(*args)"""
+ self.check(b, a)
+
+ def test_6(self):
+ b = """apply(f, args, kwds,)"""
+ a = """f(*args, **kwds)"""
+ self.check(b, a)
+
+ # Test that complex functions are parenthesized
+
+ def test_complex_1(self):
+ b = """x = apply(f+g, args)"""
+ a = """x = (f+g)(*args)"""
+ self.check(b, a)
+
+ def test_complex_2(self):
+ b = """x = apply(f*g, args)"""
+ a = """x = (f*g)(*args)"""
+ self.check(b, a)
+
+ def test_complex_3(self):
+ b = """x = apply(f**g, args)"""
+ a = """x = (f**g)(*args)"""
+ self.check(b, a)
+
+ # But dotted names etc. not
+
+ def test_dotted_name(self):
+ b = """x = apply(f.g, args)"""
+ a = """x = f.g(*args)"""
+ self.check(b, a)
+
+ def test_subscript(self):
+ b = """x = apply(f[x], args)"""
+ a = """x = f[x](*args)"""
+ self.check(b, a)
+
+ def test_call(self):
+ b = """x = apply(f(), args)"""
+ a = """x = f()(*args)"""
+ self.check(b, a)
+
+ # Extreme case
+ def test_extreme(self):
+ b = """x = apply(a.b.c.d.e.f, args, kwds)"""
+ a = """x = a.b.c.d.e.f(*args, **kwds)"""
+ self.check(b, a)
+
+ # XXX Comments in weird places still get lost
+ def test_weird_comments(self):
+ b = """apply( # foo
+ f, # bar
+ args)"""
+ a = """f(*args)"""
+ self.check(b, a)
+
+ # These should *not* be touched
+
+ def test_unchanged_1(self):
+ s = """apply()"""
+ self.unchanged(s)
+
+ def test_unchanged_2(self):
+ s = """apply(f)"""
+ self.unchanged(s)
+
+ def test_unchanged_3(self):
+ s = """apply(f,)"""
+ self.unchanged(s)
+
+ def test_unchanged_4(self):
+ s = """apply(f, args, kwds, extras)"""
+ self.unchanged(s)
+
+ def test_unchanged_5(self):
+ s = """apply(f, *args, **kwds)"""
+ self.unchanged(s)
+
+ def test_unchanged_6(self):
+ s = """apply(f, *args)"""
+ self.unchanged(s)
+
+ def test_unchanged_7(self):
+ s = """apply(func=f, args=args, kwds=kwds)"""
+ self.unchanged(s)
+
+ def test_unchanged_8(self):
+ s = """apply(f, args=args, kwds=kwds)"""
+ self.unchanged(s)
+
+ def test_unchanged_9(self):
+ s = """apply(f, args, kwds=kwds)"""
+ self.unchanged(s)
+
+ def test_space_1(self):
+ a = """apply( f, args, kwds)"""
+ b = """f(*args, **kwds)"""
+ self.check(a, b)
+
+ def test_space_2(self):
+ a = """apply( f ,args,kwds )"""
+ b = """f(*args, **kwds)"""
+ self.check(a, b)
+
+class Test_intern(FixerTestCase):
+ fixer = "intern"
+
+ def test_prefix_preservation(self):
+ b = """x = intern( a )"""
+ a = """x = sys.intern( a )"""
+ self.check(b, a)
+
+ b = """y = intern("b" # test
+ )"""
+ a = """y = sys.intern("b" # test
+ )"""
+ self.check(b, a)
+
+ b = """z = intern(a+b+c.d, )"""
+ a = """z = sys.intern(a+b+c.d, )"""
+ self.check(b, a)
+
+ def test(self):
+ b = """x = intern(a)"""
+ a = """x = sys.intern(a)"""
+ self.check(b, a)
+
+ b = """z = intern(a+b+c.d,)"""
+ a = """z = sys.intern(a+b+c.d,)"""
+ self.check(b, a)
+
+ b = """intern("y%s" % 5).replace("y", "")"""
+ a = """sys.intern("y%s" % 5).replace("y", "")"""
+ self.check(b, a)
+
+ # These should not be refactored
+
+ def test_unchanged(self):
+ s = """intern(a=1)"""
+ self.unchanged(s)
+
+ s = """intern(f, g)"""
+ self.unchanged(s)
+
+ s = """intern(*h)"""
+ self.unchanged(s)
+
+ s = """intern(**i)"""
+ self.unchanged(s)
+
+ s = """intern()"""
+ self.unchanged(s)
+
+class Test_print(FixerTestCase):
+ fixer = "print"
+
+ def test_prefix_preservation(self):
+ b = """print 1, 1+1, 1+1+1"""
+ a = """print(1, 1+1, 1+1+1)"""
+ self.check(b, a)
+
+ def test_idempotency(self):
+ s = """print()"""
+ self.unchanged(s)
+
+ s = """print('')"""
+ self.unchanged(s)
+
+ def test_idempotency_print_as_function(self):
+ print_stmt = pygram.python_grammar.keywords.pop("print")
+ try:
+ s = """print(1, 1+1, 1+1+1)"""
+ self.unchanged(s)
+
+ s = """print()"""
+ self.unchanged(s)
+
+ s = """print('')"""
+ self.unchanged(s)
+ finally:
+ pygram.python_grammar.keywords["print"] = print_stmt
+
+ def test_1(self):
+ b = """print 1, 1+1, 1+1+1"""
+ a = """print(1, 1+1, 1+1+1)"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """print 1, 2"""
+ a = """print(1, 2)"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """print"""
+ a = """print()"""
+ self.check(b, a)
+
+ def test_tuple(self):
+ b = """print (a, b, c)"""
+ a = """print((a, b, c))"""
+ self.check(b, a)
+
+ # trailing commas
+
+ def test_trailing_comma_1(self):
+ b = """print 1, 2, 3,"""
+ a = """print(1, 2, 3, end=' ')"""
+ self.check(b, a)
+
+ def test_trailing_comma_2(self):
+ b = """print 1, 2,"""
+ a = """print(1, 2, end=' ')"""
+ self.check(b, a)
+
+ def test_trailing_comma_3(self):
+ b = """print 1,"""
+ a = """print(1, end=' ')"""
+ self.check(b, a)
+
+ # >> stuff
+
+ def test_vargs_without_trailing_comma(self):
+ b = """print >>sys.stderr, 1, 2, 3"""
+ a = """print(1, 2, 3, file=sys.stderr)"""
+ self.check(b, a)
+
+ def test_with_trailing_comma(self):
+ b = """print >>sys.stderr, 1, 2,"""
+ a = """print(1, 2, end=' ', file=sys.stderr)"""
+ self.check(b, a)
+
+ def test_no_trailing_comma(self):
+ b = """print >>sys.stderr, 1+1"""
+ a = """print(1+1, file=sys.stderr)"""
+ self.check(b, a)
+
+ def test_spaces_before_file(self):
+ b = """print >> sys.stderr"""
+ a = """print(file=sys.stderr)"""
+ self.check(b, a)
+
+
+class Test_exec(FixerTestCase):
+ fixer = "exec"
+
+ def test_prefix_preservation(self):
+ b = """ exec code in ns1, ns2"""
+ a = """ exec(code, ns1, ns2)"""
+ self.check(b, a)
+
+ def test_basic(self):
+ b = """exec code"""
+ a = """exec(code)"""
+ self.check(b, a)
+
+ def test_with_globals(self):
+ b = """exec code in ns"""
+ a = """exec(code, ns)"""
+ self.check(b, a)
+
+ def test_with_globals_locals(self):
+ b = """exec code in ns1, ns2"""
+ a = """exec(code, ns1, ns2)"""
+ self.check(b, a)
+
+ def test_complex_1(self):
+ b = """exec (a.b()) in ns"""
+ a = """exec((a.b()), ns)"""
+ self.check(b, a)
+
+ def test_complex_2(self):
+ b = """exec a.b() + c in ns"""
+ a = """exec(a.b() + c, ns)"""
+ self.check(b, a)
+
+ # These should not be touched
+
+ def test_unchanged_1(self):
+ s = """exec(code)"""
+ self.unchanged(s)
+
+ def test_unchanged_2(self):
+ s = """exec (code)"""
+ self.unchanged(s)
+
+ def test_unchanged_3(self):
+ s = """exec(code, ns)"""
+ self.unchanged(s)
+
+ def test_unchanged_4(self):
+ s = """exec(code, ns1, ns2)"""
+ self.unchanged(s)
+
+
+class Test_repr(FixerTestCase):
+ fixer = "repr"
+
+ def test_prefix_preservation(self):
+ b = """x = `1 + 2`"""
+ a = """x = repr(1 + 2)"""
+ self.check(b, a)
+
+ def test_simple_1(self):
+ b = """x = `1 + 2`"""
+ a = """x = repr(1 + 2)"""
+ self.check(b, a)
+
+ def test_simple_2(self):
+ b = """y = `x`"""
+ a = """y = repr(x)"""
+ self.check(b, a)
+
+ def test_complex(self):
+ b = """z = `y`.__repr__()"""
+ a = """z = repr(y).__repr__()"""
+ self.check(b, a)
+
+ def test_tuple(self):
+ b = """x = `1, 2, 3`"""
+ a = """x = repr((1, 2, 3))"""
+ self.check(b, a)
+
+ def test_nested(self):
+ b = """x = `1 + `2``"""
+ a = """x = repr(1 + repr(2))"""
+ self.check(b, a)
+
+ def test_nested_tuples(self):
+ b = """x = `1, 2 + `3, 4``"""
+ a = """x = repr((1, 2 + repr((3, 4))))"""
+ self.check(b, a)
+
+class Test_except(FixerTestCase):
+ fixer = "except"
+
+ def test_prefix_preservation(self):
+ b = """
+ try:
+ pass
+ except (RuntimeError, ImportError), e:
+ pass"""
+ a = """
+ try:
+ pass
+ except (RuntimeError, ImportError) as e:
+ pass"""
+ self.check(b, a)
+
+ def test_simple(self):
+ b = """
+ try:
+ pass
+ except Foo, e:
+ pass"""
+ a = """
+ try:
+ pass
+ except Foo as e:
+ pass"""
+ self.check(b, a)
+
+ def test_simple_no_space_before_target(self):
+ b = """
+ try:
+ pass
+ except Foo,e:
+ pass"""
+ a = """
+ try:
+ pass
+ except Foo as e:
+ pass"""
+ self.check(b, a)
+
+ def test_tuple_unpack(self):
+ b = """
+ def foo():
+ try:
+ pass
+ except Exception, (f, e):
+ pass
+ except ImportError, e:
+ pass"""
+
+ a = """
+ def foo():
+ try:
+ pass
+ except Exception as xxx_todo_changeme:
+ (f, e) = xxx_todo_changeme.args
+ pass
+ except ImportError as e:
+ pass"""
+ self.check(b, a)
+
+ def test_multi_class(self):
+ b = """
+ try:
+ pass
+ except (RuntimeError, ImportError), e:
+ pass"""
+
+ a = """
+ try:
+ pass
+ except (RuntimeError, ImportError) as e:
+ pass"""
+ self.check(b, a)
+
+ def test_list_unpack(self):
+ b = """
+ try:
+ pass
+ except Exception, [a, b]:
+ pass"""
+
+ a = """
+ try:
+ pass
+ except Exception as xxx_todo_changeme:
+ [a, b] = xxx_todo_changeme.args
+ pass"""
+ self.check(b, a)
+
+ def test_weird_target_1(self):
+ b = """
+ try:
+ pass
+ except Exception, d[5]:
+ pass"""
+
+ a = """
+ try:
+ pass
+ except Exception as xxx_todo_changeme:
+ d[5] = xxx_todo_changeme
+ pass"""
+ self.check(b, a)
+
+ def test_weird_target_2(self):
+ b = """
+ try:
+ pass
+ except Exception, a.foo:
+ pass"""
+
+ a = """
+ try:
+ pass
+ except Exception as xxx_todo_changeme:
+ a.foo = xxx_todo_changeme
+ pass"""
+ self.check(b, a)
+
+ def test_weird_target_3(self):
+ b = """
+ try:
+ pass
+ except Exception, a().foo:
+ pass"""
+
+ a = """
+ try:
+ pass
+ except Exception as xxx_todo_changeme:
+ a().foo = xxx_todo_changeme
+ pass"""
+ self.check(b, a)
+
+ # These should not be touched:
+
+ def test_unchanged_1(self):
+ s = """
+ try:
+ pass
+ except:
+ pass"""
+ self.unchanged(s)
+
+ def test_unchanged_2(self):
+ s = """
+ try:
+ pass
+ except Exception:
+ pass"""
+ self.unchanged(s)
+
+ def test_unchanged_3(self):
+ s = """
+ try:
+ pass
+ except (Exception, SystemExit):
+ pass"""
+ self.unchanged(s)
+
+
+class Test_raise(FixerTestCase):
+ fixer = "raise"
+
+ def test_basic(self):
+ b = """raise Exception, 5"""
+ a = """raise Exception(5)"""
+ self.check(b, a)
+
+ def test_prefix_preservation(self):
+ b = """raise Exception,5"""
+ a = """raise Exception(5)"""
+ self.check(b, a)
+
+ b = """raise Exception, 5"""
+ a = """raise Exception(5)"""
+ self.check(b, a)
+
+ def test_with_comments(self):
+ b = """raise Exception, 5 # foo"""
+ a = """raise Exception(5) # foo"""
+ self.check(b, a)
+
+ b = """raise E, (5, 6) % (a, b) # foo"""
+ a = """raise E((5, 6) % (a, b)) # foo"""
+ self.check(b, a)
+
+ b = """def foo():
+ raise Exception, 5, 6 # foo"""
+ a = """def foo():
+ raise Exception(5).with_traceback(6) # foo"""
+ self.check(b, a)
+
+ def test_tuple_value(self):
+ b = """raise Exception, (5, 6, 7)"""
+ a = """raise Exception(5, 6, 7)"""
+ self.check(b, a)
+
+ def test_tuple_detection(self):
+ b = """raise E, (5, 6) % (a, b)"""
+ a = """raise E((5, 6) % (a, b))"""
+ self.check(b, a)
+
+ def test_tuple_exc_1(self):
+ b = """raise (((E1, E2), E3), E4), V"""
+ a = """raise E1(V)"""
+ self.check(b, a)
+
+ def test_tuple_exc_2(self):
+ b = """raise (E1, (E2, E3), E4), V"""
+ a = """raise E1(V)"""
+ self.check(b, a)
+
+ # These should produce a warning
+
+ def test_string_exc(self):
+ s = """raise 'foo'"""
+ self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+ def test_string_exc_val(self):
+ s = """raise "foo", 5"""
+ self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+ def test_string_exc_val_tb(self):
+ s = """raise "foo", 5, 6"""
+ self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+ # These should result in traceback-assignment
+
+ def test_tb_1(self):
+ b = """def foo():
+ raise Exception, 5, 6"""
+ a = """def foo():
+ raise Exception(5).with_traceback(6)"""
+ self.check(b, a)
+
+ def test_tb_2(self):
+ b = """def foo():
+ a = 5
+ raise Exception, 5, 6
+ b = 6"""
+ a = """def foo():
+ a = 5
+ raise Exception(5).with_traceback(6)
+ b = 6"""
+ self.check(b, a)
+
+ def test_tb_3(self):
+ b = """def foo():
+ raise Exception,5,6"""
+ a = """def foo():
+ raise Exception(5).with_traceback(6)"""
+ self.check(b, a)
+
+ def test_tb_4(self):
+ b = """def foo():
+ a = 5
+ raise Exception,5,6
+ b = 6"""
+ a = """def foo():
+ a = 5
+ raise Exception(5).with_traceback(6)
+ b = 6"""
+ self.check(b, a)
+
+ def test_tb_5(self):
+ b = """def foo():
+ raise Exception, (5, 6, 7), 6"""
+ a = """def foo():
+ raise Exception(5, 6, 7).with_traceback(6)"""
+ self.check(b, a)
+
+ def test_tb_6(self):
+ b = """def foo():
+ a = 5
+ raise Exception, (5, 6, 7), 6
+ b = 6"""
+ a = """def foo():
+ a = 5
+ raise Exception(5, 6, 7).with_traceback(6)
+ b = 6"""
+ self.check(b, a)
+
+
+class Test_throw(FixerTestCase):
+ fixer = "throw"
+
+ def test_1(self):
+ b = """g.throw(Exception, 5)"""
+ a = """g.throw(Exception(5))"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """g.throw(Exception,5)"""
+ a = """g.throw(Exception(5))"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """g.throw(Exception, (5, 6, 7))"""
+ a = """g.throw(Exception(5, 6, 7))"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """5 + g.throw(Exception, 5)"""
+ a = """5 + g.throw(Exception(5))"""
+ self.check(b, a)
+
+ # These should produce warnings
+
+ def test_warn_1(self):
+ s = """g.throw("foo")"""
+ self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+ def test_warn_2(self):
+ s = """g.throw("foo", 5)"""
+ self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+ def test_warn_3(self):
+ s = """g.throw("foo", 5, 6)"""
+ self.warns_unchanged(s, "Python 3 does not support string exceptions")
+
+ # These should not be touched
+
+ def test_untouched_1(self):
+ s = """g.throw(Exception)"""
+ self.unchanged(s)
+
+ def test_untouched_2(self):
+ s = """g.throw(Exception(5, 6))"""
+ self.unchanged(s)
+
+ def test_untouched_3(self):
+ s = """5 + g.throw(Exception(5, 6))"""
+ self.unchanged(s)
+
+ # These should result in traceback-assignment
+
+ def test_tb_1(self):
+ b = """def foo():
+ g.throw(Exception, 5, 6)"""
+ a = """def foo():
+ g.throw(Exception(5).with_traceback(6))"""
+ self.check(b, a)
+
+ def test_tb_2(self):
+ b = """def foo():
+ a = 5
+ g.throw(Exception, 5, 6)
+ b = 6"""
+ a = """def foo():
+ a = 5
+ g.throw(Exception(5).with_traceback(6))
+ b = 6"""
+ self.check(b, a)
+
+ def test_tb_3(self):
+ b = """def foo():
+ g.throw(Exception,5,6)"""
+ a = """def foo():
+ g.throw(Exception(5).with_traceback(6))"""
+ self.check(b, a)
+
+ def test_tb_4(self):
+ b = """def foo():
+ a = 5
+ g.throw(Exception,5,6)
+ b = 6"""
+ a = """def foo():
+ a = 5
+ g.throw(Exception(5).with_traceback(6))
+ b = 6"""
+ self.check(b, a)
+
+ def test_tb_5(self):
+ b = """def foo():
+ g.throw(Exception, (5, 6, 7), 6)"""
+ a = """def foo():
+ g.throw(Exception(5, 6, 7).with_traceback(6))"""
+ self.check(b, a)
+
+ def test_tb_6(self):
+ b = """def foo():
+ a = 5
+ g.throw(Exception, (5, 6, 7), 6)
+ b = 6"""
+ a = """def foo():
+ a = 5
+ g.throw(Exception(5, 6, 7).with_traceback(6))
+ b = 6"""
+ self.check(b, a)
+
+ def test_tb_7(self):
+ b = """def foo():
+ a + g.throw(Exception, 5, 6)"""
+ a = """def foo():
+ a + g.throw(Exception(5).with_traceback(6))"""
+ self.check(b, a)
+
+ def test_tb_8(self):
+ b = """def foo():
+ a = 5
+ a + g.throw(Exception, 5, 6)
+ b = 6"""
+ a = """def foo():
+ a = 5
+ a + g.throw(Exception(5).with_traceback(6))
+ b = 6"""
+ self.check(b, a)
+
+
+class Test_long(FixerTestCase):
+ fixer = "long"
+
+ def test_1(self):
+ b = """x = long(x)"""
+ a = """x = int(x)"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """y = isinstance(x, long)"""
+ a = """y = isinstance(x, int)"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """z = type(x) in (int, long)"""
+ a = """z = type(x) in (int, int)"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """a = 12L"""
+ a = """a = 12"""
+ self.check(b, a)
+
+ def test_5(self):
+ b = """b = 0x12l"""
+ a = """b = 0x12"""
+ self.check(b, a)
+
+ def test_unchanged_1(self):
+ s = """a = 12"""
+ self.unchanged(s)
+
+ def test_unchanged_2(self):
+ s = """b = 0x12"""
+ self.unchanged(s)
+
+ def test_unchanged_3(self):
+ s = """c = 3.14"""
+ self.unchanged(s)
+
+ def test_prefix_preservation(self):
+ b = """x = long( x )"""
+ a = """x = int( x )"""
+ self.check(b, a)
+
+
+class Test_dict(FixerTestCase):
+ fixer = "dict"
+
+ def test_prefix_preservation(self):
+ b = "if d. keys ( ) : pass"
+ a = "if list(d. keys ( )) : pass"
+ self.check(b, a)
+
+ b = "if d. items ( ) : pass"
+ a = "if list(d. items ( )) : pass"
+ self.check(b, a)
+
+ b = "if d. iterkeys ( ) : pass"
+ a = "if iter(d. keys ( )) : pass"
+ self.check(b, a)
+
+ b = "[i for i in d. iterkeys( ) ]"
+ a = "[i for i in d. keys( ) ]"
+ self.check(b, a)
+
+ def test_trailing_comment(self):
+ b = "d.keys() # foo"
+ a = "list(d.keys()) # foo"
+ self.check(b, a)
+
+ b = "d.items() # foo"
+ a = "list(d.items()) # foo"
+ self.check(b, a)
+
+ b = "d.iterkeys() # foo"
+ a = "iter(d.keys()) # foo"
+ self.check(b, a)
+
+ b = """[i for i in d.iterkeys() # foo
+ ]"""
+ a = """[i for i in d.keys() # foo
+ ]"""
+ self.check(b, a)
+
+ def test_unchanged(self):
+ wrappers = ["set", "sorted", "any", "all", "tuple", "sum"]
+ for wrapper in wrappers:
+ s = "s = %s(d.keys())" % wrapper
+ self.unchanged(s)
+
+ s = "s = %s(d.values())" % wrapper
+ self.unchanged(s)
+
+ s = "s = %s(d.items())" % wrapper
+ self.unchanged(s)
+
+ def test_01(self):
+ b = "d.keys()"
+ a = "list(d.keys())"
+ self.check(b, a)
+
+ b = "a[0].foo().keys()"
+ a = "list(a[0].foo().keys())"
+ self.check(b, a)
+
+ def test_02(self):
+ b = "d.items()"
+ a = "list(d.items())"
+ self.check(b, a)
+
+ def test_03(self):
+ b = "d.values()"
+ a = "list(d.values())"
+ self.check(b, a)
+
+ def test_04(self):
+ b = "d.iterkeys()"
+ a = "iter(d.keys())"
+ self.check(b, a)
+
+ def test_05(self):
+ b = "d.iteritems()"
+ a = "iter(d.items())"
+ self.check(b, a)
+
+ def test_06(self):
+ b = "d.itervalues()"
+ a = "iter(d.values())"
+ self.check(b, a)
+
+ def test_07(self):
+ s = "list(d.keys())"
+ self.unchanged(s)
+
+ def test_08(self):
+ s = "sorted(d.keys())"
+ self.unchanged(s)
+
+ def test_09(self):
+ b = "iter(d.keys())"
+ a = "iter(list(d.keys()))"
+ self.check(b, a)
+
+ def test_10(self):
+ b = "foo(d.keys())"
+ a = "foo(list(d.keys()))"
+ self.check(b, a)
+
+ def test_11(self):
+ b = "for i in d.keys(): print i"
+ a = "for i in list(d.keys()): print i"
+ self.check(b, a)
+
+ def test_12(self):
+ b = "for i in d.iterkeys(): print i"
+ a = "for i in d.keys(): print i"
+ self.check(b, a)
+
+ def test_13(self):
+ b = "[i for i in d.keys()]"
+ a = "[i for i in list(d.keys())]"
+ self.check(b, a)
+
+ def test_14(self):
+ b = "[i for i in d.iterkeys()]"
+ a = "[i for i in d.keys()]"
+ self.check(b, a)
+
+ def test_15(self):
+ b = "(i for i in d.keys())"
+ a = "(i for i in list(d.keys()))"
+ self.check(b, a)
+
+ def test_16(self):
+ b = "(i for i in d.iterkeys())"
+ a = "(i for i in d.keys())"
+ self.check(b, a)
+
+ def test_17(self):
+ b = "iter(d.iterkeys())"
+ a = "iter(d.keys())"
+ self.check(b, a)
+
+ def test_18(self):
+ b = "list(d.iterkeys())"
+ a = "list(d.keys())"
+ self.check(b, a)
+
+ def test_19(self):
+ b = "sorted(d.iterkeys())"
+ a = "sorted(d.keys())"
+ self.check(b, a)
+
+ def test_20(self):
+ b = "foo(d.iterkeys())"
+ a = "foo(iter(d.keys()))"
+ self.check(b, a)
+
+ def test_21(self):
+ b = "print h.iterkeys().next()"
+ a = "print iter(h.keys()).next()"
+ self.check(b, a)
+
+ def test_22(self):
+ b = "print h.keys()[0]"
+ a = "print list(h.keys())[0]"
+ self.check(b, a)
+
+ def test_23(self):
+ b = "print list(h.iterkeys().next())"
+ a = "print list(iter(h.keys()).next())"
+ self.check(b, a)
+
+ def test_24(self):
+ b = "for x in h.keys()[0]: print x"
+ a = "for x in list(h.keys())[0]: print x"
+ self.check(b, a)
+
+class Test_xrange(FixerTestCase):
+ fixer = "xrange"
+
+ def test_prefix_preservation(self):
+ b = """x = xrange( 10 )"""
+ a = """x = range( 10 )"""
+ self.check(b, a)
+
+ b = """x = xrange( 1 , 10 )"""
+ a = """x = range( 1 , 10 )"""
+ self.check(b, a)
+
+ b = """x = xrange( 0 , 10 , 2 )"""
+ a = """x = range( 0 , 10 , 2 )"""
+ self.check(b, a)
+
+ def test_1(self):
+ b = """x = xrange(10)"""
+ a = """x = range(10)"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """x = xrange(1, 10)"""
+ a = """x = range(1, 10)"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """x = xrange(0, 10, 2)"""
+ a = """x = range(0, 10, 2)"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """for i in xrange(10):\n j=i"""
+ a = """for i in range(10):\n j=i"""
+ self.check(b, a)
+
+
+class Test_raw_input(FixerTestCase):
+ fixer = "raw_input"
+
+ def test_prefix_preservation(self):
+ b = """x = raw_input( )"""
+ a = """x = input( )"""
+ self.check(b, a)
+
+ b = """x = raw_input( '' )"""
+ a = """x = input( '' )"""
+ self.check(b, a)
+
+ def test_1(self):
+ b = """x = raw_input()"""
+ a = """x = input()"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """x = raw_input('')"""
+ a = """x = input('')"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """x = raw_input('prompt')"""
+ a = """x = input('prompt')"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """x = raw_input(foo(a) + 6)"""
+ a = """x = input(foo(a) + 6)"""
+ self.check(b, a)
+
+
+class Test_funcattrs(FixerTestCase):
+ fixer = "funcattrs"
+
+ attrs = ["closure", "doc", "name", "defaults", "code", "globals", "dict"]
+
+ def test(self):
+ for attr in self.attrs:
+ b = "a.func_%s" % attr
+ a = "a.__%s__" % attr
+ self.check(b, a)
+
+ b = "self.foo.func_%s.foo_bar" % attr
+ a = "self.foo.__%s__.foo_bar" % attr
+ self.check(b, a)
+
+ def test_unchanged(self):
+ for attr in self.attrs:
+ s = "foo(func_%s + 5)" % attr
+ self.unchanged(s)
+
+ s = "f(foo.__%s__)" % attr
+ self.unchanged(s)
+
+ s = "f(foo.__%s__.foo)" % attr
+ self.unchanged(s)
+
+
+class Test_xreadlines(FixerTestCase):
+ fixer = "xreadlines"
+
+ def test_call(self):
+ b = "for x in f.xreadlines(): pass"
+ a = "for x in f: pass"
+ self.check(b, a)
+
+ b = "for x in foo().xreadlines(): pass"
+ a = "for x in foo(): pass"
+ self.check(b, a)
+
+ b = "for x in (5 + foo()).xreadlines(): pass"
+ a = "for x in (5 + foo()): pass"
+ self.check(b, a)
+
+ def test_attr_ref(self):
+ b = "foo(f.xreadlines + 5)"
+ a = "foo(f.__iter__ + 5)"
+ self.check(b, a)
+
+ b = "foo(f().xreadlines + 5)"
+ a = "foo(f().__iter__ + 5)"
+ self.check(b, a)
+
+ b = "foo((5 + f()).xreadlines + 5)"
+ a = "foo((5 + f()).__iter__ + 5)"
+ self.check(b, a)
+
+ def test_unchanged(self):
+ s = "for x in f.xreadlines(5): pass"
+ self.unchanged(s)
+
+ s = "for x in f.xreadlines(k=5): pass"
+ self.unchanged(s)
+
+ s = "for x in f.xreadlines(*k, **v): pass"
+ self.unchanged(s)
+
+ s = "foo(xreadlines)"
+ self.unchanged(s)
+
+
+class Test_imports(FixerTestCase):
+ fixer = "imports"
+
+ modules = {"StringIO": ("io", ["StringIO"]),
+ "cStringIO": ("io", ["StringIO"]),
+ "__builtin__" : ("builtins", ["open", "Exception",
+ "__debug__", "str"]),
+ }
+
+ def test_import_module(self):
+ for old, (new, members) in self.modules.items():
+ b = "import %s" % old
+ a = "import %s" % new
+ self.check(b, a)
+
+ b = "import foo, %s, bar" % old
+ a = "import foo, %s, bar" % new
+ self.check(b, a)
+
+ def test_import_from(self):
+ for old, (new, members) in self.modules.items():
+ for member in members:
+ b = "from %s import %s" % (old, member)
+ a = "from %s import %s" % (new, member)
+ self.check(b, a)
+
+ s = "from foo import %s" % member
+ self.unchanged(s)
+
+ def test_import_module_as(self):
+ for old, (new, members) in self.modules.items():
+ b = "import %s as foo_bar" % old
+ a = "import %s as foo_bar" % new
+ self.check(b, a)
+
+ b = "import %s as foo_bar" % old
+ a = "import %s as foo_bar" % new
+ self.check(b, a)
+
+ def test_import_from_as(self):
+ for old, (new, members) in self.modules.items():
+ for member in members:
+ b = "from %s import %s as foo_bar" % (old, member)
+ a = "from %s import %s as foo_bar" % (new, member)
+ self.check(b, a)
+
+ def test_star(self):
+ for old in self.modules:
+ s = "from %s import *" % old
+ self.warns_unchanged(s, "Cannot handle star imports")
+
+ def test_import_module_usage(self):
+ for old, (new, members) in self.modules.items():
+ for member in members:
+ b = """
+ import %s
+ foo(%s, %s.%s)
+ """ % (old, old, old, member)
+ a = """
+ import %s
+ foo(%s, %s.%s)
+ """ % (new, new, new, member)
+ self.check(b, a)
+
+ def test_from_import_usage(self):
+ for old, (new, members) in self.modules.items():
+ for member in members:
+ b = """
+ from %s import %s
+ foo(%s, %s())
+ """ % (old, member, member, member)
+ a = """
+ from %s import %s
+ foo(%s, %s())
+ """ % (new, member, member, member)
+ self.check(b, a)
+
+
+class Test_input(FixerTestCase):
+ fixer = "input"
+
+ def test_prefix_preservation(self):
+ b = """x = input( )"""
+ a = """x = eval(input( ))"""
+ self.check(b, a)
+
+ b = """x = input( '' )"""
+ a = """x = eval(input( '' ))"""
+ self.check(b, a)
+
+ def test_trailing_comment(self):
+ b = """x = input() # foo"""
+ a = """x = eval(input()) # foo"""
+ self.check(b, a)
+
+ def test_idempotency(self):
+ s = """x = eval(input())"""
+ self.unchanged(s)
+
+ s = """x = eval(input(''))"""
+ self.unchanged(s)
+
+ s = """x = eval(input(foo(5) + 9))"""
+ self.unchanged(s)
+
+ def test_1(self):
+ b = """x = input()"""
+ a = """x = eval(input())"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """x = input('')"""
+ a = """x = eval(input(''))"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """x = input('prompt')"""
+ a = """x = eval(input('prompt'))"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """x = input(foo(5) + 9)"""
+ a = """x = eval(input(foo(5) + 9))"""
+ self.check(b, a)
+
+
+class Test_tuple_params(FixerTestCase):
+ fixer = "tuple_params"
+
+ def test_unchanged_1(self):
+ s = """def foo(): pass"""
+ self.unchanged(s)
+
+ def test_unchanged_2(self):
+ s = """def foo(a, b, c): pass"""
+ self.unchanged(s)
+
+ def test_unchanged_3(self):
+ s = """def foo(a=3, b=4, c=5): pass"""
+ self.unchanged(s)
+
+ def test_1(self):
+ b = """
+ def foo(((a, b), c)):
+ x = 5"""
+
+ a = """
+ def foo(xxx_todo_changeme):
+ ((a, b), c) = xxx_todo_changeme
+ x = 5"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """
+ def foo(((a, b), c), d):
+ x = 5"""
+
+ a = """
+ def foo(xxx_todo_changeme, d):
+ ((a, b), c) = xxx_todo_changeme
+ x = 5"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """
+ def foo(((a, b), c), d) -> e:
+ x = 5"""
+
+ a = """
+ def foo(xxx_todo_changeme, d) -> e:
+ ((a, b), c) = xxx_todo_changeme
+ x = 5"""
+ self.check(b, a)
+
+ def test_semicolon(self):
+ b = """
+ def foo(((a, b), c)): x = 5; y = 7"""
+
+ a = """
+ def foo(xxx_todo_changeme): ((a, b), c) = xxx_todo_changeme; x = 5; y = 7"""
+ self.check(b, a)
+
+ def test_keywords(self):
+ b = """
+ def foo(((a, b), c), d, e=5) -> z:
+ x = 5"""
+
+ a = """
+ def foo(xxx_todo_changeme, d, e=5) -> z:
+ ((a, b), c) = xxx_todo_changeme
+ x = 5"""
+ self.check(b, a)
+
+ def test_varargs(self):
+ b = """
+ def foo(((a, b), c), d, *vargs, **kwargs) -> z:
+ x = 5"""
+
+ a = """
+ def foo(xxx_todo_changeme, d, *vargs, **kwargs) -> z:
+ ((a, b), c) = xxx_todo_changeme
+ x = 5"""
+ self.check(b, a)
+
+ def test_multi_1(self):
+ b = """
+ def foo(((a, b), c), (d, e, f)) -> z:
+ x = 5"""
+
+ a = """
+ def foo(xxx_todo_changeme, xxx_todo_changeme1) -> z:
+ ((a, b), c) = xxx_todo_changeme
+ (d, e, f) = xxx_todo_changeme1
+ x = 5"""
+ self.check(b, a)
+
+ def test_multi_2(self):
+ b = """
+ def foo(x, ((a, b), c), d, (e, f, g), y) -> z:
+ x = 5"""
+
+ a = """
+ def foo(x, xxx_todo_changeme, d, xxx_todo_changeme1, y) -> z:
+ ((a, b), c) = xxx_todo_changeme
+ (e, f, g) = xxx_todo_changeme1
+ x = 5"""
+ self.check(b, a)
+
+ def test_docstring(self):
+ b = """
+ def foo(((a, b), c), (d, e, f)) -> z:
+ "foo foo foo foo"
+ x = 5"""
+
+ a = """
+ def foo(xxx_todo_changeme, xxx_todo_changeme1) -> z:
+ "foo foo foo foo"
+ ((a, b), c) = xxx_todo_changeme
+ (d, e, f) = xxx_todo_changeme1
+ x = 5"""
+ self.check(b, a)
+
+ def test_lambda_no_change(self):
+ s = """lambda x: x + 5"""
+ self.unchanged(s)
+
+ def test_lambda_parens_single_arg(self):
+ b = """lambda (x): x + 5"""
+ a = """lambda x: x + 5"""
+ self.check(b, a)
+
+ b = """lambda(x): x + 5"""
+ a = """lambda x: x + 5"""
+ self.check(b, a)
+
+ b = """lambda ((((x)))): x + 5"""
+ a = """lambda x: x + 5"""
+ self.check(b, a)
+
+ b = """lambda((((x)))): x + 5"""
+ a = """lambda x: x + 5"""
+ self.check(b, a)
+
+ def test_lambda_simple(self):
+ b = """lambda (x, y): x + f(y)"""
+ a = """lambda x_y: x_y[0] + f(x_y[1])"""
+ self.check(b, a)
+
+ b = """lambda(x, y): x + f(y)"""
+ a = """lambda x_y: x_y[0] + f(x_y[1])"""
+ self.check(b, a)
+
+ b = """lambda (((x, y))): x + f(y)"""
+ a = """lambda x_y: x_y[0] + f(x_y[1])"""
+ self.check(b, a)
+
+ b = """lambda(((x, y))): x + f(y)"""
+ a = """lambda x_y: x_y[0] + f(x_y[1])"""
+ self.check(b, a)
+
+ def test_lambda_one_tuple(self):
+ b = """lambda (x,): x + f(x)"""
+ a = """lambda x1: x1[0] + f(x1[0])"""
+ self.check(b, a)
+
+ b = """lambda (((x,))): x + f(x)"""
+ a = """lambda x1: x1[0] + f(x1[0])"""
+ self.check(b, a)
+
+ def test_lambda_simple_multi_use(self):
+ b = """lambda (x, y): x + x + f(x) + x"""
+ a = """lambda x_y: x_y[0] + x_y[0] + f(x_y[0]) + x_y[0]"""
+ self.check(b, a)
+
+ def test_lambda_simple_reverse(self):
+ b = """lambda (x, y): y + x"""
+ a = """lambda x_y: x_y[1] + x_y[0]"""
+ self.check(b, a)
+
+ def test_lambda_nested(self):
+ b = """lambda (x, (y, z)): x + y + z"""
+ a = """lambda x_y_z: x_y_z[0] + x_y_z[1][0] + x_y_z[1][1]"""
+ self.check(b, a)
+
+ b = """lambda (((x, (y, z)))): x + y + z"""
+ a = """lambda x_y_z: x_y_z[0] + x_y_z[1][0] + x_y_z[1][1]"""
+ self.check(b, a)
+
+ def test_lambda_nested_multi_use(self):
+ b = """lambda (x, (y, z)): x + y + f(y)"""
+ a = """lambda x_y_z: x_y_z[0] + x_y_z[1][0] + f(x_y_z[1][0])"""
+ self.check(b, a)
+
+class Test_methodattrs(FixerTestCase):
+ fixer = "methodattrs"
+
+ attrs = ["func", "self", "class"]
+
+ def test(self):
+ for attr in self.attrs:
+ b = "a.im_%s" % attr
+ if attr == "class":
+ a = "a.__self__.__class__"
+ else:
+ a = "a.__%s__" % attr
+ self.check(b, a)
+
+ b = "self.foo.im_%s.foo_bar" % attr
+ if attr == "class":
+ a = "self.foo.__self__.__class__.foo_bar"
+ else:
+ a = "self.foo.__%s__.foo_bar" % attr
+ self.check(b, a)
+
+ def test_unchanged(self):
+ for attr in self.attrs:
+ s = "foo(im_%s + 5)" % attr
+ self.unchanged(s)
+
+ s = "f(foo.__%s__)" % attr
+ self.unchanged(s)
+
+ s = "f(foo.__%s__.foo)" % attr
+ self.unchanged(s)
+
+
+class Test_next(FixerTestCase):
+ fixer = "next"
+
+ def test_1(self):
+ b = """it.next()"""
+ a = """next(it)"""
+ self.check(b, a)
+
+ def test_2(self):
+ b = """a.b.c.d.next()"""
+ a = """next(a.b.c.d)"""
+ self.check(b, a)
+
+ def test_3(self):
+ b = """(a + b).next()"""
+ a = """next((a + b))"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """a().next()"""
+ a = """next(a())"""
+ self.check(b, a)
+
+ def test_5(self):
+ b = """a().next() + b"""
+ a = """next(a()) + b"""
+ self.check(b, a)
+
+ def test_6(self):
+ b = """c( a().next() + b)"""
+ a = """c( next(a()) + b)"""
+ self.check(b, a)
+
+ def test_prefix_preservation_1(self):
+ b = """
+ for a in b:
+ foo(a)
+ a.next()
+ """
+ a = """
+ for a in b:
+ foo(a)
+ next(a)
+ """
+ self.check(b, a)
+
+ def test_prefix_preservation_2(self):
+ b = """
+ for a in b:
+ foo(a) # abc
+ # def
+ a.next()
+ """
+ a = """
+ for a in b:
+ foo(a) # abc
+ # def
+ next(a)
+ """
+ self.check(b, a)
+
+ def test_prefix_preservation_3(self):
+ b = """
+ next = 5
+ for a in b:
+ foo(a)
+ a.next()
+ """
+ a = """
+ next = 5
+ for a in b:
+ foo(a)
+ a.__next__()
+ """
+ self.check(b, a, ignore_warnings=True)
+
+ def test_prefix_preservation_4(self):
+ b = """
+ next = 5
+ for a in b:
+ foo(a) # abc
+ # def
+ a.next()
+ """
+ a = """
+ next = 5
+ for a in b:
+ foo(a) # abc
+ # def
+ a.__next__()
+ """
+ self.check(b, a, ignore_warnings=True)
+
+ def test_prefix_preservation_5(self):
+ b = """
+ next = 5
+ for a in b:
+ foo(foo(a), # abc
+ a.next())
+ """
+ a = """
+ next = 5
+ for a in b:
+ foo(foo(a), # abc
+ a.__next__())
+ """
+ self.check(b, a, ignore_warnings=True)
+
+ def test_prefix_preservation_6(self):
+ b = """
+ for a in b:
+ foo(foo(a), # abc
+ a.next())
+ """
+ a = """
+ for a in b:
+ foo(foo(a), # abc
+ next(a))
+ """
+ self.check(b, a)
+
+ def test_method_1(self):
+ b = """
+ class A:
+ def next(self):
+ pass
+ """
+ a = """
+ class A:
+ def __next__(self):
+ pass
+ """
+ self.check(b, a)
+
+ def test_method_2(self):
+ b = """
+ class A(object):
+ def next(self):
+ pass
+ """
+ a = """
+ class A(object):
+ def __next__(self):
+ pass
+ """
+ self.check(b, a)
+
+ def test_method_3(self):
+ b = """
+ class A:
+ def next(x):
+ pass
+ """
+ a = """
+ class A:
+ def __next__(x):
+ pass
+ """
+ self.check(b, a)
+
+ def test_method_4(self):
+ b = """
+ class A:
+ def __init__(self, foo):
+ self.foo = foo
+
+ def next(self):
+ pass
+
+ def __iter__(self):
+ return self
+ """
+ a = """
+ class A:
+ def __init__(self, foo):
+ self.foo = foo
+
+ def __next__(self):
+ pass
+
+ def __iter__(self):
+ return self
+ """
+ self.check(b, a)
+
+ def test_method_unchanged(self):
+ s = """
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.unchanged(s)
+
+ def test_shadowing_assign_simple(self):
+ s = """
+ next = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_assign_tuple_1(self):
+ s = """
+ (next, a) = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_assign_tuple_2(self):
+ s = """
+ (a, (b, (next, c)), a) = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_assign_list_1(self):
+ s = """
+ [next, a] = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_assign_list_2(self):
+ s = """
+ [a, [b, [next, c]], a] = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_builtin_assign(self):
+ s = """
+ def foo():
+ __builtin__.next = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_builtin_assign_in_tuple(self):
+ s = """
+ def foo():
+ (a, __builtin__.next) = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_builtin_assign_in_list(self):
+ s = """
+ def foo():
+ [a, __builtin__.next] = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_assign_to_next(self):
+ s = """
+ def foo():
+ A.next = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.unchanged(s)
+
+ def test_assign_to_next_in_tuple(self):
+ s = """
+ def foo():
+ (a, A.next) = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.unchanged(s)
+
+ def test_assign_to_next_in_list(self):
+ s = """
+ def foo():
+ [a, A.next] = foo
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.unchanged(s)
+
+ def test_shadowing_import_1(self):
+ s = """
+ import foo.bar as next
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_import_2(self):
+ s = """
+ import bar, bar.foo as next
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_import_3(self):
+ s = """
+ import bar, bar.foo as next, baz
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_import_from_1(self):
+ s = """
+ from x import next
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_import_from_2(self):
+ s = """
+ from x.a import next
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_import_from_3(self):
+ s = """
+ from x import a, next, b
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_import_from_4(self):
+ s = """
+ from x.a import a, next, b
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_funcdef_1(self):
+ s = """
+ def next(a):
+ pass
+
+ class A:
+ def next(self, a, b):
+ pass
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_funcdef_2(self):
+ b = """
+ def next(a):
+ pass
+
+ class A:
+ def next(self):
+ pass
+
+ it.next()
+ """
+ a = """
+ def next(a):
+ pass
+
+ class A:
+ def __next__(self):
+ pass
+
+ it.__next__()
+ """
+ self.warns(b, a, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_global_1(self):
+ s = """
+ def f():
+ global next
+ next = 5
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_global_2(self):
+ s = """
+ def f():
+ global a, next, b
+ next = 5
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_for_simple(self):
+ s = """
+ for next in it():
+ pass
+
+ b = 5
+ c = 6
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_for_tuple_1(self):
+ s = """
+ for next, b in it():
+ pass
+
+ b = 5
+ c = 6
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_shadowing_for_tuple_2(self):
+ s = """
+ for a, (next, c), b in it():
+ pass
+
+ b = 5
+ c = 6
+ """
+ self.warns_unchanged(s, "Calls to builtin next() possibly shadowed")
+
+ def test_noncall_access_1(self):
+ b = """gnext = g.next"""
+ a = """gnext = g.__next__"""
+ self.check(b, a)
+
+ def test_noncall_access_2(self):
+ b = """f(g.next + 5)"""
+ a = """f(g.__next__ + 5)"""
+ self.check(b, a)
+
+ def test_noncall_access_3(self):
+ b = """f(g().next + 5)"""
+ a = """f(g().__next__ + 5)"""
+ self.check(b, a)
+
+class Test_nonzero(FixerTestCase):
+ fixer = "nonzero"
+
+ def test_1(self):
+ b = """
+ class A:
+ def __nonzero__(self):
+ pass
+ """
+ a = """
+ class A:
+ def __bool__(self):
+ pass
+ """
+ self.check(b, a)
+
+ def test_2(self):
+ b = """
+ class A(object):
+ def __nonzero__(self):
+ pass
+ """
+ a = """
+ class A(object):
+ def __bool__(self):
+ pass
+ """
+ self.check(b, a)
+
+ def test_unchanged_1(self):
+ s = """
+ class A(object):
+ def __bool__(self):
+ pass
+ """
+ self.unchanged(s)
+
+ def test_unchanged_2(self):
+ s = """
+ class A(object):
+ def __nonzero__(self, a):
+ pass
+ """
+ self.unchanged(s)
+
+ def test_unchanged_func(self):
+ s = """
+ def __nonzero__(self):
+ pass
+ """
+ self.unchanged(s)
+
+class Test_numliterals(FixerTestCase):
+ fixer = "numliterals"
+
+ def test_octal_1(self):
+ b = """0755"""
+ a = """0o755"""
+ self.check(b, a)
+
+ def test_long_int_1(self):
+ b = """a = 12L"""
+ a = """a = 12"""
+ self.check(b, a)
+
+ def test_long_int_2(self):
+ b = """a = 12l"""
+ a = """a = 12"""
+ self.check(b, a)
+
+ def test_long_hex(self):
+ b = """b = 0x12l"""
+ a = """b = 0x12"""
+ self.check(b, a)
+
+ def test_unchanged_int(self):
+ s = """5"""
+ self.unchanged(s)
+
+ def test_unchanged_float(self):
+ s = """5.0"""
+ self.unchanged(s)
+
+ def test_unchanged_octal(self):
+ s = """0o755"""
+ self.unchanged(s)
+
+ def test_unchanged_hex(self):
+ s = """0xABC"""
+ self.unchanged(s)
+
+ def test_unchanged_exp(self):
+ s = """5.0e10"""
+ self.unchanged(s)
+
+ def test_unchanged_complex_int(self):
+ s = """5 + 4j"""
+ self.unchanged(s)
+
+ def test_unchanged_complex_float(self):
+ s = """5.4 + 4.9j"""
+ self.unchanged(s)
+
+ def test_unchanged_complex_bare(self):
+ s = """4j"""
+ self.unchanged(s)
+ s = """4.4j"""
+ self.unchanged(s)
+
+class Test_renames(FixerTestCase):
+ fixer = "renames"
+
+ modules = {"sys": ("maxint", "maxsize"),
+ }
+
+ def test_import_from(self):
+ for mod, (old, new) in self.modules.items():
+ b = "from %s import %s" % (mod, old)
+ a = "from %s import %s" % (mod, new)
+ self.check(b, a)
+
+ s = "from foo import %s" % old
+ self.unchanged(s)
+
+ def test_import_from_as(self):
+ for mod, (old, new) in self.modules.items():
+ b = "from %s import %s as foo_bar" % (mod, old)
+ a = "from %s import %s as foo_bar" % (mod, new)
+ self.check(b, a)
+
+ def test_import_module_usage(self):
+ for mod, (old, new) in self.modules.items():
+ b = """
+ import %s
+ foo(%s, %s.%s)
+ """ % (mod, mod, mod, old)
+ a = """
+ import %s
+ foo(%s, %s.%s)
+ """ % (mod, mod, mod, new)
+ self.check(b, a)
+
+ def XXX_test_from_import_usage(self):
+ # not implemented yet
+ for mod, (old, new) in self.modules.items():
+ b = """
+ from %s import %s
+ foo(%s, %s)
+ """ % (mod, old, mod, old)
+ a = """
+ from %s import %s
+ foo(%s, %s)
+ """ % (mod, new, mod, new)
+ self.check(b, a)
+
+
+class Test_unicode(FixerTestCase):
+ fixer = "unicode"
+
+ def test_unicode_call(self):
+ b = """unicode(x, y, z)"""
+ a = """str(x, y, z)"""
+ self.check(b, a)
+
+ def test_unicode_literal_1(self):
+ b = '''u"x"'''
+ a = '''"x"'''
+ self.check(b, a)
+
+ def test_unicode_literal_2(self):
+ b = """ur'x'"""
+ a = """r'x'"""
+ self.check(b, a)
+
+ def test_unicode_literal_3(self):
+ b = """UR'''x'''"""
+ a = """R'''x'''"""
+ self.check(b, a)
+
+class Test_callable(FixerTestCase):
+ fixer = "callable"
+
+ def test_prefix_preservation(self):
+ b = """callable( x)"""
+ a = """hasattr( x, '__call__')"""
+ self.check(b, a)
+
+ b = """if callable(x): pass"""
+ a = """if hasattr(x, '__call__'): pass"""
+ self.check(b, a)
+
+ def test_callable_call(self):
+ b = """callable(x)"""
+ a = """hasattr(x, '__call__')"""
+ self.check(b, a)
+
+ def test_callable_should_not_change(self):
+ a = """callable(*x)"""
+ self.unchanged(a)
+
+ a = """callable(x, y)"""
+ self.unchanged(a)
+
+ a = """callable(x, kw=y)"""
+ self.unchanged(a)
+
+ a = """callable()"""
+ self.unchanged(a)
+
+class Test_filter(FixerTestCase):
+ fixer = "filter"
+
+ def test_prefix_preservation(self):
+ b = """x = filter( foo, 'abc' )"""
+ a = """x = list(filter( foo, 'abc' ))"""
+ self.check(b, a)
+
+ b = """x = filter( None , 'abc' )"""
+ a = """x = [_f for _f in 'abc' if _f]"""
+ self.check(b, a)
+
+ def test_filter_basic(self):
+ b = """x = filter(None, 'abc')"""
+ a = """x = [_f for _f in 'abc' if _f]"""
+ self.check(b, a)
+
+ b = """x = len(filter(f, 'abc'))"""
+ a = """x = len(list(filter(f, 'abc')))"""
+ self.check(b, a)
+
+ b = """x = filter(lambda x: x%2 == 0, range(10))"""
+ a = """x = [x for x in range(10) if x%2 == 0]"""
+ self.check(b, a)
+
+ # Note the parens around x
+ b = """x = filter(lambda (x): x%2 == 0, range(10))"""
+ a = """x = [x for x in range(10) if x%2 == 0]"""
+ self.check(b, a)
+
+ # XXX This (rare) case is not supported
+## b = """x = filter(f, 'abc')[0]"""
+## a = """x = list(filter(f, 'abc'))[0]"""
+## self.check(b, a)
+
+ def test_filter_nochange(self):
+ a = """b.join(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """(a + foo(5)).join(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """iter(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """list(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """list(filter(f, 'abc'))[0]"""
+ self.unchanged(a)
+ a = """set(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """set(filter(f, 'abc')).pop()"""
+ self.unchanged(a)
+ a = """tuple(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """any(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """all(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """sum(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """sorted(filter(f, 'abc'))"""
+ self.unchanged(a)
+ a = """sorted(filter(f, 'abc'), key=blah)"""
+ self.unchanged(a)
+ a = """sorted(filter(f, 'abc'), key=blah)[0]"""
+ self.unchanged(a)
+ a = """for i in filter(f, 'abc'): pass"""
+ self.unchanged(a)
+ a = """[x for x in filter(f, 'abc')]"""
+ self.unchanged(a)
+ a = """(x for x in filter(f, 'abc'))"""
+ self.unchanged(a)
+
+ def test_future_builtins(self):
+ a = "from future_builtins import spam, filter; filter(f, 'ham')"
+ self.unchanged(a)
+
+ b = """from future_builtins import spam; x = filter(f, 'abc')"""
+ a = """from future_builtins import spam; x = list(filter(f, 'abc'))"""
+ self.check(b, a)
+
+ a = "from future_builtins import *; filter(f, 'ham')"
+ self.unchanged(a)
+
+class Test_map(FixerTestCase):
+ fixer = "map"
+
+ def check(self, b, a):
+ self.unchanged("from future_builtins import map; " + b, a)
+ FixerTestCase.check(self, b, a)
+
+ def test_prefix_preservation(self):
+ b = """x = map( f, 'abc' )"""
+ a = """x = list(map( f, 'abc' ))"""
+ self.check(b, a)
+
+ def test_trailing_comment(self):
+ b = """x = map(f, 'abc') # foo"""
+ a = """x = list(map(f, 'abc')) # foo"""
+ self.check(b, a)
+
+ def test_map_basic(self):
+ b = """x = map(f, 'abc')"""
+ a = """x = list(map(f, 'abc'))"""
+ self.check(b, a)
+
+ b = """x = len(map(f, 'abc', 'def'))"""
+ a = """x = len(list(map(f, 'abc', 'def')))"""
+ self.check(b, a)
+
+ b = """x = map(None, 'abc')"""
+ a = """x = list('abc')"""
+ self.check(b, a)
+
+ b = """x = map(None, 'abc', 'def')"""
+ a = """x = list(map(None, 'abc', 'def'))"""
+ self.check(b, a)
+
+ b = """x = map(lambda x: x+1, range(4))"""
+ a = """x = [x+1 for x in range(4)]"""
+ self.check(b, a)
+
+ # Note the parens around x
+ b = """x = map(lambda (x): x+1, range(4))"""
+ a = """x = [x+1 for x in range(4)]"""
+ self.check(b, a)
+
+ b = """
+ foo()
+ # foo
+ map(f, x)
+ """
+ a = """
+ foo()
+ # foo
+ list(map(f, x))
+ """
+ self.warns(b, a, "You should use a for loop here")
+
+ # XXX This (rare) case is not supported
+## b = """x = map(f, 'abc')[0]"""
+## a = """x = list(map(f, 'abc'))[0]"""
+## self.check(b, a)
+
+ def test_map_nochange(self):
+ a = """b.join(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """(a + foo(5)).join(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """iter(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """list(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """list(map(f, 'abc'))[0]"""
+ self.unchanged(a)
+ a = """set(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """set(map(f, 'abc')).pop()"""
+ self.unchanged(a)
+ a = """tuple(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """any(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """all(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """sum(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """sorted(map(f, 'abc'))"""
+ self.unchanged(a)
+ a = """sorted(map(f, 'abc'), key=blah)"""
+ self.unchanged(a)
+ a = """sorted(map(f, 'abc'), key=blah)[0]"""
+ self.unchanged(a)
+ a = """for i in map(f, 'abc'): pass"""
+ self.unchanged(a)
+ a = """[x for x in map(f, 'abc')]"""
+ self.unchanged(a)
+ a = """(x for x in map(f, 'abc'))"""
+ self.unchanged(a)
+
+ def test_future_builtins(self):
+ a = "from future_builtins import spam, map, eggs; map(f, 'ham')"
+ self.unchanged(a)
+
+ b = """from future_builtins import spam, eggs; x = map(f, 'abc')"""
+ a = """from future_builtins import spam, eggs; x = list(map(f, 'abc'))"""
+ self.check(b, a)
+
+ a = "from future_builtins import *; map(f, 'ham')"
+ self.unchanged(a)
+
+class Test_standarderror(FixerTestCase):
+ fixer = "standarderror"
+
+ def test(self):
+ b = """x = StandardError()"""
+ a = """x = Exception()"""
+ self.check(b, a)
+
+ b = """x = StandardError(a, b, c)"""
+ a = """x = Exception(a, b, c)"""
+ self.check(b, a)
+
+ b = """f(2 + StandardError(a, b, c))"""
+ a = """f(2 + Exception(a, b, c))"""
+ self.check(b, a)
+
+class Test_types(FixerTestCase):
+ fixer = "types"
+
+ def test_basic_types_convert(self):
+ b = """types.StringType"""
+ a = """bytes"""
+ self.check(b, a)
+
+ b = """types.DictType"""
+ a = """dict"""
+ self.check(b, a)
+
+ b = """types . IntType"""
+ a = """int"""
+ self.check(b, a)
+
+ b = """types.ListType"""
+ a = """list"""
+ self.check(b, a)
+
+ b = """types.LongType"""
+ a = """int"""
+ self.check(b, a)
+
+ b = """types.NoneType"""
+ a = """type(None)"""
+ self.check(b, a)
+
+class Test_idioms(FixerTestCase):
+ fixer = "idioms"
+
+ def test_while(self):
+ b = """while 1: foo()"""
+ a = """while True: foo()"""
+ self.check(b, a)
+
+ b = """while 1: foo()"""
+ a = """while True: foo()"""
+ self.check(b, a)
+
+ b = """
+ while 1:
+ foo()
+ """
+ a = """
+ while True:
+ foo()
+ """
+ self.check(b, a)
+
+ def test_while_unchanged(self):
+ s = """while 11: foo()"""
+ self.unchanged(s)
+
+ s = """while 0: foo()"""
+ self.unchanged(s)
+
+ s = """while foo(): foo()"""
+ self.unchanged(s)
+
+ s = """while []: foo()"""
+ self.unchanged(s)
+
+ def test_eq_simple(self):
+ b = """type(x) == T"""
+ a = """isinstance(x, T)"""
+ self.check(b, a)
+
+ b = """if type(x) == T: pass"""
+ a = """if isinstance(x, T): pass"""
+ self.check(b, a)
+
+ def test_eq_reverse(self):
+ b = """T == type(x)"""
+ a = """isinstance(x, T)"""
+ self.check(b, a)
+
+ b = """if T == type(x): pass"""
+ a = """if isinstance(x, T): pass"""
+ self.check(b, a)
+
+ def test_eq_expression(self):
+ b = """type(x+y) == d.get('T')"""
+ a = """isinstance(x+y, d.get('T'))"""
+ self.check(b, a)
+
+ b = """type( x + y) == d.get('T')"""
+ a = """isinstance(x + y, d.get('T'))"""
+ self.check(b, a)
+
+ def test_is_simple(self):
+ b = """type(x) is T"""
+ a = """isinstance(x, T)"""
+ self.check(b, a)
+
+ b = """if type(x) is T: pass"""
+ a = """if isinstance(x, T): pass"""
+ self.check(b, a)
+
+ def test_is_reverse(self):
+ b = """T is type(x)"""
+ a = """isinstance(x, T)"""
+ self.check(b, a)
+
+ b = """if T is type(x): pass"""
+ a = """if isinstance(x, T): pass"""
+ self.check(b, a)
+
+ def test_is_expression(self):
+ b = """type(x+y) is d.get('T')"""
+ a = """isinstance(x+y, d.get('T'))"""
+ self.check(b, a)
+
+ b = """type( x + y) is d.get('T')"""
+ a = """isinstance(x + y, d.get('T'))"""
+ self.check(b, a)
+
+ def test_is_not_simple(self):
+ b = """type(x) is not T"""
+ a = """not isinstance(x, T)"""
+ self.check(b, a)
+
+ b = """if type(x) is not T: pass"""
+ a = """if not isinstance(x, T): pass"""
+ self.check(b, a)
+
+ def test_is_not_reverse(self):
+ b = """T is not type(x)"""
+ a = """not isinstance(x, T)"""
+ self.check(b, a)
+
+ b = """if T is not type(x): pass"""
+ a = """if not isinstance(x, T): pass"""
+ self.check(b, a)
+
+ def test_is_not_expression(self):
+ b = """type(x+y) is not d.get('T')"""
+ a = """not isinstance(x+y, d.get('T'))"""
+ self.check(b, a)
+
+ b = """type( x + y) is not d.get('T')"""
+ a = """not isinstance(x + y, d.get('T'))"""
+ self.check(b, a)
+
+ def test_ne_simple(self):
+ b = """type(x) != T"""
+ a = """not isinstance(x, T)"""
+ self.check(b, a)
+
+ b = """if type(x) != T: pass"""
+ a = """if not isinstance(x, T): pass"""
+ self.check(b, a)
+
+ def test_ne_reverse(self):
+ b = """T != type(x)"""
+ a = """not isinstance(x, T)"""
+ self.check(b, a)
+
+ b = """if T != type(x): pass"""
+ a = """if not isinstance(x, T): pass"""
+ self.check(b, a)
+
+ def test_ne_expression(self):
+ b = """type(x+y) != d.get('T')"""
+ a = """not isinstance(x+y, d.get('T'))"""
+ self.check(b, a)
+
+ b = """type( x + y) != d.get('T')"""
+ a = """not isinstance(x + y, d.get('T'))"""
+ self.check(b, a)
+
+ def test_type_unchanged(self):
+ a = """type(x).__name__"""
+ self.unchanged(a)
+
+ def test_sort_list_call(self):
+ b = """
+ v = list(t)
+ v.sort()
+ foo(v)
+ """
+ a = """
+ v = sorted(t)
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = """
+ v = list(foo(b) + d)
+ v.sort()
+ foo(v)
+ """
+ a = """
+ v = sorted(foo(b) + d)
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = """
+ while x:
+ v = list(t)
+ v.sort()
+ foo(v)
+ """
+ a = """
+ while x:
+ v = sorted(t)
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = """
+ v = list(t)
+ # foo
+ v.sort()
+ foo(v)
+ """
+ a = """
+ v = sorted(t)
+ # foo
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = r"""
+ v = list( t)
+ v.sort()
+ foo(v)
+ """
+ a = r"""
+ v = sorted( t)
+ foo(v)
+ """
+ self.check(b, a)
+
+ def test_sort_simple_expr(self):
+ b = """
+ v = t
+ v.sort()
+ foo(v)
+ """
+ a = """
+ v = sorted(t)
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = """
+ v = foo(b)
+ v.sort()
+ foo(v)
+ """
+ a = """
+ v = sorted(foo(b))
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = """
+ v = b.keys()
+ v.sort()
+ foo(v)
+ """
+ a = """
+ v = sorted(b.keys())
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = """
+ v = foo(b) + d
+ v.sort()
+ foo(v)
+ """
+ a = """
+ v = sorted(foo(b) + d)
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = """
+ while x:
+ v = t
+ v.sort()
+ foo(v)
+ """
+ a = """
+ while x:
+ v = sorted(t)
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = """
+ v = t
+ # foo
+ v.sort()
+ foo(v)
+ """
+ a = """
+ v = sorted(t)
+ # foo
+ foo(v)
+ """
+ self.check(b, a)
+
+ b = r"""
+ v = t
+ v.sort()
+ foo(v)
+ """
+ a = r"""
+ v = sorted(t)
+ foo(v)
+ """
+ self.check(b, a)
+
+ def test_sort_unchanged(self):
+ s = """
+ v = list(t)
+ w.sort()
+ foo(w)
+ """
+ self.unchanged(s)
+
+ s = """
+ v = list(t)
+ v.sort(u)
+ foo(v)
+ """
+ self.unchanged(s)
+
+
+class Test_basestring(FixerTestCase):
+ fixer = "basestring"
+
+ def test_basestring(self):
+ b = """isinstance(x, basestring)"""
+ a = """isinstance(x, str)"""
+ self.check(b, a)
+
+
+class Test_buffer(FixerTestCase):
+ fixer = "buffer"
+
+ def test_buffer(self):
+ b = """x = buffer(y)"""
+ a = """x = memoryview(y)"""
+ self.check(b, a)
+
+class Test_future(FixerTestCase):
+ fixer = "future"
+
+ def test_future(self):
+ b = """from __future__ import braces"""
+ a = """"""
+ self.check(b, a)
+
+class Test_itertools(FixerTestCase):
+ fixer = "itertools"
+
+ def checkall(self, before, after):
+ # Because we need to check with and without the itertools prefix
+ # and on each of the three functions, these loops make it all
+ # much easier
+ for i in ('itertools.', ''):
+ for f in ('map', 'filter', 'zip'):
+ b = before %(i+'i'+f)
+ a = after %(f)
+ self.check(b, a)
+
+ def test_0(self):
+ # A simple example -- test_1 covers exactly the same thing,
+ # but it's not quite as clear.
+ b = "itertools.izip(a, b)"
+ a = "zip(a, b)"
+ self.check(b, a)
+
+ def test_1(self):
+ b = """%s(f, a)"""
+ a = """%s(f, a)"""
+ self.checkall(b, a)
+
+ def test_2(self):
+ b = """itertools.ifilterfalse(a, b)"""
+ a = """itertools.filterfalse(a, b)"""
+ self.check(b, a)
+
+ def test_4(self):
+ b = """ifilterfalse(a, b)"""
+ a = """filterfalse(a, b)"""
+ self.check(b, a)
+
+ def test_space_1(self):
+ b = """ %s(f, a)"""
+ a = """ %s(f, a)"""
+ self.checkall(b, a)
+
+ def test_space_2(self):
+ b = """ itertools.ifilterfalse(a, b)"""
+ a = """ itertools.filterfalse(a, b)"""
+ self.check(b, a)
+
+
+if __name__ == "__main__":
+ import __main__
+ support.run_all_tests(__main__)
diff --git a/Lib/lib2to3/tests/test_parser.py b/Lib/lib2to3/tests/test_parser.py
new file mode 100644
index 0000000..33ddc92
--- /dev/null
+++ b/Lib/lib2to3/tests/test_parser.py
@@ -0,0 +1,202 @@
+#!/usr/bin/env python2.5
+"""Test suite for 2to3's parser and grammar files.
+
+This is the place to add tests for changes to 2to3's grammar, such as those
+merging the grammars for Python 2 and 3. In addition to specific tests for
+parts of the grammar we've changed, we also make sure we can parse the
+test_grammar.py files from both Python 2 and Python 3.
+"""
+# Author: Collin Winter
+
+# Testing imports
+from . import support
+from .support import driver, test_dir
+
+# Python imports
+import os
+import os.path
+
+# Local imports
+from ..pgen2.parse import ParseError
+
+
+class GrammarTest(support.TestCase):
+ def validate(self, code):
+ support.parse_string(code)
+
+ def invalid_syntax(self, code):
+ try:
+ self.validate(code)
+ except ParseError:
+ pass
+ else:
+ raise AssertionError("Syntax shouldn't have been valid")
+
+
+class TestRaiseChanges(GrammarTest):
+ def test_2x_style_1(self):
+ self.validate("raise")
+
+ def test_2x_style_2(self):
+ self.validate("raise E, V")
+
+ def test_2x_style_3(self):
+ self.validate("raise E, V, T")
+
+ def test_2x_style_invalid_1(self):
+ self.invalid_syntax("raise E, V, T, Z")
+
+ def test_3x_style(self):
+ self.validate("raise E1 from E2")
+
+ def test_3x_style_invalid_1(self):
+ self.invalid_syntax("raise E, V from E1")
+
+ def test_3x_style_invalid_2(self):
+ self.invalid_syntax("raise E from E1, E2")
+
+ def test_3x_style_invalid_3(self):
+ self.invalid_syntax("raise from E1, E2")
+
+ def test_3x_style_invalid_4(self):
+ self.invalid_syntax("raise E from")
+
+
+# Adapated from Python 3's Lib/test/test_grammar.py:GrammarTests.testFuncdef
+class TestFunctionAnnotations(GrammarTest):
+ def test_1(self):
+ self.validate("""def f(x) -> list: pass""")
+
+ def test_2(self):
+ self.validate("""def f(x:int): pass""")
+
+ def test_3(self):
+ self.validate("""def f(*x:str): pass""")
+
+ def test_4(self):
+ self.validate("""def f(**x:float): pass""")
+
+ def test_5(self):
+ self.validate("""def f(x, y:1+2): pass""")
+
+ def test_6(self):
+ self.validate("""def f(a, (b:1, c:2, d)): pass""")
+
+ def test_7(self):
+ self.validate("""def f(a, (b:1, c:2, d), e:3=4, f=5, *g:6): pass""")
+
+ def test_8(self):
+ s = """def f(a, (b:1, c:2, d), e:3=4, f=5,
+ *g:6, h:7, i=8, j:9=10, **k:11) -> 12: pass"""
+ self.validate(s)
+
+
+class TestExcept(GrammarTest):
+ def test_new(self):
+ s = """
+ try:
+ x
+ except E as N:
+ y"""
+ self.validate(s)
+
+ def test_old(self):
+ s = """
+ try:
+ x
+ except E, N:
+ y"""
+ self.validate(s)
+
+
+# Adapted from Python 3's Lib/test/test_grammar.py:GrammarTests.testAtoms
+class TestSetLiteral(GrammarTest):
+ def test_1(self):
+ self.validate("""x = {'one'}""")
+
+ def test_2(self):
+ self.validate("""x = {'one', 1,}""")
+
+ def test_3(self):
+ self.validate("""x = {'one', 'two', 'three'}""")
+
+ def test_4(self):
+ self.validate("""x = {2, 3, 4,}""")
+
+
+class TestNumericLiterals(GrammarTest):
+ def test_new_octal_notation(self):
+ self.validate("""0o7777777777777""")
+ self.invalid_syntax("""0o7324528887""")
+
+ def test_new_binary_notation(self):
+ self.validate("""0b101010""")
+ self.invalid_syntax("""0b0101021""")
+
+
+class TestClassDef(GrammarTest):
+ def test_new_syntax(self):
+ self.validate("class B(t=7): pass")
+ self.validate("class B(t, *args): pass")
+ self.validate("class B(t, **kwargs): pass")
+ self.validate("class B(t, *args, **kwargs): pass")
+ self.validate("class B(t, y=9, *args, **kwargs): pass")
+
+
+class TestParserIdempotency(support.TestCase):
+
+ """A cut-down version of pytree_idempotency.py."""
+
+ def test_all_project_files(self):
+ for filepath in support.all_project_files():
+ print "Parsing %s..." % filepath
+ tree = driver.parse_file(filepath, debug=True)
+ if diff(filepath, tree):
+ self.fail("Idempotency failed: %s" % filepath)
+
+
+class TestLiterals(GrammarTest):
+
+ def test_multiline_bytes_literals(self):
+ s = """
+ md5test(b"\xaa" * 80,
+ (b"Test Using Larger Than Block-Size Key "
+ b"and Larger Than One Block-Size Data"),
+ "6f630fad67cda0ee1fb1f562db3aa53e")
+ """
+ self.validate(s)
+
+ def test_multiline_bytes_tripquote_literals(self):
+ s = '''
+ b"""
+ <?xml version="1.0" encoding="UTF-8"?>
+ <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN">
+ """
+ '''
+ self.validate(s)
+
+ def test_multiline_str_literals(self):
+ s = """
+ md5test("\xaa" * 80,
+ ("Test Using Larger Than Block-Size Key "
+ "and Larger Than One Block-Size Data"),
+ "6f630fad67cda0ee1fb1f562db3aa53e")
+ """
+ self.validate(s)
+
+
+def diff(fn, tree):
+ f = open("@", "w")
+ try:
+ f.write(str(tree))
+ finally:
+ f.close()
+ try:
+ return os.system("diff -u %s @" % fn)
+ finally:
+ os.remove("@")
+
+
+if __name__ == "__main__":
+ import __main__
+ support.run_all_tests(__main__)
diff --git a/Lib/lib2to3/tests/test_pytree.py b/Lib/lib2to3/tests/test_pytree.py
new file mode 100755
index 0000000..7a94423
--- /dev/null
+++ b/Lib/lib2to3/tests/test_pytree.py
@@ -0,0 +1,440 @@
+#!/usr/bin/env python2.5
+# Copyright 2006 Google, Inc. All Rights Reserved.
+# Licensed to PSF under a Contributor Agreement.
+
+"""Unit tests for pytree.py.
+
+NOTE: Please *don't* add doc strings to individual test methods!
+In verbose mode, printing of the module, class and method name is much
+more helpful than printing of (the first line of) the docstring,
+especially when debugging a test.
+"""
+
+# Testing imports
+from . import support
+
+# Local imports (XXX should become a package)
+from .. import pytree
+
+try:
+ sorted
+except NameError:
+ def sorted(lst):
+ l = list(lst)
+ l.sort()
+ return l
+
+class TestNodes(support.TestCase):
+
+ """Unit tests for nodes (Base, Leaf, Node)."""
+
+ def testBaseCantConstruct(self):
+ if __debug__:
+ # Test that instantiating Base() raises an AssertionError
+ self.assertRaises(AssertionError, pytree.Base)
+
+ def testLeaf(self):
+ l1 = pytree.Leaf(100, "foo")
+ self.assertEqual(l1.type, 100)
+ self.assertEqual(l1.value, "foo")
+
+ def testLeafRepr(self):
+ l1 = pytree.Leaf(100, "foo")
+ self.assertEqual(repr(l1), "Leaf(100, 'foo')")
+
+ def testLeafStr(self):
+ l1 = pytree.Leaf(100, "foo")
+ self.assertEqual(str(l1), "foo")
+ l2 = pytree.Leaf(100, "foo", context=(" ", (10, 1)))
+ self.assertEqual(str(l2), " foo")
+
+ def testLeafStrNumericValue(self):
+ # Make sure that the Leaf's value is stringified. Failing to
+ # do this can cause a TypeError in certain situations.
+ l1 = pytree.Leaf(2, 5)
+ l1.set_prefix("foo_")
+ self.assertEqual(str(l1), "foo_5")
+
+ def testLeafEq(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "foo", context=(" ", (1, 0)))
+ self.assertEqual(l1, l2)
+ l3 = pytree.Leaf(101, "foo")
+ l4 = pytree.Leaf(100, "bar")
+ self.assertNotEqual(l1, l3)
+ self.assertNotEqual(l1, l4)
+
+ def testLeafPrefix(self):
+ l1 = pytree.Leaf(100, "foo")
+ self.assertEqual(l1.get_prefix(), "")
+ self.failIf(l1.was_changed)
+ l1.set_prefix(" ##\n\n")
+ self.assertEqual(l1.get_prefix(), " ##\n\n")
+ self.failUnless(l1.was_changed)
+
+ def testNode(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(200, "bar")
+ n1 = pytree.Node(1000, [l1, l2])
+ self.assertEqual(n1.type, 1000)
+ self.assertEqual(n1.children, [l1, l2])
+
+ def testNodeRepr(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "bar", context=(" ", (1, 0)))
+ n1 = pytree.Node(1000, [l1, l2])
+ self.assertEqual(repr(n1),
+ "Node(1000, [%s, %s])" % (repr(l1), repr(l2)))
+
+ def testNodeStr(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "bar", context=(" ", (1, 0)))
+ n1 = pytree.Node(1000, [l1, l2])
+ self.assertEqual(str(n1), "foo bar")
+
+ def testNodePrefix(self):
+ l1 = pytree.Leaf(100, "foo")
+ self.assertEqual(l1.get_prefix(), "")
+ n1 = pytree.Node(1000, [l1])
+ self.assertEqual(n1.get_prefix(), "")
+ n1.set_prefix(" ")
+ self.assertEqual(n1.get_prefix(), " ")
+ self.assertEqual(l1.get_prefix(), " ")
+
+ def testGetSuffix(self):
+ l1 = pytree.Leaf(100, "foo", prefix="a")
+ l2 = pytree.Leaf(100, "bar", prefix="b")
+ n1 = pytree.Node(1000, [l1, l2])
+
+ self.assertEqual(l1.get_suffix(), l2.get_prefix())
+ self.assertEqual(l2.get_suffix(), "")
+ self.assertEqual(n1.get_suffix(), "")
+
+ l3 = pytree.Leaf(100, "bar", prefix="c")
+ n2 = pytree.Node(1000, [n1, l3])
+
+ self.assertEqual(n1.get_suffix(), l3.get_prefix())
+ self.assertEqual(l3.get_suffix(), "")
+ self.assertEqual(n2.get_suffix(), "")
+
+ def testNodeEq(self):
+ n1 = pytree.Node(1000, ())
+ n2 = pytree.Node(1000, [], context=(" ", (1, 0)))
+ self.assertEqual(n1, n2)
+ n3 = pytree.Node(1001, ())
+ self.assertNotEqual(n1, n3)
+
+ def testNodeEqRecursive(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "foo")
+ n1 = pytree.Node(1000, [l1])
+ n2 = pytree.Node(1000, [l2])
+ self.assertEqual(n1, n2)
+ l3 = pytree.Leaf(100, "bar")
+ n3 = pytree.Node(1000, [l3])
+ self.assertNotEqual(n1, n3)
+
+ def testReplace(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "+")
+ l3 = pytree.Leaf(100, "bar")
+ n1 = pytree.Node(1000, [l1, l2, l3])
+ self.assertEqual(n1.children, [l1, l2, l3])
+ self.failUnless(isinstance(n1.children, list))
+ self.failIf(n1.was_changed)
+ l2new = pytree.Leaf(100, "-")
+ l2.replace(l2new)
+ self.assertEqual(n1.children, [l1, l2new, l3])
+ self.failUnless(isinstance(n1.children, list))
+ self.failUnless(n1.was_changed)
+
+ def testReplaceWithList(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "+")
+ l3 = pytree.Leaf(100, "bar")
+ n1 = pytree.Node(1000, [l1, l2, l3])
+
+ l2.replace([pytree.Leaf(100, "*"), pytree.Leaf(100, "*")])
+ self.assertEqual(str(n1), "foo**bar")
+ self.failUnless(isinstance(n1.children, list))
+
+ def testPostOrder(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "bar")
+ n1 = pytree.Node(1000, [l1, l2])
+ self.assertEqual(list(n1.post_order()), [l1, l2, n1])
+
+ def testPreOrder(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "bar")
+ n1 = pytree.Node(1000, [l1, l2])
+ self.assertEqual(list(n1.pre_order()), [n1, l1, l2])
+
+ def testChangedLeaf(self):
+ l1 = pytree.Leaf(100, "f")
+ self.failIf(l1.was_changed)
+
+ l1.changed()
+ self.failUnless(l1.was_changed)
+
+ def testChangedNode(self):
+ l1 = pytree.Leaf(100, "f")
+ n1 = pytree.Node(1000, [l1])
+ self.failIf(n1.was_changed)
+
+ n1.changed()
+ self.failUnless(n1.was_changed)
+
+ def testChangedRecursive(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "+")
+ l3 = pytree.Leaf(100, "bar")
+ n1 = pytree.Node(1000, [l1, l2, l3])
+ n2 = pytree.Node(1000, [n1])
+ self.failIf(l1.was_changed)
+ self.failIf(n1.was_changed)
+ self.failIf(n2.was_changed)
+
+ n1.changed()
+ self.failUnless(n1.was_changed)
+ self.failUnless(n2.was_changed)
+ self.failIf(l1.was_changed)
+
+ def testLeafConstructorPrefix(self):
+ for prefix in ("xyz_", ""):
+ l1 = pytree.Leaf(100, "self", prefix=prefix)
+ self.failUnless(str(l1), prefix + "self")
+ self.assertEqual(l1.get_prefix(), prefix)
+
+ def testNodeConstructorPrefix(self):
+ for prefix in ("xyz_", ""):
+ l1 = pytree.Leaf(100, "self")
+ l2 = pytree.Leaf(100, "foo", prefix="_")
+ n1 = pytree.Node(1000, [l1, l2], prefix=prefix)
+ self.failUnless(str(n1), prefix + "self_foo")
+ self.assertEqual(n1.get_prefix(), prefix)
+ self.assertEqual(l1.get_prefix(), prefix)
+ self.assertEqual(l2.get_prefix(), "_")
+
+ def testRemove(self):
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "foo")
+ n1 = pytree.Node(1000, [l1, l2])
+ n2 = pytree.Node(1000, [n1])
+
+ self.assertEqual(n1.remove(), 0)
+ self.assertEqual(n2.children, [])
+ self.assertEqual(l1.parent, n1)
+ self.assertEqual(n1.parent, None)
+ self.assertEqual(n2.parent, None)
+ self.failIf(n1.was_changed)
+ self.failUnless(n2.was_changed)
+
+ self.assertEqual(l2.remove(), 1)
+ self.assertEqual(l1.remove(), 0)
+ self.assertEqual(n1.children, [])
+ self.assertEqual(l1.parent, None)
+ self.assertEqual(n1.parent, None)
+ self.assertEqual(n2.parent, None)
+ self.failUnless(n1.was_changed)
+ self.failUnless(n2.was_changed)
+
+ def testRemoveParentless(self):
+ n1 = pytree.Node(1000, [])
+ n1.remove()
+ self.assertEqual(n1.parent, None)
+
+ l1 = pytree.Leaf(100, "foo")
+ l1.remove()
+ self.assertEqual(l1.parent, None)
+
+ def testNodeSetChild(self):
+ l1 = pytree.Leaf(100, "foo")
+ n1 = pytree.Node(1000, [l1])
+
+ l2 = pytree.Leaf(100, "bar")
+ n1.set_child(0, l2)
+ self.assertEqual(l1.parent, None)
+ self.assertEqual(l2.parent, n1)
+ self.assertEqual(n1.children, [l2])
+
+ n2 = pytree.Node(1000, [l1])
+ n2.set_child(0, n1)
+ self.assertEqual(l1.parent, None)
+ self.assertEqual(n1.parent, n2)
+ self.assertEqual(n2.parent, None)
+ self.assertEqual(n2.children, [n1])
+
+ self.assertRaises(IndexError, n1.set_child, 4, l2)
+ # I don't care what it raises, so long as it's an exception
+ self.assertRaises(Exception, n1.set_child, 0, list)
+
+ def testNodeInsertChild(self):
+ l1 = pytree.Leaf(100, "foo")
+ n1 = pytree.Node(1000, [l1])
+
+ l2 = pytree.Leaf(100, "bar")
+ n1.insert_child(0, l2)
+ self.assertEqual(l2.parent, n1)
+ self.assertEqual(n1.children, [l2, l1])
+
+ l3 = pytree.Leaf(100, "abc")
+ n1.insert_child(2, l3)
+ self.assertEqual(n1.children, [l2, l1, l3])
+
+ # I don't care what it raises, so long as it's an exception
+ self.assertRaises(Exception, n1.insert_child, 0, list)
+
+ def testNodeAppendChild(self):
+ n1 = pytree.Node(1000, [])
+
+ l1 = pytree.Leaf(100, "foo")
+ n1.append_child(l1)
+ self.assertEqual(l1.parent, n1)
+ self.assertEqual(n1.children, [l1])
+
+ l2 = pytree.Leaf(100, "bar")
+ n1.append_child(l2)
+ self.assertEqual(l2.parent, n1)
+ self.assertEqual(n1.children, [l1, l2])
+
+ # I don't care what it raises, so long as it's an exception
+ self.assertRaises(Exception, n1.append_child, list)
+
+ def testNodeNextSibling(self):
+ n1 = pytree.Node(1000, [])
+ n2 = pytree.Node(1000, [])
+ p1 = pytree.Node(1000, [n1, n2])
+
+ self.failUnless(n1.get_next_sibling() is n2)
+ self.assertEqual(n2.get_next_sibling(), None)
+ self.assertEqual(p1.get_next_sibling(), None)
+
+ def testLeafNextSibling(self):
+ l1 = pytree.Leaf(100, "a")
+ l2 = pytree.Leaf(100, "b")
+ p1 = pytree.Node(1000, [l1, l2])
+
+ self.failUnless(l1.get_next_sibling() is l2)
+ self.assertEqual(l2.get_next_sibling(), None)
+ self.assertEqual(p1.get_next_sibling(), None)
+
+
+class TestPatterns(support.TestCase):
+
+ """Unit tests for tree matching patterns."""
+
+ def testBasicPatterns(self):
+ # Build a tree
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "bar")
+ l3 = pytree.Leaf(100, "foo")
+ n1 = pytree.Node(1000, [l1, l2])
+ n2 = pytree.Node(1000, [l3])
+ root = pytree.Node(1000, [n1, n2])
+ # Build a pattern matching a leaf
+ pl = pytree.LeafPattern(100, "foo", name="pl")
+ r = {}
+ self.assertEqual(pl.match(root, results=r), False)
+ self.assertEqual(r, {})
+ self.assertEqual(pl.match(n1, results=r), False)
+ self.assertEqual(r, {})
+ self.assertEqual(pl.match(n2, results=r), False)
+ self.assertEqual(r, {})
+ self.assertEqual(pl.match(l1, results=r), True)
+ self.assertEqual(r, {"pl": l1})
+ r = {}
+ self.assertEqual(pl.match(l2, results=r), False)
+ self.assertEqual(r, {})
+ # Build a pattern matching a node
+ pn = pytree.NodePattern(1000, [pl], name="pn")
+ self.assertEqual(pn.match(root, results=r), False)
+ self.assertEqual(r, {})
+ self.assertEqual(pn.match(n1, results=r), False)
+ self.assertEqual(r, {})
+ self.assertEqual(pn.match(n2, results=r), True)
+ self.assertEqual(r, {"pn": n2, "pl": l3})
+ r = {}
+ self.assertEqual(pn.match(l1, results=r), False)
+ self.assertEqual(r, {})
+ self.assertEqual(pn.match(l2, results=r), False)
+ self.assertEqual(r, {})
+
+ def testWildcardPatterns(self):
+ # Build a tree for testing
+ l1 = pytree.Leaf(100, "foo")
+ l2 = pytree.Leaf(100, "bar")
+ l3 = pytree.Leaf(100, "foo")
+ n1 = pytree.Node(1000, [l1, l2])
+ n2 = pytree.Node(1000, [l3])
+ root = pytree.Node(1000, [n1, n2])
+ # Build a pattern
+ pl = pytree.LeafPattern(100, "foo", name="pl")
+ pn = pytree.NodePattern(1000, [pl], name="pn")
+ pw = pytree.WildcardPattern([[pn], [pl, pl]], name="pw")
+ r = {}
+ self.assertEqual(pw.match_seq([root], r), False)
+ self.assertEqual(r, {})
+ self.assertEqual(pw.match_seq([n1], r), False)
+ self.assertEqual(r, {})
+ self.assertEqual(pw.match_seq([n2], r), True)
+ # These are easier to debug
+ self.assertEqual(sorted(r.keys()), ["pl", "pn", "pw"])
+ self.assertEqual(r["pl"], l1)
+ self.assertEqual(r["pn"], n2)
+ self.assertEqual(r["pw"], [n2])
+ # But this is equivalent
+ self.assertEqual(r, {"pl": l1, "pn": n2, "pw": [n2]})
+ r = {}
+ self.assertEqual(pw.match_seq([l1, l3], r), True)
+ self.assertEqual(r, {"pl": l3, "pw": [l1, l3]})
+ self.assert_(r["pl"] is l3)
+ r = {}
+
+ def testGenerateMatches(self):
+ la = pytree.Leaf(1, "a")
+ lb = pytree.Leaf(1, "b")
+ lc = pytree.Leaf(1, "c")
+ ld = pytree.Leaf(1, "d")
+ le = pytree.Leaf(1, "e")
+ lf = pytree.Leaf(1, "f")
+ leaves = [la, lb, lc, ld, le, lf]
+ root = pytree.Node(1000, leaves)
+ pa = pytree.LeafPattern(1, "a", "pa")
+ pb = pytree.LeafPattern(1, "b", "pb")
+ pc = pytree.LeafPattern(1, "c", "pc")
+ pd = pytree.LeafPattern(1, "d", "pd")
+ pe = pytree.LeafPattern(1, "e", "pe")
+ pf = pytree.LeafPattern(1, "f", "pf")
+ pw = pytree.WildcardPattern([[pa, pb, pc], [pd, pe],
+ [pa, pb], [pc, pd], [pe, pf]],
+ min=1, max=4, name="pw")
+ self.assertEqual([x[0] for x in pw.generate_matches(leaves)],
+ [3, 5, 2, 4, 6])
+ pr = pytree.NodePattern(type=1000, content=[pw], name="pr")
+ matches = list(pytree.generate_matches([pr], [root]))
+ self.assertEqual(len(matches), 1)
+ c, r = matches[0]
+ self.assertEqual(c, 1)
+ self.assertEqual(str(r["pr"]), "abcdef")
+ self.assertEqual(r["pw"], [la, lb, lc, ld, le, lf])
+ for c in "abcdef":
+ self.assertEqual(r["p" + c], pytree.Leaf(1, c))
+
+ def testHasKeyExample(self):
+ pattern = pytree.NodePattern(331,
+ (pytree.LeafPattern(7),
+ pytree.WildcardPattern(name="args"),
+ pytree.LeafPattern(8)))
+ l1 = pytree.Leaf(7, "(")
+ l2 = pytree.Leaf(3, "x")
+ l3 = pytree.Leaf(8, ")")
+ node = pytree.Node(331, [l1, l2, l3])
+ r = {}
+ self.assert_(pattern.match(node, r))
+ self.assertEqual(r["args"], [l2])
+
+
+if __name__ == "__main__":
+ import __main__
+ support.run_all_tests(__main__)
diff --git a/Lib/lib2to3/tests/test_util.py b/Lib/lib2to3/tests/test_util.py
new file mode 100644
index 0000000..2c22378
--- /dev/null
+++ b/Lib/lib2to3/tests/test_util.py
@@ -0,0 +1,536 @@
+#!/usr/bin/env python2.5
+""" Test suite for the code in fixes.util """
+# Author: Collin Winter
+
+# Testing imports
+from . import support
+
+# Python imports
+import os.path
+
+# Local imports
+from .. import pytree
+from ..fixes import util
+
+
+def parse(code, strip_levels=0):
+ # The topmost node is file_input, which we don't care about.
+ # The next-topmost node is a *_stmt node, which we also don't care about
+ tree = support.parse_string(code)
+ for i in range(strip_levels):
+ tree = tree.children[0]
+ tree.parent = None
+ return tree
+
+class MacroTestCase(support.TestCase):
+ def assertStr(self, node, string):
+ if isinstance(node, (tuple, list)):
+ node = pytree.Node(util.syms.simple_stmt, node)
+ self.assertEqual(str(node), string)
+
+
+class Test_is_tuple(support.TestCase):
+ def is_tuple(self, string):
+ return util.is_tuple(parse(string, strip_levels=2))
+
+ def test_valid(self):
+ self.failUnless(self.is_tuple("(a, b)"))
+ self.failUnless(self.is_tuple("(a, (b, c))"))
+ self.failUnless(self.is_tuple("((a, (b, c)),)"))
+ self.failUnless(self.is_tuple("(a,)"))
+ self.failUnless(self.is_tuple("()"))
+
+ def test_invalid(self):
+ self.failIf(self.is_tuple("(a)"))
+ self.failIf(self.is_tuple("('foo') % (b, c)"))
+
+
+class Test_is_list(support.TestCase):
+ def is_list(self, string):
+ return util.is_list(parse(string, strip_levels=2))
+
+ def test_valid(self):
+ self.failUnless(self.is_list("[]"))
+ self.failUnless(self.is_list("[a]"))
+ self.failUnless(self.is_list("[a, b]"))
+ self.failUnless(self.is_list("[a, [b, c]]"))
+ self.failUnless(self.is_list("[[a, [b, c]],]"))
+
+ def test_invalid(self):
+ self.failIf(self.is_list("[]+[]"))
+
+
+class Test_Attr(MacroTestCase):
+ def test(self):
+ from ..fixes.util import Attr, Name
+ call = parse("foo()", strip_levels=2)
+
+ self.assertStr(Attr(Name("a"), Name("b")), "a.b")
+ self.assertStr(Attr(call, Name("b")), "foo().b")
+
+ def test_returns(self):
+ from ..fixes.util import Attr, Name
+
+ attr = Attr(Name("a"), Name("b"))
+ self.assertEqual(type(attr), list)
+
+
+class Test_Name(MacroTestCase):
+ def test(self):
+ from ..fixes.util import Name
+
+ self.assertStr(Name("a"), "a")
+ self.assertStr(Name("foo.foo().bar"), "foo.foo().bar")
+ self.assertStr(Name("a", prefix="b"), "ba")
+
+
+class Test_does_tree_import(support.TestCase):
+ def _find_bind_rec(self, name, node):
+ # Search a tree for a binding -- used to find the starting
+ # point for these tests.
+ c = util.find_binding(name, node)
+ if c: return c
+ for child in node.children:
+ c = self._find_bind_rec(name, child)
+ if c: return c
+
+ def does_tree_import(self, package, name, string):
+ node = parse(string)
+ # Find the binding of start -- that's what we'll go from
+ node = self._find_bind_rec('start', node)
+ return util.does_tree_import(package, name, node)
+
+ def try_with(self, string):
+ failing_tests = (("a", "a", "from a import b"),
+ ("a.d", "a", "from a.d import b"),
+ ("d.a", "a", "from d.a import b"),
+ (None, "a", "import b"),
+ (None, "a", "import b, c, d"))
+ for package, name, import_ in failing_tests:
+ n = self.does_tree_import(package, name, import_ + "\n" + string)
+ self.failIf(n)
+ n = self.does_tree_import(package, name, string + "\n" + import_)
+ self.failIf(n)
+
+ passing_tests = (("a", "a", "from a import a"),
+ ("x", "a", "from x import a"),
+ ("x", "a", "from x import b, c, a, d"),
+ ("x.b", "a", "from x.b import a"),
+ ("x.b", "a", "from x.b import b, c, a, d"),
+ (None, "a", "import a"),
+ (None, "a", "import b, c, a, d"))
+ for package, name, import_ in passing_tests:
+ n = self.does_tree_import(package, name, import_ + "\n" + string)
+ self.failUnless(n)
+ n = self.does_tree_import(package, name, string + "\n" + import_)
+ self.failUnless(n)
+
+ def test_in_function(self):
+ self.try_with("def foo():\n\tbar.baz()\n\tstart=3")
+
+class Test_find_binding(support.TestCase):
+ def find_binding(self, name, string, package=None):
+ return util.find_binding(name, parse(string), package)
+
+ def test_simple_assignment(self):
+ self.failUnless(self.find_binding("a", "a = b"))
+ self.failUnless(self.find_binding("a", "a = [b, c, d]"))
+ self.failUnless(self.find_binding("a", "a = foo()"))
+ self.failUnless(self.find_binding("a", "a = foo().foo.foo[6][foo]"))
+ self.failIf(self.find_binding("a", "foo = a"))
+ self.failIf(self.find_binding("a", "foo = (a, b, c)"))
+
+ def test_tuple_assignment(self):
+ self.failUnless(self.find_binding("a", "(a,) = b"))
+ self.failUnless(self.find_binding("a", "(a, b, c) = [b, c, d]"))
+ self.failUnless(self.find_binding("a", "(c, (d, a), b) = foo()"))
+ self.failUnless(self.find_binding("a", "(a, b) = foo().foo[6][foo]"))
+ self.failIf(self.find_binding("a", "(foo, b) = (b, a)"))
+ self.failIf(self.find_binding("a", "(foo, (b, c)) = (a, b, c)"))
+
+ def test_list_assignment(self):
+ self.failUnless(self.find_binding("a", "[a] = b"))
+ self.failUnless(self.find_binding("a", "[a, b, c] = [b, c, d]"))
+ self.failUnless(self.find_binding("a", "[c, [d, a], b] = foo()"))
+ self.failUnless(self.find_binding("a", "[a, b] = foo().foo[a][foo]"))
+ self.failIf(self.find_binding("a", "[foo, b] = (b, a)"))
+ self.failIf(self.find_binding("a", "[foo, [b, c]] = (a, b, c)"))
+
+ def test_invalid_assignments(self):
+ self.failIf(self.find_binding("a", "foo.a = 5"))
+ self.failIf(self.find_binding("a", "foo[a] = 5"))
+ self.failIf(self.find_binding("a", "foo(a) = 5"))
+ self.failIf(self.find_binding("a", "foo(a, b) = 5"))
+
+ def test_simple_import(self):
+ self.failUnless(self.find_binding("a", "import a"))
+ self.failUnless(self.find_binding("a", "import b, c, a, d"))
+ self.failIf(self.find_binding("a", "import b"))
+ self.failIf(self.find_binding("a", "import b, c, d"))
+
+ def test_from_import(self):
+ self.failUnless(self.find_binding("a", "from x import a"))
+ self.failUnless(self.find_binding("a", "from a import a"))
+ self.failUnless(self.find_binding("a", "from x import b, c, a, d"))
+ self.failUnless(self.find_binding("a", "from x.b import a"))
+ self.failUnless(self.find_binding("a", "from x.b import b, c, a, d"))
+ self.failIf(self.find_binding("a", "from a import b"))
+ self.failIf(self.find_binding("a", "from a.d import b"))
+ self.failIf(self.find_binding("a", "from d.a import b"))
+
+ def test_import_as(self):
+ self.failUnless(self.find_binding("a", "import b as a"))
+ self.failUnless(self.find_binding("a", "import b as a, c, a as f, d"))
+ self.failIf(self.find_binding("a", "import a as f"))
+ self.failIf(self.find_binding("a", "import b, c as f, d as e"))
+
+ def test_from_import_as(self):
+ self.failUnless(self.find_binding("a", "from x import b as a"))
+ self.failUnless(self.find_binding("a", "from x import g as a, d as b"))
+ self.failUnless(self.find_binding("a", "from x.b import t as a"))
+ self.failUnless(self.find_binding("a", "from x.b import g as a, d"))
+ self.failIf(self.find_binding("a", "from a import b as t"))
+ self.failIf(self.find_binding("a", "from a.d import b as t"))
+ self.failIf(self.find_binding("a", "from d.a import b as t"))
+
+ def test_simple_import_with_package(self):
+ self.failUnless(self.find_binding("b", "import b"))
+ self.failUnless(self.find_binding("b", "import b, c, d"))
+ self.failIf(self.find_binding("b", "import b", "b"))
+ self.failIf(self.find_binding("b", "import b, c, d", "c"))
+
+ def test_from_import_with_package(self):
+ self.failUnless(self.find_binding("a", "from x import a", "x"))
+ self.failUnless(self.find_binding("a", "from a import a", "a"))
+ self.failUnless(self.find_binding("a", "from x import *", "x"))
+ self.failUnless(self.find_binding("a", "from x import b, c, a, d", "x"))
+ self.failUnless(self.find_binding("a", "from x.b import a", "x.b"))
+ self.failUnless(self.find_binding("a", "from x.b import *", "x.b"))
+ self.failUnless(self.find_binding("a", "from x.b import b, c, a, d", "x.b"))
+ self.failIf(self.find_binding("a", "from a import b", "a"))
+ self.failIf(self.find_binding("a", "from a.d import b", "a.d"))
+ self.failIf(self.find_binding("a", "from d.a import b", "a.d"))
+ self.failIf(self.find_binding("a", "from x.y import *", "a.b"))
+
+ def test_import_as_with_package(self):
+ self.failIf(self.find_binding("a", "import b.c as a", "b.c"))
+ self.failIf(self.find_binding("a", "import a as f", "f"))
+ self.failIf(self.find_binding("a", "import a as f", "a"))
+
+ def test_from_import_as_with_package(self):
+ # Because it would take a lot of special-case code in the fixers
+ # to deal with from foo import bar as baz, we'll simply always
+ # fail if there is an "from ... import ... as ..."
+ self.failIf(self.find_binding("a", "from x import b as a", "x"))
+ self.failIf(self.find_binding("a", "from x import g as a, d as b", "x"))
+ self.failIf(self.find_binding("a", "from x.b import t as a", "x.b"))
+ self.failIf(self.find_binding("a", "from x.b import g as a, d", "x.b"))
+ self.failIf(self.find_binding("a", "from a import b as t", "a"))
+ self.failIf(self.find_binding("a", "from a import b as t", "b"))
+ self.failIf(self.find_binding("a", "from a import b as t", "t"))
+
+ def test_function_def(self):
+ self.failUnless(self.find_binding("a", "def a(): pass"))
+ self.failUnless(self.find_binding("a", "def a(b, c, d): pass"))
+ self.failUnless(self.find_binding("a", "def a(): b = 7"))
+ self.failIf(self.find_binding("a", "def d(b, (c, a), e): pass"))
+ self.failIf(self.find_binding("a", "def d(a=7): pass"))
+ self.failIf(self.find_binding("a", "def d(a): pass"))
+ self.failIf(self.find_binding("a", "def d(): a = 7"))
+
+ s = """
+ def d():
+ def a():
+ pass"""
+ self.failIf(self.find_binding("a", s))
+
+ def test_class_def(self):
+ self.failUnless(self.find_binding("a", "class a: pass"))
+ self.failUnless(self.find_binding("a", "class a(): pass"))
+ self.failUnless(self.find_binding("a", "class a(b): pass"))
+ self.failUnless(self.find_binding("a", "class a(b, c=8): pass"))
+ self.failIf(self.find_binding("a", "class d: pass"))
+ self.failIf(self.find_binding("a", "class d(a): pass"))
+ self.failIf(self.find_binding("a", "class d(b, a=7): pass"))
+ self.failIf(self.find_binding("a", "class d(b, *a): pass"))
+ self.failIf(self.find_binding("a", "class d(b, **a): pass"))
+ self.failIf(self.find_binding("a", "class d: a = 7"))
+
+ s = """
+ class d():
+ class a():
+ pass"""
+ self.failIf(self.find_binding("a", s))
+
+ def test_for(self):
+ self.failUnless(self.find_binding("a", "for a in r: pass"))
+ self.failUnless(self.find_binding("a", "for a, b in r: pass"))
+ self.failUnless(self.find_binding("a", "for (a, b) in r: pass"))
+ self.failUnless(self.find_binding("a", "for c, (a,) in r: pass"))
+ self.failUnless(self.find_binding("a", "for c, (a, b) in r: pass"))
+ self.failUnless(self.find_binding("a", "for c in r: a = c"))
+ self.failIf(self.find_binding("a", "for c in a: pass"))
+
+ def test_for_nested(self):
+ s = """
+ for b in r:
+ for a in b:
+ pass"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ for b in r:
+ for a, c in b:
+ pass"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ for b in r:
+ for (a, c) in b:
+ pass"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ for b in r:
+ for (a,) in b:
+ pass"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ for b in r:
+ for c, (a, d) in b:
+ pass"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ for b in r:
+ for c in b:
+ a = 7"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ for b in r:
+ for c in b:
+ d = a"""
+ self.failIf(self.find_binding("a", s))
+
+ s = """
+ for b in r:
+ for c in a:
+ d = 7"""
+ self.failIf(self.find_binding("a", s))
+
+ def test_if(self):
+ self.failUnless(self.find_binding("a", "if b in r: a = c"))
+ self.failIf(self.find_binding("a", "if a in r: d = e"))
+
+ def test_if_nested(self):
+ s = """
+ if b in r:
+ if c in d:
+ a = c"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ if b in r:
+ if c in d:
+ c = a"""
+ self.failIf(self.find_binding("a", s))
+
+ def test_while(self):
+ self.failUnless(self.find_binding("a", "while b in r: a = c"))
+ self.failIf(self.find_binding("a", "while a in r: d = e"))
+
+ def test_while_nested(self):
+ s = """
+ while b in r:
+ while c in d:
+ a = c"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ while b in r:
+ while c in d:
+ c = a"""
+ self.failIf(self.find_binding("a", s))
+
+ def test_try_except(self):
+ s = """
+ try:
+ a = 6
+ except:
+ b = 8"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ except:
+ a = 6"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ except KeyError:
+ pass
+ except:
+ a = 6"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ except:
+ b = 6"""
+ self.failIf(self.find_binding("a", s))
+
+ def test_try_except_nested(self):
+ s = """
+ try:
+ try:
+ a = 6
+ except:
+ pass
+ except:
+ b = 8"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ except:
+ try:
+ a = 6
+ except:
+ pass"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ except:
+ try:
+ pass
+ except:
+ a = 6"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ try:
+ b = 8
+ except KeyError:
+ pass
+ except:
+ a = 6
+ except:
+ pass"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ pass
+ except:
+ try:
+ b = 8
+ except KeyError:
+ pass
+ except:
+ a = 6"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ except:
+ b = 6"""
+ self.failIf(self.find_binding("a", s))
+
+ s = """
+ try:
+ try:
+ b = 8
+ except:
+ c = d
+ except:
+ try:
+ b = 6
+ except:
+ t = 8
+ except:
+ o = y"""
+ self.failIf(self.find_binding("a", s))
+
+ def test_try_except_finally(self):
+ s = """
+ try:
+ c = 6
+ except:
+ b = 8
+ finally:
+ a = 9"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ finally:
+ a = 6"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ finally:
+ b = 6"""
+ self.failIf(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ except:
+ b = 9
+ finally:
+ b = 6"""
+ self.failIf(self.find_binding("a", s))
+
+ def test_try_except_finally_nested(self):
+ s = """
+ try:
+ c = 6
+ except:
+ b = 8
+ finally:
+ try:
+ a = 9
+ except:
+ b = 9
+ finally:
+ c = 9"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ finally:
+ try:
+ pass
+ finally:
+ a = 6"""
+ self.failUnless(self.find_binding("a", s))
+
+ s = """
+ try:
+ b = 8
+ finally:
+ try:
+ b = 6
+ finally:
+ b = 7"""
+ self.failIf(self.find_binding("a", s))
+
+
+if __name__ == "__main__":
+ import __main__
+ support.run_all_tests(__main__)