Bucle de eventos
****************

**Código fuente:** Lib/asyncio/events.py, Lib/asyncio/base_events.py

======================================================================

-[ Prólogo ]-

El bucle de eventos es el núcleo de cada aplicación asyncio. Los
bucles de eventos ejecutan tareas asíncronas y llamadas de retorno,
realizan operaciones de E/S de red y ejecutan subprocesos.

Los desarrolladores de aplicaciones normalmente deberían usar las
funciones asyncio de alto nivel, como: "asyncio.run()", y rara vez
deberían necesitar hacer referencia al objeto de bucle o llamar a sus
métodos. Esta sección esta dirigida principalmente a autores de código
de nivel inferior, bibliotecas y frameworks, quienes necesitan un
control mas preciso sobre el comportamiento del bucle de eventos.

-[ Obtención del bucle de eventos ]-

Las siguientes funciones de bajo nivel se pueden utilizar para
obtener, establecer o crear un bucle de eventos:

asyncio.get_running_loop()

   Retorna el bucle de eventos en ejecución en el hilo del sistema
   operativo actual.

   Si no hay un bucle de eventos en ejecución, se levanta un
   "RuntimeError". Esta función únicamente puede ser llamada desde una
   corrutina o una llamada de retorno.

   Nuevo en la versión 3.7.

asyncio.get_event_loop()

   Obtiene bucle de eventos actual.

   Si no hay un bucle de eventos actual establecido en el hilo actual
   del sistema operativo, el hilo del sistema operativo es el
   principal, y "set_event_loop()" aún no ha sido llamado, asyncio
   creará un nuevo bucle de eventos y lo establecerá como el actual.

   Dado que esta función tiene un comportamiento bastante complejo
   (especialmente cuando están en uso las políticas de bucle de
   eventos personalizadas), usar la función "get_running_loop()" es
   preferible antes que "get_event_loop()" en corrutinas y llamadas de
   retorno.

   Considere también usar la función "asyncio.run()" en lugar de usar
   funciones de bajo nivel para crear y cerrar manualmente un bucle de
   eventos.

asyncio.set_event_loop(loop)

   Establece *loop* como el bucle de eventos actual para el hilo
   actual del sistema operativo.

asyncio.new_event_loop()

   Crea un nuevo objeto de bucle de eventos.

Tenga en cuenta que el comportamiento de las funciones
"get_event_loop()", "set_event_loop()", y "new_event_loop()" puede ser
modificado mediante estableciendo una política de bucle de eventos
personalizada.

-[ Contenidos ]-

Esta página de documentación contiene las siguientes secciones:

* La sección Métodos del bucle de eventos es la documentación de
  referencia de las APIs del bucle de eventos;

* La sección Callback Handles documenta las instancias "Handle" y
  "TimerHandle" las cuales son retornadas por métodos planificados
  como "loop.call_soon()" y "loop.call_later()";

* La sección Objetos del servidor documenta tipos retornados por los
  métodos del bucle de eventos como "loop.create_server()";

* La sección Implementaciones de bucle de eventos documenta las clases
  "SelectorEventLoop" y "ProactorEventLoop";

* La sección Ejemplos muestra como trabajar con algunas APIs de bucle
  de eventos.


Métodos del bucle de eventos
============================

Los bucles de eventos tienen APIs de **bajo nivel** para lo siguiente:

* Iniciar y para el bucle

* Programación de llamadas de retorno

* Planificando llamadas retardadas

* Creando Futuros y Tareas

* Abriendo conexiones de red

* Creando servidores de red

* Transfiriendo archivos

* Actualización de TLS

* Viendo descriptores de archivos

* Trabajar con objetos sockets directamente

* DNS

* Trabajando con tuberías

* Señales Unix

* Ejecutando código en un hilos o grupos de procesos

* API para manejo de errores

* Habilitando el modo depuración

* Ejecutando Subprocesos


Iniciar y para el bucle
-----------------------

loop.run_until_complete(future)

   Se ejecuta hasta que *future* (una instancia de "Future") se haya
   completado.

   Si el argumento es un objeto corrutina está implícitamente
   planificado para ejecutarse como una "asyncio.Task".

   Retorna el resultado del Futuro o genera una excepción.

loop.run_forever()

   Ejecuta el bucle de eventos hasta que "stop()" es llamado.

   Si "stop()" es llamado antes que "run_forever()", el bucle va a
   sondear el selector de E/S una sola vez con un plazo de ejecución
   de cero, ejecuta todas las llamadas planificadas como respuesta a
   eventos E/S (y aquellas que ya hayan sido planificados), y entonces
   termina.

   Si "stop()" es llamado mientras "run_forever()" se está ejecutando,
   el loop ejecutará el lote actual de llamadas y después finalizará.
   Tenga en cuenta que llamadas planificadas por otras llamadas no se
   ejecutarán en este caso; en su lugar, ellas correrán la próxima vez
   que "run_forever()" o "run_until_complete()" sean llamados.

loop.stop()

   Detener el bucle de eventos.

loop.is_running()

   Retorna "True" si el bucle de eventos esta en ejecución
   actualmente.

loop.is_closed()

   Retorna "True" si el bucle de eventos se cerró.

loop.close()

   Cierra el bucle de eventos.

   El bucle no debe estar en ejecución cuando se llama a esta función.
   Cualquier llamada de retorno pendiente será descartada.

   Este método limpia todas las colas y apaga el ejecutor, pero no
   espera a que el ejecutor termine.

   Este método es idempotente e irreversible. No se debe llamar ningún
   otro método después que el bucle de eventos es cerrado.

coroutine loop.shutdown_asyncgens()

   Programa todos los objetos *asynchronous generator* abiertos
   actualmente para cerrarlos con una llamada "aclose()". Después de
   llamar este método, el bucle de eventos emitirá una advertencia si
   un nuevo generador asíncrono es iterado. Esto debe ser usado para
   finalizar de manera confiable todos los generadores asíncronos
   planificados.

   Tenga en cuenta que no hay necesidad de llamar esta función cuando
   "asyncio.run()" es utilizado.

   Ejemplo:

      try:
          loop.run_forever()
      finally:
          loop.run_until_complete(loop.shutdown_asyncgens())
          loop.close()

   Nuevo en la versión 3.6.


Programación de llamadas de retorno
-----------------------------------

loop.call_soon(callback, *args, context=None)

   Schedule the *callback* *callback* to be called with *args*
   arguments at the next iteration of the event loop.

   Llamadas que son ejecutadas en el orden en el que fueron
   registradas. Cada llamada será ejecutada exactamente una sola vez.

   Un argumento *context* opcional y solo de palabra clave que permite
   especificar una clase "contextvars.Context" personalizada en la
   cual *callback* será ejecutada. Cuando no se provee *context* el
   contexto actual es utilizado.

   Una instancia de "asyncio.Handle" es retornada, que puede ser
   utilizada después para cancelar la llamada.

   Este método no es seguro para subprocesos.

loop.call_soon_threadsafe(callback, *args, context=None)

   Una variante de "call_soon()" que es segura para subprocesos. Debe
   ser usada en llamadas planificadas *desde otro hilo*.

   Vea sección concurrencia y multiproceso de la documentación.

