[Checkins] SVN: zope.session/trunk/ Added Python 3.3 support.

Stephen Richter cvs-admin at zope.org
Thu Feb 21 16:08:18 UTC 2013


Log message for revision 129574:
  Added Python 3.3 support.
  

Changed:
  _U  zope.session/trunk/
  UU  zope.session/trunk/CHANGES.txt
  A   zope.session/trunk/MANIFEST.in
  A   zope.session/trunk/ZODB-4.0.0dev.tar.gz
  U   zope.session/trunk/bootstrap.py
  U   zope.session/trunk/buildout.cfg
  U   zope.session/trunk/setup.py
  U   zope.session/trunk/src/zope/session/api.txt
  U   zope.session/trunk/src/zope/session/http.py
  U   zope.session/trunk/src/zope/session/session.py
  U   zope.session/trunk/src/zope/session/tests.py
  A   zope.session/trunk/tox.ini

-=-

Property changes on: zope.session/trunk
___________________________________________________________________
Modified: svn:ignore
   - bin
build
dist
lib
develop-eggs
eggs
parts
.installed.cfg

   + .coverage
.tox
bin
build
dist
lib
develop-eggs
eggs
parts
.installed.cfg


Modified: zope.session/trunk/CHANGES.txt
===================================================================
--- zope.session/trunk/CHANGES.txt	2013-02-21 16:02:40 UTC (rev 129573)
+++ zope.session/trunk/CHANGES.txt	2013-02-21 16:08:18 UTC (rev 129574)
@@ -1,9 +1,11 @@
 CHANGES
 =======
 
-4.0.0 (unreleased)
-------------------
+4.0.0a1 (unreleased)
+--------------------
 
+- Added support for Python 3.3
+
 - Replaced deprecated ``zope.component.adapts`` usage with equivalent
   ``zope.component.adapter`` decorator.
 
@@ -18,7 +20,7 @@
 
 - LP #824355:  enable support for HttpOnly cookies.
 
-- Fix a bug in zope.session.session.Session that would trigger an
+- Fix a bug in ``zope.session.session.Session`` that would trigger an
   infinite loop if either iteration or a containment test were
   attempted on an instance.
 


Property changes on: zope.session/trunk/CHANGES.txt
___________________________________________________________________
Deleted: svn:eol-style
   - native

Added: zope.session/trunk/MANIFEST.in
===================================================================
--- zope.session/trunk/MANIFEST.in	                        (rev 0)
+++ zope.session/trunk/MANIFEST.in	2013-02-21 16:08:18 UTC (rev 129574)
@@ -0,0 +1,9 @@
+include *.rst
+include *.txt
+include *.py
+include buildout.cfg
+include tox.ini
+
+recursive-include src *
+
+global-exclude *.pyc

Added: zope.session/trunk/ZODB-4.0.0dev.tar.gz
===================================================================
(Binary files differ)


Property changes on: zope.session/trunk/ZODB-4.0.0dev.tar.gz
___________________________________________________________________
Added: svn:mime-type
   + application/octet-stream

Modified: zope.session/trunk/bootstrap.py
===================================================================
--- zope.session/trunk/bootstrap.py	2013-02-21 16:02:40 UTC (rev 129573)
+++ zope.session/trunk/bootstrap.py	2013-02-21 16:08:18 UTC (rev 129574)
@@ -18,33 +18,148 @@
 use the -c option to specify an alternate configuration file.
 """
 
-import os, shutil, sys, tempfile, urllib2
+import os, shutil, sys, tempfile
+from optparse import OptionParser
 
 tmpeggs = tempfile.mkdtemp()
 
-ez = {}
-exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
-                     ).read() in ez
-ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+usage = '''\
+[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
 
-import pkg_resources
+Bootstraps a buildout-based project.
 
-cmd = 'from setuptools.command.easy_install import main; main()'
-if sys.platform == 'win32':
-    cmd = '"%s"' % cmd # work around spawn lamosity on windows
+Simply run this script in a directory containing a buildout.cfg, using the
+Python that you want bin/buildout to use.
 
-ws = pkg_resources.working_set
-assert os.spawnle(
-    os.P_WAIT, sys.executable, sys.executable,
-    '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
-    dict(os.environ,
-         PYTHONPATH=
-         ws.find(pkg_resources.Requirement.parse('setuptools')).location
-         ),
-    ) == 0
+Note that by using --setup-source and --download-base to point to
+local resources, you can keep this script from going over the network.
+'''
 
+parser = OptionParser(usage=usage)
+parser.add_option("-v", "--version", help="use a specific zc.buildout version")
+
+parser.add_option("-t", "--accept-buildout-test-releases",
+                  dest='accept_buildout_test_releases',
+                  action="store_true", default=False,
+                  help=("Normally, if you do not specify a --version, the "
+                        "bootstrap script and buildout gets the newest "
+                        "*final* versions of zc.buildout and its recipes and "
+                        "extensions for you.  If you use this flag, "
+                        "bootstrap and buildout will get the newest releases "
+                        "even if they are alphas or betas."))
+parser.add_option("-c", "--config-file",
+                   help=("Specify the path to the buildout configuration "
+                         "file to be used."))
+parser.add_option("-f", "--find-links",
+                   help=("Specify a URL to search for buildout releases"))
+
+
+options, args = parser.parse_args()
+
+######################################################################
+# load/install distribute
+
+to_reload = False
+try:
+    import pkg_resources, setuptools
+    if not hasattr(pkg_resources, '_distribute'):
+        to_reload = True
+        raise ImportError
+except ImportError:
+    ez = {}
+
+    try:
+        from urllib.request import urlopen
+    except ImportError:
+        from urllib2 import urlopen
+
+    exec(urlopen('http://python-distribute.org/distribute_setup.py').read(), ez)
+    setup_args = dict(to_dir=tmpeggs, download_delay=0, no_fake=True)
+    ez['use_setuptools'](**setup_args)
+
+    if to_reload:
+        reload(pkg_resources)
+    import pkg_resources
+    # This does not (always?) update the default working set.  We will
+    # do it.
+    for path in sys.path:
+        if path not in pkg_resources.working_set.entries:
+            pkg_resources.working_set.add_entry(path)
+
+######################################################################
+# Install buildout
+
+ws  = pkg_resources.working_set
+
+cmd = [sys.executable, '-c',
+       'from setuptools.command.easy_install import main; main()',
+       '-mZqNxd', tmpeggs]
+
+find_links = os.environ.get(
+    'bootstrap-testing-find-links',
+    options.find_links or
+    ('http://downloads.buildout.org/'
+     if options.accept_buildout_test_releases else None)
+    )
+if find_links:
+    cmd.extend(['-f', find_links])
+
+distribute_path = ws.find(
+    pkg_resources.Requirement.parse('distribute')).location
+
+requirement = 'zc.buildout'
+version = options.version
+if version is None and not options.accept_buildout_test_releases:
+    # Figure out the most recent final version of zc.buildout.
+    import setuptools.package_index
+    _final_parts = '*final-', '*final'
+    def _final_version(parsed_version):
+        for part in parsed_version:
+            if (part[:1] == '*') and (part not in _final_parts):
+                return False
+        return True
+    index = setuptools.package_index.PackageIndex(
+        search_path=[distribute_path])
+    if find_links:
+        index.add_find_links((find_links,))
+    req = pkg_resources.Requirement.parse(requirement)
+    if index.obtain(req) is not None:
+        best = []
+        bestv = None
+        for dist in index[req.project_name]:
+            distv = dist.parsed_version
+            if _final_version(distv):
+                if bestv is None or distv > bestv:
+                    best = [dist]
+                    bestv = distv
+                elif distv == bestv:
+                    best.append(dist)
+        if best:
+            best.sort()
+            version = best[-1].version
+if version:
+    requirement = '=='.join((requirement, version))
+cmd.append(requirement)
+
+import subprocess
+if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=distribute_path)) != 0:
+    raise Exception(
+        "Failed to execute command:\n%s",
+        repr(cmd)[1:-1])
+
+######################################################################
+# Import and run buildout
+
 ws.add_entry(tmpeggs)
-ws.require('zc.buildout')
+ws.require(requirement)
 import zc.buildout.buildout
-zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+
+if not [a for a in args if '=' not in a]:
+    args.append('bootstrap')
+
+# if -c was provided, we push it back into args for buildout' main function
+if options.config_file is not None:
+    args[0:0] = ['-c', options.config_file]
+
+zc.buildout.buildout.main(args)
 shutil.rmtree(tmpeggs)

Modified: zope.session/trunk/buildout.cfg
===================================================================
--- zope.session/trunk/buildout.cfg	2013-02-21 16:02:40 UTC (rev 129573)
+++ zope.session/trunk/buildout.cfg	2013-02-21 16:08:18 UTC (rev 129574)
@@ -1,6 +1,9 @@
 [buildout]
 develop = .
+find-links =
+    ${buildout:directory}/ZODB-4.0.0dev.tar.gz
 parts = test py
+versions = versions
 
 [test]
 recipe = zc.recipe.testrunner
@@ -10,3 +13,6 @@
 recipe = zc.recipe.egg
 eggs = zope.session
 interpreter = py
+
+[versions]
+ZODB = 4.0.0dev

Modified: zope.session/trunk/setup.py
===================================================================
--- zope.session/trunk/setup.py	2013-02-21 16:02:40 UTC (rev 129573)
+++ zope.session/trunk/setup.py	2013-02-21 16:08:18 UTC (rev 129574)
@@ -18,16 +18,14 @@
 ##############################################################################
 """Setup for zope.session package
 """
-
 import os
-
 from setuptools import setup, find_packages
 
 def read(*rnames):
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
 
 setup(name='zope.session',
-    version='4.0.0dev',
+    version='4.0.0a1.dev',
     author='Zope Foundation and Contributors',
     author_email='zope-dev at zope.org',
     description='Client identification and sessions for Zope',
@@ -51,6 +49,9 @@
         'Programming Language :: Python :: 2',
         'Programming Language :: Python :: 2.6',
         'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.3',
+        'Programming Language :: Python :: Implementation :: CPython',
         'Natural Language :: English',
         'Operating System :: OS Independent',
         'Topic :: Internet :: WWW/HTTP',
@@ -61,18 +62,19 @@
     namespace_packages=['zope',],
     install_requires=[
         'setuptools',
-        'ZODB3',
+        'ZODB',
         'zope.component',
         'zope.i18nmessageid >= 3.4.2',
         'zope.interface',
         'zope.location',
-        'zope.publisher',
+        'zope.publisher >= 4.0.0a1',
         'zope.minmax',
         ],
     extras_require=dict(
-          test=[
-              'zope.testing',
-              ]),
+          test=['zope.testing',],
+          ),
+    tests_require = ['zope.testing',],
+    test_suite = 'zope.session.tests.test_suite',
     include_package_data = True,
     zip_safe = False,
     )

Modified: zope.session/trunk/src/zope/session/api.txt
===================================================================
--- zope.session/trunk/src/zope/session/api.txt	2013-02-21 16:02:40 UTC (rev 129573)
+++ zope.session/trunk/src/zope/session/api.txt	2013-02-21 16:08:18 UTC (rev 129574)
@@ -99,7 +99,7 @@
     >>> transaction.commit()
     Traceback (most recent call last):
         [...]
-    TypeError: can't pickle file objects
+    TypeError: ...
 
 Clean up:
 

Modified: zope.session/trunk/src/zope/session/http.py
===================================================================
--- zope.session/trunk/src/zope/session/http.py	2013-02-21 16:02:40 UTC (rev 129573)
+++ zope.session/trunk/src/zope/session/http.py	2013-02-21 16:08:18 UTC (rev 129574)
@@ -17,18 +17,10 @@
 import logging
 import random
 import re
-import string
-import sys
 import time
-from cStringIO import StringIO
+from hashlib import sha1
+from email.utils import formatdate
 
-if sys.version_info[:2] >= (2, 5):
-    from hashlib import sha1
-    from email.utils import formatdate
-else:
-    import sha as sha1
-    from email.Utils import formatdate
-
 import zope.location
 from persistent import Persistent
 from zope import schema, component
@@ -36,19 +28,19 @@
 from zope.publisher.interfaces.http import IHTTPRequest
 from zope.publisher.interfaces.http import IHTTPApplicationRequest
 from zope.i18nmessageid import ZopeMessageFactory as _
-from zope.session.interfaces import IClientIdManager
 from zope.schema.fieldproperty import FieldProperty
 
-__docformat__ = 'restructuredtext'
+from zope.session.interfaces import IClientIdManager
+from zope.session.session import digestEncode
 
-cookieSafeTrans = string.maketrans("+/", "-.")
+try:
+    from cStringIO import StringIO
+except ImportError:
+    # Py3: Now StringIO location.
+    from io import StringIO
 
 logger = logging.getLogger()
 
-def digestEncode(s):
-    """Encode SHA digest for cookie."""
-    return s.encode("base64")[:-2].translate(cookieSafeTrans)
-
 class MissingClientIdException(Exception):
     """No ClientId found in Request"""
 
@@ -186,9 +178,7 @@
         if namespace is None:
             namespace = "zope3_cs_%x" % (int(time.time()) - 1000000000)
         if secret is None:
-            secret = '%.20f' % random.random()
-        else:
-            secret = str(secret)
+            secret = u'%.20f' % random.random()
         self.namespace = namespace
         self.secret = secret
 
@@ -220,7 +210,7 @@
         an IClientId. This is because this method is used to implement
         the IClientId Adapter.
 
-          >>> type(id) == type('')
+          >>> type(id) == type(u'')
           True
 
         We don't set the client id unless we need to, so, for example,
@@ -250,7 +240,7 @@
           ...
           MissingClientIdException
 
-          >>> print request.response.getCookie(bim.namespace)
+          >>> print(request.response.getCookie(bim.namespace))
           None
 
           >>> request = HTTPRequest(StringIO(''), {'REQUEST_METHOD': 'POST'})
@@ -275,7 +265,7 @@
           ...
           MissingClientIdException
 
-          >>> print request.response.getCookie(bim.namespace)
+          >>> print(request.response.getCookie(bim.namespace))
           None
 
         """
@@ -307,16 +297,12 @@
 
         """
         data = "%.20f%.20f%.20f" % (random.random(), time.time(), time.clock())
-        # BBB code for Python 2.4, inspired by the fallback in hmac
-        if hasattr(sha1, '__call__'):
-            digest = sha1(data).digest()
-        else:
-            digest = sha1.new(data).digest()
+        digest = sha1(data.encode()).digest()
         s = digestEncode(digest)
         # we store a HMAC of the random value together with it, which makes
         # our session ids unforgeable.
-        mac = hmac.new(self.secret, s, digestmod=sha1).digest()
-        return s + digestEncode(mac)
+        mac = hmac.new(self.secret.encode(), s, digestmod=sha1).digest()
+        return (s + digestEncode(mac)).decode()
 
     def getRequestId(self, request):
         """Return the browser id encoded in request as a string
@@ -361,7 +347,7 @@
         Test a corner case where Python 2.6 hmac module does not allow
         unicode as input:
 
-          >>> id_uni = unicode(bim.generateUniqueId())
+          >>> id_uni = bim.generateUniqueId()
           >>> bim.setRequestId(request, id_uni)
           >>> bim.getRequestId(request) == id_uni
           True
@@ -399,8 +385,8 @@
             # call encode() on value s a workaround a bug where the hmac
             # module only accepts str() types in Python 2.6
             if (digestEncode(hmac.new(
-                    self.secret, s.encode(), digestmod=sha1
-                ).digest()) != mac):
+                    self.secret.encode(), s.encode(), digestmod=sha1
+                ).digest()).decode() != mac):
                 return None
             else:
                 return sid
@@ -430,7 +416,7 @@
 
         By default, session cookies don't expire:
 
-            >>> cookie.has_key('expires')
+            >>> 'expires' in cookie
             False
 
         Expiry time of 0 means never (well - close enough)
@@ -448,8 +434,8 @@
             >>> request = HTTPRequest(StringIO(''), {}, None)
             >>> bid = bim.getClientId(request)
             >>> cookie = request.response.getCookie(bim.namespace)
-            >>> import rfc822
-            >>> expires = time.mktime(rfc822.parsedate(cookie['expires']))
+            >>> import email.utils
+            >>> expires = time.mktime(email.utils.parsedate(cookie['expires']))
             >>> expires > time.mktime(time.gmtime()) + 55*60
             True
 
@@ -470,15 +456,17 @@
           >>> request = HTTPRequest(StringIO(''), {}, None)
           >>> bim.secure = True
           >>> bim.setRequestId(request, '1234')
-          >>> print request.response.getCookie(bim.namespace)
+          >>> from pprint import pprint
+          >>> pprint(request.response.getCookie(bim.namespace))
           {'path': '/', 'secure': True, 'value': '1234'}
 
         If the domain is specified, it will be set as a cookie attribute.
 
           >>> bim.domain = u'.example.org'
           >>> bim.setRequestId(request, '1234')
-          >>> print request.response.getCookie(bim.namespace)
-          {'path': '/', 'domain': u'.example.org', 'secure': True, 'value': '1234'}
+          >>> pprint(request.response.getCookie(bim.namespace))
+          {'domain': '.example.org', 'path': '/', 'secure': True,
+           'value': '1234'}
 
         When the cookie is set, cache headers are added to the
         response to try to prevent the cookie header from being cached:
@@ -497,8 +485,9 @@
           >>> bim.secure = False
           >>> bim.httpOnly = True
           >>> bim.setRequestId(request, '1234')
-          >>> print request.response.getCookie(bim.namespace)
-          {'path': '/', 'domain': u'.example.org', 'value': '1234', 'httponly': True}
+          >>> pprint(request.response.getCookie(bim.namespace))
+          {'domain': '.example.org', 'httponly': True, 'path': '/',
+           'value': '1234'}
 
         """
         # TODO: Currently, the path is the ApplicationURL. This is reasonable,

Modified: zope.session/trunk/src/zope/session/session.py
===================================================================
--- zope.session/trunk/src/zope/session/session.py	2013-02-21 16:02:40 UTC (rev 129573)
+++ zope.session/trunk/src/zope/session/session.py	2013-02-21 16:08:18 UTC (rev 129574)
@@ -13,9 +13,8 @@
 ##############################################################################
 """Session implementation
 """
-from cStringIO import StringIO
-import time, string, random, thread
-from UserDict import IterableUserDict
+import random
+import time
 from heapq import heapify, heappop
 
 import ZODB
@@ -34,21 +33,54 @@
         IClientIdManager, IClientId, ISession, ISessionDataContainer, \
         ISessionPkgData, ISessionData
 
-__docformat__ = 'restructuredtext'
+try:
+    from cStringIO import StringIO
+except ImportError:
+    # Py3: Now StringIO location.
+    from io import StringIO
 
-cookieSafeTrans = string.maketrans("+/", "-.")
+try:
+    from UserDict import IterableUserDict as UserDict
+except ImportError:
+    # Py3: Now StringIO location.
+    from collections import UserDict
 
+try:
+    from base64 import encodebytes
+except ImportError:
+    # Py3: Python 2 only has encodestring
+    from base64 import encodestring as encodebytes
+
+try:
+    from threading import get_ident
+except ImportError:
+    # Py3: Python 2 has a different location.
+    from thread import get_ident
+
+try:
+    unicode
+except NameError:
+    # Py3: Define unicode
+    unicode = str
+
+try:
+    transtable = bytes.maketrans(b'+/', b'-.')
+except AttributeError:
+    # Py3: Python 2 has a differnt location for maketrans.
+    import string
+    transtable = string.maketrans(b'+/', b'-.')
+
 def digestEncode(s):
     """Encode SHA digest for cookie."""
-    return s.encode("base64")[:-2].translate(cookieSafeTrans)
+    return encodebytes(s)[:-2].translate(transtable)
 
 
 @zope.interface.implementer(IClientId)
 @zope.component.adapter(IRequest)
-class ClientId(str):
+class ClientId(unicode):
     """See zope.session.interfaces.IClientId
 
