Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ jobs:
# To speed-up process until ast_serialize is on PyPI.
- name: Install pinned ast-serialize
if: ${{ matrix.dev_ast_serialize }}
run: pip install ast-serialize@git+https://github.com/mypyc/ast_serialize.git@da5a16cf268dbec63ed6b2e6b715470576e2d1a6
run: pip install ast-serialize@git+https://github.com/mypyc/ast_serialize.git@555942927658b45941b834d61c7074267e5f7075

- name: Setup tox environment
run: |
Expand Down
33 changes: 31 additions & 2 deletions mypy/nativeparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
Statement,
StrExpr,
SuperExpr,
TemplateStrExpr,
TempNode,
TryStmt,
TupleExpr,
Expand Down Expand Up @@ -168,6 +169,7 @@ def __init__(self, options: Options) -> None:
self.options = options
self.errors: list[dict[str, Any]] = []
self.num_funcs = 0
self.uses_template_strings = False

def add_error(
self,
Expand Down Expand Up @@ -233,6 +235,7 @@ def native_parse(
node = MypyFile(defs, imports)
node.path = filename
node.is_partial_stub_package = is_partial_package
node.uses_template_strings = state.uses_template_strings
# Merge deserialization errors with parsing errors
all_errors = errors + state.errors
return node, all_errors, ignores
Expand All @@ -243,7 +246,7 @@ def expect_end_tag(data: ReadBuffer) -> None:


def expect_tag(data: ReadBuffer, tag: Tag) -> None:
assert read_tag(data) == tag
assert (actual := read_tag(data)) == tag, actual


def read_statements(state: State, data: ReadBuffer, n: int) -> list[Statement]:
Expand All @@ -263,7 +266,7 @@ def parse_to_binary_ast(
) -> tuple[bytes, list[dict[str, Any]], TypeIgnores, bytes, bool]:
ast_bytes, errors, ignores, import_bytes, is_partial_package = ast_serialize.parse(
filename,
skip_function_bodies,
skip_function_bodies=skip_function_bodies,
python_version=options.python_version,
platform=options.platform,
always_true=options.always_true,
Expand Down Expand Up @@ -1524,6 +1527,32 @@ def read_expression(state: State, data: ReadBuffer) -> Expression:
expr = build_fstring_join(state, data, fitems)
expect_end_tag(data)
return expr
elif tag == nodes.TSTRING_EXPR:
state.uses_template_strings = True
nparts = read_int(data)
titems: list[Expression | tuple[Expression, str, str | None, Expression | None]] = []
for _ in range(nparts):
if read_bool(data):
e = read_expression(state, data)
s = read_str(data)
if read_bool(data):
conv = read_str(data)
else:
conv = None
if read_bool(data):
# Parse format spec as a JoinedStr, this matches the old parser behavior.
format_spec = read_fstring_items(state, data)
else:
format_spec = None
titems.append((e, s, conv, format_spec))
else:
s = StrExpr(read_str(data))
read_loc(data, s)
titems.append(s)
expr = TemplateStrExpr(titems)
read_loc(data, expr)
expect_end_tag(data)
return expr
elif tag == nodes.LAMBDA_EXPR:
arguments, has_ann = read_parameters(state, data)
body = read_block(state, data)
Expand Down
1 change: 1 addition & 0 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5242,6 +5242,7 @@ def local_definitions(
IMPORT_METADATA: Final[Tag] = 226
IMPORTFROM_METADATA: Final[Tag] = 227
IMPORTALL_METADATA: Final[Tag] = 228
TSTRING_EXPR: Final[Tag] = 229


def read_symbol(data: ReadBuffer) -> SymbolNode:
Expand Down
14 changes: 7 additions & 7 deletions test-data/unit/check-python314.test
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[case testTemplateStringBasics_no_native_parse]
[case testTemplateStringBasics]
reveal_type(t"foobar") # N: Revealed type is "string.templatelib.Template"
t"{'foobar'}"
t"foo{'bar'}"
Expand All @@ -14,32 +14,32 @@ a = t"foobar"
a = t"{'foobar'}"
[builtins fixtures/f_string.pyi]

[case testTemplateStringWithoutExplicitImport_no_native_parse]
[case testTemplateStringWithoutExplicitImport]
reveal_type(t"implicit import works") # N: Revealed type is "string.templatelib.Template"
[builtins fixtures/f_string.pyi]

[case testTemplateStringExpressionsOk_no_native_parse]
[case testTemplateStringExpressionsOk]
t".{1 + 1}."
t".{1 + 1}.{'foo' + 'bar'}"
[builtins fixtures/f_string.pyi]

[case testTemplateStringExpressionsErrors_no_native_parse]
[case testTemplateStringExpressionsErrors]
t"{1 + ''}" # E: Unsupported operand types for + ("int" and "str")
t".{1 + ''}" # E: Unsupported operand types for + ("int" and "str")
[builtins fixtures/f_string.pyi]

[case testTemplateStringParseFormatOptions_no_native_parse]
[case testTemplateStringParseFormatOptions]
value = 10.5142
width = 10
precision = 4
t"result: {value:{width}.{precision}}"
[builtins fixtures/f_string.pyi]

[case testTemplateStringNestedExpressionsTypeChecked_no_native_parse]
[case testTemplateStringNestedExpressionsTypeChecked]
t"{2:{3 + ''}}" # E: Unsupported operand types for + ("int" and "str")
[builtins fixtures/f_string.pyi]

[case testIncrementalTemplateStringImplicitDependency_no_native_parse]
[case testIncrementalTemplateStringImplicitDependency]
import m
reveal_type(m.x)
[file m.py]
Expand Down
12 changes: 6 additions & 6 deletions test-data/unit/native-parser.test
Original file line number Diff line number Diff line change
Expand Up @@ -2552,7 +2552,7 @@ MypyFile:1(
x = [1 2]
y = 2
[out]
1:8: error: Expected ',', found int
1:8: error: Expected `,`, found int
MypyFile:1(
AssignmentStmt:1(
NameExpr(x)
Expand Down Expand Up @@ -2603,7 +2603,7 @@ MypyFile:1(
from m
1
[out]
1:7: error: Expected 'import', found newline
1:7: error: Expected `import`, found newline
MypyFile:1(
ImportFrom:1(m, [])
ExpressionStmt:2(
Expand All @@ -2613,7 +2613,7 @@ MypyFile:1(
from m
1
[out]
1:7: error: Expected 'import', found newline
1:7: error: Expected `import`, found newline
MypyFile:1(
ImportFrom:1(m, [])
ExpressionStmt:2(
Expand All @@ -2626,7 +2626,7 @@ def f():

def g(): ...
[out]
3:8: error: Expected ')', found newline
3:8: error: Expected `)`, found newline
MypyFile:1(
FuncDef:1(
f
Expand All @@ -2651,7 +2651,7 @@ def f(

def g(): ...
[out]
3:6: error: Expected ')', found newline
3:6: error: Expected `)`, found newline
5:1: error: Expected an indented block after function definition
MypyFile:1(
FuncDef:1(
Expand All @@ -2671,7 +2671,7 @@ class A

def f(): pass
[out]
1:8: error: Expected ':', found newline
1:8: error: Expected `:`, found newline
3:1: error: Expected an indented block after `class` definition
MypyFile:1(
ClassDef:1(
Expand Down
Loading