Uh oh!
There was an error while loading. Please reload this page.
gh-143050: correct PyLong_FromString() to use _PyLong_Negate() - #145901
Conversation
The long_from_string_base() might return a small integer, when the _pylong.py is used to do conversion. Hence, we must be careful here to not smash it "small int" bit by using the _PyLong_FlipSign().
skirpichev
commented
Mar 13, 2026
Shouldn't affect users, so - no news. |
skirpichev
commented
Mar 13, 2026
|
| with support.adjust_int_max_str_digits(0): | ||
| a = int('-' + '0' * 7000, 10) | ||
| del a | ||
| _testcapi.test_immortal_small_ints() |
There was a problem hiding this comment.
That's okay as long as it catches the regression in a debug build.
There was a problem hiding this comment.
_testcapi is always built with assertions, even in release mode.
There was a problem hiding this comment.
I built Python in release mode without PyLong_FromString() fix. _PyLong_FlipSign() assertion didn't catch the bug since Python was built without assertions. But at least, test_immortal_small_ints() was able to detect that small integers were corrupted:
test_bug_143050 (test.test_capi.test_long.LongTests.test_bug_143050) ...
python: ./Modules/_testcapi/immortal.c:35: test_immortal_small_ints: Assertion `has_int_immortal_bit' failed.
Fatal Python error: Aborted
Current thread 0x00007f617b91b780 [python] (most recent call first):
File "/home/vstinner/python/main/Lib/test/test_capi/test_long.py", line 811 in test_bug_143050
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
eendebakpt
left a comment
There was a problem hiding this comment.
Minor comment on the naming. But the PR fixes a bug, adds a test and cleans up along the way, so good enough for me!
Uh oh!
There was an error while loading. Please reload this page.
colesbury
left a comment
There was a problem hiding this comment.
Looks good to me. Small comment about the function name below.
| with support.adjust_int_max_str_digits(0): | ||
| a = int('-' + '0' * 7000, 10) | ||
| del a | ||
| _testcapi.test_immortal_small_ints() |
There was a problem hiding this comment.
That's okay as long as it catches the regression in a debug build.
Uh oh!
There was an error while loading. Please reload this page.
colesbury
left a comment
There was a problem hiding this comment.
Let me know if you change the function name or keep it as is.
e28e94c to
78c5b4bCompare@colesbury, I did renaming.
|
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| /* Return true if the argument is a small int */ | ||
| static inline bool | ||
| _PyLong_HasImmortalTag(const PyLongObject *op) |
There was a problem hiding this comment.
IMO the intent here is to check if an integer is a small integer, rather than checking for a tag. So I would prefer to rename the function to _PyLong_IsSmallInt().
I wanted to suggest referring to _PY_IS_SMALL_INT() but I noticed the macro is wrong. I wrote #146631 to fix the macro.
There was a problem hiding this comment.
There are two notions for "small int"'s. One - by value and other is - this. If @colesbury is ok - I'm fine with renaming..
There was a problem hiding this comment.
_PyLong_HasImmortalTag() and _PY_IS_SMALL_INT() should be true or false for the same values.
For example, you can add the following check for _PyLong_HasImmortalTag():
+ assert((_PyLong_IsCompact(op) && _PY_IS_SMALL_INT(_PyLong_CompactValue(op)))
+ || (!is_small_int));
(You need to update your branch to retrieve my _PY_IS_SMALL_INT() change.)
For me, testing IMMORTALITY_BIT_MASK is just an implementation detail to check if an integer is "a small int".
There was a problem hiding this comment.
I've added an assert. BTW, what do you think on #145901 (comment)?
I don't think we should mention _PY_IS_SMALL_INT in the description of the function, assert is readable enough.
For me, testing IMMORTALITY_BIT_MASK is just an implementation detail to check if an integer is "a small int".
For now, we use _PY_IS_SMALL_INT() to check this. But eventually this could be just testing a flag.
There was a problem hiding this comment.
I would prefer to rename the function to _PyLong_IsSmallInt().
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
vstinner
commented
Mar 31, 2026
It looks unrelated to this change. I just re-ran the CI. |
vstinner
commented
Mar 31, 2026
To backport this change to 3.14, |
skirpichev
commented
Mar 31, 2026
Why we need a backport? |
vstinner
commented
Mar 31, 2026
This change uses |
Thanks @skirpichev for the PR, and @vstinner for merging it 🌮🎉.. I'm working now to backport this PR to: 3.14. |
GH-147331 is a backport of this pull request to the 3.14 branch. |
…ythonGH-145901) The long_from_string_base() might return a small integer, when the _pylong.py is used to do conversion. Hence, we must be careful here to not smash it "small int" bit by using the _PyLong_FlipSign(). (cherry picked from commit db5936c) Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
GH-147437 is a backport of this pull request to the 3.13 branch. |
…GH-145901) (#147331) gh-143050: Correct PyLong_FromString() to use _PyLong_Negate() (GH-145901) The long_from_string_base() might return a small integer, when the _pylong.py is used to do conversion. Hence, we must be careful here to not smash it "small int" bit by using the _PyLong_FlipSign(). (cherry picked from commit db5936c) Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
…#145901) (#147437) The long_from_string_base() might return a small integer, when the _pylong.py is used to do conversion. Hence, we must be careful here to not smash it "small int" bit by using the _PyLong_FlipSign(). Co-authored-by: Victor Stinner <vstinner@python.org> (cherry picked from commit db5936c) Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
…ython#145901) The long_from_string_base() might return a small integer, when the _pylong.py is used to do conversion. Hence, we must be careful here to not smash it "small int" bit by using the _PyLong_FlipSign(). Co-authored-by: Victor Stinner <vstinner@python.org>
…ython#145901) The long_from_string_base() might return a small integer, when the _pylong.py is used to do conversion. Hence, we must be careful here to not smash it "small int" bit by using the _PyLong_FlipSign(). Co-authored-by: Victor Stinner <vstinner@python.org>
The long_from_string_base() might return a small integer, when the _pylong.py is used to do conversion. Hence, we must be careful here to not smash it "small int" bit by using the _PyLong_FlipSign().
Co-authored-by: Sam Gross colesbury@gmail.com