Distinto en la versión 3.7: Fue agregado el parámetro solo de palabra
clave *context*. Vea **PEP 567** para mas detalles.

Nota:

  La mayoría de las funciones planificadas de "asyncio" no permiten
  pasar argumentos de palabra clave. Para hacer eso utilice
  "functools.partial()":

     # will schedule "print("Hello", flush=True)"
     loop.call_soon(
         functools.partial(print, "Hello", flush=True))

  El uso de objetos parciales es usualmente mas conveniente que
  utilizar lambdas, ya que asyncio puede renderizar mejor objetos
  parciales en mensajes de depuración y error.


Planificando llamadas retardadas
--------------------------------

El bucle de eventos provee mecanismos para planificar funciones de
llamadas que serán ejecutadas en algún punto en el futuro. El bucle de
eventos usa relojes monotónicos para seguir el tiempo.

loop.call_later(delay, callback, *args, context=None)

   Planifica *callback* para ser ejecutada luego de *delay* número de
   segundos (puede ser tanto un entero como un flotante).

   Una instancia de "asyncio.TimerHandle" es retornada, la que puede
   ser utilizada para cancelar la ejecución.

   *callback* será ejecutada exactamente una sola vez. Si dos llamadas
   son planificadas para el mismo momento exacto, el orden en el que
   son ejecutadas es indefinido.

   El argumento posicional opcional *args* será pasado a la llamada
   cuando esta sea ejecutada. Si quieres que la llamada sea ejecutada
   con argumentos de palabra clave usa "functools.partial()".

   Un argumento *context* opcional y solo de palabra clave que permite
   especificar una clase "contextvars.Context" personalizada en la
   cual *callback* será ejecutada. Cuando no se provee *context* el
   contexto actual es utilizado.

   Distinto en la versión 3.7: Fue agregado el parámetro solo de
   palabra clave *context*. Vea **PEP 567** para mas detalles.

   Distinto en la versión 3.8: En Python 3.7 y versiones anteriores
   con la implementación del bucle de eventos predeterminada, el
   *delay* no puede exceder un día. Esto fue arreglado en Python 3.8.

loop.call_at(when, callback, *args, context=None)

   Planifica *callback* para ser ejecutada en una marca de tiempo
   absoluta *when* (un entero o un flotante), usando la misma
   referencia de tiempo que "loop.time()".

   El comportamiento de este método es el mismo que "call_later()".

   Una instancia de "asyncio.TimerHandle" es retornada, la que puede
   ser utilizada para cancelar la ejecución.

   Distinto en la versión 3.7: Fue agregado el parámetro solo de
   palabra clave *context*. Vea **PEP 567** para mas detalles.

   Distinto en la versión 3.8: En Python 3.7 y versiones anteriores
   con la implementación del bucle de eventos predeterminada, la
   diferencia entre *when* y el tiempo actual no puede exceder un día.
   Esto fue arreglado en Python 3.8.

loop.time()

   Retorna el tiempo actual, como un "float", de acuerdo al reloj
   monotónico interno del bucle de evento.

Nota:

  Distinto en la versión 3.8: En Python 3.7 y versiones anteriores los
  tiempos de espera (*delay* relativo o *when* absoluto) no deben
  exceder un día. Esto fue arreglado en Python 3.8.

Ver también: La función "asyncio.sleep()".


Creando Futuros y Tareas
------------------------

loop.create_future()

   Crea un objeto "asyncio.Future" adjunto al bucle de eventos.

   Esta es la manera preferida de crear Futures en asyncio. Esto
   permite que bucles de eventos de terceros provean implementaciones
   alternativas del objeto Future (con mejor rendimiento o
   instrumentación).

   Nuevo en la versión 3.5.2.

loop.create_task(coro, *, name=None)

   Planifica la ejecución de una Corrutinas. Retorna un objeto "Task".

   Bucles de eventos de terceros pueden usar sus propias subclases de
   "Task" por interoperabilidad. En este caso, el tipo de resultado es
   una subclase de "Task".

   Si el argumento *name* es provisto y no "None", se establece como
   el nombre de la tarea usando "Task.set_name()".

   Distinto en la versión 3.8: Agregado el parámetro "name".

loop.set_task_factory(factory)

   Establece una fábrica de tareas que será utilizada por
   "loop.create_task()".

   Si *factory* es "None" se establecerá la fábrica de tareas por
   defecto. En cualquier otro caso, *factory* debe ser un *callable*
   con la misma firma "(loop, coro)", donde *loop* es una referencia
   al bucle de eventos activo y *coro* es un objeto de corrutina. El
   ejecutable debe retornar una objeto "asyncio.Future" compatible.

loop.get_task_factory()

   Retorna una fábrica de tareas o "None" si la predefinida está en
   uso.


Abriendo conexiones de red
--------------------------

