"nntplib" — Protocolo de cliente NNTP
*************************************

**Código fuente:** Lib/nntplib.py

Obsoleto desde la versión 3.11: El módulo "nntplib" está en desuso
(ver **PEP 594** para más detalles).

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

Este módulo define la clase "NNTP" que implementa el lado del cliente
del Protocolo de transferencia de noticias por red. Se puede utilizar
para implementar un lector o póster de noticias, o procesadores de
noticias automatizados. Es compatible con **RFC 3977** así como con
los antiguos **RFC 977** y **RFC 2980**.

Availability: not Emscripten, not WASI.

Este módulo no funciona o no está disponible en las plataformas
WebAssembly "wasm32-emscripten" y "wasm32-wasi". Consulte Plataformas
WebAssembly para obtener más información.

Aquí hay dos pequeños ejemplos de cómo se puede utilizar. Para
enumerar algunas estadísticas sobre un grupo de noticias e imprimir
los temas de los últimos 10 artículos:

   >>> s = nntplib.NNTP('news.gmane.io')
   >>> resp, count, first, last, name = s.group('gmane.comp.python.committers')
   >>> print('Group', name, 'has', count, 'articles, range', first, 'to', last)
   Group gmane.comp.python.committers has 1096 articles, range 1 to 1096
   >>> resp, overviews = s.over((last - 9, last))
   >>> for id, over in overviews:
   ...     print(id, nntplib.decode_header(over['subject']))
   ...
   1087 Re: Commit privileges for Łukasz Langa
   1088 Re: 3.2 alpha 2 freeze
   1089 Re: 3.2 alpha 2 freeze
   1090 Re: Commit privileges for Łukasz Langa
   1091 Re: Commit privileges for Łukasz Langa
   1092 Updated ssh key
   1093 Re: Updated ssh key
   1094 Re: Updated ssh key
   1095 Hello fellow committers!
   1096 Re: Hello fellow committers!
   >>> s.quit()
   '205 Bye!'

Para publicar un artículo desde un archivo binario (esto supone que el
artículo tiene encabezados válidos y que tienes permitido publicar en
el grupo de noticias en particular):

   >>> s = nntplib.NNTP('news.gmane.io')
   >>> f = open('article.txt', 'rb')
   >>> s.post(f)
   '240 Article posted successfully.'
   >>> s.quit()
   '205 Bye!'

El módulo en sí define las siguientes clases:

class nntplib.NNTP(host, port=119, user=None, password=None, readermode=None, usenetrc=False[, timeout])

   Retorna un nuevo objeto "NNTP" que representa una conexión con el
   servidor NNTP ejecutándose en el host *host*, escuchando en el
   puerto *port*. Se puede especificar un *timeout* opcional para la
   conexión de socket. Si se proporcionan las credenciales opcionales
   *user* y *password*, o si hay credenciales adecuadas en "/.netrc" y
   el indicador opcional *usenetrc* es verdadero, los comandos
   "AUTHINFO USER" y "AUTHINFO PASS" se utilizan para identificar y
   autenticar al usuario en el servidor.  Si el indicador opcional
   *readermode* es verdadero, se envía un comando "mode reader" antes
   de que se realice la autenticación. El modo de lector a veces es
   necesario si se conecta a un servidor NNTP en el equipo local y
   tiene la intención de llamar a comandos específicos del lector,
   como "group". Si obtienes un valor inesperado "NNTPPermanentError",
   es posible que debas establecer *readermode*. La clase "NNTP"
   admite la instrucción "with" para consumir incondicionalmente las
   excepciones "OSError" y para cerrar la conexión NNTP cuando haya
   terminado, e.g.:

   >>> from nntplib import NNTP
   >>> with NNTP('news.gmane.io') as n:
   ...     n.group('gmane.comp.python.committers')
   ... 
   ('211 1755 1 1755 gmane.comp.python.committers', 1755, 1, 1755, 'gmane.comp.python.committers')
   >>>

   Genera un evento de auditoría "nntplib.connect" con los argumentos
   "self", "host", "port".

   Genera un evento de auditoría "nntplib.putline" con los argumentos
   "self", "line".

   Distinto en la versión 3.2: *usenetrc* es ahora "False" por
   defecto.

   Distinto en la versión 3.3: El soporte para la declaración "with"
   fue añadido.

   Distinto en la versión 3.9: Si el parámetro *timeout* se establece
   en cero, lanzará un "ValueError" para evitar la creación de un
   socket sin bloqueo.

