Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""" 

2Helpers for configuring locale settings. 

3 

4Name `localization` is chosen to avoid overlap with builtin `locale` module. 

5""" 

6from contextlib import contextmanager 

7import locale 

8import re 

9import subprocess 

10 

11from pandas._config.config import options 

12 

13 

14@contextmanager 

15def set_locale(new_locale, lc_var=locale.LC_ALL): 

16 """ 

17 Context manager for temporarily setting a locale. 

18 

19 Parameters 

20 ---------- 

21 new_locale : str or tuple 

22 A string of the form <language_country>.<encoding>. For example to set 

23 the current locale to US English with a UTF8 encoding, you would pass 

24 "en_US.UTF-8". 

25 lc_var : int, default `locale.LC_ALL` 

26 The category of the locale being set. 

27 

28 Notes 

29 ----- 

30 This is useful when you want to run a particular block of code under a 

31 particular locale, without globally setting the locale. This probably isn't 

32 thread-safe. 

33 """ 

34 current_locale = locale.getlocale() 

35 

36 try: 

37 locale.setlocale(lc_var, new_locale) 

38 normalized_locale = locale.getlocale() 

39 if all(x is not None for x in normalized_locale): 

40 yield ".".join(normalized_locale) 

41 else: 

42 yield new_locale 

43 finally: 

44 locale.setlocale(lc_var, current_locale) 

45 

46 

47def can_set_locale(lc, lc_var=locale.LC_ALL): 

48 """ 

49 Check to see if we can set a locale, and subsequently get the locale, 

50 without raising an Exception. 

51 

52 Parameters 

53 ---------- 

54 lc : str 

55 The locale to attempt to set. 

56 lc_var : int, default `locale.LC_ALL` 

57 The category of the locale being set. 

58 

59 Returns 

60 ------- 

61 is_valid : bool 

62 Whether the passed locale can be set 

63 """ 

64 

65 try: 

66 with set_locale(lc, lc_var=lc_var): 

67 pass 

68 except (ValueError, locale.Error): 

69 # horrible name for a Exception subclass 

70 return False 

71 else: 

72 return True 

73 

74 

75def _valid_locales(locales, normalize): 

76 """ 

77 Return a list of normalized locales that do not throw an ``Exception`` 

78 when set. 

79 

80 Parameters 

81 ---------- 

82 locales : str 

83 A string where each locale is separated by a newline. 

84 normalize : bool 

85 Whether to call ``locale.normalize`` on each locale. 

86 

87 Returns 

88 ------- 

89 valid_locales : list 

90 A list of valid locales. 

91 """ 

92 if normalize: 

93 normalizer = lambda x: locale.normalize(x.strip()) 

94 else: 

95 normalizer = lambda x: x.strip() 

96 

97 return list(filter(can_set_locale, map(normalizer, locales))) 

98 

99 

100def _default_locale_getter(): 

101 raw_locales = subprocess.check_output(["locale -a"], shell=True) 

102 return raw_locales 

103 

104 

105def get_locales(prefix=None, normalize=True, locale_getter=_default_locale_getter): 

106 """ 

107 Get all the locales that are available on the system. 

108 

109 Parameters 

110 ---------- 

111 prefix : str 

112 If not ``None`` then return only those locales with the prefix 

113 provided. For example to get all English language locales (those that 

114 start with ``"en"``), pass ``prefix="en"``. 

115 normalize : bool 

116 Call ``locale.normalize`` on the resulting list of available locales. 

117 If ``True``, only locales that can be set without throwing an 

118 ``Exception`` are returned. 

119 locale_getter : callable 

120 The function to use to retrieve the current locales. This should return 

121 a string with each locale separated by a newline character. 

122 

123 Returns 

124 ------- 

125 locales : list of strings 

126 A list of locale strings that can be set with ``locale.setlocale()``. 

127 For example:: 

128 

129 locale.setlocale(locale.LC_ALL, locale_string) 

130 

131 On error will return None (no locale available, e.g. Windows) 

132 

133 """ 

134 try: 

135 raw_locales = locale_getter() 

136 except subprocess.CalledProcessError: 

137 # Raised on (some? all?) Windows platforms because Note: "locale -a" 

138 # is not defined 

139 return None 

140 

141 try: 

142 # raw_locales is "\n" separated list of locales 

143 # it may contain non-decodable parts, so split 

144 # extract what we can and then rejoin. 

145 raw_locales = raw_locales.split(b"\n") 

146 out_locales = [] 

147 for x in raw_locales: 

148 try: 

149 out_locales.append(str(x, encoding=options.display.encoding)) 

150 except UnicodeError: 

151 # 'locale -a' is used to populated 'raw_locales' and on 

152 # Redhat 7 Linux (and maybe others) prints locale names 

153 # using windows-1252 encoding. Bug only triggered by 

154 # a few special characters and when there is an 

155 # extensive list of installed locales. 

156 out_locales.append(str(x, encoding="windows-1252")) 

157 

158 except TypeError: 

159 pass 

160 

161 if prefix is None: 

162 return _valid_locales(out_locales, normalize) 

163 

164 pattern = re.compile(f"{prefix}.*") 

165 found = pattern.findall("\n".join(out_locales)) 

166 return _valid_locales(found, normalize)