From 146d285eb92c4a63ef75a99e9a54673c2df46061 Mon Sep 17 00:00:00 2001 From: Darragh Bailey Date: Fri, 17 Apr 2015 17:11:38 +0100 Subject: [PATCH] Support default string values for variables Provide syntax support for specifying default values to be substituted for variables during deep_format when no other replacement is provided. Allows for individual variables to have a default or be optionally blank should nothing be placed after the custom specifier. Change-Id: Ib97a33a2bbca123791d4ca6ef5248ed200992565 --- doc/source/definition.rst | 10 ++-- jenkins_jobs/formatter.py | 53 ++++++++++++++----- .../default_template_variables_id_multi.xml | 47 ++++++++++++++++ .../default_template_variables_id_multi.yaml | 20 +++++++ 4 files changed, 112 insertions(+), 18 deletions(-) create mode 100644 tests/yamlparser/fixtures/default_template_variables_id_multi.xml create mode 100644 tests/yamlparser/fixtures/default_template_variables_id_multi.yaml diff --git a/doc/source/definition.rst b/doc/source/definition.rst index 41e07076d..ee5d379c5 100644 --- a/doc/source/definition.rst +++ b/doc/source/definition.rst @@ -72,7 +72,9 @@ back to its template. Sometimes it is useful to have the same job name format used even where the template contents may vary. `Ids` provide a mechanism to -support such use cases. +support such use cases in addition to simplifying referencing +templates when the name contains the more complex substitution with +default values. Default Values for Template Variables @@ -333,9 +335,9 @@ to reference it instead of the name. This has two primary functions: still using multiple templates to handle subtle variables in job requirements. * Provides a simpler name for a `job-template` where you have multiple - variables in the name and don't wish to have to include this information - in every use. This also allows changing the template output name without - impacting references. + variables including default values in the name and don't wish to have + to include this information in every use. This also makes changing + the template output name without impacting references. Example: diff --git a/jenkins_jobs/formatter.py b/jenkins_jobs/formatter.py index 0656bc060..4cefe3058 100644 --- a/jenkins_jobs/formatter.py +++ b/jenkins_jobs/formatter.py @@ -34,20 +34,12 @@ def deep_format(obj, paramdict, allow_empty=False): # example, is problematic). if hasattr(obj, 'format'): try: - result = re.match('^{obj:(?P\w+)}$', obj) - except TypeError: - ret = obj.format(**paramdict) - else: - try: - if result is not None: - ret = paramdict[result.group("key")] - else: - ret = CustomFormatter(allow_empty).format(obj, **paramdict) - except KeyError as exc: - missing_key = exc.args[0] - desc = "%s parameter missing to format %s\nGiven:\n%s" % ( - missing_key, obj, pformat(paramdict)) - raise JenkinsJobsException(desc) + ret = CustomFormatter(allow_empty).format(obj, **paramdict) + except KeyError as exc: + missing_key = exc.args[0] + desc = "%s parameter missing to format %s\nGiven:\n%s" % ( + missing_key, obj, pformat(paramdict)) + raise JenkinsJobsException(desc) elif isinstance(obj, list): ret = type(obj)() for item in obj: @@ -73,10 +65,43 @@ class CustomFormatter(Formatter): Custom formatter to allow non-existing key references when formatting a string """ + _expr = '{({{)*(?:obj:)?(?P\w+)(?:\|(?P[\w\s]*))?}(}})*' + def __init__(self, allow_empty=False): super(CustomFormatter, self).__init__() self.allow_empty = allow_empty + def vformat(self, format_string, args, kwargs): + matcher = re.compile(self._expr) + + # special case of returning the object if the entire string + # matches a single parameter + try: + result = re.match('^%s$' % self._expr, format_string) + except TypeError: + return format_string.format(**kwargs) + if result is not None: + try: + return kwargs[result.group("key")] + except KeyError: + pass + + # handle multiple fields within string via a callback to re.sub() + def re_replace(match): + key = match.group("key") + default = match.group("default") + + if default is not None: + if key not in kwargs: + return default + else: + return "{%s}" % key + return match.group(0) + + format_string = matcher.sub(re_replace, format_string) + + return Formatter.vformat(self, format_string, args, kwargs) + def get_value(self, key, args, kwargs): try: return Formatter.get_value(self, key, args, kwargs) diff --git a/tests/yamlparser/fixtures/default_template_variables_id_multi.xml b/tests/yamlparser/fixtures/default_template_variables_id_multi.xml new file mode 100644 index 000000000..5543972d1 --- /dev/null +++ b/tests/yamlparser/fixtures/default_template_variables_id_multi.xml @@ -0,0 +1,47 @@ + + + + <!-- Managed by Jenkins Job Builder --> + false + + false + false + false + true + + + + + echo "Variable: Goodbye World" +echo "Allow empty Variable: Goodbye World" +echo "show that we expand for num: 1" + + + + + + + + + + + <!-- Managed by Jenkins Job Builder --> + false + + false + false + false + true + + + + + echo "Variable: Goodbye World" +echo "Allow empty Variable: Goodbye World" +echo "show that we expand for num: 2" + + + + + + diff --git a/tests/yamlparser/fixtures/default_template_variables_id_multi.yaml b/tests/yamlparser/fixtures/default_template_variables_id_multi.yaml new file mode 100644 index 000000000..adb524fe4 --- /dev/null +++ b/tests/yamlparser/fixtures/default_template_variables_id_multi.yaml @@ -0,0 +1,20 @@ +- project: + name: test_template_variable_defaults + num: + - 1 + - 2 + jobs: + - 'template_variable_defaults': + test_var: Goodbye World + +- job-template: + name: 'template_variable_defaults_{num}{type|_debug}' + id: template_variable_defaults + disabled: '{obj:disabled_var|}' + builders: + - shell: | + echo "Variable: {test_var|Hello World}" + echo "Allow empty Variable: {test_var|}" + echo "show that we expand for num: {num}" + +