class nntplib.NNTP_SSL(host, port=563, user=None, password=None, ssl_context=None, readermode=None, usenetrc=False[, timeout])

   Retorna un nuevo objeto "NNTP_SSL", que representa una conexión
   cifrada con el servidor NNTP ejecutándose en el host *host*,
   escuchando en el puerto *port*. Los objetos "NNTP_SSL" tienen los
   mismos métodos que los objetos "NNTP". Si se omite el *port* se
   utiliza el puerto 563 (NNTPS). *ssl_context* también es opcional, y
   también el objeto "SSLContext". Por favor, lea Consideraciones de
   seguridad para conocer las buenas prácticas. Todos los demás
   parámetros se comportan igual que para "NNTP".

   Tenga en cuenta que SSL-on-563 no es recomendado por **RFC 4642**,
   en favor de STARTTLS como se describe abajo. Sin embargo, algunos
   servidores solo admiten el primero.

   Genera un evento de auditoría "nntplib.connect" con los argumentos
   "self", "host", "port".

   Genera un evento de auditoría "nntplib.putline" con los argumentos
   "self", "line".

   Nuevo en la versión 3.2.

   Distinto en la versión 3.4: La clase ahora admite la verificación
   del nombre de host con "ssl.SSLContext.check_hostname" e *Indicador
   del nombre del servidor* (SNI por sus siglas en inglés, consulte
   "ssl.HAS_SNI").

   Distinto en la versión 3.9: Si el parámetro *timeout* se establece
   en cero, lanzará un "ValueError" para evitar la creación de un
   socket sin bloqueo.

exception nntplib.NNTPError

   Derivado de la excepción estándar "Exception", esta es la clase
   base para todas las excepciones generadas por el módulo "nntplib".
   Las instancias de esta clase tienen el siguiente atributo:

   response

      La respuesta del servidor, si está disponible, como un objeto
      "str".

exception nntplib.NNTPReplyError

   Excepción generada cuando se recibe una respuesta inesperada del
   servidor.

exception nntplib.NNTPTemporaryError

   Excepción generada cuando se recibe un código de respuesta dentro
   del rango del 400--499.

exception nntplib.NNTPPermanentError

   Excepción generada cuando se recibe un código de respuesta dentro
   del rango del 500--599.

exception nntplib.NNTPProtocolError

   Excepción generada cuando se recibe una respuesta del servidor que
   no comienza con un dígito dentro del rango 1--5.

exception nntplib.NNTPDataError

   Excepción generada cuando hay algún error en los datos de la
   respuesta.


Objetos NNTP
============

Cuando están conectados, los objetos "NNTP" y "NNTP_SSL" admiten los
siguientes métodos y atributos.


Atributos
---------

NNTP.nntp_version

   Un entero que representa la versión del protocolo NNTP compatible
   con el servidor. En la práctica, esto debería ser "2" para los
   servidores que anuncian el cumplimiento **RFC 3977** y "1" para
   otros.

   Nuevo en la versión 3.2.

NNTP.nntp_implementation

   Cadena que describe el nombre de software y la versión del servidor
   NNTP, o "None" si el servidor no lo anuncia.

   Nuevo en la versión 3.2.


Métodos
-------

La *response* que es retornada como el primer elemento de la tupla de
retorno de casi todos los métodos es la respuesta del servidor: una
cadena que comienza con un código de tres dígitos. Si la respuesta del
servidor indica un error, el método genera una de las excepciones
anteriores.

