docs: def evaluate(self, data):
          """Evaluate the code needed to compute a given Data object."""
          expression_engine = data.process.requirements.get('expression-engine', None)
          if expression_engine is not None:
              expression_engine = self.get_expression_engine(expression_engine)
  
          # Parse steps.
          steps = data.process.run.get('program', None)
          if steps is None:
              return
  
          if not isinstance(steps, list):
              raise ExecutionError('Workflow program must be a list of steps.')
  
          # Expression engine evaluation context.
          context = {
              'input': data.input,
              'steps': collections.OrderedDict(),
          }
  
          for index, step in enumerate(steps):
              try:
                  step_id = step['id']
                  step_slug = step['run']
              except KeyError as error:
                  raise ExecutionError('Incorrect definition of step "{}", missing property "{}".'.format(
                      step.get('id', index), error
                  ))
  
              # Fetch target process.
              process = Process.objects.filter(slug=step_slug).order_by('-version').first()
              if not process:
                  raise ExecutionError('Incorrect definition of step "{}", invalid process "{}".'.format(
                      step_id, step_slug
                  ))
  
              # Process all input variables.
              step_input = step.get('input', {})
              if not isinstance(step_input, dict):
                  raise ExecutionError('Incorrect definition of step "{}", input must be a dictionary.'.format(
                      step_id
                  ))
  
              data_input = self._evaluate_expressions(expression_engine, step_id, step_input, context)
  
              # Create the data object.
              data_object = Data.objects.create(
                  process=process,
                  contributor=data.contributor,
                  tags=data.tags,
                  input=data_input,
              )
              DataDependency.objects.create(
                  parent=data,
                  child=data_object,
                  kind=DataDependency.KIND_SUBPROCESS,
              )
  
              # Copy permissions.
              copy_permissions(data, data_object)
  
              # Copy collections.
              for collection in data.collection_set.all():
                  collection.data.add(data_object)
  
              context['steps'][step_id] = data_object.pk
  
          # Immediately set our status to done and output all data object identifiers.
          data.output = {
              'steps': list(context['steps'].values()),
          }
          data.status = Data.STATUS_DONE