Commit 1a1c8fb4 authored by Stefan Behnel's avatar Stefan Behnel

Avoid f-string formatting of known unicode strings also for "!s" conversion.

Evaluate and join more f-string constants at compile time, including unprefixed strings.
Fix a case where f-string constants could have incorrect constant compile time result values, thus leading to incorrect comparisons and constant folding errors.
parent 97d39fea
......@@ -50,6 +50,9 @@ Bugs fixed
* abs(signed int) now returns a signed rather than unsigned int.
(Github issue #1837)
* Compile time evaluations of (partially) constant f-strings could show incorrect
results.
Other changes
-------------
......
......@@ -3126,7 +3126,7 @@ class FormattedValueNode(ExprNode):
self.format_spec = self.format_spec.analyse_types(env).coerce_to_pyobject(env)
if self.c_format_spec is None:
self.value = self.value.coerce_to_pyobject(env)
if not self.format_spec and not self.conversion_char:
if not self.format_spec and (not self.conversion_char or self.conversion_char == 's'):
if self.value.type is unicode_type and not self.value.may_be_none():
# value is definitely a unicode string and we don't format it any special
return self.value
......
......@@ -3959,10 +3959,21 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
def visit_FormattedValueNode(self, node):
self.visitchildren(node)
conversion_char = node.conversion_char or 's'
if isinstance(node.format_spec, ExprNodes.UnicodeNode) and not node.format_spec.value:
node.format_spec = None
if node.format_spec is None and node.conversion_char is None and isinstance(node.value, ExprNodes.UnicodeNode):
return node.value
if node.format_spec is None and isinstance(node.value, ExprNodes.IntNode):
value = EncodedString(node.value.value)
if value.isdigit():
return ExprNodes.UnicodeNode(node.value.pos, value=value, constant_result=value)
if node.format_spec is None and conversion_char == 's':
value = None
if isinstance(node.value, ExprNodes.UnicodeNode):
value = node.value.value
elif isinstance(node.value, ExprNodes.StringNode):
value = node.value.unicode_value
if value is not None:
return ExprNodes.UnicodeNode(node.value.pos, value=value, constant_result=value)
return node
def visit_JoinedStrNode(self, node):
......@@ -3980,7 +3991,8 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
substrings = list(substrings)
unode = substrings[0]
if len(substrings) > 1:
unode.value = EncodedString(u''.join(value.value for value in substrings))
value = EncodedString(u''.join(value.value for value in substrings))
unode = ExprNodes.UnicodeNode(unode.pos, value=value, constant_result=value)
# ignore empty Unicode strings
if unode.value:
values.append(unode)
......@@ -3988,7 +4000,8 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
values.extend(substrings)
if not values:
node = ExprNodes.UnicodeNode(node.pos, value=EncodedString(''))
value = EncodedString('')
node = ExprNodes.UnicodeNode(node.pos, value=value, constant_result=value)
elif len(values) == 1:
node = values[0]
elif len(values) == 2:
......
......@@ -5,6 +5,8 @@
# Cython specific PEP 498 tests in addition to test_fstring.pyx from CPython
####
cimport cython
import sys
IS_PYPY = hasattr(sys, 'pypy_version_info')
......@@ -18,13 +20,39 @@ max_long = LONG_MAX
min_long = LONG_MIN
@cython.test_fail_if_path_exists(
"//FormattedValueNode",
"//JoinedStrNode",
"//AddNode",
)
def escaping():
"""
>>> escaping()
"""
assert f'{{{{{"abc"}}}}}{{}}{{' == '{{abc}}{}{'
s = f'{{{{{"abc"}}}}}{{}}{{'
assert s == '{{abc}}{}{', s
assert f'\x7b}}' == '{}'
s = f'\x7b}}'
assert s == '{}', s
assert f'{"{{}}"}' == '{{}}'
s = f'{"{{}}"}'
assert s == '{{}}', s
@cython.test_fail_if_path_exists(
"//FormattedValueNode",
"//JoinedStrNode",
"//AddNode",
)
def nested_constant():
"""
>>> nested_constant()
'xyabc123321'
"""
return f"""{f'''xy{f"abc{123}{'321'}"!s}'''}"""
def format2(ab, cd):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment