diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index 0c640c2536..053c7bff22 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -529,8 +529,6 @@ def test_format_enum_date(self): self.assertFormatIsValue('{:%Y %m}', Holiday.IDES_OF_MARCH) self.assertFormatIsValue('{:%Y %m %M:00}', Holiday.IDES_OF_MARCH) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_format_enum_float(self): Konstants = self.Konstants self.assertFormatIsValue('{}', Konstants.TAU) diff --git a/Lib/test/test_format.py b/Lib/test/test_format.py index b95f592c7b..f6c11a4aad 100644 --- a/Lib/test/test_format.py +++ b/Lib/test/test_format.py @@ -420,8 +420,6 @@ def test_non_ascii(self): self.assertEqual(format(1+2j, "\u2007^8"), "\u2007(1+2j)\u2007") self.assertEqual(format(0j, "\u2007^4"), "\u20070j\u2007") - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_locale(self): try: oldloc = locale.setlocale(locale.LC_ALL) diff --git a/common/src/format.rs b/common/src/format.rs index 62c703c85f..24f296a764 100644 --- a/common/src/format.rs +++ b/common/src/format.rs @@ -126,7 +126,7 @@ pub enum FormatType { Character, Decimal, Octal, - Number, + Number(Case), Hex(Case), Exponent(Case), GeneralFormat(Case), @@ -142,7 +142,8 @@ impl From<&FormatType> for char { FormatType::Character => 'c', FormatType::Decimal => 'd', FormatType::Octal => 'o', - FormatType::Number => 'n', + FormatType::Number(Case::Lower) => 'n', + FormatType::Number(Case::Upper) => 'N', FormatType::Hex(Case::Lower) => 'x', FormatType::Hex(Case::Upper) => 'X', FormatType::Exponent(Case::Lower) => 'e', @@ -165,7 +166,8 @@ impl FormatParse for FormatType { Some('c') => (Some(Self::Character), chars.as_str()), Some('d') => (Some(Self::Decimal), chars.as_str()), Some('o') => (Some(Self::Octal), chars.as_str()), - Some('n') => (Some(Self::Number), chars.as_str()), + Some('n') => (Some(Self::Number(Case::Lower)), chars.as_str()), + Some('N') => (Some(Self::Number(Case::Upper)), chars.as_str()), Some('x') => (Some(Self::Hex(Case::Lower)), chars.as_str()), Some('X') => (Some(Self::Hex(Case::Upper)), chars.as_str()), Some('e') => (Some(Self::Exponent(Case::Lower)), chars.as_str()), @@ -367,14 +369,14 @@ impl FormatSpec { | FormatType::Binary | FormatType::Octal | FormatType::Hex(_) - | FormatType::Number, + | FormatType::Number(_), ) => { let ch = char::from(format_type); Err(FormatSpecError::UnspecifiedFormat(',', ch)) } ( Some(FormatGrouping::Underscore), - FormatType::String | FormatType::Character | FormatType::Number, + FormatType::String | FormatType::Character | FormatType::Number(_), ) => { let ch = char::from(format_type); Err(FormatSpecError::UnspecifiedFormat('_', ch)) @@ -386,7 +388,7 @@ impl FormatSpec { fn get_separator_interval(&self) -> usize { match self.format_type { Some(FormatType::Binary | FormatType::Octal | FormatType::Hex(_)) => 4, - Some(FormatType::Decimal | FormatType::Number | FormatType::FixedPoint(_)) => 3, + Some(FormatType::Decimal | FormatType::Number(_) | FormatType::FixedPoint(_)) => 3, None => 3, _ => panic!("Separators only valid for numbers!"), } @@ -430,12 +432,12 @@ impl FormatSpec { | Some(FormatType::Octal) | Some(FormatType::Hex(_)) | Some(FormatType::String) - | Some(FormatType::Character) => { + | Some(FormatType::Character) + | Some(FormatType::Number(Case::Upper)) => { let ch = char::from(self.format_type.as_ref().unwrap()); Err(FormatSpecError::UnknownFormatCode(ch, "float")) } - Some(FormatType::Number) => Err(FormatSpecError::NotImplemented('n', "float")), - Some(FormatType::GeneralFormat(case)) => { + Some(FormatType::GeneralFormat(case)) | Some(FormatType::Number(case)) => { let precision = if precision == 0 { 1 } else { precision }; Ok(float_ops::format_general( precision, @@ -531,7 +533,10 @@ impl FormatSpec { Ok(result) } }, - Some(FormatType::Number) => self.format_int_radix(magnitude, 10), + Some(FormatType::Number(Case::Lower)) => self.format_int_radix(magnitude, 10), + Some(FormatType::Number(Case::Upper)) => { + Err(FormatSpecError::UnknownFormatCode('N', "int")) + } Some(FormatType::String) => Err(FormatSpecError::UnknownFormatCode('s', "int")), Some(FormatType::Character) => match (self.sign, self.alternate_form) { (Some(_), _) => Err(FormatSpecError::NotAllowed("Sign")), diff --git a/extra_tests/snippets/builtin_str.py b/extra_tests/snippets/builtin_str.py index 8adf97b731..cd7133e355 100644 --- a/extra_tests/snippets/builtin_str.py +++ b/extra_tests/snippets/builtin_str.py @@ -611,6 +611,35 @@ def try_mutate_str(): assert '{:g}'.format(1.020e-13) == '1.02e-13' assert "{:g}".format(1.020e-4) == '0.000102' +# Test n & N formatting +assert '{:n}'.format(999999.1234) == '999999' +assert '{:n}'.format(9999.1234) == '9999.12' +assert '{:n}'.format(-1000000.1234) == '-1e+06' +assert '{:n}'.format(1000000.1234) == '1e+06' +assert '{:.1n}'.format(1000000.1234) == '1e+06' +assert '{:.2n}'.format(1000000.1234) == '1e+06' +assert '{:.3n}'.format(1000000.1234) == '1e+06' +assert '{:.4n}'.format(1000000.1234) == '1e+06' +assert '{:.5n}'.format(1000000.1234) == '1e+06' +assert '{:.6n}'.format(1000000.1234) == '1e+06' +assert '{:.7n}'.format(1000000.1234) == '1000000' +assert '{:.8n}'.format(1000000.1234) == '1000000.1' +assert '{:.10n}'.format(1000000.1234) == '1000000.123' +assert '{:.11n}'.format(1000000.1234) == '1000000.1234' +assert '{:.11n}'.format(-1000000.1234) == '-1000000.1234' +assert '{:0n}'.format(-1000000.1234) == '-1e+06' +assert '{:n}'.format(-1000000.1234) == '-1e+06' +assert '{:-1n}'.format(-1000000.1234) == '-1e+06' + +with AssertRaises(ValueError, msg="Unknown format code 'N' for object of type 'float'"): + '{:N}'.format(999999.1234) +with AssertRaises(ValueError, msg="Unknown format code 'N' for object of type 'float'"): + '{:.1N}'.format(1000000.1234) +with AssertRaises(ValueError, msg="Unknown format code 'N' for object of type 'float'"): + '{:0N}'.format(-1000000.1234) +with AssertRaises(ValueError, msg="Unknown format code 'N' for object of type 'float'"): + '{:-1N}'.format(-1000000.1234) + # remove*fix test def test_removeprefix(): s = 'foobarfoo'