launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #25386
[Merge] ~cjwatson/pygettextpo:py3 into pygettextpo:main
Colin Watson has proposed merging ~cjwatson/pygettextpo:py3 into pygettextpo:main.
Commit message:
Port to Python 3
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~cjwatson/pygettextpo/+git/pygettextpo/+merge/391412
If this looks OK, then I plan to release this to PyPI, which will allow us to consume it from Launchpad in the normal way and fix https://bugs.launchpad.net/launchpad/+bug/1016333.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/pygettextpo:py3 into pygettextpo:main.
diff --git a/.gitignore b/.gitignore
index 1ff8f33..d0f7d29 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
gettextpo.html
-gettextpo.so
+gettextpo*.so
+/*.egg-info
/build
/dist
/MANIFEST
diff --git a/Makefile b/Makefile
index bcff939..c42eb05 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ check: all
$(PYTHON) test_gettextpo.py -v
clean:
- rm -rf build dist gettextpo.so gettextpo.html
+ rm -rf build dist gettextpo*.so gettextpo.html
gettextpo.so: setup.py gettextpo.c
$(PYTHON) setup.py build_ext -i
diff --git a/README b/README
index 5fdef4e..f690fe3 100644
--- a/README
+++ b/README
@@ -6,11 +6,6 @@ GPL v3 or later as of October 2007 (before that it was GPL v2 or
later) [1]. Therefore, any code using this module must be under a license
compatible with the GPL v2, v3, or any later version.
-Note: when this code was first added to Launchpad, it made use of
-interfaces added in gettext version 0.14.2, which is not yet widely
-distributed (e.g. not in Hoary), and so a trimmed down copy of the
-code is included here. That code probably isn't needed here anymore.
-
See also http://code.google.com/p/pygettextpo/, which is probably a
duplicate project, though it has not had a public release yet (as of
26 May 2009).
diff --git a/gettextpo.c b/gettextpo.c
index d125726..451e284 100644
--- a/gettextpo.c
+++ b/gettextpo.c
@@ -10,11 +10,26 @@
#include <gettext-po.h>
-#define MIN_REQUIRED_GETTEXTPO_VERSION 0x000E02
-#define MIN_MSGCTXT_GETTEXTPO_VERSION 0x000F00
+#define MIN_REQUIRED_GETTEXTPO_VERSION 0x000F00
#if LIBGETTEXTPO_VERSION < MIN_REQUIRED_GETTEXTPO_VERSION
-# error "this module requires gettext >= 0.14.2"
+# error "this module requires gettext >= 0.15"
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+# define PyNativeString_FromString(s) PyUnicode_FromString(s)
+# define PyNativeString_FromFormat(format, ...) \
+ PyUnicode_FromFormat(format, __VA_ARGS__)
+# if PY_VERSION_HEX >= 0x03030000
+# define PyNativeString_Size(s) PyUnicode_GetLength(s)
+# else
+# define PyNativeString_Size(s) PyUnicode_GetSize(s)
+# endif
+#else
+# define PyNativeString_FromString(s) PyBytes_FromString(s)
+# define PyNativeString_FromFormat(format, ...) \
+ PyString_FromFormat(format, __VA_ARGS__)
+# define PyNativeString_Size(s) PyBytes_Size(s)
#endif
static PyObject *gettextpo_error = NULL;
@@ -42,22 +57,22 @@ static PyTypeObject PyPoMessage_Type;
/* ---------------------------- */
/**
- * get_pystring_from_pyobject:
+ * get_pybytes_from_pyobject:
* @object: a PyObject that represents a PyUnicode object.
*
- * Gets a PyString that represents the @object as UTF-8. If the
- * object is a PyString, then assume it is in UTF-8 and return it.
+ * Gets a PyBytes that represents the @object as UTF-8. If the
+ * object is a PyBytes, then assume it is in UTF-8 and return it.
*
- * Return value: a new #PyString object or NULL if there is any error
+ * Return value: a new #PyBytes object or NULL if there is any error
* in which case, PyErr is set.
**/
static PyObject *
-get_pystring_from_pyobject(PyObject *object)
+get_pybytes_from_pyobject(PyObject *object)
{
PyObject *unicode;
PyObject *string;
- if (PyString_Check(object)) {
+ if (PyBytes_Check(object)) {
Py_INCREF(object);
return object;
}
@@ -96,38 +111,6 @@ typedef struct {
static ErrorClosure *error_closure = NULL;
static PyThread_type_lock error_closure_lock= NULL;
-/* The error handler API changed in gettext-0.15 ... */
-#if LIBGETTEXTPO_VERSION < 0x000F00
-
-static void
-error_handler_error(int status, int errnum, const char *format, ...)
-{
- va_list args;
- PyObject *str;
-
- /* printf the string */
- va_start(args, format);
- str = PyString_FromFormatV(format, args);
- va_end(args);
-
- /* store the errors in a list, and as a string */
- PyList_Append(error_closure->error_list,
- Py_BuildValue("(sis)", "error", errnum, str));
- PyString_ConcatAndDel(&(error_closure->error_string), str);
-
- /* if status is nonzero, we are not meant to return */
- if (status != 0) {
- fprintf(stderr, "error_handler_error: status!=0, longjmp'ing out\n");
- longjmp(error_closure->env, 1);
- }
-}
-
-struct po_error_handler error_handler = {
- .error = error_handler_error,
-};
-
-#else /* LIBGETTEXTPO_VERSION >= 0.15.0*/
-
static void
error_handler_xerror(int severity, po_message_t message,
const char *filename, size_t lineno, size_t column,
@@ -136,12 +119,38 @@ error_handler_xerror(int severity, po_message_t message,
PyObject *str;
/* printf the string */
- str = PyString_FromString(message_text);
+#if PY_MAJOR_VERSION >= 3
+ {
+ size_t size = strlen(message_text);
+ if (size > PY_SSIZE_T_MAX)
+ /* We can't raise an exception here, so just truncate it. */
+ size = PY_SSIZE_T_MAX;
+ str = PyUnicode_DecodeUTF8(message_text, (Py_ssize_t)size, "replace");
+ }
+#else
+ str = PyBytes_FromString(message_text);
+#endif
/* store the errors in a list, and as a string */
- PyList_Append(error_closure->error_list, Py_BuildValue("(sis)",
+ PyList_Append(error_closure->error_list, Py_BuildValue("(siO)",
severity == PO_SEVERITY_WARNING ? "warning" : "error", 0, str));
- PyString_ConcatAndDel(&(error_closure->error_string), str);
+#if PY_MAJOR_VERSION >= 3
+ {
+ PyObject *old_error_string = error_closure->error_string;
+ if (PyUnicode_GET_LENGTH(error_closure->error_string)) {
+ error_closure->error_string = PyUnicode_FromFormat(
+ "%U\n%U", error_closure->error_string, str);
+ Py_XDECREF(str);
+ } else
+ error_closure->error_string = str;
+ Py_XDECREF(old_error_string);
+ }
+#else
+ if (PyBytes_GET_SIZE(error_closure->error_string))
+ PyBytes_ConcatAndDel(&(error_closure->error_string),
+ PyBytes_FromString("\n"));
+ PyBytes_ConcatAndDel(&(error_closure->error_string), str);
+#endif
/* if it is a fatal error, we are not meant to return */
if (severity == PO_SEVERITY_FATAL_ERROR) {
@@ -155,8 +164,6 @@ struct po_xerror_handler error_handler = {
.xerror = error_handler_xerror,
};
-#endif /* LIBGETTEXTPO_VERSION */
-
/* ---------------------------- */
static int
@@ -174,7 +181,7 @@ pypo_file_init(PyPoFile *self, PyObject *args, PyObject *kwargs)
ErrorClosure closure;
closure.error_list = PyList_New(0);
- closure.error_string = PyString_FromString("");
+ closure.error_string = PyNativeString_FromString("");
PyThread_acquire_lock(error_closure_lock, WAIT_LOCK);
error_closure = &closure;
@@ -186,7 +193,7 @@ pypo_file_init(PyPoFile *self, PyObject *args, PyObject *kwargs)
error_closure = NULL;
PyThread_release_lock(error_closure_lock);
- if (PyString_Size(closure.error_string) != 0) {
+ if (PyNativeString_Size(closure.error_string) != 0) {
PyObject *exc;
/* set up the exception */
@@ -262,7 +269,7 @@ pypo_file_write(PyPoFile *self, PyObject *args)
return NULL;
closure.error_list = PyList_New(0);
- closure.error_string = PyString_FromString("");
+ closure.error_string = PyNativeString_FromString("");
PyThread_acquire_lock(error_closure_lock, WAIT_LOCK);
error_closure = &closure;
@@ -274,7 +281,7 @@ pypo_file_write(PyPoFile *self, PyObject *args)
error_closure = NULL;
PyThread_release_lock(error_closure_lock);
- if (PyString_Size(closure.error_string) != 0) {
+ if (PyNativeString_Size(closure.error_string) != 0) {
PyObject *exc;
/* set up the exception */
@@ -309,7 +316,7 @@ pypo_file_domain_header(PyPoFile *self, PyObject *args)
header = po_file_domain_header(self->pofile, domain);
if (header) {
- return PyString_FromString(header);
+ return PyBytes_FromString(header);
} else {
Py_RETURN_NONE;
}
@@ -335,7 +342,7 @@ pypo_file_domains(PyPoFile *self, void *closure)
ret = PyList_New(0);
domains = po_file_domains(self->pofile);
while (domains && *domains) {
- PyObject *item = PyString_FromString(*domains);
+ PyObject *item = PyBytes_FromString(*domains);
PyList_Append(ret, item);
Py_DECREF(item);
@@ -356,8 +363,7 @@ PyDoc_STRVAR(doc_PyPoFile_Type,
"filename");
static PyTypeObject PyPoFile_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, /* ob_size */
+ PyVarObject_HEAD_INIT(NULL, 0)
"gettextpo.PoFile", /* tp_name */
sizeof(PyPoFile), /* tp_basicsize */
.tp_flags = Py_TPFLAGS_DEFAULT,
@@ -449,8 +455,7 @@ PyDoc_STRVAR(doc_PyPoMessageIterator_Type,
"Iterator type for PoFile. Iterates over the PoFile's messages.");
static PyTypeObject PyPoMessageIterator_Type = {
- PyObject_HEAD_INIT(NULL)
- 0,
+ PyVarObject_HEAD_INIT(NULL, 0)
"gettextpo.PoMessageIterator",
sizeof(PyPoMessageIterator),
.tp_flags = Py_TPFLAGS_DEFAULT,
@@ -511,8 +516,8 @@ pypo_message_repr(PyPoMessage *self)
if (self->msg)
msgid = po_message_msgid(self->msg);
- return PyString_FromFormat("<PoMessage for '%s'>",
- msgid ? msgid : "(null)");
+ return PyNativeString_FromFormat("<PoMessage for '%s'>",
+ msgid ? msgid : "(null)");
}
static PyObject *
@@ -550,13 +555,13 @@ _message_set_field(PyPoMessage *self, PyObject *args, const char *field,
if (object == Py_None) {
(*setter)(self->msg, "");
} else {
- string = get_pystring_from_pyobject(object);
+ string = get_pybytes_from_pyobject(object);
if (string == NULL)
/* Got an exception */
return NULL;
else {
- value = PyString_AsString(string);
+ value = PyBytes_AsString(string);
(*setter)(self->msg, value);
Py_DECREF(string);
}
@@ -565,9 +570,6 @@ _message_set_field(PyPoMessage *self, PyObject *args, const char *field,
Py_RETURN_NONE;
}
-/* msgctxt support was added in 0.15. */
-#if LIBGETTEXTPO_VERSION >= MIN_MSGCTXT_GETTEXTPO_VERSION
-
PyDoc_STRVAR(doc_pypo_message_set_msgctxt,
"M.set_msgctxt(msgctxt) -> None. Set the msgctxt for this PoMessage");
@@ -577,8 +579,6 @@ pypo_message_set_msgctxt(PyPoMessage *self, PyObject *args)
return _message_set_field(self, args, "O:set_msgctxt",
&po_message_set_msgctxt);
}
-#endif
-
PyDoc_STRVAR(doc_pypo_message_set_msgid,
"M.set_msgid(msgid) -> None. Set the msgid for this PoMessage");
@@ -637,13 +637,13 @@ pypo_message_set_msgstr_plural(PyPoMessage *self, PyObject *args)
if (object == Py_None) {
po_message_set_msgstr_plural(self->msg, index, "");
} else {
- string = get_pystring_from_pyobject(object);
+ string = get_pybytes_from_pyobject(object);
if (string == NULL)
/* Got an exception */
return NULL;
else {
- msgstr = PyString_AsString(string);
+ msgstr = PyBytes_AsString(string);
po_message_set_msgstr_plural(self->msg, index, msgstr);
Py_DECREF(string);
}
@@ -711,7 +711,7 @@ pypo_message_check_format(PyPoMessage *self)
}
closure.error_list = PyList_New(0);
- closure.error_string = PyString_FromString("");
+ closure.error_string = PyNativeString_FromString("");
PyThread_acquire_lock(error_closure_lock, WAIT_LOCK);
error_closure = &closure;
@@ -723,7 +723,7 @@ pypo_message_check_format(PyPoMessage *self)
error_closure = NULL;
PyThread_release_lock(error_closure_lock);
- if (PyString_Size(closure.error_string) != 0) {
+ if (PyNativeString_Size(closure.error_string) != 0) {
PyObject *exc;
/* set up the exception */
@@ -744,11 +744,8 @@ pypo_message_check_format(PyPoMessage *self)
}
static PyMethodDef pypo_message_methods[] = {
-/* msgctxt support was added in 0.15. */
-#if LIBGETTEXTPO_VERSION >= MIN_MSGCTXT_GETTEXTPO_VERSION
{ "set_msgctxt", (PyCFunction)pypo_message_set_msgctxt, METH_VARARGS,
doc_pypo_message_set_msgctxt },
-#endif
{ "set_msgid", (PyCFunction)pypo_message_set_msgid, METH_VARARGS,
doc_pypo_message_set_msgid },
{ "set_msgid_plural", (PyCFunction)pypo_message_set_msgid_plural, METH_VARARGS,
@@ -766,8 +763,6 @@ static PyMethodDef pypo_message_methods[] = {
{ NULL, 0, 0 }
};
-/* msgctxt support was added in 0.15. */
-#if LIBGETTEXTPO_VERSION >= MIN_MSGCTXT_GETTEXTPO_VERSION
PyDoc_STRVAR(doc_pypo_message_msgctxt,
"M.msgctxt -> the msgctxt for this PoMessage.");
@@ -778,10 +773,9 @@ pypo_message_get_msgctxt(PyPoMessage *self, void *closure)
msgctxt = po_message_msgctxt(self->msg);
if (msgctxt)
- return PyString_FromString(msgctxt);
+ return PyBytes_FromString(msgctxt);
Py_RETURN_NONE;
}
-#endif
PyDoc_STRVAR(doc_pypo_message_msgid,
"M.msgid -> the msgid for this PoMessage.");
@@ -793,7 +787,7 @@ pypo_message_get_msgid(PyPoMessage *self, void *closure)
msgid = po_message_msgid(self->msg);
if (msgid)
- return PyString_FromString(msgid);
+ return PyBytes_FromString(msgid);
Py_RETURN_NONE;
}
@@ -807,7 +801,7 @@ pypo_message_get_msgid_plural(PyPoMessage *self, void *closure)
msgid_plural = po_message_msgid_plural(self->msg);
if (msgid_plural)
- return PyString_FromString(msgid_plural);
+ return PyBytes_FromString(msgid_plural);
Py_RETURN_NONE;
}
@@ -821,7 +815,7 @@ pypo_message_get_msgstr(PyPoMessage *self, void *closure)
msgstr = po_message_msgstr(self->msg);
if (msgstr)
- return PyString_FromString(msgstr);
+ return PyBytes_FromString(msgstr);
Py_RETURN_NONE;
}
@@ -839,7 +833,7 @@ pypo_message_get_msgstr_plural(PyPoMessage *self, void *closure)
i = 0;
msgstr = po_message_msgstr_plural(self->msg, i);
while (msgstr) {
- item = PyString_FromString(msgstr);
+ item = PyBytes_FromString(msgstr);
PyList_Append(ret, item);
Py_DECREF(item);
@@ -859,17 +853,14 @@ pypo_message_get_comments(PyPoMessage *self, void *closure)
comments = po_message_comments(self->msg);
if (comments)
- return PyString_FromString(comments);
+ return PyBytes_FromString(comments);
Py_RETURN_NONE;
}
static PyGetSetDef pypo_message_getsets[] = {
-/* msgctxt support was added in 0.15. */
-#if LIBGETTEXTPO_VERSION >= MIN_MSGCTXT_GETTEXTPO_VERSION
{ "msgctxt", (getter)pypo_message_get_msgctxt, (setter)0,
doc_pypo_message_msgctxt },
-#endif
{ "msgid", (getter)pypo_message_get_msgid, (setter)0,
doc_pypo_message_msgid },
{ "msgid_plural", (getter)pypo_message_get_msgid_plural, (setter)0,
@@ -887,8 +878,7 @@ PyDoc_STRVAR(doc_PyPoMessage_Type,
"PyMessage() -> new empty PoMessage instance.");
static PyTypeObject PyPoMessage_Type = {
- PyObject_HEAD_INIT(NULL)
- 0, /* ob_size */
+ PyVarObject_HEAD_INIT(NULL, 0)
"gettextpo.PoMessage", /* tp_name */
sizeof(PyPoMessage), /* tp_basicsize */
.tp_flags = Py_TPFLAGS_DEFAULT,
@@ -908,14 +898,27 @@ PyDoc_STRVAR(doc_gettextpo,
"be of use to translation applications, or applications that need to\n"
"manipulate or validate translations.");
-PyMODINIT_FUNC
-initgettextpo(void)
+#if PY_MAJOR_VERSION >= 3
+# define MOD_DEF(ob, name, doc, methods) \
+ do { \
+ static struct PyModuleDef moduledef = { \
+ PyModuleDef_HEAD_INIT, name, doc, -1, methods \
+ }; \
+ ob = PyModule_Create(&moduledef); \
+ } while (0)
+#else
+# define MOD_DEF(ob, name, doc, methods) \
+ ob = Py_InitModule3(name, methods, doc);
+#endif
+
+static PyObject *
+do_init(void)
{
PyObject *mod;
if (libgettextpo_version < MIN_REQUIRED_GETTEXTPO_VERSION) {
PyErr_SetString(PyExc_RuntimeError, "version of libgettextpo too old");
- return;
+ return NULL;
}
gettextpo_error = PyErr_NewException("gettextpo.error",
@@ -925,25 +928,39 @@ initgettextpo(void)
/* initialise PoMessage type */
#define INIT_TYPE(type) \
- if (!type.ob_type) \
- type.ob_type = &PyType_Type; \
if (!type.tp_alloc) \
type.tp_alloc = PyType_GenericAlloc; \
if (!type.tp_new) \
type.tp_new = PyType_GenericNew; \
if (PyType_Ready(&type) < 0) \
- return
+ return NULL
INIT_TYPE(PyPoFile_Type);
INIT_TYPE(PyPoMessageIterator_Type);
INIT_TYPE(PyPoMessage_Type);
- mod = Py_InitModule3("gettextpo", NULL, doc_gettextpo);
-
+ MOD_DEF(mod, "gettextpo", doc_gettextpo, NULL);
+
Py_INCREF(&PyPoFile_Type);
PyModule_AddObject(mod, "PoFile", (PyObject *)&PyPoFile_Type);
Py_INCREF(&PyPoMessage_Type);
PyModule_AddObject(mod, "PoMessage", (PyObject *)&PyPoMessage_Type);
Py_INCREF(gettextpo_error);
PyModule_AddObject(mod, "error", gettextpo_error);
+
+ return mod;
+}
+
+#if PY_MAJOR_VERSION >= 3
+PyMODINIT_FUNC
+PyInit_gettextpo(void)
+{
+ return do_init();
}
+#else
+PyMODINIT_FUNC
+initgettextpo(void)
+{
+ do_init();
+}
+#endif
diff --git a/setup.py b/setup.py
index 076e994..576f14c 100755
--- a/setup.py
+++ b/setup.py
@@ -3,7 +3,7 @@
# Copyright Canonical Ltd. This software is licensed under the GNU
# Affero General Public License version 3 (see the file LICENSE).
-from distutils.core import setup, Extension
+from setuptools import setup, Extension
gettextpo = Extension(
'gettextpo', ['gettextpo.c'],
@@ -11,7 +11,7 @@ gettextpo = Extension(
setup(
name='pygettextpo',
- version='0.1',
+ version='0.2',
author='Canonical Ltd',
description='A binding for the libgettext-po library',
ext_modules=[gettextpo])
diff --git a/test_gettextpo.py b/test_gettextpo.py
index 41c4207..7dbee20 100644
--- a/test_gettextpo.py
+++ b/test_gettextpo.py
@@ -10,24 +10,24 @@ class PoFileTestCase(unittest.TestCase):
def testCreateEmpty(self):
# Test that we can create an empty pofile object
pofile = gettextpo.PoFile()
- self.assertEquals(list(iter(pofile)), [])
+ self.assertEqual(list(iter(pofile)), [])
def testAddMessage(self):
# Test that we can add messages to a new pofile object
pofile = gettextpo.PoFile()
msg = gettextpo.PoMessage()
- msg.set_msgid('Hello')
+ msg.set_msgid(b'Hello')
poiter = iter(pofile)
poiter.insert(msg)
- self.assertEquals(list(iter(pofile)), [msg])
+ self.assertEqual(list(iter(pofile)), [msg])
def testAddMessageTwice(self):
# A message object can only be added to one pofile object
pofile1 = gettextpo.PoFile()
pofile2 = gettextpo.PoFile()
msg = gettextpo.PoMessage()
- msg.set_msgid('Hello')
+ msg.set_msgid(b'Hello')
poiter = iter(pofile1)
poiter.insert(msg)
@@ -44,39 +44,47 @@ class PoMessageTestCase(unittest.TestCase):
def testSetMsgId(self):
msg = gettextpo.PoMessage()
- msg.set_msgid('Hello')
- self.assertEquals(msg.msgid, 'Hello')
- msg.set_msgid_plural('Hellos')
- self.assertEquals(msg.msgid_plural, 'Hellos')
+ msg.set_msgid(b'Hello')
+ self.assertEqual(msg.msgid, b'Hello')
+ msg.set_msgid_plural(b'Hellos')
+ self.assertEqual(msg.msgid_plural, b'Hellos')
def testSetMsgCtxt(self):
msg = gettextpo.PoMessage()
- msg.set_msgctxt('Hello')
- self.assertEquals(msg.msgctxt, 'Hello')
+ msg.set_msgctxt(b'Hello')
+ self.assertEqual(msg.msgctxt, b'Hello')
def testSetMsgStr(self):
msg = gettextpo.PoMessage()
- msg.set_msgstr('Hello World')
- self.assertEquals(msg.msgstr, 'Hello World')
+ msg.set_msgstr(b'Hello World')
+ self.assertEqual(msg.msgstr, b'Hello World')
def testSetMsgStrPlural(self):
# Test handling of plural msgstrs. The PoMessage object can
# not hold plural msgstrs if the msgid does not have a plural.
msg = gettextpo.PoMessage()
- msg.set_msgid('Something')
- self.assertRaises(ValueError, msg.set_msgstr_plural, 0, 'Zero')
- self.assertEquals(msg.msgstr_plural, [])
+ msg.set_msgid(b'Something')
+ self.assertRaises(ValueError, msg.set_msgstr_plural, 0, b'Zero')
+ self.assertEqual(msg.msgstr_plural, [])
# need to set the plural msgid first, then add the plural msgstrs
- msg.set_msgid_plural('Somethings')
- msg.set_msgstr_plural(0, 'Zero')
- msg.set_msgstr_plural(1, 'One')
- msg.set_msgstr_plural(2, 'Two')
- self.assertEquals(msg.msgstr_plural, ['Zero', 'One', 'Two'])
+ msg.set_msgid_plural(b'Somethings')
+ msg.set_msgstr_plural(0, b'Zero')
+ msg.set_msgstr_plural(1, b'One')
+ msg.set_msgstr_plural(2, b'Two')
+ self.assertEqual(msg.msgstr_plural, [b'Zero', b'One', b'Two'])
class CheckFormatTestCase(unittest.TestCase):
+ def assertGettextPoError(self, expected_errors, msg):
+ with self.assertRaises(gettextpo.error) as raised:
+ msg.check_format()
+ self.assertEqual(expected_errors, raised.exception.error_list)
+ self.assertEqual(
+ "\n".join(message for _, _, message in expected_errors),
+ str(raised.exception))
+
def testGoodFormat(self):
# Check that no exception is raised on a good translation.
@@ -85,36 +93,49 @@ class CheckFormatTestCase(unittest.TestCase):
# format a floating point value, so no error should be raised on
# that kind of change.
msg = gettextpo.PoMessage()
- msg.set_msgid('Hello %s %d %g')
+ msg.set_msgid(b'Hello %s %d %g')
msg.set_format('c-format', True)
- msg.set_msgstr('Bye %s %.2d %f')
+ msg.set_msgstr(b'Bye %s %.2d %f')
# this should run without an exception
msg.check_format()
def testAddFormatSpec(self):
- #Test that an exception is raised when a format string is added.
+ # Test that an exception is raised when a format string is added.
msg = gettextpo.PoMessage()
- msg.set_msgid('No format specifiers')
+ msg.set_msgid(b'No format specifiers')
msg.set_format('c-format', True)
- msg.set_msgstr('One format specifier: %20s')
- self.assertRaises(gettextpo.error, msg.check_format)
+ msg.set_msgstr(b'One format specifier: %20s')
+ expected_errors = [
+ ("error", 0,
+ "number of format specifications in 'msgid' and 'msgstr' does "
+ "not match"),
+ ]
+ self.assertGettextPoError(expected_errors, msg)
def testSwapFormatSpecs(self):
# Test that an exception is raised when format strings are transposed.
msg = gettextpo.PoMessage()
- msg.set_msgid('Spec 1: %s, Spec 2: %d')
+ msg.set_msgid(b'Spec 1: %s, Spec 2: %d')
msg.set_format('c-format', True)
- msg.set_msgstr('Spec 1: %d, Spec 2: %s')
- self.assertRaises(gettextpo.error, msg.check_format)
+ msg.set_msgstr(b'Spec 1: %d, Spec 2: %s')
+ expected_errors = [
+ ("error", 0,
+ "format specifications in 'msgid' and 'msgstr' for argument 1 "
+ "are not the same"),
+ ("error", 0,
+ "format specifications in 'msgid' and 'msgstr' for argument 2 "
+ "are not the same"),
+ ]
+ self.assertGettextPoError(expected_errors, msg)
def testNonFormatString(self):
# Test that no exception is raised if the message is not marked as
# a format string.
msg = gettextpo.PoMessage()
- msg.set_msgid('Spec 1: %s, Spec 2: %d')
+ msg.set_msgid(b'Spec 1: %s, Spec 2: %d')
msg.set_format('c-format', False)
- msg.set_msgstr('Spec 1: %d, Spec 2: %s')
+ msg.set_msgstr(b'Spec 1: %d, Spec 2: %s')
# this should run without an exception
msg.check_format()
@@ -122,7 +143,7 @@ class CheckFormatTestCase(unittest.TestCase):
def testEmptyMsgStr(self):
# Test that empty translations do not trigger a failure.
msg = gettextpo.PoMessage()
- msg.set_msgid('Hello %s')
+ msg.set_msgid(b'Hello %s')
msg.set_format('c-format', True)
msg.set_msgstr(None)
@@ -132,12 +153,12 @@ class CheckFormatTestCase(unittest.TestCase):
def testGoodPlural(self):
# Test that a good plural message passes without error.
msg = gettextpo.PoMessage()
- msg.set_msgid('%d apple')
- msg.set_msgid_plural('%d apples')
+ msg.set_msgid(b'%d apple')
+ msg.set_msgid_plural(b'%d apples')
msg.set_format('c-format', True)
- msg.set_msgstr_plural(0, '%d orange')
- msg.set_msgstr_plural(1, '%d oranges')
- msg.set_msgstr_plural(2, '%d oranges_')
+ msg.set_msgstr_plural(0, b'%d orange')
+ msg.set_msgstr_plural(1, b'%d oranges')
+ msg.set_msgstr_plural(2, b'%d oranges_')
# this should run without an exception
msg.check_format()
@@ -145,29 +166,34 @@ class CheckFormatTestCase(unittest.TestCase):
def testBadPlural(self):
# Test that bad plural translations raise an error error.
msg = gettextpo.PoMessage()
- msg.set_msgid('%d apple')
- msg.set_msgid_plural('%d apples')
+ msg.set_msgid(b'%d apple')
+ msg.set_msgid_plural(b'%d apples')
msg.set_format('c-format', True)
- msg.set_msgstr_plural(0, '%d orange')
- msg.set_msgstr_plural(1, '%d oranges')
- msg.set_msgstr_plural(2, '%g oranges_')
- self.assertRaises(gettextpo.error, msg.check_format)
+ msg.set_msgstr_plural(0, b'%d orange')
+ msg.set_msgstr_plural(1, b'%d oranges')
+ msg.set_msgstr_plural(2, b'%g oranges_')
+ expected_errors = [
+ ("error", 0,
+ "format specifications in 'msgid_plural' and 'msgstr[2]' for "
+ "argument 1 are not the same"),
+ ]
+ self.assertGettextPoError(expected_errors, msg)
def testUnicodeString(self):
# Test that a translation with unicode chars is working.
msg = gettextpo.PoMessage()
msg.set_msgid(u'Carlos Perell\xf3 Mar\xedn')
msg.set_msgstr(u'Carlos Perell\xf3 Mar\xedn')
- self.assertEqual(msg.msgid, 'Carlos Perell\xc3\xb3 Mar\xc3\xadn')
- self.assertEqual(msg.msgstr, 'Carlos Perell\xc3\xb3 Mar\xc3\xadn')
+ self.assertEqual(msg.msgid, b'Carlos Perell\xc3\xb3 Mar\xc3\xadn')
+ self.assertEqual(msg.msgstr, b'Carlos Perell\xc3\xb3 Mar\xc3\xadn')
## XXXX - gettext doesn't seem to check for this one
#
# def testBadPluralMsgId(self):
# # Test that conflicting plural msgids raise errors on their own.
# msg = gettextpo.PoMessage()
-# msg.set_msgid('%d apple')
-# msg.set_msgid_plural('%g apples')
+# msg.set_msgid(b'%d apple')
+# msg.set_msgid_plural(b'%g apples')
# msg.set_format('c-format', True)
# self.assertRaises(gettextpo.error, msg.check_format)
#