coroutine loop.create_connection(protocol_factory, host=None, port=None, *, ssl=None, family=0, proto=0, flags=0, sock=None, local_addr=None, server_hostname=None, ssl_handshake_timeout=None, happy_eyeballs_delay=None, interleave=None)

   Abre una conexión de transmisión de transporte a una dirección
   especificada por *host* y *port*.

   La familia de sockets puede ser tanto "AF_INET" como "AF_INET6"
   dependiendo de *host* (o del argumento *family* si es que fue
   provisto).

   El tipo de socket será "SOCK_STREAM".

   *protocol_factory* debe ser un ejecutable que retorna una
   implementación del asyncio protocol.

   Este método tratará de establecer la conexión en un segundo plano.
   Cuando es exitosa, retorna un par "(transport, protocol)".

   La sinopsis cronológica de las operaciones subyacentes es como
   sigue:

   1. La conexión es establecida y un transport es creado para ello.

   2. *protocol_factory* es llamado sin argumentos y se espera que
      retorne una instancia de protocol.

   3. La instancia del protocolo se acopla con el transporte mediante
      el llamado de su método "connection_made()".

   4. Una tupla "(transport, protocol)" es retornada cuando se tiene
      éxito.

   El transporte creado es una transmisión (*stream*) bidireccional
   que depende de la implementación.

   Otros argumentos:

   * *ssl*: si se provee y no es falso, un transporte SSL/TLS es
     creado (de manera predeterminada se crea un transporte TCP
     plano).  Si *ssl* es un objeto "ssl.SSLContext", este contexto es
     utilizado para crear el transporte; si *ssl* es "True", se
     utiliza un contexto predeterminado retornado por
     "ssl.create_default_context()".

     Ver también: Consideraciones de seguridad SSL/TLS

   * *server_hostname* establece o reemplaza el nombre de servidor
     (*hostname*) contra el cual el certificado del servidor de
     destino será comparado. Sólo debería ser pasado si *ssl* no es
     "None". De manera predeterminada es usado el valor del argumento
     *host*.  Si *host* está vacío, no hay valor predeterminado y
     debes pasar un valor para *server_hostname*. Si *server_hostname*
     es una cadena vacía, la comparación de nombres de servidores es
     deshabilitada (lo que es un riesgo de seguridad serio,
     permitiendo potenciales ataques de hombre-en-el-medio, *man-in-
     the-middle attacks*).

   * *family*, *proto*, *flags* son dirección de familia, protocolo y
     banderas opcionales que serán pasadas a través de *getaddrinfo()*
     para la resolución de *host*. Si están dados, todos ellos
     deberían ser enteros de las constantes del módulo "socket"
     correspondiente.

   * *happy_eyeballs_delay*, if given, enables Happy Eyeballs for this
     connection. It should be a floating-point number representing the
     amount of time in seconds to wait for a connection attempt to
     complete, before starting the next attempt in parallel. This is
     the "Connection Attempt Delay" as defined in **RFC 8305**. A
     sensible default value recommended by the RFC is "0.25" (250
     milliseconds).

   * *interleave* controla reordenamientos de dirección cuando un
     nombre de servidor resuelve a múltiples direcciones IP. Si es "0"
     o no es especificado, no se hace ningún reordenamiento, y las
     direcciones son intentadas en el orden retornado por
     "getaddrinfo()". Si un entero positivo es especificado, las
     direcciones son intercaladas por dirección de familia, y el
     entero dado es interpretado como "Número de familias de la
     primera dirección" (*First Address Family Count*) como es
     definida en **RFC 8305**. El valor predefinido es "0" si
     *happy_eyeballs_delay* no es especificado, y "1" si lo es.

   * *sock*, si está dado, debe ser un objeto "socket.socket"
     existente y ya conectado, que será utilizado por el transporte.
     Si *sock* es dado, ningún *host*, *port*, *family*, *proto*,
     *flags*, *happy_eyeballs_delay*, *interleave* o *local_addr*
     deben ser especificados.

   * *local_addr*, si está dado, es una tupla "(local_host,
     local_port)``usada para enlazar el socket localmente.  Los
     *local_host* y *local_port* son buscados usando ``getaddrinfo()",
     de manera similar que con *host* y *puerto*.

   * *ssl_handshake_timeout* es (para una conexión TLS) el tiempo en
     segundos a esperar que se complete el apretón de manos
     (*handshake*) TLS antes de abortar la conexión. "60.0" segundos
     si es "None" (predefinido).

   Nuevo en la versión 3.8: Agregados los parámetros
   *happy_eyeballs_delay* y *interleave*.Algoritmo de Globos Oculares
   Felices (*Happy Eyeballs*): Éxito con Servidores de Doble-Pila
   (Dual-Stack Hosts). Cuando la ruta IPv4 y el protocolo de un
   servidor están funcionando, pero la ruta IPv6 y el protocolo no
   están funcionando, una aplicación del cliente de doble-pila
   experimenta una demora de conexión significante en comparación con
   un cliente sólo de IPv4.  Esto no es deseable porque causa que el
   cliente de doble-pila tenga la peor experiencia de usuario. Este
   documento especifica requerimientos para algoritmos que reducen
   esta demora visible por el usuario, y provee un algoritmo.Para mas
   información: https://tools.ietf.org/html/rfc6555

   Nuevo en la versión 3.7: El parámetro *ssl_handshake_timeout*.

   Distinto en la versión 3.6: La opción del socket "TCP_NODELAY" es
   establecida de manera predeterminada para todas las conexiones TCP.

   Distinto en la versión 3.5: Agregado el soporte para SSL/TLS en
   "ProactorEventLoop".

   Ver también:

     La función "open_connection()" es una API alternativa de alto
     nivel. Retorna un par de ("StreamReader", "StreamWriter") que
     puede ser usado directamente en código async/await.

coroutine loop.create_datagram_endpoint(protocol_factory, local_addr=None, remote_addr=None, *, family=0, proto=0, flags=0, reuse_address=None, reuse_port=None, allow_broadcast=None, sock=None)

   Nota:

     El parámetro *reuse_address* ya no es soportado, como utiliza
     "SO_REUSEADDR" plantea un problema de seguridad importante para
     UDP. Pasando explícitamente "reuse_address=True" lanzará una
     excepción.Cuando múltiples procesos con UIDs diferentes asignan
     sockets a una misma dirección socket UDP con "SO_REUSEADDR", los
     paquetes entrantes pueden distribuirse aleatoriamente entre los
     sockets.Para plataformas soportadas, *reuse_port* puede ser
     utilizado como un reemplazo para funcionalidades similares. Con
     *reuse_port*, "SO_REUSEPORT" es usado en su lugar, que
     específicamente previene que procesos con distintos UIDs asignen
     sockets a la misma dirección de socket.

   Crea un datagrama de conexión.

   La familia de socket puede ser tanto "AF_INET", "AF_INET6", como
   "AF_UNIX", dependiendo de *host* (o del argumento *family*, si fue
   provisto).

   El tipo de socket será "SOCK_DGRAM".

   *protocol_factory* debe ser un ejecutable que retorne una
   implementación de protocol.

   Una tupla de "(transport, protocol)" es retornada cuando se tiene
   éxito.

   Otros argumentos:

   * *local_addr*, si está dado, es una tupla "(local_host,
     local_port)" usada para enlazar el socket localmente. Los
     *local_host* y *local_port* son buscados utilizando
     "getaddrinfo()".

   * *remote_addr*, si está dado, es una tupla "(remote_host,
     remote_port)" utilizada para conectar el socket a una dirección
     remota. Los *remote_host* y *remote_port* son buscados utilizando
     "getaddrinfo()".

   * *family*, *proto*, *flags* son direcciones de familia, protocolo
     y banderas opcionales que serán pasadas a través de
     "getaddrinfo()" para la resolución de *host*. Si está dado, estos
     deben ser todos enteros de las constantes del módulo "socket"
     correspondiente.

   * *reuse_port* dice al kernel que habilite este punto de conexión
     para ser unido al mismo puerto de la misma forma que otros puntos
     de conexión existentes también están unidos, siempre y cuando
     todos ellos establezcan esta bandera al ser creados. Esta opción
     no es soportada en Windows y algunos sistemas Unix. Si la
     constante "SO_REUSEPORT" no está definida entonces esta
     funcionalidad no es soportada.

   * *allow_broadcast* dice al kernel que habilite este punto de
     conexión para enviar mensajes a la dirección de transmisión
     (*broadcast*).

   * *sock* puede opcionalmente ser especificado para usar un objeto
     "socket.socket" preexistente y ya conectado que será utilizado
     por el transporte. Si están especificados, *local_addr* y
     *remote_addr* deben ser omitidos (tienen que ser "None").

   Refiérase a los ejemplos UDP echo client protocol y UDP echo server
   protocol.

   Distinto en la versión 3.4.4: Los parámetros *family*, *proto*,
   *flags*, *reuse_address*, *reuse_port*, *allow_broadcast* y *sock*
   fueron agregados.

   Distinto en la versión 3.8.1: El parámetro *reuse_address* ya no es
   soportado debido a problemas de seguridad.

   Distinto en la versión 3.8: Se agregó soporte para Windows.

