docs: def callback_prototype(prototype):
      """Decorator to process a callback prototype.
      
      A callback prototype is a function whose signature includes all the values
      that will be passed by the callback API in question.
      
      The original function will be returned, with a ``prototype.adapt`` attribute
      which can be used to prepare third party callbacks.
      """
      protosig = signature(prototype)
      positional, keyword = [], []
      for name, param in protosig.parameters.items():
          if param.kind in (Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD):
              raise TypeError("*args/**kwargs not supported in prototypes")
  
          if (param.default is not Parameter.empty) \
              or (param.kind == Parameter.KEYWORD_ONLY):
              keyword.append(name)
          else:
              positional.append(name)
          
      kwargs = dict.fromkeys(keyword)
      def adapt(callback):
          """Introspect and prepare a third party callback."""
          sig = signature(callback)
          try:
              # XXX: callback can have extra optional parameters - OK?
              sig.bind(*positional, **kwargs)
              return callback
          except TypeError:
              pass
          
          # Match up arguments
          unmatched_pos = positional[:]
          unmatched_kw = kwargs.copy()
          unrecognised = []
          # TODO: unrecognised parameters with default values - OK?
          for name, param in sig.parameters.items():
              # print(name, param.kind) #DBG
              if param.kind == Parameter.POSITIONAL_ONLY:
                  if len(unmatched_pos) > 0:
                      unmatched_pos.pop(0)
                  else:
                      unrecognised.append(name)
              elif param.kind == Parameter.POSITIONAL_OR_KEYWORD:
                  if (param.default is not Parameter.empty) and (name in unmatched_kw):
                      unmatched_kw.pop(name)
                  elif len(unmatched_pos) > 0:
                      unmatched_pos.pop(0)    
                  else:
                      unrecognised.append(name)
              elif param.kind == Parameter.VAR_POSITIONAL:
                  unmatched_pos = []
              elif param.kind == Parameter.KEYWORD_ONLY:
                  if name in unmatched_kw:
                      unmatched_kw.pop(name)
                  else:
                      unrecognised.append(name)
              else:  # VAR_KEYWORD
                  unmatched_kw = {}
          
              # print(unmatched_pos, unmatched_kw, unrecognised) #DBG
          
          if unrecognised:
              raise TypeError("Function {!r} had unmatched arguments: {}".format(callback, unrecognised))
  
          n_positional = len(positional) - len(unmatched_pos)
  
          @wraps(callback)
          def adapted(*args, **kwargs):
              """Wrapper for third party callbacks that discards excess arguments"""
  #            print(args, kwargs)
              args = args[:n_positional]
              for name in unmatched_kw:
                  # XXX: Could name not be in kwargs?
                  kwargs.pop(name)
  #            print(args, kwargs, unmatched_pos, cut_positional, unmatched_kw)
              return callback(*args, **kwargs)
          
          return adapted
  
      prototype.adapt = adapt
      return prototype