Naresh Khalasi avatar Naresh Khalasi committed 3b5faaa

Initial commit of version 1.3 downloaded from http://pypi.python.org/pypi/lazr.smtptest

Comments (0)

Files changed (27)

+		   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+..
+    This file is part of lazr.smtptest.
+
+    lazr.smtptest is free software: you can redistribute it and/or modify it
+    under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, version 3 of the License.
+
+    lazr.smtptest is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+    License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+
+This project uses zc.buildout for development.
+
+============
+Introduction
+============
+
+These are guidelines for hacking on the lazr.smtptest project.  But first,
+please see the common hacking guidelines at:
+
+    http://dev.launchpad.net/Hacking
+
+
+Getting help
+------------
+
+If you find bugs in this package, you can report them here:
+
+    https://launchpad.net/lazr.smtptest
+
+If you want to discuss this package, join the team and mailing list here:
+
+    https://launchpad.net/~lazr-users
+
+or send a message to:
+
+    lazr-users@lists.launchpad.net
+Metadata-Version: 1.0
+Name: lazr.smtptest
+Version: 1.3
+Summary: A package providing a test framework for SMTP based applications.
+Home-page: https://launchpad.net/lazr.smtptest
+Author: LAZR Developers
+Author-email: lazr-developers@lists.launchpad.net
+License: LGPL v3
+Download-URL: https://launchpad.net/lazr.smtptest/+download
+Description: ..
+        This file is part of lazr.smtptest.
+        
+        lazr.smtptest is free software: you can redistribute it and/or modify it
+        under the terms of the GNU Lesser General Public License as published by
+        the Free Software Foundation, version 3 of the License.
+        
+        lazr.smtptest is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+        or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+        License for more details.
+        
+        You should have received a copy of the GNU Lesser General Public License
+        along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+        
+        LAZR smtptest
+        *************
+        
+        This is LAZR smtptest, a framework for testing SMTP-based applications and
+        libraries.  It provides a real, live SMTP server that you can send messages
+        to, and from which you can read those test messages.  This can be used to
+        ensure proper operation of your applications which send email.
+        
+        .. toctree::
+        :glob:
+        
+        *
+        docs/*
+        
+        .. _Sphinx: http://sphinx.pocoo.org/
+        .. _Table of contents: http://sphinx.pocoo.org/concepts.html#the-toc-tree
+        
+        Importable
+        ==========
+        
+        The lazr.smtptest package is importable, and has a version number.
+        
+        >>> import lazr.smtptest
+        >>> print 'VERSION:', lazr.smtptest.__version__
+        VERSION: ...
+        
+        More information
+        ================
+        
+        For more general usage information, see usage_.  A specific, common test
+        regime can be found in queue_.
+        
+        .. _usage: docs/usage.html
+        .. _queue: docs/queue.html
+        
+        ======================
+        NEWS for lazr.smtptest
+        ======================
+        
+        1.3 (2011-06-07)
+        ================
+        
+        - Make the test server thread-safe with other code that starts an asyncore
+        loop.  Requires Python 2.6 or 2.7.
+        
+        - Be cleaner about stopping the server: before, it left sockets running
+        ans simply cleared out the socket map.  In an associated change, the EXIT
+        smtp command sends the reply first, and then shuts down the server, rather
+        than the other way around.
+        
+        1.2 (2009-07-07)
+        ================
+        
+        - [bug 393621] QueueServer.reset() was added to clear the message queue.  This
+        is invoked by sending an SMTP RSET command to the server, or through
+        Controller.reset().
+        
+        
+        1.1 (2009-06-29)
+        ================
+        
+        - [bug 391650] A non-public API was added to make QueueController more easily
+        subclassable.
+        
+        
+        1.0 (2009-06-22)
+        ================
+        
+        - Initial release
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+A package providing a test framework for SMTP based applications.
+
+..
+    This file is part of lazr.smtptest.
+
+    lazr.smtptest is free software: you can redistribute it and/or modify it
+    under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, version 3 of the License.
+
+    lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+    License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c8"
+DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+}
+
+import sys, os
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        from md5 import md5
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >>sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15, min_version=None
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    # Work around a hack in the ez_setup.py file from simplejson==1.7.3.
+    if min_version:
+        version = min_version
+
+    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+    def do_download():
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+    try:
+        import pkg_resources
+    except ImportError:
+        return do_download()       
+    try:
+        pkg_resources.require("setuptools>="+version); return
+    except pkg_resources.VersionConflict, e:
+        if was_imported:
+            print >>sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first, using 'easy_install -U setuptools'."
+            "\n\n(Currently using %r)"
+            ) % (version, e.args[0])
+            sys.exit(2)
+        else:
+            del pkg_resources, sys.modules['pkg_resources']    # reload ok
+            return do_download()
+    except pkg_resources.DistributionNotFound:
+        return do_download()
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay = 15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto,"wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0,egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv)+[egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            print >>sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+
+    req = "setuptools>="+version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv)+[download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version",version,"or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+    from md5 import md5
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name,'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >>sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile,'w')
+    f.write(src)
+    f.close()
+
+
+if __name__=='__main__':
+    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
+
+# lint Python modules using external checkers.
+# 
+# This is the main checker controlling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+# 
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=no
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories.
+#enable-msg-cat=
+
+# Disable all messages in the listed categories.
+#disable-msg-cat=
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+# I0011: *Locally disabling %s*
+disable-msg=I0011
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=parseable
+
+# Include message's id in output
+include-ids=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+# 
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+# 
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamicaly set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+# 
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+# 
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+# 
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=50
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+# 
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+# 
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=80
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='    '
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+# 
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+# 
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+[egg_info]
+tag_build = 
+tag_date = 0
+tag_svn_revision = 0
+
+#!/usr/bin/env python
+
+# Copyright 2009 Canonical Ltd.  All rights reserved.
+#
+# This file is part of lazr.smtptest
+#
+# lazr.smtptest is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+
+import ez_setup
+ez_setup.use_setuptools()
+
+import sys
+from setuptools import setup, find_packages
+
+# generic helpers primarily for the long_description
+def generate(*docname_or_string):
+    res = []
+    for value in docname_or_string:
+        if value.endswith('.txt'):
+            f = open(value)
+            value = f.read()
+            f.close()
+        res.append(value)
+        if not value.endswith('\n'):
+            res.append('')
+    return '\n'.join(res)
+# end generic helpers
+
+__version__ = open("src/lazr/smtptest/version.txt").read().strip()
+
+setup(
+    name='lazr.smtptest',
+    version=__version__,
+    namespace_packages=['lazr'],
+    packages=find_packages('src'),
+    package_dir={'':'src'},
+    include_package_data=True,
+    zip_safe=False,
+    maintainer='LAZR Developers',
+    maintainer_email='lazr-developers@lists.launchpad.net',
+    description=open('README.txt').readline().strip(),
+    long_description=generate(
+        'src/lazr/smtptest/README.txt',
+        'src/lazr/smtptest/NEWS.txt'),
+    license='LGPL v3',
+    install_requires=[
+        'setuptools',
+        'zope.interface',
+        ],
+    url='https://launchpad.net/lazr.smtptest',
+    download_url= 'https://launchpad.net/lazr.smtptest/+download',
+    classifiers=[
+        "Development Status :: 5 - Production/Stable",
+        "Intended Audience :: Developers",
+        "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
+        "Operating System :: OS Independent",
+        "Programming Language :: Python"],
+    extras_require=dict(
+        docs=['Sphinx',
+              'z3c.recipe.sphinxdoc']
+    ),
+    # This does not play nicely with buildout because it downloads but does
+    # not cache the package.
+    #setup_requires=['eggtestinfo', 'setuptools_bzr'],
+    test_suite='lazr.smtptest.tests',
+    )

src/lazr.smtptest.egg-info/PKG-INFO

+Metadata-Version: 1.0
+Name: lazr.smtptest
+Version: 1.3
+Summary: A package providing a test framework for SMTP based applications.
+Home-page: https://launchpad.net/lazr.smtptest
+Author: LAZR Developers
+Author-email: lazr-developers@lists.launchpad.net
+License: LGPL v3
+Download-URL: https://launchpad.net/lazr.smtptest/+download
+Description: ..
+        This file is part of lazr.smtptest.
+        
+        lazr.smtptest is free software: you can redistribute it and/or modify it
+        under the terms of the GNU Lesser General Public License as published by
+        the Free Software Foundation, version 3 of the License.
+        
+        lazr.smtptest is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+        or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+        License for more details.
+        
+        You should have received a copy of the GNU Lesser General Public License
+        along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+        
+        LAZR smtptest
+        *************
+        
+        This is LAZR smtptest, a framework for testing SMTP-based applications and
+        libraries.  It provides a real, live SMTP server that you can send messages
+        to, and from which you can read those test messages.  This can be used to
+        ensure proper operation of your applications which send email.
+        
+        .. toctree::
+        :glob:
+        
+        *
+        docs/*
+        
+        .. _Sphinx: http://sphinx.pocoo.org/
+        .. _Table of contents: http://sphinx.pocoo.org/concepts.html#the-toc-tree
+        
+        Importable
+        ==========
+        
+        The lazr.smtptest package is importable, and has a version number.
+        
+        >>> import lazr.smtptest
+        >>> print 'VERSION:', lazr.smtptest.__version__
+        VERSION: ...
+        
+        More information
+        ================
+        
+        For more general usage information, see usage_.  A specific, common test
+        regime can be found in queue_.
+        
+        .. _usage: docs/usage.html
+        .. _queue: docs/queue.html
+        
+        ======================
+        NEWS for lazr.smtptest
+        ======================
+        
+        1.3 (2011-06-07)
+        ================
+        
+        - Make the test server thread-safe with other code that starts an asyncore
+        loop.  Requires Python 2.6 or 2.7.
+        
+        - Be cleaner about stopping the server: before, it left sockets running
+        ans simply cleared out the socket map.  In an associated change, the EXIT
+        smtp command sends the reply first, and then shuts down the server, rather
+        than the other way around.
+        
+        1.2 (2009-07-07)
+        ================
+        
+        - [bug 393621] QueueServer.reset() was added to clear the message queue.  This
+        is invoked by sending an SMTP RSET command to the server, or through
+        Controller.reset().
+        
+        
+        1.1 (2009-06-29)
+        ================
+        
+        - [bug 391650] A non-public API was added to make QueueController more easily
+        subclassable.
+        
+        
+        1.0 (2009-06-22)
+        ================
+        
+        - Initial release
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python

src/lazr.smtptest.egg-info/SOURCES.txt

+COPYING.txt
+HACKING.txt
+README.txt
+ez_setup.py
+pylint.rc
+setup.py
+src/lazr/__init__.py
+src/lazr.smtptest.egg-info/PKG-INFO
+src/lazr.smtptest.egg-info/SOURCES.txt
+src/lazr.smtptest.egg-info/dependency_links.txt
+src/lazr.smtptest.egg-info/namespace_packages.txt
+src/lazr.smtptest.egg-info/not-zip-safe
+src/lazr.smtptest.egg-info/requires.txt
+src/lazr.smtptest.egg-info/top_level.txt
+src/lazr/smtptest/NEWS.txt
+src/lazr/smtptest/README.txt
+src/lazr/smtptest/__init__.py
+src/lazr/smtptest/controller.py
+src/lazr/smtptest/server.py
+src/lazr/smtptest/version.txt
+src/lazr/smtptest/docs/__init__.py
+src/lazr/smtptest/docs/queue.txt
+src/lazr/smtptest/docs/usage.txt
+src/lazr/smtptest/tests/__init__.py
+src/lazr/smtptest/tests/test_docs.py

src/lazr.smtptest.egg-info/dependency_links.txt

+

src/lazr.smtptest.egg-info/namespace_packages.txt

+lazr

src/lazr.smtptest.egg-info/not-zip-safe

+

src/lazr.smtptest.egg-info/requires.txt

+setuptools
+zope.interface
+
+[docs]
+Sphinx
+z3c.recipe.sphinxdoc

src/lazr.smtptest.egg-info/top_level.txt

+lazr

src/lazr/__init__.py

+# Copyright 2009 Canonical Ltd.  All rights reserved.
+#
+# This file is part of lazr.smtptest.
+#
+# lazr.smtptest is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+
+# this is a namespace package
+try:
+    import pkg_resources
+    pkg_resources.declare_namespace(__name__)
+except ImportError:
+    import pkgutil
+    __path__ = pkgutil.extend_path(__path__, __name__)

src/lazr/smtptest/NEWS.txt

+======================
+NEWS for lazr.smtptest
+======================
+
+1.3 (2011-06-07)
+================
+
+- Make the test server thread-safe with other code that starts an asyncore
+  loop.  Requires Python 2.6 or 2.7.
+
+- Be cleaner about stopping the server: before, it left sockets running
+  ans simply cleared out the socket map.  In an associated change, the EXIT
+  smtp command sends the reply first, and then shuts down the server, rather
+  than the other way around.
+
+1.2 (2009-07-07)
+================
+
+- [bug 393621] QueueServer.reset() was added to clear the message queue.  This
+  is invoked by sending an SMTP RSET command to the server, or through
+  Controller.reset().
+
+
+1.1 (2009-06-29)
+================
+
+- [bug 391650] A non-public API was added to make QueueController more easily
+  subclassable.
+
+
+1.0 (2009-06-22)
+================
+
+- Initial release

src/lazr/smtptest/README.txt

+..
+    This file is part of lazr.smtptest.
+
+    lazr.smtptest is free software: you can redistribute it and/or modify it
+    under the terms of the GNU Lesser General Public License as published by
+    the Free Software Foundation, version 3 of the License.
+
+    lazr.smtptest is distributed in the hope that it will be useful, but
+    WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+    License for more details.
+
+    You should have received a copy of the GNU Lesser General Public License
+    along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+
+LAZR smtptest
+*************
+
+This is LAZR smtptest, a framework for testing SMTP-based applications and
+libraries.  It provides a real, live SMTP server that you can send messages
+to, and from which you can read those test messages.  This can be used to
+ensure proper operation of your applications which send email.
+
+.. toctree::
+   :glob:
+
+   *
+   docs/*
+
+.. _Sphinx: http://sphinx.pocoo.org/
+.. _Table of contents: http://sphinx.pocoo.org/concepts.html#the-toc-tree
+
+Importable
+==========
+
+The lazr.smtptest package is importable, and has a version number.
+
+    >>> import lazr.smtptest
+    >>> print 'VERSION:', lazr.smtptest.__version__
+    VERSION: ...
+
+More information
+================
+
+For more general usage information, see usage_.  A specific, common test
+regime can be found in queue_.
+
+.. _usage: docs/usage.html
+.. _queue: docs/queue.html

src/lazr/smtptest/__init__.py

+# Copyright 2009 Canonical Ltd.  All rights reserved.
+#
+# This file is part of lazr.smtptest
+#
+# lazr.smtptest is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+
+"The lazr.smtptest package."
+
+import pkg_resources
+__version__ = pkg_resources.resource_string(
+    "lazr.smtptest", "version.txt").strip()

src/lazr/smtptest/controller.py

+# Copyright 2009 Canonical Ltd.  All rights reserved.
+#
+# This file is part of lazr.smtptest
+#
+# lazr.smtptest is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+
+"""The SMTP test controller."""
+
+__metaclass__ = type
+__all__ = [
+    'Controller',
+    'QueueController',
+    ]
+
+
+import logging
+import smtplib
+import threading
+
+from Queue import Empty, Queue
+
+from lazr.smtptest.server import QueueServer
+
+
+# pylint: disable-msg=C0103
+log = logging.getLogger('lazr.smtptest')
+
+
+class Controller:
+    """The SMTP server controller."""
+
+    def __init__(self, server):
+        """The controller of the SMTP server.
+
+        :param server: The SMTP server to run.  The server must have `host`
+            and `port` attributes set.
+        :type server: `lazr.smtptest.server.Server` or subclass
+        """
+        self._server = server
+        self._thread = threading.Thread(target=server.start)
+        self._thread.daemon = True
+
+    def _connect(self):
+        """Connect to the SMTP server running in the thread.
+
+        :return: the connection to the SMTP server
+        :rtype: `smtplib.SMTP`
+        """
+        smtpd = smtplib.SMTP()
+        smtpd.connect(self._server.host, self._server.port)
+        return smtpd
+
+    def start(self):
+        """Start the SMTP server in a thread."""
+        log.info('starting the SMTP server thread')
+        self._thread.start()
+        # Wait until the server is actually responding to clients.
+        log.info('connecting to %s:%s', self._server.host, self._server.port)
+        smtpd = self._connect()
+        response = smtpd.helo('test.localhost')
+        log.info('Got HELO response: %s', response)
+        smtpd.quit()
+
+    def stop(self):
+        """Stop the smtp server thread."""
+        log.info('stopping the SMTP server thread')
+        smtpd = self._connect()
+        smtpd.docmd('EXIT')
+        # Wait for the thread to exit.
+        self._thread.join()
+        log.info('SMTP server stopped')
+
+    def reset(self):
+        """Sent a RSET to the server."""
+        log.info('resetting the SMTP server.')
+        smtpd = self._connect()
+        smtpd.docmd('RSET')
+
+
+class QueueController(Controller):
+    """An SMTP server controller that coordinates through a queue."""
+
+    def __init__(self, host, port):
+        """The controller which coordinates via a Queue.
+
+        :param host: The host name to listen on.
+        :type host: str
+        :param port: The port to listen on.
+        :type port: int
+        """
+        self.queue = Queue()
+        self.server = None
+        self._make_server(host, port)
+        super(QueueController, self).__init__(self.server)
+
+    def _make_server(self, host, port):
+        """Create the server instance, storing it on `self.server`.
+
+        This interface is non-public; it is for subclasses that need to
+        override server instantiation.
+
+        :param host: The host name to listen on.
+        :type host: str
+        :param port: The port to listen on.
+        :type port: int
+        """
+        self.server = QueueServer(host, port, self.queue)
+
+    def __iter__(self):
+        """Iterate over all the messages in the queue."""
+        while True:
+            try:
+                yield self.queue.get_nowait()
+            except Empty:
+                raise StopIteration

src/lazr/smtptest/docs/__init__.py

+# Copyright 2009 Canonical Ltd.  All rights reserved.
+#
+# This file is part of lazr.smtptest
+#
+# lazr.smtptest is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+"""Executable documentation about lazr.smtptest."""

src/lazr/smtptest/docs/queue.txt

+============
+Queue server
+============
+
+As shown in the general usage_ document, you can create subclasses of the
+Server class to define how you want to handle received messages.  A very
+common pattern is to use a Queue to share messages between the controlling
+thread and the server thread.  These are nice because, unlike a mailbox based
+server, no sorting is required to get the messages out of the server in the
+same order they are sent.
+
+.. _usage: usage.html
+
+First, you need a queue.
+
+    >>> from Queue import Queue
+    >>> queue = Queue()
+
+Then you need a server.
+
+    >>> from lazr.smtptest.server import QueueServer
+    >>> server = QueueServer('localhost', 9025, queue)
+
+Finally, you need a controller.
+
+    >>> from lazr.smtptest.controller import Controller
+    >>> controller = Controller(server)
+
+Now, start the SMTP server.
+
+    >>> controller.start()
+
+Send the server a bunch of messages.
+
+    >>> import smtplib
+    >>> smtpd = smtplib.SMTP()
+    >>> smtpd.connect('localhost', 9025)
+    (220, '... Python SMTP proxy version ...')
+
+    >>> smtpd.sendmail('iperson@example.com', ['jperson@example.com'], """\
+    ... From: Irie Person <iperson@example.com>
+    ... To: Jeff Person <jperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <elephant>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+    >>> smtpd.sendmail('kperson@example.com', ['lperson@example.com'], """\
+    ... From: Kari Person <kperson@example.com>
+    ... To: Liam Person <lperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <falcon>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+    >>> smtpd.sendmail('mperson@example.com', ['nperson@example.com'], """\
+    ... From: Mary Person <mperson@example.com>
+    ... To: Neal Person <nperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <goat>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+All of these messages are available in the queue.
+
+    >>> from Queue import Empty
+    >>> while True:
+    ...     try:
+    ...         message = queue.get_nowait()
+    ...     except Empty:
+    ...         break
+    ...     print message['message-id']
+    <elephant>
+    <falcon>
+    <goat>
+
+We're done with the controller.
+
+    >>> controller.stop()
+
+
+Queue controller
+================
+
+An even more convenient interface, is to use the QueueController.
+
+    >>> from lazr.smtptest.controller import QueueController
+    >>> controller = QueueController('localhost', 9025)
+    >>> controller.start()
+
+    >>> smtpd = smtplib.SMTP()
+    >>> smtpd.connect('localhost', 9025)
+    (220, '... Python SMTP proxy version ...')
+
+We now have an SMTP server that we can send some messages to.
+
+    >>> smtpd.sendmail('operson@example.com', ['pperson@example.com'], """\
+    ... From: Onua Person <operson@example.com>
+    ... To: Paul Person <pperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <horse>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+    >>> smtpd.sendmail('qperson@example.com', ['rperson@example.com'], """\
+    ... From: Quay Person <qperson@example.com>
+    ... To: Raul Person <rperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <iguana>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+    >>> smtpd.sendmail('sperson@example.com', ['tperson@example.com'], """\
+    ... From: Sean Person <sperson@example.com>
+    ... To: Thom Person <tperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <jackel>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+And we can dump out all the messages from the controller.
+
+    >>> for message in controller:
+    ...     print message['message-id']
+    <horse>
+    <iguana>
+    <jackel>
+
+We can send more messages and view them too.
+
+    >>> smtpd.sendmail('uperson@example.com', ['vperson@example.com'], """\
+    ... From: Umma Person <uperson@example.com>
+    ... To: Vern Person <vperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <kangaroo>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+    >>> for message in controller:
+    ...     print message['message-id']
+    <kangaroo>
+
+
+Resetting
+=========
+
+Queue servers support a RSET (reset) method, which empties the queue.
+
+    >>> smtpd.sendmail('wperson@example.com', ['xperson@example.com'], """\
+    ... From: Wynn Person <wperson@example.com>
+    ... To: Xerx Person <xperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <llama>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+    >>> smtpd.sendmail('yperson@example.com', ['zperson@example.com'], """\
+    ... From: Yikes Person <yperson@example.com>
+    ... To: Zell Person <zperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <moose>
+    ...
+    ... This is a test.
+    ... """)
+    {}
+
+    >>> controller.queue.qsize()
+    2
+    >>> controller.reset()
+    >>> controller.queue.qsize()
+    0
+
+
+Clean up
+========
+
+We're done with this controller.
+
+    >>> controller.stop()

src/lazr/smtptest/docs/usage.txt

+=============================
+Using the SMTP test framework
+=============================
+
+The SMTP test framework provides a real SMTP server listening on a port and
+speaking the SMTP protocol.  It runs the server in a separate thread so that
+the main thread can send it messages, and verify that it received the
+messages.
+
+To use, start by defining a subclass of the Server class.
+
+    >>> from lazr.smtptest.server import Server
+
+Override the handle_message() method to do whatever you want to do with the
+message.  For example, you might want to pass the message between the threads
+via a Queue.
+
+    >>> from Queue import Queue
+    >>> queue = Queue()
+
+    >>> class MyServer(Server):
+    ...     def handle_message(self, message):
+    ...         queue.put(message)
+
+Start a controller, with our new server.
+
+    >>> from lazr.smtptest.controller import Controller
+    >>> controller = Controller(MyServer('localhost', 9025))
+    >>> controller.start()
+
+Connect to the server...
+
+    >>> from smtplib import SMTP
+    >>> smtpd = SMTP()
+    >>> smtpd.connect('localhost', 9025)
+    (220, '... Python SMTP proxy version ...')
+
+...and send it a message.
+
+    >>> smtpd.sendmail('aperson@example.com', ['bperson@example.com'], """\
+    ... From: Abby Person <aperson@example.com>
+    ... To: Bart Person <bperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <aardvark>
+    ...
+    ... Hi Bart, this is a test.
+    ... """)
+    {}
+
+Now print the message that the server has just received.
+
+    >>> message = queue.get()
+    >>> print message.as_string()
+    From: Abby Person <aperson@example.com>
+    To: Bart Person <bperson@example.com>
+    Subject: A test
+    Message-ID: <aardvark>
+    X-Peer: 127.0.0.1:...
+    X-MailFrom: aperson@example.com
+    X-RcptTo: bperson@example.com
+    <BLANKLINE>
+    Hi Bart, this is a test.
+
+When you're done with the server, stop it via the controller.
+
+    >>> controller.stop()
+
+The server is guaranteed to be stopped.
+
+    >>> # The traceback text is different between Python 2.5 and 2.6.
+    >>> import socket
+    >>> try:
+    ...     smtpd.connect('localhost', 9025)
+    ... except socket.error, error:
+    ...     errno, message = error
+    ...     print message
+    Connection refused
+
+
+Resetting
+=========
+
+The SMTP server can be reset, which defines application specific behavior.
+For example, a server which stores messages in an mbox can be sent the RSET
+command to clear the mbox.
+
+This server stores messages in Maildir.
+
+    >>> import os
+    >>> import mailbox
+    >>> import tempfile
+    >>> tempdir = tempfile.mkdtemp()
+    >>> mailbox_dir = os.path.join(tempdir, 'maildir')
+
+    >>> class MyServer(Server):
+    ...     def __init__(self, host, port):
+    ...         Server.__init__(self, host, port)
+    ...         self._maildir = mailbox.Maildir(mailbox_dir)
+    ...
+    ...     def handle_message(self, message):
+    ...         self._maildir.add(message)
+    ...
+    ...     def reset(self):
+    ...         self._maildir.clear()
+
+    >>> controller = Controller(MyServer('localhost', 9025))
+    >>> controller.start()
+
+Now we can send a couple of messages to the server.
+
+    >>> smtpd = SMTP()
+    >>> smtpd.connect('localhost', 9025)
+    (220, '... Python SMTP proxy version ...')
+
+    >>> smtpd.sendmail('cperson@example.com', ['dperson@example.com'], """\
+    ... From: Cris Person <cperson@example.com>
+    ... To: Dave Person <dperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <badger>
+    ...
+    ... Hi Dave, this is a test.
+    ... """)
+    {}
+
+    >>> smtpd.sendmail('eperson@example.com', ['fperson@example.com'], """\
+    ... From: Elly Person <eperson@example.com>
+    ... To: Fred Person <fperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <cougar>
+    ...
+    ... Hi Fred, this is a test.
+    ... """)
+    {}
+
+    >>> smtpd.sendmail('gperson@example.com', ['hperson@example.com'], """\
+    ... From: Gwen Person <gperson@example.com>
+    ... To: Herb Person <hperson@example.com>
+    ... Subject: A test
+    ... Message-ID: <dingo>
+    ...
+    ... Hi Herb, this is a test.
+    ... """)
+    {}
+
+All of these messages are in the mailbox.
+
+    >>> for message_id in sorted(message['message-id']
+    ...                          for message in mailbox.Maildir(mailbox_dir)):
+    ...     print message_id
+    <badger>
+    <cougar>
+    <dingo>
+
+Reading the messages does not affect their appearance in the mailbox.
+
+    >>> for message_id in sorted(message['message-id']
+    ...                          for message in mailbox.Maildir(mailbox_dir)):
+    ...     print message_id
+    <badger>
+    <cougar>
+    <dingo>
+
+But if we reset the server, the messages disappear.
+
+    >>> controller.reset()
+    >>> sum(1 for message in mailbox.Maildir(mailbox_dir))
+    0
+
+
+Clean up
+========
+
+    >>> # In Python 2.6, this returns a 221, but not in Python 2.5.
+    >>> status = smtpd.quit()
+    >>> controller.stop()
+    >>> import shutil
+    >>> shutil.rmtree(tempdir)

src/lazr/smtptest/server.py

+# Copyright 2009 Canonical Ltd.  All rights reserved.
+#
+# This file is part of lazr.smtptest
+#
+# lazr.smtptest is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+
+"""The SMTP test server."""
+
+__metaclass__ = type
+__all__ = [
+    'QueueServer',
+    'Server',
+    ]
+
+
+import smtpd
+import socket
+import logging
+import asyncore
+
+from Queue import Empty
+from email import message_from_string
+
+
+COMMASPACE = ', '
+# pylint: disable-msg=C0103
+log = logging.getLogger('lazr.smtptest')
+
+
+class Channel(smtpd.SMTPChannel):
+    """A channel that can reset the mailbox."""
+
+    def __init__(self, server, connection, address):
+        self._server = server
+        smtpd.SMTPChannel.__init__(self, server, connection, address)
+
+    # pylint: disable-msg=W0613
+    def smtp_EXIT(self, argument):
+        """EXIT - a new SMTP command to cleanly stop the server."""
+        self.push('250 Ok')
+        self._server.stop()
+
+    def smtp_RSET(self, argument):
+        """RSET - hijack this to reset the server instance."""
+        self._server.reset()
+        smtpd.SMTPChannel.smtp_RSET(self, argument)
+
+    def send(self, data):
+        """See `SMTPChannel.send()`."""
+        # Call the base class's send method, but catch all socket errors since
+        # asynchat/asyncore doesn't do it.
+        try:
+            return smtpd.SMTPChannel.send(self, data)
+        # pylint: disable-msg=W0704
+        except socket.error:
+            # Nothing here can affect the outcome.
+            pass
+
+    def add_channel(self, map=None):
+        # This has an old style base class.  We want to make _map equal to
+        # our server's socket_map, to make this thread safe with other
+        # asyncores.  We do it here, overriding the behavior of the asyncore
+        # __init__ as soon as we can, without having to copy over code from
+        # the rest of smtpd.SMTPChannel.__init__ and modify it.
+        if self._map is not self._server.socket_map:
+            self._map = self._server.socket_map
+        smtpd.SMTPChannel.add_channel(self, map)
+
+
+class Server(smtpd.SMTPServer):
+    """An SMTP server."""
+
+    def __init__(self, host, port):
+        """Create an SMTP server.
+
+        :param host: The host name to listen on.
+        :type host: str
+        :param port: The port to listen on.
+        :type port: int
+        """
+        self.host = host
+        self.port = port
+        self.socket_map = {}
+        smtpd.SMTPServer.__init__(self, (host, port), None)
+        self.set_reuse_addr()
+        log.info('[SMTPServer] listening: %s:%s', host, port)
+
+    def add_channel(self, map=None):
+        # This has an old style base class.  We want to make _map equal to
+        # socket_map, to make this thread safe with other asyncores.  We do
+        # it here, overriding the behavior of the SMTPServer __init__ as
+        # soon as we can, without having to copy over code from the rest of
+        # smtpd.SMTPServer.__init__ and modify it.
+        if self._map is not self.socket_map:
+            self._map = self.socket_map
+        smtpd.SMTPServer.add_channel(self, map)
+
+    def handle_accept(self):
+        """Handle connections by creating our own Channel object."""
+        connection, address = self.accept()
+        log.info('[SMTPServer] accepted: %s', address)
+        Channel(self, connection, address)
+
+    def process_message(self, peer, mailfrom, rcpttos, data):
+        """Process a received message."""
+        log.info('[SMTPServer] processing: %s, %s, %s, size=%s',
+                 peer, mailfrom, rcpttos, len(data))
+        message = message_from_string(data)
+        message['X-Peer'] = '%s:%s' % (peer[0], peer[1])
+        message['X-MailFrom'] = mailfrom
+        message['X-RcptTo'] = COMMASPACE.join(rcpttos)
+        self.handle_message(message)
+        log.info('[SMTPServer] processed message: %s',
+                 message.get('message-id', 'n/a'))
+
+    # pylint: disable-msg=R0201
+    def start(self):
+        """Start the asyncore loop."""
+        log.info('[SMTPServer] starting asyncore loop')
+        asyncore.loop(map=self.socket_map)
+
+    def stop(self):
+        """Stop the asyncore loop."""
+        asyncore.close_all(map=self.socket_map)
+        self.close()
+
+    # pylint: disable-msg=R0201
+    def reset(self):
+        """Do whatever you need to do on a reset."""
+        log.info('[SMTPServer] reset')
+
+    def handle_message(self, message):
+        """Handle the received message.
+
+        :param message: the completed, parsed received email message.
+        :type message: `email.message.Message`
+        """
+        pass
+
+
+class QueueServer(Server):
+    """A server which puts messages in a queue."""
+
+    def __init__(self, host, port, queue):
+        """Create an SMTP server which puts messages in a queue.
+
+        :param host: The host name to listen on.
+        :type host: str
+        :param port: The port to listen on.
+        :type port: int
+        :param queue: The queue to put messages in.
+        :type queue: object with a .put() method taking a single message
+            object.
+        """
+        Server.__init__(self, host, port)
+        self.queue = queue
+
+    def handle_message(self, message):
+        """See `Server.handle_message()`."""
+        self.queue.put(message)
+
+    def reset(self):
+        """See `Server.reset()`."""
+        while True:
+            try:
+                self.queue.get_nowait()
+            except Empty:
+                break

src/lazr/smtptest/tests/__init__.py

+# Copyright 2009 Canonical Ltd.  All rights reserved.
+#
+# This file is part of lazr.smtptest
+#
+# lazr.smtptest is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+"The lazr.smtptest tests."

src/lazr/smtptest/tests/test_docs.py

+# Copyright 2009 Canonical Ltd.  All rights reserved.
+#
+# This file is part of lazr.smtptest
+#
+# lazr.smtptest is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# lazr.smtptest is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+# License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with lazr.smtptest.  If not, see <http://www.gnu.org/licenses/>.
+"Test harness for doctests."
+
+# pylint: disable-msg=E0611,W0142
+
+__metaclass__ = type
+__all__ = [
+    'additional_tests',
+    ]
+
+import atexit
+import doctest
+import os
+# pylint: disable-msg=F0401
+from pkg_resources import (
+    resource_filename, resource_exists, resource_listdir, cleanup_resources)
+import unittest
+
+DOCTEST_FLAGS = (
+    doctest.ELLIPSIS |
+    doctest.NORMALIZE_WHITESPACE |
+    doctest.REPORT_NDIFF)
+
+
+def additional_tests():
+    "Run the doc tests (README.txt and docs/*, if any exist)"
+    doctest_files = [
+        os.path.abspath(resource_filename('lazr.smtptest', 'README.txt'))]
+    if resource_exists('lazr.smtptest', 'docs'):
+        for name in resource_listdir('lazr.smtptest', 'docs'):
+            if name.endswith('.txt'):
+                doctest_files.append(
+                    os.path.abspath(
+                        resource_filename('lazr.smtptest', 'docs/%s' % name)))
+    kwargs = dict(module_relative=False, optionflags=DOCTEST_FLAGS)
+    atexit.register(cleanup_resources)
+    return unittest.TestSuite((
+        doctest.DocFileSuite(*doctest_files, **kwargs)))

src/lazr/smtptest/version.txt

+1.3
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.