Search code examples
pythonpython-2.7string-formatting

String formatting for keys with ":" and "/"


A format string with : and / present. When tried formatting with a value dict it's throwing:

ValueError: Missing ']' in format string

Example:

In [312]: value
Out[312]: {'key:/key_part': 1}

In [313]: string_to_format
Out[313]: '{v[key:/key_part]}'

In [314]: string_to_format.format(v=SafeDict(value))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-314-3ee97d9dfb86> in <module>()
----> 1 string_to_format.format(v=SafeDict(value))

ValueError: Missing ']' in format string

where SafeDict is the implementation used from this SO answer.

In [311]: class SafeDict(dict):
     ...:     def __missing__(self, key):
     ...:         return "NULL"

Any ideas on how to get through this?


Solution

  • This is a known limitation of the basic parsing used in string formatting implementation. The section Simple and Compound Field Names in PEP 3101 describes 'getitem' support in str.format syntax (emphasis mine):

    An example of the 'getitem' syntax:

    "My name is {0[name]}".format(dict(name='Fred'))

    It should be noted that the use of 'getitem' within a format string is much more limited than its conventional usage. In the above example, the string 'name' really is the literal string 'name', not a variable named 'name'. The rules for parsing an item key are very simple. If it starts with a digit, then it is treated as a number, otherwise it is used as a string.

    Because keys are not quote-delimited, it is not possible to specify arbitrary dictionary keys (e.g., the strings "10" or ":-]") from within a format string.

    And later under the "Implementation note":

    The str.format() function will have a minimalist parser which only attempts to figure out when it is "done" with an identifier (by finding a '.' or a ']', or '}', etc.).

    So this a shortcoming of str.format by design. The attentive reader may note that the OP's string formatting works in Python 3. Some edge cases were patched up in Python 3.4, yet the same issue is still present in Python 3.3 and below.

    The relevant ticket was issue12014: str.format parses replacement field incorrectly. Since Python 2.7 is end-of-life now, the chances of getting those improvements from Python 3.4 backported to 2.7 are zero, so you will have to choose between two options:

    1. Upgrade to Python 3
    2. Refactor your code so that string formatting only uses simple names or numbers