Skip to content

Instantly share code, notes, and snippets.

@jlumbroso
Created July 5, 2019 02:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jlumbroso/57951c06a233c788e00d0fc309a93f91 to your computer and use it in GitHub Desktop.
Save jlumbroso/57951c06a233c788e00d0fc309a93f91 to your computer and use it in GitHub Desktop.
Python method to format a string with missing keys.
"""
MissingFormatKey.py
Author: Jérémie Lumbroso <lumbroso@cs.princeton.edu>
Date: 2019-07-03
Usage:
# Define replacement f"..." compatible with Python 2 and 3
_f = _make_f(globals=lambda: globals(), locals=lambda: locals())
# Use:
s = "Test"
var = 1
assert _f("{s} {var}") == "Test 1"
# Inside a non-global scope, you may have to provide locals
def test():
l_s = "Test"
l_var = 1
assert _f("{l_s} {l_var} / {s} {var}") == "{l_s} {l_var} / Test 1"
assert _f("{l_s} {l_var} / {s} {var}", **locals()) == "Test 1 / Test 1"
Comments:
This example shows how to use the Pythonic paradigm of "asking for
forgiveness" to handle format strings that contain missing keys.
In this snippet, it is possible either to:
- throw an exception, the standardbehavior, with MissingFormatKey.ERROR;
- replace the missing key with a blank string, with MissingFormatKey.REMOVE;
- replace the key with "{keyname}" so that a further substitution will work
as expected, with MissingFormatKey.REMAIN.
Note that with MissingFormatKey.REMAIN does not support format strings with
additional qualifiers (such as "{key:.5}").
"""
try:
# Python 3
from enum import Enum as _Enum
except ImportError: # pragma: no cover
no_enum = True
# Python 2 fallbacks
try:
from aenum import Enum as _Enum
no_enum = False
except ImportError:
try:
from enum34 import Enum as _Enum
no_enum = False
except ImportError:
pass
if no_enum:
raise RuntimeError(
"""
This package requires an `Enum` object type. These are available
as part of the standard library in Python 3.4+, but otherwise
require a third-party library, either `enum34` or `aenum`.
=> You can install it with `pip` or `pipenv`:
pip install --user aenum
or
pipenv install aenum
""")
# =============================================================================
class DocEnum(_Enum):
def __init__(self, value, doc):
# type: (str, str) -> None
try:
super().__init__()
except TypeError: # pragma: no cover
# Python 2: the super() syntax was only introduced in Python 3.x
super(DocEnum, self).__init__()
self._value_ = value
self.__doc__ = doc
def is_noarg_callable(obj):
# type: Any -> bool
try:
obj()
except:
return False
return True
# =============================================================================
class MissingFormatKey(DocEnum):
"""
Describes all possible ways that the formatting helper method can address
the problem of missing format keys.
"""
ERROR = "missing-keys-error", "Missing format keys throw an error."
REMAIN = "missing-keys-remain", """
Missing format keys are unaffected, and
can be filled by a later formatting call
"""
REMOVE = "missing-keys-remove", "Missing format keys are removed."
# =============================================================================
def _make_f(globals, locals):
def _f(s, missing=MissingFormatKey.REMAIN, **kwargs):
"""
Formats a string using the local and global symbols available.
Suppresses any warning that is not related to string formatting.
"""
# Resolve the arguments (may be dictionaries or callables)
g = globals
l = locals
if is_noarg_callable(g):
g = g()
if is_noarg_callable(l):
l = l()
# Make the substitution if the string provided is not empty
if s:
try:
return s.format(**kwargs, **g, **l)
except KeyError as err:
missing_key = err.args[0]
missing_key_val = None
if missing == MissingFormatKey.ERROR:
# Re-raise the error
raise
elif missing == MissingFormatKey.REMAIN:
# Replace the missing key by {missing key} so it can
# still be substituted by a subsequent call.
missing_key_val = "{{{key}}}".format(key=missing_key)
elif missing == MissingFormatKey.REMOVE:
# Remove the pattern for the missing key
missing_key_val = ""
new_kw = { missing_key: missing_key_val }
return _f(
s=s,
missing=missing,
**new_kw,
**kwargs
)
except ValueError:
raise
except:
pass
return _f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment