Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8fd3eba

Browse files
committed
Fixes for shared 2.6 code that implements PEP 3101, advanced string
formatting. Includes: - Modifying tests for basic types to use __format__ methods, instead of builtin "format". - Adding PyObject_Format. - General str/unicode cleanup discovered when backporting to 2.6. - Removing datetimemodule.c's time_format, since it was identical to date_format. The files in Objects/stringlib that implement PEP 3101 (stringdefs.h, unicodedefs.h, formatter.h, string_format.h) are identical in trunk and py3k. Any changes from here on should be made to trunk, and changes will propogate to py3k).
1 parent 18c6689 commit 8fd3eba

10 files changed

Lines changed: 329 additions & 255 deletions

File tree

Include/abstract.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,13 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
611611
*/
612612

613613

614+
PyAPI_FUNC(PyObject *) PyObject_Format(PyObject* obj,
615+
PyObject *format_spec);
616+
/*
617+
Takes an arbitrary object and returns the result of
618+
calling obj.__format__(format_spec).
619+
*/
620+
614621
/* Iterators */
615622

616623
PyAPI_FUNC(PyObject *) PyObject_GetIter(PyObject *);

Lib/test/test_builtin.py

Lines changed: 61 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -541,24 +541,58 @@ def __float__(self):
541541
self.assertRaises(TypeError, float, Foo4(42))
542542

543543
def test_format(self):
544-
class A:
545-
def __init__(self, x):
546-
self.x = x
547-
def __format__(self, format_spec):
548-
return str(self.x) + format_spec
544+
# Test the basic machinery of the format() builtin. Don't test
545+
# the specifics of the various formatters
546+
self.assertEqual(format(3, ''), '3')
549547

550-
# class that returns a bad type from __format__
551-
class B:
552-
def __format__(self, format_spec):
553-
return 1.0
548+
# Returns some classes to use for various tests. There's
549+
# an old-style version, and a new-style version
550+
def classes_new():
551+
class A(object):
552+
def __init__(self, x):
553+
self.x = x
554+
def __format__(self, format_spec):
555+
return str(self.x) + format_spec
556+
class DerivedFromA(A):
557+
pass
554558

555-
# class that is derived from string, used
556-
# as a format spec
557-
class C(str):
558-
pass
559+
class Simple(object): pass
560+
class DerivedFromSimple(Simple):
561+
def __init__(self, x):
562+
self.x = x
563+
def __format__(self, format_spec):
564+
return str(self.x) + format_spec
565+
class DerivedFromSimple2(DerivedFromSimple): pass
566+
return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
567+
568+
# In 3.0, classes_classic has the same meaning as classes_new
569+
def classes_classic():
570+
class A:
571+
def __init__(self, x):
572+
self.x = x
573+
def __format__(self, format_spec):
574+
return str(self.x) + format_spec
575+
class DerivedFromA(A):
576+
pass
559577

560-
self.assertEqual(format(3, ''), '3')
561-
self.assertEqual(format(A(3), 'spec'), '3spec')
578+
class Simple: pass
579+
class DerivedFromSimple(Simple):
580+
def __init__(self, x):
581+
self.x = x
582+
def __format__(self, format_spec):
583+
return str(self.x) + format_spec
584+
class DerivedFromSimple2(DerivedFromSimple): pass
585+
return A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2
586+
587+
def class_test(A, DerivedFromA, DerivedFromSimple, DerivedFromSimple2):
588+
self.assertEqual(format(A(3), 'spec'), '3spec')
589+
self.assertEqual(format(DerivedFromA(4), 'spec'), '4spec')
590+
self.assertEqual(format(DerivedFromSimple(5), 'abc'), '5abc')
591+
self.assertEqual(format(DerivedFromSimple2(10), 'abcdef'),
592+
'10abcdef')
593+
594+
class_test(*classes_new())
595+
class_test(*classes_classic())
562596

563597
def empty_format_spec(value):
564598
# test that:
@@ -578,19 +612,28 @@ def empty_format_spec(value):
578612
empty_format_spec(None)
579613

580614
# TypeError because self.__format__ returns the wrong type
581-
self.assertRaises(TypeError, format, B(), "")
615+
class BadFormatResult:
616+
def __format__(self, format_spec):
617+
return 1.0
618+
self.assertRaises(TypeError, format, BadFormatResult(), "")
582619

583-
# TypeError because format_spec is not unicode
620+
# TypeError because format_spec is not unicode or str
584621
self.assertRaises(TypeError, format, object(), 4)
585622
self.assertRaises(TypeError, format, object(), object())
586623

624+
# tests for object.__format__ really belong elsewhere, but
625+
# there's no good place to put them
626+
x = object().__format__('')
627+
self.assert_(x.startswith('<object object at'))
628+
587629
# first argument to object.__format__ must be string
588630
self.assertRaises(TypeError, object().__format__, 3)
589631
self.assertRaises(TypeError, object().__format__, object())
590632
self.assertRaises(TypeError, object().__format__, None)
591633

592634
# make sure we can take a subclass of str as a format spec
593-
self.assertEqual(format(0, C('10')), ' 0')
635+
class DerivedFromStr(str): pass
636+
self.assertEqual(format(0, DerivedFromStr('10')), ' 0')
594637

595638
def test_floatasratio(self):
596639
for f, ratio in [

Lib/test/test_datetime.py

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -851,29 +851,29 @@ def test_strftime(self):
851851

852852
def test_format(self):
853853
dt = self.theclass(2007, 9, 10)
854-
self.assertEqual(format(dt, ''), str(dt))
854+
self.assertEqual(dt.__format__(''), str(dt))
855855

856856
# check that a derived class's __str__() gets called
857857
class A(self.theclass):
858858
def __str__(self):
859859
return 'A'
860860
a = A(2007, 9, 10)
861-
self.assertEqual(format(a, ''), 'A')
861+
self.assertEqual(a.__format__(''), 'A')
862862

863863
# check that a derived class's strftime gets called
864864
class B(self.theclass):
865865
def strftime(self, format_spec):
866866
return 'B'
867867
b = B(2007, 9, 10)
868-
self.assertEqual(format(b, ''), str(dt))
868+
self.assertEqual(b.__format__(''), str(dt))
869869

870870
for fmt in ["m:%m d:%d y:%y",
871871
"m:%m d:%d y:%y H:%H M:%M S:%S",
872872
"%z %Z",
873873
]:
874-
self.assertEqual(format(dt, fmt), dt.strftime(fmt))
875-
self.assertEqual(format(a, fmt), dt.strftime(fmt))
876-
self.assertEqual(format(b, fmt), 'B')
874+
self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
875+
self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
876+
self.assertEqual(b.__format__(fmt), 'B')
877877

878878
def test_resolution_info(self):
879879
self.assert_(isinstance(self.theclass.min, self.theclass))
@@ -1178,31 +1178,29 @@ def test_isoformat(self):
11781178

11791179
def test_format(self):
11801180
dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
1181-
self.assertEqual(format(dt, ''), str(dt))
1181+
self.assertEqual(dt.__format__(''), str(dt))
11821182

11831183
# check that a derived class's __str__() gets called
11841184
class A(self.theclass):
11851185
def __str__(self):
11861186
return 'A'
11871187
a = A(2007, 9, 10, 4, 5, 1, 123)
1188-
self.assertEqual(format(a, ''), 'A')
1188+
self.assertEqual(a.__format__(''), 'A')
11891189

11901190
# check that a derived class's strftime gets called
11911191
class B(self.theclass):
11921192
def strftime(self, format_spec):
11931193
return 'B'
11941194
b = B(2007, 9, 10, 4, 5, 1, 123)
1195-
self.assertEqual(format(b, ''), str(dt))
1195+
self.assertEqual(b.__format__(''), str(dt))
11961196

11971197
for fmt in ["m:%m d:%d y:%y",
11981198
"m:%m d:%d y:%y H:%H M:%M S:%S",
11991199
"%z %Z",
12001200
]:
1201-
self.assertEqual(format(dt, fmt), dt.strftime(fmt))
1202-
self.assertEqual(format(a, fmt), dt.strftime(fmt))
1203-
self.assertEqual(format(b, fmt), 'B')
1204-
1205-
1201+
self.assertEqual(dt.__format__(fmt), dt.strftime(fmt))
1202+
self.assertEqual(a.__format__(fmt), dt.strftime(fmt))
1203+
self.assertEqual(b.__format__(fmt), 'B')
12061204

12071205
def test_more_ctime(self):
12081206
# Test fields that TestDate doesn't touch.
@@ -1837,27 +1835,27 @@ def test_strftime(self):
18371835

18381836
def test_format(self):
18391837
t = self.theclass(1, 2, 3, 4)
1840-
self.assertEqual(format(t, ''), str(t))
1838+
self.assertEqual(t.__format__(''), str(t))
18411839

18421840
# check that a derived class's __str__() gets called
18431841
class A(self.theclass):
18441842
def __str__(self):
18451843
return 'A'
18461844
a = A(1, 2, 3, 4)
1847-
self.assertEqual(format(a, ''), 'A')
1845+
self.assertEqual(a.__format__(''), 'A')
18481846

18491847
# check that a derived class's strftime gets called
18501848
class B(self.theclass):
18511849
def strftime(self, format_spec):
18521850
return 'B'
18531851
b = B(1, 2, 3, 4)
1854-
self.assertEqual(format(b, ''), str(t))
1852+
self.assertEqual(b.__format__(''), str(t))
18551853

18561854
for fmt in ['%H %M %S',
18571855
]:
1858-
self.assertEqual(format(t, fmt), t.strftime(fmt))
1859-
self.assertEqual(format(a, fmt), t.strftime(fmt))
1860-
self.assertEqual(format(b, fmt), 'B')
1856+
self.assertEqual(t.__format__(fmt), t.strftime(fmt))
1857+
self.assertEqual(a.__format__(fmt), t.strftime(fmt))
1858+
self.assertEqual(b.__format__(fmt), 'B')
18611859

18621860
def test_str(self):
18631861
self.assertEqual(str(self.theclass(1, 2, 3, 4)), "01:02:03.000004")

Modules/datetimemodule.c

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3210,21 +3210,6 @@ time_strftime(PyDateTime_Time *self, PyObject *args, PyObject *kw)
32103210
return result;
32113211
}
32123212

