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