Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b1ebcc6

Browse files
committed
Forward port of r64958.
Added '#' formatting to integers. This adds the 0b, 0o, or 0x prefix for bin, oct, hex. There's still one failing case, and I need to finish the docs. I hope to finish those today.
1 parent e840b9a commit b1ebcc6

3 files changed

Lines changed: 85 additions & 13 deletions

File tree

Lib/test/test_types.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,40 @@ def test(i, format_spec, result):
293293
test(1234, "+b", "+10011010010")
294294
test(-1234, "+b", "-10011010010")
295295

296+
# alternate (#) formatting
297+
test(0, "#b", '0b0')
298+
test(0, "-#b", '0b0')
299+
test(1, "-#b", '0b1')
300+
test(-1, "-#b", '-0b1')
301+
test(-1, "-#5b", ' -0b1')
302+
test(1, "+#5b", ' +0b1')
303+
test(100, "+#b", '+0b1100100')
304+
# test(100, "#012b", '0b001100100')
305+
306+
test(0, "#o", '0o0')
307+
test(0, "-#o", '0o0')
308+
test(1, "-#o", '0o1')
309+
test(-1, "-#o", '-0o1')
310+
test(-1, "-#5o", ' -0o1')
311+
test(1, "+#5o", ' +0o1')
312+
test(100, "+#o", '+0o144')
313+
314+
test(0, "#x", '0x0')
315+
test(0, "-#x", '0x0')
316+
test(1, "-#x", '0x1')
317+
test(-1, "-#x", '-0x1')
318+
test(-1, "-#5x", ' -0x1')
319+
test(1, "+#5x", ' +0x1')
320+
test(100, "+#x", '+0x64')
321+
322+
test(0, "#X", '0X0')
323+
test(0, "-#X", '0X0')
324+
test(1, "-#X", '0X1')
325+
test(-1, "-#X", '-0X1')
326+
test(-1, "-#5X", ' -0X1')
327+
test(1, "+#5X", ' +0X1')
328+
test(100, "+#X", '+0X64')
329+
296330
# make sure these are errors
297331

298332
# precision disallowed
@@ -509,6 +543,10 @@ def test(f, format_spec, result):
509543
self.assertRaises(ValueError, format, 1e-100, format_spec)
510544
self.assertRaises(ValueError, format, -1e-100, format_spec)
511545

546+
# Alternate formatting is not supported
547+
self.assertRaises(ValueError, format, 0.0, '#')
548+
self.assertRaises(ValueError, format, 0.0, '#20f')
549+
512550

513551
def test_main():
514552
run_unittest(TypesTests)

Lib/test/test_unicode.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,10 @@ def __format__(self, format_spec):
700700
self.assertRaises(ValueError, format, "", "-")
701701
self.assertRaises(ValueError, "{0:=s}".format, '')
702702

703+
# Alternate formatting is not supported
704+
self.assertRaises(ValueError, format, '', '#')
705+
self.assertRaises(ValueError, format, '', '#20')
706+
703707
def test_formatting(self):
704708
string_tests.MixinStrUnicodeUserStringTest.test_formatting(self)
705709
# Testing Unicode formatting strings...

Objects/stringlib/formatter.h

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ is_sign_element(STRINGLIB_CHAR c)
8989
typedef struct {
9090
STRINGLIB_CHAR fill_char;
9191
STRINGLIB_CHAR align;
92+
int alternate;
9293
STRINGLIB_CHAR sign;
9394
Py_ssize_t width;
9495
Py_ssize_t precision;
@@ -117,6 +118,7 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
117118

118119
format->fill_char = '\0';
119120
format->align = '\0';
121+
format->alternate = 0;
120122
format->sign = '\0';
121123
format->width = -1;
122124
format->precision = -1;
@@ -154,6 +156,13 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec,
154156
++ptr;
155157
}
156158

159+
/* If the next character is #, we're in alternate mode. This only
160+
applies to integers. */
161+
if (end-ptr >= 1 && ptr[0] == '#') {
162+
format->alternate = 1;
163+
++ptr;
164+
}
165+
157166
/* XXX add error checking */
158167
specified_width = get_integer(&ptr, end, &format->width);
159168

@@ -221,7 +230,8 @@ typedef struct {
221230
and more efficient enough to justify a little obfuscation? */
222231
static void
223232
calc_number_widths(NumberFieldWidths *r, STRINGLIB_CHAR actual_sign,
224-
Py_ssize_t n_digits, const InternalFormatSpec *format)
233+
Py_ssize_t n_prefix, Py_ssize_t n_digits,
234+
const InternalFormatSpec *format)
225235
{
226236
r->n_lpadding = 0;
227237
r->n_spadding = 0;
@@ -232,13 +242,15 @@ calc_number_widths(NumberFieldWidths *r, STRINGLIB_CHAR actual_sign,
232242
r->n_rsign = 0;
233243

234244
/* the output will look like:
235-
| |
236-
| <lpadding> <lsign> <spadding> <digits> <rsign> <rpadding> |
237-
| |
245+
| |
246+
| <lpadding> <lsign> <prefix> <spadding> <digits> <rsign> <rpadding> |
247+
| |
238248
239249
lsign and rsign are computed from format->sign and the actual
240250
sign of the number
241251
252+
prefix is given (it's for the '0x' prefix)
253+
242254
digits is already known
243255
244256
the total width is either given, or computed from the
@@ -363,6 +375,14 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format)
363375
goto done;
364376
}
365377

378+
/* alternate is not allowed on strings */
379+
if (format->alternate) {
380+
PyErr_SetString(PyExc_ValueError,
381+
"Alternate form (#) not allowed in string format "
382+
"specifier");
383+
goto done;
384+
}
385+
366386
/* '=' alignment not allowed on strings */
367387
if (format->align == '=') {
368388
PyErr_SetString(PyExc_ValueError,
@@ -505,31 +525,33 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
505525
}
506526
else {
507527
int base;
508-
int leading_chars_to_skip; /* Number of characters added by
509-
PyNumber_ToBase that we want to
510-
skip over. */
528+
int leading_chars_to_skip = 0; /* Number of characters added by
529+
PyNumber_ToBase that we want to
530+
skip over. */
511531

512532
/* Compute the base and how many characters will be added by
513533
PyNumber_ToBase */
514534
switch (format->type) {
515535
case 'b':
516536
base = 2;
517-
leading_chars_to_skip = 2; /* 0b */
537+
if (!format->alternate)
538+
leading_chars_to_skip = 2; /* 0b */
518539
break;
519540
case 'o':
520541
base = 8;
521-
leading_chars_to_skip = 2; /* 0o */
542+
if (!format->alternate)
543+
leading_chars_to_skip = 2; /* 0o */
522544
break;
523545
case 'x':
524546
case 'X':
525547
base = 16;
526-
leading_chars_to_skip = 2; /* 0x */
548+
if (!format->alternate)
549+
leading_chars_to_skip = 2; /* 0x */
527550
break;
528551
default: /* shouldn't be needed, but stops a compiler warning */
529552
case 'd':
530553
case 'n':
531554
base = 10;
532-
leading_chars_to_skip = 0;
533555
break;
534556
}
535557

@@ -564,7 +586,7 @@ format_int_or_long_internal(PyObject *value, const InternalFormatSpec *format,
564586
0, &n_grouping_chars, 0);
565587

566588
/* Calculate the widths of the various leading and trailing parts */
567-
calc_number_widths(&spec, sign, n_digits + n_grouping_chars, format);
589+
calc_number_widths(&spec, sign, 0, n_digits + n_grouping_chars, format);
568590

569591
/* Allocate a new string to hold the result */
570592
result = STRINGLIB_NEW(NULL, spec.n_total);
@@ -670,6 +692,14 @@ format_float_internal(PyObject *value,
670692
Py_UNICODE unicodebuf[FLOAT_FORMATBUFLEN];
671693
#endif
672694

695+
/* alternate is not allowed on floats. */
696+
if (format->alternate) {
697+
PyErr_SetString(PyExc_ValueError,
698+
"Alternate form (#) not allowed in float format "
699+
"specifier");
700+
goto done;
701+
}
702+
673703
/* first, do the conversion as 8-bit chars, using the platform's
674704
snprintf. then, if needed, convert to unicode. */
675705

@@ -730,7 +760,7 @@ format_float_internal(PyObject *value,
730760
--n_digits;
731761
}
732762

733-
calc_number_widths(&spec, sign, n_digits, format);
763+
calc_number_widths(&spec, sign, 0, n_digits, format);
734764

735765
/* allocate a string with enough space */
736766
result = STRINGLIB_NEW(NULL, spec.n_total);

0 commit comments

Comments
 (0)