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) #
_______________________________________________ Mailing list: https://launchpad.net/~launchpad-reviewers Post to : [email protected] Unsubscribe : https://launchpad.net/~launchpad-reviewers More help : https://help.launchpad.net/ListHelp

