Skip to content

Commit 68aece5

Browse files
Reject format-string field index above Py_ssize_t::MAX (RustPython#7708)
CPython rejects digit-only format-string field names that overflow Py_ssize_t at parse time with ValueError: Too many decimal digits in format string (Python/string_parser.c::get_integer). RustPython's FieldName::parse accepted any digit string usize::from_str could parse, producing IndexError or KeyError at lookup instead. Cap the parsed index at isize::MAX (Py_ssize_t::MAX on every platform) inside FieldName::parse. Also reject digits-only strings whose value overflows usize itself (caught when parse_usize returns None on an all-digit input). A new FormatParseError::TooManyDecimalDigits maps to the byte-identical CPython wording. Unmasks test_str.StrTest.test_format_huge_item_number.
1 parent b3d6d2f commit 68aece5

3 files changed

Lines changed: 16 additions & 1 deletion

File tree

Lib/test/test_str.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1468,7 +1468,6 @@ def test_format_huge_width(self):
14681468
with self.assertRaises(ValueError):
14691469
result = format(2.34, format_string)
14701470

1471-
@unittest.expectedFailure # TODO: RUSTPYTHON; IndexError: tuple index out of range
14721471
def test_format_huge_item_number(self):
14731472
format_string = "{{{}:.6f}}".format(sys.maxsize + 1)
14741473
with self.assertRaises(ValueError):

crates/common/src/format.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,6 +1107,7 @@ pub enum FormatParseError {
11071107
EmptyAttribute,
11081108
MissingRightBracket,
11091109
InvalidCharacterAfterRightBracket,
1110+
TooManyDecimalDigits,
11101111
}
11111112

11121113
impl FromStr for FormatSpec {
@@ -1190,7 +1191,19 @@ impl FieldName {
11901191
let field_type = if first.is_empty() {
11911192
FieldType::Auto
11921193
} else if let Some(index) = parse_usize(&first) {
1194+
// Match CPython's get_integer: digits-only segment must fit in
1195+
// Py_ssize_t. Above that, raise ValueError at parse time.
1196+
if index > isize::MAX as usize {
1197+
return Err(FormatParseError::TooManyDecimalDigits);
1198+
}
11931199
FieldType::Index(index)
1200+
} else if first
1201+
.as_str()
1202+
.ok()
1203+
.is_some_and(|s| s.bytes().all(|b| b.is_ascii_digit()))
1204+
{
1205+
// All-digit segment whose value overflows usize itself.
1206+
return Err(FormatParseError::TooManyDecimalDigits);
11941207
} else {
11951208
FieldType::Keyword(first)
11961209
};

crates/vm/src/format.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ impl ToPyException for FormatParseError {
114114
fn to_pyexception(&self, vm: &VirtualMachine) -> PyBaseExceptionRef {
115115
match self {
116116
Self::UnmatchedBracket => vm.new_value_error("expected '}' before end of string"),
117+
Self::TooManyDecimalDigits => {
118+
vm.new_value_error("Too many decimal digits in format string")
119+
}
117120
_ => vm.new_value_error("Unexpected error parsing format string"),
118121
}
119122
}

0 commit comments

Comments
 (0)