[Zope-dev] Python 3.3 port of zope.configuration

Brian Sutherland brian at vanguardistas.net
Wed Apr 11 07:57:19 UTC 2012


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
-------------- next part --------------
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
-------------- next part --------------
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
 
+ at 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"
         )
     
+ at interface.implementer(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
 
+ at 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
         """
 
+ at 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
 
+ at 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
 
+ at 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
 
+ at 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
 
+ at 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
 
+ at 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)
 
+ at 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)
 
+ at 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)
 
+ at 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
 
+ at 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)
 
 
+ at 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
 
+ at 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


More information about the Zope-Dev mailing list