[Checkins] SVN: zope.i18nmessageid/trunk/ Python 3 support.

Lennart Regebro regebro at gmail.com
Mon Mar 14 12:01:54 EDT 2011


Log message for revision 120914:
  Python 3 support.

Changed:
  U   zope.i18nmessageid/trunk/CHANGES.txt
  U   zope.i18nmessageid/trunk/setup.py
  U   zope.i18nmessageid/trunk/src/zope/i18nmessageid/_zope_i18nmessageid_message.c
  U   zope.i18nmessageid/trunk/src/zope/i18nmessageid/message.py
  U   zope.i18nmessageid/trunk/src/zope/i18nmessageid/messages.txt
  U   zope.i18nmessageid/trunk/src/zope/i18nmessageid/tests.py

-=-
Modified: zope.i18nmessageid/trunk/CHANGES.txt
===================================================================
--- zope.i18nmessageid/trunk/CHANGES.txt	2011-03-14 16:00:52 UTC (rev 120913)
+++ zope.i18nmessageid/trunk/CHANGES.txt	2011-03-14 16:01:54 UTC (rev 120914)
@@ -5,7 +5,7 @@
 3.5.4 (unreleased)
 ------------------
 
-- ...
+- Python 3 support.
 
 3.5.3 (2010-08-10)
 ------------------

Modified: zope.i18nmessageid/trunk/setup.py
===================================================================
--- zope.i18nmessageid/trunk/setup.py	2011-03-14 16:00:52 UTC (rev 120913)
+++ zope.i18nmessageid/trunk/setup.py	2011-03-14 16:01:54 UTC (rev 120914)
@@ -28,6 +28,15 @@
 from distutils.errors import DistutilsExecError
 from distutils.errors import DistutilsPlatformError
 
+if sys.version_info >= (3,):
+    extra = dict(use_2to3 = True,
+                 convert_2to3_doctests = [
+                     'src/zope/i18nmessageid/messages.txt',
+                     ],
+                 )
+else:
+    extra = {}
+
 def read(*rnames):
     return open(os.path.join(os.path.dirname(__file__), *rnames)).read()
 
@@ -39,26 +48,33 @@
         try:
             build_ext.run(self)
         
-        except DistutilsPlatformError, e:
-            self._unavailable(e)
+        except DistutilsPlatformError:
+            # The sys.exc_info()[1] is to preserve compatibility with both
+            # Python 2.5 and 3.x, which is needed in setup.py.
+            self._unavailable(sys.exc_info()[1])
 
     def build_extension(self, ext):
         try:
             build_ext.build_extension(self, ext)
         
-        except (CCompilerError, DistutilsExecError), e:
-            self._unavailable(e)
+        except (CCompilerError, DistutilsExecError):
+            # The sys.exc_info()[1] is to preserve compatibility with both
+            # Python 2.5 and 3.x, which is needed in setup.py.
+            self._unavailable(sys.exc_info()[1])
 
     def _unavailable(self, e):
-        print >> sys.stderr, '*' * 80
-        print >> sys.stderr, """WARNING:
+        # Write directly to stderr to preserve compatibility with both
+        # Python 2.5 and 3.x, which is needed in setup.py.
+        sys.stderr.write('*' * 80 + '\n')
+        sys.stderr.write("""WARNING:
 
         An optional code optimization (C extension) could not be compiled.
 
-        Optimizations for this package will not be available!"""
-        print >> sys.stderr
-        print >> sys.stderr, e
-        print >> sys.stderr, '*' * 80
+        Optimizations for this package will not be available!
+        
+        """)
+        sys.stderr.write(str(e) + '\n')
+        sys.stderr.write('*' * 80 + '\n')
 
 
 setup(name='zope.i18nmessageid',
@@ -80,6 +96,7 @@
         'Intended Audience :: Developers',
         'License :: OSI Approved :: Zope Public License',
         'Programming Language :: Python',
+        'Programming Language :: Python :: 3',
         'Natural Language :: English',
         'Operating System :: OS Independent',
         'Topic :: Internet :: WWW/HTTP',
@@ -99,5 +116,6 @@
     test_suite='zope.i18nmessageid.tests.test_suite',
     zip_safe = False,
     cmdclass = {'build_ext':optional_build_ext},
+    **extra
     )
 

Modified: zope.i18nmessageid/trunk/src/zope/i18nmessageid/_zope_i18nmessageid_message.c
===================================================================
--- zope.i18nmessageid/trunk/src/zope/i18nmessageid/_zope_i18nmessageid_message.c	2011-03-14 16:00:52 UTC (rev 120913)
+++ zope.i18nmessageid/trunk/src/zope/i18nmessageid/_zope_i18nmessageid_message.c	2011-03-14 16:01:54 UTC (rev 120914)
@@ -14,6 +14,23 @@
 
 #include "Python.h"
 
+/* Support for Python < 2.6: */
+
+#ifndef Py_TYPE
+  #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
+#endif
+
+#ifndef PyVarObject_HEAD_INIT
+    #define PyVarObject_HEAD_INIT(type, size) \
+	PyObject_HEAD_INIT(type) size,
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+  #define MOD_ERROR_VAL NULL
+#else
+  #define MOD_ERROR_VAL
+#endif
+
 /* these macros make gc support easier; they are only available in
    Python 2.4 and borrowed from there */
 
@@ -112,9 +129,9 @@
 #include "structmember.h"
 
 static PyMemberDef Message_members[] = {
-  { "domain", T_OBJECT, offsetof(Message, domain), RO },
-  { "default", T_OBJECT, offsetof(Message, default_), RO },
-  { "mapping", T_OBJECT, offsetof(Message, mapping), RO },
+  { "domain", T_OBJECT, offsetof(Message, domain), READONLY },
+  { "default", T_OBJECT, offsetof(Message, default_), READONLY },
+  { "mapping", T_OBJECT, offsetof(Message, mapping), READONLY },
   {NULL}	/* Sentinel */
 };
 
@@ -150,7 +167,7 @@
   value = PyObject_CallFunctionObjArgs((PyObject *)&PyUnicode_Type, self, NULL);
   if (value == NULL)
     return NULL;
