Merge ~cjwatson/pygettextpo:py3 into pygettextpo:main
- Git
- lp:~cjwatson/pygettextpo
- py3
- Merge into main
Proposed by
Colin Watson
Status: | Merged |
---|---|
Merged at revision: | 62673d4f42af7d8b14aa7c88e0f8be633d695a32 |
Proposed branch: | ~cjwatson/pygettextpo:py3 |
Merge into: | pygettextpo:main |
Diff against target: |
783 lines (+198/-151) 6 files modified
.gitignore (+2/-1) Makefile (+1/-1) README (+0/-5) gettextpo.c (+111/-94) setup.py (+11/-3) test_gettextpo.py (+73/-47) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tom Wardill (community) | Approve | ||
Review via email: mp+391412@code.launchpad.net |
Commit message
Port to Python 3
Description of the change
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:/
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/.gitignore b/.gitignore | |||
2 | index 1ff8f33..d0f7d29 100644 | |||
3 | --- a/.gitignore | |||
4 | +++ b/.gitignore | |||
5 | @@ -1,5 +1,6 @@ | |||
6 | 1 | gettextpo.html | 1 | gettextpo.html |
8 | 2 | gettextpo.so | 2 | gettextpo*.so |
9 | 3 | /*.egg-info | ||
10 | 3 | /build | 4 | /build |
11 | 4 | /dist | 5 | /dist |
12 | 5 | /MANIFEST | 6 | /MANIFEST |
13 | diff --git a/Makefile b/Makefile | |||
14 | index bcff939..c42eb05 100644 | |||
15 | --- a/Makefile | |||
16 | +++ b/Makefile | |||
17 | @@ -10,7 +10,7 @@ check: all | |||
18 | 10 | $(PYTHON) test_gettextpo.py -v | 10 | $(PYTHON) test_gettextpo.py -v |
19 | 11 | 11 | ||
20 | 12 | clean: | 12 | clean: |
22 | 13 | rm -rf build dist gettextpo.so gettextpo.html | 13 | rm -rf build dist gettextpo*.so gettextpo.html |
23 | 14 | 14 | ||
24 | 15 | gettextpo.so: setup.py gettextpo.c | 15 | gettextpo.so: setup.py gettextpo.c |
25 | 16 | $(PYTHON) setup.py build_ext -i | 16 | $(PYTHON) setup.py build_ext -i |
26 | diff --git a/README b/README | |||
27 | index 5fdef4e..f690fe3 100644 | |||
28 | --- a/README | |||
29 | +++ b/README | |||
30 | @@ -6,11 +6,6 @@ GPL v3 or later as of October 2007 (before that it was GPL v2 or | |||
31 | 6 | later) [1]. Therefore, any code using this module must be under a license | 6 | later) [1]. Therefore, any code using this module must be under a license |
32 | 7 | compatible with the GPL v2, v3, or any later version. | 7 | compatible with the GPL v2, v3, or any later version. |
33 | 8 | 8 | ||
34 | 9 | Note: when this code was first added to Launchpad, it made use of | ||
35 | 10 | interfaces added in gettext version 0.14.2, which is not yet widely | ||
36 | 11 | distributed (e.g. not in Hoary), and so a trimmed down copy of the | ||
37 | 12 | code is included here. That code probably isn't needed here anymore. | ||
38 | 13 | |||
39 | 14 | See also http://code.google.com/p/pygettextpo/, which is probably a | 9 | See also http://code.google.com/p/pygettextpo/, which is probably a |
40 | 15 | duplicate project, though it has not had a public release yet (as of | 10 | duplicate project, though it has not had a public release yet (as of |
41 | 16 | 26 May 2009). | 11 | 26 May 2009). |
42 | diff --git a/gettextpo.c b/gettextpo.c | |||
43 | index d125726..451e284 100644 | |||
44 | --- a/gettextpo.c | |||
45 | +++ b/gettextpo.c | |||
46 | @@ -10,11 +10,26 @@ | |||
47 | 10 | 10 | ||
48 | 11 | #include <gettext-po.h> | 11 | #include <gettext-po.h> |
49 | 12 | 12 | ||
52 | 13 | #define MIN_REQUIRED_GETTEXTPO_VERSION 0x000E02 | 13 | #define MIN_REQUIRED_GETTEXTPO_VERSION 0x000F00 |
51 | 14 | #define MIN_MSGCTXT_GETTEXTPO_VERSION 0x000F00 | ||
53 | 15 | 14 | ||
54 | 16 | #if LIBGETTEXTPO_VERSION < MIN_REQUIRED_GETTEXTPO_VERSION | 15 | #if LIBGETTEXTPO_VERSION < MIN_REQUIRED_GETTEXTPO_VERSION |
56 | 17 | # error "this module requires gettext >= 0.14.2" | 16 | # error "this module requires gettext >= 0.15" |
57 | 17 | #endif | ||
58 | 18 | |||
59 | 19 | #if PY_MAJOR_VERSION >= 3 | ||
60 | 20 | # define PyNativeString_FromString(s) PyUnicode_FromString(s) | ||
61 | 21 | # define PyNativeString_FromFormat(format, ...) \ | ||
62 | 22 | PyUnicode_FromFormat(format, __VA_ARGS__) | ||
63 | 23 | # if PY_VERSION_HEX >= 0x03030000 | ||
64 | 24 | # define PyNativeString_Size(s) PyUnicode_GetLength(s) | ||
65 | 25 | # else | ||
66 | 26 | # define PyNativeString_Size(s) PyUnicode_GetSize(s) | ||
67 | 27 | # endif | ||
68 | 28 | #else | ||
69 | 29 | # define PyNativeString_FromString(s) PyBytes_FromString(s) | ||
70 | 30 | # define PyNativeString_FromFormat(format, ...) \ | ||
71 | 31 | PyString_FromFormat(format, __VA_ARGS__) | ||
72 | 32 | # define PyNativeString_Size(s) PyBytes_Size(s) | ||
73 | 18 | #endif | 33 | #endif |
74 | 19 | 34 | ||
75 | 20 | static PyObject *gettextpo_error = NULL; | 35 | static PyObject *gettextpo_error = NULL; |
76 | @@ -42,22 +57,22 @@ static PyTypeObject PyPoMessage_Type; | |||
77 | 42 | /* ---------------------------- */ | 57 | /* ---------------------------- */ |
78 | 43 | 58 | ||
79 | 44 | /** | 59 | /** |
81 | 45 | * get_pystring_from_pyobject: | 60 | * get_pybytes_from_pyobject: |
82 | 46 | * @object: a PyObject that represents a PyUnicode object. | 61 | * @object: a PyObject that represents a PyUnicode object. |
83 | 47 | * | 62 | * |
86 | 48 | * Gets a PyString that represents the @object as UTF-8. If the | 63 | * Gets a PyBytes that represents the @object as UTF-8. If the |
87 | 49 | * object is a PyString, then assume it is in UTF-8 and return it. | 64 | * object is a PyBytes, then assume it is in UTF-8 and return it. |
88 | 50 | * | 65 | * |
90 | 51 | * Return value: a new #PyString object or NULL if there is any error | 66 | * Return value: a new #PyBytes object or NULL if there is any error |
91 | 52 | * in which case, PyErr is set. | 67 | * in which case, PyErr is set. |
92 | 53 | **/ | 68 | **/ |
93 | 54 | static PyObject * | 69 | static PyObject * |
95 | 55 | get_pystring_from_pyobject(PyObject *object) | 70 | get_pybytes_from_pyobject(PyObject *object) |
96 | 56 | { | 71 | { |
97 | 57 | PyObject *unicode; | 72 | PyObject *unicode; |
98 | 58 | PyObject *string; | 73 | PyObject *string; |
99 | 59 | 74 | ||
101 | 60 | if (PyString_Check(object)) { | 75 | if (PyBytes_Check(object)) { |
102 | 61 | Py_INCREF(object); | 76 | Py_INCREF(object); |
103 | 62 | return object; | 77 | return object; |
104 | 63 | } | 78 | } |
105 | @@ -96,38 +111,6 @@ typedef struct { | |||
106 | 96 | static ErrorClosure *error_closure = NULL; | 111 | static ErrorClosure *error_closure = NULL; |
107 | 97 | static PyThread_type_lock error_closure_lock= NULL; | 112 | static PyThread_type_lock error_closure_lock= NULL; |
108 | 98 | 113 | ||
109 | 99 | /* The error handler API changed in gettext-0.15 ... */ | ||
110 | 100 | #if LIBGETTEXTPO_VERSION < 0x000F00 | ||
111 | 101 | |||
112 | 102 | static void | ||
113 | 103 | error_handler_error(int status, int errnum, const char *format, ...) | ||
114 | 104 | { | ||
115 | 105 | va_list args; | ||
116 | 106 | PyObject *str; | ||
117 | 107 | |||
118 | 108 | /* printf the string */ | ||
119 | 109 | va_start(args, format); | ||
120 | 110 | str = PyString_FromFormatV(format, args); | ||
121 | 111 | va_end(args); | ||
122 | 112 | |||
123 | 113 | /* store the errors in a list, and as a string */ | ||
124 | 114 | PyList_Append(error_closure->error_list, | ||
125 | 115 | Py_BuildValue("(sis)", "error", errnum, str)); | ||
126 | 116 | PyString_ConcatAndDel(&(error_closure->error_string), str); | ||
127 | 117 | |||
128 | 118 | /* if status is nonzero, we are not meant to return */ | ||
129 | 119 | if (status != 0) { | ||
130 | 120 | fprintf(stderr, "error_handler_error: status!=0, longjmp'ing out\n"); | ||
131 | 121 | longjmp(error_closure->env, 1); | ||
132 | 122 | } | ||
133 | 123 | } | ||
134 | 124 | |||
135 | 125 | struct po_error_handler error_handler = { | ||
136 | 126 | .error = error_handler_error, | ||
137 | 127 | }; | ||
138 | 128 | |||
139 | 129 | #else /* LIBGETTEXTPO_VERSION >= 0.15.0*/ | ||
140 | 130 | |||
141 | 131 | static void | 114 | static void |
142 | 132 | error_handler_xerror(int severity, po_message_t message, | 115 | error_handler_xerror(int severity, po_message_t message, |
143 | 133 | const char *filename, size_t lineno, size_t column, | 116 | const char *filename, size_t lineno, size_t column, |
144 | @@ -136,12 +119,38 @@ error_handler_xerror(int severity, po_message_t message, | |||
145 | 136 | PyObject *str; | 119 | PyObject *str; |
146 | 137 | 120 | ||
147 | 138 | /* printf the string */ | 121 | /* printf the string */ |
149 | 139 | str = PyString_FromString(message_text); | 122 | #if PY_MAJOR_VERSION >= 3 |
150 | 123 | { | ||
151 | 124 | size_t size = strlen(message_text); | ||
152 | 125 | if (size > PY_SSIZE_T_MAX) | ||
153 | 126 | /* We can't raise an exception here, so just truncate it. */ | ||
154 | 127 | size = PY_SSIZE_T_MAX; | ||
155 | 128 | str = PyUnicode_DecodeUTF8(message_text, (Py_ssize_t)size, "replace"); | ||
156 | 129 | } | ||
157 | 130 | #else | ||
158 | 131 | str = PyBytes_FromString(message_text); | ||
159 | 132 | #endif | ||
160 | 140 | 133 | ||
161 | 141 | /* store the errors in a list, and as a string */ | 134 | /* store the errors in a list, and as a string */ |
163 | 142 | PyList_Append(error_closure->error_list, Py_BuildValue("(sis)", | 135 | PyList_Append(error_closure->error_list, Py_BuildValue("(siO)", |
164 | 143 | severity == PO_SEVERITY_WARNING ? "warning" : "error", 0, str)); | 136 | severity == PO_SEVERITY_WARNING ? "warning" : "error", 0, str)); |
166 | 144 | PyString_ConcatAndDel(&(error_closure->error_string), str); | 137 | #if PY_MAJOR_VERSION >= 3 |
167 | 138 | { | ||
168 | 139 | PyObject *old_error_string = error_closure->error_string; | ||
169 | 140 | if (PyUnicode_GET_LENGTH(error_closure->error_string)) { | ||
170 | 141 | error_closure->error_string = PyUnicode_FromFormat( | ||
171 | 142 | "%U\n%U", error_closure->error_string, str); | ||
172 | 143 | Py_XDECREF(str); | ||
173 | 144 | } else | ||
174 | 145 | error_closure->error_string = str; | ||
175 | 146 | Py_XDECREF(old_error_string); | ||
176 | 147 | } | ||
177 | 148 | #else | ||
178 | 149 | if (PyBytes_GET_SIZE(error_closure->error_string)) | ||
179 | 150 | PyBytes_ConcatAndDel(&(error_closure->error_string), | ||
180 | 151 | PyBytes_FromString("\n")); | ||
181 | 152 | PyBytes_ConcatAndDel(&(error_closure->error_string), str); | ||
182 | 153 | #endif | ||
183 | 145 | 154 | ||
184 | 146 | /* if it is a fatal error, we are not meant to return */ | 155 | /* if it is a fatal error, we are not meant to return */ |
185 | 147 | if (severity == PO_SEVERITY_FATAL_ERROR) { | 156 | if (severity == PO_SEVERITY_FATAL_ERROR) { |
186 | @@ -155,8 +164,6 @@ struct po_xerror_handler error_handler = { | |||
187 | 155 | .xerror = error_handler_xerror, | 164 | .xerror = error_handler_xerror, |
188 | 156 | }; | 165 | }; |
189 | 157 | 166 | ||
190 | 158 | #endif /* LIBGETTEXTPO_VERSION */ | ||
191 | 159 | |||
192 | 160 | /* ---------------------------- */ | 167 | /* ---------------------------- */ |
193 | 161 | 168 | ||
194 | 162 | static int | 169 | static int |
195 | @@ -174,7 +181,7 @@ pypo_file_init(PyPoFile *self, PyObject *args, PyObject *kwargs) | |||
196 | 174 | ErrorClosure closure; | 181 | ErrorClosure closure; |
197 | 175 | 182 | ||
198 | 176 | closure.error_list = PyList_New(0); | 183 | closure.error_list = PyList_New(0); |
200 | 177 | closure.error_string = PyString_FromString(""); | 184 | closure.error_string = PyNativeString_FromString(""); |
201 | 178 | 185 | ||
202 | 179 | PyThread_acquire_lock(error_closure_lock, WAIT_LOCK); | 186 | PyThread_acquire_lock(error_closure_lock, WAIT_LOCK); |
203 | 180 | error_closure = &closure; | 187 | error_closure = &closure; |
204 | @@ -186,7 +193,7 @@ pypo_file_init(PyPoFile *self, PyObject *args, PyObject *kwargs) | |||
205 | 186 | error_closure = NULL; | 193 | error_closure = NULL; |
206 | 187 | PyThread_release_lock(error_closure_lock); | 194 | PyThread_release_lock(error_closure_lock); |
207 | 188 | 195 | ||
209 | 189 | if (PyString_Size(closure.error_string) != 0) { | 196 | if (PyNativeString_Size(closure.error_string) != 0) { |
210 | 190 | PyObject *exc; | 197 | PyObject *exc; |
211 | 191 | 198 | ||
212 | 192 | /* set up the exception */ | 199 | /* set up the exception */ |
213 | @@ -262,7 +269,7 @@ pypo_file_write(PyPoFile *self, PyObject *args) | |||
214 | 262 | return NULL; | 269 | return NULL; |
215 | 263 | 270 | ||
216 | 264 | closure.error_list = PyList_New(0); | 271 | closure.error_list = PyList_New(0); |
218 | 265 | closure.error_string = PyString_FromString(""); | 272 | closure.error_string = PyNativeString_FromString(""); |
219 | 266 | 273 | ||
220 | 267 | PyThread_acquire_lock(error_closure_lock, WAIT_LOCK); | 274 | PyThread_acquire_lock(error_closure_lock, WAIT_LOCK); |
221 | 268 | error_closure = &closure; | 275 | error_closure = &closure; |
222 | @@ -274,7 +281,7 @@ pypo_file_write(PyPoFile *self, PyObject *args) | |||
223 | 274 | error_closure = NULL; | 281 | error_closure = NULL; |
224 | 275 | PyThread_release_lock(error_closure_lock); | 282 | PyThread_release_lock(error_closure_lock); |
225 | 276 | 283 | ||
227 | 277 | if (PyString_Size(closure.error_string) != 0) { | 284 | if (PyNativeString_Size(closure.error_string) != 0) { |
228 | 278 | PyObject *exc; | 285 | PyObject *exc; |
229 | 279 | 286 | ||
230 | 280 | /* set up the exception */ | 287 | /* set up the exception */ |
231 | @@ -309,7 +316,7 @@ pypo_file_domain_header(PyPoFile *self, PyObject *args) | |||
232 | 309 | 316 | ||
233 | 310 | header = po_file_domain_header(self->pofile, domain); | 317 | header = po_file_domain_header(self->pofile, domain); |
234 | 311 | if (header) { | 318 | if (header) { |
236 | 312 | return PyString_FromString(header); | 319 | return PyBytes_FromString(header); |
237 | 313 | } else { | 320 | } else { |
238 | 314 | Py_RETURN_NONE; | 321 | Py_RETURN_NONE; |
239 | 315 | } | 322 | } |
240 | @@ -335,7 +342,7 @@ pypo_file_domains(PyPoFile *self, void *closure) | |||
241 | 335 | ret = PyList_New(0); | 342 | ret = PyList_New(0); |
242 | 336 | domains = po_file_domains(self->pofile); | 343 | domains = po_file_domains(self->pofile); |
243 | 337 | while (domains && *domains) { | 344 | while (domains && *domains) { |
245 | 338 | PyObject *item = PyString_FromString(*domains); | 345 | PyObject *item = PyBytes_FromString(*domains); |
246 | 339 | 346 | ||
247 | 340 | PyList_Append(ret, item); | 347 | PyList_Append(ret, item); |
248 | 341 | Py_DECREF(item); | 348 | Py_DECREF(item); |
249 | @@ -356,8 +363,7 @@ PyDoc_STRVAR(doc_PyPoFile_Type, | |||
250 | 356 | "filename"); | 363 | "filename"); |
251 | 357 | 364 | ||
252 | 358 | static PyTypeObject PyPoFile_Type = { | 365 | static PyTypeObject PyPoFile_Type = { |
255 | 359 | PyObject_HEAD_INIT(NULL) | 366 | PyVarObject_HEAD_INIT(NULL, 0) |
254 | 360 | 0, /* ob_size */ | ||
256 | 361 | "gettextpo.PoFile", /* tp_name */ | 367 | "gettextpo.PoFile", /* tp_name */ |
257 | 362 | sizeof(PyPoFile), /* tp_basicsize */ | 368 | sizeof(PyPoFile), /* tp_basicsize */ |
258 | 363 | .tp_flags = Py_TPFLAGS_DEFAULT, | 369 | .tp_flags = Py_TPFLAGS_DEFAULT, |
259 | @@ -449,8 +455,7 @@ PyDoc_STRVAR(doc_PyPoMessageIterator_Type, | |||
260 | 449 | "Iterator type for PoFile. Iterates over the PoFile's messages."); | 455 | "Iterator type for PoFile. Iterates over the PoFile's messages."); |
261 | 450 | 456 | ||
262 | 451 | static PyTypeObject PyPoMessageIterator_Type = { | 457 | static PyTypeObject PyPoMessageIterator_Type = { |
265 | 452 | PyObject_HEAD_INIT(NULL) | 458 | PyVarObject_HEAD_INIT(NULL, 0) |
264 | 453 | 0, | ||
266 | 454 | "gettextpo.PoMessageIterator", | 459 | "gettextpo.PoMessageIterator", |
267 | 455 | sizeof(PyPoMessageIterator), | 460 | sizeof(PyPoMessageIterator), |
268 | 456 | .tp_flags = Py_TPFLAGS_DEFAULT, | 461 | .tp_flags = Py_TPFLAGS_DEFAULT, |
269 | @@ -511,8 +516,8 @@ pypo_message_repr(PyPoMessage *self) | |||
270 | 511 | if (self->msg) | 516 | if (self->msg) |
271 | 512 | msgid = po_message_msgid(self->msg); | 517 | msgid = po_message_msgid(self->msg); |
272 | 513 | 518 | ||
275 | 514 | return PyString_FromFormat("<PoMessage for '%s'>", | 519 | return PyNativeString_FromFormat("<PoMessage for '%s'>", |
276 | 515 | msgid ? msgid : "(null)"); | 520 | msgid ? msgid : "(null)"); |
277 | 516 | } | 521 | } |
278 | 517 | 522 | ||
279 | 518 | static PyObject * | 523 | static PyObject * |
280 | @@ -550,13 +555,13 @@ _message_set_field(PyPoMessage *self, PyObject *args, const char *field, | |||
281 | 550 | if (object == Py_None) { | 555 | if (object == Py_None) { |
282 | 551 | (*setter)(self->msg, ""); | 556 | (*setter)(self->msg, ""); |
283 | 552 | } else { | 557 | } else { |
285 | 553 | string = get_pystring_from_pyobject(object); | 558 | string = get_pybytes_from_pyobject(object); |
286 | 554 | 559 | ||
287 | 555 | if (string == NULL) | 560 | if (string == NULL) |
288 | 556 | /* Got an exception */ | 561 | /* Got an exception */ |
289 | 557 | return NULL; | 562 | return NULL; |
290 | 558 | else { | 563 | else { |
292 | 559 | value = PyString_AsString(string); | 564 | value = PyBytes_AsString(string); |
293 | 560 | (*setter)(self->msg, value); | 565 | (*setter)(self->msg, value); |
294 | 561 | Py_DECREF(string); | 566 | Py_DECREF(string); |
295 | 562 | } | 567 | } |
296 | @@ -565,9 +570,6 @@ _message_set_field(PyPoMessage *self, PyObject *args, const char *field, | |||
297 | 565 | Py_RETURN_NONE; | 570 | Py_RETURN_NONE; |
298 | 566 | } | 571 | } |
299 | 567 | 572 | ||
300 | 568 | /* msgctxt support was added in 0.15. */ | ||
301 | 569 | #if LIBGETTEXTPO_VERSION >= MIN_MSGCTXT_GETTEXTPO_VERSION | ||
302 | 570 | |||
303 | 571 | PyDoc_STRVAR(doc_pypo_message_set_msgctxt, | 573 | PyDoc_STRVAR(doc_pypo_message_set_msgctxt, |
304 | 572 | "M.set_msgctxt(msgctxt) -> None. Set the msgctxt for this PoMessage"); | 574 | "M.set_msgctxt(msgctxt) -> None. Set the msgctxt for this PoMessage"); |
305 | 573 | 575 | ||
306 | @@ -577,8 +579,6 @@ pypo_message_set_msgctxt(PyPoMessage *self, PyObject *args) | |||
307 | 577 | return _message_set_field(self, args, "O:set_msgctxt", | 579 | return _message_set_field(self, args, "O:set_msgctxt", |
308 | 578 | &po_message_set_msgctxt); | 580 | &po_message_set_msgctxt); |
309 | 579 | } | 581 | } |
310 | 580 | #endif | ||
311 | 581 | |||
312 | 582 | 582 | ||
313 | 583 | PyDoc_STRVAR(doc_pypo_message_set_msgid, | 583 | PyDoc_STRVAR(doc_pypo_message_set_msgid, |
314 | 584 | "M.set_msgid(msgid) -> None. Set the msgid for this PoMessage"); | 584 | "M.set_msgid(msgid) -> None. Set the msgid for this PoMessage"); |
315 | @@ -637,13 +637,13 @@ pypo_message_set_msgstr_plural(PyPoMessage *self, PyObject *args) | |||
316 | 637 | if (object == Py_None) { | 637 | if (object == Py_None) { |
317 | 638 | po_message_set_msgstr_plural(self->msg, index, ""); | 638 | po_message_set_msgstr_plural(self->msg, index, ""); |
318 | 639 | } else { | 639 | } else { |
320 | 640 | string = get_pystring_from_pyobject(object); | 640 | string = get_pybytes_from_pyobject(object); |
321 | 641 | 641 | ||
322 | 642 | if (string == NULL) | 642 | if (string == NULL) |
323 | 643 | /* Got an exception */ | 643 | /* Got an exception */ |
324 | 644 | return NULL; | 644 | return NULL; |
325 | 645 | else { | 645 | else { |
327 | 646 | msgstr = PyString_AsString(string); | 646 | msgstr = PyBytes_AsString(string); |
328 | 647 | po_message_set_msgstr_plural(self->msg, index, msgstr); | 647 | po_message_set_msgstr_plural(self->msg, index, msgstr); |
329 | 648 | Py_DECREF(string); | 648 | Py_DECREF(string); |
330 | 649 | } | 649 | } |
331 | @@ -711,7 +711,7 @@ pypo_message_check_format(PyPoMessage *self) | |||
332 | 711 | } | 711 | } |
333 | 712 | 712 | ||
334 | 713 | closure.error_list = PyList_New(0); | 713 | closure.error_list = PyList_New(0); |
336 | 714 | closure.error_string = PyString_FromString(""); | 714 | closure.error_string = PyNativeString_FromString(""); |
337 | 715 | 715 | ||
338 | 716 | PyThread_acquire_lock(error_closure_lock, WAIT_LOCK); | 716 | PyThread_acquire_lock(error_closure_lock, WAIT_LOCK); |
339 | 717 | error_closure = &closure; | 717 | error_closure = &closure; |
340 | @@ -723,7 +723,7 @@ pypo_message_check_format(PyPoMessage *self) | |||
341 | 723 | error_closure = NULL; | 723 | error_closure = NULL; |
342 | 724 | PyThread_release_lock(error_closure_lock); | 724 | PyThread_release_lock(error_closure_lock); |
343 | 725 | 725 | ||
345 | 726 | if (PyString_Size(closure.error_string) != 0) { | 726 | if (PyNativeString_Size(closure.error_string) != 0) { |
346 | 727 | PyObject *exc; | 727 | PyObject *exc; |
347 | 728 | 728 | ||
348 | 729 | /* set up the exception */ | 729 | /* set up the exception */ |
349 | @@ -744,11 +744,8 @@ pypo_message_check_format(PyPoMessage *self) | |||
350 | 744 | } | 744 | } |
351 | 745 | 745 | ||
352 | 746 | static PyMethodDef pypo_message_methods[] = { | 746 | static PyMethodDef pypo_message_methods[] = { |
353 | 747 | /* msgctxt support was added in 0.15. */ | ||
354 | 748 | #if LIBGETTEXTPO_VERSION >= MIN_MSGCTXT_GETTEXTPO_VERSION | ||
355 | 749 | { "set_msgctxt", (PyCFunction)pypo_message_set_msgctxt, METH_VARARGS, | 747 | { "set_msgctxt", (PyCFunction)pypo_message_set_msgctxt, METH_VARARGS, |
356 | 750 | doc_pypo_message_set_msgctxt }, | 748 | doc_pypo_message_set_msgctxt }, |
357 | 751 | #endif | ||
358 | 752 | { "set_msgid", (PyCFunction)pypo_message_set_msgid, METH_VARARGS, | 749 | { "set_msgid", (PyCFunction)pypo_message_set_msgid, METH_VARARGS, |
359 | 753 | doc_pypo_message_set_msgid }, | 750 | doc_pypo_message_set_msgid }, |
360 | 754 | { "set_msgid_plural", (PyCFunction)pypo_message_set_msgid_plural, METH_VARARGS, | 751 | { "set_msgid_plural", (PyCFunction)pypo_message_set_msgid_plural, METH_VARARGS, |
361 | @@ -766,8 +763,6 @@ static PyMethodDef pypo_message_methods[] = { | |||
362 | 766 | { NULL, 0, 0 } | 763 | { NULL, 0, 0 } |
363 | 767 | }; | 764 | }; |
364 | 768 | 765 | ||
365 | 769 | /* msgctxt support was added in 0.15. */ | ||
366 | 770 | #if LIBGETTEXTPO_VERSION >= MIN_MSGCTXT_GETTEXTPO_VERSION | ||
367 | 771 | PyDoc_STRVAR(doc_pypo_message_msgctxt, | 766 | PyDoc_STRVAR(doc_pypo_message_msgctxt, |
368 | 772 | "M.msgctxt -> the msgctxt for this PoMessage."); | 767 | "M.msgctxt -> the msgctxt for this PoMessage."); |
369 | 773 | 768 | ||
370 | @@ -778,10 +773,9 @@ pypo_message_get_msgctxt(PyPoMessage *self, void *closure) | |||
371 | 778 | 773 | ||
372 | 779 | msgctxt = po_message_msgctxt(self->msg); | 774 | msgctxt = po_message_msgctxt(self->msg); |
373 | 780 | if (msgctxt) | 775 | if (msgctxt) |
375 | 781 | return PyString_FromString(msgctxt); | 776 | return PyBytes_FromString(msgctxt); |
376 | 782 | Py_RETURN_NONE; | 777 | Py_RETURN_NONE; |
377 | 783 | } | 778 | } |
378 | 784 | #endif | ||
379 | 785 | 779 | ||
380 | 786 | PyDoc_STRVAR(doc_pypo_message_msgid, | 780 | PyDoc_STRVAR(doc_pypo_message_msgid, |
381 | 787 | "M.msgid -> the msgid for this PoMessage."); | 781 | "M.msgid -> the msgid for this PoMessage."); |
382 | @@ -793,7 +787,7 @@ pypo_message_get_msgid(PyPoMessage *self, void *closure) | |||
383 | 793 | 787 | ||
384 | 794 | msgid = po_message_msgid(self->msg); | 788 | msgid = po_message_msgid(self->msg); |
385 | 795 | if (msgid) | 789 | if (msgid) |
387 | 796 | return PyString_FromString(msgid); | 790 | return PyBytes_FromString(msgid); |
388 | 797 | Py_RETURN_NONE; | 791 | Py_RETURN_NONE; |
389 | 798 | } | 792 | } |
390 | 799 | 793 | ||
391 | @@ -807,7 +801,7 @@ pypo_message_get_msgid_plural(PyPoMessage *self, void *closure) | |||
392 | 807 | 801 | ||
393 | 808 | msgid_plural = po_message_msgid_plural(self->msg); | 802 | msgid_plural = po_message_msgid_plural(self->msg); |
394 | 809 | if (msgid_plural) | 803 | if (msgid_plural) |
396 | 810 | return PyString_FromString(msgid_plural); | 804 | return PyBytes_FromString(msgid_plural); |
397 | 811 | Py_RETURN_NONE; | 805 | Py_RETURN_NONE; |
398 | 812 | } | 806 | } |
399 | 813 | 807 | ||
400 | @@ -821,7 +815,7 @@ pypo_message_get_msgstr(PyPoMessage *self, void *closure) | |||
401 | 821 | 815 | ||
402 | 822 | msgstr = po_message_msgstr(self->msg); | 816 | msgstr = po_message_msgstr(self->msg); |
403 | 823 | if (msgstr) | 817 | if (msgstr) |
405 | 824 | return PyString_FromString(msgstr); | 818 | return PyBytes_FromString(msgstr); |
406 | 825 | Py_RETURN_NONE; | 819 | Py_RETURN_NONE; |
407 | 826 | } | 820 | } |
408 | 827 | 821 | ||
409 | @@ -839,7 +833,7 @@ pypo_message_get_msgstr_plural(PyPoMessage *self, void *closure) | |||
410 | 839 | i = 0; | 833 | i = 0; |
411 | 840 | msgstr = po_message_msgstr_plural(self->msg, i); | 834 | msgstr = po_message_msgstr_plural(self->msg, i); |
412 | 841 | while (msgstr) { | 835 | while (msgstr) { |
414 | 842 | item = PyString_FromString(msgstr); | 836 | item = PyBytes_FromString(msgstr); |
415 | 843 | PyList_Append(ret, item); | 837 | PyList_Append(ret, item); |
416 | 844 | Py_DECREF(item); | 838 | Py_DECREF(item); |
417 | 845 | 839 | ||
418 | @@ -859,17 +853,14 @@ pypo_message_get_comments(PyPoMessage *self, void *closure) | |||
419 | 859 | 853 | ||
420 | 860 | comments = po_message_comments(self->msg); | 854 | comments = po_message_comments(self->msg); |
421 | 861 | if (comments) | 855 | if (comments) |
423 | 862 | return PyString_FromString(comments); | 856 | return PyBytes_FromString(comments); |
424 | 863 | Py_RETURN_NONE; | 857 | Py_RETURN_NONE; |
425 | 864 | } | 858 | } |
426 | 865 | 859 | ||
427 | 866 | 860 | ||
428 | 867 | static PyGetSetDef pypo_message_getsets[] = { | 861 | static PyGetSetDef pypo_message_getsets[] = { |
429 | 868 | /* msgctxt support was added in 0.15. */ | ||
430 | 869 | #if LIBGETTEXTPO_VERSION >= MIN_MSGCTXT_GETTEXTPO_VERSION | ||
431 | 870 | { "msgctxt", (getter)pypo_message_get_msgctxt, (setter)0, | 862 | { "msgctxt", (getter)pypo_message_get_msgctxt, (setter)0, |
432 | 871 | doc_pypo_message_msgctxt }, | 863 | doc_pypo_message_msgctxt }, |
433 | 872 | #endif | ||
434 | 873 | { "msgid", (getter)pypo_message_get_msgid, (setter)0, | 864 | { "msgid", (getter)pypo_message_get_msgid, (setter)0, |
435 | 874 | doc_pypo_message_msgid }, | 865 | doc_pypo_message_msgid }, |
436 | 875 | { "msgid_plural", (getter)pypo_message_get_msgid_plural, (setter)0, | 866 | { "msgid_plural", (getter)pypo_message_get_msgid_plural, (setter)0, |
437 | @@ -887,8 +878,7 @@ PyDoc_STRVAR(doc_PyPoMessage_Type, | |||
438 | 887 | "PyMessage() -> new empty PoMessage instance."); | 878 | "PyMessage() -> new empty PoMessage instance."); |
439 | 888 | 879 | ||
440 | 889 | static PyTypeObject PyPoMessage_Type = { | 880 | static PyTypeObject PyPoMessage_Type = { |
443 | 890 | PyObject_HEAD_INIT(NULL) | 881 | PyVarObject_HEAD_INIT(NULL, 0) |
442 | 891 | 0, /* ob_size */ | ||
444 | 892 | "gettextpo.PoMessage", /* tp_name */ | 882 | "gettextpo.PoMessage", /* tp_name */ |
445 | 893 | sizeof(PyPoMessage), /* tp_basicsize */ | 883 | sizeof(PyPoMessage), /* tp_basicsize */ |
446 | 894 | .tp_flags = Py_TPFLAGS_DEFAULT, | 884 | .tp_flags = Py_TPFLAGS_DEFAULT, |
447 | @@ -908,14 +898,27 @@ PyDoc_STRVAR(doc_gettextpo, | |||
448 | 908 | "be of use to translation applications, or applications that need to\n" | 898 | "be of use to translation applications, or applications that need to\n" |
449 | 909 | "manipulate or validate translations."); | 899 | "manipulate or validate translations."); |
450 | 910 | 900 | ||
453 | 911 | PyMODINIT_FUNC | 901 | #if PY_MAJOR_VERSION >= 3 |
454 | 912 | initgettextpo(void) | 902 | # define MOD_DEF(ob, name, doc, methods) \ |
455 | 903 | do { \ | ||
456 | 904 | static struct PyModuleDef moduledef = { \ | ||
457 | 905 | PyModuleDef_HEAD_INIT, name, doc, -1, methods \ | ||
458 | 906 | }; \ | ||
459 | 907 | ob = PyModule_Create(&moduledef); \ | ||
460 | 908 | } while (0) | ||
461 | 909 | #else | ||
462 | 910 | # define MOD_DEF(ob, name, doc, methods) \ | ||
463 | 911 | ob = Py_InitModule3(name, methods, doc); | ||
464 | 912 | #endif | ||
465 | 913 | |||
466 | 914 | static PyObject * | ||
467 | 915 | do_init(void) | ||
468 | 913 | { | 916 | { |
469 | 914 | PyObject *mod; | 917 | PyObject *mod; |
470 | 915 | 918 | ||
471 | 916 | if (libgettextpo_version < MIN_REQUIRED_GETTEXTPO_VERSION) { | 919 | if (libgettextpo_version < MIN_REQUIRED_GETTEXTPO_VERSION) { |
472 | 917 | PyErr_SetString(PyExc_RuntimeError, "version of libgettextpo too old"); | 920 | PyErr_SetString(PyExc_RuntimeError, "version of libgettextpo too old"); |
474 | 918 | return; | 921 | return NULL; |
475 | 919 | } | 922 | } |
476 | 920 | 923 | ||
477 | 921 | gettextpo_error = PyErr_NewException("gettextpo.error", | 924 | gettextpo_error = PyErr_NewException("gettextpo.error", |
478 | @@ -925,25 +928,39 @@ initgettextpo(void) | |||
479 | 925 | 928 | ||
480 | 926 | /* initialise PoMessage type */ | 929 | /* initialise PoMessage type */ |
481 | 927 | #define INIT_TYPE(type) \ | 930 | #define INIT_TYPE(type) \ |
482 | 928 | if (!type.ob_type) \ | ||
483 | 929 | type.ob_type = &PyType_Type; \ | ||
484 | 930 | if (!type.tp_alloc) \ | 931 | if (!type.tp_alloc) \ |
485 | 931 | type.tp_alloc = PyType_GenericAlloc; \ | 932 | type.tp_alloc = PyType_GenericAlloc; \ |
486 | 932 | if (!type.tp_new) \ | 933 | if (!type.tp_new) \ |
487 | 933 | type.tp_new = PyType_GenericNew; \ | 934 | type.tp_new = PyType_GenericNew; \ |
488 | 934 | if (PyType_Ready(&type) < 0) \ | 935 | if (PyType_Ready(&type) < 0) \ |
490 | 935 | return | 936 | return NULL |
491 | 936 | 937 | ||
492 | 937 | INIT_TYPE(PyPoFile_Type); | 938 | INIT_TYPE(PyPoFile_Type); |
493 | 938 | INIT_TYPE(PyPoMessageIterator_Type); | 939 | INIT_TYPE(PyPoMessageIterator_Type); |
494 | 939 | INIT_TYPE(PyPoMessage_Type); | 940 | INIT_TYPE(PyPoMessage_Type); |
495 | 940 | 941 | ||
498 | 941 | mod = Py_InitModule3("gettextpo", NULL, doc_gettextpo); | 942 | MOD_DEF(mod, "gettextpo", doc_gettextpo, NULL); |
499 | 942 | 943 | ||
500 | 943 | Py_INCREF(&PyPoFile_Type); | 944 | Py_INCREF(&PyPoFile_Type); |
501 | 944 | PyModule_AddObject(mod, "PoFile", (PyObject *)&PyPoFile_Type); | 945 | PyModule_AddObject(mod, "PoFile", (PyObject *)&PyPoFile_Type); |
502 | 945 | Py_INCREF(&PyPoMessage_Type); | 946 | Py_INCREF(&PyPoMessage_Type); |
503 | 946 | PyModule_AddObject(mod, "PoMessage", (PyObject *)&PyPoMessage_Type); | 947 | PyModule_AddObject(mod, "PoMessage", (PyObject *)&PyPoMessage_Type); |
504 | 947 | Py_INCREF(gettextpo_error); | 948 | Py_INCREF(gettextpo_error); |
505 | 948 | PyModule_AddObject(mod, "error", gettextpo_error); | 949 | PyModule_AddObject(mod, "error", gettextpo_error); |
506 | 950 | |||
507 | 951 | return mod; | ||
508 | 952 | } | ||
509 | 953 | |||
510 | 954 | #if PY_MAJOR_VERSION >= 3 | ||
511 | 955 | PyMODINIT_FUNC | ||
512 | 956 | PyInit_gettextpo(void) | ||
513 | 957 | { | ||
514 | 958 | return do_init(); | ||
515 | 949 | } | 959 | } |
516 | 960 | #else | ||
517 | 961 | PyMODINIT_FUNC | ||
518 | 962 | initgettextpo(void) | ||
519 | 963 | { | ||
520 | 964 | do_init(); | ||
521 | 965 | } | ||
522 | 966 | #endif | ||
523 | diff --git a/setup.py b/setup.py | |||
524 | index 076e994..6f1a22c 100755 | |||
525 | --- a/setup.py | |||
526 | +++ b/setup.py | |||
527 | @@ -3,7 +3,7 @@ | |||
528 | 3 | # Copyright Canonical Ltd. This software is licensed under the GNU | 3 | # Copyright Canonical Ltd. This software is licensed under the GNU |
529 | 4 | # Affero General Public License version 3 (see the file LICENSE). | 4 | # Affero General Public License version 3 (see the file LICENSE). |
530 | 5 | 5 | ||
532 | 6 | from distutils.core import setup, Extension | 6 | from setuptools import setup, Extension |
533 | 7 | 7 | ||
534 | 8 | gettextpo = Extension( | 8 | gettextpo = Extension( |
535 | 9 | 'gettextpo', ['gettextpo.c'], | 9 | 'gettextpo', ['gettextpo.c'], |
536 | @@ -11,7 +11,15 @@ gettextpo = Extension( | |||
537 | 11 | 11 | ||
538 | 12 | setup( | 12 | setup( |
539 | 13 | name='pygettextpo', | 13 | name='pygettextpo', |
542 | 14 | version='0.1', | 14 | version='0.2', |
543 | 15 | author='Canonical Ltd', | 15 | author='Canonical Ltd.', |
544 | 16 | author_email='lazr-developers@lists.launchpad.net', | ||
545 | 16 | description='A binding for the libgettext-po library', | 17 | description='A binding for the libgettext-po library', |
546 | 18 | url='https://launchpad.net/pygettextpo', | ||
547 | 19 | classifiers=[ | ||
548 | 20 | "License :: OSI Approved :: GNU Affero General Public License v3", | ||
549 | 21 | "Programming Language :: Python", | ||
550 | 22 | "Programming Language :: Python :: 2", | ||
551 | 23 | "Programming Language :: Python :: 3", | ||
552 | 24 | ], | ||
553 | 17 | ext_modules=[gettextpo]) | 25 | ext_modules=[gettextpo]) |
554 | diff --git a/test_gettextpo.py b/test_gettextpo.py | |||
555 | index 41c4207..7dbee20 100644 | |||
556 | --- a/test_gettextpo.py | |||
557 | +++ b/test_gettextpo.py | |||
558 | @@ -10,24 +10,24 @@ class PoFileTestCase(unittest.TestCase): | |||
559 | 10 | def testCreateEmpty(self): | 10 | def testCreateEmpty(self): |
560 | 11 | # Test that we can create an empty pofile object | 11 | # Test that we can create an empty pofile object |
561 | 12 | pofile = gettextpo.PoFile() | 12 | pofile = gettextpo.PoFile() |
563 | 13 | self.assertEquals(list(iter(pofile)), []) | 13 | self.assertEqual(list(iter(pofile)), []) |
564 | 14 | 14 | ||
565 | 15 | def testAddMessage(self): | 15 | def testAddMessage(self): |
566 | 16 | # Test that we can add messages to a new pofile object | 16 | # Test that we can add messages to a new pofile object |
567 | 17 | pofile = gettextpo.PoFile() | 17 | pofile = gettextpo.PoFile() |
568 | 18 | msg = gettextpo.PoMessage() | 18 | msg = gettextpo.PoMessage() |
570 | 19 | msg.set_msgid('Hello') | 19 | msg.set_msgid(b'Hello') |
571 | 20 | poiter = iter(pofile) | 20 | poiter = iter(pofile) |
572 | 21 | poiter.insert(msg) | 21 | poiter.insert(msg) |
573 | 22 | 22 | ||
575 | 23 | self.assertEquals(list(iter(pofile)), [msg]) | 23 | self.assertEqual(list(iter(pofile)), [msg]) |
576 | 24 | 24 | ||
577 | 25 | def testAddMessageTwice(self): | 25 | def testAddMessageTwice(self): |
578 | 26 | # A message object can only be added to one pofile object | 26 | # A message object can only be added to one pofile object |
579 | 27 | pofile1 = gettextpo.PoFile() | 27 | pofile1 = gettextpo.PoFile() |
580 | 28 | pofile2 = gettextpo.PoFile() | 28 | pofile2 = gettextpo.PoFile() |
581 | 29 | msg = gettextpo.PoMessage() | 29 | msg = gettextpo.PoMessage() |
583 | 30 | msg.set_msgid('Hello') | 30 | msg.set_msgid(b'Hello') |
584 | 31 | 31 | ||
585 | 32 | poiter = iter(pofile1) | 32 | poiter = iter(pofile1) |
586 | 33 | poiter.insert(msg) | 33 | poiter.insert(msg) |
587 | @@ -44,39 +44,47 @@ class PoMessageTestCase(unittest.TestCase): | |||
588 | 44 | 44 | ||
589 | 45 | def testSetMsgId(self): | 45 | def testSetMsgId(self): |
590 | 46 | msg = gettextpo.PoMessage() | 46 | msg = gettextpo.PoMessage() |
595 | 47 | msg.set_msgid('Hello') | 47 | msg.set_msgid(b'Hello') |
596 | 48 | self.assertEquals(msg.msgid, 'Hello') | 48 | self.assertEqual(msg.msgid, b'Hello') |
597 | 49 | msg.set_msgid_plural('Hellos') | 49 | msg.set_msgid_plural(b'Hellos') |
598 | 50 | self.assertEquals(msg.msgid_plural, 'Hellos') | 50 | self.assertEqual(msg.msgid_plural, b'Hellos') |
599 | 51 | 51 | ||
600 | 52 | def testSetMsgCtxt(self): | 52 | def testSetMsgCtxt(self): |
601 | 53 | msg = gettextpo.PoMessage() | 53 | msg = gettextpo.PoMessage() |
604 | 54 | msg.set_msgctxt('Hello') | 54 | msg.set_msgctxt(b'Hello') |
605 | 55 | self.assertEquals(msg.msgctxt, 'Hello') | 55 | self.assertEqual(msg.msgctxt, b'Hello') |
606 | 56 | 56 | ||
607 | 57 | def testSetMsgStr(self): | 57 | def testSetMsgStr(self): |
608 | 58 | msg = gettextpo.PoMessage() | 58 | msg = gettextpo.PoMessage() |
611 | 59 | msg.set_msgstr('Hello World') | 59 | msg.set_msgstr(b'Hello World') |
612 | 60 | self.assertEquals(msg.msgstr, 'Hello World') | 60 | self.assertEqual(msg.msgstr, b'Hello World') |
613 | 61 | 61 | ||
614 | 62 | def testSetMsgStrPlural(self): | 62 | def testSetMsgStrPlural(self): |
615 | 63 | # Test handling of plural msgstrs. The PoMessage object can | 63 | # Test handling of plural msgstrs. The PoMessage object can |
616 | 64 | # not hold plural msgstrs if the msgid does not have a plural. | 64 | # not hold plural msgstrs if the msgid does not have a plural. |
617 | 65 | msg = gettextpo.PoMessage() | 65 | msg = gettextpo.PoMessage() |
621 | 66 | msg.set_msgid('Something') | 66 | msg.set_msgid(b'Something') |
622 | 67 | self.assertRaises(ValueError, msg.set_msgstr_plural, 0, 'Zero') | 67 | self.assertRaises(ValueError, msg.set_msgstr_plural, 0, b'Zero') |
623 | 68 | self.assertEquals(msg.msgstr_plural, []) | 68 | self.assertEqual(msg.msgstr_plural, []) |
624 | 69 | 69 | ||
625 | 70 | # need to set the plural msgid first, then add the plural msgstrs | 70 | # need to set the plural msgid first, then add the plural msgstrs |
631 | 71 | msg.set_msgid_plural('Somethings') | 71 | msg.set_msgid_plural(b'Somethings') |
632 | 72 | msg.set_msgstr_plural(0, 'Zero') | 72 | msg.set_msgstr_plural(0, b'Zero') |
633 | 73 | msg.set_msgstr_plural(1, 'One') | 73 | msg.set_msgstr_plural(1, b'One') |
634 | 74 | msg.set_msgstr_plural(2, 'Two') | 74 | msg.set_msgstr_plural(2, b'Two') |
635 | 75 | self.assertEquals(msg.msgstr_plural, ['Zero', 'One', 'Two']) | 75 | self.assertEqual(msg.msgstr_plural, [b'Zero', b'One', b'Two']) |
636 | 76 | 76 | ||
637 | 77 | 77 | ||
638 | 78 | class CheckFormatTestCase(unittest.TestCase): | 78 | class CheckFormatTestCase(unittest.TestCase): |
639 | 79 | 79 | ||
640 | 80 | def assertGettextPoError(self, expected_errors, msg): | ||
641 | 81 | with self.assertRaises(gettextpo.error) as raised: | ||
642 | 82 | msg.check_format() | ||
643 | 83 | self.assertEqual(expected_errors, raised.exception.error_list) | ||
644 | 84 | self.assertEqual( | ||
645 | 85 | "\n".join(message for _, _, message in expected_errors), | ||
646 | 86 | str(raised.exception)) | ||
647 | 87 | |||
648 | 80 | def testGoodFormat(self): | 88 | def testGoodFormat(self): |
649 | 81 | # Check that no exception is raised on a good translation. | 89 | # Check that no exception is raised on a good translation. |
650 | 82 | 90 | ||
651 | @@ -85,36 +93,49 @@ class CheckFormatTestCase(unittest.TestCase): | |||
652 | 85 | # format a floating point value, so no error should be raised on | 93 | # format a floating point value, so no error should be raised on |
653 | 86 | # that kind of change. | 94 | # that kind of change. |
654 | 87 | msg = gettextpo.PoMessage() | 95 | msg = gettextpo.PoMessage() |
656 | 88 | msg.set_msgid('Hello %s %d %g') | 96 | msg.set_msgid(b'Hello %s %d %g') |
657 | 89 | msg.set_format('c-format', True) | 97 | msg.set_format('c-format', True) |
659 | 90 | msg.set_msgstr('Bye %s %.2d %f') | 98 | msg.set_msgstr(b'Bye %s %.2d %f') |
660 | 91 | 99 | ||
661 | 92 | # this should run without an exception | 100 | # this should run without an exception |
662 | 93 | msg.check_format() | 101 | msg.check_format() |
663 | 94 | 102 | ||
664 | 95 | def testAddFormatSpec(self): | 103 | def testAddFormatSpec(self): |
666 | 96 | #Test that an exception is raised when a format string is added. | 104 | # Test that an exception is raised when a format string is added. |
667 | 97 | msg = gettextpo.PoMessage() | 105 | msg = gettextpo.PoMessage() |
669 | 98 | msg.set_msgid('No format specifiers') | 106 | msg.set_msgid(b'No format specifiers') |
670 | 99 | msg.set_format('c-format', True) | 107 | msg.set_format('c-format', True) |
673 | 100 | msg.set_msgstr('One format specifier: %20s') | 108 | msg.set_msgstr(b'One format specifier: %20s') |
674 | 101 | self.assertRaises(gettextpo.error, msg.check_format) | 109 | expected_errors = [ |
675 | 110 | ("error", 0, | ||
676 | 111 | "number of format specifications in 'msgid' and 'msgstr' does " | ||
677 | 112 | "not match"), | ||
678 | 113 | ] | ||
679 | 114 | self.assertGettextPoError(expected_errors, msg) | ||
680 | 102 | 115 | ||
681 | 103 | def testSwapFormatSpecs(self): | 116 | def testSwapFormatSpecs(self): |
682 | 104 | # Test that an exception is raised when format strings are transposed. | 117 | # Test that an exception is raised when format strings are transposed. |
683 | 105 | msg = gettextpo.PoMessage() | 118 | msg = gettextpo.PoMessage() |
685 | 106 | msg.set_msgid('Spec 1: %s, Spec 2: %d') | 119 | msg.set_msgid(b'Spec 1: %s, Spec 2: %d') |
686 | 107 | msg.set_format('c-format', True) | 120 | msg.set_format('c-format', True) |
689 | 108 | msg.set_msgstr('Spec 1: %d, Spec 2: %s') | 121 | msg.set_msgstr(b'Spec 1: %d, Spec 2: %s') |
690 | 109 | self.assertRaises(gettextpo.error, msg.check_format) | 122 | expected_errors = [ |
691 | 123 | ("error", 0, | ||
692 | 124 | "format specifications in 'msgid' and 'msgstr' for argument 1 " | ||
693 | 125 | "are not the same"), | ||
694 | 126 | ("error", 0, | ||
695 | 127 | "format specifications in 'msgid' and 'msgstr' for argument 2 " | ||
696 | 128 | "are not the same"), | ||
697 | 129 | ] | ||
698 | 130 | self.assertGettextPoError(expected_errors, msg) | ||
699 | 110 | 131 | ||
700 | 111 | def testNonFormatString(self): | 132 | def testNonFormatString(self): |
701 | 112 | # Test that no exception is raised if the message is not marked as | 133 | # Test that no exception is raised if the message is not marked as |
702 | 113 | # a format string. | 134 | # a format string. |
703 | 114 | msg = gettextpo.PoMessage() | 135 | msg = gettextpo.PoMessage() |
705 | 115 | msg.set_msgid('Spec 1: %s, Spec 2: %d') | 136 | msg.set_msgid(b'Spec 1: %s, Spec 2: %d') |
706 | 116 | msg.set_format('c-format', False) | 137 | msg.set_format('c-format', False) |
708 | 117 | msg.set_msgstr('Spec 1: %d, Spec 2: %s') | 138 | msg.set_msgstr(b'Spec 1: %d, Spec 2: %s') |
709 | 118 | 139 | ||
710 | 119 | # this should run without an exception | 140 | # this should run without an exception |
711 | 120 | msg.check_format() | 141 | msg.check_format() |
712 | @@ -122,7 +143,7 @@ class CheckFormatTestCase(unittest.TestCase): | |||
713 | 122 | def testEmptyMsgStr(self): | 143 | def testEmptyMsgStr(self): |
714 | 123 | # Test that empty translations do not trigger a failure. | 144 | # Test that empty translations do not trigger a failure. |
715 | 124 | msg = gettextpo.PoMessage() | 145 | msg = gettextpo.PoMessage() |
717 | 125 | msg.set_msgid('Hello %s') | 146 | msg.set_msgid(b'Hello %s') |
718 | 126 | msg.set_format('c-format', True) | 147 | msg.set_format('c-format', True) |
719 | 127 | msg.set_msgstr(None) | 148 | msg.set_msgstr(None) |
720 | 128 | 149 | ||
721 | @@ -132,12 +153,12 @@ class CheckFormatTestCase(unittest.TestCase): | |||
722 | 132 | def testGoodPlural(self): | 153 | def testGoodPlural(self): |
723 | 133 | # Test that a good plural message passes without error. | 154 | # Test that a good plural message passes without error. |
724 | 134 | msg = gettextpo.PoMessage() | 155 | msg = gettextpo.PoMessage() |
727 | 135 | msg.set_msgid('%d apple') | 156 | msg.set_msgid(b'%d apple') |
728 | 136 | msg.set_msgid_plural('%d apples') | 157 | msg.set_msgid_plural(b'%d apples') |
729 | 137 | msg.set_format('c-format', True) | 158 | msg.set_format('c-format', True) |
733 | 138 | msg.set_msgstr_plural(0, '%d orange') | 159 | msg.set_msgstr_plural(0, b'%d orange') |
734 | 139 | msg.set_msgstr_plural(1, '%d oranges') | 160 | msg.set_msgstr_plural(1, b'%d oranges') |
735 | 140 | msg.set_msgstr_plural(2, '%d oranges_') | 161 | msg.set_msgstr_plural(2, b'%d oranges_') |
736 | 141 | 162 | ||
737 | 142 | # this should run without an exception | 163 | # this should run without an exception |
738 | 143 | msg.check_format() | 164 | msg.check_format() |
739 | @@ -145,29 +166,34 @@ class CheckFormatTestCase(unittest.TestCase): | |||
740 | 145 | def testBadPlural(self): | 166 | def testBadPlural(self): |
741 | 146 | # Test that bad plural translations raise an error error. | 167 | # Test that bad plural translations raise an error error. |
742 | 147 | msg = gettextpo.PoMessage() | 168 | msg = gettextpo.PoMessage() |
745 | 148 | msg.set_msgid('%d apple') | 169 | msg.set_msgid(b'%d apple') |
746 | 149 | msg.set_msgid_plural('%d apples') | 170 | msg.set_msgid_plural(b'%d apples') |
747 | 150 | msg.set_format('c-format', True) | 171 | msg.set_format('c-format', True) |
752 | 151 | msg.set_msgstr_plural(0, '%d orange') | 172 | msg.set_msgstr_plural(0, b'%d orange') |
753 | 152 | msg.set_msgstr_plural(1, '%d oranges') | 173 | msg.set_msgstr_plural(1, b'%d oranges') |
754 | 153 | msg.set_msgstr_plural(2, '%g oranges_') | 174 | msg.set_msgstr_plural(2, b'%g oranges_') |
755 | 154 | self.assertRaises(gettextpo.error, msg.check_format) | 175 | expected_errors = [ |
756 | 176 | ("error", 0, | ||
757 | 177 | "format specifications in 'msgid_plural' and 'msgstr[2]' for " | ||
758 | 178 | "argument 1 are not the same"), | ||
759 | 179 | ] | ||
760 | 180 | self.assertGettextPoError(expected_errors, msg) | ||
761 | 155 | 181 | ||
762 | 156 | def testUnicodeString(self): | 182 | def testUnicodeString(self): |
763 | 157 | # Test that a translation with unicode chars is working. | 183 | # Test that a translation with unicode chars is working. |
764 | 158 | msg = gettextpo.PoMessage() | 184 | msg = gettextpo.PoMessage() |
765 | 159 | msg.set_msgid(u'Carlos Perell\xf3 Mar\xedn') | 185 | msg.set_msgid(u'Carlos Perell\xf3 Mar\xedn') |
766 | 160 | msg.set_msgstr(u'Carlos Perell\xf3 Mar\xedn') | 186 | msg.set_msgstr(u'Carlos Perell\xf3 Mar\xedn') |
769 | 161 | self.assertEqual(msg.msgid, 'Carlos Perell\xc3\xb3 Mar\xc3\xadn') | 187 | self.assertEqual(msg.msgid, b'Carlos Perell\xc3\xb3 Mar\xc3\xadn') |
770 | 162 | self.assertEqual(msg.msgstr, 'Carlos Perell\xc3\xb3 Mar\xc3\xadn') | 188 | self.assertEqual(msg.msgstr, b'Carlos Perell\xc3\xb3 Mar\xc3\xadn') |
771 | 163 | 189 | ||
772 | 164 | ## XXXX - gettext doesn't seem to check for this one | 190 | ## XXXX - gettext doesn't seem to check for this one |
773 | 165 | # | 191 | # |
774 | 166 | # def testBadPluralMsgId(self): | 192 | # def testBadPluralMsgId(self): |
775 | 167 | # # Test that conflicting plural msgids raise errors on their own. | 193 | # # Test that conflicting plural msgids raise errors on their own. |
776 | 168 | # msg = gettextpo.PoMessage() | 194 | # msg = gettextpo.PoMessage() |
779 | 169 | # msg.set_msgid('%d apple') | 195 | # msg.set_msgid(b'%d apple') |
780 | 170 | # msg.set_msgid_plural('%g apples') | 196 | # msg.set_msgid_plural(b'%g apples') |
781 | 171 | # msg.set_format('c-format', True) | 197 | # msg.set_format('c-format', True) |
782 | 172 | # self.assertRaises(gettextpo.error, msg.check_format) | 198 | # self.assertRaises(gettextpo.error, msg.check_format) |
783 | 173 | # | 199 | # |
I've built this and poked some strings at it, on py 3.5 and py3.8, seems to work :)