[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