3213-
static PyObject *
3214-
time_format(PyDateTime_Time *self, PyObject *args)
3215-
{
3216-
PyObject *format;
3217-
3218-
if (!PyArg_ParseTuple(args, "U:__format__", &format))
3219-
return NULL;
3220-
3221-
/* if the format is zero length, return str(self) */
3222-
if (PyUnicode_GetSize(format) == 0)
3223-
return PyObject_Str((PyObject *)self);
3224-
3225-
return PyObject_CallMethod((PyObject *)self, "strftime", "O", format);
3226-
}
3227-
32283213
/*
32293214
* Miscellaneous methods.
32303215
*/
@@ -3412,7 +3397,7 @@ static PyMethodDef time_methods[] = {
34123397
{"strftime", (PyCFunction)time_strftime, METH_VARARGS | METH_KEYWORDS,
34133398
PyDoc_STR("format -> strftime() style string.")},
34143399

3415-
{"__format__", (PyCFunction)time_format, METH_VARARGS,
3400+
{"__format__", (PyCFunction)date_format, METH_VARARGS,
34163401
PyDoc_STR("Formats self with strftime.")},
34173402

34183403
{"utcoffset", (PyCFunction)time_utcoffset, METH_NOARGS,

Objects/abstract.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,57 @@ PyBuffer_FillInfo(Py_buffer *view, void *buf, Py_ssize_t len,
704704
return 0;
705705
}
706706

707+
PyObject *
708+
PyObject_Format(PyObject *obj, PyObject *format_spec)
709+
{
710+
static PyObject * str__format__ = NULL;
711+
PyObject *meth;
712+
PyObject *empty = NULL;
713+
PyObject *result = NULL;
714+
715+
/* Initialize cached value */
716+
if (str__format__ == NULL) {
717+
/* Initialize static variable needed by _PyType_Lookup */
718+
str__format__ = PyUnicode_FromString("__format__");
719+
if (str__format__ == NULL)
720+
goto done;
721+
}
722+
723+
/* If no format_spec is provided, use an empty string */
724+
if (format_spec == NULL) {
725+
empty = PyUnicode_FromUnicode(NULL, 0);
726+
format_spec = empty;
727+
}
728+
729+
/* Make sure the type is initialized. float gets initialized late */
730+
if (Py_TYPE(obj)->tp_dict == NULL)
731+
if (PyType_Ready(Py_TYPE(obj)) < 0)
732+
goto done;
733+
734+
/* Find the (unbound!) __format__ method (a borrowed reference) */
735+
meth = _PyType_Lookup(Py_TYPE(obj), str__format__);
736+
if (meth == NULL) {
737+
PyErr_Format(PyExc_TypeError,
738+
"Type %.100s doesn't define __format__",
739+
Py_TYPE(obj)->tp_name);
740+
goto done;
741+
}
742+
743+
/* And call it, binding it to the value */
744+
result = PyObject_CallFunctionObjArgs(meth, obj, format_spec, NULL);
745+
746+
if (result && !PyUnicode_Check(result)) {
747+
PyErr_SetString(PyExc_TypeError,
748+
"__format__ method did not return string");
749+
Py_DECREF(result);
750+
result = NULL;
751+
goto done;
752+
}
753+
754+
done:
755+
Py_XDECREF(empty);
756+
return result;
757+
}
707758
/* Operations on numbers */
708759

709760
int

0 commit comments

Comments
 (0)