mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	Issue #1588: Add complex.__format__.
This commit is contained in:
		
							parent
							
								
									738a41dd85
								
							
						
					
					
						commit
						58a42244cf
					
				
					 6 changed files with 425 additions and 52 deletions
				
			
		|  | @ -54,6 +54,12 @@ PyAPI_FUNC(double) PyComplex_RealAsDouble(PyObject *op); | ||||||
| PyAPI_FUNC(double) PyComplex_ImagAsDouble(PyObject *op); | PyAPI_FUNC(double) PyComplex_ImagAsDouble(PyObject *op); | ||||||
| PyAPI_FUNC(Py_complex) PyComplex_AsCComplex(PyObject *op); | PyAPI_FUNC(Py_complex) PyComplex_AsCComplex(PyObject *op); | ||||||
| 
 | 
 | ||||||
|  | /* Format the object based on the format_spec, as defined in PEP 3101
 | ||||||
|  |    (Advanced String Formatting). */ | ||||||
|  | PyAPI_FUNC(PyObject *) _PyComplex_FormatAdvanced(PyObject *obj, | ||||||
|  |                                                  Py_UNICODE *format_spec, | ||||||
|  |                                                  Py_ssize_t format_spec_len); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | @ -436,7 +436,66 @@ def test_repr_roundtrip(self): | ||||||
|                 self.assertFloatsAreIdentical(0.0 + z.imag, |                 self.assertFloatsAreIdentical(0.0 + z.imag, | ||||||
|                                               0.0 + roundtrip.imag) |                                               0.0 + roundtrip.imag) | ||||||
| 
 | 
 | ||||||
|  |     def test_format(self): | ||||||
|  |         # empty format string is same as str() | ||||||
|  |         self.assertEqual(format(1+3j, ''), str(1+3j)) | ||||||
|  |         self.assertEqual(format(1.5+3.5j, ''), str(1.5+3.5j)) | ||||||
|  |         self.assertEqual(format(3j, ''), str(3j)) | ||||||
|  |         self.assertEqual(format(3.2j, ''), str(3.2j)) | ||||||
|  |         self.assertEqual(format(3+0j, ''), str(3+0j)) | ||||||
|  |         self.assertEqual(format(3.2+0j, ''), str(3.2+0j)) | ||||||
| 
 | 
 | ||||||
|  |         self.assertEqual(format(1+3j, 'g'), '1+3j') | ||||||
|  |         self.assertEqual(format(3j, 'g'), '0+3j') | ||||||
|  |         self.assertEqual(format(1.5+3.5j, 'g'), '1.5+3.5j') | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(format(1.5+3.5j, '+g'), '+1.5+3.5j') | ||||||
|  |         self.assertEqual(format(1.5-3.5j, '+g'), '+1.5-3.5j') | ||||||
|  |         self.assertEqual(format(1.5-3.5j, '-g'), '1.5-3.5j') | ||||||
|  |         self.assertEqual(format(1.5+3.5j, ' g'), ' 1.5+3.5j') | ||||||
|  |         self.assertEqual(format(1.5-3.5j, ' g'), ' 1.5-3.5j') | ||||||
|  |         self.assertEqual(format(-1.5+3.5j, ' g'), '-1.5+3.5j') | ||||||
|  |         self.assertEqual(format(-1.5-3.5j, ' g'), '-1.5-3.5j') | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(format(-1.5-3.5e-20j, 'g'), '-1.5-3.5e-20j') | ||||||
|  |         self.assertEqual(format(-1.5-3.5j, 'f'), '-1.500000-3.500000j') | ||||||
|  |         self.assertEqual(format(-1.5-3.5j, 'F'), '-1.500000-3.500000j') | ||||||
|  |         self.assertEqual(format(-1.5-3.5j, 'e'), '-1.500000e+00-3.500000e+00j') | ||||||
|  |         self.assertEqual(format(-1.5-3.5j, '.2e'), '-1.50e+00-3.50e+00j') | ||||||
|  |         self.assertEqual(format(-1.5-3.5j, '.2E'), '-1.50E+00-3.50E+00j') | ||||||
|  |         self.assertEqual(format(-1.5e10-3.5e5j, '.2G'), '-1.5E+10-3.5E+05j') | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(format(1.5+3j, '<20g'),  '1.5+3j              ') | ||||||
|  |         self.assertEqual(format(1.5+3j, '*<20g'), '1.5+3j**************') | ||||||
|  |         self.assertEqual(format(1.5+3j, '>20g'),  '              1.5+3j') | ||||||
|  |         self.assertEqual(format(1.5+3j, '^20g'),  '       1.5+3j       ') | ||||||
|  |         self.assertEqual(format(1.5+3j, '<20'),   '(1.5+3j)            ') | ||||||
|  |         self.assertEqual(format(1.5+3j, '>20'),   '            (1.5+3j)') | ||||||
|  |         self.assertEqual(format(1.5+3j, '^20'),   '      (1.5+3j)      ') | ||||||
|  |         self.assertEqual(format(1.123-3.123j, '^20.2'), '     (1.1-3.1j)     ') | ||||||
|  | 
 | ||||||