coroutine loop.create_unix_connection(protocol_factory, path=None, *, ssl=None, sock=None, server_hostname=None, ssl_handshake_timeout=None)

   Crear una conexión Unix.

   La familia de sockets será "AF_UNIX"; el tipo de socket será
   "SOCK_STREAM".

   Una tupla de "(transport, protocol)" es retornada cuando se tiene
   éxito.

   *path* es el nombre de un dominio de un socket Unix y es requerido,
   a menos que un parámetro *sock* sea especificado. Los socket Unix
   abstractos, "str", "bytes", y "Path" son soportados.

   Vea la documentación del método "loop.create_connection()" para
   información acerca de los argumentos de este método.

   Availability: Unix.

   Nuevo en la versión 3.7: El parámetro *ssl_handshake_timeout*.

   Distinto en la versión 3.7: El parámetro *path* ahora puede ser un
   *path-like object*.


Creando servidores de red
-------------------------

coroutine loop.create_server(protocol_factory, host=None, port=None, *, family=socket.AF_UNSPEC, flags=socket.AI_PASSIVE, sock=None, backlog=100, ssl=None, reuse_address=None, reuse_port=None, ssl_handshake_timeout=None, start_serving=True)

   Crea un servidor TCP (tipo de socket "SOCK_STREAM") escuchando en
   *port* de la dirección *host*.

   Retorna un objeto "Server".

   Argumentos:

   * *protocol_factory* debe ser un ejecutable que retorne una
     implementación de protocol.

   * El parámetro *host* puede ser establecido a distintos tipos que
     determinan donde el servidor estaría escuchando:

     * Si *host* es una cadena, el servidor TCP está enlazado a una
       sola interfaz de red especificada por *host*.

     * Si *host* es una secuencia de cadenas, el servidor TCP está
       enlazado a todas las interfaces de red especificadas por la
       secuencia.

     * Si *host* es una cadena vacía o "None", se asumen todas las
       interfaces y una lista con múltiples sockets será retornada
       (mas probablemente uno para IPv4 y otro para IPv6).

   * *family* puede ser establecido como "socket.AF_INET" o "AF_INET6"
     para forzar al socket a usar IPv4 o IPv6. Si no es establecido,
     la *family* será determinada por medio del nombre del host (por
     defecto será "AF_UNSPEC").

   * *flags* es una máscara de bits para "getaddrinfo()".

   * *sock* puede ser especificado opcionalmente para usar objetos
     socket preexistentes. Si se utiliza, entonces *host* y *port* no
     deben ser especificados.

   * *backlog* es el número máximo de conexiones encoladas pasadas a
     "listen()" (el valor predeterminado es 100).

   * *ssl* puede ser establecido como una instancia de "SSLContext"
     para habilitar TLS sobre las conexiones aceptadas.

   * *reuse_address* indica al kernel que reutilice un socket local en
     estado "TIME_WAIT", sin esperar que su plazo de ejecución expire.
     Si no es especificado será establecido automáticamente como
     "True" en Unix.

   * *reuse_port* dice al kernel que habilite este punto de conexión
     para ser unido al mismo puerto de la misma forma que otros puntos
     de conexión existentes también están unidos, siempre y cuando
     todos ellos establezcan esta bandera al ser creados.

   * *ssl_handshake_timeout* es (para un servidor TLS) el tiempo en
     segundos a esperar por el apretón de manos (*handshake*) TLS a
     ser completado antes de abortar la conexión. "60.0" si es "None"
     (su valor predeterminado).

   * *start_serving* establecido como "True" (de manera
     predeterminada) produce que los servidores creados comiencen a
     aceptar conexiones inmediatamente. Si es establecido como
     "False", el usuario debe esperar por "Server.start_serving()" o
     "Server.serve_forever()" para que el servidor comience a aceptar
     conexiones.

   Nuevo en la versión 3.7: Agregados los parámetros
   *ssl_handshake_timeout* y *start_serving*.

   Distinto en la versión 3.6: La opción del socket "TCP_NODELAY" es
   establecida de manera predeterminada para todas las conexiones TCP.

   Distinto en la versión 3.5: Agregado el soporte para SSL/TLS en
   "ProactorEventLoop".

   Distinto en la versión 3.5.1: El parámetro *host* puede ser una
   secuencia de cadenas.

   Ver también:

     La función "start_server()" es una API alternativa de alto nivel
     que retorna un par de "StreamReader" y "StreamWriter" que pueden
     ser usados en código async/await.

coroutine loop.create_unix_server(protocol_factory, path=None, *, sock=None, backlog=100, ssl=None, ssl_handshake_timeout=None, start_serving=True)

   Similar a "loop.create_server()" pero funciona con la familia de
   sockets "AF_UNIX".

   *path* es el nombre de un dominio de socket Unix, y es requerido a
   menos que el argumento *sock* sea provisto. Son soportados sockets
   unix abstractos, "str", "bytes", y rutas "Path".

   Vea la documentación de el método "loop.create_server()" para mas
   información acerca de los argumentos de este método.

   Availability: Unix.

   Nuevo en la versión 3.7: Los parámetros *ssl_handshake_timeout*y
   *start_serving*.

   Distinto en la versión 3.7: El parámetro *path* ahora puede ser un
   objeto "Path".

coroutine loop.connect_accepted_socket(protocol_factory, sock, *, ssl=None, ssl_handshake_timeout=None)

   Envuelve una conexión ya aceptada en un par de
   transporte/protocolo.

   Este método puede ser usado por servidores que acepten conexiones
   por fuera de asyncio, pero que usen asyncio para manejarlas.

   Parámetros:

   * *protocol_factory* debe ser un ejecutable que retorne una
     implementación de protocol.

   * *sock* es un objeto socket preexistente retornado por
     "socket.accept".

   * *ssl* puede ser establecido como un "SSLContext" para habilitar
     SSL sobre las conexiones aceptadas.

   * *ssl_handshake_timeout* es (para una conexión SSL) el tiempo en
     segundos que se esperará para que se complete el apretón de manos
     (*handshake*) SSL antes de abortar la conexión. "60.0" si es
     "None" (su valor predeterminado).

   Retorna un par "(transport, protocol)".

   Nuevo en la versión 3.7: El parámetro *ssl_handshake_timeout*.

   Nuevo en la versión 3.5.3.


Transfiriendo archivos
----------------------

coroutine loop.sendfile(transport, file, offset=0, count=None, *, fallback=True)

   Envía un *file* a través de un *transport*. Retorna el numero total
   de bytes enviados.

   El método usa "os.sendfile()" de alto rendimiento si está
   disponible.

   *file* debe ser un objeto de archivo regular abierto en modo
   binario.

   *offset* indica desde donde se empezará a leer el archivo. Si es
   especificado, *count* es el número total de bytes a transmitir en
   contraposición con enviar el archivo hasta que se alcance EOF. La
   posición del archivo es actualizada siempre, incluso cuando este
   método genere un error, y "file.tell()" puede ser usado para
   obtener el número de bytes enviados hasta el momento.

   *fallback* establecido como "True" hace que asyncio lea y envíe el
   archivo manualmente cuando la plataforma no soporta la llamada de
   envío de archivos del sistema (por ejemplo, Windows o sockets SSL
   en Unix).

   Lanza "SendfileNotAvailableError" si el sistema no soporta la
   llamada de envío de archivos del sistema y *fallback* es "True".

   Nuevo en la versión 3.7.


