Index: Python/formatter_unicode.c =================================================================== --- Python/formatter_unicode.c (revision 72043) +++ Python/formatter_unicode.c (working copy) @@ -6,8 +6,9 @@ #include "../Objects/stringlib/unicodedefs.h" -#define FORMAT_STRING _PyUnicode_FormatAdvanced -#define FORMAT_LONG _PyLong_FormatAdvanced -#define FORMAT_FLOAT _PyFloat_FormatAdvanced +#define FORMAT_STRING _PyUnicode_FormatAdvanced +#define FORMAT_LONG _PyLong_FormatAdvanced +#define FORMAT_FLOAT _PyFloat_FormatAdvanced +#define FORMAT_COMPLEX _PyComplex_FormatAdvanced #include "../Objects/stringlib/formatter.h" Index: Include/complexobject.h =================================================================== --- Include/complexobject.h (revision 72043) +++ Include/complexobject.h (working copy) @@ -54,6 +54,12 @@ PyAPI_FUNC(double) PyComplex_ImagAsDouble(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 } #endif Index: Objects/complexobject.c =================================================================== --- Objects/complexobject.c (revision 72043) +++ Objects/complexobject.c (working copy) @@ -681,6 +681,23 @@ 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 static PyObject * complex_is_finite(PyObject *self) @@ -705,6 +722,8 @@ complex_is_finite_doc}, #endif {"__getnewargs__", (PyCFunction)complex_getnewargs, METH_NOARGS}, + {"__format__", (PyCFunction)complex__format__, + METH_VARARGS, complex__format__doc}, {NULL, NULL} /* sentinel */ }; Index: Objects/stringlib/formatter.h =================================================================== --- Objects/stringlib/formatter.h (revision 72043) +++ Objects/stringlib/formatter.h (working copy) @@ -11,6 +11,7 @@ FORMAT_STRING FORMAT_LONG FORMAT_FLOAT + FORMAT_COMPLEX to be whatever you want the public names of these functions to be. These are the only non-static functions defined here. */ @@ -261,7 +262,7 @@ return 1; } -#if defined FORMAT_FLOAT || defined FORMAT_LONG +#if defined FORMAT_FLOAT || defined FORMAT_LONG || defined FORMAT_COMPLEX /************************************************************************/ /*********** common routines for numeric formatting *********************/ /************************************************************************/ @@ -564,7 +565,7 @@ } } -#endif /* FORMAT_FLOAT || FORMAT_LONG */ +#endif /* FORMAT_FLOAT || FORMAT_LONG || FORMAT_COMPLEX */ /************************************************************************/ /*********** string formatting ******************************************/ @@ -998,6 +999,210 @@ #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 im_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 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; + +#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; + } + + 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; + } + + /* The imaginary format must always have a sign of '+', no matter what was + specified for the real part (unless we're skipping the real part?) */ + if (!skip_re) + im_format.sign = '+'; + + 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); + + /* 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, format); + + n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, p_im, + n_im_digits, n_im_remainder, + im_has_decimal, &locale, &im_format); + + if (skip_re) + n_re_total = 0; + + /* Allocate the memory. Add 1 for the 'j', and optionally 2 for parens. */ + result = STRINGLIB_NEW(NULL, n_re_total + n_im_total + 1 + + (add_parens ? 2 : 0)); + if (result == NULL) + goto done; + + /* Populate the memory. */ + if (!skip_re) + fill_number(STRINGLIB_STR(result)+(add_parens ? 1 : 0), + &re_spec, p_re, n_re_digits, NULL, + format->fill_char=='\0' ? ' ' : format->fill_char, &locale, + 0); + fill_number(STRINGLIB_STR(result)+n_re_total+(add_parens ? 1 : 0), + &im_spec, p_im, n_im_digits, NULL, + format->fill_char=='\0' ? ' ' : format->fill_char, &locale, 0); + STRINGLIB_STR(result)[n_re_total+n_im_total+(add_parens ? 1 : 0)] = 'j'; + if (add_parens) { + /* Add parens. */ + STRINGLIB_STR(result)[0] = '('; + STRINGLIB_STR(result)[STRINGLIB_LEN(result)-1] = ')'; + } + +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 ****************************************/ /************************************************************************/ PyObject * @@ -1196,3 +1401,50 @@ return result; } #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 */