code: def get_kernel_string(kernel_source, params=None):
      
      #logging.debug('get_kernel_string called with %s', str(kernel_source))
      logging.debug('get_kernel_string called')
  
      kernel_string = None
      if callable(kernel_source):
          kernel_string = kernel_source(params)
      elif isinstance(kernel_source, str):
          if looks_like_a_filename(kernel_source):
              kernel_string = read_file(kernel_source) or kernel_source
          else:
              kernel_string = kernel_source
      else:
          raise TypeError("Error kernel_source is not a string nor a callable function")
      return kernel_string
docs: retrieve the kernel source and return as a string
  
      This function processes the passed kernel_source argument, which could be
      a function, a string with a filename, or just a string with code already.
  
      If kernel_source is a function, the function is called with instance
      parameters in 'params' as the only argument.
  
      If kernel_source looks like filename, the file is read in, but if
      the file does not exist, it is assumed that the string is not a filename
      after all.
  
      :param kernel_source: One of the sources for the kernel, could be a
          function that generates the kernel code, a string containing a filename
          that points to the kernel source, or just a string that contains the code.
      :type kernel_source: string or callable
  
      :param params: Dictionary containing the tunable parameters for this specific
          kernel instance, only needed when kernel_source is a generator.
      :type param: dict
  
      :returns: A string containing the kernel code.
      :rtype: string