-  result = Py_BuildValue("(O(OOOO))", self->base.ob_type,
+  result = Py_BuildValue("(O(OOOO))", Py_TYPE(&(self->base)),
 			 value,
 			 self->domain ? self->domain : Py_None,
 			 self->default_ ? self->default_ : Py_None,
@@ -175,10 +192,9 @@
 "no translation domain.  default may also be None, in which case the\n"
 "message id itself implicitly serves as the default text.\n";
 
-statichere PyTypeObject
+static PyTypeObject
 MessageType = {
-	PyObject_HEAD_INIT(NULL)
-	/* ob_size           */ 0,
+	PyVarObject_HEAD_INIT(NULL, 0)
 	/* tp_name           */ "zope.i18nmessageid.message."
                                 "Message",
 	/* tp_basicsize      */ sizeof(Message),
@@ -187,7 +203,7 @@
 	/* tp_print          */ (printfunc)0,
 	/* tp_getattr        */ (getattrfunc)0,
 	/* tp_setattr        */ (setattrfunc)0,
-	/* tp_compare        */ (cmpfunc)0,
+	/* tp_compare        */ 0,
 	/* tp_repr           */ (reprfunc)0,
 	/* tp_as_number      */ 0,
 	/* tp_as_sequence    */ 0,
@@ -228,37 +244,65 @@
 
 
 /* List of methods defined in the module */
-
 static struct PyMethodDef _zope_i18nmessageid_message_methods[] = {
-  {NULL, (PyCFunction)NULL, 0, NULL}         /* sentinel */
+  {NULL, (PyCFunction)NULL, 0, NULL},         /* sentinel */
 };
 
+static char _zope_i18nmessageid_message_module_name[] =
+"_zope_i18nmessageid_message";
 
 static char _zope_i18nmessageid_message_module_documentation[] = 
-"I18n Messages"
-;
+"I18n Messages";
 
+#if PY_MAJOR_VERSION >= 3
+  static struct PyModuleDef moduledef = {
+    PyModuleDef_HEAD_INIT,
+    _zope_i18nmessageid_message_module_name,/* m_name */
+    _zope_i18nmessageid_message_module_documentation,/* m_doc */
+    -1,/* m_size */
+    _zope_i18nmessageid_message_methods,/* m_methods */
+    NULL,/* m_reload */
+    NULL,/* m_traverse */
+    NULL,/* m_clear */
+    NULL,/* m_free */
+  };
+#endif
+
 #ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
-#define PyMODINIT_FUNC void
+  #define PyMODINIT_FUNC void
 #endif
+
 PyMODINIT_FUNC
-init_zope_i18nmessageid_message(void)
+#if PY_MAJOR_VERSION >= 3
+ PyInit__zope_i18nmessageid_message(void)
+#else
+ init_zope_i18nmessageid_message(void)
+#endif
 {
   PyObject *m;
   /* Initialize types: */
   MessageType.tp_base = &PyUnicode_Type;
   if (PyType_Ready(&MessageType) < 0)
-    return;
+    return MOD_ERROR_VAL;
         
   /* Create the module and add the functions */
-  m = Py_InitModule3("_zope_i18nmessageid_message",
+#if PY_MAJOR_VERSION >= 3
+  m = PyModule_Create(&moduledef);
+#else
+  m = Py_InitModule3(_zope_i18nmessageid_message_module_name,
                      _zope_i18nmessageid_message_methods,
                      _zope_i18nmessageid_message_module_documentation);
-
+#endif
+   
   if (m == NULL)
-    return;
+    return MOD_ERROR_VAL;
        
   /* Add types: */
   if (PyModule_AddObject(m, "Message", (PyObject *)&MessageType) < 0)
-    return;
+    return MOD_ERROR_VAL;
+
+#if PY_MAJOR_VERSION >= 3
+  return m;
+#endif
+
 }

Modified: zope.i18nmessageid/trunk/src/zope/i18nmessageid/message.py
===================================================================
--- zope.i18nmessageid/trunk/src/zope/i18nmessageid/message.py	2011-03-14 16:00:52 UTC (rev 120913)
+++ zope.i18nmessageid/trunk/src/zope/i18nmessageid/message.py	2011-03-14 16:01:54 UTC (rev 120914)
@@ -30,13 +30,13 @@
     >>> from zope.i18nmessageid.message import Message
     >>> robot = Message(u"robot-message", 'futurama', u"${name} is a robot.")
 
-    >>> robot
-    u'robot-message'
+    >>> robot == u'robot-message'
+    True
     >>> isinstance(robot, unicode)
     True
 
-    >>> robot.default
-    u'${name} is a robot.'
+    >>> robot.default == u'${name} is a robot.'
+    True
     >>> robot.mapping
 
     >>> robot.domain = "planetexpress"
@@ -55,34 +55,34 @@
     TypeError: readonly attribute
 
     >>> new_robot = Message(robot, mapping={u'name': u'Bender'})
-    >>> new_robot
-    u'robot-message'
-    >>> new_robot.domain
-    'futurama'
-    >>> new_robot.default
-    u'${name} is a robot.'
-    >>> new_robot.mapping
-    {u'name': u'Bender'}
+    >>> new_robot == u'robot-message'
+    True
+    >>> new_robot.domain == 'futurama'
+    True
+    >>> new_robot.default == u'${name} is a robot.'
+    True
+    >>> new_robot.mapping == {u'name': u'Bender'}
+    True
 
     >>> callable, args = new_robot.__reduce__()
     >>> callable is Message
     True
-    >>> args
-    (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
+    >>> args == (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
+    True
 
     >>> fembot = Message(u'fembot')
     >>> callable, args = fembot.__reduce__()
     >>> callable is Message
     True
-    >>> args
-    (u'fembot', None, None, None)
+    >>> args == (u'fembot', None, None, None)
+    True
 
     Check if pickling and unpickling works
     >>> from pickle import dumps, loads
     >>> pystate = dumps(new_robot)
     >>> pickle_bot = loads(pystate)
-    >>> pickle_bot, pickle_bot.domain, pickle_bot.default, pickle_bot.mapping
-    (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
+    >>> (pickle_bot, pickle_bot.domain, pickle_bot.default, pickle_bot.mapping) == (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
+    True
     >>> pickle_bot.__reduce__()[0] is Message
     True
     """

Modified: zope.i18nmessageid/trunk/src/zope/i18nmessageid/messages.txt
===================================================================
--- zope.i18nmessageid/trunk/src/zope/i18nmessageid/messages.txt	2011-03-14 16:00:52 UTC (rev 120913)
+++ zope.i18nmessageid/trunk/src/zope/i18nmessageid/messages.txt	2011-03-14 16:01:54 UTC (rev 120914)
@@ -48,16 +48,16 @@
 
 Messages at first seem like they are unicode strings:
 
-  >>> robot
-  u'robot-message'
+  >>> robot == u'robot-message'
+  True
   >>> isinstance(robot, unicode)
   True
 
 The additional domain, default and mapping information is available
 through attributes:
 
-  >>> robot.default
-  u'${name} is a robot.'
+  >>> robot.default == u'${name} is a robot.'
+  True
   >>> robot.mapping
   >>> robot.domain
   'futurama'
@@ -84,31 +84,30 @@
 message id object:
 
   >>> new_robot = Message(robot, mapping={u'name': u'Bender'})
-  >>> new_robot
-  u'robot-message'
+  >>> new_robot == u'robot-message'
+  True
   >>> new_robot.domain
   'futurama'
-  >>> new_robot.default
-  u'${name} is a robot.'
-  >>> new_robot.mapping
-  {u'name': u'Bender'}
+  >>> new_robot.default == u'${name} is a robot.'
+  True
+  >>> new_robot.mapping == {u'name': u'Bender'}
+  True
 
 Last but not least, messages are reduceable for pickling:
 
   >>> callable, args = new_robot.__reduce__()
   >>> callable is Message
   True
-  >>> args
-  (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
+  >>> args == (u'robot-message', 'futurama', u'${name} is a robot.', {u'name': u'Bender'})
+  True
 
   >>> fembot = Message(u'fembot')
   >>> callable, args = fembot.__reduce__()
   >>> callable is Message
   True
-  >>> args
-  (u'fembot', None, None, None)
+  >>> args == (u'fembot', None, None, None)
+  True
 
-
 Message IDs and backward compatability
 --------------------------------------
 

Modified: zope.i18nmessageid/trunk/src/zope/i18nmessageid/tests.py
===================================================================
--- zope.i18nmessageid/trunk/src/zope/i18nmessageid/tests.py	2011-03-14 16:00:52 UTC (rev 120913)
+++ zope.i18nmessageid/trunk/src/zope/i18nmessageid/tests.py	2011-03-14 16:01:54 UTC (rev 120914)
@@ -86,7 +86,7 @@
         del pickle_bot
 
         # Second check if cMessage is able to load the state of a pyMessage
-        from _zope_i18nmessageid_message import Message
+        from zope.i18nmessageid._zope_i18nmessageid_message import Message
         zope.i18nmessageid.message.Message = Message
         c_bot = loads(pystate) 
         self.assertEqual(c_bot, u'robot-message')
@@ -94,7 +94,7 @@
         self.assertEqual(c_bot.default, u'${name} is a robot.')
         self.assertEqual(c_bot.mapping, {u'name': u'Bender'})
         self.failIf(hasattr(c_bot, '_readonly'))
-        from _zope_i18nmessageid_message import Message as cMessage
+        from zope.i18nmessageid._zope_i18nmessageid_message import Message as cMessage
         self.failUnless(c_bot.__reduce__()[0] is cMessage)
 
         # Last check if pyMessage can load a state of cMessage
@@ -114,15 +114,18 @@
         self.assertEqual(pystate, cstate)
 
 try:
-    from _zope_i18nmessageid_message import Message as import_test
+    from zope.i18nmessageid._zope_i18nmessageid_message import Message as import_test
     def test_suite():
         return unittest.TestSuite((
 	    DocTestSuite('zope.i18nmessageid.message'),
 	    DocFileSuite('messages.txt', package='zope.i18nmessageid'),
             unittest.makeSuite(PickleEqualityTests),
 	    ))
-except ImportError: # pragma: no cover
-    # couldnt import C version
+except ImportError, e: # pragma: no cover
+    print '=' * 80
+    print "Could not import C version:"
+    print e
+    print '=' * 80
     def test_suite():
         return unittest.TestSuite((
 	    DocTestSuite('zope.i18nmessageid.message'),



More information about the checkins mailing list