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