Commit f54b0337 authored by scoder's avatar scoder Committed by Stefan Behnel

Fix optimised string formatting when '%d' argument is a float object. (GH-3589)

Floats do not support '{x:d}' formatting and need conversion to 'int' first.
Closes https://github.com/cython/cython/issues/3092
parent 42ae0e48
...@@ -3222,7 +3222,7 @@ class FormattedValueNode(ExprNode): ...@@ -3222,7 +3222,7 @@ class FormattedValueNode(ExprNode):
# {}-delimited portions of an f-string # {}-delimited portions of an f-string
# #
# value ExprNode The expression itself # value ExprNode The expression itself
# conversion_char str or None Type conversion (!s, !r, !a, or none) # conversion_char str or None Type conversion (!s, !r, !a, or none, or 'd' for integer conversion)
# format_spec JoinedStrNode or None Format string passed to __format__ # format_spec JoinedStrNode or None Format string passed to __format__
# c_format_spec str or None If not None, formatting can be done at the C level # c_format_spec str or None If not None, formatting can be done at the C level
...@@ -3236,6 +3236,7 @@ class FormattedValueNode(ExprNode): ...@@ -3236,6 +3236,7 @@ class FormattedValueNode(ExprNode):
's': 'PyObject_Unicode', 's': 'PyObject_Unicode',
'r': 'PyObject_Repr', 'r': 'PyObject_Repr',
'a': 'PyObject_ASCII', # NOTE: mapped to PyObject_Repr() in Py2 'a': 'PyObject_ASCII', # NOTE: mapped to PyObject_Repr() in Py2
'd': '__Pyx_PyNumber_IntOrLong', # NOTE: internal mapping for '%d' formatting
}.get }.get
def may_be_none(self): def may_be_none(self):
......
...@@ -4327,14 +4327,19 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -4327,14 +4327,19 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
break break
if format_type in u'asrfdoxX': if format_type in u'asrfdoxX':
format_spec = s[1:] format_spec = s[1:]
conversion_char = None
if format_type in u'doxX' and u'.' in format_spec: if format_type in u'doxX' and u'.' in format_spec:
# Precision is not allowed for integers in format(), but ok in %-formatting. # Precision is not allowed for integers in format(), but ok in %-formatting.
can_be_optimised = False can_be_optimised = False
elif format_type in u'ars': elif format_type in u'ars':
format_spec = format_spec[:-1] format_spec = format_spec[:-1]
conversion_char = format_type
elif format_type == u'd':
# '%d' formatting supports float, but '{obj:d}' does not => convert to int first.
conversion_char = 'd'
substrings.append(ExprNodes.FormattedValueNode( substrings.append(ExprNodes.FormattedValueNode(
arg.pos, value=arg, arg.pos, value=arg,
conversion_char=format_type if format_type in u'ars' else None, conversion_char=conversion_char,
format_spec=ExprNodes.UnicodeNode( format_spec=ExprNodes.UnicodeNode(
pos, value=EncodedString(format_spec), constant_result=format_spec) pos, value=EncodedString(format_spec), constant_result=format_spec)
if format_spec else None, if format_spec else None,
......
...@@ -474,31 +474,35 @@ def format_decoded_bytes(bytes value): ...@@ -474,31 +474,35 @@ def format_decoded_bytes(bytes value):
"//FormattedValueNode", "//FormattedValueNode",
"//JoinedStrNode", "//JoinedStrNode",
) )
def generated_fstring(int i, unicode u not None, o): def generated_fstring(int i, float f, unicode u not None, o):
""" """
>>> i, u, o = 11, u'xyz', [1] >>> i, f, u, o = 11, 1.3125, u'xyz', [1]
>>> print((( >>> print(((
... u"(i) %s-%.3s-%r-%.3r-%d-%3d-%o-%04o-%x-%4x-%X-%03X-%.1f-%04.2f %% " ... u"(i) %s-%.3s-%r-%.3r-%d-%3d-%o-%04o-%x-%4x-%X-%03X-%.1f-%04.2f %% "
... u"(u) %s-%.2s-%r-%.7r %% " ... u"(u) %s-%.2s-%r-%.7r %% "
... u"(o) %s-%.2s-%r-%.2r" ... u"(o) %s-%.2s-%r-%.2r %% "
... u"(f) %.2f-%d"
... ) % ( ... ) % (
... i, i, i, i, i, i, i, i, i, i, i, i, i, i, ... i, i, i, i, i, i, i, i, i, i, i, i, i, i,
... u, u, u, u, ... u, u, u, u,
... o, o, o, o, ... o, o, o, o,
... f, f,
... )).replace("-u'xyz'", "-'xyz'")) ... )).replace("-u'xyz'", "-'xyz'"))
(i) 11-11-11-11-11- 11-13-0013-b- b-B-00B-11.0-11.00 % (u) xyz-xy-'xyz'-'xyz' % (o) [1]-[1-[1]-[1 (i) 11-11-11-11-11- 11-13-0013-b- b-B-00B-11.0-11.00 % (u) xyz-xy-'xyz'-'xyz' % (o) [1]-[1-[1]-[1 % (f) 1.31-1
>>> print(generated_fstring(i, u, o).replace("-u'xyz'", "-'xyz'")) >>> print(generated_fstring(i, f, u, o).replace("-u'xyz'", "-'xyz'"))
(i) 11-11-11-11-11- 11-13-0013-b- b-B-00B-11.0-11.00 % (u) xyz-xy-'xyz'-'xyz' % (o) [1]-[1-[1]-[1 (i) 11-11-11-11-11- 11-13-0013-b- b-B-00B-11.0-11.00 % (u) xyz-xy-'xyz'-'xyz' % (o) [1]-[1-[1]-[1 % (f) 1.31-1
""" """
return ( return (
u"(i) %s-%.3s-%r-%.3r-%d-%3d-%o-%04o-%x-%4x-%X-%03X-%.1f-%04.2f %% " u"(i) %s-%.3s-%r-%.3r-%d-%3d-%o-%04o-%x-%4x-%X-%03X-%.1f-%04.2f %% "
u"(u) %s-%.2s-%r-%.7r %% " u"(u) %s-%.2s-%r-%.7r %% "
u"(o) %s-%.2s-%r-%.2r" u"(o) %s-%.2s-%r-%.2r %% "
u"(f) %.2f-%d"
) % ( ) % (
i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i,
u, u, u, u, u, u, u, u,
o, o, o, o, o, o, o, o,
f, f,
) )
......
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