Skip to content

Commit 58a4224

Browse files
committed
Issue python#1588: Add complex.__format__.
1 parent 738a41d commit 58a4224

6 files changed

Lines changed: 426 additions & 53 deletions

File tree

Include/complexobject.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ PyAPI_FUNC(double) PyComplex_RealAsDouble(PyObject *op);
5454
PyAPI_FUNC(double) PyComplex_ImagAsDouble(PyObject *op);
5555
PyAPI_FUNC(Py_complex) PyComplex_AsCComplex(PyObject *op);
5656

57+
/* Format the object based on the format_spec, as defined in PEP 3101
58+
(Advanced String Formatting). */
59+
PyAPI_FUNC(PyObject *) _PyComplex_FormatAdvanced(PyObject *obj,
60+
Py_UNICODE *format_spec,
61+
Py_ssize_t format_spec_len);
62+
5763
#ifdef __cplusplus
5864
}
5965
#endif

Lib/test/test_complex.py

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,66 @@ def test_repr_roundtrip(self):
436436
self.assertFloatsAreIdentical(0.0 + z.imag,
437437
0.0 + roundtrip.imag)
438438

439-
439+
def test_format(self):
440+
# empty format string is same as str()
441+
self.assertEqual(format(1+3j, ''), str(1+3j))
442+
self.assertEqual(format(1.5+3.5j, ''), str(1.5+3.5j))
443+
self.assertEqual(format(3j, ''), str(3j))
444+
self.assertEqual(format(3.2j, ''), str(3.2j))
445+
self.assertEqual(format(3+0j, ''), str(3+0j))
446+
self.assertEqual(format(3.2+0j, ''), str(3.2+0j))
447+
448+
self.assertEqual(format(1+3j, 'g'), '1+3j')
449+
self.assertEqual(format(3j, 'g'), '0+3j')
450+
self.assertEqual(format(1.5+3.5j, 'g'), '1.5+3.5j')
451+
452+
self.assertEqual(format(1.5+3.5j, '+g'), '+1.5+3.5j')
453+
self.assertEqual(format(1.5-3.5j, '+g'), '+1.5-3.5j')
454+
self.assertEqual(format(1.5-3.5j, '-g'), '1.5-3.5j')
455+
self.assertEqual(format(1.5+3.5j, ' g'), ' 1.5+3.5j')
456+
self.assertEqual(format(1.5-3.5j, ' g'), ' 1.5-3.5j')
457+
self.assertEqual(format(-1.5+3.5j, ' g'), '-1.5+3.5j')
458+
self.assertEqual(format(-1.5-3.5j, ' g'), '-1.5-3.5j')
459+
460+
self.assertEqual(format(-1.5-3.5e-20j, 'g'), '-1.5-3.5e-20j')
461+
self.assertEqual(format(-1.5-3.5j, 'f'), '-1.500000-3.500000j')
462+
self.assertEqual(format(-1.5-3.5j, 'F'), '-1.500000-3.500000j')
463+
self.assertEqual(format(-1.5-3.5j, 'e'), '-1.500000e+00-3.500000e+00j')
464+
self.assertEqual(format(-1.5-3.5j, '.2e'), '-1.50e+00-3.50e+00j')
465+
self.assertEqual(format(-1.5-3.5j, '.2E'), '-1.50E+00-3.50E+00j')
466+
self.assertEqual(format(-1.5e10-3.5e5j, '.2G'), '-1.5E+10-3.5E+05j')
467+
468+
self.assertEqual(format(1.5+3j, '<20g'), '1.5+3j ')
469+
self.assertEqual(format(1.5+3j, '*<20g'), '1.5+3j**************')
470+
self.assertEqual(format(1.5+3j, '>20g'), ' 1.5+3j')
471+
self.assertEqual(format(1.5+3j, '^20g'), ' 1.5+3j ')
472+
self.assertEqual(format(1.5+3j, '<20'), '(1.5+3j) ')
473+
self.assertEqual(format(1.5+3j, '>20'), ' (1.5+3j)')
474+
self.assertEqual(format(1.5+3j, '^20'), ' (1.5+3j) ')
475+
self.assertEqual(format(1.123-3.123j, '^20.2'), ' (1.1-3.1j) ')
476+
477+
self.assertEqual(format(1.5+3j, '<20.2f'), '1.50+3.00j ')
478+
self.assertEqual(format(1.5e20+3j, '<20.2f'), '150000000000000000000.00+3.00j')
479+
self.assertEqual(format(1.5e20+3j, '>40.2f'), ' 150000000000000000000.00+3.00j')
480+
self.assertEqual(format(1.5e20+3j, '^40,.2f'), ' 150,000,000,000,000,000,000.00+3.00j ')
481+
self.assertEqual(format(1.5e21+3j, '^40,.2f'), ' 1,500,000,000,000,000,000,000.00+3.00j ')
482+
self.assertEqual(format(1.5e21+3000j, ',.2f'), '1,500,000,000,000,000,000,000.00+3,000.00j')
483+
484+
# alternate is invalid
485+
self.assertRaises(ValueError, (1.5+0.5j).__format__, '#f')
486+
487+
# zero padding is invalid
488+
self.assertRaises(ValueError, (1.5+0.5j).__format__, '010f')
489+
490+
# '=' alignment is invalid
491+
self.assertRaises(ValueError, (1.5+3j).__format__, '=20')
492+
493+
# integer presentation types are an error
494+
for t in 'bcdoxX':
495+
self.assertRaises(ValueError, (1.5+0.5j).__format__, t)
496+
497+
# make sure everything works in ''.format()
498+
self.assertEqual('*{0:.3f}*'.format(3.14159+2.71828j), '*3.142+2.718j*')
440499

441500
def test_main():
442501
support.run_unittest(ComplexTest)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ What's New in Python 3.1 beta 1?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #1588: Add complex.__format__. For example,
16+
format(complex(1, 2./3), '.5') now produces a sensible result.
17+
1518
- Issue #5864: Fix empty format code formatting for floats so that it
1619
never gives more than the requested number of significant digits.
1720

Objects/complexobject.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,23 @@ complex_getnewargs(PyComplexObject *v)
681681
return Py_BuildValue("(dd)", c.real, c.imag);
682682
}
683683

684+
PyDoc_STRVAR(complex__format__doc,
685+
"complex.__format__() -> str\n"
686+
"\n"
687+
"Converts to a string according to format_spec.");
688+
689+
static PyObject *
690+
complex__format__(PyObject* self, PyObject* args)
691+
{
692+
PyObject *format_spec;
693+
694+
if (!PyArg_ParseTuple(args, "U:__format__", &format_spec))
695+
return NULL;
696+
return _PyComplex_FormatAdvanced(self,
697+
PyUnicode_AS_UNICODE(format_spec),
698+
PyUnicode_GET_SIZE(format_spec));
699+
}
700+
684701
#if 0
685702
static PyObject *
686703
complex_is_finite(PyObject *self)
@@ -705,6 +722,8 @@ static PyMethodDef complex_methods[] = {
705722
complex_is_finite_doc},
706723
#endif
707724
{"__getnewargs__", (PyCFunction)complex_getnewargs, METH_NOARGS},
725+
{"__format__", (PyCFunction)complex__format__,
726+
METH_VARARGS, complex__format__doc},
708727
{NULL, NULL} /* sentinel */
709728
};
710729

0 commit comments

Comments
 (0)