mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-24 09:30:04 +08:00
165 lines
4.0 KiB
Python
Vendored
165 lines
4.0 KiB
Python
Vendored
import pytest
|
|
from textwrap import dedent
|
|
|
|
from parso import load_grammar, ParserSyntaxError
|
|
from parso.python.tokenize import tokenize
|
|
|
|
|
|
@pytest.fixture
|
|
def grammar():
|
|
return load_grammar(version='3.8')
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'code', [
|
|
# simple cases
|
|
'f"{1}"',
|
|
'f"""{1}"""',
|
|
'f"{foo} {bar}"',
|
|
|
|
# empty string
|
|
'f""',
|
|
'f""""""',
|
|
|
|
# empty format specifier is okay
|
|
'f"{1:}"',
|
|
|
|
# use of conversion options
|
|
'f"{1!a}"',
|
|
'f"{1!a:1}"',
|
|
|
|
# format specifiers
|
|
'f"{1:1}"',
|
|
'f"{1:1.{32}}"',
|
|
'f"{1::>4}"',
|
|
'f"{x:{y}}"',
|
|
'f"{x:{y:}}"',
|
|
'f"{x:{y:1}}"',
|
|
|
|
# Escapes
|
|
'f"{{}}"',
|
|
'f"{{{1}}}"',
|
|
'f"{{{1}"',
|
|
'f"1{{2{{3"',
|
|
'f"}}"',
|
|
|
|
# New Python 3.8 syntax f'{a=}'
|
|
'f"{a=}"',
|
|
'f"{a()=}"',
|
|
|
|
# multiline f-string
|
|
'f"""abc\ndef"""',
|
|
'f"""abc{\n123}def"""',
|
|
|
|
# a line continuation inside of an fstring_string
|
|
'f"abc\\\ndef"',
|
|
'f"\\\n{123}\\\n"',
|
|
|
|
# a line continuation inside of an fstring_expr
|
|
'f"{\\\n123}"',
|
|
|
|
# a line continuation inside of an format spec
|
|
'f"{123:.2\\\nf}"',
|
|
|
|
# some unparenthesized syntactic structures
|
|
'f"{*x,}"',
|
|
'f"{*x, *y}"',
|
|
'f"{x, *y}"',
|
|
'f"{*x, y}"',
|
|
'f"{x for x in [1]}"',
|
|
|
|
# named unicode characters
|
|
'f"\\N{BULLET}"',
|
|
'f"\\N{FLEUR-DE-LIS}"',
|
|
'f"\\N{NO ENTRY}"',
|
|
'f"Combo {expr} and \\N{NO ENTRY}"',
|
|
'f"\\N{NO ENTRY} and {expr}"',
|
|
'f"\\N{no entry}"',
|
|
'f"\\N{SOYOMBO LETTER -A}"',
|
|
'f"\\N{DOMINO TILE HORIZONTAL-00-00}"',
|
|
'f"""\\N{NO ENTRY}"""',
|
|
]
|
|
)
|
|
def test_valid(code, grammar):
|
|
module = grammar.parse(code, error_recovery=False)
|
|
fstring = module.children[0]
|
|
assert fstring.type == 'fstring'
|
|
assert fstring.get_code() == code
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'code', [
|
|
# an f-string can't contain unmatched curly braces
|
|
'f"}"',
|
|
'f"{"',
|
|
'f"""}"""',
|
|
'f"""{"""',
|
|
|
|
# invalid conversion characters
|
|
'f"{1!{a}}"',
|
|
'f"{1=!{a}}"',
|
|
'f"{!{a}}"',
|
|
|
|
# The curly braces must contain an expression
|
|
'f"{}"',
|
|
'f"{:}"',
|
|
'f"{:}}}"',
|
|
'f"{:1}"',
|
|
'f"{!:}"',
|
|
'f"{!}"',
|
|
'f"{!a}"',
|
|
|
|
# invalid (empty) format specifiers
|
|
'f"{1:{}}"',
|
|
'f"{1:{:}}"',
|
|
|
|
# a newline without a line continuation inside a single-line string
|
|
'f"abc\ndef"',
|
|
|
|
# various named unicode escapes that aren't name-shaped
|
|
'f"\\N{ BULLET }"',
|
|
'f"\\N{NO ENTRY}"',
|
|
'f"""\\N{NO\nENTRY}"""',
|
|
]
|
|
)
|
|
def test_invalid(code, grammar):
|
|
with pytest.raises(ParserSyntaxError):
|
|
grammar.parse(code, error_recovery=False)
|
|
|
|
# It should work with error recovery.
|
|
grammar.parse(code, error_recovery=True)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
('code', 'positions'), [
|
|
# 2 times 2, 5 because python expr and endmarker.
|
|
('f"}{"', [(1, 0), (1, 2), (1, 3), (1, 4), (1, 5)]),
|
|
('f" :{ 1 : } "', [(1, 0), (1, 2), (1, 4), (1, 6), (1, 8), (1, 9),
|
|
(1, 10), (1, 11), (1, 12), (1, 13)]),
|
|
('f"""\n {\nfoo\n }"""', [(1, 0), (1, 4), (2, 1), (3, 0), (4, 1),
|
|
(4, 2), (4, 5)]),
|
|
('f"\\N{NO ENTRY} and {expr}"', [(1, 0), (1, 2), (1, 19), (1, 20),
|
|
(1, 24), (1, 25), (1, 26)]),
|
|
]
|
|
)
|
|
def test_tokenize_start_pos(code, positions):
|
|
tokens = list(tokenize(code, version_info=(3, 6)))
|
|
assert positions == [p.start_pos for p in tokens]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'code', [
|
|
dedent("""\
|
|
f'''s{
|
|
str.uppe
|
|
'''
|
|
"""),
|
|
'f"foo',
|
|
'f"""foo',
|
|
'f"abc\ndef"',
|
|
]
|
|
)
|
|
def test_roundtrip(grammar, code):
|
|
tree = grammar.parse(code)
|
|
assert tree.get_code() == code
|