1. zjes
  2. rope_py3k

Commits

Ali Gholami Rudi  committed d48ccd2

Handling more ASTs in patchedast

  • Participants
  • Parent commits 9a4baf0
  • Branches trunk

Comments (0)

Files changed (10)

File docs/dev/issues.txt

View file
  • Ignore whitespace
 
 Metaphor: Supporting restructurings
 
-Goals:
+Other Possible Goals:
 
-* Restructuring
 * Finding similar pieces to extracted
-* Formatting
+* Formatting?
 
 
 Hot Topics
 * Renamed function attributes; ``f.func_whatever`` to ``f.__whatever__``
 * Use new function signature
 * Not using ``__cmp__``
+* Updating integer and string literal patterns
 
 
 What Rope Assumes...

File docs/dev/workingon.txt

View file
  • Ignore whitespace
 Patched AST
 ===========
 
-- Adding `rope.refactor.patchedast`
+- ``(a + #)\nb)``
+- ``a + ((b))``
+- ``(a) + (b)``
+- Handling new string literals u''
+- Adding `_Source.consume_number()` and `_Source.consume_string()`
+- Testing different forms of strings
+- Testing different forms of numbers
+- ``(a + ')')``
+- Handling strings and docs
+- Integer literals; 10l, 0x08
+- Adding `patchedasttest` to testsuite
+- classes with no super
 
-* Refactor `_PatchedASTWalker.visitAssign()`
-* Handling string literals
-* Adding `patchedasttest` to testsuite
+* not loosing blank lines in the begining and end of the file
+
+* py3k
+
+  * metadata=? and classes
+  * function annotations
+  * // and /
 
 
 Remaining Small Stories

File rope/base/codeanalyze.py

View file
  • Ignore whitespace
         else:
             return indents
     return 0
+
+
+def get_string_pattern():
+    start = r'(\b[uU]?[rR]?)?'
+    return '|'.join([r"%s'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?" % start,
+                     r'%s"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?' % start,
+                     r"%s'[^'\\\n]*(\\.[^'\\\n]*)*'?" % start,
+                     r'%s"[^"\\\n]*(\\.[^"\\\n]*)*"?' % start])

File rope/refactor/inline.py

View file
  • Ignore whitespace
             def named_pattern(name, list_):
                 return "(?P<%s>" % name + "|".join(list_) + ")"
             comment_pattern = named_pattern('comment', [r'#[^\n]*'])
-            sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
-            dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
-            sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
-            dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
-            string_pattern = named_pattern('string', [sq3string, dq3string, sqstring, dqstring])
+            string_pattern = named_pattern('string',
+                                           [codeanalyze.get_string_pattern()])
             return_pattern = r'\b(?P<return>return)\b'
             cls._return_pattern = re.compile(comment_pattern + "|" +
                                              string_pattern + "|" +

File rope/refactor/occurrences.py

View file
  • Ignore whitespace
         self.name = name
         self.docs = docs
         self.comment_pattern = OccurrenceFinder.any('comment', [r'#[^\n]*'])
-        sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
-        dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
-        sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
-        dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
         self.string_pattern = OccurrenceFinder.any(
-            'string', [sq3string, dq3string, sqstring, dqstring])
+            'string', [codeanalyze.get_string_pattern()])
         self.pattern = self._get_occurrence_pattern(self.name)
 
     def find_occurrences(self, resource=None, pymodule=None):

File rope/refactor/patchedast.py

View file
  • Ignore whitespace
-import compiler
+import compiler.ast
+import compiler.consts
+import re
+
+from rope.base import codeanalyze
 
 
 def get_patched_ast(source):
     def __init__(self, source):
         self.source = _Source(source)
 
+    Number = object()
+    String = object()
+
     def __call__(self, node):
         method = getattr(self, 'visit' + node.__class__.__name__, None)
         if method is not None:
             return method(node)
         # ???: Unknown node; What should we do here?
         raise RuntimeError('Unknown node type <%s>' %
-                           type(node).__name__)
+                           node.__class__.__name__)
 
-    def visitAssign(self, node):
+    def _handle(self, node, base_children):
         children = []
-        start = self.source.offset
-        offset = self.source.offset
-        for child in node.nodes:
-            call_for_nodes(child, self)
-            children.append(self.source.get(offset, child.region[0]))
+        suspected_start = self.source.offset
+        start = suspected_start
+        first_token = True
+        for child in base_children:
+            if child is None:
+                continue
+            offset = self.source.offset
+            if isinstance(child, compiler.ast.Node):
+                call_for_nodes(child, self)
+                token_start = child.region[0]
+            else:
+                if child is self.String:
+                    region = self.source.consume_string()
+                elif child is self.Number:
+                    region = self.source.consume_number()
+                else:
+                    region = self.source.consume(child)
+                child = self.source.get(region[0], region[1])
+                token_start = region[0]
+            if not first_token:
+                children.append(self.source.get(offset, token_start))
+            else:
+                first_token = False
+                start = token_start
             children.append(child)
-            children.append(self.source.till_token('='))
-            self.source.consume('=')
-            children.append('=')
-            offset = self.source.offset
-        call_for_nodes(node.expr, self)
-        children.append(self.source.get(offset, node.expr.region[0]))
-        children.append(node.expr)
+        start = self._handle_parens(children, start)
         node.sorted_children = children
         node.region = (start, self.source.offset)
 
+    def _handle_parens(self, children, start):
+        """Changes `children` and returns new start"""
+        opens, closes = self._count_needed_parens(children)
+        for i in range(closes):
+            token_start, token_end = self.source.consume(')')
+            children.append(self.source.get(token_start, token_end))
+        for i in range(opens):
+            new_start = self.source.find_backwards('(', start)
+            children.insert(0, self.source.get(new_start, start))
+            start = new_start
+        return start
+
+    def _count_needed_parens(self, children):
+        start = 0
+        opens = 0
+        for child in children:
+            if not isinstance(child, basestring):
+                continue
+            if child == '' or child[0] in '\'"':
+                continue
+            index = 0
+            while index < len(child):
+                if child[index] == ')':
+                    if opens > 0:
+                        opens -= 1
+                    else:
+                        start += 1
+                if child[index] == '(':
+                    opens += 1
+                if child[index] == '#':
+                    try:
+                        index = child.index('\n', index)
+                    except ValueError:
+                        break
+                index += 1
+        return start, opens
+
+    def visitAdd(self, node):
+        self._handle(node, [node.left, '+', node.right])
+
+    def visitAnd(self, node):
+        self._handle(node, self._child_nodes(node.nodes, 'and'))
+
+    def visitAssAttr(self, node):
+        self._handle(node, [node.expr, '.', node.attrname])
+
+    def visitAssList(self, node):
+        children = self._child_nodes(node.nodes, ',')
+        children.insert(0, '[')
+        children.append(']')
+        self._handle(node, children)
+
     def visitAssName(self, node):
-        name = str(node.name)
-        node.region = self.source.consume(name)
-        node.sorted_children = [name]
+        self._handle(node, [node.name])
+
+    def visitAssTuple(self, node):
+        self._handle_tuple(node)
+
+    def visitAssert(self, node):
+        children = ['assert', node.test]
+        if node.fail:
+            children.append(',')
+            children.append(node.fail)
+        self._handle(node, children)
+
+    def visitAssign(self, node):
+        children = self._child_nodes(node.nodes, '=')
+        children.append('=')
+        children.append(node.expr)
+        self._handle(node, children)
+
+    def visitAugAssign(self, node):
+        self._handle(node, [node.node, node.op, node.expr])
+
+    def visitBackquote(self, node):
+        self._handle(node, ['`', node.expr, '`'])
+
+    def visitBitand(self, node):
+        self._handle(node, self._child_nodes(node.nodes, '&'))
+
+    def visitBitor(self, node):
+        self._handle(node, self._child_nodes(node.nodes, '|'))
+
+    def visitBitxor(self, node):
+        self._handle(node, self._child_nodes(node.nodes, '^'))
+
+    def visitBreak(self, node):
+        self._handle(node, ['break'])
+
+    def visitCallFunc(self, node):
+        children = []
+        children.append(node.node)
+        children.append('(')
+        children.extend(self._child_nodes(node.args, ','))
+        if node.star_args is not None:
+            children.extend([',', '*', node.star_args])
+        if node.dstar_args is not None:
+            children.extend([',', '**', node.dstar_args])
+        children.append(')')
+        self._handle(node, children)
+
+    def visitClass(self, node):
+        children = []
+        children.extend(['class', node.name])
+        if node.bases:
+            children.append('(')
+            children.extend(self._child_nodes(node.bases, ','))
+            children.append(')')
+        children.append(':')
+        if node.doc is not None:
+            children.append(self.String)
+        children.append(node.code)
+        self._handle(node, children)
+
+    def visitCompare(self, node):
+        children = []
+        children.append(node.expr)
+        for op, child in node.ops:
+            children.append(op)
+            children.append(child)
+        self._handle(node, children)
 
     def visitConst(self, node):
