Hi all,

I would like to modify the standard str.format() in a way that when the
input field is of type str, there is some character replacement, and the
string gets padded or truncated to the given field width. Basically like
this:

fmt = MagicString('<{s:6}>')
print(fmt.format(s='Äußerst'))

Output:
<Aeusse>

I've written a function fix_format() which, given a string and a field width,
does just that. However, I find myself unable to implement a Formatter that
uses this function in the intened way. See the example below, I hope I
sprinkled it with enough comments to make my intents clear.  Thanks for any
enlightenment. The interesting part starts somewhere in the middle.

### Self contained example
import re
from string import Formatter

_replacements = [(re.compile(rx), repl) for rx, repl in (\
    ('Ä', 'Ae'),
    ('ä', 'ae'),
    ('Ö', 'Oe'),
    ('ö', 'oe'),
    ('Ü', 'Ue'),
    ('ü', 'ue'),
    ('ß', 'ss'))]

def fix_format(text, width):

    # Seven regex passes seems awfully inefficient. I can't think of a
    # better way. Besides the point though.
    for rx, repl in _replacements:
        text = re.sub(rx, repl, text)

    # return truncated / padded version of string
    return text[:width] + ' ' * max(0, width - len(text))

class Obj():
    """I'm just an object with some attributes"""
    def __init__(self, **kw):
        self.__dict__.update(kw)

o = Obj(x="I am X, and I'm too long",
        y="ÄÖÜ Ich bin auch zu lang")
z = 'Pad me!'

format_spec = '<{o.x:6}>\n<{o.y:6}>\n<{z:10}>'

# Standard string formatting
print('Standard string formatting:')
print(format_spec.format(o=o, z=z))

# Demonstrate fix_format()
print('\nWanted output:')
print('<' + fix_format(o.x, 6) + '>')
print('<' + fix_format(o.y, 6) + '>')
print('<' + fix_format(z, 10) + '>')

##### This is where my struggle begins. #####

class MagicString(Formatter):
    def __init__(self, format_spec):
        self.spec = format_spec
        super().__init__()

    def format(self, **kw):
        return(self.vformat(self.spec, [], kw))

    def get_field(self, name, a, kw):
        # Compound fields have a dot:
        obj_name, _, key = name.partition('.')
        obj = getattr(kw[obj_name], key) if key else kw[obj_name]
        if isinstance(obj, str):
            # Here I would like to call fix_format(), but I don't know where
            # to get the field width.
            print('get_field(): <' + obj + '>')
        else:
            # Here I'd like to use the "native" formatter of whatever type
            # the field is.
            pass
        return obj, key

    def get_value(self, key, a, kw):
        '''I don't understand what this method is for, it never gets called'''
        raise NotImplementedError

fmt = MagicString(format_spec)
print('\nReal output:')
print(fmt.format(o=o, z=z))

# Weirdly, somewhere on the way the standard formatting kicks in, too, as
# the 'Pad me!' string does get padded (which must be some postprocessing,
# as the string is still unpadded when passed into get_field())

-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to