mirror of
https://github.com/python/cpython.git
synced 2025-07-29 14:15:07 +00:00
Fix issue 2782: be less strict about the format string type in strftime.
Accept unicode and anything else ParseTuple "s#" can deal with. This matches the time.strftime behavior.
This commit is contained in:
parent
8856ddae25
commit
137d824148
3 changed files with 32 additions and 21 deletions
|
@ -850,9 +850,13 @@ class TestDate(HarmlessMixedComparison, unittest.TestCase):
|
||||||
self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
|
self.assertRaises(TypeError, t.strftime, "one", "two") # too many args
|
||||||
self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
|
self.assertRaises(TypeError, t.strftime, 42) # arg wrong type
|
||||||
|
|
||||||
|
# test that unicode input is allowed (issue 2782)
|
||||||
|
self.assertEqual(t.strftime(u"%m"), "03")
|
||||||
|
|
||||||
# A naive object replaces %z and %Z w/ empty strings.
|
# A naive object replaces %z and %Z w/ empty strings.
|
||||||
self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
|
self.assertEqual(t.strftime("'%z' '%Z'"), "'' ''")
|
||||||
|
|
||||||
|
|
||||||
def test_format(self):
|
def test_format(self):
|
||||||
dt = self.theclass(2007, 9, 10)
|
dt = self.theclass(2007, 9, 10)
|
||||||
self.assertEqual(dt.__format__(''), str(dt))
|
self.assertEqual(dt.__format__(''), str(dt))
|
||||||
|
|
|
@ -72,6 +72,9 @@ Extension Modules
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #2782: The datetime module's strftime methods now accept
|
||||||
|
unicode format strings just as time.strftime always has.
|
||||||
|
|
||||||
- The sgmllib and htmllib modules have been deprecated for removal
|
- The sgmllib and htmllib modules have been deprecated for removal
|
||||||
in Python 3.0.
|
in Python 3.0.
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
* http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage
|
* http://www.zope.org/Members/fdrake/DateTimeWiki/FrontPage
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define PY_SSIZE_T_CLEAN
|
||||||
|
|
||||||
#include "Python.h"
|
#include "Python.h"
|
||||||
#include "modsupport.h"
|
#include "modsupport.h"
|
||||||
#include "structmember.h"
|
#include "structmember.h"
|
||||||
|
@ -1152,8 +1154,8 @@ make_freplacement(PyObject *object)
|
||||||
* needed.
|
* needed.
|
||||||
*/
|
*/
|
||||||
static PyObject *
|
static PyObject *
|
||||||
wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
wrap_strftime(PyObject *object, const char *format, size_t format_len,
|
||||||
PyObject *tzinfoarg)
|
PyObject *timetuple, PyObject *tzinfoarg)
|
||||||
{
|
{
|
||||||
PyObject *result = NULL; /* guilty until proved innocent */
|
PyObject *result = NULL; /* guilty until proved innocent */
|
||||||
|
|
||||||
|
@ -1161,20 +1163,19 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
||||||
PyObject *Zreplacement = NULL; /* py string, replacement for %Z */
|
PyObject *Zreplacement = NULL; /* py string, replacement for %Z */
|
||||||
PyObject *freplacement = NULL; /* py string, replacement for %f */
|
PyObject *freplacement = NULL; /* py string, replacement for %f */
|
||||||
|
|
||||||
char *pin; /* pointer to next char in input format */
|
const char *pin; /* pointer to next char in input format */
|
||||||
char ch; /* next char in input format */
|
char ch; /* next char in input format */
|
||||||
|
|
||||||
PyObject *newfmt = NULL; /* py string, the output format */
|
PyObject *newfmt = NULL; /* py string, the output format */
|
||||||
char *pnew; /* pointer to available byte in output format */
|
char *pnew; /* pointer to available byte in output format */
|
||||||
int totalnew; /* number bytes total in output format buffer,
|
size_t totalnew; /* number bytes total in output format buffer,
|
||||||
exclusive of trailing \0 */
|
exclusive of trailing \0 */
|
||||||
int usednew; /* number bytes used so far in output format buffer */
|
size_t usednew; /* number bytes used so far in output format buffer */
|
||||||
|
|
||||||
char *ptoappend; /* pointer to string to append to output buffer */
|
const char *ptoappend; /* ptr to string to append to output buffer */
|
||||||
int ntoappend; /* # of bytes to append to output buffer */
|
size_t ntoappend; /* # of bytes to append to output buffer */
|
||||||
|
|
||||||
assert(object && format && timetuple);
|
assert(object && format && timetuple);
|
||||||
assert(PyBytes_Check(format));
|
|
||||||
|
|
||||||
/* Give up if the year is before 1900.
|
/* Give up if the year is before 1900.
|
||||||
* Python strftime() plays games with the year, and different
|
* Python strftime() plays games with the year, and different
|
||||||
|
@ -1205,13 +1206,13 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
||||||
* a new format. Since computing the replacements for those codes
|
* a new format. Since computing the replacements for those codes
|
||||||
* is expensive, don't unless they're actually used.
|
* is expensive, don't unless they're actually used.
|
||||||
*/
|
*/
|
||||||
totalnew = PyBytes_Size(format) + 1; /* realistic if no %z/%Z/%f */
|
totalnew = format_len + 1; /* realistic if no %z/%Z/%f */
|
||||||
newfmt = PyBytes_FromStringAndSize(NULL, totalnew);
|
newfmt = PyBytes_FromStringAndSize(NULL, totalnew);
|
||||||
if (newfmt == NULL) goto Done;
|
if (newfmt == NULL) goto Done;
|
||||||
pnew = PyBytes_AsString(newfmt);
|
pnew = PyBytes_AsString(newfmt);
|
||||||
usednew = 0;
|
usednew = 0;
|
||||||
|
|
||||||
pin = PyBytes_AsString(format);
|
pin = format;
|
||||||
while ((ch = *pin++) != '\0') {
|
while ((ch = *pin++) != '\0') {
|
||||||
if (ch != '%') {
|
if (ch != '%') {
|
||||||
ptoappend = pin - 1;
|
ptoappend = pin - 1;
|
||||||
|
@ -1313,7 +1314,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple,
|
||||||
if (ntoappend == 0)
|
if (ntoappend == 0)
|
||||||
continue;
|
continue;
|
||||||
while (usednew + ntoappend > totalnew) {
|
while (usednew + ntoappend > totalnew) {
|
||||||
int bigger = totalnew << 1;
|
size_t bigger = totalnew << 1;
|
||||||
if ((bigger >> 1) != totalnew) { /* overflow */
|
if ((bigger >> 1) != totalnew) { /* overflow */
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
goto Done;
|
goto Done;
|
||||||
|
@ -2480,18 +2481,19 @@ date_strftime(PyDateTime_Date *self, PyObject *args, PyObject *kw)
|
||||||
* timetuple() method appropriate to self's class.
|
* timetuple() method appropriate to self's class.
|
||||||
*/
|
*/
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
PyObject *format;
|
|
||||||
PyObject *tuple;
|
PyObject *tuple;
|
||||||
|
const char *format;
|
||||||
|
Py_ssize_t format_len;
|
||||||
static char *keywords[] = {"format", NULL};
|
static char *keywords[] = {"format", NULL};
|
||||||
|
|
||||||
if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:strftime", keywords,
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "s#:strftime", keywords,
|
||||||
&PyBytes_Type, &format))
|
&format, &format_len))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
tuple = PyObject_CallMethod((PyObject *)self, "timetuple", "()");
|
tuple = PyObject_CallMethod((PyObject *)self, "timetuple", "()");
|
||||||
if (tuple == NULL)
|
if (tuple == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
result = wrap_strftime((PyObject *)self, format, tuple,
|
result = wrap_strftime((PyObject *)self, format, format_len, tuple,
|
||||||
(PyObject *)self);
|
(PyObject *)self);
|
||||||
Py_DECREF(tuple);
|
Py_DECREF(tuple);
|
||||||
return result;
|
return result;
|
||||||
|
@ -3256,12 +3258,13 @@ static PyObject *
|
||||||
time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
|
time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
|
||||||
{
|
{
|
||||||
PyObject *result;
|
PyObject *result;
|
||||||
PyObject *format;
|
|
||||||
PyObject *tuple;
|
PyObject *tuple;
|
||||||
|
const char *format;
|
||||||
|
Py_ssize_t format_len;
|
||||||
static char *keywords[] = {"format", NULL};
|
static char *keywords[] = {"format", NULL};
|
||||||
|
|
||||||
if (! PyArg_ParseTupleAndKeywords(args, kw, "O!:strftime", keywords,
|
if (! PyArg_ParseTupleAndKeywords(args, kw, "s#:strftime", keywords,
|
||||||
&PyBytes_Type, &format))
|
&format, &format_len))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Python's strftime does insane things with the year part of the
|
/* Python's strftime does insane things with the year part of the
|
||||||
|
@ -3277,7 +3280,8 @@ time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
|
||||||
if (tuple == NULL)
|
if (tuple == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
assert(PyTuple_Size(tuple) == 9);
|
assert(PyTuple_Size(tuple) == 9);
|
||||||
result = wrap_strftime((PyObject *)self, format, tuple, Py_None);
|
result = wrap_strftime((PyObject *)self, format, format_len, tuple,
|
||||||
|
Py_None);
|
||||||
Py_DECREF(tuple);
|
Py_DECREF(tuple);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue