summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/email.headerregistry.rst379
-rw-r--r--Doc/library/email.policy.rst186
-rw-r--r--Lib/email/headerregistry.py (renamed from Lib/email/_headerregistry.py)0
-rw-r--r--Lib/email/policy.py6
-rw-r--r--Lib/test/test_email/test_headerregistry.py (renamed from Lib/test/test_email/test__headerregistry.py)57
-rw-r--r--Lib/test/test_email/test_pickleable.py2
-rw-r--r--Lib/test/test_email/test_policy.py12
7 files changed, 429 insertions, 213 deletions
diff --git a/Doc/library/email.headerregistry.rst b/Doc/library/email.headerregistry.rst
new file mode 100644
index 0000000..4fc9594
--- /dev/null
+++ b/Doc/library/email.headerregistry.rst
@@ -0,0 +1,379 @@
+:mod:`email.headerregistry`: Custom Header Objects
+--------------------------------------------------
+
+.. module:: email.headerregistry
+ :synopsis: Automatic Parsing of headers based on the field name
+
+.. note::
+
+ The headerregistry module has been included in the standard library on a
+ :term:`provisional basis <provisional package>`. Backwards incompatible
+ changes (up to and including removal of the module) may occur if deemed
+ necessary by the core developers.
+
+.. versionadded:: 3.3
+ as a :term:`provisional module <provisional package>`
+
+Headers are represented by customized subclasses of :class:`str`. The
+particular class used to represent a given header is determined by the
+:attr:`~email.policy.EmailPolicy.header_factory` of the :mod:`~email.policy` in
+effect when the headers are created. This section documents the particular
+``header_factory`` implemented by the email package for handling :RFC:`5322`
+compliant email messages, which not only provides customized header objects for
+various header types, but also provides an extension mechanism for applications
+to add their own custom header types.
+
+When using any of the policy objects derived from
+:data:`~email.policy.EmailPolicy`, all headers are produced by
+:class:`.HeaderRegistry` and have :class:`.BaseHeader` as their last base
+class. Each header class has an additional base class that is determined by
+the type of the header. For example, many headers have the class
+:class:`.UnstructuredHeader` as their other base class. The specialized second
+class for a header is determined by the name of the header, using a lookup
+table stored in the :class:`.HeaderRegistry`. All of this is managed
+transparently for the typical application program, but interfaces are provided
+for modifying the default behavior for use by more complex applications.
+
+The sections below first document the header base classes and their attributes,
+followed by the API for modifying the behavior of :class:`.HeaderRegistry`, and
+finally the support classes used to represent the data parsed from structured
+headers.
+
+
+.. class:: BaseHeader(name, value)
+
+ *name* and *value* are passed to ``BaseHeader`` from the
+ :attr:`~email.policy.EmailPolicy.header_factory` call. The string value of
+ any header object is the *value* fully decoded to unicode.
+
+ This base class defines the following read-only properties:
+
+
+ .. attribute:: name
+
+ The name of the header (the portion of the field before the ':'). This
+ is exactly the value passed in the :attr:`~EmailPolicy.header_factory`
+ call for *name*; that is, case is preserved.
+
+
+ .. attribute:: defects
+
+ A tuple of :exc:`~email.errors.HeaderDefect` instances reporting any
+ RFC compliance problems found during parsing. The email package tries to
+ be complete about detecting compliance issues. See the :mod:`errors`
+ module for a discussion of the types of defects that may be reported.
+
+
+ .. attribute:: max_count
+
+ The maximum number of headers of this type that can have the same
+ ``name``. A value of ``None`` means unlimited. The ``BaseHeader`` value
+ for this attribute is ``None``; it is expected that specialized header
+ classes will override this value as needed.
+
+ ``BaseHeader`` also provides the following method, which is called by the
+ email library code and should not in general be called by application
+ programs:
+
+ .. method:: fold(*, policy)
+
+ Return a string containing :attr:`~email.policy.Policy.linesep`
+ characters as required to correctly fold the header according
+ to *policy*. A :attr:`~email.policy.Policy.cte_type` of
+ ``8bit`` will be treated as if it were ``7bit``, since strings
+ may not contain binary data.
+
+
+ ``BaseHeader`` by itself cannot be used to create a header object. It
+ defines a protocol that each specialized header cooperates with in order to
+ produce the header object. Specifically, ``BaseHeader`` requires that
+ the specialized class provide a :func:`classmethod` named ``parse``. This
+ method is called as follows::
+
+ parse(string, kwds)
+
+ ``kwds`` is a dictionary containing one pre-initialized key, ``defects``.
+ ``defects`` is an empty list. The parse method should append any detected
+ defects to this list. On return, the ``kwds`` dictionary *must* contain
+ values for at least the keys ``decoded`` and ``defects``. ``decoded``
+ should be the string value for the header (that is, the header value fully
+ decoded to unicode). The parse method should assume that *string* may
+ contain transport encoded parts, but should correctly handle all valid
+ unicode characters as well so that it can parse un-encoded header values.
+
+ ``BaseHeader``'s ``__new__`` then creates the header instance, and calls its
+ ``init`` method. The specialized class only needs to provide an ``init``
+ method if it wishes to set additional attributes beyond those provided by
+ ``BaseHeader`` itself. Such an ``init`` method should look like this::
+
+ def init(self, *args, **kw):
+ self._myattr = kw.pop('myattr')
+ super().init(*args, **kw)
+
+ That is, anything extra that the specialized class puts in to the ``kwds``
+ dictionary should be removed and handled, and the remaining contents of
+ ``kw`` (and ``args``) passed to the ``BaseHeader`` ``init`` method.
+
+
+.. class:: UnstructuredHeader
+
+ An "unstructured" header is the default type of header in :rfc:`5322`.
+ Any header that does not have a specified syntax is treated as
+ unstructured. The classic example of an unstructured header is the
+ :mailheader:`Subject` header.
+
+ In :rfc:`5322`, an unstructured header is a run of arbitrary text in the
+ ASCII character set. :rfc:`2047`, however, has an :rfc:`5322` compatible
+ mechanism for encoding non-ASCII text as ASCII characters within a header
+ value. When a *value* containing encoded words is passed to the
+ constructor, the ``UnstructuredHeader`` parser converts such encoded words
+ back in to the original unicode, following the :rfc:`2047` rules for
+ unstructured text. The parser uses heuristics to attempt to decode certain
+ non-compliant encoded words. Defects are registered in such cases, as well
+ as defects for issues such as invalid characters within the encoded words or
+ the non-encoded text.
+
+ This header type provides no additional attributes.
+
+
+.. class:: DateHeader
+
+ :rfc:`5322` specifies a very specific format for dates within email headers.
+ The ``DateHeader`` parser recognizes that date format, as well as
+ recognizing a number of variant forms that are sometimes found "in the
+ wild".
+
+ This header type provides the following additional attributes:
+
+ .. attribute:: datetime
+
+ If the header value can be recognized as a valid date of one form or
+ another, this attribute will contain a :class:`~datetime.datetime`
+ instance representing that date. If the timezone of the input date is
+ specified as ``-0000`` (indicating it is in UTC but contains no
+ information about the source timezone), then :attr:`.datetime` will be a
+ naive :class:`~datetime.datetime`. If a specific timezone offset is
+ found (including `+0000`), then :attr:`.datetime` will contain an aware
+ ``datetime`` that uses :class:`datetime.timezone` to record the timezone
+ offset.
+
+ The ``decoded`` value of the header is determined by formatting the
+ ``datetime`` according to the :rfc:`5322` rules; that is, it is set to::
+
+ email.utils.format_datetime(self.datetime)
+
+ When creating a ``DateHeader``, *value* may be
+ :class:`~datetime.datetime` instance. This means, for example, that
+ the following code is valid and does what one would expect::
+
+ msg['Date'] = datetime(2011, 7, 15, 21)
+
+ Because this is a naive ``datetime`` it will be interpreted as a UTC
+ timestamp, and the resulting value will have a timezone of ``-0000``. Much
+ more useful is to use the :func:`~email.utils.localtime` function from the
+ :mod:`~email.utils` module::
+
+ msg['Date'] = utils.localtime()
+
+ This example sets the date header to the current time and date using
+ the current timezone offset.
+
+
+.. class:: AddressHeader
+
+ Address headers are one of the most complex structured header types.
+ The ``AddressHeader`` class provides a generic interface to any address
+ header.
+
+ This header type provides the following additional attributes:
+
+
+ .. attribute:: groups
+
+ A tuple of :class:`.Group` objects encoding the
+ addresses and groups found in the header value. Addresses that are
+ not part of a group are represented in this list as single-address
+ ``Groups`` whose :attr:`~.Group.display_name` is ``None``.
+
+
+ .. attribute:: addresses
+
+ A tuple of :class:`.Address` objects encoding all
+ of the individual addresses from the header value. If the header value
+ contains any groups, the individual addresses from the group are included
+ in the list at the point where the group occurs in the value (that is,
+ the list of addresses is "flattened" into a one dimensional list).
+
+ The ``decoded`` value of the header will have all encoded words decoded to
+ unicode. :class:`~encodings.idna` encoded domain names are also decoded to unicode. The
+ ``decoded`` value is set by :attr:`~str.join`\ ing the :class:`str` value of
+ the elements of the ``groups`` attribute with ``', '``.
+
+ A list of :class:`.Address` and :class:`.Group` objects in any combination
+ may be used to set the value of an address header. ``Group`` objects whose
+ ``display_name`` is ``None`` will be interpreted as single addresses, which
+ allows an address list to be copied with groups intact by using the list
+ obtained ``groups`` attribute of the source header.
+
+
+.. class:: SingleAddressHeader
+
+ A subclass of :class:`.AddressHeader` that adds one
+ additional attribute:
+
+
+ .. attribute:: address
+
+ The single address encoded by the header value. If the header value
+ actually contains more than one address (which would be a violation of
+ the RFC under the default :mod:`policy`), accessing this attribute will
+ result in a :exc:`ValueError`.
+
+
+Each of the above classes also has a ``Unique`` variant (for example,
+``UniqueUnstructuredHeader``). The only difference is that in the ``Unique``
+variant, :attr:`~.BaseHeader.max_count` is set to 1.
+
+
+.. class:: HeaderRegistry(base_class=BaseHeader, \
+ default_class=UnstructuredHeader, \
+ use_default_map=True)
+
+ This is the factory used by :class:`~email.policy.EmailPolicy` by default.
+ ``HeaderRegistry`` builds the class used to create a header instance
+ dynamically, using *base_class* and a specialized class retrieved from a
+ registry that it holds. When a given header name does not appear in the
+ registry, the class specified by *default_class* is used as the specialized
+ class. When *use_default_map* is ``True`` (the default), the standard
+ mapping of header names to classes is copied in to the registry during
+ initialization. *base_class* is always the last class in the generated
+ class's ``__bases__`` list.
+
+ The default mappings are:
+
+ :subject: UniqueUnstructuredHeader
+ :date: UniqueDateHeader
+ :resent-date: DateHeader
+ :orig-date: UniqueDateHeader
+ :sender: UniqueSingleAddressHeader
+ :resent-sender: SingleAddressHeader
+ :to: UniqueAddressHeader
+ :resent-to: AddressHeader
+ :cc: UniqueAddressHeader
+ :resent-cc: AddressHeader
+ :from: UniqueAddressHeader
+ :resent-from: AddressHeader
+ :reply-to: UniqueAddressHeader
+
+ ``HeaderRegistry`` has the following methods:
+
+
+ .. method:: map_to_type(self, name, cls)
+
+ *name* is the name of the header to be mapped. It will be converted to
+ lower case in the registry. *cls* is the specialized class to be used,
+ along with *base_class*, to create the class used to instantiate headers
+ that match *name*.
+
+
+ .. method:: __getitem__(name)
+
+ Construct and return a class to handle creating a *name* header.
+
+
+ .. method:: __call__(name, value)
+
+ Retrieves the specialized header associated with *name* from the
+ registry (using *default_class* if *name* does not appear in the
+ registry) and composes it with *base_class* to produce a class,
+ calls the constructed class's constructor, passing it the same
+ argument list, and finally returns the class instance created thereby.
+
+
+The following classes are the classes used to represent data parsed from
+structured headers and can, in general, be used by an application program to
+construct structured values to assign to specific headers.
+
+
+.. class:: Address(display_name='', username='', domain='', addr_spec=None)
+
+ The class used to represent an email address. The general form of an
+ address is::
+
+ [display_name] <username@domain>
+
+ or::
+
+ username@domain
+
+ where each part must conform to specific syntax rules spelled out in
+ :rfc:`5322`.
+
+ As a convenience *addr_spec* can be specified instead of *username* and
+ *domain*, in which case *username* and *domain* will be parsed from the
+ *addr_spec*. An *addr_spec* must be a properly RFC quoted string; if it is
+ not ``Address`` will raise an error. Unicode characters are allowed and
+ will be property encoded when serialized. However, per the RFCs, unicode is
+ *not* allowed in the username portion of the address.
+
+ .. attribute:: display_name
+
+ The display name portion of the address, if any, with all quoting
+ removed. If the address does not have a display name, this attribute
+ will be an empty string.
+
+ .. attribute:: username
+
+ The ``username`` portion of the address, with all quoting removed.
+
+ .. attribute:: domain
+
+ The ``domain`` portion of the address.
+
+ .. attribute:: addr_spec
+
+ The ``username@domain`` portion of the address, correctly quoted
+ for use as a bare address (the second form shown above). This
+ attribute is not mutable.
+
+ .. method:: __str__()
+
+ The ``str`` value of the object is the address quoted according to
+ :rfc:`5322` rules, but with no Content Transfer Encoding of any non-ASCII
+ characters.
+
+ To support SMTP (:rfc:`5321`), ``Address`` handles one special case: if
+ ``username`` and ``domain`` are both the empty string (or ``None``), then
+ the string value of the ``Address`` is ``<>``.
+
+
+.. class:: Group(display_name=None, addresses=None)
+
+ The class used to represent an address group. The general form of an
+ address group is::
+
+ display_name: [address-list];
+
+ As a convenience for processing lists of addresses that consist of a mixture
+ of groups and single addresses, a ``Group`` may also be used to represent
+ single addresses that are not part of a group by setting *display_name* to
+ ``None`` and providing a list of the single address as *addresses*.
+
+ .. attribute:: display_name
+
+ The ``display_name`` of the group. If it is ``None`` and there is
+ exactly one ``Address`` in ``addresses``, then the ``Group`` represents a
+ single address that is not in a group.
+
+ .. attribute:: addresses
+
+ A possibly empty tuple of :class:`.Address` objects representing the
+ addresses in the group.
+
+ .. method:: __str__()
+
+ The ``str`` value of a ``Group`` is formatted according to :rfc:`5322`,
+ but with no Content Transfer Encoding of any non-ASCII characters. If
+ ``display_name`` is none and there is a single ``Address`` in the
+ ``addresses`` list, the ``str`` value will be the same as the ``str`` of
+ that single ``Address``.
diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst
index c1734e2..2ba0dba 100644
--- a/Doc/library/email.policy.rst
+++ b/Doc/library/email.policy.rst
@@ -310,10 +310,10 @@ added matters. To illustrate::
.. note::
- The remainder of the classes documented below are included in the standard
- library on a :term:`provisional basis <provisional package>`. Backwards
- incompatible changes (up to and including removal of the feature) may occur
- if deemed necessary by the core developers.
+ The documentation below describes new policies that are included in the
+ standard library on a :term:`provisional basis <provisional package>`.
+ Backwards incompatible changes (up to and including removal of the feature)
+ may occur if deemed necessary by the core developers.
.. class:: EmailPolicy(**kw)
@@ -353,12 +353,12 @@ added matters. To illustrate::
A callable that takes two arguments, ``name`` and ``value``, where
``name`` is a header field name and ``value`` is an unfolded header field
- value, and returns a string-like object that represents that header. A
- default ``header_factory`` is provided that understands some of the
- :RFC:`5322` header field types. (Currently address fields and date
- fields have special treatment, while all other fields are treated as
- unstructured. This list will be completed before the extension is marked
- stable.)
+ value, and returns a string subclass that represents that header. A
+ default ``header_factory`` (see :mod:`~email.headerregistry`) is provided
+ that understands some of the :RFC:`5322` header field types. (Currently
+ address fields and date fields have special treatment, while all other
+ fields are treated as unstructured. This list will be completed before
+ the extension is marked stable.)
The class provides the following concrete implementations of the abstract
methods of :class:`Policy`:
@@ -465,167 +465,5 @@ header. Likewise, a header may be assigned a new value, or a new header
created, using a unicode string, and the policy will take care of converting
the unicode string into the correct RFC encoded form.
-The custom header objects and their attributes are described below. All custom
-header objects are string subclasses, and their string value is the fully
-decoded value of the header field (the part of the field after the ``:``)
-
-
-.. class:: BaseHeader
-
- This is the base class for all custom header objects. It provides the
- following attributes:
-
- .. attribute:: name
-
- The header field name (the portion of the field before the ':').
-
- .. attribute:: defects
-
- A possibly empty list of :class:`~email.errors.MessageDefect` objects
- that record any RFC violations found while parsing the header field.
-
- .. method:: fold(*, policy)
-
- Return a string containing :attr:`~email.policy.Policy.linesep`
- characters as required to correctly fold the header according
- to *policy*. A :attr:`~email.policy.Policy.cte_type` of
- ``8bit`` will be treated as if it were ``7bit``, since strings
- may not contain binary data.
-
-
-.. class:: UnstructuredHeader
-
- The class used for any header that does not have a more specific
- type. (The :mailheader:`Subject` header is an example of an
- unstructured header.) It does not have any additional attributes.
-
-
-.. class:: DateHeader
-
- The value of this type of header is a single date and time value. The
- primary example of this type of header is the :mailheader:`Date` header.
-
- .. attribute:: datetime
-
- A :class:`~datetime.datetime` encoding the date and time from the
- header value.
-
- The ``datetime`` will be a naive ``datetime`` if the value either does
- not have a specified timezone (which would be a violation of the RFC) or
- if the timezone is specified as ``-0000``. This timezone value indicates
- that the date and time is to be considered to be in UTC, but with no
- indication of the local timezone in which it was generated. (This
- contrasts to ``+0000``, which indicates a date and time that really is in
- the UTC ``0000`` timezone.)
-
- If the header value contains a valid timezone that is not ``-0000``, the
- ``datetime`` will be an aware ``datetime`` having a
- :class:`~datetime.tzinfo` set to the :class:`~datetime.timezone`
- indicated by the header value.
-
- A ``datetime`` may also be assigned to a :mailheader:`Date` type header.
- The resulting string value will use a timezone of ``-0000`` if the
- ``datetime`` is naive, and the appropriate UTC offset if the ``datetime`` is
- aware.
-
-
-.. class:: AddressHeader
-
- This class is used for all headers that can contain addresses, whether they
- are supposed to be singleton addresses or a list.
-
- .. attribute:: addresses
-
- A list of :class:`.Address` objects listing all of the addresses that
- could be parsed out of the field value.
-
- .. attribute:: groups
-
- A list of :class:`.Group` objects. Every address in :attr:`.addresses`
- appears in one of the group objects in the tuple. Addresses that are not
- syntactically part of a group are represented by ``Group`` objects whose
- ``name`` is ``None``.
-
- In addition to addresses in string form, any combination of
- :class:`.Address` and :class:`.Group` objects, singly or in a list, may be
- assigned to an address header.
-
-
-.. class:: Address(display_name='', username='', domain='', addr_spec=None):
-
- The class used to represent an email address. The general form of an
- address is::
-
- [display_name] <username@domain>
-
- or::
-
- username@domain
-
- where each part must conform to specific syntax rules spelled out in
- :rfc:`5322`.
-
- As a convenience *addr_spec* can be specified instead of *username* and
- *domain*, in which case *username* and *domain* will be parsed from the
- *addr_spec*. An *addr_spec* must be a properly RFC quoted string; if it is
- not ``Address`` will raise an error. Unicode characters are allowed and
- will be property encoded when serialized. However, per the RFCs, unicode is
- *not* allowed in the username portion of the address.
-
- .. attribute:: display_name
-
- The display name portion of the address, if any, with all quoting
- removed. If the address does not have a display name, this attribute
- will be an empty string.
-
- .. attribute:: username
-
- The ``username`` portion of the address, with all quoting removed.
-
- .. attribute:: domain
-
- The ``domain`` portion of the address.
-
- .. attribute:: addr_spec
-
- The ``username@domain`` portion of the address, correctly quoted
- for use as a bare address (the second form shown above). This
- attribute is not mutable.
-
- .. method:: __str__()
-
- The ``str`` value of the object is the address quoted according to
- :rfc:`5322` rules, but with no Content Transfer Encoding of any non-ASCII
- characters.
-
-
-.. class:: Group(display_name=None, addresses=None)
-
- The class used to represent an address group. The general form of an
- address group is::
-
- display_name: [address-list];
-
- As a convenience for processing lists of addresses that consist of a mixture
- of groups and single addresses, a ``Group`` may also be used to represent
- single addresses that are not part of a group by setting *display_name* to
- ``None`` and providing a list of the single address as *addresses*.
-
- .. attribute:: display_name
-
- The ``display_name`` of the group. If it is ``None`` and there is
- exactly one ``Address`` in ``addresses``, then the ``Group`` represents a
- single address that is not in a group.
-
- .. attribute:: addresses
-
- A possibly empty tuple of :class:`.Address` objects representing the
- addresses in the group.
-
- .. method:: __str__()
-
- The ``str`` value of a ``Group`` is formatted according to :rfc:`5322`,
- but with no Content Transfer Encoding of any non-ASCII characters. If
- ``display_name`` is none and there is a single ``Address`` in the
- ``addresses` list, the ``str`` value will be the same as the ``str`` of
- that single ``Address``.
+The custom header objects and their attributes are described in
+:mod:`~email.headerregistry`.
diff --git a/Lib/email/_headerregistry.py b/Lib/email/headerregistry.py
index 6588546..6588546 100644
--- a/Lib/email/_headerregistry.py
+++ b/Lib/email/headerregistry.py
diff --git a/Lib/email/policy.py b/Lib/email/policy.py
index 18946c3..47ed66b 100644
--- a/Lib/email/policy.py
+++ b/Lib/email/policy.py
@@ -4,7 +4,7 @@ code that adds all the email6 features.
from email._policybase import Policy, Compat32, compat32
from email.utils import _has_surrogates
-from email._headerregistry import HeaderRegistry as _HeaderRegistry
+from email.headerregistry import HeaderRegistry as HeaderRegistry
__all__ = [
'Compat32',
@@ -60,13 +60,13 @@ class EmailPolicy(Policy):
"""
refold_source = 'long'
- header_factory = _HeaderRegistry()
+ header_factory = HeaderRegistry()
def __init__(self, **kw):
# Ensure that each new instance gets a unique header factory
# (as opposed to clones, which share the factory).
if 'header_factory' not in kw:
- object.__setattr__(self, 'header_factory', _HeaderRegistry())
+ object.__setattr__(self, 'header_factory', HeaderRegistry())
super().__init__(**kw)
# The logic of the next three methods is chosen such that it is possible to
diff --git a/Lib/test/test_email/test__headerregistry.py b/Lib/test/test_email/test_headerregistry.py
index 23bc5ff..fa8fd97 100644
--- a/Lib/test/test_email/test__headerregistry.py
+++ b/Lib/test/test_email/test_headerregistry.py
@@ -5,72 +5,71 @@ from email import errors
from email import policy
from email.message import Message
from test.test_email import TestEmailBase
-from email import _headerregistry
-# Address and Group are public but I'm not sure where to put them yet.
-from email._headerregistry import Address, Group
+from email import headerregistry
+from email.headerregistry import Address, Group
class TestHeaderRegistry(TestEmailBase):
def test_arbitrary_name_unstructured(self):
- factory = _headerregistry.HeaderRegistry()
+ factory = headerregistry.HeaderRegistry()
h = factory('foobar', 'test')
- self.assertIsInstance(h, _headerregistry.BaseHeader)
- self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+ self.assertIsInstance(h, headerregistry.BaseHeader)
+ self.assertIsInstance(h, headerregistry.UnstructuredHeader)
def test_name_case_ignored(self):
- factory = _headerregistry.HeaderRegistry()
+ factory = headerregistry.HeaderRegistry()
# Whitebox check that test is valid
self.assertNotIn('Subject', factory.registry)
h = factory('Subject', 'test')
- self.assertIsInstance(h, _headerregistry.BaseHeader)
- self.assertIsInstance(h, _headerregistry.UniqueUnstructuredHeader)
+ self.assertIsInstance(h, headerregistry.BaseHeader)
+ self.assertIsInstance(h, headerregistry.UniqueUnstructuredHeader)
class FooBase:
def __init__(self, *args, **kw):
pass
def test_override_default_base_class(self):
- factory = _headerregistry.HeaderRegistry(base_class=self.FooBase)
+ factory = headerregistry.HeaderRegistry(base_class=self.FooBase)
h = factory('foobar', 'test')
self.assertIsInstance(h, self.FooBase)
- self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+ self.assertIsInstance(h, headerregistry.UnstructuredHeader)
class FooDefault:
- parse = _headerregistry.UnstructuredHeader.parse
+ parse = headerregistry.UnstructuredHeader.parse
def test_override_default_class(self):
- factory = _headerregistry.HeaderRegistry(default_class=self.FooDefault)
+ factory = headerregistry.HeaderRegistry(default_class=self.FooDefault)
h = factory('foobar', 'test')
- self.assertIsInstance(h, _headerregistry.BaseHeader)
+ self.assertIsInstance(h, headerregistry.BaseHeader)
self.assertIsInstance(h, self.FooDefault)
def test_override_default_class_only_overrides_default(self):
- factory = _headerregistry.HeaderRegistry(default_class=self.FooDefault)
+ factory = headerregistry.HeaderRegistry(default_class=self.FooDefault)
h = factory('subject', 'test')
- self.assertIsInstance(h, _headerregistry.BaseHeader)
- self.assertIsInstance(h, _headerregistry.UniqueUnstructuredHeader)
+ self.assertIsInstance(h, headerregistry.BaseHeader)
+ self.assertIsInstance(h, headerregistry.UniqueUnstructuredHeader)
def test_dont_use_default_map(self):
- factory = _headerregistry.HeaderRegistry(use_default_map=False)
+ factory = headerregistry.HeaderRegistry(use_default_map=False)
h = factory('subject', 'test')
- self.assertIsInstance(h, _headerregistry.BaseHeader)
- self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+ self.assertIsInstance(h, headerregistry.BaseHeader)
+ self.assertIsInstance(h, headerregistry.UnstructuredHeader)
def test_map_to_type(self):
- factory = _headerregistry.HeaderRegistry()
+ factory = headerregistry.HeaderRegistry()
h1 = factory('foobar', 'test')
- factory.map_to_type('foobar', _headerregistry.UniqueUnstructuredHeader)
+ factory.map_to_type('foobar', headerregistry.UniqueUnstructuredHeader)
h2 = factory('foobar', 'test')
- self.assertIsInstance(h1, _headerregistry.BaseHeader)
- self.assertIsInstance(h1, _headerregistry.UnstructuredHeader)
- self.assertIsInstance(h2, _headerregistry.BaseHeader)
- self.assertIsInstance(h2, _headerregistry.UniqueUnstructuredHeader)
+ self.assertIsInstance(h1, headerregistry.BaseHeader)
+ self.assertIsInstance(h1, headerregistry.UnstructuredHeader)
+ self.assertIsInstance(h2, headerregistry.BaseHeader)
+ self.assertIsInstance(h2, headerregistry.UniqueUnstructuredHeader)
class TestHeaderBase(TestEmailBase):
- factory = _headerregistry.HeaderRegistry()
+ factory = headerregistry.HeaderRegistry()
def make_header(self, name, value):
return self.factory(name, value)
@@ -149,13 +148,13 @@ class TestDateHeader(TestHeaderBase):
def test_date_header_properties(self):
h = self.make_header('date', self.datestring)
- self.assertIsInstance(h, _headerregistry.UniqueDateHeader)
+ self.assertIsInstance(h, headerregistry.UniqueDateHeader)
self.assertEqual(h.max_count, 1)
self.assertEqual(h.defects, ())
def test_resent_date_header_properties(self):
h = self.make_header('resent-date', self.datestring)
- self.assertIsInstance(h, _headerregistry.DateHeader)
+ self.assertIsInstance(h, headerregistry.DateHeader)
self.assertEqual(h.max_count, None)
self.assertEqual(h.defects, ())
diff --git a/Lib/test/test_email/test_pickleable.py b/Lib/test/test_email/test_pickleable.py
index e4c77ca..9ebf933 100644
--- a/Lib/test/test_email/test_pickleable.py
+++ b/Lib/test/test_email/test_pickleable.py
@@ -4,7 +4,7 @@ import copy
import pickle
from email import policy
from email import message_from_string
-from email._headerregistry import HeaderRegistry
+from email.headerregistry import HeaderRegistry
from test.test_email import TestEmailBase
class TestPickleCopyHeader(TestEmailBase):
diff --git a/Lib/test/test_email/test_policy.py b/Lib/test/test_email/test_policy.py
index 45a7666..983bd49 100644
--- a/Lib/test/test_email/test_policy.py
+++ b/Lib/test/test_email/test_policy.py
@@ -5,7 +5,7 @@ import unittest
import email.policy
import email.parser
import email.generator
-from email import _headerregistry
+from email import headerregistry
def make_defaults(base_defaults, differences):
defaults = base_defaults.copy()
@@ -185,11 +185,11 @@ class PolicyAPITests(unittest.TestCase):
def test_default_header_factory(self):
h = email.policy.default.header_factory('Test', 'test')
self.assertEqual(h.name, 'Test')
- self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
- self.assertIsInstance(h, _headerregistry.BaseHeader)
+ self.assertIsInstance(h, headerregistry.UnstructuredHeader)
+ self.assertIsInstance(h, headerregistry.BaseHeader)
class Foo:
- parse = _headerregistry.UnstructuredHeader.parse
+ parse = headerregistry.UnstructuredHeader.parse
def test_each_Policy_gets_unique_factory(self):
policy1 = email.policy.EmailPolicy()
@@ -197,10 +197,10 @@ class PolicyAPITests(unittest.TestCase):
policy1.header_factory.map_to_type('foo', self.Foo)
h = policy1.header_factory('foo', 'test')
self.assertIsInstance(h, self.Foo)
- self.assertNotIsInstance(h, _headerregistry.UnstructuredHeader)
+ self.assertNotIsInstance(h, headerregistry.UnstructuredHeader)
h = policy2.header_factory('foo', 'test')
self.assertNotIsInstance(h, self.Foo)
- self.assertIsInstance(h, _headerregistry.UnstructuredHeader)
+ self.assertIsInstance(h, headerregistry.UnstructuredHeader)
def test_clone_copies_factory(self):
policy1 = email.policy.EmailPolicy()