You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PyUnicode_FromFormat: implement width and precision for %s, %S, %R, %V, %U, %A #51579
On my machine (OS X 10.6), this results in a segfault when invoking
range_repr:
Python 3.2a0 (py3k:76311M, Nov 15 2009, 19:16:40)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> range(0, 10)
Segmentation fault
Perhaps these modifiers aren't supposed to be used with a width?
It looks like PyUnicode_FromFormatV is computing callcount incorrectly.
It's looking for 'S', 'R', or 'A' immediately following '%', before the
width. It seems to me it should be treating them the same as 's',
although I'll admit to not having looked at it close enough to know
exactly what's going on.
The whole routine could use some attention, I think.
changed the title [-]PyUnicode_FromFormat segfault when using widths.[/-][+]PyUnicode_FromFormat segfault[/+]on Jul 27, 2010
ysjray commented on Jul 28, 2010
ysjraymannequin
Mannequin
More actions
I feel it's not proper to allow the width restrict on types %S, %R, %A. These types correspond to PyObject_Str(), PyObject_Repr, PyObject_ASCII() respectively, the results of them are usually a complete string representation of a object. If you put a width restriction on the string, it's likely that the result string is intercepted and is of no complete meaning. If you really want to put a width restriction on the result, you can use %s instead, with one or two more lines to get the corresponding char* from the object.
I feel it's not proper to allow the width restrict on types %S, %R, %A. These types correspond to PyObject_Str(), PyObject_Repr, PyObject_ASCII() respectively, the results of them are usually a complete string representation of a object. If you put a width restriction on the string, it's likely that the result string is intercepted and is of no complete meaning. If you really want to put a width restriction on the result, you can use %s instead, with one or two more lines to get the corresponding char* from the object.
I agree with that, but don't feel strongly about not allowing this
use case.
If it's easy to support, why not have it ? Otherwise, I'd be +1 on
adding a check and raise an error in case a width modifier is used
with these markers.
I think under the "we're all consenting adults" doctrine that it should be allowed. If you really want that behavior, why force the char*/%s dance at each call site when it's easy enough to do it in one place? I don't think anyone supplying a width would really be surprised that it would truncate the result and possibly break round-tripping through repr.
Besides, it's allowed in pure python code:
>>> '%.5r'%object()
'<obje'
ysjray commented on Jul 29, 2010
ysjraymannequin
Mannequin
More actions
You can write "%20s" as a argument for PyUnicode_FromFormat(), but it has no effect. The width and precision modifiers are not intended to apply to string formating(%s, %S, %R, %A), only apply to integer(%d, %u, %i, %x). Though you can write "%20s", but you cannot write "%20S", "%20R" and "%20A".
There can be several fixes:
make the presence of width and precision modifiers of %s, %S, %R, %A raise an Exception, like ValueError, instead of segment fault.
make the presence of width and precision modifiers of %s, %S, %R, %A have no effect, just like current %s.
make the presence of width and precision modifiers of %s, %S, %R, %A do have correct effect, like %r and %s in string formatting in python code.
Thanks to Eric's ideas. Now I'm sure I prefer the last fix. I will work out a patch for this.
ysjray commented on Jul 30, 2010
ysjraymannequin
Mannequin
More actions
Is this really worthy to fix?
ysjray commented on Aug 1, 2010
ysjraymannequin
Mannequin
More actions
Here is the patch, it add support to use width and precision formatters in PyUnicode_FromFormat() for type %s, %S, %R, %V, %U, %A, besides fixed two bugs, which at least I believe:
1.AccordingtoPyUnicode_FromFormat() doc: http://docs.python.org/dev/py3k/c-api/unicode.html?highlight=pyunicode_fromformat#PyUnicode_FromFormat, the "%A" should produce result of ascii(). But in the existing code, I only find code of call to ascii(object) and calculate the spaces needed for it, but not appending the ascii() output to result. Also according to my simple test, the %A doesn't work, as the following simple test function:staticPyObject*getstr(PyObject*self, PyObject*args)
{
constchar*s="hello world";
PyObject*unicode=PyUnicode_FromString(s);
returnPyUnicode_FromFormat("%A", unicode);
}
Whichshouldreturntheresultofcallingascii() withtheobjectnamed*unicode*asitsargument. Theresultshouldbeaunicodeobjectwithstring"hello world". Butitactuallyreturnaunicodeobjectwithstring"%A". Thiscanbefixedbyaddingthefollowingline:
case'A':
instep4.
another bug, here is a piece of code in Object/unicodeobject.c, PyUnicode_FromFormatV():
797 if (f == '%') {
798 #ifdef HAVE_LONG_LONG
799 int longlongflag = 0;
800 #endif
801 const char p = f;
802 width = 0;
803 while (ISDIGIT((unsigned)*f))
804 width = (width*10) + *f++ - '0';
Here the variable *width* cannot be correctly calculated, because the while loop will not execute, the *f currently is definitely '%'! So the width is always 0. But currently this doesn't cause error, since the following codes will ensure width >= MAX_LONG_CHARS:
834 case 'd': case 'u': case 'i': case 'x':
835 (void) va_arg(count, int);
836 #ifdef HAVE_LONG_LONG
837 if (longlongflag) {
838 if (width < MAX_LONG_LONG_CHARS)
839 width = MAX_LONG_LONG_CHARS;
840 }
841 else
842 #endif
843 /* MAX_LONG_CHARS is enough to hold a 64-bit integer,
844 including sign. Decimal takes the most space. This
845 isn't enough for octal. If a width is specified we
846 need more (which we allocate later). */
847 if (width < MAX_LONG_CHARS)
848 width = MAX_LONG_CHARS;
(currently width and precision only apply to integer types:%d, %u, %i, %x, not string and object types:%s, %S, %R, %A, %U, %V )
To fix, the following line:
801 const char* p = f;
should be:
801 const char* p = f++;
just as the similar loop in step 4, and add another line:
f--;
after calculate width to adapting the character pointer.
My patch fixed these two problems. Hoping somebody could take a look at it.
ysjray commented on Oct 5, 2010
ysjraymannequin
Mannequin
More actions
I update the patch. Hope somebody could do a review.
ysjray commented on Oct 5, 2010
ysjraymannequin
Mannequin
More actions
I update the patch. Hope somebody could do a review.
I rewrote PyUnicode_FromFormatV() to use a single step instead of four: see issue bpo-16147. So it's now simpler to fix this issue. Here is a new patch to implement width and precision modifiers for %s, %A, %R, %S and %U formats.
I read again this old issue. I still think that it would be better to truncate to a number of *bytes* for "%s" format (and %V format when the first argument is NULL) to mimic printf(). The "replace" error handler of the UTF-8 decoder handles truncated string correctly. So I should update my patch.
Updated patch: precision for "%s" and "%V" (if the first PyObject* argument is NULL) formats is now a number of bytes, rather than a number of characters. width is still always a number of character.
The reason is that "%.100s" can be used for avoid a crash if the argument is not terminated by a null character (see issue bpo-10833).
New version of my patch taking Serhiy's remarks into account:
add a check_format() function to cleanup unit tests
only call _PyUnicodeWriter_Prepare() once per formatted argument: compute the length and maximum character. Be more optimistic about sprintf() for integer and pointer: expect that the maximum character is 127 or less
uniformize code parsing width and precision
factorize code for '%s' and '%V'
Note: remove also _PyUnicode_WriteSubstring() from the patch, it was already added.
I didn't add the following optimization (proposed by Serhiy in his review) because I'm not convinced that it's faster, and it's unrelated to this issue:
Finally, I closed this issue. Sorry for the long delay, but many other PyUnicode_FromFormat() issues had to be discussed/fixed before this one can be fixed. It was also much easier to fix this issue since my refactoring of PyUnicode_FromFormat() to only parse the format string once (thanks to the _PyUnicodeWriter API) instead of having 4 steps.
Thanks to Ysj Ray, thanks to reviewers.
This is one of the oldest issue that I had to fix :-)
Activity
mdickinson commentedon Nov 15, 2009
There seems to be something wrong with the width handling code in
PyUnicode_FromFormat; or perhaps I'm misusing it.
To reproduce: replace the line
return PyUnicode_FromFormat("range(%R, %R)", r->start, r->stop);
in range_repr in Objects/rangeobject.c with
return PyUnicode_FromFormat("range(%20R, %20R)", r->start, r->stop);
On my machine (OS X 10.6), this results in a segfault when invoking
range_repr:
Perhaps these modifiers aren't supposed to be used with a width?
ericvsmith commentedon Nov 15, 2009
It looks like PyUnicode_FromFormatV is computing callcount incorrectly.
It's looking for 'S', 'R', or 'A' immediately following '%', before the
width. It seems to me it should be treating them the same as 's',
although I'll admit to not having looked at it close enough to know
exactly what's going on.
The whole routine could use some attention, I think.
[-]PyUnicode_FromFormat segfault when using widths.[/-][+]PyUnicode_FromFormat segfault[/+]ysjray commentedon Jul 28, 2010
I feel it's not proper to allow the width restrict on types %S, %R, %A. These types correspond to PyObject_Str(), PyObject_Repr, PyObject_ASCII() respectively, the results of them are usually a complete string representation of a object. If you put a width restriction on the string, it's likely that the result string is intercepted and is of no complete meaning. If you really want to put a width restriction on the result, you can use %s instead, with one or two more lines to get the corresponding char* from the object.
malemburg commentedon Jul 28, 2010
Ray.Allen wrote:
I agree with that, but don't feel strongly about not allowing this
use case.
If it's easy to support, why not have it ? Otherwise, I'd be +1 on
adding a check and raise an error in case a width modifier is used
with these markers.
ericvsmith commentedon Jul 28, 2010
I think under the "we're all consenting adults" doctrine that it should be allowed. If you really want that behavior, why force the char*/%s dance at each call site when it's easy enough to do it in one place? I don't think anyone supplying a width would really be surprised that it would truncate the result and possibly break round-tripping through repr.
ysjray commentedon Jul 29, 2010
You can write "%20s" as a argument for PyUnicode_FromFormat(), but it has no effect. The width and precision modifiers are not intended to apply to string formating(%s, %S, %R, %A), only apply to integer(%d, %u, %i, %x). Though you can write "%20s", but you cannot write "%20S", "%20R" and "%20A".
There can be several fixes:
Thanks to Eric's ideas. Now I'm sure I prefer the last fix. I will work out a patch for this.
ysjray commentedon Jul 30, 2010
Is this really worthy to fix?
ysjray commentedon Aug 1, 2010
Here is the patch, it add support to use width and precision formatters in PyUnicode_FromFormat() for type %s, %S, %R, %V, %U, %A, besides fixed two bugs, which at least I believe:
797 if (f == '%') {
798 #ifdef HAVE_LONG_LONG
799 int longlongflag = 0;
800 #endif
801 const char p = f;
802 width = 0;
803 while (ISDIGIT((unsigned)*f))
804 width = (width*10) + *f++ - '0';
Here the variable *width* cannot be correctly calculated, because the while loop will not execute, the *f currently is definitely '%'! So the width is always 0. But currently this doesn't cause error, since the following codes will ensure width >= MAX_LONG_CHARS:
834 case 'd': case 'u': case 'i': case 'x':
835 (void) va_arg(count, int);
836 #ifdef HAVE_LONG_LONG
837 if (longlongflag) {
838 if (width < MAX_LONG_LONG_CHARS)
839 width = MAX_LONG_LONG_CHARS;
840 }
841 else
842 #endif
843 /* MAX_LONG_CHARS is enough to hold a 64-bit integer,
844 including sign. Decimal takes the most space. This
845 isn't enough for octal. If a width is specified we
846 need more (which we allocate later). */
847 if (width < MAX_LONG_CHARS)
848 width = MAX_LONG_CHARS;
(currently width and precision only apply to integer types:%d, %u, %i, %x, not string and object types:%s, %S, %R, %A, %U, %V )
To fix, the following line:
801 const char* p = f;
should be:
801 const char* p = f++;
just as the similar loop in step 4, and add another line:
f--;
after calculate width to adapting the character pointer.
My patch fixed these two problems. Hoping somebody could take a look at it.
ysjray commentedon Oct 5, 2010
I update the patch. Hope somebody could do a review.
ysjray commentedon Oct 5, 2010
I update the patch. Hope somebody could do a review.
ysjray commentedon Oct 5, 2010
Oooops! Sorry for re-submit the request...
vstinner commentedon Feb 1, 2011
I opened other tickets related to PyUnicode_FromFormatV:
(see also bpo-10832: Add support of bytes objects in PyBytes_FromFormatV())
PyUnicode_FromFormatV() has now tests in test_unicode: issue_7330.diff should add new tests, at least to check that %20R doesn't crash.
20 remaining items
vstinner commentedon Sep 29, 2011
Hum, the issue is still open, I will try to review it.
vstinner commentedon Nov 18, 2011
Issue bpo-13428 has been marked as a duplicate of this issue.
akheron commentedon Nov 19, 2011
Hi!
I'd like to have this committed to be able to fix bpo-13349. So here's a review.
In Doc/c-api/unicode.rst, the two "versionchanged:: 3.3" directives can be merged
In tests, I'd use 'abcde' rather than 'xxxxx' to make sure that correct characters are copied to the output (hope you understand what I mean)
No test checks that width and precision work on characters rather than bytes
The changes to unicodeobject.c don't apply on top of current default branch.
vstinner commentedon Oct 6, 2012
I rewrote PyUnicode_FromFormatV() to use a single step instead of four: see issue bpo-16147. So it's now simpler to fix this issue. Here is a new patch to implement width and precision modifiers for %s, %A, %R, %S and %U formats.
vstinner commentedon Oct 6, 2012
I read again this old issue. I still think that it would be better to truncate to a number of *bytes* for "%s" format (and %V format when the first argument is NULL) to mimic printf(). The "replace" error handler of the UTF-8 decoder handles truncated string correctly. So I should update my patch.
vstinner commentedon Oct 7, 2012
Updated patch: precision for "%s" and "%V" (if the first PyObject* argument is NULL) formats is now a number of bytes, rather than a number of characters. width is still always a number of character.
The reason is that "%.100s" can be used for avoid a crash if the argument is not terminated by a null character (see issue bpo-10833).
serhiy-storchaka commentedon Dec 9, 2012
I found one bug and add some nitpicks and optimization suggestion on Rietveld.
vstinner commentedon May 5, 2013
New version of my patch taking Serhiy's remarks into account:
Note: remove also _PyUnicode_WriteSubstring() from the patch, it was already added.
vstinner commentedon May 5, 2013
I didn't add the following optimization (proposed by Serhiy in his review) because I'm not convinced that it's faster, and it's unrelated to this issue:
if (width > (PY_SSIZE_T_MAX - 9) / 10
&& width > (PY_SSIZE_T_MAX - ((int)*f - '0')) / 10)
{ ... }
instead of
if (width > (PY_SSIZE_T_MAX - ((int)*f - '0')) / 10)
{ ... }
python-dev commentedon May 6, 2013
New changeset 9e0f1c3bf9b6 by Victor Stinner in branch 'default':
Issue bpo-7330: Implement width and precision (ex: "%5.3s") for the format string
http://hg.python.org/cpython/rev/9e0f1c3bf9b6
vstinner commentedon May 6, 2013
Finally, I closed this issue. Sorry for the long delay, but many other PyUnicode_FromFormat() issues had to be discussed/fixed before this one can be fixed. It was also much easier to fix this issue since my refactoring of PyUnicode_FromFormat() to only parse the format string once (thanks to the _PyUnicodeWriter API) instead of having 4 steps.
Thanks to Ysj Ray, thanks to reviewers.
This is one of the oldest issue that I had to fix :-)