-        >>> import tests
+        >>> from zope.session import tests
         >>> request = tests.setUp()
 
         >>> id1 = ClientId(request)
@@ -61,15 +93,14 @@
     """
 
     def __new__(cls, request):
-        return str.__new__(cls,
+        return unicode.__new__(cls,
             zope.component.getUtility(IClientIdManager).getClientId(request)
             )
 
 
 @zope.interface.implementer(ISessionDataContainer)
-class PersistentSessionDataContainer(zope.location.Location,
-    persistent.Persistent,
-    IterableUserDict):
+class PersistentSessionDataContainer(
+    zope.location.Location, persistent.Persistent, UserDict):
     """A SessionDataContainer that stores data in the ZODB"""
 
 
@@ -116,8 +147,8 @@
 
         But the data is not automatically removed.
 
-            >>> sdc['stale'] #doctest: +ELLIPSIS
-            <zope.session.session.SessionData object at ...>
+            >>> sdc['stale'] is stale
+            True
 
         We can manually remove stale data by calling sweep() if stale
         data isn't being automatically removed.
@@ -214,7 +245,7 @@
 
         """
         if self.timeout == 0:
-            return IterableUserDict.__getitem__(self, pkg_id)
+            return UserDict.__getitem__(self, pkg_id)
 
         now = time.time()
 
@@ -239,7 +270,7 @@
                 self._v_old_sweep = self._v_last_sweep
             self._v_last_sweep = now
 
-        rv = IterableUserDict.__getitem__(self, pkg_id)
+        rv = UserDict.__getitem__(self, pkg_id)
         # Only update the lastAccessTime once every few minutes, rather than
         # every hit, to avoid ZODB bloat and conflicts
         if rv.getLastAccessTime() + self.resolution < now:
@@ -267,7 +298,7 @@
 
         """
         session_data.setLastAccessTime(int(time.time()))
-        return IterableUserDict.__setitem__(self, pkg_id, session_data)
+        return UserDict.__setitem__(self, pkg_id, session_data)
 
     def sweep(self):
         """Clean out stale data
@@ -334,12 +365,12 @@
     def _getData(self):
 
         # Open a connection to _ram_storage per thread
-        tid = thread.get_ident()
-        if not self._conns.has_key(tid):
+        tid = get_ident()
+        if tid not in self._conns:
             self._conns[tid] = self._ram_db.open()
 
         root = self._conns[tid].root()
-        if not root.has_key(self.key):
+        if self.key not in root:
             root[self.key] = OOBTree()
         return root[self.key]
 
@@ -370,7 +401,7 @@
 
         """See zope.session.interfaces.ISession
 
-            >>> import tests
+            >>> from zope.session import tests
             >>> request = tests.setUp(PersistentSessionDataContainer)
 
            If we use get we get None or default returned if the pkg_id
@@ -414,7 +445,7 @@
     def __getitem__(self, pkg_id):
         """See zope.session.interfaces.ISession
 
-            >>> import tests
+            >>> from zope.session import tests
             >>> request = tests.setUp(PersistentSessionDataContainer)
             >>> request2 = tests.HTTPRequest(StringIO(''), {}, None)
 
@@ -475,7 +506,7 @@
 
 
 @zope.interface.implementer(ISessionData)
