str.format用の文字列から、正規表現を作る。

#encoding:shift-jis
u"""
    str.format用の文字列から、format後の文字列にマッチする正規表現を作ります。
    
    仕様:末尾まで完全に一致させます。
          format後の文字列の末尾に余計な物がついていると、マッチしません。
    
    仕様:str.formatのパラメータには数値のみが渡されると想定します。
          "{0}".format(None) のように、数値以外のものを渡す場合は使えません。
    
    >>> fmt = "PaletteItem({image_id}, QColor({red}, {green}, {blue}), {index})"
    >>> s = fmt.format(image_id=0, red=1,green=2, blue=3, index=4)
    >>> m = match(fmt, s)
    >>> m.group("red")
    '1'
    >>> matchdict(fmt, s)
    {'blue': 3, 'image_id': 0, 'green': 2, 'red': 1, 'index': 4}
    >>> matchdict(fmt, s + "some noise in tail") is None
    True
    >>> match(fmt, s + "some noise in tail") is None
    True
"""


from __future__ import division, print_function
__metaclass__ = type 
import re
import string

def escape(pattern):
    s = list(pattern)
    not_escape = set(string.digits + string.ascii_letters + " " + "_")
    
    for i, c in enumerate(pattern):
        if c not in not_escape:
            if c == "\000":
                s[i] = "\\000"
            else:
                s[i] = "\\" + c
    return pattern[:0].join(s)

def format_to_re(format_string):
    ss = escape(format_string)
    ss = ss.replace(" ", "\\s*")
    
    def repl(m):
        if m.group("double"):
            return "\\" + m.group("double")[0]
        else:
            return r"(?P<{0}>(\+|\-)?\d+)".format(m.group("field"))
    
    pattern = r"\\{(?P<field>.+?)\\}|(?P<double>\\{\\{|\\}\\})"
    return re.sub(pattern, repl, ss) + r"\s*$"

def match(format, ss):
    return re.match(format_to_re(format), ss)

def matchdict(format, ss):
    m = match(format, ss)
    if not m:
        return None
    else:
        return dict((k, int(v)) for k, v in m.groupdict().items())

def main():
    import doctest
    doctest.testmod()
    
if "__main__" == __name__:
    main()