Python 3.3 port of zope.configuration

Brian Sutherland <[email protected]>
Newsgroups gmane.comp.web.zope.devel
Message-ID <[email protected]>
Hi,

I'd like to merge 2 branches which get zope.configuration's tests
running on Python 3.3. Here is an overview (with patches attached):

zope.schema:

    * svn+ssh://svn.zope.org/repos/main/zope.schema/branches/jinty-native_string

    * This branch corrects a mistake I think I made in the zope.schema
      Python3 port. After my experience with the zope.configuration port
      I now think that the URI field should be a "native" string rather
      than bytes.

      Unfortunately this change is backwards incompatible for Python 3
      users of zope.schema. But I think the number users of zope.schema
      under Python 3 are rather small at this point.

    * I make public NativeString and NativeStringLine classes. These are
      equivalent to the Bytes type under Python 2 and the Text type under
      Python 3. Please devote some bikeshedding energy to better names
      for these classes ;)

zope.configuration:

    * svn+ssh://svn.zope.org/repos/main/zope.configuration/branches/jinty-python3

    * Adds a dependency on six

    * Drops Python 2.4 and 2.5 compatibility

To run the tests of the zope.configuration branch you currently need to
use the zope.interface trunk.

If there are no objections, I'll merge in a few weeks or so.

-- 
Brian Sutherland

