Teach PyString_FromFormat, PyErr_Format, and PyString_FromFormatV

about "%u", "%lu" and "%zu" formats.

Since PyString_FromFormat and PyErr_Format have exactly the same rules
(both inherited from PyString_FromFormatV), it would be good if someone
with more LaTeX Fu changed one of them to just point to the other.
Their docs were way out of synch before this patch, and I just did a
mass copy+paste to repair that.

Not a backport candidate (this is a new feature).
This commit is contained in:
Tim Peters 2006-05-13 23:28:20 +00:00
parent 822f34a848
commit 8931ff1f67
5 changed files with 127 additions and 40 deletions

View file

@ -618,12 +618,24 @@ parameter and are called with a non-string parameter.
exactly to the format characters in the \var{format} string. The exactly to the format characters in the \var{format} string. The
following format characters are allowed: following format characters are allowed:
% This should be exactly the same as the table in PyErr_Format.
% One should just refer to the other.
% The descriptions for %zd and %zu are wrong, but the truth is complicated
% because not all compilers support the %z width modifier -- we fake it
% when necessary via interpolating PY_FORMAT_SIZE_T.
% %u, %lu, %zu should have "new in Python 2.5" blurbs.
\begin{tableiii}{l|l|l}{member}{Format Characters}{Type}{Comment} \begin{tableiii}{l|l|l}{member}{Format Characters}{Type}{Comment}
\lineiii{\%\%}{\emph{n/a}}{The literal \% character.} \lineiii{\%\%}{\emph{n/a}}{The literal \% character.}
\lineiii{\%c}{int}{A single character, represented as an C int.} \lineiii{\%c}{int}{A single character, represented as an C int.}
\lineiii{\%d}{int}{Exactly equivalent to \code{printf("\%d")}.} \lineiii{\%d}{int}{Exactly equivalent to \code{printf("\%d")}.}
\lineiii{\%u}{unsigned int}{Exactly equivalent to \code{printf("\%u")}.}
\lineiii{\%ld}{long}{Exactly equivalent to \code{printf("\%ld")}.} \lineiii{\%ld}{long}{Exactly equivalent to \code{printf("\%ld")}.}
\lineiii{\%zd}{long}{Exactly equivalent to \code{printf("\%zd")}.} \lineiii{\%lu}{unsigned long}{Exactly equivalent to \code{printf("\%lu")}.}
\lineiii{\%zd}{Py_ssize_t}{Exactly equivalent to \code{printf("\%zd")}.}
\lineiii{\%zu}{ssize_t}{Exactly equivalent to \code{printf("\%zu")}.}
\lineiii{\%i}{int}{Exactly equivalent to \code{printf("\%i")}.} \lineiii{\%i}{int}{Exactly equivalent to \code{printf("\%i")}.}
\lineiii{\%x}{int}{Exactly equivalent to \code{printf("\%x")}.} \lineiii{\%x}{int}{Exactly equivalent to \code{printf("\%x")}.}
\lineiii{\%s}{char*}{A null-terminated C character array.} \lineiii{\%s}{char*}{A null-terminated C character array.}
@ -632,6 +644,10 @@ parameter and are called with a non-string parameter.
guaranteed to start with the literal \code{0x} regardless of guaranteed to start with the literal \code{0x} regardless of
what the platform's \code{printf} yields.} what the platform's \code{printf} yields.}
\end{tableiii} \end{tableiii}
An unrecognized format character causes all the rest of the format
string to be copied as-is to the result string, and any extra
arguments discarded.
\end{cfuncdesc} \end{cfuncdesc}
\begin{cfuncdesc}{PyObject*}{PyString_FromFormatV}{const char *format, \begin{cfuncdesc}{PyObject*}{PyString_FromFormatV}{const char *format,

View file

@ -135,13 +135,32 @@ for each thread.
codes, similar to \cfunction{printf()}. The \code{width.precision} codes, similar to \cfunction{printf()}. The \code{width.precision}
before a format code is parsed, but the width part is ignored. before a format code is parsed, but the width part is ignored.
\begin{tableii}{c|l}{character}{Character}{Meaning} % This should be exactly the same as the table in PyString_FromFormat.
\lineii{c}{Character, as an \ctype{int} parameter} % One should just refer to the other.
\lineii{d}{Number in decimal, as an \ctype{int} parameter}
\lineii{x}{Number in hexadecimal, as an \ctype{int} parameter} % The descriptions for %zd and %zu are wrong, but the truth is complicated
\lineii{s}{A string, as a \ctype{char *} parameter} % because not all compilers support the %z width modifier -- we fake it
\lineii{p}{A hex pointer, as a \ctype{void *} parameter} % when necessary via interpolating PY_FORMAT_SIZE_T.
\end{tableii}
% %u, %lu, %zu should have "new in Python 2.5" blurbs.
\begin{tableiii}{l|l|l}{member}{Format Characters}{Type}{Comment}
\lineiii{\%\%}{\emph{n/a}}{The literal \% character.}
\lineiii{\%c}{int}{A single character, represented as an C int.}
\lineiii{\%d}{int}{Exactly equivalent to \code{printf("\%d")}.}
\lineiii{\%u}{unsigned int}{Exactly equivalent to \code{printf("\%u")}.}
\lineiii{\%ld}{long}{Exactly equivalent to \code{printf("\%ld")}.}
\lineiii{\%lu}{unsigned long}{Exactly equivalent to \code{printf("\%lu")}.}
\lineiii{\%zd}{Py_ssize_t}{Exactly equivalent to \code{printf("\%zd")}.}
\lineiii{\%zu}{ssize_t}{Exactly equivalent to \code{printf("\%zu")}.}
\lineiii{\%i}{int}{Exactly equivalent to \code{printf("\%i")}.}
\lineiii{\%x}{int}{Exactly equivalent to \code{printf("\%x")}.}
\lineiii{\%s}{char*}{A null-terminated C character array.}
\lineiii{\%p}{void*}{The hex representation of a C pointer.
Mostly equivalent to \code{printf("\%p")} except that it is
guaranteed to start with the literal \code{0x} regardless of
what the platform's \code{printf} yields.}
\end{tableiii}
An unrecognized format character causes all the rest of the format An unrecognized format character causes all the rest of the format
string to be copied as-is to the result string, and any extra string to be copied as-is to the result string, and any extra

View file

@ -200,6 +200,10 @@ Build
C API C API
----- -----
- ``PyString_FromFormat``, ``PyErr_Format``, and ``PyString_FromFormatV``
now accept formats "%u" for unsigned ints, "%lu" for unsigned longs,
and "%zu" for unsigned integers of type ``size_t``.
Tests Tests
----- -----

View file

@ -486,8 +486,8 @@ test_u_code(PyObject *self)
return Py_None; return Py_None;
} }
static static PyObject *
PyObject *codec_incrementalencoder(PyObject *self, PyObject *args) codec_incrementalencoder(PyObject *self, PyObject *args)
{ {
const char *encoding, *errors = NULL; const char *encoding, *errors = NULL;
if (!PyArg_ParseTuple(args, "s|s:test_incrementalencoder", if (!PyArg_ParseTuple(args, "s|s:test_incrementalencoder",
@ -496,8 +496,8 @@ PyObject *codec_incrementalencoder(PyObject *self, PyObject *args)
return PyCodec_IncrementalEncoder(encoding, errors); return PyCodec_IncrementalEncoder(encoding, errors);
} }
static static PyObject *
PyObject *codec_incrementaldecoder(PyObject *self, PyObject *args) codec_incrementaldecoder(PyObject *self, PyObject *args)
{ {
const char *encoding, *errors = NULL; const char *encoding, *errors = NULL;
if (!PyArg_ParseTuple(args, "s|s:test_incrementaldecoder", if (!PyArg_ParseTuple(args, "s|s:test_incrementaldecoder",
@ -660,6 +660,44 @@ test_thread_state(PyObject *self, PyObject *args)
} }
#endif #endif
/* Some tests of PyString_FromFormat(). This needs more tests.
* PyString_FromFormat() also needs docs.
*/
static PyObject *
test_string_from_format(PyObject *self, PyObject *args)
{
PyObject *result;
char *msg;
#define CHECK_1_FORMAT(FORMAT, TYPE) \
result = PyString_FromFormat(FORMAT, (TYPE)1); \
if (result == NULL) \
return NULL; \
if (strcmp(PyString_AsString(result), "1")) { \
msg = FORMAT " failed at 1"; \
goto Fail; \
} \
Py_DECREF(result)
CHECK_1_FORMAT("%d", int);
CHECK_1_FORMAT("%ld", long);
/* The z width modifier was added in Python 2.5. */
CHECK_1_FORMAT("%zd", Py_ssize_t);
/* The u type code was added in Python 2.5. */
CHECK_1_FORMAT("%u", unsigned int);
CHECK_1_FORMAT("%lu", unsigned long);
CHECK_1_FORMAT("%zu", size_t);
Py_RETURN_NONE;
Fail:
Py_XDECREF(result);
return raiseTestError("test_string_from_format", msg);
#undef CHECK_1_FORMAT
}
static PyMethodDef TestMethods[] = { static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS}, {"raise_exception", raise_exception, METH_VARARGS},
{"test_config", (PyCFunction)test_config, METH_NOARGS}, {"test_config", (PyCFunction)test_config, METH_NOARGS},
@ -669,6 +707,7 @@ static PyMethodDef TestMethods[] = {
{"test_long_numbits", (PyCFunction)test_long_numbits, METH_NOARGS}, {"test_long_numbits", (PyCFunction)test_long_numbits, METH_NOARGS},
{"test_k_code", (PyCFunction)test_k_code, METH_NOARGS}, {"test_k_code", (PyCFunction)test_k_code, METH_NOARGS},
{"test_null_strings", (PyCFunction)test_null_strings, METH_NOARGS}, {"test_null_strings", (PyCFunction)test_null_strings, METH_NOARGS},
{"test_string_from_format", (PyCFunction)test_string_from_format, METH_NOARGS},
{"getargs_b", getargs_b, METH_VARARGS}, {"getargs_b", getargs_b, METH_VARARGS},
{"getargs_B", getargs_B, METH_VARARGS}, {"getargs_B", getargs_B, METH_VARARGS},

View file

@ -176,14 +176,11 @@ PyString_FromFormatV(const char *format, va_list vargs)
while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f))) while (*++f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
; ;
/* skip the 'l' in %ld, since it doesn't change the /* skip the 'l' or 'z' in {%ld, %zd, %lu, %zu} since
width. although only %d is supported (see * they don't affect the amount of space we reserve.
"expand" section below), others can be easily */
added */ if ((*f == 'l' || *f == 'z') &&
if (*f == 'l' && *(f+1) == 'd') (f[1] == 'd' || f[1] == 'u'))
++f;
/* likewise for %zd */
if (*f == 'z' && *(f+1) == 'd')
++f; ++f;
switch (*f) { switch (*f) {
@ -193,7 +190,7 @@ PyString_FromFormatV(const char *format, va_list vargs)
case '%': case '%':
n++; n++;
break; break;
case 'd': case 'i': case 'x': case 'd': case 'u': case 'i': case 'x':
(void) va_arg(count, int); (void) va_arg(count, int);
/* 20 bytes is enough to hold a 64-bit /* 20 bytes is enough to hold a 64-bit
integer. Decimal takes the most space. integer. Decimal takes the most space.
@ -255,14 +252,14 @@ PyString_FromFormatV(const char *format, va_list vargs)
} }
while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f))) while (*f && *f != '%' && !isalpha(Py_CHARMASK(*f)))
f++; f++;
/* handle the long flag, but only for %ld. others /* handle the long flag, but only for %ld and %lu.
can be added when necessary. */ others can be added when necessary. */
if (*f == 'l' && *(f+1) == 'd') { if (*f == 'l' && (f[1] == 'd' || f[1] == 'u')) {
longflag = 1; longflag = 1;
++f; ++f;
} }
/* handle the size_t flag. */ /* handle the size_t flag. */
if (*f == 'z' && *(f+1) == 'd') { if (*f == 'z' && (f[1] == 'd' || f[1] == 'u')) {
size_tflag = 1; size_tflag = 1;
++f; ++f;
} }
@ -281,6 +278,18 @@ PyString_FromFormatV(const char *format, va_list vargs)
sprintf(s, "%d", va_arg(vargs, int)); sprintf(s, "%d", va_arg(vargs, int));
s += strlen(s); s += strlen(s);
break; break;
case 'u':
if (longflag)
sprintf(s, "%lu",
va_arg(vargs, unsigned long));
else if (size_tflag)
sprintf(s, "%" PY_FORMAT_SIZE_T "u",
va_arg(vargs, size_t));
else
sprintf(s, "%u",
va_arg(vargs, unsigned int));
s += strlen(s);
break;
case 'i': case 'i':
sprintf(s, "%i", va_arg(vargs, int)); sprintf(s, "%i", va_arg(vargs, int));
s += strlen(s); s += strlen(s);