test_pgen2.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. from textwrap import dedent
  2. import pytest
  3. from parso import load_grammar
  4. from parso import ParserSyntaxError
  5. from parso.pgen2 import generate_grammar
  6. from parso.python import tokenize
  7. def _parse(code, version=None):
  8. code = dedent(code) + "\n\n"
  9. grammar = load_grammar(version=version)
  10. return grammar.parse(code, error_recovery=False)
  11. def _invalid_syntax(code, version=None, **kwargs):
  12. with pytest.raises(ParserSyntaxError):
  13. module = _parse(code, version=version, **kwargs)
  14. # For debugging
  15. print(module.children)
  16. def test_formfeed(each_version):
  17. s = "foo\n\x0c\nfoo\n"
  18. t = _parse(s, each_version)
  19. assert t.children[0].children[0].type == 'name'
  20. assert t.children[1].children[0].type == 'name'
  21. s = "1\n\x0c\x0c\n2\n"
  22. t = _parse(s, each_version)
  23. with pytest.raises(ParserSyntaxError):
  24. s = "\n\x0c2\n"
  25. _parse(s, each_version)
  26. def test_matrix_multiplication_operator(works_in_py):
  27. works_in_py.parse("a @ b")
  28. works_in_py.parse("a @= b")
  29. def test_yield_from(works_in_py, each_version):
  30. works_in_py.parse("yield from x")
  31. works_in_py.parse("(yield from x) + y")
  32. _invalid_syntax("yield from", each_version)
  33. def test_await_expr(works_in_py):
  34. works_in_py.parse("""async def foo():
  35. await x
  36. """)
  37. works_in_py.parse("""async def foo():
  38. def foo(): pass
  39. def foo(): pass
  40. await x
  41. """)
  42. works_in_py.parse("""async def foo(): return await a""")
  43. works_in_py.parse("""def foo():
  44. def foo(): pass
  45. async def foo(): await x
  46. """)
  47. @pytest.mark.parametrize(
  48. 'code', [
  49. "async = 1",
  50. "await = 1",
  51. "def async(): pass",
  52. ]
  53. )
  54. def test_async_var(works_not_in_py, code):
  55. works_not_in_py.parse(code)
  56. def test_async_for(works_in_py):
  57. works_in_py.parse("async def foo():\n async for a in b: pass")
  58. @pytest.mark.parametrize("body", [
  59. """[1 async for a in b
  60. ]""",
  61. """[1 async
  62. for a in b
  63. ]""",
  64. """[
  65. 1
  66. async for a in b
  67. ]""",
  68. """[
  69. 1
  70. async for a
  71. in b
  72. ]""",
  73. """[
  74. 1
  75. async
  76. for
  77. a
  78. in
  79. b
  80. ]""",
  81. """ [
  82. 1 async for a in b
  83. ]""",
  84. ])
  85. def test_async_for_comprehension_newline(works_in_py, body):
  86. # Issue #139
  87. works_in_py.parse("""async def foo():
  88. {}""".format(body))
  89. def test_async_with(works_in_py):
  90. works_in_py.parse("async def foo():\n async with a: pass")
  91. def test_async_with_invalid(works_in_py):
  92. works_in_py.parse("""def foo():\n async with a: pass""")
  93. def test_raise_3x_style_1(each_version):
  94. _parse("raise", each_version)
  95. def test_raise_2x_style_2(works_not_in_py):
  96. works_not_in_py.parse("raise E, V")
  97. def test_raise_2x_style_3(works_not_in_py):
  98. works_not_in_py.parse("raise E, V, T")
  99. def test_raise_2x_style_invalid_1(each_version):
  100. _invalid_syntax("raise E, V, T, Z", version=each_version)
  101. def test_raise_3x_style(works_in_py):
  102. works_in_py.parse("raise E1 from E2")
  103. def test_raise_3x_style_invalid_1(each_version):
  104. _invalid_syntax("raise E, V from E1", each_version)
  105. def test_raise_3x_style_invalid_2(each_version):
  106. _invalid_syntax("raise E from E1, E2", each_version)
  107. def test_raise_3x_style_invalid_3(each_version):
  108. _invalid_syntax("raise from E1, E2", each_version)
  109. def test_raise_3x_style_invalid_4(each_version):
  110. _invalid_syntax("raise E from", each_version)
  111. # Adapted from Python 3's Lib/test/test_grammar.py:GrammarTests.testFuncdef
  112. def test_annotation_1(works_in_py):
  113. works_in_py.parse("""def f(x) -> list: pass""")
  114. def test_annotation_2(works_in_py):
  115. works_in_py.parse("""def f(x:int): pass""")
  116. def test_annotation_3(works_in_py):
  117. works_in_py.parse("""def f(*x:str): pass""")
  118. def test_annotation_4(works_in_py):
  119. works_in_py.parse("""def f(**x:float): pass""")
  120. def test_annotation_5(works_in_py):
  121. works_in_py.parse("""def f(x, y:1+2): pass""")
  122. def test_annotation_6(each_version):
  123. _invalid_syntax("""def f(a, (b:1, c:2, d)): pass""", each_version)
  124. def test_annotation_7(each_version):
  125. _invalid_syntax("""def f(a, (b:1, c:2, d), e:3=4, f=5, *g:6): pass""", each_version)
  126. def test_annotation_8(each_version):
  127. s = """def f(a, (b:1, c:2, d), e:3=4, f=5,
  128. *g:6, h:7, i=8, j:9=10, **k:11) -> 12: pass"""
  129. _invalid_syntax(s, each_version)
  130. def test_except_new(each_version):
  131. s = dedent("""
  132. try:
  133. x
  134. except E as N:
  135. y""")
  136. _parse(s, each_version)
  137. def test_except_old(works_not_in_py):
  138. s = dedent("""
  139. try:
  140. x
  141. except E, N:
  142. y""")
  143. works_not_in_py.parse(s)
  144. # Adapted from Python 3's Lib/test/test_grammar.py:GrammarTests.testAtoms
  145. def test_set_literal_1(works_in_py):
  146. works_in_py.parse("""x = {'one'}""")
  147. def test_set_literal_2(works_in_py):
  148. works_in_py.parse("""x = {'one', 1,}""")
  149. def test_set_literal_3(works_in_py):
  150. works_in_py.parse("""x = {'one', 'two', 'three'}""")
  151. def test_set_literal_4(works_in_py):
  152. works_in_py.parse("""x = {2, 3, 4,}""")
  153. def test_new_octal_notation(each_version):
  154. _parse("""0o7777777777777""", each_version)
  155. _invalid_syntax("""0o7324528887""", each_version)
  156. def test_old_octal_notation(works_not_in_py):
  157. works_not_in_py.parse("07")
  158. def test_long_notation(works_not_in_py):
  159. works_not_in_py.parse("0xFl")
  160. works_not_in_py.parse("0xFL")
  161. works_not_in_py.parse("0b1l")
  162. works_not_in_py.parse("0B1L")
  163. works_not_in_py.parse("0o7l")
  164. works_not_in_py.parse("0O7L")
  165. works_not_in_py.parse("0l")
  166. works_not_in_py.parse("0L")
  167. works_not_in_py.parse("10l")
  168. works_not_in_py.parse("10L")
  169. def test_new_binary_notation(each_version):
  170. _parse("""0b101010""", each_version)
  171. _invalid_syntax("""0b0101021""", each_version)
  172. def test_class_new_syntax(works_in_py):
  173. works_in_py.parse("class B(t=7): pass")
  174. works_in_py.parse("class B(t, *args): pass")
  175. works_in_py.parse("class B(t, **kwargs): pass")
  176. works_in_py.parse("class B(t, *args, **kwargs): pass")
  177. works_in_py.parse("class B(t, y=9, *args, **kwargs): pass")
  178. def test_parser_idempotency_extended_unpacking(works_in_py):
  179. """A cut-down version of pytree_idempotency.py."""
  180. works_in_py.parse("a, *b, c = x\n")
  181. works_in_py.parse("[*a, b] = x\n")
  182. works_in_py.parse("(z, *y, w) = m\n")
  183. works_in_py.parse("for *z, m in d: pass\n")
  184. def test_multiline_bytes_literals(each_version):
  185. s = """
  186. md5test(b"\xaa" * 80,
  187. (b"Test Using Larger Than Block-Size Key "
  188. b"and Larger Than One Block-Size Data"),
  189. "6f630fad67cda0ee1fb1f562db3aa53e")
  190. """
  191. _parse(s, each_version)
  192. def test_multiline_bytes_tripquote_literals(each_version):
  193. s = '''
  194. b"""
  195. <?xml version="1.0" encoding="UTF-8"?>
  196. <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN">
  197. """
  198. '''
  199. _parse(s, each_version)
  200. def test_ellipsis(works_in_py, each_version):
  201. works_in_py.parse("...")
  202. _parse("[0][...]", version=each_version)
  203. def test_dict_unpacking(works_in_py):
  204. works_in_py.parse("{**dict(a=3), foo:2}")
  205. def test_multiline_str_literals(each_version):
  206. s = """
  207. md5test("\xaa" * 80,
  208. ("Test Using Larger Than Block-Size Key "
  209. "and Larger Than One Block-Size Data"),
  210. "6f630fad67cda0ee1fb1f562db3aa53e")
  211. """
  212. _parse(s, each_version)
  213. def test_py2_backticks(works_not_in_py):
  214. works_not_in_py.parse("`1`")
  215. def test_py2_string_prefixes(works_not_in_py):
  216. works_not_in_py.parse("ur'1'")
  217. works_not_in_py.parse("Ur'1'")
  218. works_not_in_py.parse("UR'1'")
  219. _invalid_syntax("ru'1'", works_not_in_py.version)
  220. def py_br(each_version):
  221. _parse('br""', each_version)
  222. def test_py3_rb(works_in_py):
  223. works_in_py.parse("rb'1'")
  224. works_in_py.parse("RB'1'")
  225. def test_left_recursion():
  226. with pytest.raises(ValueError, match='left recursion'):
  227. generate_grammar('foo: foo NAME\n', tokenize.PythonTokenTypes)
  228. @pytest.mark.parametrize(
  229. 'grammar, error_match', [
  230. ['foo: bar | baz\nbar: NAME\nbaz: NAME\n',
  231. r"foo is ambiguous.*given a (PythonTokenTypes\.)?NAME.*bar or baz"],
  232. ['''foo: bar | baz\nbar: 'x'\nbaz: "x"\n''',
  233. r"foo is ambiguous.*given a ReservedString\(x\).*bar or baz"],
  234. ['''foo: bar | 'x'\nbar: 'x'\n''',
  235. r"foo is ambiguous.*given a ReservedString\(x\).*bar or foo"],
  236. # An ambiguity with the second (not the first) child of a production
  237. ['outer: "a" [inner] "b" "c"\ninner: "b" "c" [inner]\n',
  238. r"outer is ambiguous.*given a ReservedString\(b\).*inner or outer"],
  239. # An ambiguity hidden by a level of indirection (middle)
  240. ['outer: "a" [middle] "b" "c"\nmiddle: inner\ninner: "b" "c" [inner]\n',
  241. r"outer is ambiguous.*given a ReservedString\(b\).*middle or outer"],
  242. ]
  243. )
  244. def test_ambiguities(grammar, error_match):
  245. with pytest.raises(ValueError, match=error_match):
  246. generate_grammar(grammar, tokenize.PythonTokenTypes)