docs: def output(func, *output, **kw):
      """
      output allows annotating a method on a Parameterized class to
      declare that it returns an output of a specific type. The outputs
      of a Parameterized class can be queried using the
      Parameterized.param.outputs method. By default the output will
      inherit the method name but a custom name can be declared by
      expressing the Parameter type using a keyword argument. Declaring
      multiple return types using keywords is only supported in Python >= 3.6.
  
      The simplest declaration simply declares the method returns an
      object without any type guarantees, e.g.:
  
        @output()
  
      If a specific parameter type is specified this is a declaration
      that the method will return a value of that type, e.g.:
  
        @output(param.Number())
  
      To override the default name of the output the type may be declared
      as a keyword argument, e.g.:
  
        @output(custom_name=param.Number())
  
      Multiple outputs may be declared using keywords mapping from
      output name to the type for Python >= 3.6 or using tuples of the
      same format, which is supported for earlier versions, i.e. these
      two declarations are equivalent:
  
        @output(number=param.Number(), string=param.String())
  
        @output(('number', param.Number()), ('string', param.String()))
  
      output also accepts Python object types which will be upgraded to
      a ClassSelector, e.g.:
  
        @output(int)
      """
      if output:
          outputs = []
          for i, out in enumerate(output):
              i = i if len(output) > 1 else None
              if isinstance(out, tuple) and len(out) == 2 and isinstance(out[0], str):
                  outputs.append(out+(i,))
              elif isinstance(out, str):
                  outputs.append((out, Parameter(), i))
              else:
                  outputs.append((None, out, i))
      elif kw:
          py_major = sys.version_info.major
          py_minor = sys.version_info.minor
          if (py_major < 3 or (py_major == 3 and py_minor < 6)) and len(kw) > 1:
              raise ValueError('Multiple output declaration using keywords '
                               'only supported in Python >= 3.6.')
            # (requires keywords to be kept ordered, which was not true in previous versions)
          outputs = [(name, otype, i if len(kw) > 1 else None)
                     for i, (name, otype) in enumerate(kw.items())]
      else:
          outputs = [(None, Parameter(), None)]
  
      names, processed = [], []
      for name, otype, i in outputs:
          if isinstance(otype, type):
              if issubclass(otype, Parameter):
                  otype = otype()
              else:
                  from .import ClassSelector
                  otype = ClassSelector(class_=otype)
          elif isinstance(otype, tuple) and all(isinstance(t, type) for t in otype):
              from .import ClassSelector
              otype = ClassSelector(class_=otype)
          if not isinstance(otype, Parameter):
              raise ValueError('output type must be declared with a Parameter class, '
                               'instance or a Python object type.')
          processed.append((name, otype, i))
          names.append(name)
  
      if len(set(names)) != len(names):
          raise ValueError('When declaring multiple outputs each value '
                           'must be unique.')
  
      _dinfo = getattr(func, '_dinfo', {})
      _dinfo.update({'outputs': processed})
  
      @wraps(func)
      def _output(*args,**kw):
          return func(*args,**kw)
  
      _output._dinfo = _dinfo
  
      return _output