Actualización de TLS
--------------------

coroutine loop.start_tls(transport, protocol, sslcontext, *, server_side=False, server_hostname=None, ssl_handshake_timeout=None)

   Actualiza una conexión basada en transporte ya existente a TLS.

   Retorna una nueva instancia de transporte, que el *protocol* debe
   empezar a usar inmediatamente después del *await*. La instancia
   *transport* pasada al método *start_tls* nunca debe ser usada de
   nuevo.

   Parámetros:

   * Las instancias *transport* y *protocol* que retornan los métodos
     como "create_server()" y "create_connection()".

   * *sslcontext*: una instancia configurada de "SSLContext".

   * *server_side* pasa *True* cuando se actualiza una conexión del
     lado del servidor (como en el caso de una creada por
     "create_server()").

   * *server_hostname*: establece o reemplaza el nombre del host
     contra el cual se compara el certificado del servidor de destino.

   * *ssl_handshake_timeout* es (para una conexión TLS) el tiempo en
     segundos a esperar que se complete el apretón de manos
     (*handshake*) TLS antes de abortar la conexión. "60.0" segundos
     si es "None" (predefinido).

   Nuevo en la versión 3.7.


Viendo descriptores de archivos
-------------------------------

loop.add_reader(fd, callback, *args)

   Empieza a monitorear el descriptor de archivos *fd* para
   disponibilidad de lectura e invoca *callback* con los argumentos
   especificados una vez que *fd* está habilitado para ser leído.

loop.remove_reader(fd)

   Deja de monitorear el descriptor de archivos *fd* para
   disponibilidad de lectura.

loop.add_writer(fd, callback, *args)

   Empieza a monitorear el descriptor de archivos *fd* para
   disponibilidad de escritura e invoca *callback* con los argumentos
   especificados una vez que *fd* está habilitado para ser escrito.

   Use "functools.partial()" para pasar argumentos de palabra clave a
   *callback*.

loop.remove_writer(fd)

   Deja de monitorear el descriptor de archivos *fd* para
   disponibilidad de escritura.

Vea también la sección Soporte de plataforma para algunas limitaciones
de estos métodos.


Trabajar con objetos sockets directamente
-----------------------------------------

En general, implementaciones de protocolo que usen APIs basadas en
transporte como "loop.create_connection()" y "loop.create_server()"
son mas rápidas que aquellas implementaciones que trabajan con
directamente con sockets. De cualquier forma, hay algunos casos de uso
en los cuales el rendimiento no es crítico, y trabajar directamente
con objetos "socket" es mas conveniente.

coroutine loop.sock_recv(sock, nbytes)

   Recibe hasta *nbytes* de *sock*. Versión asíncrona de
   "socket.recv()".

   Retorna los datos recibidos como un objeto bytes.

   *sock* debe ser un socket no bloqueante.

   Distinto en la versión 3.7: A pesar de que este método siempre fue
   documentado como un método de corrutina, los lanzamientos previos a
   Python 3.7 retornaban un "Future". Desde Python 3.7 este es un
   método "async def".

coroutine loop.sock_recv_into(sock, buf)

   Recibe datos desde *sock* en el búfer *buf*. Modelado después del
   método bloqueante "socket.recv_into()".

   Retorna el número de bytes escritos en el búfer.

   *sock* debe ser un socket no bloqueante.

   Nuevo en la versión 3.7.

coroutine loop.sock_sendall(sock, data)

   Envía *data* al socket *sock*. Versión asíncrona de
   "socket.sendall()".

   Este método continua enviando al socket hasta que se hayan enviado
   todos los datos en *data* u ocurra un error. "None" es retornado
   cuando se tiene éxito. Cuando ocurre un error, se lanza una
   excepción. Adicionalmente, no hay manera de determinar cuantos
   datos, si es que se hubo alguno, se procesaron correctamente por el
   extremo receptor de la conexión.

   *sock* debe ser un socket no bloqueante.

   Distinto en la versión 3.7: A pesar de que este método siempre fue
   documentado como un método de corrutina, antes de Python 3.7
   retorna un "Future". Desde Python 3.7, este es un método "async
   def".

coroutine loop.sock_connect(sock, address)

   Conecta *sock* a un socket remoto en *address*.

   Versión asíncrona de "socket.connect()".

   *sock* debe ser un socket no bloqueante.

   Distinto en la versión 3.5.2: "address" ya no necesita ser
   resuelto. "sock_connect" va a intentar verificar si *address* ya
   fue resuelto a partir del llamado de "socket.inet_pton()". Si no lo
   fue, se utilizará "loop.getaddrinfo()" ara resolver *address*.

   Ver también:

     "loop.create_connection()" y "asyncio.open_connection()".

coroutine loop.sock_accept(sock)

   Acepta una conexión. Modelado después del método bloqueante
   "socket.accept()".

   The socket must be bound to an address and listening for
   connections. The return value is a pair "(conn, address)" where
   *conn* is a *new* socket object usable to send and receive data on
   the connection, and *address* is the address bound to the socket on
   the other end of the connection.

   *sock* debe ser un socket no bloqueante.

   Distinto en la versión 3.7: A pesar de que este método siempre fue
   documentado como un método de corrutina, antes de Python 3.7
   retorna un "Future". Desde Python 3.7, este es un método "async
   def".

   Ver también: "loop.create_server()" y "start_server()".

coroutine loop.sock_sendfile(sock, file, offset=0, count=None, *, fallback=True)

   Envía un archivo usando "os.sendfile" de alto rendimiento si es
   posible. Retorna el número total de bytes enviados.

   Versión asíncrona de "socket.sendfile()".

   *sock* debe ser un "socket.SOCK_STREAM" "socket" no bloqueante.

   *file* debe ser un objeto de archivo regular abierto en modo
   binario.

   *offset* indica desde donde se empezará a leer el archivo. Si es
   especificado, *count* es el número total de bytes a transmitir en
   contraposición con enviar el archivo hasta que se alcance EOF. La
   posición del archivo es actualizada siempre, incluso cuando este
   método genere un error, y "file.tell()" puede ser usado para
   obtener el número de bytes enviados hasta el momento.

   *fallback*, cuando es establecida como "True", hace que asyncio lea
   y escriba el archivo manualmente cuando el sistema no soporta la
   llamada de envío de archivos del sistema (por ejemplo, Windows o
   sockets SSL en Unix).

   Lanza "SendfileNotAvailableError" si el sistema no soporta la
   llamada de envío de archivos del sistema *sendfile* y *fallback* es
   "False".

   *sock* debe ser un socket no bloqueante.

   Nuevo en la versión 3.7.


DNS
---

coroutine loop.getaddrinfo(host, port, *, family=0, type=0, proto=0, flags=0)

   Versión asíncrona de "socket.getaddrinfo()".

coroutine loop.getnameinfo(sockaddr, flags=0)

   Asynchronous version of "socket.getnameinfo()".

