| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent b08a53a commit 0923d1d
16 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -91,24 +91,22 @@ PyAPI_FUNC(int) PyBytes_AsStringAndSize( | |||
| 91 | 91 | into the string pointed to by buffer. For the argument descriptions, | |
| 92 | 92 | see Objects/stringlib/localeutil.h */ | |
| 93 | 93 | ||
| 94 | - PyAPI_FUNC(int) _PyBytes_InsertThousandsGroupingLocale(char *buffer, | ||
| 95 | - Py_ssize_t n_buffer, | ||
| 96 | - Py_ssize_t n_digits, | ||
| 97 | - Py_ssize_t buf_size, | ||
| 98 | - Py_ssize_t *count, | ||
| 99 | - int append_zero_char); | ||
| 94 | + PyAPI_FUNC(Py_ssize_t) _PyBytes_InsertThousandsGroupingLocale(char *buffer, | ||
| 95 | + Py_ssize_t n_buffer, | ||
| 96 | + char *digits, | ||
| 97 | + Py_ssize_t n_digits, | ||
| 98 | + Py_ssize_t min_width); | ||
| 100 | 99 | ||
| 101 | 100 | /* Using explicit passed-in values, insert the thousands grouping | |
| 102 | 101 | into the string pointed to by buffer. For the argument descriptions, | |
| 103 | 102 | see Objects/stringlib/localeutil.h */ | |
| 104 | - PyAPI_FUNC(int) _PyBytes_InsertThousandsGrouping(char *buffer, | ||
| 105 | - Py_ssize_t n_buffer, | ||
| 106 | - Py_ssize_t n_digits, | ||
| 107 | - Py_ssize_t buf_size, | ||
| 108 | - Py_ssize_t *count, | ||
| 109 | - int append_zero_char, | ||
| 110 | - const char *grouping, | ||
| 111 | - const char *thousands_sep); | ||
| 103 | + PyAPI_FUNC(Py_ssize_t) _PyBytes_InsertThousandsGrouping(char *buffer, | ||
| 104 | + Py_ssize_t n_buffer, | ||
| 105 | + char *digits, | ||
| 106 | + Py_ssize_t n_digits, | ||
| 107 | + Py_ssize_t min_width, | ||
| 108 | + const char *grouping, | ||
| 109 | + const char *thousands_sep); | ||
| 112 | 110 | ||
| 113 | 111 | /* Flags used by string formatting */ | |
| 114 | 112 | #define F_LJUST (1<<0) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -10,6 +10,25 @@ PyAPI_FUNC(double) PyOS_ascii_strtod(const char *str, char **ptr); | |||
| 10 | 10 | PyAPI_FUNC(double) PyOS_ascii_atof(const char *str); | |
| 11 | 11 | PyAPI_FUNC(char *) PyOS_ascii_formatd(char *buffer, size_t buf_len, const char *format, double d); | |
| 12 | 12 | ||
| 13 | + /* The caller is responsible for calling PyMem_Free to free the buffer | ||
| 14 | + that's is returned. */ | ||
| 15 | + PyAPI_FUNC(char *) PyOS_double_to_string(double val, | ||
| 16 | + char format_code, | ||
| 17 | + int precision, | ||
| 18 | + int flags, | ||
| 19 | + int *type); | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + /* PyOS_double_to_string's "flags" parameter can be set to 0 or more of: */ | ||
| 23 | + #define Py_DTSF_SIGN 0x01 /* always add the sign */ | ||
| 24 | + #define Py_DTSF_ADD_DOT_0 0x02 /* if the result is an integer add ".0" */ | ||
| 25 | + #define Py_DTSF_ALT 0x04 /* "alternate" formatting. it's format_code | ||
| 26 | + specific */ | ||
| 27 | + | ||
| 28 | + /* PyOS_double_to_string's "type", if non-NULL, will be set to one of: */ | ||
| 29 | + #define Py_DTST_FINITE 0 | ||
| 30 | + #define Py_DTST_INFINITE 1 | ||
| 31 | + #define Py_DTST_NAN 2 | ||
| 13 | 32 | ||
| 14 | 33 | #ifdef __cplusplus | |
| 15 | 34 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1482,24 +1482,22 @@ PyAPI_FUNC(PyObject *) _PyUnicode_XStrip( | |||
| 1482 | 1482 | into the string pointed to by buffer. For the argument descriptions, | |
| 1483 | 1483 | see Objects/stringlib/localeutil.h */ | |
| 1484 | 1484 | ||
| 1485 | - PyAPI_FUNC(int) _PyUnicode_InsertThousandsGroupingLocale(Py_UNICODE *buffer, | ||
| 1486 | - Py_ssize_t n_buffer, | ||
| 1487 | - Py_ssize_t n_digits, | ||
| 1488 | - Py_ssize_t buf_size, | ||
| 1489 | - Py_ssize_t *count, | ||
| 1490 | - int append_zero_char); | ||
| 1485 | + PyAPI_FUNC(Py_ssize_t) _PyUnicode_InsertThousandsGroupingLocale(Py_UNICODE *buffer, | ||
| 1486 | + Py_ssize_t n_buffer, | ||
| 1487 | + Py_UNICODE *digits, | ||
| 1488 | + Py_ssize_t n_digits, | ||
| 1489 | + Py_ssize_t min_width); | ||
| 1491 | 1490 | ||
| 1492 | 1491 | /* Using explicit passed-in values, insert the thousands grouping | |
| 1493 | 1492 | into the string pointed to by buffer. For the argument descriptions, | |
| 1494 | 1493 | see Objects/stringlib/localeutil.h */ | |
| 1495 | - PyAPI_FUNC(int) _PyUnicode_InsertThousandsGrouping(Py_UNICODE *buffer, | ||
| 1496 | - Py_ssize_t n_buffer, | ||
| 1497 | - Py_ssize_t n_digits, | ||
| 1498 | - Py_ssize_t buf_size, | ||
| 1499 | - Py_ssize_t *count, | ||
| 1500 | - int append_zero_char, | ||
| 1501 | - const char *grouping, | ||
| 1502 | - const char *thousands_sep); | ||
| 1494 | + PyAPI_FUNC(Py_ssize_t) _PyUnicode_InsertThousandsGrouping(Py_UNICODE *buffer, | ||
| 1495 | + Py_ssize_t n_buffer, | ||
| 1496 | + Py_UNICODE *digits, | ||
| 1497 | + Py_ssize_t n_digits, | ||
| 1498 | + Py_ssize_t min_width, | ||
| 1499 | + const char *grouping, | ||
| 1500 | + const char *thousands_sep); | ||
| 1503 | 1501 | /* === Characters Type APIs =============================================== */ | |
| 1504 | 1502 | ||
| 1505 | 1503 | /* Helper array used by Py_UNICODE_ISSPACE(). */ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,6 +1,7 @@ | |||
| 1 | 1 | ||
| 2 | 2 | import unittest, struct | |
| 3 | 3 | import os | |
| 4 | + import sys | ||
| 4 | 5 | from test import support | |
| 5 | 6 | import math | |
| 6 | 7 | from math import isinf, isnan, copysign, ldexp | |
@@ -10,6 +11,10 @@ | |||
| 10 | 11 | INF = float("inf") | |
| 11 | 12 | NAN = float("nan") | |
| 12 | 13 | ||
| 14 | + #locate file with float format test values | ||
| 15 | + test_dir = os.path.dirname(__file__) or os.curdir | ||
| 16 | + format_testfile = os.path.join(test_dir, 'formatfloat_testcases.txt') | ||
| 17 | + | ||
| 13 | 18 | class GeneralFloatCases(unittest.TestCase): | |
| 14 | 19 | ||
| 15 | 20 | def test_float(self): | |
@@ -24,6 +29,10 @@ def test_float(self): | |||
| 24 | 29 | self.assertRaises(ValueError, float, "+-3.14") | |
| 25 | 30 | self.assertRaises(ValueError, float, "-+3.14") | |
| 26 | 31 | self.assertRaises(ValueError, float, "--3.14") | |
| 32 | + self.assertRaises(ValueError, float, ".nan") | ||
| 33 | + self.assertRaises(ValueError, float, "+.inf") | ||
| 34 | + self.assertRaises(ValueError, float, ".") | ||
| 35 | + self.assertRaises(ValueError, float, "-.") | ||
| 27 | 36 | self.assertEqual(float(b" \u0663.\u0661\u0664 ".decode('raw-unicode-escape')), 3.14) | |
| 28 | 37 | ||
| 29 | 38 | @support.run_with_locale('LC_NUMERIC', 'fr_FR', 'de_DE') | |
@@ -316,6 +325,73 @@ def test_repr(self): | |||
| 316 | 325 | self.assertEqual(v, eval(repr(v))) | |
| 317 | 326 | floats_file.close() | |
| 318 | 327 | ||
| 328 | + class FormatTestCase(unittest.TestCase): | ||
| 329 | + @unittest.skipUnless(float.__getformat__("double").startswith("IEEE"), | ||
| 330 | + "test requires IEEE 754 doubles") | ||
| 331 | + def test_format_testfile(self): | ||
| 332 | + for line in open(format_testfile): | ||
| 333 | + if line.startswith('--'): | ||
| 334 | + continue | ||
| 335 | + line = line.strip() | ||
| 336 | + if not line: | ||
| 337 | + continue | ||
| 338 | + | ||
| 339 | + lhs, rhs = map(str.strip, line.split('->')) | ||
| 340 | + fmt, arg = lhs.split() | ||
| 341 | + self.assertEqual(fmt % float(arg), rhs) | ||
| 342 | + self.assertEqual(fmt % -float(arg), '-' + rhs) | ||
| 343 | + | ||
| 344 | + @unittest.skipUnless(getattr(sys, 'float_repr_style', '') == 'short', | ||
| 345 | + "applies only when using short float repr style") | ||
| 346 | + def test_short_repr(self): | ||
| 347 | + # test short float repr introduced in Python 3.1. One aspect | ||
| 348 | + # of this repr is that we get some degree of str -> float -> | ||
| 349 | + # str roundtripping. In particular, for any numeric string | ||
| 350 | + # containing 15 or fewer significant digits, those exact same | ||
| 351 | + # digits (modulo trailing zeros) should appear in the output. | ||
| 352 | + # No more repr(0.03) -> "0.029999999999999999"! | ||
| 353 | + | ||
| 354 | + test_strings = [ | ||
| 355 | + # output always includes *either* a decimal point and at | ||
| 356 | + # least one digit after that point, or an exponent. | ||
| 357 | + '0.0', | ||
| 358 | + '1.0', | ||
| 359 | + '0.01', | ||
| 360 | + '0.02', | ||
| 361 | + '0.03', | ||
| 362 | + '0.04', | ||
| 363 | + '0.05', | ||
| 364 | + '1.23456789', | ||
| 365 | + '10.0', | ||
| 366 | + '100.0', | ||
| 367 | + # values >= 1e16 get an exponent... | ||
| 368 | + '1000000000000000.0', | ||
| 369 | + '9999999999999990.0', | ||
| 370 | + '1e+16', | ||
| 371 | + '1e+17', | ||
| 372 | + # ... and so do values < 1e-4 | ||
| 373 | + '0.001', | ||
| 374 | + '0.001001', | ||
| 375 | + '0.00010000000000001', | ||
| 376 | + '0.0001', | ||
| 377 | + '9.999999999999e-05', | ||
| 378 | + '1e-05', | ||
| 379 | + # values designed to provoke failure if the FPU rounding | ||
| 380 | + # precision isn't set correctly | ||
| 381 | + '8.72293771110361e+25', | ||
| 382 | + '7.47005307342313e+26', | ||
| 383 | + '2.86438000439698e+28', | ||
| 384 | + '8.89142905246179e+28', | ||
| 385 | + '3.08578087079232e+35', | ||
| 386 | + ] | ||
| 387 | + | ||
| 388 | + for s in test_strings: | ||
| 389 | + negs = '-'+s | ||
| 390 | + self.assertEqual(s, repr(float(s))) | ||
| 391 | + self.assertEqual(negs, repr(float(negs))) | ||
| 392 | + | ||
| 393 | + | ||
| 394 | + | ||
| 319 | 395 | # Beginning with Python 2.6 float has cross platform compatible | |
| 320 | 396 | # ways to create and represent inf and nan | |
| 321 | 397 | class InfNanTest(unittest.TestCase): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -220,6 +220,11 @@ def test_format(self): | |||
| 220 | 220 | testformat("%a", "\u0378", "'\\u0378'") # non printable | |
| 221 | 221 | testformat("%r", "\u0374", "'\u0374'") # printable | |
| 222 | 222 | testformat("%a", "\u0374", "'\\u0374'") # printable | |
| 223 | + | ||
| 224 | + # alternate float formatting | ||
| 225 | + testformat('%g', 1.1, '1.1') | ||
| 226 | + testformat('%#g', 1.1, '1.10000') | ||
| 227 | + | ||
| 223 | 228 | # Test exception for unknown format characters | |
| 224 | 229 | if verbose: | |
| 225 | 230 | print('Testing exceptions') | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -113,6 +113,9 @@ def test(f, result): | |||
| 113 | 113 | self.assertEqual(1.5e-101.__format__('e'), '1.500000e-101') | |
| 114 | 114 | self.assertEqual('%e' % 1.5e-101, '1.500000e-101') | |
| 115 | 115 | ||
| 116 | + self.assertEqual('%g' % 1.0, '1') | ||
| 117 | + self.assertEqual('%#g' % 1.0, '1.00000') | ||
| 118 | + | ||
| 116 | 119 | def test_normal_integers(self): | |
| 117 | 120 | # Ensure the first 256 integers are shared | |
| 118 | 121 | a = 256 | |
@@ -358,6 +361,8 @@ def test(i, format_spec, result): | |||
| 358 | 361 | self.assertRaises(TypeError, 3 .__format__, 0) | |
| 359 | 362 | # can't have ',' with 'n' | |
| 360 | 363 | self.assertRaises(ValueError, 3 .__format__, ",n") | |
| 364 | + # can't have ',' with 'c' | ||
| 365 | + self.assertRaises(ValueError, 3 .__format__, ",c") | ||
| 361 | 366 | ||
| 362 | 367 | # ensure that only int and float type specifiers work | |
| 363 | 368 | for format_spec in ([chr(x) for x in range(ord('a'), ord('z')+1)] + | |
@@ -547,10 +552,34 @@ def test(f, format_spec, result): | |||
| 547 | 552 | # a totaly empty format specifier means something else. | |
| 548 | 553 | # So, just use a sign flag | |
| 549 | 554 | test(1e200, '+g', '+1e+200') | |
| 550 | - test(1e200, '+', '+1.0e+200') | ||
| 555 | + test(1e200, '+', '+1e+200') | ||
| 556 | + | ||
| 551 | 557 | test(1.1e200, '+g', '+1.1e+200') | |
| 552 | 558 | test(1.1e200, '+', '+1.1e+200') | |
| 553 | 559 | ||
| 560 | + # 0 padding | ||
| 561 | + test(1234., '010f', '1234.000000') | ||
| 562 | + test(1234., '011f', '1234.000000') | ||
| 563 | + test(1234., '012f', '01234.000000') | ||
| 564 | + test(-1234., '011f', '-1234.000000') | ||
| 565 | + test(-1234., '012f', '-1234.000000') | ||
| 566 | + test(-1234., '013f', '-01234.000000') | ||
| 567 | + test(-1234.12341234, '013f', '-01234.123412') | ||
| 568 | + test(-123456.12341234, '011.2f', '-0123456.12') | ||
| 569 | + | ||
| 570 | + # 0 padding with commas | ||
| 571 | + test(1234., '011,f', '1,234.000000') | ||
| 572 | + test(1234., '012,f', '1,234.000000') | ||
| 573 | + test(1234., '013,f', '01,234.000000') | ||
| 574 | + test(-1234., '012,f', '-1,234.000000') | ||
| 575 | + test(-1234., '013,f', '-1,234.000000') | ||
| 576 | + test(-1234., '014,f', '-01,234.000000') | ||
| 577 | + test(-12345., '015,f', '-012,345.000000') | ||
| 578 | + test(-123456., '016,f', '-0,123,456.000000') | ||
| 579 | + test(-123456., '017,f', '-0,123,456.000000') | ||
| 580 | + test(-123456.12341234, '017,f', '-0,123,456.123412') | ||
| 581 | + test(-123456.12341234, '013,.2f', '-0,123,456.12') | ||
| 582 | + | ||
| 554 | 583 | # % formatting | |
| 555 | 584 | test(-1.0, '%', '-100.000000%') | |
| 556 | 585 | ||
@@ -575,6 +604,24 @@ def test(f, format_spec, result): | |||
| 575 | 604 | self.assertRaises(ValueError, format, 0.0, '#') | |
| 576 | 605 | self.assertRaises(ValueError, format, 0.0, '#20f') | |
| 577 | 606 | ||
| 607 | + def test_format_spec_errors(self): | ||
| 608 | + # int, float, and string all share the same format spec | ||
| 609 | + # mini-language parser. | ||
| 610 | + | ||
| 611 | + # Check that we can't ask for too many digits. This is | ||
| 612 | + # probably a CPython specific test. It tries to put the width | ||
| 613 | + # into a C long. | ||
| 614 | + self.assertRaises(ValueError, format, 0, '1'*10000 + 'd') | ||
| 615 | + | ||
| 616 | + # Similar with the precision. | ||
| 617 | + self.assertRaises(ValueError, format, 0, '.' + '1'*10000 + 'd') | ||
| 618 | + | ||
| 619 | + # And may as well test both. | ||
| 620 | + self.assertRaises(ValueError, format, 0, '1'*1000 + '.' + '1'*10000 + 'd') | ||
| 621 | + | ||
| 622 | + # Make sure commas aren't allowed with various type codes | ||
| 623 | + for code in 'xXobns': | ||
| 624 | + self.assertRaises(ValueError, format, 0, ',' + code) | ||
| 578 | 625 | ||
| 579 | 626 | def test_main(): | |
| 580 | 627 | run_unittest(TypesTests) | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -12,6 +12,15 @@ What's New in Python 3.1 beta 1? | |||
| 12 | 12 | Core and Builtins | |
| 13 | 13 | ----------------- | |
| 14 | 14 | ||
| 15 | + - Issue #5772: format(1e100, '<') produces '1e+100', not '1.0e+100'. | ||
| 16 | + | ||
| 17 | + - Issue #5515: str.format() presentation type 'n' with commas no | ||
| 18 | + longer works poorly with leading zeros when formatting ints and | ||
| 19 | + floats. | ||
| 20 | + | ||
| 21 | + - Implement PEP 378, Format Specifier for Thousands Separator, for | ||
| 22 | + floats. | ||
| 23 | + | ||
| 15 | 24 | - The repr function switches to exponential notation at 1e16, not 1e17 | |
| 16 | 25 | as it did before. This change applies to both 'short' and legacy | |
| 17 | 26 | float repr styles. For the new repr style, it avoids misleading | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1016,16 +1016,31 @@ save_float(PicklerObject *self, PyObject *obj) | |||
| 1016 | 1016 | return -1; | |
| 1017 | 1017 | if (pickler_write(self, pdata, 9) < 0) | |
| 1018 | 1018 | return -1; | |
| 1019 | - } | ||
| 1019 | + } | ||
| 1020 | 1020 | else { | |
| 1021 | - char pdata[250]; | ||
| 1022 | - pdata[0] = FLOAT; | ||
| 1023 | - PyOS_ascii_formatd(pdata + 1, sizeof(pdata) - 2, "%.17g", x); | ||
| 1024 | - /* Extend the formatted string with a newline character */ | ||
| 1025 | - strcat(pdata, "\n"); | ||
| 1021 | + int result = -1; | ||
| 1022 | + char *buf = NULL; | ||
| 1023 | + char op = FLOAT; | ||
| 1026 | 1024 | ||
| 1027 | - if (pickler_write(self, pdata, strlen(pdata)) < 0) | ||
| 1028 | - return -1; | ||
| 1025 | + if (pickler_write(self, &op, 1) < 0) | ||
| 1026 | + goto done; | ||
| 1027 | + | ||
| 1028 | + buf = PyOS_double_to_string(x, 'r', 0, 0, NULL); | ||
| 1029 | + if (!buf) { | ||
| 1030 | + PyErr_NoMemory(); | ||
| 1031 | + goto done; | ||
| 1032 | + } | ||
| 1033 | + | ||
| 1034 | + if (pickler_write(self, buf, strlen(buf)) < 0) | ||
| 1035 | + goto done; | ||
| 1036 | + | ||
| 1037 | + if (pickler_write(self, "\n", 1) < 0) | ||
| 1038 | + goto done; | ||
| 1039 | + | ||
| 1040 | + result = 0; | ||
| 1041 | + done: | ||
| 1042 | + PyMem_Free(buf); | ||
| 1043 | + return result; | ||
| 1029 | 1044 | } | |
| 1030 | 1045 | ||
| 1031 | 1046 | return 0; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -562,6 +562,7 @@ PyBytes_AsStringAndSize(register PyObject *obj, | |||
| 562 | 562 | /* -------------------------------------------------------------------- */ | |
| 563 | 563 | /* Methods */ | |
| 564 | 564 | ||
| 565 | + #include "stringlib/stringdefs.h" | ||
| 565 | 566 | #define STRINGLIB_CHAR char | |
| 566 | 567 | ||
| 567 | 568 | #define STRINGLIB_CMP memcmp | |
| Back | FazBrowse Home | New Git URL |
0 commit comments