_______________________________________________
Zope-Dev maillist  -  [email protected]
https://mail.zope.org/mailman/listinfo/zope-dev
**  No cross posts or HTML encoding!  **
(Related lists -
 https://mail.zope.org/mailman/listinfo/zope-announce
 https://mail.zope.org/mailman/listinfo/zope )
zope.schema.diff (text/plain, 10.3 KB)
Index: CHANGES.txt
===================================================================
--- CHANGES.txt	(.../trunk)	(revision 125127)
+++ CHANGES.txt	(.../branches/jinty-native_string)	(revision 125127)
@@ -2,12 +2,16 @@
 CHANGES
 =======
 
-4.1.2 (unreleased)
+4.2.0 (unreleased)
 ------------------
 
-- Nothing changed yet.
+- Introduce NativeString and NativeStringLine which are equal to Bytes and
+  BytesLine on Python 2 and Text and TextLine on Python 3.
 
+- Change IURI from a Bytes string to a "native" string. This is a backwards
+  incompatibility which only affects Python 3.
 
+
 4.1.1 (2012-03-23)
 ------------------
 
Index: src/zope/schema/__init__.py
===================================================================
--- src/zope/schema/__init__.py	(.../trunk)	(revision 125127)
+++ src/zope/schema/__init__.py	(.../branches/jinty-native_string)	(revision 125127)
@@ -17,6 +17,7 @@
 from zope.schema._field import MinMaxLen, Choice
 from zope.schema._field import Bytes, ASCII, BytesLine, ASCIILine
 from zope.schema._field import Text, TextLine, Bool, Int, Float, Decimal
+from zope.schema._field import NativeString, NativeStringLine
 from zope.schema._field import Tuple, List, Set, FrozenSet
 from zope.schema._field import Password, Dict, Datetime, Date, Timedelta
 from zope.schema._field import Time, SourceText
Index: src/zope/schema/tests/test_uri.py
===================================================================
--- src/zope/schema/tests/test_uri.py	(.../trunk)	(revision 0)
+++ src/zope/schema/tests/test_uri.py	(.../branches/jinty-native_string)	(revision 125127)
@@ -0,0 +1,67 @@
+##############################################################################
+#
+# Copyright (c) 2012 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""URI field tests
+"""
+from unittest import main, makeSuite
+
+from six import u
+from zope.schema import URI
+from zope.schema.tests.test_field import FieldTestBase
+from zope.schema.interfaces import RequiredMissing
+from zope.schema.interfaces import InvalidURI, WrongType
+
+class URITest(FieldTestBase):
+    """Test the URI Field."""
+
+    _Field_Factory = URI
+
+    def testValidate(self):
+        field = self._Field_Factory(
+            title=u('Not required field'), description=u(''),
+            readonly=False, required=False)
+        field.validate(None)
+        field.validate('http://www.example.com')
+        self.assertRaises(WrongType, field.validate, 2)
+
+    def testValidateRequired(self):
+        field = self._Field_Factory(
+            title=u('Required field'), description=u(''),
+            readonly=False, required=True)
+        field.validate('http://www.example.com')
+        self.assertRaises(RequiredMissing, field.validate, None)
+
+    def testFromUnicode(self):
+        field = self._Field_Factory()
+        # result is a native string
+        self.assertEqual(
+                field.fromUnicode(u("http://www.python.org/foo/bar")),
+                'http://www.python.org/foo/bar')
+        # leading/trailing whitespace is stripped
+        self.assertEqual(
+                field.fromUnicode(u("          http://www.python.org/foo/bar")),
+                'http://www.python.org/foo/bar')
+        self.assertEqual(
+                field.fromUnicode(u("  \n http://www.python.org/foo/bar\n")),
+                'http://www.python.org/foo/bar')
+        # but not in the middle
+        self.assertRaises(InvalidURI,
+            field.fromUnicode,
+            u("http://www.python.org/ foo/bar"))
+
+def test_suite():
+    suite = makeSuite(URITest)
+    return suite
+
+if __name__ == '__main__':
+    main(defaultTest='test_suite')

Property changes on: src/zope/schema/tests/test_uri.py
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/zope/schema/tests/test_native_string.py
===================================================================
--- src/zope/schema/tests/test_native_string.py	(.../trunk)	(revision 0)
+++ src/zope/schema/tests/test_native_string.py	(.../branches/jinty-native_string)	(revision 125127)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2012 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+import unittest
+
+import six
+
+from zope.schema import Text, Bytes, NativeString
+from zope.schema import TextLine, BytesLine, NativeStringLine
+
+class TestNativeString(unittest.TestCase):
+
+    def test_string_py2(self):
+        if six.PY3:
+            return
+        self.assertTrue(NativeString is Bytes)
+        self.assertTrue(NativeStringLine is BytesLine)
+
+    def test_string_py3(self):
+        if not six.PY3:
+            return
+        self.assertTrue(NativeString is Text)
+        self.assertTrue(NativeStringLine is TextLine)
+
+def test_suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestNativeString))
+    return suite

Property changes on: src/zope/schema/tests/test_native_string.py
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: src/zope/schema/_field.py
===================================================================
--- src/zope/schema/_field.py	(.../trunk)	(revision 125127)
+++ src/zope/schema/_field.py	(.../branches/jinty-native_string)	(revision 125127)
@@ -107,12 +107,12 @@
 
 # for things which are of the str type on both Python 2 and 3
 if PY3:
-    _Str = Text
+    NativeString = Text
 else:
-    _Str = Bytes
+    NativeString = Bytes
 
 @implementer(IASCII)
-class ASCII(_Str):
+class ASCII(NativeString):
     __doc__ = IASCII.__doc__
 
     def _validate(self, value):
@@ -152,9 +152,9 @@
 
 # for things which are of the str type on both Python 2 and 3
 if PY3:
-    _StrLine = TextLine
+    NativeStringLine = TextLine
 else:
-    _StrLine = BytesLine
+    NativeStringLine = BytesLine
 
 @implementer(IASCIILine)
 class ASCIILine(ASCII):
@@ -602,46 +602,32 @@
 _isuri = r"[a-zA-z0-9+.-]+:" # scheme
 _isuri += r"\S*$" # non space (should be pickier)
 
-_isuri_bytes = re.compile(_isuri.encode('ascii')).match
 _isuri = re.compile(_isuri).match
 
 @implementer(IURI, IFromUnicode)
-class URI(BytesLine):
+class URI(NativeStringLine):
     """URI schema field
     """
 
     def _validate(self, value):
         """
         >>> uri = URI(__name__='test')
-        >>> uri.validate(b("http://www.python.org/foo/bar"))
-        >>> uri.validate(b("DAV:"))
-        >>> uri.validate(b("www.python.org/foo/bar"))
+        >>> uri.validate("http://www.python.org/foo/bar")
+        >>> uri.validate("DAV:")
+        >>> uri.validate("www.python.org/foo/bar")
         Traceback (most recent call last):
         ...
         InvalidURI: www.python.org/foo/bar
         """
 
         super(URI, self)._validate(value)
-        if _isuri_bytes(value):
+        if _isuri(value):
             return
 
         raise InvalidURI(value)
 
     def fromUnicode(self, value):
-        """
-        >>> uri = URI(__name__='test')
-        >>> uri.fromUnicode("http://www.python.org/foo/bar")
-        'http://www.python.org/foo/bar'
-        >>> uri.fromUnicode("          http://www.python.org/foo/bar")
-        'http://www.python.org/foo/bar'
-        >>> uri.fromUnicode("      \\n    http://www.python.org/foo/bar\\n")
-        'http://www.python.org/foo/bar'
-        >>> uri.fromUnicode("http://www.python.org/ foo/bar")
-        Traceback (most recent call last):
-        ...
-        InvalidURI: http://www.python.org/ foo/bar
-        """
-        v = value.strip().encode('ascii')
+        v = str(value.strip())
         self.validate(v)
         return v
 
@@ -654,7 +640,7 @@
 
 
 @implementer(IId, IFromUnicode)
-class Id(_StrLine):
+class Id(NativeStringLine):
     """Id field
 
     Values of id fields must be either uris or dotted names.
@@ -705,7 +691,7 @@
 
 
 @implementer(IDottedName)
-class DottedName(_StrLine):
+class DottedName(NativeStringLine):
     """Dotted name field.
 
     Values of DottedName fields must be Python-style dotted names.
Index: src/zope/schema/interfaces.py
===================================================================
--- src/zope/schema/interfaces.py	(.../trunk)	(revision 125127)
+++ src/zope/schema/interfaces.py	(.../branches/jinty-native_string)	(revision 125127)
@@ -373,7 +373,7 @@
     return True
 
 
-class IURI(IBytesLine):
+class IURI(_IStrLine):
     """A field containing an absolute URI
     """
 
Index: src/zope/schema/README.txt
===================================================================
--- src/zope/schema/README.txt	(.../trunk)	(revision 125127)
+++ src/zope/schema/README.txt	(.../branches/jinty-native_string)	(revision 125127)
@@ -65,7 +65,7 @@
 is to define some data:
 
   >>> title = u('Zope 3 Website')
-  >>> url = b('http://dev.zope.org/Zope3')
+  >>> url = 'http://dev.zope.org/Zope3'
 
 Now we, get the fields from the interface:
 
@@ -86,14 +86,9 @@
 If the validation is successful, ``None`` is returned. If a validation error
 occurs a ``ValidationError`` will be raised; for example:
 
-  >>> url_bound.validate(u('http://zope.org/foo'))
+  >>> url_bound.validate('foo.bar')
   Traceback (most recent call last):
   ...
-  WrongType: (u'http://zope.org/foo', <type 'str'>, 'url')
-
-  >>> url_bound.validate(b('foo.bar'))
-  Traceback (most recent call last):
-  ...
   InvalidURI: foo.bar
 
 Now that the data has been successfully validated, we can set it on the
zope.configuration.diff (text/plain, 61.2 KB)
Index: CHANGES.txt
===================================================================
--- CHANGES.txt	(.../trunk)	(revision 125127)
+++ CHANGES.txt	(.../branches/jinty-python3)	(revision 125127)
@@ -2,11 +2,11 @@
 Changes
 =======
 
-3.8.1 (unreleased)
+4.0.0 (unreleased)
 ------------------
 
-- Fixed Python 2.4 backwards incompat (itemgetter used with multiple args);
-  Python 2.4 now works (at least if you use zope.schema == 3.8.1).
+- Support Python 3.3.
+- Drop support for Python 2.4 and Python 2.5.
 
 3.8.0 (2011-12-06)
 ------------------
Index: setup.py
===================================================================
--- setup.py	(.../trunk)	(revision 125127)
+++ setup.py	(.../branches/jinty-python3)	(revision 125127)
@@ -78,6 +78,10 @@
           'Intended Audience :: Developers',
           'License :: OSI Approved :: Zope Public License',
           'Programming Language :: Python',
+          "Programming Language :: Python :: 2.6",
+          "Programming Language :: Python :: 2.7",
+          "Programming Language :: Python :: 3",
+          "Programming Language :: Python :: 3.3",
           'Natural Language :: English',
           'Operating System :: OS Independent',
           'Topic :: Internet :: WWW/HTTP',
@@ -89,7 +93,8 @@
       namespace_packages=['zope'],
       extras_require=dict(
           test=['zope.testing']),
-      install_requires=['zope.i18nmessageid',
+      install_requires=['six',
+                        'zope.i18nmessageid',
                         'zope.interface',
                         'zope.schema',
                         'setuptools',
Index: src/zope/configuration/tests/test_simple.py
===================================================================
--- src/zope/configuration/tests/test_simple.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/tests/test_simple.py	(.../branches/jinty-python3)	(revision 125127)
@@ -121,15 +121,16 @@
 
 >>> from zope.configuration.tests.test_xmlconfig import clean_text_w_paths
 >>> from zope.configuration.tests.test_xmlconfig import clean_path
+>>> from six import print_
 >>> for i in file_registry:
-...   print "path:", clean_path(i.path)
-...   print "title:", i.title
-...   print "description:", '\n'.join(
+...   print_("path:", clean_path(i.path))
+...   print_("title:", i.title)
+...   print_("description:", '\n'.join(
 ...               [l.rstrip()
 ...                for l in i.description.strip().split('\n')
-...                if l.rstrip()])
-...   print "info:"
-...   print clean_text_w_paths(i.info)
+...                if l.rstrip()]))
+...   print_("info:")
+...   print_(clean_text_w_paths(i.info))
 path: tests/test_simple.py
 title: How to create a simple directive
 description: Describes how to implement a simple directive
Index: src/zope/configuration/tests/victim.py
===================================================================
--- src/zope/configuration/tests/victim.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/tests/victim.py	(.../branches/jinty-python3)	(revision 125127)
@@ -1 +1 @@
-import bad
+from . import bad
Index: src/zope/configuration/tests/directives.py
===================================================================
--- src/zope/configuration/tests/directives.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/tests/directives.py	(.../branches/jinty-python3)	(revision 125127)
@@ -13,7 +13,7 @@
 ##############################################################################
 """Test directives
 """
-from zope.interface import Interface, implements
+from zope.interface import Interface, implementer
 from zope.schema import Text, BytesLine
 from zope.configuration.config import GroupingContextDecorator
 from zope.configuration.interfaces import IConfigurationContext
@@ -48,11 +48,11 @@
 class IPackagedContext(IPackaged, IConfigurationContext):
     pass
 
+@implementer(IPackagedContext)
 class Packaged(GroupingContextDecorator):
+    pass
 
-    implements(IPackagedContext)
 
-
 class IFactory(Interface):
 
     factory = GlobalObject()
Index: src/zope/configuration/tests/test_config.py
===================================================================
--- src/zope/configuration/tests/test_config.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/tests/test_config.py	(.../branches/jinty-python3)	(revision 125127)
@@ -17,250 +17,245 @@
 import sys
 import unittest
 import re
+import six
 from doctest import DocTestSuite
 from zope.testing import renormalizing
 from zope.configuration.config import metans, ConfigurationMachine
 from zope.configuration import config
 
-def test_config_extended_example():
-    """Configuration machine
+class TestConfig(unittest.TestCase):
 
-    Examples:
+    def test_config_extended_example(self):
+        from zope.configuration.tests.directives import f
+        # Examples:
 
-    >>> machine = ConfigurationMachine()
-    >>> ns = "http://www.zope.org/testing"
+        machine = ConfigurationMachine()
+        ns = "http://www.zope.org/testing"
 
-    Register some test directives:
+        # Register some test directives:
+        # Start with a grouping directive that sets a package:
 
-    Start with a grouping directive that sets a package:
+        machine((metans, "groupingDirective"),
+                name="package", namespace=ns,
+                schema="zope.configuration.tests.directives.IPackaged",
+                handler="zope.configuration.tests.directives.Packaged",
+                )
 
-    >>> machine((metans, "groupingDirective"),
-    ...         name="package", namespace=ns,
-    ...         schema="zope.configuration.tests.directives.IPackaged",
-    ...         handler="zope.configuration.tests.directives.Packaged",
-    ...         )
+        # we can set the package:
 
-    Now we can set the package:
+        machine.begin((ns, "package"),
+                      package="zope.configuration.tests.directives",
+                      )
 
-    >>> machine.begin((ns, "package"),
-    ...               package="zope.configuration.tests.directives",
-    ...               )
+        # Which makes it easier to define the other directives:
+        # First, define some simple directives:
 
-    Which makes it easier to define the other directives:
+        machine((metans, "directive"),
+                namespace=ns, name="simple",
+                schema=".ISimple", handler=".simple")
 
-    First, define some simple directives:
+        machine((metans, "directive"),
+                namespace=ns, name="newsimple",
+                schema=".ISimple", handler=".newsimple")
 
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="simple",
-    ...         schema=".ISimple", handler=".simple")
 
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="newsimple",
-    ...         schema=".ISimple", handler=".newsimple")
+        # try them out:
 
+        machine((ns, "simple"), "first", a=u"aa", c=u"cc")
+        machine((ns, "newsimple"), "second", a=u"naa", c=u"ncc", b=u"nbb")
 
