# -*- coding: utf-8 -*- from textwrap import dedent import logging import pytest from parso.utils import split_lines from parso import cache from parso import load_grammar from parso.python.diff import DiffParser, _assert_valid_graph, _assert_nodes_are_equal from parso import parse ANY = object() def test_simple(): """ The diff parser reuses modules. So check for that. """ grammar = load_grammar() module_a = grammar.parse('a', diff_cache=True) assert grammar.parse('b', diff_cache=True) == module_a def _check_error_leaves_nodes(node): if node.type in ('error_leaf', 'error_node'): return node try: children = node.children except AttributeError: pass else: for child in children: x_node = _check_error_leaves_nodes(child) if x_node is not None: return x_node return None class Differ: grammar = load_grammar() def initialize(self, code): logging.debug('differ: initialize') try: del cache.parser_cache[self.grammar._hashed][None] except KeyError: pass self.lines = split_lines(code, keepends=True) self.module = parse(code, diff_cache=True, cache=True) assert code == self.module.get_code() _assert_valid_graph(self.module) return self.module def parse(self, code, copies=0, parsers=0, expect_error_leaves=False): logging.debug('differ: parse copies=%s parsers=%s', copies, parsers) lines = split_lines(code, keepends=True) diff_parser = DiffParser( self.grammar._pgen_grammar, self.grammar._tokenizer, self.module, ) new_module = diff_parser.update(self.lines, lines) self.lines = lines assert code == new_module.get_code() _assert_valid_graph(new_module) without_diff_parser_module = parse(code) _assert_nodes_are_equal(new_module, without_diff_parser_module) error_node = _check_error_leaves_nodes(new_module) assert expect_error_leaves == (error_node is not None), error_node if parsers is not ANY: assert diff_parser._parser_count == parsers if copies is not ANY: assert diff_parser._copy_count == copies return new_module @pytest.fixture() def differ(): return Differ() def test_change_and_undo(differ): func_before = 'def func():\n pass\n' # Parse the function and a. differ.initialize(func_before + 'a') # Parse just b. differ.parse(func_before + 'b', copies=1, parsers=2) # b has changed to a again, so parse that. differ.parse(func_before + 'a', copies=1, parsers=2) # Same as before parsers should not be used. Just a simple copy. differ.parse(func_before + 'a', copies=1) # Now that we have a newline at the end, everything is easier in Python # syntax, we can parse once and then get a copy. differ.parse(func_before + 'a\n', copies=1, parsers=2) differ.parse(func_before + 'a\n', copies=1) # Getting rid of an old parser: Still no parsers used. differ.parse('a\n', copies=1) # Now the file has completely changed and we need to parse. differ.parse('b\n', parsers=1) # And again. differ.parse('a\n', parsers=1) def test_positions(differ): func_before = 'class A:\n pass\n' m = differ.initialize(func_before + 'a') assert m.start_pos == (1, 0) assert m.end_pos == (3, 1) m = differ.parse('a', copies=1) assert m.start_pos == (1, 0) assert m.end_pos == (1, 1) m = differ.parse('a\n\n', parsers=1) assert m.end_pos == (3, 0) m = differ.parse('a\n\n ', copies=1, parsers=2) assert m.end_pos == (3, 1) m = differ.parse('a ', parsers=1) assert m.end_pos == (1, 2) def test_if_simple(differ): src = dedent('''\ if 1: a = 3 ''') else_ = "else:\n a = ''\n" differ.initialize(src + 'a') differ.parse(src + else_ + "a", copies=0, parsers=1) differ.parse(else_, parsers=2, expect_error_leaves=True) differ.parse(src + else_, parsers=1) def test_func_with_for_and_comment(differ): # The first newline is important, leave it. It should not trigger another # parser split. src = dedent("""\ def func(): pass for a in [1]: # COMMENT a""") differ.initialize(src) differ.parse('a\n' + src, copies=1, parsers=3) def test_one_statement_func(differ): src = dedent("""\ first def func(): a """) differ.initialize(src + 'second') differ.parse(src + 'def second():\n a', parsers=1, copies=1) def test_for_on_one_line(differ): src = dedent("""\ foo = 1 for x in foo: pass def hi(): pass """) differ.initialize(src) src = dedent("""\ def hi(): for x in foo: pass pass pass """) differ.parse(src, parsers=2) src = dedent("""\ def hi(): for x in foo: pass pass def nested(): pass """) # The second parser is for parsing the `def nested()` which is an `equal` # operation in the SequenceMatcher. differ.parse(src, parsers=1, copies=1) def test_open_parentheses(differ): func = 'def func():\n a\n' code = 'isinstance(\n\n' + func new_code = 'isinstance(\n' + func differ.initialize(code) differ.parse(new_code, parsers=1, expect_error_leaves=True) new_code = 'a = 1\n' + new_code differ.parse(new_code, parsers=2, expect_error_leaves=True) func += 'def other_func():\n pass\n' differ.initialize('isinstance(\n' + func) # Cannot copy all, because the prefix of the function is once a newline and # once not. differ.parse('isinstance()\n' + func, parsers=2, copies=1) def test_open_parentheses_at_end(differ): code = "a['" differ.initialize(code) differ.parse(code, parsers=1, expect_error_leaves=True) def test_backslash(differ): src = dedent(r""" a = 1\ if 1 else 2 def x(): pass """) differ.initialize(src) src = dedent(r""" def x(): a = 1\ if 1 else 2 def y(): pass """) differ.parse(src, parsers=1) src = dedent(r""" def first(): if foo \ and bar \ or baz: pass def second(): pass """) differ.parse(src, parsers=2) def test_full_copy(differ): code = 'def foo(bar, baz):\n pass\n bar' differ.initialize(code) differ.parse(code, copies=1) def test_wrong_whitespace(differ): code = ''' hello ''' differ.initialize(code) differ.parse(code + 'bar\n ', parsers=2, expect_error_leaves=True) code += """abc(\npass\n """ differ.parse(code, parsers=2, expect_error_leaves=True) def test_issues_with_error_leaves(differ): code = dedent(''' def ints(): str.. str ''') code2 = dedent(''' def ints(): str. str ''') differ.initialize(code) differ.parse(code2, parsers=1, expect_error_leaves=True) def test_unfinished_nodes(differ): code = dedent(''' class a(): def __init__(self, a): self.a = a def p(self): a(1) ''') code2 = dedent(''' class a(): def __init__(self, a): self.a = a def p(self): self a(1) ''') differ.initialize(code) differ.parse(code2, parsers=2, copies=2) def test_nested_if_and_scopes(differ): code = dedent(''' class a(): if 1: def b(): 2 ''') code2 = code + ' else:\n 3' differ.initialize(code) differ.parse(code2, parsers=1, copies=0) def test_word_before_def(differ): code1 = 'blub def x():\n' code2 = code1 + ' s' differ.initialize(code1) differ.parse(code2, parsers=1, copies=0, expect_error_leaves=True) def test_classes_with_error_leaves(differ): code1 = dedent(''' class X(): def x(self): blablabla assert 3 self. class Y(): pass ''') code2 = dedent(''' class X(): def x(self): blablabla assert 3 str( class Y(): pass ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) def test_totally_wrong_whitespace(differ): code1 = ''' class X(): raise n class Y(): pass ''' code2 = ''' class X(): raise n str( class Y(): pass ''' differ.initialize(code1) differ.parse(code2, parsers=2, copies=0, expect_error_leaves=True) def test_node_insertion(differ): code1 = dedent(''' class X(): def y(self): a = 1 b = 2 c = 3 d = 4 ''') code2 = dedent(''' class X(): def y(self): a = 1 b = 2 str c = 3 d = 4 ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=2) def test_whitespace_at_end(differ): code = dedent('str\n\n') differ.initialize(code) differ.parse(code + '\n', parsers=1, copies=1) def test_endless_while_loop(differ): """ This was a bug in Jedi #878. """ code = '#dead' differ.initialize(code) module = differ.parse(code, parsers=1) assert module.end_pos == (1, 5) code = '#dead\n' differ.initialize(code) module = differ.parse(code + '\n', parsers=1) assert module.end_pos == (3, 0) def test_in_class_movements(differ): code1 = dedent("""\ class PlaybookExecutor: p b def run(self): 1 try: x except: pass """) code2 = dedent("""\ class PlaybookExecutor: b def run(self): 1 try: x except: pass """) differ.initialize(code1) differ.parse(code2, parsers=1) def test_in_parentheses_newlines(differ): code1 = dedent(""" x = str( True) a = 1 def foo(): pass b = 2""") code2 = dedent(""" x = str(True) a = 1 def foo(): pass b = 2""") differ.initialize(code1) differ.parse(code2, parsers=1, copies=1) def test_indentation_issue(differ): code1 = dedent(""" import module """) code2 = dedent(""" class L1: class L2: class L3: def f(): pass def f(): pass def f(): pass def f(): pass """) differ.initialize(code1) differ.parse(code2, parsers=2) def test_endmarker_newline(differ): code1 = dedent('''\ docu = None # some comment result = codet incomplete_dctassign = { "module" if "a": x = 3 # asdf ''') code2 = code1.replace('codet', 'coded') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) def test_newlines_at_end(differ): differ.initialize('a\n\n') differ.parse('a\n', copies=1) def test_end_newline_with_decorator(differ): code = dedent('''\ @staticmethod def spam(): import json json.l''') differ.initialize(code) module = differ.parse(code + '\n', copies=1, parsers=1) decorated, endmarker = module.children assert decorated.type == 'decorated' decorator, func = decorated.children suite = func.children[-1] assert suite.type == 'suite' newline, first_stmt, second_stmt = suite.children assert first_stmt.get_code() == ' import json\n' assert second_stmt.get_code() == ' json.l\n' def test_invalid_to_valid_nodes(differ): code1 = dedent('''\ def a(): foo = 3 def b(): la = 3 else: la return foo base ''') code2 = dedent('''\ def a(): foo = 3 def b(): la = 3 if foo: latte = 3 else: la return foo base ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=3) def test_if_removal_and_reappearence(differ): code1 = dedent('''\ la = 3 if foo: latte = 3 else: la pass ''') code2 = dedent('''\ la = 3 latte = 3 else: la pass ''') code3 = dedent('''\ la = 3 if foo: latte = 3 else: la ''') differ.initialize(code1) differ.parse(code2, parsers=3, copies=2, expect_error_leaves=True) differ.parse(code1, parsers=1, copies=1) differ.parse(code3, parsers=1, copies=1) def test_add_error_indentation(differ): code = 'if x:\n 1\n' differ.initialize(code) differ.parse(code + ' 2\n', parsers=1, copies=0, expect_error_leaves=True) def test_differing_docstrings(differ): code1 = dedent('''\ def foobar(x, y): 1 return x def bazbiz(): foobar() lala ''') code2 = dedent('''\ def foobar(x, y): 2 return x + y def bazbiz(): z = foobar() lala ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1) differ.parse(code1, parsers=2, copies=1) def test_one_call_in_function_change(differ): code1 = dedent('''\ def f(self): mro = [self] for a in something: yield a def g(self): return C( a=str, b=self, ) ''') code2 = dedent('''\ def f(self): mro = [self] def g(self): return C( a=str, t b=self, ) ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=2, copies=1) def test_function_deletion(differ): code1 = dedent('''\ class C(list): def f(self): def iterate(): for x in b: break return list(iterate()) ''') code2 = dedent('''\ class C(): def f(self): for x in b: break return list(iterate()) ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=0, expect_error_leaves=True) differ.parse(code1, parsers=1, copies=0) def test_docstring_removal(differ): code1 = dedent('''\ class E(Exception): """ 1 2 3 """ class S(object): @property def f(self): return cmd def __repr__(self): return cmd2 ''') code2 = dedent('''\ class E(Exception): """ 1 3 """ class S(object): @property def f(self): return cmd return cmd2 ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=2) differ.parse(code1, parsers=3, copies=1) def test_paren_in_strange_position(differ): code1 = dedent('''\ class C: """ ha """ def __init__(self, message): self.message = message ''') code2 = dedent('''\ class C: """ ha """ ) def __init__(self, message): self.message = message ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=2, expect_error_leaves=True) differ.parse(code1, parsers=0, copies=2) def insert_line_into_code(code, index, line): lines = split_lines(code, keepends=True) lines.insert(index, line) return ''.join(lines) def test_paren_before_docstring(differ): code1 = dedent('''\ # comment """ The """ from parso import tree from parso import python ''') code2 = insert_line_into_code(code1, 1, ' ' * 16 + 'raise InternalParseError(\n') differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=2, copies=1) def test_parentheses_before_method(differ): code1 = dedent('''\ class A: def a(self): pass class B: def b(self): if 1: pass ''') code2 = dedent('''\ class A: def a(self): pass Exception.__init__(self, "x" % def b(self): if 1: pass ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=2, copies=1) def test_indentation_issues(differ): code1 = dedent('''\ class C: def f(): 1 if 2: return 3 def g(): to_be_removed pass ''') code2 = dedent('''\ class C: def f(): 1 ``something``, very ``weird``). if 2: return 3 def g(): to_be_removed pass ''') code3 = dedent('''\ class C: def f(): 1 if 2: return 3 def g(): pass ''') differ.initialize(code1) differ.parse(code2, parsers=3, copies=1, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=2) differ.parse(code3, parsers=2, copies=1) differ.parse(code1, parsers=2, copies=1) def test_error_dedent_issues(differ): code1 = dedent('''\ while True: try: 1 except KeyError: if 2: 3 except IndexError: 4 5 ''') code2 = dedent('''\ while True: try: except KeyError: 1 except KeyError: if 2: 3 except IndexError: 4 something_inserted 5 ''') differ.initialize(code1) differ.parse(code2, parsers=3, copies=0, expect_error_leaves=True) differ.parse(code1, parsers=1, copies=0) def test_random_text_insertion(differ): code1 = dedent('''\ class C: def f(): return node def g(): try: 1 except KeyError: 2 ''') code2 = dedent('''\ class C: def f(): return node Some'random text: yeah for push in plan.dfa_pushes: def g(): try: 1 except KeyError: 2 ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=2, copies=1) def test_many_nested_ifs(differ): code1 = dedent('''\ class C: def f(self): def iterate(): if 1: yield t else: yield return def g(): 3 ''') code2 = dedent('''\ def f(self): def iterate(): if 1: yield t hahahaha if 2: else: yield return def g(): 3 ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=1, copies=1) @pytest.mark.parametrize('prefix', ['', 'async ']) def test_with_and_funcdef_in_call(differ, prefix): code1 = prefix + dedent('''\ with x: la = C( a=1, b=2, c=3, ) ''') code2 = insert_line_into_code(code1, 3, 'def y(self, args):\n') differ.initialize(code1) differ.parse(code2, parsers=1, expect_error_leaves=True) differ.parse(code1, parsers=1) def test_wrong_backslash(differ): code1 = dedent('''\ def y(): 1 for x in y: continue ''') code2 = insert_line_into_code(code1, 3, '\\.whl$\n') differ.initialize(code1) differ.parse(code2, parsers=3, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=1, copies=1) def test_random_unicode_characters(differ): """ Those issues were all found with the fuzzer. """ differ.initialize('') differ.parse('\x1dĔBϞɛˁşʑ˳˻ȣſéÎ\x90̕ȟòwʘ\x1dĔBϞɛˁşʑ˳˻ȣſéÎ', parsers=1, expect_error_leaves=True) differ.parse('\r\r', parsers=1) differ.parse("˟Ę\x05À\r rúƣ@\x8a\x15r()\n", parsers=1, expect_error_leaves=True) differ.parse('a\ntaǁ\rGĒōns__\n\nb', parsers=1) s = ' if not (self, "_fi\x02\x0e\x08\n\nle"):' differ.parse(s, parsers=1, expect_error_leaves=True) differ.parse('') differ.parse(s + '\n', parsers=1, expect_error_leaves=True) differ.parse(' result = (\r\f\x17\t\x11res)', parsers=1, expect_error_leaves=True) differ.parse('') differ.parse(' a( # xx\ndef', parsers=1, expect_error_leaves=True) def test_dedent_end_positions(differ): code1 = dedent('''\ if 1: if b: 2 c = { 5} ''') code2 = dedent('''\ if 1: if ⌟ഒᜈྡྷṭb: 2 'l': ''} c = { 5} ''') differ.initialize(code1) differ.parse(code2, parsers=1, expect_error_leaves=True) differ.parse(code1, parsers=1) def test_special_no_newline_ending(differ): code1 = dedent('''\ 1 ''') code2 = dedent('''\ 1 is ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=1, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=0) def test_random_character_insertion(differ): code1 = dedent('''\ def create(self): 1 if self.path is not None: return # 3 # 4 ''') code2 = dedent('''\ def create(self): 1 if 2: x return # 3 # 4 ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=1, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=1) def test_import_opening_bracket(differ): code1 = dedent('''\ 1 2 from bubu import (X, ''') code2 = dedent('''\ 11 2 from bubu import (X, ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=2, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=2, expect_error_leaves=True) def test_opening_bracket_at_end(differ): code1 = dedent('''\ class C: 1 [ ''') code2 = dedent('''\ 3 class C: 1 [ ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=2, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=1, expect_error_leaves=True) def test_all_sorts_of_indentation(differ): code1 = dedent('''\ class C: 1 def f(): 'same' if foo: a = b end ''') code2 = dedent('''\ class C: 1 def f(yield await %|( 'same' \x02\x06\x0f\x1c\x11 if foo: a = b end ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=1, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=1, expect_error_leaves=True) code3 = dedent('''\ if 1: a b c d \x00 ''') differ.parse(code3, parsers=1, expect_error_leaves=True) differ.parse('') def test_dont_copy_dedents_in_beginning(differ): code1 = dedent('''\ a 4 ''') code2 = dedent('''\ 1 2 3 4 ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=1, expect_error_leaves=True) differ.parse(code1, parsers=1, copies=1) def test_dont_copy_error_leaves(differ): code1 = dedent('''\ def f(n): x if 2: 3 ''') code2 = dedent('''\ def f(n): def if 1: indent x if 2: 3 ''') differ.initialize(code1) differ.parse(code2, parsers=1, expect_error_leaves=True) differ.parse(code1, parsers=1) def test_error_dedent_in_between(differ): code1 = dedent('''\ class C: def f(): a if something: x z ''') code2 = dedent('''\ class C: def f(): a dedent if other_thing: b if something: x z ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=2, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=2) def test_some_other_indentation_issues(differ): code1 = dedent('''\ class C: x def f(): "" copied a ''') code2 = dedent('''\ try: de a b c d def f(): "" copied a ''') differ.initialize(code1) differ.parse(code2, copies=0, parsers=1, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=1) def test_open_bracket_case1(differ): code1 = dedent('''\ class C: 1 2 # ha ''') code2 = insert_line_into_code(code1, 2, ' [str\n') code3 = insert_line_into_code(code2, 4, ' str\n') differ.initialize(code1) differ.parse(code2, copies=1, parsers=1, expect_error_leaves=True) differ.parse(code3, copies=1, parsers=1, expect_error_leaves=True) differ.parse(code1, copies=1, parsers=1) def test_open_bracket_case2(differ): code1 = dedent('''\ class C: def f(self): ( b c def g(self): d ''') code2 = dedent('''\ class C: def f(self): ( b c self. def g(self): d ''') differ.initialize(code1) differ.parse(code2, copies=0, parsers=1, expect_error_leaves=True) differ.parse(code1, copies=0, parsers=1, expect_error_leaves=True) def test_some_weird_removals(differ): code1 = dedent('''\ class C: 1 ''') code2 = dedent('''\ class C: 1 @property A return # x omega ''') code3 = dedent('''\ class C: 1 ; omega ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=1, expect_error_leaves=True) differ.parse(code3, copies=1, parsers=3, expect_error_leaves=True) differ.parse(code1, copies=1) def test_async_copy(differ): code1 = dedent('''\ async def main(): x = 3 print( ''') code2 = dedent('''\ async def main(): x = 3 print() ''') differ.initialize(code1) differ.parse(code2, copies=1, parsers=1) differ.parse(code1, copies=1, parsers=1, expect_error_leaves=True) def test_parent_on_decorator(differ): code1 = dedent('''\ class AClass: @decorator() def b_test(self): print("Hello") print("world") def a_test(self): pass''') code2 = dedent('''\ class AClass: @decorator() def b_test(self): print("Hello") print("world") def a_test(self): pass''') differ.initialize(code1) module_node = differ.parse(code2, parsers=1) cls = module_node.children[0] cls_suite = cls.children[-1] assert len(cls_suite.children) == 3 def test_wrong_indent_in_def(differ): code1 = dedent('''\ def x(): a b ''') code2 = dedent('''\ def x(): // b c ''') differ.initialize(code1) differ.parse(code2, parsers=1, expect_error_leaves=True) differ.parse(code1, parsers=1) def test_backslash_issue(differ): code1 = dedent(''' pre = ( '') after = 'instead' ''') code2 = dedent(''' pre = ( '') \\if ''') # noqa differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=1, copies=1) def test_paren_with_indentation(differ): code1 = dedent(''' class C: def f(self, fullname, path=None): x def load_module(self, fullname): a for prefix in self.search_path: try: b except ImportError: c else: raise def x(): pass ''') code2 = dedent(''' class C: def f(self, fullname, path=None): x ( a for prefix in self.search_path: try: b except ImportError: c else: raise ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=3, copies=1) def test_error_dedent_in_function(differ): code1 = dedent('''\ def x(): a b c d ''') code2 = dedent('''\ def x(): a b c d e ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) def test_with_formfeed(differ): code1 = dedent('''\ @bla async def foo(): 1 yield from [] return return '' ''') code2 = dedent('''\ @bla async def foo(): 1 \x0cimport return return '' ''') # noqa differ.initialize(code1) differ.parse(code2, parsers=ANY, copies=ANY, expect_error_leaves=True) def test_repeating_invalid_indent(differ): code1 = dedent('''\ def foo(): return @bla a def foo(): a b c ''') code2 = dedent('''\ def foo(): return @bla a b c ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) def test_another_random_indent(differ): code1 = dedent('''\ def foo(): a b c return def foo(): d ''') code2 = dedent('''\ def foo(): a c return def foo(): d ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=3) def test_invalid_function(differ): code1 = dedent('''\ a def foo(): def foo(): b ''') code2 = dedent('''\ a def foo(): def foo(): b ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) def test_async_func2(differ): code1 = dedent('''\ async def foo(): return '' @bla async def foo(): x ''') code2 = dedent('''\ async def foo(): return '' { @bla async def foo(): x y ''') differ.initialize(code1) differ.parse(code2, parsers=ANY, copies=ANY, expect_error_leaves=True) def test_weird_ending(differ): code1 = dedent('''\ def foo(): a return ''') code2 = dedent('''\ def foo(): a nonlocal xF""" y"""''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) def test_nested_class(differ): code1 = dedent('''\ def c(): a = 3 class X: b ''') code2 = dedent('''\ def c(): a = 3 class X: elif ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) def test_class_with_paren_breaker(differ): code1 = dedent('''\ class Grammar: x def parse(): y parser( ) z ''') code2 = dedent('''\ class Grammar: x def parse(): y parser( finally ; ) z ''') differ.initialize(code1) differ.parse(code2, parsers=3, copies=1, expect_error_leaves=True) def test_byte_order_mark(differ): code2 = dedent('''\ x \ufeff else : ''') differ.initialize('\n') differ.parse(code2, parsers=2, expect_error_leaves=True) code3 = dedent('''\ \ufeff if: x ''') differ.initialize('\n') differ.parse(code3, parsers=2, expect_error_leaves=True) def test_byte_order_mark2(differ): code = '\ufeff# foo' differ.initialize(code) differ.parse(code + 'x', parsers=ANY) def test_byte_order_mark3(differ): code1 = "\ufeff#\ny\n" code2 = 'x\n\ufeff#\n\ufeff#\ny\n' differ.initialize(code1) differ.parse(code2, expect_error_leaves=True, parsers=ANY, copies=ANY) differ.parse(code1, parsers=1) def test_backslash_insertion(differ): code1 = dedent(''' def f(): x def g(): base = "" \\ "" return ''') code2 = dedent(''' def f(): x def g(): base = "" \\ def h(): "" return ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True) differ.parse(code1, parsers=2, copies=1) def test_fstring_with_error_leaf(differ): code1 = dedent("""\ def f(): x def g(): y """) code2 = dedent("""\ def f(): x F''' def g(): y {a \x01 """) differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) def test_yet_another_backslash(differ): code1 = dedent('''\ def f(): x def g(): y base = "" \\ "" % to return ''') code2 = dedent('''\ def f(): x def g(): y base = "" \\ \x0f return ''') differ.initialize(code1) differ.parse(code2, parsers=ANY, copies=ANY, expect_error_leaves=True) differ.parse(code1, parsers=ANY, copies=ANY) def test_backslash_before_def(differ): code1 = dedent('''\ def f(): x def g(): y z ''') code2 = dedent('''\ def f(): x >\\ def g(): y x z ''') differ.initialize(code1) differ.parse(code2, parsers=3, copies=1, expect_error_leaves=True) def test_backslash_with_imports(differ): code1 = dedent('''\ from x import y, \\ ''') code2 = dedent('''\ from x import y, \\ z ''') differ.initialize(code1) differ.parse(code2, parsers=1) differ.parse(code1, parsers=1) def test_one_line_function_error_recovery(differ): code1 = dedent('''\ class X: x def y(): word """ # a # b c(self) ''') code2 = dedent('''\ class X: x def y(): word """ # a # b c(\x01+self) ''') differ.initialize(code1) differ.parse(code2, parsers=1, copies=1, expect_error_leaves=True) def test_one_line_property_error_recovery(differ): code1 = dedent('''\ class X: x @property def encoding(self): True - return 1 ''') code2 = dedent('''\ class X: x @property def encoding(self): True - return 1 ''') differ.initialize(code1) differ.parse(code2, parsers=2, copies=1, expect_error_leaves=True)