|  |         self.assertEqual(format(1.5+3j, '<20.2f'), '1.50+3.00j          ') | ||||||
|  |         self.assertEqual(format(1.5e20+3j, '<20.2f'), '150000000000000000000.00+3.00j') | ||||||
|  |         self.assertEqual(format(1.5e20+3j, '>40.2f'), '          150000000000000000000.00+3.00j') | ||||||
|  |         self.assertEqual(format(1.5e20+3j, '^40,.2f'), '  150,000,000,000,000,000,000.00+3.00j  ') | ||||||
|  |         self.assertEqual(format(1.5e21+3j, '^40,.2f'), ' 1,500,000,000,000,000,000,000.00+3.00j ') | ||||||
|  |         self.assertEqual(format(1.5e21+3000j, ',.2f'), '1,500,000,000,000,000,000,000.00+3,000.00j') | ||||||
|  | 
 | ||||||
|  |         # alternate is invalid | ||||||
|  |         self.assertRaises(ValueError, (1.5+0.5j).__format__, '#f') | ||||||
|  | 
 | ||||||
|  |         # zero padding is invalid | ||||||
|  |         self.assertRaises(ValueError, (1.5+0.5j).__format__, '010f') | ||||||
|  | 
 | ||||||
|  |         # '=' alignment is invalid | ||||||
|  |         self.assertRaises(ValueError, (1.5+3j).__format__, '=20') | ||||||
|  | 
 | ||||||
|  |         # integer presentation types are an error | ||||||
|  |         for t in 'bcdoxX': | ||||||
|  |             self.assertRaises(ValueError, (1.5+0.5j).__format__, t) | ||||||
|  | 
 | ||||||
|  |         # make sure everything works in ''.format() | ||||||
|  |         self.assertEqual('*{0:.3f}*'.format(3.14159+2.71828j), '*3.142+2.718j*') | ||||||
| 
 | 
 | ||||||
| def test_main(): | def test_main(): | ||||||
|     support.run_unittest(ComplexTest) |     support.run_unittest(ComplexTest) | ||||||
|  |  | ||||||
|  | @ -12,6 +12,9 @@ What's New in Python 3.1 beta 1? | ||||||
| Core and Builtins | Core and Builtins | ||||||
| ----------------- | ----------------- | ||||||
| 
 | 
 | ||||||
|  | - Issue #1588: Add complex.__format__. For example,  | ||||||
|  |   format(complex(1, 2./3), '.5') now produces a sensible result. | ||||||
|  | 
 | ||||||
