Skip to content

Commit c585541

Browse files
committed
Fix complex 'n' format and remove locale expectedFailure markers
Rewrite format_complex_locale to reuse format_complex_re_im, matching formatter_unicode.c: add_parens=0 and skip_re=0 for 'n' type. Remove @expectedfailure from test_float__format__locale and test_int__format__locale in test_types.py.
1 parent d79baa5 commit c585541

File tree

2 files changed

+29
-68
lines changed

2 files changed

+29
-68
lines changed

Lib/test/test_types.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,6 @@ def test(i, format_spec, result):
431431
test(123456, "1=20", '11111111111111123456')
432432
test(123456, "*=20", '**************123456')
433433

434-
@unittest.expectedFailure # TODO: RUSTPYTHON; + 1234.57
435434
@run_with_locale('LC_NUMERIC', 'en_US.UTF8', '')
436435
def test_float__format__locale(self):
437436
# test locale support for __format__ code 'n'
@@ -441,7 +440,6 @@ def test_float__format__locale(self):
441440
self.assertEqual(locale.format_string('%g', x, grouping=True), format(x, 'n'))
442441
self.assertEqual(locale.format_string('%.10g', x, grouping=True), format(x, '.10n'))
443442

444-
@unittest.expectedFailure # TODO: RUSTPYTHON; + 123456789012345678901234567890
445443
@run_with_locale('LC_NUMERIC', 'en_US.UTF8', '')
446444
def test_int__format__locale(self):
447445
# test locale support for __format__ code 'n' for integers

crates/common/src/format.rs

Lines changed: 29 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -610,84 +610,47 @@ impl FormatSpec {
610610
num: &Complex64,
611611
locale: &LocaleInfo,
612612
) -> Result<String, FormatSpecError> {
613-
// For complex, format real and imaginary parts separately with locale
614-
if self.alternate_form {
615-
return Err(FormatSpecError::NotAllowed("Alternate form (#)"));
616-
}
617-
if matches!(
618-
self.format_type,
619-
Some(
620-
FormatType::String
621-
| FormatType::Binary
622-
| FormatType::Octal
623-
| FormatType::Hex(_)
624-
| FormatType::Character
625-
| FormatType::Decimal
626-
| FormatType::Number(Case::Upper)
627-
| FormatType::Unknown(_)
628-
)
629-
) {
630-
let ch = char::from(self.format_type.as_ref().unwrap());
631-
return Err(FormatSpecError::UnknownFormatCode(ch, "complex"));
632-
}
633-
634-
let precision = self.precision.unwrap_or(6);
635-
let format_type = match self.format_type {
636-
Some(FormatType::Number(case)) => FormatType::GeneralFormat(case),
637-
_ => return self.format_complex(num),
638-
};
639-
640-
let magnitude_format = FormatSpec {
641-
format_type: Some(format_type),
642-
precision: Some(if precision == 0 { 1 } else { precision }),
613+
// Reuse format_complex_re_im with 'g' type to get the base formatted parts,
614+
// then apply locale grouping. This matches CPython's format_complex_internal:
615+
// 'n' → 'g', add_parens=0, skip_re=0.
616+
let locale_spec = FormatSpec {
617+
format_type: Some(FormatType::GeneralFormat(Case::Lower)),
643618
..*self
644619
};
620+
let (formatted_re, formatted_im) = locale_spec.format_complex_re_im(num)?;
645621

646-
let re_str = if num.re == 0.0 && !num.re.is_sign_negative() {
647-
None
622+
// Apply locale grouping to both parts
623+
let grouped_re = if formatted_re.is_empty() {
624+
formatted_re
648625
} else {
649-
let re_sign = if num.re.is_sign_negative() && !num.re.is_nan() {
650-
"-"
626+
// Split sign from magnitude, apply grouping, recombine
627+
let (sign, mag) = if formatted_re.starts_with('-')
628+
|| formatted_re.starts_with('+')
629+
|| formatted_re.starts_with(' ')
630+
{
631+
formatted_re.split_at(1)
651632
} else {
652-
""
633+
("", formatted_re.as_str())
653634
};
654-
let re_mag = magnitude_format.format_float(num.re.abs())?;
655-
let re_grouped = Self::apply_locale_formatting(re_mag, locale);
656-
Some(format!("{re_sign}{re_grouped}"))
657-
};
658-
659-
let im_sign = if num.im.is_sign_negative() && !num.im.is_nan() {
660-
"-"
661-
} else {
662-
""
635+
format!("{sign}{}", Self::apply_locale_formatting(mag.to_string(), locale))
663636
};
664-
let im_mag = magnitude_format.format_float(num.im.abs())?;
665-
let im_grouped = Self::apply_locale_formatting(im_mag, locale);
666637

667-
let has_re = re_str.is_some();
668-
let formatted = if let Some(re_str) = re_str {
669-
let sep = if num.im >= 0.0 || num.im.is_nan() {
670-
"+"
671-
} else {
672-
""
673-
};
674-
format!("({re_str}{sep}{im_sign}{im_grouped}j)")
638+
// formatted_im is like "+1234j" or "-1234j" or "1234j"
639+
// Split sign, magnitude, and 'j' suffix
640+
let im_str = &formatted_im;
641+
let (im_sign, im_rest) = if im_str.starts_with('+') || im_str.starts_with('-') {
642+
im_str.split_at(1)
675643
} else {
676-
format!("{im_sign}{im_grouped}j")
644+
("", im_str.as_str())
677645
};
646+
let im_mag = im_rest.strip_suffix('j').unwrap_or(im_rest);
647+
let im_grouped = Self::apply_locale_formatting(im_mag.to_string(), locale);
648+
let grouped_im = format!("{im_sign}{im_grouped}j");
678649

679-
let format_sign = self.sign.unwrap_or(FormatSign::Minus);
680-
let sign_str = match format_sign {
681-
FormatSign::Plus => "+",
682-
FormatSign::Minus => "",
683-
FormatSign::MinusOrSpace => " ",
684-
};
650+
// No parentheses for 'n' format (CPython: add_parens=0)
651+
let magnitude_str = format!("{grouped_re}{grouped_im}");
685652

686-
self.format_sign_and_align(
687-
&AsciiStr::new(&formatted),
688-
if has_re { "" } else { sign_str },
689-
FormatAlign::Right,
690-
)
653+
self.format_sign_and_align(&AsciiStr::new(&magnitude_str), "", FormatAlign::Right)
691654
}
692655

693656
pub fn format_bool(&self, input: bool) -> Result<String, FormatSpecError> {

0 commit comments

Comments
 (0)