Muchos de los métodos siguientes toman un argumento opcional de
solamente palabra clave *file*. Cuando se proporciona el argumento
*file*, debe ser un *file object* abierto para la escritura binaria o
el nombre de un archivo en disco a ser escrito. El método escribirá
los datos retornados por el servidor (excepto la línea de respuesta y
el punto de terminación) en el archivo; cualquier lista de líneas,
tuplas u objetos que el método retorna normalmente estará vacía.

Distinto en la versión 3.2: Muchos de los siguientes métodos se han
rediseñado y corregido, lo que los hace incompatibles con sus
contrapartes 3.1.

NNTP.quit()

   Envía un comando "QUIT" y cierra la conexión. Una vez que se ha
   invocado este método, no se debe invocar ningún otro método del
   objeto NNTP.

NNTP.getwelcome()

   Retorna el mensaje de bienvenida enviado por el servidor en
   respuesta a la conexión inicial. (Este mensaje a veces contiene
   aclaraciones o información de ayuda que puede ser relevante para el
   usuario.)

NNTP.getcapabilities()

   Retorna las capacidades **RFC 3977** anunciadas por el servidor,
   como una instancia "dict" mapeando nombres de capacidades a listas
   de valores (posiblemente vacías). En los servidores heredados que
   no entienden el comando "CAPABILITIES", se retorna un diccionario
   vacío en su lugar.

   >>> s = NNTP('news.gmane.io')
   >>> 'POST' in s.getcapabilities()
   True

   Nuevo en la versión 3.2.

NNTP.login(user=None, password=None, usenetrc=True)

   Envía comandos "AUTHINFO" con el nombre de usuario y la contraseña.
   Si *user* y *password* son "None" y *usenetrc* es verdadero, se
   utilizarán las credenciales de "~/.netrc" si su uso es posible.

   A menos que se retrase intencionalmente, el inicio de sesión se
   realiza normalmente durante la inicialización del objeto "NNTP" y
   no es necesario invocar esta función por separado. Para forzar el
   retraso de la autenticación, no debes establecer *user* o
   *password* al crear el objeto y debes establecer *usenetrc* en
   *False*.

   Nuevo en la versión 3.2.

NNTP.starttls(context=None)

   Envía un comando "STARTTLS". Esto habilitará el cifrado en la
   conexión NNTP. El argumento *context* es opcional y debe ser el
   objeto "ssl. SSLContext". Por favor lea Consideraciones de
   seguridad para conocer las buenas prácticas.

   Tenga en cuenta que esto no se puede hacer después de que se haya
   transmitido la información de autenticación y la autenticación se
   produce de forma predeterminada, si es posible, durante la
   inicialización de un objeto "NNTP". Consulte "NNTP.login()" para
   obtener información sobre cómo suprimir este comportamiento.

   Nuevo en la versión 3.2.

   Distinto en la versión 3.4: El método ahora admite la comprobación
   del nombre de host con "ssl. SSLContext.check_hostname" y
   *Indicador del nombre del servidor* (SNI por sus siglas en inglés,
   consulte "ssl.HAS_SNI").

NNTP.newgroups(date, *, file=None)

   Envía un comando "NEWGROUPS". El argumento *date* debe ser un
   objeto "datetime.date" o "datetime.datetime". Retorna un par
   "(response, groups)" donde *groups* es una lista que representa los
   grupos que son nuevos desde la fecha determinada. Sin embargo, si
   se proporciona *file*, entonces *groups* estará vacío.

   >>> from datetime import date, timedelta
   >>> resp, groups = s.newgroups(date.today() - timedelta(days=3))
   >>> len(groups) 
   85
   >>> groups[0] 
   GroupInfo(group='gmane.network.tor.devel', last='4', first='1', flag='m')