Distinto en la versión 3.7: Ambos métodos *getaddrinfo* y
*getnameinfo* siempre fueron documentados para retornar una corrutina,
pero antes de Python 3.7 retornaban, de hecho, objetos "Future". A
partir de Python 3.7, ambos métodos son corrutinas.


Trabajando con tuberías
-----------------------

coroutine loop.connect_read_pipe(protocol_factory, pipe)

   Registra el fin de lectura de *pipe* en el bucle de eventos.

   *protocol_factory* debe ser un ejecutable que retorna una
   implementación del asyncio protocol.

   *pipe* es un *objeto de tipo archivo*.

   Retorna un par "(transport, protocol)", donde *transport*  soporta
   la interface "ReadTransport" y *protocol* es un objeto instanciado
   por *protocol_factory*.

   Con el bucle de eventos "SelectorEventLoop", el *pipe* es
   establecido en modo no bloqueante.

coroutine loop.connect_write_pipe(protocol_factory, pipe)

   Registra el fin de escritura de *pipe* en el bucle de eventos.

   *protocol_factory* debe ser un ejecutable que retorna una
   implementación del asyncio protocol.

   *pipe* es un *objeto de tipo archivo*.

   Retorna un par "(transport, protocol)", donde *transport* soporta
   la interface "WriteTransport" y *protocol* es un objeto
   inicializado por *protocol_factory*.

   Con el bucle de eventos "SelectorEventLoop", el *pipe* es
   establecido en modo no bloqueante.

Nota:

  "SelectorEventLoop" no soporta los métodos anteriores en windows. En
  su lugar, use "ProactorEventLoop" para Windows.

Ver también:

  Los métodos "loop.subprocess_exec()" y "loop.subprocess_shell()".


Señales Unix
------------

loop.add_signal_handler(signum, callback, *args)

   Establece *callback* como el gestor para la señal *signum*.

   La llamada será invocada por *loop*, junto con otras llamadas
   encoladas y corrutinas ejecutables de ese bucle de eventos. A menos
   que los gestores de señal la registren usando "signal.signal()",
   una llamada registrada con esta función tiene permitido interactuar
   con el bucle de eventos.

   Lanza "ValueError" si el número de señal es invalido o
   inalcanzable. Lanza "RuntimeError" si hay algún problema preparando
   el gestor.

   Use "functools.partial()" para pasar argumentos de palabra clave a
   *callback*.

   Como "signal.signal()", esta función debe ser invocada en el hilo
   principal.

loop.remove_signal_handler(sig)

   Elimina el gestor para la señal *sig*.

   Retorna "True" si el gestor de señal fue eliminado, o "False" si no
   se estableció gestor para la señal dada.

   Availability: Unix.

Ver también: El módulo "signal".


Ejecutando código en un hilos o grupos de procesos
--------------------------------------------------

awaitable loop.run_in_executor(executor, func, *args)

   Arrange for *func* to be called in the specified executor.

   El argumento *executor* debe ser una instancia de
   "concurrent.futures.Executor". El ejecutor predeterminado es usado
   si *executor* es "None".

   Ejemplo:

      import asyncio
      import concurrent.futures

      def blocking_io():
          # File operations (such as logging) can block the
          # event loop: run them in a thread pool.
          with open('/dev/urandom', 'rb') as f:
              return f.read(100)

      def cpu_bound():
          # CPU-bound operations will block the event loop:
          # in general it is preferable to run them in a
          # process pool.
          return sum(i * i for i in range(10 ** 7))

      async def main():
          loop = asyncio.get_running_loop()

          ## Options:

          # 1. Run in the default loop's executor:
          result = await loop.run_in_executor(
              None, blocking_io)
          print('default thread pool', result)

          # 2. Run in a custom thread pool:
          with concurrent.futures.ThreadPoolExecutor() as pool:
              result = await loop.run_in_executor(
                  pool, blocking_io)
              print('custom thread pool', result)

          # 3. Run in a custom process pool:
          with concurrent.futures.ProcessPoolExecutor() as pool:
              result = await loop.run_in_executor(
                  pool, cpu_bound)
              print('custom process pool', result)

      asyncio.run(main())

   Este método retorna un objeto "asyncio.Future".

   Use "functools.partial()" para pasar argumentos de palabra clave a
   *func*.

   Distinto en la versión 3.5.3: "loop.run_in_executor()" no longer
   configures the "max_workers" of the thread pool executor it
   creates, instead leaving it up to the thread pool executor
   ("ThreadPoolExecutor") to set the default.

loop.set_default_executor(executor)

   Establece *executor* como el ejecutor predeterminado utilizado por
   "run_in_executor()". *executor* debe ser una instancia de
   "ThreadPoolExecutor".

   Obsoleto desde la versión 3.8: Usar un ejecutor que no es una
   instancia de "ThreadPoolExecutor" es obsoleto y disparará un error
   en Python 3.9.

   *executor* debe ser una instancia de
   "concurrent.futures.ThreadPoolExecutor".


API para manejo de errores
--------------------------

Permite personalizar como son manejadas las excepciones en el bucle de
eventos.

loop.set_exception_handler(handler)

   Establece *handler* como el nuevo gestor de excepciones del bucle
   de eventos.

   Si *handler* es "None", se establecerá el gestor de excepciones
   predeterminado. De otro modo, *handler* debe ser un invocable con
   la misma firma "(loop, context)", donde "loop" es una referencia al
   bucle de eventos activo, y "context" es un objeto "dict" que
   contiene los detalles de la excepción (vea la documentación de
   "call_exception_handler()" para detalles acerca del contexto).

loop.get_exception_handler()

   Retorna el gesto de excepciones actual, o "None" si no fue
   establecido ningún gestor de excepciones personalizado.

   Nuevo en la versión 3.5.2.

loop.default_exception_handler(context)

   Gestor de excepciones por defecto.

   Esto es llamado cuando ocurre una excepción y no se estableció
   ningún gestor de excepciones. Esto puede ser llamado por un gestor
   de excepciones personalizado que quiera cambiar el comportamiento
   del gestor predeterminado.

   El parámetro *context* tiene el mismo significado que en
   "call_exception_handler()".

loop.call_exception_handler(context)

   Llama al gestor de excepciones del bucle de eventos actual.

   *context* es un objeto "dict" conteniendo las siguientes claves (en
   futuras versiones de Python podrían introducirse nuevas claves):

   * 'message': Mensaje de error;

   * 'exception' (opcional): Objeto de excepción;

   * 'future' (opcional): instancia de "asyncio.Future";

   * 'handle' (opcional): instancia de "asyncio.Handle";

   * 'protocol' (opcional): instancia de Protocol;

   * 'transport' (opcional): instancia de Transport;

   * 'socket' (opcional): instancia de "socket.socket".

   Nota:

     Este método no debe ser sobrecargado en bucles de eventos en
     subclase. Para gestión de excepciones personalizadas, use el
     método "set_exception_handler()".


Habilitando el modo depuración
------------------------------

loop.get_debug()

   Obtiene el modo depuración ("bool") del bucle de eventos.

   El valor predeterminado es "True" si la variable de entorno
   "PYTHONASYNCIODEBUG" es establecida a una cadena no vacía, de otro
   modo será "False".