-class SessionData(persistent.Persistent, IterableUserDict):
+class SessionData(persistent.Persistent, UserDict):
     """See zope.session.interfaces.ISessionData
 
         >>> session = SessionData()
@@ -553,7 +584,7 @@
 
 
 @zope.interface.implementer(ISessionPkgData)
-class SessionPkgData(persistent.Persistent, IterableUserDict):
+class SessionPkgData(persistent.Persistent, UserDict):
     """See zope.session.interfaces.ISessionPkgData
 
         >>> session = SessionPkgData()

Modified: zope.session/trunk/src/zope/session/tests.py
===================================================================
--- zope.session/trunk/src/zope/session/tests.py	2013-02-21 16:02:40 UTC (rev 129573)
+++ zope.session/trunk/src/zope/session/tests.py	2013-02-21 16:08:18 UTC (rev 129574)
@@ -13,16 +13,20 @@
 ##############################################################################
 """Session tests
 """
-from cStringIO import StringIO
 from zope.testing import cleanup
 import doctest
 import os
 import os.path
+import re
 import transaction
 import unittest
 import zope.component
 
 from zope.component import provideHandler, getGlobalSiteManager
+from zope.publisher.interfaces import IRequest
+from zope.publisher.http import HTTPRequest
+from zope.testing import renormalizing
+
 from zope.session.interfaces import IClientId, IClientIdManager, ISession
 from zope.session.interfaces import ISessionDataContainer
 from zope.session.interfaces import ISessionPkgData, ISessionData
@@ -31,9 +35,30 @@
 from zope.session.session import RAMSessionDataContainer
 from zope.session.http import CookieClientIdManager
 
-from zope.publisher.interfaces import IRequest
-from zope.publisher.http import HTTPRequest
+try:
+    from cStringIO import StringIO
+except ImportError:
+    # Py3: Now StringIO location.
+    from io import StringIO
 
+
+checker = renormalizing.RENormalizing([
+    # Python 3 strings remove the "u".
+    (re.compile("u('.*?')"),
+     r"\1"),
+    (re.compile('u(".*?")'),
+     r"\1"),
+    # Python 3 bytes add a "b".
+    (re.compile("b('.*?')"),
+     r"\1"),
+    (re.compile('b(".*?")'),
+     r"\1"),
+    # Python 3 adds module name to exceptions.
+    (re.compile("zope.session.http.MissingClientIdException"),
+     r"MissingClientIdException"),
+    ])
+
+
 def setUp(session_data_container_class=PersistentSessionDataContainer):
     cleanup.setUp()
     zope.component.provideAdapter(ClientId, (IRequest,), IClientId)
@@ -157,11 +182,15 @@
 
 def test_suite():
     suite = unittest.TestSuite()
-    suite.addTest(doctest.DocTestSuite())
-    suite.addTest(doctest.DocTestSuite('zope.session.session',
-        tearDown=tearDownTransaction))
-    suite.addTest(doctest.DocTestSuite('zope.session.http',
-        optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,)
+    suite.addTest(doctest.DocTestSuite(
+            checker=checker,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,))
+    suite.addTest(doctest.DocTestSuite(
+            'zope.session.session', checker=checker,
+            tearDown=tearDownTransaction))
+    suite.addTest(doctest.DocTestSuite(
+            'zope.session.http', checker=checker,
+            optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,)
         )
     return suite
 

Added: zope.session/trunk/tox.ini
===================================================================
--- zope.session/trunk/tox.ini	                        (rev 0)
+++ zope.session/trunk/tox.ini	2013-02-21 16:08:18 UTC (rev 129574)
@@ -0,0 +1,20 @@
+[tox]
+envlist =
+    py26,py27,py33
+
+[testenv]
+commands =
+    python setup.py test -q
+# without explicit deps, setup.py test will download a bunch of eggs into $PWD
+# (and it seems I can't use zope.dottedname[testing] here, so forget DRY)
+deps =
+    {toxinidir}/ZODB-4.0.0dev.tar.gz
+    zope.component
+    zope.i18nmessageid
+    zope.interface
+    zope.location
+    zope.minmax
+    zope.publisher
+    zope.testing
+
+



More information about the checkins mailing list