Files
2026-07-13 12:40:42 +08:00

249 lines
7.6 KiB
Python

# Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest
from test_case_base import TestCaseBase
from paddle.jit.sot import psdb, symbolic_translate # noqa: F401
from paddle.jit.sot.psdb import check_no_breakgraph # noqa: F401
from paddle.jit.sot.utils.envs import strict_mode_guard
from paddle.jit.sot.utils.exceptions import InnerError
try:
from string.templatelib import Interpolation, Template
except Exception: # pragma: no cover - env without t-string support
Interpolation = None
Template = None
def _tstring_supported():
if Template is None or Interpolation is None:
return False
try:
compile("x = t'hello'", "<tstring>", "exec")
except SyntaxError:
return False
return True
def _tstring_skip(*_args, **_kwargs):
pass
if _tstring_supported():
_tstring_ns = {}
exec(
"""
@check_no_breakgraph
def test_t_literal():
name = "world"
return t"hello, {name}!"
@check_no_breakgraph
def test_t_with_expression():
left = 2
right = 5
return t"{left} + {right} = {left + right}"
@check_no_breakgraph
def test_t_with_conversion_and_format():
value = 3.14159
return t"value={value!r:.2f}"
@check_no_breakgraph
def test_t_in_containers():
prefix = "id"
first = t"{prefix}-{1}"
second = t"{prefix}-{2!a}"
return [first, {"second": second}]
@check_no_breakgraph
def test_t_with_no_interpolation():
return t"plain literal"
@check_no_breakgraph
def test_t_multiple_interpolations():
a = 1
b = 2
c = 3
return t"{a},{b},{c}"
@check_no_breakgraph
def test_t_with_format_spec_expression():
value = 12.3456
precision = 3
return t"value={value:.{precision}f}"
@check_no_breakgraph
def test_t_with_ascii_conversion():
text = "中文"
return t"ascii={text!a}"
@check_no_breakgraph
def test_t_with_str_conversion():
value = "hello"
return t"str={value!s}"
def test_t_with_fallback_not_recursive():
psdb.fallback(recursive=False)
value = "hello"
return t"fallback={value!s}"
def test_t_with_fallback_recursive():
psdb.fallback(recursive=True)
value = "hello"
return t"fallback={value!s}"
@psdb.check_no_fallback
def test_t_with_forbidden_fallback():
psdb.fallback(recursive=False)
value = "hello"
return t"fallback={value!s}"
""",
globals(),
_tstring_ns,
)
test_t_literal = _tstring_ns["test_t_literal"]
test_t_with_expression = _tstring_ns["test_t_with_expression"]
test_t_with_conversion_and_format = _tstring_ns[
"test_t_with_conversion_and_format"
]
test_t_in_containers = _tstring_ns["test_t_in_containers"]
test_t_with_no_interpolation = _tstring_ns["test_t_with_no_interpolation"]
test_t_multiple_interpolations = _tstring_ns[
"test_t_multiple_interpolations"
]
test_t_with_format_spec_expression = _tstring_ns[
"test_t_with_format_spec_expression"
]
test_t_with_ascii_conversion = _tstring_ns["test_t_with_ascii_conversion"]
test_t_with_str_conversion = _tstring_ns["test_t_with_str_conversion"]
test_t_with_fallback_not_recursive = _tstring_ns[
"test_t_with_fallback_not_recursive"
]
test_t_with_fallback_recursive = _tstring_ns[
"test_t_with_fallback_recursive"
]
test_t_with_forbidden_fallback = _tstring_ns[
"test_t_with_forbidden_fallback"
]
else:
test_t_literal = _tstring_skip
test_t_with_expression = _tstring_skip
test_t_with_conversion_and_format = _tstring_skip
test_t_in_containers = _tstring_skip
test_t_with_no_interpolation = _tstring_skip
test_t_multiple_interpolations = _tstring_skip
test_t_with_format_spec_expression = _tstring_skip
test_t_with_ascii_conversion = _tstring_skip
test_t_with_str_conversion = _tstring_skip
test_t_with_fallback_not_recursive = _tstring_skip
test_t_with_fallback_recursive = _tstring_skip
test_t_with_forbidden_fallback = _tstring_skip
class TestTString(TestCaseBase):
def _assert_tstring_like(self, actual, expected):
if Template is None or Interpolation is None:
self.skipTest(
"Template strings are not supported by this interpreter."
)
self.assertIs(type(actual), type(expected))
if isinstance(actual, Template):
self._assert_tstring_like(actual.strings, expected.strings)
self._assert_tstring_like(
actual.interpolations, expected.interpolations
)
return
if isinstance(actual, Interpolation):
self._assert_tstring_like(actual.value, expected.value)
self._assert_tstring_like(actual.expression, expected.expression)
self.assertEqual(actual.conversion, expected.conversion)
self.assertEqual(actual.format_spec, expected.format_spec)
return
if isinstance(actual, (list, tuple)):
self.assertEqual(len(actual), len(expected))
for a_item, e_item in zip(actual, expected):
self._assert_tstring_like(a_item, e_item)
return
if isinstance(actual, dict):
self.assertEqual(set(actual.keys()), set(expected.keys()))
for key in actual:
self._assert_tstring_like(actual[key], expected[key])
return
if isinstance(actual, set):
self.assertEqual(actual, expected)
return
# Fallback to the generic nested matcher for tensors / arrays / scalars.
self.assert_nest_match(actual, expected)
def assert_tstring_results(self, func, *args, **kwargs):
sym_output = symbolic_translate(func)(*args, **kwargs)
paddle_output = func(*args, **kwargs)
self._assert_tstring_like(sym_output, paddle_output)
def test_symbolic_translate_handles_t_string(self):
self.assert_tstring_results(test_t_literal)
def test_symbolic_translate_handles_formats_and_containers(self):
self.assert_tstring_results(test_t_with_expression)
self.assert_tstring_results(test_t_with_conversion_and_format)
self.assert_tstring_results(test_t_in_containers)
def test_symbolic_translate_handles_more_cases(self):
self.assert_tstring_results(test_t_with_no_interpolation)
self.assert_tstring_results(test_t_multiple_interpolations)
self.assert_tstring_results(test_t_with_format_spec_expression)
self.assert_tstring_results(test_t_with_ascii_conversion)
self.assert_tstring_results(test_t_with_str_conversion)
@strict_mode_guard(False)
def test_tstring_fallback_not_recursive(self):
self.assert_tstring_results(test_t_with_fallback_not_recursive)
@strict_mode_guard(False)
def test_tstring_fallback_recursive(self):
self.assert_tstring_results(test_t_with_fallback_recursive)
def test_tstring_check_no_fallback(self):
if not _tstring_supported():
self.skipTest(
"Template strings are not supported by this interpreter."
)
with self.assertRaises(InnerError):
symbolic_translate(test_t_with_forbidden_fallback)()
if __name__ == "__main__":
unittest.main()