aboutsummaryrefslogtreecommitdiff
blob: 1321c3c0f65eecfe0346e54badea9c5bbc6a8d95 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
https://bugs.gentoo.org/show_bug.cgi?id=250075
http://bugs.python.org/issue1813

--- Lib/decimal.py
+++ Lib/decimal.py
@@ -153,6 +153,13 @@
 ROUND_HALF_DOWN = 'ROUND_HALF_DOWN'
 ROUND_05UP = 'ROUND_05UP'
 
+import string
+
+def ascii_upper(s):
+    trans_table = string.maketrans(string.ascii_lowercase, string.ascii_uppercase)
+    return s.translate(trans_table)
+
+
 # Errors
 
 class DecimalException(ArithmeticError):
@@ -3645,7 +3652,7 @@
                                     if name.startswith('_round_')]
 for name in rounding_functions:
     # name is like _round_half_even, goes to the global ROUND_HALF_EVEN value.
-    globalname = name[1:].upper()
+    globalname = ascii_upper(name[1:])
     val = globals()[globalname]
     Decimal._pick_rounding_function[val] = name
 
--- Lib/email/__init__.py
+++ Lib/email/__init__.py
@@ -109,15 +109,19 @@
     'Text',
     ]
 
+import string
+lower_map = string.maketrans(string.ascii_uppercase, string.ascii_lowercase)
+
+
 for _name in _LOWERNAMES:
-    importer = LazyImporter(_name.lower())
+    importer = LazyImporter(_name.translate(lower_map))
     sys.modules['email.' + _name] = importer
     setattr(sys.modules['email'], _name, importer)
 
 
 import email.mime
 for _name in _MIMENAMES:
-    importer = LazyImporter('mime.' + _name.lower())
+    importer = LazyImporter('mime.' + _name.translate(lower_map))
     sys.modules['email.MIME' + _name] = importer
     setattr(sys.modules['email'], 'MIME' + _name, importer)
     setattr(sys.modules['email.mime'], _name, importer)
--- Lib/locale.py
+++ Lib/locale.py
@@ -313,6 +313,14 @@
 # overridden below)
 _setlocale = setlocale
 
+# Avoid relying on the locale-dependent .lower() method
+# (see bug #1813).
+_ascii_lower_map = ''.join(
+    chr(x + 32 if x >= ord('A') and x <= ord('Z') else x)
+    for x in range(256)
+)
+
+
 def normalize(localename):
 
     """ Returns a normalized locale code for the given locale
@@ -330,7 +338,7 @@
 
     """
     # Normalize the locale name and extract the encoding
-    fullname = localename.lower()
+    fullname = localename.encode('ascii').translate(_ascii_lower_map)
     if ':' in fullname:
         # ':' is sometimes used as encoding delimiter.
         fullname = fullname.replace(':', '.')
--- Lib/test/test_codecs.py
+++ Lib/test/test_codecs.py
@@ -1,5 +1,6 @@
 from test import test_support
 import unittest
+import locale
 import codecs
 import sys, StringIO, _testcapi
 
@@ -1133,6 +1134,16 @@
         self.assertRaises(LookupError, codecs.lookup, "__spam__")
         self.assertRaises(LookupError, codecs.lookup, " ")
 
+    def test_lookup_with_locale(self):
+        # Bug #1813: when normalizing codec name, lowercasing must be locale
+        # agnostic, otherwise the looked up codec name might end up wrong.
+        try:
+            locale.setlocale(locale.LC_CTYPE, 'tr')
+        except locale.Error:
+            # SKIPped test
+            return
+        codecs.lookup('ISO8859_1')
+
     def test_getencoder(self):
         self.assertRaises(TypeError, codecs.getencoder)
         self.assertRaises(LookupError, codecs.getencoder, "__spam__")
--- Python/codecs.c
+++ Python/codecs.c
@@ -45,6 +45,12 @@
     return -1;
 }
 
+/* isupper() forced into the ASCII locale */
+#define ascii_isupper(x) (((x) >= 0x41) && ((x) <= 0x5A))
+/* tolower() forced into the ASCII locale */
+#define ascii_tolower(x) (ascii_isupper(x) ? ((x) + 0x20) : (x))
+
+
 /* Convert a string to a normalized Python string: all characters are
    converted to lower case, spaces are replaced with underscores. */
 
@@ -70,7 +76,7 @@
         if (ch == ' ')
             ch = '-';
         else
-            ch = tolower(Py_CHARMASK(ch));
+            ch = ascii_tolower(Py_CHARMASK(ch));
         p[i] = ch;
     }
     return v;