Skip to content

Commit e2c4038

Browse files
gh-76763: Make chr() always raising ValueError for out-of-range values (GH-114882)
Previously it raised OverflowError for very large or very small values.
1 parent 6e222a5 commit e2c4038

File tree

4 files changed

+32
-28
lines changed

4 files changed

+32
-28
lines changed

Lib/test/test_builtin.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -308,14 +308,13 @@ class C3(C2): pass
308308
self.assertTrue(callable(c3))
309309

310310
def test_chr(self):
311+
self.assertEqual(chr(0), '\0')
311312
self.assertEqual(chr(32), ' ')
312313
self.assertEqual(chr(65), 'A')
313314
self.assertEqual(chr(97), 'a')
314315
self.assertEqual(chr(0xff), '\xff')
315-
self.assertRaises(ValueError, chr, 1<<24)
316-
self.assertEqual(chr(sys.maxunicode),
317-
str('\\U0010ffff'.encode("ascii"), 'unicode-escape'))
318316
self.assertRaises(TypeError, chr)
317+
self.assertRaises(TypeError, chr, 65.0)
319318
self.assertEqual(chr(0x0000FFFF), "\U0000FFFF")
320319
self.assertEqual(chr(0x00010000), "\U00010000")
321320
self.assertEqual(chr(0x00010001), "\U00010001")
@@ -327,7 +326,11 @@ def test_chr(self):
327326
self.assertEqual(chr(0x0010FFFF), "\U0010FFFF")
328327
self.assertRaises(ValueError, chr, -1)
329328
self.assertRaises(ValueError, chr, 0x00110000)
330-
self.assertRaises((OverflowError, ValueError), chr, 2**32)
329+
self.assertRaises(ValueError, chr, 1<<24)
330+
self.assertRaises(ValueError, chr, 2**32-1)
331+
self.assertRaises(ValueError, chr, -2**32)
332+
self.assertRaises(ValueError, chr, 2**1000)
333+
self.assertRaises(ValueError, chr, -2**1000)
331334

332335
def test_cmp(self):
333336
self.assertTrue(not hasattr(builtins, "cmp"))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The :func:`chr` builtin function now always raises :exc:`ValueError` for
2+
values outside the valid range. Previously it raised :exc:`OverflowError` for
3+
very large or small values.

Python/bltinmodule.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -703,17 +703,34 @@ builtin_format_impl(PyObject *module, PyObject *value, PyObject *format_spec)
703703
/*[clinic input]
704704
chr as builtin_chr
705705
706-
i: int
706+
i: object
707707
/
708708
709709
Return a Unicode string of one character with ordinal i; 0 <= i <= 0x10ffff.
710710
[clinic start generated code]*/
711711

712712
static PyObject *
713-
builtin_chr_impl(PyObject *module, int i)
714-
/*[clinic end generated code: output=c733afcd200afcb7 input=3f604ef45a70750d]*/
713+
builtin_chr(PyObject *module, PyObject *i)
714+
/*[clinic end generated code: output=d34f25b8035a9b10 input=f919867f0ba2f496]*/
715715
{
716-
return PyUnicode_FromOrdinal(i);
716+
int overflow;
717+
long v = PyLong_AsLongAndOverflow(i, &overflow);
718+
if (v == -1 && PyErr_Occurred()) {
719+
return NULL;
720+
}
721+
if (overflow) {
722+
v = overflow < 0 ? INT_MIN : INT_MAX;
723+
/* Allow PyUnicode_FromOrdinal() to raise an exception */
724+
}
725+
#if SIZEOF_INT < SIZEOF_LONG
726+
else if (v < INT_MIN) {
727+
v = INT_MIN;
728+
}
729+
else if (v > INT_MAX) {
730+
v = INT_MAX;
731+
}
732+
#endif
733+
return PyUnicode_FromOrdinal(v);
717734
}
718735

719736

Python/clinic/bltinmodule.c.h

Lines changed: 1 addition & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)