-    and try them out:
+        self.assertEqual(machine.actions,
+            [{'args': (u'aa', u'xxx', b'cc'),
+              'callable': f,
+              'discriminator': ('simple',
+                                u'aa',
+                                u'xxx',
+                                b'cc'),
+              'includepath': (),
+              'info': 'first',
+              'kw': {},
+              'order': 0},
+             {'args': (u'naa', u'nbb', b'ncc'),
+              'callable': f,
+              'discriminator': ('newsimple',
+                                u'naa',
+                                u'nbb',
+                                b'ncc'),
+              'includepath': (),
+              'info': 'second',
+              'kw': {},
+              'order': 0}])
 
-    >>> machine((ns, "simple"), "first", a=u"aa", c=u"cc")
-    >>> machine((ns, "newsimple"), "second", a=u"naa", c=u"ncc", b=u"nbb")
+        # Define and try a simple directive that uses a component:
 
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=50).pprint
+        machine((metans, "directive"),
+                namespace=ns, name="factory",
+                schema=".IFactory", handler=".factory")
 
-    >>> pprint(machine.actions)
-    [{'args': (u'aa', u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('simple',
-                        u'aa',
-                        u'xxx',
-                        'cc'),
-      'includepath': (),
-      'info': 'first',
-      'kw': {},
-      'order': 0},
-     {'args': (u'naa', u'nbb', 'ncc'),
-      'callable': f,
-      'discriminator': ('newsimple',
-                        u'naa',
-                        u'nbb',
-                        'ncc'),
-      'includepath': (),
-      'info': 'second',
-      'kw': {},
-      'order': 0}]
 
-    Define and try a simple directive that uses a component:
+        machine((ns, "factory"), factory=u".f")
 
-    >>> machine((metans, "directive"),
-    ...         namespace=ns, name="factory",
-    ...         schema=".IFactory", handler=".factory")
+        self.assertEqual(machine.actions[-1:],
+            [{'args': (),
+              'callable': f,
+              'discriminator': ('factory', 1, 2),
+              'includepath': (),
+              'info': None,
+              'kw': {},
+              'order': 0}])
 
+        # Define and try a complex directive:
 
-    >>> machine((ns, "factory"), factory=u".f")
-    >>> pprint(machine.actions[-1:])
-    [{'args': (),
-      'callable': f,
-      'discriminator': ('factory', 1, 2),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0}]
+        machine.begin((metans, "complexDirective"),
+                      namespace=ns, name="testc",
+                      schema=".ISimple", handler=".Complex")
 
-    Define and try a complex directive:
+        machine((metans, "subdirective"),
+                name="factory", schema=".IFactory")
 
-    >>> machine.begin((metans, "complexDirective"),
-    ...               namespace=ns, name="testc",
-    ...               schema=".ISimple", handler=".Complex")
+        machine.end()
 
-    >>> machine((metans, "subdirective"),
-    ...         name="factory", schema=".IFactory")
+        machine.begin((ns, "testc"), None, "third", a=u'ca', c='cc')
+        machine((ns, "factory"), "fourth", factory=".f")
 
-    >>> machine.end()
+        # Note that we can't call a complex method unless there is a directive for
+        # it:
 
-    >>> machine.begin((ns, "testc"), None, "third", a=u'ca', c='cc')
-    >>> machine((ns, "factory"), "fourth", factory=".f")
+        self.assertRaises(config.ConfigurationError, machine, (ns, "factory2"), factory=".f")
+        #Traceback (most recent call last):
+        #        ...
+        #ConfigurationError: ('Invalid directive', 'factory2')
 
-    Note that we can't call a complex method unless there is a directive for
-    it:
+        machine.end()
 
-    >>> machine((ns, "factory2"), factory=".f")
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Invalid directive', 'factory2')
+        self.assertEqual(machine.actions,
+            [{'args': (u'aa', u'xxx', b'cc'),
+              'callable': f,
+              'discriminator': ('simple',
+                                u'aa',
+                                u'xxx',
+                                b'cc'),
+              'includepath': (),
+              'info': 'first',
+              'kw': {},
+              'order': 0},
+             {'args': (u'naa', u'nbb', b'ncc'),
+              'callable': f,
+              'discriminator': ('newsimple',
+                                u'naa',
+                                u'nbb',
+                                b'ncc'),
+              'includepath': (),
+              'info': 'second',
+              'kw': {},
+              'order': 0},
+             {'args': (),
+              'callable': f,
+              'discriminator': ('factory', 1, 2),
+              'includepath': (),
+              'info': None,
+              'kw': {},
+              'order': 0},
+             {'args': (),
+              'callable': None,
+              'discriminator': 'Complex.__init__',
+              'includepath': (),
+              'info': 'third',
+              'kw': {},
+              'order': 0},
+             {'args': (u'ca',),
+              'callable': f,
+              'discriminator': ('Complex.factory', 1, 2),
+              'includepath': (),
+              'info': 'fourth',
+              'kw': {},
+              'order': 0},
+             {'args': (u'xxx', b'cc'),
+              'callable': f,
+              'discriminator': ('Complex', 1, 2),
+              'includepath': (),
+              'info': 'third',
+              'kw': {},
+              'order': 0}])
 
+        # Done with the package
 
-    >>> machine.end()
-    >>> pprint(machine.actions)
-    [{'args': (u'aa', u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('simple',
-                        u'aa',
-                        u'xxx',
-                        'cc'),
-      'includepath': (),
-      'info': 'first',
-      'kw': {},
-      'order': 0},
-     {'args': (u'naa', u'nbb', 'ncc'),
-      'callable': f,
-      'discriminator': ('newsimple',
-                        u'naa',
-                        u'nbb',
-                        'ncc'),
-      'includepath': (),
-      'info': 'second',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('factory', 1, 2),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': None,
-      'discriminator': 'Complex.__init__',
-      'includepath': (),
-      'info': 'third',
-      'kw': {},
-      'order': 0},
-     {'args': (u'ca',),
-      'callable': f,
-      'discriminator': ('Complex.factory', 1, 2),
-      'includepath': (),
-      'info': 'fourth',
-      'kw': {},
-      'order': 0},
-     {'args': (u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('Complex', 1, 2),
-      'includepath': (),
-      'info': 'third',
-      'kw': {},
-      'order': 0}]
+        machine.end()
 
-    Done with the package
+        # Verify that we can use a simple directive outside of the package:
 
-    >>> machine.end()
+        machine((ns, "simple"), a=u"oaa", c=u"occ", b=u"obb")
 
+        # we can't use the factory directive, because it's only valid
+        # inside a package directive:
 
-    Verify that we can use a simple directive outside of the package:
+        self.assertRaises(config.ConfigurationError, machine, (ns, "factory"), factory=u".F")
+    #Traceback (most recent call last):
+    #...
+    #ConfigurationError: ('Invalid value for', 'factory',""" \
+    #   """ "Can't use leading dots in dotted names, no package has been set.")
 
-    >>> machine((ns, "simple"), a=u"oaa", c=u"occ", b=u"obb")
+        self.assertEqual(machine.actions,
+            [{'args': (u'aa', u'xxx', b'cc'),
+              'callable': f,
+              'discriminator': ('simple',
+                                u'aa',
+                                u'xxx',
+                                b'cc'),
+              'includepath': (),
+              'info': 'first',
+              'kw': {},
+              'order': 0},
+             {'args': (u'naa', u'nbb', b'ncc'),
+              'callable': f,
+              'discriminator': ('newsimple',
+                                u'naa',
+                                u'nbb',
+                                b'ncc'),
+              'includepath': (),
+              'info': 'second',
+              'kw': {},
+              'order': 0},
+             {'args': (),
+              'callable': f,
+              'discriminator': ('factory', 1, 2),
+              'includepath': (),
+              'info': None,
+              'kw': {},
+              'order': 0},
+             {'args': (),
+              'callable': None,
+              'discriminator': 'Complex.__init__',
+              'includepath': (),
+              'info': 'third',
+              'kw': {},
+              'order': 0},
+             {'args': (u'ca',),
+              'callable': f,
+              'discriminator': ('Complex.factory', 1, 2),
+              'includepath': (),
+              'info': 'fourth',
+              'kw': {},
+              'order': 0},
+             {'args': (u'xxx', b'cc'),
+              'callable': f,
+              'discriminator': ('Complex', 1, 2),
+              'includepath': (),
+              'info': 'third',
+              'kw': {},
+              'order': 0},
+             {'args': (u'oaa', u'obb', b'occ'),
+              'callable': f,
+              'discriminator': ('simple',
+                                u'oaa',
+                                u'obb',
+                                b'occ'),
+              'includepath': (),
+              'info': None,
+              'kw': {},
+              'order': 0}])
 
-    But we can't use the factory directive, because it's only valid
-    inside a package directive:
 
-    >>> machine((ns, "factory"), factory=u".F")
-    Traceback (most recent call last):
-    ...
-    ConfigurationError: ('Invalid value for', 'factory',""" \
-       """ "Can't use leading dots in dotted names, no package has been set.")
-
-    >>> pprint(machine.actions)
-    [{'args': (u'aa', u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('simple',
-                        u'aa',
-                        u'xxx',
-                        'cc'),
-      'includepath': (),
-      'info': 'first',
-      'kw': {},
-      'order': 0},
-     {'args': (u'naa', u'nbb', 'ncc'),
-      'callable': f,
-      'discriminator': ('newsimple',
-                        u'naa',
-                        u'nbb',
-                        'ncc'),
-      'includepath': (),
-      'info': 'second',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('factory', 1, 2),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': None,
-      'discriminator': 'Complex.__init__',
-      'includepath': (),
-      'info': 'third',
-      'kw': {},
-      'order': 0},
-     {'args': (u'ca',),
-      'callable': f,
-      'discriminator': ('Complex.factory', 1, 2),
-      'includepath': (),
-      'info': 'fourth',
-      'kw': {},
-      'order': 0},
-     {'args': (u'xxx', 'cc'),
-      'callable': f,
-      'discriminator': ('Complex', 1, 2),
-      'includepath': (),
-      'info': 'third',
-      'kw': {},
-      'order': 0},
-     {'args': (u'oaa', u'obb', 'occ'),
-      'callable': f,
-      'discriminator': ('simple',
-                        u'oaa',
-                        u'obb',
-                        'occ'),
-      'includepath': (),
-      'info': None,
-      'kw': {},
-      'order': 0}]
-
-    """
-    #'
-
 def test_keyword_handling():
     """
     >>> machine = ConfigurationMachine()
@@ -382,7 +377,7 @@
     >>> c.resolve('zope.configuration.tests.victim')
     Traceback (most recent call last):
     ...
-      File "...bad.py", line 3 in ?
+      File "...bad.py", line 3 in ...
        import bad_to_the_bone
     ImportError: No module named bad_to_the_bone
 
@@ -417,14 +412,37 @@
     """
 
 def test_suite():
-    checker = renormalizing.RENormalizing([
+    checkers = [
         (re.compile(r"<type 'exceptions.(\w+)Error'>:"),
                     r'exceptions.\1Error:'),
+        ]
+    if six.PY3:
+        checkers.extend([
+        (re.compile(r"^zope.schema._bootstrapinterfaces.([a-zA-Z]*):"),
+                    r'\1:'),
+        (re.compile(r"^zope.configuration.interfaces.([a-zA-Z]*):"),
+                    r'\1:'),
+        (re.compile(r"^zope.configuration.exceptions.([a-zA-Z]*):"),
+                    r'\1:'),
+        (re.compile(r"b'([^']*)'"),
+                    r"'\1'"),
+        (re.compile(r'b"([^"]*)"'),
+                    r'"\1"'),
+        (re.compile(r"u'([^']*)'"),
+                    r"'\1'"),
+        (re.compile(r'u"([^"]*)"'),
+                    r'"\1"'),
+        (re.compile(r"\(<class 'int'>,\)"),
+                    r"(<type 'int'>, <type 'long'>)"),
+        (re.compile(r"No module named '([^']*)'"),
+                    r'No module named \1'),
         ])
+    checker = renormalizing.RENormalizing(checkers)
     return unittest.TestSuite((
-        DocTestSuite('zope.configuration.fields'),
-        DocTestSuite('zope.configuration.config',checker=checker),
-        DocTestSuite(),
+        unittest.findTestCases(sys.modules[__name__]),
+        DocTestSuite('zope.configuration.fields', checker=checker),
+        DocTestSuite('zope.configuration.config', checker=checker),
+        DocTestSuite(checker=checker),
         ))
 
 if __name__ == '__main__': unittest.main()
Index: src/zope/configuration/tests/samplepackage/foo.py
===================================================================
--- src/zope/configuration/tests/samplepackage/foo.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/tests/samplepackage/foo.py	(.../branches/jinty-python3)	(revision 125127)
@@ -28,8 +28,7 @@
          ) = args, info, basepath, package, includepath
 
 def handler(_context, **kw):
-    args = kw.items()
-    args.sort()
+    args = sorted(kw.items())
     args = tuple(args)
     discriminator = args
     args = (stuff(args, _context.info, _context.basepath, _context.package,
Index: src/zope/configuration/tests/test_nested.py
===================================================================
--- src/zope/configuration/tests/test_nested.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/tests/test_nested.py	(.../branches/jinty-python3)	(revision 125127)
@@ -114,7 +114,7 @@
 
 >>> from pprint import PrettyPrinter
 >>> pprint=PrettyPrinter(width=70).pprint
->>> pprint(list(schema_registry))
+>>> pprint(sorted(schema_registry))
 ['zope.configuration.tests.test_nested.I1',
  'zope.configuration.tests.test_nested.I2']
 
@@ -126,19 +126,19 @@
 >>> i1 = schema_registry['zope.configuration.tests.test_nested.I1']
 >>> sorted(i1)
 ['a', 'b']
->>> i1['a'].__class__.__name__
+>>> i1[b'a'].__class__.__name__
 'Text'
->>> i1['a'].description.strip()
+>>> i1[b'a'].description.strip()
 u'A\n\n          Blah blah'
->>> i1['a'].min_length
+>>> i1[b'a'].min_length
 1
->>> i1['b'].__class__.__name__
+>>> i1[b'b'].__class__.__name__
 'Int'
->>> i1['b'].description.strip()
+>>> i1[b'b'].description.strip()
 u'B\n\n          Not feeling very creative'
->>> i1['b'].min
+>>> i1[b'b'].min
 1
->>> i1['b'].max
+>>> i1[b'b'].max
 10
 
 >>> i2 = schema_registry['zope.configuration.tests.test_nested.I2']
@@ -151,13 +151,13 @@
 (Note that we used the context we created above, so we don't have to
 redefine our directives:
 
+>>> from six import print_
 >>> try:
 ...    v = xmlconfig.string(
 ...      '<text xmlns="http://sample.namespaces.zope.org/schema" name="x" />',
 ...      context)
-... except xmlconfig.ZopeXMLConfigurationError, v:
-...   pass
->>> print v
+... except xmlconfig.ZopeXMLConfigurationError as v:
+...   print_(v)
 File "<string>", line 1.0
     ConfigurationError: The directive """ \
         """(u'http://sample.namespaces.zope.org/schema', u'text') """ \
@@ -175,16 +175,20 @@
 ...      </schema>
 ...      ''',
 ...      context)
-... except xmlconfig.ZopeXMLConfigurationError, v:
-...   pass
->>> print v
+... except xmlconfig.ZopeXMLConfigurationError as v:
+...   print_(v)
 File "<string>", line 5.7-5.24
     ValueError: ('Duplicate field', 'x')
 """
 
+import re
 import unittest
 from doctest import DocTestSuite
+
+import six
 from zope import interface, schema
+from zope.testing import renormalizing
+
 from zope.configuration import config, xmlconfig, fields
 
 
@@ -210,12 +214,11 @@
     fields = interface.Attribute("Dictionary of field definitions"
         )
     
[email protected](config.IConfigurationContext, ISchema)
 class Schema(config.GroupingContextDecorator):
     """Handle schema directives
     """
 
-    interface.implements(config.IConfigurationContext, ISchema)
-
     def __init__(self, context, name, id):
         self.context, self.name, self.id = context, name, id
         self.fields = {}
@@ -312,8 +315,21 @@
     
 
 def test_suite():
+    checkers = []
+    if six.PY3:
+        checkers.extend([
+        (re.compile(r"b'([^']*)'"),
+                    r"'\1'"),
+        (re.compile(r'b"([^"]*)"'),
+                    r'"\1"'),
+        (re.compile(r"u'([^']*)'"),
+                    r"'\1'"),
+        (re.compile(r'u"([^"]*)"'),
+                    r'"\1"'),
+        ])
+    checker = renormalizing.RENormalizing(checkers)
     return unittest.TestSuite((
-        DocTestSuite(),
+        DocTestSuite(checker=checker),
         ))
 
 if __name__ == '__main__': unittest.main()
Index: src/zope/configuration/tests/test_xmlconfig.py
===================================================================
--- src/zope/configuration/tests/test_xmlconfig.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/tests/test_xmlconfig.py	(.../branches/jinty-python3)	(revision 125127)
@@ -14,9 +14,12 @@
 """Test XML configuration (ZCML) machinery.
 """
 import unittest
+import sys
 import os
 import re
 from doctest import DocTestSuite, DocFileSuite
+import six
+from six import print_
 from zope.testing import renormalizing
 from zope.configuration import xmlconfig, config
 from zope.configuration.tests.samplepackage import foo
@@ -48,37 +51,34 @@
 def path(*p):
     return os.path.join(os.path.dirname(__file__), *p)
 
-def test_ConfigurationHandler_normal():
-    """
-    >>> context = FauxContext()
-    >>> locator = FauxLocator('tests//sample.zcml', 1, 1)
-    >>> handler = xmlconfig.ConfigurationHandler(context)
-    >>> handler.setDocumentLocator(locator)
 
-    >>> handler.startElementNS((u"ns", u"foo"), u"foo",
-    ...                        {(u"xxx", u"splat"): u"splatv",
-    ...                         (None, u"a"): u"avalue",
-    ...                         (None, u"b"): u"bvalue",
-    ...                        })
-    >>> context.info
-    File "tests//sample.zcml", line 1.1
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=50).pprint
-    >>> pprint(context.begin_args)
-    ((u'ns', u'foo'),
-     {'a': u'avalue', 'b': u'bvalue'})
-    >>> getattr(context, "end_called", 0)
-    0
+class TestConfigurationHandler(unittest.TestCase):
 
-    >>> locator.line, locator.column = 7, 16
-    >>> handler.endElementNS((u"ns", u"foo"), u"foo")
-    >>> context.info
-    File "tests//sample.zcml", line 1.1-7.16
-    >>> context.end_called
-    1
+    def test_normal(self):
+        context = FauxContext()
+        locator = FauxLocator('tests//sample.zcml', 1, 1)
+        handler = xmlconfig.ConfigurationHandler(context)
+        handler.setDocumentLocator(locator)
 
-    """
+        handler.startElementNS((u"ns", u"foo"),
+                u"foo",
+                {(u"xxx", u"splat"): u"splatv",
+                    (None, u"a"): u"avalue",
+                    (None, u"b"): u"bvalue",
+                    })
 
+        self.assertEqual(repr(context.info), 'File "tests//sample.zcml", line 1.1')
+        self.assertEqual(context.begin_args, ((u'ns', u'foo'),
+                    {'a': u'avalue', 'b': u'bvalue'}))
+        self.assertEqual(getattr(context, "end_called", 0), 0)
+
+        locator.line, locator.column = 7, 16
+        handler.endElementNS((u"ns", u"foo"), u"foo")
+
+        self.assertEqual(repr(context.info), 'File "tests//sample.zcml", line 1.1-7.16')
+        self.assertEqual(context.end_called, 1)
+
+
 def test_ConfigurationHandler_err_start():
     """
 
@@ -97,9 +97,8 @@
     ...                               (None, u"a"): u"avalue",
     ...                               (None, u"b"): u"bvalue",
     ...                              })
-    ... except xmlconfig.ZopeXMLConfigurationError, v:
-    ...   pass
-    >>> print v
+    ... except xmlconfig.ZopeXMLConfigurationError as v:
+    ...   print_(v)
     File "tests//sample.zcml", line 1.1
         AttributeError: xxx
 
@@ -126,9 +125,8 @@
     >>> locator.line, locator.column = 7, 16
     >>> try:
     ...   v = handler.endElementNS((u"ns", u"foo"), u"foo")
-    ... except xmlconfig.ZopeXMLConfigurationError, v:
-    ...   pass
-    >>> print v
+    ... except xmlconfig.ZopeXMLConfigurationError as v:
+    ...   print_(v)
     File "tests//sample.zcml", line 1.1-7.16
         AttributeError: xxx
 
@@ -165,10 +163,10 @@
     >>> data.args
     (('x', 'blah'), ('y', 0))
 
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/configure.zcml", line 12.2-12.29
 
-    >>> print clean_info_path(str(data.info))
+    >>> print_(clean_info_path(str(data.info)))
     File "tests/samplepackage/configure.zcml", line 12.2-12.29
         <test:foo x="blah" y="0" />
 
@@ -187,15 +185,15 @@
     >>> data.args
     (('x', 'blah'), ('y', 0))
 
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/configure.zcml", line 12.2-12.29
 
-    >>> print clean_info_path(str(data.info))
+    >>> print_(clean_info_path(str(data.info)))
     File "tests/samplepackage/configure.zcml", line 12.2-12.29
         <test:foo x="blah" y="0" />
 
     >>> data.package
-    >>> print clean_path(data.basepath)
+    >>> print_(clean_path(data.basepath))
     tests/samplepackage
     """
 
@@ -212,10 +210,10 @@
     >>> data.args
     (('x', 'blah'), ('y', 0))
 
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/configure.zcml", line 12.2-12.29
 
-    >>> print clean_info_path(str(data.info))
+    >>> print_(clean_info_path(str(data.info)))
     File "tests/samplepackage/configure.zcml", line 12.2-12.29
         <test:foo x="blah" y="0" />
 
@@ -255,10 +253,10 @@
     >>> data.args
     (('x', 'foo'), ('y', 2))
 
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/foo.zcml.in", line 12.2-12.28
 
-    >>> print clean_info_path(str(data.info))
+    >>> print_(clean_info_path(str(data.info)))
     File "tests/samplepackage/foo.zcml.in", line 12.2-12.28
         <test:foo x="foo" y="2" />
 
@@ -284,10 +282,10 @@
     >>> data.args
     (('x', 'foo'), ('y', 3))
 
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/baz3.zcml", line 5.2-5.28
 
-    >>> print clean_info_path(str(data.info))
+    >>> print_(clean_info_path(str(data.info)))
     File "tests/samplepackage/baz3.zcml", line 5.2-5.28
         <test:foo x="foo" y="3" />
 
@@ -303,10 +301,10 @@
     >>> data.args
     (('x', 'foo'), ('y', 2))
 
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/baz2.zcml", line 5.2-5.28
 
-    >>> print clean_info_path(str(data.info))
+    >>> print_(clean_info_path(str(data.info)))
     File "tests/samplepackage/baz2.zcml", line 5.2-5.28
         <test:foo x="foo" y="2" />
 
@@ -322,7 +320,7 @@
 def clean_actions(actions):
     return [
       {'discriminator': action['discriminator'],
-       'info': clean_info_path(`action['info']`),
+       'info': clean_info_path(repr(action['info'])),
        'includepath': [clean_path(p) for p in action['includepath']],
        }
       for action in actions
@@ -330,7 +328,7 @@
 
 def clean_text_w_paths(error):
     r = []
-    for line in unicode(error).split("\n"):
+    for line in six.text_type(error).split("\n"):
       line = line.rstrip()
       if not line:
         continue
@@ -404,9 +402,8 @@
 
     >>> try:
     ...    v = context.execute_actions()
-    ... except config.ConfigurationConflictError, v:
-    ...    pass
-    >>> print clean_text_w_paths(str(v))
+    ... except config.ConfigurationConflictError as v:
+    ...    print_(clean_text_w_paths(str(v)))
     Conflicting configuration actions
       For: (('x', 'blah'), ('y', 0))
         File "tests/samplepackage/configure.zcml", line 12.2-12.29
@@ -475,19 +472,19 @@
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 0))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar21.zcml", line 3.2-3.24
 
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 2))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar2.zcml", line 5.2-5.24
 
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 1))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar2.zcml", line 6.2-6.24
 
 
@@ -553,19 +550,19 @@
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 0))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar21.zcml", line 3.2-3.24
 
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 2))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar2.zcml", line 5.2-5.24
 
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 1))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar2.zcml", line 6.2-6.24
 
     Finally, clean up.
@@ -597,19 +594,19 @@
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 0))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar21.zcml", line 3.2-3.24
 
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 2))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar2.zcml", line 5.2-5.24
 
     >>> data = foo.data.pop(0)
     >>> data.args
     (('x', 'blah'), ('y', 1))
-    >>> print clean_info_path(`data.info`)
+    >>> print_(clean_info_path(repr(data.info)))
     File "tests/samplepackage/bar2.zcml", line 6.2-6.24
 
     Finally, clean up.
@@ -621,15 +618,29 @@
 
 
 def test_suite():
+    checkers = []
+    if six.PY3:
+        checkers.extend([
+        (re.compile(r"b'([^']*)'"),
+                    r"'\1'"),
+        (re.compile(r'b"([^"]*)"'),
+                    r'"\1"'),
+        (re.compile(r"u'([^']*)'"),
+                    r"'\1'"),
+        (re.compile(r'u"([^"]*)"'),
+                    r'"\1"'),
+        ])
+    checker = renormalizing.RENormalizing(checkers)
     return unittest.TestSuite((
-        DocTestSuite('zope.configuration.xmlconfig'),
-        DocTestSuite(),
+        unittest.findTestCases(sys.modules[__name__]),
+        DocTestSuite('zope.configuration.xmlconfig', checker=checker),
+        DocTestSuite(checker=checker),
         DocFileSuite('../exclude.txt',
             checker=renormalizing.RENormalizing([
                 (re.compile('include [^\n]+zope.configuration[\S+]'),
                  'include /zope.configuration\2'),
                 (re.compile(r'\\'), '/'),
-                ]))
+                ] + checkers))
         ))
 
 if __name__ == '__main__':
Index: src/zope/configuration/config.py
===================================================================
--- src/zope/configuration/config.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/config.py	(.../branches/jinty-python3)	(revision 125127)
@@ -16,11 +16,12 @@
 See README.txt.
 """
 __docformat__ = 'restructuredtext'
-import __builtin__
+from six.moves import builtins
 import operator
 import os.path
 import sys
 
+import six
 import zope.schema
 
 from keyword import iskeyword
@@ -28,7 +29,7 @@
 from zope.configuration.interfaces import IConfigurationContext
 from zope.configuration.interfaces import IGroupingContext
 from zope.interface.adapter import AdapterRegistry
-from zope.interface import Interface, implements, providedBy
+from zope.interface import Interface, implementer, providedBy
 from zope.configuration import fields
 
 
@@ -121,8 +122,8 @@
         1
         >>> c.resolve('..interface') is zope.interface
         1
-        >>> c.resolve('unicode')
-        <type 'unicode'>
+        >>> c.resolve('str') is str
+        True
         """
 
         name = dottedname.strip()
@@ -140,7 +141,7 @@
         if len(names) == 1:
             # Check for built-in objects
             marker = object()
-            obj = getattr(__builtin__, names[0], marker)
+            obj = getattr(builtins, names[0], marker)
             if obj is not marker:
                 return obj
 
@@ -176,7 +177,7 @@
 
         try:
             mod = __import__(mname, *_import_chickens)
-        except ImportError, v:
+        except ImportError as v:
             if sys.exc_info()[2].tb_next is not None:
                 # ImportError was caused deeper
                 raise
@@ -259,7 +260,7 @@
         >>> c.checkDuplicate('/foo.zcml')
         >>> try:
         ...     c.checkDuplicate('/foo.zcml')
-        ... except ConfigurationError, e:
+        ... except ConfigurationError as e:
         ...     # On Linux the exact msg has /foo, on Windows \foo.
         ...     str(e).endswith("foo.zcml' included more than once")
         True
@@ -273,7 +274,7 @@
         >>> c.checkDuplicate('bar.zcml')
         >>> try:
         ...   c.checkDuplicate(d + os.path.normpath('/bar.zcml'))
-        ... except ConfigurationError, e:
+        ... except ConfigurationError as e:
         ...   str(e).endswith("bar.zcml' included more than once")
         ...
         True
@@ -542,7 +543,7 @@
         r.register([interface], Interface, '', factory)
 
     def document(self, name, schema, usedIn, handler, info, parent=None):
-        if isinstance(name, (str, unicode)):
+        if isinstance(name, six.string_types):
             name = ('', name)
         self._docRegistry.append((name, schema, usedIn, handler, info, parent))
 
@@ -561,6 +562,7 @@
                 "The directive %s cannot be used in this context" % (name, ))
         return f
 
+@implementer(IConfigurationContext)
 class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext):
     """Configuration machine
 
@@ -593,8 +595,6 @@
     A more extensive example can be found in the unit tests.
     """
 
-    implements(IConfigurationContext)
-
     package = None
     basepath = None
     includepath = ()
@@ -663,14 +663,10 @@
         ...   ]
         >>> try:
         ...    v = context.execute_actions()
-        ... except ConfigurationExecutionError, v:
-        ...    pass
-        >>> print v
-        exceptions.AttributeError: 'function' object has no attribute 'xxx'
-          in:
-          oops
+        ... except ConfigurationExecutionError as v:
+        ...    six.print_(repr(v))
+        ConfigurationExecutionError()
 
-
         Note that actions executed before the error still have an effect:
 
         >>> output
@@ -696,7 +692,10 @@
                         raise
                     t, v, tb = sys.exc_info()
                     try:
-                        raise ConfigurationExecutionError(t, v, info), None, tb
+                        six.reraise(
+                                ConfigurationExecutionError,
+                                ConfigurationExecutionError(t, v, info),
+                                tb)
                     finally:
                        del t, v, tb
                 
@@ -741,6 +740,7 @@
         """Finish processing a directive
         """
 
+@implementer(IStackItem)
 class SimpleStackItem(object):
     """Simple stack item
 
@@ -752,8 +752,6 @@
     has been reached.
     """
 
-    implements(IStackItem)
-
     def __init__(self, context, handler, info, *argdata):
         newcontext = GroupingContextDecorator(context)
         newcontext.info = info
@@ -800,6 +798,7 @@
     def finish(self):
         pass
 
+@implementer(IStackItem)
 class GroupingStackItem(RootStackItem):
     """Stack item for a grouping directive
 
@@ -950,8 +949,6 @@
       'order': 0}]
     """
 
-    implements(IStackItem)
-
     def __init__(self, context):
         super(GroupingStackItem, self).__init__(context)
 
@@ -980,6 +977,7 @@
 def noop():
     pass
 
+@implementer(IStackItem)
 class ComplexStackItem(object):
     """Complex stack item
 
@@ -1056,25 +1054,22 @@
     Note that the name passed to ``contained`` is a 2-part name, consisting of
     a namespace and a name within the namespace.
 
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=60).pprint
+    >>> context.actions == [{'args': (),
+    ...                      'callable': f,
+    ...                      'discriminator': 'init',
+    ...                      'includepath': (),
+    ...                      'info': 'foo',
+    ...                      'kw': {},
+    ...                      'order': 0},
+    ...                     {'args': (),
+    ...                      'callable': f,
+    ...                      'discriminator': ('sub', u'av', u'bv'),
+    ...                      'includepath': (),
+    ...                      'info': 'baz',
+    ...                      'kw': {},
+    ...                      'order': 0}]
+    True
 
-    >>> pprint(context.actions)
-    [{'args': (),
-      'callable': f,
-      'discriminator': 'init',
-      'includepath': (),
-      'info': 'foo',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('sub', u'av', u'bv'),
-      'includepath': (),
-      'info': 'baz',
-      'kw': {},
-      'order': 0}]
-
     The new stack item returned by contained is one that doesn't allow
     any more subdirectives,
 
@@ -1085,32 +1080,30 @@
 
     The stack item will call the handler if it is callable.
 
-    >>> pprint(context.actions)
-    [{'args': (),
-      'callable': f,
-      'discriminator': 'init',
-      'includepath': (),
-      'info': 'foo',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('sub', u'av', u'bv'),
-      'includepath': (),
-      'info': 'baz',
-      'kw': {},
-      'order': 0},
-     {'args': (),
-      'callable': f,
-      'discriminator': ('call', u'xv', u'yv'),
-      'includepath': (),
-      'info': 'foo',
-      'kw': {},
-      'order': 0}]
+    >>> context.actions == [{'args': (),
+    ...                      'callable': f,
+    ...                      'discriminator': 'init',
+    ...                      'includepath': (),
+    ...                      'info': 'foo',
+    ...                      'kw': {},
+    ...                      'order': 0},
+    ...                     {'args': (),
+    ...                      'callable': f,
+    ...                      'discriminator': ('sub', u'av', u'bv'),
+    ...                      'includepath': (),
+    ...                      'info': 'baz',
+    ...                      'kw': {},
+    ...                      'order': 0},
+    ...                     {'args': (),
+    ...                      'callable': f,
+    ...                      'discriminator': ('call', u'xv', u'yv'),
+    ...                      'includepath': (),
+    ...                      'info': 'foo',
+    ...                      'kw': {},
+    ...                      'order': 0}]
+    True
     """
 
-    implements(IStackItem)
-
     def __init__(self, meta, context, data, info):
         newcontext = GroupingContextDecorator(context)
         newcontext.info = info
@@ -1142,7 +1135,7 @@
 
         try:
             actions = self.handler()
-        except AttributeError, v:
+        except AttributeError as v:
             if v[0] == '__call__':
                 return # noncallable
             raise
@@ -1159,14 +1152,13 @@
 ##############################################################################
 # Helper classes
 
+@implementer(IConfigurationContext, IGroupingContext)
 class GroupingContextDecorator(ConfigurationContext):
     """Helper mix-in class for building grouping directives
 
     See the discussion (and test) in GroupingStackItem.
     """
 
-    implements(IConfigurationContext, IGroupingContext)
-
     def __init__(self, context, **kw):
         self.context = context
         for name, v in kw.items():
@@ -1204,6 +1196,7 @@
 class IDirectivesContext(IDirectivesInfo, IConfigurationContext):
     pass
 
+@implementer(IDirectivesContext)
 class DirectivesHandler(GroupingContextDecorator):
     """Handler for the directives directive
 
@@ -1211,9 +1204,7 @@
     to the normal directive context.
 
     """
-    implements(IDirectivesContext)
 
-
 class IDirectiveInfo(Interface):
     """Information common to all directive definitions have
     """
@@ -1377,14 +1368,13 @@
 class IComplexDirectiveContext(IFullInfo, IConfigurationContext):
     pass
 
+@implementer(IComplexDirectiveContext)
 class ComplexDirectiveDefinition(GroupingContextDecorator, dict):
     """Handler for defining complex directives
 
     See the description and tests for ComplexStackItem.
     """
 
-    implements(IComplexDirectiveContext)
-
     def before(self):
 
         def factory(context, data, info):
@@ -1557,14 +1547,16 @@
 
         s = data.get(n, data)
         if s is not data:
-            s = unicode(s)
+            s = six.text_type(s)
             del data[n]
 
             try:
                 args[str(name)] = field.fromUnicode(s)
-            except zope.schema.ValidationError, v:
-                raise ConfigurationError(
-                    "Invalid value for", n, str(v)), None, sys.exc_info()[2]
+            except zope.schema.ValidationError as v:
+                six.reraise(
+                        ConfigurationError,
+                        ConfigurationError("Invalid value for", n, str(v)),
+                        sys.exc_info()[2])
         elif field.required:
             # if the default is valid, we can use that:
             default = field.default
@@ -1697,12 +1689,11 @@
 
     def __str__(self):
         r = ["Conflicting configuration actions"]
-        items = self._conflicts.items()
-        items.sort()
+        items = sorted(self._conflicts.items())
         for discriminator, infos in items:
             r.append("  For: %s" % (discriminator, ))
             for info in infos:
-                for line in unicode(info).rstrip().split(u'\n'):
+                for line in six.text_type(info).rstrip().split(u'\n'):
                     r.append(u"    "+line)
 
         return "\n".join(r)
Index: src/zope/configuration/docutils.py
===================================================================
--- src/zope/configuration/docutils.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/docutils.py	(.../branches/jinty-python3)	(revision 125127)
@@ -25,15 +25,16 @@
 
     Examples:
 
-    >>> print wrap('foo bar')[:-2]
+    >>> from six import print_
+    >>> print_(wrap('foo bar')[:-2])
     foo bar
-    >>> print wrap('foo bar', indent=2)[:-2]
+    >>> print_(wrap('foo bar', indent=2)[:-2])
       foo bar
-    >>> print wrap('foo bar, more foo bar', 10)[:-2]
+    >>> print_(wrap('foo bar, more foo bar', 10)[:-2])
     foo bar,
     more foo
     bar
-    >>> print wrap('foo bar, more foo bar', 10, 2)[:-2]
+    >>> print_(wrap('foo bar, more foo bar', 10, 2)[:-2])
       foo bar,
       more foo
       bar
Index: src/zope/configuration/fields.py
===================================================================
--- src/zope/configuration/fields.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/fields.py	(.../branches/jinty-python3)	(revision 125127)
@@ -19,12 +19,13 @@
 from zope.schema.interfaces import IFromUnicode
 from zope.schema.interfaces import ConstraintNotSatisfied
 from zope.configuration.exceptions import ConfigurationError
-from zope.interface import implements
+from zope.interface import implementer
 from zope.configuration.interfaces import InvalidToken
 
 PYIDENTIFIER_REGEX = u'\\A[a-zA-Z_]+[a-zA-Z0-9_]*\\Z'
 pyidentifierPattern = re.compile(PYIDENTIFIER_REGEX)
 
+@implementer(IFromUnicode)
 class PythonIdentifier(schema.TextLine):
     r"""This field describes a python identifier, i.e. a variable name.
 
@@ -50,20 +51,19 @@
     ...     field._validate(value)
     >>>
     >>> from zope import schema
+    >>> from six import print_
     >>>
     >>> for value in (u'3foo', u'foo:', u'\\', u''):
     ...     try:
     ...         field._validate(value)
     ...     except schema.ValidationError:
-    ...         print 'Validation Error'
+    ...         print_('Validation Error')
     Validation Error
     Validation Error
     Validation Error
     Validation Error
 
     """
-    implements(IFromUnicode)
-
     def fromUnicode(self, u):
         return u.strip()
 
@@ -72,6 +72,7 @@
         if pyidentifierPattern.match(value) is None:
             raise schema.ValidationError(value)
 
+@implementer(IFromUnicode)
 class GlobalObject(schema.Field):
     """An object that can be accessed as a module global.
 
@@ -115,8 +116,6 @@
 
     """
 
-    implements(IFromUnicode)
-
     def __init__(self, value_type=None, **kw):
         self.value_type = value_type
         super(GlobalObject, self).__init__(**kw)
@@ -135,7 +134,7 @@
 
         try:
             value = self.context.resolve(name)
-        except ConfigurationError, v:
+        except ConfigurationError as v:
             raise schema.ValidationError(v)
 
         self.validate(value)
@@ -175,6 +174,7 @@
     def __init__(self, **kw):
         super(GlobalInterface, self).__init__(schema.InterfaceField(), **kw)
 
+@implementer(IFromUnicode)
 class Tokens(schema.List):
     """A list that can be read from a space-separated string
 
@@ -215,7 +215,6 @@
     >>>
 
     """
-    implements(IFromUnicode)
 
     def fromUnicode(self, u):
         u = u.strip()
@@ -225,7 +224,7 @@
             for s in u.split():
                 try:
                     v = vt.fromUnicode(s)
-                except schema.ValidationError, v:
+                except schema.ValidationError as v:
                     raise InvalidToken("%s in %s" % (v, u))
                 else:
                     values.append(v)
@@ -236,6 +235,7 @@
 
         return values
 
+@implementer(IFromUnicode)
 class Path(schema.Text):
     r"""A file path name, which may be input as a relative path
 
@@ -248,6 +248,7 @@
 
     We'll be careful to do this in an os-independent fashion.
 
+    >>> import six
     >>> class FauxContext(object):
     ...    def path(self, p):
     ...       return os.path.join(os.sep, 'faux', 'context', p)
@@ -257,7 +258,7 @@
 
     Lets try an absolute path first:
 
-    >>> p = unicode(os.path.join(os.sep, 'a', 'b'))
+    >>> p = six.text_type(os.path.join(os.sep, 'a', 'b'))
     >>> n = field.fromUnicode(p)
     >>> n.split(os.sep)
     [u'', u'a', u'b']
@@ -271,7 +272,7 @@
 
     Now try a relative path:
 
-    >>> p = unicode(os.path.join('a', 'b'))
+    >>> p = six.text_type(os.path.join('a', 'b'))
     >>> n = field.fromUnicode(p)
     >>> n.split(os.sep)
     [u'', u'faux', u'context', u'a', u'b']
@@ -279,8 +280,6 @@
 
     """
 
-    implements(IFromUnicode)
-
     def fromUnicode(self, u):
         u = u.strip()
         if os.path.isabs(u):
@@ -289,6 +288,7 @@
         return self.context.path(u)
 
 
+@implementer(IFromUnicode)
 class Bool(schema.Bool):
     """A boolean value
 
@@ -305,8 +305,6 @@
     0
     """
 
-    implements(IFromUnicode)
-
     def fromUnicode(self, u):
         u = u.lower()
         if u in ('1', 'true', 'yes', 't', 'y'):
@@ -315,6 +313,7 @@
             return False
         raise schema.ValidationError
 
+@implementer(IFromUnicode)
 class MessageID(schema.Text):
     """Text string that should be translated.
 
@@ -377,15 +376,13 @@
 
     >>> i = field.fromUnicode(u"Foo Bar")
     >>> i = field.fromUnicode(u"Hello world!")
-    >>> from pprint import PrettyPrinter
-    >>> pprint=PrettyPrinter(width=70).pprint
-    >>> pprint(context.i18n_strings)
-    {'testing': {u'Foo Bar': [('file location', 8)],
-                 u'Hello world!': [('file location', 8),
-                                   ('file location', 8)]}}
+    >>> context.i18n_strings == {'testing': {u'Foo Bar': [('file location', 8)],
+    ...                                      u'Hello world!': [('file location', 8),
+    ...                                                        ('file location', 8)]}}
+    True
 
     >>> from zope.i18nmessageid import Message
-    >>> isinstance(context.i18n_strings['testing'].keys()[0], Message)
+    >>> isinstance(list(context.i18n_strings['testing'].keys())[0], Message)
     1
 
     Explicit Message IDs
@@ -403,8 +400,6 @@
     True
     """
 
-    implements(IFromUnicode)
-
     __factories = {}
 
     def fromUnicode(self, u):
Index: src/zope/configuration/xmlconfig.py
===================================================================
--- src/zope/configuration/xmlconfig.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/xmlconfig.py	(.../branches/jinty-python3)	(revision 125127)
@@ -25,6 +25,7 @@
 import logging
 import zope.configuration.config as config
 
+import six
 from glob import glob
 from xml.sax import make_parser
 from xml.sax.xmlreader import InputSource
@@ -48,7 +49,7 @@
     info and the wrapped error type and value:
 
     >>> v = ZopeXMLConfigurationError("blah", AttributeError, "xxx")
-    >>> print v
+    >>> six.print_(v)
     'blah'
         AttributeError: xxx
 
@@ -61,13 +62,13 @@
         # Only use the repr of the info. This is because we expect to
         # get a parse info and we only want the location information.
         return "%s\n    %s: %s" % (
-            `self.info`, self.etype.__name__, self.evalue)
+            repr(self.info), self.etype.__name__, self.evalue)
 
 class ZopeSAXParseException(ConfigurationError):
     """Sax Parser errors, reformatted in an emacs friendly way
 
     >>> v = ZopeSAXParseException("foo.xml:12:3:Not well formed")
-    >>> print v
+    >>> six.print_(v)
     File "foo.xml", line 12.3, Not well formed
 
     """
@@ -93,7 +94,7 @@
     >>> info
     File "tests//sample.zcml", line 1.0
 
-    >>> print info
+    >>> six.print_(info)
     File "tests//sample.zcml", line 1.0
 
     >>> info.characters("blah\\n")
@@ -105,7 +106,7 @@
     >>> info
     File "tests//sample.zcml", line 1.0-7.0
 
-    >>> print info
+    >>> six.print_(info)
     File "tests//sample.zcml", line 1.0-7.0
       <configure xmlns='http://namespaces.zope.org/zope'>
         <!-- zope.configure -->
@@ -151,7 +152,8 @@
         except IOError:
             src = "  Could not read source."
         else:
-            lines = f.readlines()[self.line-1:self.eline]
+            with f:
+                lines = f.readlines()[self.line-1:self.eline]
             ecolumn = self.ecolumn
             if lines[-1][ecolumn:ecolumn+2] == '</':
                 # We're pointing to the start of an end tag. Try to find
@@ -177,7 +179,7 @@
                 # unicode won't be printable, at least on my console
                 src = src.encode('ascii','replace')
 
-        return "%s\n%s" % (`self`, src)
+        return "%s\n%s" % (repr(self), src)
 
     def characters(self, characters):
         self.text += characters
@@ -235,8 +237,10 @@
         except:
             if self.testing:
                 raise
-            raise ZopeXMLConfigurationError(info, sys.exc_info()[0],
-                sys.exc_info()[1]), None, sys.exc_info()[2]
+            six.reraise(
+                    ZopeXMLConfigurationError,
+                    ZopeXMLConfigurationError(info, sys.exc_info()[0], sys.exc_info()[1]),
+                    sys.exc_info()[2])
 
         self.context.setInfo(info)
 
@@ -362,8 +366,9 @@
         except:
             if self.testing:
                 raise
-            raise ZopeXMLConfigurationError(info, sys.exc_info()[0],
-                sys.exc_info()[1]), None, sys.exc_info()[2]
+            six.reraise(ZopeXMLConfigurationError,
+                    ZopeXMLConfigurationError(info, sys.exc_info()[0], sys.exc_info()[1]),
+                    sys.exc_info()[2])
 
 
 def processxmlfile(file, context, testing=False):
@@ -379,7 +384,10 @@
     try:
         parser.parse(src)
     except SAXParseException:
-        raise ZopeSAXParseException(sys.exc_info()[1]), None, sys.exc_info()[2]
+        six.reraise(
+                ZopeSAXParseException,
+                ZopeSAXParseException(sys.exc_info()[1]),
+                sys.exc_info()[2])
 
 
 def openInOrPlain(filename):
@@ -399,16 +407,16 @@
 
     >>> here = os.path.dirname(__file__)
     >>> path = os.path.join(here, 'tests', 'samplepackage', 'configure.zcml')
-    >>> f = openInOrPlain(path)
-    >>> f.name[-14:]
+    >>> with openInOrPlain(path) as f:
+    ...     f.name[-14:]
     'configure.zcml'
 
     But if we open foo.zcml, we'll get foo.zcml.in, since there isn't a
     foo.zcml:
 
     >>> path = os.path.join(here, 'tests', 'samplepackage', 'foo.zcml')
-    >>> f = openInOrPlain(path)
-    >>> f.name[-11:]
+    >>> with openInOrPlain(path) as f:
+    ...     f.name[-11:]
     'foo.zcml.in'
 
     Make sure other IOErrors are re-raised.  We need to do this in a
@@ -418,17 +426,17 @@
     >>> try:
     ...     f = openInOrPlain('.')
     ... except IOError:
-    ...     print "passed"
+    ...     six.print_("passed")
     ... else:
-    ...     print "failed"
     ...
+    ...     six.print_("failed")
     passed
 
     """
     try:
         fp = open(filename)
-    except IOError, (code, msg):
-        if code == errno.ENOENT:
+    except IOError as e:
+        if e.errno == errno.ENOENT:
             fn = filename + ".in"
             if os.path.exists(fn):
                 fp = open(fn)
@@ -446,7 +454,7 @@
     files in each package and then link them together.
     """
 
-    file = schema.BytesLine(
+    file = schema.NativeStringLine(
         title=u"Configuration file name",
         description=u"The name of a configuration file to be included/excluded, "
                     u"relative to the directive containing the "
@@ -454,7 +462,7 @@
         required=False,
         )
 
-    files = schema.BytesLine(
+    files = schema.NativeStringLine(
         title=u"Configuration file name pattern",
         description=u"""
         The names of multiple configuration files to be included/excluded,
@@ -530,23 +538,21 @@
 
     if files:
         paths = glob(context.path(files))
-        paths = zip([path.lower() for path in paths], paths)
-        paths.sort()
+        paths = sorted(zip([path.lower() for path in paths], paths))
         paths = [path for (l, path) in paths]
     else:
         paths = [context.path(file)]
 
     for path in paths:
         if context.processFile(path):
-            f = openInOrPlain(path)
-            logger.debug("include %s" % f.name)
+            with openInOrPlain(path) as f:
+                logger.debug("include %s" % f.name)
 
-            context.basepath = os.path.dirname(path)
-            context.includepath = _context.includepath + (f.name, )
-            _context.stack.append(config.GroupingStackItem(context))
+                context.basepath = os.path.dirname(path)
+                context.includepath = _context.includepath + (f.name, )
+                _context.stack.append(config.GroupingStackItem(context))
 
-            processxmlfile(f, context)
-            f.close()
+                processxmlfile(f, context)
             assert _context.stack[-1].context is context
             _context.stack.pop()
 
@@ -571,8 +577,7 @@
 
     if files:
         paths = glob(context.path(files))
-        paths = zip([path.lower() for path in paths], paths)
-        paths.sort()
+        paths = sorted(zip([path.lower() for path in paths], paths))
         paths = [path for (l, path) in paths]
     else:
         paths = [context.path(file)]
@@ -652,7 +657,7 @@
 def string(s, context=None, name="<string>", execute=True):
     """Execute a zcml string
     """
-    from StringIO import StringIO
+    from six import StringIO
 
     if context is None:
         context = config.ConfigurationMachine()
Index: src/zope/configuration/stxdocs.py
===================================================================
--- src/zope/configuration/stxdocs.py	(.../trunk)	(revision 125127)
+++ src/zope/configuration/stxdocs.py	(.../branches/jinty-python3)	(revision 125127)
@@ -28,6 +28,7 @@
         sub-directories with files in them. 
 """
 import sys, os, getopt
+from six import print_
 import zope.configuration
 from zope.schema import getFieldsInOrder 
 from zope.configuration import config, xmlconfig
@@ -35,9 +36,9 @@
 
 def usage(code, msg=''):
     # Python 2.1 required
-    print >> sys.stderr, __doc__
+    print_(__doc__, file=sys.stderr)
     if msg:
-        print >> sys.stderr, msg
+        print_(msg, file=sys.stderr)
     sys.exit(code)
 
 def _directiveDocs(name, schema, handler, info, indent_offset=0):
@@ -88,7 +89,7 @@
 
 def _subDirectiveDocs(subdirs, namespace, name):
     """Appends a list of sub-directives and their full specification."""
-    if subdirs.has_key((namespace, name)):
+    if (namespace, name) in subdirs:
         text = '\n  Subdirectives\n\n'
         sub_dirs = []
         # Simply walk through all sub-directives here.
@@ -127,7 +128,7 @@
     if not path == os.path.abspath(path):
         cwd = os.getcwd()
         # This is for symlinks.
-        if os.environ.has_key('PWD'):
+        if 'PWD' in os.environ:
             cwd = os.environ['PWD']
         path = os.path.normpath(os.path.join(cwd, path))    
     return path
@@ -138,7 +139,7 @@
             sys.argv[1:],
             'h:f:o:',
             ['help'])
-    except getopt.error, msg:
+    except getopt.error as msg:
         usage(1, msg)
 
     zcml_file = None
Index: MANIFEST.in
===================================================================
--- MANIFEST.in	(.../trunk)	(revision 0)
+++ MANIFEST.in	(.../branches/jinty-python3)	(revision 125127)
@@ -0,0 +1,2 @@
+include *.txt
+recursive-include src/zope/configuration *.txt *.zcml *.zcml.in
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.