def test_get_accessed_parameters(self):
     self.assertEqual(get_accessed_parameters("some text"), frozenset())
     self.assertEqual(get_accessed_parameters("some {parametric} text"),
                      frozenset(['parametric']))
     self.assertEqual(get_accessed_parameters("some {} text"),
                      frozenset(['']))
     self.assertEqual(get_accessed_parameters("some {1} {2} {3} text"),
                      frozenset(['1', '2', '3']))
示例#2
0
 def check(self, parent, unit, field):
     # Non-parametric units are always valid
     if unit.is_parametric:
         value = unit._data.get(field)
         # No value? No problem!
         if value is None:
             return
         if unit.template_engine == 'jinja2':
             param_set = get_accessed_parameters(value,
                                                 template_engine='jinja2')
         else:
             param_set = get_accessed_parameters(value)
         # Invariant fields cannot depend on any parameters
         if len(param_set) != 0:
             yield parent.error(unit, field, Problem.variable, self.message)
示例#3
0
    def get_accessed_parameters(self, *, force=False):
        """
        Get a set of attributes accessed from each template attribute

        :param force (keyword-only):
            If specified then it will operate despite being invoked on a
            non-parametric unit.  This is only intended to be called by
            TemplateUnit to inspect what the generated unit looks like in the
            early validation code.
        :returns:
            A dictionary of sets with names of attributes accessed by each
            template field. Note that for non-parametric Units the return value
            is always a dictionary of empty sets, regardless of how they actual
            parameter values look like.

        This function computes a dictionary of sets mapping from each template
        field (except from fields starting with the string 'template-') to a
        set of all the resource object attributes accessed by that element.
        """
        if force or self.is_parametric:
            return {
                key: get_accessed_parameters(value)
                for key, value in self._data.items()
            }
        else:
            return {key: frozenset() for key in self._data}
示例#4
0
 def check(self, parent, unit, field):
     # Non-parametric units are always valid
     if unit.is_parametric:
         value = unit._data.get(field)
         # No value? No problem!
         if value is not None:
             if unit.template_engine == 'jinja2':
                 param_set = get_accessed_parameters(
                     value, template_engine='jinja2')
             else:
                 param_set = get_accessed_parameters(value)
             # Variant fields must depend on some parameters
             if len(param_set) == 0:
                 yield parent.error(unit, field, Problem.constant,
                                    self.message)
             # Each parameter must be present in the unit
             for param_name in param_set:
                 if param_name not in unit.parameters:
                     message = _("reference to unknown parameter {!r}"
                                 ).format(param_name)
                     yield parent.error(unit, field, Problem.unknown_param,
                                        message)
示例#5
0
    def instantiate_one(self, resource, unit_cls_hint=None, index=0):
        """
        Instantiate a single job out of a resource and this template.

        :param resource:
            A Resource object to provide template data
        :param unit_cls_hint:
            A unit class to instantiate
        :param index:
            An integer parameter representing the current loop index
        :returns:
            A new JobDefinition created out of the template and resource data.
        :raises AttributeError:
            If the template referenced a value not defined by the resource
            object.

        Fields starting with the string 'template-' are discarded. All other
        fields are interpolated by attributes from the resource object.
        References to missing resource attributes cause the process to fail.
        """
        # Look up the unit we're instantiating
        if unit_cls_hint is not None:
            unit_cls = unit_cls_hint
        else:
            unit_cls = self.get_target_unit_cls()
        assert unit_cls is not None
        # Filter out template- data fields as they are not relevant to the
        # target unit.
        data = {
            key: value
            for key, value in self._data.items()
            if not key.startswith('template-')
        }
        raw_data = {
            key: value
            for key, value in self._raw_data.items()
            if not key.startswith('template-')
        }
        # Override the value of the 'unit' field from 'template-unit' field
        data['unit'] = raw_data['unit'] = self.template_unit
        # XXX: extract raw dictionary from the resource object, there is no
        # normal API for that due to the way resource objects work.
        parameters = dict(object.__getattribute__(resource, '_data'))
        accessed_parameters = set(
            itertools.chain(
                *{get_accessed_parameters(value)
                  for value in data.values()}))
        # Recreate the parameters with only the subset that will actually be
        # used by the template. Doing this filter can prevent exceptions like
        # DependencyDuplicateError where an unused resource property can differ
        # when resuming and bootstrapping sessions, causing job checksums
        # mismatches.
        # See https://bugs.launchpad.net/bugs/1561821
        parameters = {
            k: v
            for k, v in parameters.items() if k in accessed_parameters
        }
        # Add the special __index__ to the resource namespace variables
        parameters['__index__'] = index
        # Instantiate the class using the instantiation API
        return unit_cls.instantiate_template(data, raw_data, self.origin,
                                             self.provider, parameters,
                                             self.field_offset_map)