Belle II Software  release-05-01-25
tolerate_missing_key_formatter.py
1 import string
2 import unittest
3 
4 
5 class NoReplacementField(object):
6 
7  """This class serves as a placeholder for keys in not found during lookup into
8  keyword or positional arguments during a call to TolerateMissingKeyFormatter.
9 
10  It records potential item and attribute lookups, conversion and format_spec to
11  reproduce original replacement_field entry into the formatted string.
12 
13  Parameters
14  ----------
15  field_name : str
16  field_name part of the replacement_field specifier
17  conversion : str
18  conversion part of the replacement_field specifier
19  format_spec : str%
20  format_spec part of the replacement_field specifier
21 
22  Notes
23  -----
24  replacement_field, field_name, conversion and format_spec have the meaning as outlined in
25  https://docs.python.org/2/library/string.html#format-string-syntax
26  """
27 
28  def __init__(self, field_name, conversion=None, format_spec=None):
29  """Constructor"""
30 
31 
32  self.field_name = field_name
33 
34  self.conversion = conversion
35 
36  self.format_spec = format_spec
37 
38  def __getattr__(self, attr):
39  """Record attribute lookup"""
40  return NoReplacementField(self.field_name + "." + attr)
41 
42  def __getitem__(self, attr):
43  """Record item lookup"""
44  return NoReplacementField(self.field_name + "[" + str(attr) + "]")
45 
46  def compose(self):
47  """Compose a replacement_field string equivalent to the original replacement_field in the string formatting."""
48  text_in_brackets = self.field_name
49 
50  if self.conversion is not None:
51  text_in_brackets += "!" + self.conversion
52 
53  if self.format_spec is not None:
54  text_in_brackets += ":" + self.format_spec
55 
56  replacement_field = "{" + text_in_brackets + "}"
57  return replacement_field
58 
59 
60 class TolerateMissingKeyFormatter(string.Formatter):
61 
62  """A string formatter that implements format most equivalent to the str.format, but does not replace keys,
63  that are not present in the replacements dictionary
64 
65  Example
66  -------
67  >>> formatter = TolerateMissingKeyFormatter()
68  >>> template = "{present}_{missing}"
69  >>> partially_substituted = formatter.format(template, present="replaced")
70  >>> print partially_substituted
71  "replaced_{missing}"
72 
73  """
74 
75  def get_value(self, key, args, kwds):
76  """Retrieves the value that corresponds to the key from either the postional or
77  the keyword arguments given to format
78 
79  Overrides the standard lookup such that missing keys in the keyword arguments or
80  transformed in a NoReplacementField signal object.
81  """
82 
83  if isinstance(key, str):
84  try:
85  return kwds[key]
86  except KeyError:
87  return NoReplacementField(key)
88  else:
89  return super(TolerateMissingKeyFormatter, self).get_value(key, args, kwds)
90 
91  def convert_field(self, value, conversion):
92  """Applies the conversion to the value.
93 
94  Overrides the standard method such that a potential conversion is attached to the NoReplacementField
95  """
96 
97  if isinstance(value, NoReplacementField):
98  conversion = conversion or None
99  value.conversion = conversion
100  return value
101  else:
102  return super(TolerateMissingKeyFormatter, self).convert_field(value, conversion)
103 
104  def format_field(self, value, format_spec):
105  """Applies the conversion to the value.
106 
107  Overrides the standard method such that a potential format_spec is attached to the NoReplacementField.
108  Than composes the replacement_field specification to be inserted in the formatted string.
109  The outcome should be equivalent to the unformatted string for missing keys.
110  """
111 
112  if isinstance(value, NoReplacementField):
113  format_spec = format_spec or None
114  value.format_spec = format_spec
115  return value.compose()
116  else:
117  return super(TolerateMissingKeyFormatter, self).format_field(value, format_spec)
118 
119 
120 class TolerateMissingKeyFormatterTest(unittest.TestCase):
121  """Test the string formatter for cases where the keys are missing"""
122 
123  def setUp(self):
124  """Prepare for the string-formatter test"""
125 
127 
128  def test_missing_key(self):
129  """Test for a missing key"""
130  template = "{present}_{missing}"
131  replaced = self.formatter.format(template, present="replaced")
132  self.assertEqual("replaced_{missing}", replaced)
133 
135  """Test for a missing key attribute"""
136  template = "{present}_{missing.field}"
137  replaced = self.formatter.format(template, present="replaced")
138  self.assertEqual("replaced_{missing.field}", replaced)
139 
141  """Test for a missing key item"""
142  template = "{present}_{missing[0]}"
143  replaced = self.formatter.format(template, present="replaced")
144  self.assertEqual("replaced_{missing[0]}", replaced)
145 
147  """Test for a missing key item"""
148  template = "{present}_{missing!s}"
149  replaced = self.formatter.format(template, present="replaced")
150  self.assertEqual("replaced_{missing!s}", replaced)
151 
153  """Test for a missing key item"""
154  template = "{present}_{missing!r}"
155  replaced = self.formatter.format(template, present="replaced")
156  self.assertEqual("replaced_{missing!r}", replaced)
157 
159  """Test for a missing key item"""
160  template = "{present}_{missing!r:^10s}"
161  replaced = self.formatter.format(template, present="replaced")
162  self.assertEqual("replaced_{missing!r:^10s}", replaced)
163 
165  """Test for a missing key item"""
166  template = "{present}_{missing:^10s}"
167  replaced = self.formatter.format(template, present="replaced")
168  self.assertEqual("replaced_{missing:^10s}", replaced)
169 
170 
171 if __name__ == '__main__':
172  unittest.main()
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.test_missing_key_with_repr_conversion
def test_missing_key_with_repr_conversion(self)
Definition: tolerate_missing_key_formatter.py:152
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.formatter
formatter
Use the custom string formatter that tolerates missing values for keywords.
Definition: tolerate_missing_key_formatter.py:126
tracking.validation.tolerate_missing_key_formatter.NoReplacementField.__getitem__
def __getitem__(self, attr)
Definition: tolerate_missing_key_formatter.py:42
tracking.validation.tolerate_missing_key_formatter.NoReplacementField
Definition: tolerate_missing_key_formatter.py:5
tracking.validation.tolerate_missing_key_formatter.NoReplacementField.field_name
field_name
cached value of the field name
Definition: tolerate_missing_key_formatter.py:32
tracking.validation.tolerate_missing_key_formatter.NoReplacementField.__getattr__
def __getattr__(self, attr)
Definition: tolerate_missing_key_formatter.py:38
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.test_missing_key_item
def test_missing_key_item(self)
Definition: tolerate_missing_key_formatter.py:140
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatter.format_field
def format_field(self, value, format_spec)
Definition: tolerate_missing_key_formatter.py:104
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatter.get_value
def get_value(self, key, args, kwds)
Definition: tolerate_missing_key_formatter.py:75
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.setUp
def setUp(self)
Definition: tolerate_missing_key_formatter.py:123
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatter
Definition: tolerate_missing_key_formatter.py:60
tracking.validation.tolerate_missing_key_formatter.NoReplacementField.__init__
def __init__(self, field_name, conversion=None, format_spec=None)
Definition: tolerate_missing_key_formatter.py:28
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.test_missing_key_attribute
def test_missing_key_attribute(self)
Definition: tolerate_missing_key_formatter.py:134
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.test_missing_key_with_str_conversion
def test_missing_key_with_str_conversion(self)
Definition: tolerate_missing_key_formatter.py:146
tracking.validation.tolerate_missing_key_formatter.NoReplacementField.conversion
conversion
cached value of the conversion specifier
Definition: tolerate_missing_key_formatter.py:34
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.test_missing_key_with_format
def test_missing_key_with_format(self)
Definition: tolerate_missing_key_formatter.py:164
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.test_missing_key_with_conversion_and_format
def test_missing_key_with_conversion_and_format(self)
Definition: tolerate_missing_key_formatter.py:158
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest.test_missing_key
def test_missing_key(self)
Definition: tolerate_missing_key_formatter.py:128
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatterTest
Definition: tolerate_missing_key_formatter.py:120
tracking.validation.tolerate_missing_key_formatter.NoReplacementField.format_spec
format_spec
cached value of the format specifier
Definition: tolerate_missing_key_formatter.py:36
tracking.validation.tolerate_missing_key_formatter.TolerateMissingKeyFormatter.convert_field
def convert_field(self, value, conversion)
Definition: tolerate_missing_key_formatter.py:91
tracking.validation.tolerate_missing_key_formatter.NoReplacementField.compose
def compose(self)
Definition: tolerate_missing_key_formatter.py:46