[Python-3000] string.Formatter class

Eric Smith eric+python-dev at trueblade.com
Tue Aug 28 13:48:24 CEST 2007


One of the things that PEP 3101 deliberately under specifies is the 
Formatter class, leaving decisions up to the implementation.  Now that a 
working implementation exists, I think it's reasonable to tighten it up.

I have checked in a Formatter class that specifies the following methods 
(in addition to the ones already defined in the PEP):

parse(format_string)
Loops over the format_string and returns an iterable of tuples 
(literal_text, field_name, format_spec, conversion).  This is used by 
vformat to break the string in to either literal text, or fields that 
need expanding.  If literal_text is None, then expand (field_name, 
format_spec, conversion) and append it to the output.  If literal_text 
is not None, append it to the output.

get_field(field_name, args, kwargs, used_args)
Given a field_name as returned by parse, convert it to an object to be 
formatted.  The default version takes strings of the form defined in the 
PEP, such as "0[name]" or "label.title".  It records which args have 
been used in used_args.  args and kwargs are as passed in to vformat.

convert_field(value, conversion)
Converts the value (returned by get_field) using the conversion 
(returned by the parse tuple).  The default version understands 'r' 
(repr) and 's' (str).

Given these, we can define a formatter that uses the normal syntax, but 
calls its arguments to get their value:

=================
class CallFormatter(Formatter):
     def format_field(self, value, format_spec):
         return format(value(), format_spec)

fmt = CallFormatter()
print(fmt.format('*{0}*', datetime.datetime.now))
=================
which prints:
*2007-08-28 07:39:29.946909*

Or, something that uses vertical bars for separating markup:
=================
class BarFormatter(Formatter):
     # returns an iterable that contains tuples of the form:
     # (literal_text, field_name, format_spec, conversion)
     def parse(self, format_string):
         for field in format_string.split('|'):
             if field[0] == '+':
		# it's markup
                 field_name, _, format_spec = field[1:].partition(':')
                 yield None, field_name, format_spec, None
             else:
                 yield field, None, None, None
fmt = BarFormatter()
print(fmt.format('*|+0:^10s|*', 'foo'))
=================
which prints:
*   foo    *

Or, define your own conversion character:
=================
class XFormatter(Formatter):
     def convert_field(self, value, conversion):
         if conversion == 'x':
             return None
         if conversion == 'r':
             return repr(value)
         if conversion == 's':
             return str(value)
         return value
fmt = XFormatter()
print(fmt.format("{0!r}:{0!x}", fmt))
=================
which prints:
<__main__.XFormatter object at 0xf6f6d2cc>:None

These are obviously contrived examples, without great error checking, 
but I think they demonstrate the flexibility.  I'm not wild about the 
method names, so any suggestions are appreciated.  Any other comments 
are welcome, too.

Eric.


More information about the Python-3000 mailing list