]> granicus.if.org Git - python/commitdiff
Issue #6081: Add str.format_map. str.format_map(mapping) is similar to str.format...
authorEric Smith <eric@trueblade.com>
Thu, 4 Nov 2010 17:06:58 +0000 (17:06 +0000)
committerEric Smith <eric@trueblade.com>
Thu, 4 Nov 2010 17:06:58 +0000 (17:06 +0000)
Doc/library/stdtypes.rst
Lib/test/test_unicode.py
Misc/NEWS
Objects/stringlib/string_format.h
Objects/unicodeobject.c

index e75cfc7acfb740954dd5acba28949557b8578479..0a3ba996ea0eadb133e84a73b3b1b1895876ac1e 100644 (file)
@@ -1038,6 +1038,14 @@ functions based on regular expressions.
    that can be specified in format strings.
 
 
+.. method:: str.format_map(mapping)
+
+   Similar to ``str.forrmat(**mapping)``, except that ``mapping`` is
+   used directly and not copied to a :class:`dict` .  This is useful
+   if for example ``mapping`` is a dict subclass.
+
+    .. versionadded:: 3.2
+
 .. method:: str.index(sub[, start[, end]])
 
    Like :meth:`find`, but raise :exc:`ValueError` when the substring is not found.
index 7927834e282325781f24b0c2891701a03b8a3d76..cc891bd49d6aaa4ad98b93051775be4f2f0bf95b 100644 (file)
@@ -695,6 +695,84 @@ class UnicodeTest(string_tests.CommonTest,
         self.assertRaises(ValueError, format, '', '#')
         self.assertRaises(ValueError, format, '', '#20')
 
+    def test_format_map(self):
+        self.assertEqual(''.format_map({}), '')
+        self.assertEqual('a'.format_map({}), 'a')
+        self.assertEqual('ab'.format_map({}), 'ab')
+        self.assertEqual('a{{'.format_map({}), 'a{')
+        self.assertEqual('a}}'.format_map({}), 'a}')
+        self.assertEqual('{{b'.format_map({}), '{b')
+        self.assertEqual('}}b'.format_map({}), '}b')
+        self.assertEqual('a{{b'.format_map({}), 'a{b')
+
+        # using mappings
+        class Mapping(dict):
+            def __missing__(self, key):
+                return key
+        self.assertEqual('{hello}'.format_map(Mapping()), 'hello')
+        self.assertEqual('{a} {world}'.format_map(Mapping(a='hello')), 'hello world')
+
+        class InternalMapping:
+            def __init__(self):
+                self.mapping = {'a': 'hello'}
+            def __getitem__(self, key):
+                return self.mapping[key]
+        self.assertEqual('{a}'.format_map(InternalMapping()), 'hello')
+
+
+        # classes we'll use for testing
+        class C:
+            def __init__(self, x=100):
+                self._x = x
+            def __format__(self, spec):
+                return spec
+
+        class D:
+            def __init__(self, x):
+                self.x = x
+            def __format__(self, spec):
+                return str(self.x)
+
+        # class with __str__, but no __format__
+        class E:
+            def __init__(self, x):
+                self.x = x
+            def __str__(self):
+                return 'E(' + self.x + ')'
+
+        # class with __repr__, but no __format__ or __str__
+        class F:
+            def __init__(self, x):
+                self.x = x
+            def __repr__(self):
+                return 'F(' + self.x + ')'
+
+        # class with __format__ that forwards to string, for some format_spec's
+        class G:
+            def __init__(self, x):
+                self.x = x
+            def __str__(self):
+                return "string is " + self.x
+            def __format__(self, format_spec):
+                if format_spec == 'd':
+                    return 'G(' + self.x + ')'
+                return object.__format__(self, format_spec)
+
+        # class that returns a bad type from __format__
+        class H:
+            def __format__(self, format_spec):
+                return 1.0
+
+        self.assertEqual('{foo._x}'.format_map({'foo': C(20)}), '20')
+
+        # test various errors
+        self.assertRaises(TypeError, '{'.format_map)
+        self.assertRaises(TypeError, '}'.format_map)
+        self.assertRaises(TypeError, 'a{'.format_map)
+        self.assertRaises(TypeError, 'a}'.format_map)
+        self.assertRaises(TypeError, '{a'.format_map)
+        self.assertRaises(TypeError, '}a'.format_map)
+
     def test_format_auto_numbering(self):
         class C:
             def __init__(self, x=100):
index 2b813712d9626d6519ee9d8b24674464f6b9548a..39c3bf07bc98243729003f4be6e991944548b711 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,8 @@ What's New in Python 3.2 Beta 1?
 Core and Builtins
 -----------------
 
+- Issue #6081: Add str.format_map, similar to str.format(**mapping).
+
 - If FileIO.__init__ fails, close the file descriptor.
 
 - Issue #10221: dict.pop(k) now has a key error message that includes the
index 126c870113350eb56c4b2f499af402879e67682a..5205aa9fbc418fb9136a3eec74bd01eeae0dbfa6 100644 (file)
@@ -499,7 +499,11 @@ get_field_object(SubString *input, PyObject *args, PyObject *kwargs,
         PyObject *key = SubString_new_object(&first);
         if (key == NULL)
             goto error;
-        if ((kwargs == NULL) || (obj = PyDict_GetItem(kwargs, key)) == NULL) {
+
+        /* Use PyObject_GetItem instead of PyDict_GetItem because this
+           code is no longer just used with kwargs. It might be passed
+           a non-dict when called through format_map. */
+        if ((kwargs == NULL) || (obj = PyObject_GetItem(kwargs, key)) == NULL) {
             PyErr_SetObject(PyExc_KeyError, key);
             Py_DECREF(key);
             goto error;
@@ -1039,6 +1043,11 @@ do_string_format(PyObject *self, PyObject *args, PyObject *kwargs)
     return build_string(&input, args, kwargs, recursion_depth, &auto_number);
 }
 
+static PyObject *
+do_string_format_map(PyObject *self, PyObject *obj)
+{
+    return do_string_format(self, NULL, obj);
+}
 
 
 /************************************************************************/
index 17dc27e6e932da9548a32626778c6d076b980165..b67b8f9fb14d6f61fa01b8f797cf774f2dc06812 100644 (file)
@@ -9028,6 +9028,11 @@ PyDoc_STRVAR(format__doc__,
 \n\
 ");
 
+PyDoc_STRVAR(format_map__doc__,
+             "S.format_map(mapping) -> str\n\
+\n\
+");
+
 static PyObject *
 unicode__format__(PyObject* self, PyObject* args)
 {
@@ -9109,6 +9114,7 @@ static PyMethodDef unicode_methods[] = {
     {"isprintable", (PyCFunction) unicode_isprintable, METH_NOARGS, isprintable__doc__},
     {"zfill", (PyCFunction) unicode_zfill, METH_VARARGS, zfill__doc__},
     {"format", (PyCFunction) do_string_format, METH_VARARGS | METH_KEYWORDS, format__doc__},
+    {"format_map", (PyCFunction) do_string_format_map, METH_O, format_map__doc__},
     {"__format__", (PyCFunction) unicode__format__, METH_VARARGS, p_format__doc__},
     {"maketrans", (PyCFunction) unicode_maketrans,
      METH_VARARGS | METH_STATIC, maketrans__doc__},