Added policy to format text w/o exception for method traverse
The method traverse can be used for formatting task parameters, but it raises exception in case if format fails. The most shell tasks contains symbols '{', '}' and python string format tries to format such string. This change allows to specify the safe_formatter and leave argument as is if format fails. This patch also improves error handling for traverse, the default text formatter added information about text that cannot be formatted. Change-Id: I2d398d7a27966d842812d20ad1f5e7c5ecbff676 Related-bug: #1614401
This commit is contained in:
parent
77bd52b07a
commit
3198aa7b19
@ -26,6 +26,7 @@ from nailgun.utils import dict_merge
|
||||
from nailgun.utils import flatten
|
||||
from nailgun.utils import grouper
|
||||
from nailgun.utils import http_get
|
||||
from nailgun.utils import text_format_safe
|
||||
from nailgun.utils import traverse
|
||||
|
||||
from nailgun.utils.debian import get_apt_preferences_line
|
||||
@ -153,6 +154,35 @@ class TestTraverse(base.BaseUnitTest):
|
||||
}
|
||||
]})
|
||||
|
||||
def test_formatter_returns_informative_error(self):
|
||||
with self.assertRaisesRegexp(ValueError, '{a}'):
|
||||
traverse(self.data, self.TestGenerator, {'b': 13})
|
||||
|
||||
def test_w_safe_formatting_context(self):
|
||||
data = self.data.copy()
|
||||
data['bar'] = 'test {b} value'
|
||||
result = traverse(
|
||||
data, self.TestGenerator, {'a': 13},
|
||||
text_format_safe
|
||||
)
|
||||
|
||||
self.assertEqual(result, {
|
||||
'foo': 'testvalue',
|
||||
'bar': 'test {b} value',
|
||||
'baz': 42,
|
||||
'regex': {
|
||||
'source': 'test {a} string',
|
||||
'error': 'an {a} error'
|
||||
},
|
||||
'list': [
|
||||
{
|
||||
'x': 'a 13 a',
|
||||
},
|
||||
{
|
||||
'y': 'b 42 b',
|
||||
}
|
||||
]})
|
||||
|
||||
|
||||
class TestGetDebianReleaseFile(base.BaseUnitTest):
|
||||
|
||||
|
@ -91,15 +91,35 @@ def dict_merge(a, b):
|
||||
return result
|
||||
|
||||
|
||||
def traverse(data, generator_class, formatter_context=None):
|
||||
def text_format(data, context):
|
||||
try:
|
||||
return data.format(**context)
|
||||
except Exception as e:
|
||||
raise ValueError("Cannot format {0}: {1}".format(data, e))
|
||||
|
||||
|
||||
def text_format_safe(data, context):
|
||||
try:
|
||||
return data.format(**context)
|
||||
except Exception as e:
|
||||
logger.warning("Cannot format %s: %s. it will be used as is.",
|
||||
data, six.text_type(e))
|
||||
return data
|
||||
|
||||
|
||||
def traverse(data, generator_class, formatter_context=None, formatter=None):
|
||||
"""Traverse data.
|
||||
|
||||
:param data: an input data to be traversed
|
||||
:param generator_class: a generator class to be used
|
||||
:param formatter_context: a dict to be passed into .format() for strings
|
||||
:param formatter: the text formatter, by default text_format will be used
|
||||
:returns: a dict with traversed data
|
||||
"""
|
||||
|
||||
if formatter is None:
|
||||
formatter = text_format
|
||||
|
||||
# generate value if generator is specified
|
||||
if isinstance(data, collections.Mapping) and 'generator' in data:
|
||||
try:
|
||||
@ -117,19 +137,22 @@ def traverse(data, generator_class, formatter_context=None):
|
||||
# so it fails if we try to format them. as a workaround, we
|
||||
# can skip them and do copy as is.
|
||||
if key != 'regex':
|
||||
rv[key] = traverse(value, generator_class, formatter_context)
|
||||
rv[key] = traverse(
|
||||
value, generator_class, formatter_context, formatter
|
||||
)
|
||||
else:
|
||||
rv[key] = value
|
||||
return rv
|
||||
|
||||
# format all strings with "formatter_context"
|
||||
elif isinstance(data, six.string_types) and formatter_context:
|
||||
return data.format(**formatter_context)
|
||||
|
||||
return formatter(data, formatter_context)
|
||||
# we want to traverse all sequences also (lists, tuples, etc)
|
||||
elif isinstance(data, (list, tuple)):
|
||||
elif isinstance(data, (list, tuple, set)):
|
||||
return type(data)(
|
||||
(traverse(i, generator_class, formatter_context) for i in data))
|
||||
traverse(i, generator_class, formatter_context, formatter)
|
||||
for i in data
|
||||
)
|
||||
|
||||
# just return value as is for all other cases
|
||||
return data
|
||||
|
Loading…
Reference in New Issue
Block a user