-        value = str(node.value)
-        node.region = self.source.consume(value)
-        node.sorted_children = [value]
+        value = repr(node.value)
+        if isinstance(node.value, (int, long, float, complex)):
+            value = self.Number
+        if isinstance(node.value, basestring):
+            value = self.String
+        self._handle(node, [value])
+
+    def visitContinue(self, node):
+        self._handle(node, ['continue'])
+
+    def visitDecorators(self, node):
+        self._handle(node, ['@'] + self._child_nodes(node.nodes, '@'))
+
+    def visitDict(self, node):
+        children = []
+        children.append('{')
+        for index, (key, value) in enumerate(node.items):
+            children.extend([key, ':', value])
+            if index < len(node.items) - 1:
+                children.append(',')
+        children.append('}')
+        self._handle(node, children)
+
+    def visitDiscard(self, node):
+        self._handle(node, [node.expr])
+
+    def visitDiv(self, node):
+        self._handle(node, [node.left, '/', node.right])
+
+    def visitEllipsis(self, node):
+        self._handle(node, ['...'])
+
+    def visitExpression(self, node):
+        self._handle(node, [node.node])
+
+    def visitExec(self, node):
+        children = []
+        children.extend(['exec', node.expr])
+        if node.locals:
+            children.extend(['in', node.locals])
+        if node.globals:
+            children.extend([',', node.globals])
+        self._handle(node, children)
+
+    def visitFloorDiv(self, node):
+        self._handle(node, [node.left, '//', node.right])
+
+    def visitFor(self, node):
+        children = ['for', node.assign, 'in', node.list, ':', node.body]
+        if node.else_:
+            children.extend(['else', ':', node.else_])
+        self._handle(node, children)
+
+    def visitFrom(self, node):
+        children = ['from']
+        if hasattr(node, 'level') and node.level > 0:
+            children.append('.' * node.level)
+        children.extend([node.modname, 'import'])
+        for index, (name, alias) in enumerate(node.names):
+            children.append(name)
+            if alias is not None:
+                children.extend(['as', alias])
+            if index < len(node.names) - 1:
+                children.append(',')
+        self._handle(node, children)
+
+    def visitFunction(self, node):
+        children = []
+        if node.decorators:
+            children.append(node.decorators)
+        children.extend(['def', node.name, '('])
+        args = list(node.argnames)
+        dstar_args = None
+        if node.flags & compiler.consts.CO_VARKEYWORDS:
+            dstar_args = args.pop()
+        star_args = None
+        if node.flags & compiler.consts.CO_VARARGS:
+            star_args = args.pop()
+        defaults = [None] * (len(args) - len(node.defaults)) + list(node.defaults)
+        for arg, default in zip(args[:-1], defaults[:-1]):
+            self._add_args_to_children(children, arg, default)
+            children.append(',')
+        if args:
+            self._add_args_to_children(children, args[-1], defaults[-1])
+        if star_args is not None:
+            if args:
+                children.append(',')
+            children.extend(['*', star_args])
+        if dstar_args is not None:
+            if args:
+                children.append(',')
+            children.extend(['**', dstar_args])
+        children.extend([')', ':'])
+        if node.doc:
+            children.append(self.String)
+        children.append(node.code)
+        self._handle(node, children)
+
+    def _add_args_to_children(self, children, arg, default):
+        children.append(arg)
+        if default is not None:
+            children.append('=')
+            children.append(default)
+
+    def visitKeyword(self, node):
+        self._handle(node, [node.name, '=', node.expr])
 
     def visitModule(self, node):
