From b410599275459005e1b005459687ccbfea6e7b53 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Fri, 8 May 2020 14:21:51 +0200 Subject: Fix optimised string formatting when '%d' argument is a float object, which does not support '{x:d}' formatting. Closes https://github.com/cython/cython/issues/3092 --- Cython/Compiler/ExprNodes.py | 3 ++- Cython/Compiler/Optimize.py | 7 ++++++- tests/run/fstring.pyx | 18 +++++++++++------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 895ff3fe2..f4c677436 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -3293,7 +3293,7 @@ class FormattedValueNode(ExprNode): # {}-delimited portions of an f-string # # 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__ # c_format_spec str or None If not None, formatting can be done at the C level @@ -3308,6 +3308,7 @@ class FormattedValueNode(ExprNode): 's': 'PyObject_Unicode', 'r': 'PyObject_Repr', 'a': 'PyObject_ASCII', # NOTE: mapped to PyObject_Repr() in Py2 + 'd': '__Pyx_PyNumber_IntOrLong', # NOTE: internal mapping for '%d' formatting }.get def may_be_none(self): diff --git a/Cython/Compiler/Optimize.py b/Cython/Compiler/Optimize.py index 4e6149c07..b8e1cee2e 100644 --- a/Cython/Compiler/Optimize.py +++ b/Cython/Compiler/Optimize.py @@ -4382,14 +4382,19 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): break if format_type in u'asrfdoxX': format_spec = s[1:] + conversion_char = None if format_type in u'doxX' and u'.' in format_spec: # Precision is not allowed for integers in format(), but ok in %-formatting. can_be_optimised = False elif format_type in u'ars': 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( arg.pos, value=arg, - conversion_char=format_type if format_type in u'ars' else None, + conversion_char=conversion_char, format_spec=ExprNodes.UnicodeNode( pos, value=EncodedString(format_spec), constant_result=format_spec) if format_spec else None, diff --git a/tests/run/fstring.pyx b/tests/run/fstring.pyx index 574d56fa3..30aa7fe01 100644 --- a/tests/run/fstring.pyx +++ b/tests/run/fstring.pyx @@ -533,31 +533,35 @@ def format_decoded_bytes(bytes value): "//FormattedValueNode", "//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((( ... u"(i) %s-%.3s-%r-%.3r-%d-%3d-%o-%04o-%x-%4x-%X-%03X-%.1f-%04.2f %% " ... 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, ... u, u, u, u, ... o, o, o, o, + ... f, f, ... )).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'")) - (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 + >>> 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 % (f) 1.31-1 """ return ( u"(i) %s-%.3s-%r-%.3r-%d-%3d-%o-%04o-%x-%4x-%X-%03X-%.1f-%04.2f %% " 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, u, u, u, u, o, o, o, o, + f, f, ) -- cgit v1.2.1