Issue #6081: Add str.format_map. str.format_map(mapping) is similar t… · pythoncapi/cpython@27bbca6 · GitHub
Skip to content

Commit 27bbca6

Browse files
committed
Issue python#6081: Add str.format_map. str.format_map(mapping) is similar to str.format(**mapping), except mapping does not get converted to a dict.
1 parent 2397dd5 commit 27bbca6

5 files changed

Lines changed: 104 additions & 1 deletion

File tree

Doc/library/stdtypes.rst

Lines changed: 8 additions & 0 deletions

Lib/test/test_unicode.py

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,84 @@ def __format__(self, format_spec):
695695
self.assertRaises(ValueError, format, '', '#')
696696
self.assertRaises(ValueError, format, '', '#20')
697697

698+
def test_format_map(self):
699+
self.assertEqual(''.format_map({}), '')
700+
self.assertEqual('a'.format_map({}), 'a')
701+
self.assertEqual('ab'.format_map({}), 'ab')
702+
self.assertEqual('a{{'.format_map({}), 'a{')
703+
self.assertEqual('a}}'.format_map({}), 'a}')
704+
self.assertEqual('{{b'.format_map({}), '{b')
705+
self.assertEqual('}}b'.format_map({}), '}b')
706+
self.assertEqual('a{{b'.format_map({}), 'a{b')
707+
708+
# using mappings
709+
class Mapping(dict):
710+
def __missing__(self, key):
711+
return key
712+
self.assertEqual('{hello}'.format_map(Mapping()), 'hello')
713+
self.assertEqual('{a} {world}'.format_map(Mapping(a='hello')), 'hello world')
714+
715+
class InternalMapping:
716+
def __init__(self):
717+
self.mapping = {'a': 'hello'}
718+
def __getitem__(self, key):
719+
return self.mapping[key]
720+
self.assertEqual('{a}'.format_map(InternalMapping()), 'hello')
721+
722+
723+
# classes we'll use for testing
724+
class C:
725+
def __init__(self, x=100):
726+
self._x = x
727+
def __format__(self, spec):
728+
return spec
729+
730+
class D:
731+
def __init__(self, x):
732+
self.x = x
733+
def __format__(self, spec):
734+
return str(self.x)
735+
736+
# class with __str__, but no __format__
737+
class E:
738+
def __init__(self, x):
739+
self.x = x
740+
def __str__(self):
741+
return 'E(' + self.x + ')'
742+
743+
# class with __repr__, but no __format__ or __str__
744+
class F:
745+
def __init__(self, x):
746+
self.x = x
747+
def __repr__(self):
748+
return 'F(' + self.x + ')'
749+
750+
# class with __format__ that forwards to string, for some format_spec's
751+
class G:
752+
def __init__(self, x):
753+
self.x = x
754+
def __str__(self):
755+
return "string is " + self.x
756+
def __format__(self, format_spec):
757+
if format_spec == 'd':
758+
return 'G(' + self.x + ')'
759+
return object.__format__(self, format_spec)
760+
761+
# class that returns a bad type from __format__
762+
class H:
763+
def __format__(self, format_spec):
764+
return 1.0
765+
766+
self.assertEqual('{foo._x}'.format_map({'foo': C(20)}), '20')
767+
768+
# test various errors
769+
self.assertRaises(TypeError, '{'.format_map)
770+
self.assertRaises(TypeError, '}'.format_map)
771+
self.assertRaises(TypeError, 'a{'.format_map)
772+
self.assertRaises(TypeError, 'a}'.format_map)
773+
self.assertRaises(TypeError, '{a'.format_map)
774+
self.assertRaises(TypeError, '}a'.format_map)
775+
698776
def test_format_auto_numbering(self):
699777
class C:
700778
def __init__(self, x=100):

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ What's New in Python 3.2 Beta 1?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #6081: Add str.format_map, similar to str.format(**mapping).
14+
1315
- If FileIO.__init__ fails, close the file descriptor.
1416

1517
- Issue #10221: dict.pop(k) now has a key error message that includes the

Objects/stringlib/string_format.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,11 @@ get_field_object(SubString *input, PyObject *args, PyObject *kwargs,
499499
PyObject *key = SubString_new_object(&first);
500500
if (key == NULL)
501501
goto error;
502-
if ((kwargs == NULL) || (obj = PyDict_GetItem(kwargs, key)) == NULL) {
502+
503+
/* Use PyObject_GetItem instead of PyDict_GetItem because this
504+
code is no longer just used with kwargs. It might be passed
505+
a non-dict when called through format_map. */
506+
if ((kwargs == NULL) || (obj = PyObject_GetItem(kwargs, key)) == NULL) {
503507
PyErr_SetObject(PyExc_KeyError, key);
504508
Py_DECREF(key);
505509
goto error;
@@ -1039,6 +1043,11 @@ do_string_format(PyObject *self, PyObject *args, PyObject *kwargs)
10391043
return build_string(&input, args, kwargs, recursion_depth, &auto_number);
10401044
}
10411045

1046+
static PyObject *
1047+
do_string_format_map(PyObject *self, PyObject *obj)
1048+
{
1049+
return do_string_format(self, NULL, obj);
1050+
}
10421051

10431052

10441053
/************************************************************************/

Objects/unicodeobject.c

Lines changed: 6 additions & 0 deletions

0 commit comments

Comments
 (0)