-        call_for_nodes(node.node, self)
+        doc = None
+        if node.doc is not None:
+            doc = self.String
+        self._handle(node, [doc, node.node])
+
+    def visitName(self, node):
+        self._handle(node, [node.name])
+
+    def visitPass(self, node):
+        self._handle(node, ['pass'])
 
     def visitStmt(self, node):
-        for child in node.nodes:
-            call_for_nodes(child, self)
+        self._handle(node, node.nodes)
+
+    def visitTuple(self, node):
+        self._handle_tuple(node)
+
+    def _handle_tuple(self, node):
+        self._handle(node, self._child_nodes(node.nodes, ','))
+
+    def _child_nodes(self, nodes, separator):
+        children = []
+        for index, child in enumerate(nodes):
+            children.append(child)
+            if index < len(nodes) - 1:
+                children.append(separator)
+        return children
 
 
 class _Source(object):
         self.offset = new_offset + len(token)
         return (new_offset, self.offset)
 
+    def consume_string(self):
+        if _Source._string_pattern is None:
+            original = codeanalyze.get_string_pattern()
+            pattern = r'(%s)((\s|\\\n)*(%s))*' % (original, original)
+            _Source._string_pattern = re.compile(pattern)
+        repattern = _Source._string_pattern
+        return self._consume_pattern(repattern)
+
+    def consume_number(self):
+        if _Source._number_pattern is None:
+            _Source._number_pattern = re.compile(
+                self._get_number_pattern())
+        repattern = _Source._number_pattern
+        return self._consume_pattern(repattern)
+
+    def _consume_pattern(self, repattern):
+        match = repattern.search(self.source, self.offset)
+        self.offset = match.end()
+        return match.start(), match.end()
+
     def till_token(self, token):
         new_offset = self.source.index(token, self.offset)
         return self.get(self.offset, new_offset)
 
     def from_offset(self, offset):
         return self.get(offset, self.offset)
+
+    def find_backwards(self, pattern, offset):
+        return self.source.rindex(pattern, 0, offset)
+
+    def __getitem__(self, index):
+        return self.source[index]
+
+    def __getslice__(self, i, j):
+        return self.source[i:j]
+
+    def _get_number_pattern(self):
+        # We should handle integer, long_integer, others, imagnumber
+        # HACK: An approaximation does the job
+        integer = r'(0|0x)?[\da-fA-F]+[lL]?'
+        return r'(%s(\.\d*)?|(\.\d+))([eE][-+]?\d*)?[jJ]?' % integer
+
+    _string_pattern = None
+    _number_pattern = None

File rope/ui/highlighter.py

View file
  • Ignore whitespace
         builtin_pattern = r"([^.'\"\\]\b|^)" + \
                           PythonHighlighting.any("builtin", builtinlist) + r"\b"
         comment_pattern = PythonHighlighting.any("comment", [r"#[^\n]*"])
-        sqstring = r"(\b[rR])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
-        dqstring = r'(\b[rR])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
-        sq3string = r"(\b[rR])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
-        dq3string = r'(\b[rR])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
         string_pattern = PythonHighlighting.any(
-                       "string", [sq3string, dq3string, sqstring, dqstring])
+                       "string", [rope.base.codeanalyze.get_string_pattern()])
         definition_pattern = r'^\s*(?P<defkeyword>def|class)\s+(?P<definition>\w+)'
         all_patterns = definition_pattern + '|' + keyword_pattern + '|' + \
                        builtin_pattern + '|' + comment_pattern + '|' + string_pattern

File ropetest/codeanalyzetest.py

View file
  • Ignore whitespace
 from rope.base import exceptions
 from rope.base.codeanalyze import (StatementRangeFinder, ArrayLinesAdapter,
                                    SourceLinesAdapter, WordRangeFinder,
-                                   ScopeNameFinder, LogicalLineFinder,
-                                   get_block_start)
+                                   ScopeNameFinder, LogicalLineFinder, get_block_start)
 from ropetest import testutils
 
 
 
 if __name__ == '__main__':
     unittest.main()
-

File ropetest/refactor/__init__.py

View file
  • Ignore whitespace
 import ropetest.refactor.inlinetest
 import ropetest.refactor.movetest
 import ropetest.refactor.renametest
+import ropetest.refactor.patchedasttest
 from rope.base.exceptions import RefactoringError, InterruptedTaskError
 from rope.refactor.encapsulate_field import EncapsulateField
 from rope.refactor.introduce_factory import IntroduceFactoryRefactoring
     result.addTests(unittest.makeSuite(
                     ropetest.refactor.movetest.MoveRefactoringTest))
     result.addTests(ropetest.refactor.inlinetest.suite())
+    result.addTests(unittest.makeSuite(
+                    ropetest.refactor.patchedasttest.PatchedASTTest))
     result.addTests(unittest.makeSuite(EncapsulateFieldTest))
     result.addTests(unittest.makeSuite(LocalToFieldTest))
     result.addTests(unittest.makeSuite(

File ropetest/refactor/patchedasttest.py

View file
  • Ignore whitespace
         start = source.index('a')
         checker.check_region('Assign', 0, len(source) - 1)
         checker.check_children(
-            'Assign', ['', 'AssName', ' ', '=', ' ', 'Const'])
+            'Assign', ['AssName', ' ', '=', ' ', 'Const'])
+
+    def test_add_node(self):
+        source = '1 + 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Add', 0, len(source) - 1)
+        checker.check_children(
+            'Add', ['Const(1)', ' ', '+', ' ', 'Const(2)'])
+
+    def test_and_node(self):
+        source = 'True and True\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('And', 0, len(source) - 1)
+        checker.check_children(
+            'And', ['Name', ' ', 'and', ' ', 'Name'])
+
+    def test_basic_closing_parens(self):
+        source = '1 + (2)\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        two_start = source.index('2')
+        checker.check_region('Const(2)', two_start, two_start + 1)
+        checker.check_children('Const(2)', ['2'])
+        checker.check_region('Add', 0, len(source) - 1)
+        checker.check_children(
+            'Add', ['Const(1)', ' ', '+', ' (', 'Const(2)', ')'])
+
+    def test_basic_opening_parens(self):
+        source = '(1) + 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Const(1)', 1, 2)
+        checker.check_children('Const(1)', ['1'])
+        checker.check_region('Add', 0, len(source) - 1)
+        checker.check_children(
+            'Add', ['(', 'Const(1)', ') ', '+', ' ', 'Const(2)'])
+
+    def test_basic_opening_biway(self):
+        source = '(1) + (2)\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Add', 0, len(source) - 1)
+        checker.check_children(
+            'Add', ['(', 'Const(1)', ') ', '+', ' (', 'Const(2)', ')'])
+
+    def test_basic_opening_double(self):
+        source = '1 + ((2))\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Add', 0, len(source) - 1)
+        checker.check_children(
+            'Add', ['Const(1)', ' ', '+', ' ((', 'Const(2)', ')', ')'])
+
+    def test_handling_comments(self):
+        source = '(1 + #(\n2)\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_children(
+            'Add', ['Const(1)', ' ', '+', ' #(\n', 'Const(2)'])
+
+    def test_handling_strings(self):
+        source = '1 + "("\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_children(
+            'Add', ['Const(1)', ' ', '+', ' ', 'Const'])
+
+    def test_handling_implicit_string_concatenation(self):
+        source = "a = '1''2'"
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_children(
+            'Assign', ['AssName' , ' ', '=', ' ', "Const('12')"])
+        checker.check_children('Const', ["'1''2'"])
+
+    def test_handling_implicit_string_concatenation_line_breaks(self):
+        source = "a = '1' \\\n'2'"
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_children(
+            'Assign', ['AssName' , ' ', '=', ' ', "Const('12')"])
+        checker.check_children('Const', ["'1' \\\n'2'"])
+
+    def test_long_integer_literals(self):
+        source = "0x1L + a"
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_children(
+            'Add', ['Const' , ' ', '+', ' ', 'Name'])
+        checker.check_children('Const', ['0x1L'])
+
+    def test_complex_number_literals(self):
+        source = "1.0e2j + a"
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_children(
+            'Add', ['Const' , ' ', '+', ' ', 'Name'])
+        checker.check_children('Const', ['1.0e2j'])
+
+    def test_ass_attr_node(self):
+        source = 'a.b = 1\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('AssAttr', 0, source.index('=') - 1)
+        checker.check_children('AssAttr', ['Name', '', '.', '', 'b'])
+
+    def test_ass_list_node(self):
+        source = '[a, b] = 1, 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('AssList', 0, source.index(']') + 1)
+        checker.check_children('AssList', ['[', '', 'AssName', '', ',',
+                                           ' ', 'AssName', '', ']'])
+
+    def test_ass_tuple(self):
+        source = 'a, b = 1, 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('AssTuple', 0, source.index('=') - 1)
+        checker.check_children(
+            'AssTuple', ['AssName', '', ',', ' ', 'AssName'])
+
+    def test_ass_tuple2(self):
+        source = '(a, b) = 1, 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('AssTuple', 1, source.index('=') - 2)
+        checker.check_children(
+            'AssTuple', ['AssName', '', ',', ' ', 'AssName'])
+
+    def test_assert(self):
+        source = 'assert True\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Assert', 0, len(source) - 1)
+        checker.check_children(
+            'Assert', ['assert', ' ', 'Name'])
+
+    def test_assert2(self):
+        source = 'assert True, "error"\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Assert', 0, len(source) - 1)
+        checker.check_children(
+            'Assert', ['assert', ' ', 'Name', '', ',', ' ', 'Const'])
+
+    def test_aug_assign_node(self):
+        source = 'a += 1\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        start = source.index('a')
+        checker.check_region('AugAssign', 0, len(source) - 1)
+        checker.check_children(
+            'AugAssign', ['Name', ' ', '+=', ' ', 'Const'])
+
+    def test_back_quotenode(self):
+        source = '`1`\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Backquote', 0, len(source) - 1)
+        checker.check_children(
+            'Backquote', ['`', '', 'Const(1)', '', '`'])
+
+    def test_bitand(self):
+        source = '1 & 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Bitand', 0, len(source) - 1)
+        checker.check_children(
+            'Bitand', ['Const(1)', ' ', '&', ' ', 'Const(2)'])
+
+    def test_bitor(self):
+        source = '1 | 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Bitor', 0, len(source) - 1)
+        checker.check_children(
+            'Bitor', ['Const(1)', ' ', '|', ' ', 'Const(2)'])
+
+    def test_call_func(self):
+        source = 'f(1, 2)\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('CallFunc', 0, len(source) - 1)
+        checker.check_children(
+            'CallFunc', ['Name', '', '(', '', 'Const(1)', '', ',',
+                         ' ', 'Const(2)', '', ')'])
+
+    def test_call_func_and_keywords(self):
+        source = 'f(1, p=2)\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_children(
+            'CallFunc', ['Name', '', '(', '', 'Const(1)', '', ',',
+                         ' ', 'Keyword', '', ')'])
+
+    def test_call_func_and_start_args(self):
+        source = 'f(1, *args)\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_children(
+            'CallFunc', ['Name', '', '(', '', 'Const(1)', '', ',',
+                         ' ', '*', '', 'Name', '', ')'])
+
+    def test_class_node(self):
+        source = 'class A(object):\n    """class docs"""\n    pass\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Class', 0, len(source) - 1)
+        checker.check_children(
+            'Class', ['class', ' ', 'A', '', '(', '', 'Name', '', ')',
+                      '', ':', '\n    ', '"""class docs"""', '\n    ', 'Stmt'])
+
+    def test_class_with_no_bases(self):
+        source = 'class A:\n    pass\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Class', 0, len(source) - 1)
+        checker.check_children(
+            'Class', ['class', ' ', 'A', '', ':', '\n    ', 'Stmt'])
+
+    def test_simple_compare(self):
+        source = '1 < 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Compare', 0, len(source) - 1)
+        checker.check_children(
+            'Compare', ['Const(1)', ' ', '<', ' ', 'Const(2)'])
+
+    def test_multiple_compare(self):
+        source = '1 < 2 <= 3\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Compare', 0, len(source) - 1)
+        checker.check_children(
+            'Compare', ['Const(1)', ' ', '<', ' ', 'Const(2)', ' ',
+                        '<=', ' ', 'Const(3)'])
+
+    def test_decorators_node(self):
+        source = '@d\ndef f():\n    pass\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Decorator', 0, 2)
+        checker.check_children('Decorator', ['@', '', 'Name'])
+
+    def test_function_node(self):
+        source = 'def f():\n    pass\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Function', 0, len(source) - 1)
+        checker.check_children('Function', ['def', ' ', 'f', '', '(', '',
+                                            ')', '', ':', '\n    ', 'Stmt'])
+
+    def test_function_node2(self):
+        source = 'def f(p1, **p2):\n    """docs"""\n    pass\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Function', 0, len(source) - 1)
+        checker.check_children(
+            'Function', ['def', ' ', 'f', '', '(', '', 'p1', '', ',',
+                         ' ', '**', '', 'p2', '', ')', '', ':', '\n    ',
+                         '"""docs"""', '\n    ', 'Stmt'])
+
+    def test_dict_node(self):
+        source = '{1: 2, 3: 4}\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Dict', 0, len(source) - 1)
+        checker.check_children(
+            'Dict', ['{', '', 'Const(1)', '', ':', ' ', 'Const(2)', '', ',',
+                     ' ', 'Const(3)', '', ':', ' ', 'Const(4)', '', '}'])
+
+    def test_div_node(self):
+        source = '1 / 2\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Div', 0, len(source) - 1)
+        checker.check_children('Div', ['Const(1)', ' ', '/', ' ', 'Const(2)'])
+
+    def test_simple_exec_node(self):
+        source = 'exec ""\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Exec', 0, len(source) - 1)
+        checker.check_children('Exec', ['exec', ' ', 'Const'])
+
+    def test_exec_node(self):
+        source = 'exec "" in locals(), globals()\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('Exec', 0, len(source) - 1)
+        checker.check_children(
+            'Exec', ['exec', ' ', 'Const', ' ', 'in',
+                     ' ', 'CallFunc', '', ',', ' ', 'CallFunc'])
+
+    def test_for_node(self):
+        source = 'for i in range(1):\n    pass\nelse:\n    pass\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('For', 0, len(source) - 1)
+        checker.check_children(
+            'For', ['for', ' ', 'AssName', ' ', 'in', ' ', 'CallFunc', '',
+                    ':', '\n    ', 'Stmt', '\n',
+                    'else', '', ':', '\n    ', 'Stmt'])
+
+    def test_normal_from_node(self):
+        source = 'from x import y\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('From', 0, len(source) - 1)
+        checker.check_children(
+            'From', ['from', ' ', 'x', ' ', 'import', ' ', 'y'])
+
+    def test_from_node(self):
+        source = 'from ..x import y as z\n'
+        ast = patchedast.get_patched_ast(source)
+        checker = _ResultChecker(self, ast)
+        checker.check_region('From', 0, len(source) - 1)
+        checker.check_children(
+            'From', ['from', ' ', '..', '', 'x', ' ', 'import', ' ', 'y',
+                     ' ', 'as', ' ', 'z'])
 
 
 class _ResultChecker(object):
 
     def check_children(self, text, children):
         node = self._find_node(text)
-        result = list(node.sorted_children)
+        try:
+            result = list(node.sorted_children)
+        except:
+            raise
         self.test_case.assertEquals(len(children), len(result))
         for expected, child in zip(children, result):
             if isinstance(child, basestring):