loop.set_debug(enabled: bool)

   Establece el modo de depuración del bucle de eventos.

   Distinto en la versión 3.7: La nueva opción de linea de comandos
   "-X dev" ahora también puede ser utilizada para habilitar el modo
   depuración.

Ver también: El modo depuración de asyncio.


Ejecutando Subprocesos
----------------------

Los métodos descritos en esta subsección son de bajo nivel. En código
async/await regular considere usar las convenientes funciones de alto
nivel "asyncio.create_subprocess_shell()" y
"asyncio.create_subprocess_exec()".

Nota:

  El bucle de eventos predeterminado de asyncio en **Windows** no
  soporta subprocesos. Vea Soporte de subprocesos en Windows para mas
  detalles.

coroutine loop.subprocess_exec(protocol_factory, *args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

   Crea un subproceso de uno o mas argumentos de cadena especificados
   por *args*.

   *args* debe ser una lista de cadenas representadas por:

   * "str";

   * o "bytes", codificados a la codificación del sistema de archivos.

   La primer cadena especifica el programa ejecutable, y las cadenas
   restantes especifican los argumentos. En conjunto, los argumentos
   de cadena forman el "argv" del programa.

   Esto es similar a la clase de la librería estándar
   "subprocess.Popen" llamada con "shell=False" y la lista de cadenas
   pasadas como el primer argumento; de cualquier forma, cuando
   "Popen" toma un sólo argumento que es una lista de cadenas,
   *subprocess_exec* toma múltiples cadenas como argumentos.

   El *protocol_factory* debe ser un ejecutable que retorne una
   subclase de la clase "asyncio.SubprocessProtocol".

   Otros parámetros:

   * *stdin* puede ser cualquier de estos:

     * un objeto de tipo archivo representando una tubería que será
       conectada al flujo de entrada estándar del subproceso
       utilizando "connect_write_pipe()"

     * la constante "subprocess.PIPE" (predeterminado) que creará una
       tubería nueva y la conectará,

     * el valor "None" que hará que el subproceso herede el descriptor
       de archivo de este proceso

     * la constante "subprocess.DEVNULL" que indica que el archivo
       especial "os.devnull" será utilizado

   * *stdout* puede ser cualquier de estos:

     * un objeto de tipo archivo representando una tubería que será
       conectada al flujo de salida estándar del subproceso utilizando
       "connect_write_pipe()"

     * la constante "subprocess.PIPE" (predeterminado) que creará una
       tubería nueva y la conectará,

     * el valor "None" que hará que el subproceso herede el descriptor
       de archivo de este proceso

     * la constante "subprocess.DEVNULL" que indica que el archivo
       especial "os.devnull" será utilizado

   * *stderr* puede ser cualquier de estos:

     * un objeto de tipo archivo representando una tubería que será
       conectada al flujo de error estándar del subproceso utilizando
       "connect_write_pipe()"

     * la constante "subprocess.PIPE" (predeterminado) que creará una
       tubería nueva y la conectará,

     * el valor "None" que hará que el subproceso herede el descriptor
       de archivo de este proceso

     * la constante "subprocess.DEVNULL" que indica que el archivo
       especial "os.devnull" será utilizado

     * la constante "subprocess.STDOUT" que conectará el flujo de
       errores predeterminado al flujo de salida predeterminado del
       proceso

   * El resto de argumentos de palabra clave son pasados a
     "subprocess.Popen" sin interpretación, excepto por *bufsize*,
     *universal_newlines*, *shell*, *text*, *encoding* y *errors*, que
     no deben ser especificados en lo absoluto.

     La API subproceso "asyncio" no soporta decodificar los flujos
     como texto. "bytes.decode()" puede ser usado para convertir a
     texto los bytes retornados por el flujo.

   Vea el constructor de la clase "subprocess.Popen" para
   documentación acerca de otros argumentos.

   Retorna un par de "(transport, protocol)", donde *transport* se
   ajusta a la clase base "asyncio.SubprocessTransport" y *protocol*
   es un objeto instanciado por *protocol_factory*.

coroutine loop.subprocess_shell(protocol_factory, cmd, *, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs)

   Crea un subproceso desde *cmd*, que puede ser una cadena "str" o
   "bytes" codificado a la codificación del sistema de archivos,
   usando la sintaxis "shell" de la plataforma.

   Esto es similar a la clase de la librería estándar
   "subprocess.Popen" llamada con "shell=True".

   El *protocol_factory* debe ser un ejecutable que retorne una
   subclase de la clase "asyncio.SubprocessProtocol".

   Vea "subprocess_exec()" para mas detalles acerca de los argumentos
   restantes.

   Retorna un par de "(transport, protocol)", donde *transport* se
   ajusta a la clase base "SubprocessTransport" y *protocol* es un
   objeto instanciado por *protocol_factory*.

Nota:

  Es responsabilidad de la aplicación asegurar que todos los espacios
  en blanco y caracteres especiales estén escapados correctamente para
  evitar vulnerabilidades de inyección de código. La función
  "shlex.quote()" puede ser usada para escapar apropiadamente espacios
  en blanco y caracteres especiales en cadenas que van a ser usadas
  para construir comandos de consola.


Gestores de llamadas
====================

class asyncio.Handle

   Un objeto de contenedor de llamada retornado por
   "loop.call_soon()", "loop.call_soon_threadsafe()".

   cancel()

      Cancela la llamada. Si la llamada ya fue cancelada o ejecutada,
      este método no tiene efecto.

   cancelled()

      Retorna "True" si la llamada fue cancelada.

      Nuevo en la versión 3.7.

class asyncio.TimerHandle

   Un objeto de contenedor de llamada retornado por
   "loop.call_later()", and "loop.call_at()".

   Esta clase es una subclase de "Handle".

   when()

      Retorna el tiempo de una llamada planificada como "float"
      segundos.

      El tiempo es una marca de tiempo absoluta, usando la misma
      referencia de tiempo que "loop.time()".

      Nuevo en la versión 3.7.


Objetos Servidor
================

Los objetos de servidor son creados por las funciones
"loop.create_server()", "loop.create_unix_server()", "start_server()",
y "start_unix_server()".

No instanciar la clase directamente.

class asyncio.Server

   Los objetos *Server* son gestores de asíncronos de contexto. Cuando
   son usados en una declaración "async with", está garantizado que el
   objeto Servidor está cerrado y no está aceptando nuevas conexiones
   cuando la declaración "async with" es completada:

      srv = await loop.create_server(...)

      async with srv:
          # some code

      # At this point, srv is closed and no longer accepts new connections.

   Distinto en la versión 3.7: El objeto Servidor es un gestor
   asíncrono de contexto desde Python 3.7.

   close()

      Deja de servir: deja de escuchar sockets y establece el atributo
      "sockets" a "None".

      Los sockets que representan conexiones entrantes existentes de
      clientes se dejan abiertas.

      El servidor es cerrado de manera asíncrona, usa la corrutina
      "wait_closed()" para esperar hasta que el servidor esté cerrado.

   get_loop()

      Retorna el bucle de eventos asociado con el objeto Servidor.

      Nuevo en la versión 3.7.

   coroutine start_serving()

      Comienza a aceptar conexiones.

      Este método es idempotente, así que puede ser llamado cuando el
      servidor ya está sirviendo.

      El parámetro sólo de palabra clave *start_serving* de
      "loop.create_server()" y "asyncio.start_server()" permite crear
      un objeto Servidor que no está aceptando conexiones
      inicialmente. En este caso "Server.start_serving()", o
      "Server.serve_forever()" pueden ser usados para hacer que el
      servidor empiece a aceptar conexiones.

      Nuevo en la versión 3.7.

   coroutine serve_forever()

      Comienza a aceptar conexiones hasta que la corrutina sea
      cancelada. La cancelación de la tarea "serve_forever" hace que
      el servidor sea cerrado.

      Este método puede ser llamado si el servidor ya está aceptando
      conexiones. Solamente una tarea "serve_forever" puede existir
      para un objeto *Server*.

      Ejemplo:

         async def client_connected(reader, writer):
             # Communicate with the client with
             # reader/writer streams.  For example:
             await reader.readline()

         async def main(host, port):
             srv = await asyncio.start_server(
                 client_connected, host, port)
             await srv.serve_forever()

         asyncio.run(main('127.0.0.1', 0))

      Nuevo en la versión 3.7.

   is_serving()

      Retorna "True" si el servidor está aceptando nuevas conexiones.

      Nuevo en la versión 3.7.

   coroutine wait_closed()

      Espera hasta que el método "close()" se complete.

   sockets

      Lista todos los objetos "socket.socket" en los que el servidor
      está escuchando.

      Distinto en la versión 3.7: Antes de Python 3.7 "Server.sockets"
      solía retornar directamente una lista interna de servidores
      socket. En 3.7 se retorna una copia de esa lista.


Implementaciones del bucle de eventos
=====================================

asyncio viene con dos implementaciones diferentes del bucle de
eventos: "SelectorEventLoop" y "ProactorEventLoop".

De manera predefinida asyncio está configurado para usar
"SelectorEventLoop" en Unix y "ProactorEventLoop" en Windows.

class asyncio.SelectorEventLoop

   Un bucle de eventos basado en el módulo "selectors".

   Usa el *selector* disponible mas eficiente para la plataforma dada.
   También es posible configurar manualmente la implementación exacta
   del selector a utilizar:

      import asyncio
      import selectors

      selector = selectors.SelectSelector()
      loop = asyncio.SelectorEventLoop(selector)
      asyncio.set_event_loop(loop)

   Disponibilidad: Unix, Windows.

class asyncio.ProactorEventLoop

   Un bucle de eventos para Windows que usa "E/S Puertos de
   Finalización" (IOCP).

   Disponibilidad: Windows.

   Ver también:

     Documentaci´on de MSDN sobre E/S Puertos de Finalización.

class asyncio.AbstractEventLoop

   Clase base abstracta para bucles de evento compatibles con asyncio.

   La sección Métodos del bucle de eventos lista todos los métodos que
   como implementación alternativa de "AbstractEventLoop" debería
   haber estado definido.


Examples
========

Nótese que todos los ejemplos en esta sección muestran **a propósito**
como usar las APIs de bucle de eventos de bajo nivel, como ser
"loop.run_forever()" y "loop.call_soon()". Aplicaciones asyncio
modernas raramente necesitan ser escritas de esta manera; considere
utilizar funciones de alto nivel como "asyncio.run()".


Hola Mundo con call_soon()
--------------------------

Un ejemplo usando el método "loop.call_soon()" para planificar una
llamada. La llamada muestra ""Hello World"" y luego para el bucle de
eventos:

   import asyncio

   def hello_world(loop):
       """A callback to print 'Hello World' and stop the event loop"""
       print('Hello World')
       loop.stop()

   loop = asyncio.get_event_loop()

   # Schedule a call to hello_world()
   loop.call_soon(hello_world, loop)

   # Blocking call interrupted by loop.stop()
   try:
       loop.run_forever()
   finally:
       loop.close()

Ver también:

  Un ejemplo similar de Hola Mundo creado con una corrutina y la
  función "run()".


Muestra la fecha actual con call_later()
----------------------------------------

Un ejemplo de llamada mostrando la fecha actual cada un segundo. La
llamada usa el método "loop.call_later()" para volver a planificarse
después de 5 segundos, y después para el bucle de eventos:

   import asyncio
   import datetime

   def display_date(end_time, loop):
       print(datetime.datetime.now())
       if (loop.time() + 1.0) < end_time:
           loop.call_later(1, display_date, end_time, loop)
       else:
           loop.stop()

   loop = asyncio.get_event_loop()

   # Schedule the first call to display_date()
   end_time = loop.time() + 5.0
   loop.call_soon(display_date, end_time, loop)

   # Blocking call interrupted by loop.stop()
   try:
       loop.run_forever()
   finally:
       loop.close()

Ver también:

  Un ejemplo similar a fecha actual creado con una corrutina y la
  función "run()".


Mirar un descriptor de archivo para leer eventos
------------------------------------------------

Espera hasta que el descriptor de archivo reciba algún dato usando el
método "loop.add_reader()" y entonces cierra el bucle de eventos:

   import asyncio
   from socket import socketpair

   # Create a pair of connected file descriptors
   rsock, wsock = socketpair()

   loop = asyncio.get_event_loop()

   def reader():
       data = rsock.recv(100)
       print("Received:", data.decode())

       # We are done: unregister the file descriptor
       loop.remove_reader(rsock)

       # Stop the event loop
       loop.stop()

   # Register the file descriptor for read event
   loop.add_reader(rsock, reader)

   # Simulate the reception of data from the network
   loop.call_soon(wsock.send, 'abc'.encode())

   try:
       # Run the event loop
       loop.run_forever()
   finally:
       # We are done. Close sockets and the event loop.
       rsock.close()
       wsock.close()
       loop.close()

Ver también:

  * Un ejemplo similar usando transportes, protocolos y el método
    "loop.create_connection()".

  * Otro ejemplo similar usando la función de alto nivel
    "asyncio.open_connection()" y transmisiones.


Establece los gestores de señal para SIGINT y SIGTERM
-----------------------------------------------------

(Este ejemplo de "signals" solamente funcionan en Unix.)

Registra gestores para las señales "SIGINT" y "SIGTERM" usando el
método "loop.add_signal_handler()":

   import asyncio
   import functools
   import os
   import signal

   def ask_exit(signame, loop):
       print("got signal %s: exit" % signame)
       loop.stop()

   async def main():
       loop = asyncio.get_running_loop()

       for signame in {'SIGINT', 'SIGTERM'}:
           loop.add_signal_handler(
               getattr(signal, signame),
               functools.partial(ask_exit, signame, loop))

       await asyncio.sleep(3600)

   print("Event loop running for 1 hour, press Ctrl+C to interrupt.")
   print(f"pid {os.getpid()}: send SIGINT or SIGTERM to exit.")

   asyncio.run(main())
