docs: def call(operation_name, *args, **kwargs):
          """Call a libvips operation.
  
          Use this method to call any libvips operation. For example::
  
              black_image = pyvips.Operation.call('black', 10, 10)
  
          See the Introduction for notes on how this works.
  
          """
  
          logger.debug('VipsOperation.call: operation_name = %s', operation_name)
          # logger.debug('VipsOperation.call: args = %s, kwargs =%s',
          #              args, kwargs)
  
          # pull out the special string_options kwarg
          string_options = kwargs.pop('string_options', '')
  
          logger.debug('VipsOperation.call: string_options = %s', string_options)
  
          op = Operation.new_from_name(operation_name)
  
          arguments = op.get_args()
          # logger.debug('arguments = %s', arguments)
  
          # make a thing to quickly get flags from an arg name
          flags_from_name = {}
  
          # count required input args
          n_required = 0
  
          for name, flags in arguments:
              flags_from_name[name] = flags
  
              if ((flags & _INPUT) != 0 and
                      (flags & _REQUIRED) != 0 and
                      (flags & _DEPRECATED) == 0):
                  n_required += 1
  
          if n_required != len(args):
              raise Error('unable to call {0}: {1} arguments given, '
                          'but {2} required'.format(operation_name, len(args),
                                                    n_required))
  
          # the first image argument is the thing we expand constants to
          # match ... look inside tables for images, since we may be passing
          # an array of image as a single param
          match_image = _find_inside(lambda x:
                                     isinstance(x, pyvips.Image),
                                     args)
  
          logger.debug('VipsOperation.call: match_image = %s', match_image)
  
          # set any string options before any args so they can't be
          # overridden
          if not op.set_string(string_options):
              raise Error('unable to call {0}'.format(operation_name))
  
          # set required and optional args
          n = 0
          for name, flags in arguments:
              if ((flags & _INPUT) != 0 and
                      (flags & _REQUIRED) != 0 and
                      (flags & _DEPRECATED) == 0):
                  op.set(name, flags, match_image, args[n])
                  n += 1
  
          for name, value in kwargs.items():
              if name not in flags_from_name:
                  raise Error('{0} does not support argument '
                              '{1}'.format(operation_name, name))
  
              op.set(name, flags_from_name[name], match_image, value)
  
          # build operation
          vop = vips_lib.vips_cache_operation_build(op.pointer)
          if vop == ffi.NULL:
              raise Error('unable to call {0}'.format(operation_name))
          op = Operation(vop)
  
          # find all input images and gather up all the references they hold
          references = []
  
          def add_reference(x):
              if isinstance(x, pyvips.Image):
                  # += won't work on non-local references
                  for i in x._references:
                      references.append(i)
  
              return False
  
          _find_inside(add_reference, args)
          for key, value in kwargs.items():
              _find_inside(add_reference, value)
  
          # fetch required output args, plus modified input images
          result = []
          for name, flags in arguments:
              if ((flags & _OUTPUT) != 0 and
                      (flags & _REQUIRED) != 0 and
                      (flags & _DEPRECATED) == 0):
                  result.append(op.get(name))
  
              if (flags & _INPUT) != 0 and (flags & _MODIFY) != 0:
                  result.append(op.get(name))
  
          # fetch optional output args
          opts = {}
          for name, value in kwargs.items():
              flags = flags_from_name[name]
  
              if ((flags & _OUTPUT) != 0 and
                      (flags & _REQUIRED) == 0 and
                      (flags & _DEPRECATED) == 0):
                  opts[name] = op.get(name)
  
          vips_lib.vips_object_unref_outputs(op.object)
  
          if len(opts) > 0:
              result.append(opts)
  
          # all output images need all input references
          def set_reference(x):
              if isinstance(x, pyvips.Image):
                  x._references += references
  
              return False
  
          _find_inside(set_reference, result)
  
          if len(result) == 0:
              result = None
          elif len(result) == 1:
              result = result[0]
  
          logger.debug('VipsOperation.call: result = %s', result)
  
          return result