2

In Python 2.7.6, these both work:

>>> '{0} {1}'.format('hello', 'world')
'hello world'
>>> '{} {}'.format('hello', 'world')
'hello world'

but only one of these works:

>>> from string import Formatter
>>> Formatter().format('{0} {1}', 'hello', 'world')
'hello world'
>>> Formatter().format('{} {}', 'hello', 'world')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/string.py", line 545, in format
    return self.vformat(format_string, args, kwargs)
  File "/usr/lib/python2.7/string.py", line 549, in vformat
    result = self._vformat(format_string, args, kwargs, used_args, 2)
  File "/usr/lib/python2.7/string.py", line 571, in _vformat
    obj, arg_used = self.get_field(field_name, args, kwargs)
  File "/usr/lib/python2.7/string.py", line 632, in get_field
    obj = self.get_value(first, args, kwargs)
  File "/usr/lib/python2.7/string.py", line 591, in get_value
    return kwargs[key]
KeyError: ''

Why are str.format and string.Formatter.format different in this way, though I haven't found anything in the documentation that implies this? Is this difference intended and/or documented anywhere?

1 Answer 1

2

This issue/bug has been raised here. It has been fixed in Python3.5.


It has not (yet) been patched for Python2.7. Until then, you can subclass string.Formatter to implement auto-arg-indexing:

import string    

class AutoArgFormatter(string.Formatter):
    """
    Backport of https://hg.python.org/cpython/rev/ad74229a6fba to Python2.7
    """
    def _vformat(self, format_string, args, kwargs, used_args, recursion_depth,
                 auto_arg_index=0):
        if recursion_depth < 0:
            raise ValueError('Max string recursion exceeded')
        result = []

        for literal_text, field_name, format_spec, conversion in \
                self.parse(format_string):

            # output the literal text
            if literal_text:
                result.append(literal_text)

            # this is some markup, find the object and do
            #  the formatting

            # handle arg indexing when empty field_names are given.
            if field_name == '':
                if auto_arg_index is False:
                    raise ValueError('cannot switch from manual field '
                                     'specification to automatic field '
                                     'numbering')
                field_name = str(auto_arg_index)
                auto_arg_index += 1
            elif field_name.isdigit():
                if auto_arg_index:
                    raise ValueError('cannot switch from manual field '
                                     'specification to automatic field '
                                     'numbering')
                # disable auto arg incrementing, if it gets
                # used later on, then an exception will be raised
                auto_arg_index = False

            # given the field_name, find the object it references
            #  and the argument it came from
            obj, arg_used = self.get_field(field_name, args, kwargs)
            used_args.add(arg_used)

            # do any conversion on the resulting object
            obj = self.convert_field(obj, conversion)

            # expand the format spec, if needed
            format_spec = self._vformat(format_spec, args, kwargs,
                                        used_args, recursion_depth-1,
                                        auto_arg_index=auto_arg_index)

            # format the object and append to the result
            result.append(self.format_field(obj, format_spec))

        return ''.join(result)

fmt = AutoArgFormatter()
print(fmt.format('{} {}', 'hello', 'world'))

yields

hello world
Sign up to request clarification or add additional context in comments.

1 Comment

and note final (plaintive) query in that thread: "2014-05-08 Is there any chance this will be fixed for 2.7 as well?"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.