1import os 2import base64 3import gettext 4import unittest 5 6from test import support 7from test.support import os_helper 8 9 10# TODO: 11# - Add new tests, for example for "dgettext" 12# - Remove dummy tests, for example testing for single and double quotes 13# has no sense, it would have if we were testing a parser (i.e. pygettext) 14# - Tests should have only one assert. 15 16GNU_MO_DATA = b'''\ 173hIElQAAAAAJAAAAHAAAAGQAAAAAAAAArAAAAAAAAACsAAAAFQAAAK0AAAAjAAAAwwAAAKEAAADn 18AAAAMAAAAIkBAAAHAAAAugEAABYAAADCAQAAHAAAANkBAAALAAAA9gEAAEIBAAACAgAAFgAAAEUD 19AAAeAAAAXAMAAKEAAAB7AwAAMgAAAB0EAAAFAAAAUAQAABsAAABWBAAAIQAAAHIEAAAJAAAAlAQA 20AABSYXltb25kIEx1eHVyeSBZYWNoLXQAVGhlcmUgaXMgJXMgZmlsZQBUaGVyZSBhcmUgJXMgZmls 21ZXMAVGhpcyBtb2R1bGUgcHJvdmlkZXMgaW50ZXJuYXRpb25hbGl6YXRpb24gYW5kIGxvY2FsaXph 22dGlvbgpzdXBwb3J0IGZvciB5b3VyIFB5dGhvbiBwcm9ncmFtcyBieSBwcm92aWRpbmcgYW4gaW50 23ZXJmYWNlIHRvIHRoZSBHTlUKZ2V0dGV4dCBtZXNzYWdlIGNhdGFsb2cgbGlicmFyeS4AV2l0aCBj 24b250ZXh0BFRoZXJlIGlzICVzIGZpbGUAVGhlcmUgYXJlICVzIGZpbGVzAG11bGx1c2sAbXkgY29u 25dGV4dARudWRnZSBudWRnZQBteSBvdGhlciBjb250ZXh0BG51ZGdlIG51ZGdlAG51ZGdlIG51ZGdl 26AFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDMtMDQtMTEgMTQ6 27MzItMDQwMApMYXN0LVRyYW5zbGF0b3I6IEouIERhdmlkIEliYW5leiA8ai1kYXZpZEBub29zLmZy 28PgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpNSU1FLVZlcnNpb246 29IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4NTktMQpDb250ZW50 30LVRyYW5zZmVyLUVuY29kaW5nOiA4Yml0CkdlbmVyYXRlZC1CeTogcHlnZXR0ZXh0LnB5IDEuMQpQ 31bHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0d29iYmxlciBNYW5n 32cm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFoeXIgY2ViaXZxcmYg 33dmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVnIHNiZSBsYmhlIENs 34Z3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1ciBUQUgKdHJnZ3Jr 35ZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4ASGF5ICVzIGZpY2hlcm8gKGNvbnRleHQpAEhheSAl 36cyBmaWNoZXJvcyAoY29udGV4dCkAYmFjb24Ad2luayB3aW5rIChpbiAibXkgY29udGV4dCIpAHdp 37bmsgd2luayAoaW4gIm15IG90aGVyIGNvbnRleHQiKQB3aW5rIHdpbmsA 38''' 39 40# This data contains an invalid major version number (5) 41# An unexpected major version number should be treated as an error when 42# parsing a .mo file 43 44GNU_MO_DATA_BAD_MAJOR_VERSION = b'''\ 453hIElQAABQAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj 46AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD 47AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh 48eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU 49aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u 50CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh 51Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51 52ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt 53MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k 54YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN 55SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4 56NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0 57ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0 58d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo 59eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn 60IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1 61ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA== 62''' 63 64# This data contains an invalid minor version number (7) 65# An unexpected minor version number only indicates that some of the file's 66# contents may not be able to be read. It does not indicate an error. 67 68GNU_MO_DATA_BAD_MINOR_VERSION = b'''\ 693hIElQcAAAAGAAAAHAAAAEwAAAALAAAAfAAAAAAAAACoAAAAFQAAAKkAAAAjAAAAvwAAAKEAAADj 70AAAABwAAAIUBAAALAAAAjQEAAEUBAACZAQAAFgAAAN8CAAAeAAAA9gIAAKEAAAAVAwAABQAAALcD 71AAAJAAAAvQMAAAEAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAABQAAAAYAAAACAAAAAFJh 72eW1vbmQgTHV4dXJ5IFlhY2gtdABUaGVyZSBpcyAlcyBmaWxlAFRoZXJlIGFyZSAlcyBmaWxlcwBU 73aGlzIG1vZHVsZSBwcm92aWRlcyBpbnRlcm5hdGlvbmFsaXphdGlvbiBhbmQgbG9jYWxpemF0aW9u 74CnN1cHBvcnQgZm9yIHlvdXIgUHl0aG9uIHByb2dyYW1zIGJ5IHByb3ZpZGluZyBhbiBpbnRlcmZh 75Y2UgdG8gdGhlIEdOVQpnZXR0ZXh0IG1lc3NhZ2UgY2F0YWxvZyBsaWJyYXJ5LgBtdWxsdXNrAG51 76ZGdlIG51ZGdlAFByb2plY3QtSWQtVmVyc2lvbjogMi4wClBPLVJldmlzaW9uLURhdGU6IDIwMDAt 77MDgtMjkgMTI6MTktMDQ6MDAKTGFzdC1UcmFuc2xhdG9yOiBKLiBEYXZpZCBJYsOhw7FleiA8ai1k 78YXZpZEBub29zLmZyPgpMYW5ndWFnZS1UZWFtOiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpN 79SU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9aXNvLTg4 80NTktMQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBub25lCkdlbmVyYXRlZC1CeTogcHlnZXR0 81ZXh0LnB5IDEuMQpQbHVyYWwtRm9ybXM6IG5wbHVyYWxzPTI7IHBsdXJhbD1uIT0xOwoAVGhyb2F0 82d29iYmxlciBNYW5ncm92ZQBIYXkgJXMgZmljaGVybwBIYXkgJXMgZmljaGVyb3MAR3V2ZiB6YnFo 83eXIgY2ViaXZxcmYgdmFncmVhbmd2YmFueXZtbmd2YmEgbmFxIHlicG55dm1uZ3ZiYQpmaGNjYmVn 84IHNiZSBsYmhlIENsZ3ViYSBjZWJ0ZW56ZiBvbCBjZWJpdnF2YXQgbmEgdmFncmVzbnByIGdiIGd1 85ciBUQUgKdHJnZ3JrZyB6cmZmbnRyIHBuZ255YnQgeXZvZW5lbC4AYmFjb24Ad2luayB3aW5rAA== 86''' 87 88 89UMO_DATA = b'''\ 903hIElQAAAAADAAAAHAAAADQAAAAAAAAAAAAAAAAAAABMAAAABAAAAE0AAAAQAAAAUgAAAA8BAABj 91AAAABAAAAHMBAAAWAAAAeAEAAABhYsOeAG15Y29udGV4dMOeBGFiw54AUHJvamVjdC1JZC1WZXJz 92aW9uOiAyLjAKUE8tUmV2aXNpb24tRGF0ZTogMjAwMy0wNC0xMSAxMjo0Mi0wNDAwCkxhc3QtVHJh 93bnNsYXRvcjogQmFycnkgQS4gV0Fyc2F3IDxiYXJyeUBweXRob24ub3JnPgpMYW5ndWFnZS1UZWFt 94OiBYWCA8cHl0aG9uLWRldkBweXRob24ub3JnPgpNSU1FLVZlcnNpb246IDEuMApDb250ZW50LVR5 95cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9dXRmLTgKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzog 96N2JpdApHZW5lcmF0ZWQtQnk6IG1hbnVhbGx5CgDCpHl6AMKkeXogKGNvbnRleHQgdmVyc2lvbikA 97''' 98 99MMO_DATA = b'''\ 1003hIElQAAAAABAAAAHAAAACQAAAADAAAALAAAAAAAAAA4AAAAeAEAADkAAAABAAAAAAAAAAAAAAAA 101UHJvamVjdC1JZC1WZXJzaW9uOiBObyBQcm9qZWN0IDAuMApQT1QtQ3JlYXRpb24tRGF0ZTogV2Vk 102IERlYyAxMSAwNzo0NDoxNSAyMDAyClBPLVJldmlzaW9uLURhdGU6IDIwMDItMDgtMTQgMDE6MTg6 103NTgrMDA6MDAKTGFzdC1UcmFuc2xhdG9yOiBKb2huIERvZSA8amRvZUBleGFtcGxlLmNvbT4KSmFu 104ZSBGb29iYXIgPGpmb29iYXJAZXhhbXBsZS5jb20+Ckxhbmd1YWdlLVRlYW06IHh4IDx4eEBleGFt 105cGxlLmNvbT4KTUlNRS1WZXJzaW9uOiAxLjAKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFy 106c2V0PWlzby04ODU5LTE1CkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IHF1b3RlZC1wcmludGFi 107bGUKR2VuZXJhdGVkLUJ5OiBweWdldHRleHQucHkgMS4zCgA= 108''' 109 110LOCALEDIR = os.path.join('xx', 'LC_MESSAGES') 111MOFILE = os.path.join(LOCALEDIR, 'gettext.mo') 112MOFILE_BAD_MAJOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_major_version.mo') 113MOFILE_BAD_MINOR_VERSION = os.path.join(LOCALEDIR, 'gettext_bad_minor_version.mo') 114UMOFILE = os.path.join(LOCALEDIR, 'ugettext.mo') 115MMOFILE = os.path.join(LOCALEDIR, 'metadata.mo') 116 117 118class GettextBaseTest(unittest.TestCase): 119 def setUp(self): 120 self.addCleanup(os_helper.rmtree, os.path.split(LOCALEDIR)[0]) 121 if not os.path.isdir(LOCALEDIR): 122 os.makedirs(LOCALEDIR) 123 with open(MOFILE, 'wb') as fp: 124 fp.write(base64.decodebytes(GNU_MO_DATA)) 125 with open(MOFILE_BAD_MAJOR_VERSION, 'wb') as fp: 126 fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MAJOR_VERSION)) 127 with open(MOFILE_BAD_MINOR_VERSION, 'wb') as fp: 128 fp.write(base64.decodebytes(GNU_MO_DATA_BAD_MINOR_VERSION)) 129 with open(UMOFILE, 'wb') as fp: 130 fp.write(base64.decodebytes(UMO_DATA)) 131 with open(MMOFILE, 'wb') as fp: 132 fp.write(base64.decodebytes(MMO_DATA)) 133 self.env = self.enterContext(os_helper.EnvironmentVarGuard()) 134 self.env['LANGUAGE'] = 'xx' 135 gettext._translations.clear() 136 137 138GNU_MO_DATA_ISSUE_17898 = b'''\ 1393hIElQAAAAABAAAAHAAAACQAAAAAAAAAAAAAAAAAAAAsAAAAggAAAC0AAAAAUGx1cmFsLUZvcm1z 140OiBucGx1cmFscz0yOyBwbHVyYWw9KG4gIT0gMSk7CiMtIy0jLSMtIyAgbWVzc2FnZXMucG8gKEVk 141WCBTdHVkaW8pICAjLSMtIy0jLSMKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFyc2V0PVVU 142Ri04CgA= 143''' 144 145class GettextTestCase1(GettextBaseTest): 146 def setUp(self): 147 GettextBaseTest.setUp(self) 148 self.localedir = os.curdir 149 self.mofile = MOFILE 150 gettext.install('gettext', self.localedir, names=['pgettext']) 151 152 def test_some_translations(self): 153 eq = self.assertEqual 154 # test some translations 155 eq(_('albatross'), 'albatross') 156 eq(_('mullusk'), 'bacon') 157 eq(_(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove') 158 eq(_(r'nudge nudge'), 'wink wink') 159 160 def test_some_translations_with_context(self): 161 eq = self.assertEqual 162 eq(pgettext('my context', 'nudge nudge'), 163 'wink wink (in "my context")') 164 eq(pgettext('my other context', 'nudge nudge'), 165 'wink wink (in "my other context")') 166 167 def test_double_quotes(self): 168 eq = self.assertEqual 169 # double quotes 170 eq(_("albatross"), 'albatross') 171 eq(_("mullusk"), 'bacon') 172 eq(_(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove') 173 eq(_(r"nudge nudge"), 'wink wink') 174 175 def test_triple_single_quotes(self): 176 eq = self.assertEqual 177 # triple single quotes 178 eq(_('''albatross'''), 'albatross') 179 eq(_('''mullusk'''), 'bacon') 180 eq(_(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove') 181 eq(_(r'''nudge nudge'''), 'wink wink') 182 183 def test_triple_double_quotes(self): 184 eq = self.assertEqual 185 # triple double quotes 186 eq(_("""albatross"""), 'albatross') 187 eq(_("""mullusk"""), 'bacon') 188 eq(_(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove') 189 eq(_(r"""nudge nudge"""), 'wink wink') 190 191 def test_multiline_strings(self): 192 eq = self.assertEqual 193 # multiline strings 194 eq(_('''This module provides internationalization and localization 195support for your Python programs by providing an interface to the GNU 196gettext message catalog library.'''), 197 '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba 198fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH 199trggrkg zrffntr pngnybt yvoenel.''') 200 201 def test_the_alternative_interface(self): 202 eq = self.assertEqual 203 neq = self.assertNotEqual 204 # test the alternative interface 205 with open(self.mofile, 'rb') as fp: 206 t = gettext.GNUTranslations(fp) 207 # Install the translation object 208 t.install() 209 eq(_('nudge nudge'), 'wink wink') 210 # Try unicode return type 211 t.install() 212 eq(_('mullusk'), 'bacon') 213 # Test installation of other methods 214 import builtins 215 t.install(names=["gettext", "ngettext"]) 216 eq(_, t.gettext) 217 eq(builtins.gettext, t.gettext) 218 eq(ngettext, t.ngettext) 219 neq(pgettext, t.pgettext) 220 del builtins.gettext 221 del builtins.ngettext 222 223 224class GettextTestCase2(GettextBaseTest): 225 def setUp(self): 226 GettextBaseTest.setUp(self) 227 self.localedir = os.curdir 228 # Set up the bindings 229 gettext.bindtextdomain('gettext', self.localedir) 230 gettext.textdomain('gettext') 231 # For convenience 232 self._ = gettext.gettext 233 234 def test_bindtextdomain(self): 235 self.assertEqual(gettext.bindtextdomain('gettext'), self.localedir) 236 237 def test_textdomain(self): 238 self.assertEqual(gettext.textdomain(), 'gettext') 239 240 def test_bad_major_version(self): 241 with open(MOFILE_BAD_MAJOR_VERSION, 'rb') as fp: 242 with self.assertRaises(OSError) as cm: 243 gettext.GNUTranslations(fp) 244 245 exception = cm.exception 246 self.assertEqual(exception.errno, 0) 247 self.assertEqual(exception.strerror, "Bad version number 5") 248 self.assertEqual(exception.filename, MOFILE_BAD_MAJOR_VERSION) 249 250 def test_bad_minor_version(self): 251 with open(MOFILE_BAD_MINOR_VERSION, 'rb') as fp: 252 # Check that no error is thrown with a bad minor version number 253 gettext.GNUTranslations(fp) 254 255 def test_some_translations(self): 256 eq = self.assertEqual 257 # test some translations 258 eq(self._('albatross'), 'albatross') 259 eq(self._('mullusk'), 'bacon') 260 eq(self._(r'Raymond Luxury Yach-t'), 'Throatwobbler Mangrove') 261 eq(self._(r'nudge nudge'), 'wink wink') 262 263 def test_some_translations_with_context(self): 264 eq = self.assertEqual 265 eq(gettext.pgettext('my context', 'nudge nudge'), 266 'wink wink (in "my context")') 267 eq(gettext.pgettext('my other context', 'nudge nudge'), 268 'wink wink (in "my other context")') 269 270 def test_some_translations_with_context_and_domain(self): 271 eq = self.assertEqual 272 eq(gettext.dpgettext('gettext', 'my context', 'nudge nudge'), 273 'wink wink (in "my context")') 274 eq(gettext.dpgettext('gettext', 'my other context', 'nudge nudge'), 275 'wink wink (in "my other context")') 276 277 def test_double_quotes(self): 278 eq = self.assertEqual 279 # double quotes 280 eq(self._("albatross"), 'albatross') 281 eq(self._("mullusk"), 'bacon') 282 eq(self._(r"Raymond Luxury Yach-t"), 'Throatwobbler Mangrove') 283 eq(self._(r"nudge nudge"), 'wink wink') 284 285 def test_triple_single_quotes(self): 286 eq = self.assertEqual 287 # triple single quotes 288 eq(self._('''albatross'''), 'albatross') 289 eq(self._('''mullusk'''), 'bacon') 290 eq(self._(r'''Raymond Luxury Yach-t'''), 'Throatwobbler Mangrove') 291 eq(self._(r'''nudge nudge'''), 'wink wink') 292 293 def test_triple_double_quotes(self): 294 eq = self.assertEqual 295 # triple double quotes 296 eq(self._("""albatross"""), 'albatross') 297 eq(self._("""mullusk"""), 'bacon') 298 eq(self._(r"""Raymond Luxury Yach-t"""), 'Throatwobbler Mangrove') 299 eq(self._(r"""nudge nudge"""), 'wink wink') 300 301 def test_multiline_strings(self): 302 eq = self.assertEqual 303 # multiline strings 304 eq(self._('''This module provides internationalization and localization 305support for your Python programs by providing an interface to the GNU 306gettext message catalog library.'''), 307 '''Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba 308fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH 309trggrkg zrffntr pngnybt yvoenel.''') 310 311 312class PluralFormsTestCase(GettextBaseTest): 313 def setUp(self): 314 GettextBaseTest.setUp(self) 315 self.mofile = MOFILE 316 317 def test_plural_forms1(self): 318 eq = self.assertEqual 319 x = gettext.ngettext('There is %s file', 'There are %s files', 1) 320 eq(x, 'Hay %s fichero') 321 x = gettext.ngettext('There is %s file', 'There are %s files', 2) 322 eq(x, 'Hay %s ficheros') 323 324 def test_plural_context_forms1(self): 325 eq = self.assertEqual 326 x = gettext.npgettext('With context', 327 'There is %s file', 'There are %s files', 1) 328 eq(x, 'Hay %s fichero (context)') 329 x = gettext.npgettext('With context', 330 'There is %s file', 'There are %s files', 2) 331 eq(x, 'Hay %s ficheros (context)') 332 333 def test_plural_forms2(self): 334 eq = self.assertEqual 335 with open(self.mofile, 'rb') as fp: 336 t = gettext.GNUTranslations(fp) 337 x = t.ngettext('There is %s file', 'There are %s files', 1) 338 eq(x, 'Hay %s fichero') 339 x = t.ngettext('There is %s file', 'There are %s files', 2) 340 eq(x, 'Hay %s ficheros') 341 342 def test_plural_context_forms2(self): 343 eq = self.assertEqual 344 with open(self.mofile, 'rb') as fp: 345 t = gettext.GNUTranslations(fp) 346 x = t.npgettext('With context', 347 'There is %s file', 'There are %s files', 1) 348 eq(x, 'Hay %s fichero (context)') 349 x = t.npgettext('With context', 350 'There is %s file', 'There are %s files', 2) 351 eq(x, 'Hay %s ficheros (context)') 352 353 # Examples from http://www.gnu.org/software/gettext/manual/gettext.html 354 355 def test_ja(self): 356 eq = self.assertEqual 357 f = gettext.c2py('0') 358 s = ''.join([ str(f(x)) for x in range(200) ]) 359 eq(s, "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") 360 361 def test_de(self): 362 eq = self.assertEqual 363 f = gettext.c2py('n != 1') 364 s = ''.join([ str(f(x)) for x in range(200) ]) 365 eq(s, "10111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") 366 367 def test_fr(self): 368 eq = self.assertEqual 369 f = gettext.c2py('n>1') 370 s = ''.join([ str(f(x)) for x in range(200) ]) 371 eq(s, "00111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111") 372 373 def test_lv(self): 374 eq = self.assertEqual 375 f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2') 376 s = ''.join([ str(f(x)) for x in range(200) ]) 377 eq(s, "20111111111111111111101111111110111111111011111111101111111110111111111011111111101111111110111111111011111111111111111110111111111011111111101111111110111111111011111111101111111110111111111011111111") 378 379 def test_gd(self): 380 eq = self.assertEqual 381 f = gettext.c2py('n==1 ? 0 : n==2 ? 1 : 2') 382 s = ''.join([ str(f(x)) for x in range(200) ]) 383 eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") 384 385 def test_gd2(self): 386 eq = self.assertEqual 387 # Tests the combination of parentheses and "?:" 388 f = gettext.c2py('n==1 ? 0 : (n==2 ? 1 : 2)') 389 s = ''.join([ str(f(x)) for x in range(200) ]) 390 eq(s, "20122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") 391 392 def test_ro(self): 393 eq = self.assertEqual 394 f = gettext.c2py('n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2') 395 s = ''.join([ str(f(x)) for x in range(200) ]) 396 eq(s, "10111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222") 397 398 def test_lt(self): 399 eq = self.assertEqual 400 f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2') 401 s = ''.join([ str(f(x)) for x in range(200) ]) 402 eq(s, "20111111112222222222201111111120111111112011111111201111111120111111112011111111201111111120111111112011111111222222222220111111112011111111201111111120111111112011111111201111111120111111112011111111") 403 404 def test_ru(self): 405 eq = self.assertEqual 406 f = gettext.c2py('n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2') 407 s = ''.join([ str(f(x)) for x in range(200) ]) 408 eq(s, "20111222222222222222201112222220111222222011122222201112222220111222222011122222201112222220111222222011122222222222222220111222222011122222201112222220111222222011122222201112222220111222222011122222") 409 410 def test_cs(self): 411 eq = self.assertEqual 412 f = gettext.c2py('(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2') 413 s = ''.join([ str(f(x)) for x in range(200) ]) 414 eq(s, "20111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222") 415 416 def test_pl(self): 417 eq = self.assertEqual 418 f = gettext.c2py('n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2') 419 s = ''.join([ str(f(x)) for x in range(200) ]) 420 eq(s, "20111222222222222222221112222222111222222211122222221112222222111222222211122222221112222222111222222211122222222222222222111222222211122222221112222222111222222211122222221112222222111222222211122222") 421 422 def test_sl(self): 423 eq = self.assertEqual 424 f = gettext.c2py('n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3') 425 s = ''.join([ str(f(x)) for x in range(200) ]) 426 eq(s, "30122333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333012233333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333") 427 428 def test_ar(self): 429 eq = self.assertEqual 430 f = gettext.c2py('n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5') 431 s = ''.join([ str(f(x)) for x in range(200) ]) 432 eq(s, "01233333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444445553333333344444444444444444444444444444444444444444444444444444444444444444444444444444444444444444") 433 434 def test_security(self): 435 raises = self.assertRaises 436 # Test for a dangerous expression 437 raises(ValueError, gettext.c2py, "os.chmod('/etc/passwd',0777)") 438 # issue28563 439 raises(ValueError, gettext.c2py, '"(eval(foo) && ""') 440 raises(ValueError, gettext.c2py, 'f"{os.system(\'sh\')}"') 441 # Maximum recursion depth exceeded during compilation 442 raises(ValueError, gettext.c2py, 'n+'*10000 + 'n') 443 self.assertEqual(gettext.c2py('n+'*100 + 'n')(1), 101) 444 # MemoryError during compilation 445 raises(ValueError, gettext.c2py, '('*100 + 'n' + ')'*100) 446 # Maximum recursion depth exceeded in C to Python translator 447 raises(ValueError, gettext.c2py, '('*10000 + 'n' + ')'*10000) 448 self.assertEqual(gettext.c2py('('*20 + 'n' + ')'*20)(1), 1) 449 450 def test_chained_comparison(self): 451 # C doesn't chain comparison as Python so 2 == 2 == 2 gets different results 452 f = gettext.c2py('n == n == n') 453 self.assertEqual(''.join(str(f(x)) for x in range(3)), '010') 454 f = gettext.c2py('1 < n == n') 455 self.assertEqual(''.join(str(f(x)) for x in range(3)), '100') 456 f = gettext.c2py('n == n < 2') 457 self.assertEqual(''.join(str(f(x)) for x in range(3)), '010') 458 f = gettext.c2py('0 < n < 2') 459 self.assertEqual(''.join(str(f(x)) for x in range(3)), '111') 460 461 def test_decimal_number(self): 462 self.assertEqual(gettext.c2py('0123')(1), 123) 463 464 def test_invalid_syntax(self): 465 invalid_expressions = [ 466 'x>1', '(n>1', 'n>1)', '42**42**42', '0xa', '1.0', '1e2', 467 'n>0x1', '+n', '-n', 'n()', 'n(1)', '1+', 'nn', 'n n', 468 ] 469 for expr in invalid_expressions: 470 with self.assertRaises(ValueError): 471 gettext.c2py(expr) 472 473 def test_nested_condition_operator(self): 474 self.assertEqual(gettext.c2py('n?1?2:3:4')(0), 4) 475 self.assertEqual(gettext.c2py('n?1?2:3:4')(1), 2) 476 self.assertEqual(gettext.c2py('n?1:3?4:5')(0), 4) 477 self.assertEqual(gettext.c2py('n?1:3?4:5')(1), 1) 478 479 def test_division(self): 480 f = gettext.c2py('2/n*3') 481 self.assertEqual(f(1), 6) 482 self.assertEqual(f(2), 3) 483 self.assertEqual(f(3), 0) 484 self.assertEqual(f(-1), -6) 485 self.assertRaises(ZeroDivisionError, f, 0) 486 487 def test_plural_number(self): 488 f = gettext.c2py('n != 1') 489 self.assertEqual(f(1), 0) 490 self.assertEqual(f(2), 1) 491 with self.assertWarns(DeprecationWarning): 492 self.assertEqual(f(1.0), 0) 493 with self.assertWarns(DeprecationWarning): 494 self.assertEqual(f(2.0), 1) 495 with self.assertWarns(DeprecationWarning): 496 self.assertEqual(f(1.1), 1) 497 self.assertRaises(TypeError, f, '2') 498 self.assertRaises(TypeError, f, b'2') 499 self.assertRaises(TypeError, f, []) 500 self.assertRaises(TypeError, f, object()) 501 502 503class GNUTranslationParsingTest(GettextBaseTest): 504 def test_plural_form_error_issue17898(self): 505 with open(MOFILE, 'wb') as fp: 506 fp.write(base64.decodebytes(GNU_MO_DATA_ISSUE_17898)) 507 with open(MOFILE, 'rb') as fp: 508 # If this runs cleanly, the bug is fixed. 509 t = gettext.GNUTranslations(fp) 510 511 def test_ignore_comments_in_headers_issue36239(self): 512 """Checks that comments like: 513 514 #-#-#-#-# messages.po (EdX Studio) #-#-#-#-# 515 516 are ignored. 517 """ 518 with open(MOFILE, 'wb') as fp: 519 fp.write(base64.decodebytes(GNU_MO_DATA_ISSUE_17898)) 520 with open(MOFILE, 'rb') as fp: 521 t = gettext.GNUTranslations(fp) 522 self.assertEqual(t.info()["plural-forms"], "nplurals=2; plural=(n != 1);") 523 524 525class UnicodeTranslationsTest(GettextBaseTest): 526 def setUp(self): 527 GettextBaseTest.setUp(self) 528 with open(UMOFILE, 'rb') as fp: 529 self.t = gettext.GNUTranslations(fp) 530 self._ = self.t.gettext 531 self.pgettext = self.t.pgettext 532 533 def test_unicode_msgid(self): 534 self.assertIsInstance(self._(''), str) 535 536 def test_unicode_msgstr(self): 537 self.assertEqual(self._('ab\xde'), '\xa4yz') 538 539 def test_unicode_context_msgstr(self): 540 t = self.pgettext('mycontext\xde', 'ab\xde') 541 self.assertTrue(isinstance(t, str)) 542 self.assertEqual(t, '\xa4yz (context version)') 543 544 545class UnicodeTranslationsPluralTest(GettextBaseTest): 546 def setUp(self): 547 GettextBaseTest.setUp(self) 548 with open(MOFILE, 'rb') as fp: 549 self.t = gettext.GNUTranslations(fp) 550 self.ngettext = self.t.ngettext 551 self.npgettext = self.t.npgettext 552 553 def test_unicode_msgid(self): 554 unless = self.assertTrue 555 unless(isinstance(self.ngettext('', '', 1), str)) 556 unless(isinstance(self.ngettext('', '', 2), str)) 557 558 def test_unicode_context_msgid(self): 559 unless = self.assertTrue 560 unless(isinstance(self.npgettext('', '', '', 1), str)) 561 unless(isinstance(self.npgettext('', '', '', 2), str)) 562 563 def test_unicode_msgstr(self): 564 eq = self.assertEqual 565 unless = self.assertTrue 566 t = self.ngettext("There is %s file", "There are %s files", 1) 567 unless(isinstance(t, str)) 568 eq(t, "Hay %s fichero") 569 unless(isinstance(t, str)) 570 t = self.ngettext("There is %s file", "There are %s files", 5) 571 unless(isinstance(t, str)) 572 eq(t, "Hay %s ficheros") 573 574 def test_unicode_msgstr_with_context(self): 575 eq = self.assertEqual 576 unless = self.assertTrue 577 t = self.npgettext("With context", 578 "There is %s file", "There are %s files", 1) 579 unless(isinstance(t, str)) 580 eq(t, "Hay %s fichero (context)") 581 t = self.npgettext("With context", 582 "There is %s file", "There are %s files", 5) 583 unless(isinstance(t, str)) 584 eq(t, "Hay %s ficheros (context)") 585 586 587class WeirdMetadataTest(GettextBaseTest): 588 def setUp(self): 589 GettextBaseTest.setUp(self) 590 with open(MMOFILE, 'rb') as fp: 591 try: 592 self.t = gettext.GNUTranslations(fp) 593 except: 594 self.tearDown() 595 raise 596 597 def test_weird_metadata(self): 598 info = self.t.info() 599 self.assertEqual(len(info), 9) 600 self.assertEqual(info['last-translator'], 601 'John Doe <[email protected]>\nJane Foobar <[email protected]>') 602 603 604class DummyGNUTranslations(gettext.GNUTranslations): 605 def foo(self): 606 return 'foo' 607 608 609class GettextCacheTestCase(GettextBaseTest): 610 def test_cache(self): 611 self.localedir = os.curdir 612 self.mofile = MOFILE 613 614 self.assertEqual(len(gettext._translations), 0) 615 616 t = gettext.translation('gettext', self.localedir) 617 618 self.assertEqual(len(gettext._translations), 1) 619 620 t = gettext.translation('gettext', self.localedir, 621 class_=DummyGNUTranslations) 622 623 self.assertEqual(len(gettext._translations), 2) 624 self.assertEqual(t.__class__, DummyGNUTranslations) 625 626 # Calling it again doesn't add to the cache 627 628 t = gettext.translation('gettext', self.localedir, 629 class_=DummyGNUTranslations) 630 631 self.assertEqual(len(gettext._translations), 2) 632 self.assertEqual(t.__class__, DummyGNUTranslations) 633 634 635class MiscTestCase(unittest.TestCase): 636 def test__all__(self): 637 support.check__all__(self, gettext, 638 not_exported={'c2py', 'ENOENT'}) 639 640 641if __name__ == '__main__': 642 unittest.main() 643 644 645# For reference, here's the .po file used to created the GNU_MO_DATA above. 646# 647# The original version was automatically generated from the sources with 648# pygettext. Later it was manually modified to add plural forms support. 649 650b''' 651# Dummy translation for the Python test_gettext.py module. 652# Copyright (C) 2001 Python Software Foundation 653# Barry Warsaw <[email protected]>, 2000. 654# 655msgid "" 656msgstr "" 657"Project-Id-Version: 2.0\n" 658"PO-Revision-Date: 2003-04-11 14:32-0400\n" 659"Last-Translator: J. David Ibanez <[email protected]>\n" 660"Language-Team: XX <[email protected]>\n" 661"MIME-Version: 1.0\n" 662"Content-Type: text/plain; charset=iso-8859-1\n" 663"Content-Transfer-Encoding: 8bit\n" 664"Generated-By: pygettext.py 1.1\n" 665"Plural-Forms: nplurals=2; plural=n!=1;\n" 666 667#: test_gettext.py:19 test_gettext.py:25 test_gettext.py:31 test_gettext.py:37 668#: test_gettext.py:51 test_gettext.py:80 test_gettext.py:86 test_gettext.py:92 669#: test_gettext.py:98 670msgid "nudge nudge" 671msgstr "wink wink" 672 673msgctxt "my context" 674msgid "nudge nudge" 675msgstr "wink wink (in \"my context\")" 676 677msgctxt "my other context" 678msgid "nudge nudge" 679msgstr "wink wink (in \"my other context\")" 680 681#: test_gettext.py:16 test_gettext.py:22 test_gettext.py:28 test_gettext.py:34 682#: test_gettext.py:77 test_gettext.py:83 test_gettext.py:89 test_gettext.py:95 683msgid "albatross" 684msgstr "" 685 686#: test_gettext.py:18 test_gettext.py:24 test_gettext.py:30 test_gettext.py:36 687#: test_gettext.py:79 test_gettext.py:85 test_gettext.py:91 test_gettext.py:97 688msgid "Raymond Luxury Yach-t" 689msgstr "Throatwobbler Mangrove" 690 691#: test_gettext.py:17 test_gettext.py:23 test_gettext.py:29 test_gettext.py:35 692#: test_gettext.py:56 test_gettext.py:78 test_gettext.py:84 test_gettext.py:90 693#: test_gettext.py:96 694msgid "mullusk" 695msgstr "bacon" 696 697#: test_gettext.py:40 test_gettext.py:101 698msgid "" 699"This module provides internationalization and localization\n" 700"support for your Python programs by providing an interface to the GNU\n" 701"gettext message catalog library." 702msgstr "" 703"Guvf zbqhyr cebivqrf vagreangvbanyvmngvba naq ybpnyvmngvba\n" 704"fhccbeg sbe lbhe Clguba cebtenzf ol cebivqvat na vagresnpr gb gur TAH\n" 705"trggrkg zrffntr pngnybt yvoenel." 706 707# Manually added, as neither pygettext nor xgettext support plural forms 708# in Python. 709msgid "There is %s file" 710msgid_plural "There are %s files" 711msgstr[0] "Hay %s fichero" 712msgstr[1] "Hay %s ficheros" 713 714# Manually added, as neither pygettext nor xgettext support plural forms 715# and context in Python. 716msgctxt "With context" 717msgid "There is %s file" 718msgid_plural "There are %s files" 719msgstr[0] "Hay %s fichero (context)" 720msgstr[1] "Hay %s ficheros (context)" 721''' 722 723# Here's the second example po file example, used to generate the UMO_DATA 724# containing utf-8 encoded Unicode strings 725 726b''' 727# Dummy translation for the Python test_gettext.py module. 728# Copyright (C) 2001 Python Software Foundation 729# Barry Warsaw <[email protected]>, 2000. 730# 731msgid "" 732msgstr "" 733"Project-Id-Version: 2.0\n" 734"PO-Revision-Date: 2003-04-11 12:42-0400\n" 735"Last-Translator: Barry A. WArsaw <[email protected]>\n" 736"Language-Team: XX <[email protected]>\n" 737"MIME-Version: 1.0\n" 738"Content-Type: text/plain; charset=utf-8\n" 739"Content-Transfer-Encoding: 7bit\n" 740"Generated-By: manually\n" 741 742#: nofile:0 743msgid "ab\xc3\x9e" 744msgstr "\xc2\xa4yz" 745 746#: nofile:1 747msgctxt "mycontext\xc3\x9e" 748msgid "ab\xc3\x9e" 749msgstr "\xc2\xa4yz (context version)" 750''' 751 752# Here's the third example po file, used to generate MMO_DATA 753 754b''' 755msgid "" 756msgstr "" 757"Project-Id-Version: No Project 0.0\n" 758"POT-Creation-Date: Wed Dec 11 07:44:15 2002\n" 759"PO-Revision-Date: 2002-08-14 01:18:58+00:00\n" 760"Last-Translator: John Doe <[email protected]>\n" 761"Jane Foobar <[email protected]>\n" 762"Language-Team: xx <[email protected]>\n" 763"MIME-Version: 1.0\n" 764"Content-Type: text/plain; charset=iso-8859-15\n" 765"Content-Transfer-Encoding: quoted-printable\n" 766"Generated-By: pygettext.py 1.3\n" 767''' 768 769# 770# messages.po, used for bug 17898 771# 772 773b''' 774# test file for http://bugs.python.org/issue17898 775msgid "" 776msgstr "" 777"Plural-Forms: nplurals=2; plural=(n != 1);\n" 778"#-#-#-#-# messages.po (EdX Studio) #-#-#-#-#\n" 779"Content-Type: text/plain; charset=UTF-8\n" 780''' 781