diff -r 9dcd88977694 -r 650549138a3d .hgeol --- a/.hgeol Tue Mar 22 21:31:46 2011 +1000 +++ b/.hgeol Tue Mar 22 21:32:51 2011 +1000 @@ -27,6 +27,7 @@ **.zip = BIN Lib/email/test/data/msg_26.txt = BIN +Lib/test/test_email/data/msg_26.txt = BIN Lib/test/sndhdrdata/sndhdr.* = BIN Lib/test/decimaltestdata/*.decTest = BIN diff -r 9dcd88977694 -r 650549138a3d .hgignore --- a/.hgignore Tue Mar 22 21:31:46 2011 +1000 +++ b/.hgignore Tue Mar 22 21:32:51 2011 +1000 @@ -50,6 +50,7 @@ *~ Lib/lib2to3/*.pickle Lib/test/data/* +Misc/*.wpu PC/python_nt*.h PC/pythonnt_rc*.h PC/*.obj diff -r 9dcd88977694 -r 650549138a3d Doc/c-api/import.rst --- a/Doc/c-api/import.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/c-api/import.rst Tue Mar 22 21:32:51 2011 +1000 @@ -57,7 +57,7 @@ :c:func:`PyImport_ImportModule`. -.. c:function:: PyObject* PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) +.. c:function:: PyObject* PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) Import a module. This is best described by referring to the built-in Python function :func:`__import__`, as the standard :func:`__import__` function calls @@ -68,6 +68,13 @@ the return value when a submodule of a package was requested is normally the top-level package, unless a non-empty *fromlist* was given. + .. versionadded:: 3.3 + + +.. c:function:: PyObject* PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) + + Similar to :c:func:`PyImport_ImportModuleLevelObject`, but the name is an + UTF-8 encoded string instead of a Unicode object. .. c:function:: PyObject* PyImport_Import(PyObject *name) @@ -105,7 +112,7 @@ .. c:function:: PyObject* PyImport_AddModule(const char *name) - Similar to :c:func:`PyImport_AddModuleObject`, but the name is an UTF-8 + Similar to :c:func:`PyImport_AddModuleObject`, but the name is a UTF-8 encoded string instead of a Unicode object. @@ -217,7 +224,7 @@ For internal use only. -.. c:function:: int PyImport_ImportFrozenModule(char *name) +.. c:function:: int PyImport_ImportFrozenModuleObject(PyObject *name) Load a frozen module named *name*. Return ``1`` for success, ``0`` if the module is not found, and ``-1`` with an exception set if the initialization @@ -225,6 +232,14 @@ :c:func:`PyImport_ImportModule`. (Note the misnomer --- this function would reload the module if it was already imported.) + .. versionadded:: 3.3 + + +.. c:function:: int PyImport_ImportFrozenModule(char *name) + + Similar to :c:func:`PyImport_ImportFrozenModuleObject`, but the name is a + UTF-8 encoded string instead of a Unicode object. + .. c:type:: struct _frozen @@ -264,13 +279,13 @@ Structure describing a single entry in the list of built-in modules. Each of these structures gives the name and initialization function for a module built - into the interpreter. Programs which embed Python may use an array of these - structures in conjunction with :c:func:`PyImport_ExtendInittab` to provide - additional built-in modules. The structure is defined in - :file:`Include/import.h` as:: + into the interpreter. The name is an ASCII encoded string. Programs which + embed Python may use an array of these structures in conjunction with + :c:func:`PyImport_ExtendInittab` to provide additional built-in modules. + The structure is defined in :file:`Include/import.h` as:: struct _inittab { - char *name; + char *name; /* ASCII encoded string */ PyObject* (*initfunc)(void); }; diff -r 9dcd88977694 -r 650549138a3d Doc/c-api/init.rst --- a/Doc/c-api/init.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/c-api/init.rst Tue Mar 22 21:32:51 2011 +1000 @@ -537,9 +537,10 @@ operations such as ``PyEval_ReleaseThread(tstate)``. It is not needed before calling :c:func:`PyEval_SaveThread` or :c:func:`PyEval_RestoreThread`. - .. index:: single: Py_Initialize() + This is a no-op when called for a second time. - This is a no-op when called for a second time. + .. versionchanged:: 3.2 + This function cannot be called before :c:func:`Py_Initialize()` anymore. .. index:: module: _thread diff -r 9dcd88977694 -r 650549138a3d Doc/documenting/style.rst --- a/Doc/documenting/style.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/documenting/style.rst Tue Mar 22 21:32:51 2011 +1000 @@ -14,9 +14,10 @@ Use of whitespace ----------------- -All reST files use an indentation of 3 spaces. The maximum line length is 80 -characters for normal text, but tables, deeply indented code samples and long -links may extend beyond that. +All reST files use an indentation of 3 spaces; no tabs are allowed. The +maximum line length is 80 characters for normal text, but tables, deeply +indented code samples and long links may extend beyond that. Code example +bodies should use normal Python 4-space indentation. Make generous use of blank lines where applicable; they help grouping things together. diff -r 9dcd88977694 -r 650549138a3d Doc/includes/sqlite3/shared_cache.py --- a/Doc/includes/sqlite3/shared_cache.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/includes/sqlite3/shared_cache.py Tue Mar 22 21:32:51 2011 +1000 @@ -1,6 +1,6 @@ import sqlite3 # The shared cache is only available in SQLite versions 3.3.3 or later -# See the SQLite documentaton for details. +# See the SQLite documentation for details. sqlite3.enable_shared_cache(True) diff -r 9dcd88977694 -r 650549138a3d Doc/library/collections.rst --- a/Doc/library/collections.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/collections.rst Tue Mar 22 21:32:51 2011 +1000 @@ -706,7 +706,7 @@ :options: +NORMALIZE_WHITESPACE >>> # Basic example - >>> Point = namedtuple('Point', 'x y') + >>> Point = namedtuple('Point', ['x', 'y']) >>> p = Point(x=10, y=11) >>> # Example using the verbose option to print the class definition @@ -851,15 +851,15 @@ a fixed-width print format: >>> class Point(namedtuple('Point', 'x y')): - ... __slots__ = () - ... @property - ... def hypot(self): - ... return (self.x ** 2 + self.y ** 2) ** 0.5 - ... def __str__(self): - ... return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) + __slots__ = () + @property + def hypot(self): + return (self.x ** 2 + self.y ** 2) ** 0.5 + def __str__(self): + return 'Point: x=%6.3f y=%6.3f hypot=%6.3f' % (self.x, self.y, self.hypot) >>> for p in Point(3, 4), Point(14, 5/7): - ... print(p) + print(p) Point: x= 3.000 y= 4.000 hypot= 5.000 Point: x=14.000 y= 0.714 hypot=14.018 @@ -886,7 +886,7 @@ >>> Status.open, Status.pending, Status.closed (0, 1, 2) >>> class Status: - ... open, pending, closed = range(3) + open, pending, closed = range(3) .. seealso:: diff -r 9dcd88977694 -r 650549138a3d Doc/library/csv.rst --- a/Doc/library/csv.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/csv.rst Tue Mar 22 21:32:51 2011 +1000 @@ -11,15 +11,15 @@ pair: data; tabular The so-called CSV (Comma Separated Values) format is the most common import and -export format for spreadsheets and databases. There is no "CSV standard", so -the format is operationally defined by the many applications which read and -write it. The lack of a standard means that subtle differences often exist in -the data produced and consumed by different applications. These differences can -make it annoying to process CSV files from multiple sources. Still, while the -delimiters and quoting characters vary, the overall format is similar enough -that it is possible to write a single module which can efficiently manipulate -such data, hiding the details of reading and writing the data from the -programmer. +export format for spreadsheets and databases. CSV format was used for many +years prior to attempts to describe the format in a standardized way in +:rfc:`4180`. The lack of a well-defined standard means that subtle differences +often exist in the data produced and consumed by different applications. These +differences can make it annoying to process CSV files from multiple sources. +Still, while the delimiters and quoting characters vary, the overall format is +similar enough that it is possible to write a single module which can +efficiently manipulate such data, hiding the details of reading and writing the +data from the programmer. The :mod:`csv` module implements classes to read and write tabular data in CSV format. It allows programmers to say, "write this data in the format preferred @@ -52,7 +52,7 @@ *csvfile* can be any object which supports the :term:`iterator` protocol and returns a string each time its :meth:`!__next__` method is called --- :term:`file objects ` and list objects are both suitable. If *csvfile* is a file object, - it should be opened with ``newline=''``. [#]_ An optional + it should be opened with ``newline=''``. [1]_ An optional *dialect* parameter can be given which is used to define a set of parameters specific to a particular CSV dialect. It may be an instance of a subclass of the :class:`Dialect` class or one of the strings returned by the @@ -79,7 +79,8 @@ Return a writer object responsible for converting the user's data into delimited strings on the given file-like object. *csvfile* can be any object with a - :func:`write` method. An optional *dialect* + :func:`write` method. If *csvfile* is a file object, it should be opened with + ``newline=''`` [1]_. An optional *dialect* parameter can be given which is used to define a set of parameters specific to a particular CSV dialect. It may be an instance of a subclass of the :class:`Dialect` class or one of the strings returned by the @@ -96,7 +97,7 @@ A short usage example:: >>> import csv - >>> spamWriter = csv.writer(open('eggs.csv', 'w'), delimiter=' ', + >>> spamWriter = csv.writer(open('eggs.csv', 'w', newline=''), delimiter=' ', ... quotechar='|', quoting=csv.QUOTE_MINIMAL) >>> spamWriter.writerow(['Spam'] * 5 + ['Baked Beans']) >>> spamWriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam']) @@ -427,7 +428,7 @@ Reading a file with an alternate format:: import csv - with open('passwd') as f: + with open('passwd', newline='') as f: reader = csv.reader(f, delimiter=':', quoting=csv.QUOTE_NONE) for row in reader: print(row) @@ -435,7 +436,7 @@ The corresponding simplest possible writing example is:: import csv - with open('some.csv', 'w') as f: + with open('some.csv', 'w', newline='') as f: writer = csv.writer(f) writer.writerows(someiterable) @@ -457,7 +458,7 @@ import csv csv.register_dialect('unixpwd', delimiter=':', quoting=csv.QUOTE_NONE) - with open('passwd') as f: + with open('passwd', newline='') as f: reader = csv.reader(f, 'unixpwd') A slightly more advanced use of the reader --- catching and reporting errors:: @@ -482,7 +483,7 @@ .. rubric:: Footnotes -.. [#] If ``newline=''`` is not specified, newlines embedded inside quoted fields - will not be interpreted correctly. It should always be safe to specify - ``newline=''``, since the csv module does its own universal newline handling - on input. +.. [1] If ``newline=''`` is not specified, newlines embedded inside quoted fields + will not be interpreted correctly, and on platforms that use ``\r\n`` linendings + on write an extra ``\r`` will be added. It should always be safe to specify + ``newline=''``, since the csv module does its own (universal) newline handling. diff -r 9dcd88977694 -r 650549138a3d Doc/library/email.message.rst --- a/Doc/library/email.message.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/email.message.rst Tue Mar 22 21:32:51 2011 +1000 @@ -46,15 +46,16 @@ be generated or modified). Note that this method is provided as a convenience and may not always - format the message the way you want. For example, by default it mangles - lines that begin with ``From``. For more flexibility, instantiate a + format the message the way you want. For example, by default it does + not do the mangling of lines that begin with ``From`` that is + required by the unix mbox format. For more flexibility, instantiate a :class:`~email.generator.Generator` instance and use its :meth:`flatten` method directly. For example:: from io import StringIO from email.generator import Generator fp = StringIO() - g = Generator(fp, mangle_from_=False, maxheaderlen=60) + g = Generator(fp, mangle_from_=True, maxheaderlen=60) g.flatten(msg) text = fp.getvalue() @@ -138,15 +139,22 @@ string naming a character set, or ``None``. If it is a string, it will be converted to a :class:`~email.charset.Charset` instance. If *charset* is ``None``, the ``charset`` parameter will be removed from the - :mailheader:`Content-Type` header. Anything else will generate a - :exc:`TypeError`. + :mailheader:`Content-Type` header (the message will not be otherwise + modified). Anything else will generate a :exc:`TypeError`. - The message will be assumed to be of type :mimetype:`text/\*` encoded with - *charset.input_charset*. It will be converted to *charset.output_charset* - and encoded properly, if needed, when generating the plain text - representation of the message. MIME headers (:mailheader:`MIME-Version`, - :mailheader:`Content-Type`, :mailheader:`Content-Transfer-Encoding`) will - be added as needed. + If there is no existing :mailheader:`MIME-Version` header one will be + added. If there is no existing :mailheader:`Content-Type` header, one + will be added with a value of :mimetype:`text/plain`. Whether the + :mailheader:`Content-Type` header already exists or not, its ``charset`` + parameter will be set to *charset.output_charset*. If + *charset.input_charset* and *charset.output_charset* differ, the payload + will be re-encoded to the *output_charset*. If there is no existing + :mailheader:`Content-Transfer-Encoding` header, then the payload will be + transfer-encoded, if needed, using the specified + :class:`~email.charset.Charset`, and a header with the appropriate value + will be added. If a :mailheader:`Content-Transfer-Encoding` header + already exists, the payload is assumed to already be correctly encoded + using that :mailheader:`Content-Transfer-Encoding` and is not modified. .. method:: get_charset() diff -r 9dcd88977694 -r 650549138a3d Doc/library/html.parser.rst --- a/Doc/library/html.parser.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/html.parser.rst Tue Mar 22 21:32:51 2011 +1000 @@ -154,7 +154,7 @@ Method called when an unrecognized SGML declaration is read by the parser. The *data* parameter will be the entire contents of the declaration inside - the ```` markup. It is sometimes useful to be be overridden by a + the ```` markup. It is sometimes useful to be overridden by a derived class; the base class implementation raises an :exc:`HTMLParseError`. diff -r 9dcd88977694 -r 650549138a3d Doc/library/inspect.rst --- a/Doc/library/inspect.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/inspect.rst Tue Mar 22 21:32:51 2011 +1000 @@ -589,14 +589,11 @@ that raise AttributeError). It can also return descriptors objects instead of instance members. + If the instance `__dict__` is shadowed by another member (for example a + property) then this function will be unable to find instance members. + .. versionadded:: 3.2 -The only known case that can cause `getattr_static` to trigger code execution, -and cause it to return incorrect results (or even break), is where a class uses -:data:`~object.__slots__` and provides a `__dict__` member using a property or -descriptor. If you find other cases please report them so they can be fixed -or documented. - `getattr_static` does not resolve descriptors, for example slot descriptors or getset descriptors on objects implemented in C. The descriptor object is returned instead of the underlying attribute. diff -r 9dcd88977694 -r 650549138a3d Doc/library/os.rst --- a/Doc/library/os.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/os.rst Tue Mar 22 21:32:51 2011 +1000 @@ -743,6 +743,17 @@ .. versionadded:: 3.3 +.. function:: fexecve(fd, args, env) + + Execute the program specified by a file descriptor *fd* with arguments given + by *args* and environment given by *env*, replacing the current process. + *args* and *env* are given as in :func:`execve`. + + Availability: Unix. + + .. versionadded:: 3.3 + + .. function:: fpathconf(fd, name) Return system configuration information relevant to an open file. *name* @@ -819,6 +830,45 @@ .. versionadded:: 3.3 +.. function:: futimens(fd, (atime_sec, atime_nsec), (mtime_sec, mtime_nsec)) + futimens(fd, None, None) + + Updates the timestamps of a file specified by the file descriptor *fd*, with + nanosecond precision. + The second form sets *atime* and *mtime* to the current time. + If *atime_nsec* or *mtime_nsec* is specified as :data:`UTIME_NOW`, the corresponding + timestamp is updated to the current time. + If *atime_nsec* or *mtime_nsec* is specified as :data:`UTIME_OMIT`, the corresponding + timestamp is not updated. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. data:: UTIME_NOW + UTIME_OMIT + + Flags used with :func:`futimens` to specify that the timestamp must be + updated either to the current time or not updated at all. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. function:: futimes(fd, (atime, mtime)) + futimes(fd, None) + + Set the access and modified time of the file specified by the file + descriptor *fd* to the given values. If the second form is used, set the + access and modified times to the current time. + + Availability: Unix. + + .. versionadded:: 3.3 + + .. function:: isatty(fd) Return ``True`` if the file descriptor *fd* is open and connected to a @@ -841,6 +891,30 @@ .. versionadded:: 3.3 +.. function:: lockf(fd, cmd, len) + + Apply, test or remove a POSIX lock on an open file descriptor. + *fd* is an open file descriptor. + *cmd* specifies the command to use - one of :data:`F_LOCK`, :data:`F_TLOCK`, + :data:`F_ULOCK` or :data:`F_TEST`. + *len* specifies the section of the file to lock. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. data:: F_LOCK + F_TLOCK + F_ULOCK + F_TEST + + Flags that specify what action :func:`lockf` will take. + + Availability: Unix. + + .. versionadded:: 3.3 + .. function:: lseek(fd, pos, how) Set the current position of file descriptor *fd* to position *pos*, modified @@ -945,6 +1019,66 @@ Availability: Unix, Windows. +.. function:: posix_fallocate(fd, offset, len) + + Ensures that enough disk space is allocated for the file specified by *fd* + starting from *offset* and continuing for *len* bytes. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. function:: posix_fadvise(fd, offset, len, advice) + + Announces an intention to access data in a specific pattern thus allowing + the kernel to make optimizations. + The advice applies to the region of the file specified by *fd* starting at + *offset* and continuing for *len* bytes. + *advice* is one of :data:`POSIX_FADV_NORMAL`, :data:`POSIX_FADV_SEQUENTIAL`, + :data:`POSIX_FADV_RANDOM`, :data:`POSIX_FADV_NOREUSE`, + :data:`POSIX_FADV_WILLNEED` or :data:`POSIX_FADV_DONTNEED`. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. data:: POSIX_FADV_NORMAL + POSIX_FADV_SEQUENTIAL + POSIX_FADV_RANDOM + POSIX_FADV_NOREUSE + POSIX_FADV_WILLNEED + POSIX_FADV_DONTNEED + + Flags that can be used in *advice* in :func:`posix_fadvise` that specify + the access pattern that is likely to be used. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. function:: pread(fd, buffersize, offset) + + Read from a file descriptor, *fd*, at a position of *offset*. It will read up + to *buffersize* number of bytes. The file offset remains unchanged. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. function:: pwrite(fd, string, offset) + + Write *string* to a file descriptor, *fd*, from *offset*, leaving the file + offset unchanged. + + Availability: Unix. + + .. versionadded:: 3.3 + + .. function:: read(fd, n) Read at most *n* bytes from file descriptor *fd*. Return a bytestring containing the @@ -1038,6 +1172,17 @@ .. versionadded:: 3.3 +.. function:: readv(fd, buffers) + + Read from a file descriptor into a number of writable buffers. *buffers* is + an arbitrary sequence of writable buffers. Returns the total number of bytes + read. + + Availability: Unix. + + .. versionadded:: 3.3 + + .. function:: tcgetpgrp(fd) Return the process group associated with the terminal given by *fd* (an open @@ -1111,6 +1256,17 @@ :meth:`~file.write` method. +.. function:: writev(fd, buffers) + + Write the the contents of *buffers* to file descriptor *fd*, where *buffers* + is an arbitrary sequence of buffers. + Returns the total number of bytes written. + + Availability: Unix. + + .. versionadded:: 3.3 + + .. _open-constants: ``open()`` flag constants @@ -1384,6 +1540,17 @@ Added support for Windows 6.0 (Vista) symbolic links. +.. function:: lutimes(path, (atime, mtime)) + lutimes(path, None) + + Like :func:`utime`, but if *path* is a symbolic link, it is not + dereferenced. + + Availability: Unix. + + .. versionadded:: 3.3 + + .. function:: mkfifo(path[, mode]) Create a FIFO (a named pipe) named *path* with numeric mode *mode*. The @@ -1727,6 +1894,25 @@ Added support for Windows 6.0 (Vista) symbolic links. +.. function:: sync() + + Force write of everything to disk. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. function:: truncate(path, length) + + Truncate the file corresponding to *path*, so that it is at most + *length* bytes in size. + + Availability: Unix. + + .. versionadded:: 3.3 + + .. function:: unlink(path) Remove (delete) the file *path*. This is the same function as @@ -2306,6 +2492,58 @@ Availability: Unix. +.. function:: waitid(idtype, id, options) + + Wait for the completion of one or more child processes. + *idtype* can be :data:`P_PID`, :data:`P_PGID` or :data:`P_ALL`. + *id* specifies the pid to wait on. + *options* is constructed from the ORing of one or more of :data:`WEXITED`, + :data:`WSTOPPED` or :data:`WCONTINUED` and additionally may be ORed with + :data:`WNOHANG` or :data:`WNOWAIT`. The return value is an object + representing the data contained in the :c:type:`siginfo_t` structure, namely: + :attr:`si_pid`, :attr:`si_uid`, :attr:`si_signo`, :attr:`si_status`, + :attr:`si_code` or ``None`` if :data:`WNOHANG` is specified and there are no + children in a waitable state. + + Availability: Unix. + + .. versionadded:: 3.3 + +.. data:: P_PID + P_PGID + P_ALL + + These are the possible values for *idtype* in :func:`waitid`. They affect + how *id* is interpreted. + + Availability: Unix. + + .. versionadded:: 3.3 + +.. data:: WEXITED + WSTOPPED + WNOWAIT + + Flags that can be used in *options* in :func:`waitid` that specify what + child signal to wait for. + + Availability: Unix. + + .. versionadded:: 3.3 + + +.. data:: CLD_EXITED + CLD_DUMPED + CLD_TRAPPED + CLD_CONTINUED + + These are the possible values for :attr:`si_code` in the result returned by + :func:`waitid`. + + Availability: Unix. + + .. versionadded:: 3.3 + .. function:: waitpid(pid, options) diff -r 9dcd88977694 -r 650549138a3d Doc/library/platform.rst --- a/Doc/library/platform.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/platform.rst Tue Mar 22 21:32:51 2011 +1000 @@ -208,7 +208,7 @@ Win95/98 specific ^^^^^^^^^^^^^^^^^ -.. function:: popen(cmd, mode='r', bufsize=None) +.. function:: popen(cmd, mode='r', bufsize=-1) Portable :func:`popen` interface. Find a working popen implementation preferring :func:`win32pipe.popen`. On Windows NT, :func:`win32pipe.popen` diff -r 9dcd88977694 -r 650549138a3d Doc/library/smtplib.rst --- a/Doc/library/smtplib.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/smtplib.rst Tue Mar 22 21:32:51 2011 +1000 @@ -34,6 +34,20 @@ For normal use, you should only require the initialization/connect, :meth:`sendmail`, and :meth:`quit` methods. An example is included below. + The :class:`SMTP` class supports the :keyword:`with` statement. When used + like this, the SMTP ``QUIT`` command is issued automatically when the + :keyword:`with` statement exits. E.g.:: + + >>> from smtplib import SMTP + >>> with SMTP("domain.org") as smtp: + ... smtp.noop() + ... + (250, b'Ok') + >>> + + .. versionadded:: 3.3 + Support for the :keyword:`with` statement was added. + .. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout]) diff -r 9dcd88977694 -r 650549138a3d Doc/library/subprocess.rst --- a/Doc/library/subprocess.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/subprocess.rst Tue Mar 22 21:32:51 2011 +1000 @@ -125,12 +125,14 @@ *stdin*, *stdout* and *stderr* specify the executed programs' standard input, standard output and standard error file handles, respectively. Valid values - are :data:`PIPE`, an existing file descriptor (a positive integer), an - existing :term:`file object`, and ``None``. :data:`PIPE` indicates that a - new pipe to the child should be created. With ``None``, no redirection will - occur; the child's file handles will be inherited from the parent. Additionally, - *stderr* can be :data:`STDOUT`, which indicates that the stderr data from the - applications should be captured into the same file handle as for stdout. + are :data:`PIPE`, :data:`DEVNULL`, an existing file descriptor (a positive + integer), an existing :term:`file object`, and ``None``. :data:`PIPE` + indicates that a new pipe to the child should be created. :data:`DEVNULL` + indicates that the special file :data:`os.devnull` will be used. With ``None``, + no redirection will occur; the child's file handles will be inherited from + the parent. Additionally, *stderr* can be :data:`STDOUT`, which indicates + that the stderr data from the applications should be captured into the same + file handle as for stdout. If *preexec_fn* is set to a callable object, this object will be called in the child process just before the child is executed. @@ -229,6 +231,15 @@ Added context manager support. +.. data:: DEVNULL + + Special value that can be used as the *stdin*, *stdout* or *stderr* argument + to :class:`Popen` and indicates that the special file :data:`os.devnull` + will be used. + + .. versionadded:: 3.3 + + .. data:: PIPE Special value that can be used as the *stdin*, *stdout* or *stderr* argument @@ -387,7 +398,7 @@ :func:`call` and :meth:`Popen.communicate` will raise :exc:`TimeoutExpired` if the timeout expires before the process exits. -Exceptions defined in this module all inherit from :ext:`SubprocessError`. +Exceptions defined in this module all inherit from :exc:`SubprocessError`. .. versionadded:: 3.3 The :exc:`SubprocessError` base class was added. diff -r 9dcd88977694 -r 650549138a3d Doc/library/urllib.request.rst --- a/Doc/library/urllib.request.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/library/urllib.request.rst Tue Mar 22 21:32:51 2011 +1000 @@ -1282,7 +1282,7 @@ you try to fetch a file whose read permissions make it inaccessible; the FTP code will try to read it, fail with a 550 error, and then perform a directory listing for the unreadable file. If fine-grained control is needed, consider - using the :mod:`ftplib` module, subclassing :class:`FancyURLOpener`, or changing + using the :mod:`ftplib` module, subclassing :class:`FancyURLopener`, or changing *_urlopener* to meet your needs. diff -r 9dcd88977694 -r 650549138a3d Doc/whatsnew/3.2.rst --- a/Doc/whatsnew/3.2.rst Tue Mar 22 21:31:46 2011 +1000 +++ b/Doc/whatsnew/3.2.rst Tue Mar 22 21:32:51 2011 +1000 @@ -2698,3 +2698,7 @@ a new function, :func:`asyncore.handle_accepted`, was added to replace it. (Contributed by Giampaolo Rodola in :issue:`6706`.) + +* Due to the new :term:`GIL` implementation, :c:func:`PyEval_InitThreads()` + cannot be called before :c:func:`Py_Initialize()` anymore. + diff -r 9dcd88977694 -r 650549138a3d Include/abstract.h --- a/Include/abstract.h Tue Mar 22 21:31:46 2011 +1000 +++ b/Include/abstract.h Tue Mar 22 21:32:51 2011 +1000 @@ -468,7 +468,7 @@ arbitrary data. 0 is returned on success. buffer and buffer_len are only - set in case no error occurrs. Otherwise, -1 is returned and + set in case no error occurs. Otherwise, -1 is returned and an exception set. */ @@ -482,7 +482,7 @@ writable memory location in buffer of size buffer_len. 0 is returned on success. buffer and buffer_len are only - set in case no error occurrs. Otherwise, -1 is returned and + set in case no error occurs. Otherwise, -1 is returned and an exception set. */ diff -r 9dcd88977694 -r 650549138a3d Include/import.h --- a/Include/import.h Tue Mar 22 21:31:46 2011 +1000 +++ b/Include/import.h Tue Mar 22 21:32:51 2011 +1000 @@ -50,6 +50,13 @@ PyObject *fromlist, int level ); +PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevelObject( + PyObject *name, + PyObject *globals, + PyObject *locals, + PyObject *fromlist, + int level + ); #define PyImport_ImportModuleEx(n, g, l, f) \ PyImport_ImportModuleLevel(n, g, l, f, -1) @@ -58,6 +65,9 @@ PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name); PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m); PyAPI_FUNC(void) PyImport_Cleanup(void); +PyAPI_FUNC(int) PyImport_ImportFrozenModuleObject( + PyObject *name + ); PyAPI_FUNC(int) PyImport_ImportFrozenModule( char *name /* UTF-8 encoded string */ ); @@ -76,15 +86,15 @@ PyAPI_FUNC(PyObject *)_PyImport_FindBuiltin( const char *name /* UTF-8 encoded string */ ); -PyAPI_FUNC(PyObject *)_PyImport_FindExtensionUnicode(const char *, PyObject *); +PyAPI_FUNC(PyObject *)_PyImport_FindExtensionObject(PyObject *, PyObject *); PyAPI_FUNC(int)_PyImport_FixupBuiltin( PyObject *mod, char *name /* UTF-8 encoded string */ ); -PyAPI_FUNC(int)_PyImport_FixupExtensionUnicode(PyObject*, char *, PyObject *); +PyAPI_FUNC(int)_PyImport_FixupExtensionObject(PyObject*, PyObject *, PyObject *); struct _inittab { - char *name; + char *name; /* ASCII encoded string */ PyObject* (*initfunc)(void); }; PyAPI_DATA(struct _inittab *) PyImport_Inittab; diff -r 9dcd88977694 -r 650549138a3d Include/patchlevel.h --- a/Include/patchlevel.h Tue Mar 22 21:31:46 2011 +1000 +++ b/Include/patchlevel.h Tue Mar 22 21:32:51 2011 +1000 @@ -26,9 +26,6 @@ #define PY_VERSION "3.3a0" /*--end constants--*/ -/* Subversion Revision number of this file (not of the repository) */ -#define PY_PATCHLEVEL_REVISION "$Revision$" - /* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2. Use this for numeric comparisons, e.g. #if PY_VERSION_HEX >= ... */ #define PY_VERSION_HEX ((PY_MAJOR_VERSION << 24) | \ diff -r 9dcd88977694 -r 650549138a3d Include/pyerrors.h --- a/Include/pyerrors.h Tue Mar 22 21:31:46 2011 +1000 +++ b/Include/pyerrors.h Tue Mar 22 21:32:51 2011 +1000 @@ -208,8 +208,6 @@ ); #ifdef MS_WINDOWS -PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilenameObject( - int, const char *); PyAPI_FUNC(PyObject *) PyErr_SetFromWindowsErrWithFilename( int ierr, const char *filename /* decoded from the filesystem encoding */ diff -r 9dcd88977694 -r 650549138a3d Include/pymacconfig.h --- a/Include/pymacconfig.h Tue Mar 22 21:31:46 2011 +1000 +++ b/Include/pymacconfig.h Tue Mar 22 21:32:51 2011 +1000 @@ -61,7 +61,7 @@ # endif # if defined(__LP64__) - /* MacOSX 10.4 (the first release to suppport 64-bit code + /* MacOSX 10.4 (the first release to support 64-bit code * at all) only supports 64-bit in the UNIX layer. * Therefore surpress the toolbox-glue in 64-bit mode. */ diff -r 9dcd88977694 -r 650549138a3d Lib/binhex.py --- a/Lib/binhex.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/binhex.py Tue Mar 22 21:32:51 2011 +1000 @@ -52,14 +52,13 @@ def getfileinfo(name): finfo = FInfo() - fp = io.open(name, 'rb') - # Quick check for textfile - data = fp.read(512) - if 0 not in data: - finfo.Type = 'TEXT' - fp.seek(0, 2) - dsize = fp.tell() - fp.close() + with io.open(name, 'rb') as fp: + # Quick check for textfile + data = fp.read(512) + if 0 not in data: + finfo.Type = 'TEXT' + fp.seek(0, 2) + dsize = fp.tell() dir, file = os.path.split(name) file = file.replace(':', '-', 1) return file, finfo, dsize, 0 @@ -140,19 +139,26 @@ class BinHex: def __init__(self, name_finfo_dlen_rlen, ofp): name, finfo, dlen, rlen = name_finfo_dlen_rlen + close_on_error = False if isinstance(ofp, str): ofname = ofp ofp = io.open(ofname, 'wb') - ofp.write(b'(This file must be converted with BinHex 4.0)\r\r:') - hqxer = _Hqxcoderengine(ofp) - self.ofp = _Rlecoderengine(hqxer) - self.crc = 0 - if finfo is None: - finfo = FInfo() - self.dlen = dlen - self.rlen = rlen - self._writeinfo(name, finfo) - self.state = _DID_HEADER + close_on_error = True + try: + ofp.write(b'(This file must be converted with BinHex 4.0)\r\r:') + hqxer = _Hqxcoderengine(ofp) + self.ofp = _Rlecoderengine(hqxer) + self.crc = 0 + if finfo is None: + finfo = FInfo() + self.dlen = dlen + self.rlen = rlen + self._writeinfo(name, finfo) + self.state = _DID_HEADER + except: + if close_on_error: + ofp.close() + raise def _writeinfo(self, name, finfo): nl = len(name) diff -r 9dcd88977694 -r 650549138a3d Lib/concurrent/futures/process.py --- a/Lib/concurrent/futures/process.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/concurrent/futures/process.py Tue Mar 22 21:32:51 2011 +1000 @@ -66,28 +66,14 @@ # workers to exit when their work queues are empty and then waits until the # threads/processes finish. -_thread_references = set() +_live_threads = weakref.WeakSet() _shutdown = False def _python_exit(): global _shutdown _shutdown = True - for thread_reference in _thread_references: - thread = thread_reference() - if thread is not None: - thread.join() - -def _remove_dead_thread_references(): - """Remove inactive threads from _thread_references. - - Should be called periodically to prevent memory leaks in scenarios such as: - >>> while True: - >>> ... t = ThreadPoolExecutor(max_workers=5) - >>> ... t.map(int, ['1', '2', '3', '4', '5']) - """ - for thread_reference in set(_thread_references): - if thread_reference() is None: - _thread_references.discard(thread_reference) + for thread in _live_threads: + thread.join() # Controls how many more calls than processes will be queued in the call queue. # A smaller number will mean that processes spend more time idle waiting for @@ -279,7 +265,6 @@ worker processes will be created as the machine has processors. """ _check_system_limits() - _remove_dead_thread_references() if max_workers is None: self._max_workers = multiprocessing.cpu_count() @@ -316,7 +301,7 @@ self._shutdown_process_event)) self._queue_management_thread.daemon = True self._queue_management_thread.start() - _thread_references.add(weakref.ref(self._queue_management_thread)) + _live_threads.add(self._queue_management_thread) def _adjust_process_count(self): for _ in range(len(self._processes), self._max_workers): diff -r 9dcd88977694 -r 650549138a3d Lib/concurrent/futures/thread.py --- a/Lib/concurrent/futures/thread.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/concurrent/futures/thread.py Tue Mar 22 21:32:51 2011 +1000 @@ -25,29 +25,14 @@ # workers to exit when their work queues are empty and then waits until the # threads finish. -_thread_references = set() +_live_threads = weakref.WeakSet() _shutdown = False def _python_exit(): global _shutdown _shutdown = True - for thread_reference in _thread_references: - thread = thread_reference() - if thread is not None: - thread.join() - -def _remove_dead_thread_references(): - """Remove inactive threads from _thread_references. - - Should be called periodically to prevent memory leaks in scenarios such as: - >>> while True: - ... t = ThreadPoolExecutor(max_workers=5) - ... t.map(int, ['1', '2', '3', '4', '5']) - """ - for thread_reference in set(_thread_references): - if thread_reference() is None: - _thread_references.discard(thread_reference) - + for thread in _live_threads: + thread.join() atexit.register(_python_exit) class _WorkItem(object): @@ -95,8 +80,6 @@ max_workers: The maximum number of threads that can be used to execute the given calls. """ - _remove_dead_thread_references() - self._max_workers = max_workers self._work_queue = queue.Queue() self._threads = set() @@ -125,7 +108,7 @@ t.daemon = True t.start() self._threads.add(t) - _thread_references.add(weakref.ref(t)) + _live_threads.add(t) def shutdown(self, wait=True): with self._shutdown_lock: diff -r 9dcd88977694 -r 650549138a3d Lib/csv.py --- a/Lib/csv.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/csv.py Tue Mar 22 21:32:51 2011 +1000 @@ -284,7 +284,7 @@ an all or nothing approach, so we allow for small variations in this number. 1) build a table of the frequency of each character on every line. - 2) build a table of freqencies of this frequency (meta-frequency?), + 2) build a table of frequencies of this frequency (meta-frequency?), e.g. 'x occurred 5 times in 10 rows, 6 times in 1000 rows, 7 times in 2 rows' 3) use the mode of the meta-frequency to determine the /expected/ diff -r 9dcd88977694 -r 650549138a3d Lib/ctypes/test/test_arrays.py --- a/Lib/ctypes/test/test_arrays.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/ctypes/test/test_arrays.py Tue Mar 22 21:32:51 2011 +1000 @@ -37,7 +37,7 @@ values = [ia[i] for i in range(len(init))] self.assertEqual(values, [0] * len(init)) - # Too many in itializers should be caught + # Too many initializers should be caught self.assertRaises(IndexError, int_array, *range(alen*2)) CharArray = ARRAY(c_char, 3) diff -r 9dcd88977694 -r 650549138a3d Lib/ctypes/test/test_init.py --- a/Lib/ctypes/test/test_init.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/ctypes/test/test_init.py Tue Mar 22 21:32:51 2011 +1000 @@ -27,7 +27,7 @@ self.assertEqual((y.x.a, y.x.b), (0, 0)) self.assertEqual(y.x.new_was_called, False) - # But explicitely creating an X structure calls __new__ and __init__, of course. + # But explicitly creating an X structure calls __new__ and __init__, of course. x = X() self.assertEqual((x.a, x.b), (9, 12)) self.assertEqual(x.new_was_called, True) diff -r 9dcd88977694 -r 650549138a3d Lib/ctypes/test/test_numbers.py --- a/Lib/ctypes/test/test_numbers.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/ctypes/test/test_numbers.py Tue Mar 22 21:32:51 2011 +1000 @@ -157,7 +157,7 @@ def test_int_from_address(self): from array import array for t in signed_types + unsigned_types: - # the array module doesn't suppport all format codes + # the array module doesn't support all format codes # (no 'q' or 'Q') try: array(t._type_) diff -r 9dcd88977694 -r 650549138a3d Lib/ctypes/test/test_win32.py --- a/Lib/ctypes/test/test_win32.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/ctypes/test/test_win32.py Tue Mar 22 21:32:51 2011 +1000 @@ -17,7 +17,7 @@ # ValueError: Procedure probably called with not enough arguments (4 bytes missing) self.assertRaises(ValueError, IsWindow) - # This one should succeeed... + # This one should succeed... self.assertEqual(0, IsWindow(0)) # ValueError: Procedure probably called with too many arguments (8 bytes in excess) diff -r 9dcd88977694 -r 650549138a3d Lib/difflib.py --- a/Lib/difflib.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/difflib.py Tue Mar 22 21:32:51 2011 +1000 @@ -1719,7 +1719,7 @@ line = line.replace(' ','\0') # expand tabs into spaces line = line.expandtabs(self._tabsize) - # relace spaces from expanded tabs back into tab characters + # replace spaces from expanded tabs back into tab characters # (we'll replace them with markup after we do differencing) line = line.replace(' ','\t') return line.replace('\0',' ').rstrip('\n') diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/archive_util.py --- a/Lib/distutils/archive_util.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/archive_util.py Tue Mar 22 21:32:51 2011 +1000 @@ -9,6 +9,12 @@ from warnings import warn import sys +try: + import zipfile +except ImportError: + zipfile = None + + from distutils.errors import DistutilsExecError from distutils.spawn import spawn from distutils.dir_util import mkpath @@ -74,11 +80,6 @@ available, raises DistutilsExecError. Returns the name of the output zip file. """ - try: - import zipfile - except ImportError: - zipfile = None - zip_filename = base_name + ".zip" mkpath(os.path.dirname(zip_filename), dry_run=dry_run) @@ -105,8 +106,12 @@ zip_filename, base_dir) if not dry_run: - zip = zipfile.ZipFile(zip_filename, "w", - compression=zipfile.ZIP_DEFLATED) + try: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_DEFLATED) + except RuntimeError: + zip = zipfile.ZipFile(zip_filename, "w", + compression=zipfile.ZIP_STORED) for dirpath, dirnames, filenames in os.walk(base_dir): for name in filenames: diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/cmd.py --- a/Lib/distutils/cmd.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/cmd.py Tue Mar 22 21:32:51 2011 +1000 @@ -359,7 +359,7 @@ not self.force, dry_run=self.dry_run) def move_file (self, src, dst, level=1): - """Move a file respectin dry-run flag.""" + """Move a file respecting dry-run flag.""" return file_util.move_file(src, dst, dry_run=self.dry_run) def spawn(self, cmd, search_path=1, level=1): diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/cygwinccompiler.py --- a/Lib/distutils/cygwinccompiler.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/cygwinccompiler.py Tue Mar 22 21:32:51 2011 +1000 @@ -155,7 +155,7 @@ self.dll_libraries = get_msvcr() def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts): - """Compiles the source by spawing GCC and windres if needed.""" + """Compiles the source by spawning GCC and windres if needed.""" if ext == '.rc' or ext == '.res': # gcc needs '.res' and '.rc' compiled to object files !!! try: diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/tests/test_archive_util.py --- a/Lib/distutils/tests/test_archive_util.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/tests/test_archive_util.py Tue Mar 22 21:32:51 2011 +1000 @@ -7,12 +7,13 @@ from os.path import splitdrive import warnings +from distutils import archive_util from distutils.archive_util import (check_archive_formats, make_tarball, make_zipfile, make_archive, ARCHIVE_FORMATS) from distutils.spawn import find_executable, spawn from distutils.tests import support -from test.support import check_warnings, run_unittest +from test.support import check_warnings, run_unittest, patch try: import zipfile @@ -20,10 +21,18 @@ except ImportError: ZIP_SUPPORT = find_executable('zip') +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase): + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_make_tarball(self): # creating something to tar tmpdir = self.mkdtemp() @@ -84,8 +93,9 @@ base_name = os.path.join(tmpdir2, 'archive') return tmpdir, tmpdir2, base_name - @unittest.skipUnless(find_executable('tar') and find_executable('gzip'), - 'Need the tar command to run') + @unittest.skipUnless(find_executable('tar') and find_executable('gzip') + and ZLIB_SUPPORT, + 'Need the tar, gzip and zlib command to run') def test_tarfile_vs_tar(self): tmpdir, tmpdir2, base_name = self._create_files() old_dir = os.getcwd() @@ -169,7 +179,8 @@ self.assertTrue(not os.path.exists(tarball)) self.assertEqual(len(w.warnings), 1) - @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') + @unittest.skipUnless(ZIP_SUPPORT and ZLIB_SUPPORT, + 'Need zip and zlib support to run') def test_make_zipfile(self): # creating something to tar tmpdir = self.mkdtemp() @@ -182,6 +193,29 @@ # check if the compressed tarball was created tarball = base_name + '.zip' + self.assertTrue(os.path.exists(tarball)) + + @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run') + def test_make_zipfile_no_zlib(self): + patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError + + called = [] + zipfile_class = zipfile.ZipFile + def fake_zipfile(*a, **kw): + if kw.get('compression', None) == zipfile.ZIP_STORED: + called.append((a, kw)) + return zipfile_class(*a, **kw) + + patch(self, archive_util.zipfile, 'ZipFile', fake_zipfile) + + # create something to tar and compress + tmpdir, tmpdir2, base_name = self._create_files() + make_zipfile(base_name, tmpdir) + + tarball = base_name + '.zip' + self.assertEqual(called, + [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]) + self.assertTrue(os.path.exists(tarball)) def test_check_archive_formats(self): self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/tests/test_bdist_dumb.py --- a/Lib/distutils/tests/test_bdist_dumb.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/tests/test_bdist_dumb.py Tue Mar 22 21:32:51 2011 +1000 @@ -18,6 +18,13 @@ """ +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + class BuildDumbTestCase(support.TempdirManager, support.LoggingSilencer, support.EnvironGuard, @@ -34,6 +41,7 @@ sys.argv[:] = self.old_sys_argv[1] super(BuildDumbTestCase, self).tearDown() + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_simple_built(self): # let's create a simple package diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/tests/test_bdist_rpm.py --- a/Lib/distutils/tests/test_bdist_rpm.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/tests/test_bdist_rpm.py Tue Mar 22 21:32:51 2011 +1000 @@ -28,6 +28,11 @@ unittest.TestCase): def setUp(self): + try: + sys.executable.encode("UTF-8") + except UnicodeEncodeError: + raise unittest.SkipTest("sys.executable is not encodable to UTF-8") + super(BuildRpmTestCase, self).setUp() self.old_location = os.getcwd() self.old_sys_argv = sys.argv, sys.argv[:] diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/tests/test_clean.py --- a/Lib/distutils/tests/test_clean.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/tests/test_clean.py Tue Mar 22 21:32:51 2011 +1000 @@ -39,7 +39,7 @@ self.assertTrue(not os.path.exists(path), '%s was not removed' % path) - # let's run the command again (should spit warnings but suceed) + # let's run the command again (should spit warnings but succeed) cmd.all = 1 cmd.ensure_finalized() cmd.run() diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/tests/test_install.py --- a/Lib/distutils/tests/test_install.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/tests/test_install.py Tue Mar 22 21:32:51 2011 +1000 @@ -62,7 +62,7 @@ if sys.version < '2.6': return - # preparing the environement for the test + # preparing the environment for the test self.old_user_base = site.USER_BASE self.old_user_site = site.USER_SITE self.tmpdir = self.mkdtemp() diff -r 9dcd88977694 -r 650549138a3d Lib/distutils/tests/test_sdist.py --- a/Lib/distutils/tests/test_sdist.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/distutils/tests/test_sdist.py Tue Mar 22 21:32:51 2011 +1000 @@ -40,6 +40,13 @@ somecode%(sep)sdoc.txt """ +try: + import zlib + ZLIB_SUPPORT = True +except ImportError: + ZLIB_SUPPORT = False + + class SDistTestCase(PyPIRCCommandTestCase): def setUp(self): @@ -78,6 +85,7 @@ cmd.warn = _warn return dist, cmd + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_prune_file_list(self): # this test creates a package with some vcs dirs in it # and launch sdist to make sure they get pruned @@ -119,6 +127,7 @@ # making sure everything has been pruned correctly self.assertEqual(len(content), 4) + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_make_distribution(self): # check if tar and gzip are installed @@ -153,6 +162,7 @@ result.sort() self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_add_defaults(self): # http://bugs.python.org/issue2279 @@ -218,6 +228,7 @@ finally: f.close() + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_metadata_check_option(self): # testing the `medata-check` option dist, cmd = self.get_cmd(metadata={}) @@ -277,7 +288,7 @@ cmd.formats = 'supazipa' self.assertRaises(DistutilsOptionError, cmd.finalize_options) - + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_get_file_list(self): # make sure MANIFEST is recalculated dist, cmd = self.get_cmd() @@ -300,7 +311,7 @@ # adding a file self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#') - # make sure build_py is reinitinialized, like a fresh run + # make sure build_py is reinitialized, like a fresh run build_py = dist.get_command_obj('build_py') build_py.finalized = False build_py.ensure_finalized() @@ -318,6 +329,7 @@ self.assertEqual(len(manifest2), 6) self.assertIn('doc2.txt', manifest2[-1]) + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_manifest_marker(self): # check that autogenerated MANIFESTs have a marker dist, cmd = self.get_cmd() @@ -334,6 +346,7 @@ self.assertEqual(manifest[0], '# file GENERATED by distutils, do NOT edit') + @unittest.skipUnless(ZLIB_SUPPORT, 'Need zlib support to run') def test_manual_manifest(self): # check that a MANIFEST without a marker is left alone dist, cmd = self.get_cmd() diff -r 9dcd88977694 -r 650549138a3d Lib/doctest.py --- a/Lib/doctest.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/doctest.py Tue Mar 22 21:32:51 2011 +1000 @@ -1211,7 +1211,7 @@ # Process each example. for examplenum, example in enumerate(test.examples): - # If REPORT_ONLY_FIRST_FAILURE is set, then supress + # If REPORT_ONLY_FIRST_FAILURE is set, then suppress # reporting after the first failure. quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and failures > 0) @@ -2135,7 +2135,7 @@ caller can catch the errors and initiate post-mortem debugging. The DocTestCase provides a debug method that raises - UnexpectedException errors if there is an unexepcted + UnexpectedException errors if there is an unexpected exception: >>> test = DocTestParser().get_doctest('>>> raise KeyError\n42', diff -r 9dcd88977694 -r 650549138a3d Lib/email/charset.py --- a/Lib/email/charset.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/email/charset.py Tue Mar 22 21:32:51 2011 +1000 @@ -263,7 +263,7 @@ Returns "quoted-printable" if self.body_encoding is QP. Returns "base64" if self.body_encoding is BASE64. - Returns "7bit" otherwise. + Returns conversion function otherwise. """ assert self.body_encoding != SHORTEST if self.body_encoding == QP: @@ -381,7 +381,10 @@ """Body-encode a string by converting it first to bytes. The type of encoding (base64 or quoted-printable) will be based on - self.body_encoding. + self.body_encoding. If body_encoding is None, we assume the + output charset is a 7bit encoding, so re-encoding the decoded + string using the ascii codec produces the correct string version + of the content. """ # 7bit/8bit encodings return the string unchanged (module conversions) if self.body_encoding is BASE64: @@ -391,4 +394,6 @@ elif self.body_encoding is QP: return email.quoprimime.body_encode(string) else: + if isinstance(string, str): + string = string.encode(self.output_charset).decode('ascii') return string diff -r 9dcd88977694 -r 650549138a3d Lib/email/encoders.py --- a/Lib/email/encoders.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/email/encoders.py Tue Mar 22 21:32:51 2011 +1000 @@ -12,7 +12,7 @@ ] -from base64 import b64encode as _bencode +from base64 import encodebytes as _bencode from quopri import encodestring as _encodestring @@ -54,10 +54,13 @@ # There's no payload. For backwards compatibility we use 7bit msg['Content-Transfer-Encoding'] = '7bit' return - # We play a trick to make this go fast. If encoding to ASCII succeeds, we - # know the data must be 7bit, otherwise treat it as 8bit. + # We play a trick to make this go fast. If encoding/decode to ASCII + # succeeds, we know the data must be 7bit, otherwise treat it as 8bit. try: - orig.encode('ascii') + if isinstance(orig, str): + orig.encode('ascii') + else: + orig.decode('ascii') except UnicodeError: # iso-2022-* is non-ASCII but still 7-bit charset = msg.get_charset() diff -r 9dcd88977694 -r 650549138a3d Lib/email/header.py --- a/Lib/email/header.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/email/header.py Tue Mar 22 21:32:51 2011 +1000 @@ -47,7 +47,7 @@ # For use with .match() fcre = re.compile(r'[\041-\176]+:$') -# Find a header embeded in a putative header value. Used to check for +# Find a header embedded in a putative header value. Used to check for # header injection attack. _embeded_header = re.compile(r'\n[^ \t]+:') @@ -314,7 +314,7 @@ self._continuation_ws, splitchars) for string, charset in self._chunks: lines = string.splitlines() - formatter.feed(lines[0], charset) + formatter.feed(lines[0] if lines else '', charset) for line in lines[1:]: formatter.newline() if charset.header_encoding is not None: diff -r 9dcd88977694 -r 650549138a3d Lib/email/message.py --- a/Lib/email/message.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/email/message.py Tue Mar 22 21:32:51 2011 +1000 @@ -48,9 +48,9 @@ def _splitparam(param): # Split header parameters. BAW: this may be too simple. It isn't # strictly RFC 2045 (section 5.1) compliant, but it catches most headers - # found in the wild. We may eventually need a full fledged parser - # eventually. - a, sep, b = param.partition(';') + # found in the wild. We may eventually need a full fledged parser. + # RDM: we might have a Header here; for now just stringify it. + a, sep, b = str(param).partition(';') if not sep: return a.strip(), None return a.strip(), b.strip() @@ -90,6 +90,8 @@ return param def _parseparam(s): + # RDM This might be a Header, so for now stringify it. + s = ';' + str(s) plist = [] while s[:1] == ';': s = s[1:] @@ -157,8 +159,7 @@ header. This is a convenience method and may not generate the message exactly - as you intend because by default it mangles lines that begin with - "From ". For more flexibility, use the flatten() method of a + as you intend. For more flexibility, use the flatten() method of a Generator instance. """ from email.generator import Generator @@ -241,8 +242,9 @@ if i is not None and not isinstance(self._payload, list): raise TypeError('Expected list, got %s' % type(self._payload)) payload = self._payload - cte = self.get('content-transfer-encoding', '').lower() - # payload can be bytes here, (I wonder if that is actually a bug?) + # cte might be a Header, so for now stringify it. + cte = str(self.get('content-transfer-encoding', '')).lower() + # payload may be bytes here. if isinstance(payload, str): if _has_surrogates(payload): bpayload = payload.encode('ascii', 'surrogateescape') @@ -562,7 +564,7 @@ if value is missing: return failobj params = [] - for p in _parseparam(';' + value): + for p in _parseparam(value): try: name, val = p.split('=', 1) name = name.strip() diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/PyBanner048.gif Binary file Lib/email/test/data/PyBanner048.gif has changed diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/audiotest.au Binary file Lib/email/test/data/audiotest.au has changed diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_01.txt --- a/Lib/email/test/data/msg_01.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -Return-Path: -Delivered-To: bbb@zzz.org -Received: by mail.zzz.org (Postfix, from userid 889) - id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) -MIME-Version: 1.0 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit -Message-ID: <15090.61304.110929.45684@aaa.zzz.org> -From: bbb@ddd.com (John X. Doe) -To: bbb@zzz.org -Subject: This is a test message -Date: Fri, 4 May 2001 14:05:44 -0400 - - -Hi, - -Do you like this message? - --Me diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_02.txt --- a/Lib/email/test/data/msg_02.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,135 +0,0 @@ -MIME-version: 1.0 -From: ppp-request@zzz.org -Sender: ppp-admin@zzz.org -To: ppp@zzz.org -Subject: Ppp digest, Vol 1 #2 - 5 msgs -Date: Fri, 20 Apr 2001 20:18:00 -0400 (EDT) -X-Mailer: Mailman v2.0.4 -X-Mailman-Version: 2.0.4 -Content-Type: multipart/mixed; boundary="192.168.1.2.889.32614.987812255.500.21814" - ---192.168.1.2.889.32614.987812255.500.21814 -Content-type: text/plain; charset=us-ascii -Content-description: Masthead (Ppp digest, Vol 1 #2) - -Send Ppp mailing list submissions to - ppp@zzz.org - -To subscribe or unsubscribe via the World Wide Web, visit - http://www.zzz.org/mailman/listinfo/ppp -or, via email, send a message with subject or body 'help' to - ppp-request@zzz.org - -You can reach the person managing the list at - ppp-admin@zzz.org - -When replying, please edit your Subject line so it is more specific -than "Re: Contents of Ppp digest..." - - ---192.168.1.2.889.32614.987812255.500.21814 -Content-type: text/plain; charset=us-ascii -Content-description: Today's Topics (5 msgs) - -Today's Topics: - - 1. testing #1 (Barry A. Warsaw) - 2. testing #2 (Barry A. Warsaw) - 3. testing #3 (Barry A. Warsaw) - 4. testing #4 (Barry A. Warsaw) - 5. testing #5 (Barry A. Warsaw) - ---192.168.1.2.889.32614.987812255.500.21814 -Content-Type: multipart/digest; boundary="__--__--" - ---__--__-- - -Message: 1 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit -Date: Fri, 20 Apr 2001 20:16:13 -0400 -To: ppp@zzz.org -From: barry@digicool.com (Barry A. Warsaw) -Subject: [Ppp] testing #1 -Precedence: bulk - - -hello - - ---__--__-- - -Message: 2 -Date: Fri, 20 Apr 2001 20:16:21 -0400 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit -To: ppp@zzz.org -From: barry@digicool.com (Barry A. Warsaw) -Precedence: bulk - - -hello - - ---__--__-- - -Message: 3 -Date: Fri, 20 Apr 2001 20:16:25 -0400 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit -To: ppp@zzz.org -From: barry@digicool.com (Barry A. Warsaw) -Subject: [Ppp] testing #3 -Precedence: bulk - - -hello - - ---__--__-- - -Message: 4 -Date: Fri, 20 Apr 2001 20:16:28 -0400 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit -To: ppp@zzz.org -From: barry@digicool.com (Barry A. Warsaw) -Subject: [Ppp] testing #4 -Precedence: bulk - - -hello - - ---__--__-- - -Message: 5 -Date: Fri, 20 Apr 2001 20:16:32 -0400 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit -To: ppp@zzz.org -From: barry@digicool.com (Barry A. Warsaw) -Subject: [Ppp] testing #5 -Precedence: bulk - - -hello - - - - ---__--__---- ---192.168.1.2.889.32614.987812255.500.21814 -Content-type: text/plain; charset=us-ascii -Content-description: Digest Footer - -_______________________________________________ -Ppp mailing list -Ppp@zzz.org -http://www.zzz.org/mailman/listinfo/ppp - - ---192.168.1.2.889.32614.987812255.500.21814-- - -End of Ppp Digest - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_03.txt --- a/Lib/email/test/data/msg_03.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -Return-Path: -Delivered-To: bbb@zzz.org -Received: by mail.zzz.org (Postfix, from userid 889) - id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) -Message-ID: <15090.61304.110929.45684@aaa.zzz.org> -From: bbb@ddd.com (John X. Doe) -To: bbb@zzz.org -Subject: This is a test message -Date: Fri, 4 May 2001 14:05:44 -0400 - - -Hi, - -Do you like this message? - --Me diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_04.txt --- a/Lib/email/test/data/msg_04.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -Return-Path: -Delivered-To: barry@python.org -Received: by mail.python.org (Postfix, from userid 889) - id C2BF0D37C6; Tue, 11 Sep 2001 00:05:05 -0400 (EDT) -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary="h90VIIIKmx" -Content-Transfer-Encoding: 7bit -Message-ID: <15261.36209.358846.118674@anthem.python.org> -From: barry@python.org (Barry A. Warsaw) -To: barry@python.org -Subject: a simple multipart -Date: Tue, 11 Sep 2001 00:05:05 -0400 -X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid -X-Attribution: BAW -X-Oblique-Strategy: Make a door into a window - - ---h90VIIIKmx -Content-Type: text/plain -Content-Disposition: inline; - filename="msg.txt" -Content-Transfer-Encoding: 7bit - -a simple kind of mirror -to reflect upon our own - ---h90VIIIKmx -Content-Type: text/plain -Content-Disposition: inline; - filename="msg.txt" -Content-Transfer-Encoding: 7bit - -a simple kind of mirror -to reflect upon our own - ---h90VIIIKmx-- - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_05.txt --- a/Lib/email/test/data/msg_05.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -From: foo -Subject: bar -To: baz -MIME-Version: 1.0 -Content-Type: multipart/report; report-type=delivery-status; - boundary="D1690A7AC1.996856090/mail.example.com" -Message-Id: <20010803162810.0CA8AA7ACC@mail.example.com> - -This is a MIME-encapsulated message. - ---D1690A7AC1.996856090/mail.example.com -Content-Type: text/plain - -Yadda yadda yadda - ---D1690A7AC1.996856090/mail.example.com - -Yadda yadda yadda - ---D1690A7AC1.996856090/mail.example.com -Content-Type: message/rfc822 - -From: nobody@python.org - -Yadda yadda yadda - ---D1690A7AC1.996856090/mail.example.com-- - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_06.txt --- a/Lib/email/test/data/msg_06.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -Return-Path: -Delivered-To: barry@python.org -MIME-Version: 1.0 -Content-Type: message/rfc822 -Content-Description: forwarded message -Content-Transfer-Encoding: 7bit -Message-ID: <15265.9482.641338.555352@python.org> -From: barry@zope.com (Barry A. Warsaw) -Sender: barry@python.org -To: barry@python.org -Subject: forwarded message from Barry A. Warsaw -Date: Thu, 13 Sep 2001 17:28:42 -0400 -X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid -X-Attribution: BAW -X-Oblique-Strategy: Be dirty -X-Url: http://barry.wooz.org - -MIME-Version: 1.0 -Content-Type: text/plain; charset=us-ascii -Return-Path: -Delivered-To: barry@python.org -Message-ID: <15265.9468.713530.98441@python.org> -From: barry@zope.com (Barry A. Warsaw) -Sender: barry@python.org -To: barry@python.org -Subject: testing -Date: Thu, 13 Sep 2001 17:28:28 -0400 -X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid -X-Attribution: BAW -X-Oblique-Strategy: Spectrum analysis -X-Url: http://barry.wooz.org - - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_07.txt --- a/Lib/email/test/data/msg_07.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -MIME-Version: 1.0 -From: Barry -To: Dingus Lovers -Subject: Here is your dingus fish -Date: Fri, 20 Apr 2001 19:35:02 -0400 -Content-Type: multipart/mixed; boundary="BOUNDARY" - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" - -Hi there, - -This is the dingus fish. - ---BOUNDARY -Content-Type: image/gif; name="dingusfish.gif" -Content-Transfer-Encoding: base64 -content-disposition: attachment; filename="dingusfish.gif" - -R0lGODdhAAEAAfAAAP///wAAACwAAAAAAAEAAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq2 -7gvH8kzX9o3n+s73/g8MCofEovGITGICTKbyCV0FDNOo9SqpQqpOrJfXzTQj2vD3TGtqL+NtGQ2f -qTXmxzuOd7WXdcc9DyjU53ewFni4s0fGhdiYaEhGBelICTNoV1j5NUnFcrmUqemjNifJVWpaOqaI -oFq3SspZsSraE7sHq3jr1MZqWvi662vxV4tD+pvKW6aLDOCLyur8PDwbanyDeq0N3DctbQYeLDvR -RY6t95m6UB0d3mwIrV7e2VGNvjjffukeJp4w7F65KecGFsTHQGAygOrgrWs1jt28Rc88KESYcGLA -/obvTkH6p+CinWJiJmIMqXGQwH/y4qk0SYjgQTczT3ajKZGfuI0uJ4kkVI/DT5s3/ejkxI0aT4Y+ -YTYgWbImUaXk9nlLmnSh1qJiJFl0OpUqRK4oOy7NyRQtHWofhoYVxkwWXKUSn0YsS+fUV6lhqfYb -6ayd3Z5qQdG1B7bvQzaJjwUV2lixMUZ7JVsOlfjWVr/3NB/uFvnySBN6Dcb6rGwaRM3wsormw5cC -M9NxWy/bWdufudCvy8bOAjXjVVwta/uO21sE5RHBCzNFXtgq9ORtH4eYjVP4Yryo026nvkFmCeyA -B29efV6ravCMK5JwWd5897Qrx7ll38o6iHDZ/rXPR//feevhF4l7wjUGX3xq1eeRfM4RSJGBIV1D -z1gKPkfWag3mVBVvva1RlX5bAJTPR/2YqNtw/FkIYYEi/pIZiAdpcxpoHtmnYYoZtvhUftzdx5ZX -JSKDW405zkGcZzzGZ6KEv4FI224oDmijlEf+xp6MJK5ojY/ASeVUR+wsKRuJ+XFZ5o7ZeEime8t1 -ouUsU6YjF5ZtUihhkGfCdFQLWQFJ3UXxmElfhQnR+eCdcDbkFZp6vTRmj56ApCihn5QGpaToNZmR -n3NVSpZcQpZ2KEONusaiCsKAug0wkQbJSFO+PTSjneGxOuFjPlUk3ovWvdIerjUg9ZGIOtGq/qeX -eCYrrCX+1UPsgTKGGRSbzd5q156d/gpfbJxe66eD5iQKrXj7RGgruGxs62qebBHUKS32CKluCiqZ -qh+pmehmEb71noAUoe5e9Zm17S7773V10pjrtG4CmuurCV/n6zLK5turWNhqOvFXbjhZrMD0YhKe -wR0zOyuvsh6MWrGoIuzvyWu5y1WIFAqmJselypxXh6dKLNOKEB98L88bS2rkNqqlKzCNJp9c0G0j -Gzh0iRrCbHSXmPR643QS+4rWhgFmnSbSuXCjS0xAOWkU2UdLqyuUNfHSFdUouy3bm5i5GnDM3tG8 -doJ4r5tqu3pPbRSVfvs8uJzeNXhp3n4j/tZ42SwH7eaWUUOjc3qFV9453UHTXZfcLH+OeNs5g36x -lBnHvTm7EbMbLeuaLncao8vWCXimfo1o+843Ak6y4ChNeGntvAYvfLK4ezmoyNIbNCLTCXO9ZV3A -E8/s88RczPzDwI4Ob7XZyl7+9Miban29h+tJZPrE21wgvBphDfrrfPdCTPKJD/y98L1rZwHcV6Jq -Zab0metpuNIX/qAFPoz171WUaUb4HAhBSzHuHfjzHb3kha/2Cctis/ORArVHNYfFyYRH2pYIRzic -isVOfPWD1b6mRTqpCRBozzof6UZVvFXRxWIr3GGrEviGYgyPMfahheiSaLs/9QeFu7oZ/ndSY8DD -ya9x+uPed+7mxN2IzIISBOMLFYWVqC3Pew1T2nFuuCiwZS5/v6II10i4t1OJcUH2U9zxKodHsGGv -Oa+zkvNUYUOa/TCCRutF9MzDwdlUMJADTCGSbDQ5OV4PTamDoPEi6Ecc/RF5RWwkcdSXvSOaDWSn -I9LlvubFTQpuc6JKXLcKeb+xdbKRBnwREemXyjg6ME65aJiOuBgrktzykfPLJBKR9ClMavJ62/Ff -BlNIyod9yX9wcSXexnXFpvkrbXk64xsx5Db7wXKP5fSgsvwIMM/9631VLBfkmtbHRXpqmtei52hG -pUwSlo+BASQoeILDOBgREECxBBh5/iYmNsQ9dIv5+OI++QkqdsJPc3uykz5fkM+OraeekcQF7X4n -B5S67za5U967PmooGQhUXfF7afXyCD7ONdRe17QogYjVx38uLwtrS6nhTnm15LQUnu9E2uK6CNI/ -1HOABj0ESwOjut4FEpFQpdNAm4K2LHnDWHNcmKB2ioKBogysVZtMO2nSxUdZ8Yk2kJc7URioLVI0 -YgmtIwZj4LoeKemgnOnbUdGnzZ4Oa6scqiolBGqS6RgWNLu0RMhcaE6rhhU4hiuqFXPAG8fGwTPW -FKeLMtdVmXLSs5YJGF/YeVm7rREMlY3UYE+yCxbaMXX8y15m5zVHq6GOKDMynzII/jdUHdyVqIy0 -ifX2+r/EgtZcvRzSb72gU9ui87M2VecjKildW/aFqaYhKoryUjfB/g4qtyVuc60xFDGmCxwjW+qu -zjuwl2GkOWn66+3QiiEctvd04OVvcCVzjgT7lrkvjVGKKHmmlDUKowSeikb5kK/mJReuWOxONx+s -ULsl+Lqb0CVn0SrVyJ6wt4t6yTeSCafhPhAf0OXn6L60UMxiLolFAtmN35S2Ob1lZpQ1r/n0Qb5D -oQ1zJiRVDgF8N3Q8TYfbi3DyWCy3lT1nxyBs6FT3S2GOzWRlxwKvlRP0RPJA9SjxEy0UoEnkA+M4 -cnzLMJrBGWLFEaaUb5lvpqbq/loOaU5+DFuHPxo82/OZuM8FXG3oVNZhtWpMpb/0Xu5m/LfLhHZQ -7yuVI0MqZ7NE43imC8jH3IwGZlbPm0xkJYs7+2U48hXTsFSMqgGDvai0kLxyynKNT/waj+q1c1tz -GjOpPBgdCSq3UKZxCSsqFIY+O6JbAWGWcV1pwqLyj5sGqCF1xb1F3varUWqrJv6cN3PrUXzijtfZ -FshpBL3Xwr4GIPvU2N8EjrJgS1zl21rbXQMXeXc5jjFyrhpCzijSv/RQtyPSzHCFMhlME95fHglt -pRsX+dfSQjUeHAlpWzJ5iOo79Ldnaxai6bXTcGO3fp07ri7HLEmXXPlYi8bv/qVxvNcdra6m7Rlb -6JBTb5fd66VhFRjGArh2n7R1rDW4P5NOT9K0I183T2scYkeZ3q/VFyLb09U9ajzXBS8Kgkhc4mBS -kYY9cy3Vy9lUnuNJH8HGIclUilwnBtjUOH0gteGOZ4c/XNrhXLSYDyxfnD8z1pDy7rYRvDolhnbe -UMzxCZUs40s6s7UIvBnLgc0+vKuOkIXeOrDymlp+Zxra4MZLBbVrqD/jTJ597pDmnw5c4+DbyB88 -9Cg9DodYcSuMZT/114pptqc/EuTjRPvH/z5slzI3tluOEBBLqOXLOX+0I5929tO97wkvl/atCz+y -xJrdwteW2FNW/NSmBP+f/maYtVs/bYyBC7Ox3jsYZHL05CIrBa/nS+b3bHfiYm4Ueil1YZZSgAUI -fFZ1dxUmeA2oQRQ3RuGXNGLFV9/XbGFGPV6kfzk1TBBCd+izc7q1H+OHMJwmaBX2IQNYVAKHYepV -SSGCe6CnbYHHETKGNe43EDvFgZr0gB/nVHPHZ80VV1ojOiI3XDvYIkl4ayo4bxQIgrFXWTvBI0nH -VElWMuw2aLUWCRHHf8ymVCHjFlJnOSojfevCYyyyZDH0IcvHhrsnQ5O1OsWzONuVVKIxSxiFZ/tR -fKDAf6xFTnw4O9Qig2VCfW2hJQrmMOuHW0W3dLQmCMO2ccdUd/xyfflH/olTiHZVdGwb8nIwRzSE -J15jFlOJuBZBZ4CiyHyd2IFylFlB+HgHhYabhWOGwYO1ZH/Og1dtQlFMk352CGRSIFTapnWQEUtN -l4zv8S0aaCFDyGCBqDUxZYpxGHX01y/JuH1xhn7TOCnNCI4eKDs5WGX4R425F4vF1o3BJ4vO0otq -I3rimI7jJY1jISqnBxknCIvruF83mF5wN4X7qGLIhR8A2Vg0yFERSIXn9Vv3GHy3Vj/WIkKddlYi -yIMv2I/VMjTLpW7pt05SWIZR0RPyxpB4SIUM9lBPGBl0GC7oSEEwRYLe4pJpZY2P0zbI1n+Oc44w -qY3PUnmF0ixjVpDD/mJ9wpOBGTVgXlaCaZiPcIWK5NiKBIiPdGaQ0TWGvAiG7nMchdZb7Vgf8zNi -MuMyzRdy/lePe9iC4TRx7WhhOQI/QiSVNAmAa2lT/piFbuh7ofJoYSZzrSZ1bvmWw3eN2nKUPVky -uPN5/VRfohRd0VYZoqhKIlU6TXYhJxmPUIloAwc1bPmHEpaZYZORHNlXUJM07hATwHR8MJYqkwWR -WaIezFhxSFlc8/Fq82hEnpeRozg3ULhhr9lAGtVEkCg5ZNRuuVleBPaZadhG0ZgkyPmDOTOKzViM -YgOcpukKqQcbjAWS0IleQ2ROjdh6A+md1qWdBRSX7iSYgFRTtRmBpJioieXJiHfJiMGIR9fJOn8I -MSfXYhspn4ooSa2mSAj4n+8Bmg03fBJZoPOJgsVZRxu1oOMRPXYYjdqjihFaEoZpXBREanuJoRI6 -cibFinq4ngUKh/wQd/H5ofYCZ0HJXR62opZFaAT0iFIZo4DIiUojkjeqKiuoZirKo5Y1a7AWckGa -BkuYoD5lpDK6eUs6CkDqpETwl1EqpfhJpVeKpVl6EgUAADs= - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_08.txt --- a/Lib/email/test/data/msg_08.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -MIME-Version: 1.0 -From: Barry Warsaw -To: Dingus Lovers -Subject: Lyrics -Date: Fri, 20 Apr 2001 19:35:02 -0400 -Content-Type: multipart/mixed; boundary="BOUNDARY" - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" - - ---BOUNDARY -Content-Type: text/html; charset="iso-8859-1" - - ---BOUNDARY -Content-Type: text/plain; charset="iso-8859-2" - - ---BOUNDARY -Content-Type: text/plain; charset="koi8-r" - - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_09.txt --- a/Lib/email/test/data/msg_09.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -MIME-Version: 1.0 -From: Barry Warsaw -To: Dingus Lovers -Subject: Lyrics -Date: Fri, 20 Apr 2001 19:35:02 -0400 -Content-Type: multipart/mixed; boundary="BOUNDARY" - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" - - ---BOUNDARY -Content-Type: text/html; charset="iso-8859-1" - - ---BOUNDARY -Content-Type: text/plain - - ---BOUNDARY -Content-Type: text/plain; charset="koi8-r" - - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_10.txt --- a/Lib/email/test/data/msg_10.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -MIME-Version: 1.0 -From: Barry Warsaw -To: Dingus Lovers -Subject: Lyrics -Date: Fri, 20 Apr 2001 19:35:02 -0400 -Content-Type: multipart/mixed; boundary="BOUNDARY" - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -Content-Transfer-Encoding: 7bit - -This is a 7bit encoded message. - ---BOUNDARY -Content-Type: text/html; charset="iso-8859-1" -Content-Transfer-Encoding: Quoted-Printable - -=A1This is a Quoted Printable encoded message! - ---BOUNDARY -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: Base64 - -VGhpcyBpcyBhIEJhc2U2NCBlbmNvZGVkIG1lc3NhZ2Uu - - ---BOUNDARY -Content-Type: text/plain; charset="iso-8859-1" -Content-Transfer-Encoding: Base64 - -VGhpcyBpcyBhIEJhc2U2NCBlbmNvZGVkIG1lc3NhZ2UuCg== - - ---BOUNDARY -Content-Type: text/plain; charset="iso-8859-1" - -This has no Content-Transfer-Encoding: header. - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_11.txt --- a/Lib/email/test/data/msg_11.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -Content-Type: message/rfc822 -MIME-Version: 1.0 -Subject: The enclosing message - -Subject: An enclosed message - -Here is the body of the message. diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_12.txt --- a/Lib/email/test/data/msg_12.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -MIME-Version: 1.0 -From: Barry Warsaw -To: Dingus Lovers -Subject: Lyrics -Date: Fri, 20 Apr 2001 19:35:02 -0400 -Content-Type: multipart/mixed; boundary="BOUNDARY" - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" - - ---BOUNDARY -Content-Type: text/html; charset="iso-8859-1" - - ---BOUNDARY -Content-Type: multipart/mixed; boundary="ANOTHER" - ---ANOTHER -Content-Type: text/plain; charset="iso-8859-2" - - ---ANOTHER -Content-Type: text/plain; charset="iso-8859-3" - ---ANOTHER-- - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" - - ---BOUNDARY -Content-Type: text/plain; charset="koi8-r" - - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_12a.txt --- a/Lib/email/test/data/msg_12a.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -MIME-Version: 1.0 -From: Barry Warsaw -To: Dingus Lovers -Subject: Lyrics -Date: Fri, 20 Apr 2001 19:35:02 -0400 -Content-Type: multipart/mixed; boundary="BOUNDARY" - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" - - ---BOUNDARY -Content-Type: text/html; charset="iso-8859-1" - - ---BOUNDARY -Content-Type: multipart/mixed; boundary="ANOTHER" - ---ANOTHER -Content-Type: text/plain; charset="iso-8859-2" - - ---ANOTHER -Content-Type: text/plain; charset="iso-8859-3" - - ---ANOTHER-- - - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" - - ---BOUNDARY -Content-Type: text/plain; charset="koi8-r" - - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_13.txt --- a/Lib/email/test/data/msg_13.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -MIME-Version: 1.0 -From: Barry -To: Dingus Lovers -Subject: Here is your dingus fish -Date: Fri, 20 Apr 2001 19:35:02 -0400 -Content-Type: multipart/mixed; boundary="OUTER" - ---OUTER -Content-Type: text/plain; charset="us-ascii" - -A text/plain part - ---OUTER -Content-Type: multipart/mixed; boundary=BOUNDARY - - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" - -Hi there, - -This is the dingus fish. - ---BOUNDARY -Content-Type: image/gif; name="dingusfish.gif" -Content-Transfer-Encoding: base64 -content-disposition: attachment; filename="dingusfish.gif" - -R0lGODdhAAEAAfAAAP///wAAACwAAAAAAAEAAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq2 -7gvH8kzX9o3n+s73/g8MCofEovGITGICTKbyCV0FDNOo9SqpQqpOrJfXzTQj2vD3TGtqL+NtGQ2f -qTXmxzuOd7WXdcc9DyjU53ewFni4s0fGhdiYaEhGBelICTNoV1j5NUnFcrmUqemjNifJVWpaOqaI -oFq3SspZsSraE7sHq3jr1MZqWvi662vxV4tD+pvKW6aLDOCLyur8PDwbanyDeq0N3DctbQYeLDvR -RY6t95m6UB0d3mwIrV7e2VGNvjjffukeJp4w7F65KecGFsTHQGAygOrgrWs1jt28Rc88KESYcGLA -/obvTkH6p+CinWJiJmIMqXGQwH/y4qk0SYjgQTczT3ajKZGfuI0uJ4kkVI/DT5s3/ejkxI0aT4Y+ -YTYgWbImUaXk9nlLmnSh1qJiJFl0OpUqRK4oOy7NyRQtHWofhoYVxkwWXKUSn0YsS+fUV6lhqfYb -6ayd3Z5qQdG1B7bvQzaJjwUV2lixMUZ7JVsOlfjWVr/3NB/uFvnySBN6Dcb6rGwaRM3wsormw5cC -M9NxWy/bWdufudCvy8bOAjXjVVwta/uO21sE5RHBCzNFXtgq9ORtH4eYjVP4Yryo026nvkFmCeyA -B29efV6ravCMK5JwWd5897Qrx7ll38o6iHDZ/rXPR//feevhF4l7wjUGX3xq1eeRfM4RSJGBIV1D -z1gKPkfWag3mVBVvva1RlX5bAJTPR/2YqNtw/FkIYYEi/pIZiAdpcxpoHtmnYYoZtvhUftzdx5ZX -JSKDW405zkGcZzzGZ6KEv4FI224oDmijlEf+xp6MJK5ojY/ASeVUR+wsKRuJ+XFZ5o7ZeEime8t1 -ouUsU6YjF5ZtUihhkGfCdFQLWQFJ3UXxmElfhQnR+eCdcDbkFZp6vTRmj56ApCihn5QGpaToNZmR -n3NVSpZcQpZ2KEONusaiCsKAug0wkQbJSFO+PTSjneGxOuFjPlUk3ovWvdIerjUg9ZGIOtGq/qeX -eCYrrCX+1UPsgTKGGRSbzd5q156d/gpfbJxe66eD5iQKrXj7RGgruGxs62qebBHUKS32CKluCiqZ -qh+pmehmEb71noAUoe5e9Zm17S7773V10pjrtG4CmuurCV/n6zLK5turWNhqOvFXbjhZrMD0YhKe -wR0zOyuvsh6MWrGoIuzvyWu5y1WIFAqmJselypxXh6dKLNOKEB98L88bS2rkNqqlKzCNJp9c0G0j -Gzh0iRrCbHSXmPR643QS+4rWhgFmnSbSuXCjS0xAOWkU2UdLqyuUNfHSFdUouy3bm5i5GnDM3tG8 -doJ4r5tqu3pPbRSVfvs8uJzeNXhp3n4j/tZ42SwH7eaWUUOjc3qFV9453UHTXZfcLH+OeNs5g36x -lBnHvTm7EbMbLeuaLncao8vWCXimfo1o+843Ak6y4ChNeGntvAYvfLK4ezmoyNIbNCLTCXO9ZV3A -E8/s88RczPzDwI4Ob7XZyl7+9Miban29h+tJZPrE21wgvBphDfrrfPdCTPKJD/y98L1rZwHcV6Jq -Zab0metpuNIX/qAFPoz171WUaUb4HAhBSzHuHfjzHb3kha/2Cctis/ORArVHNYfFyYRH2pYIRzic -isVOfPWD1b6mRTqpCRBozzof6UZVvFXRxWIr3GGrEviGYgyPMfahheiSaLs/9QeFu7oZ/ndSY8DD -ya9x+uPed+7mxN2IzIISBOMLFYWVqC3Pew1T2nFuuCiwZS5/v6II10i4t1OJcUH2U9zxKodHsGGv -Oa+zkvNUYUOa/TCCRutF9MzDwdlUMJADTCGSbDQ5OV4PTamDoPEi6Ecc/RF5RWwkcdSXvSOaDWSn -I9LlvubFTQpuc6JKXLcKeb+xdbKRBnwREemXyjg6ME65aJiOuBgrktzykfPLJBKR9ClMavJ62/Ff -BlNIyod9yX9wcSXexnXFpvkrbXk64xsx5Db7wXKP5fSgsvwIMM/9631VLBfkmtbHRXpqmtei52hG -pUwSlo+BASQoeILDOBgREECxBBh5/iYmNsQ9dIv5+OI++QkqdsJPc3uykz5fkM+OraeekcQF7X4n -B5S67za5U967PmooGQhUXfF7afXyCD7ONdRe17QogYjVx38uLwtrS6nhTnm15LQUnu9E2uK6CNI/ -1HOABj0ESwOjut4FEpFQpdNAm4K2LHnDWHNcmKB2ioKBogysVZtMO2nSxUdZ8Yk2kJc7URioLVI0 -YgmtIwZj4LoeKemgnOnbUdGnzZ4Oa6scqiolBGqS6RgWNLu0RMhcaE6rhhU4hiuqFXPAG8fGwTPW -FKeLMtdVmXLSs5YJGF/YeVm7rREMlY3UYE+yCxbaMXX8y15m5zVHq6GOKDMynzII/jdUHdyVqIy0 -ifX2+r/EgtZcvRzSb72gU9ui87M2VecjKildW/aFqaYhKoryUjfB/g4qtyVuc60xFDGmCxwjW+qu -zjuwl2GkOWn66+3QiiEctvd04OVvcCVzjgT7lrkvjVGKKHmmlDUKowSeikb5kK/mJReuWOxONx+s -ULsl+Lqb0CVn0SrVyJ6wt4t6yTeSCafhPhAf0OXn6L60UMxiLolFAtmN35S2Ob1lZpQ1r/n0Qb5D -oQ1zJiRVDgF8N3Q8TYfbi3DyWCy3lT1nxyBs6FT3S2GOzWRlxwKvlRP0RPJA9SjxEy0UoEnkA+M4 -cnzLMJrBGWLFEaaUb5lvpqbq/loOaU5+DFuHPxo82/OZuM8FXG3oVNZhtWpMpb/0Xu5m/LfLhHZQ -7yuVI0MqZ7NE43imC8jH3IwGZlbPm0xkJYs7+2U48hXTsFSMqgGDvai0kLxyynKNT/waj+q1c1tz -GjOpPBgdCSq3UKZxCSsqFIY+O6JbAWGWcV1pwqLyj5sGqCF1xb1F3varUWqrJv6cN3PrUXzijtfZ -FshpBL3Xwr4GIPvU2N8EjrJgS1zl21rbXQMXeXc5jjFyrhpCzijSv/RQtyPSzHCFMhlME95fHglt -pRsX+dfSQjUeHAlpWzJ5iOo79Ldnaxai6bXTcGO3fp07ri7HLEmXXPlYi8bv/qVxvNcdra6m7Rlb -6JBTb5fd66VhFRjGArh2n7R1rDW4P5NOT9K0I183T2scYkeZ3q/VFyLb09U9ajzXBS8Kgkhc4mBS -kYY9cy3Vy9lUnuNJH8HGIclUilwnBtjUOH0gteGOZ4c/XNrhXLSYDyxfnD8z1pDy7rYRvDolhnbe -UMzxCZUs40s6s7UIvBnLgc0+vKuOkIXeOrDymlp+Zxra4MZLBbVrqD/jTJ597pDmnw5c4+DbyB88 -9Cg9DodYcSuMZT/114pptqc/EuTjRPvH/z5slzI3tluOEBBLqOXLOX+0I5929tO97wkvl/atCz+y -xJrdwteW2FNW/NSmBP+f/maYtVs/bYyBC7Ox3jsYZHL05CIrBa/nS+b3bHfiYm4Ueil1YZZSgAUI -fFZ1dxUmeA2oQRQ3RuGXNGLFV9/XbGFGPV6kfzk1TBBCd+izc7q1H+OHMJwmaBX2IQNYVAKHYepV -SSGCe6CnbYHHETKGNe43EDvFgZr0gB/nVHPHZ80VV1ojOiI3XDvYIkl4ayo4bxQIgrFXWTvBI0nH -VElWMuw2aLUWCRHHf8ymVCHjFlJnOSojfevCYyyyZDH0IcvHhrsnQ5O1OsWzONuVVKIxSxiFZ/tR -fKDAf6xFTnw4O9Qig2VCfW2hJQrmMOuHW0W3dLQmCMO2ccdUd/xyfflH/olTiHZVdGwb8nIwRzSE -J15jFlOJuBZBZ4CiyHyd2IFylFlB+HgHhYabhWOGwYO1ZH/Og1dtQlFMk352CGRSIFTapnWQEUtN -l4zv8S0aaCFDyGCBqDUxZYpxGHX01y/JuH1xhn7TOCnNCI4eKDs5WGX4R425F4vF1o3BJ4vO0otq -I3rimI7jJY1jISqnBxknCIvruF83mF5wN4X7qGLIhR8A2Vg0yFERSIXn9Vv3GHy3Vj/WIkKddlYi -yIMv2I/VMjTLpW7pt05SWIZR0RPyxpB4SIUM9lBPGBl0GC7oSEEwRYLe4pJpZY2P0zbI1n+Oc44w -qY3PUnmF0ixjVpDD/mJ9wpOBGTVgXlaCaZiPcIWK5NiKBIiPdGaQ0TWGvAiG7nMchdZb7Vgf8zNi -MuMyzRdy/lePe9iC4TRx7WhhOQI/QiSVNAmAa2lT/piFbuh7ofJoYSZzrSZ1bvmWw3eN2nKUPVky -uPN5/VRfohRd0VYZoqhKIlU6TXYhJxmPUIloAwc1bPmHEpaZYZORHNlXUJM07hATwHR8MJYqkwWR -WaIezFhxSFlc8/Fq82hEnpeRozg3ULhhr9lAGtVEkCg5ZNRuuVleBPaZadhG0ZgkyPmDOTOKzViM -YgOcpukKqQcbjAWS0IleQ2ROjdh6A+md1qWdBRSX7iSYgFRTtRmBpJioieXJiHfJiMGIR9fJOn8I -MSfXYhspn4ooSa2mSAj4n+8Bmg03fBJZoPOJgsVZRxu1oOMRPXYYjdqjihFaEoZpXBREanuJoRI6 -cibFinq4ngUKh/wQd/H5ofYCZ0HJXR62opZFaAT0iFIZo4DIiUojkjeqKiuoZirKo5Y1a7AWckGa -BkuYoD5lpDK6eUs6CkDqpETwl1EqpfhJpVeKpVl6EgUAADs= - ---BOUNDARY-- - ---OUTER-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_14.txt --- a/Lib/email/test/data/msg_14.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -Return-Path: -Delivered-To: bbb@zzz.org -Received: by mail.zzz.org (Postfix, from userid 889) - id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) -MIME-Version: 1.0 -Content-Type: text; charset=us-ascii -Content-Transfer-Encoding: 7bit -Message-ID: <15090.61304.110929.45684@aaa.zzz.org> -From: bbb@ddd.com (John X. Doe) -To: bbb@zzz.org -Subject: This is a test message -Date: Fri, 4 May 2001 14:05:44 -0400 - - -Hi, - -I'm sorry but I'm using a drainbread ISP, which although big and -wealthy can't seem to generate standard compliant email. :( - -This message has a Content-Type: header with no subtype. I hope you -can still read it. - --Me diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_15.txt --- a/Lib/email/test/data/msg_15.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -Return-Path: -Received: from fepD.post.tele.dk (195.41.46.149) by mail.groupcare.dk (LSMTP for Windows NT v1.1b) with SMTP id <0.0014F8A2@mail.groupcare.dk>; Mon, 30 Apr 2001 12:17:50 +0200 -User-Agent: Microsoft-Outlook-Express-Macintosh-Edition/5.02.2106 -Subject: XX -From: xx@xx.dk -To: XX -Message-ID: -Mime-version: 1.0 -Content-type: multipart/mixed; - boundary="MS_Mac_OE_3071477847_720252_MIME_Part" - -> Denne meddelelse er i MIME-format. Da dit postl - ---MS_Mac_OE_3071477847_720252_MIME_Part -Content-type: multipart/alternative; - boundary="MS_Mac_OE_3071477847_720252_MIME_Part" - - ---MS_Mac_OE_3071477847_720252_MIME_Part -Content-type: text/plain; charset="ISO-8859-1" -Content-transfer-encoding: quoted-printable - -Some removed test. - ---MS_Mac_OE_3071477847_720252_MIME_Part -Content-type: text/html; charset="ISO-8859-1" -Content-transfer-encoding: quoted-printable - - - -Some removed HTML - - -Some removed text. - - - - ---MS_Mac_OE_3071477847_720252_MIME_Part-- - - ---MS_Mac_OE_3071477847_720252_MIME_Part -Content-type: image/gif; name="xx.gif"; - x-mac-creator="6F676C65"; - x-mac-type="47494666" -Content-disposition: attachment -Content-transfer-encoding: base64 - -Some removed base64 encoded chars. - ---MS_Mac_OE_3071477847_720252_MIME_Part-- - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_16.txt --- a/Lib/email/test/data/msg_16.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,123 +0,0 @@ -Return-Path: <> -Delivered-To: scr-admin@socal-raves.org -Received: from cougar.noc.ucla.edu (cougar.noc.ucla.edu [169.232.10.18]) - by babylon.socal-raves.org (Postfix) with ESMTP id CCC2C51B84 - for ; Sun, 23 Sep 2001 20:13:54 -0700 (PDT) -Received: from sims-ms-daemon by cougar.noc.ucla.edu - (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10) - id <0GK500B01D0B8Y@cougar.noc.ucla.edu> for scr-admin@socal-raves.org; Sun, - 23 Sep 2001 20:14:35 -0700 (PDT) -Received: from cougar.noc.ucla.edu - (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10) - id <0GK500B01D0B8X@cougar.noc.ucla.edu>; Sun, 23 Sep 2001 20:14:35 -0700 (PDT) -Date: Sun, 23 Sep 2001 20:14:35 -0700 (PDT) -From: Internet Mail Delivery -Subject: Delivery Notification: Delivery has failed -To: scr-admin@socal-raves.org -Message-id: <0GK500B04D0B8X@cougar.noc.ucla.edu> -MIME-version: 1.0 -Sender: scr-owner@socal-raves.org -Errors-To: scr-owner@socal-raves.org -X-BeenThere: scr@socal-raves.org -X-Mailman-Version: 2.1a3 -Precedence: bulk -List-Help: -List-Post: -List-Subscribe: , - -List-Id: SoCal-Raves -List-Unsubscribe: , - -List-Archive: -Content-Type: multipart/report; boundary="Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)" - - ---Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA) -Content-type: text/plain; charset=ISO-8859-1 - -This report relates to a message you sent with the following header fields: - - Message-id: <002001c144a6$8752e060$56104586@oxy.edu> - Date: Sun, 23 Sep 2001 20:10:55 -0700 - From: "Ian T. Henry" - To: SoCal Raves - Subject: [scr] yeah for Ians!! - -Your message cannot be delivered to the following recipients: - - Recipient address: jangel1@cougar.noc.ucla.edu - Reason: recipient reached disk quota - - ---Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA) -Content-type: message/DELIVERY-STATUS - -Original-envelope-id: 0GK500B4HD0888@cougar.noc.ucla.edu -Reporting-MTA: dns; cougar.noc.ucla.edu - -Action: failed -Status: 5.0.0 (recipient reached disk quota) -Original-recipient: rfc822;jangel1@cougar.noc.ucla.edu -Final-recipient: rfc822;jangel1@cougar.noc.ucla.edu - ---Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA) -Content-type: MESSAGE/RFC822 - -Return-path: scr-admin@socal-raves.org -Received: from sims-ms-daemon by cougar.noc.ucla.edu - (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10) - id <0GK500B01D0B8X@cougar.noc.ucla.edu>; Sun, 23 Sep 2001 20:14:35 -0700 (PDT) -Received: from panther.noc.ucla.edu by cougar.noc.ucla.edu - (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10) - with ESMTP id <0GK500B4GD0888@cougar.noc.ucla.edu> for jangel1@sims-ms-daemon; - Sun, 23 Sep 2001 20:14:33 -0700 (PDT) -Received: from babylon.socal-raves.org - (ip-209-85-222-117.dreamhost.com [209.85.222.117]) - by panther.noc.ucla.edu (8.9.1a/8.9.1) with ESMTP id UAA09793 for - ; Sun, 23 Sep 2001 20:14:32 -0700 (PDT) -Received: from babylon (localhost [127.0.0.1]) by babylon.socal-raves.org - (Postfix) with ESMTP id D3B2951B70; Sun, 23 Sep 2001 20:13:47 -0700 (PDT) -Received: by babylon.socal-raves.org (Postfix, from userid 60001) - id A611F51B82; Sun, 23 Sep 2001 20:13:46 -0700 (PDT) -Received: from tiger.cc.oxy.edu (tiger.cc.oxy.edu [134.69.3.112]) - by babylon.socal-raves.org (Postfix) with ESMTP id ADA7351B70 for - ; Sun, 23 Sep 2001 20:13:44 -0700 (PDT) -Received: from ent (n16h86.dhcp.oxy.edu [134.69.16.86]) - by tiger.cc.oxy.edu (8.8.8/8.8.8) with SMTP id UAA08100 for - ; Sun, 23 Sep 2001 20:14:24 -0700 (PDT) -Date: Sun, 23 Sep 2001 20:10:55 -0700 -From: "Ian T. Henry" -Subject: [scr] yeah for Ians!! -Sender: scr-admin@socal-raves.org -To: SoCal Raves -Errors-to: scr-admin@socal-raves.org -Message-id: <002001c144a6$8752e060$56104586@oxy.edu> -MIME-version: 1.0 -X-Mailer: Microsoft Outlook Express 5.50.4522.1200 -Content-type: text/plain; charset=us-ascii -Precedence: bulk -Delivered-to: scr-post@babylon.socal-raves.org -Delivered-to: scr@socal-raves.org -X-Converted-To-Plain-Text: from multipart/alternative by demime 0.98e -X-Converted-To-Plain-Text: Alternative section used was text/plain -X-BeenThere: scr@socal-raves.org -X-Mailman-Version: 2.1a3 -List-Help: -List-Post: -List-Subscribe: , - -List-Id: SoCal-Raves -List-Unsubscribe: , - -List-Archive: - -I always love to find more Ian's that are over 3 years old!! - -Ian -_______________________________________________ -For event info, list questions, or to unsubscribe, see http://www.socal-raves.org/ - - - ---Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)-- - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_17.txt --- a/Lib/email/test/data/msg_17.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -MIME-Version: 1.0 -From: Barry -To: Dingus Lovers -Subject: Here is your dingus fish -Date: Fri, 20 Apr 2001 19:35:02 -0400 -Content-Type: multipart/mixed; boundary="BOUNDARY" - -Hi there, - -This is the dingus fish. - -[Non-text (image/gif) part of message omitted, filename dingusfish.gif] diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_18.txt --- a/Lib/email/test/data/msg_18.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals"; - spooge="yummy"; hippos="gargantuan"; marshmallows="gooey" - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_19.txt --- a/Lib/email/test/data/msg_19.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -Send Ppp mailing list submissions to - ppp@zzz.org - -To subscribe or unsubscribe via the World Wide Web, visit - http://www.zzz.org/mailman/listinfo/ppp -or, via email, send a message with subject or body 'help' to - ppp-request@zzz.org - -You can reach the person managing the list at - ppp-admin@zzz.org - -When replying, please edit your Subject line so it is more specific -than "Re: Contents of Ppp digest..." - -Today's Topics: - - 1. testing #1 (Barry A. Warsaw) - 2. testing #2 (Barry A. Warsaw) - 3. testing #3 (Barry A. Warsaw) - 4. testing #4 (Barry A. Warsaw) - 5. testing #5 (Barry A. Warsaw) - -hello - - -hello - - -hello - - -hello - - -hello - - - -_______________________________________________ -Ppp mailing list -Ppp@zzz.org -http://www.zzz.org/mailman/listinfo/ppp - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_20.txt --- a/Lib/email/test/data/msg_20.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -Return-Path: -Delivered-To: bbb@zzz.org -Received: by mail.zzz.org (Postfix, from userid 889) - id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) -MIME-Version: 1.0 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit -Message-ID: <15090.61304.110929.45684@aaa.zzz.org> -From: bbb@ddd.com (John X. Doe) -To: bbb@zzz.org -Cc: ccc@zzz.org -CC: ddd@zzz.org -cc: eee@zzz.org -Subject: This is a test message -Date: Fri, 4 May 2001 14:05:44 -0400 - - -Hi, - -Do you like this message? - --Me diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_21.txt --- a/Lib/email/test/data/msg_21.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -From: aperson@dom.ain -To: bperson@dom.ain -Subject: Test -Content-Type: multipart/mixed; boundary="BOUNDARY" - -MIME message ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -One ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -Two ---BOUNDARY-- -End of MIME message diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_22.txt --- a/Lib/email/test/data/msg_22.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -Mime-Version: 1.0 -Message-Id: -Date: Tue, 16 Oct 2001 13:59:25 +0300 -To: a@example.com -From: b@example.com -Content-Type: multipart/mixed; boundary="============_-1208892523==_============" - ---============_-1208892523==_============ -Content-Type: text/plain; charset="us-ascii" ; format="flowed" - -Text text text. ---============_-1208892523==_============ -Content-Id: -Content-Type: image/jpeg; name="wibble.JPG" - ; x-mac-type="4A504547" - ; x-mac-creator="474B4F4E" -Content-Disposition: attachment; filename="wibble.JPG" -Content-Transfer-Encoding: base64 - -/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB -AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAXABIEBAREA -g6bCjjw/pIZSjO6FWFpldjySOmCNrO7DBZibUXhTwtCixw+GtAijVdqxxaPp0aKvmGXa -qrbBQvms0mAMeYS/3iTV1dG0hHaRNK01XblnWxtVdjkHLMIgTyqnk9VB7CrP2KzIINpa -4O7I+zxYO9WV8jZg71Zlb+8rMDkEirAVQFAUAKAFAAAUAYAUDgADgY6DjpRtXj5RxjHA -4wQRj0wQCMdCAewpaKKK/9k= ---============_-1208892523==_============ -Content-Id: -Content-Type: image/jpeg; name="wibble2.JPG" - ; x-mac-type="4A504547" - ; x-mac-creator="474B4F4E" -Content-Disposition: attachment; filename="wibble2.JPG" -Content-Transfer-Encoding: base64 - -/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB -AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAXABJ0BAREA -/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA -W6NFJJBEkU10kKGTcWMDwxuU+0JHvk8qAtOpNwqSR0n8c3BlDyXHlqsUltHEiTvdXLxR -7vMiGDNJAJWkAMk8ZkCFp5G2oo5W++INrbQtNfTQxJAuXlupz9oS4d5Y1W+E2XlWZJJE -Y7LWYQxTLE1zuMbfBPxw8X2fibVdIbSbI6nLZxX635t9TjtYreWR7WGKJTLJFFKSlozO -0ShxIXM43uC3/9k= ---============_-1208892523==_============ -Content-Type: text/plain; charset="us-ascii" ; format="flowed" - -Text text text. ---============_-1208892523==_============-- - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_23.txt --- a/Lib/email/test/data/msg_23.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -From: aperson@dom.ain -Content-Type: multipart/mixed; boundary="BOUNDARY" - ---BOUNDARY -Content-Type: text/plain - -A message part ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_24.txt --- a/Lib/email/test/data/msg_24.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - ---BOUNDARY - - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_25.txt --- a/Lib/email/test/data/msg_25.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,117 +0,0 @@ -From MAILER-DAEMON Fri Apr 06 16:46:09 2001 -Received: from [204.245.199.98] (helo=zinfandel.lacita.com) - by www.linux.org.uk with esmtp (Exim 3.13 #1) - id 14lYR6-0008Iv-00 - for linuxuser-admin@www.linux.org.uk; Fri, 06 Apr 2001 16:46:09 +0100 -Received: from localhost (localhost) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with internal id JAB03225; Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800) -Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800) -From: Mail Delivery Subsystem -Subject: Returned mail: Too many hops 19 (17 max): from via [199.164.235.226], to -Message-Id: <200104061723.JAB03225@zinfandel.lacita.com> -To: -To: postmaster@zinfandel.lacita.com -MIME-Version: 1.0 -Content-Type: multipart/report; report-type=delivery-status; - bo -Auto-Submitted: auto-generated (failure) - -This is a MIME-encapsulated message - ---JAB03225.986577786/zinfandel.lacita.com - -The original message was received at Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800) -from [199.164.235.226] - - ----- The following addresses have delivery notifications ----- - (unrecoverable error) - - ----- Transcript of session follows ----- -554 Too many hops 19 (17 max): from via [199.164.235.226], to - ---JAB03225.986577786/zinfandel.lacita.com -Content-Type: message/delivery-status - -Reporting-MTA: dns; zinfandel.lacita.com -Received-From-MTA: dns; [199.164.235.226] -Arrival-Date: Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800) - -Final-Recipient: rfc822; scoffman@wellpartner.com -Action: failed -Status: 5.4.6 -Last-Attempt-Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800) - ---JAB03225.986577786/zinfandel.lacita.com -Content-Type: text/rfc822-headers - -Return-Path: linuxuser-admin@www.linux.org.uk -Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03225 for ; Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800) -Received: from zinfandel.lacita.com ([204.245.199.98]) - by - fo -Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03221 for ; Fri, 6 Apr 2001 09:22:18 -0800 (GMT-0800) -Received: from zinfandel.lacita.com ([204.245.199.98]) - by - fo -Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03217 for ; Fri, 6 Apr 2001 09:21:37 -0800 (GMT-0800) -Received: from zinfandel.lacita.com ([204.245.199.98]) - by - fo -Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03213 for ; Fri, 6 Apr 2001 09:20:56 -0800 (GMT-0800) -Received: from zinfandel.lacita.com ([204.245.199.98]) - by - fo -Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03209 for ; Fri, 6 Apr 2001 09:20:15 -0800 (GMT-0800) -Received: from zinfandel.lacita.com ([204.245.199.98]) - by - fo -Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03205 for ; Fri, 6 Apr 2001 09:19:33 -0800 (GMT-0800) -Received: from zinfandel.lacita.com ([204.245.199.98]) - by - fo -Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03201 for ; Fri, 6 Apr 2001 09:18:52 -0800 (GMT-0800) -Received: from zinfandel.lacita.com ([204.245.199.98]) - by - fo -Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03197 for ; Fri, 6 Apr 2001 09:17:54 -0800 (GMT-0800) -Received: from www.linux.org.uk (parcelfarce.linux.theplanet.co.uk [195.92.249.252]) - by - fo -Received: from localhost.localdomain - ([ - by - id -Received: from [212.1.130.11] (helo=s1.uklinux.net ident=root) - by - id - fo -Received: from server (ppp-2-22.cvx4.telinco.net [212.1.149.22]) - by - fo -From: Daniel James -Organization: LinuxUser -To: linuxuser@www.linux.org.uk -X-Mailer: KMail [version 1.1.99] -Content-Type: text/plain; - c -MIME-Version: 1.0 -Message-Id: <01040616033903.00962@server> -Content-Transfer-Encoding: 8bit -Subject: [LinuxUser] bulletin no. 45 -Sender: linuxuser-admin@www.linux.org.uk -Errors-To: linuxuser-admin@www.linux.org.uk -X-BeenThere: linuxuser@www.linux.org.uk -X-Mailman-Version: 2.0.3 -Precedence: bulk -List-Help: -List-Post: -List-Subscribe: , - -List-Unsubscribe: , - -Date: Fri, 6 Apr 2001 16:03:39 +0100 - ---JAB03225.986577786/zinfandel.lacita.com-- - - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_26.txt --- a/Lib/email/test/data/msg_26.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -Received: from xcar [192.168.0.2] by jeeves.wooster.local - (SMTPD32-7.07 EVAL) id AFF92F0214; Sun, 12 May 2002 08:55:37 +0100 -Date: Sun, 12 May 2002 08:56:15 +0100 -From: Father Time -To: timbo@jeeves.wooster.local -Subject: IMAP file test -Message-ID: <6df65d354b.father.time@rpc.wooster.local> -X-Organization: Home -User-Agent: Messenger-Pro/2.50a (MsgServe/1.50) (RISC-OS/4.02) POPstar/2.03 -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary="1618492860--2051301190--113853680" -Status: R -X-UIDL: 319998302 - -This message is in MIME format which your mailer apparently does not support. -You either require a newer version of your software which supports MIME, or -a separate MIME decoding utility. Alternatively, ask the sender of this -message to resend it in a different format. - ---1618492860--2051301190--113853680 -Content-Type: text/plain; charset=us-ascii - -Simple email with attachment. - - ---1618492860--2051301190--113853680 -Content-Type: application/riscos; name="clock.bmp,69c"; type=BMP; - load=&fff69c4b; exec=&355dd4d1; access=&03 -Content-Disposition: attachment; filename="clock.bmp" -Content-Transfer-Encoding: base64 - -Qk12AgAAAAAAAHYAAAAoAAAAIAAAACAAAAABAAQAAAAAAAAAAADXDQAA1w0AAAAAAAAA -AAAAAAAAAAAAiAAAiAAAAIiIAIgAAACIAIgAiIgAALu7uwCIiIgAERHdACLuIgAz//8A -zAAAAN0R3QDu7iIA////AAAAAAAAAAAAAAAAAAAAAAAAAAi3AAAAAAAAADeAAAAAAAAA -C3ADMzMzMANwAAAAAAAAAAAHMAAAAANwAAAAAAAAAACAMAd3zPfwAwgAAAAAAAAIAwd/ -f8x/f3AwgAAAAAAAgDB0x/f3//zPAwgAAAAAAAcHfM9////8z/AwAAAAAAiwd/f3//// -////A4AAAAAAcEx/f///////zAMAAAAAiwfM9////3///8zwOAAAAAcHf3////B///// -8DAAAAALB/f3///wd3d3//AwAAAABwTPf//wCQAAD/zAMAAAAAsEx/f///B////8wDAA -AAAHB39////wf/////AwAAAACwf39///8H/////wMAAAAIcHfM9///B////M8DgAAAAA -sHTH///wf///xAMAAAAACHB3f3//8H////cDgAAAAAALB3zH//D//M9wMAAAAAAAgLB0 -z39///xHAwgAAAAAAAgLB3d3RHd3cDCAAAAAAAAAgLAHd0R3cAMIAAAAAAAAgAgLcAAA -AAMwgAgAAAAACDAAAAu7t7cwAAgDgAAAAABzcIAAAAAAAAgDMwAAAAAAN7uwgAAAAAgH -MzMAAAAACH97tzAAAAALu3c3gAAAAAAL+7tzDABAu7f7cAAAAAAACA+3MA7EQAv/sIAA -AAAAAAAIAAAAAAAAAIAAAAAA - ---1618492860--2051301190--113853680-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_27.txt --- a/Lib/email/test/data/msg_27.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -Return-Path: -Received: by mail.dom.ain (Postfix, from userid 889) - id B9D0AD35DB; Tue, 4 Jun 2002 21:46:59 -0400 (EDT) -Message-ID: <15613.28051.707126.569693@dom.ain> -Date: Tue, 4 Jun 2002 21:46:59 -0400 -MIME-Version: 1.0 -Content-Type: text/plain; charset=us-ascii -Content-Transfer-Encoding: 7bit -Subject: bug demonstration - 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 - more text -From: aperson@dom.ain (Anne P. Erson) -To: bperson@dom.ain (Barney P. Erson) - -test diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_28.txt --- a/Lib/email/test/data/msg_28.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -From: aperson@dom.ain -MIME-Version: 1.0 -Content-Type: multipart/digest; boundary=BOUNDARY - ---BOUNDARY -Content-Type: message/rfc822 - -Content-Type: text/plain; charset=us-ascii -To: aa@bb.org -From: cc@dd.org -Subject: ee - -message 1 - ---BOUNDARY -Content-Type: message/rfc822 - -Content-Type: text/plain; charset=us-ascii -To: aa@bb.org -From: cc@dd.org -Subject: ee - -message 2 - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_29.txt --- a/Lib/email/test/data/msg_29.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -Return-Path: -Delivered-To: bbb@zzz.org -Received: by mail.zzz.org (Postfix, from userid 889) - id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) -MIME-Version: 1.0 -Content-Type: text/plain; charset=us-ascii; - title*0*="us-ascii'en'This%20is%20even%20more%20"; - title*1*="%2A%2A%2Afun%2A%2A%2A%20"; - title*2="isn't it!" -Content-Transfer-Encoding: 7bit -Message-ID: <15090.61304.110929.45684@aaa.zzz.org> -From: bbb@ddd.com (John X. Doe) -To: bbb@zzz.org -Subject: This is a test message -Date: Fri, 4 May 2001 14:05:44 -0400 - - -Hi, - -Do you like this message? - --Me diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_30.txt --- a/Lib/email/test/data/msg_30.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -From: aperson@dom.ain -MIME-Version: 1.0 -Content-Type: multipart/digest; boundary=BOUNDARY - ---BOUNDARY - -Content-Type: text/plain; charset=us-ascii -To: aa@bb.org -From: cc@dd.org -Subject: ee - -message 1 - ---BOUNDARY - -Content-Type: text/plain; charset=us-ascii -To: aa@bb.org -From: cc@dd.org -Subject: ee - -message 2 - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_31.txt --- a/Lib/email/test/data/msg_31.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ -From: aperson@dom.ain -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary=BOUNDARY_ - ---BOUNDARY -Content-Type: text/plain - -message 1 - ---BOUNDARY -Content-Type: text/plain - -message 2 - ---BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_32.txt --- a/Lib/email/test/data/msg_32.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,14 +0,0 @@ -Delivered-To: freebsd-isp@freebsd.org -Date: Tue, 26 Sep 2000 12:23:03 -0500 -From: Anne Person -To: Barney Dude -Subject: Re: Limiting Perl CPU Utilization... -Mime-Version: 1.0 -Content-Type: text/plain; charset*=ansi-x3.4-1968''us-ascii -Content-Disposition: inline -User-Agent: Mutt/1.3.8i -Sender: owner-freebsd-isp@FreeBSD.ORG -Precedence: bulk -X-Loop: FreeBSD.org - -Some message. diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_33.txt --- a/Lib/email/test/data/msg_33.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -Delivered-To: freebsd-isp@freebsd.org -Date: Wed, 27 Sep 2000 11:11:09 -0500 -From: Anne Person -To: Barney Dude -Subject: Re: Limiting Perl CPU Utilization... -Mime-Version: 1.0 -Content-Type: multipart/signed; micalg*=ansi-x3.4-1968''pgp-md5; - protocol*=ansi-x3.4-1968''application%2Fpgp-signature; - boundary*="ansi-x3.4-1968''EeQfGwPcQSOJBaQU" -Content-Disposition: inline -Sender: owner-freebsd-isp@FreeBSD.ORG -Precedence: bulk -X-Loop: FreeBSD.org - - ---EeQfGwPcQSOJBaQU -Content-Type: text/plain; charset*=ansi-x3.4-1968''us-ascii -Content-Disposition: inline -Content-Transfer-Encoding: quoted-printable - -part 1 - ---EeQfGwPcQSOJBaQU -Content-Type: text/plain -Content-Disposition: inline - -part 2 - ---EeQfGwPcQSOJBaQU-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_34.txt --- a/Lib/email/test/data/msg_34.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -From: aperson@dom.ain -To: bperson@dom.ain -Content-Type: multipart/digest; boundary=XYZ - ---XYZ -Content-Type: text/plain - - -This is a text plain part that is counter to recommended practice in -RFC 2046, $5.1.5, but is not illegal - ---XYZ - -From: cperson@dom.ain -To: dperson@dom.ain - -A submessage - ---XYZ-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_35.txt --- a/Lib/email/test/data/msg_35.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -From: aperson@dom.ain -To: bperson@dom.ain -Subject: here's something interesting -counter to RFC 2822, there's no separating newline here diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_36.txt --- a/Lib/email/test/data/msg_36.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -Mime-Version: 1.0 -Content-Type: Multipart/Mixed; Boundary="NextPart" -To: IETF-Announce:; -From: Internet-Drafts@ietf.org -Subject: I-D ACTION:draft-ietf-mboned-mix-00.txt -Date: Tue, 22 Dec 1998 16:55:06 -0500 - ---NextPart - -Blah blah blah - ---NextPart -Content-Type: Multipart/Alternative; Boundary="OtherAccess" - ---OtherAccess -Content-Type: Message/External-body; - access-type="mail-server"; - server="mailserv@ietf.org" - -Content-Type: text/plain -Content-ID: <19981222151406.I-D@ietf.org> - -ENCODING mime -FILE /internet-drafts/draft-ietf-mboned-mix-00.txt - ---OtherAccess -Content-Type: Message/External-body; - name="draft-ietf-mboned-mix-00.txt"; - site="ftp.ietf.org"; - access-type="anon-ftp"; - directory="internet-drafts" - -Content-Type: text/plain -Content-ID: <19981222151406.I-D@ietf.org> - - ---OtherAccess-- - ---NextPart-- - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_37.txt --- a/Lib/email/test/data/msg_37.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -Content-Type: multipart/mixed; boundary=ABCDE - ---ABCDE -Content-Type: text/x-one - -Blah - ---ABCDE ---ABCDE -Content-Type: text/x-two - -Blah - ---ABCDE ---ABCDE ---ABCDE ---ABCDE -Content-Type: text/x-two - -Blah - ---ABCDE-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_38.txt --- a/Lib/email/test/data/msg_38.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,101 +0,0 @@ -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" - -------- =_aaaaaaaaaa0 -Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa1" -Content-ID: <20592.1022586929.1@example.com> - -------- =_aaaaaaaaaa1 -Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa2" -Content-ID: <20592.1022586929.2@example.com> - -------- =_aaaaaaaaaa2 -Content-Type: text/plain -Content-ID: <20592.1022586929.3@example.com> -Content-Description: very tricky -Content-Transfer-Encoding: 7bit - - -Unlike the test test_nested-multiples-with-internal-boundary, this -piece of text not only contains the outer boundary tags -------- =_aaaaaaaaaa1 -and -------- =_aaaaaaaaaa0 -but puts them at the start of a line! And, to be even nastier, it -even includes a couple of end tags, such as this one: - -------- =_aaaaaaaaaa1-- - -and this one, which is from a multipart we haven't even seen yet! - -------- =_aaaaaaaaaa4-- - -This will, I'm sure, cause much breakage of MIME parsers. But, as -far as I can tell, it's perfectly legal. I have not yet ever seen -a case of this in the wild, but I've seen *similar* things. - - -------- =_aaaaaaaaaa2 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.4@example.com> -Content-Description: patch2 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa2-- - -------- =_aaaaaaaaaa1 -Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa3" -Content-ID: <20592.1022586929.6@example.com> - -------- =_aaaaaaaaaa3 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.7@example.com> -Content-Description: patch3 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa3 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.8@example.com> -Content-Description: patch4 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa3-- - -------- =_aaaaaaaaaa1 -Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa4" -Content-ID: <20592.1022586929.10@example.com> - -------- =_aaaaaaaaaa4 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.11@example.com> -Content-Description: patch5 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa4 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.12@example.com> -Content-Description: patch6 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa4-- - -------- =_aaaaaaaaaa1-- - -------- =_aaaaaaaaaa0 -Content-Type: text/plain; charset="us-ascii" -Content-ID: <20592.1022586929.15@example.com> - --- -It's never too late to have a happy childhood. - -------- =_aaaaaaaaaa0-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_39.txt --- a/Lib/email/test/data/msg_39.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" - -------- =_aaaaaaaaaa0 -Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa1" -Content-ID: <20592.1022586929.1@example.com> - -------- =_aaaaaaaaaa1 -Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1" -Content-ID: <20592.1022586929.2@example.com> - -------- =_aaaaaaaaaa1 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.3@example.com> -Content-Description: patch1 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa1 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.4@example.com> -Content-Description: patch2 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa1-- - -------- =_aaaaaaaaaa1 -Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1" -Content-ID: <20592.1022586929.6@example.com> - -------- =_aaaaaaaaaa1 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.7@example.com> -Content-Description: patch3 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa1 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.8@example.com> -Content-Description: patch4 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa1-- - -------- =_aaaaaaaaaa1 -Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1" -Content-ID: <20592.1022586929.10@example.com> - -------- =_aaaaaaaaaa1 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.11@example.com> -Content-Description: patch5 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa1 -Content-Type: application/octet-stream -Content-ID: <20592.1022586929.12@example.com> -Content-Description: patch6 -Content-Transfer-Encoding: base64 - -XXX - -------- =_aaaaaaaaaa1-- - -------- =_aaaaaaaaaa1-- - -------- =_aaaaaaaaaa0 -Content-Type: text/plain; charset="us-ascii" -Content-ID: <20592.1022586929.15@example.com> - --- -It's never too late to have a happy childhood. - -------- =_aaaaaaaaaa0-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_40.txt --- a/Lib/email/test/data/msg_40.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,10 +0,0 @@ -MIME-Version: 1.0 -Content-Type: text/html; boundary="--961284236552522269" - -----961284236552522269 -Content-Type: text/html; -Content-Transfer-Encoding: 7Bit - - - -----961284236552522269-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_41.txt --- a/Lib/email/test/data/msg_41.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -From: "Allison Dunlap" -To: yyy@example.com -Subject: 64423 -Date: Sun, 11 Jul 2004 16:09:27 -0300 -MIME-Version: 1.0 -Content-Type: multipart/alternative; - -Blah blah blah diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_42.txt --- a/Lib/email/test/data/msg_42.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -Content-Type: multipart/mixed; boundary="AAA" -From: Mail Delivery Subsystem -To: yyy@example.com - -This is a MIME-encapsulated message - ---AAA - -Stuff - ---AAA -Content-Type: message/rfc822 - -From: webmaster@python.org -To: zzz@example.com -Content-Type: multipart/mixed; boundary="BBB" - ---BBB-- - ---AAA-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_43.txt --- a/Lib/email/test/data/msg_43.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,217 +0,0 @@ -From SRS0=aO/p=ON=bag.python.org=None@bounce2.pobox.com Fri Nov 26 21:40:36 2004 -X-VM-v5-Data: ([nil nil nil nil nil nil nil nil nil] - [nil nil nil nil nil nil nil "MAILER DAEMON <>" "MAILER DAEMON <>" nil nil "Banned file: auto__mail.python.bat in mail from you" "^From:" nil nil nil nil "Banned file: auto__mail.python.bat in mail from you" nil nil nil nil nil nil nil] - nil) -MIME-Version: 1.0 -Message-Id: -Content-Type: multipart/report; report-type=delivery-status; - charset=utf-8; - boundary="----------=_1101526904-1956-5" -X-Virus-Scanned: by XS4ALL Virus Scanner -X-UIDL: 4\G!!! -To: -Subject: Banned file: auto__mail.python.bat in mail from you -Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -This is a multi-part message in MIME format... - -------------=_1101526904-1956-5 -Content-Type: text/plain; charset="utf-8" -Content-Disposition: inline -Content-Transfer-Encoding: 7bit - -BANNED FILENAME ALERT - -Your message to: xxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxx@dot.ca.gov, xxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxxxxx@dot.ca.gov -was blocked by our Spam Firewall. The email you sent with the following subject has NOT BEEN DELIVERED: - -Subject: Delivery_failure_notice - -An attachment in that mail was of a file type that the Spam Firewall is set to block. - - - -------------=_1101526904-1956-5 -Content-Type: message/delivery-status -Content-Disposition: inline -Content-Transfer-Encoding: 7bit -Content-Description: Delivery error report - -Reporting-MTA: dns; sacspam01.dot.ca.gov -Received-From-MTA: smtp; sacspam01.dot.ca.gov ([127.0.0.1]) -Arrival-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -Final-Recipient: rfc822; xxxxxxx@dot.ca.gov -Action: failed -Status: 5.7.1 -Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat -Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) - -------------=_1101526904-1956-5 -Content-Type: text/rfc822-headers -Content-Disposition: inline -Content-Transfer-Encoding: 7bit -Content-Description: Undelivered-message headers - -Received: from kgsav.org (ppp-70-242-162-63.dsl.spfdmo.swbell.net [70.242.162.63]) - by sacspam01.dot.ca.gov (Spam Firewall) with SMTP - id A232AD03DE3A; Fri, 26 Nov 2004 19:41:35 -0800 (PST) -From: webmaster@python.org -To: xxxxx@dot.ca.gov -Date: Sat, 27 Nov 2004 03:35:30 UTC -Subject: Delivery_failure_notice -Importance: Normal -X-Priority: 3 (Normal) -X-MSMail-Priority: Normal -Message-ID: -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary="====67bd2b7a5.f99f7" -Content-Transfer-Encoding: 7bit - -------------=_1101526904-1956-5-- - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_44.txt --- a/Lib/email/test/data/msg_44.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -Return-Path: -Delivered-To: barry@python.org -Received: by mail.python.org (Postfix, from userid 889) - id C2BF0D37C6; Tue, 11 Sep 2001 00:05:05 -0400 (EDT) -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary="h90VIIIKmx" -Content-Transfer-Encoding: 7bit -Message-ID: <15261.36209.358846.118674@anthem.python.org> -From: barry@python.org (Barry A. Warsaw) -To: barry@python.org -Subject: a simple multipart -Date: Tue, 11 Sep 2001 00:05:05 -0400 -X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid -X-Attribution: BAW -X-Oblique-Strategy: Make a door into a window - - ---h90VIIIKmx -Content-Type: text/plain; name="msg.txt" -Content-Transfer-Encoding: 7bit - -a simple kind of mirror -to reflect upon our own - ---h90VIIIKmx -Content-Type: text/plain; name="msg.txt" -Content-Transfer-Encoding: 7bit - -a simple kind of mirror -to reflect upon our own - ---h90VIIIKmx-- - diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_45.txt --- a/Lib/email/test/data/msg_45.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -From: -To: -Subject: test -X-Long-Line: Some really long line contains a lot of text and thus has to be rewrapped because it is some - really long - line -MIME-Version: 1.0 -Content-Type: multipart/signed; boundary="borderline"; - protocol="application/pgp-signature"; micalg=pgp-sha1 - -This is an OpenPGP/MIME signed message (RFC 2440 and 3156) ---borderline -Content-Type: text/plain -X-Long-Line: Another really long line contains a lot of text and thus has to be rewrapped because it is another - really long - line - -This is the signed contents. - ---borderline -Content-Type: application/pgp-signature; name="signature.asc" -Content-Description: OpenPGP digital signature -Content-Disposition: attachment; filename="signature.asc" - ------BEGIN PGP SIGNATURE----- -Version: GnuPG v2.0.6 (GNU/Linux) - -iD8DBQFG03voRhp6o4m9dFsRApSZAKCCAN3IkJlVRg6NvAiMHlvvIuMGPQCeLZtj -FGwfnRHFBFO/S4/DKysm0lI= -=t7+s ------END PGP SIGNATURE----- - ---borderline-- diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/data/msg_46.txt --- a/Lib/email/test/data/msg_46.txt Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -Return-Path: -Delivery-Date: Mon, 08 Feb 2010 14:05:16 +0100 -Received: from example.org (example.org [64.5.53.58]) - by example.net (node=mxbap2) with ESMTP (Nemesis) - id UNIQUE for someone@example.com; Mon, 08 Feb 2010 14:05:16 +0100 -Date: Mon, 01 Feb 2010 12:21:16 +0100 -From: "Sender" -To: -Subject: GroupwiseForwardingTest -Mime-Version: 1.0 -Content-Type: message/rfc822 - -Return-path: -Message-ID: <4B66B890.4070408@teconcept.de> -Date: Mon, 01 Feb 2010 12:18:40 +0100 -From: "Dr. Sender" -MIME-Version: 1.0 -To: "Recipient" -Subject: GroupwiseForwardingTest -Content-Type: text/plain; charset=ISO-8859-15 -Content-Transfer-Encoding: 7bit - -Testing email forwarding with Groupwise 1.2.2010 diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/test_email.py --- a/Lib/email/test/test_email.py Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4095 +0,0 @@ -# Copyright (C) 2001-2010 Python Software Foundation -# Contact: email-sig@python.org -# email package unit tests - -import os -import re -import sys -import time -import base64 -import difflib -import unittest -import warnings -import textwrap - -from io import StringIO, BytesIO -from itertools import chain - -import email - -from email.charset import Charset -from email.header import Header, decode_header, make_header -from email.parser import Parser, HeaderParser -from email.generator import Generator, DecodedGenerator -from email.message import Message -from email.mime.application import MIMEApplication -from email.mime.audio import MIMEAudio -from email.mime.text import MIMEText -from email.mime.image import MIMEImage -from email.mime.base import MIMEBase -from email.mime.message import MIMEMessage -from email.mime.multipart import MIMEMultipart -from email import utils -from email import errors -from email import encoders -from email import iterators -from email import base64mime -from email import quoprimime - -from test.support import findfile, run_unittest, unlink -from email.test import __file__ as landmark - - -NL = '\n' -EMPTYSTRING = '' -SPACE = ' ' - - - -def openfile(filename, *args, **kws): - path = os.path.join(os.path.dirname(landmark), 'data', filename) - return open(path, *args, **kws) - - - -# Base test class -class TestEmailBase(unittest.TestCase): - def ndiffAssertEqual(self, first, second): - """Like assertEqual except use ndiff for readable output.""" - if first != second: - sfirst = str(first) - ssecond = str(second) - rfirst = [repr(line) for line in sfirst.splitlines()] - rsecond = [repr(line) for line in ssecond.splitlines()] - diff = difflib.ndiff(rfirst, rsecond) - raise self.failureException(NL + NL.join(diff)) - - def _msgobj(self, filename): - with openfile(findfile(filename)) as fp: - return email.message_from_file(fp) - - - -# Test various aspects of the Message class's API -class TestMessageAPI(TestEmailBase): - def test_get_all(self): - eq = self.assertEqual - msg = self._msgobj('msg_20.txt') - eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org']) - eq(msg.get_all('xx', 'n/a'), 'n/a') - - def test_getset_charset(self): - eq = self.assertEqual - msg = Message() - eq(msg.get_charset(), None) - charset = Charset('iso-8859-1') - msg.set_charset(charset) - eq(msg['mime-version'], '1.0') - eq(msg.get_content_type(), 'text/plain') - eq(msg['content-type'], 'text/plain; charset="iso-8859-1"') - eq(msg.get_param('charset'), 'iso-8859-1') - eq(msg['content-transfer-encoding'], 'quoted-printable') - eq(msg.get_charset().input_charset, 'iso-8859-1') - # Remove the charset - msg.set_charset(None) - eq(msg.get_charset(), None) - eq(msg['content-type'], 'text/plain') - # Try adding a charset when there's already MIME headers present - msg = Message() - msg['MIME-Version'] = '2.0' - msg['Content-Type'] = 'text/x-weird' - msg['Content-Transfer-Encoding'] = 'quinted-puntable' - msg.set_charset(charset) - eq(msg['mime-version'], '2.0') - eq(msg['content-type'], 'text/x-weird; charset="iso-8859-1"') - eq(msg['content-transfer-encoding'], 'quinted-puntable') - - def test_set_charset_from_string(self): - eq = self.assertEqual - msg = Message() - msg.set_charset('us-ascii') - eq(msg.get_charset().input_charset, 'us-ascii') - eq(msg['content-type'], 'text/plain; charset="us-ascii"') - - def test_set_payload_with_charset(self): - msg = Message() - charset = Charset('iso-8859-1') - msg.set_payload('This is a string payload', charset) - self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1') - - def test_get_charsets(self): - eq = self.assertEqual - - msg = self._msgobj('msg_08.txt') - charsets = msg.get_charsets() - eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r']) - - msg = self._msgobj('msg_09.txt') - charsets = msg.get_charsets('dingbat') - eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat', - 'koi8-r']) - - msg = self._msgobj('msg_12.txt') - charsets = msg.get_charsets() - eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2', - 'iso-8859-3', 'us-ascii', 'koi8-r']) - - def test_get_filename(self): - eq = self.assertEqual - - msg = self._msgobj('msg_04.txt') - filenames = [p.get_filename() for p in msg.get_payload()] - eq(filenames, ['msg.txt', 'msg.txt']) - - msg = self._msgobj('msg_07.txt') - subpart = msg.get_payload(1) - eq(subpart.get_filename(), 'dingusfish.gif') - - def test_get_filename_with_name_parameter(self): - eq = self.assertEqual - - msg = self._msgobj('msg_44.txt') - filenames = [p.get_filename() for p in msg.get_payload()] - eq(filenames, ['msg.txt', 'msg.txt']) - - def test_get_boundary(self): - eq = self.assertEqual - msg = self._msgobj('msg_07.txt') - # No quotes! - eq(msg.get_boundary(), 'BOUNDARY') - - def test_set_boundary(self): - eq = self.assertEqual - # This one has no existing boundary parameter, but the Content-Type: - # header appears fifth. - msg = self._msgobj('msg_01.txt') - msg.set_boundary('BOUNDARY') - header, value = msg.items()[4] - eq(header.lower(), 'content-type') - eq(value, 'text/plain; charset="us-ascii"; boundary="BOUNDARY"') - # This one has a Content-Type: header, with a boundary, stuck in the - # middle of its headers. Make sure the order is preserved; it should - # be fifth. - msg = self._msgobj('msg_04.txt') - msg.set_boundary('BOUNDARY') - header, value = msg.items()[4] - eq(header.lower(), 'content-type') - eq(value, 'multipart/mixed; boundary="BOUNDARY"') - # And this one has no Content-Type: header at all. - msg = self._msgobj('msg_03.txt') - self.assertRaises(errors.HeaderParseError, - msg.set_boundary, 'BOUNDARY') - - def test_make_boundary(self): - msg = MIMEMultipart('form-data') - # Note that when the boundary gets created is an implementation - # detail and might change. - self.assertEqual(msg.items()[0][1], 'multipart/form-data') - # Trigger creation of boundary - msg.as_string() - self.assertEqual(msg.items()[0][1][:33], - 'multipart/form-data; boundary="==') - # XXX: there ought to be tests of the uniqueness of the boundary, too. - - def test_message_rfc822_only(self): - # Issue 7970: message/rfc822 not in multipart parsed by - # HeaderParser caused an exception when flattened. - with openfile(findfile('msg_46.txt')) as fp: - msgdata = fp.read() - parser = HeaderParser() - msg = parser.parsestr(msgdata) - out = StringIO() - gen = Generator(out, True, 0) - gen.flatten(msg, False) - self.assertEqual(out.getvalue(), msgdata) - - def test_get_decoded_payload(self): - eq = self.assertEqual - msg = self._msgobj('msg_10.txt') - # The outer message is a multipart - eq(msg.get_payload(decode=True), None) - # Subpart 1 is 7bit encoded - eq(msg.get_payload(0).get_payload(decode=True), - b'This is a 7bit encoded message.\n') - # Subpart 2 is quopri - eq(msg.get_payload(1).get_payload(decode=True), - b'\xa1This is a Quoted Printable encoded message!\n') - # Subpart 3 is base64 - eq(msg.get_payload(2).get_payload(decode=True), - b'This is a Base64 encoded message.') - # Subpart 4 is base64 with a trailing newline, which - # used to be stripped (issue 7143). - eq(msg.get_payload(3).get_payload(decode=True), - b'This is a Base64 encoded message.\n') - # Subpart 5 has no Content-Transfer-Encoding: header. - eq(msg.get_payload(4).get_payload(decode=True), - b'This has no Content-Transfer-Encoding: header.\n') - - def test_get_decoded_uu_payload(self): - eq = self.assertEqual - msg = Message() - msg.set_payload('begin 666 -\n+:&5L;&\\@=V]R;&0 \n \nend\n') - for cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): - msg['content-transfer-encoding'] = cte - eq(msg.get_payload(decode=True), b'hello world') - # Now try some bogus data - msg.set_payload('foo') - eq(msg.get_payload(decode=True), b'foo') - - def test_decoded_generator(self): - eq = self.assertEqual - msg = self._msgobj('msg_07.txt') - with openfile('msg_17.txt') as fp: - text = fp.read() - s = StringIO() - g = DecodedGenerator(s) - g.flatten(msg) - eq(s.getvalue(), text) - - def test__contains__(self): - msg = Message() - msg['From'] = 'Me' - msg['to'] = 'You' - # Check for case insensitivity - self.assertTrue('from' in msg) - self.assertTrue('From' in msg) - self.assertTrue('FROM' in msg) - self.assertTrue('to' in msg) - self.assertTrue('To' in msg) - self.assertTrue('TO' in msg) - - def test_as_string(self): - eq = self.ndiffAssertEqual - msg = self._msgobj('msg_01.txt') - with openfile('msg_01.txt') as fp: - text = fp.read() - eq(text, str(msg)) - fullrepr = msg.as_string(unixfrom=True) - lines = fullrepr.split('\n') - self.assertTrue(lines[0].startswith('From ')) - eq(text, NL.join(lines[1:])) - - def test_bad_param(self): - msg = email.message_from_string("Content-Type: blarg; baz; boo\n") - self.assertEqual(msg.get_param('baz'), '') - - def test_missing_filename(self): - msg = email.message_from_string("From: foo\n") - self.assertEqual(msg.get_filename(), None) - - def test_bogus_filename(self): - msg = email.message_from_string( - "Content-Disposition: blarg; filename\n") - self.assertEqual(msg.get_filename(), '') - - def test_missing_boundary(self): - msg = email.message_from_string("From: foo\n") - self.assertEqual(msg.get_boundary(), None) - - def test_get_params(self): - eq = self.assertEqual - msg = email.message_from_string( - 'X-Header: foo=one; bar=two; baz=three\n') - eq(msg.get_params(header='x-header'), - [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]) - msg = email.message_from_string( - 'X-Header: foo; bar=one; baz=two\n') - eq(msg.get_params(header='x-header'), - [('foo', ''), ('bar', 'one'), ('baz', 'two')]) - eq(msg.get_params(), None) - msg = email.message_from_string( - 'X-Header: foo; bar="one"; baz=two\n') - eq(msg.get_params(header='x-header'), - [('foo', ''), ('bar', 'one'), ('baz', 'two')]) - - def test_get_param_liberal(self): - msg = Message() - msg['Content-Type'] = 'Content-Type: Multipart/mixed; boundary = "CPIMSSMTPC06p5f3tG"' - self.assertEqual(msg.get_param('boundary'), 'CPIMSSMTPC06p5f3tG') - - def test_get_param(self): - eq = self.assertEqual - msg = email.message_from_string( - "X-Header: foo=one; bar=two; baz=three\n") - eq(msg.get_param('bar', header='x-header'), 'two') - eq(msg.get_param('quuz', header='x-header'), None) - eq(msg.get_param('quuz'), None) - msg = email.message_from_string( - 'X-Header: foo; bar="one"; baz=two\n') - eq(msg.get_param('foo', header='x-header'), '') - eq(msg.get_param('bar', header='x-header'), 'one') - eq(msg.get_param('baz', header='x-header'), 'two') - # XXX: We are not RFC-2045 compliant! We cannot parse: - # msg["Content-Type"] = 'text/plain; weird="hey; dolly? [you] @ <\\"home\\">?"' - # msg.get_param("weird") - # yet. - - def test_get_param_funky_continuation_lines(self): - msg = self._msgobj('msg_22.txt') - self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG') - - def test_get_param_with_semis_in_quotes(self): - msg = email.message_from_string( - 'Content-Type: image/pjpeg; name="Jim&&Jill"\n') - self.assertEqual(msg.get_param('name'), 'Jim&&Jill') - self.assertEqual(msg.get_param('name', unquote=False), - '"Jim&&Jill"') - - def test_get_param_with_quotes(self): - msg = email.message_from_string( - 'Content-Type: foo; bar*0="baz\\"foobar"; bar*1="\\"baz"') - self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz') - msg = email.message_from_string( - "Content-Type: foo; bar*0=\"baz\\\"foobar\"; bar*1=\"\\\"baz\"") - self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz') - - def test_field_containment(self): - unless = self.assertTrue - msg = email.message_from_string('Header: exists') - unless('header' in msg) - unless('Header' in msg) - unless('HEADER' in msg) - self.assertFalse('headerx' in msg) - - def test_set_param(self): - eq = self.assertEqual - msg = Message() - msg.set_param('charset', 'iso-2022-jp') - eq(msg.get_param('charset'), 'iso-2022-jp') - msg.set_param('importance', 'high value') - eq(msg.get_param('importance'), 'high value') - eq(msg.get_param('importance', unquote=False), '"high value"') - eq(msg.get_params(), [('text/plain', ''), - ('charset', 'iso-2022-jp'), - ('importance', 'high value')]) - eq(msg.get_params(unquote=False), [('text/plain', ''), - ('charset', '"iso-2022-jp"'), - ('importance', '"high value"')]) - msg.set_param('charset', 'iso-9999-xx', header='X-Jimmy') - eq(msg.get_param('charset', header='X-Jimmy'), 'iso-9999-xx') - - def test_del_param(self): - eq = self.assertEqual - msg = self._msgobj('msg_05.txt') - eq(msg.get_params(), - [('multipart/report', ''), ('report-type', 'delivery-status'), - ('boundary', 'D1690A7AC1.996856090/mail.example.com')]) - old_val = msg.get_param("report-type") - msg.del_param("report-type") - eq(msg.get_params(), - [('multipart/report', ''), - ('boundary', 'D1690A7AC1.996856090/mail.example.com')]) - msg.set_param("report-type", old_val) - eq(msg.get_params(), - [('multipart/report', ''), - ('boundary', 'D1690A7AC1.996856090/mail.example.com'), - ('report-type', old_val)]) - - def test_del_param_on_other_header(self): - msg = Message() - msg.add_header('Content-Disposition', 'attachment', filename='bud.gif') - msg.del_param('filename', 'content-disposition') - self.assertEqual(msg['content-disposition'], 'attachment') - - def test_set_type(self): - eq = self.assertEqual - msg = Message() - self.assertRaises(ValueError, msg.set_type, 'text') - msg.set_type('text/plain') - eq(msg['content-type'], 'text/plain') - msg.set_param('charset', 'us-ascii') - eq(msg['content-type'], 'text/plain; charset="us-ascii"') - msg.set_type('text/html') - eq(msg['content-type'], 'text/html; charset="us-ascii"') - - def test_set_type_on_other_header(self): - msg = Message() - msg['X-Content-Type'] = 'text/plain' - msg.set_type('application/octet-stream', 'X-Content-Type') - self.assertEqual(msg['x-content-type'], 'application/octet-stream') - - def test_get_content_type_missing(self): - msg = Message() - self.assertEqual(msg.get_content_type(), 'text/plain') - - def test_get_content_type_missing_with_default_type(self): - msg = Message() - msg.set_default_type('message/rfc822') - self.assertEqual(msg.get_content_type(), 'message/rfc822') - - def test_get_content_type_from_message_implicit(self): - msg = self._msgobj('msg_30.txt') - self.assertEqual(msg.get_payload(0).get_content_type(), - 'message/rfc822') - - def test_get_content_type_from_message_explicit(self): - msg = self._msgobj('msg_28.txt') - self.assertEqual(msg.get_payload(0).get_content_type(), - 'message/rfc822') - - def test_get_content_type_from_message_text_plain_implicit(self): - msg = self._msgobj('msg_03.txt') - self.assertEqual(msg.get_content_type(), 'text/plain') - - def test_get_content_type_from_message_text_plain_explicit(self): - msg = self._msgobj('msg_01.txt') - self.assertEqual(msg.get_content_type(), 'text/plain') - - def test_get_content_maintype_missing(self): - msg = Message() - self.assertEqual(msg.get_content_maintype(), 'text') - - def test_get_content_maintype_missing_with_default_type(self): - msg = Message() - msg.set_default_type('message/rfc822') - self.assertEqual(msg.get_content_maintype(), 'message') - - def test_get_content_maintype_from_message_implicit(self): - msg = self._msgobj('msg_30.txt') - self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message') - - def test_get_content_maintype_from_message_explicit(self): - msg = self._msgobj('msg_28.txt') - self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message') - - def test_get_content_maintype_from_message_text_plain_implicit(self): - msg = self._msgobj('msg_03.txt') - self.assertEqual(msg.get_content_maintype(), 'text') - - def test_get_content_maintype_from_message_text_plain_explicit(self): - msg = self._msgobj('msg_01.txt') - self.assertEqual(msg.get_content_maintype(), 'text') - - def test_get_content_subtype_missing(self): - msg = Message() - self.assertEqual(msg.get_content_subtype(), 'plain') - - def test_get_content_subtype_missing_with_default_type(self): - msg = Message() - msg.set_default_type('message/rfc822') - self.assertEqual(msg.get_content_subtype(), 'rfc822') - - def test_get_content_subtype_from_message_implicit(self): - msg = self._msgobj('msg_30.txt') - self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822') - - def test_get_content_subtype_from_message_explicit(self): - msg = self._msgobj('msg_28.txt') - self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822') - - def test_get_content_subtype_from_message_text_plain_implicit(self): - msg = self._msgobj('msg_03.txt') - self.assertEqual(msg.get_content_subtype(), 'plain') - - def test_get_content_subtype_from_message_text_plain_explicit(self): - msg = self._msgobj('msg_01.txt') - self.assertEqual(msg.get_content_subtype(), 'plain') - - def test_get_content_maintype_error(self): - msg = Message() - msg['Content-Type'] = 'no-slash-in-this-string' - self.assertEqual(msg.get_content_maintype(), 'text') - - def test_get_content_subtype_error(self): - msg = Message() - msg['Content-Type'] = 'no-slash-in-this-string' - self.assertEqual(msg.get_content_subtype(), 'plain') - - def test_replace_header(self): - eq = self.assertEqual - msg = Message() - msg.add_header('First', 'One') - msg.add_header('Second', 'Two') - msg.add_header('Third', 'Three') - eq(msg.keys(), ['First', 'Second', 'Third']) - eq(msg.values(), ['One', 'Two', 'Three']) - msg.replace_header('Second', 'Twenty') - eq(msg.keys(), ['First', 'Second', 'Third']) - eq(msg.values(), ['One', 'Twenty', 'Three']) - msg.add_header('First', 'Eleven') - msg.replace_header('First', 'One Hundred') - eq(msg.keys(), ['First', 'Second', 'Third', 'First']) - eq(msg.values(), ['One Hundred', 'Twenty', 'Three', 'Eleven']) - self.assertRaises(KeyError, msg.replace_header, 'Fourth', 'Missing') - - def test_broken_base64_payload(self): - x = 'AwDp0P7//y6LwKEAcPa/6Q=9' - msg = Message() - msg['content-type'] = 'audio/x-midi' - msg['content-transfer-encoding'] = 'base64' - msg.set_payload(x) - self.assertEqual(msg.get_payload(decode=True), - bytes(x, 'raw-unicode-escape')) - - # Issue 1078919 - def test_ascii_add_header(self): - msg = Message() - msg.add_header('Content-Disposition', 'attachment', - filename='bud.gif') - self.assertEqual('attachment; filename="bud.gif"', - msg['Content-Disposition']) - - def test_noascii_add_header(self): - msg = Message() - msg.add_header('Content-Disposition', 'attachment', - filename="Fußballer.ppt") - self.assertEqual( - 'attachment; filename*=utf-8\'\'Fu%C3%9Fballer.ppt', - msg['Content-Disposition']) - - def test_nonascii_add_header_via_triple(self): - msg = Message() - msg.add_header('Content-Disposition', 'attachment', - filename=('iso-8859-1', '', 'Fußballer.ppt')) - self.assertEqual( - 'attachment; filename*=iso-8859-1\'\'Fu%DFballer.ppt', - msg['Content-Disposition']) - - def test_ascii_add_header_with_tspecial(self): - msg = Message() - msg.add_header('Content-Disposition', 'attachment', - filename="windows [filename].ppt") - self.assertEqual( - 'attachment; filename="windows [filename].ppt"', - msg['Content-Disposition']) - - def test_nonascii_add_header_with_tspecial(self): - msg = Message() - msg.add_header('Content-Disposition', 'attachment', - filename="Fußballer [filename].ppt") - self.assertEqual( - "attachment; filename*=utf-8''Fu%C3%9Fballer%20%5Bfilename%5D.ppt", - msg['Content-Disposition']) - - # Issue 5871: reject an attempt to embed a header inside a header value - # (header injection attack). - def test_embeded_header_via_Header_rejected(self): - msg = Message() - msg['Dummy'] = Header('dummy\nX-Injected-Header: test') - self.assertRaises(errors.HeaderParseError, msg.as_string) - - def test_embeded_header_via_string_rejected(self): - msg = Message() - msg['Dummy'] = 'dummy\nX-Injected-Header: test' - self.assertRaises(errors.HeaderParseError, msg.as_string) - - -# Test the email.encoders module -class TestEncoders(unittest.TestCase): - def test_encode_empty_payload(self): - eq = self.assertEqual - msg = Message() - msg.set_charset('us-ascii') - eq(msg['content-transfer-encoding'], '7bit') - - def test_default_cte(self): - eq = self.assertEqual - # 7bit data and the default us-ascii _charset - msg = MIMEText('hello world') - eq(msg['content-transfer-encoding'], '7bit') - # Similar, but with 8bit data - msg = MIMEText('hello \xf8 world') - eq(msg['content-transfer-encoding'], '8bit') - # And now with a different charset - msg = MIMEText('hello \xf8 world', _charset='iso-8859-1') - eq(msg['content-transfer-encoding'], 'quoted-printable') - - def test_encode7or8bit(self): - # Make sure a charset whose input character set is 8bit but - # whose output character set is 7bit gets a transfer-encoding - # of 7bit. - eq = self.assertEqual - msg = MIMEText('文', _charset='euc-jp') - eq(msg['content-transfer-encoding'], '7bit') - - -# Test long header wrapping -class TestLongHeaders(TestEmailBase): - def test_split_long_continuation(self): - eq = self.ndiffAssertEqual - msg = email.message_from_string("""\ -Subject: bug demonstration -\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 -\tmore text - -test -""") - sfp = StringIO() - g = Generator(sfp) - g.flatten(msg) - eq(sfp.getvalue(), """\ -Subject: bug demonstration -\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 -\tmore text - -test -""") - - def test_another_long_almost_unsplittable_header(self): - eq = self.ndiffAssertEqual - hstr = """\ -bug demonstration -\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 -\tmore text""" - h = Header(hstr, continuation_ws='\t') - eq(h.encode(), """\ -bug demonstration -\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 -\tmore text""") - h = Header(hstr.replace('\t', ' ')) - eq(h.encode(), """\ -bug demonstration - 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 - more text""") - - def test_long_nonstring(self): - eq = self.ndiffAssertEqual - g = Charset("iso-8859-1") - cz = Charset("iso-8859-2") - utf8 = Charset("utf-8") - g_head = (b'Die Mieter treten hier ein werden mit einem Foerderband ' - b'komfortabel den Korridor entlang, an s\xfcdl\xfcndischen ' - b'Wandgem\xe4lden vorbei, gegen die rotierenden Klingen ' - b'bef\xf6rdert. ') - cz_head = (b'Finan\xe8ni metropole se hroutily pod tlakem jejich ' - b'd\xf9vtipu.. ') - utf8_head = ('\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f' - '\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00' - '\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c' - '\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067' - '\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das ' - 'Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder ' - 'die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066' - '\u3044\u307e\u3059\u3002') - h = Header(g_head, g, header_name='Subject') - h.append(cz_head, cz) - h.append(utf8_head, utf8) - msg = Message() - msg['Subject'] = h - sfp = StringIO() - g = Generator(sfp) - g.flatten(msg) - eq(sfp.getvalue(), """\ -Subject: =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerderb?= - =?iso-8859-1?q?and_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndischen?= - =?iso-8859-1?q?_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Klingen_bef?= - =?iso-8859-1?q?=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se_hrouti?= - =?iso-8859-2?q?ly_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= =?utf-8?b?5q2j56K6?= - =?utf-8?b?44Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?= - =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go44Gv44Gn44Gf44KJ?= - =?utf-8?b?44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGFzIE51bnN0dWNr?= - =?utf-8?b?IGdpdCB1bmQgU2xvdGVybWV5ZXI/IEphISBCZWloZXJodW5kIGRhcyBPZGVyIGRp?= - =?utf-8?b?ZSBGbGlwcGVyd2FsZHQgZ2Vyc3B1dC7jgI3jgajoqIDjgaPjgabjgYTjgb7jgZk=?= - =?utf-8?b?44CC?= - -""") - eq(h.encode(maxlinelen=76), """\ -=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerde?= - =?iso-8859-1?q?rband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndis?= - =?iso-8859-1?q?chen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Klinge?= - =?iso-8859-1?q?n_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se?= - =?iso-8859-2?q?_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= - =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb?= - =?utf-8?b?44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go?= - =?utf-8?b?44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBp?= - =?utf-8?b?c3QgZGFzIE51bnN0dWNrIGdpdCB1bmQgU2xvdGVybWV5ZXI/IEphISBCZWlo?= - =?utf-8?b?ZXJodW5kIGRhcyBPZGVyIGRpZSBGbGlwcGVyd2FsZHQgZ2Vyc3B1dC7jgI0=?= - =?utf-8?b?44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=""") - - def test_long_header_encode(self): - eq = self.ndiffAssertEqual - h = Header('wasnipoop; giraffes="very-long-necked-animals"; ' - 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', - header_name='X-Foobar-Spoink-Defrobnit') - eq(h.encode(), '''\ -wasnipoop; giraffes="very-long-necked-animals"; - spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') - - def test_long_header_encode_with_tab_continuation_is_just_a_hint(self): - eq = self.ndiffAssertEqual - h = Header('wasnipoop; giraffes="very-long-necked-animals"; ' - 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', - header_name='X-Foobar-Spoink-Defrobnit', - continuation_ws='\t') - eq(h.encode(), '''\ -wasnipoop; giraffes="very-long-necked-animals"; - spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') - - def test_long_header_encode_with_tab_continuation(self): - eq = self.ndiffAssertEqual - h = Header('wasnipoop; giraffes="very-long-necked-animals";\t' - 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', - header_name='X-Foobar-Spoink-Defrobnit', - continuation_ws='\t') - eq(h.encode(), '''\ -wasnipoop; giraffes="very-long-necked-animals"; -\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') - - def test_header_encode_with_different_output_charset(self): - h = Header('文', 'euc-jp') - self.assertEqual(h.encode(), "=?iso-2022-jp?b?GyRCSjgbKEI=?=") - - def test_long_header_encode_with_different_output_charset(self): - h = Header(b'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5\xa4\xec\xa4' - b'\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2\xf1\xbc\xd4' - b'\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3\xa4\xc6\xa4' - b'\xa4\xa4\xde\xa4\xb9'.decode('euc-jp'), 'euc-jp') - res = """\ -=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKMnE8VCROPjUbKEI=?= - =?iso-2022-jp?b?GyRCRyckckJUJEMkRiQkJF4kORsoQg==?=""" - self.assertEqual(h.encode(), res) - - def test_header_splitter(self): - eq = self.ndiffAssertEqual - msg = MIMEText('') - # It'd be great if we could use add_header() here, but that doesn't - # guarantee an order of the parameters. - msg['X-Foobar-Spoink-Defrobnit'] = ( - 'wasnipoop; giraffes="very-long-necked-animals"; ' - 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"') - sfp = StringIO() - g = Generator(sfp) - g.flatten(msg) - eq(sfp.getvalue(), '''\ -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals"; - spooge="yummy"; hippos="gargantuan"; marshmallows="gooey" - -''') - - def test_no_semis_header_splitter(self): - eq = self.ndiffAssertEqual - msg = Message() - msg['From'] = 'test@dom.ain' - msg['References'] = SPACE.join('<%d@dom.ain>' % i for i in range(10)) - msg.set_payload('Test') - sfp = StringIO() - g = Generator(sfp) - g.flatten(msg) - eq(sfp.getvalue(), """\ -From: test@dom.ain -References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain> - <5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain> - -Test""") - - def test_no_split_long_header(self): - eq = self.ndiffAssertEqual - hstr = 'References: ' + 'x' * 80 - h = Header(hstr) - # These come on two lines because Headers are really field value - # classes and don't really know about their field names. - eq(h.encode(), """\ -References: - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""") - h = Header('x' * 80) - eq(h.encode(), 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') - - def test_splitting_multiple_long_lines(self): - eq = self.ndiffAssertEqual - hstr = """\ -from babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for ; Sat, 2 Feb 2002 17:00:06 -0800 (PST) -\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for ; Sat, 2 Feb 2002 17:00:06 -0800 (PST) -\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for ; Sat, 2 Feb 2002 17:00:06 -0800 (PST) -""" - h = Header(hstr, continuation_ws='\t') - eq(h.encode(), """\ -from babylon.socal-raves.org (localhost [127.0.0.1]); - by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; - for ; - Sat, 2 Feb 2002 17:00:06 -0800 (PST) -\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); - by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; - for ; - Sat, 2 Feb 2002 17:00:06 -0800 (PST) -\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); - by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; - for ; - Sat, 2 Feb 2002 17:00:06 -0800 (PST)""") - - def test_splitting_first_line_only_is_long(self): - eq = self.ndiffAssertEqual - hstr = """\ -from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] helo=cthulhu.gerg.ca) -\tby kronos.mems-exchange.org with esmtp (Exim 4.05) -\tid 17k4h5-00034i-00 -\tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""" - h = Header(hstr, maxlinelen=78, header_name='Received', - continuation_ws='\t') - eq(h.encode(), """\ -from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] - helo=cthulhu.gerg.ca) -\tby kronos.mems-exchange.org with esmtp (Exim 4.05) -\tid 17k4h5-00034i-00 -\tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""") - - def test_long_8bit_header(self): - eq = self.ndiffAssertEqual - msg = Message() - h = Header('Britische Regierung gibt', 'iso-8859-1', - header_name='Subject') - h.append('gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte') - eq(h.encode(maxlinelen=76), """\ -=?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offs?= - =?iso-8859-1?q?hore-Windkraftprojekte?=""") - msg['Subject'] = h - eq(msg.as_string(maxheaderlen=76), """\ -Subject: =?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offs?= - =?iso-8859-1?q?hore-Windkraftprojekte?= - -""") - eq(msg.as_string(maxheaderlen=0), """\ -Subject: =?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offshore-Windkraftprojekte?= - -""") - - def test_long_8bit_header_no_charset(self): - eq = self.ndiffAssertEqual - msg = Message() - header_string = ('Britische Regierung gibt gr\xfcnes Licht ' - 'f\xfcr Offshore-Windkraftprojekte ' - '') - msg['Reply-To'] = header_string - self.assertRaises(UnicodeEncodeError, msg.as_string) - msg = Message() - msg['Reply-To'] = Header(header_string, 'utf-8', - header_name='Reply-To') - eq(msg.as_string(maxheaderlen=78), """\ -Reply-To: =?utf-8?q?Britische_Regierung_gibt_gr=C3=BCnes_Licht_f=C3=BCr_Offs?= - =?utf-8?q?hore-Windkraftprojekte_=3Ca-very-long-address=40example=2Ecom=3E?= - -""") - - def test_long_to_header(self): - eq = self.ndiffAssertEqual - to = ('"Someone Test #A" ,' - ',' - '"Someone Test #B" , ' - '"Someone Test #C" , ' - '"Someone Test #D" ') - msg = Message() - msg['To'] = to - eq(msg.as_string(maxheaderlen=78), '''\ -To: "Someone Test #A" ,, - "Someone Test #B" , - "Someone Test #C" , - "Someone Test #D" - -''') - - def test_long_line_after_append(self): - eq = self.ndiffAssertEqual - s = 'This is an example of string which has almost the limit of header length.' - h = Header(s) - h.append('Add another line.') - eq(h.encode(maxlinelen=76), """\ -This is an example of string which has almost the limit of header length. - Add another line.""") - - def test_shorter_line_with_append(self): - eq = self.ndiffAssertEqual - s = 'This is a shorter line.' - h = Header(s) - h.append('Add another sentence. (Surprise?)') - eq(h.encode(), - 'This is a shorter line. Add another sentence. (Surprise?)') - - def test_long_field_name(self): - eq = self.ndiffAssertEqual - fn = 'X-Very-Very-Very-Long-Header-Name' - gs = ('Die Mieter treten hier ein werden mit einem Foerderband ' - 'komfortabel den Korridor entlang, an s\xfcdl\xfcndischen ' - 'Wandgem\xe4lden vorbei, gegen die rotierenden Klingen ' - 'bef\xf6rdert. ') - h = Header(gs, 'iso-8859-1', header_name=fn) - # BAW: this seems broken because the first line is too long - eq(h.encode(maxlinelen=76), """\ -=?iso-8859-1?q?Die_Mieter_treten_hier_e?= - =?iso-8859-1?q?in_werden_mit_einem_Foerderband_komfortabel_den_Korridor_e?= - =?iso-8859-1?q?ntlang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei=2C_ge?= - =?iso-8859-1?q?gen_die_rotierenden_Klingen_bef=F6rdert=2E_?=""") - - def test_long_received_header(self): - h = ('from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) ' - 'by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; ' - 'Wed, 05 Mar 2003 18:10:18 -0700') - msg = Message() - msg['Received-1'] = Header(h, continuation_ws='\t') - msg['Received-2'] = h - # This should be splitting on spaces not semicolons. - self.ndiffAssertEqual(msg.as_string(maxheaderlen=78), """\ -Received-1: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; - Wed, 05 Mar 2003 18:10:18 -0700 -Received-2: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; - Wed, 05 Mar 2003 18:10:18 -0700 - -""") - - def test_string_headerinst_eq(self): - h = ('<15975.17901.207240.414604@sgigritzmann1.mathematik.' - 'tu-muenchen.de> (David Bremner\'s message of ' - '"Thu, 6 Mar 2003 13:58:21 +0100")') - msg = Message() - msg['Received-1'] = Header(h, header_name='Received-1', - continuation_ws='\t') - msg['Received-2'] = h - # XXX This should be splitting on spaces not commas. - self.ndiffAssertEqual(msg.as_string(maxheaderlen=78), """\ -Received-1: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David Bremner's message of \"Thu, - 6 Mar 2003 13:58:21 +0100\") -Received-2: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David Bremner's message of \"Thu, - 6 Mar 2003 13:58:21 +0100\") - -""") - - def test_long_unbreakable_lines_with_continuation(self): - eq = self.ndiffAssertEqual - msg = Message() - t = """\ -iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 - locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp""" - msg['Face-1'] = t - msg['Face-2'] = Header(t, header_name='Face-2') - # XXX This splitting is all wrong. It the first value line should be - # snug against the field name. - eq(msg.as_string(maxheaderlen=78), """\ -Face-1:\x20 - iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 - locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp -Face-2:\x20 - iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 - locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp - -""") - - def test_another_long_multiline_header(self): - eq = self.ndiffAssertEqual - m = ('Received: from siimage.com ' - '([172.25.1.3]) by zima.siliconimage.com with ' - 'Microsoft SMTPSVC(5.0.2195.4905); ' - 'Wed, 16 Oct 2002 07:41:11 -0700') - msg = email.message_from_string(m) - eq(msg.as_string(maxheaderlen=78), '''\ -Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with Microsoft SMTPSVC(5.0.2195.4905); - Wed, 16 Oct 2002 07:41:11 -0700 - -''') - - def test_long_lines_with_different_header(self): - eq = self.ndiffAssertEqual - h = ('List-Unsubscribe: ' - ',' - ' ') - msg = Message() - msg['List'] = h - msg['List'] = Header(h, header_name='List') - eq(msg.as_string(maxheaderlen=78), """\ -List: List-Unsubscribe: , - -List: List-Unsubscribe: , - - -""") - - def test_long_rfc2047_header_with_embedded_fws(self): - h = Header(textwrap.dedent("""\ - We're going to pretend this header is in a non-ascii character set - \tto see if line wrapping with encoded words and embedded - folding white space works"""), - charset='utf-8', - header_name='Test') - self.assertEqual(h.encode()+'\n', textwrap.dedent("""\ - =?utf-8?q?We=27re_going_to_pretend_this_header_is_in_a_non-ascii_chara?= - =?utf-8?q?cter_set?= - =?utf-8?q?_to_see_if_line_wrapping_with_encoded_words_and_embedded?= - =?utf-8?q?_folding_white_space_works?=""")+'\n') - - - -# Test mangling of "From " lines in the body of a message -class TestFromMangling(unittest.TestCase): - def setUp(self): - self.msg = Message() - self.msg['From'] = 'aaa@bbb.org' - self.msg.set_payload("""\ -From the desk of A.A.A.: -Blah blah blah -""") - - def test_mangled_from(self): - s = StringIO() - g = Generator(s, mangle_from_=True) - g.flatten(self.msg) - self.assertEqual(s.getvalue(), """\ -From: aaa@bbb.org - ->From the desk of A.A.A.: -Blah blah blah -""") - - def test_dont_mangle_from(self): - s = StringIO() - g = Generator(s, mangle_from_=False) - g.flatten(self.msg) - self.assertEqual(s.getvalue(), """\ -From: aaa@bbb.org - -From the desk of A.A.A.: -Blah blah blah -""") - - - -# Test the basic MIMEAudio class -class TestMIMEAudio(unittest.TestCase): - def setUp(self): - # Make sure we pick up the audiotest.au that lives in email/test/data. - # In Python, there's an audiotest.au living in Lib/test but that isn't - # included in some binary distros that don't include the test - # package. The trailing empty string on the .join() is significant - # since findfile() will do a dirname(). - datadir = os.path.join(os.path.dirname(landmark), 'data', '') - with open(findfile('audiotest.au', datadir), 'rb') as fp: - self._audiodata = fp.read() - self._au = MIMEAudio(self._audiodata) - - def test_guess_minor_type(self): - self.assertEqual(self._au.get_content_type(), 'audio/basic') - - def test_encoding(self): - payload = self._au.get_payload() - self.assertEqual(base64.decodebytes(bytes(payload, 'ascii')), - self._audiodata) - - def test_checkSetMinor(self): - au = MIMEAudio(self._audiodata, 'fish') - self.assertEqual(au.get_content_type(), 'audio/fish') - - def test_add_header(self): - eq = self.assertEqual - unless = self.assertTrue - self._au.add_header('Content-Disposition', 'attachment', - filename='audiotest.au') - eq(self._au['content-disposition'], - 'attachment; filename="audiotest.au"') - eq(self._au.get_params(header='content-disposition'), - [('attachment', ''), ('filename', 'audiotest.au')]) - eq(self._au.get_param('filename', header='content-disposition'), - 'audiotest.au') - missing = [] - eq(self._au.get_param('attachment', header='content-disposition'), '') - unless(self._au.get_param('foo', failobj=missing, - header='content-disposition') is missing) - # Try some missing stuff - unless(self._au.get_param('foobar', missing) is missing) - unless(self._au.get_param('attachment', missing, - header='foobar') is missing) - - - -# Test the basic MIMEImage class -class TestMIMEImage(unittest.TestCase): - def setUp(self): - with openfile('PyBanner048.gif', 'rb') as fp: - self._imgdata = fp.read() - self._im = MIMEImage(self._imgdata) - - def test_guess_minor_type(self): - self.assertEqual(self._im.get_content_type(), 'image/gif') - - def test_encoding(self): - payload = self._im.get_payload() - self.assertEqual(base64.decodebytes(bytes(payload, 'ascii')), - self._imgdata) - - def test_checkSetMinor(self): - im = MIMEImage(self._imgdata, 'fish') - self.assertEqual(im.get_content_type(), 'image/fish') - - def test_add_header(self): - eq = self.assertEqual - unless = self.assertTrue - self._im.add_header('Content-Disposition', 'attachment', - filename='dingusfish.gif') - eq(self._im['content-disposition'], - 'attachment; filename="dingusfish.gif"') - eq(self._im.get_params(header='content-disposition'), - [('attachment', ''), ('filename', 'dingusfish.gif')]) - eq(self._im.get_param('filename', header='content-disposition'), - 'dingusfish.gif') - missing = [] - eq(self._im.get_param('attachment', header='content-disposition'), '') - unless(self._im.get_param('foo', failobj=missing, - header='content-disposition') is missing) - # Try some missing stuff - unless(self._im.get_param('foobar', missing) is missing) - unless(self._im.get_param('attachment', missing, - header='foobar') is missing) - - - -# Test the basic MIMEApplication class -class TestMIMEApplication(unittest.TestCase): - def test_headers(self): - eq = self.assertEqual - msg = MIMEApplication(b'\xfa\xfb\xfc\xfd\xfe\xff') - eq(msg.get_content_type(), 'application/octet-stream') - eq(msg['content-transfer-encoding'], 'base64') - - def test_body(self): - eq = self.assertEqual - bytes = b'\xfa\xfb\xfc\xfd\xfe\xff' - msg = MIMEApplication(bytes) - eq(msg.get_payload(), '+vv8/f7/') - eq(msg.get_payload(decode=True), bytes) - - - -# Test the basic MIMEText class -class TestMIMEText(unittest.TestCase): - def setUp(self): - self._msg = MIMEText('hello there') - - def test_types(self): - eq = self.assertEqual - unless = self.assertTrue - eq(self._msg.get_content_type(), 'text/plain') - eq(self._msg.get_param('charset'), 'us-ascii') - missing = [] - unless(self._msg.get_param('foobar', missing) is missing) - unless(self._msg.get_param('charset', missing, header='foobar') - is missing) - - def test_payload(self): - self.assertEqual(self._msg.get_payload(), 'hello there') - self.assertTrue(not self._msg.is_multipart()) - - def test_charset(self): - eq = self.assertEqual - msg = MIMEText('hello there', _charset='us-ascii') - eq(msg.get_charset().input_charset, 'us-ascii') - eq(msg['content-type'], 'text/plain; charset="us-ascii"') - - def test_7bit_input(self): - eq = self.assertEqual - msg = MIMEText('hello there', _charset='us-ascii') - eq(msg.get_charset().input_charset, 'us-ascii') - eq(msg['content-type'], 'text/plain; charset="us-ascii"') - - def test_7bit_input_no_charset(self): - eq = self.assertEqual - msg = MIMEText('hello there') - eq(msg.get_charset(), 'us-ascii') - eq(msg['content-type'], 'text/plain; charset="us-ascii"') - self.assertTrue('hello there' in msg.as_string()) - - def test_utf8_input(self): - teststr = '\u043a\u0438\u0440\u0438\u043b\u0438\u0446\u0430' - eq = self.assertEqual - msg = MIMEText(teststr, _charset='utf-8') - eq(msg.get_charset().output_charset, 'utf-8') - eq(msg['content-type'], 'text/plain; charset="utf-8"') - eq(msg.get_payload(decode=True), teststr.encode('utf-8')) - - @unittest.skip("can't fix because of backward compat in email5, " - "will fix in email6") - def test_utf8_input_no_charset(self): - teststr = '\u043a\u0438\u0440\u0438\u043b\u0438\u0446\u0430' - self.assertRaises(UnicodeEncodeError, MIMEText, teststr) - - - -# Test complicated multipart/* messages -class TestMultipart(TestEmailBase): - def setUp(self): - with openfile('PyBanner048.gif', 'rb') as fp: - data = fp.read() - container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY') - image = MIMEImage(data, name='dingusfish.gif') - image.add_header('content-disposition', 'attachment', - filename='dingusfish.gif') - intro = MIMEText('''\ -Hi there, - -This is the dingus fish. -''') - container.attach(intro) - container.attach(image) - container['From'] = 'Barry ' - container['To'] = 'Dingus Lovers ' - container['Subject'] = 'Here is your dingus fish' - - now = 987809702.54848599 - timetuple = time.localtime(now) - if timetuple[-1] == 0: - tzsecs = time.timezone - else: - tzsecs = time.altzone - if tzsecs > 0: - sign = '-' - else: - sign = '+' - tzoffset = ' %s%04d' % (sign, tzsecs / 36) - container['Date'] = time.strftime( - '%a, %d %b %Y %H:%M:%S', - time.localtime(now)) + tzoffset - self._msg = container - self._im = image - self._txt = intro - - def test_hierarchy(self): - # convenience - eq = self.assertEqual - unless = self.assertTrue - raises = self.assertRaises - # tests - m = self._msg - unless(m.is_multipart()) - eq(m.get_content_type(), 'multipart/mixed') - eq(len(m.get_payload()), 2) - raises(IndexError, m.get_payload, 2) - m0 = m.get_payload(0) - m1 = m.get_payload(1) - unless(m0 is self._txt) - unless(m1 is self._im) - eq(m.get_payload(), [m0, m1]) - unless(not m0.is_multipart()) - unless(not m1.is_multipart()) - - def test_empty_multipart_idempotent(self): - text = """\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - - ---BOUNDARY - - ---BOUNDARY-- -""" - msg = Parser().parsestr(text) - self.ndiffAssertEqual(text, msg.as_string()) - - def test_no_parts_in_a_multipart_with_none_epilogue(self): - outer = MIMEBase('multipart', 'mixed') - outer['Subject'] = 'A subject' - outer['To'] = 'aperson@dom.ain' - outer['From'] = 'bperson@dom.ain' - outer.set_boundary('BOUNDARY') - self.ndiffAssertEqual(outer.as_string(), '''\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - ---BOUNDARY - ---BOUNDARY--''') - - def test_no_parts_in_a_multipart_with_empty_epilogue(self): - outer = MIMEBase('multipart', 'mixed') - outer['Subject'] = 'A subject' - outer['To'] = 'aperson@dom.ain' - outer['From'] = 'bperson@dom.ain' - outer.preamble = '' - outer.epilogue = '' - outer.set_boundary('BOUNDARY') - self.ndiffAssertEqual(outer.as_string(), '''\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - - ---BOUNDARY - ---BOUNDARY-- -''') - - def test_one_part_in_a_multipart(self): - eq = self.ndiffAssertEqual - outer = MIMEBase('multipart', 'mixed') - outer['Subject'] = 'A subject' - outer['To'] = 'aperson@dom.ain' - outer['From'] = 'bperson@dom.ain' - outer.set_boundary('BOUNDARY') - msg = MIMEText('hello world') - outer.attach(msg) - eq(outer.as_string(), '''\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -hello world ---BOUNDARY--''') - - def test_seq_parts_in_a_multipart_with_empty_preamble(self): - eq = self.ndiffAssertEqual - outer = MIMEBase('multipart', 'mixed') - outer['Subject'] = 'A subject' - outer['To'] = 'aperson@dom.ain' - outer['From'] = 'bperson@dom.ain' - outer.preamble = '' - msg = MIMEText('hello world') - outer.attach(msg) - outer.set_boundary('BOUNDARY') - eq(outer.as_string(), '''\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -hello world ---BOUNDARY--''') - - - def test_seq_parts_in_a_multipart_with_none_preamble(self): - eq = self.ndiffAssertEqual - outer = MIMEBase('multipart', 'mixed') - outer['Subject'] = 'A subject' - outer['To'] = 'aperson@dom.ain' - outer['From'] = 'bperson@dom.ain' - outer.preamble = None - msg = MIMEText('hello world') - outer.attach(msg) - outer.set_boundary('BOUNDARY') - eq(outer.as_string(), '''\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -hello world ---BOUNDARY--''') - - - def test_seq_parts_in_a_multipart_with_none_epilogue(self): - eq = self.ndiffAssertEqual - outer = MIMEBase('multipart', 'mixed') - outer['Subject'] = 'A subject' - outer['To'] = 'aperson@dom.ain' - outer['From'] = 'bperson@dom.ain' - outer.epilogue = None - msg = MIMEText('hello world') - outer.attach(msg) - outer.set_boundary('BOUNDARY') - eq(outer.as_string(), '''\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -hello world ---BOUNDARY--''') - - - def test_seq_parts_in_a_multipart_with_empty_epilogue(self): - eq = self.ndiffAssertEqual - outer = MIMEBase('multipart', 'mixed') - outer['Subject'] = 'A subject' - outer['To'] = 'aperson@dom.ain' - outer['From'] = 'bperson@dom.ain' - outer.epilogue = '' - msg = MIMEText('hello world') - outer.attach(msg) - outer.set_boundary('BOUNDARY') - eq(outer.as_string(), '''\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -hello world ---BOUNDARY-- -''') - - - def test_seq_parts_in_a_multipart_with_nl_epilogue(self): - eq = self.ndiffAssertEqual - outer = MIMEBase('multipart', 'mixed') - outer['Subject'] = 'A subject' - outer['To'] = 'aperson@dom.ain' - outer['From'] = 'bperson@dom.ain' - outer.epilogue = '\n' - msg = MIMEText('hello world') - outer.attach(msg) - outer.set_boundary('BOUNDARY') - eq(outer.as_string(), '''\ -Content-Type: multipart/mixed; boundary="BOUNDARY" -MIME-Version: 1.0 -Subject: A subject -To: aperson@dom.ain -From: bperson@dom.ain - ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -hello world ---BOUNDARY-- - -''') - - def test_message_external_body(self): - eq = self.assertEqual - msg = self._msgobj('msg_36.txt') - eq(len(msg.get_payload()), 2) - msg1 = msg.get_payload(1) - eq(msg1.get_content_type(), 'multipart/alternative') - eq(len(msg1.get_payload()), 2) - for subpart in msg1.get_payload(): - eq(subpart.get_content_type(), 'message/external-body') - eq(len(subpart.get_payload()), 1) - subsubpart = subpart.get_payload(0) - eq(subsubpart.get_content_type(), 'text/plain') - - def test_double_boundary(self): - # msg_37.txt is a multipart that contains two dash-boundary's in a - # row. Our interpretation of RFC 2046 calls for ignoring the second - # and subsequent boundaries. - msg = self._msgobj('msg_37.txt') - self.assertEqual(len(msg.get_payload()), 3) - - def test_nested_inner_contains_outer_boundary(self): - eq = self.ndiffAssertEqual - # msg_38.txt has an inner part that contains outer boundaries. My - # interpretation of RFC 2046 (based on sections 5.1 and 5.1.2) say - # these are illegal and should be interpreted as unterminated inner - # parts. - msg = self._msgobj('msg_38.txt') - sfp = StringIO() - iterators._structure(msg, sfp) - eq(sfp.getvalue(), """\ -multipart/mixed - multipart/mixed - multipart/alternative - text/plain - text/plain - text/plain - text/plain -""") - - def test_nested_with_same_boundary(self): - eq = self.ndiffAssertEqual - # msg 39.txt is similarly evil in that it's got inner parts that use - # the same boundary as outer parts. Again, I believe the way this is - # parsed is closest to the spirit of RFC 2046 - msg = self._msgobj('msg_39.txt') - sfp = StringIO() - iterators._structure(msg, sfp) - eq(sfp.getvalue(), """\ -multipart/mixed - multipart/mixed - multipart/alternative - application/octet-stream - application/octet-stream - text/plain -""") - - def test_boundary_in_non_multipart(self): - msg = self._msgobj('msg_40.txt') - self.assertEqual(msg.as_string(), '''\ -MIME-Version: 1.0 -Content-Type: text/html; boundary="--961284236552522269" - -----961284236552522269 -Content-Type: text/html; -Content-Transfer-Encoding: 7Bit - - - -----961284236552522269-- -''') - - def test_boundary_with_leading_space(self): - eq = self.assertEqual - msg = email.message_from_string('''\ -MIME-Version: 1.0 -Content-Type: multipart/mixed; boundary=" XXXX" - --- XXXX -Content-Type: text/plain - - --- XXXX -Content-Type: text/plain - --- XXXX-- -''') - self.assertTrue(msg.is_multipart()) - eq(msg.get_boundary(), ' XXXX') - eq(len(msg.get_payload()), 2) - - def test_boundary_without_trailing_newline(self): - m = Parser().parsestr("""\ -Content-Type: multipart/mixed; boundary="===============0012394164==" -MIME-Version: 1.0 - ---===============0012394164== -Content-Type: image/file1.jpg -MIME-Version: 1.0 -Content-Transfer-Encoding: base64 - -YXNkZg== ---===============0012394164==--""") - self.assertEqual(m.get_payload(0).get_payload(), 'YXNkZg==') - - - -# Test some badly formatted messages -class TestNonConformant(TestEmailBase): - def test_parse_missing_minor_type(self): - eq = self.assertEqual - msg = self._msgobj('msg_14.txt') - eq(msg.get_content_type(), 'text/plain') - eq(msg.get_content_maintype(), 'text') - eq(msg.get_content_subtype(), 'plain') - - def test_same_boundary_inner_outer(self): - unless = self.assertTrue - msg = self._msgobj('msg_15.txt') - # XXX We can probably eventually do better - inner = msg.get_payload(0) - unless(hasattr(inner, 'defects')) - self.assertEqual(len(inner.defects), 1) - unless(isinstance(inner.defects[0], - errors.StartBoundaryNotFoundDefect)) - - def test_multipart_no_boundary(self): - unless = self.assertTrue - msg = self._msgobj('msg_25.txt') - unless(isinstance(msg.get_payload(), str)) - self.assertEqual(len(msg.defects), 2) - unless(isinstance(msg.defects[0], errors.NoBoundaryInMultipartDefect)) - unless(isinstance(msg.defects[1], - errors.MultipartInvariantViolationDefect)) - - def test_invalid_content_type(self): - eq = self.assertEqual - neq = self.ndiffAssertEqual - msg = Message() - # RFC 2045, $5.2 says invalid yields text/plain - msg['Content-Type'] = 'text' - eq(msg.get_content_maintype(), 'text') - eq(msg.get_content_subtype(), 'plain') - eq(msg.get_content_type(), 'text/plain') - # Clear the old value and try something /really/ invalid - del msg['content-type'] - msg['Content-Type'] = 'foo' - eq(msg.get_content_maintype(), 'text') - eq(msg.get_content_subtype(), 'plain') - eq(msg.get_content_type(), 'text/plain') - # Still, make sure that the message is idempotently generated - s = StringIO() - g = Generator(s) - g.flatten(msg) - neq(s.getvalue(), 'Content-Type: foo\n\n') - - def test_no_start_boundary(self): - eq = self.ndiffAssertEqual - msg = self._msgobj('msg_31.txt') - eq(msg.get_payload(), """\ ---BOUNDARY -Content-Type: text/plain - -message 1 - ---BOUNDARY -Content-Type: text/plain - -message 2 - ---BOUNDARY-- -""") - - def test_no_separating_blank_line(self): - eq = self.ndiffAssertEqual - msg = self._msgobj('msg_35.txt') - eq(msg.as_string(), """\ -From: aperson@dom.ain -To: bperson@dom.ain -Subject: here's something interesting - -counter to RFC 2822, there's no separating newline here -""") - - def test_lying_multipart(self): - unless = self.assertTrue - msg = self._msgobj('msg_41.txt') - unless(hasattr(msg, 'defects')) - self.assertEqual(len(msg.defects), 2) - unless(isinstance(msg.defects[0], errors.NoBoundaryInMultipartDefect)) - unless(isinstance(msg.defects[1], - errors.MultipartInvariantViolationDefect)) - - def test_missing_start_boundary(self): - outer = self._msgobj('msg_42.txt') - # The message structure is: - # - # multipart/mixed - # text/plain - # message/rfc822 - # multipart/mixed [*] - # - # [*] This message is missing its start boundary - bad = outer.get_payload(1).get_payload(0) - self.assertEqual(len(bad.defects), 1) - self.assertTrue(isinstance(bad.defects[0], - errors.StartBoundaryNotFoundDefect)) - - def test_first_line_is_continuation_header(self): - eq = self.assertEqual - m = ' Line 1\nLine 2\nLine 3' - msg = email.message_from_string(m) - eq(msg.keys(), []) - eq(msg.get_payload(), 'Line 2\nLine 3') - eq(len(msg.defects), 1) - self.assertTrue(isinstance(msg.defects[0], - errors.FirstHeaderLineIsContinuationDefect)) - eq(msg.defects[0].line, ' Line 1\n') - - - -# Test RFC 2047 header encoding and decoding -class TestRFC2047(TestEmailBase): - def test_rfc2047_multiline(self): - eq = self.assertEqual - s = """Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz - foo bar =?mac-iceland?q?r=8Aksm=9Arg=8Cs?=""" - dh = decode_header(s) - eq(dh, [ - (b'Re:', None), - (b'r\x8aksm\x9arg\x8cs', 'mac-iceland'), - (b'baz foo bar', None), - (b'r\x8aksm\x9arg\x8cs', 'mac-iceland')]) - header = make_header(dh) - eq(str(header), - 'Re: r\xe4ksm\xf6rg\xe5s baz foo bar r\xe4ksm\xf6rg\xe5s') - self.ndiffAssertEqual(header.encode(maxlinelen=76), """\ -Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz foo bar =?mac-iceland?q?r=8Aksm?= - =?mac-iceland?q?=9Arg=8Cs?=""") - - def test_whitespace_eater_unicode(self): - eq = self.assertEqual - s = '=?ISO-8859-1?Q?Andr=E9?= Pirard ' - dh = decode_header(s) - eq(dh, [(b'Andr\xe9', 'iso-8859-1'), - (b'Pirard ', None)]) - header = str(make_header(dh)) - eq(header, 'Andr\xe9 Pirard ') - - def test_whitespace_eater_unicode_2(self): - eq = self.assertEqual - s = 'The =?iso-8859-1?b?cXVpY2sgYnJvd24gZm94?= jumped over the =?iso-8859-1?b?bGF6eSBkb2c=?=' - dh = decode_header(s) - eq(dh, [(b'The', None), (b'quick brown fox', 'iso-8859-1'), - (b'jumped over the', None), (b'lazy dog', 'iso-8859-1')]) - hu = str(make_header(dh)) - eq(hu, 'The quick brown fox jumped over the lazy dog') - - def test_rfc2047_missing_whitespace(self): - s = 'Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord' - dh = decode_header(s) - self.assertEqual(dh, [(s, None)]) - - def test_rfc2047_with_whitespace(self): - s = 'Sm =?ISO-8859-1?B?9g==?= rg =?ISO-8859-1?B?5Q==?= sbord' - dh = decode_header(s) - self.assertEqual(dh, [(b'Sm', None), (b'\xf6', 'iso-8859-1'), - (b'rg', None), (b'\xe5', 'iso-8859-1'), - (b'sbord', None)]) - - def test_rfc2047_B_bad_padding(self): - s = '=?iso-8859-1?B?%s?=' - data = [ # only test complete bytes - ('dm==', b'v'), ('dm=', b'v'), ('dm', b'v'), - ('dmk=', b'vi'), ('dmk', b'vi') - ] - for q, a in data: - dh = decode_header(s % q) - self.assertEqual(dh, [(a, 'iso-8859-1')]) - - def test_rfc2047_Q_invalid_digits(self): - # issue 10004. - s = '=?iso-8659-1?Q?andr=e9=zz?=' - self.assertEqual(decode_header(s), - [(b'andr\xe9=zz', 'iso-8659-1')]) - - -# Test the MIMEMessage class -class TestMIMEMessage(TestEmailBase): - def setUp(self): - with openfile('msg_11.txt') as fp: - self._text = fp.read() - - def test_type_error(self): - self.assertRaises(TypeError, MIMEMessage, 'a plain string') - - def test_valid_argument(self): - eq = self.assertEqual - unless = self.assertTrue - subject = 'A sub-message' - m = Message() - m['Subject'] = subject - r = MIMEMessage(m) - eq(r.get_content_type(), 'message/rfc822') - payload = r.get_payload() - unless(isinstance(payload, list)) - eq(len(payload), 1) - subpart = payload[0] - unless(subpart is m) - eq(subpart['subject'], subject) - - def test_bad_multipart(self): - eq = self.assertEqual - msg1 = Message() - msg1['Subject'] = 'subpart 1' - msg2 = Message() - msg2['Subject'] = 'subpart 2' - r = MIMEMessage(msg1) - self.assertRaises(errors.MultipartConversionError, r.attach, msg2) - - def test_generate(self): - # First craft the message to be encapsulated - m = Message() - m['Subject'] = 'An enclosed message' - m.set_payload('Here is the body of the message.\n') - r = MIMEMessage(m) - r['Subject'] = 'The enclosing message' - s = StringIO() - g = Generator(s) - g.flatten(r) - self.assertEqual(s.getvalue(), """\ -Content-Type: message/rfc822 -MIME-Version: 1.0 -Subject: The enclosing message - -Subject: An enclosed message - -Here is the body of the message. -""") - - def test_parse_message_rfc822(self): - eq = self.assertEqual - unless = self.assertTrue - msg = self._msgobj('msg_11.txt') - eq(msg.get_content_type(), 'message/rfc822') - payload = msg.get_payload() - unless(isinstance(payload, list)) - eq(len(payload), 1) - submsg = payload[0] - self.assertTrue(isinstance(submsg, Message)) - eq(submsg['subject'], 'An enclosed message') - eq(submsg.get_payload(), 'Here is the body of the message.\n') - - def test_dsn(self): - eq = self.assertEqual - unless = self.assertTrue - # msg 16 is a Delivery Status Notification, see RFC 1894 - msg = self._msgobj('msg_16.txt') - eq(msg.get_content_type(), 'multipart/report') - unless(msg.is_multipart()) - eq(len(msg.get_payload()), 3) - # Subpart 1 is a text/plain, human readable section - subpart = msg.get_payload(0) - eq(subpart.get_content_type(), 'text/plain') - eq(subpart.get_payload(), """\ -This report relates to a message you sent with the following header fields: - - Message-id: <002001c144a6$8752e060$56104586@oxy.edu> - Date: Sun, 23 Sep 2001 20:10:55 -0700 - From: "Ian T. Henry" - To: SoCal Raves - Subject: [scr] yeah for Ians!! - -Your message cannot be delivered to the following recipients: - - Recipient address: jangel1@cougar.noc.ucla.edu - Reason: recipient reached disk quota - -""") - # Subpart 2 contains the machine parsable DSN information. It - # consists of two blocks of headers, represented by two nested Message - # objects. - subpart = msg.get_payload(1) - eq(subpart.get_content_type(), 'message/delivery-status') - eq(len(subpart.get_payload()), 2) - # message/delivery-status should treat each block as a bunch of - # headers, i.e. a bunch of Message objects. - dsn1 = subpart.get_payload(0) - unless(isinstance(dsn1, Message)) - eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu') - eq(dsn1.get_param('dns', header='reporting-mta'), '') - # Try a missing one - eq(dsn1.get_param('nsd', header='reporting-mta'), None) - dsn2 = subpart.get_payload(1) - unless(isinstance(dsn2, Message)) - eq(dsn2['action'], 'failed') - eq(dsn2.get_params(header='original-recipient'), - [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')]) - eq(dsn2.get_param('rfc822', header='final-recipient'), '') - # Subpart 3 is the original message - subpart = msg.get_payload(2) - eq(subpart.get_content_type(), 'message/rfc822') - payload = subpart.get_payload() - unless(isinstance(payload, list)) - eq(len(payload), 1) - subsubpart = payload[0] - unless(isinstance(subsubpart, Message)) - eq(subsubpart.get_content_type(), 'text/plain') - eq(subsubpart['message-id'], - '<002001c144a6$8752e060$56104586@oxy.edu>') - - def test_epilogue(self): - eq = self.ndiffAssertEqual - with openfile('msg_21.txt') as fp: - text = fp.read() - msg = Message() - msg['From'] = 'aperson@dom.ain' - msg['To'] = 'bperson@dom.ain' - msg['Subject'] = 'Test' - msg.preamble = 'MIME message' - msg.epilogue = 'End of MIME message\n' - msg1 = MIMEText('One') - msg2 = MIMEText('Two') - msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') - msg.attach(msg1) - msg.attach(msg2) - sfp = StringIO() - g = Generator(sfp) - g.flatten(msg) - eq(sfp.getvalue(), text) - - def test_no_nl_preamble(self): - eq = self.ndiffAssertEqual - msg = Message() - msg['From'] = 'aperson@dom.ain' - msg['To'] = 'bperson@dom.ain' - msg['Subject'] = 'Test' - msg.preamble = 'MIME message' - msg.epilogue = '' - msg1 = MIMEText('One') - msg2 = MIMEText('Two') - msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') - msg.attach(msg1) - msg.attach(msg2) - eq(msg.as_string(), """\ -From: aperson@dom.ain -To: bperson@dom.ain -Subject: Test -Content-Type: multipart/mixed; boundary="BOUNDARY" - -MIME message ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -One ---BOUNDARY -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -Two ---BOUNDARY-- -""") - - def test_default_type(self): - eq = self.assertEqual - with openfile('msg_30.txt') as fp: - msg = email.message_from_file(fp) - container1 = msg.get_payload(0) - eq(container1.get_default_type(), 'message/rfc822') - eq(container1.get_content_type(), 'message/rfc822') - container2 = msg.get_payload(1) - eq(container2.get_default_type(), 'message/rfc822') - eq(container2.get_content_type(), 'message/rfc822') - container1a = container1.get_payload(0) - eq(container1a.get_default_type(), 'text/plain') - eq(container1a.get_content_type(), 'text/plain') - container2a = container2.get_payload(0) - eq(container2a.get_default_type(), 'text/plain') - eq(container2a.get_content_type(), 'text/plain') - - def test_default_type_with_explicit_container_type(self): - eq = self.assertEqual - with openfile('msg_28.txt') as fp: - msg = email.message_from_file(fp) - container1 = msg.get_payload(0) - eq(container1.get_default_type(), 'message/rfc822') - eq(container1.get_content_type(), 'message/rfc822') - container2 = msg.get_payload(1) - eq(container2.get_default_type(), 'message/rfc822') - eq(container2.get_content_type(), 'message/rfc822') - container1a = container1.get_payload(0) - eq(container1a.get_default_type(), 'text/plain') - eq(container1a.get_content_type(), 'text/plain') - container2a = container2.get_payload(0) - eq(container2a.get_default_type(), 'text/plain') - eq(container2a.get_content_type(), 'text/plain') - - def test_default_type_non_parsed(self): - eq = self.assertEqual - neq = self.ndiffAssertEqual - # Set up container - container = MIMEMultipart('digest', 'BOUNDARY') - container.epilogue = '' - # Set up subparts - subpart1a = MIMEText('message 1\n') - subpart2a = MIMEText('message 2\n') - subpart1 = MIMEMessage(subpart1a) - subpart2 = MIMEMessage(subpart2a) - container.attach(subpart1) - container.attach(subpart2) - eq(subpart1.get_content_type(), 'message/rfc822') - eq(subpart1.get_default_type(), 'message/rfc822') - eq(subpart2.get_content_type(), 'message/rfc822') - eq(subpart2.get_default_type(), 'message/rfc822') - neq(container.as_string(0), '''\ -Content-Type: multipart/digest; boundary="BOUNDARY" -MIME-Version: 1.0 - ---BOUNDARY -Content-Type: message/rfc822 -MIME-Version: 1.0 - -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -message 1 - ---BOUNDARY -Content-Type: message/rfc822 -MIME-Version: 1.0 - -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -message 2 - ---BOUNDARY-- -''') - del subpart1['content-type'] - del subpart1['mime-version'] - del subpart2['content-type'] - del subpart2['mime-version'] - eq(subpart1.get_content_type(), 'message/rfc822') - eq(subpart1.get_default_type(), 'message/rfc822') - eq(subpart2.get_content_type(), 'message/rfc822') - eq(subpart2.get_default_type(), 'message/rfc822') - neq(container.as_string(0), '''\ -Content-Type: multipart/digest; boundary="BOUNDARY" -MIME-Version: 1.0 - ---BOUNDARY - -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -message 1 - ---BOUNDARY - -Content-Type: text/plain; charset="us-ascii" -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit - -message 2 - ---BOUNDARY-- -''') - - def test_mime_attachments_in_constructor(self): - eq = self.assertEqual - text1 = MIMEText('') - text2 = MIMEText('') - msg = MIMEMultipart(_subparts=(text1, text2)) - eq(len(msg.get_payload()), 2) - eq(msg.get_payload(0), text1) - eq(msg.get_payload(1), text2) - - def test_default_multipart_constructor(self): - msg = MIMEMultipart() - self.assertTrue(msg.is_multipart()) - - -# A general test of parser->model->generator idempotency. IOW, read a message -# in, parse it into a message object tree, then without touching the tree, -# regenerate the plain text. The original text and the transformed text -# should be identical. Note: that we ignore the Unix-From since that may -# contain a changed date. -class TestIdempotent(TestEmailBase): - - linesep = '\n' - - def _msgobj(self, filename): - with openfile(filename) as fp: - data = fp.read() - msg = email.message_from_string(data) - return msg, data - - def _idempotent(self, msg, text, unixfrom=False): - eq = self.ndiffAssertEqual - s = StringIO() - g = Generator(s, maxheaderlen=0) - g.flatten(msg, unixfrom=unixfrom) - eq(text, s.getvalue()) - - def test_parse_text_message(self): - eq = self.assertEqual - msg, text = self._msgobj('msg_01.txt') - eq(msg.get_content_type(), 'text/plain') - eq(msg.get_content_maintype(), 'text') - eq(msg.get_content_subtype(), 'plain') - eq(msg.get_params()[1], ('charset', 'us-ascii')) - eq(msg.get_param('charset'), 'us-ascii') - eq(msg.preamble, None) - eq(msg.epilogue, None) - self._idempotent(msg, text) - - def test_parse_untyped_message(self): - eq = self.assertEqual - msg, text = self._msgobj('msg_03.txt') - eq(msg.get_content_type(), 'text/plain') - eq(msg.get_params(), None) - eq(msg.get_param('charset'), None) - self._idempotent(msg, text) - - def test_simple_multipart(self): - msg, text = self._msgobj('msg_04.txt') - self._idempotent(msg, text) - - def test_MIME_digest(self): - msg, text = self._msgobj('msg_02.txt') - self._idempotent(msg, text) - - def test_long_header(self): - msg, text = self._msgobj('msg_27.txt') - self._idempotent(msg, text) - - def test_MIME_digest_with_part_headers(self): - msg, text = self._msgobj('msg_28.txt') - self._idempotent(msg, text) - - def test_mixed_with_image(self): - msg, text = self._msgobj('msg_06.txt') - self._idempotent(msg, text) - - def test_multipart_report(self): - msg, text = self._msgobj('msg_05.txt') - self._idempotent(msg, text) - - def test_dsn(self): - msg, text = self._msgobj('msg_16.txt') - self._idempotent(msg, text) - - def test_preamble_epilogue(self): - msg, text = self._msgobj('msg_21.txt') - self._idempotent(msg, text) - - def test_multipart_one_part(self): - msg, text = self._msgobj('msg_23.txt') - self._idempotent(msg, text) - - def test_multipart_no_parts(self): - msg, text = self._msgobj('msg_24.txt') - self._idempotent(msg, text) - - def test_no_start_boundary(self): - msg, text = self._msgobj('msg_31.txt') - self._idempotent(msg, text) - - def test_rfc2231_charset(self): - msg, text = self._msgobj('msg_32.txt') - self._idempotent(msg, text) - - def test_more_rfc2231_parameters(self): - msg, text = self._msgobj('msg_33.txt') - self._idempotent(msg, text) - - def test_text_plain_in_a_multipart_digest(self): - msg, text = self._msgobj('msg_34.txt') - self._idempotent(msg, text) - - def test_nested_multipart_mixeds(self): - msg, text = self._msgobj('msg_12a.txt') - self._idempotent(msg, text) - - def test_message_external_body_idempotent(self): - msg, text = self._msgobj('msg_36.txt') - self._idempotent(msg, text) - - def test_message_delivery_status(self): - msg, text = self._msgobj('msg_43.txt') - self._idempotent(msg, text, unixfrom=True) - - def test_message_signed_idempotent(self): - msg, text = self._msgobj('msg_45.txt') - self._idempotent(msg, text) - - def test_content_type(self): - eq = self.assertEqual - unless = self.assertTrue - # Get a message object and reset the seek pointer for other tests - msg, text = self._msgobj('msg_05.txt') - eq(msg.get_content_type(), 'multipart/report') - # Test the Content-Type: parameters - params = {} - for pk, pv in msg.get_params(): - params[pk] = pv - eq(params['report-type'], 'delivery-status') - eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com') - eq(msg.preamble, 'This is a MIME-encapsulated message.' + self.linesep) - eq(msg.epilogue, self.linesep) - eq(len(msg.get_payload()), 3) - # Make sure the subparts are what we expect - msg1 = msg.get_payload(0) - eq(msg1.get_content_type(), 'text/plain') - eq(msg1.get_payload(), 'Yadda yadda yadda' + self.linesep) - msg2 = msg.get_payload(1) - eq(msg2.get_content_type(), 'text/plain') - eq(msg2.get_payload(), 'Yadda yadda yadda' + self.linesep) - msg3 = msg.get_payload(2) - eq(msg3.get_content_type(), 'message/rfc822') - self.assertTrue(isinstance(msg3, Message)) - payload = msg3.get_payload() - unless(isinstance(payload, list)) - eq(len(payload), 1) - msg4 = payload[0] - unless(isinstance(msg4, Message)) - eq(msg4.get_payload(), 'Yadda yadda yadda' + self.linesep) - - def test_parser(self): - eq = self.assertEqual - unless = self.assertTrue - msg, text = self._msgobj('msg_06.txt') - # Check some of the outer headers - eq(msg.get_content_type(), 'message/rfc822') - # Make sure the payload is a list of exactly one sub-Message, and that - # that submessage has a type of text/plain - payload = msg.get_payload() - unless(isinstance(payload, list)) - eq(len(payload), 1) - msg1 = payload[0] - self.assertTrue(isinstance(msg1, Message)) - eq(msg1.get_content_type(), 'text/plain') - self.assertTrue(isinstance(msg1.get_payload(), str)) - eq(msg1.get_payload(), self.linesep) - - - -# Test various other bits of the package's functionality -class TestMiscellaneous(TestEmailBase): - def test_message_from_string(self): - with openfile('msg_01.txt') as fp: - text = fp.read() - msg = email.message_from_string(text) - s = StringIO() - # Don't wrap/continue long headers since we're trying to test - # idempotency. - g = Generator(s, maxheaderlen=0) - g.flatten(msg) - self.assertEqual(text, s.getvalue()) - - def test_message_from_file(self): - with openfile('msg_01.txt') as fp: - text = fp.read() - fp.seek(0) - msg = email.message_from_file(fp) - s = StringIO() - # Don't wrap/continue long headers since we're trying to test - # idempotency. - g = Generator(s, maxheaderlen=0) - g.flatten(msg) - self.assertEqual(text, s.getvalue()) - - def test_message_from_string_with_class(self): - unless = self.assertTrue - with openfile('msg_01.txt') as fp: - text = fp.read() - - # Create a subclass - class MyMessage(Message): - pass - - msg = email.message_from_string(text, MyMessage) - unless(isinstance(msg, MyMessage)) - # Try something more complicated - with openfile('msg_02.txt') as fp: - text = fp.read() - msg = email.message_from_string(text, MyMessage) - for subpart in msg.walk(): - unless(isinstance(subpart, MyMessage)) - - def test_message_from_file_with_class(self): - unless = self.assertTrue - # Create a subclass - class MyMessage(Message): - pass - - with openfile('msg_01.txt') as fp: - msg = email.message_from_file(fp, MyMessage) - unless(isinstance(msg, MyMessage)) - # Try something more complicated - with openfile('msg_02.txt') as fp: - msg = email.message_from_file(fp, MyMessage) - for subpart in msg.walk(): - unless(isinstance(subpart, MyMessage)) - - def test__all__(self): - module = __import__('email') - # Can't use sorted() here due to Python 2.3 compatibility - all = module.__all__[:] - all.sort() - self.assertEqual(all, [ - 'base64mime', 'charset', 'encoders', 'errors', 'generator', - 'header', 'iterators', 'message', 'message_from_binary_file', - 'message_from_bytes', 'message_from_file', - 'message_from_string', 'mime', 'parser', - 'quoprimime', 'utils', - ]) - - def test_formatdate(self): - now = time.time() - self.assertEqual(utils.parsedate(utils.formatdate(now))[:6], - time.gmtime(now)[:6]) - - def test_formatdate_localtime(self): - now = time.time() - self.assertEqual( - utils.parsedate(utils.formatdate(now, localtime=True))[:6], - time.localtime(now)[:6]) - - def test_formatdate_usegmt(self): - now = time.time() - self.assertEqual( - utils.formatdate(now, localtime=False), - time.strftime('%a, %d %b %Y %H:%M:%S -0000', time.gmtime(now))) - self.assertEqual( - utils.formatdate(now, localtime=False, usegmt=True), - time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(now))) - - def test_parsedate_none(self): - self.assertEqual(utils.parsedate(''), None) - - def test_parsedate_compact(self): - # The FWS after the comma is optional - self.assertEqual(utils.parsedate('Wed,3 Apr 2002 14:58:26 +0800'), - utils.parsedate('Wed, 3 Apr 2002 14:58:26 +0800')) - - def test_parsedate_no_dayofweek(self): - eq = self.assertEqual - eq(utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'), - (2003, 2, 25, 13, 47, 26, 0, 1, -1, -28800)) - - def test_parsedate_compact_no_dayofweek(self): - eq = self.assertEqual - eq(utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'), - (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) - - def test_parsedate_no_space_before_positive_offset(self): - self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26+0800'), - (2002, 4, 3, 14, 58, 26, 0, 1, -1, 28800)) - - def test_parsedate_no_space_before_negative_offset(self): - # Issue 1155362: we already handled '+' for this case. - self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26-0800'), - (2002, 4, 3, 14, 58, 26, 0, 1, -1, -28800)) - - - def test_parsedate_accepts_time_with_dots(self): - eq = self.assertEqual - eq(utils.parsedate_tz('5 Feb 2003 13.47.26 -0800'), - (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) - eq(utils.parsedate_tz('5 Feb 2003 13.47 -0800'), - (2003, 2, 5, 13, 47, 0, 0, 1, -1, -28800)) - - def test_parsedate_acceptable_to_time_functions(self): - eq = self.assertEqual - timetup = utils.parsedate('5 Feb 2003 13:47:26 -0800') - t = int(time.mktime(timetup)) - eq(time.localtime(t)[:6], timetup[:6]) - eq(int(time.strftime('%Y', timetup)), 2003) - timetup = utils.parsedate_tz('5 Feb 2003 13:47:26 -0800') - t = int(time.mktime(timetup[:9])) - eq(time.localtime(t)[:6], timetup[:6]) - eq(int(time.strftime('%Y', timetup[:9])), 2003) - - def test_parsedate_y2k(self): - """Test for parsing a date with a two-digit year. - - Parsing a date with a two-digit year should return the correct - four-digit year. RFC822 allows two-digit years, but RFC2822 (which - obsoletes RFC822) requires four-digit years. - - """ - self.assertEqual(utils.parsedate_tz('25 Feb 03 13:47:26 -0800'), - utils.parsedate_tz('25 Feb 2003 13:47:26 -0800')) - self.assertEqual(utils.parsedate_tz('25 Feb 71 13:47:26 -0800'), - utils.parsedate_tz('25 Feb 1971 13:47:26 -0800')) - - def test_parseaddr_empty(self): - self.assertEqual(utils.parseaddr('<>'), ('', '')) - self.assertEqual(utils.formataddr(utils.parseaddr('<>')), '') - - def test_noquote_dump(self): - self.assertEqual( - utils.formataddr(('A Silly Person', 'person@dom.ain')), - 'A Silly Person ') - - def test_escape_dump(self): - self.assertEqual( - utils.formataddr(('A (Very) Silly Person', 'person@dom.ain')), - r'"A \(Very\) Silly Person" ') - a = r'A \(Special\) Person' - b = 'person@dom.ain' - self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) - - def test_escape_backslashes(self): - self.assertEqual( - utils.formataddr(('Arthur \Backslash\ Foobar', 'person@dom.ain')), - r'"Arthur \\Backslash\\ Foobar" ') - a = r'Arthur \Backslash\ Foobar' - b = 'person@dom.ain' - self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) - - def test_name_with_dot(self): - x = 'John X. Doe ' - y = '"John X. Doe" ' - a, b = ('John X. Doe', 'jxd@example.com') - self.assertEqual(utils.parseaddr(x), (a, b)) - self.assertEqual(utils.parseaddr(y), (a, b)) - # formataddr() quotes the name if there's a dot in it - self.assertEqual(utils.formataddr((a, b)), y) - - def test_parseaddr_preserves_quoted_pairs_in_addresses(self): - # issue 10005. Note that in the third test the second pair of - # backslashes is not actually a quoted pair because it is not inside a - # comment or quoted string: the address being parsed has a quoted - # string containing a quoted backslash, followed by 'example' and two - # backslashes, followed by another quoted string containing a space and - # the word 'example'. parseaddr copies those two backslashes - # literally. Per rfc5322 this is not technically correct since a \ may - # not appear in an address outside of a quoted string. It is probably - # a sensible Postel interpretation, though. - eq = self.assertEqual - eq(utils.parseaddr('""example" example"@example.com'), - ('', '""example" example"@example.com')) - eq(utils.parseaddr('"\\"example\\" example"@example.com'), - ('', '"\\"example\\" example"@example.com')) - eq(utils.parseaddr('"\\\\"example\\\\" example"@example.com'), - ('', '"\\\\"example\\\\" example"@example.com')) - - def test_parseaddr_preserves_spaces_in_local_part(self): - # issue 9286. A normal RFC5322 local part should not contain any - # folding white space, but legacy local parts can (they are a sequence - # of atoms, not dotatoms). On the other hand we strip whitespace from - # before the @ and around dots, on the assumption that the whitespace - # around the punctuation is a mistake in what would otherwise be - # an RFC5322 local part. Leading whitespace is, usual, stripped as well. - self.assertEqual(('', "merwok wok@xample.com"), - utils.parseaddr("merwok wok@xample.com")) - self.assertEqual(('', "merwok wok@xample.com"), - utils.parseaddr("merwok wok@xample.com")) - self.assertEqual(('', "merwok wok@xample.com"), - utils.parseaddr(" merwok wok @xample.com")) - self.assertEqual(('', 'merwok"wok" wok@xample.com'), - utils.parseaddr('merwok"wok" wok@xample.com')) - self.assertEqual(('', 'merwok.wok.wok@xample.com'), - utils.parseaddr('merwok. wok . wok@xample.com')) - - def test_multiline_from_comment(self): - x = """\ -Foo -\tBar """ - self.assertEqual(utils.parseaddr(x), ('Foo Bar', 'foo@example.com')) - - def test_quote_dump(self): - self.assertEqual( - utils.formataddr(('A Silly; Person', 'person@dom.ain')), - r'"A Silly; Person" ') - - def test_charset_richcomparisons(self): - eq = self.assertEqual - ne = self.assertNotEqual - cset1 = Charset() - cset2 = Charset() - eq(cset1, 'us-ascii') - eq(cset1, 'US-ASCII') - eq(cset1, 'Us-AsCiI') - eq('us-ascii', cset1) - eq('US-ASCII', cset1) - eq('Us-AsCiI', cset1) - ne(cset1, 'usascii') - ne(cset1, 'USASCII') - ne(cset1, 'UsAsCiI') - ne('usascii', cset1) - ne('USASCII', cset1) - ne('UsAsCiI', cset1) - eq(cset1, cset2) - eq(cset2, cset1) - - def test_getaddresses(self): - eq = self.assertEqual - eq(utils.getaddresses(['aperson@dom.ain (Al Person)', - 'Bud Person ']), - [('Al Person', 'aperson@dom.ain'), - ('Bud Person', 'bperson@dom.ain')]) - - def test_getaddresses_nasty(self): - eq = self.assertEqual - eq(utils.getaddresses(['foo: ;']), [('', '')]) - eq(utils.getaddresses( - ['[]*-- =~$']), - [('', ''), ('', ''), ('', '*--')]) - eq(utils.getaddresses( - ['foo: ;', '"Jason R. Mastaler" ']), - [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) - - def test_getaddresses_embedded_comment(self): - """Test proper handling of a nested comment""" - eq = self.assertEqual - addrs = utils.getaddresses(['User ((nested comment)) ']) - eq(addrs[0][1], 'foo@bar.com') - - def test_utils_quote_unquote(self): - eq = self.assertEqual - msg = Message() - msg.add_header('content-disposition', 'attachment', - filename='foo\\wacky"name') - eq(msg.get_filename(), 'foo\\wacky"name') - - def test_get_body_encoding_with_bogus_charset(self): - charset = Charset('not a charset') - self.assertEqual(charset.get_body_encoding(), 'base64') - - def test_get_body_encoding_with_uppercase_charset(self): - eq = self.assertEqual - msg = Message() - msg['Content-Type'] = 'text/plain; charset=UTF-8' - eq(msg['content-type'], 'text/plain; charset=UTF-8') - charsets = msg.get_charsets() - eq(len(charsets), 1) - eq(charsets[0], 'utf-8') - charset = Charset(charsets[0]) - eq(charset.get_body_encoding(), 'base64') - msg.set_payload(b'hello world', charset=charset) - eq(msg.get_payload(), 'aGVsbG8gd29ybGQ=\n') - eq(msg.get_payload(decode=True), b'hello world') - eq(msg['content-transfer-encoding'], 'base64') - # Try another one - msg = Message() - msg['Content-Type'] = 'text/plain; charset="US-ASCII"' - charsets = msg.get_charsets() - eq(len(charsets), 1) - eq(charsets[0], 'us-ascii') - charset = Charset(charsets[0]) - eq(charset.get_body_encoding(), encoders.encode_7or8bit) - msg.set_payload('hello world', charset=charset) - eq(msg.get_payload(), 'hello world') - eq(msg['content-transfer-encoding'], '7bit') - - def test_charsets_case_insensitive(self): - lc = Charset('us-ascii') - uc = Charset('US-ASCII') - self.assertEqual(lc.get_body_encoding(), uc.get_body_encoding()) - - def test_partial_falls_inside_message_delivery_status(self): - eq = self.ndiffAssertEqual - # The Parser interface provides chunks of data to FeedParser in 8192 - # byte gulps. SF bug #1076485 found one of those chunks inside - # message/delivery-status header block, which triggered an - # unreadline() of NeedMoreData. - msg = self._msgobj('msg_43.txt') - sfp = StringIO() - iterators._structure(msg, sfp) - eq(sfp.getvalue(), """\ -multipart/report - text/plain - message/delivery-status - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/plain - text/rfc822-headers -""") - - def test_make_msgid_domain(self): - self.assertEqual( - email.utils.make_msgid(domain='testdomain-string')[-19:], - '@testdomain-string>') - - -# Test the iterator/generators -class TestIterators(TestEmailBase): - def test_body_line_iterator(self): - eq = self.assertEqual - neq = self.ndiffAssertEqual - # First a simple non-multipart message - msg = self._msgobj('msg_01.txt') - it = iterators.body_line_iterator(msg) - lines = list(it) - eq(len(lines), 6) - neq(EMPTYSTRING.join(lines), msg.get_payload()) - # Now a more complicated multipart - msg = self._msgobj('msg_02.txt') - it = iterators.body_line_iterator(msg) - lines = list(it) - eq(len(lines), 43) - with openfile('msg_19.txt') as fp: - neq(EMPTYSTRING.join(lines), fp.read()) - - def test_typed_subpart_iterator(self): - eq = self.assertEqual - msg = self._msgobj('msg_04.txt') - it = iterators.typed_subpart_iterator(msg, 'text') - lines = [] - subparts = 0 - for subpart in it: - subparts += 1 - lines.append(subpart.get_payload()) - eq(subparts, 2) - eq(EMPTYSTRING.join(lines), """\ -a simple kind of mirror -to reflect upon our own -a simple kind of mirror -to reflect upon our own -""") - - def test_typed_subpart_iterator_default_type(self): - eq = self.assertEqual - msg = self._msgobj('msg_03.txt') - it = iterators.typed_subpart_iterator(msg, 'text', 'plain') - lines = [] - subparts = 0 - for subpart in it: - subparts += 1 - lines.append(subpart.get_payload()) - eq(subparts, 1) - eq(EMPTYSTRING.join(lines), """\ - -Hi, - -Do you like this message? - --Me -""") - - def test_pushCR_LF(self): - '''FeedParser BufferedSubFile.push() assumed it received complete - line endings. A CR ending one push() followed by a LF starting - the next push() added an empty line. - ''' - imt = [ - ("a\r \n", 2), - ("b", 0), - ("c\n", 1), - ("", 0), - ("d\r\n", 1), - ("e\r", 0), - ("\nf", 1), - ("\r\n", 1), - ] - from email.feedparser import BufferedSubFile, NeedMoreData - bsf = BufferedSubFile() - om = [] - nt = 0 - for il, n in imt: - bsf.push(il) - nt += n - n1 = 0 - while True: - ol = bsf.readline() - if ol == NeedMoreData: - break - om.append(ol) - n1 += 1 - self.assertTrue(n == n1) - self.assertTrue(len(om) == nt) - self.assertTrue(''.join([il for il, n in imt]) == ''.join(om)) - - - -class TestParsers(TestEmailBase): - def test_header_parser(self): - eq = self.assertEqual - # Parse only the headers of a complex multipart MIME document - with openfile('msg_02.txt') as fp: - msg = HeaderParser().parse(fp) - eq(msg['from'], 'ppp-request@zzz.org') - eq(msg['to'], 'ppp@zzz.org') - eq(msg.get_content_type(), 'multipart/mixed') - self.assertFalse(msg.is_multipart()) - self.assertTrue(isinstance(msg.get_payload(), str)) - - def test_whitespace_continuation(self): - eq = self.assertEqual - # This message contains a line after the Subject: header that has only - # whitespace, but it is not empty! - msg = email.message_from_string("""\ -From: aperson@dom.ain -To: bperson@dom.ain -Subject: the next line has a space on it -\x20 -Date: Mon, 8 Apr 2002 15:09:19 -0400 -Message-ID: spam - -Here's the message body -""") - eq(msg['subject'], 'the next line has a space on it\n ') - eq(msg['message-id'], 'spam') - eq(msg.get_payload(), "Here's the message body\n") - - def test_whitespace_continuation_last_header(self): - eq = self.assertEqual - # Like the previous test, but the subject line is the last - # header. - msg = email.message_from_string("""\ -From: aperson@dom.ain -To: bperson@dom.ain -Date: Mon, 8 Apr 2002 15:09:19 -0400 -Message-ID: spam -Subject: the next line has a space on it -\x20 - -Here's the message body -""") - eq(msg['subject'], 'the next line has a space on it\n ') - eq(msg['message-id'], 'spam') - eq(msg.get_payload(), "Here's the message body\n") - - def test_crlf_separation(self): - eq = self.assertEqual - with openfile('msg_26.txt', newline='\n') as fp: - msg = Parser().parse(fp) - eq(len(msg.get_payload()), 2) - part1 = msg.get_payload(0) - eq(part1.get_content_type(), 'text/plain') - eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n') - part2 = msg.get_payload(1) - eq(part2.get_content_type(), 'application/riscos') - - def test_crlf_flatten(self): - # Using newline='\n' preserves the crlfs in this input file. - with openfile('msg_26.txt', newline='\n') as fp: - text = fp.read() - msg = email.message_from_string(text) - s = StringIO() - g = Generator(s) - g.flatten(msg, linesep='\r\n') - self.assertEqual(s.getvalue(), text) - - maxDiff = None - - def test_multipart_digest_with_extra_mime_headers(self): - eq = self.assertEqual - neq = self.ndiffAssertEqual - with openfile('msg_28.txt') as fp: - msg = email.message_from_file(fp) - # Structure is: - # multipart/digest - # message/rfc822 - # text/plain - # message/rfc822 - # text/plain - eq(msg.is_multipart(), 1) - eq(len(msg.get_payload()), 2) - part1 = msg.get_payload(0) - eq(part1.get_content_type(), 'message/rfc822') - eq(part1.is_multipart(), 1) - eq(len(part1.get_payload()), 1) - part1a = part1.get_payload(0) - eq(part1a.is_multipart(), 0) - eq(part1a.get_content_type(), 'text/plain') - neq(part1a.get_payload(), 'message 1\n') - # next message/rfc822 - part2 = msg.get_payload(1) - eq(part2.get_content_type(), 'message/rfc822') - eq(part2.is_multipart(), 1) - eq(len(part2.get_payload()), 1) - part2a = part2.get_payload(0) - eq(part2a.is_multipart(), 0) - eq(part2a.get_content_type(), 'text/plain') - neq(part2a.get_payload(), 'message 2\n') - - def test_three_lines(self): - # A bug report by Andrew McNamara - lines = ['From: Andrew Person From', 'From']) - eq(msg.get_payload(), 'body') - - def test_rfc2822_space_not_allowed_in_header(self): - eq = self.assertEqual - m = '>From foo@example.com 11:25:53\nFrom: bar\n!"#QUX;~: zoo\n\nbody' - msg = email.message_from_string(m) - eq(len(msg.keys()), 0) - - def test_rfc2822_one_character_header(self): - eq = self.assertEqual - m = 'A: first header\nB: second header\nCC: third header\n\nbody' - msg = email.message_from_string(m) - headers = msg.keys() - headers.sort() - eq(headers, ['A', 'B', 'CC']) - eq(msg.get_payload(), 'body') - - def test_CRLFLF_at_end_of_part(self): - # issue 5610: feedparser should not eat two chars from body part ending - # with "\r\n\n". - m = ( - "From: foo@bar.com\n" - "To: baz\n" - "Mime-Version: 1.0\n" - "Content-Type: multipart/mixed; boundary=BOUNDARY\n" - "\n" - "--BOUNDARY\n" - "Content-Type: text/plain\n" - "\n" - "body ending with CRLF newline\r\n" - "\n" - "--BOUNDARY--\n" - ) - msg = email.message_from_string(m) - self.assertTrue(msg.get_payload(0).get_payload().endswith('\r\n')) - - -class Test8BitBytesHandling(unittest.TestCase): - # In Python3 all input is string, but that doesn't work if the actual input - # uses an 8bit transfer encoding. To hack around that, in email 5.1 we - # decode byte streams using the surrogateescape error handler, and - # reconvert to binary at appropriate places if we detect surrogates. This - # doesn't allow us to transform headers with 8bit bytes (they get munged), - # but it does allow us to parse and preserve them, and to decode body - # parts that use an 8bit CTE. - - bodytest_msg = textwrap.dedent("""\ - From: foo@bar.com - To: baz - Mime-Version: 1.0 - Content-Type: text/plain; charset={charset} - Content-Transfer-Encoding: {cte} - - {bodyline} - """) - - def test_known_8bit_CTE(self): - m = self.bodytest_msg.format(charset='utf-8', - cte='8bit', - bodyline='pöstal').encode('utf-8') - msg = email.message_from_bytes(m) - self.assertEqual(msg.get_payload(), "pöstal\n") - self.assertEqual(msg.get_payload(decode=True), - "pöstal\n".encode('utf-8')) - - def test_unknown_8bit_CTE(self): - m = self.bodytest_msg.format(charset='notavalidcharset', - cte='8bit', - bodyline='pöstal').encode('utf-8') - msg = email.message_from_bytes(m) - self.assertEqual(msg.get_payload(), "p\uFFFD\uFFFDstal\n") - self.assertEqual(msg.get_payload(decode=True), - "pöstal\n".encode('utf-8')) - - def test_8bit_in_quopri_body(self): - # This is non-RFC compliant data...without 'decode' the library code - # decodes the body using the charset from the headers, and because the - # source byte really is utf-8 this works. This is likely to fail - # against real dirty data (ie: produce mojibake), but the data is - # invalid anyway so it is as good a guess as any. But this means that - # this test just confirms the current behavior; that behavior is not - # necessarily the best possible behavior. With 'decode' it is - # returning the raw bytes, so that test should be of correct behavior, - # or at least produce the same result that email4 did. - m = self.bodytest_msg.format(charset='utf-8', - cte='quoted-printable', - bodyline='p=C3=B6stál').encode('utf-8') - msg = email.message_from_bytes(m) - self.assertEqual(msg.get_payload(), 'p=C3=B6stál\n') - self.assertEqual(msg.get_payload(decode=True), - 'pöstál\n'.encode('utf-8')) - - def test_invalid_8bit_in_non_8bit_cte_uses_replace(self): - # This is similar to the previous test, but proves that if the 8bit - # byte is undecodeable in the specified charset, it gets replaced - # by the unicode 'unknown' character. Again, this may or may not - # be the ideal behavior. Note that if decode=False none of the - # decoders will get involved, so this is the only test we need - # for this behavior. - m = self.bodytest_msg.format(charset='ascii', - cte='quoted-printable', - bodyline='p=C3=B6stál').encode('utf-8') - msg = email.message_from_bytes(m) - self.assertEqual(msg.get_payload(), 'p=C3=B6st\uFFFD\uFFFDl\n') - self.assertEqual(msg.get_payload(decode=True), - 'pöstál\n'.encode('utf-8')) - - def test_8bit_in_base64_body(self): - # Sticking an 8bit byte in a base64 block makes it undecodable by - # normal means, so the block is returned undecoded, but as bytes. - m = self.bodytest_msg.format(charset='utf-8', - cte='base64', - bodyline='cMO2c3RhbAá=').encode('utf-8') - msg = email.message_from_bytes(m) - self.assertEqual(msg.get_payload(decode=True), - 'cMO2c3RhbAá=\n'.encode('utf-8')) - - def test_8bit_in_uuencode_body(self): - # Sticking an 8bit byte in a uuencode block makes it undecodable by - # normal means, so the block is returned undecoded, but as bytes. - m = self.bodytest_msg.format(charset='utf-8', - cte='uuencode', - bodyline='<,.V7bit conversion. - self.assertEqual(out.getvalue(), - self.latin_bin_msg.decode('latin-1')+'\n') - - def test_bytes_feedparser(self): - bfp = email.feedparser.BytesFeedParser() - for i in range(0, len(self.latin_bin_msg), 10): - bfp.feed(self.latin_bin_msg[i:i+10]) - m = bfp.close() - self.assertEqual(str(m), self.latin_bin_msg_as7bit) - - def test_crlf_flatten(self): - with openfile('msg_26.txt', 'rb') as fp: - text = fp.read() - msg = email.message_from_bytes(text) - s = BytesIO() - g = email.generator.BytesGenerator(s) - g.flatten(msg, linesep='\r\n') - self.assertEqual(s.getvalue(), text) - maxDiff = None - - -class BaseTestBytesGeneratorIdempotent: - - maxDiff = None - - def _msgobj(self, filename): - with openfile(filename, 'rb') as fp: - data = fp.read() - data = self.normalize_linesep_regex.sub(self.blinesep, data) - msg = email.message_from_bytes(data) - return msg, data - - def _idempotent(self, msg, data, unixfrom=False): - b = BytesIO() - g = email.generator.BytesGenerator(b, maxheaderlen=0) - g.flatten(msg, unixfrom=unixfrom, linesep=self.linesep) - self.assertByteStringsEqual(data, b.getvalue()) - - def assertByteStringsEqual(self, str1, str2): - # Not using self.blinesep here is intentional. This way the output - # is more useful when the failure results in mixed line endings. - self.assertListEqual(str1.split(b'\n'), str2.split(b'\n')) - - -class TestBytesGeneratorIdempotentNL(BaseTestBytesGeneratorIdempotent, - TestIdempotent): - linesep = '\n' - blinesep = b'\n' - normalize_linesep_regex = re.compile(br'\r\n') - - -class TestBytesGeneratorIdempotentCRLF(BaseTestBytesGeneratorIdempotent, - TestIdempotent): - linesep = '\r\n' - blinesep = b'\r\n' - normalize_linesep_regex = re.compile(br'(? -Delivered-To: bbb@zzz.org -Received: by mail.zzz.org (Postfix, from userid 889) -\tid 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Message-ID: <15090.61304.110929.45684@aaa.zzz.org> -From: bbb@ddd.com (John X. Doe) -To: bbb@zzz.org -Subject: This is a test message -Date: Fri, 4 May 2001 14:05:44 -0400 -Content-Type: text/plain; charset=us-ascii; - title*=us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21 - - -Hi, - -Do you like this message? - --Me -""") - - def test_del_param(self): - eq = self.ndiffAssertEqual - msg = self._msgobj('msg_01.txt') - msg.set_param('foo', 'bar', charset='us-ascii', language='en') - msg.set_param('title', 'This is even more ***fun*** isn\'t it!', - charset='us-ascii', language='en') - msg.del_param('foo', header='Content-Type') - eq(msg.as_string(maxheaderlen=78), """\ -Return-Path: -Delivered-To: bbb@zzz.org -Received: by mail.zzz.org (Postfix, from userid 889) -\tid 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) -MIME-Version: 1.0 -Content-Transfer-Encoding: 7bit -Message-ID: <15090.61304.110929.45684@aaa.zzz.org> -From: bbb@ddd.com (John X. Doe) -To: bbb@zzz.org -Subject: This is a test message -Date: Fri, 4 May 2001 14:05:44 -0400 -Content-Type: text/plain; charset="us-ascii"; - title*=us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21 - - -Hi, - -Do you like this message? - --Me -""") - - def test_rfc2231_get_content_charset(self): - eq = self.assertEqual - msg = self._msgobj('msg_32.txt') - eq(msg.get_content_charset(), 'us-ascii') - - def test_rfc2231_parse_rfc_quoting(self): - m = textwrap.dedent('''\ - Content-Disposition: inline; - \tfilename*0*=''This%20is%20even%20more%20; - \tfilename*1*=%2A%2A%2Afun%2A%2A%2A%20; - \tfilename*2="is it not.pdf" - - ''') - msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), - 'This is even more ***fun*** is it not.pdf') - self.assertEqual(m, msg.as_string()) - - def test_rfc2231_parse_extra_quoting(self): - m = textwrap.dedent('''\ - Content-Disposition: inline; - \tfilename*0*="''This%20is%20even%20more%20"; - \tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; - \tfilename*2="is it not.pdf" - - ''') - msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), - 'This is even more ***fun*** is it not.pdf') - self.assertEqual(m, msg.as_string()) - - def test_rfc2231_no_language_or_charset(self): - m = '''\ -Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename="file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm" -Content-Type: text/html; NAME*0=file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEM; NAME*1=P_nsmail.htm - -''' - msg = email.message_from_string(m) - param = msg.get_param('NAME') - self.assertFalse(isinstance(param, tuple)) - self.assertEqual( - param, - 'file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm') - - def test_rfc2231_no_language_or_charset_in_filename(self): - m = '''\ -Content-Disposition: inline; -\tfilename*0*="''This%20is%20even%20more%20"; -\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; -\tfilename*2="is it not.pdf" - -''' - msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), - 'This is even more ***fun*** is it not.pdf') - - def test_rfc2231_no_language_or_charset_in_filename_encoded(self): - m = '''\ -Content-Disposition: inline; -\tfilename*0*="''This%20is%20even%20more%20"; -\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; -\tfilename*2="is it not.pdf" - -''' - msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), - 'This is even more ***fun*** is it not.pdf') - - def test_rfc2231_partly_encoded(self): - m = '''\ -Content-Disposition: inline; -\tfilename*0="''This%20is%20even%20more%20"; -\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; -\tfilename*2="is it not.pdf" - -''' - msg = email.message_from_string(m) - self.assertEqual( - msg.get_filename(), - 'This%20is%20even%20more%20***fun*** is it not.pdf') - - def test_rfc2231_partly_nonencoded(self): - m = '''\ -Content-Disposition: inline; -\tfilename*0="This%20is%20even%20more%20"; -\tfilename*1="%2A%2A%2Afun%2A%2A%2A%20"; -\tfilename*2="is it not.pdf" - -''' - msg = email.message_from_string(m) - self.assertEqual( - msg.get_filename(), - 'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20is it not.pdf') - - def test_rfc2231_no_language_or_charset_in_boundary(self): - m = '''\ -Content-Type: multipart/alternative; -\tboundary*0*="''This%20is%20even%20more%20"; -\tboundary*1*="%2A%2A%2Afun%2A%2A%2A%20"; -\tboundary*2="is it not.pdf" - -''' - msg = email.message_from_string(m) - self.assertEqual(msg.get_boundary(), - 'This is even more ***fun*** is it not.pdf') - - def test_rfc2231_no_language_or_charset_in_charset(self): - # This is a nonsensical charset value, but tests the code anyway - m = '''\ -Content-Type: text/plain; -\tcharset*0*="This%20is%20even%20more%20"; -\tcharset*1*="%2A%2A%2Afun%2A%2A%2A%20"; -\tcharset*2="is it not.pdf" - -''' - msg = email.message_from_string(m) - self.assertEqual(msg.get_content_charset(), - 'this is even more ***fun*** is it not.pdf') - - def test_rfc2231_bad_encoding_in_filename(self): - m = '''\ -Content-Disposition: inline; -\tfilename*0*="bogus'xx'This%20is%20even%20more%20"; -\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; -\tfilename*2="is it not.pdf" - -''' - msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), - 'This is even more ***fun*** is it not.pdf') - - def test_rfc2231_bad_encoding_in_charset(self): - m = """\ -Content-Type: text/plain; charset*=bogus''utf-8%E2%80%9D - -""" - msg = email.message_from_string(m) - # This should return None because non-ascii characters in the charset - # are not allowed. - self.assertEqual(msg.get_content_charset(), None) - - def test_rfc2231_bad_character_in_charset(self): - m = """\ -Content-Type: text/plain; charset*=ascii''utf-8%E2%80%9D - -""" - msg = email.message_from_string(m) - # This should return None because non-ascii characters in the charset - # are not allowed. - self.assertEqual(msg.get_content_charset(), None) - - def test_rfc2231_bad_character_in_filename(self): - m = '''\ -Content-Disposition: inline; -\tfilename*0*="ascii'xx'This%20is%20even%20more%20"; -\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; -\tfilename*2*="is it not.pdf%E2" - -''' - msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), - 'This is even more ***fun*** is it not.pdf\ufffd') - - def test_rfc2231_unknown_encoding(self): - m = """\ -Content-Transfer-Encoding: 8bit -Content-Disposition: inline; filename*=X-UNKNOWN''myfile.txt - -""" - msg = email.message_from_string(m) - self.assertEqual(msg.get_filename(), 'myfile.txt') - - def test_rfc2231_single_tick_in_filename_extended(self): - eq = self.assertEqual - m = """\ -Content-Type: application/x-foo; -\tname*0*=\"Frank's\"; name*1*=\" Document\" - -""" - msg = email.message_from_string(m) - charset, language, s = msg.get_param('name') - eq(charset, None) - eq(language, None) - eq(s, "Frank's Document") - - def test_rfc2231_single_tick_in_filename(self): - m = """\ -Content-Type: application/x-foo; name*0=\"Frank's\"; name*1=\" Document\" - -""" - msg = email.message_from_string(m) - param = msg.get_param('name') - self.assertFalse(isinstance(param, tuple)) - self.assertEqual(param, "Frank's Document") - - def test_rfc2231_tick_attack_extended(self): - eq = self.assertEqual - m = """\ -Content-Type: application/x-foo; -\tname*0*=\"us-ascii'en-us'Frank's\"; name*1*=\" Document\" - -""" - msg = email.message_from_string(m) - charset, language, s = msg.get_param('name') - eq(charset, 'us-ascii') - eq(language, 'en-us') - eq(s, "Frank's Document") - - def test_rfc2231_tick_attack(self): - m = """\ -Content-Type: application/x-foo; -\tname*0=\"us-ascii'en-us'Frank's\"; name*1=\" Document\" - -""" - msg = email.message_from_string(m) - param = msg.get_param('name') - self.assertFalse(isinstance(param, tuple)) - self.assertEqual(param, "us-ascii'en-us'Frank's Document") - - def test_rfc2231_no_extended_values(self): - eq = self.assertEqual - m = """\ -Content-Type: application/x-foo; name=\"Frank's Document\" - -""" - msg = email.message_from_string(m) - eq(msg.get_param('name'), "Frank's Document") - - def test_rfc2231_encoded_then_unencoded_segments(self): - eq = self.assertEqual - m = """\ -Content-Type: application/x-foo; -\tname*0*=\"us-ascii'en-us'My\"; -\tname*1=\" Document\"; -\tname*2*=\" For You\" - -""" - msg = email.message_from_string(m) - charset, language, s = msg.get_param('name') - eq(charset, 'us-ascii') - eq(language, 'en-us') - eq(s, 'My Document For You') - - def test_rfc2231_unencoded_then_encoded_segments(self): - eq = self.assertEqual - m = """\ -Content-Type: application/x-foo; -\tname*0=\"us-ascii'en-us'My\"; -\tname*1*=\" Document\"; -\tname*2*=\" For You\" - -""" - msg = email.message_from_string(m) - charset, language, s = msg.get_param('name') - eq(charset, 'us-ascii') - eq(language, 'en-us') - eq(s, 'My Document For You') - - - -# Tests to ensure that signed parts of an email are completely preserved, as -# required by RFC1847 section 2.1. Note that these are incomplete, because the -# email package does not currently always preserve the body. See issue 1670765. -class TestSigned(TestEmailBase): - - def _msg_and_obj(self, filename): - with openfile(findfile(filename)) as fp: - original = fp.read() - msg = email.message_from_string(original) - return original, msg - - def _signed_parts_eq(self, original, result): - # Extract the first mime part of each message - import re - repart = re.compile(r'^--([^\n]+)\n(.*?)\n--\1$', re.S | re.M) - inpart = repart.search(original).group(2) - outpart = repart.search(result).group(2) - self.assertEqual(outpart, inpart) - - def test_long_headers_as_string(self): - original, msg = self._msg_and_obj('msg_45.txt') - result = msg.as_string() - self._signed_parts_eq(original, result) - - def test_long_headers_as_string_maxheaderlen(self): - original, msg = self._msg_and_obj('msg_45.txt') - result = msg.as_string(maxheaderlen=60) - self._signed_parts_eq(original, result) - - def test_long_headers_flatten(self): - original, msg = self._msg_and_obj('msg_45.txt') - fp = StringIO() - Generator(fp).flatten(msg) - result = fp.getvalue() - self._signed_parts_eq(original, result) - - - -def _testclasses(): - mod = sys.modules[__name__] - return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')] - - -def suite(): - suite = unittest.TestSuite() - for testclass in _testclasses(): - suite.addTest(unittest.makeSuite(testclass)) - return suite - - -def test_main(): - for testclass in _testclasses(): - run_unittest(testclass) - - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/test_email_codecs.py --- a/Lib/email/test/test_email_codecs.py Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -# Copyright (C) 2002-2006 Python Software Foundation -# Contact: email-sig@python.org -# email package unit tests for (optional) Asian codecs - -import unittest -from test.support import run_unittest - -from email.test.test_email import TestEmailBase -from email.charset import Charset -from email.header import Header, decode_header -from email.message import Message - -# We're compatible with Python 2.3, but it doesn't have the built-in Asian -# codecs, so we have to skip all these tests. -try: - str('foo', 'euc-jp') -except LookupError: - raise unittest.SkipTest - - - -class TestEmailAsianCodecs(TestEmailBase): - def test_japanese_codecs(self): - eq = self.ndiffAssertEqual - j = Charset("euc-jp") - g = Charset("iso-8859-1") - h = Header("Hello World!") - jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa' - ghello = 'Gr\xfc\xdf Gott!' - h.append(jhello, j) - h.append(ghello, g) - # BAW: This used to -- and maybe should -- fold the two iso-8859-1 - # chunks into a single encoded word. However it doesn't violate the - # standard to have them as two encoded chunks and maybe it's - # reasonable for each .append() call to result in a separate - # encoded word. - eq(h.encode(), """\ -Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?= - =?iso-8859-1?q?Gr=FC=DF?= =?iso-8859-1?q?_Gott!?=""") - eq(decode_header(h.encode()), - [('Hello World!', None), - ('\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'), - ('Gr\xfc\xdf Gott!', 'iso-8859-1')]) - int = 'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5\xa4\xec\xa4\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2\xf1\xbc\xd4\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3\xa4\xc6\xa4\xa4\xa4\xde\xa4\xb9' - h = Header(int, j, header_name="Subject") - # test a very long header - enc = h.encode() - # TK: splitting point may differ by codec design and/or Header encoding - eq(enc , """\ -=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKGyhC?= - =?iso-2022-jp?b?GyRCMnE8VCROPjVHJyRyQlQkQyRGJCQkXiQ5GyhC?=""") - # TK: full decode comparison - eq(h.__unicode__().encode('euc-jp'), int) - - def test_payload_encoding(self): - jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa' - jcode = 'euc-jp' - msg = Message() - msg.set_payload(jhello, jcode) - ustr = str(msg.get_payload(), msg.get_content_charset()) - self.assertEqual(jhello, ustr.encode(jcode)) - - - -def suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestEmailAsianCodecs)) - return suite - - -def test_main(): - run_unittest(TestEmailAsianCodecs) - - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff -r 9dcd88977694 -r 650549138a3d Lib/email/test/test_email_torture.py --- a/Lib/email/test/test_email_torture.py Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,136 +0,0 @@ -# Copyright (C) 2002-2004 Python Software Foundation -# -# A torture test of the email package. This should not be run as part of the -# standard Python test suite since it requires several meg of email messages -# collected in the wild. These source messages are not checked into the -# Python distro, but are available as part of the standalone email package at -# http://sf.net/projects/mimelib - -import sys -import os -import unittest -from io import StringIO -from types import ListType - -from email.test.test_email import TestEmailBase -from test.support import TestSkipped, run_unittest - -import email -from email import __file__ as testfile -from email.iterators import _structure - -def openfile(filename): - from os.path import join, dirname, abspath - path = abspath(join(dirname(testfile), os.pardir, 'moredata', filename)) - return open(path, 'r') - -# Prevent this test from running in the Python distro -try: - openfile('crispin-torture.txt') -except IOError: - raise TestSkipped - - - -class TortureBase(TestEmailBase): - def _msgobj(self, filename): - fp = openfile(filename) - try: - msg = email.message_from_file(fp) - finally: - fp.close() - return msg - - - -class TestCrispinTorture(TortureBase): - # Mark Crispin's torture test from the SquirrelMail project - def test_mondo_message(self): - eq = self.assertEqual - neq = self.ndiffAssertEqual - msg = self._msgobj('crispin-torture.txt') - payload = msg.get_payload() - eq(type(payload), ListType) - eq(len(payload), 12) - eq(msg.preamble, None) - eq(msg.epilogue, '\n') - # Probably the best way to verify the message is parsed correctly is to - # dump its structure and compare it against the known structure. - fp = StringIO() - _structure(msg, fp=fp) - neq(fp.getvalue(), """\ -multipart/mixed - text/plain - message/rfc822 - multipart/alternative - text/plain - multipart/mixed - text/richtext - application/andrew-inset - message/rfc822 - audio/basic - audio/basic - image/pbm - message/rfc822 - multipart/mixed - multipart/mixed - text/plain - audio/x-sun - multipart/mixed - image/gif - image/gif - application/x-be2 - application/atomicmail - audio/x-sun - message/rfc822 - multipart/mixed - text/plain - image/pgm - text/plain - message/rfc822 - multipart/mixed - text/plain - image/pbm - message/rfc822 - application/postscript - image/gif - message/rfc822 - multipart/mixed - audio/basic - audio/basic - message/rfc822 - multipart/mixed - application/postscript - text/plain - message/rfc822 - multipart/mixed - text/plain - multipart/parallel - image/gif - audio/basic - application/atomicmail - message/rfc822 - audio/x-sun -""") - - -def _testclasses(): - mod = sys.modules[__name__] - return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')] - - -def suite(): - suite = unittest.TestSuite() - for testclass in _testclasses(): - suite.addTest(unittest.makeSuite(testclass)) - return suite - - -def test_main(): - for testclass in _testclasses(): - run_unittest(testclass) - - - -if __name__ == '__main__': - unittest.main(defaultTest='suite') diff -r 9dcd88977694 -r 650549138a3d Lib/functools.py --- a/Lib/functools.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/functools.py Tue Mar 22 21:32:51 2011 +1000 @@ -96,6 +96,7 @@ def cmp_to_key(mycmp): """Convert a cmp= function into a key= function""" class K(object): + __slots__ = ['obj'] def __init__(self, obj, *args): self.obj = obj def __lt__(self, other): @@ -140,7 +141,7 @@ tuple=tuple, sorted=sorted, len=len, KeyError=KeyError): hits = misses = 0 - kwd_mark = object() # separates positional and keyword args + kwd_mark = (object(),) # separates positional and keyword args lock = Lock() # needed because ordereddicts aren't threadsafe if maxsize is None: @@ -151,7 +152,7 @@ nonlocal hits, misses key = args if kwds: - key += (kwd_mark,) + tuple(sorted(kwds.items())) + key += kwd_mark + tuple(sorted(kwds.items())) try: result = cache[key] hits += 1 @@ -170,7 +171,7 @@ nonlocal hits, misses key = args if kwds: - key += (kwd_mark,) + tuple(sorted(kwds.items())) + key += kwd_mark + tuple(sorted(kwds.items())) try: with lock: result = cache[key] diff -r 9dcd88977694 -r 650549138a3d Lib/getopt.py --- a/Lib/getopt.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/getopt.py Tue Mar 22 21:32:51 2011 +1000 @@ -19,7 +19,7 @@ # Gerrit Holl moved the string-based exceptions # to class-based exceptions. # -# Peter Åstrand added gnu_getopt(). +# Peter Åstrand added gnu_getopt(). # # TODO for gnu_getopt(): # @@ -34,6 +34,11 @@ __all__ = ["GetoptError","error","getopt","gnu_getopt"] import os +try: + from gettext import gettext as _ +except ImportError: + # Bootstrapping Python: gettext's dependencies not built yet + def _(s): return s class GetoptError(Exception): opt = '' @@ -153,10 +158,10 @@ if has_arg: if optarg is None: if not args: - raise GetoptError('option --%s requires argument' % opt, opt) + raise GetoptError(_('option --%s requires argument') % opt, opt) optarg, args = args[0], args[1:] elif optarg is not None: - raise GetoptError('option --%s must not have an argument' % opt, opt) + raise GetoptError(_('option --%s must not have an argument') % opt, opt) opts.append(('--' + opt, optarg or '')) return opts, args @@ -166,7 +171,7 @@ def long_has_args(opt, longopts): possibilities = [o for o in longopts if o.startswith(opt)] if not possibilities: - raise GetoptError('option --%s not recognized' % opt, opt) + raise GetoptError(_('option --%s not recognized') % opt, opt) # Is there an exact match? if opt in possibilities: return False, opt @@ -176,7 +181,7 @@ if len(possibilities) > 1: # XXX since possibilities contains all valid continuations, might be # nice to work them into the error msg - raise GetoptError('option --%s not a unique prefix' % opt, opt) + raise GetoptError(_('option --%s not a unique prefix') % opt, opt) assert len(possibilities) == 1 unique_match = possibilities[0] has_arg = unique_match.endswith('=') @@ -190,7 +195,7 @@ if short_has_arg(opt, shortopts): if optstring == '': if not args: - raise GetoptError('option -%s requires argument' % opt, + raise GetoptError(_('option -%s requires argument') % opt, opt) optstring, args = args[0], args[1:] optarg, optstring = optstring, '' @@ -203,7 +208,7 @@ for i in range(len(shortopts)): if opt == shortopts[i] != ':': return shortopts.startswith(':', i+1) - raise GetoptError('option -%s not recognized' % opt, opt) + raise GetoptError(_('option -%s not recognized') % opt, opt) if __name__ == '__main__': import sys diff -r 9dcd88977694 -r 650549138a3d Lib/http/cookiejar.py --- a/Lib/http/cookiejar.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/http/cookiejar.py Tue Mar 22 21:32:51 2011 +1000 @@ -29,6 +29,7 @@ 'FileCookieJar', 'LWPCookieJar', 'LoadError', 'MozillaCookieJar'] import copy +import datetime import re import time import urllib.parse, urllib.request @@ -97,10 +98,12 @@ 1994-11-24 08:49:37Z """ - if t is None: t = time.time() - year, mon, mday, hour, min, sec = time.gmtime(t)[:6] + if t is None: + dt = datetime.datetime.utcnow() + else: + dt = datetime.datetime.utcfromtimestamp(t) return "%04d-%02d-%02d %02d:%02d:%02dZ" % ( - year, mon, mday, hour, min, sec) + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) def time2netscape(t=None): """Return a string representing time in seconds since epoch, t. @@ -113,10 +116,13 @@ Wed, DD-Mon-YYYY HH:MM:SS GMT """ - if t is None: t = time.time() - year, mon, mday, hour, min, sec, wday = time.gmtime(t)[:7] + if t is None: + dt = datetime.datetime.utcnow() + else: + dt = datetime.datetime.utcfromtimestamp(t) return "%s %02d-%s-%04d %02d:%02d:%02d GMT" % ( - DAYS[wday], mday, MONTHS[mon-1], year, hour, min, sec) + DAYS[dt.weekday()], dt.day, MONTHS[dt.month-1], + dt.year, dt.hour, dt.minute, dt.second) UTC_ZONES = {"GMT": None, "UTC": None, "UT": None, "Z": None} diff -r 9dcd88977694 -r 650549138a3d Lib/http/server.py --- a/Lib/http/server.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/http/server.py Tue Mar 22 21:32:51 2011 +1000 @@ -103,15 +103,19 @@ # Default error message template DEFAULT_ERROR_MESSAGE = """\ - -Error response - - -

Error response

-

Error code %(code)d. -

Message: %(message)s. -

Error code explanation: %(code)s = %(explain)s. - + + + + Error response + + +

Error response

+

Error code: %(code)d

+

Message: %(message)s.

+

Error code explanation: %(code)s - %(explain)s.

+ + """ DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8" diff -r 9dcd88977694 -r 650549138a3d Lib/idlelib/FormatParagraph.py --- a/Lib/idlelib/FormatParagraph.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/idlelib/FormatParagraph.py Tue Mar 22 21:32:51 2011 +1000 @@ -54,7 +54,7 @@ # If the block ends in a \n, we dont want the comment # prefix inserted after it. (Im not sure it makes sense to # reformat a comment block that isnt made of complete - # lines, but whatever!) Can't think of a clean soltution, + # lines, but whatever!) Can't think of a clean solution, # so we hack away block_suffix = "" if not newdata[-1]: diff -r 9dcd88977694 -r 650549138a3d Lib/idlelib/extend.txt --- a/Lib/idlelib/extend.txt Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/idlelib/extend.txt Tue Mar 22 21:32:51 2011 +1000 @@ -18,7 +18,7 @@ An IDLE extension class is instantiated with a single argument, `editwin', an EditorWindow instance. The extension cannot assume much -about this argument, but it is guarateed to have the following instance +about this argument, but it is guaranteed to have the following instance variables: text a Text instance (a widget) diff -r 9dcd88977694 -r 650549138a3d Lib/idlelib/macosxSupport.py --- a/Lib/idlelib/macosxSupport.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/idlelib/macosxSupport.py Tue Mar 22 21:32:51 2011 +1000 @@ -53,8 +53,8 @@ def addOpenEventSupport(root, flist): """ - This ensures that the application will respont to open AppleEvents, which - makes is feaseable to use IDLE as the default application for python files. + This ensures that the application will respond to open AppleEvents, which + makes is feasible to use IDLE as the default application for python files. """ def doOpenFile(*args): for fn in args: diff -r 9dcd88977694 -r 650549138a3d Lib/inspect.py --- a/Lib/inspect.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/inspect.py Tue Mar 22 21:32:51 2011 +1000 @@ -1069,15 +1069,16 @@ instance_dict = object.__getattribute__(obj, "__dict__") except AttributeError: pass - return instance_dict.get(attr, _sentinel) + return dict.get(instance_dict, attr, _sentinel) def _check_class(klass, attr): for entry in _static_getmro(klass): - try: - return entry.__dict__[attr] - except KeyError: - pass + if not _shadowed_dict(type(entry)): + try: + return entry.__dict__[attr] + except KeyError: + pass return _sentinel def _is_type(obj): @@ -1087,6 +1088,19 @@ return False return True +def _shadowed_dict(klass): + dict_attr = type.__dict__["__dict__"] + for entry in _static_getmro(klass): + try: + class_dict = dict_attr.__get__(entry)["__dict__"] + except KeyError: + pass + else: + if not (type(class_dict) is types.GetSetDescriptorType and + class_dict.__name__ == "__dict__" and + class_dict.__objclass__ is entry): + return True + return False def getattr_static(obj, attr, default=_sentinel): """Retrieve attributes without triggering dynamic lookup via the @@ -1101,8 +1115,9 @@ """ instance_result = _sentinel if not _is_type(obj): - instance_result = _check_instance(obj, attr) klass = type(obj) + if not _shadowed_dict(klass): + instance_result = _check_instance(obj, attr) else: klass = obj diff -r 9dcd88977694 -r 650549138a3d Lib/lib2to3/fixes/fix_metaclass.py --- a/Lib/lib2to3/fixes/fix_metaclass.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/lib2to3/fixes/fix_metaclass.py Tue Mar 22 21:32:51 2011 +1000 @@ -48,7 +48,7 @@ """ for node in cls_node.children: if node.type == syms.suite: - # already in the prefered format, do nothing + # already in the preferred format, do nothing return # !%@#! oneliners have no suite node, we have to fake one up diff -r 9dcd88977694 -r 650549138a3d Lib/lib2to3/pgen2/conv.py --- a/Lib/lib2to3/pgen2/conv.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/lib2to3/pgen2/conv.py Tue Mar 22 21:32:51 2011 +1000 @@ -51,7 +51,7 @@ self.finish_off() def parse_graminit_h(self, filename): - """Parse the .h file writen by pgen. (Internal) + """Parse the .h file written by pgen. (Internal) This file is a sequence of #define statements defining the nonterminals of the grammar as numbers. We build two tables @@ -82,7 +82,7 @@ return True def parse_graminit_c(self, filename): - """Parse the .c file writen by pgen. (Internal) + """Parse the .c file written by pgen. (Internal) The file looks as follows. The first two lines are always this: diff -r 9dcd88977694 -r 650549138a3d Lib/lib2to3/pytree.py --- a/Lib/lib2to3/pytree.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/lib2to3/pytree.py Tue Mar 22 21:32:51 2011 +1000 @@ -658,8 +658,8 @@ content: optional sequence of subsequences of patterns; if absent, matches one node; if present, each subsequence is an alternative [*] - min: optinal minumum number of times to match, default 0 - max: optional maximum number of times tro match, default HUGE + min: optional minimum number of times to match, default 0 + max: optional maximum number of times to match, default HUGE name: optional name assigned to this match [*] Thus, if content is [[a, b, c], [d, e], [f, g, h]] this is diff -r 9dcd88977694 -r 650549138a3d Lib/lib2to3/tests/data/py2_test_grammar.py --- a/Lib/lib2to3/tests/data/py2_test_grammar.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/lib2to3/tests/data/py2_test_grammar.py Tue Mar 22 21:32:51 2011 +1000 @@ -316,7 +316,7 @@ ### simple_stmt: small_stmt (';' small_stmt)* [';'] x = 1; pass; del x def foo(): - # verify statments that end with semi-colons + # verify statements that end with semi-colons x = 1; pass; del x; foo() diff -r 9dcd88977694 -r 650549138a3d Lib/lib2to3/tests/data/py3_test_grammar.py --- a/Lib/lib2to3/tests/data/py3_test_grammar.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/lib2to3/tests/data/py3_test_grammar.py Tue Mar 22 21:32:51 2011 +1000 @@ -356,7 +356,7 @@ ### simple_stmt: small_stmt (';' small_stmt)* [';'] x = 1; pass; del x def foo(): - # verify statments that end with semi-colons + # verify statements that end with semi-colons x = 1; pass; del x; foo() diff -r 9dcd88977694 -r 650549138a3d Lib/multiprocessing/__init__.py --- a/Lib/multiprocessing/__init__.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/multiprocessing/__init__.py Tue Mar 22 21:32:51 2011 +1000 @@ -115,8 +115,11 @@ except (ValueError, KeyError): num = 0 elif 'bsd' in sys.platform or sys.platform == 'darwin': + comm = '/sbin/sysctl -n hw.ncpu' + if sys.platform == 'darwin': + comm = '/usr' + comm try: - with os.popen('sysctl -n hw.ncpu') as p: + with os.popen(comm) as p: num = int(p.read()) except ValueError: num = 0 diff -r 9dcd88977694 -r 650549138a3d Lib/ntpath.py --- a/Lib/ntpath.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/ntpath.py Tue Mar 22 21:32:51 2011 +1000 @@ -405,7 +405,7 @@ # - $varname is accepted. # - %varname% is accepted. # - varnames can be made out of letters, digits and the characters '_-' -# (though is not verifed in the ${varname} and %varname% cases) +# (though is not verified in the ${varname} and %varname% cases) # XXX With COMMAND.COM you can use any characters in a variable name, # XXX except '^|<>='. diff -r 9dcd88977694 -r 650549138a3d Lib/optparse.py --- a/Lib/optparse.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/optparse.py Tue Mar 22 21:32:51 2011 +1000 @@ -86,10 +86,16 @@ # Id: errors.py 509 2006-04-20 00:58:24Z gward try: - from gettext import gettext + from gettext import gettext, ngettext except ImportError: def gettext(message): return message + + def ngettext(singular, plural, n): + if n == 1: + return singular + return plural + _ = gettext @@ -1478,11 +1484,10 @@ if option.takes_value(): nargs = option.nargs if len(rargs) < nargs: - if nargs == 1: - self.error(_("%s option requires an argument") % opt) - else: - self.error(_("%s option requires %d arguments") - % (opt, nargs)) + self.error(ngettext( + "%(option)s option requires %(number)d argument", + "%(option)s option requires %(number)d arguments", + nargs) % {"option": opt, "number": nargs}) elif nargs == 1: value = rargs.pop(0) else: @@ -1517,11 +1522,10 @@ nargs = option.nargs if len(rargs) < nargs: - if nargs == 1: - self.error(_("%s option requires an argument") % opt) - else: - self.error(_("%s option requires %d arguments") - % (opt, nargs)) + self.error(ngettext( + "%(option)s option requires %(number)d argument", + "%(option)s option requires %(number)d arguments", + nargs) % {"option": opt, "number": nargs}) elif nargs == 1: value = rargs.pop(0) else: diff -r 9dcd88977694 -r 650549138a3d Lib/os.py --- a/Lib/os.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/os.py Tue Mar 22 21:32:51 2011 +1000 @@ -779,11 +779,13 @@ return bs # Supply os.popen() -def popen(cmd, mode="r", buffering=None): +def popen(cmd, mode="r", buffering=-1): if not isinstance(cmd, str): raise TypeError("invalid cmd type (%s, expected string)" % type(cmd)) if mode not in ("r", "w"): raise ValueError("invalid mode %r" % mode) + if buffering == 0 or buffering == None: + raise ValueError("popen() does not support unbuffered streams") import subprocess, io if mode == "r": proc = subprocess.Popen(cmd, diff -r 9dcd88977694 -r 650549138a3d Lib/pickletools.py --- a/Lib/pickletools.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/pickletools.py Tue Mar 22 21:32:51 2011 +1000 @@ -1406,7 +1406,7 @@ proto=0, doc="""Read an object from the memo and push it on the stack. - The index of the memo object to push is given by the newline-teriminated + The index of the memo object to push is given by the newline-terminated decimal string following. BINGET and LONG_BINGET are space-optimized versions. """), diff -r 9dcd88977694 -r 650549138a3d Lib/platform.py --- a/Lib/platform.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/platform.py Tue Mar 22 21:32:51 2011 +1000 @@ -357,7 +357,7 @@ supported_dists=supported_dists, full_distribution_name=0) -def popen(cmd, mode='r', bufsize=None): +def popen(cmd, mode='r', bufsize=-1): """ Portable popen() interface. """ @@ -417,7 +417,7 @@ info = pipe.read() if pipe.close(): raise os.error('command failed') - # XXX How can I supress shell errors from being written + # XXX How can I suppress shell errors from being written # to stderr ? except os.error as why: #print 'Command %s failed: %s' % (cmd,why) diff -r 9dcd88977694 -r 650549138a3d Lib/pydoc.py --- a/Lib/pydoc.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/pydoc.py Tue Mar 22 21:32:51 2011 +1000 @@ -168,11 +168,11 @@ def visiblename(name, all=None): """Decide whether to show documentation on a variable.""" # Certain special names are redundant. - _hidden_names = ('__builtins__', '__doc__', '__file__', '__path__', + if name in {'__builtins__', '__doc__', '__file__', '__path__', '__module__', '__name__', '__slots__', '__package__', '__cached__', '__author__', '__credits__', '__date__', - '__version__') - if name in _hidden_names: return 0 + '__version__'}: + return 0 # Private names are hidden, but special names are displayed. if name.startswith('__') and name.endswith('__'): return 1 if all is not None: diff -r 9dcd88977694 -r 650549138a3d Lib/shutil.py --- a/Lib/shutil.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/shutil.py Tue Mar 22 21:32:51 2011 +1000 @@ -737,8 +737,8 @@ except KeyError: raise ValueError("Unknown unpack format '{0}'".format(format)) - func = format_info[0] - func(filename, extract_dir, **dict(format_info[1])) + func = format_info[1] + func(filename, extract_dir, **dict(format_info[2])) else: # we need to look at the registered unpackers supported extensions format = _find_unpack_format(filename) diff -r 9dcd88977694 -r 650549138a3d Lib/smtplib.py --- a/Lib/smtplib.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/smtplib.py Tue Mar 22 21:32:51 2011 +1000 @@ -269,6 +269,19 @@ pass self.local_hostname = '[%s]' % addr + def __enter__(self): + return self + + def __exit__(self, *args): + try: + code, message = self.docmd("QUIT") + if code != 221: + raise SMTPResponseException(code, message) + except SMTPServerDisconnected: + pass + finally: + self.close() + def set_debuglevel(self, debuglevel): """Set the debug output level. diff -r 9dcd88977694 -r 650549138a3d Lib/socket.py --- a/Lib/socket.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/socket.py Tue Mar 22 21:32:51 2011 +1000 @@ -112,6 +112,9 @@ s[7:]) return s + def __getstate__(self): + raise TypeError("Cannot serialize socket object") + def dup(self): """dup() -> socket object diff -r 9dcd88977694 -r 650549138a3d Lib/subprocess.py --- a/Lib/subprocess.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/subprocess.py Tue Mar 22 21:32:51 2011 +1000 @@ -371,8 +371,9 @@ """This exception is raised when the timeout expires while waiting for a child process. """ - def __init__(self, cmd, output=None): + def __init__(self, cmd, timeout, output=None): self.cmd = cmd + self.timeout = timeout self.output = output def __str__(self): @@ -431,7 +432,7 @@ return fds __all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "getstatusoutput", - "getoutput", "check_output", "CalledProcessError"] + "getoutput", "check_output", "CalledProcessError", "DEVNULL"] if mswindows: from _subprocess import CREATE_NEW_CONSOLE, CREATE_NEW_PROCESS_GROUP @@ -456,6 +457,7 @@ PIPE = -1 STDOUT = -2 +DEVNULL = -3 def _eintr_retry_call(func, *args): @@ -532,7 +534,7 @@ except TimeoutExpired: process.kill() output, unused_err = process.communicate() - raise TimeoutExpired(process.args, output=output) + raise TimeoutExpired(process.args, timeout, output=output) retcode = process.poll() if retcode: raise CalledProcessError(retcode, process.args, output=output) @@ -741,8 +743,6 @@ if errread != -1: errread = msvcrt.open_osfhandle(errread.Detach(), 0) - if bufsize == 0: - bufsize = 1 # Nearly unbuffered (XXX for now) if p2cwrite != -1: self.stdin = io.open(p2cwrite, 'wb', bufsize) if self.universal_newlines: @@ -800,6 +800,10 @@ # Child is still running, keep us alive until we can wait on it. _active.append(self) + def _get_devnull(self): + if not hasattr(self, '_devnull'): + self._devnull = os.open(os.devnull, os.O_RDWR) + return self._devnull def communicate(self, input=None, timeout=None): """Interact with process: Send data to stdin. Read data from @@ -839,7 +843,7 @@ return (stdout, stderr) try: - stdout, stderr = self._communicate(input, endtime) + stdout, stderr = self._communicate(input, endtime, timeout) finally: self._communication_started = True @@ -860,12 +864,12 @@ return endtime - time.time() - def _check_timeout(self, endtime): + def _check_timeout(self, endtime, orig_timeout): """Convenience for checking if a timeout has expired.""" if endtime is None: return if time.time() > endtime: - raise TimeoutExpired(self.args) + raise TimeoutExpired(self.args, orig_timeout) if mswindows: @@ -889,6 +893,8 @@ p2cread, _ = _subprocess.CreatePipe(None, 0) elif stdin == PIPE: p2cread, p2cwrite = _subprocess.CreatePipe(None, 0) + elif stdin == DEVNULL: + p2cread = msvcrt.get_osfhandle(self._get_devnull()) elif isinstance(stdin, int): p2cread = msvcrt.get_osfhandle(stdin) else: @@ -902,6 +908,8 @@ _, c2pwrite = _subprocess.CreatePipe(None, 0) elif stdout == PIPE: c2pread, c2pwrite = _subprocess.CreatePipe(None, 0) + elif stdout == DEVNULL: + c2pwrite = msvcrt.get_osfhandle(self._get_devnull()) elif isinstance(stdout, int): c2pwrite = msvcrt.get_osfhandle(stdout) else: @@ -917,6 +925,8 @@ errread, errwrite = _subprocess.CreatePipe(None, 0) elif stderr == STDOUT: errwrite = c2pwrite + elif stderr == DEVNULL: + errwrite = msvcrt.get_osfhandle(self._get_devnull()) elif isinstance(stderr, int): errwrite = msvcrt.get_osfhandle(stderr) else: @@ -1010,7 +1020,7 @@ except pywintypes.error as e: # Translate pywintypes.error to WindowsError, which is # a subclass of OSError. FIXME: We should really - # translate errno using _sys_errlist (or simliar), but + # translate errno using _sys_errlist (or similar), but # how can this be done from Python? raise WindowsError(*e.args) finally: @@ -1026,6 +1036,8 @@ c2pwrite.Close() if errwrite != -1: errwrite.Close() + if hasattr(self, '_devnull'): + os.close(self._devnull) # Retain the process handle, but close the thread handle self._child_created = True @@ -1050,17 +1062,20 @@ return self.returncode - def wait(self, timeout=None): + def wait(self, timeout=None, endtime=None): """Wait for child process to terminate. Returns returncode attribute.""" + if endtime is not None: + timeout = self._remaining_time(endtime) if timeout is None: - timeout = _subprocess.INFINITE + timeout_millis = _subprocess.INFINITE else: - timeout = int(timeout * 1000) + timeout_millis = int(timeout * 1000) if self.returncode is None: - result = _subprocess.WaitForSingleObject(self._handle, timeout) + result = _subprocess.WaitForSingleObject(self._handle, + timeout_millis) if result == _subprocess.WAIT_TIMEOUT: - raise TimeoutExpired(self.args) + raise TimeoutExpired(self.args, timeout) self.returncode = _subprocess.GetExitCodeProcess(self._handle) return self.returncode @@ -1070,7 +1085,7 @@ fh.close() - def _communicate(self, input, endtime): + def _communicate(self, input, endtime, orig_timeout): # Start reader threads feeding into a list hanging off of this # object, unless they've already been started. if self.stdout and not hasattr(self, "_stdout_buff"): @@ -1099,11 +1114,11 @@ if self.stdout is not None: self.stdout_thread.join(self._remaining_time(endtime)) if self.stdout_thread.isAlive(): - raise TimeoutExpired(self.args) + raise TimeoutExpired(self.args, orig_timeout) if self.stderr is not None: self.stderr_thread.join(self._remaining_time(endtime)) if self.stderr_thread.isAlive(): - raise TimeoutExpired(self.args) + raise TimeoutExpired(self.args, orig_timeout) # Collect the output from and close both pipes, now that we know # both have been read successfully. @@ -1159,6 +1174,8 @@ pass elif stdin == PIPE: p2cread, p2cwrite = _create_pipe() + elif stdin == DEVNULL: + p2cread = self._get_devnull() elif isinstance(stdin, int): p2cread = stdin else: @@ -1169,6 +1186,8 @@ pass elif stdout == PIPE: c2pread, c2pwrite = _create_pipe() + elif stdout == DEVNULL: + c2pwrite = self._get_devnull() elif isinstance(stdout, int): c2pwrite = stdout else: @@ -1181,6 +1200,8 @@ errread, errwrite = _create_pipe() elif stderr == STDOUT: errwrite = c2pwrite + elif stderr == DEVNULL: + errwrite = self._get_devnull() elif isinstance(stderr, int): errwrite = stderr else: @@ -1374,6 +1395,8 @@ os.close(c2pwrite) if errwrite != -1 and errread != -1: os.close(errwrite) + if hasattr(self, '_devnull'): + os.close(self._devnull) # Wait for exec to fail or succeed; possibly raising an # exception (limited in size) @@ -1468,13 +1491,18 @@ def wait(self, timeout=None, endtime=None): """Wait for child process to terminate. Returns returncode attribute.""" - # If timeout was passed but not endtime, compute endtime in terms of - # timeout. - if endtime is None and timeout is not None: - endtime = time.time() + timeout if self.returncode is not None: return self.returncode - elif endtime is not None: + + # endtime is preferred to timeout. timeout is only used for + # printing. + if endtime is not None or timeout is not None: + if endtime is None: + endtime = time.time() + timeout + elif timeout is None: + timeout = self._remaining_time(endtime) + + if endtime is not None: # Enter a busy loop if we have a timeout. This busy loop was # cribbed from Lib/threading.py in Thread.wait() at r71065. delay = 0.0005 # 500 us -> initial delay of 1 ms @@ -1486,7 +1514,7 @@ break remaining = self._remaining_time(endtime) if remaining <= 0: - raise TimeoutExpired(self.args) + raise TimeoutExpired(self.args, timeout) delay = min(delay * 2, remaining, .05) time.sleep(delay) elif self.returncode is None: @@ -1495,7 +1523,7 @@ return self.returncode - def _communicate(self, input, endtime): + def _communicate(self, input, endtime, orig_timeout): if self.stdin and not self._communication_started: # Flush stdio buffer. This might block, if the user has # been writing to .stdin in an uncontrolled fashion. @@ -1504,9 +1532,11 @@ self.stdin.close() if _has_poll: - stdout, stderr = self._communicate_with_poll(input, endtime) + stdout, stderr = self._communicate_with_poll(input, endtime, + orig_timeout) else: - stdout, stderr = self._communicate_with_select(input, endtime) + stdout, stderr = self._communicate_with_select(input, endtime, + orig_timeout) self.wait(timeout=self._remaining_time(endtime)) @@ -1529,7 +1559,7 @@ return (stdout, stderr) - def _communicate_with_poll(self, input, endtime): + def _communicate_with_poll(self, input, endtime, orig_timeout): stdout = None # Return stderr = None # Return @@ -1580,7 +1610,7 @@ if e.args[0] == errno.EINTR: continue raise - self._check_timeout(endtime) + self._check_timeout(endtime, orig_timeout) # XXX Rewrite these to use non-blocking I/O on the # file objects; they are no longer using C stdio! @@ -1604,7 +1634,7 @@ return (stdout, stderr) - def _communicate_with_select(self, input, endtime): + def _communicate_with_select(self, input, endtime, orig_timeout): if not self._communication_started: self._read_set = [] self._write_set = [] @@ -1646,9 +1676,9 @@ # According to the docs, returning three empty lists indicates # that the timeout expired. if not (rlist or wlist or xlist): - raise TimeoutExpired(self.args) + raise TimeoutExpired(self.args, orig_timeout) # We also check what time it is ourselves for good measure. - self._check_timeout(endtime) + self._check_timeout(endtime, orig_timeout) # XXX Rewrite these to use non-blocking I/O on the # file objects; they are no longer using C stdio! diff -r 9dcd88977694 -r 650549138a3d Lib/test/crashers/README --- a/Lib/test/crashers/README Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/crashers/README Tue Mar 22 21:32:51 2011 +1000 @@ -14,3 +14,7 @@ Once the crash is fixed, the test case should be moved into an appropriate test (even if it was originally from the test suite). This ensures the regression doesn't happen again. And if it does, it should be easier to track down. + +Also see Lib/test_crashers.py which exercises the crashers in this directory. +In particular, make sure to add any new infinite loop crashers to the black +list so it doesn't try to run them. diff -r 9dcd88977694 -r 650549138a3d Lib/test/crashers/compiler_recursion.py --- a/Lib/test/crashers/compiler_recursion.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/crashers/compiler_recursion.py Tue Mar 22 21:32:51 2011 +1000 @@ -9,5 +9,5 @@ # e.g. '1*'*10**5+'1' will die in compiler_visit_expr # The exact limit to destroy the stack will vary by platform -# but 100k should do the trick most places -compile('()'*10**5, '?', 'exec') +# but 10M should do the trick even with huge stack allocations +compile('()'*10**7, '?', 'exec') diff -r 9dcd88977694 -r 650549138a3d Lib/test/datetimetester.py --- a/Lib/test/datetimetester.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/datetimetester.py Tue Mar 22 21:32:51 2011 +1000 @@ -3414,7 +3414,7 @@ self.assertEqual(dt, there_and_back) # Because we have a redundant spelling when DST begins, there is - # (unforunately) an hour when DST ends that can't be spelled at all in + # (unfortunately) an hour when DST ends that can't be spelled at all in # local time. When DST ends, the clock jumps from 1:59 back to 1:00 # again. The hour 1:MM DST has no spelling then: 1:MM is taken to be # standard time. 1:MM DST == 0:MM EST, but 0:MM is taken to be diff -r 9dcd88977694 -r 650549138a3d Lib/test/pyclbr_input.py --- a/Lib/test/pyclbr_input.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/pyclbr_input.py Tue Mar 22 21:32:51 2011 +1000 @@ -19,7 +19,7 @@ # XXX: This causes test_pyclbr.py to fail, but only because the # introspection-based is_method() code in the test can't - # distinguish between this and a geniune method function like m(). + # distinguish between this and a genuine method function like m(). # The pyclbr.py module gets this right as it parses the text. # #f = f diff -r 9dcd88977694 -r 650549138a3d Lib/test/regrtest.py --- a/Lib/test/regrtest.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/regrtest.py Tue Mar 22 21:32:51 2011 +1000 @@ -732,9 +732,9 @@ tests = [] others = set(stdtests) | nottests for name in names: - modname, ext = os.path.splitext(name) - if modname[:5] == "test_" and ext == ".py" and modname not in others: - tests.append(modname) + mod, ext = os.path.splitext(name) + if mod[:5] == "test_" and ext in (".py", "") and mod not in others: + tests.append(mod) return stdtests + sorted(tests) def replace_stdout(): @@ -827,7 +827,8 @@ resources = ('sys.argv', 'cwd', 'sys.stdin', 'sys.stdout', 'sys.stderr', 'os.environ', 'sys.path', 'sys.path_hooks', '__import__', 'warnings.filters', 'asyncore.socket_map', - 'logging._handlers', 'logging._handlerList', 'sys.gettrace') + 'logging._handlers', 'logging._handlerList', 'sys.gettrace', + 'sys.warnoptions') def get_sys_argv(self): return id(sys.argv), sys.argv, sys.argv[:] @@ -914,6 +915,12 @@ # Can't easily revert the logging state pass + def get_sys_warnoptions(self): + return id(sys.warnoptions), sys.warnoptions, sys.warnoptions[:] + def restore_sys_warnoptions(self, saved_options): + sys.warnoptions = saved_options[1] + sys.warnoptions[:] = saved_options[2] + def resource_info(self): for name in self.resources: method_suffix = name.replace('.', '_') @@ -993,12 +1000,9 @@ sys.stderr.flush() return FAILED, test_time except: - type, value = sys.exc_info()[:2] - print("test", test, "crashed --", str(type) + ":", value, file=sys.stderr) + msg = traceback.format_exc() + print("test", test, "crashed --", msg, file=sys.stderr) sys.stderr.flush() - if verbose or debug: - traceback.print_exc(file=sys.stderr) - sys.stderr.flush() return FAILED, test_time else: if refleak: diff -r 9dcd88977694 -r 650549138a3d Lib/test/support.py --- a/Lib/test/support.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/support.py Tue Mar 22 21:32:51 2011 +1000 @@ -1374,7 +1374,7 @@ def args_from_interpreter_flags(): """Return a list of command-line arguments reproducing the current - settings in sys.flags.""" + settings in sys.flags and sys.warnoptions.""" flag_opt_map = { 'bytes_warning': 'b', 'dont_write_bytecode': 'B', @@ -1389,6 +1389,9 @@ v = getattr(sys.flags, flag) if v > 0: args.append('-' + opt * v) + if sys.warnoptions: + args.append('-W') + args.extend(sys.warnoptions) return args #============================================================ @@ -1462,9 +1465,11 @@ global _can_symlink if _can_symlink is not None: return _can_symlink + symlink_path = TESTFN + "can_symlink" try: - os.symlink(TESTFN, TESTFN + "can_symlink") + os.symlink(TESTFN, symlink_path) can = True + os.remove(symlink_path) except (OSError, NotImplementedError): can = False _can_symlink = can @@ -1475,3 +1480,36 @@ ok = can_symlink() msg = "Requires functional symlink implementation" return test if ok else unittest.skip(msg)(test) + +def patch(test_instance, object_to_patch, attr_name, new_value): + """Override 'object_to_patch'.'attr_name' with 'new_value'. + + Also, add a cleanup procedure to 'test_instance' to restore + 'object_to_patch' value for 'attr_name'. + The 'attr_name' should be a valid attribute for 'object_to_patch'. + + """ + # check that 'attr_name' is a real attribute for 'object_to_patch' + # will raise AttributeError if it does not exist + getattr(object_to_patch, attr_name) + + # keep a copy of the old value + attr_is_local = False + try: + old_value = object_to_patch.__dict__[attr_name] + except (AttributeError, KeyError): + old_value = getattr(object_to_patch, attr_name, None) + else: + attr_is_local = True + + # restore the value when the test is done + def cleanup(): + if attr_is_local: + setattr(object_to_patch, attr_name, old_value) + else: + delattr(object_to_patch, attr_name) + + test_instance.addCleanup(cleanup) + + # actually override the attribute + setattr(object_to_patch, attr_name, new_value) diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_binhex.py --- a/Lib/test/test_binhex.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_binhex.py Tue Mar 22 21:32:51 2011 +1000 @@ -15,10 +15,12 @@ def setUp(self): self.fname1 = support.TESTFN + "1" self.fname2 = support.TESTFN + "2" + self.fname3 = support.TESTFN + "very_long_filename__very_long_filename__very_long_filename__very_long_filename__" def tearDown(self): support.unlink(self.fname1) support.unlink(self.fname2) + support.unlink(self.fname3) DATA = b'Jack is my hero' @@ -37,6 +39,15 @@ self.assertEqual(self.DATA, finish) + def test_binhex_error_on_long_filename(self): + """ + The testcase fails if no exception is raised when a filename parameter provided to binhex.binhex() + is too long, or if the exception raised in binhex.binhex() is not an instance of binhex.Error. + """ + f3 = open(self.fname3, 'wb') + f3.close() + + self.assertRaises(binhex.Error, binhex.binhex, self.fname3, self.fname2) def test_main(): support.run_unittest(BinHexTestCase) diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_capi.py --- a/Lib/test/test_capi.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_capi.py Tue Mar 22 21:32:51 2011 +1000 @@ -127,7 +127,7 @@ context.event.set() def test_pendingcalls_non_threaded(self): - #again, just using the main thread, likely they will all be dispathced at + #again, just using the main thread, likely they will all be dispatched at #once. It is ok to ask for too many, because we loop until we find a slot. #the loop can be interrupted to dispatch. #there are only 32 dispatch slots, so we go for twice that! diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_crashers.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_crashers.py Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,38 @@ +# Tests that the crashers in the Lib/test/crashers directory actually +# do crash the interpreter as expected +# +# If a crasher is fixed, it should be moved elsewhere in the test suite to +# ensure it continues to work correctly. + +import unittest +import glob +import os.path +import test.support +from test.script_helper import assert_python_failure + +CRASHER_DIR = os.path.join(os.path.dirname(__file__), "crashers") +CRASHER_FILES = os.path.join(CRASHER_DIR, "*.py") + +infinite_loops = ["infinite_loop_re.py", "nasty_eq_vs_dict.py"] + +class CrasherTest(unittest.TestCase): + + @unittest.skip("these tests are too fragile") + @test.support.cpython_only + def test_crashers_crash(self): + for fname in glob.glob(CRASHER_FILES): + if os.path.basename(fname) in infinite_loops: + continue + # Some "crashers" only trigger an exception rather than a + # segfault. Consider that an acceptable outcome. + if test.support.verbose: + print("Checking crasher:", fname) + assert_python_failure(fname) + + +def test_main(): + test.support.run_unittest(CrasherTest) + test.support.reap_children() + +if __name__ == "__main__": + test_main() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_decimal.py --- a/Lib/test/test_decimal.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_decimal.py Tue Mar 22 21:32:51 2011 +1000 @@ -228,7 +228,7 @@ try: t = self.eval_line(line) except DecimalException as exception: - #Exception raised where there shoudn't have been one. + #Exception raised where there shouldn't have been one. self.fail('Exception "'+exception.__class__.__name__ + '" raised on line '+line) return diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_descr.py --- a/Lib/test/test_descr.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_descr.py Tue Mar 22 21:32:51 2011 +1000 @@ -3967,7 +3967,7 @@ except TypeError: pass else: - self.fail("Carlo Verre __setattr__ suceeded!") + self.fail("Carlo Verre __setattr__ succeeded!") try: object.__delattr__(str, "lower") except TypeError: diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_dis.py --- a/Lib/test/test_dis.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_dis.py Tue Mar 22 21:32:51 2011 +1000 @@ -1,11 +1,35 @@ # Minimal tests for dis module from test.support import run_unittest, captured_stdout +import difflib import unittest import sys import dis import io +class _C: + def __init__(self, x): + self.x = x == 1 + +dis_c_instance_method = """\ + %-4d 0 LOAD_FAST 1 (x) + 3 LOAD_CONST 1 (1) + 6 COMPARE_OP 2 (==) + 9 LOAD_FAST 0 (self) + 12 STORE_ATTR 0 (x) + 15 LOAD_CONST 0 (None) + 18 RETURN_VALUE +""" % (_C.__init__.__code__.co_firstlineno + 1,) + +dis_c_instance_method_bytes = """\ + 0 LOAD_FAST 1 (1) + 3 LOAD_CONST 1 (1) + 6 COMPARE_OP 2 (==) + 9 LOAD_FAST 0 (0) + 12 STORE_ATTR 0 (0) + 15 LOAD_CONST 0 (0) + 18 RETURN_VALUE +""" def _f(a): print(a) @@ -23,6 +47,16 @@ _f.__code__.co_firstlineno + 2) +dis_f_co_code = """\ + 0 LOAD_GLOBAL 0 (0) + 3 LOAD_FAST 0 (0) + 6 CALL_FUNCTION 1 + 9 POP_TOP + 10 LOAD_CONST 1 (1) + 13 RETURN_VALUE +""" + + def bug708901(): for res in range(1, 10): @@ -138,18 +172,27 @@ """ class DisTests(unittest.TestCase): - def do_disassembly_test(self, func, expected): + + def get_disassembly(self, func, lasti=-1, wrapper=True): s = io.StringIO() save_stdout = sys.stdout sys.stdout = s - dis.dis(func) - sys.stdout = save_stdout - got = s.getvalue() + try: + if wrapper: + dis.dis(func) + else: + dis.disassemble(func, lasti) + finally: + sys.stdout = save_stdout # Trim trailing blanks (if any). - lines = got.split('\n') - lines = [line.rstrip() for line in lines] - expected = expected.split("\n") - import difflib + return [line.rstrip() for line in s.getvalue().splitlines()] + + def get_disassemble_as_string(self, func, lasti=-1): + return '\n'.join(self.get_disassembly(func, lasti, False)) + + def do_disassembly_test(self, func, expected): + lines = self.get_disassembly(func) + expected = expected.splitlines() if expected != lines: self.fail( "events did not match expectation:\n" + @@ -211,6 +254,44 @@ self.do_disassembly_test(simple_stmt_str, dis_simple_stmt_str) self.do_disassembly_test(compound_stmt_str, dis_compound_stmt_str) + def test_disassemble_bytes(self): + self.do_disassembly_test(_f.__code__.co_code, dis_f_co_code) + + def test_disassemble_method(self): + self.do_disassembly_test(_C(1).__init__, dis_c_instance_method) + + def test_disassemble_method_bytes(self): + method_bytecode = _C(1).__init__.__code__.co_code + self.do_disassembly_test(method_bytecode, dis_c_instance_method_bytes) + + def test_dis_none(self): + try: + del sys.last_traceback + except AttributeError: + pass + self.assertRaises(RuntimeError, dis.dis, None) + + def test_dis_object(self): + self.assertRaises(TypeError, dis.dis, object()) + + def test_dis_traceback(self): + try: + del sys.last_traceback + except AttributeError: + pass + + try: + 1/0 + except Exception as e: + tb = e.__traceback__ + sys.last_traceback = tb + + tb_dis = self.get_disassemble_as_string(tb.tb_frame.f_code, tb.tb_lasti) + self.do_disassembly_test(None, tb_dis) + + def test_dis_object(self): + self.assertRaises(TypeError, dis.dis, object()) + code_info_code_info = """\ Name: code_info Filename: (.*) @@ -363,6 +444,13 @@ dis.show_code(x) self.assertRegex(output.getvalue(), expected+"\n") + def test_code_info_object(self): + self.assertRaises(TypeError, dis.code_info, object()) + + def test_pretty_flags_no_flags(self): + self.assertEqual(dis.pretty_flags(0), '0x0') + + def test_main(): run_unittest(DisTests, CodeInfoTests) diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_doctest.py --- a/Lib/test/test_doctest.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_doctest.py Tue Mar 22 21:32:51 2011 +1000 @@ -1297,7 +1297,7 @@ ? + ++ ^ TestResults(failed=1, attempted=1) -The REPORT_ONLY_FIRST_FAILURE supresses result output after the first +The REPORT_ONLY_FIRST_FAILURE suppresses result output after the first failing example: >>> def f(x): @@ -1327,7 +1327,7 @@ 2 TestResults(failed=3, attempted=5) -However, output from `report_start` is not supressed: +However, output from `report_start` is not suppressed: >>> doctest.DocTestRunner(verbose=True, optionflags=flags).run(test) ... # doctest: +ELLIPSIS @@ -2278,7 +2278,7 @@ >>> doctest.master = None # Reset master. (Note: we'll be clearing doctest.master after each call to -`doctest.testfile`, to supress warnings about multiple tests with the +`doctest.testfile`, to suppress warnings about multiple tests with the same name.) Globals may be specified with the `globs` and `extraglobs` parameters: @@ -2314,7 +2314,7 @@ TestResults(failed=0, attempted=2) >>> doctest.master = None # Reset master. -Verbosity can be increased with the optional `verbose` paremter: +Verbosity can be increased with the optional `verbose` parameter: >>> doctest.testfile('test_doctest.txt', globs=globs, verbose=True) Trying: @@ -2351,7 +2351,7 @@ TestResults(failed=1, attempted=2) >>> doctest.master = None # Reset master. -The summary report may be supressed with the optional `report` +The summary report may be suppressed with the optional `report` parameter: >>> doctest.testfile('test_doctest.txt', report=False) diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email.py --- a/Lib/test/test_email.py Tue Mar 22 21:31:46 2011 +1000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# email package unit tests - -# The specific tests now live in Lib/email/test -from email.test.test_email import suite -from test import support - -def test_main(): - support.run_unittest(suite()) - -if __name__ == '__main__': - test_main() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/__init__.py Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,13 @@ +import os +import sys +import unittest +import test.support + +# used by regrtest and __main__. +def test_main(): + here = os.path.dirname(__file__) + # Unittest mucks with the path, so we have to save and restore + # it to keep regrtest happy. + savepath = sys.path[:] + test.support._run_suite(unittest.defaultTestLoader.discover(here)) + sys.path[:] = savepath diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/PyBanner048.gif Binary file Lib/test/test_email/data/PyBanner048.gif has changed diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/audiotest.au Binary file Lib/test/test_email/data/audiotest.au has changed diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_01.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_01.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,19 @@ +Return-Path: +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) + id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 + + +Hi, + +Do you like this message? + +-Me diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_02.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_02.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,135 @@ +MIME-version: 1.0 +From: ppp-request@zzz.org +Sender: ppp-admin@zzz.org +To: ppp@zzz.org +Subject: Ppp digest, Vol 1 #2 - 5 msgs +Date: Fri, 20 Apr 2001 20:18:00 -0400 (EDT) +X-Mailer: Mailman v2.0.4 +X-Mailman-Version: 2.0.4 +Content-Type: multipart/mixed; boundary="192.168.1.2.889.32614.987812255.500.21814" + +--192.168.1.2.889.32614.987812255.500.21814 +Content-type: text/plain; charset=us-ascii +Content-description: Masthead (Ppp digest, Vol 1 #2) + +Send Ppp mailing list submissions to + ppp@zzz.org + +To subscribe or unsubscribe via the World Wide Web, visit + http://www.zzz.org/mailman/listinfo/ppp +or, via email, send a message with subject or body 'help' to + ppp-request@zzz.org + +You can reach the person managing the list at + ppp-admin@zzz.org + +When replying, please edit your Subject line so it is more specific +than "Re: Contents of Ppp digest..." + + +--192.168.1.2.889.32614.987812255.500.21814 +Content-type: text/plain; charset=us-ascii +Content-description: Today's Topics (5 msgs) + +Today's Topics: + + 1. testing #1 (Barry A. Warsaw) + 2. testing #2 (Barry A. Warsaw) + 3. testing #3 (Barry A. Warsaw) + 4. testing #4 (Barry A. Warsaw) + 5. testing #5 (Barry A. Warsaw) + +--192.168.1.2.889.32614.987812255.500.21814 +Content-Type: multipart/digest; boundary="__--__--" + +--__--__-- + +Message: 1 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +Date: Fri, 20 Apr 2001 20:16:13 -0400 +To: ppp@zzz.org +From: barry@digicool.com (Barry A. Warsaw) +Subject: [Ppp] testing #1 +Precedence: bulk + + +hello + + +--__--__-- + +Message: 2 +Date: Fri, 20 Apr 2001 20:16:21 -0400 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +To: ppp@zzz.org +From: barry@digicool.com (Barry A. Warsaw) +Precedence: bulk + + +hello + + +--__--__-- + +Message: 3 +Date: Fri, 20 Apr 2001 20:16:25 -0400 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +To: ppp@zzz.org +From: barry@digicool.com (Barry A. Warsaw) +Subject: [Ppp] testing #3 +Precedence: bulk + + +hello + + +--__--__-- + +Message: 4 +Date: Fri, 20 Apr 2001 20:16:28 -0400 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +To: ppp@zzz.org +From: barry@digicool.com (Barry A. Warsaw) +Subject: [Ppp] testing #4 +Precedence: bulk + + +hello + + +--__--__-- + +Message: 5 +Date: Fri, 20 Apr 2001 20:16:32 -0400 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +To: ppp@zzz.org +From: barry@digicool.com (Barry A. Warsaw) +Subject: [Ppp] testing #5 +Precedence: bulk + + +hello + + + + +--__--__---- +--192.168.1.2.889.32614.987812255.500.21814 +Content-type: text/plain; charset=us-ascii +Content-description: Digest Footer + +_______________________________________________ +Ppp mailing list +Ppp@zzz.org +http://www.zzz.org/mailman/listinfo/ppp + + +--192.168.1.2.889.32614.987812255.500.21814-- + +End of Ppp Digest + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_03.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_03.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,16 @@ +Return-Path: +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) + id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 + + +Hi, + +Do you like this message? + +-Me diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_04.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_04.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,37 @@ +Return-Path: +Delivered-To: barry@python.org +Received: by mail.python.org (Postfix, from userid 889) + id C2BF0D37C6; Tue, 11 Sep 2001 00:05:05 -0400 (EDT) +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="h90VIIIKmx" +Content-Transfer-Encoding: 7bit +Message-ID: <15261.36209.358846.118674@anthem.python.org> +From: barry@python.org (Barry A. Warsaw) +To: barry@python.org +Subject: a simple multipart +Date: Tue, 11 Sep 2001 00:05:05 -0400 +X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid +X-Attribution: BAW +X-Oblique-Strategy: Make a door into a window + + +--h90VIIIKmx +Content-Type: text/plain +Content-Disposition: inline; + filename="msg.txt" +Content-Transfer-Encoding: 7bit + +a simple kind of mirror +to reflect upon our own + +--h90VIIIKmx +Content-Type: text/plain +Content-Disposition: inline; + filename="msg.txt" +Content-Transfer-Encoding: 7bit + +a simple kind of mirror +to reflect upon our own + +--h90VIIIKmx-- + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_05.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_05.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,28 @@ +From: foo +Subject: bar +To: baz +MIME-Version: 1.0 +Content-Type: multipart/report; report-type=delivery-status; + boundary="D1690A7AC1.996856090/mail.example.com" +Message-Id: <20010803162810.0CA8AA7ACC@mail.example.com> + +This is a MIME-encapsulated message. + +--D1690A7AC1.996856090/mail.example.com +Content-Type: text/plain + +Yadda yadda yadda + +--D1690A7AC1.996856090/mail.example.com + +Yadda yadda yadda + +--D1690A7AC1.996856090/mail.example.com +Content-Type: message/rfc822 + +From: nobody@python.org + +Yadda yadda yadda + +--D1690A7AC1.996856090/mail.example.com-- + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_06.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_06.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,33 @@ +Return-Path: +Delivered-To: barry@python.org +MIME-Version: 1.0 +Content-Type: message/rfc822 +Content-Description: forwarded message +Content-Transfer-Encoding: 7bit +Message-ID: <15265.9482.641338.555352@python.org> +From: barry@zope.com (Barry A. Warsaw) +Sender: barry@python.org +To: barry@python.org +Subject: forwarded message from Barry A. Warsaw +Date: Thu, 13 Sep 2001 17:28:42 -0400 +X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid +X-Attribution: BAW +X-Oblique-Strategy: Be dirty +X-Url: http://barry.wooz.org + +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Return-Path: +Delivered-To: barry@python.org +Message-ID: <15265.9468.713530.98441@python.org> +From: barry@zope.com (Barry A. Warsaw) +Sender: barry@python.org +To: barry@python.org +Subject: testing +Date: Thu, 13 Sep 2001 17:28:28 -0400 +X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid +X-Attribution: BAW +X-Oblique-Strategy: Spectrum analysis +X-Url: http://barry.wooz.org + + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_07.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_07.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,83 @@ +MIME-Version: 1.0 +From: Barry +To: Dingus Lovers +Subject: Here is your dingus fish +Date: Fri, 20 Apr 2001 19:35:02 -0400 +Content-Type: multipart/mixed; boundary="BOUNDARY" + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" + +Hi there, + +This is the dingus fish. + +--BOUNDARY +Content-Type: image/gif; name="dingusfish.gif" +Content-Transfer-Encoding: base64 +content-disposition: attachment; filename="dingusfish.gif" + +R0lGODdhAAEAAfAAAP///wAAACwAAAAAAAEAAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq2 +7gvH8kzX9o3n+s73/g8MCofEovGITGICTKbyCV0FDNOo9SqpQqpOrJfXzTQj2vD3TGtqL+NtGQ2f +qTXmxzuOd7WXdcc9DyjU53ewFni4s0fGhdiYaEhGBelICTNoV1j5NUnFcrmUqemjNifJVWpaOqaI +oFq3SspZsSraE7sHq3jr1MZqWvi662vxV4tD+pvKW6aLDOCLyur8PDwbanyDeq0N3DctbQYeLDvR +RY6t95m6UB0d3mwIrV7e2VGNvjjffukeJp4w7F65KecGFsTHQGAygOrgrWs1jt28Rc88KESYcGLA +/obvTkH6p+CinWJiJmIMqXGQwH/y4qk0SYjgQTczT3ajKZGfuI0uJ4kkVI/DT5s3/ejkxI0aT4Y+ +YTYgWbImUaXk9nlLmnSh1qJiJFl0OpUqRK4oOy7NyRQtHWofhoYVxkwWXKUSn0YsS+fUV6lhqfYb +6ayd3Z5qQdG1B7bvQzaJjwUV2lixMUZ7JVsOlfjWVr/3NB/uFvnySBN6Dcb6rGwaRM3wsormw5cC +M9NxWy/bWdufudCvy8bOAjXjVVwta/uO21sE5RHBCzNFXtgq9ORtH4eYjVP4Yryo026nvkFmCeyA +B29efV6ravCMK5JwWd5897Qrx7ll38o6iHDZ/rXPR//feevhF4l7wjUGX3xq1eeRfM4RSJGBIV1D +z1gKPkfWag3mVBVvva1RlX5bAJTPR/2YqNtw/FkIYYEi/pIZiAdpcxpoHtmnYYoZtvhUftzdx5ZX +JSKDW405zkGcZzzGZ6KEv4FI224oDmijlEf+xp6MJK5ojY/ASeVUR+wsKRuJ+XFZ5o7ZeEime8t1 +ouUsU6YjF5ZtUihhkGfCdFQLWQFJ3UXxmElfhQnR+eCdcDbkFZp6vTRmj56ApCihn5QGpaToNZmR +n3NVSpZcQpZ2KEONusaiCsKAug0wkQbJSFO+PTSjneGxOuFjPlUk3ovWvdIerjUg9ZGIOtGq/qeX +eCYrrCX+1UPsgTKGGRSbzd5q156d/gpfbJxe66eD5iQKrXj7RGgruGxs62qebBHUKS32CKluCiqZ +qh+pmehmEb71noAUoe5e9Zm17S7773V10pjrtG4CmuurCV/n6zLK5turWNhqOvFXbjhZrMD0YhKe +wR0zOyuvsh6MWrGoIuzvyWu5y1WIFAqmJselypxXh6dKLNOKEB98L88bS2rkNqqlKzCNJp9c0G0j +Gzh0iRrCbHSXmPR643QS+4rWhgFmnSbSuXCjS0xAOWkU2UdLqyuUNfHSFdUouy3bm5i5GnDM3tG8 +doJ4r5tqu3pPbRSVfvs8uJzeNXhp3n4j/tZ42SwH7eaWUUOjc3qFV9453UHTXZfcLH+OeNs5g36x +lBnHvTm7EbMbLeuaLncao8vWCXimfo1o+843Ak6y4ChNeGntvAYvfLK4ezmoyNIbNCLTCXO9ZV3A +E8/s88RczPzDwI4Ob7XZyl7+9Miban29h+tJZPrE21wgvBphDfrrfPdCTPKJD/y98L1rZwHcV6Jq +Zab0metpuNIX/qAFPoz171WUaUb4HAhBSzHuHfjzHb3kha/2Cctis/ORArVHNYfFyYRH2pYIRzic +isVOfPWD1b6mRTqpCRBozzof6UZVvFXRxWIr3GGrEviGYgyPMfahheiSaLs/9QeFu7oZ/ndSY8DD +ya9x+uPed+7mxN2IzIISBOMLFYWVqC3Pew1T2nFuuCiwZS5/v6II10i4t1OJcUH2U9zxKodHsGGv +Oa+zkvNUYUOa/TCCRutF9MzDwdlUMJADTCGSbDQ5OV4PTamDoPEi6Ecc/RF5RWwkcdSXvSOaDWSn +I9LlvubFTQpuc6JKXLcKeb+xdbKRBnwREemXyjg6ME65aJiOuBgrktzykfPLJBKR9ClMavJ62/Ff +BlNIyod9yX9wcSXexnXFpvkrbXk64xsx5Db7wXKP5fSgsvwIMM/9631VLBfkmtbHRXpqmtei52hG +pUwSlo+BASQoeILDOBgREECxBBh5/iYmNsQ9dIv5+OI++QkqdsJPc3uykz5fkM+OraeekcQF7X4n +B5S67za5U967PmooGQhUXfF7afXyCD7ONdRe17QogYjVx38uLwtrS6nhTnm15LQUnu9E2uK6CNI/ +1HOABj0ESwOjut4FEpFQpdNAm4K2LHnDWHNcmKB2ioKBogysVZtMO2nSxUdZ8Yk2kJc7URioLVI0 +YgmtIwZj4LoeKemgnOnbUdGnzZ4Oa6scqiolBGqS6RgWNLu0RMhcaE6rhhU4hiuqFXPAG8fGwTPW +FKeLMtdVmXLSs5YJGF/YeVm7rREMlY3UYE+yCxbaMXX8y15m5zVHq6GOKDMynzII/jdUHdyVqIy0 +ifX2+r/EgtZcvRzSb72gU9ui87M2VecjKildW/aFqaYhKoryUjfB/g4qtyVuc60xFDGmCxwjW+qu +zjuwl2GkOWn66+3QiiEctvd04OVvcCVzjgT7lrkvjVGKKHmmlDUKowSeikb5kK/mJReuWOxONx+s +ULsl+Lqb0CVn0SrVyJ6wt4t6yTeSCafhPhAf0OXn6L60UMxiLolFAtmN35S2Ob1lZpQ1r/n0Qb5D +oQ1zJiRVDgF8N3Q8TYfbi3DyWCy3lT1nxyBs6FT3S2GOzWRlxwKvlRP0RPJA9SjxEy0UoEnkA+M4 +cnzLMJrBGWLFEaaUb5lvpqbq/loOaU5+DFuHPxo82/OZuM8FXG3oVNZhtWpMpb/0Xu5m/LfLhHZQ +7yuVI0MqZ7NE43imC8jH3IwGZlbPm0xkJYs7+2U48hXTsFSMqgGDvai0kLxyynKNT/waj+q1c1tz +GjOpPBgdCSq3UKZxCSsqFIY+O6JbAWGWcV1pwqLyj5sGqCF1xb1F3varUWqrJv6cN3PrUXzijtfZ +FshpBL3Xwr4GIPvU2N8EjrJgS1zl21rbXQMXeXc5jjFyrhpCzijSv/RQtyPSzHCFMhlME95fHglt +pRsX+dfSQjUeHAlpWzJ5iOo79Ldnaxai6bXTcGO3fp07ri7HLEmXXPlYi8bv/qVxvNcdra6m7Rlb +6JBTb5fd66VhFRjGArh2n7R1rDW4P5NOT9K0I183T2scYkeZ3q/VFyLb09U9ajzXBS8Kgkhc4mBS +kYY9cy3Vy9lUnuNJH8HGIclUilwnBtjUOH0gteGOZ4c/XNrhXLSYDyxfnD8z1pDy7rYRvDolhnbe +UMzxCZUs40s6s7UIvBnLgc0+vKuOkIXeOrDymlp+Zxra4MZLBbVrqD/jTJ597pDmnw5c4+DbyB88 +9Cg9DodYcSuMZT/114pptqc/EuTjRPvH/z5slzI3tluOEBBLqOXLOX+0I5929tO97wkvl/atCz+y +xJrdwteW2FNW/NSmBP+f/maYtVs/bYyBC7Ox3jsYZHL05CIrBa/nS+b3bHfiYm4Ueil1YZZSgAUI +fFZ1dxUmeA2oQRQ3RuGXNGLFV9/XbGFGPV6kfzk1TBBCd+izc7q1H+OHMJwmaBX2IQNYVAKHYepV +SSGCe6CnbYHHETKGNe43EDvFgZr0gB/nVHPHZ80VV1ojOiI3XDvYIkl4ayo4bxQIgrFXWTvBI0nH +VElWMuw2aLUWCRHHf8ymVCHjFlJnOSojfevCYyyyZDH0IcvHhrsnQ5O1OsWzONuVVKIxSxiFZ/tR +fKDAf6xFTnw4O9Qig2VCfW2hJQrmMOuHW0W3dLQmCMO2ccdUd/xyfflH/olTiHZVdGwb8nIwRzSE +J15jFlOJuBZBZ4CiyHyd2IFylFlB+HgHhYabhWOGwYO1ZH/Og1dtQlFMk352CGRSIFTapnWQEUtN +l4zv8S0aaCFDyGCBqDUxZYpxGHX01y/JuH1xhn7TOCnNCI4eKDs5WGX4R425F4vF1o3BJ4vO0otq +I3rimI7jJY1jISqnBxknCIvruF83mF5wN4X7qGLIhR8A2Vg0yFERSIXn9Vv3GHy3Vj/WIkKddlYi +yIMv2I/VMjTLpW7pt05SWIZR0RPyxpB4SIUM9lBPGBl0GC7oSEEwRYLe4pJpZY2P0zbI1n+Oc44w +qY3PUnmF0ixjVpDD/mJ9wpOBGTVgXlaCaZiPcIWK5NiKBIiPdGaQ0TWGvAiG7nMchdZb7Vgf8zNi +MuMyzRdy/lePe9iC4TRx7WhhOQI/QiSVNAmAa2lT/piFbuh7ofJoYSZzrSZ1bvmWw3eN2nKUPVky +uPN5/VRfohRd0VYZoqhKIlU6TXYhJxmPUIloAwc1bPmHEpaZYZORHNlXUJM07hATwHR8MJYqkwWR +WaIezFhxSFlc8/Fq82hEnpeRozg3ULhhr9lAGtVEkCg5ZNRuuVleBPaZadhG0ZgkyPmDOTOKzViM +YgOcpukKqQcbjAWS0IleQ2ROjdh6A+md1qWdBRSX7iSYgFRTtRmBpJioieXJiHfJiMGIR9fJOn8I +MSfXYhspn4ooSa2mSAj4n+8Bmg03fBJZoPOJgsVZRxu1oOMRPXYYjdqjihFaEoZpXBREanuJoRI6 +cibFinq4ngUKh/wQd/H5ofYCZ0HJXR62opZFaAT0iFIZo4DIiUojkjeqKiuoZirKo5Y1a7AWckGa +BkuYoD5lpDK6eUs6CkDqpETwl1EqpfhJpVeKpVl6EgUAADs= + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_08.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_08.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,24 @@ +MIME-Version: 1.0 +From: Barry Warsaw +To: Dingus Lovers +Subject: Lyrics +Date: Fri, 20 Apr 2001 19:35:02 -0400 +Content-Type: multipart/mixed; boundary="BOUNDARY" + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" + + +--BOUNDARY +Content-Type: text/html; charset="iso-8859-1" + + +--BOUNDARY +Content-Type: text/plain; charset="iso-8859-2" + + +--BOUNDARY +Content-Type: text/plain; charset="koi8-r" + + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_09.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_09.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,24 @@ +MIME-Version: 1.0 +From: Barry Warsaw +To: Dingus Lovers +Subject: Lyrics +Date: Fri, 20 Apr 2001 19:35:02 -0400 +Content-Type: multipart/mixed; boundary="BOUNDARY" + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" + + +--BOUNDARY +Content-Type: text/html; charset="iso-8859-1" + + +--BOUNDARY +Content-Type: text/plain + + +--BOUNDARY +Content-Type: text/plain; charset="koi8-r" + + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_10.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_10.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,39 @@ +MIME-Version: 1.0 +From: Barry Warsaw +To: Dingus Lovers +Subject: Lyrics +Date: Fri, 20 Apr 2001 19:35:02 -0400 +Content-Type: multipart/mixed; boundary="BOUNDARY" + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +Content-Transfer-Encoding: 7bit + +This is a 7bit encoded message. + +--BOUNDARY +Content-Type: text/html; charset="iso-8859-1" +Content-Transfer-Encoding: Quoted-Printable + +=A1This is a Quoted Printable encoded message! + +--BOUNDARY +Content-Type: text/plain; charset="iso-8859-1" +Content-Transfer-Encoding: Base64 + +VGhpcyBpcyBhIEJhc2U2NCBlbmNvZGVkIG1lc3NhZ2Uu + + +--BOUNDARY +Content-Type: text/plain; charset="iso-8859-1" +Content-Transfer-Encoding: Base64 + +VGhpcyBpcyBhIEJhc2U2NCBlbmNvZGVkIG1lc3NhZ2UuCg== + + +--BOUNDARY +Content-Type: text/plain; charset="iso-8859-1" + +This has no Content-Transfer-Encoding: header. + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_11.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_11.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,7 @@ +Content-Type: message/rfc822 +MIME-Version: 1.0 +Subject: The enclosing message + +Subject: An enclosed message + +Here is the body of the message. diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_12.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_12.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,36 @@ +MIME-Version: 1.0 +From: Barry Warsaw +To: Dingus Lovers +Subject: Lyrics +Date: Fri, 20 Apr 2001 19:35:02 -0400 +Content-Type: multipart/mixed; boundary="BOUNDARY" + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" + + +--BOUNDARY +Content-Type: text/html; charset="iso-8859-1" + + +--BOUNDARY +Content-Type: multipart/mixed; boundary="ANOTHER" + +--ANOTHER +Content-Type: text/plain; charset="iso-8859-2" + + +--ANOTHER +Content-Type: text/plain; charset="iso-8859-3" + +--ANOTHER-- + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" + + +--BOUNDARY +Content-Type: text/plain; charset="koi8-r" + + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_12a.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_12a.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,38 @@ +MIME-Version: 1.0 +From: Barry Warsaw +To: Dingus Lovers +Subject: Lyrics +Date: Fri, 20 Apr 2001 19:35:02 -0400 +Content-Type: multipart/mixed; boundary="BOUNDARY" + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" + + +--BOUNDARY +Content-Type: text/html; charset="iso-8859-1" + + +--BOUNDARY +Content-Type: multipart/mixed; boundary="ANOTHER" + +--ANOTHER +Content-Type: text/plain; charset="iso-8859-2" + + +--ANOTHER +Content-Type: text/plain; charset="iso-8859-3" + + +--ANOTHER-- + + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" + + +--BOUNDARY +Content-Type: text/plain; charset="koi8-r" + + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_13.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_13.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,94 @@ +MIME-Version: 1.0 +From: Barry +To: Dingus Lovers +Subject: Here is your dingus fish +Date: Fri, 20 Apr 2001 19:35:02 -0400 +Content-Type: multipart/mixed; boundary="OUTER" + +--OUTER +Content-Type: text/plain; charset="us-ascii" + +A text/plain part + +--OUTER +Content-Type: multipart/mixed; boundary=BOUNDARY + + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" + +Hi there, + +This is the dingus fish. + +--BOUNDARY +Content-Type: image/gif; name="dingusfish.gif" +Content-Transfer-Encoding: base64 +content-disposition: attachment; filename="dingusfish.gif" + +R0lGODdhAAEAAfAAAP///wAAACwAAAAAAAEAAQAC/oSPqcvtD6OctNqLs968+w+G4kiW5omm6sq2 +7gvH8kzX9o3n+s73/g8MCofEovGITGICTKbyCV0FDNOo9SqpQqpOrJfXzTQj2vD3TGtqL+NtGQ2f +qTXmxzuOd7WXdcc9DyjU53ewFni4s0fGhdiYaEhGBelICTNoV1j5NUnFcrmUqemjNifJVWpaOqaI +oFq3SspZsSraE7sHq3jr1MZqWvi662vxV4tD+pvKW6aLDOCLyur8PDwbanyDeq0N3DctbQYeLDvR +RY6t95m6UB0d3mwIrV7e2VGNvjjffukeJp4w7F65KecGFsTHQGAygOrgrWs1jt28Rc88KESYcGLA +/obvTkH6p+CinWJiJmIMqXGQwH/y4qk0SYjgQTczT3ajKZGfuI0uJ4kkVI/DT5s3/ejkxI0aT4Y+ +YTYgWbImUaXk9nlLmnSh1qJiJFl0OpUqRK4oOy7NyRQtHWofhoYVxkwWXKUSn0YsS+fUV6lhqfYb +6ayd3Z5qQdG1B7bvQzaJjwUV2lixMUZ7JVsOlfjWVr/3NB/uFvnySBN6Dcb6rGwaRM3wsormw5cC +M9NxWy/bWdufudCvy8bOAjXjVVwta/uO21sE5RHBCzNFXtgq9ORtH4eYjVP4Yryo026nvkFmCeyA +B29efV6ravCMK5JwWd5897Qrx7ll38o6iHDZ/rXPR//feevhF4l7wjUGX3xq1eeRfM4RSJGBIV1D +z1gKPkfWag3mVBVvva1RlX5bAJTPR/2YqNtw/FkIYYEi/pIZiAdpcxpoHtmnYYoZtvhUftzdx5ZX +JSKDW405zkGcZzzGZ6KEv4FI224oDmijlEf+xp6MJK5ojY/ASeVUR+wsKRuJ+XFZ5o7ZeEime8t1 +ouUsU6YjF5ZtUihhkGfCdFQLWQFJ3UXxmElfhQnR+eCdcDbkFZp6vTRmj56ApCihn5QGpaToNZmR +n3NVSpZcQpZ2KEONusaiCsKAug0wkQbJSFO+PTSjneGxOuFjPlUk3ovWvdIerjUg9ZGIOtGq/qeX +eCYrrCX+1UPsgTKGGRSbzd5q156d/gpfbJxe66eD5iQKrXj7RGgruGxs62qebBHUKS32CKluCiqZ +qh+pmehmEb71noAUoe5e9Zm17S7773V10pjrtG4CmuurCV/n6zLK5turWNhqOvFXbjhZrMD0YhKe +wR0zOyuvsh6MWrGoIuzvyWu5y1WIFAqmJselypxXh6dKLNOKEB98L88bS2rkNqqlKzCNJp9c0G0j +Gzh0iRrCbHSXmPR643QS+4rWhgFmnSbSuXCjS0xAOWkU2UdLqyuUNfHSFdUouy3bm5i5GnDM3tG8 +doJ4r5tqu3pPbRSVfvs8uJzeNXhp3n4j/tZ42SwH7eaWUUOjc3qFV9453UHTXZfcLH+OeNs5g36x +lBnHvTm7EbMbLeuaLncao8vWCXimfo1o+843Ak6y4ChNeGntvAYvfLK4ezmoyNIbNCLTCXO9ZV3A +E8/s88RczPzDwI4Ob7XZyl7+9Miban29h+tJZPrE21wgvBphDfrrfPdCTPKJD/y98L1rZwHcV6Jq +Zab0metpuNIX/qAFPoz171WUaUb4HAhBSzHuHfjzHb3kha/2Cctis/ORArVHNYfFyYRH2pYIRzic +isVOfPWD1b6mRTqpCRBozzof6UZVvFXRxWIr3GGrEviGYgyPMfahheiSaLs/9QeFu7oZ/ndSY8DD +ya9x+uPed+7mxN2IzIISBOMLFYWVqC3Pew1T2nFuuCiwZS5/v6II10i4t1OJcUH2U9zxKodHsGGv +Oa+zkvNUYUOa/TCCRutF9MzDwdlUMJADTCGSbDQ5OV4PTamDoPEi6Ecc/RF5RWwkcdSXvSOaDWSn +I9LlvubFTQpuc6JKXLcKeb+xdbKRBnwREemXyjg6ME65aJiOuBgrktzykfPLJBKR9ClMavJ62/Ff +BlNIyod9yX9wcSXexnXFpvkrbXk64xsx5Db7wXKP5fSgsvwIMM/9631VLBfkmtbHRXpqmtei52hG +pUwSlo+BASQoeILDOBgREECxBBh5/iYmNsQ9dIv5+OI++QkqdsJPc3uykz5fkM+OraeekcQF7X4n +B5S67za5U967PmooGQhUXfF7afXyCD7ONdRe17QogYjVx38uLwtrS6nhTnm15LQUnu9E2uK6CNI/ +1HOABj0ESwOjut4FEpFQpdNAm4K2LHnDWHNcmKB2ioKBogysVZtMO2nSxUdZ8Yk2kJc7URioLVI0 +YgmtIwZj4LoeKemgnOnbUdGnzZ4Oa6scqiolBGqS6RgWNLu0RMhcaE6rhhU4hiuqFXPAG8fGwTPW +FKeLMtdVmXLSs5YJGF/YeVm7rREMlY3UYE+yCxbaMXX8y15m5zVHq6GOKDMynzII/jdUHdyVqIy0 +ifX2+r/EgtZcvRzSb72gU9ui87M2VecjKildW/aFqaYhKoryUjfB/g4qtyVuc60xFDGmCxwjW+qu +zjuwl2GkOWn66+3QiiEctvd04OVvcCVzjgT7lrkvjVGKKHmmlDUKowSeikb5kK/mJReuWOxONx+s +ULsl+Lqb0CVn0SrVyJ6wt4t6yTeSCafhPhAf0OXn6L60UMxiLolFAtmN35S2Ob1lZpQ1r/n0Qb5D +oQ1zJiRVDgF8N3Q8TYfbi3DyWCy3lT1nxyBs6FT3S2GOzWRlxwKvlRP0RPJA9SjxEy0UoEnkA+M4 +cnzLMJrBGWLFEaaUb5lvpqbq/loOaU5+DFuHPxo82/OZuM8FXG3oVNZhtWpMpb/0Xu5m/LfLhHZQ +7yuVI0MqZ7NE43imC8jH3IwGZlbPm0xkJYs7+2U48hXTsFSMqgGDvai0kLxyynKNT/waj+q1c1tz +GjOpPBgdCSq3UKZxCSsqFIY+O6JbAWGWcV1pwqLyj5sGqCF1xb1F3varUWqrJv6cN3PrUXzijtfZ +FshpBL3Xwr4GIPvU2N8EjrJgS1zl21rbXQMXeXc5jjFyrhpCzijSv/RQtyPSzHCFMhlME95fHglt +pRsX+dfSQjUeHAlpWzJ5iOo79Ldnaxai6bXTcGO3fp07ri7HLEmXXPlYi8bv/qVxvNcdra6m7Rlb +6JBTb5fd66VhFRjGArh2n7R1rDW4P5NOT9K0I183T2scYkeZ3q/VFyLb09U9ajzXBS8Kgkhc4mBS +kYY9cy3Vy9lUnuNJH8HGIclUilwnBtjUOH0gteGOZ4c/XNrhXLSYDyxfnD8z1pDy7rYRvDolhnbe +UMzxCZUs40s6s7UIvBnLgc0+vKuOkIXeOrDymlp+Zxra4MZLBbVrqD/jTJ597pDmnw5c4+DbyB88 +9Cg9DodYcSuMZT/114pptqc/EuTjRPvH/z5slzI3tluOEBBLqOXLOX+0I5929tO97wkvl/atCz+y +xJrdwteW2FNW/NSmBP+f/maYtVs/bYyBC7Ox3jsYZHL05CIrBa/nS+b3bHfiYm4Ueil1YZZSgAUI +fFZ1dxUmeA2oQRQ3RuGXNGLFV9/XbGFGPV6kfzk1TBBCd+izc7q1H+OHMJwmaBX2IQNYVAKHYepV +SSGCe6CnbYHHETKGNe43EDvFgZr0gB/nVHPHZ80VV1ojOiI3XDvYIkl4ayo4bxQIgrFXWTvBI0nH +VElWMuw2aLUWCRHHf8ymVCHjFlJnOSojfevCYyyyZDH0IcvHhrsnQ5O1OsWzONuVVKIxSxiFZ/tR +fKDAf6xFTnw4O9Qig2VCfW2hJQrmMOuHW0W3dLQmCMO2ccdUd/xyfflH/olTiHZVdGwb8nIwRzSE +J15jFlOJuBZBZ4CiyHyd2IFylFlB+HgHhYabhWOGwYO1ZH/Og1dtQlFMk352CGRSIFTapnWQEUtN +l4zv8S0aaCFDyGCBqDUxZYpxGHX01y/JuH1xhn7TOCnNCI4eKDs5WGX4R425F4vF1o3BJ4vO0otq +I3rimI7jJY1jISqnBxknCIvruF83mF5wN4X7qGLIhR8A2Vg0yFERSIXn9Vv3GHy3Vj/WIkKddlYi +yIMv2I/VMjTLpW7pt05SWIZR0RPyxpB4SIUM9lBPGBl0GC7oSEEwRYLe4pJpZY2P0zbI1n+Oc44w +qY3PUnmF0ixjVpDD/mJ9wpOBGTVgXlaCaZiPcIWK5NiKBIiPdGaQ0TWGvAiG7nMchdZb7Vgf8zNi +MuMyzRdy/lePe9iC4TRx7WhhOQI/QiSVNAmAa2lT/piFbuh7ofJoYSZzrSZ1bvmWw3eN2nKUPVky +uPN5/VRfohRd0VYZoqhKIlU6TXYhJxmPUIloAwc1bPmHEpaZYZORHNlXUJM07hATwHR8MJYqkwWR +WaIezFhxSFlc8/Fq82hEnpeRozg3ULhhr9lAGtVEkCg5ZNRuuVleBPaZadhG0ZgkyPmDOTOKzViM +YgOcpukKqQcbjAWS0IleQ2ROjdh6A+md1qWdBRSX7iSYgFRTtRmBpJioieXJiHfJiMGIR9fJOn8I +MSfXYhspn4ooSa2mSAj4n+8Bmg03fBJZoPOJgsVZRxu1oOMRPXYYjdqjihFaEoZpXBREanuJoRI6 +cibFinq4ngUKh/wQd/H5ofYCZ0HJXR62opZFaAT0iFIZo4DIiUojkjeqKiuoZirKo5Y1a7AWckGa +BkuYoD5lpDK6eUs6CkDqpETwl1EqpfhJpVeKpVl6EgUAADs= + +--BOUNDARY-- + +--OUTER-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_14.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_14.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,23 @@ +Return-Path: +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) + id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +MIME-Version: 1.0 +Content-Type: text; charset=us-ascii +Content-Transfer-Encoding: 7bit +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 + + +Hi, + +I'm sorry but I'm using a drainbread ISP, which although big and +wealthy can't seem to generate standard compliant email. :( + +This message has a Content-Type: header with no subtype. I hope you +can still read it. + +-Me diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_15.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_15.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,52 @@ +Return-Path: +Received: from fepD.post.tele.dk (195.41.46.149) by mail.groupcare.dk (LSMTP for Windows NT v1.1b) with SMTP id <0.0014F8A2@mail.groupcare.dk>; Mon, 30 Apr 2001 12:17:50 +0200 +User-Agent: Microsoft-Outlook-Express-Macintosh-Edition/5.02.2106 +Subject: XX +From: xx@xx.dk +To: XX +Message-ID: +Mime-version: 1.0 +Content-type: multipart/mixed; + boundary="MS_Mac_OE_3071477847_720252_MIME_Part" + +> Denne meddelelse er i MIME-format. Da dit postl + +--MS_Mac_OE_3071477847_720252_MIME_Part +Content-type: multipart/alternative; + boundary="MS_Mac_OE_3071477847_720252_MIME_Part" + + +--MS_Mac_OE_3071477847_720252_MIME_Part +Content-type: text/plain; charset="ISO-8859-1" +Content-transfer-encoding: quoted-printable + +Some removed test. + +--MS_Mac_OE_3071477847_720252_MIME_Part +Content-type: text/html; charset="ISO-8859-1" +Content-transfer-encoding: quoted-printable + + + +Some removed HTML + + +Some removed text. + + + + +--MS_Mac_OE_3071477847_720252_MIME_Part-- + + +--MS_Mac_OE_3071477847_720252_MIME_Part +Content-type: image/gif; name="xx.gif"; + x-mac-creator="6F676C65"; + x-mac-type="47494666" +Content-disposition: attachment +Content-transfer-encoding: base64 + +Some removed base64 encoded chars. + +--MS_Mac_OE_3071477847_720252_MIME_Part-- + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_16.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_16.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,123 @@ +Return-Path: <> +Delivered-To: scr-admin@socal-raves.org +Received: from cougar.noc.ucla.edu (cougar.noc.ucla.edu [169.232.10.18]) + by babylon.socal-raves.org (Postfix) with ESMTP id CCC2C51B84 + for ; Sun, 23 Sep 2001 20:13:54 -0700 (PDT) +Received: from sims-ms-daemon by cougar.noc.ucla.edu + (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10) + id <0GK500B01D0B8Y@cougar.noc.ucla.edu> for scr-admin@socal-raves.org; Sun, + 23 Sep 2001 20:14:35 -0700 (PDT) +Received: from cougar.noc.ucla.edu + (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10) + id <0GK500B01D0B8X@cougar.noc.ucla.edu>; Sun, 23 Sep 2001 20:14:35 -0700 (PDT) +Date: Sun, 23 Sep 2001 20:14:35 -0700 (PDT) +From: Internet Mail Delivery +Subject: Delivery Notification: Delivery has failed +To: scr-admin@socal-raves.org +Message-id: <0GK500B04D0B8X@cougar.noc.ucla.edu> +MIME-version: 1.0 +Sender: scr-owner@socal-raves.org +Errors-To: scr-owner@socal-raves.org +X-BeenThere: scr@socal-raves.org +X-Mailman-Version: 2.1a3 +Precedence: bulk +List-Help: +List-Post: +List-Subscribe: , + +List-Id: SoCal-Raves +List-Unsubscribe: , + +List-Archive: +Content-Type: multipart/report; boundary="Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)" + + +--Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA) +Content-type: text/plain; charset=ISO-8859-1 + +This report relates to a message you sent with the following header fields: + + Message-id: <002001c144a6$8752e060$56104586@oxy.edu> + Date: Sun, 23 Sep 2001 20:10:55 -0700 + From: "Ian T. Henry" + To: SoCal Raves + Subject: [scr] yeah for Ians!! + +Your message cannot be delivered to the following recipients: + + Recipient address: jangel1@cougar.noc.ucla.edu + Reason: recipient reached disk quota + + +--Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA) +Content-type: message/DELIVERY-STATUS + +Original-envelope-id: 0GK500B4HD0888@cougar.noc.ucla.edu +Reporting-MTA: dns; cougar.noc.ucla.edu + +Action: failed +Status: 5.0.0 (recipient reached disk quota) +Original-recipient: rfc822;jangel1@cougar.noc.ucla.edu +Final-recipient: rfc822;jangel1@cougar.noc.ucla.edu + +--Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA) +Content-type: MESSAGE/RFC822 + +Return-path: scr-admin@socal-raves.org +Received: from sims-ms-daemon by cougar.noc.ucla.edu + (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10) + id <0GK500B01D0B8X@cougar.noc.ucla.edu>; Sun, 23 Sep 2001 20:14:35 -0700 (PDT) +Received: from panther.noc.ucla.edu by cougar.noc.ucla.edu + (Sun Internet Mail Server sims.3.5.2000.03.23.18.03.p10) + with ESMTP id <0GK500B4GD0888@cougar.noc.ucla.edu> for jangel1@sims-ms-daemon; + Sun, 23 Sep 2001 20:14:33 -0700 (PDT) +Received: from babylon.socal-raves.org + (ip-209-85-222-117.dreamhost.com [209.85.222.117]) + by panther.noc.ucla.edu (8.9.1a/8.9.1) with ESMTP id UAA09793 for + ; Sun, 23 Sep 2001 20:14:32 -0700 (PDT) +Received: from babylon (localhost [127.0.0.1]) by babylon.socal-raves.org + (Postfix) with ESMTP id D3B2951B70; Sun, 23 Sep 2001 20:13:47 -0700 (PDT) +Received: by babylon.socal-raves.org (Postfix, from userid 60001) + id A611F51B82; Sun, 23 Sep 2001 20:13:46 -0700 (PDT) +Received: from tiger.cc.oxy.edu (tiger.cc.oxy.edu [134.69.3.112]) + by babylon.socal-raves.org (Postfix) with ESMTP id ADA7351B70 for + ; Sun, 23 Sep 2001 20:13:44 -0700 (PDT) +Received: from ent (n16h86.dhcp.oxy.edu [134.69.16.86]) + by tiger.cc.oxy.edu (8.8.8/8.8.8) with SMTP id UAA08100 for + ; Sun, 23 Sep 2001 20:14:24 -0700 (PDT) +Date: Sun, 23 Sep 2001 20:10:55 -0700 +From: "Ian T. Henry" +Subject: [scr] yeah for Ians!! +Sender: scr-admin@socal-raves.org +To: SoCal Raves +Errors-to: scr-admin@socal-raves.org +Message-id: <002001c144a6$8752e060$56104586@oxy.edu> +MIME-version: 1.0 +X-Mailer: Microsoft Outlook Express 5.50.4522.1200 +Content-type: text/plain; charset=us-ascii +Precedence: bulk +Delivered-to: scr-post@babylon.socal-raves.org +Delivered-to: scr@socal-raves.org +X-Converted-To-Plain-Text: from multipart/alternative by demime 0.98e +X-Converted-To-Plain-Text: Alternative section used was text/plain +X-BeenThere: scr@socal-raves.org +X-Mailman-Version: 2.1a3 +List-Help: +List-Post: +List-Subscribe: , + +List-Id: SoCal-Raves +List-Unsubscribe: , + +List-Archive: + +I always love to find more Ian's that are over 3 years old!! + +Ian +_______________________________________________ +For event info, list questions, or to unsubscribe, see http://www.socal-raves.org/ + + + +--Boundary_(ID_PGS2F2a+z+/jL7hupKgRhA)-- + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_17.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_17.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,12 @@ +MIME-Version: 1.0 +From: Barry +To: Dingus Lovers +Subject: Here is your dingus fish +Date: Fri, 20 Apr 2001 19:35:02 -0400 +Content-Type: multipart/mixed; boundary="BOUNDARY" + +Hi there, + +This is the dingus fish. + +[Non-text (image/gif) part of message omitted, filename dingusfish.gif] diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_18.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_18.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,6 @@ +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals"; + spooge="yummy"; hippos="gargantuan"; marshmallows="gooey" + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_19.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_19.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,43 @@ +Send Ppp mailing list submissions to + ppp@zzz.org + +To subscribe or unsubscribe via the World Wide Web, visit + http://www.zzz.org/mailman/listinfo/ppp +or, via email, send a message with subject or body 'help' to + ppp-request@zzz.org + +You can reach the person managing the list at + ppp-admin@zzz.org + +When replying, please edit your Subject line so it is more specific +than "Re: Contents of Ppp digest..." + +Today's Topics: + + 1. testing #1 (Barry A. Warsaw) + 2. testing #2 (Barry A. Warsaw) + 3. testing #3 (Barry A. Warsaw) + 4. testing #4 (Barry A. Warsaw) + 5. testing #5 (Barry A. Warsaw) + +hello + + +hello + + +hello + + +hello + + +hello + + + +_______________________________________________ +Ppp mailing list +Ppp@zzz.org +http://www.zzz.org/mailman/listinfo/ppp + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_20.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_20.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,22 @@ +Return-Path: +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) + id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Cc: ccc@zzz.org +CC: ddd@zzz.org +cc: eee@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 + + +Hi, + +Do you like this message? + +-Me diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_21.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_21.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,20 @@ +From: aperson@dom.ain +To: bperson@dom.ain +Subject: Test +Content-Type: multipart/mixed; boundary="BOUNDARY" + +MIME message +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +One +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +Two +--BOUNDARY-- +End of MIME message diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_22.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_22.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,46 @@ +Mime-Version: 1.0 +Message-Id: +Date: Tue, 16 Oct 2001 13:59:25 +0300 +To: a@example.com +From: b@example.com +Content-Type: multipart/mixed; boundary="============_-1208892523==_============" + +--============_-1208892523==_============ +Content-Type: text/plain; charset="us-ascii" ; format="flowed" + +Text text text. +--============_-1208892523==_============ +Content-Id: +Content-Type: image/jpeg; name="wibble.JPG" + ; x-mac-type="4A504547" + ; x-mac-creator="474B4F4E" +Content-Disposition: attachment; filename="wibble.JPG" +Content-Transfer-Encoding: base64 + +/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB +AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAXABIEBAREA +g6bCjjw/pIZSjO6FWFpldjySOmCNrO7DBZibUXhTwtCixw+GtAijVdqxxaPp0aKvmGXa +qrbBQvms0mAMeYS/3iTV1dG0hHaRNK01XblnWxtVdjkHLMIgTyqnk9VB7CrP2KzIINpa +4O7I+zxYO9WV8jZg71Zlb+8rMDkEirAVQFAUAKAFAAAUAYAUDgADgY6DjpRtXj5RxjHA +4wQRj0wQCMdCAewpaKKK/9k= +--============_-1208892523==_============ +Content-Id: +Content-Type: image/jpeg; name="wibble2.JPG" + ; x-mac-type="4A504547" + ; x-mac-creator="474B4F4E" +Content-Disposition: attachment; filename="wibble2.JPG" +Content-Transfer-Encoding: base64 + +/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB +AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAALCAXABJ0BAREA +/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQA +W6NFJJBEkU10kKGTcWMDwxuU+0JHvk8qAtOpNwqSR0n8c3BlDyXHlqsUltHEiTvdXLxR +7vMiGDNJAJWkAMk8ZkCFp5G2oo5W++INrbQtNfTQxJAuXlupz9oS4d5Y1W+E2XlWZJJE +Y7LWYQxTLE1zuMbfBPxw8X2fibVdIbSbI6nLZxX635t9TjtYreWR7WGKJTLJFFKSlozO +0ShxIXM43uC3/9k= +--============_-1208892523==_============ +Content-Type: text/plain; charset="us-ascii" ; format="flowed" + +Text text text. +--============_-1208892523==_============-- + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_23.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_23.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,8 @@ +From: aperson@dom.ain +Content-Type: multipart/mixed; boundary="BOUNDARY" + +--BOUNDARY +Content-Type: text/plain + +A message part +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_24.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_24.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,10 @@ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY + + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_25.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_25.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,117 @@ +From MAILER-DAEMON Fri Apr 06 16:46:09 2001 +Received: from [204.245.199.98] (helo=zinfandel.lacita.com) + by www.linux.org.uk with esmtp (Exim 3.13 #1) + id 14lYR6-0008Iv-00 + for linuxuser-admin@www.linux.org.uk; Fri, 06 Apr 2001 16:46:09 +0100 +Received: from localhost (localhost) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with internal id JAB03225; Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800) +Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800) +From: Mail Delivery Subsystem +Subject: Returned mail: Too many hops 19 (17 max): from via [199.164.235.226], to +Message-Id: <200104061723.JAB03225@zinfandel.lacita.com> +To: +To: postmaster@zinfandel.lacita.com +MIME-Version: 1.0 +Content-Type: multipart/report; report-type=delivery-status; + bo +Auto-Submitted: auto-generated (failure) + +This is a MIME-encapsulated message + +--JAB03225.986577786/zinfandel.lacita.com + +The original message was received at Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800) +from [199.164.235.226] + + ----- The following addresses have delivery notifications ----- + (unrecoverable error) + + ----- Transcript of session follows ----- +554 Too many hops 19 (17 max): from via [199.164.235.226], to + +--JAB03225.986577786/zinfandel.lacita.com +Content-Type: message/delivery-status + +Reporting-MTA: dns; zinfandel.lacita.com +Received-From-MTA: dns; [199.164.235.226] +Arrival-Date: Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800) + +Final-Recipient: rfc822; scoffman@wellpartner.com +Action: failed +Status: 5.4.6 +Last-Attempt-Date: Fri, 6 Apr 2001 09:23:06 -0800 (GMT-0800) + +--JAB03225.986577786/zinfandel.lacita.com +Content-Type: text/rfc822-headers + +Return-Path: linuxuser-admin@www.linux.org.uk +Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03225 for ; Fri, 6 Apr 2001 09:23:03 -0800 (GMT-0800) +Received: from zinfandel.lacita.com ([204.245.199.98]) + by + fo +Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03221 for ; Fri, 6 Apr 2001 09:22:18 -0800 (GMT-0800) +Received: from zinfandel.lacita.com ([204.245.199.98]) + by + fo +Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03217 for ; Fri, 6 Apr 2001 09:21:37 -0800 (GMT-0800) +Received: from zinfandel.lacita.com ([204.245.199.98]) + by + fo +Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03213 for ; Fri, 6 Apr 2001 09:20:56 -0800 (GMT-0800) +Received: from zinfandel.lacita.com ([204.245.199.98]) + by + fo +Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03209 for ; Fri, 6 Apr 2001 09:20:15 -0800 (GMT-0800) +Received: from zinfandel.lacita.com ([204.245.199.98]) + by + fo +Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03205 for ; Fri, 6 Apr 2001 09:19:33 -0800 (GMT-0800) +Received: from zinfandel.lacita.com ([204.245.199.98]) + by + fo +Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03201 for ; Fri, 6 Apr 2001 09:18:52 -0800 (GMT-0800) +Received: from zinfandel.lacita.com ([204.245.199.98]) + by + fo +Received: from ns1.wellpartner.net ([199.164.235.226]) by zinfandel.lacita.com (8.7.3/8.6.10-MT4.00) with ESMTP id JAA03197 for ; Fri, 6 Apr 2001 09:17:54 -0800 (GMT-0800) +Received: from www.linux.org.uk (parcelfarce.linux.theplanet.co.uk [195.92.249.252]) + by + fo +Received: from localhost.localdomain + ([ + by + id +Received: from [212.1.130.11] (helo=s1.uklinux.net ident=root) + by + id + fo +Received: from server (ppp-2-22.cvx4.telinco.net [212.1.149.22]) + by + fo +From: Daniel James +Organization: LinuxUser +To: linuxuser@www.linux.org.uk +X-Mailer: KMail [version 1.1.99] +Content-Type: text/plain; + c +MIME-Version: 1.0 +Message-Id: <01040616033903.00962@server> +Content-Transfer-Encoding: 8bit +Subject: [LinuxUser] bulletin no. 45 +Sender: linuxuser-admin@www.linux.org.uk +Errors-To: linuxuser-admin@www.linux.org.uk +X-BeenThere: linuxuser@www.linux.org.uk +X-Mailman-Version: 2.0.3 +Precedence: bulk +List-Help: +List-Post: +List-Subscribe: , + +List-Unsubscribe: , + +Date: Fri, 6 Apr 2001 16:03:39 +0100 + +--JAB03225.986577786/zinfandel.lacita.com-- + + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_26.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_26.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,46 @@ +Received: from xcar [192.168.0.2] by jeeves.wooster.local + (SMTPD32-7.07 EVAL) id AFF92F0214; Sun, 12 May 2002 08:55:37 +0100 +Date: Sun, 12 May 2002 08:56:15 +0100 +From: Father Time +To: timbo@jeeves.wooster.local +Subject: IMAP file test +Message-ID: <6df65d354b.father.time@rpc.wooster.local> +X-Organization: Home +User-Agent: Messenger-Pro/2.50a (MsgServe/1.50) (RISC-OS/4.02) POPstar/2.03 +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="1618492860--2051301190--113853680" +Status: R +X-UIDL: 319998302 + +This message is in MIME format which your mailer apparently does not support. +You either require a newer version of your software which supports MIME, or +a separate MIME decoding utility. Alternatively, ask the sender of this +message to resend it in a different format. + +--1618492860--2051301190--113853680 +Content-Type: text/plain; charset=us-ascii + +Simple email with attachment. + + +--1618492860--2051301190--113853680 +Content-Type: application/riscos; name="clock.bmp,69c"; type=BMP; + load=&fff69c4b; exec=&355dd4d1; access=&03 +Content-Disposition: attachment; filename="clock.bmp" +Content-Transfer-Encoding: base64 + +Qk12AgAAAAAAAHYAAAAoAAAAIAAAACAAAAABAAQAAAAAAAAAAADXDQAA1w0AAAAAAAAA +AAAAAAAAAAAAiAAAiAAAAIiIAIgAAACIAIgAiIgAALu7uwCIiIgAERHdACLuIgAz//8A +zAAAAN0R3QDu7iIA////AAAAAAAAAAAAAAAAAAAAAAAAAAi3AAAAAAAAADeAAAAAAAAA +C3ADMzMzMANwAAAAAAAAAAAHMAAAAANwAAAAAAAAAACAMAd3zPfwAwgAAAAAAAAIAwd/ +f8x/f3AwgAAAAAAAgDB0x/f3//zPAwgAAAAAAAcHfM9////8z/AwAAAAAAiwd/f3//// +////A4AAAAAAcEx/f///////zAMAAAAAiwfM9////3///8zwOAAAAAcHf3////B///// +8DAAAAALB/f3///wd3d3//AwAAAABwTPf//wCQAAD/zAMAAAAAsEx/f///B////8wDAA +AAAHB39////wf/////AwAAAACwf39///8H/////wMAAAAIcHfM9///B////M8DgAAAAA +sHTH///wf///xAMAAAAACHB3f3//8H////cDgAAAAAALB3zH//D//M9wMAAAAAAAgLB0 +z39///xHAwgAAAAAAAgLB3d3RHd3cDCAAAAAAAAAgLAHd0R3cAMIAAAAAAAAgAgLcAAA +AAMwgAgAAAAACDAAAAu7t7cwAAgDgAAAAABzcIAAAAAAAAgDMwAAAAAAN7uwgAAAAAgH +MzMAAAAACH97tzAAAAALu3c3gAAAAAAL+7tzDABAu7f7cAAAAAAACA+3MA7EQAv/sIAA +AAAAAAAIAAAAAAAAAIAAAAAA + +--1618492860--2051301190--113853680-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_27.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_27.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,15 @@ +Return-Path: +Received: by mail.dom.ain (Postfix, from userid 889) + id B9D0AD35DB; Tue, 4 Jun 2002 21:46:59 -0400 (EDT) +Message-ID: <15613.28051.707126.569693@dom.ain> +Date: Tue, 4 Jun 2002 21:46:59 -0400 +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii +Content-Transfer-Encoding: 7bit +Subject: bug demonstration + 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 + more text +From: aperson@dom.ain (Anne P. Erson) +To: bperson@dom.ain (Barney P. Erson) + +test diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_28.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_28.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,25 @@ +From: aperson@dom.ain +MIME-Version: 1.0 +Content-Type: multipart/digest; boundary=BOUNDARY + +--BOUNDARY +Content-Type: message/rfc822 + +Content-Type: text/plain; charset=us-ascii +To: aa@bb.org +From: cc@dd.org +Subject: ee + +message 1 + +--BOUNDARY +Content-Type: message/rfc822 + +Content-Type: text/plain; charset=us-ascii +To: aa@bb.org +From: cc@dd.org +Subject: ee + +message 2 + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_29.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_29.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,22 @@ +Return-Path: +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) + id 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +MIME-Version: 1.0 +Content-Type: text/plain; charset=us-ascii; + title*0*="us-ascii'en'This%20is%20even%20more%20"; + title*1*="%2A%2A%2Afun%2A%2A%2A%20"; + title*2="isn't it!" +Content-Transfer-Encoding: 7bit +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 + + +Hi, + +Do you like this message? + +-Me diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_30.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_30.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,23 @@ +From: aperson@dom.ain +MIME-Version: 1.0 +Content-Type: multipart/digest; boundary=BOUNDARY + +--BOUNDARY + +Content-Type: text/plain; charset=us-ascii +To: aa@bb.org +From: cc@dd.org +Subject: ee + +message 1 + +--BOUNDARY + +Content-Type: text/plain; charset=us-ascii +To: aa@bb.org +From: cc@dd.org +Subject: ee + +message 2 + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_31.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_31.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,15 @@ +From: aperson@dom.ain +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary=BOUNDARY_ + +--BOUNDARY +Content-Type: text/plain + +message 1 + +--BOUNDARY +Content-Type: text/plain + +message 2 + +--BOUNDARY-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_32.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_32.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,14 @@ +Delivered-To: freebsd-isp@freebsd.org +Date: Tue, 26 Sep 2000 12:23:03 -0500 +From: Anne Person +To: Barney Dude +Subject: Re: Limiting Perl CPU Utilization... +Mime-Version: 1.0 +Content-Type: text/plain; charset*=ansi-x3.4-1968''us-ascii +Content-Disposition: inline +User-Agent: Mutt/1.3.8i +Sender: owner-freebsd-isp@FreeBSD.ORG +Precedence: bulk +X-Loop: FreeBSD.org + +Some message. diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_33.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_33.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,29 @@ +Delivered-To: freebsd-isp@freebsd.org +Date: Wed, 27 Sep 2000 11:11:09 -0500 +From: Anne Person +To: Barney Dude +Subject: Re: Limiting Perl CPU Utilization... +Mime-Version: 1.0 +Content-Type: multipart/signed; micalg*=ansi-x3.4-1968''pgp-md5; + protocol*=ansi-x3.4-1968''application%2Fpgp-signature; + boundary*="ansi-x3.4-1968''EeQfGwPcQSOJBaQU" +Content-Disposition: inline +Sender: owner-freebsd-isp@FreeBSD.ORG +Precedence: bulk +X-Loop: FreeBSD.org + + +--EeQfGwPcQSOJBaQU +Content-Type: text/plain; charset*=ansi-x3.4-1968''us-ascii +Content-Disposition: inline +Content-Transfer-Encoding: quoted-printable + +part 1 + +--EeQfGwPcQSOJBaQU +Content-Type: text/plain +Content-Disposition: inline + +part 2 + +--EeQfGwPcQSOJBaQU-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_34.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_34.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,19 @@ +From: aperson@dom.ain +To: bperson@dom.ain +Content-Type: multipart/digest; boundary=XYZ + +--XYZ +Content-Type: text/plain + + +This is a text plain part that is counter to recommended practice in +RFC 2046, $5.1.5, but is not illegal + +--XYZ + +From: cperson@dom.ain +To: dperson@dom.ain + +A submessage + +--XYZ-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_35.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_35.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,4 @@ +From: aperson@dom.ain +To: bperson@dom.ain +Subject: here's something interesting +counter to RFC 2822, there's no separating newline here diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_36.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_36.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,40 @@ +Mime-Version: 1.0 +Content-Type: Multipart/Mixed; Boundary="NextPart" +To: IETF-Announce:; +From: Internet-Drafts@ietf.org +Subject: I-D ACTION:draft-ietf-mboned-mix-00.txt +Date: Tue, 22 Dec 1998 16:55:06 -0500 + +--NextPart + +Blah blah blah + +--NextPart +Content-Type: Multipart/Alternative; Boundary="OtherAccess" + +--OtherAccess +Content-Type: Message/External-body; + access-type="mail-server"; + server="mailserv@ietf.org" + +Content-Type: text/plain +Content-ID: <19981222151406.I-D@ietf.org> + +ENCODING mime +FILE /internet-drafts/draft-ietf-mboned-mix-00.txt + +--OtherAccess +Content-Type: Message/External-body; + name="draft-ietf-mboned-mix-00.txt"; + site="ftp.ietf.org"; + access-type="anon-ftp"; + directory="internet-drafts" + +Content-Type: text/plain +Content-ID: <19981222151406.I-D@ietf.org> + + +--OtherAccess-- + +--NextPart-- + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_37.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_37.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,22 @@ +Content-Type: multipart/mixed; boundary=ABCDE + +--ABCDE +Content-Type: text/x-one + +Blah + +--ABCDE +--ABCDE +Content-Type: text/x-two + +Blah + +--ABCDE +--ABCDE +--ABCDE +--ABCDE +Content-Type: text/x-two + +Blah + +--ABCDE-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_38.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_38.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,101 @@ +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" + +------- =_aaaaaaaaaa0 +Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa1" +Content-ID: <20592.1022586929.1@example.com> + +------- =_aaaaaaaaaa1 +Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa2" +Content-ID: <20592.1022586929.2@example.com> + +------- =_aaaaaaaaaa2 +Content-Type: text/plain +Content-ID: <20592.1022586929.3@example.com> +Content-Description: very tricky +Content-Transfer-Encoding: 7bit + + +Unlike the test test_nested-multiples-with-internal-boundary, this +piece of text not only contains the outer boundary tags +------- =_aaaaaaaaaa1 +and +------- =_aaaaaaaaaa0 +but puts them at the start of a line! And, to be even nastier, it +even includes a couple of end tags, such as this one: + +------- =_aaaaaaaaaa1-- + +and this one, which is from a multipart we haven't even seen yet! + +------- =_aaaaaaaaaa4-- + +This will, I'm sure, cause much breakage of MIME parsers. But, as +far as I can tell, it's perfectly legal. I have not yet ever seen +a case of this in the wild, but I've seen *similar* things. + + +------- =_aaaaaaaaaa2 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.4@example.com> +Content-Description: patch2 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa2-- + +------- =_aaaaaaaaaa1 +Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa3" +Content-ID: <20592.1022586929.6@example.com> + +------- =_aaaaaaaaaa3 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.7@example.com> +Content-Description: patch3 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa3 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.8@example.com> +Content-Description: patch4 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa3-- + +------- =_aaaaaaaaaa1 +Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa4" +Content-ID: <20592.1022586929.10@example.com> + +------- =_aaaaaaaaaa4 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.11@example.com> +Content-Description: patch5 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa4 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.12@example.com> +Content-Description: patch6 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa4-- + +------- =_aaaaaaaaaa1-- + +------- =_aaaaaaaaaa0 +Content-Type: text/plain; charset="us-ascii" +Content-ID: <20592.1022586929.15@example.com> + +-- +It's never too late to have a happy childhood. + +------- =_aaaaaaaaaa0-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_39.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_39.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,83 @@ +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" + +------- =_aaaaaaaaaa0 +Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa1" +Content-ID: <20592.1022586929.1@example.com> + +------- =_aaaaaaaaaa1 +Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1" +Content-ID: <20592.1022586929.2@example.com> + +------- =_aaaaaaaaaa1 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.3@example.com> +Content-Description: patch1 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa1 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.4@example.com> +Content-Description: patch2 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa1-- + +------- =_aaaaaaaaaa1 +Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1" +Content-ID: <20592.1022586929.6@example.com> + +------- =_aaaaaaaaaa1 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.7@example.com> +Content-Description: patch3 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa1 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.8@example.com> +Content-Description: patch4 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa1-- + +------- =_aaaaaaaaaa1 +Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1" +Content-ID: <20592.1022586929.10@example.com> + +------- =_aaaaaaaaaa1 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.11@example.com> +Content-Description: patch5 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa1 +Content-Type: application/octet-stream +Content-ID: <20592.1022586929.12@example.com> +Content-Description: patch6 +Content-Transfer-Encoding: base64 + +XXX + +------- =_aaaaaaaaaa1-- + +------- =_aaaaaaaaaa1-- + +------- =_aaaaaaaaaa0 +Content-Type: text/plain; charset="us-ascii" +Content-ID: <20592.1022586929.15@example.com> + +-- +It's never too late to have a happy childhood. + +------- =_aaaaaaaaaa0-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_40.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_40.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,10 @@ +MIME-Version: 1.0 +Content-Type: text/html; boundary="--961284236552522269" + +----961284236552522269 +Content-Type: text/html; +Content-Transfer-Encoding: 7Bit + + + +----961284236552522269-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_41.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_41.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,8 @@ +From: "Allison Dunlap" +To: yyy@example.com +Subject: 64423 +Date: Sun, 11 Jul 2004 16:09:27 -0300 +MIME-Version: 1.0 +Content-Type: multipart/alternative; + +Blah blah blah diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_42.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_42.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,20 @@ +Content-Type: multipart/mixed; boundary="AAA" +From: Mail Delivery Subsystem +To: yyy@example.com + +This is a MIME-encapsulated message + +--AAA + +Stuff + +--AAA +Content-Type: message/rfc822 + +From: webmaster@python.org +To: zzz@example.com +Content-Type: multipart/mixed; boundary="BBB" + +--BBB-- + +--AAA-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_43.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_43.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,217 @@ +From SRS0=aO/p=ON=bag.python.org=None@bounce2.pobox.com Fri Nov 26 21:40:36 2004 +X-VM-v5-Data: ([nil nil nil nil nil nil nil nil nil] + [nil nil nil nil nil nil nil "MAILER DAEMON <>" "MAILER DAEMON <>" nil nil "Banned file: auto__mail.python.bat in mail from you" "^From:" nil nil nil nil "Banned file: auto__mail.python.bat in mail from you" nil nil nil nil nil nil nil] + nil) +MIME-Version: 1.0 +Message-Id: +Content-Type: multipart/report; report-type=delivery-status; + charset=utf-8; + boundary="----------=_1101526904-1956-5" +X-Virus-Scanned: by XS4ALL Virus Scanner +X-UIDL: 4\G!!! +To: +Subject: Banned file: auto__mail.python.bat in mail from you +Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +This is a multi-part message in MIME format... + +------------=_1101526904-1956-5 +Content-Type: text/plain; charset="utf-8" +Content-Disposition: inline +Content-Transfer-Encoding: 7bit + +BANNED FILENAME ALERT + +Your message to: xxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxx@dot.ca.gov, xxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxx@dot.ca.gov, xxxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxx@dot.ca.gov, xxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxx@dot.ca.gov, xxxx@dot.ca.gov, xxxxxxxx@dot.ca.gov, xxxxxxxxxx@dot.ca.gov, xxxxxxxxxxxxxxxxxx@dot.ca.gov +was blocked by our Spam Firewall. The email you sent with the following subject has NOT BEEN DELIVERED: + +Subject: Delivery_failure_notice + +An attachment in that mail was of a file type that the Spam Firewall is set to block. + + + +------------=_1101526904-1956-5 +Content-Type: message/delivery-status +Content-Disposition: inline +Content-Transfer-Encoding: 7bit +Content-Description: Delivery error report + +Reporting-MTA: dns; sacspam01.dot.ca.gov +Received-From-MTA: smtp; sacspam01.dot.ca.gov ([127.0.0.1]) +Arrival-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +Final-Recipient: rfc822; xxxxxxx@dot.ca.gov +Action: failed +Status: 5.7.1 +Diagnostic-Code: smtp; 550 5.7.1 Message content rejected, id=01956-02-2 - BANNED: auto__mail.python.bat +Last-Attempt-Date: Fri, 26 Nov 2004 19:41:44 -0800 (PST) + +------------=_1101526904-1956-5 +Content-Type: text/rfc822-headers +Content-Disposition: inline +Content-Transfer-Encoding: 7bit +Content-Description: Undelivered-message headers + +Received: from kgsav.org (ppp-70-242-162-63.dsl.spfdmo.swbell.net [70.242.162.63]) + by sacspam01.dot.ca.gov (Spam Firewall) with SMTP + id A232AD03DE3A; Fri, 26 Nov 2004 19:41:35 -0800 (PST) +From: webmaster@python.org +To: xxxxx@dot.ca.gov +Date: Sat, 27 Nov 2004 03:35:30 UTC +Subject: Delivery_failure_notice +Importance: Normal +X-Priority: 3 (Normal) +X-MSMail-Priority: Normal +Message-ID: +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="====67bd2b7a5.f99f7" +Content-Transfer-Encoding: 7bit + +------------=_1101526904-1956-5-- + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_44.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_44.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,33 @@ +Return-Path: +Delivered-To: barry@python.org +Received: by mail.python.org (Postfix, from userid 889) + id C2BF0D37C6; Tue, 11 Sep 2001 00:05:05 -0400 (EDT) +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary="h90VIIIKmx" +Content-Transfer-Encoding: 7bit +Message-ID: <15261.36209.358846.118674@anthem.python.org> +From: barry@python.org (Barry A. Warsaw) +To: barry@python.org +Subject: a simple multipart +Date: Tue, 11 Sep 2001 00:05:05 -0400 +X-Mailer: VM 6.95 under 21.4 (patch 4) "Artificial Intelligence" XEmacs Lucid +X-Attribution: BAW +X-Oblique-Strategy: Make a door into a window + + +--h90VIIIKmx +Content-Type: text/plain; name="msg.txt" +Content-Transfer-Encoding: 7bit + +a simple kind of mirror +to reflect upon our own + +--h90VIIIKmx +Content-Type: text/plain; name="msg.txt" +Content-Transfer-Encoding: 7bit + +a simple kind of mirror +to reflect upon our own + +--h90VIIIKmx-- + diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_45.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_45.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,33 @@ +From: +To: +Subject: test +X-Long-Line: Some really long line contains a lot of text and thus has to be rewrapped because it is some + really long + line +MIME-Version: 1.0 +Content-Type: multipart/signed; boundary="borderline"; + protocol="application/pgp-signature"; micalg=pgp-sha1 + +This is an OpenPGP/MIME signed message (RFC 2440 and 3156) +--borderline +Content-Type: text/plain +X-Long-Line: Another really long line contains a lot of text and thus has to be rewrapped because it is another + really long + line + +This is the signed contents. + +--borderline +Content-Type: application/pgp-signature; name="signature.asc" +Content-Description: OpenPGP digital signature +Content-Disposition: attachment; filename="signature.asc" + +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v2.0.6 (GNU/Linux) + +iD8DBQFG03voRhp6o4m9dFsRApSZAKCCAN3IkJlVRg6NvAiMHlvvIuMGPQCeLZtj +FGwfnRHFBFO/S4/DKysm0lI= +=t7+s +-----END PGP SIGNATURE----- + +--borderline-- diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/data/msg_46.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/data/msg_46.txt Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,23 @@ +Return-Path: +Delivery-Date: Mon, 08 Feb 2010 14:05:16 +0100 +Received: from example.org (example.org [64.5.53.58]) + by example.net (node=mxbap2) with ESMTP (Nemesis) + id UNIQUE for someone@example.com; Mon, 08 Feb 2010 14:05:16 +0100 +Date: Mon, 01 Feb 2010 12:21:16 +0100 +From: "Sender" +To: +Subject: GroupwiseForwardingTest +Mime-Version: 1.0 +Content-Type: message/rfc822 + +Return-path: +Message-ID: <4B66B890.4070408@teconcept.de> +Date: Mon, 01 Feb 2010 12:18:40 +0100 +From: "Dr. Sender" +MIME-Version: 1.0 +To: "Recipient" +Subject: GroupwiseForwardingTest +Content-Type: text/plain; charset=ISO-8859-15 +Content-Transfer-Encoding: 7bit + +Testing email forwarding with Groupwise 1.2.2010 diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/test_asian_codecs.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/test_asian_codecs.py Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,82 @@ +# Copyright (C) 2002-2006 Python Software Foundation +# Contact: email-sig@python.org +# email package unit tests for (optional) Asian codecs + +import unittest +from test.support import run_unittest + +from test.test_email.test_email import TestEmailBase +from email.charset import Charset +from email.header import Header, decode_header +from email.message import Message + +# We're compatible with Python 2.3, but it doesn't have the built-in Asian +# codecs, so we have to skip all these tests. +try: + str(b'foo', 'euc-jp') +except LookupError: + raise unittest.SkipTest + + + +class TestEmailAsianCodecs(TestEmailBase): + def test_japanese_codecs(self): + eq = self.ndiffAssertEqual + jcode = "euc-jp" + gcode = "iso-8859-1" + j = Charset(jcode) + g = Charset(gcode) + h = Header("Hello World!") + jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc' + b'\xa5\xeb\xa5\xc9\xa1\xaa', jcode) + ghello = str(b'Gr\xfc\xdf Gott!', gcode) + h.append(jhello, j) + h.append(ghello, g) + # BAW: This used to -- and maybe should -- fold the two iso-8859-1 + # chunks into a single encoded word. However it doesn't violate the + # standard to have them as two encoded chunks and maybe it's + # reasonable for each .append() call to result in a separate + # encoded word. + eq(h.encode(), """\ +Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?= + =?iso-8859-1?q?Gr=FC=DF_Gott!?=""") + eq(decode_header(h.encode()), + [(b'Hello World!', None), + (b'\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'), + (b'Gr\xfc\xdf Gott!', gcode)]) + subject_bytes = (b'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5' + b'\xa4\xec\xa4\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2' + b'\xf1\xbc\xd4\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3' + b'\xa4\xc6\xa4\xa4\xa4\xde\xa4\xb9') + subject = str(subject_bytes, jcode) + h = Header(subject, j, header_name="Subject") + # test a very long header + enc = h.encode() + # TK: splitting point may differ by codec design and/or Header encoding + eq(enc , """\ +=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKGyhC?= + =?iso-2022-jp?b?GyRCMnE8VCROPjVHJyRyQlQkQyRGJCQkXiQ5GyhC?=""") + # TK: full decode comparison + eq(str(h).encode(jcode), subject_bytes) + + def test_payload_encoding_utf8(self): + jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc' + b'\xa5\xeb\xa5\xc9\xa1\xaa', 'euc-jp') + msg = Message() + msg.set_payload(jhello, 'utf-8') + ustr = msg.get_payload(decode=True).decode(msg.get_content_charset()) + self.assertEqual(jhello, ustr) + + def test_payload_encoding(self): + jcode = 'euc-jp' + jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc' + b'\xa5\xeb\xa5\xc9\xa1\xaa', jcode) + msg = Message() + msg.set_payload(jhello, jcode) + ustr = msg.get_payload(decode=True).decode(msg.get_content_charset()) + self.assertEqual(jhello, ustr) + + + +if __name__ == '__main__': + unittest.main() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/test_email.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/test_email.py Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,4146 @@ +# Copyright (C) 2001-2010 Python Software Foundation +# Contact: email-sig@python.org +# email package unit tests + +import os +import re +import sys +import time +import base64 +import difflib +import unittest +import warnings +import textwrap + +from io import StringIO, BytesIO +from itertools import chain + +import email + +from email.charset import Charset +from email.header import Header, decode_header, make_header +from email.parser import Parser, HeaderParser +from email.generator import Generator, DecodedGenerator +from email.message import Message +from email.mime.application import MIMEApplication +from email.mime.audio import MIMEAudio +from email.mime.text import MIMEText +from email.mime.image import MIMEImage +from email.mime.base import MIMEBase +from email.mime.message import MIMEMessage +from email.mime.multipart import MIMEMultipart +from email import utils +from email import errors +from email import encoders +from email import iterators +from email import base64mime +from email import quoprimime + +from test.support import findfile, run_unittest, unlink +from test.test_email import __file__ as landmark + + +NL = '\n' +EMPTYSTRING = '' +SPACE = ' ' + + + +def openfile(filename, *args, **kws): + path = os.path.join(os.path.dirname(landmark), 'data', filename) + return open(path, *args, **kws) + + + +# Base test class +class TestEmailBase(unittest.TestCase): + def ndiffAssertEqual(self, first, second): + """Like assertEqual except use ndiff for readable output.""" + if first != second: + sfirst = str(first) + ssecond = str(second) + rfirst = [repr(line) for line in sfirst.splitlines()] + rsecond = [repr(line) for line in ssecond.splitlines()] + diff = difflib.ndiff(rfirst, rsecond) + raise self.failureException(NL + NL.join(diff)) + + def _msgobj(self, filename): + with openfile(findfile(filename)) as fp: + return email.message_from_file(fp) + + + +# Test various aspects of the Message class's API +class TestMessageAPI(TestEmailBase): + def test_get_all(self): + eq = self.assertEqual + msg = self._msgobj('msg_20.txt') + eq(msg.get_all('cc'), ['ccc@zzz.org', 'ddd@zzz.org', 'eee@zzz.org']) + eq(msg.get_all('xx', 'n/a'), 'n/a') + + def test_getset_charset(self): + eq = self.assertEqual + msg = Message() + eq(msg.get_charset(), None) + charset = Charset('iso-8859-1') + msg.set_charset(charset) + eq(msg['mime-version'], '1.0') + eq(msg.get_content_type(), 'text/plain') + eq(msg['content-type'], 'text/plain; charset="iso-8859-1"') + eq(msg.get_param('charset'), 'iso-8859-1') + eq(msg['content-transfer-encoding'], 'quoted-printable') + eq(msg.get_charset().input_charset, 'iso-8859-1') + # Remove the charset + msg.set_charset(None) + eq(msg.get_charset(), None) + eq(msg['content-type'], 'text/plain') + # Try adding a charset when there's already MIME headers present + msg = Message() + msg['MIME-Version'] = '2.0' + msg['Content-Type'] = 'text/x-weird' + msg['Content-Transfer-Encoding'] = 'quinted-puntable' + msg.set_charset(charset) + eq(msg['mime-version'], '2.0') + eq(msg['content-type'], 'text/x-weird; charset="iso-8859-1"') + eq(msg['content-transfer-encoding'], 'quinted-puntable') + + def test_set_charset_from_string(self): + eq = self.assertEqual + msg = Message() + msg.set_charset('us-ascii') + eq(msg.get_charset().input_charset, 'us-ascii') + eq(msg['content-type'], 'text/plain; charset="us-ascii"') + + def test_set_payload_with_charset(self): + msg = Message() + charset = Charset('iso-8859-1') + msg.set_payload('This is a string payload', charset) + self.assertEqual(msg.get_charset().input_charset, 'iso-8859-1') + + def test_get_charsets(self): + eq = self.assertEqual + + msg = self._msgobj('msg_08.txt') + charsets = msg.get_charsets() + eq(charsets, [None, 'us-ascii', 'iso-8859-1', 'iso-8859-2', 'koi8-r']) + + msg = self._msgobj('msg_09.txt') + charsets = msg.get_charsets('dingbat') + eq(charsets, ['dingbat', 'us-ascii', 'iso-8859-1', 'dingbat', + 'koi8-r']) + + msg = self._msgobj('msg_12.txt') + charsets = msg.get_charsets() + eq(charsets, [None, 'us-ascii', 'iso-8859-1', None, 'iso-8859-2', + 'iso-8859-3', 'us-ascii', 'koi8-r']) + + def test_get_filename(self): + eq = self.assertEqual + + msg = self._msgobj('msg_04.txt') + filenames = [p.get_filename() for p in msg.get_payload()] + eq(filenames, ['msg.txt', 'msg.txt']) + + msg = self._msgobj('msg_07.txt') + subpart = msg.get_payload(1) + eq(subpart.get_filename(), 'dingusfish.gif') + + def test_get_filename_with_name_parameter(self): + eq = self.assertEqual + + msg = self._msgobj('msg_44.txt') + filenames = [p.get_filename() for p in msg.get_payload()] + eq(filenames, ['msg.txt', 'msg.txt']) + + def test_get_boundary(self): + eq = self.assertEqual + msg = self._msgobj('msg_07.txt') + # No quotes! + eq(msg.get_boundary(), 'BOUNDARY') + + def test_set_boundary(self): + eq = self.assertEqual + # This one has no existing boundary parameter, but the Content-Type: + # header appears fifth. + msg = self._msgobj('msg_01.txt') + msg.set_boundary('BOUNDARY') + header, value = msg.items()[4] + eq(header.lower(), 'content-type') + eq(value, 'text/plain; charset="us-ascii"; boundary="BOUNDARY"') + # This one has a Content-Type: header, with a boundary, stuck in the + # middle of its headers. Make sure the order is preserved; it should + # be fifth. + msg = self._msgobj('msg_04.txt') + msg.set_boundary('BOUNDARY') + header, value = msg.items()[4] + eq(header.lower(), 'content-type') + eq(value, 'multipart/mixed; boundary="BOUNDARY"') + # And this one has no Content-Type: header at all. + msg = self._msgobj('msg_03.txt') + self.assertRaises(errors.HeaderParseError, + msg.set_boundary, 'BOUNDARY') + + def test_make_boundary(self): + msg = MIMEMultipart('form-data') + # Note that when the boundary gets created is an implementation + # detail and might change. + self.assertEqual(msg.items()[0][1], 'multipart/form-data') + # Trigger creation of boundary + msg.as_string() + self.assertEqual(msg.items()[0][1][:33], + 'multipart/form-data; boundary="==') + # XXX: there ought to be tests of the uniqueness of the boundary, too. + + def test_message_rfc822_only(self): + # Issue 7970: message/rfc822 not in multipart parsed by + # HeaderParser caused an exception when flattened. + with openfile(findfile('msg_46.txt')) as fp: + msgdata = fp.read() + parser = HeaderParser() + msg = parser.parsestr(msgdata) + out = StringIO() + gen = Generator(out, True, 0) + gen.flatten(msg, False) + self.assertEqual(out.getvalue(), msgdata) + + def test_get_decoded_payload(self): + eq = self.assertEqual + msg = self._msgobj('msg_10.txt') + # The outer message is a multipart + eq(msg.get_payload(decode=True), None) + # Subpart 1 is 7bit encoded + eq(msg.get_payload(0).get_payload(decode=True), + b'This is a 7bit encoded message.\n') + # Subpart 2 is quopri + eq(msg.get_payload(1).get_payload(decode=True), + b'\xa1This is a Quoted Printable encoded message!\n') + # Subpart 3 is base64 + eq(msg.get_payload(2).get_payload(decode=True), + b'This is a Base64 encoded message.') + # Subpart 4 is base64 with a trailing newline, which + # used to be stripped (issue 7143). + eq(msg.get_payload(3).get_payload(decode=True), + b'This is a Base64 encoded message.\n') + # Subpart 5 has no Content-Transfer-Encoding: header. + eq(msg.get_payload(4).get_payload(decode=True), + b'This has no Content-Transfer-Encoding: header.\n') + + def test_get_decoded_uu_payload(self): + eq = self.assertEqual + msg = Message() + msg.set_payload('begin 666 -\n+:&5L;&\\@=V]R;&0 \n \nend\n') + for cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): + msg['content-transfer-encoding'] = cte + eq(msg.get_payload(decode=True), b'hello world') + # Now try some bogus data + msg.set_payload('foo') + eq(msg.get_payload(decode=True), b'foo') + + def test_decoded_generator(self): + eq = self.assertEqual + msg = self._msgobj('msg_07.txt') + with openfile('msg_17.txt') as fp: + text = fp.read() + s = StringIO() + g = DecodedGenerator(s) + g.flatten(msg) + eq(s.getvalue(), text) + + def test__contains__(self): + msg = Message() + msg['From'] = 'Me' + msg['to'] = 'You' + # Check for case insensitivity + self.assertTrue('from' in msg) + self.assertTrue('From' in msg) + self.assertTrue('FROM' in msg) + self.assertTrue('to' in msg) + self.assertTrue('To' in msg) + self.assertTrue('TO' in msg) + + def test_as_string(self): + eq = self.ndiffAssertEqual + msg = self._msgobj('msg_01.txt') + with openfile('msg_01.txt') as fp: + text = fp.read() + eq(text, str(msg)) + fullrepr = msg.as_string(unixfrom=True) + lines = fullrepr.split('\n') + self.assertTrue(lines[0].startswith('From ')) + eq(text, NL.join(lines[1:])) + + def test_bad_param(self): + msg = email.message_from_string("Content-Type: blarg; baz; boo\n") + self.assertEqual(msg.get_param('baz'), '') + + def test_missing_filename(self): + msg = email.message_from_string("From: foo\n") + self.assertEqual(msg.get_filename(), None) + + def test_bogus_filename(self): + msg = email.message_from_string( + "Content-Disposition: blarg; filename\n") + self.assertEqual(msg.get_filename(), '') + + def test_missing_boundary(self): + msg = email.message_from_string("From: foo\n") + self.assertEqual(msg.get_boundary(), None) + + def test_get_params(self): + eq = self.assertEqual + msg = email.message_from_string( + 'X-Header: foo=one; bar=two; baz=three\n') + eq(msg.get_params(header='x-header'), + [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]) + msg = email.message_from_string( + 'X-Header: foo; bar=one; baz=two\n') + eq(msg.get_params(header='x-header'), + [('foo', ''), ('bar', 'one'), ('baz', 'two')]) + eq(msg.get_params(), None) + msg = email.message_from_string( + 'X-Header: foo; bar="one"; baz=two\n') + eq(msg.get_params(header='x-header'), + [('foo', ''), ('bar', 'one'), ('baz', 'two')]) + + def test_get_param_liberal(self): + msg = Message() + msg['Content-Type'] = 'Content-Type: Multipart/mixed; boundary = "CPIMSSMTPC06p5f3tG"' + self.assertEqual(msg.get_param('boundary'), 'CPIMSSMTPC06p5f3tG') + + def test_get_param(self): + eq = self.assertEqual + msg = email.message_from_string( + "X-Header: foo=one; bar=two; baz=three\n") + eq(msg.get_param('bar', header='x-header'), 'two') + eq(msg.get_param('quuz', header='x-header'), None) + eq(msg.get_param('quuz'), None) + msg = email.message_from_string( + 'X-Header: foo; bar="one"; baz=two\n') + eq(msg.get_param('foo', header='x-header'), '') + eq(msg.get_param('bar', header='x-header'), 'one') + eq(msg.get_param('baz', header='x-header'), 'two') + # XXX: We are not RFC-2045 compliant! We cannot parse: + # msg["Content-Type"] = 'text/plain; weird="hey; dolly? [you] @ <\\"home\\">?"' + # msg.get_param("weird") + # yet. + + def test_get_param_funky_continuation_lines(self): + msg = self._msgobj('msg_22.txt') + self.assertEqual(msg.get_payload(1).get_param('name'), 'wibble.JPG') + + def test_get_param_with_semis_in_quotes(self): + msg = email.message_from_string( + 'Content-Type: image/pjpeg; name="Jim&&Jill"\n') + self.assertEqual(msg.get_param('name'), 'Jim&&Jill') + self.assertEqual(msg.get_param('name', unquote=False), + '"Jim&&Jill"') + + def test_get_param_with_quotes(self): + msg = email.message_from_string( + 'Content-Type: foo; bar*0="baz\\"foobar"; bar*1="\\"baz"') + self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz') + msg = email.message_from_string( + "Content-Type: foo; bar*0=\"baz\\\"foobar\"; bar*1=\"\\\"baz\"") + self.assertEqual(msg.get_param('bar'), 'baz"foobar"baz') + + def test_field_containment(self): + unless = self.assertTrue + msg = email.message_from_string('Header: exists') + unless('header' in msg) + unless('Header' in msg) + unless('HEADER' in msg) + self.assertFalse('headerx' in msg) + + def test_set_param(self): + eq = self.assertEqual + msg = Message() + msg.set_param('charset', 'iso-2022-jp') + eq(msg.get_param('charset'), 'iso-2022-jp') + msg.set_param('importance', 'high value') + eq(msg.get_param('importance'), 'high value') + eq(msg.get_param('importance', unquote=False), '"high value"') + eq(msg.get_params(), [('text/plain', ''), + ('charset', 'iso-2022-jp'), + ('importance', 'high value')]) + eq(msg.get_params(unquote=False), [('text/plain', ''), + ('charset', '"iso-2022-jp"'), + ('importance', '"high value"')]) + msg.set_param('charset', 'iso-9999-xx', header='X-Jimmy') + eq(msg.get_param('charset', header='X-Jimmy'), 'iso-9999-xx') + + def test_del_param(self): + eq = self.assertEqual + msg = self._msgobj('msg_05.txt') + eq(msg.get_params(), + [('multipart/report', ''), ('report-type', 'delivery-status'), + ('boundary', 'D1690A7AC1.996856090/mail.example.com')]) + old_val = msg.get_param("report-type") + msg.del_param("report-type") + eq(msg.get_params(), + [('multipart/report', ''), + ('boundary', 'D1690A7AC1.996856090/mail.example.com')]) + msg.set_param("report-type", old_val) + eq(msg.get_params(), + [('multipart/report', ''), + ('boundary', 'D1690A7AC1.996856090/mail.example.com'), + ('report-type', old_val)]) + + def test_del_param_on_other_header(self): + msg = Message() + msg.add_header('Content-Disposition', 'attachment', filename='bud.gif') + msg.del_param('filename', 'content-disposition') + self.assertEqual(msg['content-disposition'], 'attachment') + + def test_set_type(self): + eq = self.assertEqual + msg = Message() + self.assertRaises(ValueError, msg.set_type, 'text') + msg.set_type('text/plain') + eq(msg['content-type'], 'text/plain') + msg.set_param('charset', 'us-ascii') + eq(msg['content-type'], 'text/plain; charset="us-ascii"') + msg.set_type('text/html') + eq(msg['content-type'], 'text/html; charset="us-ascii"') + + def test_set_type_on_other_header(self): + msg = Message() + msg['X-Content-Type'] = 'text/plain' + msg.set_type('application/octet-stream', 'X-Content-Type') + self.assertEqual(msg['x-content-type'], 'application/octet-stream') + + def test_get_content_type_missing(self): + msg = Message() + self.assertEqual(msg.get_content_type(), 'text/plain') + + def test_get_content_type_missing_with_default_type(self): + msg = Message() + msg.set_default_type('message/rfc822') + self.assertEqual(msg.get_content_type(), 'message/rfc822') + + def test_get_content_type_from_message_implicit(self): + msg = self._msgobj('msg_30.txt') + self.assertEqual(msg.get_payload(0).get_content_type(), + 'message/rfc822') + + def test_get_content_type_from_message_explicit(self): + msg = self._msgobj('msg_28.txt') + self.assertEqual(msg.get_payload(0).get_content_type(), + 'message/rfc822') + + def test_get_content_type_from_message_text_plain_implicit(self): + msg = self._msgobj('msg_03.txt') + self.assertEqual(msg.get_content_type(), 'text/plain') + + def test_get_content_type_from_message_text_plain_explicit(self): + msg = self._msgobj('msg_01.txt') + self.assertEqual(msg.get_content_type(), 'text/plain') + + def test_get_content_maintype_missing(self): + msg = Message() + self.assertEqual(msg.get_content_maintype(), 'text') + + def test_get_content_maintype_missing_with_default_type(self): + msg = Message() + msg.set_default_type('message/rfc822') + self.assertEqual(msg.get_content_maintype(), 'message') + + def test_get_content_maintype_from_message_implicit(self): + msg = self._msgobj('msg_30.txt') + self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message') + + def test_get_content_maintype_from_message_explicit(self): + msg = self._msgobj('msg_28.txt') + self.assertEqual(msg.get_payload(0).get_content_maintype(), 'message') + + def test_get_content_maintype_from_message_text_plain_implicit(self): + msg = self._msgobj('msg_03.txt') + self.assertEqual(msg.get_content_maintype(), 'text') + + def test_get_content_maintype_from_message_text_plain_explicit(self): + msg = self._msgobj('msg_01.txt') + self.assertEqual(msg.get_content_maintype(), 'text') + + def test_get_content_subtype_missing(self): + msg = Message() + self.assertEqual(msg.get_content_subtype(), 'plain') + + def test_get_content_subtype_missing_with_default_type(self): + msg = Message() + msg.set_default_type('message/rfc822') + self.assertEqual(msg.get_content_subtype(), 'rfc822') + + def test_get_content_subtype_from_message_implicit(self): + msg = self._msgobj('msg_30.txt') + self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822') + + def test_get_content_subtype_from_message_explicit(self): + msg = self._msgobj('msg_28.txt') + self.assertEqual(msg.get_payload(0).get_content_subtype(), 'rfc822') + + def test_get_content_subtype_from_message_text_plain_implicit(self): + msg = self._msgobj('msg_03.txt') + self.assertEqual(msg.get_content_subtype(), 'plain') + + def test_get_content_subtype_from_message_text_plain_explicit(self): + msg = self._msgobj('msg_01.txt') + self.assertEqual(msg.get_content_subtype(), 'plain') + + def test_get_content_maintype_error(self): + msg = Message() + msg['Content-Type'] = 'no-slash-in-this-string' + self.assertEqual(msg.get_content_maintype(), 'text') + + def test_get_content_subtype_error(self): + msg = Message() + msg['Content-Type'] = 'no-slash-in-this-string' + self.assertEqual(msg.get_content_subtype(), 'plain') + + def test_replace_header(self): + eq = self.assertEqual + msg = Message() + msg.add_header('First', 'One') + msg.add_header('Second', 'Two') + msg.add_header('Third', 'Three') + eq(msg.keys(), ['First', 'Second', 'Third']) + eq(msg.values(), ['One', 'Two', 'Three']) + msg.replace_header('Second', 'Twenty') + eq(msg.keys(), ['First', 'Second', 'Third']) + eq(msg.values(), ['One', 'Twenty', 'Three']) + msg.add_header('First', 'Eleven') + msg.replace_header('First', 'One Hundred') + eq(msg.keys(), ['First', 'Second', 'Third', 'First']) + eq(msg.values(), ['One Hundred', 'Twenty', 'Three', 'Eleven']) + self.assertRaises(KeyError, msg.replace_header, 'Fourth', 'Missing') + + def test_broken_base64_payload(self): + x = 'AwDp0P7//y6LwKEAcPa/6Q=9' + msg = Message() + msg['content-type'] = 'audio/x-midi' + msg['content-transfer-encoding'] = 'base64' + msg.set_payload(x) + self.assertEqual(msg.get_payload(decode=True), + bytes(x, 'raw-unicode-escape')) + + # Issue 1078919 + def test_ascii_add_header(self): + msg = Message() + msg.add_header('Content-Disposition', 'attachment', + filename='bud.gif') + self.assertEqual('attachment; filename="bud.gif"', + msg['Content-Disposition']) + + def test_noascii_add_header(self): + msg = Message() + msg.add_header('Content-Disposition', 'attachment', + filename="Fußballer.ppt") + self.assertEqual( + 'attachment; filename*=utf-8\'\'Fu%C3%9Fballer.ppt', + msg['Content-Disposition']) + + def test_nonascii_add_header_via_triple(self): + msg = Message() + msg.add_header('Content-Disposition', 'attachment', + filename=('iso-8859-1', '', 'Fußballer.ppt')) + self.assertEqual( + 'attachment; filename*=iso-8859-1\'\'Fu%DFballer.ppt', + msg['Content-Disposition']) + + def test_ascii_add_header_with_tspecial(self): + msg = Message() + msg.add_header('Content-Disposition', 'attachment', + filename="windows [filename].ppt") + self.assertEqual( + 'attachment; filename="windows [filename].ppt"', + msg['Content-Disposition']) + + def test_nonascii_add_header_with_tspecial(self): + msg = Message() + msg.add_header('Content-Disposition', 'attachment', + filename="Fußballer [filename].ppt") + self.assertEqual( + "attachment; filename*=utf-8''Fu%C3%9Fballer%20%5Bfilename%5D.ppt", + msg['Content-Disposition']) + + # Issue 5871: reject an attempt to embed a header inside a header value + # (header injection attack). + def test_embeded_header_via_Header_rejected(self): + msg = Message() + msg['Dummy'] = Header('dummy\nX-Injected-Header: test') + self.assertRaises(errors.HeaderParseError, msg.as_string) + + def test_embeded_header_via_string_rejected(self): + msg = Message() + msg['Dummy'] = 'dummy\nX-Injected-Header: test' + self.assertRaises(errors.HeaderParseError, msg.as_string) + +# Test the email.encoders module +class TestEncoders(unittest.TestCase): + + def test_EncodersEncode_base64(self): + with openfile('PyBanner048.gif', 'rb') as fp: + bindata = fp.read() + mimed = email.mime.image.MIMEImage(bindata) + base64ed = mimed.get_payload() + # the transfer-encoded body lines should all be <=76 characters + lines = base64ed.split('\n') + self.assertLessEqual(max([ len(x) for x in lines ]), 76) + + def test_encode_empty_payload(self): + eq = self.assertEqual + msg = Message() + msg.set_charset('us-ascii') + eq(msg['content-transfer-encoding'], '7bit') + + def test_default_cte(self): + eq = self.assertEqual + # 7bit data and the default us-ascii _charset + msg = MIMEText('hello world') + eq(msg['content-transfer-encoding'], '7bit') + # Similar, but with 8bit data + msg = MIMEText('hello \xf8 world') + eq(msg['content-transfer-encoding'], '8bit') + # And now with a different charset + msg = MIMEText('hello \xf8 world', _charset='iso-8859-1') + eq(msg['content-transfer-encoding'], 'quoted-printable') + + def test_encode7or8bit(self): + # Make sure a charset whose input character set is 8bit but + # whose output character set is 7bit gets a transfer-encoding + # of 7bit. + eq = self.assertEqual + msg = MIMEText('文', _charset='euc-jp') + eq(msg['content-transfer-encoding'], '7bit') + + +# Test long header wrapping +class TestLongHeaders(TestEmailBase): + def test_split_long_continuation(self): + eq = self.ndiffAssertEqual + msg = email.message_from_string("""\ +Subject: bug demonstration +\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 +\tmore text + +test +""") + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), """\ +Subject: bug demonstration +\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 +\tmore text + +test +""") + + def test_another_long_almost_unsplittable_header(self): + eq = self.ndiffAssertEqual + hstr = """\ +bug demonstration +\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 +\tmore text""" + h = Header(hstr, continuation_ws='\t') + eq(h.encode(), """\ +bug demonstration +\t12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 +\tmore text""") + h = Header(hstr.replace('\t', ' ')) + eq(h.encode(), """\ +bug demonstration + 12345678911234567892123456789312345678941234567895123456789612345678971234567898112345678911234567892123456789112345678911234567892123456789 + more text""") + + def test_long_nonstring(self): + eq = self.ndiffAssertEqual + g = Charset("iso-8859-1") + cz = Charset("iso-8859-2") + utf8 = Charset("utf-8") + g_head = (b'Die Mieter treten hier ein werden mit einem Foerderband ' + b'komfortabel den Korridor entlang, an s\xfcdl\xfcndischen ' + b'Wandgem\xe4lden vorbei, gegen die rotierenden Klingen ' + b'bef\xf6rdert. ') + cz_head = (b'Finan\xe8ni metropole se hroutily pod tlakem jejich ' + b'd\xf9vtipu.. ') + utf8_head = ('\u6b63\u78ba\u306b\u8a00\u3046\u3068\u7ffb\u8a33\u306f' + '\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u4e00' + '\u90e8\u306f\u30c9\u30a4\u30c4\u8a9e\u3067\u3059\u304c' + '\u3001\u3042\u3068\u306f\u3067\u305f\u3089\u3081\u3067' + '\u3059\u3002\u5b9f\u969b\u306b\u306f\u300cWenn ist das ' + 'Nunstuck git und Slotermeyer? Ja! Beiherhund das Oder ' + 'die Flipperwaldt gersput.\u300d\u3068\u8a00\u3063\u3066' + '\u3044\u307e\u3059\u3002') + h = Header(g_head, g, header_name='Subject') + h.append(cz_head, cz) + h.append(utf8_head, utf8) + msg = Message() + msg['Subject'] = h + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), """\ +Subject: =?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerderb?= + =?iso-8859-1?q?and_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndischen?= + =?iso-8859-1?q?_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Klingen_bef?= + =?iso-8859-1?q?=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se_hrouti?= + =?iso-8859-2?q?ly_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= =?utf-8?b?5q2j56K6?= + =?utf-8?b?44Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb44KT44CC5LiA?= + =?utf-8?b?6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go44Gv44Gn44Gf44KJ?= + =?utf-8?b?44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBpc3QgZGFzIE51bnN0dWNr?= + =?utf-8?b?IGdpdCB1bmQgU2xvdGVybWV5ZXI/IEphISBCZWloZXJodW5kIGRhcyBPZGVyIGRp?= + =?utf-8?b?ZSBGbGlwcGVyd2FsZHQgZ2Vyc3B1dC7jgI3jgajoqIDjgaPjgabjgYTjgb7jgZk=?= + =?utf-8?b?44CC?= + +""") + eq(h.encode(maxlinelen=76), """\ +=?iso-8859-1?q?Die_Mieter_treten_hier_ein_werden_mit_einem_Foerde?= + =?iso-8859-1?q?rband_komfortabel_den_Korridor_entlang=2C_an_s=FCdl=FCndis?= + =?iso-8859-1?q?chen_Wandgem=E4lden_vorbei=2C_gegen_die_rotierenden_Klinge?= + =?iso-8859-1?q?n_bef=F6rdert=2E_?= =?iso-8859-2?q?Finan=E8ni_metropole_se?= + =?iso-8859-2?q?_hroutily_pod_tlakem_jejich_d=F9vtipu=2E=2E_?= + =?utf-8?b?5q2j56K644Gr6KiA44GG44Go57+76Kiz44Gv44GV44KM44Gm44GE44G+44Gb?= + =?utf-8?b?44KT44CC5LiA6YOo44Gv44OJ44Kk44OE6Kqe44Gn44GZ44GM44CB44GC44Go?= + =?utf-8?b?44Gv44Gn44Gf44KJ44KB44Gn44GZ44CC5a6f6Zqb44Gr44Gv44CMV2VubiBp?= + =?utf-8?b?c3QgZGFzIE51bnN0dWNrIGdpdCB1bmQgU2xvdGVybWV5ZXI/IEphISBCZWlo?= + =?utf-8?b?ZXJodW5kIGRhcyBPZGVyIGRpZSBGbGlwcGVyd2FsZHQgZ2Vyc3B1dC7jgI0=?= + =?utf-8?b?44Go6KiA44Gj44Gm44GE44G+44GZ44CC?=""") + + def test_long_header_encode(self): + eq = self.ndiffAssertEqual + h = Header('wasnipoop; giraffes="very-long-necked-animals"; ' + 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', + header_name='X-Foobar-Spoink-Defrobnit') + eq(h.encode(), '''\ +wasnipoop; giraffes="very-long-necked-animals"; + spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') + + def test_long_header_encode_with_tab_continuation_is_just_a_hint(self): + eq = self.ndiffAssertEqual + h = Header('wasnipoop; giraffes="very-long-necked-animals"; ' + 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', + header_name='X-Foobar-Spoink-Defrobnit', + continuation_ws='\t') + eq(h.encode(), '''\ +wasnipoop; giraffes="very-long-necked-animals"; + spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') + + def test_long_header_encode_with_tab_continuation(self): + eq = self.ndiffAssertEqual + h = Header('wasnipoop; giraffes="very-long-necked-animals";\t' + 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"', + header_name='X-Foobar-Spoink-Defrobnit', + continuation_ws='\t') + eq(h.encode(), '''\ +wasnipoop; giraffes="very-long-necked-animals"; +\tspooge="yummy"; hippos="gargantuan"; marshmallows="gooey"''') + + def test_header_encode_with_different_output_charset(self): + h = Header('文', 'euc-jp') + self.assertEqual(h.encode(), "=?iso-2022-jp?b?GyRCSjgbKEI=?=") + + def test_long_header_encode_with_different_output_charset(self): + h = Header(b'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5\xa4\xec\xa4' + b'\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2\xf1\xbc\xd4' + b'\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3\xa4\xc6\xa4' + b'\xa4\xa4\xde\xa4\xb9'.decode('euc-jp'), 'euc-jp') + res = """\ +=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKMnE8VCROPjUbKEI=?= + =?iso-2022-jp?b?GyRCRyckckJUJEMkRiQkJF4kORsoQg==?=""" + self.assertEqual(h.encode(), res) + + def test_header_splitter(self): + eq = self.ndiffAssertEqual + msg = MIMEText('') + # It'd be great if we could use add_header() here, but that doesn't + # guarantee an order of the parameters. + msg['X-Foobar-Spoink-Defrobnit'] = ( + 'wasnipoop; giraffes="very-long-necked-animals"; ' + 'spooge="yummy"; hippos="gargantuan"; marshmallows="gooey"') + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), '''\ +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +X-Foobar-Spoink-Defrobnit: wasnipoop; giraffes="very-long-necked-animals"; + spooge="yummy"; hippos="gargantuan"; marshmallows="gooey" + +''') + + def test_no_semis_header_splitter(self): + eq = self.ndiffAssertEqual + msg = Message() + msg['From'] = 'test@dom.ain' + msg['References'] = SPACE.join('<%d@dom.ain>' % i for i in range(10)) + msg.set_payload('Test') + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), """\ +From: test@dom.ain +References: <0@dom.ain> <1@dom.ain> <2@dom.ain> <3@dom.ain> <4@dom.ain> + <5@dom.ain> <6@dom.ain> <7@dom.ain> <8@dom.ain> <9@dom.ain> + +Test""") + + def test_no_split_long_header(self): + eq = self.ndiffAssertEqual + hstr = 'References: ' + 'x' * 80 + h = Header(hstr) + # These come on two lines because Headers are really field value + # classes and don't really know about their field names. + eq(h.encode(), """\ +References: + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""") + h = Header('x' * 80) + eq(h.encode(), 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx') + + def test_splitting_multiple_long_lines(self): + eq = self.ndiffAssertEqual + hstr = """\ +from babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for ; Sat, 2 Feb 2002 17:00:06 -0800 (PST) +\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for ; Sat, 2 Feb 2002 17:00:06 -0800 (PST) +\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; for ; Sat, 2 Feb 2002 17:00:06 -0800 (PST) +""" + h = Header(hstr, continuation_ws='\t') + eq(h.encode(), """\ +from babylon.socal-raves.org (localhost [127.0.0.1]); + by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; + for ; + Sat, 2 Feb 2002 17:00:06 -0800 (PST) +\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); + by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; + for ; + Sat, 2 Feb 2002 17:00:06 -0800 (PST) +\tfrom babylon.socal-raves.org (localhost [127.0.0.1]); + by babylon.socal-raves.org (Postfix) with ESMTP id B570E51B81; + for ; + Sat, 2 Feb 2002 17:00:06 -0800 (PST)""") + + def test_splitting_first_line_only_is_long(self): + eq = self.ndiffAssertEqual + hstr = """\ +from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] helo=cthulhu.gerg.ca) +\tby kronos.mems-exchange.org with esmtp (Exim 4.05) +\tid 17k4h5-00034i-00 +\tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""" + h = Header(hstr, maxlinelen=78, header_name='Received', + continuation_ws='\t') + eq(h.encode(), """\ +from modemcable093.139-201-24.que.mc.videotron.ca ([24.201.139.93] + helo=cthulhu.gerg.ca) +\tby kronos.mems-exchange.org with esmtp (Exim 4.05) +\tid 17k4h5-00034i-00 +\tfor test@mems-exchange.org; Wed, 28 Aug 2002 11:25:20 -0400""") + + def test_long_8bit_header(self): + eq = self.ndiffAssertEqual + msg = Message() + h = Header('Britische Regierung gibt', 'iso-8859-1', + header_name='Subject') + h.append('gr\xfcnes Licht f\xfcr Offshore-Windkraftprojekte') + eq(h.encode(maxlinelen=76), """\ +=?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offs?= + =?iso-8859-1?q?hore-Windkraftprojekte?=""") + msg['Subject'] = h + eq(msg.as_string(maxheaderlen=76), """\ +Subject: =?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offs?= + =?iso-8859-1?q?hore-Windkraftprojekte?= + +""") + eq(msg.as_string(maxheaderlen=0), """\ +Subject: =?iso-8859-1?q?Britische_Regierung_gibt_gr=FCnes_Licht_f=FCr_Offshore-Windkraftprojekte?= + +""") + + def test_long_8bit_header_no_charset(self): + eq = self.ndiffAssertEqual + msg = Message() + header_string = ('Britische Regierung gibt gr\xfcnes Licht ' + 'f\xfcr Offshore-Windkraftprojekte ' + '') + msg['Reply-To'] = header_string + self.assertRaises(UnicodeEncodeError, msg.as_string) + msg = Message() + msg['Reply-To'] = Header(header_string, 'utf-8', + header_name='Reply-To') + eq(msg.as_string(maxheaderlen=78), """\ +Reply-To: =?utf-8?q?Britische_Regierung_gibt_gr=C3=BCnes_Licht_f=C3=BCr_Offs?= + =?utf-8?q?hore-Windkraftprojekte_=3Ca-very-long-address=40example=2Ecom=3E?= + +""") + + def test_long_to_header(self): + eq = self.ndiffAssertEqual + to = ('"Someone Test #A" ,' + ',' + '"Someone Test #B" , ' + '"Someone Test #C" , ' + '"Someone Test #D" ') + msg = Message() + msg['To'] = to + eq(msg.as_string(maxheaderlen=78), '''\ +To: "Someone Test #A" ,, + "Someone Test #B" , + "Someone Test #C" , + "Someone Test #D" + +''') + + def test_long_line_after_append(self): + eq = self.ndiffAssertEqual + s = 'This is an example of string which has almost the limit of header length.' + h = Header(s) + h.append('Add another line.') + eq(h.encode(maxlinelen=76), """\ +This is an example of string which has almost the limit of header length. + Add another line.""") + + def test_shorter_line_with_append(self): + eq = self.ndiffAssertEqual + s = 'This is a shorter line.' + h = Header(s) + h.append('Add another sentence. (Surprise?)') + eq(h.encode(), + 'This is a shorter line. Add another sentence. (Surprise?)') + + def test_long_field_name(self): + eq = self.ndiffAssertEqual + fn = 'X-Very-Very-Very-Long-Header-Name' + gs = ('Die Mieter treten hier ein werden mit einem Foerderband ' + 'komfortabel den Korridor entlang, an s\xfcdl\xfcndischen ' + 'Wandgem\xe4lden vorbei, gegen die rotierenden Klingen ' + 'bef\xf6rdert. ') + h = Header(gs, 'iso-8859-1', header_name=fn) + # BAW: this seems broken because the first line is too long + eq(h.encode(maxlinelen=76), """\ +=?iso-8859-1?q?Die_Mieter_treten_hier_e?= + =?iso-8859-1?q?in_werden_mit_einem_Foerderband_komfortabel_den_Korridor_e?= + =?iso-8859-1?q?ntlang=2C_an_s=FCdl=FCndischen_Wandgem=E4lden_vorbei=2C_ge?= + =?iso-8859-1?q?gen_die_rotierenden_Klingen_bef=F6rdert=2E_?=""") + + def test_long_received_header(self): + h = ('from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) ' + 'by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; ' + 'Wed, 05 Mar 2003 18:10:18 -0700') + msg = Message() + msg['Received-1'] = Header(h, continuation_ws='\t') + msg['Received-2'] = h + # This should be splitting on spaces not semicolons. + self.ndiffAssertEqual(msg.as_string(maxheaderlen=78), """\ +Received-1: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; + Wed, 05 Mar 2003 18:10:18 -0700 +Received-2: from FOO.TLD (vizworld.acl.foo.tld [123.452.678.9]) by hrothgar.la.mastaler.com (tmda-ofmipd) with ESMTP; + Wed, 05 Mar 2003 18:10:18 -0700 + +""") + + def test_string_headerinst_eq(self): + h = ('<15975.17901.207240.414604@sgigritzmann1.mathematik.' + 'tu-muenchen.de> (David Bremner\'s message of ' + '"Thu, 6 Mar 2003 13:58:21 +0100")') + msg = Message() + msg['Received-1'] = Header(h, header_name='Received-1', + continuation_ws='\t') + msg['Received-2'] = h + # XXX This should be splitting on spaces not commas. + self.ndiffAssertEqual(msg.as_string(maxheaderlen=78), """\ +Received-1: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David Bremner's message of \"Thu, + 6 Mar 2003 13:58:21 +0100\") +Received-2: <15975.17901.207240.414604@sgigritzmann1.mathematik.tu-muenchen.de> (David Bremner's message of \"Thu, + 6 Mar 2003 13:58:21 +0100\") + +""") + + def test_long_unbreakable_lines_with_continuation(self): + eq = self.ndiffAssertEqual + msg = Message() + t = """\ +iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 + locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp""" + msg['Face-1'] = t + msg['Face-2'] = Header(t, header_name='Face-2') + # XXX This splitting is all wrong. It the first value line should be + # snug against the field name. + eq(msg.as_string(maxheaderlen=78), """\ +Face-1:\x20 + iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 + locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp +Face-2:\x20 + iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAGFBMVEUAAAAkHiJeRUIcGBi9 + locQDQ4zJykFBAXJfWDjAAACYUlEQVR4nF2TQY/jIAyFc6lydlG5x8Nyp1Y69wj1PN2I5gzp + +""") + + def test_another_long_multiline_header(self): + eq = self.ndiffAssertEqual + m = ('Received: from siimage.com ' + '([172.25.1.3]) by zima.siliconimage.com with ' + 'Microsoft SMTPSVC(5.0.2195.4905); ' + 'Wed, 16 Oct 2002 07:41:11 -0700') + msg = email.message_from_string(m) + eq(msg.as_string(maxheaderlen=78), '''\ +Received: from siimage.com ([172.25.1.3]) by zima.siliconimage.com with Microsoft SMTPSVC(5.0.2195.4905); + Wed, 16 Oct 2002 07:41:11 -0700 + +''') + + def test_long_lines_with_different_header(self): + eq = self.ndiffAssertEqual + h = ('List-Unsubscribe: ' + ',' + ' ') + msg = Message() + msg['List'] = h + msg['List'] = Header(h, header_name='List') + eq(msg.as_string(maxheaderlen=78), """\ +List: List-Unsubscribe: , + +List: List-Unsubscribe: , + + +""") + + def test_long_rfc2047_header_with_embedded_fws(self): + h = Header(textwrap.dedent("""\ + We're going to pretend this header is in a non-ascii character set + \tto see if line wrapping with encoded words and embedded + folding white space works"""), + charset='utf-8', + header_name='Test') + self.assertEqual(h.encode()+'\n', textwrap.dedent("""\ + =?utf-8?q?We=27re_going_to_pretend_this_header_is_in_a_non-ascii_chara?= + =?utf-8?q?cter_set?= + =?utf-8?q?_to_see_if_line_wrapping_with_encoded_words_and_embedded?= + =?utf-8?q?_folding_white_space_works?=""")+'\n') + + + +# Test mangling of "From " lines in the body of a message +class TestFromMangling(unittest.TestCase): + def setUp(self): + self.msg = Message() + self.msg['From'] = 'aaa@bbb.org' + self.msg.set_payload("""\ +From the desk of A.A.A.: +Blah blah blah +""") + + def test_mangled_from(self): + s = StringIO() + g = Generator(s, mangle_from_=True) + g.flatten(self.msg) + self.assertEqual(s.getvalue(), """\ +From: aaa@bbb.org + +>From the desk of A.A.A.: +Blah blah blah +""") + + def test_dont_mangle_from(self): + s = StringIO() + g = Generator(s, mangle_from_=False) + g.flatten(self.msg) + self.assertEqual(s.getvalue(), """\ +From: aaa@bbb.org + +From the desk of A.A.A.: +Blah blah blah +""") + + + +# Test the basic MIMEAudio class +class TestMIMEAudio(unittest.TestCase): + def setUp(self): + # Make sure we pick up the audiotest.au that lives in email/test/data. + # In Python, there's an audiotest.au living in Lib/test but that isn't + # included in some binary distros that don't include the test + # package. The trailing empty string on the .join() is significant + # since findfile() will do a dirname(). + datadir = os.path.join(os.path.dirname(landmark), 'data', '') + with open(findfile('audiotest.au', datadir), 'rb') as fp: + self._audiodata = fp.read() + self._au = MIMEAudio(self._audiodata) + + def test_guess_minor_type(self): + self.assertEqual(self._au.get_content_type(), 'audio/basic') + + def test_encoding(self): + payload = self._au.get_payload() + self.assertEqual(base64.decodebytes(bytes(payload, 'ascii')), + self._audiodata) + + def test_checkSetMinor(self): + au = MIMEAudio(self._audiodata, 'fish') + self.assertEqual(au.get_content_type(), 'audio/fish') + + def test_add_header(self): + eq = self.assertEqual + unless = self.assertTrue + self._au.add_header('Content-Disposition', 'attachment', + filename='audiotest.au') + eq(self._au['content-disposition'], + 'attachment; filename="audiotest.au"') + eq(self._au.get_params(header='content-disposition'), + [('attachment', ''), ('filename', 'audiotest.au')]) + eq(self._au.get_param('filename', header='content-disposition'), + 'audiotest.au') + missing = [] + eq(self._au.get_param('attachment', header='content-disposition'), '') + unless(self._au.get_param('foo', failobj=missing, + header='content-disposition') is missing) + # Try some missing stuff + unless(self._au.get_param('foobar', missing) is missing) + unless(self._au.get_param('attachment', missing, + header='foobar') is missing) + + + +# Test the basic MIMEImage class +class TestMIMEImage(unittest.TestCase): + def setUp(self): + with openfile('PyBanner048.gif', 'rb') as fp: + self._imgdata = fp.read() + self._im = MIMEImage(self._imgdata) + + def test_guess_minor_type(self): + self.assertEqual(self._im.get_content_type(), 'image/gif') + + def test_encoding(self): + payload = self._im.get_payload() + self.assertEqual(base64.decodebytes(bytes(payload, 'ascii')), + self._imgdata) + + def test_checkSetMinor(self): + im = MIMEImage(self._imgdata, 'fish') + self.assertEqual(im.get_content_type(), 'image/fish') + + def test_add_header(self): + eq = self.assertEqual + unless = self.assertTrue + self._im.add_header('Content-Disposition', 'attachment', + filename='dingusfish.gif') + eq(self._im['content-disposition'], + 'attachment; filename="dingusfish.gif"') + eq(self._im.get_params(header='content-disposition'), + [('attachment', ''), ('filename', 'dingusfish.gif')]) + eq(self._im.get_param('filename', header='content-disposition'), + 'dingusfish.gif') + missing = [] + eq(self._im.get_param('attachment', header='content-disposition'), '') + unless(self._im.get_param('foo', failobj=missing, + header='content-disposition') is missing) + # Try some missing stuff + unless(self._im.get_param('foobar', missing) is missing) + unless(self._im.get_param('attachment', missing, + header='foobar') is missing) + + + +# Test the basic MIMEApplication class +class TestMIMEApplication(unittest.TestCase): + def test_headers(self): + eq = self.assertEqual + msg = MIMEApplication(b'\xfa\xfb\xfc\xfd\xfe\xff') + eq(msg.get_content_type(), 'application/octet-stream') + eq(msg['content-transfer-encoding'], 'base64') + + def test_body(self): + eq = self.assertEqual + bytesdata = b'\xfa\xfb\xfc\xfd\xfe\xff' + msg = MIMEApplication(bytesdata) + # whitespace in the cte encoded block is RFC-irrelevant. + eq(msg.get_payload().strip(), '+vv8/f7/') + eq(msg.get_payload(decode=True), bytesdata) + + + +# Test the basic MIMEText class +class TestMIMEText(unittest.TestCase): + def setUp(self): + self._msg = MIMEText('hello there') + + def test_types(self): + eq = self.assertEqual + unless = self.assertTrue + eq(self._msg.get_content_type(), 'text/plain') + eq(self._msg.get_param('charset'), 'us-ascii') + missing = [] + unless(self._msg.get_param('foobar', missing) is missing) + unless(self._msg.get_param('charset', missing, header='foobar') + is missing) + + def test_payload(self): + self.assertEqual(self._msg.get_payload(), 'hello there') + self.assertTrue(not self._msg.is_multipart()) + + def test_charset(self): + eq = self.assertEqual + msg = MIMEText('hello there', _charset='us-ascii') + eq(msg.get_charset().input_charset, 'us-ascii') + eq(msg['content-type'], 'text/plain; charset="us-ascii"') + + def test_7bit_input(self): + eq = self.assertEqual + msg = MIMEText('hello there', _charset='us-ascii') + eq(msg.get_charset().input_charset, 'us-ascii') + eq(msg['content-type'], 'text/plain; charset="us-ascii"') + + def test_7bit_input_no_charset(self): + eq = self.assertEqual + msg = MIMEText('hello there') + eq(msg.get_charset(), 'us-ascii') + eq(msg['content-type'], 'text/plain; charset="us-ascii"') + self.assertTrue('hello there' in msg.as_string()) + + def test_utf8_input(self): + teststr = '\u043a\u0438\u0440\u0438\u043b\u0438\u0446\u0430' + eq = self.assertEqual + msg = MIMEText(teststr, _charset='utf-8') + eq(msg.get_charset().output_charset, 'utf-8') + eq(msg['content-type'], 'text/plain; charset="utf-8"') + eq(msg.get_payload(decode=True), teststr.encode('utf-8')) + + @unittest.skip("can't fix because of backward compat in email5, " + "will fix in email6") + def test_utf8_input_no_charset(self): + teststr = '\u043a\u0438\u0440\u0438\u043b\u0438\u0446\u0430' + self.assertRaises(UnicodeEncodeError, MIMEText, teststr) + + + +# Test complicated multipart/* messages +class TestMultipart(TestEmailBase): + def setUp(self): + with openfile('PyBanner048.gif', 'rb') as fp: + data = fp.read() + container = MIMEBase('multipart', 'mixed', boundary='BOUNDARY') + image = MIMEImage(data, name='dingusfish.gif') + image.add_header('content-disposition', 'attachment', + filename='dingusfish.gif') + intro = MIMEText('''\ +Hi there, + +This is the dingus fish. +''') + container.attach(intro) + container.attach(image) + container['From'] = 'Barry ' + container['To'] = 'Dingus Lovers ' + container['Subject'] = 'Here is your dingus fish' + + now = 987809702.54848599 + timetuple = time.localtime(now) + if timetuple[-1] == 0: + tzsecs = time.timezone + else: + tzsecs = time.altzone + if tzsecs > 0: + sign = '-' + else: + sign = '+' + tzoffset = ' %s%04d' % (sign, tzsecs / 36) + container['Date'] = time.strftime( + '%a, %d %b %Y %H:%M:%S', + time.localtime(now)) + tzoffset + self._msg = container + self._im = image + self._txt = intro + + def test_hierarchy(self): + # convenience + eq = self.assertEqual + unless = self.assertTrue + raises = self.assertRaises + # tests + m = self._msg + unless(m.is_multipart()) + eq(m.get_content_type(), 'multipart/mixed') + eq(len(m.get_payload()), 2) + raises(IndexError, m.get_payload, 2) + m0 = m.get_payload(0) + m1 = m.get_payload(1) + unless(m0 is self._txt) + unless(m1 is self._im) + eq(m.get_payload(), [m0, m1]) + unless(not m0.is_multipart()) + unless(not m1.is_multipart()) + + def test_empty_multipart_idempotent(self): + text = """\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + + +--BOUNDARY + + +--BOUNDARY-- +""" + msg = Parser().parsestr(text) + self.ndiffAssertEqual(text, msg.as_string()) + + def test_no_parts_in_a_multipart_with_none_epilogue(self): + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.set_boundary('BOUNDARY') + self.ndiffAssertEqual(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY + +--BOUNDARY--''') + + def test_no_parts_in_a_multipart_with_empty_epilogue(self): + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.preamble = '' + outer.epilogue = '' + outer.set_boundary('BOUNDARY') + self.ndiffAssertEqual(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + + +--BOUNDARY + +--BOUNDARY-- +''') + + def test_one_part_in_a_multipart(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.set_boundary('BOUNDARY') + msg = MIMEText('hello world') + outer.attach(msg) + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY--''') + + def test_seq_parts_in_a_multipart_with_empty_preamble(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.preamble = '' + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY--''') + + + def test_seq_parts_in_a_multipart_with_none_preamble(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.preamble = None + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY--''') + + + def test_seq_parts_in_a_multipart_with_none_epilogue(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.epilogue = None + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY--''') + + + def test_seq_parts_in_a_multipart_with_empty_epilogue(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.epilogue = '' + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY-- +''') + + + def test_seq_parts_in_a_multipart_with_nl_epilogue(self): + eq = self.ndiffAssertEqual + outer = MIMEBase('multipart', 'mixed') + outer['Subject'] = 'A subject' + outer['To'] = 'aperson@dom.ain' + outer['From'] = 'bperson@dom.ain' + outer.epilogue = '\n' + msg = MIMEText('hello world') + outer.attach(msg) + outer.set_boundary('BOUNDARY') + eq(outer.as_string(), '''\ +Content-Type: multipart/mixed; boundary="BOUNDARY" +MIME-Version: 1.0 +Subject: A subject +To: aperson@dom.ain +From: bperson@dom.ain + +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +hello world +--BOUNDARY-- + +''') + + def test_message_external_body(self): + eq = self.assertEqual + msg = self._msgobj('msg_36.txt') + eq(len(msg.get_payload()), 2) + msg1 = msg.get_payload(1) + eq(msg1.get_content_type(), 'multipart/alternative') + eq(len(msg1.get_payload()), 2) + for subpart in msg1.get_payload(): + eq(subpart.get_content_type(), 'message/external-body') + eq(len(subpart.get_payload()), 1) + subsubpart = subpart.get_payload(0) + eq(subsubpart.get_content_type(), 'text/plain') + + def test_double_boundary(self): + # msg_37.txt is a multipart that contains two dash-boundary's in a + # row. Our interpretation of RFC 2046 calls for ignoring the second + # and subsequent boundaries. + msg = self._msgobj('msg_37.txt') + self.assertEqual(len(msg.get_payload()), 3) + + def test_nested_inner_contains_outer_boundary(self): + eq = self.ndiffAssertEqual + # msg_38.txt has an inner part that contains outer boundaries. My + # interpretation of RFC 2046 (based on sections 5.1 and 5.1.2) say + # these are illegal and should be interpreted as unterminated inner + # parts. + msg = self._msgobj('msg_38.txt') + sfp = StringIO() + iterators._structure(msg, sfp) + eq(sfp.getvalue(), """\ +multipart/mixed + multipart/mixed + multipart/alternative + text/plain + text/plain + text/plain + text/plain +""") + + def test_nested_with_same_boundary(self): + eq = self.ndiffAssertEqual + # msg 39.txt is similarly evil in that it's got inner parts that use + # the same boundary as outer parts. Again, I believe the way this is + # parsed is closest to the spirit of RFC 2046 + msg = self._msgobj('msg_39.txt') + sfp = StringIO() + iterators._structure(msg, sfp) + eq(sfp.getvalue(), """\ +multipart/mixed + multipart/mixed + multipart/alternative + application/octet-stream + application/octet-stream + text/plain +""") + + def test_boundary_in_non_multipart(self): + msg = self._msgobj('msg_40.txt') + self.assertEqual(msg.as_string(), '''\ +MIME-Version: 1.0 +Content-Type: text/html; boundary="--961284236552522269" + +----961284236552522269 +Content-Type: text/html; +Content-Transfer-Encoding: 7Bit + + + +----961284236552522269-- +''') + + def test_boundary_with_leading_space(self): + eq = self.assertEqual + msg = email.message_from_string('''\ +MIME-Version: 1.0 +Content-Type: multipart/mixed; boundary=" XXXX" + +-- XXXX +Content-Type: text/plain + + +-- XXXX +Content-Type: text/plain + +-- XXXX-- +''') + self.assertTrue(msg.is_multipart()) + eq(msg.get_boundary(), ' XXXX') + eq(len(msg.get_payload()), 2) + + def test_boundary_without_trailing_newline(self): + m = Parser().parsestr("""\ +Content-Type: multipart/mixed; boundary="===============0012394164==" +MIME-Version: 1.0 + +--===============0012394164== +Content-Type: image/file1.jpg +MIME-Version: 1.0 +Content-Transfer-Encoding: base64 + +YXNkZg== +--===============0012394164==--""") + self.assertEqual(m.get_payload(0).get_payload(), 'YXNkZg==') + + + +# Test some badly formatted messages +class TestNonConformant(TestEmailBase): + def test_parse_missing_minor_type(self): + eq = self.assertEqual + msg = self._msgobj('msg_14.txt') + eq(msg.get_content_type(), 'text/plain') + eq(msg.get_content_maintype(), 'text') + eq(msg.get_content_subtype(), 'plain') + + def test_same_boundary_inner_outer(self): + unless = self.assertTrue + msg = self._msgobj('msg_15.txt') + # XXX We can probably eventually do better + inner = msg.get_payload(0) + unless(hasattr(inner, 'defects')) + self.assertEqual(len(inner.defects), 1) + unless(isinstance(inner.defects[0], + errors.StartBoundaryNotFoundDefect)) + + def test_multipart_no_boundary(self): + unless = self.assertTrue + msg = self._msgobj('msg_25.txt') + unless(isinstance(msg.get_payload(), str)) + self.assertEqual(len(msg.defects), 2) + unless(isinstance(msg.defects[0], errors.NoBoundaryInMultipartDefect)) + unless(isinstance(msg.defects[1], + errors.MultipartInvariantViolationDefect)) + + def test_invalid_content_type(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + msg = Message() + # RFC 2045, $5.2 says invalid yields text/plain + msg['Content-Type'] = 'text' + eq(msg.get_content_maintype(), 'text') + eq(msg.get_content_subtype(), 'plain') + eq(msg.get_content_type(), 'text/plain') + # Clear the old value and try something /really/ invalid + del msg['content-type'] + msg['Content-Type'] = 'foo' + eq(msg.get_content_maintype(), 'text') + eq(msg.get_content_subtype(), 'plain') + eq(msg.get_content_type(), 'text/plain') + # Still, make sure that the message is idempotently generated + s = StringIO() + g = Generator(s) + g.flatten(msg) + neq(s.getvalue(), 'Content-Type: foo\n\n') + + def test_no_start_boundary(self): + eq = self.ndiffAssertEqual + msg = self._msgobj('msg_31.txt') + eq(msg.get_payload(), """\ +--BOUNDARY +Content-Type: text/plain + +message 1 + +--BOUNDARY +Content-Type: text/plain + +message 2 + +--BOUNDARY-- +""") + + def test_no_separating_blank_line(self): + eq = self.ndiffAssertEqual + msg = self._msgobj('msg_35.txt') + eq(msg.as_string(), """\ +From: aperson@dom.ain +To: bperson@dom.ain +Subject: here's something interesting + +counter to RFC 2822, there's no separating newline here +""") + + def test_lying_multipart(self): + unless = self.assertTrue + msg = self._msgobj('msg_41.txt') + unless(hasattr(msg, 'defects')) + self.assertEqual(len(msg.defects), 2) + unless(isinstance(msg.defects[0], errors.NoBoundaryInMultipartDefect)) + unless(isinstance(msg.defects[1], + errors.MultipartInvariantViolationDefect)) + + def test_missing_start_boundary(self): + outer = self._msgobj('msg_42.txt') + # The message structure is: + # + # multipart/mixed + # text/plain + # message/rfc822 + # multipart/mixed [*] + # + # [*] This message is missing its start boundary + bad = outer.get_payload(1).get_payload(0) + self.assertEqual(len(bad.defects), 1) + self.assertTrue(isinstance(bad.defects[0], + errors.StartBoundaryNotFoundDefect)) + + def test_first_line_is_continuation_header(self): + eq = self.assertEqual + m = ' Line 1\nLine 2\nLine 3' + msg = email.message_from_string(m) + eq(msg.keys(), []) + eq(msg.get_payload(), 'Line 2\nLine 3') + eq(len(msg.defects), 1) + self.assertTrue(isinstance(msg.defects[0], + errors.FirstHeaderLineIsContinuationDefect)) + eq(msg.defects[0].line, ' Line 1\n') + + + +# Test RFC 2047 header encoding and decoding +class TestRFC2047(TestEmailBase): + def test_rfc2047_multiline(self): + eq = self.assertEqual + s = """Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz + foo bar =?mac-iceland?q?r=8Aksm=9Arg=8Cs?=""" + dh = decode_header(s) + eq(dh, [ + (b'Re:', None), + (b'r\x8aksm\x9arg\x8cs', 'mac-iceland'), + (b'baz foo bar', None), + (b'r\x8aksm\x9arg\x8cs', 'mac-iceland')]) + header = make_header(dh) + eq(str(header), + 'Re: r\xe4ksm\xf6rg\xe5s baz foo bar r\xe4ksm\xf6rg\xe5s') + self.ndiffAssertEqual(header.encode(maxlinelen=76), """\ +Re: =?mac-iceland?q?r=8Aksm=9Arg=8Cs?= baz foo bar =?mac-iceland?q?r=8Aksm?= + =?mac-iceland?q?=9Arg=8Cs?=""") + + def test_whitespace_eater_unicode(self): + eq = self.assertEqual + s = '=?ISO-8859-1?Q?Andr=E9?= Pirard ' + dh = decode_header(s) + eq(dh, [(b'Andr\xe9', 'iso-8859-1'), + (b'Pirard ', None)]) + header = str(make_header(dh)) + eq(header, 'Andr\xe9 Pirard ') + + def test_whitespace_eater_unicode_2(self): + eq = self.assertEqual + s = 'The =?iso-8859-1?b?cXVpY2sgYnJvd24gZm94?= jumped over the =?iso-8859-1?b?bGF6eSBkb2c=?=' + dh = decode_header(s) + eq(dh, [(b'The', None), (b'quick brown fox', 'iso-8859-1'), + (b'jumped over the', None), (b'lazy dog', 'iso-8859-1')]) + hu = str(make_header(dh)) + eq(hu, 'The quick brown fox jumped over the lazy dog') + + def test_rfc2047_missing_whitespace(self): + s = 'Sm=?ISO-8859-1?B?9g==?=rg=?ISO-8859-1?B?5Q==?=sbord' + dh = decode_header(s) + self.assertEqual(dh, [(s, None)]) + + def test_rfc2047_with_whitespace(self): + s = 'Sm =?ISO-8859-1?B?9g==?= rg =?ISO-8859-1?B?5Q==?= sbord' + dh = decode_header(s) + self.assertEqual(dh, [(b'Sm', None), (b'\xf6', 'iso-8859-1'), + (b'rg', None), (b'\xe5', 'iso-8859-1'), + (b'sbord', None)]) + + def test_rfc2047_B_bad_padding(self): + s = '=?iso-8859-1?B?%s?=' + data = [ # only test complete bytes + ('dm==', b'v'), ('dm=', b'v'), ('dm', b'v'), + ('dmk=', b'vi'), ('dmk', b'vi') + ] + for q, a in data: + dh = decode_header(s % q) + self.assertEqual(dh, [(a, 'iso-8859-1')]) + + def test_rfc2047_Q_invalid_digits(self): + # issue 10004. + s = '=?iso-8659-1?Q?andr=e9=zz?=' + self.assertEqual(decode_header(s), + [(b'andr\xe9=zz', 'iso-8659-1')]) + + +# Test the MIMEMessage class +class TestMIMEMessage(TestEmailBase): + def setUp(self): + with openfile('msg_11.txt') as fp: + self._text = fp.read() + + def test_type_error(self): + self.assertRaises(TypeError, MIMEMessage, 'a plain string') + + def test_valid_argument(self): + eq = self.assertEqual + unless = self.assertTrue + subject = 'A sub-message' + m = Message() + m['Subject'] = subject + r = MIMEMessage(m) + eq(r.get_content_type(), 'message/rfc822') + payload = r.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + subpart = payload[0] + unless(subpart is m) + eq(subpart['subject'], subject) + + def test_bad_multipart(self): + eq = self.assertEqual + msg1 = Message() + msg1['Subject'] = 'subpart 1' + msg2 = Message() + msg2['Subject'] = 'subpart 2' + r = MIMEMessage(msg1) + self.assertRaises(errors.MultipartConversionError, r.attach, msg2) + + def test_generate(self): + # First craft the message to be encapsulated + m = Message() + m['Subject'] = 'An enclosed message' + m.set_payload('Here is the body of the message.\n') + r = MIMEMessage(m) + r['Subject'] = 'The enclosing message' + s = StringIO() + g = Generator(s) + g.flatten(r) + self.assertEqual(s.getvalue(), """\ +Content-Type: message/rfc822 +MIME-Version: 1.0 +Subject: The enclosing message + +Subject: An enclosed message + +Here is the body of the message. +""") + + def test_parse_message_rfc822(self): + eq = self.assertEqual + unless = self.assertTrue + msg = self._msgobj('msg_11.txt') + eq(msg.get_content_type(), 'message/rfc822') + payload = msg.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + submsg = payload[0] + self.assertTrue(isinstance(submsg, Message)) + eq(submsg['subject'], 'An enclosed message') + eq(submsg.get_payload(), 'Here is the body of the message.\n') + + def test_dsn(self): + eq = self.assertEqual + unless = self.assertTrue + # msg 16 is a Delivery Status Notification, see RFC 1894 + msg = self._msgobj('msg_16.txt') + eq(msg.get_content_type(), 'multipart/report') + unless(msg.is_multipart()) + eq(len(msg.get_payload()), 3) + # Subpart 1 is a text/plain, human readable section + subpart = msg.get_payload(0) + eq(subpart.get_content_type(), 'text/plain') + eq(subpart.get_payload(), """\ +This report relates to a message you sent with the following header fields: + + Message-id: <002001c144a6$8752e060$56104586@oxy.edu> + Date: Sun, 23 Sep 2001 20:10:55 -0700 + From: "Ian T. Henry" + To: SoCal Raves + Subject: [scr] yeah for Ians!! + +Your message cannot be delivered to the following recipients: + + Recipient address: jangel1@cougar.noc.ucla.edu + Reason: recipient reached disk quota + +""") + # Subpart 2 contains the machine parsable DSN information. It + # consists of two blocks of headers, represented by two nested Message + # objects. + subpart = msg.get_payload(1) + eq(subpart.get_content_type(), 'message/delivery-status') + eq(len(subpart.get_payload()), 2) + # message/delivery-status should treat each block as a bunch of + # headers, i.e. a bunch of Message objects. + dsn1 = subpart.get_payload(0) + unless(isinstance(dsn1, Message)) + eq(dsn1['original-envelope-id'], '0GK500B4HD0888@cougar.noc.ucla.edu') + eq(dsn1.get_param('dns', header='reporting-mta'), '') + # Try a missing one + eq(dsn1.get_param('nsd', header='reporting-mta'), None) + dsn2 = subpart.get_payload(1) + unless(isinstance(dsn2, Message)) + eq(dsn2['action'], 'failed') + eq(dsn2.get_params(header='original-recipient'), + [('rfc822', ''), ('jangel1@cougar.noc.ucla.edu', '')]) + eq(dsn2.get_param('rfc822', header='final-recipient'), '') + # Subpart 3 is the original message + subpart = msg.get_payload(2) + eq(subpart.get_content_type(), 'message/rfc822') + payload = subpart.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + subsubpart = payload[0] + unless(isinstance(subsubpart, Message)) + eq(subsubpart.get_content_type(), 'text/plain') + eq(subsubpart['message-id'], + '<002001c144a6$8752e060$56104586@oxy.edu>') + + def test_epilogue(self): + eq = self.ndiffAssertEqual + with openfile('msg_21.txt') as fp: + text = fp.read() + msg = Message() + msg['From'] = 'aperson@dom.ain' + msg['To'] = 'bperson@dom.ain' + msg['Subject'] = 'Test' + msg.preamble = 'MIME message' + msg.epilogue = 'End of MIME message\n' + msg1 = MIMEText('One') + msg2 = MIMEText('Two') + msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') + msg.attach(msg1) + msg.attach(msg2) + sfp = StringIO() + g = Generator(sfp) + g.flatten(msg) + eq(sfp.getvalue(), text) + + def test_no_nl_preamble(self): + eq = self.ndiffAssertEqual + msg = Message() + msg['From'] = 'aperson@dom.ain' + msg['To'] = 'bperson@dom.ain' + msg['Subject'] = 'Test' + msg.preamble = 'MIME message' + msg.epilogue = '' + msg1 = MIMEText('One') + msg2 = MIMEText('Two') + msg.add_header('Content-Type', 'multipart/mixed', boundary='BOUNDARY') + msg.attach(msg1) + msg.attach(msg2) + eq(msg.as_string(), """\ +From: aperson@dom.ain +To: bperson@dom.ain +Subject: Test +Content-Type: multipart/mixed; boundary="BOUNDARY" + +MIME message +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +One +--BOUNDARY +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +Two +--BOUNDARY-- +""") + + def test_default_type(self): + eq = self.assertEqual + with openfile('msg_30.txt') as fp: + msg = email.message_from_file(fp) + container1 = msg.get_payload(0) + eq(container1.get_default_type(), 'message/rfc822') + eq(container1.get_content_type(), 'message/rfc822') + container2 = msg.get_payload(1) + eq(container2.get_default_type(), 'message/rfc822') + eq(container2.get_content_type(), 'message/rfc822') + container1a = container1.get_payload(0) + eq(container1a.get_default_type(), 'text/plain') + eq(container1a.get_content_type(), 'text/plain') + container2a = container2.get_payload(0) + eq(container2a.get_default_type(), 'text/plain') + eq(container2a.get_content_type(), 'text/plain') + + def test_default_type_with_explicit_container_type(self): + eq = self.assertEqual + with openfile('msg_28.txt') as fp: + msg = email.message_from_file(fp) + container1 = msg.get_payload(0) + eq(container1.get_default_type(), 'message/rfc822') + eq(container1.get_content_type(), 'message/rfc822') + container2 = msg.get_payload(1) + eq(container2.get_default_type(), 'message/rfc822') + eq(container2.get_content_type(), 'message/rfc822') + container1a = container1.get_payload(0) + eq(container1a.get_default_type(), 'text/plain') + eq(container1a.get_content_type(), 'text/plain') + container2a = container2.get_payload(0) + eq(container2a.get_default_type(), 'text/plain') + eq(container2a.get_content_type(), 'text/plain') + + def test_default_type_non_parsed(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + # Set up container + container = MIMEMultipart('digest', 'BOUNDARY') + container.epilogue = '' + # Set up subparts + subpart1a = MIMEText('message 1\n') + subpart2a = MIMEText('message 2\n') + subpart1 = MIMEMessage(subpart1a) + subpart2 = MIMEMessage(subpart2a) + container.attach(subpart1) + container.attach(subpart2) + eq(subpart1.get_content_type(), 'message/rfc822') + eq(subpart1.get_default_type(), 'message/rfc822') + eq(subpart2.get_content_type(), 'message/rfc822') + eq(subpart2.get_default_type(), 'message/rfc822') + neq(container.as_string(0), '''\ +Content-Type: multipart/digest; boundary="BOUNDARY" +MIME-Version: 1.0 + +--BOUNDARY +Content-Type: message/rfc822 +MIME-Version: 1.0 + +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +message 1 + +--BOUNDARY +Content-Type: message/rfc822 +MIME-Version: 1.0 + +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +message 2 + +--BOUNDARY-- +''') + del subpart1['content-type'] + del subpart1['mime-version'] + del subpart2['content-type'] + del subpart2['mime-version'] + eq(subpart1.get_content_type(), 'message/rfc822') + eq(subpart1.get_default_type(), 'message/rfc822') + eq(subpart2.get_content_type(), 'message/rfc822') + eq(subpart2.get_default_type(), 'message/rfc822') + neq(container.as_string(0), '''\ +Content-Type: multipart/digest; boundary="BOUNDARY" +MIME-Version: 1.0 + +--BOUNDARY + +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +message 1 + +--BOUNDARY + +Content-Type: text/plain; charset="us-ascii" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit + +message 2 + +--BOUNDARY-- +''') + + def test_mime_attachments_in_constructor(self): + eq = self.assertEqual + text1 = MIMEText('') + text2 = MIMEText('') + msg = MIMEMultipart(_subparts=(text1, text2)) + eq(len(msg.get_payload()), 2) + eq(msg.get_payload(0), text1) + eq(msg.get_payload(1), text2) + + def test_default_multipart_constructor(self): + msg = MIMEMultipart() + self.assertTrue(msg.is_multipart()) + + +# A general test of parser->model->generator idempotency. IOW, read a message +# in, parse it into a message object tree, then without touching the tree, +# regenerate the plain text. The original text and the transformed text +# should be identical. Note: that we ignore the Unix-From since that may +# contain a changed date. +class TestIdempotent(TestEmailBase): + + linesep = '\n' + + def _msgobj(self, filename): + with openfile(filename) as fp: + data = fp.read() + msg = email.message_from_string(data) + return msg, data + + def _idempotent(self, msg, text, unixfrom=False): + eq = self.ndiffAssertEqual + s = StringIO() + g = Generator(s, maxheaderlen=0) + g.flatten(msg, unixfrom=unixfrom) + eq(text, s.getvalue()) + + def test_parse_text_message(self): + eq = self.assertEqual + msg, text = self._msgobj('msg_01.txt') + eq(msg.get_content_type(), 'text/plain') + eq(msg.get_content_maintype(), 'text') + eq(msg.get_content_subtype(), 'plain') + eq(msg.get_params()[1], ('charset', 'us-ascii')) + eq(msg.get_param('charset'), 'us-ascii') + eq(msg.preamble, None) + eq(msg.epilogue, None) + self._idempotent(msg, text) + + def test_parse_untyped_message(self): + eq = self.assertEqual + msg, text = self._msgobj('msg_03.txt') + eq(msg.get_content_type(), 'text/plain') + eq(msg.get_params(), None) + eq(msg.get_param('charset'), None) + self._idempotent(msg, text) + + def test_simple_multipart(self): + msg, text = self._msgobj('msg_04.txt') + self._idempotent(msg, text) + + def test_MIME_digest(self): + msg, text = self._msgobj('msg_02.txt') + self._idempotent(msg, text) + + def test_long_header(self): + msg, text = self._msgobj('msg_27.txt') + self._idempotent(msg, text) + + def test_MIME_digest_with_part_headers(self): + msg, text = self._msgobj('msg_28.txt') + self._idempotent(msg, text) + + def test_mixed_with_image(self): + msg, text = self._msgobj('msg_06.txt') + self._idempotent(msg, text) + + def test_multipart_report(self): + msg, text = self._msgobj('msg_05.txt') + self._idempotent(msg, text) + + def test_dsn(self): + msg, text = self._msgobj('msg_16.txt') + self._idempotent(msg, text) + + def test_preamble_epilogue(self): + msg, text = self._msgobj('msg_21.txt') + self._idempotent(msg, text) + + def test_multipart_one_part(self): + msg, text = self._msgobj('msg_23.txt') + self._idempotent(msg, text) + + def test_multipart_no_parts(self): + msg, text = self._msgobj('msg_24.txt') + self._idempotent(msg, text) + + def test_no_start_boundary(self): + msg, text = self._msgobj('msg_31.txt') + self._idempotent(msg, text) + + def test_rfc2231_charset(self): + msg, text = self._msgobj('msg_32.txt') + self._idempotent(msg, text) + + def test_more_rfc2231_parameters(self): + msg, text = self._msgobj('msg_33.txt') + self._idempotent(msg, text) + + def test_text_plain_in_a_multipart_digest(self): + msg, text = self._msgobj('msg_34.txt') + self._idempotent(msg, text) + + def test_nested_multipart_mixeds(self): + msg, text = self._msgobj('msg_12a.txt') + self._idempotent(msg, text) + + def test_message_external_body_idempotent(self): + msg, text = self._msgobj('msg_36.txt') + self._idempotent(msg, text) + + def test_message_delivery_status(self): + msg, text = self._msgobj('msg_43.txt') + self._idempotent(msg, text, unixfrom=True) + + def test_message_signed_idempotent(self): + msg, text = self._msgobj('msg_45.txt') + self._idempotent(msg, text) + + def test_content_type(self): + eq = self.assertEqual + unless = self.assertTrue + # Get a message object and reset the seek pointer for other tests + msg, text = self._msgobj('msg_05.txt') + eq(msg.get_content_type(), 'multipart/report') + # Test the Content-Type: parameters + params = {} + for pk, pv in msg.get_params(): + params[pk] = pv + eq(params['report-type'], 'delivery-status') + eq(params['boundary'], 'D1690A7AC1.996856090/mail.example.com') + eq(msg.preamble, 'This is a MIME-encapsulated message.' + self.linesep) + eq(msg.epilogue, self.linesep) + eq(len(msg.get_payload()), 3) + # Make sure the subparts are what we expect + msg1 = msg.get_payload(0) + eq(msg1.get_content_type(), 'text/plain') + eq(msg1.get_payload(), 'Yadda yadda yadda' + self.linesep) + msg2 = msg.get_payload(1) + eq(msg2.get_content_type(), 'text/plain') + eq(msg2.get_payload(), 'Yadda yadda yadda' + self.linesep) + msg3 = msg.get_payload(2) + eq(msg3.get_content_type(), 'message/rfc822') + self.assertTrue(isinstance(msg3, Message)) + payload = msg3.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + msg4 = payload[0] + unless(isinstance(msg4, Message)) + eq(msg4.get_payload(), 'Yadda yadda yadda' + self.linesep) + + def test_parser(self): + eq = self.assertEqual + unless = self.assertTrue + msg, text = self._msgobj('msg_06.txt') + # Check some of the outer headers + eq(msg.get_content_type(), 'message/rfc822') + # Make sure the payload is a list of exactly one sub-Message, and that + # that submessage has a type of text/plain + payload = msg.get_payload() + unless(isinstance(payload, list)) + eq(len(payload), 1) + msg1 = payload[0] + self.assertTrue(isinstance(msg1, Message)) + eq(msg1.get_content_type(), 'text/plain') + self.assertTrue(isinstance(msg1.get_payload(), str)) + eq(msg1.get_payload(), self.linesep) + + + +# Test various other bits of the package's functionality +class TestMiscellaneous(TestEmailBase): + def test_message_from_string(self): + with openfile('msg_01.txt') as fp: + text = fp.read() + msg = email.message_from_string(text) + s = StringIO() + # Don't wrap/continue long headers since we're trying to test + # idempotency. + g = Generator(s, maxheaderlen=0) + g.flatten(msg) + self.assertEqual(text, s.getvalue()) + + def test_message_from_file(self): + with openfile('msg_01.txt') as fp: + text = fp.read() + fp.seek(0) + msg = email.message_from_file(fp) + s = StringIO() + # Don't wrap/continue long headers since we're trying to test + # idempotency. + g = Generator(s, maxheaderlen=0) + g.flatten(msg) + self.assertEqual(text, s.getvalue()) + + def test_message_from_string_with_class(self): + unless = self.assertTrue + with openfile('msg_01.txt') as fp: + text = fp.read() + + # Create a subclass + class MyMessage(Message): + pass + + msg = email.message_from_string(text, MyMessage) + unless(isinstance(msg, MyMessage)) + # Try something more complicated + with openfile('msg_02.txt') as fp: + text = fp.read() + msg = email.message_from_string(text, MyMessage) + for subpart in msg.walk(): + unless(isinstance(subpart, MyMessage)) + + def test_message_from_file_with_class(self): + unless = self.assertTrue + # Create a subclass + class MyMessage(Message): + pass + + with openfile('msg_01.txt') as fp: + msg = email.message_from_file(fp, MyMessage) + unless(isinstance(msg, MyMessage)) + # Try something more complicated + with openfile('msg_02.txt') as fp: + msg = email.message_from_file(fp, MyMessage) + for subpart in msg.walk(): + unless(isinstance(subpart, MyMessage)) + + def test__all__(self): + module = __import__('email') + # Can't use sorted() here due to Python 2.3 compatibility + all = module.__all__[:] + all.sort() + self.assertEqual(all, [ + 'base64mime', 'charset', 'encoders', 'errors', 'generator', + 'header', 'iterators', 'message', 'message_from_binary_file', + 'message_from_bytes', 'message_from_file', + 'message_from_string', 'mime', 'parser', + 'quoprimime', 'utils', + ]) + + def test_formatdate(self): + now = time.time() + self.assertEqual(utils.parsedate(utils.formatdate(now))[:6], + time.gmtime(now)[:6]) + + def test_formatdate_localtime(self): + now = time.time() + self.assertEqual( + utils.parsedate(utils.formatdate(now, localtime=True))[:6], + time.localtime(now)[:6]) + + def test_formatdate_usegmt(self): + now = time.time() + self.assertEqual( + utils.formatdate(now, localtime=False), + time.strftime('%a, %d %b %Y %H:%M:%S -0000', time.gmtime(now))) + self.assertEqual( + utils.formatdate(now, localtime=False, usegmt=True), + time.strftime('%a, %d %b %Y %H:%M:%S GMT', time.gmtime(now))) + + def test_parsedate_none(self): + self.assertEqual(utils.parsedate(''), None) + + def test_parsedate_compact(self): + # The FWS after the comma is optional + self.assertEqual(utils.parsedate('Wed,3 Apr 2002 14:58:26 +0800'), + utils.parsedate('Wed, 3 Apr 2002 14:58:26 +0800')) + + def test_parsedate_no_dayofweek(self): + eq = self.assertEqual + eq(utils.parsedate_tz('25 Feb 2003 13:47:26 -0800'), + (2003, 2, 25, 13, 47, 26, 0, 1, -1, -28800)) + + def test_parsedate_compact_no_dayofweek(self): + eq = self.assertEqual + eq(utils.parsedate_tz('5 Feb 2003 13:47:26 -0800'), + (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) + + def test_parsedate_no_space_before_positive_offset(self): + self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26+0800'), + (2002, 4, 3, 14, 58, 26, 0, 1, -1, 28800)) + + def test_parsedate_no_space_before_negative_offset(self): + # Issue 1155362: we already handled '+' for this case. + self.assertEqual(utils.parsedate_tz('Wed, 3 Apr 2002 14:58:26-0800'), + (2002, 4, 3, 14, 58, 26, 0, 1, -1, -28800)) + + + def test_parsedate_accepts_time_with_dots(self): + eq = self.assertEqual + eq(utils.parsedate_tz('5 Feb 2003 13.47.26 -0800'), + (2003, 2, 5, 13, 47, 26, 0, 1, -1, -28800)) + eq(utils.parsedate_tz('5 Feb 2003 13.47 -0800'), + (2003, 2, 5, 13, 47, 0, 0, 1, -1, -28800)) + + def test_parsedate_acceptable_to_time_functions(self): + eq = self.assertEqual + timetup = utils.parsedate('5 Feb 2003 13:47:26 -0800') + t = int(time.mktime(timetup)) + eq(time.localtime(t)[:6], timetup[:6]) + eq(int(time.strftime('%Y', timetup)), 2003) + timetup = utils.parsedate_tz('5 Feb 2003 13:47:26 -0800') + t = int(time.mktime(timetup[:9])) + eq(time.localtime(t)[:6], timetup[:6]) + eq(int(time.strftime('%Y', timetup[:9])), 2003) + + def test_parsedate_y2k(self): + """Test for parsing a date with a two-digit year. + + Parsing a date with a two-digit year should return the correct + four-digit year. RFC822 allows two-digit years, but RFC2822 (which + obsoletes RFC822) requires four-digit years. + + """ + self.assertEqual(utils.parsedate_tz('25 Feb 03 13:47:26 -0800'), + utils.parsedate_tz('25 Feb 2003 13:47:26 -0800')) + self.assertEqual(utils.parsedate_tz('25 Feb 71 13:47:26 -0800'), + utils.parsedate_tz('25 Feb 1971 13:47:26 -0800')) + + def test_parseaddr_empty(self): + self.assertEqual(utils.parseaddr('<>'), ('', '')) + self.assertEqual(utils.formataddr(utils.parseaddr('<>')), '') + + def test_noquote_dump(self): + self.assertEqual( + utils.formataddr(('A Silly Person', 'person@dom.ain')), + 'A Silly Person ') + + def test_escape_dump(self): + self.assertEqual( + utils.formataddr(('A (Very) Silly Person', 'person@dom.ain')), + r'"A \(Very\) Silly Person" ') + a = r'A \(Special\) Person' + b = 'person@dom.ain' + self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) + + def test_escape_backslashes(self): + self.assertEqual( + utils.formataddr(('Arthur \Backslash\ Foobar', 'person@dom.ain')), + r'"Arthur \\Backslash\\ Foobar" ') + a = r'Arthur \Backslash\ Foobar' + b = 'person@dom.ain' + self.assertEqual(utils.parseaddr(utils.formataddr((a, b))), (a, b)) + + def test_name_with_dot(self): + x = 'John X. Doe ' + y = '"John X. Doe" ' + a, b = ('John X. Doe', 'jxd@example.com') + self.assertEqual(utils.parseaddr(x), (a, b)) + self.assertEqual(utils.parseaddr(y), (a, b)) + # formataddr() quotes the name if there's a dot in it + self.assertEqual(utils.formataddr((a, b)), y) + + def test_parseaddr_preserves_quoted_pairs_in_addresses(self): + # issue 10005. Note that in the third test the second pair of + # backslashes is not actually a quoted pair because it is not inside a + # comment or quoted string: the address being parsed has a quoted + # string containing a quoted backslash, followed by 'example' and two + # backslashes, followed by another quoted string containing a space and + # the word 'example'. parseaddr copies those two backslashes + # literally. Per rfc5322 this is not technically correct since a \ may + # not appear in an address outside of a quoted string. It is probably + # a sensible Postel interpretation, though. + eq = self.assertEqual + eq(utils.parseaddr('""example" example"@example.com'), + ('', '""example" example"@example.com')) + eq(utils.parseaddr('"\\"example\\" example"@example.com'), + ('', '"\\"example\\" example"@example.com')) + eq(utils.parseaddr('"\\\\"example\\\\" example"@example.com'), + ('', '"\\\\"example\\\\" example"@example.com')) + + def test_parseaddr_preserves_spaces_in_local_part(self): + # issue 9286. A normal RFC5322 local part should not contain any + # folding white space, but legacy local parts can (they are a sequence + # of atoms, not dotatoms). On the other hand we strip whitespace from + # before the @ and around dots, on the assumption that the whitespace + # around the punctuation is a mistake in what would otherwise be + # an RFC5322 local part. Leading whitespace is, usual, stripped as well. + self.assertEqual(('', "merwok wok@xample.com"), + utils.parseaddr("merwok wok@xample.com")) + self.assertEqual(('', "merwok wok@xample.com"), + utils.parseaddr("merwok wok@xample.com")) + self.assertEqual(('', "merwok wok@xample.com"), + utils.parseaddr(" merwok wok @xample.com")) + self.assertEqual(('', 'merwok"wok" wok@xample.com'), + utils.parseaddr('merwok"wok" wok@xample.com')) + self.assertEqual(('', 'merwok.wok.wok@xample.com'), + utils.parseaddr('merwok. wok . wok@xample.com')) + + def test_multiline_from_comment(self): + x = """\ +Foo +\tBar """ + self.assertEqual(utils.parseaddr(x), ('Foo Bar', 'foo@example.com')) + + def test_quote_dump(self): + self.assertEqual( + utils.formataddr(('A Silly; Person', 'person@dom.ain')), + r'"A Silly; Person" ') + + def test_charset_richcomparisons(self): + eq = self.assertEqual + ne = self.assertNotEqual + cset1 = Charset() + cset2 = Charset() + eq(cset1, 'us-ascii') + eq(cset1, 'US-ASCII') + eq(cset1, 'Us-AsCiI') + eq('us-ascii', cset1) + eq('US-ASCII', cset1) + eq('Us-AsCiI', cset1) + ne(cset1, 'usascii') + ne(cset1, 'USASCII') + ne(cset1, 'UsAsCiI') + ne('usascii', cset1) + ne('USASCII', cset1) + ne('UsAsCiI', cset1) + eq(cset1, cset2) + eq(cset2, cset1) + + def test_getaddresses(self): + eq = self.assertEqual + eq(utils.getaddresses(['aperson@dom.ain (Al Person)', + 'Bud Person ']), + [('Al Person', 'aperson@dom.ain'), + ('Bud Person', 'bperson@dom.ain')]) + + def test_getaddresses_nasty(self): + eq = self.assertEqual + eq(utils.getaddresses(['foo: ;']), [('', '')]) + eq(utils.getaddresses( + ['[]*-- =~$']), + [('', ''), ('', ''), ('', '*--')]) + eq(utils.getaddresses( + ['foo: ;', '"Jason R. Mastaler" ']), + [('', ''), ('Jason R. Mastaler', 'jason@dom.ain')]) + + def test_getaddresses_embedded_comment(self): + """Test proper handling of a nested comment""" + eq = self.assertEqual + addrs = utils.getaddresses(['User ((nested comment)) ']) + eq(addrs[0][1], 'foo@bar.com') + + def test_utils_quote_unquote(self): + eq = self.assertEqual + msg = Message() + msg.add_header('content-disposition', 'attachment', + filename='foo\\wacky"name') + eq(msg.get_filename(), 'foo\\wacky"name') + + def test_get_body_encoding_with_bogus_charset(self): + charset = Charset('not a charset') + self.assertEqual(charset.get_body_encoding(), 'base64') + + def test_get_body_encoding_with_uppercase_charset(self): + eq = self.assertEqual + msg = Message() + msg['Content-Type'] = 'text/plain; charset=UTF-8' + eq(msg['content-type'], 'text/plain; charset=UTF-8') + charsets = msg.get_charsets() + eq(len(charsets), 1) + eq(charsets[0], 'utf-8') + charset = Charset(charsets[0]) + eq(charset.get_body_encoding(), 'base64') + msg.set_payload(b'hello world', charset=charset) + eq(msg.get_payload(), 'aGVsbG8gd29ybGQ=\n') + eq(msg.get_payload(decode=True), b'hello world') + eq(msg['content-transfer-encoding'], 'base64') + # Try another one + msg = Message() + msg['Content-Type'] = 'text/plain; charset="US-ASCII"' + charsets = msg.get_charsets() + eq(len(charsets), 1) + eq(charsets[0], 'us-ascii') + charset = Charset(charsets[0]) + eq(charset.get_body_encoding(), encoders.encode_7or8bit) + msg.set_payload('hello world', charset=charset) + eq(msg.get_payload(), 'hello world') + eq(msg['content-transfer-encoding'], '7bit') + + def test_charsets_case_insensitive(self): + lc = Charset('us-ascii') + uc = Charset('US-ASCII') + self.assertEqual(lc.get_body_encoding(), uc.get_body_encoding()) + + def test_partial_falls_inside_message_delivery_status(self): + eq = self.ndiffAssertEqual + # The Parser interface provides chunks of data to FeedParser in 8192 + # byte gulps. SF bug #1076485 found one of those chunks inside + # message/delivery-status header block, which triggered an + # unreadline() of NeedMoreData. + msg = self._msgobj('msg_43.txt') + sfp = StringIO() + iterators._structure(msg, sfp) + eq(sfp.getvalue(), """\ +multipart/report + text/plain + message/delivery-status + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/plain + text/rfc822-headers +""") + + def test_make_msgid_domain(self): + self.assertEqual( + email.utils.make_msgid(domain='testdomain-string')[-19:], + '@testdomain-string>') + + +# Test the iterator/generators +class TestIterators(TestEmailBase): + def test_body_line_iterator(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + # First a simple non-multipart message + msg = self._msgobj('msg_01.txt') + it = iterators.body_line_iterator(msg) + lines = list(it) + eq(len(lines), 6) + neq(EMPTYSTRING.join(lines), msg.get_payload()) + # Now a more complicated multipart + msg = self._msgobj('msg_02.txt') + it = iterators.body_line_iterator(msg) + lines = list(it) + eq(len(lines), 43) + with openfile('msg_19.txt') as fp: + neq(EMPTYSTRING.join(lines), fp.read()) + + def test_typed_subpart_iterator(self): + eq = self.assertEqual + msg = self._msgobj('msg_04.txt') + it = iterators.typed_subpart_iterator(msg, 'text') + lines = [] + subparts = 0 + for subpart in it: + subparts += 1 + lines.append(subpart.get_payload()) + eq(subparts, 2) + eq(EMPTYSTRING.join(lines), """\ +a simple kind of mirror +to reflect upon our own +a simple kind of mirror +to reflect upon our own +""") + + def test_typed_subpart_iterator_default_type(self): + eq = self.assertEqual + msg = self._msgobj('msg_03.txt') + it = iterators.typed_subpart_iterator(msg, 'text', 'plain') + lines = [] + subparts = 0 + for subpart in it: + subparts += 1 + lines.append(subpart.get_payload()) + eq(subparts, 1) + eq(EMPTYSTRING.join(lines), """\ + +Hi, + +Do you like this message? + +-Me +""") + + def test_pushCR_LF(self): + '''FeedParser BufferedSubFile.push() assumed it received complete + line endings. A CR ending one push() followed by a LF starting + the next push() added an empty line. + ''' + imt = [ + ("a\r \n", 2), + ("b", 0), + ("c\n", 1), + ("", 0), + ("d\r\n", 1), + ("e\r", 0), + ("\nf", 1), + ("\r\n", 1), + ] + from email.feedparser import BufferedSubFile, NeedMoreData + bsf = BufferedSubFile() + om = [] + nt = 0 + for il, n in imt: + bsf.push(il) + nt += n + n1 = 0 + while True: + ol = bsf.readline() + if ol == NeedMoreData: + break + om.append(ol) + n1 += 1 + self.assertTrue(n == n1) + self.assertTrue(len(om) == nt) + self.assertTrue(''.join([il for il, n in imt]) == ''.join(om)) + + + +class TestParsers(TestEmailBase): + def test_header_parser(self): + eq = self.assertEqual + # Parse only the headers of a complex multipart MIME document + with openfile('msg_02.txt') as fp: + msg = HeaderParser().parse(fp) + eq(msg['from'], 'ppp-request@zzz.org') + eq(msg['to'], 'ppp@zzz.org') + eq(msg.get_content_type(), 'multipart/mixed') + self.assertFalse(msg.is_multipart()) + self.assertTrue(isinstance(msg.get_payload(), str)) + + def test_whitespace_continuation(self): + eq = self.assertEqual + # This message contains a line after the Subject: header that has only + # whitespace, but it is not empty! + msg = email.message_from_string("""\ +From: aperson@dom.ain +To: bperson@dom.ain +Subject: the next line has a space on it +\x20 +Date: Mon, 8 Apr 2002 15:09:19 -0400 +Message-ID: spam + +Here's the message body +""") + eq(msg['subject'], 'the next line has a space on it\n ') + eq(msg['message-id'], 'spam') + eq(msg.get_payload(), "Here's the message body\n") + + def test_whitespace_continuation_last_header(self): + eq = self.assertEqual + # Like the previous test, but the subject line is the last + # header. + msg = email.message_from_string("""\ +From: aperson@dom.ain +To: bperson@dom.ain +Date: Mon, 8 Apr 2002 15:09:19 -0400 +Message-ID: spam +Subject: the next line has a space on it +\x20 + +Here's the message body +""") + eq(msg['subject'], 'the next line has a space on it\n ') + eq(msg['message-id'], 'spam') + eq(msg.get_payload(), "Here's the message body\n") + + def test_crlf_separation(self): + eq = self.assertEqual + with openfile('msg_26.txt', newline='\n') as fp: + msg = Parser().parse(fp) + eq(len(msg.get_payload()), 2) + part1 = msg.get_payload(0) + eq(part1.get_content_type(), 'text/plain') + eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n') + part2 = msg.get_payload(1) + eq(part2.get_content_type(), 'application/riscos') + + def test_crlf_flatten(self): + # Using newline='\n' preserves the crlfs in this input file. + with openfile('msg_26.txt', newline='\n') as fp: + text = fp.read() + msg = email.message_from_string(text) + s = StringIO() + g = Generator(s) + g.flatten(msg, linesep='\r\n') + self.assertEqual(s.getvalue(), text) + + maxDiff = None + + def test_multipart_digest_with_extra_mime_headers(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + with openfile('msg_28.txt') as fp: + msg = email.message_from_file(fp) + # Structure is: + # multipart/digest + # message/rfc822 + # text/plain + # message/rfc822 + # text/plain + eq(msg.is_multipart(), 1) + eq(len(msg.get_payload()), 2) + part1 = msg.get_payload(0) + eq(part1.get_content_type(), 'message/rfc822') + eq(part1.is_multipart(), 1) + eq(len(part1.get_payload()), 1) + part1a = part1.get_payload(0) + eq(part1a.is_multipart(), 0) + eq(part1a.get_content_type(), 'text/plain') + neq(part1a.get_payload(), 'message 1\n') + # next message/rfc822 + part2 = msg.get_payload(1) + eq(part2.get_content_type(), 'message/rfc822') + eq(part2.is_multipart(), 1) + eq(len(part2.get_payload()), 1) + part2a = part2.get_payload(0) + eq(part2a.is_multipart(), 0) + eq(part2a.get_content_type(), 'text/plain') + neq(part2a.get_payload(), 'message 2\n') + + def test_three_lines(self): + # A bug report by Andrew McNamara + lines = ['From: Andrew Person From', 'From']) + eq(msg.get_payload(), 'body') + + def test_rfc2822_space_not_allowed_in_header(self): + eq = self.assertEqual + m = '>From foo@example.com 11:25:53\nFrom: bar\n!"#QUX;~: zoo\n\nbody' + msg = email.message_from_string(m) + eq(len(msg.keys()), 0) + + def test_rfc2822_one_character_header(self): + eq = self.assertEqual + m = 'A: first header\nB: second header\nCC: third header\n\nbody' + msg = email.message_from_string(m) + headers = msg.keys() + headers.sort() + eq(headers, ['A', 'B', 'CC']) + eq(msg.get_payload(), 'body') + + def test_CRLFLF_at_end_of_part(self): + # issue 5610: feedparser should not eat two chars from body part ending + # with "\r\n\n". + m = ( + "From: foo@bar.com\n" + "To: baz\n" + "Mime-Version: 1.0\n" + "Content-Type: multipart/mixed; boundary=BOUNDARY\n" + "\n" + "--BOUNDARY\n" + "Content-Type: text/plain\n" + "\n" + "body ending with CRLF newline\r\n" + "\n" + "--BOUNDARY--\n" + ) + msg = email.message_from_string(m) + self.assertTrue(msg.get_payload(0).get_payload().endswith('\r\n')) + + +class Test8BitBytesHandling(unittest.TestCase): + # In Python3 all input is string, but that doesn't work if the actual input + # uses an 8bit transfer encoding. To hack around that, in email 5.1 we + # decode byte streams using the surrogateescape error handler, and + # reconvert to binary at appropriate places if we detect surrogates. This + # doesn't allow us to transform headers with 8bit bytes (they get munged), + # but it does allow us to parse and preserve them, and to decode body + # parts that use an 8bit CTE. + + bodytest_msg = textwrap.dedent("""\ + From: foo@bar.com + To: baz + Mime-Version: 1.0 + Content-Type: text/plain; charset={charset} + Content-Transfer-Encoding: {cte} + + {bodyline} + """) + + def test_known_8bit_CTE(self): + m = self.bodytest_msg.format(charset='utf-8', + cte='8bit', + bodyline='pöstal').encode('utf-8') + msg = email.message_from_bytes(m) + self.assertEqual(msg.get_payload(), "pöstal\n") + self.assertEqual(msg.get_payload(decode=True), + "pöstal\n".encode('utf-8')) + + def test_unknown_8bit_CTE(self): + m = self.bodytest_msg.format(charset='notavalidcharset', + cte='8bit', + bodyline='pöstal').encode('utf-8') + msg = email.message_from_bytes(m) + self.assertEqual(msg.get_payload(), "p\uFFFD\uFFFDstal\n") + self.assertEqual(msg.get_payload(decode=True), + "pöstal\n".encode('utf-8')) + + def test_8bit_in_quopri_body(self): + # This is non-RFC compliant data...without 'decode' the library code + # decodes the body using the charset from the headers, and because the + # source byte really is utf-8 this works. This is likely to fail + # against real dirty data (ie: produce mojibake), but the data is + # invalid anyway so it is as good a guess as any. But this means that + # this test just confirms the current behavior; that behavior is not + # necessarily the best possible behavior. With 'decode' it is + # returning the raw bytes, so that test should be of correct behavior, + # or at least produce the same result that email4 did. + m = self.bodytest_msg.format(charset='utf-8', + cte='quoted-printable', + bodyline='p=C3=B6stál').encode('utf-8') + msg = email.message_from_bytes(m) + self.assertEqual(msg.get_payload(), 'p=C3=B6stál\n') + self.assertEqual(msg.get_payload(decode=True), + 'pöstál\n'.encode('utf-8')) + + def test_invalid_8bit_in_non_8bit_cte_uses_replace(self): + # This is similar to the previous test, but proves that if the 8bit + # byte is undecodeable in the specified charset, it gets replaced + # by the unicode 'unknown' character. Again, this may or may not + # be the ideal behavior. Note that if decode=False none of the + # decoders will get involved, so this is the only test we need + # for this behavior. + m = self.bodytest_msg.format(charset='ascii', + cte='quoted-printable', + bodyline='p=C3=B6stál').encode('utf-8') + msg = email.message_from_bytes(m) + self.assertEqual(msg.get_payload(), 'p=C3=B6st\uFFFD\uFFFDl\n') + self.assertEqual(msg.get_payload(decode=True), + 'pöstál\n'.encode('utf-8')) + + def test_8bit_in_base64_body(self): + # Sticking an 8bit byte in a base64 block makes it undecodable by + # normal means, so the block is returned undecoded, but as bytes. + m = self.bodytest_msg.format(charset='utf-8', + cte='base64', + bodyline='cMO2c3RhbAá=').encode('utf-8') + msg = email.message_from_bytes(m) + self.assertEqual(msg.get_payload(decode=True), + 'cMO2c3RhbAá=\n'.encode('utf-8')) + + def test_8bit_in_uuencode_body(self): + # Sticking an 8bit byte in a uuencode block makes it undecodable by + # normal means, so the block is returned undecoded, but as bytes. + m = self.bodytest_msg.format(charset='utf-8', + cte='uuencode', + bodyline='<,.V7bit conversion. + self.assertEqual(out.getvalue(), + self.latin_bin_msg.decode('latin-1')+'\n') + + def test_bytes_feedparser(self): + bfp = email.feedparser.BytesFeedParser() + for i in range(0, len(self.latin_bin_msg), 10): + bfp.feed(self.latin_bin_msg[i:i+10]) + m = bfp.close() + self.assertEqual(str(m), self.latin_bin_msg_as7bit) + + def test_crlf_flatten(self): + with openfile('msg_26.txt', 'rb') as fp: + text = fp.read() + msg = email.message_from_bytes(text) + s = BytesIO() + g = email.generator.BytesGenerator(s) + g.flatten(msg, linesep='\r\n') + self.assertEqual(s.getvalue(), text) + maxDiff = None + + +class BaseTestBytesGeneratorIdempotent: + + maxDiff = None + + def _msgobj(self, filename): + with openfile(filename, 'rb') as fp: + data = fp.read() + data = self.normalize_linesep_regex.sub(self.blinesep, data) + msg = email.message_from_bytes(data) + return msg, data + + def _idempotent(self, msg, data, unixfrom=False): + b = BytesIO() + g = email.generator.BytesGenerator(b, maxheaderlen=0) + g.flatten(msg, unixfrom=unixfrom, linesep=self.linesep) + self.assertByteStringsEqual(data, b.getvalue()) + + def assertByteStringsEqual(self, str1, str2): + # Not using self.blinesep here is intentional. This way the output + # is more useful when the failure results in mixed line endings. + self.assertListEqual(str1.split(b'\n'), str2.split(b'\n')) + + +class TestBytesGeneratorIdempotentNL(BaseTestBytesGeneratorIdempotent, + TestIdempotent): + linesep = '\n' + blinesep = b'\n' + normalize_linesep_regex = re.compile(br'\r\n') + + +class TestBytesGeneratorIdempotentCRLF(BaseTestBytesGeneratorIdempotent, + TestIdempotent): + linesep = '\r\n' + blinesep = b'\r\n' + normalize_linesep_regex = re.compile(br'(? +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) +\tid 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 +Content-Type: text/plain; charset=us-ascii; + title*=us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21 + + +Hi, + +Do you like this message? + +-Me +""") + + def test_del_param(self): + eq = self.ndiffAssertEqual + msg = self._msgobj('msg_01.txt') + msg.set_param('foo', 'bar', charset='us-ascii', language='en') + msg.set_param('title', 'This is even more ***fun*** isn\'t it!', + charset='us-ascii', language='en') + msg.del_param('foo', header='Content-Type') + eq(msg.as_string(maxheaderlen=78), """\ +Return-Path: +Delivered-To: bbb@zzz.org +Received: by mail.zzz.org (Postfix, from userid 889) +\tid 27CEAD38CC; Fri, 4 May 2001 14:05:44 -0400 (EDT) +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Message-ID: <15090.61304.110929.45684@aaa.zzz.org> +From: bbb@ddd.com (John X. Doe) +To: bbb@zzz.org +Subject: This is a test message +Date: Fri, 4 May 2001 14:05:44 -0400 +Content-Type: text/plain; charset="us-ascii"; + title*=us-ascii'en'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20isn%27t%20it%21 + + +Hi, + +Do you like this message? + +-Me +""") + + def test_rfc2231_get_content_charset(self): + eq = self.assertEqual + msg = self._msgobj('msg_32.txt') + eq(msg.get_content_charset(), 'us-ascii') + + def test_rfc2231_parse_rfc_quoting(self): + m = textwrap.dedent('''\ + Content-Disposition: inline; + \tfilename*0*=''This%20is%20even%20more%20; + \tfilename*1*=%2A%2A%2Afun%2A%2A%2A%20; + \tfilename*2="is it not.pdf" + + ''') + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), + 'This is even more ***fun*** is it not.pdf') + self.assertEqual(m, msg.as_string()) + + def test_rfc2231_parse_extra_quoting(self): + m = textwrap.dedent('''\ + Content-Disposition: inline; + \tfilename*0*="''This%20is%20even%20more%20"; + \tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; + \tfilename*2="is it not.pdf" + + ''') + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), + 'This is even more ***fun*** is it not.pdf') + self.assertEqual(m, msg.as_string()) + + def test_rfc2231_no_language_or_charset(self): + m = '''\ +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename="file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm" +Content-Type: text/html; NAME*0=file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEM; NAME*1=P_nsmail.htm + +''' + msg = email.message_from_string(m) + param = msg.get_param('NAME') + self.assertFalse(isinstance(param, tuple)) + self.assertEqual( + param, + 'file____C__DOCUMENTS_20AND_20SETTINGS_FABIEN_LOCAL_20SETTINGS_TEMP_nsmail.htm') + + def test_rfc2231_no_language_or_charset_in_filename(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0*="''This%20is%20even%20more%20"; +\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; +\tfilename*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), + 'This is even more ***fun*** is it not.pdf') + + def test_rfc2231_no_language_or_charset_in_filename_encoded(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0*="''This%20is%20even%20more%20"; +\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; +\tfilename*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), + 'This is even more ***fun*** is it not.pdf') + + def test_rfc2231_partly_encoded(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0="''This%20is%20even%20more%20"; +\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; +\tfilename*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual( + msg.get_filename(), + 'This%20is%20even%20more%20***fun*** is it not.pdf') + + def test_rfc2231_partly_nonencoded(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0="This%20is%20even%20more%20"; +\tfilename*1="%2A%2A%2Afun%2A%2A%2A%20"; +\tfilename*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual( + msg.get_filename(), + 'This%20is%20even%20more%20%2A%2A%2Afun%2A%2A%2A%20is it not.pdf') + + def test_rfc2231_no_language_or_charset_in_boundary(self): + m = '''\ +Content-Type: multipart/alternative; +\tboundary*0*="''This%20is%20even%20more%20"; +\tboundary*1*="%2A%2A%2Afun%2A%2A%2A%20"; +\tboundary*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_boundary(), + 'This is even more ***fun*** is it not.pdf') + + def test_rfc2231_no_language_or_charset_in_charset(self): + # This is a nonsensical charset value, but tests the code anyway + m = '''\ +Content-Type: text/plain; +\tcharset*0*="This%20is%20even%20more%20"; +\tcharset*1*="%2A%2A%2Afun%2A%2A%2A%20"; +\tcharset*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_content_charset(), + 'this is even more ***fun*** is it not.pdf') + + def test_rfc2231_bad_encoding_in_filename(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0*="bogus'xx'This%20is%20even%20more%20"; +\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; +\tfilename*2="is it not.pdf" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), + 'This is even more ***fun*** is it not.pdf') + + def test_rfc2231_bad_encoding_in_charset(self): + m = """\ +Content-Type: text/plain; charset*=bogus''utf-8%E2%80%9D + +""" + msg = email.message_from_string(m) + # This should return None because non-ascii characters in the charset + # are not allowed. + self.assertEqual(msg.get_content_charset(), None) + + def test_rfc2231_bad_character_in_charset(self): + m = """\ +Content-Type: text/plain; charset*=ascii''utf-8%E2%80%9D + +""" + msg = email.message_from_string(m) + # This should return None because non-ascii characters in the charset + # are not allowed. + self.assertEqual(msg.get_content_charset(), None) + + def test_rfc2231_bad_character_in_filename(self): + m = '''\ +Content-Disposition: inline; +\tfilename*0*="ascii'xx'This%20is%20even%20more%20"; +\tfilename*1*="%2A%2A%2Afun%2A%2A%2A%20"; +\tfilename*2*="is it not.pdf%E2" + +''' + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), + 'This is even more ***fun*** is it not.pdf\ufffd') + + def test_rfc2231_unknown_encoding(self): + m = """\ +Content-Transfer-Encoding: 8bit +Content-Disposition: inline; filename*=X-UNKNOWN''myfile.txt + +""" + msg = email.message_from_string(m) + self.assertEqual(msg.get_filename(), 'myfile.txt') + + def test_rfc2231_single_tick_in_filename_extended(self): + eq = self.assertEqual + m = """\ +Content-Type: application/x-foo; +\tname*0*=\"Frank's\"; name*1*=\" Document\" + +""" + msg = email.message_from_string(m) + charset, language, s = msg.get_param('name') + eq(charset, None) + eq(language, None) + eq(s, "Frank's Document") + + def test_rfc2231_single_tick_in_filename(self): + m = """\ +Content-Type: application/x-foo; name*0=\"Frank's\"; name*1=\" Document\" + +""" + msg = email.message_from_string(m) + param = msg.get_param('name') + self.assertFalse(isinstance(param, tuple)) + self.assertEqual(param, "Frank's Document") + + def test_rfc2231_tick_attack_extended(self): + eq = self.assertEqual + m = """\ +Content-Type: application/x-foo; +\tname*0*=\"us-ascii'en-us'Frank's\"; name*1*=\" Document\" + +""" + msg = email.message_from_string(m) + charset, language, s = msg.get_param('name') + eq(charset, 'us-ascii') + eq(language, 'en-us') + eq(s, "Frank's Document") + + def test_rfc2231_tick_attack(self): + m = """\ +Content-Type: application/x-foo; +\tname*0=\"us-ascii'en-us'Frank's\"; name*1=\" Document\" + +""" + msg = email.message_from_string(m) + param = msg.get_param('name') + self.assertFalse(isinstance(param, tuple)) + self.assertEqual(param, "us-ascii'en-us'Frank's Document") + + def test_rfc2231_no_extended_values(self): + eq = self.assertEqual + m = """\ +Content-Type: application/x-foo; name=\"Frank's Document\" + +""" + msg = email.message_from_string(m) + eq(msg.get_param('name'), "Frank's Document") + + def test_rfc2231_encoded_then_unencoded_segments(self): + eq = self.assertEqual + m = """\ +Content-Type: application/x-foo; +\tname*0*=\"us-ascii'en-us'My\"; +\tname*1=\" Document\"; +\tname*2*=\" For You\" + +""" + msg = email.message_from_string(m) + charset, language, s = msg.get_param('name') + eq(charset, 'us-ascii') + eq(language, 'en-us') + eq(s, 'My Document For You') + + def test_rfc2231_unencoded_then_encoded_segments(self): + eq = self.assertEqual + m = """\ +Content-Type: application/x-foo; +\tname*0=\"us-ascii'en-us'My\"; +\tname*1*=\" Document\"; +\tname*2*=\" For You\" + +""" + msg = email.message_from_string(m) + charset, language, s = msg.get_param('name') + eq(charset, 'us-ascii') + eq(language, 'en-us') + eq(s, 'My Document For You') + + + +# Tests to ensure that signed parts of an email are completely preserved, as +# required by RFC1847 section 2.1. Note that these are incomplete, because the +# email package does not currently always preserve the body. See issue 1670765. +class TestSigned(TestEmailBase): + + def _msg_and_obj(self, filename): + with openfile(findfile(filename)) as fp: + original = fp.read() + msg = email.message_from_string(original) + return original, msg + + def _signed_parts_eq(self, original, result): + # Extract the first mime part of each message + import re + repart = re.compile(r'^--([^\n]+)\n(.*?)\n--\1$', re.S | re.M) + inpart = repart.search(original).group(2) + outpart = repart.search(result).group(2) + self.assertEqual(outpart, inpart) + + def test_long_headers_as_string(self): + original, msg = self._msg_and_obj('msg_45.txt') + result = msg.as_string() + self._signed_parts_eq(original, result) + + def test_long_headers_as_string_maxheaderlen(self): + original, msg = self._msg_and_obj('msg_45.txt') + result = msg.as_string(maxheaderlen=60) + self._signed_parts_eq(original, result) + + def test_long_headers_flatten(self): + original, msg = self._msg_and_obj('msg_45.txt') + fp = StringIO() + Generator(fp).flatten(msg) + result = fp.getvalue() + self._signed_parts_eq(original, result) + + + +if __name__ == '__main__': + unittest.main() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_email/torture_test.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_email/torture_test.py Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,136 @@ +# Copyright (C) 2002-2004 Python Software Foundation +# +# A torture test of the email package. This should not be run as part of the +# standard Python test suite since it requires several meg of email messages +# collected in the wild. These source messages are not checked into the +# Python distro, but are available as part of the standalone email package at +# http://sf.net/projects/mimelib + +import sys +import os +import unittest +from io import StringIO +from types import ListType + +from email.test.test_email import TestEmailBase +from test.support import TestSkipped, run_unittest + +import email +from email import __file__ as testfile +from email.iterators import _structure + +def openfile(filename): + from os.path import join, dirname, abspath + path = abspath(join(dirname(testfile), os.pardir, 'moredata', filename)) + return open(path, 'r') + +# Prevent this test from running in the Python distro +try: + openfile('crispin-torture.txt') +except IOError: + raise TestSkipped + + + +class TortureBase(TestEmailBase): + def _msgobj(self, filename): + fp = openfile(filename) + try: + msg = email.message_from_file(fp) + finally: + fp.close() + return msg + + + +class TestCrispinTorture(TortureBase): + # Mark Crispin's torture test from the SquirrelMail project + def test_mondo_message(self): + eq = self.assertEqual + neq = self.ndiffAssertEqual + msg = self._msgobj('crispin-torture.txt') + payload = msg.get_payload() + eq(type(payload), ListType) + eq(len(payload), 12) + eq(msg.preamble, None) + eq(msg.epilogue, '\n') + # Probably the best way to verify the message is parsed correctly is to + # dump its structure and compare it against the known structure. + fp = StringIO() + _structure(msg, fp=fp) + neq(fp.getvalue(), """\ +multipart/mixed + text/plain + message/rfc822 + multipart/alternative + text/plain + multipart/mixed + text/richtext + application/andrew-inset + message/rfc822 + audio/basic + audio/basic + image/pbm + message/rfc822 + multipart/mixed + multipart/mixed + text/plain + audio/x-sun + multipart/mixed + image/gif + image/gif + application/x-be2 + application/atomicmail + audio/x-sun + message/rfc822 + multipart/mixed + text/plain + image/pgm + text/plain + message/rfc822 + multipart/mixed + text/plain + image/pbm + message/rfc822 + application/postscript + image/gif + message/rfc822 + multipart/mixed + audio/basic + audio/basic + message/rfc822 + multipart/mixed + application/postscript + text/plain + message/rfc822 + multipart/mixed + text/plain + multipart/parallel + image/gif + audio/basic + application/atomicmail + message/rfc822 + audio/x-sun +""") + + +def _testclasses(): + mod = sys.modules[__name__] + return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')] + + +def suite(): + suite = unittest.TestSuite() + for testclass in _testclasses(): + suite.addTest(unittest.makeSuite(testclass)) + return suite + + +def test_main(): + for testclass in _testclasses(): + run_unittest(testclass) + + + +if __name__ == '__main__': + unittest.main(defaultTest='suite') diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_extcall.py --- a/Lib/test/test_extcall.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_extcall.py Tue Mar 22 21:32:51 2011 +1000 @@ -228,7 +228,7 @@ >>> Foo.method(1, *[2, 3]) 5 -A PyCFunction that takes only positional parameters shoud allow an +A PyCFunction that takes only positional parameters should allow an empty keyword dictionary to pass without a complaint, but raise a TypeError if te dictionary is not empty diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_fileinput.py --- a/Lib/test/test_fileinput.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_fileinput.py Tue Mar 22 21:32:51 2011 +1000 @@ -8,11 +8,15 @@ import fileinput import collections import gzip -import bz2 import types import codecs import unittest +try: + import bz2 +except ImportError: + bz2 = None + from io import StringIO from fileinput import FileInput, hook_encoded @@ -277,7 +281,7 @@ def test_empty_files_list_specified_to_constructor(self): with FileInput(files=[]) as fi: - self.assertEquals(fi._files, ('-',)) + self.assertEqual(fi._files, ('-',)) def test__getitem__(self): """Tests invoking FileInput.__getitem__() with the current @@ -298,7 +302,7 @@ with FileInput(files=[t]) as fi: with self.assertRaises(RuntimeError) as cm: fi[1] - self.assertEquals(cm.exception.args, ("accessing lines out of order",)) + self.assertEqual(cm.exception.args, ("accessing lines out of order",)) def test__getitem__eof(self): """Tests invoking FileInput.__getitem__() with the line number but at @@ -308,7 +312,7 @@ with FileInput(files=[t]) as fi: with self.assertRaises(IndexError) as cm: fi[0] - self.assertEquals(cm.exception.args, ("end of input reached",)) + self.assertEqual(cm.exception.args, ("end of input reached",)) def test_nextfile_oserror_deleting_backup(self): """Tests invoking FileInput.nextfile() when the attempt to delete @@ -765,6 +769,7 @@ self.assertEqual(self.fake_open.invocation_count, 1) self.assertEqual(self.fake_open.last_invocation, (("test.gz", 3), {})) + @unittest.skipUnless(bz2, "Requires bz2") def test_bz2_ext_fake(self): original_open = bz2.BZ2File bz2.BZ2File = self.fake_open diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_float.py --- a/Lib/test/test_float.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_float.py Tue Mar 22 21:32:51 2011 +1000 @@ -67,7 +67,7 @@ def test_float_with_comma(self): # set locale to something that doesn't use '.' for the decimal point # float must not accept the locale specific decimal point but - # it still has to accept the normal python syntac + # it still has to accept the normal python syntax import locale if not locale.localeconv()['decimal_point'] == ',': return @@ -189,7 +189,7 @@ def assertEqualAndEqualSign(self, a, b): # fail unless a == b and a and b have the same sign bit; # the only difference from assertEqual is that this test - # distingishes -0.0 and 0.0. + # distinguishes -0.0 and 0.0. self.assertEqual((a, copysign(1.0, a)), (b, copysign(1.0, b))) @support.requires_IEEE_754 diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_gdb.py --- a/Lib/test/test_gdb.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_gdb.py Tue Mar 22 21:32:51 2011 +1000 @@ -127,6 +127,9 @@ " inferior's thread library, thread debugging will" " not be available.\n", '') + err = err.replace("warning: Cannot initialize thread debugging" + " library: Debugger service failed\n", + '') # Ensure no unexpected error messages: self.assertEqual(err, '') diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_grammar.py --- a/Lib/test/test_grammar.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_grammar.py Tue Mar 22 21:32:51 2011 +1000 @@ -350,7 +350,7 @@ ### simple_stmt: small_stmt (';' small_stmt)* [';'] x = 1; pass; del x def foo(): - # verify statments that end with semi-colons + # verify statements that end with semi-colons x = 1; pass; del x; foo() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_httpservers.py --- a/Lib/test/test_httpservers.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_httpservers.py Tue Mar 22 21:32:51 2011 +1000 @@ -38,8 +38,8 @@ self.test_object = test_object def run(self): - self.server = HTTPServer(('', 0), self.request_handler) - self.test_object.PORT = self.server.socket.getsockname()[1] + self.server = HTTPServer(('localhost', 0), self.request_handler) + self.test_object.HOST, self.test_object.PORT = self.server.socket.getsockname() self.test_object.server_started.set() self.test_object = None try: @@ -66,7 +66,7 @@ support.threading_cleanup(*self._threads) def request(self, uri, method='GET', body=None, headers={}): - self.connection = http.client.HTTPConnection('localhost', self.PORT) + self.connection = http.client.HTTPConnection(self.HOST, self.PORT) self.connection.request(method, uri, body, headers) return self.connection.getresponse() @@ -107,7 +107,7 @@ def setUp(self): BaseTestCase.setUp(self) - self.con = http.client.HTTPConnection('localhost', self.PORT) + self.con = http.client.HTTPConnection(self.HOST, self.PORT) self.con.connect() def test_command(self): @@ -462,7 +462,7 @@ return False class BaseHTTPRequestHandlerTestCase(unittest.TestCase): - """Test the functionaility of the BaseHTTPServer. + """Test the functionality of the BaseHTTPServer. Test the support for the Expect 100-continue header. """ diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_imp.py --- a/Lib/test/test_imp.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_imp.py Tue Mar 22 21:32:51 2011 +1000 @@ -210,6 +210,10 @@ self.assertEqual( imp.cache_from_source('/foo/bar/baz/qux.py', True), '/foo/bar/baz/__pycache__/qux.{}.pyc'.format(self.tag)) + # Directory with a dot, filename without dot + self.assertEqual( + imp.cache_from_source('/foo.bar/file', True), + '/foo.bar/__pycache__/file{}.pyc'.format(self.tag)) def test_cache_from_source_optimized(self): # Given the path to a .py file, return the path to its PEP 3147 diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_import.py --- a/Lib/test/test_import.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_import.py Tue Mar 22 21:32:51 2011 +1000 @@ -30,6 +30,9 @@ class ImportTests(unittest.TestCase): + def setUp(self): + remove_files(TESTFN) + def tearDown(self): unload(TESTFN) @@ -283,8 +286,6 @@ self.skipTest('path is not encodable to {}'.format(encoding)) with self.assertRaises(ImportError) as c: __import__(path) - self.assertEqual("Import by filename is not supported.", - c.exception.args[0]) def test_import_in_del_does_not_crash(self): # Issue 4236 diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_importhooks.py --- a/Lib/test/test_importhooks.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_importhooks.py Tue Mar 22 21:32:51 2011 +1000 @@ -229,7 +229,9 @@ i = ImpWrapper() sys.meta_path.append(i) sys.path_hooks.append(ImpWrapper) - mnames = ("colorsys", "urllib.parse", "distutils.core") + mnames = ( + "colorsys", "urllib.parse", "distutils.core", "sys", + ) for mname in mnames: parent = mname.split(".")[0] for n in list(sys.modules): @@ -237,7 +239,8 @@ del sys.modules[n] for mname in mnames: m = __import__(mname, globals(), locals(), ["__dummy__"]) - m.__loader__ # to make sure we actually handled the import + # to make sure we actually handled the import + self.assertTrue(hasattr(m, "__loader__")) def test_main(): diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_inspect.py --- a/Lib/test/test_inspect.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_inspect.py Tue Mar 22 21:32:51 2011 +1000 @@ -906,6 +906,53 @@ self.assertEqual(inspect.getattr_static(Something(), 'foo'), 3) self.assertEqual(inspect.getattr_static(Something, 'foo'), 3) + def test_dict_as_property(self): + test = self + test.called = False + + class Foo(dict): + a = 3 + @property + def __dict__(self): + test.called = True + return {} + + foo = Foo() + foo.a = 4 + self.assertEqual(inspect.getattr_static(foo, 'a'), 3) + self.assertFalse(test.called) + + def test_custom_object_dict(self): + test = self + test.called = False + + class Custom(dict): + def get(self, key, default=None): + test.called = True + super().get(key, default) + + class Foo(object): + a = 3 + foo = Foo() + foo.__dict__ = Custom() + self.assertEqual(inspect.getattr_static(foo, 'a'), 3) + self.assertFalse(test.called) + + def test_metaclass_dict_as_property(self): + class Meta(type): + @property + def __dict__(self): + self.executed = True + + class Thing(metaclass=Meta): + executed = False + + def __init__(self): + self.spam = 42 + + instance = Thing() + self.assertEqual(inspect.getattr_static(instance, "spam"), 42) + self.assertFalse(Thing.executed) class TestGetGeneratorState(unittest.TestCase): diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_iterlen.py --- a/Lib/test/test_iterlen.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_iterlen.py Tue Mar 22 21:32:51 2011 +1000 @@ -20,11 +20,11 @@ Some containers become temporarily immutable during iteration. This includes dicts, sets, and collections.deque. Their implementation is equally simple -though they need to permantently set their length to zero whenever there is +though they need to permanently set their length to zero whenever there is an attempt to iterate after a length mutation. The situation slightly more involved whenever an object allows length mutation -during iteration. Lists and sequence iterators are dynanamically updatable. +during iteration. Lists and sequence iterators are dynamically updatable. So, if a list is extended during iteration, the iterator will continue through the new items. If it shrinks to a point before the most recent iteration, then no further items are available and the length is reported at zero. diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_itertools.py --- a/Lib/test/test_itertools.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_itertools.py Tue Mar 22 21:32:51 2011 +1000 @@ -1526,7 +1526,7 @@ ... return chain(iterable, repeat(None)) >>> def ncycles(iterable, n): -... "Returns the seqeuence elements n times" +... "Returns the sequence elements n times" ... return chain(*repeat(iterable, n)) >>> def dotproduct(vec1, vec2): diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_marshal.py --- a/Lib/test/test_marshal.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_marshal.py Tue Mar 22 21:32:51 2011 +1000 @@ -194,7 +194,7 @@ # >>> type(loads(dumps(Int()))) # for typ in (int, float, complex, tuple, list, dict, set, frozenset): - # Note: str sublclasses are not tested because they get handled + # Note: str subclasses are not tested because they get handled # by marshal's routines for objects supporting the buffer API. subtyp = type('subtyp', (typ,), {}) self.assertRaises(ValueError, marshal.dumps, subtyp()) diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_math.py --- a/Lib/test/test_math.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_math.py Tue Mar 22 21:32:51 2011 +1000 @@ -820,7 +820,7 @@ # the following tests have been commented out since they don't # really belong here: the implementation of ** for floats is - # independent of the implemention of math.pow + # independent of the implementation of math.pow #self.assertEqual(1**NAN, 1) #self.assertEqual(1**INF, 1) #self.assertEqual(1**NINF, 1) diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_mmap.py --- a/Lib/test/test_mmap.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_mmap.py Tue Mar 22 21:32:51 2011 +1000 @@ -594,7 +594,7 @@ m2.close() m1.close() - # Test differnt tag + # Test different tag m1 = mmap.mmap(-1, len(data1), tagname="foo") m1[:] = data1 m2 = mmap.mmap(-1, len(data2), tagname="boo") diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_multiprocessing.py --- a/Lib/test/test_multiprocessing.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_multiprocessing.py Tue Mar 22 21:32:51 2011 +1000 @@ -795,7 +795,7 @@ event = self.Event() wait = TimingWrapper(event.wait) - # Removed temporaily, due to API shear, this does not + # Removed temporarily, due to API shear, this does not # work with threading._Event objects. is_set == isSet self.assertEqual(event.is_set(), False) @@ -1100,7 +1100,7 @@ self.pool.terminate() join = TimingWrapper(self.pool.join) join() - self.assertTrue(join.elapsed < 0.2) + self.assertTrue(join.elapsed < 0.5) def raising(): raise KeyError("key") @@ -1765,7 +1765,7 @@ util.Finalize(None, conn.send, args=('STOP',), exitpriority=-100) - # call mutliprocessing's cleanup function then exit process without + # call multiprocessing's cleanup function then exit process without # garbage collecting locals util._exit_function() conn.close() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_optparse.py --- a/Lib/test/test_optparse.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_optparse.py Tue Mar 22 21:32:51 2011 +1000 @@ -631,7 +631,7 @@ option_list=options) def test_required_value(self): - self.assertParseFail(["-a"], "-a option requires an argument") + self.assertParseFail(["-a"], "-a option requires 1 argument") def test_invalid_integer(self): self.assertParseFail(["-b", "5x"], diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_os.py --- a/Lib/test/test_os.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_os.py Tue Mar 22 21:32:51 2011 +1000 @@ -97,6 +97,25 @@ self.assertEqual(fobj.read().splitlines(), [b"bacon", b"eggs", b"spam"]) + def write_windows_console(self, *args): + retcode = subprocess.call(args, + # use a new console to not flood the test output + creationflags=subprocess.CREATE_NEW_CONSOLE, + # use a shell to hide the console window (SW_HIDE) + shell=True) + self.assertEqual(retcode, 0) + + @unittest.skipUnless(sys.platform == 'win32', + 'test specific to the Windows console') + def test_write_windows_console(self): + # Issue #11395: the Windows console returns an error (12: not enough + # space error) on writing into stdout if stdout mode is binary and the + # length is greater than 66,000 bytes (or less, depending on heap + # usage). + code = "print('x' * 100000)" + self.write_windows_console(sys.executable, "-c", code) + self.write_windows_console(sys.executable, "-u", "-c", code) + class TemporaryFileTests(unittest.TestCase): def setUp(self): @@ -1528,18 +1547,16 @@ def test_trailers(self): TESTFN2 = support.TESTFN + "2" - f = open(TESTFN2, 'wb') - f.write(b"abcde") - f.close() - f = open(TESTFN2, 'rb') - try: - os.sendfile(self.sockno, f.fileno(), 0, 4096, trailers=[b"12345"]) + with open(TESTFN2, 'wb') as f: + f.write(b"abcde") + with open(TESTFN2, 'rb')as f: + self.addCleanup(os.remove, TESTFN2) + os.sendfile(self.sockno, f.fileno(), 0, 4096, + trailers=[b"12345"]) self.client.close() self.server.wait() data = self.server.handler_instance.get_data() self.assertEqual(data, b"abcde12345") - finally: - os.remove(TESTFN2) if hasattr(os, "SF_NODISKIO"): def test_flags(self): diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_peepholer.py --- a/Lib/test/test_peepholer.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_peepholer.py Tue Mar 22 21:32:51 2011 +1000 @@ -19,6 +19,7 @@ def dis_single(line): return disassemble(compile(line, '', 'single')) + class TestTranforms(unittest.TestCase): def test_unot(self): @@ -294,11 +295,23 @@ self.assertNotIn('BINARY_', asm, e) self.assertNotIn('BUILD_', asm, e) +class TestBuglets(unittest.TestCase): + + def test_bug_11510(self): + # folded constant set optimization was commingled with the tuple + # unpacking optimization which would fail if the set had duplicate + # elements so that the set length was unexpected + def f(): + x, y = {1, 1} + return x, y + with self.assertRaises(ValueError): + f() + def test_main(verbose=None): import sys from test import support - test_classes = (TestTranforms,) + test_classes = (TestTranforms, TestBuglets) support.run_unittest(*test_classes) # verify reference counting diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_pkg.py --- a/Lib/test/test_pkg.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_pkg.py Tue Mar 22 21:32:51 2011 +1000 @@ -56,7 +56,7 @@ if self.root: # Only clean if the test was actually run cleanout(self.root) - # delete all modules concerning the tested hiearchy + # delete all modules concerning the tested hierarchy if self.pkgname: modules = [name for name in sys.modules if self.pkgname in name.split('.')] diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_posix.py --- a/Lib/test/test_posix.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_posix.py Tue Mar 22 21:32:51 2011 +1000 @@ -37,7 +37,7 @@ NO_ARG_FUNCTIONS = [ "ctermid", "getcwd", "getcwdb", "uname", "times", "getloadavg", "getegid", "geteuid", "getgid", "getgroups", - "getpid", "getpgrp", "getppid", "getuid", + "getpid", "getpgrp", "getppid", "getuid", "sync", ] for name in NO_ARG_FUNCTIONS: @@ -132,6 +132,156 @@ finally: fp.close() + @unittest.skipUnless(hasattr(posix, 'truncate'), "test needs posix.truncate()") + def test_truncate(self): + with open(support.TESTFN, 'w') as fp: + fp.write('test') + fp.flush() + posix.truncate(support.TESTFN, 0) + + @unittest.skipUnless(hasattr(posix, 'fexecve'), "test needs posix.fexecve()") + @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") + @unittest.skipUnless(hasattr(os, 'waitpid'), "test needs os.waitpid()") + def test_fexecve(self): + fp = os.open(sys.executable, os.O_RDONLY) + try: + pid = os.fork() + if pid == 0: + os.chdir(os.path.split(sys.executable)[0]) + posix.fexecve(fp, [sys.executable, '-c', 'pass'], os.environ) + else: + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) + finally: + os.close(fp) + + @unittest.skipUnless(hasattr(posix, 'waitid'), "test needs posix.waitid()") + @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") + def test_waitid(self): + pid = os.fork() + if pid == 0: + os.chdir(os.path.split(sys.executable)[0]) + posix.execve(sys.executable, [sys.executable, '-c', 'pass'], os.environ) + else: + res = posix.waitid(posix.P_PID, pid, posix.WEXITED) + self.assertEqual(pid, res.si_pid) + + @unittest.skipUnless(hasattr(posix, 'lockf'), "test needs posix.lockf()") + def test_lockf(self): + fd = os.open(support.TESTFN, os.O_WRONLY | os.O_CREAT) + try: + os.write(fd, b'test') + os.lseek(fd, 0, os.SEEK_SET) + posix.lockf(fd, posix.F_LOCK, 4) + # section is locked + posix.lockf(fd, posix.F_ULOCK, 4) + finally: + os.close(fd) + + @unittest.skipUnless(hasattr(posix, 'pread'), "test needs posix.pread()") + def test_pread(self): + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + try: + os.write(fd, b'test') + os.lseek(fd, 0, os.SEEK_SET) + self.assertEqual(b'es', posix.pread(fd, 2, 1)) + # the first pread() shoudn't disturb the file offset + self.assertEqual(b'te', posix.read(fd, 2)) + finally: + os.close(fd) + + @unittest.skipUnless(hasattr(posix, 'pwrite'), "test needs posix.pwrite()") + def test_pwrite(self): + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + try: + os.write(fd, b'test') + os.lseek(fd, 0, os.SEEK_SET) + posix.pwrite(fd, b'xx', 1) + self.assertEqual(b'txxt', posix.read(fd, 4)) + finally: + os.close(fd) + + @unittest.skipUnless(hasattr(posix, 'posix_fallocate'), + "test needs posix.posix_fallocate()") + def test_posix_fallocate(self): + fd = os.open(support.TESTFN, os.O_WRONLY | os.O_CREAT) + try: + posix.posix_fallocate(fd, 0, 10) + except OSError as inst: + # issue10812, ZFS doesn't appear to support posix_fallocate, + # so skip Solaris-based since they are likely to have ZFS. + if inst.errno != errno.EINVAL or not sys.platform.startswith("sunos"): + raise + finally: + os.close(fd) + + @unittest.skipUnless(hasattr(posix, 'posix_fadvise'), + "test needs posix.posix_fadvise()") + def test_posix_fadvise(self): + fd = os.open(support.TESTFN, os.O_RDONLY) + try: + posix.posix_fadvise(fd, 0, 0, posix.POSIX_FADV_WILLNEED) + finally: + os.close(fd) + + @unittest.skipUnless(hasattr(posix, 'futimes'), "test needs posix.futimes()") + def test_futimes(self): + now = time.time() + fd = os.open(support.TESTFN, os.O_RDONLY) + try: + posix.futimes(fd, None) + self.assertRaises(TypeError, posix.futimes, fd, (None, None)) + self.assertRaises(TypeError, posix.futimes, fd, (now, None)) + self.assertRaises(TypeError, posix.futimes, fd, (None, now)) + posix.futimes(fd, (int(now), int(now))) + posix.futimes(fd, (now, now)) + finally: + os.close(fd) + + @unittest.skipUnless(hasattr(posix, 'lutimes'), "test needs posix.lutimes()") + def test_lutimes(self): + now = time.time() + posix.lutimes(support.TESTFN, None) + self.assertRaises(TypeError, posix.lutimes, support.TESTFN, (None, None)) + self.assertRaises(TypeError, posix.lutimes, support.TESTFN, (now, None)) + self.assertRaises(TypeError, posix.lutimes, support.TESTFN, (None, now)) + posix.lutimes(support.TESTFN, (int(now), int(now))) + posix.lutimes(support.TESTFN, (now, now)) + + @unittest.skipUnless(hasattr(posix, 'futimens'), "test needs posix.futimens()") + def test_futimens(self): + now = time.time() + fd = os.open(support.TESTFN, os.O_RDONLY) + try: + self.assertRaises(TypeError, posix.futimens, fd, (None, None), (None, None)) + self.assertRaises(TypeError, posix.futimens, fd, (now, 0), None) + self.assertRaises(TypeError, posix.futimens, fd, None, (now, 0)) + posix.futimens(fd, (int(now), int((now - int(now)) * 1e9)), + (int(now), int((now - int(now)) * 1e9))) + finally: + os.close(fd) + + @unittest.skipUnless(hasattr(posix, 'writev'), "test needs posix.writev()") + def test_writev(self): + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + try: + os.writev(fd, (b'test1', b'tt2', b't3')) + os.lseek(fd, 0, os.SEEK_SET) + self.assertEqual(b'test1tt2t3', posix.read(fd, 10)) + finally: + os.close(fd) + + @unittest.skipUnless(hasattr(posix, 'readv'), "test needs posix.readv()") + def test_readv(self): + fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) + try: + os.write(fd, b'test1tt2t3') + os.lseek(fd, 0, os.SEEK_SET) + buf = [bytearray(i) for i in [5, 3, 2]] + self.assertEqual(posix.readv(fd, buf), 10) + self.assertEqual([b'test1', b'tt2', b't3'], [bytes(i) for i in buf]) + finally: + os.close(fd) + def test_dup(self): if hasattr(posix, 'dup'): fp = open(support.TESTFN) @@ -630,7 +780,10 @@ def test_main(): - support.run_unittest(PosixTester, PosixGroupsTester) + try: + support.run_unittest(PosixTester, PosixGroupsTester) + finally: + support.reap_children() if __name__ == '__main__': test_main() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_posixpath.py --- a/Lib/test/test_posixpath.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_posixpath.py Tue Mar 22 21:32:51 2011 +1000 @@ -6,6 +6,11 @@ import sys from posixpath import realpath, abspath, dirname, basename +try: + import posix +except ImportError: + posix = None + # An absolute path to a temporary filename for testing. We can't rely on TESTFN # being an absolute path, so we need this. @@ -150,6 +155,7 @@ def test_islink(self): self.assertIs(posixpath.islink(support.TESTFN + "1"), False) + self.assertIs(posixpath.lexists(support.TESTFN + "2"), False) f = open(support.TESTFN + "1", "wb") try: f.write(b"foo") @@ -225,6 +231,44 @@ def test_ismount(self): self.assertIs(posixpath.ismount("/"), True) + self.assertIs(posixpath.ismount(b"/"), True) + + def test_ismount_non_existent(self): + # Non-existent mountpoint. + self.assertIs(posixpath.ismount(ABSTFN), False) + try: + os.mkdir(ABSTFN) + self.assertIs(posixpath.ismount(ABSTFN), False) + finally: + safe_rmdir(ABSTFN) + + @unittest.skipUnless(support.can_symlink(), + "Test requires symlink support") + def test_ismount_symlinks(self): + # Symlinks are never mountpoints. + try: + os.symlink("/", ABSTFN) + self.assertIs(posixpath.ismount(ABSTFN), False) + finally: + os.unlink(ABSTFN) + + @unittest.skipIf(posix is None, "Test requires posix module") + def test_ismount_different_device(self): + # Simulate the path being on a different device from its parent by + # mocking out st_dev. + save_lstat = os.lstat + def fake_lstat(path): + st_ino = 0 + st_dev = 0 + if path == ABSTFN: + st_dev = 1 + st_ino = 1 + return posix.stat_result((0, st_ino, st_dev, 0, 0, 0, 0, 0, 0, 0)) + try: + os.lstat = fake_lstat + self.assertIs(posixpath.ismount(ABSTFN), True) + finally: + os.lstat = save_lstat def test_expanduser(self): self.assertEqual(posixpath.expanduser("foo"), "foo") @@ -254,6 +298,10 @@ with support.EnvironmentVarGuard() as env: env['HOME'] = '/' self.assertEqual(posixpath.expanduser("~"), "/") + # expanduser should fall back to using the password database + del env['HOME'] + home = pwd.getpwuid(os.getuid()).pw_dir + self.assertEqual(posixpath.expanduser("~"), home) def test_normpath(self): self.assertEqual(posixpath.normpath(""), ".") @@ -289,6 +337,16 @@ @unittest.skipUnless(hasattr(os, "symlink"), "Missing symlink implementation") @skip_if_ABSTFN_contains_backslash + def test_realpath_relative(self): + try: + os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN) + self.assertEqual(realpath(ABSTFN), ABSTFN+"1") + finally: + support.unlink(ABSTFN) + + @unittest.skipUnless(hasattr(os, "symlink"), + "Missing symlink implementation") + @skip_if_ABSTFN_contains_backslash def test_realpath_symlink_loops(self): # Bug #930024, return the path unchanged if we get into an infinite # symlink loop. @@ -443,6 +501,11 @@ finally: os.getcwdb = real_getcwdb + def test_sameopenfile(self): + fname = support.TESTFN + "1" + with open(fname, "wb") as a, open(fname, "wb") as b: + self.assertTrue(posixpath.sameopenfile(a.fileno(), b.fileno())) + class PosixCommonTest(test_genericpath.CommonTest): pathmodule = posixpath diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_print.py --- a/Lib/test/test_print.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_print.py Tue Mar 22 21:32:51 2011 +1000 @@ -20,7 +20,7 @@ # A dispatch table all 8 combinations of providing # sep, end, and file # I use this machinery so that I'm not just passing default -# values to print, I'm eiher passing or not passing in the +# values to print, I'm either passing or not passing in the # arguments dispatch = { (False, False, False): diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_pty.py --- a/Lib/test/test_pty.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_pty.py Tue Mar 22 21:32:51 2011 +1000 @@ -1,4 +1,4 @@ -from test.support import verbose, run_unittest, import_module +from test.support import verbose, run_unittest, import_module, reap_children #Skip these tests if either fcntl or termios is not available fcntl = import_module('fcntl') @@ -195,7 +195,10 @@ # pty.fork() passed. def test_main(verbose=None): - run_unittest(PtyTest) + try: + run_unittest(PtyTest) + finally: + reap_children() if __name__ == "__main__": test_main() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_pulldom.py --- a/Lib/test/test_pulldom.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_pulldom.py Tue Mar 22 21:32:51 2011 +1000 @@ -32,7 +32,9 @@ # fragment. # Test with a filename: - list(pulldom.parse(tstfile)) + handler = pulldom.parse(tstfile) + self.addCleanup(handler.stream.close) + list(handler) # Test with a file object: with open(tstfile, "rb") as fin: diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_pydoc.py Tue Mar 22 21:32:51 2011 +1000 @@ -190,7 +190,7 @@ missing_pattern = "no Python documentation found for '%s'" # output pattern for module with bad imports -badimport_pattern = "problem in %s - ImportError: No module named %s" +badimport_pattern = "problem in %s - ImportError: No module named %r" def run_pydoc(module_name, *args): """ diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_shutil.py --- a/Lib/test/test_shutil.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_shutil.py Tue Mar 22 21:32:51 2011 +1000 @@ -7,6 +7,7 @@ import stat import os import os.path +import functools from test import support from test.support import TESTFN from os.path import splitdrive @@ -48,6 +49,21 @@ except ImportError: ZIP_SUPPORT = find_executable('zip') +def _fake_rename(*args, **kwargs): + # Pretend the destination path is on a different filesystem. + raise OSError() + +def mock_rename(func): + @functools.wraps(func) + def wrap(*args, **kwargs): + try: + builtin_rename = os.rename + os.rename = _fake_rename + return func(*args, **kwargs) + finally: + os.rename = builtin_rename + return wrap + class TestShutil(unittest.TestCase): def setUp(self): @@ -393,6 +409,41 @@ shutil.copytree(src_dir, dst_dir, symlinks=True) self.assertIn('test.txt', os.listdir(dst_dir)) + def _copy_file(self, method): + fname = 'test.txt' + tmpdir = self.mkdtemp() + self.write_file([tmpdir, fname]) + file1 = os.path.join(tmpdir, fname) + tmpdir2 = self.mkdtemp() + method(file1, tmpdir2) + file2 = os.path.join(tmpdir2, fname) + return (file1, file2) + + @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod') + def test_copy(self): + # Ensure that the copied file exists and has the same mode bits. + file1, file2 = self._copy_file(shutil.copy) + self.assertTrue(os.path.exists(file2)) + self.assertEqual(os.stat(file1).st_mode, os.stat(file2).st_mode) + + @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.chmod') + @unittest.skipUnless(hasattr(os, 'chmod'), 'requires os.utime') + def test_copy2(self): + # Ensure that the copied file exists and has the same mode and + # modification time bits. + file1, file2 = self._copy_file(shutil.copy2) + self.assertTrue(os.path.exists(file2)) + file1_stat = os.stat(file1) + file2_stat = os.stat(file2) + self.assertEqual(file1_stat.st_mode, file2_stat.st_mode) + for attr in 'st_atime', 'st_mtime': + # The modification times may be truncated in the new file. + self.assertLessEqual(getattr(file1_stat, attr), + getattr(file2_stat, attr) + 1) + if hasattr(os, 'chflags') and hasattr(file1_stat, 'st_flags'): + self.assertEqual(getattr(file1_stat, 'st_flags'), + getattr(file2_stat, 'st_flags')) + @unittest.skipUnless(zlib, "requires zlib") def test_make_tarball(self): # creating something to tar @@ -403,6 +454,8 @@ self.write_file([tmpdir, 'sub', 'file3'], 'xxx') tmpdir2 = self.mkdtemp() + # force shutil to create the directory + os.rmdir(tmpdir2) unittest.skipUnless(splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0], "source and target should be on same drive") @@ -518,6 +571,8 @@ self.write_file([tmpdir, 'file2'], 'xxx') tmpdir2 = self.mkdtemp() + # force shutil to create the directory + os.rmdir(tmpdir2) base_name = os.path.join(tmpdir2, 'archive') _make_zipfile(base_name, tmpdir) @@ -645,6 +700,14 @@ diff = self._compare_dirs(tmpdir, tmpdir2) self.assertEqual(diff, []) + # and again, this time with the format specified + tmpdir3 = self.mkdtemp() + unpack_archive(filename, tmpdir3, format=format) + diff = self._compare_dirs(tmpdir, tmpdir3) + self.assertEqual(diff, []) + self.assertRaises(shutil.ReadError, unpack_archive, TESTFN) + self.assertRaises(ValueError, unpack_archive, TESTFN, format='xxx') + def test_unpack_registery(self): formats = get_unpack_formats() @@ -680,20 +743,11 @@ self.dst_dir = tempfile.mkdtemp() self.src_file = os.path.join(self.src_dir, filename) self.dst_file = os.path.join(self.dst_dir, filename) - # Try to create a dir in the current directory, hoping that it is - # not located on the same filesystem as the system tmp dir. - try: - self.dir_other_fs = tempfile.mkdtemp( - dir=os.path.dirname(__file__)) - self.file_other_fs = os.path.join(self.dir_other_fs, - filename) - except OSError: - self.dir_other_fs = None with open(self.src_file, "wb") as f: f.write(b"spam") def tearDown(self): - for d in (self.src_dir, self.dst_dir, self.dir_other_fs): + for d in (self.src_dir, self.dst_dir): try: if d: shutil.rmtree(d) @@ -722,21 +776,15 @@ # Move a file inside an existing dir on the same filesystem. self._check_move_file(self.src_file, self.dst_dir, self.dst_file) + @mock_rename def test_move_file_other_fs(self): # Move a file to an existing dir on another filesystem. - if not self.dir_other_fs: - # skip - return - self._check_move_file(self.src_file, self.file_other_fs, - self.file_other_fs) + self.test_move_file() + @mock_rename def test_move_file_to_dir_other_fs(self): # Move a file to another location on another filesystem. - if not self.dir_other_fs: - # skip - return - self._check_move_file(self.src_file, self.dir_other_fs, - self.file_other_fs) + self.test_move_file_to_dir() def test_move_dir(self): # Move a dir to another location on the same filesystem. @@ -749,32 +797,20 @@ except: pass + @mock_rename def test_move_dir_other_fs(self): # Move a dir to another location on another filesystem. - if not self.dir_other_fs: - # skip - return - dst_dir = tempfile.mktemp(dir=self.dir_other_fs) - try: - self._check_move_dir(self.src_dir, dst_dir, dst_dir) - finally: - try: - shutil.rmtree(dst_dir) - except: - pass + self.test_move_dir() def test_move_dir_to_dir(self): # Move a dir inside an existing dir on the same filesystem. self._check_move_dir(self.src_dir, self.dst_dir, os.path.join(self.dst_dir, os.path.basename(self.src_dir))) + @mock_rename def test_move_dir_to_dir_other_fs(self): # Move a dir inside an existing dir on another filesystem. - if not self.dir_other_fs: - # skip - return - self._check_move_dir(self.src_dir, self.dir_other_fs, - os.path.join(self.dir_other_fs, os.path.basename(self.src_dir))) + self.test_move_dir_to_dir() def test_existing_file_inside_dest_dir(self): # A file with the same name inside the destination dir already exists. diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_signal.py --- a/Lib/test/test_signal.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_signal.py Tue Mar 22 21:32:51 2011 +1000 @@ -484,9 +484,12 @@ self.assertEqual(self.hndl_called, True) def test_main(): - support.run_unittest(BasicSignalTests, InterProcessSignalTests, - WakeupSignalTests, SiginterruptTest, - ItimerTest, WindowsSignalTests) + try: + support.run_unittest(BasicSignalTests, InterProcessSignalTests, + WakeupSignalTests, SiginterruptTest, + ItimerTest, WindowsSignalTests) + finally: + support.reap_children() if __name__ == "__main__": diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_smtplib.py --- a/Lib/test/test_smtplib.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_smtplib.py Tue Mar 22 21:32:51 2011 +1000 @@ -424,6 +424,9 @@ # Simulated SMTP channel & server class SimSMTPChannel(smtpd.SMTPChannel): + # For testing failures in QUIT when using the context manager API. + quit_response = None + def __init__(self, extra_features, *args, **kw): self._extrafeatures = ''.join( [ "250-{0}\r\n".format(x) for x in extra_features ]) @@ -475,19 +478,31 @@ else: self.push('550 No access for you!') + def smtp_QUIT(self, arg): + # args is ignored + if self.quit_response is None: + super(SimSMTPChannel, self).smtp_QUIT(arg) + else: + self.push(self.quit_response) + self.close_when_done() + def handle_error(self): raise class SimSMTPServer(smtpd.SMTPServer): + # For testing failures in QUIT when using the context manager API. + quit_response = None + def __init__(self, *args, **kw): self._extra_features = [] smtpd.SMTPServer.__init__(self, *args, **kw) def handle_accepted(self, conn, addr): - self._SMTPchannel = SimSMTPChannel(self._extra_features, - self, conn, addr) + self._SMTPchannel = SimSMTPChannel( + self._extra_features, self, conn, addr) + self._SMTPchannel.quit_response = self.quit_response def process_message(self, peer, mailfrom, rcpttos, data): pass @@ -620,6 +635,25 @@ self.assertIn(sim_auth_credentials['cram-md5'], str(err)) smtp.close() + def test_with_statement(self): + with smtplib.SMTP(HOST, self.port) as smtp: + code, message = smtp.noop() + self.assertEqual(code, 250) + self.assertRaises(smtplib.SMTPServerDisconnected, smtp.send, b'foo') + with smtplib.SMTP(HOST, self.port) as smtp: + smtp.close() + self.assertRaises(smtplib.SMTPServerDisconnected, smtp.send, b'foo') + + def test_with_statement_QUIT_failure(self): + self.serv.quit_response = '421 QUIT FAILED' + with self.assertRaises(smtplib.SMTPResponseException) as error: + with smtplib.SMTP(HOST, self.port) as smtp: + smtp.noop() + self.assertEqual(error.exception.smtp_code, 421) + self.assertEqual(error.exception.smtp_error, b'QUIT FAILED') + # We don't need to clean up self.serv.quit_response because a new + # server is always instantiated in the setUp(). + #TODO: add tests for correct AUTH method fallback now that the #test infrastructure can support it. diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_socket.py --- a/Lib/test/test_socket.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_socket.py Tue Mar 22 21:32:51 2011 +1000 @@ -18,6 +18,7 @@ from weakref import proxy import signal import math +import pickle try: import fcntl except ImportError: @@ -764,6 +765,12 @@ fp.close() self.assertEqual(repr(fp), "<_io.BufferedReader name=-1>") + def test_pickle(self): + sock = socket.socket() + with sock: + for protocol in range(pickle.HIGHEST_PROTOCOL + 1): + self.assertRaises(TypeError, pickle.dumps, sock, protocol) + @unittest.skipUnless(thread, 'Threading required for this test.') class BasicTCPTest(SocketConnectedTest): diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_strlit.py --- a/Lib/test/test_strlit.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_strlit.py Tue Mar 22 21:32:51 2011 +1000 @@ -22,7 +22,7 @@ exec()/eval(), which uses a different code path. This file is really about correct treatment of encodings and -backslashes. It doens't concern itself with issues like single +backslashes. It doesn't concern itself with issues like single vs. double quotes or singly- vs. triply-quoted strings: that's dealt with elsewhere (I assume). """ diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_strptime.py --- a/Lib/test/test_strptime.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_strptime.py Tue Mar 22 21:32:51 2011 +1000 @@ -536,7 +536,7 @@ self.assertIsNot(first_time_re, second_time_re) # Possible test locale is not supported while initial locale is. # If this is the case just suppress the exception and fall-through - # to the reseting to the original locale. + # to the resetting to the original locale. except locale.Error: pass # Make sure we don't trample on the locale setting once we leave the diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_struct.py --- a/Lib/test/test_struct.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_struct.py Tue Mar 22 21:32:51 2011 +1000 @@ -463,7 +463,7 @@ test_string) def test_unpack_with_buffer(self): - # SF bug 1563759: struct.unpack doens't support buffer protocol objects + # SF bug 1563759: struct.unpack doesn't support buffer protocol objects data1 = array.array('B', b'\x12\x34\x56\x78') data2 = memoryview(b'\x12\x34\x56\x78') # XXX b'......XXXX......', 6, 4 for data in [data1, data2]: diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_subprocess.py --- a/Lib/test/test_subprocess.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_subprocess.py Tue Mar 22 21:32:51 2011 +1000 @@ -3,6 +3,7 @@ import subprocess import sys import signal +import io import os import errno import tempfile @@ -129,7 +130,9 @@ "import sys; sys.stdout.write('BDFL')\n" "sys.stdout.flush()\n" "while True: pass"], - timeout=1.5) + # Some heavily loaded buildbots (sparc Debian 3.x) require + # this much time to start and print. + timeout=3) self.fail("Expected TimeoutExpired.") self.assertEqual(c.exception.output, b'BDFL') @@ -322,6 +325,31 @@ rc = subprocess.call([sys.executable, "-c", cmd], stdout=1) self.assertEqual(rc, 2) + def test_stdout_devnull(self): + p = subprocess.Popen([sys.executable, "-c", + 'for i in range(10240):' + 'print("x" * 1024)'], + stdout=subprocess.DEVNULL) + p.wait() + self.assertEqual(p.stdout, None) + + def test_stderr_devnull(self): + p = subprocess.Popen([sys.executable, "-c", + 'import sys\n' + 'for i in range(10240):' + 'sys.stderr.write("x" * 1024)'], + stderr=subprocess.DEVNULL) + p.wait() + self.assertEqual(p.stderr, None) + + def test_stdin_devnull(self): + p = subprocess.Popen([sys.executable, "-c", + 'import sys;' + 'sys.stdin.read(1)'], + stdin=subprocess.DEVNULL) + p.wait() + self.assertEqual(p.stdin, None) + def test_cwd(self): tmpdir = tempfile.gettempdir() # We cannot use os.path.realpath to canonicalize the path, @@ -621,13 +649,15 @@ # Subsequent invocations should just return the returncode self.assertEqual(p.wait(), 0) - def test_wait_timeout(self): p = subprocess.Popen([sys.executable, "-c", "import time; time.sleep(0.1)"]) - self.assertRaises(subprocess.TimeoutExpired, p.wait, timeout=0.01) - self.assertEqual(p.wait(timeout=2), 0) - + with self.assertRaises(subprocess.TimeoutExpired) as c: + p.wait(timeout=0.01) + self.assertIn("0.01", str(c.exception)) # For coverage of __str__. + # Some heavily loaded buildbots (sparc Debian 3.x) require this much + # time to start. + self.assertEqual(p.wait(timeout=3), 0) def test_invalid_bufsize(self): # an invalid type of the bufsize argument should raise @@ -1256,6 +1286,24 @@ close_fds=False, pass_fds=(fd, ))) self.assertIn('overriding close_fds', str(context.warning)) + def test_stdout_stdin_are_single_inout_fd(self): + with io.open(os.devnull, "r+") as inout: + p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"], + stdout=inout, stdin=inout) + p.wait() + + def test_stdout_stderr_are_single_inout_fd(self): + with io.open(os.devnull, "r+") as inout: + p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"], + stdout=inout, stderr=inout) + p.wait() + + def test_stderr_stdin_are_single_inout_fd(self): + with io.open(os.devnull, "r+") as inout: + p = subprocess.Popen([sys.executable, "-c", "import sys; sys.exit(0)"], + stderr=inout, stdin=inout) + p.wait() + def test_wait_when_sigchild_ignored(self): # NOTE: sigchild_ignore.py may not be an effective test on all OSes. sigchild_ignore = support.findfile("sigchild_ignore.py", @@ -1267,6 +1315,22 @@ " non-zero with this error:\n%s" % stderr.decode('utf-8')) + def test_select_unbuffered(self): + # Issue #11459: bufsize=0 should really set the pipes as + # unbuffered (and therefore let select() work properly). + select = support.import_module("select") + p = subprocess.Popen([sys.executable, "-c", + 'import sys;' + 'sys.stdout.write("apple")'], + stdout=subprocess.PIPE, + bufsize=0) + f = p.stdout + try: + self.assertEqual(f.read(4), b"appl") + self.assertIn(f, select.select([f], [], [], 0.0)[0]) + finally: + p.wait() + @unittest.skipUnless(mswindows, "Windows specific tests") class Win32ProcessTestCase(BaseTestCase): @@ -1543,4 +1607,4 @@ support.reap_children() if __name__ == "__main__": - test_main() + unittest.main() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_sundry.py --- a/Lib/test/test_sundry.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_sundry.py Tue Mar 22 21:32:51 2011 +1000 @@ -54,7 +54,6 @@ import py_compile import sndhdr import tabnanny - import timeit try: import tty # not available on Windows except ImportError: diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_syntax.py --- a/Lib/test/test_syntax.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_syntax.py Tue Mar 22 21:32:51 2011 +1000 @@ -237,7 +237,7 @@ Test continue in finally in weird combinations. -continue in for loop under finally shouuld be ok. +continue in for loop under finally should be ok. >>> def test(): ... try: diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_sys.py --- a/Lib/test/test_sys.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_sys.py Tue Mar 22 21:32:51 2011 +1000 @@ -492,7 +492,7 @@ # provide too much opportunity for insane things to happen. # We don't want them in the interned dict and if they aren't # actually interned, we don't want to create the appearance - # that they are by allowing intern() to succeeed. + # that they are by allowing intern() to succeed. class S(str): def __hash__(self): return 123 diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_threading.py --- a/Lib/test/test_threading.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_threading.py Tue Mar 22 21:32:51 2011 +1000 @@ -578,7 +578,7 @@ # This acquires the lock and then waits until the child has forked # before returning, which will release the lock soon after. If # someone else tries to fix this test case by acquiring this lock - # before forking instead of reseting it, the test case will + # before forking instead of resetting it, the test case will # deadlock when it shouldn't. condition = w._block orig_acquire = condition.acquire diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_time.py --- a/Lib/test/test_time.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_time.py Tue Mar 22 21:32:51 2011 +1000 @@ -295,12 +295,8 @@ except ValueError: # strftime() is limited to [1; 9999] with Visual Studio return - # Issue #10864: OpenIndiana is limited to 4 digits, - # but Python doesn't raise a ValueError - #self.assertEqual(text, '12345') - #self.assertEqual(self.yearstr(123456789), '123456789') - self.assertIn(text, ('2345', '12345')) - self.assertIn(self.yearstr(123456789), ('123456789', '6789')) + self.assertEqual(text, '12345') + self.assertEqual(self.yearstr(123456789), '123456789') class _Test2dYear(_BaseYearTest): accept2dyear = 1 diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_timeit.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Lib/test/test_timeit.py Tue Mar 22 21:32:51 2011 +1000 @@ -0,0 +1,305 @@ +import timeit +import unittest +import sys +import io +import time +from textwrap import dedent + +from test.support import run_unittest +from test.support import captured_stdout +from test.support import captured_stderr + +# timeit's default number of iterations. +DEFAULT_NUMBER = 1000000 + +# timeit's default number of repetitions. +DEFAULT_REPEAT = 3 + +# XXX: some tests are commented out that would improve the coverage but take a +# long time to run because they test the default number of loops, which is +# large. The tests could be enabled if there was a way to override the default +# number of loops during testing, but this would require changing the signature +# of some functions that use the default as a default argument. + +class FakeTimer: + BASE_TIME = 42.0 + def __init__(self, seconds_per_increment=1.0): + self.count = 0 + self.setup_calls = 0 + self.seconds_per_increment=seconds_per_increment + timeit._fake_timer = self + + def __call__(self): + return self.BASE_TIME + self.count * self.seconds_per_increment + + def inc(self): + self.count += 1 + + def setup(self): + self.setup_calls += 1 + + def wrap_timer(self, timer): + """Records 'timer' and returns self as callable timer.""" + self.saved_timer = timer + return self + +class TestTimeit(unittest.TestCase): + + def tearDown(self): + try: + del timeit._fake_timer + except AttributeError: + pass + + def test_reindent_empty(self): + self.assertEqual(timeit.reindent("", 0), "") + self.assertEqual(timeit.reindent("", 4), "") + + def test_reindent_single(self): + self.assertEqual(timeit.reindent("pass", 0), "pass") + self.assertEqual(timeit.reindent("pass", 4), "pass") + + def test_reindent_multi_empty(self): + self.assertEqual(timeit.reindent("\n\n", 0), "\n\n") + self.assertEqual(timeit.reindent("\n\n", 4), "\n \n ") + + def test_reindent_multi(self): + self.assertEqual(timeit.reindent( + "print()\npass\nbreak", 0), + "print()\npass\nbreak") + self.assertEqual(timeit.reindent( + "print()\npass\nbreak", 4), + "print()\n pass\n break") + + def test_timer_invalid_stmt(self): + self.assertRaises(ValueError, timeit.Timer, stmt=None) + + def test_timer_invalid_setup(self): + self.assertRaises(ValueError, timeit.Timer, setup=None) + + fake_setup = "import timeit; timeit._fake_timer.setup()" + fake_stmt = "import timeit; timeit._fake_timer.inc()" + + def fake_callable_setup(self): + self.fake_timer.setup() + + def fake_callable_stmt(self): + self.fake_timer.inc() + + def timeit(self, stmt, setup, number=None): + self.fake_timer = FakeTimer() + t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer) + kwargs = {} + if number is None: + number = DEFAULT_NUMBER + else: + kwargs['number'] = number + delta_time = t.timeit(**kwargs) + self.assertEqual(self.fake_timer.setup_calls, 1) + self.assertEqual(self.fake_timer.count, number) + self.assertEqual(delta_time, number) + + # Takes too long to run in debug build. + #def test_timeit_default_iters(self): + # self.timeit(self.fake_stmt, self.fake_setup) + + def test_timeit_zero_iters(self): + self.timeit(self.fake_stmt, self.fake_setup, number=0) + + def test_timeit_few_iters(self): + self.timeit(self.fake_stmt, self.fake_setup, number=3) + + def test_timeit_callable_stmt(self): + self.timeit(self.fake_callable_stmt, self.fake_setup, number=3) + + def test_timeit_callable_stmt_and_setup(self): + self.timeit(self.fake_callable_stmt, + self.fake_callable_setup, number=3) + + # Takes too long to run in debug build. + #def test_timeit_function(self): + # delta_time = timeit.timeit(self.fake_stmt, self.fake_setup, + # timer=FakeTimer()) + # self.assertEqual(delta_time, DEFAULT_NUMBER) + + def test_timeit_function_zero_iters(self): + delta_time = timeit.timeit(self.fake_stmt, self.fake_setup, number=0, + timer=FakeTimer()) + self.assertEqual(delta_time, 0) + + def repeat(self, stmt, setup, repeat=None, number=None): + self.fake_timer = FakeTimer() + t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer) + kwargs = {} + if repeat is None: + repeat = DEFAULT_REPEAT + else: + kwargs['repeat'] = repeat + if number is None: + number = DEFAULT_NUMBER + else: + kwargs['number'] = number + delta_times = t.repeat(**kwargs) + self.assertEqual(self.fake_timer.setup_calls, repeat) + self.assertEqual(self.fake_timer.count, repeat * number) + self.assertEqual(delta_times, repeat * [float(number)]) + + # Takes too long to run in debug build. + #def test_repeat_default(self): + # self.repeat(self.fake_stmt, self.fake_setup) + + def test_repeat_zero_reps(self): + self.repeat(self.fake_stmt, self.fake_setup, repeat=0) + + def test_repeat_zero_iters(self): + self.repeat(self.fake_stmt, self.fake_setup, number=0) + + def test_repeat_few_reps_and_iters(self): + self.repeat(self.fake_stmt, self.fake_setup, repeat=3, number=5) + + def test_repeat_callable_stmt(self): + self.repeat(self.fake_callable_stmt, self.fake_setup, + repeat=3, number=5) + + def test_repeat_callable_stmt_and_setup(self): + self.repeat(self.fake_callable_stmt, self.fake_callable_setup, + repeat=3, number=5) + + # Takes too long to run in debug build. + #def test_repeat_function(self): + # delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, + # timer=FakeTimer()) + # self.assertEqual(delta_times, DEFAULT_REPEAT * [float(DEFAULT_NUMBER)]) + + def test_repeat_function_zero_reps(self): + delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, repeat=0, + timer=FakeTimer()) + self.assertEqual(delta_times, []) + + def test_repeat_function_zero_iters(self): + delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, number=0, + timer=FakeTimer()) + self.assertEqual(delta_times, DEFAULT_REPEAT * [0.0]) + + def assert_exc_string(self, exc_string, expected_exc_name): + exc_lines = exc_string.splitlines() + self.assertGreater(len(exc_lines), 2) + self.assertTrue(exc_lines[0].startswith('Traceback')) + self.assertTrue(exc_lines[-1].startswith(expected_exc_name)) + + def test_print_exc(self): + s = io.StringIO() + t = timeit.Timer("1/0") + try: + t.timeit() + except: + t.print_exc(s) + self.assert_exc_string(s.getvalue(), 'ZeroDivisionError') + + MAIN_DEFAULT_OUTPUT = "10 loops, best of 3: 1 sec per loop\n" + + def run_main(self, seconds_per_increment=1.0, switches=None, timer=None): + if timer is None: + timer = FakeTimer(seconds_per_increment=seconds_per_increment) + if switches is None: + args = [] + else: + args = switches[:] + args.append(self.fake_stmt) + # timeit.main() modifies sys.path, so save and restore it. + orig_sys_path = sys.path[:] + with captured_stdout() as s: + timeit.main(args=args, _wrap_timer=timer.wrap_timer) + sys.path[:] = orig_sys_path[:] + return s.getvalue() + + def test_main_bad_switch(self): + s = self.run_main(switches=['--bad-switch']) + self.assertEqual(s, dedent("""\ + option --bad-switch not recognized + use -h/--help for command line help + """)) + + def test_main_seconds(self): + s = self.run_main(seconds_per_increment=5.5) + self.assertEqual(s, "10 loops, best of 3: 5.5 sec per loop\n") + + def test_main_milliseconds(self): + s = self.run_main(seconds_per_increment=0.0055) + self.assertEqual(s, "100 loops, best of 3: 5.5 msec per loop\n") + + def test_main_microseconds(self): + s = self.run_main(seconds_per_increment=0.0000025, switches=['-n100']) + self.assertEqual(s, "100 loops, best of 3: 2.5 usec per loop\n") + + def test_main_fixed_iters(self): + s = self.run_main(seconds_per_increment=2.0, switches=['-n35']) + self.assertEqual(s, "35 loops, best of 3: 2 sec per loop\n") + + def test_main_setup(self): + s = self.run_main(seconds_per_increment=2.0, + switches=['-n35', '-s', 'print("CustomSetup")']) + self.assertEqual(s, "CustomSetup\n" * 3 + + "35 loops, best of 3: 2 sec per loop\n") + + def test_main_fixed_reps(self): + s = self.run_main(seconds_per_increment=60.0, switches=['-r9']) + self.assertEqual(s, "10 loops, best of 9: 60 sec per loop\n") + + def test_main_negative_reps(self): + s = self.run_main(seconds_per_increment=60.0, switches=['-r-5']) + self.assertEqual(s, "10 loops, best of 1: 60 sec per loop\n") + + def test_main_help(self): + s = self.run_main(switches=['-h']) + # Note: It's not clear that the trailing space was intended as part of + # the help text, but since it's there, check for it. + self.assertEqual(s, timeit.__doc__ + ' ') + + def test_main_using_time(self): + fake_timer = FakeTimer() + s = self.run_main(switches=['-t'], timer=fake_timer) + self.assertEqual(s, self.MAIN_DEFAULT_OUTPUT) + self.assertIs(fake_timer.saved_timer, time.time) + + def test_main_using_clock(self): + fake_timer = FakeTimer() + s = self.run_main(switches=['-c'], timer=fake_timer) + self.assertEqual(s, self.MAIN_DEFAULT_OUTPUT) + self.assertIs(fake_timer.saved_timer, time.clock) + + def test_main_verbose(self): + s = self.run_main(switches=['-v']) + self.assertEqual(s, dedent("""\ + 10 loops -> 10 secs + raw times: 10 10 10 + 10 loops, best of 3: 1 sec per loop + """)) + + def test_main_very_verbose(self): + s = self.run_main(seconds_per_increment=0.000050, switches=['-vv']) + self.assertEqual(s, dedent("""\ + 10 loops -> 0.0005 secs + 100 loops -> 0.005 secs + 1000 loops -> 0.05 secs + 10000 loops -> 0.5 secs + raw times: 0.5 0.5 0.5 + 10000 loops, best of 3: 50 usec per loop + """)) + + def test_main_exception(self): + with captured_stderr() as error_stringio: + s = self.run_main(switches=['1/0']) + self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') + + def test_main_exception_fixed_reps(self): + with captured_stderr() as error_stringio: + s = self.run_main(switches=['-n1', '1/0']) + self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') + + +def test_main(): + run_unittest(TestTimeit) + +if __name__ == '__main__': + test_main() diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_trace.py --- a/Lib/test/test_trace.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_trace.py Tue Mar 22 21:32:51 2011 +1000 @@ -209,7 +209,7 @@ (self.my_py_filename, firstlineno + 4): 1, } - # When used through 'run', some other spurios counts are produced, like + # When used through 'run', some other spurious counts are produced, like # the settrace of threading, which we ignore, just making sure that the # counts fo traced_func_loop were right. # diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_urllib.py Tue Mar 22 21:32:51 2011 +1000 @@ -91,7 +91,7 @@ "did not return the expected text") def test_close(self): - # Test close() by calling it hear and then having it be called again + # Test close() by calling it here and then having it be called again # by the tearDown() method for the test self.returned_obj.close() @@ -174,6 +174,14 @@ finally: self.unfakehttp() + def test_willclose(self): + self.fakehttp(b"HTTP/1.1 200 OK\r\n\r\nHello!") + try: + resp = urlopen("http://www.python.org") + self.assertTrue(resp.fp.will_close) + finally: + self.unfakehttp() + def test_read_0_9(self): # "0.9" response accepted (but not "simple responses" without # a status line) @@ -1021,7 +1029,7 @@ # Just commented them out. # Can't really tell why keep failing in windows and sparc. -# Everywhere else they work ok, but on those machines, someteimes +# Everywhere else they work ok, but on those machines, sometimes # fail in one of the tests, sometimes in other. I have a linux, and # the tests go ok. # If anybody has one of the problematic enviroments, please help! diff -r 9dcd88977694 -r 650549138a3d Lib/test/test_warnings.py --- a/Lib/test/test_warnings.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/test/test_warnings.py Tue Mar 22 21:32:51 2011 +1000 @@ -332,7 +332,7 @@ sys.argv = argv def test_warn_explicit_type_errors(self): - # warn_explicit() shoud error out gracefully if it is given objects + # warn_explicit() should error out gracefully if it is given objects # of the wrong types. # lineno is expected to be an integer. self.assertRaises(TypeError, self.module.warn_explicit, diff -r 9dcd88977694 -r 650549138a3d Lib/timeit.py --- a/Lib/timeit.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/timeit.py Tue Mar 22 21:32:51 2011 +1000 @@ -232,10 +232,10 @@ """Convenience function to create Timer object and call repeat method.""" return Timer(stmt, setup, timer).repeat(repeat, number) -def main(args=None): +def main(args=None, *, _wrap_timer=None): """Main program, used when run as a script. - The optional argument specifies the command line to be parsed, + The optional 'args' argument specifies the command line to be parsed, defaulting to sys.argv[1:]. The return value is an exit code to be passed to sys.exit(); it @@ -244,6 +244,10 @@ When an exception happens during timing, a traceback is printed to stderr and the return value is 1. Exceptions at other times (including the template compilation) are not caught. + + '_wrap_timer' is an internal interface used for unit testing. If it + is not None, it must be a callable that accepts a timer function + and returns another timer function (used for unit testing). """ if args is None: args = sys.argv[1:] @@ -289,6 +293,8 @@ # directory) import os sys.path.insert(0, os.curdir) + if _wrap_timer is not None: + timer = _wrap_timer(timer) t = Timer(stmt, setup, timer) if number == 0: # determine number so that 0.2 <= total time < 2.0 diff -r 9dcd88977694 -r 650549138a3d Lib/tkinter/test/test_ttk/test_functions.py --- a/Lib/tkinter/test/test_ttk/test_functions.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/tkinter/test/test_ttk/test_functions.py Tue Mar 22 21:32:51 2011 +1000 @@ -135,7 +135,7 @@ # minimum acceptable for image type self.assertEqual(ttk._format_elemcreate('image', False, 'test'), ("test ", ())) - # specifiyng a state spec + # specifying a state spec self.assertEqual(ttk._format_elemcreate('image', False, 'test', ('', 'a')), ("test {} a", ())) # state spec with multiple states diff -r 9dcd88977694 -r 650549138a3d Lib/tkinter/tix.py --- a/Lib/tkinter/tix.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/tkinter/tix.py Tue Mar 22 21:32:51 2011 +1000 @@ -171,7 +171,7 @@ return self.tk.call('tix', 'getimage', name) def tix_option_get(self, name): - """Gets the options manitained by the Tix + """Gets the options maintained by the Tix scheme mechanism. Available options include: active_bg active_fg bg @@ -576,7 +576,7 @@ class ComboBox(TixWidget): """ComboBox - an Entry field with a dropdown menu. The user can select a - choice by either typing in the entry subwdget or selecting from the + choice by either typing in the entry subwidget or selecting from the listbox subwidget. Subwidget Class @@ -869,7 +869,7 @@ """HList - Hierarchy display widget can be used to display any data that have a hierarchical structure, for example, file system directory trees. The list entries are indented and connected by branch lines - according to their places in the hierachy. + according to their places in the hierarchy. Subwidgets - None""" @@ -1519,7 +1519,7 @@ self.tk.call(self._w, 'selection', 'set', first, last) class Tree(TixWidget): - """Tree - The tixTree widget can be used to display hierachical + """Tree - The tixTree widget can be used to display hierarchical data in a tree form. The user can adjust the view of the tree by opening or closing parts of the tree.""" diff -r 9dcd88977694 -r 650549138a3d Lib/tkinter/ttk.py --- a/Lib/tkinter/ttk.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/tkinter/ttk.py Tue Mar 22 21:32:51 2011 +1000 @@ -707,7 +707,7 @@ textvariable, values, width """ # The "values" option may need special formatting, so leave to - # _format_optdict the responsability to format it + # _format_optdict the responsibility to format it if "values" in kw: kw["values"] = _format_optdict({'v': kw["values"]})[1] diff -r 9dcd88977694 -r 650549138a3d Lib/turtle.py --- a/Lib/turtle.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/turtle.py Tue Mar 22 21:32:51 2011 +1000 @@ -1488,7 +1488,7 @@ Optional arguments: canvwidth -- positive integer, new width of canvas in pixels canvheight -- positive integer, new height of canvas in pixels - bg -- colorstring or color-tupel, new backgroundcolor + bg -- colorstring or color-tuple, new backgroundcolor If no arguments are given, return current (canvaswidth, canvasheight) Do not alter the drawing window. To observe hidden parts of @@ -3242,9 +3242,9 @@ fill="", width=ps) # Turtle now at position old, self._position = old - ## if undo is done during crating a polygon, the last vertex - ## will be deleted. if the polygon is entirel deleted, - ## creatigPoly will be set to False. + ## if undo is done during creating a polygon, the last vertex + ## will be deleted. if the polygon is entirely deleted, + ## creatingPoly will be set to False. ## Polygons created before the last one will not be affected by undo() if self._creatingPoly: if len(self._poly) > 0: @@ -3796,7 +3796,7 @@ class Turtle(RawTurtle): - """RawTurtle auto-crating (scrolled) canvas. + """RawTurtle auto-creating (scrolled) canvas. When a Turtle object is created or a function derived from some Turtle method is called a TurtleScreen object is automatically created. @@ -3836,7 +3836,7 @@ filename -- a string, used as filename default value is turtle_docstringdict - Has to be called explicitely, (not used by the turtle-graphics classes) + Has to be called explicitly, (not used by the turtle-graphics classes) The docstring dictionary will be written to the Python script .py It is intended to serve as a template for translation of the docstrings into different languages. diff -r 9dcd88977694 -r 650549138a3d Lib/turtledemo/bytedesign.py --- a/Lib/turtledemo/bytedesign.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/turtledemo/bytedesign.py Tue Mar 22 21:32:51 2011 +1000 @@ -4,7 +4,7 @@ tdemo_bytedesign.py An example adapted from the example-suite -of PythonCard's turtle graphcis. +of PythonCard's turtle graphics. It's based on an article in BYTE magazine Problem Solving with Logo: Using Turtle diff -r 9dcd88977694 -r 650549138a3d Lib/unittest/result.py --- a/Lib/unittest/result.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/unittest/result.py Tue Mar 22 21:32:51 2011 +1000 @@ -59,6 +59,9 @@ "Called when the given test is about to be run" self.testsRun += 1 self._mirrorOutput = False + self._setupStdout() + + def _setupStdout(self): if self.buffer: if self._stderr_buffer is None: self._stderr_buffer = io.StringIO() @@ -74,6 +77,10 @@ def stopTest(self, test): """Called when the given test has been run""" + self._restoreStdout() + self._mirrorOutput = False + + def _restoreStdout(self): if self.buffer: if self._mirrorOutput: output = sys.stdout.getvalue() @@ -93,7 +100,6 @@ self._stdout_buffer.truncate() self._stderr_buffer.seek(0) self._stderr_buffer.truncate() - self._mirrorOutput = False def stopTestRun(self): """Called once after all tests are executed. diff -r 9dcd88977694 -r 650549138a3d Lib/unittest/suite.py --- a/Lib/unittest/suite.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/unittest/suite.py Tue Mar 22 21:32:51 2011 +1000 @@ -8,6 +8,11 @@ __unittest = True +def _call_if_exists(parent, attr): + func = getattr(parent, attr, lambda: None) + func() + + class BaseTestSuite(object): """A simple test suite that doesn't provide class or module shared fixtures. """ @@ -133,6 +138,7 @@ setUpClass = getattr(currentClass, 'setUpClass', None) if setUpClass is not None: + _call_if_exists(result, '_setupStdout') try: setUpClass() except Exception as e: @@ -142,6 +148,8 @@ className = util.strclass(currentClass) errorName = 'setUpClass (%s)' % className self._addClassOrModuleLevelException(result, e, errorName) + finally: + _call_if_exists(result, '_restoreStdout') def _get_previous_module(self, result): previousModule = None @@ -167,6 +175,7 @@ return setUpModule = getattr(module, 'setUpModule', None) if setUpModule is not None: + _call_if_exists(result, '_setupStdout') try: setUpModule() except Exception as e: @@ -175,6 +184,8 @@ result._moduleSetUpFailed = True errorName = 'setUpModule (%s)' % currentModule self._addClassOrModuleLevelException(result, e, errorName) + finally: + _call_if_exists(result, '_restoreStdout') def _addClassOrModuleLevelException(self, result, exception, errorName): error = _ErrorHolder(errorName) @@ -198,6 +209,7 @@ tearDownModule = getattr(module, 'tearDownModule', None) if tearDownModule is not None: + _call_if_exists(result, '_setupStdout') try: tearDownModule() except Exception as e: @@ -205,6 +217,8 @@ raise errorName = 'tearDownModule (%s)' % previousModule self._addClassOrModuleLevelException(result, e, errorName) + finally: + _call_if_exists(result, '_restoreStdout') def _tearDownPreviousClass(self, test, result): previousClass = getattr(result, '_previousTestClass', None) @@ -220,6 +234,7 @@ tearDownClass = getattr(previousClass, 'tearDownClass', None) if tearDownClass is not None: + _call_if_exists(result, '_setupStdout') try: tearDownClass() except Exception as e: @@ -228,7 +243,8 @@ className = util.strclass(previousClass) errorName = 'tearDownClass (%s)' % className self._addClassOrModuleLevelException(result, e, errorName) - + finally: + _call_if_exists(result, '_restoreStdout') class _ErrorHolder(object): diff -r 9dcd88977694 -r 650549138a3d Lib/unittest/test/test_loader.py --- a/Lib/unittest/test/test_loader.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/unittest/test/test_loader.py Tue Mar 22 21:32:51 2011 +1000 @@ -239,7 +239,7 @@ try: loader.loadTestsFromName('sdasfasfasdf') except ImportError as e: - self.assertEqual(str(e), "No module named sdasfasfasdf") + self.assertEqual(str(e), "No module named 'sdasfasfasdf'") else: self.fail("TestLoader.loadTestsFromName failed to raise ImportError") @@ -619,7 +619,7 @@ try: loader.loadTestsFromNames(['sdasfasfasdf']) except ImportError as e: - self.assertEqual(str(e), "No module named sdasfasfasdf") + self.assertEqual(str(e), "No module named 'sdasfasfasdf'") else: self.fail("TestLoader.loadTestsFromNames failed to raise ImportError") diff -r 9dcd88977694 -r 650549138a3d Lib/unittest/test/test_program.py --- a/Lib/unittest/test/test_program.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/unittest/test/test_program.py Tue Mar 22 21:32:51 2011 +1000 @@ -189,7 +189,7 @@ class FakeTP(unittest.TestProgram): def parseArgs(self, *args, **kw): pass def runTests(self, *args, **kw): pass - warnoptions = sys.warnoptions + warnoptions = sys.warnoptions[:] try: sys.warnoptions[:] = [] # no warn options, no arg -> default diff -r 9dcd88977694 -r 650549138a3d Lib/unittest/test/test_result.py --- a/Lib/unittest/test/test_result.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/unittest/test/test_result.py Tue Mar 22 21:32:51 2011 +1000 @@ -497,5 +497,72 @@ self.assertEqual(result._original_stderr.getvalue(), expectedErrMessage) self.assertMultiLineEqual(message, expectedFullMessage) + def testBufferSetupClass(self): + result = unittest.TestResult() + result.buffer = True + + class Foo(unittest.TestCase): + @classmethod + def setUpClass(cls): + 1/0 + def test_foo(self): + pass + suite = unittest.TestSuite([Foo('test_foo')]) + suite(result) + self.assertEqual(len(result.errors), 1) + + def testBufferTearDownClass(self): + result = unittest.TestResult() + result.buffer = True + + class Foo(unittest.TestCase): + @classmethod + def tearDownClass(cls): + 1/0 + def test_foo(self): + pass + suite = unittest.TestSuite([Foo('test_foo')]) + suite(result) + self.assertEqual(len(result.errors), 1) + + def testBufferSetUpModule(self): + result = unittest.TestResult() + result.buffer = True + + class Foo(unittest.TestCase): + def test_foo(self): + pass + class Module(object): + @staticmethod + def setUpModule(): + 1/0 + + Foo.__module__ = 'Module' + sys.modules['Module'] = Module + self.addCleanup(sys.modules.pop, 'Module') + suite = unittest.TestSuite([Foo('test_foo')]) + suite(result) + self.assertEqual(len(result.errors), 1) + + def testBufferTearDownModule(self): + result = unittest.TestResult() + result.buffer = True + + class Foo(unittest.TestCase): + def test_foo(self): + pass + class Module(object): + @staticmethod + def tearDownModule(): + 1/0 + + Foo.__module__ = 'Module' + sys.modules['Module'] = Module + self.addCleanup(sys.modules.pop, 'Module') + suite = unittest.TestSuite([Foo('test_foo')]) + suite(result) + self.assertEqual(len(result.errors), 1) + + if __name__ == '__main__': unittest.main() diff -r 9dcd88977694 -r 650549138a3d Lib/urllib/request.py --- a/Lib/urllib/request.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/urllib/request.py Tue Mar 22 21:32:51 2011 +1000 @@ -1657,6 +1657,12 @@ headers["Authorization"] = "Basic %s" % auth if realhost: headers["Host"] = realhost + + # Add Connection:close as we don't support persistent connections yet. + # This helps in closing the socket and avoiding ResourceWarning + + headers["Connection"] = "close" + for header, value in self.addheaders: headers[header] = value diff -r 9dcd88977694 -r 650549138a3d Lib/urllib/response.py --- a/Lib/urllib/response.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/urllib/response.py Tue Mar 22 21:32:51 2011 +1000 @@ -33,12 +33,15 @@ id(self), self.fp) def close(self): + if self.fp: + self.fp.close() + self.fp = None self.read = None self.readline = None self.readlines = None self.fileno = None - if self.fp: self.fp.close() - self.fp = None + self.__iter__ = None + self.__next__ = None def __enter__(self): if self.fp is None: diff -r 9dcd88977694 -r 650549138a3d Lib/webbrowser.py --- a/Lib/webbrowser.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/webbrowser.py Tue Mar 22 21:32:51 2011 +1000 @@ -228,15 +228,9 @@ else: # for TTY browsers, we need stdin/out inout = None - # if possible, put browser in separate process group, so - # keyboard interrupts don't affect browser as well as Python - setsid = getattr(os, 'setsid', None) - if not setsid: - setsid = getattr(os, 'setpgrp', None) - p = subprocess.Popen(cmdline, close_fds=True, stdin=inout, stdout=(self.redirect_stdout and inout or None), - stderr=inout, preexec_fn=setsid) + stderr=inout, start_new_session=True) if remote: # wait five secons. If the subprocess is not finished, the # remote invocation has (hopefully) started a new instance. diff -r 9dcd88977694 -r 650549138a3d Lib/xml/dom/minicompat.py --- a/Lib/xml/dom/minicompat.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/xml/dom/minicompat.py Tue Mar 22 21:32:51 2011 +1000 @@ -6,7 +6,7 @@ # # NodeList -- lightest possible NodeList implementation # -# EmptyNodeList -- lightest possible NodeList that is guarateed to +# EmptyNodeList -- lightest possible NodeList that is guaranteed to # remain empty (immutable) # # StringTypes -- tuple of defined string types diff -r 9dcd88977694 -r 650549138a3d Lib/xml/dom/minidom.py --- a/Lib/xml/dom/minidom.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/xml/dom/minidom.py Tue Mar 22 21:32:51 2011 +1000 @@ -1905,7 +1905,7 @@ e._call_user_data_handler(operation, n, entity) else: # Note the cloning of Document and DocumentType nodes is - # implemenetation specific. minidom handles those cases + # implementation specific. minidom handles those cases # directly in the cloneNode() methods. raise xml.dom.NotSupportedErr("Cannot clone node %s" % repr(node)) diff -r 9dcd88977694 -r 650549138a3d Lib/xmlrpc/server.py --- a/Lib/xmlrpc/server.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Lib/xmlrpc/server.py Tue Mar 22 21:32:51 2011 +1000 @@ -240,7 +240,7 @@ marshalled data. For backwards compatibility, a dispatch function can be provided as an argument (see comment in SimpleXMLRPCRequestHandler.do_POST) but overriding the - existing method through subclassing is the prefered means + existing method through subclassing is the preferred means of changing method dispatch behavior. """ diff -r 9dcd88977694 -r 650549138a3d Mac/BuildScript/build-installer.py --- a/Mac/BuildScript/build-installer.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Mac/BuildScript/build-installer.py Tue Mar 22 21:32:51 2011 +1000 @@ -362,7 +362,7 @@ def runCommand(commandline): """ - Run a command and raise RuntimeError if it fails. Output is surpressed + Run a command and raise RuntimeError if it fails. Output is suppressed unless the command fails. """ fd = os.popen(commandline, 'r') diff -r 9dcd88977694 -r 650549138a3d Misc/ACKS --- a/Misc/ACKS Tue Mar 22 21:31:46 2011 +1000 +++ b/Misc/ACKS Tue Mar 22 21:32:51 2011 +1000 @@ -100,6 +100,7 @@ Forest Bond Gawain Bolton Gregory Bond +Matias Bordese Jurjen Bos Peter Bosch Eric Bouck @@ -226,6 +227,7 @@ Ismail Donmez Marcos Donolo Dima Dorfman +Yves Dorfsman Cesar Douady Dean Draayer Fred L. Drake, Jr. @@ -375,6 +377,7 @@ Jason Hildebrand Richie Hindle Konrad Hinsen +Michael Henry David Hobley Tim Hochberg Joerg-Cyril Hoehle @@ -448,6 +451,7 @@ Peter van Kampen Rafe Kaplan Jacob Kaplan-Moss +Arkady Koplyarov Lou Kates Hiroaki Kawai Sebastien Keim diff -r 9dcd88977694 -r 650549138a3d Misc/NEWS --- a/Misc/NEWS Tue Mar 22 21:31:46 2011 +1000 +++ b/Misc/NEWS Tue Mar 22 21:32:51 2011 +1000 @@ -10,8 +10,21 @@ Core and Builtins ----------------- +- Issue #11395: io.FileIO().write() clamps the data length to 32,767 bytes on + Windows if the file is a TTY to workaround a Windows bug. The Windows console + returns an error (12: not enough space error) on writing into stdout if + stdout mode is binary and the length is greater than 66,000 bytes (or less, + depending on heap usage). + +- Issue #11320: fix bogus memory management in Modules/getpath.c, leading to + a possible crash when calling Py_SetPath(). + - _ast.__version__ is now a Mercurial integer and hex revision. +- Issue #11432: A bug was introduced in subprocess.Popen on posix systems with + 3.2.0 where the stdout or stderr file descriptor being the same as the stdin + file descriptor would raise an exception. webbrowser.open would fail. fixed. + - Issue #9856: Change object.__format__ with a non-empty format string to be a DeprecationWarning. In 3.2 it was a PendingDeprecationWarning. In 3.4 it will be a TypeError. @@ -68,11 +81,75 @@ Library ------- +- Issue #11371: Mark getopt error messages as localizable. Patch by Filip + Gruszczyński. + +- Issue #11628: cmp_to_key generated class should use __slots__ + +- Issue #5537: Fix time2isoz() and time2netscape() functions of + httplib.cookiejar for expiration year greater than 2038 on 32-bit systems. + +- Issue #4391: Use proper gettext plural forms in optparse. + +- Issue #11127: Raise a TypeError when trying to pickle a socket object. + +- Issue #11563: Connection:close header is sent by requests using URLOpener + class which helps in closing of sockets after connection is over. Patch + contributions by Jeff McNeil and Nadeem Vawda. + +- Issue #11459: A ``bufsize`` value of 0 in subprocess.Popen() really creates + unbuffered pipes, such that select() works properly on them. + +- Issue #5421: Fix misleading error message when one of socket.sendto()'s + arguments has the wrong type. Patch by Nikita Vetoshkin. + +- Issue #10812: Add some extra posix functions to the os module. + +- Issue #10979: unittest stdout buffering now works with class and module + setup and teardown. + +- Issue #11577: fix ResourceWarning triggered by improved binhex test coverage + +- Issue #11243: fix the parameter querying methods of Message to work if + the headers contain un-encoded non-ASCII data. + +- Issue #11401: fix handling of headers with no value; this fixes a regression + relative to Python2 and the result is now the same as it was in Python2. + +- Issue #9298: base64 bodies weren't being folded to line lengths less than 78, + which was a regression relative to Python2. Unlike Python2, the last line + of the folded body now ends with a carriage return. + +- Issue #11560: shutil.unpack_archive now correctly handles the format + parameter. Patch by Evan Dandrea. + +- Issue #5870: Add `subprocess.DEVNULL` constant. + +- Issue #11133: fix two cases where inspect.getattr_static can trigger code + execution. Patch by Andreas Stührk. + +- Issue #11569: use absolute path to the sysctl command in multiprocessing to + ensure that it will be found regardless of the shell PATH. This ensures + that multiprocessing.cpu_count works on default installs of MacOSX. + +- Issue #11501: disutils.archive_utils.make_zipfile no longer fails if zlib is + not installed. Instead, the zipfile.ZIP_STORED compression is used to create + the ZipFile. Patch by Natalia B. Bidart. + +- Issue #11289: `smtp.SMTP` class becomes a context manager so it can be used + in a `with` statement. Contributed by Giampaolo Rodola. + +- Issue #11554: Fixed support for Japanese codecs; previously the body output + encoding was not done if euc-jp or shift-jis was specified as the charset. + +- Issue #11509: Significantly increase test coverage of fileinput. + Patch by Denver Coneybeare at PyCon 2011 Sprints. + - Issue #11407: `TestCase.run` returns the result object used or created. Contributed by Janathan Hartley. -- Issue #11500: Fixed a bug in the os x proxy bypass code for fully qualified - IP addresses in the proxy exception list. +- Issue #11500: Fixed a bug in the os x proxy bypass code for fully qualified + IP addresses in the proxy exception list. - Issue #11491: dbm.error is no longer raised when dbm.open is called with the "n" as the flag argument and the file exists. The behavior matches @@ -116,7 +193,7 @@ The _thread.error exception is now an alias of RuntimeError. Patch by Filip Gruszczyński. Patch for _dummy_thread by Aymeric Augustin. -- Issue 8594: ftplib now provides a source_address parameter to specify which +- Issue #8594: ftplib now provides a source_address parameter to specify which (address, port) to bind to before connecting. - Issue #11326: Add the missing connect_ex() implementation for SSL sockets, @@ -132,9 +209,9 @@ - Issue #7322: Trying to read from a socket's file-like object after a timeout occurred now raises an error instead of silently losing data. -- Issue 11291: poplib.POP no longer suppresses errors on quit(). - -- Issue 11177: asyncore's create_socket() arguments can now be omitted. +- Issue #11291: poplib.POP no longer suppresses errors on quit(). + +- Issue #11177: asyncore's create_socket() arguments can now be omitted. - Issue #6064: Add a ``daemon`` keyword argument to the threading.Thread and multiprocessing.Process constructors in order to override the @@ -150,7 +227,7 @@ to 1000x faster in some cases). It is still one to two order of magnitudes slower than binary tell(). -- Issue 10882: Add os.sendfile function. +- Issue #10882: Add os.sendfile function. - Issue #10868: Allow usage of the register method of an ABC as a class decorator. @@ -203,7 +280,30 @@ Tests ----- -- Issue #11505: improves test coverage of string.py +- The email test suite now lives in the Lib/test/test_email package. The test + harness code has also been modernized to allow use of new unittest features. + +- regrtest now discovers test packages as well as test modules. + +- Issue #11577: improve test coverage of binhex.py. Patch by Arkady Koplyarov. + +- New test_crashers added to exercise the scripts in the Lib/test/crashers + directory and confirm they fail as expected + +- Issue #11578: added test for the timeit module. Patch Michael Henry. + +- Issue #11503: improve test coverage of posixpath.py. Patch by Evan Dandrea. + +- Issue #11505: improves test coverage of string.py. Patch by Alicia + Arlen. + +- Issue #11548: Improve test coverage of the shutil module. Patch by + Evan Dandrea. + +- Issue #11554: Reactivated test_email_codecs. + +- Issue #11505: improves test coverage of string.py. Patch by Alicia + Arlen - Issue #11490: test_subprocess:test_leaking_fds_on_error no longer gives a false positive if the last directory in the path is inaccessible. @@ -226,6 +326,11 @@ - Issue #10990: Prevent tests from clobbering a set trace function. +C-API +----- + +- PY_PATCHLEVEL_REVISION has been removed, since it's meaningless with Mercurial. + What's New in Python 3.2? ========================= @@ -553,8 +658,8 @@ time. `sys.stderr` now looked up at instantiation time. Fix contributed by Mark Roddy. -- Issue #10753: Characters ';','=' and ',' in the PATH_INFO environment variable - won't be quoted when the URI is constructed by the wsgiref.util 's request_uri +- Issue #10753: Characters ';', '=' and ',' in the PATH_INFO environment variable + won't be quoted when the URI is constructed by the wsgiref.util's request_uri method. According to RFC 3986, these characters can be a part of params in PATH component of URI and need not be quoted. @@ -1024,9 +1129,9 @@ comment prefixes, name of the DEFAULT section, empty lines in multiline values, and indentation. -- Issue 10326: unittest.TestCase instances can be pickled. - -- Issue 9926: Wrapped TestSuite subclass does not get __call__ executed. +- Issue #10326: unittest.TestCase instances can be pickled. + +- Issue #9926: Wrapped TestSuite subclass does not get __call__ executed. - Issue #9920: Skip tests for cmath.atan and cmath.atanh applied to complex zeros on systems where the log1p function fails to respect the sign of zero. @@ -1677,7 +1782,7 @@ - Issue #9853: Fix the signature of SSLSocket.recvfrom() and SSLSocket.sendto() to match the corresponding socket methods. -- Issue 9840: Added a decorator to reprlib for wrapping __repr__ methods to make +- Issue #9840: Added a decorator to reprlib for wrapping __repr__ methods to make them handle recursive calls within the same thread. - logging: Enhanced HTTPHandler with secure and credentials initializers. diff -r 9dcd88977694 -r 650549138a3d Misc/python-wing4.wpr --- a/Misc/python-wing4.wpr Tue Mar 22 21:31:46 2011 +1000 +++ b/Misc/python-wing4.wpr Tue Mar 22 21:32:51 2011 +1000 @@ -5,11 +5,12 @@ ################################################################## [project attributes] proj.directory-list = [{'dirloc': loc('..'), - 'excludes': [u'Lib/unittest/test/__pycache__', + 'excludes': [u'.hg', + u'Lib/unittest/__pycache__', + u'Lib/unittest/test/__pycache__', u'Lib/__pycache__', - u'Doc/build', - u'Lib/unittest/__pycache__', - u'build'], + u'build', + u'Doc/build'], 'filter': '*', 'include_hidden': False, 'recursive': True, diff -r 9dcd88977694 -r 650549138a3d Modules/_ctypes/_ctypes.c --- a/Modules/_ctypes/_ctypes.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_ctypes/_ctypes.c Tue Mar 22 21:32:51 2011 +1000 @@ -3317,7 +3317,7 @@ /* XXX XXX This would allow to pass additional options. For COM method *implementations*, we would probably want different behaviour than in 'normal' callback functions: return a HRESULT if - an exception occurrs in the callback, and print the traceback not + an exception occurs in the callback, and print the traceback not only on the console, but also to OutputDebugString() or something like that. */ diff -r 9dcd88977694 -r 650549138a3d Modules/_ctypes/callbacks.c --- a/Modules/_ctypes/callbacks.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_ctypes/callbacks.c Tue Mar 22 21:32:51 2011 +1000 @@ -202,7 +202,7 @@ /* XXX XXX XX We have the problem that c_byte or c_short have dict->size of 1 resp. 4, but these parameters are pushed as sizeof(int) bytes. - BTW, the same problem occurrs when they are pushed as parameters + BTW, the same problem occurs when they are pushed as parameters */ } else if (dict) { /* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */ diff -r 9dcd88977694 -r 650549138a3d Modules/_ctypes/callproc.c --- a/Modules/_ctypes/callproc.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_ctypes/callproc.c Tue Mar 22 21:32:51 2011 +1000 @@ -29,7 +29,7 @@ 4. _ctypes_callproc is then called with the 'callargs' tuple. _ctypes_callproc first allocates two arrays. The first is an array of 'struct argument' items, the - second array has 'void *' entried. + second array has 'void *' entries. 5. If 'converters' are present (converters is a sequence of argtypes' from_param methods), for each item in 'callargs' converter is called and the diff -r 9dcd88977694 -r 650549138a3d Modules/_ctypes/libffi/ChangeLog --- a/Modules/_ctypes/libffi/ChangeLog Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_ctypes/libffi/ChangeLog Tue Mar 22 21:32:51 2011 +1000 @@ -3084,7 +3084,7 @@ 2003-09-18 Kaz Kojima - * src/sh/ffi.c (ffi_prep_args): Take account into the alignment + * src/sh/ffi.c (ffi_prep_args): Take account into the alignement for the register size. (ffi_closure_helper_SYSV): Handle the structure return value address correctly. @@ -3344,7 +3344,7 @@ 2003-02-06 Andreas Tobler * libffi/src/powerpc/darwin_closure.S: - Fix alignment bug, allocate 8 bytes for the result. + Fix alignement bug, allocate 8 bytes for the result. * libffi/src/powerpc/aix_closure.S: Likewise. * libffi/src/powerpc/ffi_darwin.c: diff -r 9dcd88977694 -r 650549138a3d Modules/_ctypes/libffi/src/dlmalloc.c --- a/Modules/_ctypes/libffi/src/dlmalloc.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_ctypes/libffi/src/dlmalloc.c Tue Mar 22 21:32:51 2011 +1000 @@ -1326,7 +1326,7 @@ return (ptr != 0)? ptr: MFAIL; } -/* This function supports releasing coalesced segments */ +/* This function supports releasing coalesed segments */ static int win32munmap(void* ptr, size_t size) { MEMORY_BASIC_INFORMATION minfo; char* cptr = ptr; @@ -1362,7 +1362,7 @@ #define CALL_MORECORE(S) MFAIL #endif /* HAVE_MORECORE */ -/* mstate bit set if contiguous morecore disabled or failed */ +/* mstate bit set if continguous morecore disabled or failed */ #define USE_NONCONTIGUOUS_BIT (4U) /* segment bit set in create_mspace_with_base */ diff -r 9dcd88977694 -r 650549138a3d Modules/_ctypes/libffi/src/powerpc/ffi_darwin.c --- a/Modules/_ctypes/libffi/src/powerpc/ffi_darwin.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_ctypes/libffi/src/powerpc/ffi_darwin.c Tue Mar 22 21:32:51 2011 +1000 @@ -592,7 +592,7 @@ +---------------------------------------+ 160 | result area 8 | +---------------------------------------+ 168 - | alignment to the next multiple of 16 | + | alignement to the next multiple of 16 | SP current --> +---------------------------------------+ 176 <- parent frame | back chain to caller 4 | +---------------------------------------+ 180 diff -r 9dcd88977694 -r 650549138a3d Modules/_ctypes/libffi_osx/powerpc/ppc-ffi_darwin.c --- a/Modules/_ctypes/libffi_osx/powerpc/ppc-ffi_darwin.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_ctypes/libffi_osx/powerpc/ppc-ffi_darwin.c Tue Mar 22 21:32:51 2011 +1000 @@ -650,7 +650,7 @@ +---------------------------------------+ 160 | result area 8 | +---------------------------------------+ 168 - | alignment to the next multiple of 16 | + | alignement to the next multiple of 16 | SP current --> +---------------------------------------+ 176 <- parent frame | back chain to caller 4 | +---------------------------------------+ 180 diff -r 9dcd88977694 -r 650549138a3d Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_datetimemodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -766,7 +766,7 @@ PyObject *name; } PyDateTime_TimeZone; -/* The interned UTC timezone instance */ +/* The interned UTC timezone instance */ static PyObject *PyDateTime_TimeZone_UTC; /* Create new timezone instance checking offset range. This @@ -3287,7 +3287,6 @@ static PyObject * timezone_str(PyDateTime_TimeZone *self) { - char buf[10]; int hours, minutes, seconds; PyObject *offset; char sign; @@ -3313,11 +3312,9 @@ Py_DECREF(offset); minutes = divmod(seconds, 60, &seconds); hours = divmod(minutes, 60, &minutes); + /* XXX ignore sub-minute data, curently not allowed. */ assert(seconds == 0); - /* XXX ignore sub-minute data, curently not allowed. */ - PyOS_snprintf(buf, sizeof(buf), "UTC%c%02d:%02d", sign, hours, minutes); - - return PyUnicode_FromString(buf); + return PyUnicode_FromFormat("UTC%c%02d:%02d", sign, hours, minutes); } static PyObject * diff -r 9dcd88977694 -r 650549138a3d Modules/_elementtree.c --- a/Modules/_elementtree.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_elementtree.c Tue Mar 22 21:32:51 2011 +1000 @@ -226,7 +226,7 @@ PyObject* *children; PyObject* _children[STATIC_CHILDREN]; - + } ElementObjectExtra; typedef struct { @@ -354,14 +354,14 @@ /* use Python 2.4's list growth strategy */ size = (size >> 3) + (size < 9 ? 3 : 6) + size; /* Coverity CID #182 size_error: Allocating 1 bytes to pointer "children" - * which needs at least 4 bytes. - * Although it's a false alarm always assume at least one child to + * which needs at least 4 bytes. + * Although it's a false alarm always assume at least one child to * be safe. */ size = size ? size : 1; if (self->extra->children != self->extra->_children) { /* Coverity CID #182 size_error: Allocating 1 bytes to pointer - * "children", which needs at least 4 bytes. Although it's a + * "children", which needs at least 4 bytes. Although it's a * false alarm always assume at least one child to be safe. */ children = PyObject_Realloc(self->extra->children, @@ -606,7 +606,7 @@ Py_INCREF(JOIN_OBJ(element->tail)); if (self->extra) { - + if (element_resize(element, self->extra->length) < 0) { Py_DECREF(element); return NULL; @@ -618,7 +618,7 @@ } element->extra->length = self->extra->length; - + } return (PyObject*) element; @@ -661,7 +661,7 @@ if (!element) return NULL; - + text = deepcopy(JOIN_OBJ(self->text), memo); if (!text) goto error; @@ -675,7 +675,7 @@ element->tail = JOIN_SET(tail, JOIN_GET(self->tail)); if (self->extra) { - + if (element_resize(element, self->extra->length) < 0) goto error; @@ -689,7 +689,7 @@ } element->extra->length = self->extra->length; - + } /* add object to memo dictionary (so deepcopy won't visit it again) */ @@ -800,7 +800,7 @@ if (!self->extra) Py_RETURN_NONE; - + for (i = 0; i < self->extra->length; i++) { PyObject* item = self->extra->children[i]; if (Element_CheckExact(item) && @@ -953,7 +953,7 @@ element_iter(ElementObject* self, PyObject* args) { PyObject* result; - + PyObject* tag = Py_None; if (!PyArg_ParseTuple(args, "|O:iter", &tag)) return NULL; @@ -985,7 +985,7 @@ element_itertext(ElementObject* self, PyObject* args) { PyObject* result; - + if (!PyArg_ParseTuple(args, ":itertext")) return NULL; @@ -1483,7 +1483,7 @@ if (PyUnicode_Check(nameobj)) name = _PyUnicode_AsString(nameobj); - + if (name == NULL) return NULL; @@ -2113,7 +2113,7 @@ Py_INCREF(key); tag = key; } - + /* decode universal name */ p = PyBytes_AS_STRING(tag); value = PyUnicode_DecodeUTF8(p, size, "strict"); @@ -2138,13 +2138,15 @@ static void expat_set_error(const char* message, int line, int column) { - PyObject *error; - PyObject *position; - char buffer[256]; - - sprintf(buffer, "%.100s: line %d, column %d", message, line, column); - - error = PyObject_CallFunction(elementtree_parseerror_obj, "s", buffer); + PyObject *errmsg, *error, *position; + + errmsg = PyUnicode_FromFormat("%s: line %d, column %d", + message, line, column); + if (errmsg == NULL) + return; + + error = PyObject_CallFunction(elementtree_parseerror_obj, "O", errmsg); + Py_DECREF(errmsg); if (!error) return; @@ -2407,7 +2409,7 @@ for (i = 0; i < 256; i++) s[i] = i; - + u = PyUnicode_Decode((char*) s, 256, name, "replace"); if (!u) return XML_STATUS_ERROR; @@ -2466,7 +2468,7 @@ PyObject_Del(self); return NULL; } - + self->names = PyDict_New(); if (!self->names) { PyObject_Del(self->entity); @@ -2645,7 +2647,7 @@ reader = PyObject_GetAttrString(fileobj, "read"); if (!reader) return NULL; - + /* read from open file object */ for (;;) { @@ -2796,7 +2798,7 @@ {NULL, NULL} }; -static PyObject* +static PyObject* xmlparser_getattro(XMLParserObject* self, PyObject* nameobj) { if (PyUnicode_Check(nameobj)) { @@ -2957,7 +2959,7 @@ " break\n" " parser.feed(data)\n" " self._root = parser.close()\n" - " else:\n" + " else:\n" " parser = cElementTree.XMLParser()\n" " self._root = parser._parse(source)\n" " return self._root\n" diff -r 9dcd88977694 -r 650549138a3d Modules/_functoolsmodule.c --- a/Modules/_functoolsmodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_functoolsmodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -242,7 +242,7 @@ __reduce__ by itself doesn't support getting kwargs in the unpickle operation so we define a __setstate__ that replaces all the information about the partial. If we only replaced part of it someone would use - it as a hook to do stange things. + it as a hook to do strange things. */ static PyObject * diff -r 9dcd88977694 -r 650549138a3d Modules/_io/fileio.c --- a/Modules/_io/fileio.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_io/fileio.c Tue Mar 22 21:32:51 2011 +1000 @@ -712,7 +712,14 @@ errno = 0; len = pbuf.len; #if defined(MS_WIN64) || defined(MS_WINDOWS) - if (len > INT_MAX) + if (len > 32767 && isatty(self->fd)) { + /* Issue #11395: the Windows console returns an error (12: not + enough space error) on writing into stdout if stdout mode is + binary and the length is greater than 66,000 bytes (or less, + depending on heap usage). */ + len = 32767; + } + else if (len > INT_MAX) len = INT_MAX; n = write(self->fd, pbuf.buf, (int)len); #else diff -r 9dcd88977694 -r 650549138a3d Modules/_io/iobase.c --- a/Modules/_io/iobase.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_io/iobase.c Tue Mar 22 21:32:51 2011 +1000 @@ -50,7 +50,7 @@ "stream.\n" "\n" "IOBase also supports the :keyword:`with` statement. In this example,\n" - "fp is closed after the suite of the with statment is complete:\n" + "fp is closed after the suite of the with statement is complete:\n" "\n" "with open('spam.txt', 'r') as fp:\n" " fp.write('Spam and eggs!')\n"); diff -r 9dcd88977694 -r 650549138a3d Modules/_io/stringio.c --- a/Modules/_io/stringio.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_io/stringio.c Tue Mar 22 21:32:51 2011 +1000 @@ -157,7 +157,7 @@ 0 lo string_size hi | |<---used--->|<----------available----------->| | | <--to pad-->|<---to write---> | - 0 buf positon + 0 buf position */ memset(self->buf + self->string_size, '\0', diff -r 9dcd88977694 -r 650549138a3d Modules/_pickle.c --- a/Modules/_pickle.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_pickle.c Tue Mar 22 21:32:51 2011 +1000 @@ -6259,7 +6259,7 @@ goto error; if (!PyDict_CheckExact(name_mapping_3to2)) { PyErr_Format(PyExc_RuntimeError, - "_compat_pickle.REVERSE_NAME_MAPPING shouldbe a dict, " + "_compat_pickle.REVERSE_NAME_MAPPING should be a dict, " "not %.200s", Py_TYPE(name_mapping_3to2)->tp_name); goto error; } diff -r 9dcd88977694 -r 650549138a3d Modules/_posixsubprocess.c --- a/Modules/_posixsubprocess.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_posixsubprocess.c Tue Mar 22 21:32:51 2011 +1000 @@ -99,10 +99,10 @@ if (p2cread > 2) { POSIX_CALL(close(p2cread)); } - if (c2pwrite > 2) { + if (c2pwrite > 2 && c2pwrite != p2cread) { POSIX_CALL(close(c2pwrite)); } - if (errwrite != c2pwrite && errwrite > 2) { + if (errwrite != c2pwrite && errwrite != p2cread && errwrite > 2) { POSIX_CALL(close(errwrite)); } diff -r 9dcd88977694 -r 650549138a3d Modules/_sqlite/connection.h --- a/Modules/_sqlite/connection.h Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_sqlite/connection.h Tue Mar 22 21:32:51 2011 +1000 @@ -55,7 +55,7 @@ /* None for autocommit, otherwise a PyString with the isolation level */ PyObject* isolation_level; - /* NULL for autocommit, otherwise a string with the BEGIN statment; will be + /* NULL for autocommit, otherwise a string with the BEGIN statement; will be * freed in connection destructor */ char* begin_statement; diff -r 9dcd88977694 -r 650549138a3d Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_testcapimodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -22,14 +22,7 @@ static PyObject * raiseTestError(const char* test_name, const char* msg) { - char buf[2048]; - - if (strlen(test_name) + strlen(msg) > sizeof(buf) - 50) - PyErr_SetString(TestError, "internal error msg too large"); - else { - PyOS_snprintf(buf, sizeof(buf), "%s: %s", test_name, msg); - PyErr_SetString(TestError, buf); - } + PyErr_Format(TestError, "%s: %s", test_name, msg); return NULL; } @@ -43,11 +36,9 @@ sizeof_error(const char* fatname, const char* typname, int expected, int got) { - char buf[1024]; - PyOS_snprintf(buf, sizeof(buf), - "%.200s #define == %d but sizeof(%.200s) == %d", + PyErr_Format(TestError, + "%s #define == %d but sizeof(%s) == %d", fatname, expected, typname, got); - PyErr_SetString(TestError, buf); return (PyObject*)NULL; } diff -r 9dcd88977694 -r 650549138a3d Modules/_tkinter.c --- a/Modules/_tkinter.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/_tkinter.c Tue Mar 22 21:32:51 2011 +1000 @@ -2400,11 +2400,9 @@ Tktt_Repr(PyObject *self) { TkttObject *v = (TkttObject *)self; - char buf[100]; - - PyOS_snprintf(buf, sizeof(buf), "", v, - v->func == NULL ? ", handler deleted" : ""); - return PyUnicode_FromString(buf); + return PyUnicode_FromFormat("", + v, + v->func == NULL ? ", handler deleted" : ""); } static PyTypeObject Tktt_Type = diff -r 9dcd88977694 -r 650549138a3d Modules/cmathmodule.c --- a/Modules/cmathmodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/cmathmodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -23,7 +23,7 @@ /* CM_LARGE_DOUBLE is used to avoid spurious overflow in the sqrt, log, inverse trig and inverse hyperbolic trig functions. Its log is used in the - evaluation of exp, cos, cosh, sin, sinh, tan, and tanh to avoid unecessary + evaluation of exp, cos, cosh, sin, sinh, tan, and tanh to avoid unnecessary overflow. */ diff -r 9dcd88977694 -r 650549138a3d Modules/gcmodule.c --- a/Modules/gcmodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/gcmodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -680,8 +680,8 @@ static void debug_cycle(char *msg, PyObject *op) { - PySys_WriteStderr("gc: %.100s <%.100s %p>\n", - msg, Py_TYPE(op)->tp_name, op); + PySys_FormatStderr("gc: %s <%s %p>\n", + msg, Py_TYPE(op)->tp_name, op); } /* Handle uncollectable garbage (cycles with finalizers, and stuff reachable diff -r 9dcd88977694 -r 650549138a3d Modules/getpath.c --- a/Modules/getpath.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/getpath.c Tue Mar 22 21:32:51 2011 +1000 @@ -134,6 +134,7 @@ static wchar_t exec_prefix[MAXPATHLEN+1]; static wchar_t progpath[MAXPATHLEN+1]; static wchar_t *module_search_path = NULL; +static int module_search_path_malloced = 0; static wchar_t *lib_python = L"lib/python" VERSION; static void @@ -634,7 +635,6 @@ bufsz += wcslen(zip_path) + 1; bufsz += wcslen(exec_prefix) + 1; - /* This is the only malloc call in this file */ buf = (wchar_t *)PyMem_Malloc(bufsz*sizeof(wchar_t)); if (buf == NULL) { @@ -687,6 +687,7 @@ /* And publish the results */ module_search_path = buf; + module_search_path_malloced = 1; } /* Reduce prefix and exec_prefix to their essence, @@ -726,15 +727,18 @@ Py_SetPath(const wchar_t *path) { if (module_search_path != NULL) { - free(module_search_path); + if (module_search_path_malloced) + PyMem_Free(module_search_path); module_search_path = NULL; + module_search_path_malloced = 0; } if (path != NULL) { extern wchar_t *Py_GetProgramName(void); wchar_t *prog = Py_GetProgramName(); wcsncpy(progpath, prog, MAXPATHLEN); exec_prefix[0] = prefix[0] = L'\0'; - module_search_path = malloc((wcslen(path) + 1) * sizeof(wchar_t)); + module_search_path = PyMem_Malloc((wcslen(path) + 1) * sizeof(wchar_t)); + module_search_path_malloced = 1; if (module_search_path != NULL) wcscpy(module_search_path, path); } diff -r 9dcd88977694 -r 650549138a3d Modules/posixmodule.c --- a/Modules/posixmodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/posixmodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -59,6 +59,10 @@ #include "osdefs.h" #endif +#ifdef HAVE_SYS_UIO_H +#include +#endif + #ifdef HAVE_SYS_TYPES_H #include #endif /* HAVE_SYS_TYPES_H */ @@ -103,10 +107,6 @@ #ifdef HAVE_SYS_SOCKET_H #include #endif - -#ifdef HAVE_SYS_UIO_H -#include -#endif #endif /* Various compilers have only certain posix functions */ @@ -1503,6 +1503,33 @@ 10 }; +#if defined(HAVE_WAITID) && !defined(__APPLE__) +PyDoc_STRVAR(waitid_result__doc__, +"waitid_result: Result from waitid.\n\n\ +This object may be accessed either as a tuple of\n\ + (si_pid, si_uid, si_signo, si_status, si_code),\n\ +or via the attributes si_pid, si_uid, and so on.\n\ +\n\ +See os.waitid for more information."); + +static PyStructSequence_Field waitid_result_fields[] = { + {"si_pid", }, + {"si_uid", }, + {"si_signo", }, + {"si_status", }, + {"si_code", }, + {0} +}; + +static PyStructSequence_Desc waitid_result_desc = { + "waitid_result", /* name */ + waitid_result__doc__, /* doc */ + waitid_result_fields, + 5 +}; +static PyTypeObject WaitidResultType; +#endif + static int initialized; static PyTypeObject StatResultType; static PyTypeObject StatVFSResultType; @@ -2102,6 +2129,21 @@ } #endif /* HAVE_FSYNC */ +#ifdef HAVE_SYNC +PyDoc_STRVAR(posix_sync__doc__, +"sync()\n\n\ +Force write of everything to disk."); + +static PyObject * +posix_sync(PyObject *self, PyObject *noargs) +{ + Py_BEGIN_ALLOW_THREADS + sync(); + Py_END_ALLOW_THREADS + Py_RETURN_NONE; +} +#endif + #ifdef HAVE_FDATASYNC #ifdef __hpux @@ -3488,6 +3530,167 @@ #endif /* MS_WINDOWS */ } +#ifdef HAVE_FUTIMES +PyDoc_STRVAR(posix_futimes__doc__, +"futimes(fd, (atime, mtime))\n\ +futimes(fd, None)\n\n\ +Set the access and modified time of the file specified by the file\n\ +descriptor fd to the given values. If the second form is used, set the\n\ +access and modified times to the current time."); + +static PyObject * +posix_futimes(PyObject *self, PyObject *args) +{ + int res, fd; + PyObject* arg; + struct timeval buf[2]; + long ausec, musec; + + if (!PyArg_ParseTuple(args, "iO:futimes", &fd, &arg)) + return NULL; + + if (arg == Py_None) { + /* optional time values not given */ + Py_BEGIN_ALLOW_THREADS + res = futimes(fd, NULL); + Py_END_ALLOW_THREADS + } + else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { + PyErr_SetString(PyExc_TypeError, + "futimes() arg 2 must be a tuple (atime, mtime)"); + return NULL; + } + else { + if (extract_time(PyTuple_GET_ITEM(arg, 0), + &(buf[0].tv_sec), &ausec) == -1) { + return NULL; + } + if (extract_time(PyTuple_GET_ITEM(arg, 1), + &(buf[1].tv_sec), &musec) == -1) { + return NULL; + } + buf[0].tv_usec = ausec; + buf[1].tv_usec = musec; + Py_BEGIN_ALLOW_THREADS + res = futimes(fd, buf); + Py_END_ALLOW_THREADS + } + if (res < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif + +#ifdef HAVE_LUTIMES +PyDoc_STRVAR(posix_lutimes__doc__, +"lutimes(path, (atime, mtime))\n\ +lutimes(path, None)\n\n\ +Like utime(), but if path is a symbolic link, it is not dereferenced."); + +static PyObject * +posix_lutimes(PyObject *self, PyObject *args) +{ + PyObject *opath, *arg; + const char *path; + int res; + struct timeval buf[2]; + long ausec, musec; + + if (!PyArg_ParseTuple(args, "O&O:lutimes", + PyUnicode_FSConverter, &opath, &arg)) + return NULL; + path = PyBytes_AsString(opath); + if (arg == Py_None) { + /* optional time values not given */ + Py_BEGIN_ALLOW_THREADS + res = lutimes(path, NULL); + Py_END_ALLOW_THREADS + } + else if (!PyTuple_Check(arg) || PyTuple_Size(arg) != 2) { + PyErr_SetString(PyExc_TypeError, + "lutimes() arg 2 must be a tuple (atime, mtime)"); + Py_DECREF(opath); + return NULL; + } + else { + if (extract_time(PyTuple_GET_ITEM(arg, 0), + &(buf[0].tv_sec), &ausec) == -1) { + Py_DECREF(opath); + return NULL; + } + if (extract_time(PyTuple_GET_ITEM(arg, 1), + &(buf[1].tv_sec), &musec) == -1) { + Py_DECREF(opath); + return NULL; + } + buf[0].tv_usec = ausec; + buf[1].tv_usec = musec; + Py_BEGIN_ALLOW_THREADS + res = lutimes(path, buf); + Py_END_ALLOW_THREADS + } + Py_DECREF(opath); + if (res < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif + +#ifdef HAVE_FUTIMENS +PyDoc_STRVAR(posix_futimens__doc__, +"futimens(fd, (atime_sec, atime_nsec), (mtime_sec, mtime_nsec))\n\ +futimens(fd, None, None)\n\n\ +Updates the timestamps of a file specified by the file descriptor fd, with\n\ +nanosecond precision.\n\ +The second form sets atime and mtime to the current time.\n\ +If *_nsec is specified as UTIME_NOW, the timestamp is updated to the\n\ +current time.\n\ +If *_nsec is specified as UTIME_OMIT, the timestamp is not updated."); + +static PyObject * +posix_futimens(PyObject *self, PyObject *args) +{ + int res, fd; + PyObject *atime, *mtime; + struct timespec buf[2]; + + if (!PyArg_ParseTuple(args, "iOO:futimens", + &fd, &atime, &mtime)) + return NULL; + if (atime == Py_None && mtime == Py_None) { + /* optional time values not given */ + Py_BEGIN_ALLOW_THREADS + res = futimens(fd, NULL); + Py_END_ALLOW_THREADS + } + else if (!PyTuple_Check(atime) || PyTuple_Size(atime) != 2) { + PyErr_SetString(PyExc_TypeError, + "futimens() arg 2 must be a tuple (atime_sec, atime_nsec)"); + return NULL; + } + else if (!PyTuple_Check(mtime) || PyTuple_Size(mtime) != 2) { + PyErr_SetString(PyExc_TypeError, + "futimens() arg 3 must be a tuple (mtime_sec, mtime_nsec)"); + return NULL; + } + else { + if (!PyArg_ParseTuple(atime, "ll:futimens", + &(buf[0].tv_sec), &(buf[0].tv_nsec))) { + return NULL; + } + if (!PyArg_ParseTuple(mtime, "ll:futimens", + &(buf[1].tv_sec), &(buf[1].tv_nsec))) { + return NULL; + } + Py_BEGIN_ALLOW_THREADS + res = futimens(fd, buf); + Py_END_ALLOW_THREADS + } + if (res < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif /* Process operations */ @@ -3532,79 +3735,7 @@ } #endif - -#ifdef HAVE_EXECV -PyDoc_STRVAR(posix_execv__doc__, -"execv(path, args)\n\n\ -Execute an executable path with arguments, replacing current process.\n\ -\n\ - path: path of executable file\n\ - args: tuple or list of strings"); - -static PyObject * -posix_execv(PyObject *self, PyObject *args) -{ - PyObject *opath; - char *path; - PyObject *argv; - char **argvlist; - Py_ssize_t i, argc; - PyObject *(*getitem)(PyObject *, Py_ssize_t); - - /* execv has two arguments: (path, argv), where - argv is a list or tuple of strings. */ - - if (!PyArg_ParseTuple(args, "O&O:execv", - PyUnicode_FSConverter, - &opath, &argv)) - return NULL; - path = PyBytes_AsString(opath); - if (PyList_Check(argv)) { - argc = PyList_Size(argv); - getitem = PyList_GetItem; - } - else if (PyTuple_Check(argv)) { - argc = PyTuple_Size(argv); - getitem = PyTuple_GetItem; - } - else { - PyErr_SetString(PyExc_TypeError, "execv() arg 2 must be a tuple or list"); - Py_DECREF(opath); - return NULL; - } - if (argc < 1) { - PyErr_SetString(PyExc_ValueError, "execv() arg 2 must not be empty"); - Py_DECREF(opath); - return NULL; - } - - argvlist = PyMem_NEW(char *, argc+1); - if (argvlist == NULL) { - Py_DECREF(opath); - return PyErr_NoMemory(); - } - for (i = 0; i < argc; i++) { - if (!fsconvert_strdup((*getitem)(argv, i), - &argvlist[i])) { - free_string_array(argvlist, i); - PyErr_SetString(PyExc_TypeError, - "execv() arg 2 must contain only strings"); - Py_DECREF(opath); - return NULL; - - } - } - argvlist[argc] = NULL; - - execv(path, argvlist); - - /* If we get here it's definitely an error */ - - free_string_array(argvlist, argc); - Py_DECREF(opath); - return posix_error(); -} - +#if defined(HAVE_EXECV) || defined (HAVE_FEXECVE) static char** parse_envlist(PyObject* env, Py_ssize_t *envc_ptr) { @@ -3686,6 +3817,87 @@ return NULL; } +static char** +parse_arglist(PyObject* argv, Py_ssize_t *argc) +{ + int i; + char **argvlist = PyMem_NEW(char *, *argc+1); + if (argvlist == NULL) { + PyErr_NoMemory(); + return NULL; + } + for (i = 0; i < *argc; i++) { + PyObject* item = PySequence_ITEM(argv, i); + if (item == NULL) + goto fail; + if (!fsconvert_strdup(item, &argvlist[i])) { + Py_DECREF(item); + goto fail; + } + Py_DECREF(item); + } + argvlist[*argc] = NULL; + return argvlist; +fail: + *argc = i; + free_string_array(argvlist, *argc); + return NULL; +} +#endif + +#ifdef HAVE_EXECV +PyDoc_STRVAR(posix_execv__doc__, +"execv(path, args)\n\n\ +Execute an executable path with arguments, replacing current process.\n\ +\n\ + path: path of executable file\n\ + args: tuple or list of strings"); + +static PyObject * +posix_execv(PyObject *self, PyObject *args) +{ + PyObject *opath; + char *path; + PyObject *argv; + char **argvlist; + Py_ssize_t argc; + + /* execv has two arguments: (path, argv), where + argv is a list or tuple of strings. */ + + if (!PyArg_ParseTuple(args, "O&O:execv", + PyUnicode_FSConverter, + &opath, &argv)) + return NULL; + path = PyBytes_AsString(opath); + if (!PyList_Check(argv) && !PyTuple_Check(argv)) { + PyErr_SetString(PyExc_TypeError, + "execv() arg 2 must be a tuple or list"); + Py_DECREF(opath); + return NULL; + } + argc = PySequence_Size(argv); + if (argc < 1) { + PyErr_SetString(PyExc_ValueError, "execv() arg 2 must not be empty"); + Py_DECREF(opath); + return NULL; + } + + argvlist = parse_arglist(argv, &argc); + if (argvlist == NULL) { + Py_DECREF(opath); + return NULL; + } + + execv(path, argvlist); + + /* If we get here it's definitely an error */ + + free_string_array(argvlist, argc); + Py_DECREF(opath); + return posix_error(); +} + PyDoc_STRVAR(posix_execve__doc__, "execve(path, args, env)\n\n\ Execute a path with arguments and environment, replacing current process.\n\ @@ -3702,9 +3914,7 @@ PyObject *argv, *env; char **argvlist; char **envlist; - Py_ssize_t i, argc, envc; - PyObject *(*getitem)(PyObject *, Py_ssize_t); - Py_ssize_t lastarg = 0; + Py_ssize_t argc, envc; /* execve has three arguments: (path, argv, env), where argv is a list or tuple of strings and env is a dictionary @@ -3715,40 +3925,22 @@ &opath, &argv, &env)) return NULL; path = PyBytes_AsString(opath); - if (PyList_Check(argv)) { - argc = PyList_Size(argv); - getitem = PyList_GetItem; - } - else if (PyTuple_Check(argv)) { - argc = PyTuple_Size(argv); - getitem = PyTuple_GetItem; - } - else { + if (!PyList_Check(argv) && !PyTuple_Check(argv)) { PyErr_SetString(PyExc_TypeError, "execve() arg 2 must be a tuple or list"); goto fail_0; } + argc = PySequence_Size(argv); if (!PyMapping_Check(env)) { PyErr_SetString(PyExc_TypeError, "execve() arg 3 must be a mapping object"); goto fail_0; } - argvlist = PyMem_NEW(char *, argc+1); + argvlist = parse_arglist(argv, &argc); if (argvlist == NULL) { - PyErr_NoMemory(); goto fail_0; } - for (i = 0; i < argc; i++) { - if (!fsconvert_strdup((*getitem)(argv, i), - &argvlist[i])) - { - lastarg = i; - goto fail_1; - } - } - lastarg = argc; - argvlist[argc] = NULL; envlist = parse_envlist(env, &envc); if (envlist == NULL) @@ -3764,13 +3956,69 @@ PyMem_DEL(envlist[envc]); PyMem_DEL(envlist); fail_1: - free_string_array(argvlist, lastarg); + free_string_array(argvlist, argc); fail_0: Py_DECREF(opath); return NULL; } #endif /* HAVE_EXECV */ +#ifdef HAVE_FEXECVE +PyDoc_STRVAR(posix_fexecve__doc__, +"fexecve(fd, args, env)\n\n\ +Execute the program specified by a file descriptor with arguments and\n\ +environment, replacing the current process.\n\ +\n\ + fd: file descriptor of executable\n\ + args: tuple or list of arguments\n\ + env: dictionary of strings mapping to strings"); + +static PyObject * +posix_fexecve(PyObject *self, PyObject *args) +{ + int fd; + PyObject *argv, *env; + char **argvlist; + char **envlist; + Py_ssize_t argc, envc; + + if (!PyArg_ParseTuple(args, "iOO:fexecve", + &fd, &argv, &env)) + return NULL; + if (!PyList_Check(argv) && !PyTuple_Check(argv)) { + PyErr_SetString(PyExc_TypeError, + "fexecve() arg 2 must be a tuple or list"); + return NULL; + } + argc = PySequence_Size(argv); + if (!PyMapping_Check(env)) { + PyErr_SetString(PyExc_TypeError, + "fexecve() arg 3 must be a mapping object"); + return NULL; + } + + argvlist = parse_arglist(argv, &argc); + if (argvlist == NULL) + return NULL; + + envlist = parse_envlist(env, &envc); + if (envlist == NULL) + goto fail; + + fexecve(fd, argvlist, envlist); + + /* If we get here it's definitely an error */ + + (void) posix_error(); + + while (--envc >= 0) + PyMem_DEL(envlist[envc]); + PyMem_DEL(envlist); + fail: + free_string_array(argvlist, argc); + return NULL; +} +#endif /* HAVE_FEXECVE */ #ifdef HAVE_SPAWNV PyDoc_STRVAR(posix_spawnv__doc__, @@ -4337,6 +4585,7 @@ } #endif + #ifdef HAVE_GETEGID PyDoc_STRVAR(posix_getegid__doc__, "getegid() -> egid\n\n\ @@ -5127,6 +5376,55 @@ } #endif /* HAVE_WAIT4 */ +#if defined(HAVE_WAITID) && !defined(__APPLE__) +PyDoc_STRVAR(posix_waitid__doc__, +"waitid(idtype, id, options) -> waitid_result\n\n\ +Wait for the completion of one or more child processes.\n\n\ +idtype can be P_PID, P_PGID or P_ALL.\n\ +id specifies the pid to wait on.\n\ +options is constructed from the ORing of one or more of WEXITED, WSTOPPED\n\ +or WCONTINUED and additionally may be ORed with WNOHANG or WNOWAIT.\n\ +Returns either waitid_result or None if WNOHANG is specified and there are\n\ +no children in a waitable state."); + +static PyObject * +posix_waitid(PyObject *self, PyObject *args) +{ + PyObject *result; + idtype_t idtype; + id_t id; + int options, res; + siginfo_t si; + si.si_pid = 0; + if (!PyArg_ParseTuple(args, "i" _Py_PARSE_PID "i:waitid", &idtype, &id, &options)) + return NULL; + Py_BEGIN_ALLOW_THREADS + res = waitid(idtype, id, &si, options); + Py_END_ALLOW_THREADS + if (res == -1) + return posix_error(); + + if (si.si_pid == 0) + Py_RETURN_NONE; + + result = PyStructSequence_New(&WaitidResultType); + if (!result) + return NULL; + + PyStructSequence_SET_ITEM(result, 0, PyLong_FromPid(si.si_pid)); + PyStructSequence_SET_ITEM(result, 1, PyLong_FromPid(si.si_uid)); + PyStructSequence_SET_ITEM(result, 2, PyLong_FromLong((long)(si.si_signo))); + PyStructSequence_SET_ITEM(result, 3, PyLong_FromLong((long)(si.si_status))); + PyStructSequence_SET_ITEM(result, 4, PyLong_FromLong((long)(si.si_code))); + if (PyErr_Occurred()) { + Py_DECREF(result); + return NULL; + } + + return result; +} +#endif + #ifdef HAVE_WAITPID PyDoc_STRVAR(posix_waitpid__doc__, "waitpid(pid, options) -> (pid, status)\n\n\ @@ -5742,6 +6040,35 @@ return Py_None; } +#ifdef HAVE_LOCKF +PyDoc_STRVAR(posix_lockf__doc__, +"lockf(fd, cmd, len)\n\n\ +Apply, test or remove a POSIX lock on an open file descriptor.\n\n\ +fd is an open file descriptor.\n\ +cmd specifies the command to use - one of F_LOCK, F_TLOCK, F_ULOCK or\n\ +F_TEST.\n\ +len specifies the section of the file to lock."); + +static PyObject * +posix_lockf(PyObject *self, PyObject *args) +{ + int fd, cmd, res; + off_t len; + if (!PyArg_ParseTuple(args, "iiO&:lockf", + &fd, &cmd, _parse_off_t, &len)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + res = lockf(fd, cmd, len); + Py_END_ALLOW_THREADS + + if (res < 0) + return posix_error(); + + Py_RETURN_NONE; +} +#endif + PyDoc_STRVAR(posix_lseek__doc__, "lseek(fd, pos, how) -> newpos\n\n\ @@ -5831,6 +6158,140 @@ return buffer; } +#if (defined(HAVE_SENDFILE) && (defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(__APPLE__))) || defined(HAVE_READV) || defined(HAVE_WRITEV) +static Py_ssize_t +iov_setup(struct iovec **iov, Py_buffer **buf, PyObject *seq, int cnt, int type) +{ + int i, j; + Py_ssize_t blen, total = 0; + + *iov = PyMem_New(struct iovec, cnt); + if (*iov == NULL) { + PyErr_NoMemory(); + return total; + } + + *buf = PyMem_New(Py_buffer, cnt); + if (*buf == NULL) { + PyMem_Del(*iov); + PyErr_NoMemory(); + return total; + } + + for (i = 0; i < cnt; i++) { + PyObject *item = PySequence_GetItem(seq, i); + if (item == NULL) + goto fail; + if (PyObject_GetBuffer(item, &(*buf)[i], type) == -1) { + Py_DECREF(item); + goto fail; + } + Py_DECREF(item); + (*iov)[i].iov_base = (*buf)[i].buf; + blen = (*buf)[i].len; + (*iov)[i].iov_len = blen; + total += blen; + } + return total; + +fail: + PyMem_Del(*iov); + for (j = 0; j < i; j++) { + PyBuffer_Release(&(*buf)[j]); + } + PyMem_Del(*buf); + return 0; +} + +static void +iov_cleanup(struct iovec *iov, Py_buffer *buf, int cnt) +{ + int i; + PyMem_Del(iov); + for (i = 0; i < cnt; i++) { + PyBuffer_Release(&buf[i]); + } + PyMem_Del(buf); +} +#endif + +#ifdef HAVE_READV +PyDoc_STRVAR(posix_readv__doc__, +"readv(fd, buffers) -> bytesread\n\n\ +Read from a file descriptor into a number of writable buffers. buffers\n\ +is an arbitrary sequence of writable buffers.\n\ +Returns the total number of bytes read."); + +static PyObject * +posix_readv(PyObject *self, PyObject *args) +{ + int fd, cnt; + Py_ssize_t n; + PyObject *seq; + struct iovec *iov; + Py_buffer *buf; + + if (!PyArg_ParseTuple(args, "iO:readv", &fd, &seq)) + return NULL; + if (!PySequence_Check(seq)) { + PyErr_SetString(PyExc_TypeError, + "readv() arg 2 must be a sequence"); + return NULL; + } + cnt = PySequence_Size(seq); + + if (!iov_setup(&iov, &buf, seq, cnt, PyBUF_WRITABLE)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + n = readv(fd, iov, cnt); + Py_END_ALLOW_THREADS + + iov_cleanup(iov, buf, cnt); + return PyLong_FromSsize_t(n); +} +#endif + +#ifdef HAVE_PREAD +PyDoc_STRVAR(posix_pread__doc__, +"pread(fd, buffersize, offset) -> string\n\n\ +Read from a file descriptor, fd, at a position of offset. It will read up\n\ +to buffersize number of bytes. The file offset remains unchanged."); + +static PyObject * +posix_pread(PyObject *self, PyObject *args) +{ + int fd, size; + off_t offset; + Py_ssize_t n; + PyObject *buffer; + if (!PyArg_ParseTuple(args, "iiO&:pread", &fd, &size, _parse_off_t, &offset)) + return NULL; + + if (size < 0) { + errno = EINVAL; + return posix_error(); + } + buffer = PyBytes_FromStringAndSize((char *)NULL, size); + if (buffer == NULL) + return NULL; + if (!_PyVerify_fd(fd)) { + Py_DECREF(buffer); + return posix_error(); + } + Py_BEGIN_ALLOW_THREADS + n = pread(fd, PyBytes_AS_STRING(buffer), size, offset); + Py_END_ALLOW_THREADS + if (n < 0) { + Py_DECREF(buffer); + return posix_error(); + } + if (n != size) + _PyBytes_Resize(&buffer, n); + return buffer; +} +#endif PyDoc_STRVAR(posix_write__doc__, "write(fd, string) -> byteswritten\n\n\ @@ -5866,57 +6327,6 @@ } #ifdef HAVE_SENDFILE -#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__) -static Py_ssize_t -iov_setup(struct iovec **iov, Py_buffer **buf, PyObject *seq, int cnt, int type) -{ - int i, j; - Py_ssize_t blen, total = 0; - - *iov = PyMem_New(struct iovec, cnt); - if (*iov == NULL) { - PyErr_NoMemory(); - return total; - } - - *buf = PyMem_New(Py_buffer, cnt); - if (*buf == NULL) { - PyMem_Del(*iov); - PyErr_NoMemory(); - return total; - } - - for (i = 0; i < cnt; i++) { - if (PyObject_GetBuffer(PySequence_GetItem(seq, i), - &(*buf)[i], type) == -1) { - PyMem_Del(*iov); - for (j = 0; j < i; j++) { - PyBuffer_Release(&(*buf)[j]); - } - PyMem_Del(*buf); - total = 0; - return total; - } - (*iov)[i].iov_base = (*buf)[i].buf; - blen = (*buf)[i].len; - (*iov)[i].iov_len = blen; - total += blen; - } - return total; -} - -static void -iov_cleanup(struct iovec *iov, Py_buffer *buf, int cnt) -{ - int i; - PyMem_Del(iov); - for (i = 0; i < cnt; i++) { - PyBuffer_Release(&buf[i]); - } - PyMem_Del(buf); -} -#endif - PyDoc_STRVAR(posix_sendfile__doc__, "sendfile(out, in, offset, nbytes) -> byteswritten\n\ sendfile(out, in, offset, nbytes, headers=None, trailers=None, flags=0)\n\ @@ -6150,6 +6560,73 @@ } #endif /* HAVE_PIPE */ +#ifdef HAVE_WRITEV +PyDoc_STRVAR(posix_writev__doc__, +"writev(fd, buffers) -> byteswritten\n\n\ +Write the contents of buffers to a file descriptor, where buffers is an\n\ +arbitrary sequence of buffers.\n\ +Returns the total bytes written."); + +static PyObject * +posix_writev(PyObject *self, PyObject *args) +{ + int fd, cnt; + Py_ssize_t res; + PyObject *seq; + struct iovec *iov; + Py_buffer *buf; + if (!PyArg_ParseTuple(args, "iO:writev", &fd, &seq)) + return NULL; + if (!PySequence_Check(seq)) { + PyErr_SetString(PyExc_TypeError, + "writev() arg 2 must be a sequence"); + return NULL; + } + cnt = PySequence_Size(seq); + + if (!iov_setup(&iov, &buf, seq, cnt, PyBUF_SIMPLE)) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + res = writev(fd, iov, cnt); + Py_END_ALLOW_THREADS + + iov_cleanup(iov, buf, cnt); + return PyLong_FromSsize_t(res); +} +#endif + +#ifdef HAVE_PWRITE +PyDoc_STRVAR(posix_pwrite__doc__, +"pwrite(fd, string, offset) -> byteswritten\n\n\ +Write string to a file descriptor, fd, from offset, leaving the file\n\ +offset unchanged."); + +static PyObject * +posix_pwrite(PyObject *self, PyObject *args) +{ + Py_buffer pbuf; + int fd; + off_t offset; + Py_ssize_t size; + + if (!PyArg_ParseTuple(args, "iy*O&:pwrite", &fd, &pbuf, _parse_off_t, &offset)) + return NULL; + + if (!_PyVerify_fd(fd)) { + PyBuffer_Release(&pbuf); + return posix_error(); + } + Py_BEGIN_ALLOW_THREADS + size = pwrite(fd, pbuf.buf, (size_t)pbuf.len, offset); + Py_END_ALLOW_THREADS + PyBuffer_Release(&pbuf); + if (size < 0) + return posix_error(); + return PyLong_FromSsize_t(size); +} +#endif #ifdef HAVE_MKFIFO PyDoc_STRVAR(posix_mkfifo__doc__, @@ -6266,18 +6743,8 @@ int fd; off_t length; int res; - PyObject *lenobj; - - if (!PyArg_ParseTuple(args, "iO:ftruncate", &fd, &lenobj)) - return NULL; - -#if !defined(HAVE_LARGEFILE_SUPPORT) - length = PyLong_AsLong(lenobj); -#else - length = PyLong_Check(lenobj) ? - PyLong_AsLongLong(lenobj) : PyLong_AsLong(lenobj); -#endif - if (PyErr_Occurred()) + + if (!PyArg_ParseTuple(args, "iO&:ftruncate", &fd, _parse_off_t, &length)) return NULL; Py_BEGIN_ALLOW_THREADS @@ -6290,6 +6757,93 @@ } #endif +#ifdef HAVE_TRUNCATE +PyDoc_STRVAR(posix_truncate__doc__, +"truncate(path, length)\n\n\ +Truncate the file given by path to length bytes."); + +static PyObject * +posix_truncate(PyObject *self, PyObject *args) +{ + PyObject *opath; + const char *path; + off_t length; + int res; + + if (!PyArg_ParseTuple(args, "O&O&:truncate", + PyUnicode_FSConverter, &opath, _parse_off_t, &length)) + return NULL; + path = PyBytes_AsString(opath); + + Py_BEGIN_ALLOW_THREADS + res = truncate(path, length); + Py_END_ALLOW_THREADS + Py_DECREF(opath); + if (res < 0) + return posix_error(); + Py_RETURN_NONE; +} +#endif + +#ifdef HAVE_POSIX_FALLOCATE +PyDoc_STRVAR(posix_posix_fallocate__doc__, +"posix_fallocate(fd, offset, len)\n\n\ +Ensures that enough disk space is allocated for the file specified by fd\n\ +starting from offset and continuing for len bytes."); + +static PyObject * +posix_posix_fallocate(PyObject *self, PyObject *args) +{ + off_t len, offset; + int res, fd; + + if (!PyArg_ParseTuple(args, "iO&O&:posix_fallocate", + &fd, _parse_off_t, &offset, _parse_off_t, &len)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + res = posix_fallocate(fd, offset, len); + Py_END_ALLOW_THREADS + if (res != 0) { + errno = res; + return posix_error(); + } + Py_RETURN_NONE; +} +#endif + +#ifdef HAVE_POSIX_FADVISE +PyDoc_STRVAR(posix_posix_fadvise__doc__, +"posix_fadvise(fd, offset, len, advice)\n\n\ +Announces an intention to access data in a specific pattern thus allowing\n\ +the kernel to make optimizations.\n\ +The advice applies to the region of the file specified by fd starting at\n\ +offset and continuing for len bytes.\n\ +advice is one of POSIX_FADV_NORMAL, POSIX_FADV_SEQUENTIAL,\n\ +POSIX_FADV_RANDOM, POSIX_FADV_NOREUSE, POSIX_FADV_WILLNEED or\n\ +POSIX_FADV_DONTNEED."); + +static PyObject * +posix_posix_fadvise(PyObject *self, PyObject *args) +{ + off_t len, offset; + int res, fd, advice; + + if (!PyArg_ParseTuple(args, "iO&O&i:posix_fadvise", + &fd, _parse_off_t, &offset, _parse_off_t, &len, &advice)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + res = posix_fadvise(fd, offset, len, advice); + Py_END_ALLOW_THREADS + if (res != 0) { + errno = res; + return posix_error(); + } + Py_RETURN_NONE; +} +#endif + #ifdef HAVE_PUTENV PyDoc_STRVAR(posix_putenv__doc__, "putenv(key, value)\n\n\ @@ -8725,6 +9279,15 @@ {"unlink", posix_unlink, METH_VARARGS, posix_unlink__doc__}, {"remove", posix_unlink, METH_VARARGS, posix_remove__doc__}, {"utime", posix_utime, METH_VARARGS, posix_utime__doc__}, +#ifdef HAVE_FUTIMES + {"futimes", posix_futimes, METH_VARARGS, posix_futimes__doc__}, +#endif +#ifdef HAVE_LUTIMES + {"lutimes", posix_lutimes, METH_VARARGS, posix_lutimes__doc__}, +#endif +#ifdef HAVE_FUTIMENS + {"futimens", posix_futimens, METH_VARARGS, posix_futimens__doc__}, +#endif #ifdef HAVE_TIMES {"times", posix_times, METH_NOARGS, posix_times__doc__}, #endif /* HAVE_TIMES */ @@ -8733,6 +9296,9 @@ {"execv", posix_execv, METH_VARARGS, posix_execv__doc__}, {"execve", posix_execve, METH_VARARGS, posix_execve__doc__}, #endif /* HAVE_EXECV */ +#ifdef HAVE_FEXECVE + {"fexecve", posix_fexecve, METH_VARARGS, posix_fexecve__doc__}, +#endif #ifdef HAVE_SPAWNV {"spawnv", posix_spawnv, METH_VARARGS, posix_spawnv__doc__}, {"spawnve", posix_spawnve, METH_VARARGS, posix_spawnve__doc__}, @@ -8831,6 +9397,9 @@ #ifdef HAVE_WAIT4 {"wait4", posix_wait4, METH_VARARGS, posix_wait4__doc__}, #endif /* HAVE_WAIT4 */ +#if defined(HAVE_WAITID) && !defined(__APPLE__) + {"waitid", posix_waitid, METH_VARARGS, posix_waitid__doc__}, +#endif #if defined(HAVE_WAITPID) || defined(HAVE_CWAIT) {"waitpid", posix_waitpid, METH_VARARGS, posix_waitpid__doc__}, #endif /* HAVE_WAITPID */ @@ -8855,9 +9424,24 @@ {"device_encoding", device_encoding, METH_VARARGS, device_encoding__doc__}, {"dup", posix_dup, METH_VARARGS, posix_dup__doc__}, {"dup2", posix_dup2, METH_VARARGS, posix_dup2__doc__}, +#ifdef HAVE_LOCKF + {"lockf", posix_lockf, METH_VARARGS, posix_lockf__doc__}, +#endif {"lseek", posix_lseek, METH_VARARGS, posix_lseek__doc__}, {"read", posix_read, METH_VARARGS, posix_read__doc__}, +#ifdef HAVE_READV + {"readv", posix_readv, METH_VARARGS, posix_readv__doc__}, +#endif +#ifdef HAVE_PREAD + {"pread", posix_pread, METH_VARARGS, posix_pread__doc__}, +#endif {"write", posix_write, METH_VARARGS, posix_write__doc__}, +#ifdef HAVE_WRITEV + {"writev", posix_writev, METH_VARARGS, posix_writev__doc__}, +#endif +#ifdef HAVE_PWRITE + {"pwrite", posix_pwrite, METH_VARARGS, posix_pwrite__doc__}, +#endif #ifdef HAVE_SENDFILE {"sendfile", (PyCFunction)posix_sendfile, METH_VARARGS | METH_KEYWORDS, posix_sendfile__doc__}, @@ -8881,6 +9465,15 @@ #ifdef HAVE_FTRUNCATE {"ftruncate", posix_ftruncate, METH_VARARGS, posix_ftruncate__doc__}, #endif +#ifdef HAVE_TRUNCATE + {"truncate", posix_truncate, METH_VARARGS, posix_truncate__doc__}, +#endif +#ifdef HAVE_POSIX_FALLOCATE + {"posix_fallocate", posix_posix_fallocate, METH_VARARGS, posix_posix_fallocate__doc__}, +#endif +#ifdef HAVE_POSIX_FADVISE + {"posix_fadvise", posix_posix_fadvise, METH_VARARGS, posix_posix_fadvise__doc__}, +#endif #ifdef HAVE_PUTENV {"putenv", posix_putenv, METH_VARARGS, posix_putenv__doc__}, #endif @@ -8894,6 +9487,9 @@ #ifdef HAVE_FSYNC {"fsync", posix_fsync, METH_O, posix_fsync__doc__}, #endif +#ifdef HAVE_SYNC + {"sync", posix_sync, METH_NOARGS, posix_sync__doc__}, +#endif #ifdef HAVE_FDATASYNC {"fdatasync", posix_fdatasync, METH_O, posix_fdatasync__doc__}, #endif @@ -9342,6 +9938,76 @@ if (ins(d, "SF_SYNC", (long)SF_SYNC)) return -1; #endif + /* constants for posix_fadvise */ +#ifdef POSIX_FADV_NORMAL + if (ins(d, "POSIX_FADV_NORMAL", (long)POSIX_FADV_NORMAL)) return -1; +#endif +#ifdef POSIX_FADV_SEQUENTIAL + if (ins(d, "POSIX_FADV_SEQUENTIAL", (long)POSIX_FADV_SEQUENTIAL)) return -1; +#endif +#ifdef POSIX_FADV_RANDOM + if (ins(d, "POSIX_FADV_RANDOM", (long)POSIX_FADV_RANDOM)) return -1; +#endif +#ifdef POSIX_FADV_NOREUSE + if (ins(d, "POSIX_FADV_NOREUSE", (long)POSIX_FADV_NOREUSE)) return -1; +#endif +#ifdef POSIX_FADV_WILLNEED + if (ins(d, "POSIX_FADV_WILLNEED", (long)POSIX_FADV_WILLNEED)) return -1; +#endif +#ifdef POSIX_FADV_DONTNEED + if (ins(d, "POSIX_FADV_DONTNEED", (long)POSIX_FADV_DONTNEED)) return -1; +#endif + + /* constants for waitid */ +#if defined(HAVE_SYS_WAIT_H) && defined(HAVE_WAITID) + if (ins(d, "P_PID", (long)P_PID)) return -1; + if (ins(d, "P_PGID", (long)P_PGID)) return -1; + if (ins(d, "P_ALL", (long)P_ALL)) return -1; +#endif +#ifdef WEXITED + if (ins(d, "WEXITED", (long)WEXITED)) return -1; +#endif +#ifdef WNOWAIT + if (ins(d, "WNOWAIT", (long)WNOWAIT)) return -1; +#endif +#ifdef WSTOPPED + if (ins(d, "WSTOPPED", (long)WSTOPPED)) return -1; +#endif +#ifdef CLD_EXITED + if (ins(d, "CLD_EXITED", (long)CLD_EXITED)) return -1; +#endif +#ifdef CLD_DUMPED + if (ins(d, "CLD_DUMPED", (long)CLD_DUMPED)) return -1; +#endif +#ifdef CLD_TRAPPED + if (ins(d, "CLD_TRAPPED", (long)CLD_TRAPPED)) return -1; +#endif +#ifdef CLD_CONTINUED + if (ins(d, "CLD_CONTINUED", (long)CLD_CONTINUED)) return -1; +#endif + + /* constants for lockf */ +#ifdef F_LOCK + if (ins(d, "F_LOCK", (long)F_LOCK)) return -1; +#endif +#ifdef F_TLOCK + if (ins(d, "F_TLOCK", (long)F_TLOCK)) return -1; +#endif +#ifdef F_ULOCK + if (ins(d, "F_ULOCK", (long)F_ULOCK)) return -1; +#endif +#ifdef F_TEST + if (ins(d, "F_TEST", (long)F_TEST)) return -1; +#endif + + /* constants for futimens */ +#ifdef UTIME_NOW + if (ins(d, "UTIME_NOW", (long)UTIME_NOW)) return -1; +#endif +#ifdef UTIME_OMIT + if (ins(d, "UTIME_OMIT", (long)UTIME_OMIT)) return -1; +#endif + #ifdef HAVE_SPAWNV #if defined(PYOS_OS2) && defined(PYCC_GCC) if (ins(d, "P_WAIT", (long)P_WAIT)) return -1; @@ -9441,6 +10107,11 @@ #endif if (!initialized) { +#if defined(HAVE_WAITID) && !defined(__APPLE__) + waitid_result_desc.name = MODNAME ".waitid_result"; + PyStructSequence_InitType(&WaitidResultType, &waitid_result_desc); +#endif + stat_result_desc.name = MODNAME ".stat_result"; stat_result_desc.fields[7].name = PyStructSequence_UnnamedField; stat_result_desc.fields[8].name = PyStructSequence_UnnamedField; @@ -9461,6 +10132,10 @@ # endif #endif } +#if defined(HAVE_WAITID) && !defined(__APPLE__) + Py_INCREF((PyObject*) &WaitidResultType); + PyModule_AddObject(m, "waitid_result", (PyObject*) &WaitidResultType); +#endif Py_INCREF((PyObject*) &StatResultType); PyModule_AddObject(m, "stat_result", (PyObject*) &StatResultType); Py_INCREF((PyObject*) &StatVFSResultType); diff -r 9dcd88977694 -r 650549138a3d Modules/pyexpat.c --- a/Modules/pyexpat.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/pyexpat.c Tue Mar 22 21:32:51 2011 +1000 @@ -100,16 +100,17 @@ set_error(xmlparseobject *self, enum XML_Error code) { PyObject *err; - char buffer[256]; + PyObject *buffer; XML_Parser parser = self->itself; int lineno = XML_GetErrorLineNumber(parser); int column = XML_GetErrorColumnNumber(parser); - /* There is no risk of overflowing this buffer, since - even for 64-bit integers, there is sufficient space. */ - sprintf(buffer, "%.200s: line %i, column %i", - XML_ErrorString(code), lineno, column); - err = PyObject_CallFunction(ErrorObject, "s", buffer); + buffer = PyUnicode_FromFormat("%s: line %i, column %i", + XML_ErrorString(code), lineno, column); + if (buffer == NULL) + return NULL; + err = PyObject_CallFunction(ErrorObject, "O", buffer); + Py_DECREF(buffer); if ( err != NULL && set_error_attr(err, "code", code) && set_error_attr(err, "offset", column) diff -r 9dcd88977694 -r 650549138a3d Modules/readline.c --- a/Modules/readline.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/readline.c Tue Mar 22 21:32:51 2011 +1000 @@ -233,10 +233,9 @@ Py_XDECREF(tmp); } else { - PyOS_snprintf(buf, sizeof(buf), - "set_%.50s(func): argument not callable", - funcname); - PyErr_SetString(PyExc_TypeError, buf); + PyErr_Format(PyExc_TypeError, + "set_%.50s(func): argument not callable", + funcname); return NULL; } Py_RETURN_NONE; @@ -890,7 +889,7 @@ #endif #ifdef __APPLE__ - /* the libedit readline emulation resets key bindings etc + /* the libedit readline emulation resets key bindings etc * when calling rl_initialize. So call it upfront */ if (using_libedit_emulation) @@ -930,11 +929,11 @@ */ #ifdef __APPLE__ if (using_libedit_emulation) - rl_read_init_file(NULL); + rl_read_init_file(NULL); else #endif /* __APPLE__ */ rl_initialize(); - + RESTORE_LOCALE(saved_locale) } diff -r 9dcd88977694 -r 650549138a3d Modules/socketmodule.c --- a/Modules/socketmodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/socketmodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -2242,7 +2242,7 @@ * This is the guts of the recv() and recv_into() methods, which reads into a * char buffer. If you have any inc/dec ref to do to the objects that contain * the buffer, do it in the caller. This function returns the number of bytes - * succesfully read. If there was an error, it returns -1. Note that it is + * successfully read. If there was an error, it returns -1. Note that it is * also possible that we return a number of bytes smaller than the request * bytes. */ @@ -2446,7 +2446,7 @@ * This is the guts of the recvfrom() and recvfrom_into() methods, which reads * into a char buffer. If you have any inc/def ref to do to the objects that * contain the buffer, do it in the caller. This function returns the number - * of bytes succesfully read. If there was an error, it returns -1. Note + * of bytes successfully read. If there was an error, it returns -1. Note * that it is also possible that we return a number of bytes smaller than the * request bytes. * @@ -2541,9 +2541,9 @@ if (outlen != recvlen) { /* We did not read as many bytes as we anticipated, resize the - string if possible and be succesful. */ + string if possible and be successful. */ if (_PyBytes_Resize(&buf, outlen) < 0) - /* Oopsy, not so succesful after all. */ + /* Oopsy, not so successful after all. */ goto finally; } @@ -2747,17 +2747,28 @@ Py_buffer pbuf; PyObject *addro; char *buf; - Py_ssize_t len; + Py_ssize_t len, arglen; sock_addr_t addrbuf; int addrlen, n = -1, flags, timeout; flags = 0; - if (!PyArg_ParseTuple(args, "y*O:sendto", &pbuf, &addro)) { - PyErr_Clear(); - if (!PyArg_ParseTuple(args, "y*iO:sendto", - &pbuf, &flags, &addro)) - return NULL; + arglen = PyTuple_Size(args); + switch (arglen) { + case 2: + PyArg_ParseTuple(args, "y*O:sendto", &pbuf, &addro); + break; + case 3: + PyArg_ParseTuple(args, "y*iO:sendto", + &pbuf, &flags, &addro); + break; + default: + PyErr_Format(PyExc_TypeError, + "sendto() takes 2 or 3 arguments (%d given)", + arglen); } + if (PyErr_Occurred()) + return NULL; + buf = pbuf.buf; len = pbuf.len; @@ -4372,7 +4383,7 @@ return 0; /* Failure */ #else - /* No need to initialise sockets with GCC/EMX */ + /* No need to initialize sockets with GCC/EMX */ return 1; /* Success */ #endif } @@ -4406,7 +4417,7 @@ "socket.py" which implements some additional functionality. The import of "_socket" may fail with an ImportError exception if os-specific initialization fails. On Windows, this does WINSOCK - initialization. When WINSOCK is initialized succesfully, a call to + initialization. When WINSOCK is initialized successfully, a call to WSACleanup() is scheduled to be made at exit time. */ diff -r 9dcd88977694 -r 650549138a3d Modules/timemodule.c --- a/Modules/timemodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/timemodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -476,9 +476,8 @@ #if defined(_MSC_VER) || defined(sun) if (buf.tm_year + 1900 < 1 || 9999 < buf.tm_year + 1900) { - PyErr_Format(PyExc_ValueError, - "strftime() requires year in [1; 9999]", - buf.tm_year + 1900); + PyErr_SetString(PyExc_ValueError, + "strftime() requires year in [1; 9999]"); return NULL; } #endif @@ -602,31 +601,20 @@ { /* Inspired by Open Group reference implementation available at * http://pubs.opengroup.org/onlinepubs/009695399/functions/asctime.html */ - static char wday_name[7][3] = { + static char wday_name[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static char mon_name[12][3] = { + static char mon_name[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; - char buf[20]; /* 'Sun Sep 16 01:03:52\0' */ - int n; - - n = PyOS_snprintf(buf, sizeof(buf), "%.3s %.3s%3d %.2d:%.2d:%.2d", - wday_name[timeptr->tm_wday], - mon_name[timeptr->tm_mon], - timeptr->tm_mday, timeptr->tm_hour, - timeptr->tm_min, timeptr->tm_sec); - /* XXX: since the fields used by snprintf above are validated in checktm, - * the following condition should never trigger. We keep the check because - * historically fixed size buffer used in asctime was the source of - * crashes. */ - if (n + 1 != sizeof(buf)) { - PyErr_SetString(PyExc_ValueError, "unconvertible time"); - return NULL; - } - - return PyUnicode_FromFormat("%s %d", buf, 1900 + timeptr->tm_year); + return PyUnicode_FromFormat( + "%s %s%3d %.2d:%.2d:%.2d %d", + wday_name[timeptr->tm_wday], + mon_name[timeptr->tm_mon], + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec, + 1900 + timeptr->tm_year); } static PyObject * @@ -697,7 +685,7 @@ buf.tm_wday = -1; /* sentinel; original value ignored */ tt = mktime(&buf); /* Return value of -1 does not necessarily mean an error, but tm_wday - * cannot remain set to -1 if mktime succedded. */ + * cannot remain set to -1 if mktime succeeded. */ if (tt == (time_t)(-1) && buf.tm_wday == -1) { PyErr_SetString(PyExc_OverflowError, "mktime argument out of range"); diff -r 9dcd88977694 -r 650549138a3d Modules/zipimport.c --- a/Modules/zipimport.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Modules/zipimport.c Tue Mar 22 21:32:51 2011 +1000 @@ -49,7 +49,7 @@ /* forward decls */ static PyObject *read_directory(PyObject *archive); static PyObject *get_data(PyObject *archive, PyObject *toc_entry); -static PyObject *get_module_code(ZipImporter *self, char *fullname, +static PyObject *get_module_code(ZipImporter *self, PyObject *fullname, int *p_ispackage, PyObject **p_modpath); @@ -202,49 +202,50 @@ } /* return fullname.split(".")[-1] */ -static char * -get_subname(char *fullname) +static PyObject * +get_subname(PyObject *fullname) { - char *subname = strrchr(fullname, '.'); - if (subname == NULL) - subname = fullname; - else + Py_ssize_t len; + Py_UNICODE *subname; + subname = Py_UNICODE_strrchr(PyUnicode_AS_UNICODE(fullname), '.'); + if (subname == NULL) { + Py_INCREF(fullname); + return fullname; + } else { subname++; - return subname; + len = PyUnicode_GET_SIZE(fullname); + len -= subname - PyUnicode_AS_UNICODE(fullname); + return PyUnicode_FromUnicode(subname, len); + } } /* Given a (sub)modulename, write the potential file path in the archive (without extension) to the path buffer. Return the - length of the resulting string. */ -static int -make_filename(PyObject *prefix_obj, char *name, char *path, size_t pathsize) + length of the resulting string. + + return self.prefix + name.replace('.', os.sep) */ +static PyObject* +make_filename(PyObject *prefix, PyObject *name) { - size_t len; - char *p; - PyObject *prefix; + PyObject *pathobj; + Py_UNICODE *p; - prefix = PyUnicode_EncodeFSDefault(prefix_obj); - if (prefix == NULL) - return -1; - len = PyBytes_GET_SIZE(prefix); + pathobj = PyUnicode_FromUnicode(NULL, + PyUnicode_GET_SIZE(prefix) + + PyUnicode_GET_SIZE(name)); + if (pathobj == NULL) + return NULL; - /* self.prefix + name [+ SEP + "__init__"] + ".py[co]" */ - if (len + strlen(name) + 13 >= pathsize - 1) { - PyErr_SetString(ZipImportError, "path too long"); - Py_DECREF(prefix); - return -1; - } + p = PyUnicode_AS_UNICODE(pathobj); - strcpy(path, PyBytes_AS_STRING(prefix)); - Py_DECREF(prefix); - strcpy(path + len, name); - for (p = path + len; *p; p++) { + Py_UNICODE_strcpy(p, PyUnicode_AS_UNICODE(prefix)); + p += PyUnicode_GET_SIZE(prefix); + Py_UNICODE_strcpy(p, PyUnicode_AS_UNICODE(name)); + for (; *p; p++) { if (*p == '.') *p = SEP; } - len += strlen(name); - assert(len < INT_MAX); - return (int)len; + return pathobj; } enum zi_module_info { @@ -256,27 +257,38 @@ /* Return some information about a module. */ static enum zi_module_info -get_module_info(ZipImporter *self, char *fullname) +get_module_info(ZipImporter *self, PyObject *fullname) { - char *subname, path[MAXPATHLEN + 1]; - int len; + PyObject *subname; + PyObject *path, *fullpath, *item; struct st_zip_searchorder *zso; subname = get_subname(fullname); + if (subname == NULL) + return MI_ERROR; - len = make_filename(self->prefix, subname, path, sizeof(path)); - if (len < 0) + path = make_filename(self->prefix, subname); + Py_DECREF(subname); + if (path == NULL) return MI_ERROR; for (zso = zip_searchorder; *zso->suffix; zso++) { - strcpy(path + len, zso->suffix); - if (PyDict_GetItemString(self->files, path) != NULL) { + fullpath = PyUnicode_FromFormat("%U%s", path, zso->suffix); + if (fullpath == NULL) { + Py_DECREF(path); + return MI_ERROR; + } + item = PyDict_GetItem(self->files, fullpath); + Py_DECREF(fullpath); + if (item != NULL) { + Py_DECREF(path); if (zso->type & IS_PACKAGE) return MI_PACKAGE; else return MI_MODULE; } } + Py_DECREF(path); return MI_NOT_FOUND; } @@ -287,10 +299,10 @@ { ZipImporter *self = (ZipImporter *)obj; PyObject *path = NULL; - char *fullname; + PyObject *fullname; enum zi_module_info mi; - if (!PyArg_ParseTuple(args, "s|O:zipimporter.find_module", + if (!PyArg_ParseTuple(args, "U|O:zipimporter.find_module", &fullname, &path)) return NULL; @@ -311,11 +323,11 @@ { ZipImporter *self = (ZipImporter *)obj; PyObject *code = NULL, *mod, *dict; - char *fullname; - PyObject *modpath = NULL, *modpath_bytes; + PyObject *fullname; + PyObject *modpath = NULL; int ispackage; - if (!PyArg_ParseTuple(args, "s:zipimporter.load_module", + if (!PyArg_ParseTuple(args, "U:zipimporter.load_module", &fullname)) return NULL; @@ -323,7 +335,7 @@ if (code == NULL) goto error; - mod = PyImport_AddModule(fullname); + mod = PyImport_AddModuleObject(fullname); if (mod == NULL) goto error; dict = PyModule_GetDict(mod); @@ -336,17 +348,17 @@ /* add __path__ to the module *before* the code gets executed */ PyObject *pkgpath, *fullpath; - char *subname = get_subname(fullname); + PyObject *subname = get_subname(fullname); int err; - fullpath = PyUnicode_FromFormat("%U%c%U%s", + fullpath = PyUnicode_FromFormat("%U%c%U%U", self->archive, SEP, self->prefix, subname); + Py_DECREF(subname); if (fullpath == NULL) goto error; - pkgpath = Py_BuildValue("[O]", fullpath); - Py_DECREF(fullpath); + pkgpath = Py_BuildValue("[N]", fullpath); if (pkgpath == NULL) goto error; err = PyDict_SetItemString(dict, "__path__", pkgpath); @@ -354,18 +366,13 @@ if (err != 0) goto error; } - modpath_bytes = PyUnicode_EncodeFSDefault(modpath); - if (modpath_bytes == NULL) - goto error; - mod = PyImport_ExecCodeModuleEx(fullname, code, - PyBytes_AS_STRING(modpath_bytes)); - Py_DECREF(modpath_bytes); + mod = PyImport_ExecCodeModuleObject(fullname, code, modpath, NULL); Py_CLEAR(code); if (mod == NULL) goto error; if (Py_VerboseFlag) - PySys_FormatStderr("import %s # loaded from Zip %U\n", + PySys_FormatStderr("import %U # loaded from Zip %U\n", fullname, modpath); Py_DECREF(modpath); return mod; @@ -380,12 +387,10 @@ zipimporter_get_filename(PyObject *obj, PyObject *args) { ZipImporter *self = (ZipImporter *)obj; - PyObject *code; - char *fullname; - PyObject *modpath; + PyObject *fullname, *code, *modpath; int ispackage; - if (!PyArg_ParseTuple(args, "s:zipimporter.get_filename", + if (!PyArg_ParseTuple(args, "U:zipimporter.get_filename", &fullname)) return NULL; @@ -404,10 +409,10 @@ zipimporter_is_package(PyObject *obj, PyObject *args) { ZipImporter *self = (ZipImporter *)obj; - char *fullname; + PyObject *fullname; enum zi_module_info mi; - if (!PyArg_ParseTuple(args, "s:zipimporter.is_package", + if (!PyArg_ParseTuple(args, "U:zipimporter.is_package", &fullname)) return NULL; @@ -415,7 +420,7 @@ if (mi == MI_ERROR) return NULL; if (mi == MI_NOT_FOUND) { - PyErr_Format(ZipImportError, "can't find module '%s'", fullname); + PyErr_Format(ZipImportError, "can't find module %R", fullname); return NULL; } return PyBool_FromLong(mi == MI_PACKAGE); @@ -477,9 +482,9 @@ zipimporter_get_code(PyObject *obj, PyObject *args) { ZipImporter *self = (ZipImporter *)obj; - char *fullname; + PyObject *fullname; - if (!PyArg_ParseTuple(args, "s:zipimporter.get_code", &fullname)) + if (!PyArg_ParseTuple(args, "U:zipimporter.get_code", &fullname)) return NULL; return get_module_code(self, fullname, NULL, NULL); @@ -490,34 +495,39 @@ { ZipImporter *self = (ZipImporter *)obj; PyObject *toc_entry; - char *fullname, *subname, path[MAXPATHLEN+1]; - int len; + PyObject *fullname, *subname, *path, *fullpath; enum zi_module_info mi; - if (!PyArg_ParseTuple(args, "s:zipimporter.get_source", &fullname)) + if (!PyArg_ParseTuple(args, "U:zipimporter.get_source", &fullname)) return NULL; mi = get_module_info(self, fullname); if (mi == MI_ERROR) return NULL; if (mi == MI_NOT_FOUND) { - PyErr_Format(ZipImportError, "can't find module '%s'", fullname); + PyErr_Format(ZipImportError, "can't find module %R", fullname); return NULL; } + subname = get_subname(fullname); - - len = make_filename(self->prefix, subname, path, sizeof(path)); - if (len < 0) + if (subname == NULL) return NULL; - if (mi == MI_PACKAGE) { - path[len] = SEP; - strcpy(path + len + 1, "__init__.py"); - } + path = make_filename(self->prefix, subname); + Py_DECREF(subname); + if (path == NULL) + return NULL; + + if (mi == MI_PACKAGE) + fullpath = PyUnicode_FromFormat("%U%c__init__.py", path, SEP); else - strcpy(path + len, ".py"); + fullpath = PyUnicode_FromFormat("%U.py", path); + Py_DECREF(path); + if (fullpath == NULL) + return NULL; - toc_entry = PyDict_GetItemString(self->files, path); + toc_entry = PyDict_GetItem(self->files, fullpath); + Py_DECREF(fullpath); if (toc_entry != NULL) { PyObject *res, *bytes; bytes = get_data(self->archive, toc_entry); @@ -708,9 +718,8 @@ data_size and file_offset are 0. */ static PyObject * -read_directory(PyObject *archive_obj) +read_directory(PyObject *archive) { - /* FIXME: work on Py_UNICODE* instead of char* */ PyObject *files = NULL; FILE *fp; unsigned short flags; @@ -727,29 +736,29 @@ const char *charset; int bootstrap; - if (PyUnicode_GET_SIZE(archive_obj) > MAXPATHLEN) { + if (PyUnicode_GET_SIZE(archive) > MAXPATHLEN) { PyErr_SetString(PyExc_OverflowError, "Zip path name is too long"); return NULL; } - Py_UNICODE_strcpy(path, PyUnicode_AS_UNICODE(archive_obj)); + Py_UNICODE_strcpy(path, PyUnicode_AS_UNICODE(archive)); - fp = _Py_fopen(archive_obj, "rb"); + fp = _Py_fopen(archive, "rb"); if (fp == NULL) { - PyErr_Format(ZipImportError, "can't open Zip file: '%U'", archive_obj); + PyErr_Format(ZipImportError, "can't open Zip file: %R", archive); return NULL; } fseek(fp, -22, SEEK_END); header_position = ftell(fp); if (fread(endof_central_dir, 1, 22, fp) != 22) { fclose(fp); - PyErr_Format(ZipImportError, "can't read Zip file: '%U'", archive_obj); + PyErr_Format(ZipImportError, "can't read Zip file: %R", archive); return NULL; } if (get_long((unsigned char *)endof_central_dir) != 0x06054B50) { /* Bad: End of Central Dir signature */ fclose(fp); - PyErr_Format(ZipImportError, "not a Zip file: '%U'", archive_obj); + PyErr_Format(ZipImportError, "not a Zip file: %R", archive); return NULL; } @@ -826,7 +835,9 @@ PY_MAJOR_VERSION, PY_MINOR_VERSION); goto error; } - Py_UNICODE_strncpy(path + length + 1, PyUnicode_AS_UNICODE(nameobj), MAXPATHLEN - length - 1); + Py_UNICODE_strncpy(path + length + 1, + PyUnicode_AS_UNICODE(nameobj), + MAXPATHLEN - length - 1); pathobj = PyUnicode_FromUnicode(path, Py_UNICODE_strlen(path)); if (pathobj == NULL) @@ -844,8 +855,8 @@ } fclose(fp); if (Py_VerboseFlag) - PySys_FormatStderr("# zipimport: found %ld names in %U\n", - count, archive_obj); + PySys_FormatStderr("# zipimport: found %ld names in %R\n", + count, archive); return files; error: fclose(fp); @@ -999,7 +1010,7 @@ to .py if available and we don't want to mask other errors). Returns a new reference. */ static PyObject * -unmarshal_code(char *pathname, PyObject *data, time_t mtime) +unmarshal_code(PyObject *pathname, PyObject *data, time_t mtime) { PyObject *code; char *buf = PyBytes_AsString(data); @@ -1013,8 +1024,8 @@ if (get_long((unsigned char *)buf) != PyImport_GetMagicNumber()) { if (Py_VerboseFlag) - PySys_WriteStderr("# %s has bad magic\n", - pathname); + PySys_FormatStderr("# %R has bad magic\n", + pathname); Py_INCREF(Py_None); return Py_None; /* signal caller to try alternative */ } @@ -1022,8 +1033,8 @@ if (mtime != 0 && !eq_mtime(get_long((unsigned char *)buf + 4), mtime)) { if (Py_VerboseFlag) - PySys_WriteStderr("# %s has bad mtime\n", - pathname); + PySys_FormatStderr("# %R has bad mtime\n", + pathname); Py_INCREF(Py_None); return Py_None; /* signal caller to try alternative */ } @@ -1034,7 +1045,7 @@ if (!PyCode_Check(code)) { Py_DECREF(code); PyErr_Format(PyExc_TypeError, - "compiled module %s is not a code object", + "compiled module %R is not a code object", pathname); return NULL; } @@ -1048,11 +1059,12 @@ static PyObject * normalize_line_endings(PyObject *source) { - char *buf, *q, *p = PyBytes_AsString(source); + char *buf, *q, *p; PyObject *fixed_source; int len = 0; - if (!p) { + p = PyBytes_AsString(source); + if (p == NULL) { return PyBytes_FromStringAndSize("\n\0", 2); } @@ -1085,16 +1097,24 @@ /* Given a string buffer containing Python source code, compile it return and return a code object as a new reference. */ static PyObject * -compile_source(char *pathname, PyObject *source) +compile_source(PyObject *pathname, PyObject *source) { - PyObject *code, *fixed_source; + PyObject *code, *fixed_source, *pathbytes; + + pathbytes = PyUnicode_EncodeFSDefault(pathname); + if (pathbytes == NULL) + return NULL; fixed_source = normalize_line_endings(source); - if (fixed_source == NULL) + if (fixed_source == NULL) { + Py_DECREF(pathbytes); return NULL; + } - code = Py_CompileString(PyBytes_AsString(fixed_source), pathname, + code = Py_CompileString(PyBytes_AsString(fixed_source), + PyBytes_AsString(pathbytes), Py_file_input); + Py_DECREF(pathbytes); Py_DECREF(fixed_source); return code; } @@ -1120,17 +1140,22 @@ } /* Given a path to a .pyc or .pyo file in the archive, return the - modifictaion time of the matching .py file, or 0 if no source + modification time of the matching .py file, or 0 if no source is available. */ static time_t -get_mtime_of_source(ZipImporter *self, char *path) +get_mtime_of_source(ZipImporter *self, PyObject *path) { - PyObject *toc_entry; - time_t mtime = 0; - Py_ssize_t lastchar = strlen(path) - 1; - char savechar = path[lastchar]; - path[lastchar] = '\0'; /* strip 'c' or 'o' from *.py[co] */ - toc_entry = PyDict_GetItemString(self->files, path); + PyObject *toc_entry, *stripped; + time_t mtime; + + /* strip 'c' or 'o' from *.py[co] */ + stripped = PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(path), + PyUnicode_GET_SIZE(path) - 1); + if (stripped == NULL) + return (time_t)-1; + + toc_entry = PyDict_GetItem(self->files, stripped); + Py_DECREF(stripped); if (toc_entry != NULL && PyTuple_Check(toc_entry) && PyTuple_Size(toc_entry) == 8) { /* fetch the time stamp of the .py file for comparison @@ -1139,8 +1164,8 @@ time = PyLong_AsLong(PyTuple_GetItem(toc_entry, 5)); date = PyLong_AsLong(PyTuple_GetItem(toc_entry, 6)); mtime = parse_dostime(time, date); - } - path[lastchar] = savechar; + } else + mtime = 0; return mtime; } @@ -1150,24 +1175,17 @@ get_code_from_data(ZipImporter *self, int ispackage, int isbytecode, time_t mtime, PyObject *toc_entry) { - PyObject *data, *code; - PyObject *modpath; + PyObject *data, *modpath, *code; data = get_data(self->archive, toc_entry); if (data == NULL) return NULL; - modpath = PyUnicode_EncodeFSDefault(PyTuple_GetItem(toc_entry, 0)); - if (modpath == NULL) { - Py_DECREF(data); - return NULL; - } - + modpath = PyTuple_GetItem(toc_entry, 0); if (isbytecode) - code = unmarshal_code(PyBytes_AS_STRING(modpath), data, mtime); + code = unmarshal_code(modpath, data, mtime); else - code = compile_source(PyBytes_AS_STRING(modpath), data); - Py_DECREF(modpath); + code = compile_source(modpath, data); Py_DECREF(data); return code; } @@ -1175,35 +1193,45 @@ /* Get the code object associated with the module specified by 'fullname'. */ static PyObject * -get_module_code(ZipImporter *self, char *fullname, +get_module_code(ZipImporter *self, PyObject *fullname, int *p_ispackage, PyObject **p_modpath) { - PyObject *toc_entry; - char *subname, path[MAXPATHLEN + 1]; - int len; + PyObject *code, *toc_entry, *subname; + PyObject *path, *fullpath; struct st_zip_searchorder *zso; subname = get_subname(fullname); + if (subname == NULL) + return NULL; - len = make_filename(self->prefix, subname, path, sizeof(path)); - if (len < 0) + path = make_filename(self->prefix, subname); + Py_DECREF(subname); + if (path == NULL) return NULL; for (zso = zip_searchorder; *zso->suffix; zso++) { - PyObject *code = NULL; + code = NULL; - strcpy(path + len, zso->suffix); + fullpath = PyUnicode_FromFormat("%U%s", path, zso->suffix); + if (fullpath == NULL) + goto exit; + if (Py_VerboseFlag > 1) - PySys_FormatStderr("# trying %U%c%s\n", - self->archive, (int)SEP, path); - toc_entry = PyDict_GetItemString(self->files, path); + PySys_FormatStderr("# trying %U%c%U\n", + self->archive, (int)SEP, fullpath); + toc_entry = PyDict_GetItem(self->files, fullpath); if (toc_entry != NULL) { time_t mtime = 0; int ispackage = zso->type & IS_PACKAGE; int isbytecode = zso->type & IS_BYTECODE; - if (isbytecode) - mtime = get_mtime_of_source(self, path); + if (isbytecode) { + mtime = get_mtime_of_source(self, fullpath); + if (mtime == (time_t)-1 && PyErr_Occurred()) { + goto exit; + } + } + Py_CLEAR(fullpath); if (p_ispackage != NULL) *p_ispackage = ispackage; code = get_code_from_data(self, ispackage, @@ -1219,11 +1247,16 @@ *p_modpath = PyTuple_GetItem(toc_entry, 0); Py_INCREF(*p_modpath); } - return code; + goto exit; } + else + Py_CLEAR(fullpath); } - PyErr_Format(ZipImportError, "can't find module '%s'", fullname); - return NULL; + PyErr_Format(ZipImportError, "can't find module %R", fullname); +exit: + Py_DECREF(path); + Py_XDECREF(fullpath); + return code; } diff -r 9dcd88977694 -r 650549138a3d Objects/abstract.c --- a/Objects/abstract.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Objects/abstract.c Tue Mar 22 21:32:51 2011 +1000 @@ -331,7 +331,7 @@ { if (!PyObject_CheckBuffer(obj)) { PyErr_Format(PyExc_TypeError, - "'%100s' does not support the buffer interface", + "'%.100s' does not support the buffer interface", Py_TYPE(obj)->tp_name); return -1; } diff -r 9dcd88977694 -r 650549138a3d Objects/complexobject.c --- a/Objects/complexobject.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Objects/complexobject.c Tue Mar 22 21:32:51 2011 +1000 @@ -330,12 +330,10 @@ int precision = 0; char format_code = 'r'; PyObject *result = NULL; - Py_ssize_t len; /* If these are non-NULL, they'll need to be freed. */ char *pre = NULL; char *im = NULL; - char *buf = NULL; /* These do not need to be freed. re is either an alias for pre or a pointer to a constant. lead and tail @@ -374,20 +372,10 @@ lead = "("; tail = ")"; } - /* Alloc the final buffer. Add one for the "j" in the format string, - and one for the trailing zero byte. */ - len = strlen(lead) + strlen(re) + strlen(im) + strlen(tail) + 2; - buf = PyMem_Malloc(len); - if (!buf) { - PyErr_NoMemory(); - goto done; - } - PyOS_snprintf(buf, len, "%s%s%sj%s", lead, re, im, tail); - result = PyUnicode_FromString(buf); + result = PyUnicode_FromFormat("%s%s%sj%s", lead, re, im, tail); done: PyMem_Free(im); PyMem_Free(pre); - PyMem_Free(buf); return result; } diff -r 9dcd88977694 -r 650549138a3d Objects/dictobject.c --- a/Objects/dictobject.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Objects/dictobject.c Tue Mar 22 21:32:51 2011 +1000 @@ -2080,7 +2080,7 @@ assert(d->ma_table == NULL && d->ma_fill == 0 && d->ma_used == 0); INIT_NONZERO_DICT_SLOTS(d); d->ma_lookup = lookdict_unicode; - /* The object has been implicitely tracked by tp_alloc */ + /* The object has been implicitly tracked by tp_alloc */ if (type == &PyDict_Type) _PyObject_GC_UNTRACK(d); #ifdef SHOW_CONVERSION_COUNTS diff -r 9dcd88977694 -r 650549138a3d Objects/listobject.c --- a/Objects/listobject.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Objects/listobject.c Tue Mar 22 21:32:51 2011 +1000 @@ -11,7 +11,7 @@ /* Ensure ob_item has room for at least newsize elements, and set * ob_size to newsize. If newsize > ob_size on entry, the content * of the new slots at exit is undefined heap trash; it's the caller's - * responsiblity to overwrite them with sane values. + * responsibility to overwrite them with sane values. * The number of allocated elements may grow, shrink, or stay the same. * Failure is impossible if newsize <= self.allocated on entry, although * that partly relies on an assumption that the system realloc() never diff -r 9dcd88977694 -r 650549138a3d Objects/longobject.c --- a/Objects/longobject.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Objects/longobject.c Tue Mar 22 21:32:51 2011 +1000 @@ -709,7 +709,7 @@ is_signed = *pendbyte >= 0x80; /* Compute numsignificantbytes. This consists of finding the most - significant byte. Leading 0 bytes are insignficant if the number + significant byte. Leading 0 bytes are insignificant if the number is positive, and leading 0xff bytes if negative. */ { size_t i; diff -r 9dcd88977694 -r 650549138a3d Objects/typeobject.c --- a/Objects/typeobject.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Objects/typeobject.c Tue Mar 22 21:32:51 2011 +1000 @@ -1008,7 +1008,7 @@ self has a refcount of 0, and if gc ever gets its hands on it (which can happen if any weakref callback gets invoked), it looks like trash to gc too, and gc also tries to delete self - then. But we're already deleting self. Double dealloction is + then. But we're already deleting self. Double deallocation is a subtle disaster. Q. Why the bizarre (net-zero) manipulation of @@ -5953,7 +5953,7 @@ slots compete for the same descriptor (for example both sq_item and mp_subscript generate a __getitem__ descriptor). - In the latter case, the first slotdef entry encoutered wins. Since + In the latter case, the first slotdef entry encountered wins. Since slotdef entries are sorted by the offset of the slot in the PyHeapTypeObject, this gives us some control over disambiguating between competing slots: the members of PyHeapTypeObject are listed diff -r 9dcd88977694 -r 650549138a3d Objects/weakrefobject.c --- a/Objects/weakrefobject.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Objects/weakrefobject.c Tue Mar 22 21:32:51 2011 +1000 @@ -156,35 +156,31 @@ static PyObject * weakref_repr(PyWeakReference *self) { - char buffer[256]; - if (PyWeakref_GET_OBJECT(self) == Py_None) { - PyOS_snprintf(buffer, sizeof(buffer), "", self); + PyObject *name, *repr; + + if (PyWeakref_GET_OBJECT(self) == Py_None) + return PyUnicode_FromFormat("", self); + + name = PyObject_GetAttrString(PyWeakref_GET_OBJECT(self), "__name__"); + if (name == NULL || !PyUnicode_Check(name)) { + if (name == NULL) + PyErr_Clear(); + repr = PyUnicode_FromFormat( + "", + self, + Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name, + PyWeakref_GET_OBJECT(self)); } else { - char *name = NULL; - PyObject *nameobj = PyObject_GetAttrString(PyWeakref_GET_OBJECT(self), - "__name__"); - if (nameobj == NULL) - PyErr_Clear(); - else if (PyUnicode_Check(nameobj)) - name = _PyUnicode_AsString(nameobj); - if (name) - PyOS_snprintf(buffer, sizeof(buffer), - "", - self, - Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name, - PyWeakref_GET_OBJECT(self), - name); - else - PyOS_snprintf(buffer, sizeof(buffer), - "", - self, - Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name, - PyWeakref_GET_OBJECT(self)); - - Py_XDECREF(nameobj); + repr = PyUnicode_FromFormat( + "", + self, + Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name, + PyWeakref_GET_OBJECT(self), + name); } - return PyUnicode_FromString(buffer); + Py_XDECREF(name); + return repr; } /* Weak references only support equality, not ordering. Two weak references @@ -459,12 +455,11 @@ static PyObject * proxy_repr(PyWeakReference *proxy) { - char buf[160]; - PyOS_snprintf(buf, sizeof(buf), - "", proxy, - Py_TYPE(PyWeakref_GET_OBJECT(proxy))->tp_name, - PyWeakref_GET_OBJECT(proxy)); - return PyUnicode_FromString(buf); + return PyUnicode_FromFormat( + "", + proxy, + Py_TYPE(PyWeakref_GET_OBJECT(proxy))->tp_name, + PyWeakref_GET_OBJECT(proxy)); } diff -r 9dcd88977694 -r 650549138a3d PC/bdist_wininst/extract.c --- a/PC/bdist_wininst/extract.c Tue Mar 22 21:31:46 2011 +1000 +++ b/PC/bdist_wininst/extract.c Tue Mar 22 21:32:51 2011 +1000 @@ -54,7 +54,7 @@ return TRUE; } -/* XXX Should better explicitely specify +/* XXX Should better explicitly specify * uncomp_size and file_times instead of pfhdr! */ char *map_new_file(DWORD flags, char *filename, @@ -164,7 +164,7 @@ zstream.avail_out = uncomp_size; /* Apparently an undocumented feature of zlib: Set windowsize - to negative values to supress the gzip header and be compatible with + to negative values to suppress the gzip header and be compatible with zip! */ result = TRUE; if (Z_OK != (x = inflateInit2(&zstream, -15))) { diff -r 9dcd88977694 -r 650549138a3d PC/bdist_wininst/install.c --- a/PC/bdist_wininst/install.c Tue Mar 22 21:31:46 2011 +1000 +++ b/PC/bdist_wininst/install.c Tue Mar 22 21:32:51 2011 +1000 @@ -148,7 +148,7 @@ the permissions of the current user. */ HKEY hkey_root = (HKEY)-1; -BOOL success; /* Installation successfull? */ +BOOL success; /* Installation successful? */ char *failure_reason = NULL; HANDLE hBitmap; @@ -797,7 +797,7 @@ tempname = tempnam(NULL, NULL); // We use a static CRT while the Python version we load uses - // the CRT from one of various possibile DLLs. As a result we + // the CRT from one of various possible DLLs. As a result we // need to redirect the standard handles using the API rather // than the CRT. redirected = CreateFile( diff -r 9dcd88977694 -r 650549138a3d PC/import_nt.c --- a/PC/import_nt.c Tue Mar 22 21:31:46 2011 +1000 +++ b/PC/import_nt.c Tue Mar 22 21:32:51 2011 +1000 @@ -15,72 +15,93 @@ /* a string loaded from the DLL at startup */ extern const char *PyWin_DLLVersionString; -FILE *_PyWin_FindRegisteredModule(const char *moduleName, - struct filedescr **ppFileDesc, - char *pathBuf, - Py_ssize_t pathLen) +/* Find a module on Windows. + + Read the registry Software\Python\PythonCore\\Modules\ (or + Software\Python\PythonCore\\Modules\\Debug in debug mode) + from HKEY_CURRENT_USER, or HKEY_LOCAL_MACHINE. Find the file descriptor using + the file extension. Open the file. + + On success, write the file descriptor into *ppFileDesc, the module path + (Unicode object) into *pPath, and return the opened file object. If the + module cannot be found (e.g. no registry key or the file doesn't exist), + return NULL. On error, raise a Python exception and return NULL. + */ +FILE * +_PyWin_FindRegisteredModule(PyObject *moduleName, + struct filedescr **ppFileDesc, + PyObject **pPath) { - char *moduleKey; - const char keyPrefix[] = "Software\\Python\\PythonCore\\"; - const char keySuffix[] = "\\Modules\\"; -#ifdef _DEBUG - /* In debugging builds, we _must_ have the debug version - * registered. - */ - const char debugString[] = "\\Debug"; -#else - const char debugString[] = ""; -#endif - struct filedescr *fdp = NULL; - FILE *fp; - HKEY keyBase = HKEY_CURRENT_USER; + wchar_t pathBuf[MAXPATHLEN+1]; + int pathLen = MAXPATHLEN+1; + PyObject *path, *moduleKey, *suffix; + struct filedescr *fdp; + HKEY keyBase; int modNameSize; long regStat; + Py_ssize_t extLen; + FILE *fp; - /* Calculate the size for the sprintf buffer. - * Get the size of the chars only, plus 1 NULL. - */ - size_t bufSize = sizeof(keyPrefix)-1 + - strlen(PyWin_DLLVersionString) + - sizeof(keySuffix) + - strlen(moduleName) + - sizeof(debugString) - 1; - /* alloca == no free required, but memory only local to fn, - * also no heap fragmentation! - */ - moduleKey = alloca(bufSize); - PyOS_snprintf(moduleKey, bufSize, - "Software\\Python\\PythonCore\\%s\\Modules\\%s%s", - PyWin_DLLVersionString, moduleName, debugString); + moduleKey = PyUnicode_FromFormat( +#ifdef _DEBUG + /* In debugging builds, we _must_ have the debug version registered */ + "Software\\Python\\PythonCore\\%s\\Modules\\%U\\Debug", +#else + "Software\\Python\\PythonCore\\%s\\Modules\\%U", +#endif + PyWin_DLLVersionString, moduleName); + if (moduleKey == NULL) + return NULL; - assert(pathLen < INT_MAX); - modNameSize = (int)pathLen; - regStat = RegQueryValue(keyBase, moduleKey, pathBuf, &modNameSize); + keyBase = HKEY_CURRENT_USER; + modNameSize = pathLen; + regStat = RegQueryValueW(keyBase, PyUnicode_AS_UNICODE(moduleKey), + pathBuf, &modNameSize); if (regStat != ERROR_SUCCESS) { /* No user setting - lookup in machine settings */ keyBase = HKEY_LOCAL_MACHINE; /* be anal - failure may have reset size param */ - modNameSize = (int)pathLen; - regStat = RegQueryValue(keyBase, moduleKey, - pathBuf, &modNameSize); - - if (regStat != ERROR_SUCCESS) + modNameSize = pathLen; + regStat = RegQueryValueW(keyBase, PyUnicode_AS_UNICODE(moduleKey), + pathBuf, &modNameSize); + if (regStat != ERROR_SUCCESS) { + Py_DECREF(moduleKey); return NULL; + } + } + Py_DECREF(moduleKey); + if (modNameSize < 3) { + /* path shorter than "a.o" or negative length (cast to + size_t is wrong) */ + return NULL; } /* use the file extension to locate the type entry. */ for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { - size_t extLen = strlen(fdp->suffix); - assert(modNameSize >= 0); /* else cast to size_t is wrong */ - if ((size_t)modNameSize > extLen && - strnicmp(pathBuf + ((size_t)modNameSize-extLen-1), - fdp->suffix, - extLen) == 0) + suffix = PyUnicode_FromString(fdp->suffix); + if (suffix == NULL) + return NULL; + extLen = PyUnicode_GET_SIZE(suffix); + if ((Py_ssize_t)modNameSize > extLen && + _wcsnicmp(pathBuf + ((Py_ssize_t)modNameSize-extLen-1), + PyUnicode_AS_UNICODE(suffix), + extLen) == 0) + { + Py_DECREF(suffix); break; + } + Py_DECREF(suffix); } if (fdp->suffix == NULL) return NULL; - fp = fopen(pathBuf, fdp->mode); - if (fp != NULL) - *ppFileDesc = fdp; + path = PyUnicode_FromUnicode(pathBuf, wcslen(pathBuf)); + if (path == NULL) + return NULL; + fp = _Py_fopen(path, fdp->mode); + if (fp == NULL) { + Py_DECREF(path); + return NULL; + } + *pPath = path; + *ppFileDesc = fdp; return fp; } diff -r 9dcd88977694 -r 650549138a3d PC/os2emx/dlfcn.c --- a/PC/os2emx/dlfcn.c Tue Mar 22 21:31:46 2011 +1000 +++ b/PC/os2emx/dlfcn.c Tue Mar 22 21:32:51 2011 +1000 @@ -188,7 +188,7 @@ return NULL; } -/* free dynamicaly-linked library */ +/* free dynamically-linked library */ int dlclose(void *handle) { int rc; diff -r 9dcd88977694 -r 650549138a3d PC/os2emx/dlfcn.h --- a/PC/os2emx/dlfcn.h Tue Mar 22 21:31:46 2011 +1000 +++ b/PC/os2emx/dlfcn.h Tue Mar 22 21:32:51 2011 +1000 @@ -42,7 +42,7 @@ /* return a pointer to the `symbol' in DLL */ void *dlsym(void *handle, char *symbol); -/* free dynamicaly-linked library */ +/* free dynamically-linked library */ int dlclose(void *handle); /* return a string describing last occurred dl error */ diff -r 9dcd88977694 -r 650549138a3d Python/bltinmodule.c --- a/Python/bltinmodule.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/bltinmodule.c Tue Mar 22 21:32:51 2011 +1000 @@ -155,17 +155,14 @@ { static char *kwlist[] = {"name", "globals", "locals", "fromlist", "level", 0}; - char *name; - PyObject *globals = NULL; - PyObject *locals = NULL; - PyObject *fromlist = NULL; + PyObject *name, *globals = NULL, *locals = NULL, *fromlist = NULL; int level = -1; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|OOOi:__import__", + if (!PyArg_ParseTupleAndKeywords(args, kwds, "U|OOOi:__import__", kwlist, &name, &globals, &locals, &fromlist, &level)) return NULL; - return PyImport_ImportModuleLevel(name, globals, locals, - fromlist, level); + return PyImport_ImportModuleLevelObject(name, globals, locals, + fromlist, level); } PyDoc_STRVAR(import_doc, diff -r 9dcd88977694 -r 650549138a3d Python/ceval.c --- a/Python/ceval.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/ceval.c Tue Mar 22 21:32:51 2011 +1000 @@ -26,7 +26,7 @@ typedef unsigned long long uint64; -/* PowerPC suppport. +/* PowerPC support. "__ppc__" appears to be the preprocessor definition to detect on OS X, whereas "__powerpc__" appears to be the correct one for Linux with GCC */ @@ -1266,7 +1266,7 @@ if (_Py_atomic_load_relaxed(&eval_breaker)) { if (*next_instr == SETUP_FINALLY) { /* Make the last opcode before - a try: finally: block uninterruptable. */ + a try: finally: block uninterruptible. */ goto fast_next_opcode; } tstate->tick_counter++; @@ -4130,7 +4130,7 @@ if (PyErr_ExceptionMatches(PyExc_TypeError)) { PyErr_Format(PyExc_TypeError, "%.200s%.200s argument after * " - "must be a sequence, not %200s", + "must be a sequence, not %.200s", PyEval_GetFuncName(func), PyEval_GetFuncDesc(func), stararg->ob_type->tp_name); diff -r 9dcd88977694 -r 650549138a3d Python/getargs.c --- a/Python/getargs.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/getargs.c Tue Mar 22 21:32:51 2011 +1000 @@ -310,20 +310,18 @@ if (max == 0) { if (args == NULL) return 1; - PyOS_snprintf(msgbuf, sizeof(msgbuf), - "%.200s%s takes no arguments", - fname==NULL ? "function" : fname, - fname==NULL ? "" : "()"); - PyErr_SetString(PyExc_TypeError, msgbuf); + PyErr_Format(PyExc_TypeError, + "%.200s%s takes no arguments", + fname==NULL ? "function" : fname, + fname==NULL ? "" : "()"); return 0; } else if (min == 1 && max == 1) { if (args == NULL) { - PyOS_snprintf(msgbuf, sizeof(msgbuf), - "%.200s%s takes at least one argument", - fname==NULL ? "function" : fname, - fname==NULL ? "" : "()"); - PyErr_SetString(PyExc_TypeError, msgbuf); + PyErr_Format(PyExc_TypeError, + "%.200s%s takes at least one argument", + fname==NULL ? "function" : fname, + fname==NULL ? "" : "()"); return 0; } msg = convertitem(args, &format, p_va, flags, levels, @@ -349,20 +347,18 @@ len = PyTuple_GET_SIZE(args); if (len < min || max < len) { - if (message == NULL) { - PyOS_snprintf(msgbuf, sizeof(msgbuf), - "%.150s%s takes %s %d argument%s " - "(%ld given)", - fname==NULL ? "function" : fname, - fname==NULL ? "" : "()", - min==max ? "exactly" - : len < min ? "at least" : "at most", - len < min ? min : max, - (len < min ? min : max) == 1 ? "" : "s", - Py_SAFE_DOWNCAST(len, Py_ssize_t, long)); - message = msgbuf; - } - PyErr_SetString(PyExc_TypeError, message); + if (message == NULL) + PyErr_Format(PyExc_TypeError, + "%.150s%s takes %s %d argument%s (%ld given)", + fname==NULL ? "function" : fname, + fname==NULL ? "" : "()", + min==max ? "exactly" + : len < min ? "at least" : "at most", + len < min ? min : max, + (len < min ? min : max) == 1 ? "" : "s", + Py_SAFE_DOWNCAST(len, Py_ssize_t, long)); + else + PyErr_SetString(PyExc_TypeError, message); return 0; } @@ -1458,8 +1454,8 @@ nargs = PyTuple_GET_SIZE(args); nkeywords = (keywords == NULL) ? 0 : PyDict_Size(keywords); if (nargs + nkeywords > len) { - PyErr_Format(PyExc_TypeError, "%s%s takes at most %d " - "argument%s (%d given)", + PyErr_Format(PyExc_TypeError, + "%s%s takes at most %d argument%s (%d given)", (fname == NULL) ? "function" : fname, (fname == NULL) ? "" : "()", len, diff -r 9dcd88977694 -r 650549138a3d Python/import.c --- a/Python/import.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/import.c Tue Mar 22 21:32:51 2011 +1000 @@ -107,17 +107,21 @@ /* MAGIC must change whenever the bytecode emitted by the compiler may no longer be understood by older implementations of the eval loop (usually due to the addition of new opcodes) - TAG must change for each major Python release. The magic number will take - care of any bytecode changes that occur during development. + TAG and PYC_TAG_UNICODE must change for each major Python release. The magic + number will take care of any bytecode changes that occur during development. */ #define MAGIC (3180 | ((long)'\r'<<16) | ((long)'\n'<<24)) #define TAG "cpython-32" #define CACHEDIR "__pycache__" +static const Py_UNICODE CACHEDIR_UNICODE[] = { + '_', '_', 'p', 'y', 'c', 'a', 'c', 'h', 'e', '_', '_', '\0'}; /* Current magic word and string tag as globals. */ static long pyc_magic = MAGIC; static const char *pyc_tag = TAG; - -/* See _PyImport_FixupExtensionUnicode() below */ +static const Py_UNICODE PYC_TAG_UNICODE[] = { + 'c', 'p', 'y', 't', 'h', 'o', 'n', '-', '3', '2', '\0'}; + +/* See _PyImport_FixupExtensionObject() below */ static PyObject *extensions = NULL; /* This table is defined in config.c: */ @@ -140,6 +144,8 @@ {0, 0} }; +static PyObject *initstr = NULL; + /* Initialize things */ @@ -151,6 +157,10 @@ int countD = 0; int countS = 0; + initstr = PyUnicode_InternFromString("__init__"); + if (initstr == NULL) + Py_FatalError("Can't initialize import variables"); + /* prepare _PyImport_Filetab: copy entries from _PyImport_DynLoadFiletab and _PyImport_StandardFiletab. */ @@ -329,7 +339,7 @@ /* Forked as a side effect of import */ long me = PyThread_get_thread_ident(); PyThread_acquire_lock(import_lock, 0); - /* XXX: can the previous line fail? */ + /* XXX: can the previous line fail? */ import_lock_thread = me; import_lock_level--; } else { @@ -418,7 +428,6 @@ PyImport_Cleanup(void) { Py_ssize_t pos, ndone; - char *name; PyObject *key, *value, *dict; PyInterpreterState *interp = PyThreadState_GET()->interp; PyObject *modules = interp->modules; @@ -491,14 +500,13 @@ if (value->ob_refcnt != 1) continue; if (PyUnicode_Check(key) && PyModule_Check(value)) { - name = _PyUnicode_AsString(key); - if (strcmp(name, "builtins") == 0) + if (PyUnicode_CompareWithASCIIString(key, "builtins") == 0) continue; - if (strcmp(name, "sys") == 0) + if (PyUnicode_CompareWithASCIIString(key, "sys") == 0) continue; if (Py_VerboseFlag) - PySys_WriteStderr( - "# cleanup[1] %s\n", name); + PySys_FormatStderr( + "# cleanup[1] %U\n", key); _PyModule_Clear(value); PyDict_SetItem(modules, key, Py_None); ndone++; @@ -510,13 +518,12 @@ pos = 0; while (PyDict_Next(modules, &pos, &key, &value)) { if (PyUnicode_Check(key) && PyModule_Check(value)) { - name = _PyUnicode_AsString(key); - if (strcmp(name, "builtins") == 0) + if (PyUnicode_CompareWithASCIIString(key, "builtins") == 0) continue; - if (strcmp(name, "sys") == 0) + if (PyUnicode_CompareWithASCIIString(key, "sys") == 0) continue; if (Py_VerboseFlag) - PySys_WriteStderr("# cleanup[2] %s\n", name); + PySys_FormatStderr("# cleanup[2] %U\n", key); _PyModule_Clear(value); PyDict_SetItem(modules, key, Py_None); } @@ -566,10 +573,10 @@ once, we keep a static dictionary 'extensions' keyed by module name (for built-in modules) or by filename (for dynamically loaded modules), containing these modules. A copy of the module's - dictionary is stored by calling _PyImport_FixupExtensionUnicode() + dictionary is stored by calling _PyImport_FixupExtensionObject() immediately after the module initialization function succeeds. A copy can be retrieved from there by calling - _PyImport_FindExtensionUnicode(). + _PyImport_FindExtensionObject(). Modules which do support multiple initialization set their m_size field to a non-negative number (indicating the size of the @@ -578,7 +585,8 @@ */ int -_PyImport_FixupExtensionUnicode(PyObject *mod, char *name, PyObject *filename) +_PyImport_FixupExtensionObject(PyObject *mod, PyObject *name, + PyObject *filename) { PyObject *modules, *dict; struct PyModuleDef *def; @@ -597,10 +605,10 @@ return -1; } modules = PyImport_GetModuleDict(); - if (PyDict_SetItemString(modules, name, mod) < 0) + if (PyDict_SetItem(modules, name, mod) < 0) return -1; if (_PyState_AddModule(mod, def) < 0) { - PyDict_DelItemString(modules, name); + PyDict_DelItem(modules, name); return -1; } if (def->m_size == -1) { @@ -626,17 +634,17 @@ _PyImport_FixupBuiltin(PyObject *mod, char *name) { int res; - PyObject *filename; - filename = PyUnicode_FromString(name); - if (filename == NULL) + PyObject *nameobj; + nameobj = PyUnicode_InternFromString(name); + if (nameobj == NULL) return -1; - res = _PyImport_FixupExtensionUnicode(mod, name, filename); - Py_DECREF(filename); + res = _PyImport_FixupExtensionObject(mod, nameobj, nameobj); + Py_DECREF(nameobj); return res; } PyObject * -_PyImport_FindExtensionUnicode(const char *name, PyObject *filename) +_PyImport_FindExtensionObject(PyObject *name, PyObject *filename) { PyObject *mod, *mdict; PyModuleDef* def; @@ -649,7 +657,7 @@ /* Module does not support repeated initialization */ if (def->m_base.m_copy == NULL) return NULL; - mod = PyImport_AddModule(name); + mod = PyImport_AddModuleObject(name); if (mod == NULL) return NULL; mdict = PyModule_GetDict(mod); @@ -664,16 +672,16 @@ mod = def->m_base.m_init(); if (mod == NULL) return NULL; - PyDict_SetItemString(PyImport_GetModuleDict(), name, mod); + PyDict_SetItem(PyImport_GetModuleDict(), name, mod); Py_DECREF(mod); } if (_PyState_AddModule(mod, def) < 0) { - PyDict_DelItemString(PyImport_GetModuleDict(), name); + PyDict_DelItem(PyImport_GetModuleDict(), name); Py_DECREF(mod); return NULL; } if (Py_VerboseFlag) - PySys_FormatStderr("import %s # previously loaded (%U)\n", + PySys_FormatStderr("import %U # previously loaded (%R)\n", name, filename); return mod; @@ -682,12 +690,12 @@ PyObject * _PyImport_FindBuiltin(const char *name) { - PyObject *res, *filename; - filename = PyUnicode_FromString(name); - if (filename == NULL) + PyObject *res, *nameobj; + nameobj = PyUnicode_InternFromString(name); + if (nameobj == NULL) return NULL; - res = _PyImport_FindExtensionUnicode(name, filename); - Py_DECREF(filename); + res = _PyImport_FindExtensionObject(nameobj, nameobj); + Py_DECREF(nameobj); return res; } @@ -743,10 +751,9 @@ "sys.modules failed"); } -static PyObject * get_sourcefile(char *file); -static char *make_source_pathname(char *pathname, char *buf); -static char *make_compiled_pathname(char *pathname, char *buf, size_t buflen, - int debug); +static PyObject * get_sourcefile(PyObject *filename); +static PyObject *make_source_pathname(PyObject *pathname); +static PyObject* make_compiled_pathname(Py_UNICODE *pathname, int debug); /* Execute a code object in a module and return the module object * WITH INCREMENTED REFERENCE COUNT. If an error occurs, name is @@ -809,7 +816,6 @@ { PyObject *modules = PyImport_GetModuleDict(); PyObject *m, *d, *v; - PyObject *pathbytes; m = PyImport_AddModuleObject(name); if (m == NULL) @@ -824,12 +830,7 @@ } /* Remember the filename as the __file__ attribute */ if (pathname != NULL) { - pathbytes = PyUnicode_EncodeFSDefault(pathname); - if (pathbytes != NULL) { - v = get_sourcefile(PyBytes_AS_STRING(pathbytes)); - Py_DECREF(pathbytes); - } else - v = NULL; + v = get_sourcefile(pathname); if (v == NULL) PyErr_Clear(); } @@ -876,10 +877,10 @@ /* Like strrchr(string, '/') but searches for the rightmost of either SEP or ALTSEP, if the latter is defined. */ -static char * -rightmost_sep(char *s) +static Py_UNICODE* +rightmost_sep(Py_UNICODE *s) { - char *found, c; + Py_UNICODE *found, c; for (found = NULL; (c = *s); s++) { if (c == SEP #ifdef ALTSEP @@ -897,15 +898,18 @@ /* Given a pathname for a Python source file, fill a buffer with the pathname for the corresponding compiled file. Return the pathname for the compiled file, or NULL if there's no space in the buffer. - Doesn't set an exception. */ - -static char * -make_compiled_pathname(char *pathname, char *buf, size_t buflen, int debug) + Doesn't set an exception. + + foo.py -> __pycache__/foo..pyc */ + +static PyObject* +make_compiled_pathname(Py_UNICODE *pathname, int debug) { - /* foo.py -> __pycache__/foo..pyc */ - size_t len = strlen(pathname); + Py_UNICODE buf[MAXPATHLEN]; + size_t buflen = (size_t)MAXPATHLEN; + size_t len = Py_UNICODE_strlen(pathname); size_t i, save; - char *pos; + Py_UNICODE *pos; int sep = SEP; /* Sanity check that the buffer has roughly enough space to hold what @@ -917,35 +921,37 @@ sanity check before writing the extension to ensure we do not overflow the buffer. */ - if (len + strlen(CACHEDIR) + strlen(pyc_tag) + 5 > buflen) + if (len + Py_UNICODE_strlen(CACHEDIR_UNICODE) + Py_UNICODE_strlen(PYC_TAG_UNICODE) + 5 > buflen) return NULL; /* Find the last path separator and copy everything from the start of the source string up to and including the separator. */ - if ((pos = rightmost_sep(pathname)) == NULL) { + pos = rightmost_sep(pathname); + if (pos == NULL) { i = 0; } else { sep = *pos; i = pos - pathname + 1; - strncpy(buf, pathname, i); + Py_UNICODE_strncpy(buf, pathname, i); } save = i; buf[i++] = '\0'; /* Add __pycache__/ */ - strcat(buf, CACHEDIR); - i += strlen(CACHEDIR) - 1; + Py_UNICODE_strcat(buf, CACHEDIR_UNICODE); + i += Py_UNICODE_strlen(CACHEDIR_UNICODE) - 1; buf[i++] = sep; - buf[i++] = '\0'; + buf[i] = '\0'; /* Add the base filename, but remove the .py or .pyw extension, since the tag name must go before the extension. */ - strcat(buf, pathname + save); - if ((pos = strrchr(buf, '.')) != NULL) + Py_UNICODE_strcat(buf, pathname + save); + pos = Py_UNICODE_strrchr(buf + i, '.'); + if (pos != NULL) *++pos = '\0'; - strcat(buf, pyc_tag); + Py_UNICODE_strcat(buf, PYC_TAG_UNICODE); /* The length test above assumes that we're only adding one character to the end of what would normally be the extension. What if there is no extension, or the string ends in '.' or '.p', and otherwise @@ -995,11 +1001,15 @@ #if 0 printf("strlen(buf): %d; buflen: %d\n", (int)strlen(buf), (int)buflen); #endif - if (strlen(buf) + 5 > buflen) + len = Py_UNICODE_strlen(buf); + if (len + 5 > buflen) return NULL; - strcat(buf, debug ? ".pyc" : ".pyo"); - assert(strlen(buf) < buflen); - return buf; + buf[len] = '.'; len++; + buf[len] = 'p'; len++; + buf[len] = 'y'; len++; + buf[len] = debug ? 'c' : 'o'; len++; + assert(len <= buflen); + return PyUnicode_FromUnicode(buf, len); } @@ -1007,20 +1017,28 @@ source file, if the path matches the PEP 3147 format. This does not check for any file existence, however, if the pyc file name does not match PEP 3147 style, NULL is returned. buf must be at least as big as pathname; - the resulting path will always be shorter. */ - -static char * -make_source_pathname(char *pathname, char *buf) + the resulting path will always be shorter. + + (...)/__pycache__/foo..pyc -> (...)/foo.py */ + +static PyObject* +make_source_pathname(PyObject *pathobj) { - /* __pycache__/foo..pyc -> foo.py */ + Py_UNICODE buf[MAXPATHLEN]; + Py_UNICODE *pathname; + Py_UNICODE *left, *right, *dot0, *dot1, sep; size_t i, j; - char *left, *right, *dot0, *dot1, sep; + + if (PyUnicode_GET_SIZE(pathobj) > MAXPATHLEN) + return NULL; + pathname = PyUnicode_AS_UNICODE(pathobj); /* Look back two slashes from the end. In between these two slashes must be the string __pycache__ or this is not a PEP 3147 style path. It's possible for there to be only one slash. */ - if ((right = rightmost_sep(pathname)) == NULL) + right = rightmost_sep(pathname); + if (right == NULL) return NULL; sep = *right; *right = '\0'; @@ -1030,19 +1048,19 @@ left = pathname; else left++; - if (right-left != strlen(CACHEDIR) || - strncmp(left, CACHEDIR, right-left) != 0) + if (right-left != Py_UNICODE_strlen(CACHEDIR_UNICODE) || + Py_UNICODE_strncmp(left, CACHEDIR_UNICODE, right-left) != 0) return NULL; /* Now verify that the path component to the right of the last slash has two dots in it. */ - if ((dot0 = strchr(right + 1, '.')) == NULL) + if ((dot0 = Py_UNICODE_strchr(right + 1, '.')) == NULL) return NULL; - if ((dot1 = strchr(dot0 + 1, '.')) == NULL) + if ((dot1 = Py_UNICODE_strchr(dot0 + 1, '.')) == NULL) return NULL; /* Too many dots? */ - if (strchr(dot1 + 1, '.') != NULL) + if (Py_UNICODE_strchr(dot1 + 1, '.') != NULL) return NULL; /* This is a PEP 3147 path. Start by copying everything from the @@ -1050,10 +1068,11 @@ copy the file's basename, removing the magic tag and adding a .py suffix. */ - strncpy(buf, pathname, (i=left-pathname)); - strncpy(buf+i, right+1, (j=dot0-right)); - strcpy(buf+i+j, "py"); - return buf; + Py_UNICODE_strncpy(buf, pathname, (i=left-pathname)); + Py_UNICODE_strncpy(buf+i, right+1, (j=dot0-right)); + buf[i+j] = 'p'; + buf[i+j+1] = 'y'; + return PyUnicode_FromUnicode(buf, i+j+2); } /* Given a pathname for a Python source file, its time of last @@ -1064,31 +1083,31 @@ Doesn't set an exception. */ static FILE * -check_compiled_module(char *pathname, time_t mtime, char *cpathname) +check_compiled_module(PyObject *pathname, time_t mtime, PyObject *cpathname) { FILE *fp; long magic; long pyc_mtime; - fp = fopen(cpathname, "rb"); + fp = _Py_fopen(cpathname, "rb"); if (fp == NULL) return NULL; magic = PyMarshal_ReadLongFromFile(fp); if (magic != pyc_magic) { if (Py_VerboseFlag) - PySys_WriteStderr("# %s has bad magic\n", cpathname); + PySys_FormatStderr("# %R has bad magic\n", cpathname); fclose(fp); return NULL; } pyc_mtime = PyMarshal_ReadLongFromFile(fp); if (pyc_mtime != mtime) { if (Py_VerboseFlag) - PySys_WriteStderr("# %s has bad mtime\n", cpathname); + PySys_FormatStderr("# %R has bad mtime\n", cpathname); fclose(fp); return NULL; } if (Py_VerboseFlag) - PySys_WriteStderr("# %s matches %s\n", cpathname, pathname); + PySys_FormatStderr("# %R matches %R\n", cpathname, pathname); return fp; } @@ -1096,7 +1115,7 @@ /* Read a code object from a file and check it for validity */ static PyCodeObject * -read_compiled_module(char *cpathname, FILE *fp) +read_compiled_module(PyObject *cpathname, FILE *fp) { PyObject *co; @@ -1105,7 +1124,7 @@ return NULL; if (!PyCode_Check(co)) { PyErr_Format(PyExc_ImportError, - "Non-code object in %.200s", cpathname); + "Non-code object in %R", cpathname); Py_DECREF(co); return NULL; } @@ -1117,7 +1136,7 @@ module object WITH INCREMENTED REFERENCE COUNT */ static PyObject * -load_compiled_module(char *name, char *cpathname, FILE *fp) +load_compiled_module(PyObject *name, PyObject *cpathname, FILE *fp) { long magic; PyCodeObject *co; @@ -1126,7 +1145,7 @@ magic = PyMarshal_ReadLongFromFile(fp); if (magic != pyc_magic) { PyErr_Format(PyExc_ImportError, - "Bad magic number in %.200s", cpathname); + "Bad magic number in %R", cpathname); return NULL; } (void) PyMarshal_ReadLongFromFile(fp); @@ -1134,10 +1153,10 @@ if (co == NULL) return NULL; if (Py_VerboseFlag) - PySys_WriteStderr("import %s # precompiled from %s\n", - name, cpathname); - m = PyImport_ExecCodeModuleWithPathnames( - name, (PyObject *)co, cpathname, cpathname); + PySys_FormatStderr("import %U # precompiled from %R\n", + name, cpathname); + m = PyImport_ExecCodeModuleObject(name, (PyObject *)co, + cpathname, cpathname); Py_DECREF(co); return m; @@ -1146,27 +1165,37 @@ /* Parse a source file and return the corresponding code object */ static PyCodeObject * -parse_source_module(const char *pathname, FILE *fp) +parse_source_module(PyObject *pathname, FILE *fp) { - PyCodeObject *co = NULL; + PyCodeObject *co; + PyObject *pathbytes; mod_ty mod; PyCompilerFlags flags; - PyArena *arena = PyArena_New(); - if (arena == NULL) + PyArena *arena; + + pathbytes = PyUnicode_EncodeFSDefault(pathname); + if (pathbytes == NULL) return NULL; + arena = PyArena_New(); + if (arena == NULL) { + Py_DECREF(pathbytes); + return NULL; + } + flags.cf_flags = 0; - mod = PyParser_ASTFromFile(fp, pathname, NULL, + mod = PyParser_ASTFromFile(fp, PyBytes_AS_STRING(pathbytes), NULL, Py_file_input, 0, 0, &flags, NULL, arena); - if (mod) { - co = PyAST_Compile(mod, pathname, NULL, arena); - } + if (mod != NULL) + co = PyAST_Compile(mod, PyBytes_AS_STRING(pathbytes), NULL, arena); + else + co = NULL; + Py_DECREF(pathbytes); PyArena_Free(arena); return co; } - /* Helper to open a bytecode file for writing in exclusive mode */ static FILE * @@ -1207,10 +1236,10 @@ remove the file. */ static void -write_compiled_module(PyCodeObject *co, char *cpathname, struct stat *srcstat) +write_compiled_module(PyCodeObject *co, PyObject *cpathname, + struct stat *srcstat) { FILE *fp; - char *dirpath; time_t mtime = srcstat->st_mtime; #ifdef MS_WINDOWS /* since Windows uses different permissions */ mode_t mode = srcstat->st_mode & ~S_IEXEC; @@ -1219,39 +1248,64 @@ mode_t dirmode = (srcstat->st_mode | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR | S_IWGRP | S_IWOTH); + PyObject *dirbytes; #endif - int saved; + PyObject *cpathbytes, *dirname; + Py_UNICODE *dirsep; + int res, ok; /* Ensure that the __pycache__ directory exists. */ - dirpath = rightmost_sep(cpathname); - if (dirpath == NULL) { + dirsep = rightmost_sep(PyUnicode_AS_UNICODE(cpathname)); + if (dirsep == NULL) { if (Py_VerboseFlag) - PySys_WriteStderr( - "# no %s path found %s\n", - CACHEDIR, cpathname); + PySys_FormatStderr("# no %s path found %R\n", CACHEDIR, cpathname); return; } - saved = *dirpath; - *dirpath = '\0'; - -#ifdef MS_WINDOWS - if (_mkdir(cpathname) < 0 && errno != EEXIST) { -#else - if (mkdir(cpathname, dirmode) < 0 && errno != EEXIST) { -#endif - *dirpath = saved; - if (Py_VerboseFlag) - PySys_WriteStderr( - "# cannot create cache dir %s\n", cpathname); + dirname = PyUnicode_FromUnicode(PyUnicode_AS_UNICODE(cpathname), + dirsep - PyUnicode_AS_UNICODE(cpathname)); + if (dirname == NULL) { + PyErr_Clear(); return; } - *dirpath = saved; - - fp = open_exclusive(cpathname, mode); + +#ifdef MS_WINDOWS + res = CreateDirectoryW(PyUnicode_AS_UNICODE(dirname), NULL); + ok = (res != 0); + if (!ok && GetLastError() == ERROR_ALREADY_EXISTS) + ok = 1; +#else + dirbytes = PyUnicode_EncodeFSDefault(dirname); + if (dirbytes == NULL) { + PyErr_Clear(); + return; + } + res = mkdir(PyBytes_AS_STRING(dirbytes), dirmode); + Py_DECREF(dirbytes); + if (0 <= res) + ok = 1; + else + ok = (errno == EEXIST); +#endif + if (!ok) { + if (Py_VerboseFlag) + PySys_FormatStderr("# cannot create cache dir %R\n", dirname); + Py_DECREF(dirname); + return; + } + Py_DECREF(dirname); + + cpathbytes = PyUnicode_EncodeFSDefault(cpathname); + if (cpathbytes == NULL) { + PyErr_Clear(); + return; + } + + fp = open_exclusive(PyBytes_AS_STRING(cpathbytes), mode); if (fp == NULL) { if (Py_VerboseFlag) - PySys_WriteStderr( - "# can't create %s\n", cpathname); + PySys_FormatStderr( + "# can't create %R\n", cpathname); + Py_DECREF(cpathbytes); return; } PyMarshal_WriteLongToFile(pyc_magic, fp, Py_MARSHAL_VERSION); @@ -1260,12 +1314,18 @@ PyMarshal_WriteObjectToFile((PyObject *)co, fp, Py_MARSHAL_VERSION); if (fflush(fp) != 0 || ferror(fp)) { if (Py_VerboseFlag) - PySys_WriteStderr("# can't write %s\n", cpathname); + PySys_FormatStderr("# can't write %R\n", cpathname); /* Don't keep partial file */ fclose(fp); - (void) unlink(cpathname); +#ifdef MS_WINDOWS + (void)DeleteFileW(PyUnicode_AS_UNICODE(cpathname)); +#else + (void) unlink(PyBytes_AS_STRING(cpathbytes)); +#endif + Py_DECREF(cpathbytes); return; } + Py_DECREF(cpathbytes); /* Now write the true mtime */ fseek(fp, 4L, 0); assert(mtime < LONG_MAX); @@ -1273,7 +1333,7 @@ fflush(fp); fclose(fp); if (Py_VerboseFlag) - PySys_WriteStderr("# wrote %s\n", cpathname); + PySys_FormatStderr("# wrote %R\n", cpathname); } static void @@ -1300,26 +1360,18 @@ } } -static int -update_compiled_module(PyCodeObject *co, char *pathname) +static void +update_compiled_module(PyCodeObject *co, PyObject *newname) { - PyObject *oldname, *newname; - - newname = PyUnicode_DecodeFSDefault(pathname); - if (newname == NULL) - return -1; - - if (!PyUnicode_Compare(co->co_filename, newname)) { - Py_DECREF(newname); - return 0; - } + PyObject *oldname; + + if (PyUnicode_Compare(co->co_filename, newname) == 0) + return; oldname = co->co_filename; Py_INCREF(oldname); update_code_filenames(co, oldname, newname); Py_DECREF(oldname); - Py_DECREF(newname); - return 1; } /* Load a source module from a given file and return its module @@ -1327,20 +1379,19 @@ byte-compiled file, use that instead. */ static PyObject * -load_source_module(char *name, char *pathname, FILE *fp) +load_source_module(PyObject *name, PyObject *pathname, FILE *fp) { struct stat st; FILE *fpc; - char buf[MAXPATHLEN+1]; - char *cpathname; + PyObject *cpathname = NULL, *cpathbytes = NULL; PyCodeObject *co; - PyObject *m; + PyObject *m = NULL; if (fstat(fileno(fp), &st) != 0) { PyErr_Format(PyExc_RuntimeError, - "unable to get file status from '%s'", + "unable to get file status from %R", pathname); - return NULL; + goto error; } #if SIZEOF_TIME_T > 4 /* Python's .pyc timestamp handling presumes that the timestamp fits @@ -1350,41 +1401,50 @@ if (st.st_mtime >> 32) { PyErr_SetString(PyExc_OverflowError, "modification time overflows a 4 byte field"); - return NULL; + goto error; } #endif cpathname = make_compiled_pathname( - pathname, buf, (size_t)MAXPATHLEN + 1, !Py_OptimizeFlag); - if (cpathname != NULL && - (fpc = check_compiled_module(pathname, st.st_mtime, cpathname))) { + PyUnicode_AS_UNICODE(pathname), + !Py_OptimizeFlag); + + if (cpathname != NULL) + fpc = check_compiled_module(pathname, st.st_mtime, cpathname); + else + fpc = NULL; + + if (fpc) { co = read_compiled_module(cpathname, fpc); fclose(fpc); if (co == NULL) - return NULL; - if (update_compiled_module(co, pathname) < 0) - return NULL; + goto error; + update_compiled_module(co, pathname); if (Py_VerboseFlag) - PySys_WriteStderr("import %s # precompiled from %s\n", - name, cpathname); - pathname = cpathname; + PySys_FormatStderr("import %U # precompiled from %R\n", + name, cpathname); + m = PyImport_ExecCodeModuleObject(name, (PyObject *)co, + cpathname, cpathname); } else { co = parse_source_module(pathname, fp); if (co == NULL) - return NULL; + goto error; if (Py_VerboseFlag) - PySys_WriteStderr("import %s # from %s\n", - name, pathname); - if (cpathname) { + PySys_FormatStderr("import %U # from %R\n", + name, pathname); + if (cpathname != NULL) { PyObject *ro = PySys_GetObject("dont_write_bytecode"); if (ro == NULL || !PyObject_IsTrue(ro)) write_compiled_module(co, cpathname, &st); } + m = PyImport_ExecCodeModuleObject(name, (PyObject *)co, + pathname, cpathname); } - m = PyImport_ExecCodeModuleWithPathnames( - name, (PyObject *)co, pathname, cpathname); Py_DECREF(co); +error: + Py_XDECREF(cpathbytes); + Py_XDECREF(cpathname); return m; } @@ -1392,101 +1452,106 @@ * Returns the path to the py file if available, else the given path */ static PyObject * -get_sourcefile(char *file) +get_sourcefile(PyObject *filename) { - char py[MAXPATHLEN + 1]; Py_ssize_t len; - PyObject *u; + Py_UNICODE *fileuni; + PyObject *py; struct stat statbuf; - if (!file || !*file) { + len = PyUnicode_GET_SIZE(filename); + if (len == 0) Py_RETURN_NONE; - } - - len = strlen(file); - /* match '*.py?' */ - if (len > MAXPATHLEN || PyOS_strnicmp(&file[len-4], ".py", 3) != 0) { - return PyUnicode_DecodeFSDefault(file); - } + + /* don't match *.pyc or *.pyo? */ + fileuni = PyUnicode_AS_UNICODE(filename); + if (len < 5 + || fileuni[len-4] != '.' + || (fileuni[len-3] != 'p' && fileuni[len-3] != 'P') + || (fileuni[len-2] != 'y' && fileuni[len-2] != 'Y')) + goto unchanged; /* Start by trying to turn PEP 3147 path into source path. If that * fails, just chop off the trailing character, i.e. legacy pyc path * to py. */ - if (make_source_pathname(file, py) == NULL) { - strncpy(py, file, len-1); - py[len-1] = '\0'; + py = make_source_pathname(filename); + if (py == NULL) { + PyErr_Clear(); + py = PyUnicode_FromUnicode(fileuni, len - 1); } - - if (stat(py, &statbuf) == 0 && - S_ISREG(statbuf.st_mode)) { - u = PyUnicode_DecodeFSDefault(py); - } - else { - u = PyUnicode_DecodeFSDefault(file); - } - return u; + if (py == NULL) + goto error; + + if (_Py_stat(py, &statbuf) == 0 && S_ISREG(statbuf.st_mode)) + return py; + Py_DECREF(py); + goto unchanged; + +error: + PyErr_Clear(); +unchanged: + Py_INCREF(filename); + return filename; } /* Forward */ -static PyObject *load_module(char *, FILE *, char *, int, PyObject *); -static struct filedescr *find_module(char *, char *, PyObject *, - char *, size_t, FILE **, PyObject **); -static struct _frozen * find_frozen(char *); +static PyObject *load_module(PyObject *, FILE *, PyObject *, int, PyObject *); +static struct filedescr *find_module(PyObject *, PyObject *, PyObject *, + PyObject **, FILE **, PyObject **); +static struct _frozen * find_frozen(PyObject *); /* Load a package and return its module object WITH INCREMENTED REFERENCE COUNT */ static PyObject * -load_package(char *name, char *pathname) +load_package(PyObject *name, PyObject *pathname) { - PyObject *m, *d; - PyObject *file = NULL; - PyObject *path = NULL; + PyObject *m, *d, *bufobj; + PyObject *file = NULL, *path_list = NULL; int err; - char buf[MAXPATHLEN+1]; FILE *fp = NULL; struct filedescr *fdp; - m = PyImport_AddModule(name); + m = PyImport_AddModuleObject(name); if (m == NULL) return NULL; if (Py_VerboseFlag) - PySys_WriteStderr("import %s # directory %s\n", - name, pathname); - d = PyModule_GetDict(m); + PySys_FormatStderr("import %U # directory %R\n", + name, pathname); file = get_sourcefile(pathname); if (file == NULL) - goto error; - path = Py_BuildValue("[O]", file); - if (path == NULL) - goto error; + return NULL; + path_list = Py_BuildValue("[O]", file); + if (path_list == NULL) { + Py_DECREF(file); + return NULL; + } + d = PyModule_GetDict(m); err = PyDict_SetItemString(d, "__file__", file); + Py_DECREF(file); if (err == 0) - err = PyDict_SetItemString(d, "__path__", path); - if (err != 0) - goto error; - buf[0] = '\0'; - fdp = find_module(name, "__init__", path, buf, sizeof(buf), &fp, NULL); + err = PyDict_SetItemString(d, "__path__", path_list); + if (err != 0) { + Py_DECREF(path_list); + return NULL; + } + fdp = find_module(name, initstr, path_list, + &bufobj, &fp, NULL); + Py_DECREF(path_list); if (fdp == NULL) { if (PyErr_ExceptionMatches(PyExc_ImportError)) { PyErr_Clear(); Py_INCREF(m); + return m; } else - m = NULL; - goto cleanup; + return NULL; } - m = load_module(name, fp, buf, fdp->type, NULL); + m = load_module(name, fp, bufobj, fdp->type, NULL); + Py_XDECREF(bufobj); if (fp != NULL) fclose(fp); - goto cleanup; - - error: - m = NULL; - cleanup: - Py_XDECREF(path); - Py_XDECREF(file); return m; } @@ -1494,11 +1559,12 @@ /* Helper to test for built-in module */ static int -is_builtin(char *name) +is_builtin(PyObject *name) { - int i; + int i, cmp; for (i = 0; PyImport_Inittab[i].name != NULL; i++) { - if (strcmp(name, PyImport_Inittab[i].name) == 0) { + cmp = PyUnicode_CompareWithASCIIString(name, PyImport_Inittab[i].name); + if (cmp == 0) { if (PyImport_Inittab[i].initfunc == NULL) return -1; else @@ -1592,43 +1658,274 @@ pathname and an open file. Return NULL if the module is not found. */ #ifdef MS_COREDLL -extern FILE *_PyWin_FindRegisteredModule(const char *, struct filedescr **, - char *, Py_ssize_t); +extern FILE *_PyWin_FindRegisteredModule(PyObject *, struct filedescr **, + PyObject **p_path); #endif -static int case_ok(char *, Py_ssize_t, Py_ssize_t, char *); -static int find_init_module(char *); /* Forward */ +/* Forward */ +static int case_ok(PyObject *, Py_ssize_t, PyObject *); +static int find_init_module(PyObject *); static struct filedescr importhookdescr = {"", "", IMP_HOOK}; -static struct filedescr * -find_module(char *fullname, char *subname, PyObject *path, char *buf, - size_t buflen, FILE **p_fp, PyObject **p_loader) +/* Get the path of a module: get its importer and call importer.find_module() + hook, or check if the module if a package (if path/__init__.py exists). + + -1: error: a Python error occurred + 0: ignore: an error occurred because of invalid data, but the error is not + important enough to be reported. + 1: get path: module not found, but *buf contains its path + 2: found: *p_fd is the file descriptor (IMP_HOOK or PKG_DIRECTORY) + and *buf is the path */ + +static int +find_module_path(PyObject *fullname, PyObject *name, PyObject *path, + PyObject *path_hooks, PyObject *path_importer_cache, + PyObject **p_path, PyObject **p_loader, struct filedescr **p_fd) +{ + Py_UNICODE buf[MAXPATHLEN+1]; + Py_ssize_t buflen = MAXPATHLEN+1; + PyObject *path_unicode, *filename; + const Py_UNICODE *base; + Py_ssize_t len; + struct stat statbuf; + static struct filedescr fd_package = {"", "", PKG_DIRECTORY}; + + if (PyUnicode_Check(path)) { + Py_INCREF(path); + path_unicode = path; + } + else if (PyBytes_Check(path)) { + path_unicode = PyUnicode_DecodeFSDefaultAndSize( + PyBytes_AS_STRING(path), PyBytes_GET_SIZE(path)); + if (path_unicode == NULL) + return -1; + } + else + return 0; + + base = PyUnicode_AS_UNICODE(path_unicode); + len = PyUnicode_GET_SIZE(path_unicode); + if (len + 2 + PyUnicode_GET_SIZE(name) + MAXSUFFIXSIZE >= buflen) { + Py_DECREF(path_unicode); + return 0; /* Too long */ + } + Py_UNICODE_strcpy(buf, PyUnicode_AS_UNICODE(path_unicode)); + Py_DECREF(path_unicode); + + if (Py_UNICODE_strlen(buf) != len) + return 0; /* path contains '\0' */ + + /* sys.path_hooks import hook */ + if (p_loader != NULL) { + PyObject *importer; + + importer = get_path_importer(path_importer_cache, + path_hooks, path); + if (importer == NULL) { + return -1; + } + /* Note: importer is a borrowed reference */ + if (importer != Py_None) { + PyObject *loader; + loader = PyObject_CallMethod(importer, + "find_module", "O", fullname); + if (loader == NULL) + return -1; /* error */ + if (loader != Py_None) { + /* a loader was found */ + *p_loader = loader; + *p_fd = &importhookdescr; + return 2; + } + Py_DECREF(loader); + return 0; + } + } + /* no hook was found, use builtin import */ + + if (len > 0 && buf[len-1] != SEP +#ifdef ALTSEP + && buf[len-1] != ALTSEP +#endif + ) + buf[len++] = SEP; + Py_UNICODE_strcpy(buf+len, PyUnicode_AS_UNICODE(name)); + len += PyUnicode_GET_SIZE(name); + + filename = PyUnicode_FromUnicode(buf, len); + if (filename == NULL) + return -1; + + /* Check for package import (buf holds a directory name, + and there's an __init__ module in that directory */ +#ifdef HAVE_STAT + if (_Py_stat(filename, &statbuf) == 0 && /* it exists */ + S_ISDIR(statbuf.st_mode)) /* it's a directory */ + { + int match; + + match = case_ok(filename, 0, name); + if (match < 0) { + Py_DECREF(filename); + return -1; + } + if (match) { /* case matches */ + if (find_init_module(filename)) { /* and has __init__.py */ + *p_path = filename; + *p_fd = &fd_package; + return 2; + } + else { + int err; + err = PyErr_WarnFormat(PyExc_ImportWarning, 1, + "Not importing directory %R: missing __init__.py", + filename); + if (err) { + Py_DECREF(filename); + return -1; + } + } + } + } +#endif + *p_path = filename; + return 1; +} + +/* Find a module in search_path_list. For each path, try + find_module_path() or try each _PyImport_Filetab suffix. + + If the module is found, return a file descriptor, write the path in + *p_filename, write the pointer to the file object into *p_fp, and (if + p_loader is not NULL) the loader into *p_loader. + + Otherwise, raise an exception and return NULL. */ + +static struct filedescr* +find_module_path_list(PyObject *fullname, PyObject *name, + PyObject *search_path_list, PyObject *path_hooks, + PyObject *path_importer_cache, + PyObject **p_path, FILE **p_fp, PyObject **p_loader) { Py_ssize_t i, npath; - size_t len, namelen; struct filedescr *fdp = NULL; char *filemode; FILE *fp = NULL; - PyObject *path_hooks, *path_importer_cache; - struct stat statbuf; + PyObject *prefix, *filename; + int match; + + npath = PyList_Size(search_path_list); + for (i = 0; i < npath; i++) { + PyObject *path; + int ok; + + path = PyList_GetItem(search_path_list, i); + if (path == NULL) + return NULL; + + prefix = NULL; + ok = find_module_path(fullname, name, path, + path_hooks, path_importer_cache, + &prefix, p_loader, &fdp); + if (ok < 0) + return NULL; + if (ok == 0) + continue; + if (ok == 2) { + *p_path = prefix; + return fdp; + } + + for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { + filemode = fdp->mode; + if (filemode[0] == 'U') + filemode = "r" PY_STDIOTEXTMODE; + + filename = PyUnicode_FromFormat("%U%s", prefix, fdp->suffix); + if (filename == NULL) { + Py_DECREF(prefix); + return NULL; + } + + if (Py_VerboseFlag > 1) + PySys_FormatStderr("# trying %R\n", filename); + + fp = _Py_fopen(filename, filemode); + if (fp == NULL) { + Py_DECREF(filename); + continue; + } + match = case_ok(filename, -(Py_ssize_t)strlen(fdp->suffix), name); + if (match < 0) { + Py_DECREF(prefix); + Py_DECREF(filename); + return NULL; + } + if (match) { + Py_DECREF(prefix); + *p_path = filename; + *p_fp = fp; + return fdp; + } + Py_DECREF(filename); + + fclose(fp); + fp = NULL; + } + Py_DECREF(prefix); + } + PyErr_Format(PyExc_ImportError, + "No module named %R", name); + return NULL; +} + +/* Find a module: + + - try find_module() of each sys.meta_path hook + - try find_frozen() + - try is_builtin() + - try _PyWin_FindRegisteredModule() (Windows only) + - otherwise, call find_module_path_list() with search_path_list (if not + NULL) or sys.path + + fullname can be NULL, but only if p_loader is NULL. + + Return: + + - &fd_builtin (C_BUILTIN) if it is a builtin + - &fd_frozen (PY_FROZEN) if it is frozen + - &fd_package (PKG_DIRECTORY) and write the filename into *p_path + if it is a package + - &importhookdescr (IMP_HOOK) and write the loader into *p_loader if a + importer loader was found + - a file descriptor (PY_SOURCE, PY_COMPILED, C_EXTENSION, PY_RESOURCE or + PY_CODERESOURCE: see _PyImport_Filetab), write the filename into + *p_path and the pointer to the open file into *p_fp + - NULL on error + + By default, *p_path, *p_fp and *p_loader (if set) are set to NULL. + Eg. *p_path is set to NULL for a builtin package. +*/ + +static struct filedescr * +find_module(PyObject *fullname, PyObject *name, PyObject *search_path_list, + PyObject **p_path, FILE **p_fp, PyObject **p_loader) +{ + Py_ssize_t i, npath; static struct filedescr fd_frozen = {"", "", PY_FROZEN}; static struct filedescr fd_builtin = {"", "", C_BUILTIN}; - static struct filedescr fd_package = {"", "", PKG_DIRECTORY}; - char name[MAXPATHLEN+1]; -#if defined(PYOS_OS2) - size_t saved_len; - size_t saved_namelen; - char *saved_buf = NULL; -#endif + PyObject *path_hooks, *path_importer_cache; + + *p_path = NULL; + *p_fp = NULL; if (p_loader != NULL) *p_loader = NULL; - if (strlen(subname) > MAXPATHLEN) { + if (PyUnicode_GET_SIZE(name) > MAXPATHLEN) { PyErr_SetString(PyExc_OverflowError, "module name is too long"); return NULL; } - strcpy(name, subname); /* sys.meta_path import hook */ if (p_loader != NULL) { @@ -1647,9 +1944,9 @@ PyObject *loader; PyObject *hook = PyList_GetItem(meta_path, i); loader = PyObject_CallMethod(hook, "find_module", - "sO", fullname, - path != NULL ? - path : Py_None); + "OO", fullname, + search_path_list != NULL ? + search_path_list : Py_None); if (loader == NULL) { Py_DECREF(meta_path); return NULL; /* true error */ @@ -1665,27 +1962,29 @@ Py_DECREF(meta_path); } - if (find_frozen(fullname) != NULL) { - strcpy(buf, fullname); + if (find_frozen(fullname) != NULL) return &fd_frozen; - } - - if (path == NULL) { - if (is_builtin(name)) { - strcpy(buf, name); + + if (search_path_list == NULL) { +#ifdef MS_COREDLL + FILE *fp; + struct filedescr *fdp; +#endif + if (is_builtin(name)) return &fd_builtin; - } #ifdef MS_COREDLL - fp = _PyWin_FindRegisteredModule(name, &fdp, buf, buflen); + fp = _PyWin_FindRegisteredModule(name, &fdp, p_path); if (fp != NULL) { *p_fp = fp; return fdp; } + else if (PyErr_Occurred()) + return NULL; #endif - path = PySys_GetObject("path"); + search_path_list = PySys_GetObject("path"); } - if (path == NULL || !PyList_Check(path)) { + if (search_path_list == NULL || !PyList_Check(search_path_list)) { PyErr_SetString(PyExc_ImportError, "sys.path must be a list of directory names"); return NULL; @@ -1706,176 +2005,13 @@ return NULL; } - npath = PyList_Size(path); - namelen = strlen(name); - for (i = 0; i < npath; i++) { - PyObject *v = PyList_GetItem(path, i); - PyObject *origv = v; - const char *base; - Py_ssize_t size; - if (!v) - return NULL; - if (PyUnicode_Check(v)) { - v = PyUnicode_EncodeFSDefault(v); - if (v == NULL) - return NULL; - } - else if (!PyBytes_Check(v)) - continue; - else - Py_INCREF(v); - - base = PyBytes_AS_STRING(v); - size = PyBytes_GET_SIZE(v); - len = size; - if (len + 2 + namelen + MAXSUFFIXSIZE >= buflen) { - Py_DECREF(v); - continue; /* Too long */ - } - strcpy(buf, base); - Py_DECREF(v); - - if (strlen(buf) != len) { - continue; /* v contains '\0' */ - } - - /* sys.path_hooks import hook */ - if (p_loader != NULL) { - PyObject *importer; - - importer = get_path_importer(path_importer_cache, - path_hooks, origv); - if (importer == NULL) { - return NULL; - } - /* Note: importer is a borrowed reference */ - if (importer != Py_None) { - PyObject *loader; - loader = PyObject_CallMethod(importer, - "find_module", - "s", fullname); - if (loader == NULL) - return NULL; /* error */ - if (loader != Py_None) { - /* a loader was found */ - *p_loader = loader; - return &importhookdescr; - } - Py_DECREF(loader); - continue; - } - } - /* no hook was found, use builtin import */ - - if (len > 0 && buf[len-1] != SEP -#ifdef ALTSEP - && buf[len-1] != ALTSEP -#endif - ) - buf[len++] = SEP; - strcpy(buf+len, name); - len += namelen; - - /* Check for package import (buf holds a directory name, - and there's an __init__ module in that directory */ -#ifdef HAVE_STAT - if (stat(buf, &statbuf) == 0 && /* it exists */ - S_ISDIR(statbuf.st_mode) && /* it's a directory */ - case_ok(buf, len, namelen, name)) { /* case matches */ - if (find_init_module(buf)) { /* and has __init__.py */ - return &fd_package; - } - else { - int err; - PyObject *unicode = PyUnicode_DecodeFSDefault(buf); - if (unicode == NULL) - return NULL; - err = PyErr_WarnFormat(PyExc_ImportWarning, 1, - "Not importing directory '%U': missing __init__.py", - unicode); - Py_DECREF(unicode); - if (err) - return NULL; - } - } -#endif -#if defined(PYOS_OS2) - /* take a snapshot of the module spec for restoration - * after the 8 character DLL hackery - */ - saved_buf = strdup(buf); - saved_len = len; - saved_namelen = namelen; -#endif /* PYOS_OS2 */ - for (fdp = _PyImport_Filetab; fdp->suffix != NULL; fdp++) { -#if defined(PYOS_OS2) && defined(HAVE_DYNAMIC_LOADING) - /* OS/2 limits DLLs to 8 character names (w/o - extension) - * so if the name is longer than that and its a - * dynamically loaded module we're going to try, - * truncate the name before trying - */ - if (strlen(subname) > 8) { - /* is this an attempt to load a C extension? */ - const struct filedescr *scan; - scan = _PyImport_DynLoadFiletab; - while (scan->suffix != NULL) { - if (!strcmp(scan->suffix, fdp->suffix)) - break; - else - scan++; - } - if (scan->suffix != NULL) { - /* yes, so truncate the name */ - namelen = 8; - len -= strlen(subname) - namelen; - buf[len] = '\0'; - } - } -#endif /* PYOS_OS2 */ - strcpy(buf+len, fdp->suffix); - if (Py_VerboseFlag > 1) - PySys_WriteStderr("# trying %s\n", buf); - filemode = fdp->mode; - if (filemode[0] == 'U') - filemode = "r" PY_STDIOTEXTMODE; - fp = fopen(buf, filemode); - if (fp != NULL) { - if (case_ok(buf, len, namelen, name)) - break; - else { /* continue search */ - fclose(fp); - fp = NULL; - } - } -#if defined(PYOS_OS2) - /* restore the saved snapshot */ - strcpy(buf, saved_buf); - len = saved_len; - namelen = saved_namelen; -#endif - } -#if defined(PYOS_OS2) - /* don't need/want the module name snapshot anymore */ - if (saved_buf) - { - free(saved_buf); - saved_buf = NULL; - } -#endif - if (fp != NULL) - break; - } - if (fp == NULL) { - PyErr_Format(PyExc_ImportError, - "No module named %.200s", name); - return NULL; - } - *p_fp = fp; - return fdp; + return find_module_path_list(fullname, name, + search_path_list, path_hooks, + path_importer_cache, + p_path, p_fp, p_loader); } -/* case_ok(char* buf, Py_ssize_t len, Py_ssize_t namelen, char* name) +/* case_bytes(char* buf, Py_ssize_t len, Py_ssize_t namelen, char* name) * The arguments here are tricky, best shown by example: * /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0 * ^ ^ ^ ^ @@ -1889,18 +2025,18 @@ * We've already done a successful stat() or fopen() on buf, so know that * there's some match, possibly case-insensitive. * - * case_ok() is to return 1 if there's a case-sensitive match for - * name, else 0. case_ok() is also to return 1 if envar PYTHONCASEOK + * case_bytes() is to return 1 if there's a case-sensitive match for + * name, else 0. case_bytes() is also to return 1 if envar PYTHONCASEOK * exists. * - * case_ok() is used to implement case-sensitive import semantics even + * case_bytes() is used to implement case-sensitive import semantics even * on platforms with case-insensitive filesystems. It's trivial to implement * for case-sensitive filesystems. It's pretty much a cross-platform * nightmare for systems with case-insensitive filesystems. */ /* First we may need a pile of platform-specific header files; the sequence - * of #if's here should match the sequence in the body of case_ok(). + * of #if's here should match the sequence in the body of case_bytes(). */ #if defined(MS_WINDOWS) #include @@ -1919,33 +2055,24 @@ #include #endif +#if defined(DJGPP) \ + || ((defined(__MACH__) && defined(__APPLE__) || defined(__CYGWIN__)) \ + && defined(HAVE_DIRENT_H)) \ + || defined(PYOS_OS2) +# define USE_CASE_OK_BYTES +#endif + + +#ifdef USE_CASE_OK_BYTES static int -case_ok(char *buf, Py_ssize_t len, Py_ssize_t namelen, char *name) +case_bytes(char *buf, Py_ssize_t len, Py_ssize_t namelen, const char *name) { /* Pick a platform-specific implementation; the sequence of #if's here should * match the sequence just above. */ -/* MS_WINDOWS */ -#if defined(MS_WINDOWS) - WIN32_FIND_DATA data; - HANDLE h; - - if (Py_GETENV("PYTHONCASEOK") != NULL) - return 1; - - h = FindFirstFile(buf, &data); - if (h == INVALID_HANDLE_VALUE) { - PyErr_Format(PyExc_NameError, - "Can't find file for module %.100s\n(filename %.300s)", - name, buf); - return 0; - } - FindClose(h); - return strncmp(data.cFileName, name, namelen) == 0; - /* DJGPP */ -#elif defined(DJGPP) +#if defined(DJGPP) struct ffblk ffblk; int done; @@ -1957,7 +2084,7 @@ PyErr_Format(PyExc_NameError, "Can't find file for module %.100s\n(filename %.300s)", name, buf); - return 0; + return -1; } return strncmp(ffblk.ff_name, name, namelen) == 0; @@ -2024,96 +2151,157 @@ /* assuming it's a case-sensitive filesystem, so there's nothing to do! */ #else - return 1; - +# error "USE_CASE_OK_BYTES is not correctly defined" #endif } +#endif + +/* + * Check if a filename case matchs the name case. We've already done a + * successful stat() or fopen() on buf, so know that there's some match, + * possibly case-insensitive. + * + * case_ok() is to return 1 if there's a case-sensitive match for name, 0 if it + * the filename doesn't match, or -1 on error. case_ok() is also to return 1 + * if envar PYTHONCASEOK exists. + * + * case_ok() is used to implement case-sensitive import semantics even + * on platforms with case-insensitive filesystems. It's trivial to implement + * for case-sensitive filesystems. It's pretty much a cross-platform + * nightmare for systems with case-insensitive filesystems. + */ + +static int +case_ok(PyObject *filename, Py_ssize_t prefix_delta, PyObject *name) +{ +#ifdef MS_WINDOWS + WIN32_FIND_DATAW data; + HANDLE h; + int cmp; + + if (Py_GETENV("PYTHONCASEOK") != NULL) + return 1; + + h = FindFirstFileW(PyUnicode_AS_UNICODE(filename), &data); + if (h == INVALID_HANDLE_VALUE) { + PyErr_Format(PyExc_NameError, + "Can't find file for module %R\n(filename %R)", + name, filename); + return 0; + } + FindClose(h); + cmp = wcsncmp(data.cFileName, + PyUnicode_AS_UNICODE(name), + PyUnicode_GET_SIZE(name)); + return cmp == 0; +#elif defined(USE_CASE_OK_BYTES) + int match; + PyObject *filebytes, *namebytes; + filebytes = PyUnicode_EncodeFSDefault(filename); + if (filebytes == NULL) + return -1; + namebytes = PyUnicode_EncodeFSDefault(name); + if (namebytes == NULL) { + Py_DECREF(filebytes); + return -1; + } + match = case_bytes( + PyBytes_AS_STRING(filebytes), + PyBytes_GET_SIZE(filebytes) + prefix_delta, + PyBytes_GET_SIZE(namebytes), + PyBytes_AS_STRING(namebytes)); + Py_DECREF(filebytes); + Py_DECREF(namebytes); + return match; +#else + /* assuming it's a case-sensitive filesystem, so there's nothing to do! */ + return 1; + +#endif +} #ifdef HAVE_STAT -/* Helper to look for __init__.py or __init__.py[co] in potential package */ +/* Helper to look for __init__.py or __init__.py[co] in potential package. + Return 1 if __init__ was found, 0 if not, or -1 on error. */ static int -find_init_module(char *buf) +find_init_module(PyObject *directory) { - const size_t save_len = strlen(buf); - size_t i = save_len; - char *pname; /* pointer to start of __init__ */ + size_t len; struct stat statbuf; - -/* For calling case_ok(buf, len, namelen, name): - * /a/b/c/d/e/f/g/h/i/j/k/some_long_module_name.py\0 - * ^ ^ ^ ^ - * |--------------------- buf ---------------------| - * |------------------- len ------------------| - * |------ name -------| - * |----- namelen -----| - */ - if (save_len + 13 >= MAXPATHLEN) - return 0; - buf[i++] = SEP; - pname = buf + i; - strcpy(pname, "__init__.py"); - if (stat(buf, &statbuf) == 0) { - if (case_ok(buf, - save_len + 9, /* len("/__init__") */ - 8, /* len("__init__") */ - pname)) { - buf[save_len] = '\0'; + PyObject *filename; + int match; + + len = PyUnicode_GET_SIZE(directory); + filename = PyUnicode_FromFormat("%U%c__init__.py", directory, SEP); + if (filename == NULL) + return -1; + if (_Py_stat(filename, &statbuf) == 0) { + /* 3=len(".py") */ + match = case_ok(filename, -3, initstr); + if (match < 0) { + Py_DECREF(filename); + return -1; + } + if (match) { + Py_DECREF(filename); return 1; } } - i += strlen(pname); - strcpy(buf+i, Py_OptimizeFlag ? "o" : "c"); - if (stat(buf, &statbuf) == 0) { - if (case_ok(buf, - save_len + 9, /* len("/__init__") */ - 8, /* len("__init__") */ - pname)) { - buf[save_len] = '\0'; + Py_DECREF(filename); + + filename = PyUnicode_FromFormat("%U%c__init__.py%c", + directory, SEP, Py_OptimizeFlag ? 'o' : 'c'); + if (filename == NULL) + return -1; + if (_Py_stat(filename, &statbuf) == 0) { + /* 4=len(".pyc") */ + match = case_ok(filename, -4, initstr); + if (match < 0) { + Py_DECREF(filename); + return -1; + } + if (match) { + Py_DECREF(filename); return 1; } } - buf[save_len] = '\0'; + Py_DECREF(filename); return 0; } #endif /* HAVE_STAT */ -static int init_builtin(char *); /* Forward */ +static int init_builtin(PyObject *); /* Forward */ static PyObject* -load_builtin(char *name, char *pathname, int type) +load_builtin(PyObject *name, int type) { PyObject *m, *modules; int err; - if (pathname != NULL && pathname[0] != '\0') - name = pathname; - if (type == C_BUILTIN) err = init_builtin(name); else - err = PyImport_ImportFrozenModule(name); + err = PyImport_ImportFrozenModuleObject(name); if (err < 0) return NULL; if (err == 0) { PyErr_Format(PyExc_ImportError, - "Purported %s module %.200s not found", - type == C_BUILTIN ? - "builtin" : "frozen", + "Purported %s module %R not found", + type == C_BUILTIN ? "builtin" : "frozen", name); return NULL; } modules = PyImport_GetModuleDict(); - m = PyDict_GetItemString(modules, name); + m = PyDict_GetItem(modules, name); if (m == NULL) { PyErr_Format( PyExc_ImportError, - "%s module %.200s not properly initialized", - type == C_BUILTIN ? - "builtin" : "frozen", + "%s module %R not properly initialized", + type == C_BUILTIN ? "builtin" : "frozen", name); return NULL; } @@ -2125,7 +2313,7 @@ its module object WITH INCREMENTED REFERENCE COUNT */ static PyObject * -load_module(char *name, FILE *fp, char *pathname, int type, PyObject *loader) +load_module(PyObject *name, FILE *fp, PyObject *pathname, int type, PyObject *loader) { PyObject *m; @@ -2135,7 +2323,7 @@ case PY_COMPILED: if (fp == NULL) { PyErr_Format(PyExc_ValueError, - "file object required for import (type code %d)", + "file object required for import (type code %d)", type); return NULL; } @@ -2163,7 +2351,7 @@ case C_BUILTIN: case PY_FROZEN: - m = load_builtin(name, pathname, type); + m = load_builtin(name, type); break; case IMP_HOOK: { @@ -2172,13 +2360,13 @@ "import hook without loader"); return NULL; } - m = PyObject_CallMethod(loader, "load_module", "s", name); + m = PyObject_CallMethod(loader, "load_module", "O", name); break; } default: PyErr_Format(PyExc_ImportError, - "Don't know how to import %.200s (type code %d)", + "Don't know how to import %R (type code %d)", name, type); m = NULL; @@ -2193,28 +2381,28 @@ an exception set if the initialization failed. */ static int -init_builtin(char *name) +init_builtin(PyObject *name) { struct _inittab *p; - if (_PyImport_FindBuiltin(name) != NULL) + if (_PyImport_FindExtensionObject(name, name) != NULL) return 1; for (p = PyImport_Inittab; p->name != NULL; p++) { PyObject *mod; - if (strcmp(name, p->name) == 0) { + if (PyUnicode_CompareWithASCIIString(name, p->name) == 0) { if (p->initfunc == NULL) { PyErr_Format(PyExc_ImportError, - "Cannot re-init internal module %.200s", + "Cannot re-init internal module %R", name); return -1; } if (Py_VerboseFlag) - PySys_WriteStderr("import %s # builtin\n", name); + PySys_FormatStderr("import %U # builtin\n", name); mod = (*p->initfunc)(); if (mod == 0) return -1; - if (_PyImport_FixupBuiltin(mod, name) < 0) + if (_PyImport_FixupExtensionObject(mod, name, name) < 0) return -1; /* FixupExtension has put the module into sys.modules, so we can release our own reference. */ @@ -2229,37 +2417,37 @@ /* Frozen modules */ static struct _frozen * -find_frozen(char *name) +find_frozen(PyObject *name) { struct _frozen *p; - if (!name) + if (name == NULL) return NULL; for (p = PyImport_FrozenModules; ; p++) { if (p->name == NULL) return NULL; - if (strcmp(p->name, name) == 0) + if (PyUnicode_CompareWithASCIIString(name, p->name) == 0) break; } return p; } static PyObject * -get_frozen_object(char *name) +get_frozen_object(PyObject *name) { struct _frozen *p = find_frozen(name); int size; if (p == NULL) { PyErr_Format(PyExc_ImportError, - "No such frozen object named %.200s", + "No such frozen object named %R", name); return NULL; } if (p->code == NULL) { PyErr_Format(PyExc_ImportError, - "Excluded frozen object named %.200s", + "Excluded frozen object named %R", name); return NULL; } @@ -2270,14 +2458,14 @@ } static PyObject * -is_frozen_package(char *name) +is_frozen_package(PyObject *name) { struct _frozen *p = find_frozen(name); int size; if (p == NULL) { PyErr_Format(PyExc_ImportError, - "No such frozen object named %.200s", + "No such frozen object named %R", name); return NULL; } @@ -2297,19 +2485,20 @@ This function is also used from frozenmain.c */ int -PyImport_ImportFrozenModule(char *name) +PyImport_ImportFrozenModuleObject(PyObject *name) { - struct _frozen *p = find_frozen(name); - PyObject *co; - PyObject *m; + struct _frozen *p; + PyObject *co, *m, *path; int ispackage; int size; + p = find_frozen(name); + if (p == NULL) return 0; if (p->code == NULL) { PyErr_Format(PyExc_ImportError, - "Excluded frozen object named %.200s", + "Excluded frozen object named %R", name); return -1; } @@ -2318,40 +2507,41 @@ if (ispackage) size = -size; if (Py_VerboseFlag) - PySys_WriteStderr("import %s # frozen%s\n", + PySys_FormatStderr("import %U # frozen%s\n", name, ispackage ? " package" : ""); co = PyMarshal_ReadObjectFromString((char *)p->code, size); if (co == NULL) return -1; if (!PyCode_Check(co)) { PyErr_Format(PyExc_TypeError, - "frozen object %.200s is not a code object", + "frozen object %R is not a code object", name); goto err_return; } if (ispackage) { /* Set __path__ to the package name */ - PyObject *d, *s, *l; + PyObject *d, *l; int err; - m = PyImport_AddModule(name); + m = PyImport_AddModuleObject(name); if (m == NULL) goto err_return; d = PyModule_GetDict(m); - s = PyUnicode_InternFromString(name); - if (s == NULL) - goto err_return; l = PyList_New(1); if (l == NULL) { - Py_DECREF(s); goto err_return; } - PyList_SET_ITEM(l, 0, s); + Py_INCREF(name); + PyList_SET_ITEM(l, 0, name); err = PyDict_SetItemString(d, "__path__", l); Py_DECREF(l); if (err != 0) goto err_return; } - m = PyImport_ExecCodeModuleEx(name, co, ""); + path = PyUnicode_FromString(""); + if (path == NULL) + goto err_return; + m = PyImport_ExecCodeModuleObject(name, co, path, NULL); + Py_DECREF(path); if (m == NULL) goto err_return; Py_DECREF(co); @@ -2362,6 +2552,19 @@ return -1; } +int +PyImport_ImportFrozenModule(char *name) +{ + PyObject *nameobj; + int ret; + nameobj = PyUnicode_InternFromString(name); + if (nameobj == NULL) + return -1; + ret = PyImport_ImportFrozenModuleObject(nameobj); + Py_DECREF(nameobj); + return ret; +} + /* Import a module, either built-in, frozen, or external, and return its module object WITH INCREMENTED REFERENCE COUNT */ @@ -2392,8 +2595,7 @@ PyObject * PyImport_ImportModuleNoBlock(const char *name) { - PyObject *result; - PyObject *modules; + PyObject *nameobj, *modules, *result; long me; /* Try to get the module from sys.modules[name] */ @@ -2401,14 +2603,16 @@ if (modules == NULL) return NULL; - result = PyDict_GetItemString(modules, name); + nameobj = PyUnicode_FromString(name); + if (nameobj == NULL) + return NULL; + result = PyDict_GetItem(modules, nameobj); if (result != NULL) { + Py_DECREF(nameobj); Py_INCREF(result); return result; } - else { - PyErr_Clear(); - } + PyErr_Clear(); #ifdef WITH_THREAD /* check the import lock * me might be -1 but I ignore the error here, the lock function @@ -2416,43 +2620,54 @@ me = PyThread_get_thread_ident(); if (import_lock_thread == -1 || import_lock_thread == me) { /* no thread or me is holding the lock */ - return PyImport_ImportModule(name); + result = PyImport_Import(nameobj); } else { PyErr_Format(PyExc_ImportError, - "Failed to import %.200s because the import lock" + "Failed to import %R because the import lock" "is held by another thread.", - name); - return NULL; + nameobj); + result = NULL; } #else - return PyImport_ImportModule(name); + result = PyImport_Import(nameobj); #endif + Py_DECREF(nameobj); + return result; } /* Forward declarations for helper routines */ -static PyObject *get_parent(PyObject *globals, char *buf, - Py_ssize_t *p_buflen, int level); +static PyObject *get_parent(PyObject *globals, + PyObject **p_name, + int level); static PyObject *load_next(PyObject *mod, PyObject *altmod, - char **p_name, char *buf, Py_ssize_t *p_buflen); -static int mark_miss(char *name); + PyObject *inputname, PyObject **p_outputname, + Py_UNICODE *buf, Py_ssize_t *p_buflen, + Py_ssize_t bufsize); +static int mark_miss(PyObject *name); static int ensure_fromlist(PyObject *mod, PyObject *fromlist, - char *buf, Py_ssize_t buflen, int recursive); -static PyObject * import_submodule(PyObject *mod, char *name, char *fullname); + PyObject *buf, int recursive); +static PyObject * import_submodule(PyObject *mod, PyObject *name, + PyObject *fullname); /* The Magnum Opus of dotted-name import :-) */ static PyObject * -import_module_level(char *name, PyObject *globals, PyObject *locals, +import_module_level(PyObject *name, PyObject *globals, PyObject *locals, PyObject *fromlist, int level) { - char buf[MAXPATHLEN+1]; - Py_ssize_t buflen = 0; - PyObject *parent, *head, *next, *tail; - - if (strchr(name, '/') != NULL -#ifdef MS_WINDOWS - || strchr(name, '\\') != NULL + Py_UNICODE buf[MAXPATHLEN+1]; + Py_ssize_t buflen; + Py_ssize_t bufsize = MAXPATHLEN+1; + PyObject *parent, *head, *next, *tail, *inputname, *outputname; + PyObject *parent_name, *ensure_name; + const Py_UNICODE *nameunicode; + + nameunicode = PyUnicode_AS_UNICODE(name); + + if (Py_UNICODE_strchr(nameunicode, SEP) != NULL +#ifdef ALTSEP + || Py_UNICODE_strchr(nameunicode, ALTSEP) != NULL #endif ) { PyErr_SetString(PyExc_ImportError, @@ -2460,25 +2675,45 @@ return NULL; } - parent = get_parent(globals, buf, &buflen, level); + parent = get_parent(globals, &parent_name, level); if (parent == NULL) return NULL; - head = load_next(parent, level < 0 ? Py_None : parent, &name, buf, - &buflen); + buflen = PyUnicode_GET_SIZE(parent_name); + if (buflen+1 > bufsize) { + Py_DECREF(parent_name); + PyErr_SetString(PyExc_ValueError, + "Module name too long"); + return NULL; + } + Py_UNICODE_strcpy(buf, PyUnicode_AS_UNICODE(parent_name)); + Py_DECREF(parent_name); + + head = load_next(parent, level < 0 ? Py_None : parent, name, &outputname, + buf, &buflen, bufsize); if (head == NULL) return NULL; tail = head; Py_INCREF(tail); - while (name) { - next = load_next(tail, tail, &name, buf, &buflen); - Py_DECREF(tail); - if (next == NULL) { - Py_DECREF(head); - return NULL; + + if (outputname != NULL) { + while (1) { + inputname = outputname; + next = load_next(tail, tail, inputname, &outputname, + buf, &buflen, bufsize); + Py_DECREF(tail); + Py_DECREF(inputname); + if (next == NULL) { + Py_DECREF(head); + return NULL; + } + tail = next; + + if (outputname == NULL) { + break; + } } - tail = next; } if (tail == Py_None) { /* If tail is Py_None, both get_parent and load_next found @@ -2486,8 +2721,7 @@ doctored faulty bytecode */ Py_DECREF(tail); Py_DECREF(head); - PyErr_SetString(PyExc_ValueError, - "Empty module name"); + PyErr_SetString(PyExc_ValueError, "Empty module name"); return NULL; } @@ -2502,44 +2736,71 @@ } Py_DECREF(head); - if (!ensure_fromlist(tail, fromlist, buf, buflen, 0)) { + + ensure_name = PyUnicode_FromUnicode(buf, Py_UNICODE_strlen(buf)); + if (ensure_name == NULL) { Py_DECREF(tail); return NULL; } + if (!ensure_fromlist(tail, fromlist, ensure_name, 0)) { + Py_DECREF(tail); + Py_DECREF(ensure_name); + return NULL; + } + Py_DECREF(ensure_name); return tail; } PyObject * -PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, - PyObject *fromlist, int level) +PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals, + PyObject *locals, PyObject *fromlist, + int level) { - PyObject *result; + PyObject *mod; _PyImport_AcquireLock(); - result = import_module_level(name, globals, locals, fromlist, level); + mod = import_module_level(name, globals, locals, fromlist, level); if (_PyImport_ReleaseLock() < 0) { - Py_XDECREF(result); + Py_XDECREF(mod); PyErr_SetString(PyExc_RuntimeError, "not holding the import lock"); return NULL; } - return result; + return mod; } +PyObject * +PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals, + PyObject *fromlist, int level) +{ + PyObject *nameobj, *mod; + nameobj = PyUnicode_FromString(name); + if (nameobj == NULL) + return NULL; + mod = PyImport_ImportModuleLevelObject(nameobj, globals, locals, + fromlist, level); + Py_DECREF(nameobj); + return mod; +} + + /* Return the package that an import is being performed in. If globals comes from the module foo.bar.bat (not itself a package), this returns the sys.modules entry for foo.bar. If globals is from a package's __init__.py, the package's entry in sys.modules is returned, as a borrowed reference. - The *name* of the returned package is returned in buf, with the length of - the name in *p_buflen. + The name of the returned package is returned in *p_name. If globals doesn't come from a package or a module in a package, or a corresponding entry is not found in sys.modules, Py_None is returned. */ static PyObject * -get_parent(PyObject *globals, char *buf, Py_ssize_t *p_buflen, int level) +get_parent(PyObject *globals, PyObject **p_name, int level) { + Py_UNICODE name[MAXPATHLEN+1]; + const Py_ssize_t bufsize = MAXPATHLEN+1; + PyObject *nameobj; + static PyObject *namestr = NULL; static PyObject *pathstr = NULL; static PyObject *pkgstr = NULL; @@ -2547,7 +2808,7 @@ int orig_level = level; if (globals == NULL || !PyDict_Check(globals) || !level) - return Py_None; + goto return_none; if (namestr == NULL) { namestr = PyUnicode_InternFromString("__name__"); @@ -2565,55 +2826,46 @@ return NULL; } - *buf = '\0'; - *p_buflen = 0; pkgname = PyDict_GetItem(globals, pkgstr); if ((pkgname != NULL) && (pkgname != Py_None)) { /* __package__ is set, so use it */ - char *pkgname_str; - Py_ssize_t len; - if (!PyUnicode_Check(pkgname)) { PyErr_SetString(PyExc_ValueError, "__package__ set to non-string"); return NULL; } - pkgname_str = _PyUnicode_AsStringAndSize(pkgname, &len); - if (len == 0) { + if (PyUnicode_GET_SIZE(pkgname) == 0) { if (level > 0) { PyErr_SetString(PyExc_ValueError, "Attempted relative import in non-package"); return NULL; } - return Py_None; + goto return_none; } - if (len > MAXPATHLEN) { + if (PyUnicode_GET_SIZE(pkgname)+1 > bufsize) { PyErr_SetString(PyExc_ValueError, "Package name too long"); return NULL; } - strcpy(buf, pkgname_str); + Py_UNICODE_strcpy(name, PyUnicode_AS_UNICODE(pkgname)); } else { /* __package__ not set, so figure it out and set it */ modname = PyDict_GetItem(globals, namestr); if (modname == NULL || !PyUnicode_Check(modname)) - return Py_None; + goto return_none; modpath = PyDict_GetItem(globals, pathstr); if (modpath != NULL) { /* __path__ is set, so modname is already the package name */ - char *modname_str; - Py_ssize_t len; int error; - modname_str = _PyUnicode_AsStringAndSize(modname, &len); - if (len > MAXPATHLEN) { + if (PyUnicode_GET_SIZE(modname)+1 > bufsize) { PyErr_SetString(PyExc_ValueError, "Module name too long"); return NULL; } - strcpy(buf, modname_str); + Py_UNICODE_strcpy(name, PyUnicode_AS_UNICODE(modname)); error = PyDict_SetItem(globals, pkgstr, modname); if (error) { PyErr_SetString(PyExc_ValueError, @@ -2622,9 +2874,9 @@ } } else { /* Normal module, so work out the package name if any */ - char *start = _PyUnicode_AsString(modname); - char *lastdot = strrchr(start, '.'); - size_t len; + Py_UNICODE *start = PyUnicode_AS_UNICODE(modname); + Py_UNICODE *lastdot = Py_UNICODE_strrchr(start, '.'); + Py_ssize_t len; int error; if (lastdot == NULL && level > 0) { PyErr_SetString(PyExc_ValueError, @@ -2638,17 +2890,17 @@ "Could not set __package__"); return NULL; } - return Py_None; + goto return_none; } len = lastdot - start; - if (len >= MAXPATHLEN) { + if (len+1 > bufsize) { PyErr_SetString(PyExc_ValueError, "Module name too long"); return NULL; } - strncpy(buf, start, len); - buf[len] = '\0'; - pkgname = PyUnicode_FromString(buf); + Py_UNICODE_strncpy(name, start, len); + name[len] = '\0'; + pkgname = PyUnicode_FromUnicode(name, len); if (pkgname == NULL) { return NULL; } @@ -2662,7 +2914,7 @@ } } while (--level > 0) { - char *dot = strrchr(buf, '.'); + Py_UNICODE *dot = Py_UNICODE_strrchr(name, '.'); if (dot == NULL) { PyErr_SetString(PyExc_ValueError, "Attempted relative import beyond " @@ -2671,137 +2923,181 @@ } *dot = '\0'; } - *p_buflen = strlen(buf); + + nameobj = PyUnicode_FromUnicode(name, Py_UNICODE_strlen(name)); + if (nameobj == NULL) + return NULL; modules = PyImport_GetModuleDict(); - parent = PyDict_GetItemString(modules, buf); + parent = PyDict_GetItem(modules, nameobj); if (parent == NULL) { - if (orig_level < 1) { - PyObject *err_msg = PyBytes_FromFormat( - "Parent module '%.200s' not found " - "while handling absolute import", buf); - if (err_msg == NULL) { - return NULL; - } - if (!PyErr_WarnEx(PyExc_RuntimeWarning, - PyBytes_AsString(err_msg), 1)) { - *buf = '\0'; - *p_buflen = 0; - parent = Py_None; - } - Py_DECREF(err_msg); - } else { + int err; + + if (orig_level >= 1) { PyErr_Format(PyExc_SystemError, - "Parent module '%.200s' not loaded, " - "cannot perform relative import", buf); + "Parent module %R not loaded, " + "cannot perform relative import", nameobj); + Py_DECREF(nameobj); + return NULL; } + + err = PyErr_WarnFormat( + PyExc_RuntimeWarning, 1, + "Parent module %R not found while handling absolute import", + nameobj); + Py_DECREF(nameobj); + if (err) + return NULL; + + goto return_none; } + *p_name = nameobj; return parent; /* We expect, but can't guarantee, if parent != None, that: - - parent.__name__ == buf + - parent.__name__ == name - parent.__dict__ is globals If this is violated... Who cares? */ + +return_none: + nameobj = PyUnicode_FromUnicode(NULL, 0); + if (nameobj == NULL) + return NULL; + *p_name = nameobj; + return Py_None; } /* altmod is either None or same as mod */ static PyObject * -load_next(PyObject *mod, PyObject *altmod, char **p_name, char *buf, - Py_ssize_t *p_buflen) +load_next(PyObject *mod, PyObject *altmod, + PyObject *inputname, PyObject **p_outputname, + Py_UNICODE *buf, Py_ssize_t *p_buflen, Py_ssize_t bufsize) { - char *name = *p_name; - char *dot = strchr(name, '.'); - size_t len; - char *p; - PyObject *result; - - if (strlen(name) == 0) { + const Py_UNICODE *dot; + Py_ssize_t len; + Py_UNICODE *p; + PyObject *fullname, *name, *result, *mark_name; + const Py_UNICODE *nameuni; + + *p_outputname = NULL; + + if (PyUnicode_GET_SIZE(inputname) == 0) { /* completely empty module name should only happen in 'from . import' (or '__import__("")')*/ Py_INCREF(mod); - *p_name = NULL; return mod; } - if (dot == NULL) { - *p_name = NULL; - len = strlen(name); + nameuni = PyUnicode_AS_UNICODE(inputname); + if (nameuni == NULL) + return NULL; + + dot = Py_UNICODE_strchr(nameuni, '.'); + if (dot != NULL) { + len = dot - nameuni; + if (len == 0) { + PyErr_SetString(PyExc_ValueError, + "Empty module name"); + return NULL; + } } - else { - *p_name = dot+1; - len = dot-name; - } - if (len == 0) { - PyErr_SetString(PyExc_ValueError, - "Empty module name"); - return NULL; - } - - p = buf + *p_buflen; - if (p != buf) - *p++ = '.'; - if (p+len-buf >= MAXPATHLEN) { + else + len = PyUnicode_GET_SIZE(inputname); + + if (*p_buflen+len+1 >= bufsize) { PyErr_SetString(PyExc_ValueError, "Module name too long"); return NULL; } - strncpy(p, name, len); + + p = buf + *p_buflen; + if (p != buf) { + *p++ = '.'; + *p_buflen += 1; + } + Py_UNICODE_strncpy(p, nameuni, len); p[len] = '\0'; - *p_buflen = p+len-buf; - - result = import_submodule(mod, p, buf); + *p_buflen += len; + + fullname = PyUnicode_FromUnicode(buf, *p_buflen); + if (fullname == NULL) + return NULL; + name = PyUnicode_FromUnicode(p, len); + if (name == NULL) { + Py_DECREF(fullname); + return NULL; + } + result = import_submodule(mod, name, fullname); + Py_DECREF(fullname); if (result == Py_None && altmod != mod) { Py_DECREF(result); /* Here, altmod must be None and mod must not be None */ - result = import_submodule(altmod, p, p); + result = import_submodule(altmod, name, name); + Py_DECREF(name); if (result != NULL && result != Py_None) { - if (mark_miss(buf) != 0) { + mark_name = PyUnicode_FromUnicode(buf, *p_buflen); + if (mark_name == NULL) { Py_DECREF(result); return NULL; } - strncpy(buf, name, len); + if (mark_miss(mark_name) != 0) { + Py_DECREF(result); + Py_DECREF(mark_name); + return NULL; + } + Py_DECREF(mark_name); + Py_UNICODE_strncpy(buf, nameuni, len); buf[len] = '\0'; *p_buflen = len; } } + else + Py_DECREF(name); if (result == NULL) return NULL; if (result == Py_None) { Py_DECREF(result); PyErr_Format(PyExc_ImportError, - "No module named %.200s", name); + "No module named %R", inputname); return NULL; } + if (dot != NULL) { + *p_outputname = PyUnicode_FromUnicode(dot+1, Py_UNICODE_strlen(dot+1)); + if (*p_outputname == NULL) { + Py_DECREF(result); + return NULL; + } + } + return result; } static int -mark_miss(char *name) +mark_miss(PyObject *name) { PyObject *modules = PyImport_GetModuleDict(); - return PyDict_SetItemString(modules, name, Py_None); + return PyDict_SetItem(modules, name, Py_None); } static int -ensure_fromlist(PyObject *mod, PyObject *fromlist, char *buf, Py_ssize_t buflen, +ensure_fromlist(PyObject *mod, PyObject *fromlist, PyObject *name, int recursive) { int i; + PyObject *fullname; + Py_ssize_t fromlist_len; if (!PyObject_HasAttrString(mod, "__path__")) return 1; - for (i = 0; ; i++) { + fromlist_len = PySequence_Size(fromlist); + + for (i = 0; i < fromlist_len; i++) { PyObject *item = PySequence_GetItem(fromlist, i); int hasit; - if (item == NULL) { - if (PyErr_ExceptionMatches(PyExc_IndexError)) { - PyErr_Clear(); - return 1; - } + if (item == NULL) return 0; - } if (!PyUnicode_Check(item)) { PyErr_SetString(PyExc_TypeError, "Item in ``from list'' not a string"); @@ -2818,7 +3114,7 @@ if (all == NULL) PyErr_Clear(); else { - int ret = ensure_fromlist(mod, all, buf, buflen, 1); + int ret = ensure_fromlist(mod, all, name, 1); Py_DECREF(all); if (!ret) return 0; @@ -2827,27 +3123,14 @@ } hasit = PyObject_HasAttr(mod, item); if (!hasit) { - PyObject *item8; - char *subname; PyObject *submod; - char *p; - item8 = PyUnicode_EncodeFSDefault(item); - if (!item8) { - PyErr_SetString(PyExc_ValueError, "Cannot encode path item"); - return 0; + fullname = PyUnicode_FromFormat("%U.%U", name, item); + if (fullname != NULL) { + submod = import_submodule(mod, item, fullname); + Py_DECREF(fullname); } - subname = PyBytes_AS_STRING(item8); - if (buflen + strlen(subname) >= MAXPATHLEN) { - PyErr_SetString(PyExc_ValueError, - "Module name too long"); - Py_DECREF(item); - return 0; - } - p = buf + buflen; - *p++ = '.'; - strcpy(p, subname); - submod = import_submodule(mod, subname, buf); - Py_DECREF(item8); + else + submod = NULL; Py_XDECREF(submod); if (submod == NULL) { Py_DECREF(item); @@ -2857,12 +3140,12 @@ Py_DECREF(item); } - /* NOTREACHED */ + return 1; } static int -add_submodule(PyObject *mod, PyObject *submod, char *fullname, char *subname, - PyObject *modules) +add_submodule(PyObject *mod, PyObject *submod, PyObject *fullname, + PyObject *subname, PyObject *modules) { if (mod == Py_None) return 1; @@ -2873,7 +3156,7 @@ load failed with a SyntaxError -- then there's no trace in sys.modules. In that case, of course, do nothing extra.) */ if (submod == NULL) { - submod = PyDict_GetItemString(modules, fullname); + submod = PyDict_GetItem(modules, fullname); if (submod == NULL) return 1; } @@ -2884,68 +3167,66 @@ PyObject *dict = PyModule_GetDict(mod); if (!dict) return 0; - if (PyDict_SetItemString(dict, subname, submod) < 0) + if (PyDict_SetItem(dict, subname, submod) < 0) return 0; } else { - if (PyObject_SetAttrString(mod, subname, submod) < 0) + if (PyObject_SetAttr(mod, subname, submod) < 0) return 0; } return 1; } static PyObject * -import_submodule(PyObject *mod, char *subname, char *fullname) +import_submodule(PyObject *mod, PyObject *subname, PyObject *fullname) { PyObject *modules = PyImport_GetModuleDict(); - PyObject *m = NULL; + PyObject *m = NULL, *bufobj, *path_list, *loader; + struct filedescr *fdp; + FILE *fp; /* Require: if mod == None: subname == fullname else: mod.__name__ + "." + subname == fullname */ - if ((m = PyDict_GetItemString(modules, fullname)) != NULL) { + if ((m = PyDict_GetItem(modules, fullname)) != NULL) { Py_INCREF(m); + return m; } + + if (mod == Py_None) + path_list = NULL; else { - PyObject *path, *loader = NULL; - char buf[MAXPATHLEN+1]; - struct filedescr *fdp; - FILE *fp = NULL; - - if (mod == Py_None) - path = NULL; - else { - path = PyObject_GetAttrString(mod, "__path__"); - if (path == NULL) { - PyErr_Clear(); - Py_INCREF(Py_None); - return Py_None; - } - } - - buf[0] = '\0'; - fdp = find_module(fullname, subname, path, buf, MAXPATHLEN+1, - &fp, &loader); - Py_XDECREF(path); - if (fdp == NULL) { - if (!PyErr_ExceptionMatches(PyExc_ImportError)) - return NULL; + path_list = PyObject_GetAttrString(mod, "__path__"); + if (path_list == NULL) { PyErr_Clear(); Py_INCREF(Py_None); return Py_None; } - m = load_module(fullname, fp, buf, fdp->type, loader); - Py_XDECREF(loader); - if (fp) - fclose(fp); - if (!add_submodule(mod, m, fullname, subname, modules)) { - Py_XDECREF(m); - m = NULL; - } } + fdp = find_module(fullname, subname, path_list, + &bufobj, &fp, &loader); + Py_XDECREF(path_list); + if (fdp == NULL) { + if (!PyErr_ExceptionMatches(PyExc_ImportError)) + return NULL; + PyErr_Clear(); + Py_INCREF(Py_None); + return Py_None; + } + m = load_module(fullname, fp, bufobj, fdp->type, loader); + Py_XDECREF(bufobj); + Py_XDECREF(loader); + if (fp) + fclose(fp); + if (m == NULL) + return NULL; + if (!add_submodule(mod, m, fullname, subname, modules)) { + Py_XDECREF(m); + return NULL; + } return m; } @@ -2959,12 +3240,12 @@ PyInterpreterState *interp = PyThreadState_Get()->interp; PyObject *modules_reloading = interp->modules_reloading; PyObject *modules = PyImport_GetModuleDict(); - PyObject *path = NULL, *loader = NULL, *existing_m = NULL; - char *name, *subname; - char buf[MAXPATHLEN+1]; + PyObject *path_list = NULL, *loader = NULL, *existing_m = NULL; + PyObject *nameobj, *bufobj, *subnameobj; + Py_UNICODE *name, *subname; struct filedescr *fdp; FILE *fp = NULL; - PyObject *newm; + PyObject *newm = NULL; if (modules_reloading == NULL) { Py_FatalError("PyImport_ReloadModule: " @@ -2977,61 +3258,73 @@ "reload() argument must be module"); return NULL; } - name = (char*)PyModule_GetName(m); - if (name == NULL) + nameobj = PyModule_GetNameObject(m); + if (nameobj == NULL) return NULL; - if (m != PyDict_GetItemString(modules, name)) { + if (m != PyDict_GetItem(modules, nameobj)) { PyErr_Format(PyExc_ImportError, - "reload(): module %.200s not in sys.modules", - name); + "reload(): module %R not in sys.modules", + nameobj); + Py_DECREF(nameobj); return NULL; } - existing_m = PyDict_GetItemString(modules_reloading, name); + existing_m = PyDict_GetItem(modules_reloading, nameobj); if (existing_m != NULL) { /* Due to a recursive reload, this module is already being reloaded. */ + Py_DECREF(nameobj); Py_INCREF(existing_m); return existing_m; } - if (PyDict_SetItemString(modules_reloading, name, m) < 0) + if (PyDict_SetItem(modules_reloading, nameobj, m) < 0) { + Py_DECREF(nameobj); return NULL; - - subname = strrchr(name, '.'); - if (subname == NULL) - subname = name; + } + + name = PyUnicode_AS_UNICODE(nameobj); + subname = Py_UNICODE_strrchr(name, '.'); + if (subname == NULL) { + Py_INCREF(nameobj); + subnameobj = nameobj; + } else { PyObject *parentname, *parent; - parentname = PyUnicode_FromStringAndSize(name, (subname-name)); + Py_ssize_t len; + len = subname - name; + parentname = PyUnicode_FromUnicode(name, len); if (parentname == NULL) { - imp_modules_reloading_clear(); - return NULL; + goto error; } parent = PyDict_GetItem(modules, parentname); if (parent == NULL) { PyErr_Format(PyExc_ImportError, - "reload(): parent %U not in sys.modules", + "reload(): parent %R not in sys.modules", parentname); Py_DECREF(parentname); - imp_modules_reloading_clear(); - return NULL; + goto error; } Py_DECREF(parentname); + path_list = PyObject_GetAttrString(parent, "__path__"); + if (path_list == NULL) + PyErr_Clear(); subname++; - path = PyObject_GetAttrString(parent, "__path__"); - if (path == NULL) - PyErr_Clear(); + len = PyUnicode_GET_SIZE(nameobj) - (len + 1); + subnameobj = PyUnicode_FromUnicode(subname, len); } - buf[0] = '\0'; - fdp = find_module(name, subname, path, buf, MAXPATHLEN+1, &fp, &loader); - Py_XDECREF(path); + if (subnameobj == NULL) + goto error; + fdp = find_module(nameobj, subnameobj, path_list, + &bufobj, &fp, &loader); + Py_DECREF(subnameobj); + Py_XDECREF(path_list); if (fdp == NULL) { Py_XDECREF(loader); - imp_modules_reloading_clear(); - return NULL; + goto error; } - newm = load_module(name, fp, buf, fdp->type, loader); + newm = load_module(nameobj, fp, bufobj, fdp->type, loader); + Py_XDECREF(bufobj); Py_XDECREF(loader); if (fp) @@ -3042,9 +3335,12 @@ * going to return NULL in this case regardless of whether * replacing name succeeds, so the return value is ignored. */ - PyDict_SetItemString(modules, name, m); + PyDict_SetItem(modules, nameobj, m); } + +error: imp_modules_reloading_clear(); + Py_DECREF(nameobj); return newm; } @@ -3192,22 +3488,21 @@ } static PyObject * -call_find_module(char *name, PyObject *path) +call_find_module(PyObject *name, PyObject *path_list) { extern int fclose(FILE *); PyObject *fob, *ret; PyObject *pathobj; struct filedescr *fdp; - char pathname[MAXPATHLEN+1]; - FILE *fp = NULL; + FILE *fp; int fd = -1; char *found_encoding = NULL; char *encoding = NULL; - pathname[0] = '\0'; - if (path == Py_None) - path = NULL; - fdp = find_module(NULL, name, path, pathname, MAXPATHLEN+1, &fp, NULL); + if (path_list == Py_None) + path_list = NULL; + fdp = find_module(NULL, name, path_list, + &pathobj, &fp, NULL); if (fdp == NULL) return NULL; if (fp != NULL) { @@ -3223,14 +3518,17 @@ memory. */ found_encoding = PyTokenizer_FindEncoding(fd); lseek(fd, 0, 0); /* Reset position */ - if (found_encoding == NULL && PyErr_Occurred()) + if (found_encoding == NULL && PyErr_Occurred()) { + Py_XDECREF(pathobj); return NULL; + } encoding = (found_encoding != NULL) ? found_encoding : (char*)PyUnicode_GetDefaultEncoding(); } - fob = PyFile_FromFd(fd, pathname, fdp->mode, -1, + fob = PyFile_FromFd(fd, NULL, fdp->mode, -1, (char*)encoding, NULL, NULL, 1); if (fob == NULL) { + Py_XDECREF(pathobj); close(fd); PyMem_FREE(found_encoding); return NULL; @@ -3240,7 +3538,10 @@ fob = Py_None; Py_INCREF(fob); } - pathobj = PyUnicode_DecodeFSDefault(pathname); + if (pathobj == NULL) { + Py_INCREF(Py_None); + pathobj = Py_None; + } ret = Py_BuildValue("NN(ssi)", fob, pathobj, fdp->suffix, fdp->mode, fdp->type); PyMem_FREE(found_encoding); @@ -3251,24 +3552,20 @@ static PyObject * imp_find_module(PyObject *self, PyObject *args) { - PyObject *name; - PyObject *ret, *path = NULL; - if (!PyArg_ParseTuple(args, "O&|O:find_module", - PyUnicode_FSConverter, &name, - &path)) + PyObject *name, *path_list = NULL; + if (!PyArg_ParseTuple(args, "U|O:find_module", + &name, &path_list)) return NULL; - ret = call_find_module(PyBytes_AS_STRING(name), path); - Py_DECREF(name); - return ret; + return call_find_module(name, path_list); } static PyObject * imp_init_builtin(PyObject *self, PyObject *args) { - char *name; + PyObject *name; int ret; PyObject *m; - if (!PyArg_ParseTuple(args, "s:init_builtin", &name)) + if (!PyArg_ParseTuple(args, "U:init_builtin", &name)) return NULL; ret = init_builtin(name); if (ret < 0) @@ -3277,7 +3574,7 @@ Py_INCREF(Py_None); return Py_None; } - m = PyImport_AddModule(name); + m = PyImport_AddModuleObject(name); Py_XINCREF(m); return m; } @@ -3285,19 +3582,19 @@ static PyObject * imp_init_frozen(PyObject *self, PyObject *args) { - char *name; + PyObject *name; int ret; PyObject *m; - if (!PyArg_ParseTuple(args, "s:init_frozen", &name)) + if (!PyArg_ParseTuple(args, "U:init_frozen", &name)) return NULL; - ret = PyImport_ImportFrozenModule(name); + ret = PyImport_ImportFrozenModuleObject(name); if (ret < 0) return NULL; if (ret == 0) { Py_INCREF(Py_None); return Py_None; } - m = PyImport_AddModule(name); + m = PyImport_AddModuleObject(name); Py_XINCREF(m); return m; } @@ -3305,9 +3602,9 @@ static PyObject * imp_get_frozen_object(PyObject *self, PyObject *args) { - char *name; - - if (!PyArg_ParseTuple(args, "s:get_frozen_object", &name)) + PyObject *name; + + if (!PyArg_ParseTuple(args, "U:get_frozen_object", &name)) return NULL; return get_frozen_object(name); } @@ -3315,9 +3612,9 @@ static PyObject * imp_is_frozen_package(PyObject *self, PyObject *args) { - char *name; - - if (!PyArg_ParseTuple(args, "s:is_frozen_package", &name)) + PyObject *name; + + if (!PyArg_ParseTuple(args, "U:is_frozen_package", &name)) return NULL; return is_frozen_package(name); } @@ -3325,8 +3622,8 @@ static PyObject * imp_is_builtin(PyObject *self, PyObject *args) { - char *name; - if (!PyArg_ParseTuple(args, "s:is_builtin", &name)) + PyObject *name; + if (!PyArg_ParseTuple(args, "U:is_builtin", &name)) return NULL; return PyLong_FromLong(is_builtin(name)); } @@ -3334,22 +3631,22 @@ static PyObject * imp_is_frozen(PyObject *self, PyObject *args) { - char *name; + PyObject *name; struct _frozen *p; - if (!PyArg_ParseTuple(args, "s:is_frozen", &name)) + if (!PyArg_ParseTuple(args, "U:is_frozen", &name)) return NULL; p = find_frozen(name); return PyBool_FromLong((long) (p == NULL ? 0 : p->size)); } static FILE * -get_file(char *pathname, PyObject *fob, char *mode) +get_file(PyObject *pathname, PyObject *fob, char *mode) { FILE *fp; if (mode[0] == 'U') mode = "r" PY_STDIOTEXTMODE; if (fob == NULL) { - fp = fopen(pathname, mode); + fp = _Py_fopen(pathname, mode); } else { int fd = PyObject_AsFileDescriptor(fob); @@ -3375,22 +3672,21 @@ static PyObject * imp_load_compiled(PyObject *self, PyObject *args) { - char *name; - PyObject *pathname; + PyObject *name, *pathname; PyObject *fob = NULL; PyObject *m; FILE *fp; - if (!PyArg_ParseTuple(args, "sO&|O:load_compiled", + if (!PyArg_ParseTuple(args, "UO&|O:load_compiled", &name, - PyUnicode_FSConverter, &pathname, + PyUnicode_FSDecoder, &pathname, &fob)) return NULL; - fp = get_file(PyBytes_AS_STRING(pathname), fob, "rb"); + fp = get_file(pathname, fob, "rb"); if (fp == NULL) { Py_DECREF(pathname); return NULL; } - m = load_compiled_module(name, PyBytes_AS_STRING(pathname), fp); + m = load_compiled_module(name, pathname, fp); fclose(fp); Py_DECREF(pathname); return m; @@ -3401,28 +3697,26 @@ static PyObject * imp_load_dynamic(PyObject *self, PyObject *args) { - char *name; - PyObject *pathbytes; - char *pathname; - PyObject *fob = NULL; - PyObject *m; - FILE *fp = NULL; - if (!PyArg_ParseTuple(args, "sO&|O:load_dynamic", - &name, PyUnicode_FSConverter, &pathbytes, &fob)) + PyObject *name, *pathname, *fob = NULL, *mod; + FILE *fp; + + if (!PyArg_ParseTuple(args, "UO&|O:load_dynamic", + &name, PyUnicode_FSDecoder, &pathname, &fob)) return NULL; - pathname = PyBytes_AS_STRING(pathbytes); - if (fob) { - fp = get_file(pathname, fob, "r"); + if (fob != NULL) { + fp = get_file(NULL, fob, "r"); if (fp == NULL) { - Py_DECREF(pathbytes); + Py_DECREF(pathname); return NULL; } } - m = _PyImport_LoadDynamicModule(name, pathname, fp); - Py_DECREF(pathbytes); + else + fp = NULL; + mod = _PyImport_LoadDynamicModule(name, pathname, fp); + Py_DECREF(pathname); if (fp) fclose(fp); - return m; + return mod; } #endif /* HAVE_DYNAMIC_LOADING */ @@ -3430,22 +3724,21 @@ static PyObject * imp_load_source(PyObject *self, PyObject *args) { - char *name; - PyObject *pathname; + PyObject *name, *pathname; PyObject *fob = NULL; PyObject *m; FILE *fp; - if (!PyArg_ParseTuple(args, "sO&|O:load_source", + if (!PyArg_ParseTuple(args, "UO&|O:load_source", &name, - PyUnicode_FSConverter, &pathname, + PyUnicode_FSDecoder, &pathname, &fob)) return NULL; - fp = get_file(PyBytes_AS_STRING(pathname), fob, "r"); + fp = get_file(pathname, fob, "r"); if (fp == NULL) { Py_DECREF(pathname); return NULL; } - m = load_source_module(name, PyBytes_AS_STRING(pathname), fp); + m = load_source_module(name, pathname, fp); Py_DECREF(pathname); fclose(fp); return m; @@ -3454,20 +3747,22 @@ static PyObject * imp_load_module(PyObject *self, PyObject *args) { - char *name; - PyObject *fob; - PyObject *pathname; - PyObject * ret; + PyObject *name, *fob, *pathname, *pathname_obj, *ret; char *suffix; /* Unused */ char *mode; int type; FILE *fp; - if (!PyArg_ParseTuple(args, "sOO&(ssi):load_module", - &name, &fob, - PyUnicode_FSConverter, &pathname, - &suffix, &mode, &type)) + if (!PyArg_ParseTuple(args, "UOO(ssi):load_module", + &name, &fob, &pathname_obj, &suffix, &mode, &type)) return NULL; + if (pathname_obj != Py_None) { + if (!PyUnicode_FSDecoder(pathname_obj, &pathname)) + return NULL; + } + else + pathname = NULL; + if (*mode) { /* Mode must start with 'r' or 'U' and must not contain '+'. Implicit in this test is the assumption that the mode @@ -3476,7 +3771,7 @@ if (!(*mode == 'r' || *mode == 'U') || strchr(mode, '+')) { PyErr_Format(PyExc_ValueError, "invalid file open mode %.200s", mode); - Py_DECREF(pathname); + Py_XDECREF(pathname); return NULL; } } @@ -3485,12 +3780,12 @@ else { fp = get_file(NULL, fob, mode); if (fp == NULL) { - Py_DECREF(pathname); + Py_XDECREF(pathname); return NULL; } } - ret = load_module(name, fp, PyBytes_AS_STRING(pathname), type, NULL); - Py_DECREF(pathname); + ret = load_module(name, fp, pathname, type, NULL); + Py_XDECREF(pathname); if (fp) fclose(fp); return ret; @@ -3499,13 +3794,12 @@ static PyObject * imp_load_package(PyObject *self, PyObject *args) { - char *name; - PyObject *pathname; + PyObject *name, *pathname; PyObject * ret; - if (!PyArg_ParseTuple(args, "sO&:load_package", - &name, PyUnicode_FSConverter, &pathname)) + if (!PyArg_ParseTuple(args, "UO&:load_package", + &name, PyUnicode_FSDecoder, &pathname)) return NULL; - ret = load_package(name, PyBytes_AS_STRING(pathname)); + ret = load_package(name, pathname); Py_DECREF(pathname); return ret; } @@ -3513,10 +3807,10 @@ static PyObject * imp_new_module(PyObject *self, PyObject *args) { - char *name; - if (!PyArg_ParseTuple(args, "s:new_module", &name)) + PyObject *name; + if (!PyArg_ParseTuple(args, "U:new_module", &name)) return NULL; - return PyModule_New(name); + return PyModule_NewObject(name); } static PyObject * @@ -3535,33 +3829,31 @@ { static char *kwlist[] = {"path", "debug_override", NULL}; - char buf[MAXPATHLEN+1]; - PyObject *pathbytes; - char *cpathname; + PyObject *pathname, *cpathname; PyObject *debug_override = NULL; int debug = !Py_OptimizeFlag; if (!PyArg_ParseTupleAndKeywords( args, kws, "O&|O", kwlist, - PyUnicode_FSConverter, &pathbytes, &debug_override)) + PyUnicode_FSDecoder, &pathname, &debug_override)) return NULL; if (debug_override != NULL && (debug = PyObject_IsTrue(debug_override)) < 0) { - Py_DECREF(pathbytes); + Py_DECREF(pathname); return NULL; } cpathname = make_compiled_pathname( - PyBytes_AS_STRING(pathbytes), - buf, MAXPATHLEN+1, debug); - Py_DECREF(pathbytes); + PyUnicode_AS_UNICODE(pathname), + debug); + Py_DECREF(pathname); if (cpathname == NULL) { PyErr_Format(PyExc_SystemError, "path buffer too short"); return NULL; } - return PyUnicode_DecodeFSDefault(buf); + return cpathname; } PyDoc_STRVAR(doc_cache_from_source, @@ -3578,25 +3870,22 @@ imp_source_from_cache(PyObject *self, PyObject *args, PyObject *kws) { static char *kwlist[] = {"path", NULL}; - - PyObject *pathname_obj; - char *pathname; - char buf[MAXPATHLEN+1]; + PyObject *pathname, *source; if (!PyArg_ParseTupleAndKeywords( args, kws, "O&", kwlist, - PyUnicode_FSConverter, &pathname_obj)) + PyUnicode_FSDecoder, &pathname)) return NULL; - pathname = PyBytes_AS_STRING(pathname_obj); - if (make_source_pathname(pathname, buf) == NULL) { - PyErr_Format(PyExc_ValueError, "Not a PEP 3147 pyc path: %s", + source = make_source_pathname(pathname); + if (source == NULL) { + PyErr_Format(PyExc_ValueError, "Not a PEP 3147 pyc path: %R", pathname); - Py_DECREF(pathname_obj); + Py_DECREF(pathname); return NULL; } - Py_DECREF(pathname_obj); - return PyUnicode_FromString(buf); + Py_DECREF(pathname); + return source; } PyDoc_STRVAR(doc_source_from_cache, diff -r 9dcd88977694 -r 650549138a3d Python/importdl.c --- a/Python/importdl.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/importdl.c Tue Mar 22 21:32:51 2011 +1000 @@ -15,61 +15,68 @@ extern dl_funcptr _PyImport_GetDynLoadFunc(const char *shortname, const char *pathname, FILE *fp); - +/* name should be ASCII only because the C language doesn't accept non-ASCII + identifiers, and dynamic modules are written in C. */ PyObject * -_PyImport_LoadDynamicModule(char *name, char *pathname, FILE *fp) +_PyImport_LoadDynamicModule(PyObject *name, PyObject *path, FILE *fp) { PyObject *m; - PyObject *path; - char *lastdot, *shortname, *packagecontext, *oldcontext; + PyObject *pathbytes; + char *namestr, *lastdot, *shortname, *packagecontext, *oldcontext; dl_funcptr p0; PyObject* (*p)(void); struct PyModuleDef *def; - PyObject *result; - path = PyUnicode_DecodeFSDefault(pathname); - if (path == NULL) + namestr = _PyUnicode_AsString(name); + if (namestr == NULL) return NULL; - if ((m = _PyImport_FindExtensionUnicode(name, path)) != NULL) { + m = _PyImport_FindExtensionObject(name, path); + if (m != NULL) { Py_INCREF(m); - result = m; - goto finally; + return m; } - lastdot = strrchr(name, '.'); + + lastdot = strrchr(namestr, '.'); if (lastdot == NULL) { packagecontext = NULL; - shortname = name; + shortname = namestr; } else { - packagecontext = name; + packagecontext = namestr; shortname = lastdot+1; } - p0 = _PyImport_GetDynLoadFunc(shortname, pathname, fp); + pathbytes = PyUnicode_EncodeFSDefault(path); + if (pathbytes == NULL) + return NULL; + p0 = _PyImport_GetDynLoadFunc(shortname, + PyBytes_AS_STRING(pathbytes), fp); + Py_DECREF(pathbytes); p = (PyObject*(*)(void))p0; if (PyErr_Occurred()) - goto error; + return NULL; if (p == NULL) { PyErr_Format(PyExc_ImportError, - "dynamic module does not define init function (PyInit_%.200s)", + "dynamic module does not define init function" + " (PyInit_%s)", shortname); - goto error; + return NULL; } oldcontext = _Py_PackageContext; _Py_PackageContext = packagecontext; m = (*p)(); _Py_PackageContext = oldcontext; if (m == NULL) - goto error; + return NULL; if (PyErr_Occurred()) { Py_DECREF(m); PyErr_Format(PyExc_SystemError, "initialization of %s raised unreported exception", shortname); - goto error; + return NULL; } /* Remember pointer to module init function. */ @@ -82,20 +89,13 @@ else Py_INCREF(path); - if (_PyImport_FixupExtensionUnicode(m, name, path) < 0) - goto error; + if (_PyImport_FixupExtensionObject(m, name, path) < 0) + return NULL; if (Py_VerboseFlag) - PySys_WriteStderr( - "import %s # dynamically loaded from %s\n", - name, pathname); - result = m; - goto finally; - -error: - result = NULL; -finally: - Py_DECREF(path); - return result; + PySys_FormatStderr( + "import %U # dynamically loaded from %R\n", + name, path); + return m; } #endif /* HAVE_DYNAMIC_LOADING */ diff -r 9dcd88977694 -r 650549138a3d Python/importdl.h --- a/Python/importdl.h Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/importdl.h Tue Mar 22 21:32:51 2011 +1000 @@ -28,7 +28,7 @@ extern struct filedescr * _PyImport_Filetab; extern const struct filedescr _PyImport_DynLoadFiletab[]; -extern PyObject *_PyImport_LoadDynamicModule(char *name, char *pathname, +extern PyObject *_PyImport_LoadDynamicModule(PyObject *name, PyObject *pathname, FILE *); /* Max length of module suffix searched for -- accommodates "module.slb" */ diff -r 9dcd88977694 -r 650549138a3d Python/peephole.c --- a/Python/peephole.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/peephole.c Tue Mar 22 21:32:51 2011 +1000 @@ -535,7 +535,8 @@ } if (codestr[i+3] != UNPACK_SEQUENCE || !ISBASICBLOCK(blocks,i,6) || - j != GETARG(codestr, i+3)) + j != GETARG(codestr, i+3) || + opcode == BUILD_SET) continue; if (j == 1) { memset(codestr+i, NOP, 6); diff -r 9dcd88977694 -r 650549138a3d Python/pystate.c --- a/Python/pystate.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/pystate.c Tue Mar 22 21:32:51 2011 +1000 @@ -512,7 +512,7 @@ /* for i in all interpreters: * for t in all of i's thread states: * if t's frame isn't NULL, map t's id to its frame - * Because these lists can mutute even when the GIL is held, we + * Because these lists can mutate even when the GIL is held, we * need to grab head_mutex for the duration. */ HEAD_LOCK(); diff -r 9dcd88977694 -r 650549138a3d Python/pythonrun.c --- a/Python/pythonrun.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/pythonrun.c Tue Mar 22 21:32:51 2011 +1000 @@ -147,7 +147,7 @@ goto error; name_utf8 = _PyUnicode_AsString(name); - if (name == NULL) + if (name_utf8 == NULL) goto error; name_str = strdup(name_utf8); Py_DECREF(name); diff -r 9dcd88977694 -r 650549138a3d Python/thread.c --- a/Python/thread.c Tue Mar 22 21:31:46 2011 +1000 +++ b/Python/thread.c Tue Mar 22 21:32:51 2011 +1000 @@ -40,7 +40,7 @@ #endif /* Check if we're running on HP-UX and _SC_THREADS is defined. If so, then - enough of the Posix threads package is implimented to support python + enough of the Posix threads package is implemented to support python threads. This is valid for HP-UX 11.23 running on an ia64 system. If needed, add diff -r 9dcd88977694 -r 650549138a3d Tools/freeze/checkextensions_win32.py --- a/Tools/freeze/checkextensions_win32.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Tools/freeze/checkextensions_win32.py Tue Mar 22 21:32:51 2011 +1000 @@ -7,7 +7,7 @@ we get it just right, a specific freeze application may have specific compiler options anyway (eg, to enable or disable specific functionality) -So my basic stragtegy is: +So my basic strategy is: * Have some Windows INI files which "describe" one or more extension modules. (Freeze comes with a default one for all known modules - but you can specify diff -r 9dcd88977694 -r 650549138a3d Tools/scripts/fixcid.py --- a/Tools/scripts/fixcid.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Tools/scripts/fixcid.py Tue Mar 22 21:32:51 2011 +1000 @@ -188,7 +188,7 @@ except os.error as msg: err(filename + ': rename failed (' + str(msg) + ')\n') return 1 - # Return succes + # Return success return 0 # Tokenizing ANSI C (partly) diff -r 9dcd88977694 -r 650549138a3d Tools/unicode/makeunicodedata.py --- a/Tools/unicode/makeunicodedata.py Tue Mar 22 21:31:46 2011 +1000 +++ b/Tools/unicode/makeunicodedata.py Tue Mar 22 21:32:51 2011 +1000 @@ -1002,7 +1002,7 @@ poly = size + poly break else: - raise AssertionError("ran out of polynominals") + raise AssertionError("ran out of polynomials") print(size, "slots in hash table") diff -r 9dcd88977694 -r 650549138a3d configure --- a/configure Tue Mar 22 21:31:46 2011 +1000 +++ b/configure Tue Mar 22 21:32:51 2011 +1000 @@ -776,8 +776,7 @@ LDFLAGS LIBS CPPFLAGS -CPP -CPPFLAGS' +CPP' # Initialize some variables set by options. @@ -9251,19 +9250,21 @@ # checks for library functions for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ clock confstr ctermid execv faccessat fchmod fchmodat fchown fchownat \ - fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ + fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ + futimens futimes \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ - initgroups kill killpg lchmod lchown linkat lstat mbrtowc mkdirat mkfifo \ + initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mbrtowc mkdirat mkfifo \ mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll \ - pthread_init putenv readlink readlinkat realpath renameat \ + posix_fallocate posix_fadvise pread \ + pthread_init putenv pwrite readlink readlinkat readv realpath renameat \ select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ setgid sethostname \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ - sigaction siginterrupt sigrelse snprintf strftime strlcpy symlinkat \ + sigaction siginterrupt sigrelse snprintf strftime strlcpy symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ - truncate uname unlinkat unsetenv utimensat utimes waitpid wait3 wait4 \ - wcscoll wcsftime wcsxfrm _getpty + truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 \ + wcscoll wcsftime wcsxfrm writev _getpty do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff -r 9dcd88977694 -r 650549138a3d configure.in --- a/configure.in Tue Mar 22 21:31:46 2011 +1000 +++ b/configure.in Tue Mar 22 21:32:51 2011 +1000 @@ -2496,19 +2496,21 @@ # checks for library functions AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \ clock confstr ctermid execv faccessat fchmod fchmodat fchown fchownat \ - fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ + fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \ + futimens futimes \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \ - initgroups kill killpg lchmod lchown linkat lstat mbrtowc mkdirat mkfifo \ + initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mbrtowc mkdirat mkfifo \ mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll \ - pthread_init putenv readlink readlinkat realpath renameat \ + posix_fallocate posix_fadvise pread \ + pthread_init putenv pwrite readlink readlinkat readv realpath renameat \ select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid \ setgid sethostname \ setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf \ - sigaction siginterrupt sigrelse snprintf strftime strlcpy symlinkat \ + sigaction siginterrupt sigrelse snprintf strftime strlcpy symlinkat sync \ sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r \ - truncate uname unlinkat unsetenv utimensat utimes waitpid wait3 wait4 \ - wcscoll wcsftime wcsxfrm _getpty) + truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 \ + wcscoll wcsftime wcsxfrm writev _getpty) # For some functions, having a definition is not sufficient, since # we want to take their address. diff -r 9dcd88977694 -r 650549138a3d pyconfig.h.in --- a/pyconfig.h.in Tue Mar 22 21:31:46 2011 +1000 +++ b/pyconfig.h.in Tue Mar 22 21:32:51 2011 +1000 @@ -232,6 +232,9 @@ /* Define to 1 if you have the `fdopendir' function. */ #undef HAVE_FDOPENDIR +/* Define to 1 if you have the `fexecve' function. */ +#undef HAVE_FEXECVE + /* Define to 1 if you have the `finite' function. */ #undef HAVE_FINITE @@ -274,6 +277,12 @@ /* Define to 1 if you have the `ftruncate' function. */ #undef HAVE_FTRUNCATE +/* Define to 1 if you have the `futimens' function. */ +#undef HAVE_FUTIMENS + +/* Define to 1 if you have the `futimes' function. */ +#undef HAVE_FUTIMES + /* Define to 1 if you have the `futimesat' function. */ #undef HAVE_FUTIMESAT @@ -461,6 +470,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LINUX_TIPC_H +/* Define to 1 if you have the `lockf' function. */ +#undef HAVE_LOCKF + /* Define to 1 if you have the `log1p' function. */ #undef HAVE_LOG1P @@ -473,6 +485,9 @@ /* Define to 1 if you have the `lstat' function. */ #undef HAVE_LSTAT +/* Define to 1 if you have the `lutimes' function. */ +#undef HAVE_LUTIMES + /* Define this if you have the makedev macro. */ #undef HAVE_MAKEDEV @@ -545,6 +560,15 @@ /* Define to 1 if you have the header file. */ #undef HAVE_POLL_H +/* Define to 1 if you have the `posix_fadvise' function. */ +#undef HAVE_POSIX_FADVISE + +/* Define to 1 if you have the `posix_fallocate' function. */ +#undef HAVE_POSIX_FALLOCATE + +/* Define to 1 if you have the `pread' function. */ +#undef HAVE_PREAD + /* Define to 1 if you have the header file. */ #undef HAVE_PROCESS_H @@ -569,12 +593,18 @@ /* Define to 1 if you have the `putenv' function. */ #undef HAVE_PUTENV +/* Define to 1 if you have the `pwrite' function. */ +#undef HAVE_PWRITE + /* Define to 1 if you have the `readlink' function. */ #undef HAVE_READLINK /* Define to 1 if you have the `readlinkat' function. */ #undef HAVE_READLINKAT +/* Define to 1 if you have the `readv' function. */ +#undef HAVE_READV + /* Define to 1 if you have the `realpath' function. */ #undef HAVE_REALPATH @@ -775,6 +805,9 @@ /* Define to 1 if you have the `symlinkat' function. */ #undef HAVE_SYMLINKAT +/* Define to 1 if you have the `sync' function. */ +#undef HAVE_SYNC + /* Define to 1 if you have the `sysconf' function. */ #undef HAVE_SYSCONF @@ -952,6 +985,9 @@ /* Define to 1 if you have the `wait4' function. */ #undef HAVE_WAIT4 +/* Define to 1 if you have the `waitid' function. */ +#undef HAVE_WAITID + /* Define to 1 if you have the `waitpid' function. */ #undef HAVE_WAITPID @@ -971,6 +1007,9 @@ */ #undef HAVE_WORKING_TZSET +/* Define to 1 if you have the `writev' function. */ +#undef HAVE_WRITEV + /* Define if the zlib library has inflateCopy */ #undef HAVE_ZLIB_COPY diff -r 9dcd88977694 -r 650549138a3d setup.py --- a/setup.py Tue Mar 22 21:31:46 2011 +1000 +++ b/setup.py Tue Mar 22 21:32:51 2011 +1000 @@ -1017,8 +1017,8 @@ if sys.platform == 'darwin': # In every directory on the search path search for a dynamic # library and then a static library, instead of first looking - # for dynamic libraries on the entiry path. - # This way a staticly linked custom sqlite gets picked up + # for dynamic libraries on the entire path. + # This way a statically linked custom sqlite gets picked up # before the dynamic library in /usr/lib. sqlite_extra_link_args = ('-Wl,-search_paths_first',) else: