Skip to content

Conversation

@makukha
Copy link

@makukha makukha commented Sep 23, 2024

Fixes #124294.

TypeError is raised instead of ValueError for backward compatibility with current behaviour.
Detailed error message includes invalid string index value to avoid ambiguity.
Without these details, user might be confused by error pointing to the wrong place.

>>> '{x[-1]}'.format(x=[0,1,2])
Traceback (most recent call last):
  File "<python-input-0>", line 1, in <module>
    '{x[-1]}'.format(x=[0,1,2])
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^
TypeError: Index must be a sequence of digits, string '-1' is not allowed

@ghost
Copy link

ghost commented Sep 23, 2024

All commit authors signed the Contributor License Agreement.
CLA signed

@bedevere-app
Copy link

bedevere-app bot commented Sep 23, 2024

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@makukha
Copy link
Author

makukha commented Sep 23, 2024

@ericvsmith I think I've found the right place for the check.

@rruuaanng
Copy link
Contributor

rruuaanng commented Sep 24, 2024

Maybe you should added NEWS, Used to describe your changes.

Copy link
Contributor

@skirpichev skirpichev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And you, probably, should add a test, right?

Comment on lines 1392 to 1393
self.assertRaises(TypeError, "{[s]}".format, "abc")
self.assertRaises(TypeError, "{[-1]}".format, "abc")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something, that already fails with TypeError in the main. I think you should test actual error messages with assertRaises's msg argument or using assertRaisesRegex().

if (PySequence_Check(obj)) {
/* string index can't be passed to sequence */
PyObject *str = SubString_new_object(&name);
if (str != NULL)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (str != NULL)
if (str)

Also beware PEP 7: "braces are required everywhere, even where C permits them to be omitted".

@makukha makukha requested a review from skirpichev September 24, 2024 14:56
@ericvsmith
Copy link
Member

I still am not convinced this is a good idea. I don't think it's worth complicating things to get a slightly better error message. For example, is changing all possible errors from SubString_new_object actually correct? What about out of memory errors?

self.assertEqual("{!s}".format(n), 'N(data)')
self.assertRaises(TypeError, "{}".format, n)

# String index for sequences
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could delete this comment since the user won't see it anyway.

@makukha
Copy link
Author

makukha commented Sep 25, 2024

@ericvsmith I can't say if it's worth, but this issue has quite a long history already:

As long as str.format() behaviour and documentation for negative indices (and slices) is (IMO) misleading, this list will grow unless something is changed. I understand that current behaviour is there for a long time, and fixing this bug (?) would become a breaking change, but at least people could get informative message to start digging into docs.

I'm not totally satisfied with proposed message

Index must be a sequence of digits, string '-1' is not allowed

The index value could be omitted to not complicate things, as you suggested (no intermediate str object will be created at runtime), giving

Sequence index can only contain digits 0 to 9

Or there is a better wording?

@ericvsmith
Copy link
Member

This code works on 3.12, but fails with your patch:

class F:
    def __getitem__(self, key):
        return f'key:{key}'

f = F()

print(f['foo'])
print('{f[foo]}'.format(f=f))

With 3.12, this gives:

key:foo
key:foo

With this PR, it gives:

key:foo
Traceback (most recent call last):
  File "str-format-negative.py", line 8, in <module>
    print('{f[foo]}'.format(f=f))
          ~~~~~~~~~~~~~~~~~^^^^^
TypeError: Index must be a sequence of digits, string 'foo' is not allowed

@ericvsmith ericvsmith self-assigned this Sep 25, 2024
@makukha
Copy link
Author

makukha commented Sep 25, 2024

@ericvsmith now I see how naive was my attempt. The only solution could be parsing negative integers from the very beginning, that is already impossible. Thank you for your clarification and hints which I didn't take seriously.

@ericvsmith
Copy link
Member

No problem, @makukha! I happen to be very familiar with this code, and it's not reasonable for everyone to know all the ins and outs. I'm glad you learned something, and please don't be discouraged from continuing to contribute. If you agree, I'll close this, and hopefully people can find it and refer to it in the future.

But if you want to leave it open and continue to try and find a way to improve this (anything is possible!), then that's okay, too.

@skirpichev skirpichev removed their request for review September 26, 2024 03:14
@makukha
Copy link
Author

makukha commented Sep 26, 2024

@ericvsmith, being realistic, I think this PR should be closed. Thank you for your warm attitude!

@ericvsmith ericvsmith closed this Sep 26, 2024
@makukha makukha deleted the str-format-negative branch May 7, 2025 12:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

str.format() raises misleading error when using negative indices

4 participants