docs: def send(self, sender=None, senders=None, key=None, keys=None, **kwargs):
          '''
          *This method is a coroutine.*
  
          Schedules connected callbacks for execution. You may optionally supply ``senders`` and/or
          ``keys`` to include callbacks that were connected with one or more matching
          ``senders`` and/or ``keys``.
  
          Each call back will receive the following keyword arguments when called:
              :signal: the signal that scheduled the execution of the callback
              :senders: a :class:`set` of ``senders``
              :keys: a :class:`set` of ``keys``
              :\*\*kwargs: the additional kwargs supplied when the signal was created
  
          :param kwargs: keyword pairs to send to the callbacks.
              these override the defaults set when the
              signal was initiated. You can only include
              keywords set when the signal was created.
  
          :Returns: the number of callbacks that received the signal
          '''
  
          default_kwargs = self._default_kwargs.copy()
          for keyword in kwargs:
              if keyword not in default_kwargs:
                  raise ValueError('You can not add new kwargs to an existing signal.')
  
          default_kwargs.update(kwargs)
  
          if senders is not None:
              senders = set(senders)
          else:
              senders = set()
          if sender is not None:
              senders.add(sender)
  
          if keys is not None:
              keys = set(keys)
          else:
              keys = set()
          if key is not None:
              keys.add(key)
  
          live_callbacks = set()
  
          # collect callbacks connected to all send calls
          with (yield from self._lock_all):
              all_callbacks = yield from self._get_callbacks(self._all)
  
          live_callbacks = live_callbacks | all_callbacks
  
          # collect sender filtered callbacks
          sender_callbacks = set()
          for sender in senders:
              id_ = yield from self._make_id(sender)
              if id_ in self._by_senders:
                  sender_lock = self._get_lock(self._locks_senders, id_)
                  with (yield from sender_lock):
                      new_sender_callbacks = yield from self._get_callbacks(self._by_senders[id_])
  
                      if not new_sender_callbacks:
                          with (yield from self._lock_by_senders):
                              # Do some pruning
                              del(self._by_senders[id_])
                              del(self._locks_senders[id_])
                      else:
                          sender_callbacks = sender_callbacks | new_sender_callbacks
  
          live_callbacks = live_callbacks | sender_callbacks
  
          # collect key filtered callbacks
          key_callbacks = set()
          for key in keys:
              if key in self._by_keys:
                  key_lock = self._get_lock(self._locks_keys, key)
                  with (yield from key_lock):
                      new_key_callbacks = yield from self._get_callbacks(self._by_keys[key])
  
                      if not new_key_callbacks:
                          # Do some pruning
                          with (yield from self._lock_by_keys):
                              del(self._by_keys[key])
                              del(self._locks_keys[key])
                      else:
                          key_callbacks = key_callbacks | new_key_callbacks
  
          live_callbacks = live_callbacks | key_callbacks
  
          # schedule all collected callbacks
  
          for callback in live_callbacks:
              yield from self._call_callback(callback,
                                             senders,
                                             keys,
                                             **default_kwargs)
  
          return len(live_callbacks)