question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

% string formatting with multiple inputs via of tuples doesn't match python3.5 behavior

See original GitHub issue

I’m new to cython but this seems like incorrect behavior.

Python code put in cytest/test.py:

def test_func_no_tuples(a, b):
  print((a.__class__, b.__class__))
  print((a, b))
  s = 'string being formatted=%d' % a
  print(s)


def test_func_with_tuple(a,b):
  print((a.__class__, b.__class__))
  print((a, b))
  s = 'string being formatted=%d-%d' % (a,b)
  print(s)

test_func_no_tuples(50.,50.)

test_func_with_tuple(50.,50.)

Before compilation the output of python3 -c "from cytest import test" is:

python3 -c "from cytest import test"
(<class 'float'>, <class 'float'>)
(50.0, 50.0)
string being formatted=50
(<class 'float'>, <class 'float'>)
(50.0, 50.0)
string being formatted=50-50

After a pip install of cython==0.29.13 I compile with:

PYTHONLIB=/usr/include/python3.5
CFLAGS="-shared -pthread -fPIC -fwrapv -O2 -Wall -fno-strict-aliasing -I${PYTHONLIB}"
cython --no-docstrings -3 cytest/test.py -o cytest/test.c
gcc $CFLAGS -o cytest/test.so cytest/test.c

Now the output is:

(<class 'float'>, <class 'float'>)
(50.0, 50.0)
string being formatted=50
(<class 'float'>, <class 'float'>)
(50.0, 50.0)
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "cytest/test.py", line 16, in init cytest.test
    test_func_with_tuple(50.,50.)
  File "cytest/test.py", line 11, in cytest.test.test_func_with_tuple
    s = 'string being formatted=%d-%d' % (a,b)
ValueError: Unknown format code 'd' for object of type 'float'

This is the error I would get if did 1.2.__format__('d'), but it appears that in the other case cython isn’t using that function.

The relevant c code output by cython is:

  /* "cytest/test.py":4
 *   print((a.__class__, b.__class__))
 *   print((a, b))
 *   s = 'string being formatted=%d' % a             # <<<<<<<<<<<<<<
 *   print(s)
 * 
 */
  __pyx_t_3 = __Pyx_PyUnicode_FormatSafe(__pyx_kp_u_string_being_formatted_d, __pyx_v_a); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 4, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_v_s = ((PyObject*)__pyx_t_3);
  __pyx_t_3 = 0;

  /* "cytest/test.py":11
 *   print((a.__class__, b.__class__))
 *   print((a, b))
 *   s = 'string being formatted=%d-%d' % (a,b)             # <<<<<<<<<<<<<<
 *   print(s)
 * 
 */
  __pyx_t_3 = PyTuple_New(4); if (unlikely(!__pyx_t_3)) __PYX_ERR(0, 11, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_t_4 = 0;
  __pyx_t_5 = 127;
  __Pyx_INCREF(__pyx_kp_u_string_being_formatted);
  __pyx_t_4 += 23;
  __Pyx_GIVEREF(__pyx_kp_u_string_being_formatted);
  PyTuple_SET_ITEM(__pyx_t_3, 0, __pyx_kp_u_string_being_formatted);
  __pyx_t_2 = __Pyx_PyObject_Format(__pyx_v_a, __pyx_n_u_d); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 11, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_5 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_2) > __pyx_t_5) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_2) : __pyx_t_5;
  __pyx_t_4 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_2);
  __Pyx_GIVEREF(__pyx_t_2);
  PyTuple_SET_ITEM(__pyx_t_3, 1, __pyx_t_2);
  __pyx_t_2 = 0;
  __Pyx_INCREF(__pyx_kp_u_);
  __pyx_t_4 += 1;
  __Pyx_GIVEREF(__pyx_kp_u_);
  PyTuple_SET_ITEM(__pyx_t_3, 2, __pyx_kp_u_);
  __pyx_t_2 = __Pyx_PyObject_Format(__pyx_v_b, __pyx_n_u_d); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 11, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __pyx_t_5 = (__Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_2) > __pyx_t_5) ? __Pyx_PyUnicode_MAX_CHAR_VALUE(__pyx_t_2) : __pyx_t_5;
  __pyx_t_4 += __Pyx_PyUnicode_GET_LENGTH(__pyx_t_2);
  __Pyx_GIVEREF(__pyx_t_2);
  PyTuple_SET_ITEM(__pyx_t_3, 3, __pyx_t_2);
  __pyx_t_2 = 0;
  __pyx_t_2 = __Pyx_PyUnicode_Join(__pyx_t_3, 4, __pyx_t_4, __pyx_t_5); if (unlikely(!__pyx_t_2)) __PYX_ERR(0, 11, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
  __pyx_v_s = ((PyObject*)__pyx_t_2);
  __pyx_t_2 = 0;

It looks like the __Pyx_PyUnicode_FormatSafe function is being used only in the first case and not in the second, which may or may not be relevant. I am using Python 3.5.2.

If there is any other information needed let me know

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:3
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
scodercommented, Aug 23, 2019

Looks like this is a difference between %-formatting and format()/f-strings. Cython optimises some %-formatting cases into f-strings, that’s why this occurs.

I think the right fix would be to generate a bit of additional code that checks if the format value is an integer, and if not, passes it through PyNumber_Int(). Tests will show if the behaviour is the same in Py2 and Py3, and if both need this adaptation.

0reactions
scodercommented, May 8, 2020

I think I found a nice way of doing this in #3589.

Read more comments on GitHub >

github_iconTop Results From Across the Web

How To Use String Formatters in Python 3
This lets you concatenate elements together within a string through positional formatting. This tutorial will guide you through some of the ...
Read more >
Common string operations — Python 3.11.1 documentation
The built-in string class provides the ability to do complex variable substitutions and value formatting via the format() method described in ...
Read more >
How do I put a variable's value inside a string (interpolate it ...
To use the format string operator with multiple arguments, one can use a tuple as operand: 'foo %d, bar %d' % (foo, bar)...
Read more >
30. Errors and Exception Handling | Python Tutorial
Many programming languages like C++, Objective-C, PHP, Java, Ruby, Python, and many others have built-in support for exception handling.
Read more >
Python 3 - Quick Guide
Python 2 has two versions of input functions, input() and raw_input(). The input() function treats the received data as string if it is...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found