NNTP.newnews(group, date, *, file=None)

   Envía un comando "NEWNEWS". Aquí, *group* es un nombre de grupo o
   "'*'", y *date* tiene el mismo significado que para "newgroups()".
   Retorna un par "(response, articles)" donde *articles* es una lista
   de identificadores de mensaje.

   Este comando es inhabilitado frecuentemente por los administradores
   del servidor NNTP.

NNTP.list(group_pattern=None, *, file=None)

   Envía un comando "LIST" o "LIST ACTIVE". Retorna un par "(response,
   list)" donde *list* es una lista de tuplas que representan todos
   los grupos disponibles desde este servidor NNTP, opcionalmente
   coincidiendo con el patrón de cadena *group_pattern*. Cada tupla
   tiene el formato "(group, last, first, flag)", donde *group* es un
   nombre de grupo, *last* y *first* son los últimos y primeros
   números de artículo, y *flag* suele tomar uno de estos valores:

   * "y": Se permiten publicaciones locales y artículos de pares.

   * "m": El grupo está moderado y todas las publicaciones deben ser
     aprobadas.

   * "n": No se permiten publicaciones locales, solo artículos de
     pares.

   * "j": Los artículos de pares se archivan en el grupo de basura en
     su lugar.

   * "x": No hay publicaciones locales y los artículos de pares son
     ignorados.

   * "=foo.bar": Los artículos se archivan en el grupo "foo.bar" en su
     lugar.

   Si *flag* tiene otro valor, el estado del grupo de noticias debe
   considerarse como desconocido.

   Este comando puede retornar resultados muy grandes, especialmente
   si no se especifica *group_pattern*. Es mejor almacenar en caché
   los resultados sin conexión a menos que realmente necesite
   actualizarlos.

   Distinto en la versión 3.2: *group_pattern* fue añadido.

NNTP.descriptions(grouppattern)

   Envía un comando "LIST NEWSGROUPS", donde *grouppattern* es una
   cadena comodín como se especifica en **RFC 3977** (es esencialmente
   lo mismo que las cadenas comodín de shell DOS o UNIX). Retorna un
   par "(response, descriptions)", donde *descriptions* es un
   diccionario que asigna nombres de grupos a descripciones textuales.

   >>> resp, descs = s.descriptions('gmane.comp.python.*')
   >>> len(descs) 
   295
   >>> descs.popitem() 
   ('gmane.comp.python.bio.general', 'BioPython discussion list (Moderated)')

NNTP.description(group)

   Obtiene una descripción para un único grupo *group*. Si más de un
   grupo coincide (si *'group'* es una cadena comodín real), retorna
   la primera coincidencia. Si ningún grupo coincide, retorna una
   cadena vacía.

   Esto elude el código de respuesta del servidor. Si necesita el
   código de respuesta, use "descriptions()".

NNTP.group(name)

   Envía un comando "GROUP", donde *name* es el nombre del grupo. El
   grupo se selecciona como el grupo actual, si este existe. Retorna
   una tupla "(response, count, first, last, name)" donde *count* es
   el número (estimado) de artículos en el grupo, *first* es el primer
   número de artículo del grupo, *last* es el último número de
   artículo en el grupo y *name* es el nombre del grupo.

