"email.message": Representando un mensaje de correo electrónico
***************************************************************

**Código fuente:** Lib/email/message.py

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

Nuevo en la versión 3.6: [1]

La clase central en el paquete de "email" es la clase "EmailMessage",
importada desde el módulo "email.message". Esta es la clase base para
el modelo de objeto "email". "EmailMessage" provee la funcionalidad
clave para configurar y consultar los *headers*, para acceder al
cuerpo del mensaje, y para crear o modificar la estructura del
mensaje.

Un mensaje de e-mail consiste en *headers* y un *payload* (al que
también nos referimos como *content*). *Headers* como **RFC 5322** o
**RFC 6532** son nombres de campos de estilo y valores, donde el
nombre y valor están separados por un ':'. Los dos puntos no son parte
ni del nombre ni del valor. El *payload* puede ser un simple mensaje,
un objeto binario, o una secuencia estructurada de sub-mensajes, cada
uno con su propio conjunto de *headers* y su propio *payload*. El
último tipo de *payload* es indicado por el mensaje con un MIME como
*multipart/** o *message/rfc822* .

El modelo conceptual provisto por un objeto "EmailMessage" es el de un
diccionario ordenado de *headers* emparejados con un *payload* que
representa al cuerpo del mensaje **RFC 5322**, que podría ser una
lista de objetos sub-"EmailMessage". Además de los métodos normales de
diccionario para acceder a los *headers* y valores, tiene métodos para
acceder a información especializada desde los *headers* (por ejemplo,
el tipo de contenido MIME), para operar en el *payload*, generar una
versión serializada del mensaje, y recorrer recursivamente el árbol de
objetos.

The "EmailMessage" dictionary-like interface is indexed by the header
names, which must be ASCII values.  The values of the dictionary are
strings with some extra methods.  Headers are stored and returned in
case-preserving form, but field names are matched case-insensitively.
Unlike a real dict, there is an ordering to the keys, and there can be
duplicate keys.  Additional methods are provided for working with
headers that have duplicate keys.

El *payload* es un objeto de cadena de caracteres o bytes, en el caso
de objetos de mensaje simples, o una lista de objetos "EmailMessage",
para documentos de contenedor MIME como *multipart/** y objetos de
mensaje *message/rfc822*.

class email.message.EmailMessage(policy=default)

   Si se especifica *policy*, use las reglas especificadas para
   actualizar y serializar la representación del mensaje. Si *policy*
   no es establecida, use "default", que sigue las reglas RFC de email
   excepto para el fin de línea(en lugar del RFC "\r\n", usa los
   finales estándar de Python "\n" como final de línea). Para más
   información ve a la documentación del "policy".

   as_string(unixfrom=False, maxheaderlen=None, policy=None)

      Retorna el mensaje entero como cadena de caracteres. Cuando la
      opción *unixform* es verdadera, el *header* está incluido en la
      cadena de caracteres retornada. *unixform* está predeterminado
      con valor "False". Por compatibilidad con versiones anteriores,
      la base "Message", la case *maxheaderlen* es aceptada pero con
      valor "None" como predeterminado, por lo que la longitud de
      línea se controla mediante "max_line_length". El argumento
      *policy* puede ser usado para anular el valor predeterminado
      obtenido de la instancia del mensaje. Esto puede ser usado para
      controlar parte del formato producido por el método, ya que el
      *policy* especificado pasará a "Generator".

      Aplanar el mensaje puede acarrear cambios en "EmailMessage" si
      es necesario rellenar los valores predeterminados para completar
      la transformación a una cadena de caracteres (por ejemplo, se
      pueden generar o modificar límites MIME).

      Tenga en cuenta que este método se proporciona como una
      comodidad y quizás no sea la forma más eficiente de serializar
      mensajes en su aplicación, especialmente si estás tratando con
      múltiples mensajes. Consulte "Generator" por una API más
      flexible para serializar mensajes. No olvide también que este
      método está restringido a producir mensajes serializados como "7
      bit clean" cuando "utf8" es "False", que es el valor
      predeterminado.

      Distinto en la versión 3.6: el comportamiento predeterminado
      cuando *maxheaderlen* no está especificado cambió de 0 al valor
      de *max_line_length* .

   __str__()

      Equivalente a "as_string(policy=self.policy.clone(utf8=True))".
      Permite "str(msg)" para producir un *string* que contenga un
      mensaje serializado en un formato legible.

      Distinto en la versión 3.4: el método se cambió para usar
      "utf8=True", produciendo así un **RFC 6531** como representación
      del mensaje, en vez de ser un alias de "as_string()".

   as_bytes(unixfrom=False, policy=None)

      Retorna el mensaje plano como un objeto de bytes. Cuando
      *unixform* es verdadero, el *header* es incluido en la cadena de
      caracteres retornada. El valor predeterminado de *unixform* es
      "False". El argumento *policy* puede ser usado para
      sobreescribir el valor predeterminado obtenido de la instancia
      del mensaje. Esto puede ser usado para controlar parte del
      formato producido por el método, ya que el *policy* especificado
      pasará a "Generator".

      Aplanar el mensaje puede acarrear cambios en "EmailMessage" si
      es necesario rellenar los valores predeterminados para completar
      la transformación a una cadena de caracteres (por ejemplo, se
      pueden generar o modificar límites MIME).

      Tenga en cuenta que este método se proporciona como una
      comodidad y quizás no sea la forma más eficiente de serializar
      mensajes en su aplicación, especialmente si estas tratando con
      múltiples mensajes. Consulte "Generator" por una API más
      flexible para serializar mensajes.

   __bytes__()

      Equivalente a "as_bytes()". Permite "bytes(msg)" para producir
      un objeto byte que contenga el mensaje serializado.

   is_multipart()

      Retorna "True" si el *payload* del mensaje es una lista de
      objetos de sub-"EmailMessage", de otra manera retorna "False".
      Cuando "is_multipart()" retorna "False", el *payload* deberá ser
      un objeto cadena de caracteres (que podría ser un *payload*
      binario codificado con CTE). Note que si "is_multipart()"
      retorna "True" no necesariamente significa que
      "msg.get_content_maintype() == 'multipart'" retornará "True".
      Por ejemplo, "is_multipart" retornará "True" cuando la
      "EmailMessage" sea del tipo "message/rfc822".

   set_unixfrom(unixfrom)

      Configura la cabecera del mensaje a *unixform*, que debería ser
      una cadena de caracteres. (Consulte "mboxMessage" para una
      descripción de este *header*)

   get_unixfrom()

      Retorna la cabecera del mensaje. Predeterminado "None" si la
      cabecera no ha sido configurada.

   Los siguientes métodos implementan el mapeo como una interfaz para
   acceder al *header* del mensaje. Tenga en cuenta que hay algunas
   diferencias semánticas entre esos métodos y una interfaz de mapeo
   normal(es decir, diccionario). Por ejemplo, en un diccionario no
   hay claves duplicadas, pero pueden haber *headers* duplicados.
   Además, en los diccionarios no hay un orden garantizado para las
   claves retornadas por "keys()", pero en un objeto "EmailMessage",
   los *headers* siempre regresan en orden de aparición en el mensaje
   original, o en el que fueron agregados luego. Cualquier *header*
   borrado y vuelto a añadir siempre se agrega al final de la lista.

   Estas diferencias semánticas son intencionales y están sesgadas
   hacia la conveniencia en los casos de uso más comunes.

   Note que en todos los casos, cualquier *header* presente en el
   mensaje no se incluye en la interfaz de mapeo.

   __len__()

      Retorna el número total de *headers*, incluidos los duplicados.

   __contains__(name)

      Retorna "True" si el objeto del mensaje tiene un campo llamado
      'nombre'. La comparación se realiza sin tener en cuenta
      mayúsculas o minúsculas y 'nombre' no incluye ':'.  Se utiliza
      para el operador "in" Por ejemplo:

         if 'message-id' in myMessage:
            print('Message-ID:', myMessage['message-id'])

   __getitem__(name)

      Retorna el valor del *header* nombrado. *name* no incluye el
      separador de dos puntos, ':'. Si el *header* se pierde, regresa
      "None", un "KeyError" no se genera nunca.

      Tenga en cuenta que si el campo nombrado aparece más de una vez
      en el *header* del mensaje, esa cantidad de veces el valor
      regresado será indefinido. Use el método "get_all()" para
      obtener los valores de todos los *headers* existentes llamados
      *name*.

      Usando el *standard non-'compat32'*, el valor regresado es una
      instancia de una subclase de "email.headerregistry.BaseHeader".

   __setitem__(name, val)

      Agrega un *header* al mensaje con un campo 'nombre' y un valor
      'val'. El campo se agrega al final de los *headers* existentes
      en el mensaje.

      Tenga en cuenta que esto no sobrescribe ni borra ningún *header*
      con el mismo nombre. Si quiere asegurarse de que el nuevo
      *header* es el único en el mensaje con el campo 'nombre', borre
      el campo primero, por ejemplo:

         del msg['subject']
         msg['subject'] = 'Python roolz!'

      Si el "policy" define ciertos *headers* para ser únicos(como lo
      hace el *standard*), este método puede generar un "ValueError"
      cuando se intenta asignar un valor a un *header* preexistente.
      Este comportamiento es intencional por consistencia, pero no
      dependa de ello, ya que podemos optar por hacer que tales
      asignaciones eliminen el *header* en el futuro.

   __delitem__(name)

      Elimine todas las apariciones del campo 'nombre' de los
      *headers* del mensaje. No se genera ninguna excepción si el
      campo nombrado no está presente en los encabezados.

   keys()

      Retorna una lista de los nombres de todos los campos del
      *header* del mensaje.

   values()

      Retorna una lista de todos los valores de los campos del
      mensaje.

   items()

      Retorna una lista de 2 tuplas que contienen todos los campos
      *header* y *value* del mensaje.

   get(name, failobj=None)

      Retorna el valor del *header* nombrado. Esto es idéntico a
      "__getitem__()" excepto si el opcional *failobj* regresado en el
      *header* nombrado no se encuentra(*failobj* es por defecto
      "None").

   Aquí hay algunos métodos adicionales útiles relacionados con el
   *header*:

   get_all(name, failobj=None)

      Retorna una lista de todos los valores para el campo *nombre*.
      Si no se nombran tales *headers* en el mensaje, regresa
      *failobj* (por defecto "None")

   add_header(_name, _value, **_params)

      Configuración extendida de *headers*. Este método es similar a
      "__setitem__()", excepto que se pueden proporcionar parámetros
      de *header* adicionales como argumentos de palabras clave.

      Por cada ítem en los parámetros del diccionario *_params*, la
      clave se toma como el nombre del parámetro, con barra baja ('_')
      convertidos a guiones ('-') (ya que en Python no se permiten '-'
      como identificadores). Normalmente, el parámetro debe ser
      añadido como "key='value'" a menos que el valor sea "None", en
      ese caso solo la clave debe ser añadida.

      Si el valor contiene caracteres no-ASCII, el *charset* y el
      lenguaje deben ser controlados especificando el valor como una
      triple tupla en formato "(CHARSET, LANGUAJE, VALUE)", donde
      "CHARSET" es una *string* llamando al *charset* usado para
      codificar el valor, "LANGUAJE" generalmente se establece en
      "None" o en una cadena de caracteres vacía (consulte **RFC
      2231** para más opciones), y "VALUE" es el valor de la cadena de
      caracteres que contiene puntos de código no-ASCII. Si la triple
      tupla no pasa y el valor contiene caracteres no-ASCII, es
      automáticamente codificada en formato **RFC 2231**, usando
      "CHARSET" de "utf-8" y "LANGUAJE" "None".

      Aquí hay un ejemplo:

         msg.add_header('Content-Disposition', 'attachment', filename='bud.gif')

      Esto agregará un *header* que se ve como:

         Content-Disposition: attachment; filename="bud.gif"

      Un ejemplo de la interfaz extendida con caracteres no-ASCII:

         msg.add_header('Content-Disposition', 'attachment',
                        filename=('iso-8859-1', '', 'Fußballer.ppt'))

   replace_header(_name, _value)

      Reemplaza un *header*. Reemplaza el primer *header* encontrado
      en el mensaje que coincida con *_name*, conservando el orden de
      *header* y uso de minúsculas (y mayúsculas) del nombre de campo
      del *header* original. Si no hay coincidencia, se lanzará un
      "KeyError".

   get_content_type()

      Retorna el tipo de contenido del mensaje, pasado a minúsculas de
      la forma *maintype/subtype*. Si no hay *header* llamado
      *Content-Type* en el mensaje, regresa el valor de
      "get_default_type()". Si *Content-Type* no es válido, retorna
      "text/plain".

      (According to **RFC 2045**, messages always have a default type,
      "get_content_type()" will always return a value.  **RFC 2045**
      defines a message's default type to be *text/plain* unless it
      appears inside a *multipart/digest* container, in which case it
      would be *message/rfc822*.  If the *Content-Type* header has an
      invalid type specification, **RFC 2045** mandates that the
      default type be *text/plain*.)

   get_content_maintype()

      Return the message's main content type.  This is the *maintype*
      part of the string returned by "get_content_type()".

   get_content_subtype()

      Return the message's sub-content type.  This is the *subtype*
      part of the string returned by "get_content_type()".

   get_default_type()

      Return the default content type.  Most messages have a default
      content type of *text/plain*, except for messages that are
      subparts of *multipart/digest* containers.  Such subparts have a
      default content type of *message/rfc822*.

   set_default_type(ctype)

      Set the default content type.  *ctype* should either be
      *text/plain* or *message/rfc822*, although this is not enforced.
      The default content type is not stored in the *Content-Type*
      header, so it only affects the return value of the
      "get_content_type" methods when no *Content-Type* header is
      present in the message.

   set_param(param, value, header='Content-Type', requote=True, charset=None, language='', replace=False)

      Set a parameter in the *Content-Type* header.  If the parameter
      already exists in the header, replace its value with *value*.
      When *header* is "Content-Type" (the default) and the header
      does not yet exist in the message, add it, set its value to
      *text/plain*, and append the new parameter value.  Optional
      *header* specifies an alternative header to *Content-Type*.

      If the value contains non-ASCII characters, the charset and
      language may be explicitly specified using the optional
      *charset* and *language* parameters.  Optional *language*
      specifies the **RFC 2231** language, defaulting to the empty
      string.  Both *charset* and *language* should be strings.  The
      default is to use the "utf8" *charset* and "None" for the
      *language*.

      If *replace* is "False" (the default) the header is moved to the
      end of the list of headers.  If *replace* is "True", the header
      will be updated in place.

      El uso del parámetro *requote* con objetos "EmailMessage" está
      obsoleto.

      Note that existing parameter values of headers may be accessed
      through the "params" attribute of the header value (for example,
      "msg['Content-Type'].params['charset']").

      Distinto en la versión 3.4: Se agregó la palabra clave
      "replace".

   del_param(param, header='content-type', requote=True)

      Remove the given parameter completely from the *Content-Type*
      header.  The header will be re-written in place without the
      parameter or its value.  Optional *header* specifies an
      alternative to *Content-Type*.

      El uso del parámetro *requote* con objetos "EmailMessage" está
      obsoleto.

   get_filename(failobj=None)

      Return the value of the "filename" parameter of the *Content-
      Disposition* header of the message.  If the header does not have
      a "filename" parameter, this method falls back to looking for
      the "name" parameter on the *Content-Type* header.  If neither
      is found, or the header is missing, then *failobj* is returned.
      The returned string will always be unquoted as per
      "email.utils.unquote()".

   get_boundary(failobj=None)

      Return the value of the "boundary" parameter of the *Content-
      Type* header of the message, or *failobj* if either the header
      is missing, or has no "boundary" parameter.  The returned string
      will always be unquoted as per "email.utils.unquote()".

   set_boundary(boundary)

      Set the "boundary" parameter of the *Content-Type* header to
      *boundary*.  "set_boundary()" will always quote *boundary* if
      necessary.  A "HeaderParseError" is raised if the message object
      has no *Content-Type* header.

      Note that using this method is subtly different from deleting
      the old *Content-Type* header and adding a new one with the new
      boundary via "add_header()", because "set_boundary()" preserves
      the order of the *Content-Type* header in the list of headers.

   get_content_charset(failobj=None)

      Return the "charset" parameter of the *Content-Type* header,
      coerced to lower case.  If there is no *Content-Type* header, or
      if that header has no "charset" parameter, *failobj* is
      returned.

   get_charsets(failobj=None)

      Return a list containing the character set names in the message.
      If the message is a *multipart*, then the list will contain one
      element for each subpart in the payload, otherwise, it will be a
      list of length 1.

      Each item in the list will be a string which is the value of the
      "charset" parameter in the *Content-Type* header for the
      represented subpart.  If the subpart has no *Content-Type*
      header, no "charset" parameter, or is not of the *text* main
      MIME type, then that item in the returned list will be
      *failobj*.

   is_attachment()

      Return "True" if there is a *Content-Disposition* header and its
      (case insensitive) value is "attachment", "False" otherwise.

      Distinto en la versión 3.4.2: is_attachment is now a method
      instead of a property, for consistency with "is_multipart()".

   get_content_disposition()

      Return the lowercased value (without parameters) of the
      message's *Content-Disposition* header if it has one, or "None".
      The possible values for this method are *inline*, *attachment*
      or "None" if the message follows **RFC 2183**.

      Nuevo en la versión 3.5.

   Los siguientes métodos se refieren a interrogar y manipular el
   contenido (*payload*) del mensaje.

   walk()

      The "walk()" method is an all-purpose generator which can be
      used to iterate over all the parts and subparts of a message
      object tree, in depth-first traversal order.  You will typically
      use "walk()" as the iterator in a "for" loop; each iteration
      returns the next subpart.

      Aquí hay un ejemplo que imprime el tipo MIME de cada parte de
      una estructura de mensaje de varias partes:

         >>> for part in msg.walk():
         ...     print(part.get_content_type())
         multipart/report
         text/plain
         message/delivery-status
         text/plain
         text/plain
         message/rfc822
         text/plain

      "walk" iterates over the subparts of any part where
      "is_multipart()" returns "True", even though
      "msg.get_content_maintype() == 'multipart'" may return "False".
      We can see this in our example by making use of the "_structure"
      debug helper function:

         >>> from email.iterators import _structure
         >>> for part in msg.walk():
         ...     print(part.get_content_maintype() == 'multipart',
         ...           part.is_multipart())
         True True
         False False
         False True
         False False
         False False
         False True
         False False
         >>> _structure(msg)
         multipart/report
             text/plain
             message/delivery-status
                 text/plain
                 text/plain
             message/rfc822
                 text/plain

      Here the "message" parts are not "multiparts", but they do
      contain subparts. "is_multipart()" returns "True" and "walk"
      descends into the subparts.

   get_body(preferencelist=('related', 'html', 'plain'))

      Retorna la parte MIME que es la mejor candidata para ser el
      "cuerpo" del mensaje.

      *preferencelist* must be a sequence of strings from the set
      "related", "html", and "plain", and indicates the order of
      preference for the content type of the part returned.

      Empieza a buscar coincidencias candidatas con el objeto en el
      que se llama al método "get_body"'.

      If "related" is not included in *preferencelist*, consider the
      root part (or subpart of the root part) of any related
      encountered as a candidate if the (sub-)part matches a
      preference.

      When encountering a "multipart/related", check the "start"
      parameter and if a part with a matching *Content-ID* is found,
      consider only it when looking for candidate matches.  Otherwise
      consider only the first (default root) part of the
      "multipart/related".

      If a part has a *Content-Disposition* header, only consider the
      part a candidate match if the value of the header is "inline".

      Si ninguno de los candidatos coincide con ninguna de las
      preferencias en *preferencelist*, retorna "None".

      Notes: (1) For most applications the only *preferencelist*
      combinations that really make sense are "('plain',)", "('html',
      'plain')", and the default "('related', 'html', 'plain')".  (2)
      Because matching starts with the object on which "get_body" is
      called, calling "get_body" on a "multipart/related" will return
      the object itself unless *preferencelist* has a non-default
      value. (3) Messages (or message parts) that do not specify a
      *Content-Type* or whose *Content-Type* header is invalid will be
      treated as if they are of type "text/plain", which may
      occasionally cause "get_body" to return unexpected results.

   iter_attachments()

      Return an iterator over all of the immediate sub-parts of the
      message that are not candidate "body" parts.  That is, skip the
      first occurrence of each of "text/plain", "text/html",
      "multipart/related", or "multipart/alternative" (unless they are
      explicitly marked as attachments via *Content-Disposition:
      attachment*), and return all remaining parts.  When applied
      directly to a "multipart/related", return an iterator over the
      all the related parts except the root part (ie: the part pointed
      to by the "start" parameter, or the first part if there is no
      "start" parameter or the "start" parameter doesn't match the
      *Content-ID* of any of the parts).  When applied directly to a
      "multipart/alternative" or a non-"multipart", return an empty
      iterator.

   iter_parts()

      Return an iterator over all of the immediate sub-parts of the
      message, which will be empty for a non-"multipart".  (See also
      "walk()".)

   get_content(*args, content_manager=None, **kw)

      Call the "get_content()" method of the *content_manager*,
      passing self as the message object, and passing along any other
      arguments or keywords as additional arguments.  If
      *content_manager* is not specified, use the "content_manager"
      specified by the current "policy".

   set_content(*args, content_manager=None, **kw)

      Call the "set_content()" method of the *content_manager*,
      passing self as the message object, and passing along any other
      arguments or keywords as additional arguments.  If
      *content_manager* is not specified, use the "content_manager"
      specified by the current "policy".

   make_related(boundary=None)

      Convert a non-"multipart" message into a "multipart/related"
      message, moving any existing *Content-* headers and payload into
      a (new) first part of the "multipart".  If *boundary* is
      specified, use it as the boundary string in the multipart,
      otherwise leave the boundary to be automatically created when it
      is needed (for example, when the message is serialized).

   make_alternative(boundary=None)

      Convert a non-"multipart" or a "multipart/related" into a
      "multipart/alternative", moving any existing *Content-* headers
      and payload into a (new) first part of the "multipart".  If
      *boundary* is specified, use it as the boundary string in the
      multipart, otherwise leave the boundary to be automatically
      created when it is needed (for example, when the message is
      serialized).

   make_mixed(boundary=None)

      Convert a non-"multipart", a "multipart/related", or a
      "multipart-alternative" into a "multipart/mixed", moving any
      existing *Content-* headers and payload into a (new) first part
      of the "multipart".  If *boundary* is specified, use it as the
      boundary string in the multipart, otherwise leave the boundary
      to be automatically created when it is needed (for example, when
      the message is serialized).

   add_related(*args, content_manager=None, **kw)

      If the message is a "multipart/related", create a new message
      object, pass all of the arguments to its "set_content()" method,
      and "attach()" it to the "multipart".  If the message is a
      non-"multipart", call "make_related()" and then proceed as
      above.  If the message is any other type of "multipart", raise a
      "TypeError". If *content_manager* is not specified, use the
      "content_manager" specified by the current "policy". If the
      added part has no *Content-Disposition* header, add one with the
      value "inline".

   add_alternative(*args, content_manager=None, **kw)

      If the message is a "multipart/alternative", create a new
      message object, pass all of the arguments to its "set_content()"
      method, and "attach()" it to the "multipart".  If the message is
      a non-"multipart" or "multipart/related", call
      "make_alternative()" and then proceed as above.  If the message
      is any other type of "multipart", raise a "TypeError". If
      *content_manager* is not specified, use the "content_manager"
      specified by the current "policy".

   add_attachment(*args, content_manager=None, **kw)

      If the message is a "multipart/mixed", create a new message
      object, pass all of the arguments to its "set_content()" method,
      and "attach()" it to the "multipart".  If the message is a
      non-"multipart", "multipart/related", or
      "multipart/alternative", call "make_mixed()" and then proceed as
      above. If *content_manager* is not specified, use the
      "content_manager" specified by the current "policy".  If the
      added part has no *Content-Disposition* header, add one with the
      value "attachment".  This method can be used both for explicit
      attachments (*Content-Disposition: attachment*) and "inline"
      attachments (*Content-Disposition: inline*), by passing
      appropriate options to the "content_manager".

   clear()

      Elimina el *payload* y todos los *headers*.

   clear_content()

      Remove the payload and all of the "Content-" headers, leaving
      all other headers intact and in their original order.

   "EmailMessage" objects have the following instance attributes:

   preamble

      The format of a MIME document allows for some text between the
      blank line following the headers, and the first multipart
      boundary string. Normally, this text is never visible in a MIME-
      aware mail reader because it falls outside the standard MIME
      armor.  However, when viewing the raw text of the message, or
      when viewing the message in a non-MIME aware reader, this text
      can become visible.

      The *preamble* attribute contains this leading extra-armor text
      for MIME documents.  When the "Parser" discovers some text after
      the headers but before the first boundary string, it assigns
      this text to the message's *preamble* attribute.  When the
      "Generator" is writing out the plain text representation of a
      MIME message, and it finds the message has a *preamble*
      attribute, it will write this text in the area between the
      headers and the first boundary.  See "email.parser" and
      "email.generator" for details.

      Note that if the message object has no preamble, the *preamble*
      attribute will be "None".

   epilogue

      The *epilogue* attribute acts the same way as the *preamble*
      attribute, except that it contains text that appears between the
      last boundary and the end of the message.  As with the
      "preamble", if there is no epilog text this attribute will be
      "None".

   defects

      The *defects* attribute contains a list of all the problems
      found when parsing this message.  See "email.errors" for a
      detailed description of the possible parsing defects.

class email.message.MIMEPart(policy=default)

   This class represents a subpart of a MIME message.  It is identical
   to "EmailMessage", except that no *MIME-Version* headers are added
   when "set_content()" is called, since sub-parts do not need their
   own *MIME-Version* headers.

-[ Notas al pie ]-

[1] Originally added in 3.4 as a *provisional module*.  Docs for
    legacy message class moved to email.message.Message: Representar
    un mensaje de correo electrónico usando la API compat32.