| - Issue #5864: Fix empty format code formatting for floats so that it | - Issue #5864: Fix empty format code formatting for floats so that it | ||||||
|   never gives more than the requested number of significant digits. |   never gives more than the requested number of significant digits. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -681,6 +681,23 @@ complex_getnewargs(PyComplexObject *v) | ||||||
| 	return Py_BuildValue("(dd)", c.real, c.imag); | 	return Py_BuildValue("(dd)", c.real, c.imag); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | PyDoc_STRVAR(complex__format__doc, | ||||||
|  | "complex.__format__() -> str\n" | ||||||
|  | "\n" | ||||||
|  | "Converts to a string according to format_spec."); | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | complex__format__(PyObject* self, PyObject* args) | ||||||
|  | { | ||||||
|  |     PyObject *format_spec; | ||||||
|  | 
 | ||||||
|  |     if (!PyArg_ParseTuple(args, "U:__format__", &format_spec)) | ||||||
|  |         return NULL; | ||||||
|  |     return _PyComplex_FormatAdvanced(self, | ||||||
|  |                                      PyUnicode_AS_UNICODE(format_spec), | ||||||
|  |                                      PyUnicode_GET_SIZE(format_spec)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #if 0 | #if 0 | ||||||
| static PyObject * | static PyObject * | ||||||
| complex_is_finite(PyObject *self) | complex_is_finite(PyObject *self) | ||||||
|  | @ -705,6 +722,8 @@ static PyMethodDef complex_methods[] = { | ||||||
| 	 complex_is_finite_doc}, | 	 complex_is_finite_doc}, | ||||||
| #endif | #endif | ||||||
| 	{"__getnewargs__",	(PyCFunction)complex_getnewargs,	METH_NOARGS}, | 	{"__getnewargs__",	(PyCFunction)complex_getnewargs,	METH_NOARGS}, | ||||||
|  | 	{"__format__",          (PyCFunction)complex__format__, | ||||||
|  |                                            METH_VARARGS, complex__format__doc}, | ||||||
| 	{NULL,		NULL}		/* sentinel */ | 	{NULL,		NULL}		/* sentinel */ | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
|    FORMAT_STRING |    FORMAT_STRING | ||||||
|    FORMAT_LONG |    FORMAT_LONG | ||||||
|    FORMAT_FLOAT |    FORMAT_FLOAT | ||||||
|  |    FORMAT_COMPLEX | ||||||
|    to be whatever you want the public names of these functions to |    to be whatever you want the public names of these functions to | ||||||
|    be.  These are the only non-static functions defined here. |    be.  These are the only non-static functions defined here. | ||||||
| */ | */ | ||||||
|  | @ -261,7 +262,54 @@ parse_internal_render_format_spec(STRINGLIB_CHAR *format_spec, | ||||||
|     return 1; |     return 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #if defined FORMAT_FLOAT || defined FORMAT_LONG | /* Calculate the padding needed. */ | ||||||
|  | static void | ||||||
|  | calc_padding(Py_ssize_t nchars, Py_ssize_t width, STRINGLIB_CHAR align, | ||||||
|  |              Py_ssize_t *n_lpadding, Py_ssize_t *n_rpadding, | ||||||
|  |              Py_ssize_t *n_total) | ||||||
|  | { | ||||||
|  |     if (width >= 0) { | ||||||
|  |         if (nchars > width) | ||||||
|  |             *n_total = nchars; | ||||||
|  |         else | ||||||
|  |             *n_total = width; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |         /* not specified, use all of the chars and no more */ | ||||||
|  |         *n_total = nchars; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* figure out how much leading space we need, based on the
 | ||||||
|  |        aligning */ | ||||||
|  |     if (align == '>') | ||||||
|  |         *n_lpadding = *n_total - nchars; | ||||||
|  |     else if (align == '^') | ||||||
|  |         *n_lpadding = (*n_total - nchars) / 2; | ||||||
|  |     else | ||||||
|  |         *n_lpadding = 0; | ||||||
|  | 
 | ||||||
|  |     *n_rpadding = *n_total - nchars - *n_lpadding; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Do the padding, and return a pointer to where the caller-supplied
 | ||||||
|  |    content goes. */ | ||||||
|  | static STRINGLIB_CHAR * | ||||||
|  | fill_padding(STRINGLIB_CHAR *p, Py_ssize_t nchars, STRINGLIB_CHAR fill_char, | ||||||
|  |              Py_ssize_t n_lpadding, Py_ssize_t n_rpadding) | ||||||
|  | { | ||||||
|  |     /* Pad on left. */ | ||||||
|  |     if (n_lpadding) | ||||||
|  |         STRINGLIB_FILL(p, fill_char, n_lpadding); | ||||||
|  | 
 | ||||||
|  |     /* Pad on right. */ | ||||||
|  |     if (n_rpadding) | ||||||
|  |         STRINGLIB_FILL(p + nchars + n_lpadding, fill_char, n_rpadding); | ||||||
|  | 
 | ||||||
|  |     /* Pointer to the user content. */ | ||||||
|  |     return p + n_lpadding; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #if defined FORMAT_FLOAT || defined FORMAT_LONG || defined FORMAT_COMPLEX | ||||||
| /************************************************************************/ | /************************************************************************/ | ||||||
| /*********** common routines for numeric formatting *********************/ | /*********** common routines for numeric formatting *********************/ | ||||||
| /************************************************************************/ | /************************************************************************/ | ||||||
|  | @ -304,6 +352,7 @@ typedef struct { | ||||||
|                                the n_grouped_digits width. */ |                                the n_grouped_digits width. */ | ||||||
| } NumberFieldWidths; | } NumberFieldWidths; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| /* Given a number of the form:
 | /* Given a number of the form:
 | ||||||
|    digits[remainder] |    digits[remainder] | ||||||
|    where ptr points to the start and end points to the end, find where |    where ptr points to the start and end points to the end, find where | ||||||
|  | @ -564,7 +613,7 @@ get_locale_info(int type, LocaleInfo *locale_info) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif /* FORMAT_FLOAT || FORMAT_LONG */ | #endif /* FORMAT_FLOAT || FORMAT_LONG || FORMAT_COMPLEX */ | ||||||
| 
 | 
 | ||||||
| /************************************************************************/ | /************************************************************************/ | ||||||
| /*********** string formatting ******************************************/ | /*********** string formatting ******************************************/ | ||||||
|  | @ -573,10 +622,10 @@ get_locale_info(int type, LocaleInfo *locale_info) | ||||||
| static PyObject * | static PyObject * | ||||||
| format_string_internal(PyObject *value, const InternalFormatSpec *format) | format_string_internal(PyObject *value, const InternalFormatSpec *format) | ||||||
| { | { | ||||||
|     Py_ssize_t width; /* total field width */ |  | ||||||
|     Py_ssize_t lpad; |     Py_ssize_t lpad; | ||||||
|     STRINGLIB_CHAR *dst; |     Py_ssize_t rpad; | ||||||
|     STRINGLIB_CHAR *src = STRINGLIB_STR(value); |     Py_ssize_t total; | ||||||
|  |     STRINGLIB_CHAR *p; | ||||||
|     Py_ssize_t len = STRINGLIB_LEN(value); |     Py_ssize_t len = STRINGLIB_LEN(value); | ||||||
|     PyObject *result = NULL; |     PyObject *result = NULL; | ||||||
| 
 | 
 | ||||||
|  | @ -609,56 +658,20 @@ format_string_internal(PyObject *value, const InternalFormatSpec *format) | ||||||
|         len = format->precision; |         len = format->precision; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (format->width >= 0) { |     calc_padding(len, format->width, format->align, &lpad, &rpad, &total); | ||||||
|         width = format->width; |  | ||||||
| 
 |  | ||||||
|         /* but use at least len characters */ |  | ||||||
|         if (len > width) { |  | ||||||
|             width = len; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     else { |  | ||||||
|         /* not specified, use all of the chars and no more */ |  | ||||||
|         width = len; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     /* allocate the resulting string */ |     /* allocate the resulting string */ | ||||||
|     result = STRINGLIB_NEW(NULL, width); |     result = STRINGLIB_NEW(NULL, total); | ||||||
|     if (result == NULL) |     if (result == NULL) | ||||||
|         goto done; |         goto done; | ||||||
| 
 | 
 | ||||||
|     /* now write into that space */ |     /* Write into that space. First the padding. */ | ||||||
|     dst = STRINGLIB_STR(result); |     p = fill_padding(STRINGLIB_STR(result), len, | ||||||
|  |                      format->fill_char=='\0'?' ':format->fill_char, | ||||||
|  |                      lpad, rpad); | ||||||
| 
 | 
 | ||||||
|     /* figure out how much leading space we need, based on the
 |     /* Then the source string. */ | ||||||
|        aligning */ |     memcpy(p, STRINGLIB_STR(value), len * sizeof(STRINGLIB_CHAR)); | ||||||
|     if (format->align == '>') |  | ||||||
|         lpad = width - len; |  | ||||||
|     else if (format->align == '^') |  | ||||||
|         lpad = (width - len) / 2; |  | ||||||
|     else |  | ||||||
|         lpad = 0; |  | ||||||
| 
 |  | ||||||
|     /* if right aligning, increment the destination allow space on the
 |  | ||||||
|        left */ |  | ||||||
|     memcpy(dst + lpad, src, len * sizeof(STRINGLIB_CHAR)); |  | ||||||
| 
 |  | ||||||
|     /* do any padding */ |  | ||||||
|     if (width > len) { |  | ||||||
|         STRINGLIB_CHAR fill_char = format->fill_char; |  | ||||||
|         if (fill_char == '\0') { |  | ||||||
|             /* use the default, if not specified */ |  | ||||||
|             fill_char = ' '; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         /* pad on left */ |  | ||||||
|         if (lpad) |  | ||||||
|             STRINGLIB_FILL(dst, fill_char, lpad); |  | ||||||
| 
 |  | ||||||
|         /* pad on right */ |  | ||||||
|         if (width - len - lpad) |  | ||||||
|             STRINGLIB_FILL(dst + len + lpad, fill_char, width - len - lpad); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
| done: | done: | ||||||
|     return result; |     return result; | ||||||
|  | @ -997,6 +1010,231 @@ format_float_internal(PyObject *value, | ||||||
| } | } | ||||||
| #endif /* FORMAT_FLOAT */ | #endif /* FORMAT_FLOAT */ | ||||||
| 
 | 
 | ||||||
|  | /************************************************************************/ | ||||||
|  | /*********** complex formatting *****************************************/ | ||||||
|  | /************************************************************************/ | ||||||
|  | 
 | ||||||
|  | #ifdef FORMAT_COMPLEX | ||||||
|  | 
 | ||||||
|  | static PyObject * | ||||||
|  | format_complex_internal(PyObject *value, | ||||||
|  |                         const InternalFormatSpec *format) | ||||||
|  | { | ||||||
|  |     double re; | ||||||
|  |     double im; | ||||||
|  |     char *re_buf = NULL;       /* buffer returned from PyOS_double_to_string */ | ||||||
|  |     char *im_buf = NULL;       /* buffer returned from PyOS_double_to_string */ | ||||||
|  | 
 | ||||||
|  |     InternalFormatSpec tmp_format = *format; | ||||||
|  |     Py_ssize_t n_re_digits; | ||||||
|  |     Py_ssize_t n_im_digits; | ||||||
|  |     Py_ssize_t n_re_remainder; | ||||||
|  |     Py_ssize_t n_im_remainder; | ||||||
|  |     Py_ssize_t n_re_total; | ||||||
|  |     Py_ssize_t n_im_total; | ||||||
|  |     int re_has_decimal; | ||||||
|  |     int im_has_decimal; | ||||||
|  |     Py_ssize_t precision = format->precision; | ||||||
|  |     STRINGLIB_CHAR type = format->type; | ||||||
|  |     STRINGLIB_CHAR *p_re; | ||||||
|  |     STRINGLIB_CHAR *p_im; | ||||||
|  |     NumberFieldWidths re_spec; | ||||||
|  |     NumberFieldWidths im_spec; | ||||||
|  |     int flags = 0; | ||||||
|  |     PyObject *result = NULL; | ||||||
|  |     STRINGLIB_CHAR *p; | ||||||
|  |     STRINGLIB_CHAR re_sign_char = '\0'; | ||||||
|  |     STRINGLIB_CHAR im_sign_char = '\0'; | ||||||
|  |     int re_float_type; /* Used to see if we have a nan, inf, or regular float. */ | ||||||
|  |     int im_float_type; | ||||||
|  |     int add_parens = 0; | ||||||
|  |     int skip_re = 0; | ||||||
|  |     Py_ssize_t lpad; | ||||||
|  |     Py_ssize_t rpad; | ||||||
|  |     Py_ssize_t total; | ||||||
|  | 
 | ||||||
|  | #if STRINGLIB_IS_UNICODE | ||||||
|  |     Py_UNICODE *re_unicode_tmp = NULL; | ||||||
|  |     Py_UNICODE *im_unicode_tmp = NULL; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     /* Locale settings, either from the actual locale or
 | ||||||
|  |        from a hard-code pseudo-locale */ | ||||||
|  |     LocaleInfo locale; | ||||||
|  | 
 | ||||||
|  |     /* Alternate is not allowed on complex. */ | ||||||
|  |     if (format->alternate) { | ||||||
|  |         PyErr_SetString(PyExc_ValueError, | ||||||
|  |                         "Alternate form (#) not allowed in complex format " | ||||||
|  |                         "specifier"); | ||||||
|  |         goto done; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Neither is zero pading. */ | ||||||
|  |     if (format->fill_char == '0') { | ||||||
|  |         PyErr_SetString(PyExc_ValueError, | ||||||
|  |                         "Zero padding is not allowed in complex format " | ||||||
|  |                         "specifier"); | ||||||
|  |         goto done; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Neither is '=' alignment . */ | ||||||
|  |     if (format->align == '=') { | ||||||
|  |         PyErr_SetString(PyExc_ValueError, | ||||||
|  |                         "'=' alignment flag is not allowed in complex format " | ||||||
|  |                         "specifier"); | ||||||
|  |         goto done; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     re = PyComplex_RealAsDouble(value); | ||||||
|  |     if (re == -1.0 && PyErr_Occurred()) | ||||||
|  |         goto done; | ||||||
|  |     im = PyComplex_ImagAsDouble(value); | ||||||
|  |     if (im == -1.0 && PyErr_Occurred()) | ||||||
|  |         goto done; | ||||||
|  | 
 | ||||||
|  |     if (type == '\0') { | ||||||
|  |         /* Omitted type specifier. Should be like str(self). */ | ||||||
|  |         type = 'g'; | ||||||
|  |         add_parens = 1; | ||||||
|  |         if (re == 0.0) | ||||||
|  |             skip_re = 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (type == 'n') | ||||||
|  |         /* 'n' is the same as 'g', except for the locale used to
 | ||||||
|  |            format the result. We take care of that later. */ | ||||||
|  |         type = 'g'; | ||||||
|  | 
 | ||||||
|  |     /* 'F' is the same as 'f', per the PEP */ | ||||||
|  |     if (type == 'F') | ||||||
|  |         type = 'f'; | ||||||
|  | 
 | ||||||
|  |     if (precision < 0) | ||||||
|  |         precision = 6; | ||||||
|  | 
 | ||||||
|  |     /* Cast "type", because if we're in unicode we need to pass a
 | ||||||
|  |        8-bit char. This is safe, because we've restricted what "type" | ||||||
|  |        can be. */ | ||||||
|  |     re_buf = PyOS_double_to_string(re, (char)type, precision, flags, | ||||||
|  |                                    &re_float_type); | ||||||
|  |     if (re_buf == NULL) | ||||||
|  |         goto done; | ||||||
|  |     im_buf = PyOS_double_to_string(im, (char)type, precision, flags, | ||||||
|  |                                    &im_float_type); | ||||||
|  |     if (im_buf == NULL) | ||||||
|  |         goto done; | ||||||
|  | 
 | ||||||
|  |     n_re_digits = strlen(re_buf); | ||||||
|  |     n_im_digits = strlen(im_buf); | ||||||
|  | 
 | ||||||
|  |     /* Since there is no unicode version of PyOS_double_to_string,
 | ||||||
|  |        just use the 8 bit version and then convert to unicode. */ | ||||||
|  | #if STRINGLIB_IS_UNICODE | ||||||
|  |     re_unicode_tmp = (Py_UNICODE*)PyMem_Malloc((n_re_digits)*sizeof(Py_UNICODE)); | ||||||
|  |     if (re_unicode_tmp == NULL) { | ||||||
|  |         PyErr_NoMemory(); | ||||||
|  |         goto done; | ||||||
|  |     } | ||||||
|  |     strtounicode(re_unicode_tmp, re_buf, n_re_digits); | ||||||
|  |     p_re = re_unicode_tmp; | ||||||
|  | 
 | ||||||
|  |     im_unicode_tmp = (Py_UNICODE*)PyMem_Malloc((n_im_digits)*sizeof(Py_UNICODE)); | ||||||
|  |     if (im_unicode_tmp == NULL) { | ||||||
|  |         PyErr_NoMemory(); | ||||||
|  |         goto done; | ||||||
|  |     } | ||||||
|  |     strtounicode(im_unicode_tmp, im_buf, n_im_digits); | ||||||
|  |     p_im = im_unicode_tmp; | ||||||
|  | #else | ||||||
|  |     p_re = re_buf; | ||||||
|  |     p_im = im_buf; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |     /* Is a sign character present in the output?  If so, remember it
 | ||||||
|  |        and skip it */ | ||||||
|  |     if (*p_re == '-') { | ||||||
|  |         re_sign_char = *p_re; | ||||||
|  |         ++p_re; | ||||||
|  |         --n_re_digits; | ||||||
|  |     } | ||||||
|  |     if (*p_im == '-') { | ||||||
|  |         im_sign_char = *p_im; | ||||||
|  |         ++p_im; | ||||||
|  |         --n_im_digits; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* Determine if we have any "remainder" (after the digits, might include
 | ||||||
|  |        decimal or exponent or both (or neither)) */ | ||||||
|  |     parse_number(p_re, n_re_digits, &n_re_remainder, &re_has_decimal); | ||||||
|  |     parse_number(p_im, n_im_digits, &n_im_remainder, &im_has_decimal); | ||||||
|  | 
 | ||||||
|  |     /* Determine the grouping, separator, and decimal point, if any. */ | ||||||
|  |     get_locale_info(format->type == 'n' ? LT_CURRENT_LOCALE : | ||||||
|  |                     (format->thousands_separators ? | ||||||
|  |                      LT_DEFAULT_LOCALE : | ||||||
|  |                      LT_NO_LOCALE), | ||||||
|  |                     &locale); | ||||||
|  | 
 | ||||||
|  |     /* Turn off any padding. We'll do it later after we've composed
 | ||||||
|  |        the numbers without padding. */ | ||||||
|  |     tmp_format.fill_char = '\0'; | ||||||
|  |     tmp_format.align = '\0'; | ||||||
|  |     tmp_format.width = -1; | ||||||
|  | 
 | ||||||
|  |     /* Calculate how much memory we'll need. */ | ||||||
|  |     n_re_total = calc_number_widths(&re_spec, 0, re_sign_char, p_re, | ||||||
|  |                                     n_re_digits, n_re_remainder, | ||||||
|  |                                     re_has_decimal, &locale, &tmp_format); | ||||||
|  | 
 | ||||||
|  |     /* Same formatting, but always include a sign. */ | ||||||
|  |     tmp_format.sign = '+'; | ||||||
|  |     n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, p_im, | ||||||
|  |                                     n_im_digits, n_im_remainder, | ||||||
|  |                                     im_has_decimal, &locale, &tmp_format); | ||||||
|  | 
 | ||||||
|  |     if (skip_re) | ||||||
|  |         n_re_total = 0; | ||||||
|  | 
 | ||||||
|  |     /* Add 1 for the 'j', and optionally 2 for parens. */ | ||||||
|  |     calc_padding(n_re_total + n_im_total + 1 + add_parens * 2, | ||||||
|  |                  format->width, format->align, &lpad, &rpad, &total); | ||||||
|  | 
 | ||||||
|  |     result = STRINGLIB_NEW(NULL, total); | ||||||
|  |     if (result == NULL) | ||||||
|  |         goto done; | ||||||
|  | 
 | ||||||
|  |     /* Populate the memory. First, the padding. */ | ||||||
|  |     p = fill_padding(STRINGLIB_STR(result), | ||||||
|  |                      n_re_total + n_im_total + 1 + add_parens * 2, | ||||||
|  |                      format->fill_char=='\0' ? ' ' : format->fill_char, | ||||||
|  |                      lpad, rpad); | ||||||
|  | 
 | ||||||
|  |     if (add_parens) | ||||||
|  |         *p++ = '('; | ||||||
|  | 
 | ||||||
|  |     if (!skip_re) { | ||||||
|  |         fill_number(p, &re_spec, p_re, n_re_digits, NULL, 0, &locale, 0); | ||||||
|  |         p += n_re_total; | ||||||
|  |     } | ||||||
|  |     fill_number(p, &im_spec, p_im, n_im_digits, NULL, 0, &locale, 0); | ||||||
|  |     p += n_im_total; | ||||||
|  |     *p++ = 'j'; | ||||||
|  | 
 | ||||||
|  |     if (add_parens) | ||||||
|  |         *p++ = ')'; | ||||||
|  | 
 | ||||||
|  | done: | ||||||
|  |     PyMem_Free(re_buf); | ||||||
|  |     PyMem_Free(im_buf); | ||||||
|  | #if STRINGLIB_IS_UNICODE | ||||||
|  |     PyMem_Free(re_unicode_tmp); | ||||||
|  |     PyMem_Free(im_unicode_tmp); | ||||||
|  | #endif | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | #endif /* FORMAT_COMPLEX */ | ||||||
|  | 
 | ||||||
| /************************************************************************/ | /************************************************************************/ | ||||||
| /*********** built in formatters ****************************************/ | /*********** built in formatters ****************************************/ | ||||||
| /************************************************************************/ | /************************************************************************/ | ||||||
|  | @ -1196,3 +1434,50 @@ FORMAT_FLOAT(PyObject *obj, | ||||||
|     return result; |     return result; | ||||||
| } | } | ||||||
| #endif /* FORMAT_FLOAT */ | #endif /* FORMAT_FLOAT */ | ||||||
|  | 
 | ||||||
|  | #ifdef FORMAT_COMPLEX | ||||||
|  | PyObject * | ||||||
|  | FORMAT_COMPLEX(PyObject *obj, | ||||||
|  |                STRINGLIB_CHAR *format_spec, | ||||||
|  |                Py_ssize_t format_spec_len) | ||||||
|  | { | ||||||
|  |     PyObject *result = NULL; | ||||||
|  |     InternalFormatSpec format; | ||||||
|  | 
 | ||||||
|  |     /* check for the special case of zero length format spec, make
 | ||||||
|  |        it equivalent to str(obj) */ | ||||||
|  |     if (format_spec_len == 0) { | ||||||
|  |         result = STRINGLIB_TOSTR(obj); | ||||||
|  |         goto done; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* parse the format_spec */ | ||||||
|  |     if (!parse_internal_render_format_spec(format_spec, | ||||||
|  |                                            format_spec_len, | ||||||
|  |                                            &format, '\0')) | ||||||
|  |         goto done; | ||||||
|  | 
 | ||||||
|  |     /* type conversion? */ | ||||||
|  |     switch (format.type) { | ||||||
|  |     case '\0': /* No format code: like 'g', but with at least one decimal. */ | ||||||
|  |     case 'e': | ||||||
|  |     case 'E': | ||||||
|  |     case 'f': | ||||||
|  |     case 'F': | ||||||
|  |     case 'g': | ||||||
|  |     case 'G': | ||||||
|  |     case 'n': | ||||||
|  |         /* no conversion, already a complex.  do the formatting */ | ||||||
|  |         result = format_complex_internal(obj, &format); | ||||||
|  |         break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |         /* unknown */ | ||||||
|  |         unknown_presentation_type(format.type, obj->ob_type->tp_name); | ||||||
|  |         goto done; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | done: | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | #endif /* FORMAT_COMPLEX */ | ||||||
|  |  | ||||||
|  | @ -6,8 +6,9 @@ | ||||||
| #include "../Objects/stringlib/unicodedefs.h" | #include "../Objects/stringlib/unicodedefs.h" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #define FORMAT_STRING _PyUnicode_FormatAdvanced | #define FORMAT_STRING  _PyUnicode_FormatAdvanced | ||||||
| #define FORMAT_LONG   _PyLong_FormatAdvanced | #define FORMAT_LONG    _PyLong_FormatAdvanced | ||||||
| #define FORMAT_FLOAT  _PyFloat_FormatAdvanced | #define FORMAT_FLOAT   _PyFloat_FormatAdvanced | ||||||
|  | #define FORMAT_COMPLEX _PyComplex_FormatAdvanced | ||||||
| 
 | 
 | ||||||
| #include "../Objects/stringlib/formatter.h" | #include "../Objects/stringlib/formatter.h" | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Eric Smith
						Eric Smith