NNTP.over(message_spec, *, file=None)

   Envía un comando "OVER" o un comando "XOVER" en servidores
   heredados. *message_spec* puede ser una cadena que represente un
   identificador de mensaje o una tupla de números "(first, None)" que
   indique un rango de artículos en el grupo actual, o una tupla
   "(first, None)" que indique un rango de artículos comenzando desde
   *first* hasta el último artículo del grupo actual, o "None" para
   seleccionar el artículo actual en el grupo actual.

   Retorna un par "(response, overviews)". *overviews* es una lista de
   tuplas del tipo "(article_number, overview)", una para cada
   artículo seleccionado por *message_spec*. Cada *overview* es un
   diccionario con el mismo número de elementos, pero este número
   depende del servidor. Estos elementos son encabezados de mensajes
   (la clave es entonces el nombre del encabezado en minúsculas) o
   elementos de metadatos (la clave es entonces el nombre de los
   metadatos precedido de "":""). Se garantiza la presencia de los
   siguientes elementos por la especificación NNTP:

   * los encabezados "subject", "from", "date", "message-id" y
     "references"

   * los metadatos ":bytes": el número de bytes en todo el artículo
     sin procesar (incluidos los encabezados y el cuerpo)

   * los metadatos ":lines": el número de líneas en el cuerpo del
     artículo

   El valor de cada elemento es una cadena o "None" si no está
   presente.

   Es aconsejable utilizar la función "decode_header()" en los valores
   del encabezado cuando pueden contener caracteres no-ASCII:

      >>> _, _, first, last, _ = s.group('gmane.comp.python.devel')
      >>> resp, overviews = s.over((last, last))
      >>> art_num, over = overviews[0]
      >>> art_num
      117216
      >>> list(over.keys())
      ['xref', 'from', ':lines', ':bytes', 'references', 'date', 'message-id', 'subject']
      >>> over['from']
      '=?UTF-8?B?Ik1hcnRpbiB2LiBMw7Z3aXMi?= <martin@v.loewis.de>'
      >>> nntplib.decode_header(over['from'])
      '"Martin v. Löwis" <martin@v.loewis.de>'

   Nuevo en la versión 3.2.

NNTP.help(*, file=None)

   Envía un comando "HELP". Retorna un par "(response, list)" donde
   *list* es una lista de cadenas de caracteres de ayuda.

NNTP.stat(message_spec=None)

   Envía un comando "STAT", donde *message_spec* es un identificador
   de mensaje (incluido en "'<'" y "'>'") o un número de artículo en
   el grupo actual. Si se omite *message_spec* o es "None" se
   considera el artículo actual del grupo actual. Retorna un triple
   "(response, number, id)" donde *number* es el número de artículo e
   *id* es el identificador del mensaje.

   >>> _, _, first, last, _ = s.group('gmane.comp.python.devel')
   >>> resp, number, message_id = s.stat(first)
   >>> number, message_id
   (9099, '<20030112190404.GE29873@epoch.metaslash.com>')

NNTP.next()

   Envía un comando "NEXT". Retorna como para "stat()".

NNTP.last()

   Envía un comando "LAST". Retorna como para "stat()".

NNTP.article(message_spec=None, *, file=None)

   Envía un comando "ARTICLE", donde *message_spec* tiene el mismo
   significado que para "stat()". Retorna una tupla "(response, info)"
   donde *info* es un "namedtuple" con tres atributos *number*,
   *message_id* y *lines* (en ese orden). *number* es el número de
   artículo del grupo (o 0 si la información no está disponible),
   *message_id* el identificador del mensaje como una cadena y *lines*
   una lista de líneas (sin terminar líneas nuevas) que comprende el
   mensaje sin procesar, incluidos los encabezados y el cuerpo.

   >>> resp, info = s.article('<20030112190404.GE29873@epoch.metaslash.com>')
   >>> info.number
   0
   >>> info.message_id
   '<20030112190404.GE29873@epoch.metaslash.com>'
   >>> len(info.lines)
   65
   >>> info.lines[0]
   b'Path: main.gmane.org!not-for-mail'
   >>> info.lines[1]
   b'From: Neal Norwitz <neal@metaslash.com>'
   >>> info.lines[-3:]
   [b'There is a patch for 2.3 as well as 2.2.', b'', b'Neal']

NNTP.head(message_spec=None, *, file=None)

   Igual que "article()", pero envía un comando "HEAD". Las *lines*
   retornadas (o escritas a *file*) solo contendrán los encabezados
   del mensaje, no el cuerpo.

NNTP.body(message_spec=None, *, file=None)

   Igual que "article()", pero envía un comando "BODY". Las *lines*
   retornadas (o escritas a *file*) solo contendrán los encabezados
   del mensaje, no el cuerpo.

