mirror of
https://github.com/python/cpython.git
synced 2025-12-08 06:10:17 +00:00
Issue #6081: Add str.format_map. str.format_map(mapping) is similar to str.format(**mapping), except mapping does not get converted to a dict.
This commit is contained in:
parent
2397dd58b7
commit
27bbca6f79
5 changed files with 104 additions and 1 deletions
|
|
@ -1038,6 +1038,14 @@ functions based on regular expressions.
|
||||||
that can be specified in format strings.
|
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]])
|
.. method:: str.index(sub[, start[, end]])
|
||||||
|
|
||||||
Like :meth:`find`, but raise :exc:`ValueError` when the substring is not found.
|
Like :meth:`find`, but raise :exc:`ValueError` when the substring is not found.
|
||||||
|
|
|
||||||
|
|
@ -695,6 +695,84 @@ def __format__(self, format_spec):
|
||||||
self.assertRaises(ValueError, format, '', '#')
|
self.assertRaises(ValueError, format, '', '#')
|
||||||
self.assertRaises(ValueError, format, '', '#20')
|
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):
|
def test_format_auto_numbering(self):
|
||||||
class C:
|
class C:
|
||||||
def __init__(self, x=100):
|
def __init__(self, x=100):
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,8 @@ What's New in Python 3.2 Beta 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #6081: Add str.format_map, similar to str.format(**mapping).
|
||||||
|
|
||||||
- If FileIO.__init__ fails, close the file descriptor.
|
- If FileIO.__init__ fails, close the file descriptor.
|
||||||
|
|
||||||
- Issue #10221: dict.pop(k) now has a key error message that includes the
|
- Issue #10221: dict.pop(k) now has a key error message that includes the
|
||||||
|
|
|
||||||
|
|
@ -499,7 +499,11 @@ get_field_object(SubString *input, PyObject *args, PyObject *kwargs,
|
||||||
PyObject *key = SubString_new_object(&first);
|
PyObject *key = SubString_new_object(&first);
|
||||||
if (key == NULL)
|
if (key == NULL)
|
||||||
goto error;
|
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);
|
PyErr_SetObject(PyExc_KeyError, key);
|
||||||
Py_DECREF(key);
|
Py_DECREF(key);
|
||||||
goto error;
|
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);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
|
||||||
|
|
@ -9028,6 +9028,11 @@ PyDoc_STRVAR(format__doc__,
|
||||||
\n\
|
\n\
|
||||||
");
|
");
|
||||||
|
|
||||||
|
PyDoc_STRVAR(format_map__doc__,
|
||||||
|
"S.format_map(mapping) -> str\n\
|
||||||
|
\n\
|
||||||
|
");
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
unicode__format__(PyObject* self, PyObject* args)
|
unicode__format__(PyObject* self, PyObject* args)
|
||||||
{
|
{
|
||||||
|
|
@ -9109,6 +9114,7 @@ static PyMethodDef unicode_methods[] = {
|
||||||
{"isprintable", (PyCFunction) unicode_isprintable, METH_NOARGS, isprintable__doc__},
|
{"isprintable", (PyCFunction) unicode_isprintable, METH_NOARGS, isprintable__doc__},
|
||||||
{"zfill", (PyCFunction) unicode_zfill, METH_VARARGS, zfill__doc__},
|
{"zfill", (PyCFunction) unicode_zfill, METH_VARARGS, zfill__doc__},
|
||||||
{"format", (PyCFunction) do_string_format, METH_VARARGS | METH_KEYWORDS, format__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__},
|
{"__format__", (PyCFunction) unicode__format__, METH_VARARGS, p_format__doc__},
|
||||||
{"maketrans", (PyCFunction) unicode_maketrans,
|
{"maketrans", (PyCFunction) unicode_maketrans,
|
||||||
METH_VARARGS | METH_STATIC, maketrans__doc__},
|
METH_VARARGS | METH_STATIC, maketrans__doc__},
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue