From fdfb0050bb4e79724da052e06bab1c875076832a Mon Sep 17 00:00:00 2001 From: R David Murray Date: Mon, 29 Jul 2013 15:49:58 -0400 Subject: [PATCH] #18584: make doctest examples in email documentation pass. With the exception of the 'as_string' call in the policy docs. That one is a separate issue. Note that when building the docs sphinx is complaining about .. testcleanup:: being an invalid directive. I don't know why this is, as I'm following the sphinx docs...but fortunately the action is to omit the text in the body, so the generated documentation is correct. --- Doc/library/email.iterators.rst | 16 ++++++++++-- Doc/library/email.message.rst | 27 +++++++++++++------- Doc/library/email.policy.rst | 45 +++++++++++++++++++++++++-------- 3 files changed, 66 insertions(+), 22 deletions(-) diff --git a/Doc/library/email.iterators.rst b/Doc/library/email.iterators.rst index d1f1797e39f..6c7200f98af 100644 --- a/Doc/library/email.iterators.rst +++ b/Doc/library/email.iterators.rst @@ -33,14 +33,22 @@ useful higher level iterations over message object trees. Thus, by default :func:`typed_subpart_iterator` returns each subpart that has a MIME type of :mimetype:`text/\*`. + The following function has been added as a useful debugging tool. It should *not* be considered part of the supported public interface for the package. - .. function:: _structure(msg, fp=None, level=0, include_default=False) Prints an indented representation of the content types of the message object - structure. For example:: + structure. For example: + + .. testsetup:: + + >>> import email + >>> from email.iterators import _structure + >>> somefile = open('Lib/test/test_email/data/msg_02.txt') + + .. doctest:: >>> msg = email.message_from_file(somefile) >>> _structure(msg) @@ -60,6 +68,10 @@ The following function has been added as a useful debugging tool. It should text/plain text/plain + .. testcleanup:: + + >>> somefile.close() + Optional *fp* is a file-like object to print the output to. It must be suitable for Python's :func:`print` function. *level* is used internally. *include_default*, if true, prints the default type as well. diff --git a/Doc/library/email.message.rst b/Doc/library/email.message.rst index 59ab47dd617..2c7be8b7d46 100644 --- a/Doc/library/email.message.rst +++ b/Doc/library/email.message.rst @@ -513,16 +513,25 @@ Here are the methods of the :class:`Message` class: iterator in a ``for`` loop; each iteration returns the next subpart. Here's an example that prints the MIME type of every part of a multipart - message structure:: + message structure: - >>> for part in msg.walk(): - ... print(part.get_content_type()) - multipart/report - text/plain - message/delivery-status - text/plain - text/plain - message/rfc822 + .. testsetup:: + + >>> from email import message_from_binary_file + >>> with open('Lib/test/test_email/data/msg_16.txt', 'rb') as f: + ... msg = message_from_binary_file(f) + + .. doctest:: + + >>> 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 :class:`Message` objects can also optionally contain two instance attributes, which can be used when generating the plain text of a MIME message. diff --git a/Doc/library/email.policy.rst b/Doc/library/email.policy.rst index 06d98214ca2..d85054ac833 100644 --- a/Doc/library/email.policy.rst +++ b/Doc/library/email.policy.rst @@ -56,19 +56,42 @@ same keyword arguments as the class constructor and returning a new attributes values changed. As an example, the following code could be used to read an email message from a -file on disk and pass it to the system ``sendmail`` program on a Unix system:: +file on disk and pass it to the system ``sendmail`` program on a Unix system: - >>> from email import msg_from_binary_file +.. testsetup:: + + >>> from unittest import mock + >>> mocker = mock.patch('subprocess.Popen') + >>> m = mocker.start() + >>> proc = mock.MagicMock() + >>> m.return_value = proc + >>> proc.stdin.close.return_value = None + >>> mymsg = open('mymsg.txt', 'w') + >>> mymsg.write('To: abc@xyz.com\n\n') + 17 + >>> mymsg.flush() + +.. doctest:: + + >>> from email import message_from_binary_file >>> from email.generator import BytesGenerator + >>> from email import policy >>> from subprocess import Popen, PIPE - >>> with open('mymsg.txt', 'b') as f: - ... msg = msg_from_binary_file(f) - >>> p = Popen(['sendmail', msg['To'][0].address], stdin=PIPE) + >>> with open('mymsg.txt', 'rb') as f: + ... msg = message_from_binary_file(f, policy=policy.default) + >>> p = Popen(['sendmail', msg['To'].addresses[0]], stdin=PIPE) >>> g = BytesGenerator(p.stdin, policy=msg.policy.clone(linesep='\r\n')) >>> g.flatten(msg) >>> p.stdin.close() >>> rc = p.wait() +.. testcleanup:: + + >>> mymsg.close() + >>> mocker.stop() + >>> import os + >>> os.remove('mymsg.txt') + Here we are telling :class:`~email.generator.BytesGenerator` to use the RFC correct line separator characters when creating the binary string to feed into ``sendmail's`` ``stdin``, where the default policy would use ``\n`` line @@ -82,22 +105,22 @@ separators for the platform on which it is running:: >>> import os >>> with open('converted.txt', 'wb') as f: - ... f.write(msg.as_string(policy=msg.policy.clone(linesep=os.linesep)) + ... f.write(msg.as_string(policy=msg.policy.clone(linesep=os.linesep))) Policy objects can also be combined using the addition operator, producing a policy object whose settings are a combination of the non-default values of the summed objects:: - >>> compat_SMTP = email.policy.clone(linesep='\r\n') - >>> compat_strict = email.policy.clone(raise_on_defect=True) + >>> compat_SMTP = policy.compat32.clone(linesep='\r\n') + >>> compat_strict = policy.compat32.clone(raise_on_defect=True) >>> compat_strict_SMTP = compat_SMTP + compat_strict This operation is not commutative; that is, the order in which the objects are added matters. To illustrate:: - >>> policy100 = compat32.clone(max_line_length=100) - >>> policy80 = compat32.clone(max_line_length=80) - >>> apolicy = policy100 + Policy80 + >>> policy100 = policy.compat32.clone(max_line_length=100) + >>> policy80 = policy.compat32.clone(max_line_length=80) + >>> apolicy = policy100 + policy80 >>> apolicy.max_line_length 80 >>> apolicy = policy80 + policy100