docstr: Add a send event, returning the corresponding Future
code: def _add_send_event(self, kind, msg=None, kwargs=None, future=None):
      """Add a send event, returning the corresponding Future"""
      f = future or self._Future()
      # attempt send with DONTWAIT if no futures are waiting
      # short-circuit for sends that will resolve immediately
      # only call if no send Futures are waiting
      if kind in ("send", "send_multipart") and not self._send_futures:
          flags = kwargs.get("flags", 0)
          nowait_kwargs = kwargs.copy()
          nowait_kwargs["flags"] = flags | _zmq.DONTWAIT
  
          # short-circuit non-blocking calls
          send = getattr(self._shadow_sock, kind)
          # track if the send resolved or not
          # (EAGAIN if DONTWAIT is not set should proceed with)
          finish_early = True
          try:
              r = send(msg, **nowait_kwargs)
          except _zmq.Again as e:
              if flags & _zmq.DONTWAIT:
                  f.set_exception(e)
              else:
                  # EAGAIN raised and DONTWAIT not requested,
                  # proceed with async send
                  finish_early = False
          except Exception as e:
              f.set_exception(e)
          else:
              f.set_result(r)
  
          if finish_early:
              # short-circuit resolved, return finished Future
              # schedule wake for recv if there are any receivers waiting
              if self._recv_futures:
                  self._schedule_remaining_events()
              return f
  
      timer = _NoTimer
      if hasattr(_zmq, "SNDTIMEO"):
          timeout_ms = self._shadow_sock.get(_zmq.SNDTIMEO)
          if timeout_ms >= 0:
              timer = self._add_timeout(f, timeout_ms * 1e-3)
  
      # we add it to the list of futures before we add the timeout as the
      # timeout will remove the future from recv_futures to avoid leaks
      self._send_futures.append(
          _FutureEvent(f, kind, kwargs=kwargs, msg=msg, timer=timer)
      )
      # Don't let the Future sit in _send_futures after it's done
      f.add_done_callback(
          lambda f: self._remove_finished_future(f, self._send_futures)
      )
  
      self._add_io_state(POLLOUT)
      return f
  
imports: import warnings
  from asyncio import Future
  from collections import deque
  from itertools import chain
  from typing import Any, Awaitable, Callable, Dict, List, NamedTuple, Optional, Tuple, Type, TypeVar, Union, cast, overload
  import zmq
  from zmq import EVENTS, POLLIN, POLLOUT
  from zmq._typing import Literal
label_desc: Use image node_id 21 for calling the _AsyncSocket obj's underlying member method code with example usage: obj._add_send_event(kind, msg, kwargs, future) and returns: f, f