NNTP.post(data)

   Publica un artículo utilizando el comando "POST". El argumento
   *data* es un *file object* abierto para la lectura binaria o
   cualquier iterable de objetos bytes (que representa las líneas sin
   procesar del artículo que se va a publicar). Debe representar un
   artículo de noticias bien formado, incluidos los encabezados
   requeridos. El método "post()" escapa automáticamente las líneas
   que comienzan con "." y añade la línea de terminación.

   Si el método tiene éxito, se retorna la respuesta del servidor. Si
   el servidor se niega a publicarlo, se genera un "NNTPReplyError".

NNTP.ihave(message_id, data)

   Envía un comando "IHAVE". *message_id* es el identificador del
   mensaje que se enviará al servidor (incluido entre "'<'" y "'>'").
   El parámetro *data* y el valor de retorno son los mismos que para
   "post()".

NNTP.date()

   Retorna un par "(response, date)". *date* es un objeto "datetime"
   que contiene la fecha y hora actuales del servidor.

NNTP.slave()

   Envía un comando "SLAVE". Retorna la *response* del servidor.

NNTP.set_debuglevel(level)

   Establece el nivel de depuración de la instancia. Esto controla la
   cantidad de salida de depuración impresa. El valor por defecto,
   "0", no produce salida de depuración. Un valor de "1" produce una
   cantidad moderada de salida de depuración, generalmente una sola
   línea por solicitud o por respuesta. Un valor de "2" o superior
   produce la cantidad máxima de salida de depuración, registrando
   cada línea enviada y recibida en la conexión (incluyendo el texto
   del mensaje).

Las siguientes son extensiones NNTP opcionales definidas en **RFC
2980**. Algunas de ellas han sido reemplazados por comandos más nuevos
en **RFC 3977**.

NNTP.xhdr(hdr, str, *, file=None)

   Envía un comando "XHDR". El argumento *hdr* es una palabra clave de
   encabezado, por ejemplo "'subject'". El argumento *str* debe tener
   la forma "'first-last'" donde *first* y *last* son el primer y
   último número de artículo para buscar. Retorna un par "(response,
   list)", donde *list* es una lista de pares "(id, text)", donde *id*
   es un número de artículo (como una cadena) y *text* es el texto del
   encabezado solicitado para ese artículo. Si se proporciona el
   parámetro *file*, entonces la salida del comando "XHDR" se almacena
   en un archivo. Si *file* es una cadena, entonces el método abrirá
   un archivo con ese nombre, que escribirá en él y luego lo cerrará.
   Si *file* es un *file object*, entonces comenzará invocando
   "write()" en él para almacenar las líneas de la salida del comando.
   Si se proporciona *file*, entonces retorna *list* o una lista
   vacía.

NNTP.xover(start, end, *, file=None)

   Envía un comando "XOVER". *start* and *end* son números de artículo
   que delimitan el rango de artículos a seleccionar. El valor de
   retorno es el mismo que para "over()". Se recomienda usar "over()"
   en su lugar, ya que se usará automáticamente el comando más nuevo
   "OVER" si está disponible.


Funciones de utilidad
=====================

El módulo también define la siguiente función de utilidad:

nntplib.decode_header(header_str)

   Decodifica un valor de encabezado, eliminando los caracteres de
   escape que no sean ASCII. *header_str* debe ser un objeto "str". Se
   retorna el valor sin escape. Se recomienda utilizar esta función
   para mostrar algunos encabezados en una forma legible por humanos:

      >>> decode_header("Some subject")
      'Some subject'
      >>> decode_header("=?ISO-8859-15?Q?D=E9buter_en_Python?=")
      'Débuter en Python'
      >>> decode_header("Re: =?UTF-8?B?cHJvYmzDqG1lIGRlIG1hdHJpY2U=?=")
      'Re: problème de matrice'
