Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/_config/localization.py: 31%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

49 statements  

1""" 

2Helpers for configuring locale settings. 

3 

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

5""" 

6 

7from __future__ import annotations 

8 

9from contextlib import contextmanager 

10import locale 

11import platform 

12import re 

13import subprocess 

14from typing import ( 

15 TYPE_CHECKING, 

16 cast, 

17) 

18 

19from pandas._config.config import options 

20 

21if TYPE_CHECKING: 

22 from collections.abc import Generator 

23 

24 

25@contextmanager 

26def set_locale( 

27 new_locale: str | tuple[str, str], lc_var: int = locale.LC_ALL 

28) -> Generator[str | tuple[str, str]]: 

29 """ 

30 Context manager for temporarily setting a locale. 

31 

32 Parameters 

33 ---------- 

34 new_locale : str or tuple 

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

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

37 "en_US.UTF-8". 

38 lc_var : int, default `locale.LC_ALL` 

39 The category of the locale being set. 

40 

41 Notes 

42 ----- 

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

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

45 thread-safe. 

46 """ 

47 # getlocale is not always compliant with setlocale, use setlocale. GH#46595 

48 current_locale = locale.setlocale(lc_var) 

49 

50 try: 

51 locale.setlocale(lc_var, new_locale) 

52 normalized_code, normalized_encoding = locale.getlocale() 

53 if normalized_code is not None and normalized_encoding is not None: 

54 yield f"{normalized_code}.{normalized_encoding}" 

55 else: 

56 yield new_locale 

57 finally: 

58 locale.setlocale(lc_var, current_locale) 

59 

60 

61def can_set_locale(lc: str, lc_var: int = locale.LC_ALL) -> bool: 

62 """ 

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

64 without raising an Exception. 

65 

66 Parameters 

67 ---------- 

68 lc : str 

69 The locale to attempt to set. 

70 lc_var : int, default `locale.LC_ALL` 

71 The category of the locale being set. 

72 

73 Returns 

74 ------- 

75 bool 

76 Whether the passed locale can be set 

77 """ 

78 try: 

79 with set_locale(lc, lc_var=lc_var): 

80 pass 

81 except (ValueError, locale.Error): 

82 # horrible name for an Exception subclass 

83 return False 

84 else: 

85 return True 

86 

87 

88def _valid_locales(locales: list[str] | str, normalize: bool) -> list[str]: 

89 """ 

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

91 when set. 

92 

93 Parameters 

94 ---------- 

95 locales : str 

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

97 normalize : bool 

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

99 

100 Returns 

101 ------- 

102 valid_locales : list 

103 A list of valid locales. 

104 """ 

105 return [ 

106 loc 

107 for loc in ( 

108 locale.normalize(loc.strip()) if normalize else loc.strip() 

109 for loc in locales 

110 ) 

111 if can_set_locale(loc) 

112 ] 

113 

114 

115def get_locales( 

116 prefix: str | None = None, 

117 normalize: bool = True, 

118) -> list[str]: 

119 """ 

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

121 

122 Parameters 

123 ---------- 

124 prefix : str 

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

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

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

128 normalize : bool 

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

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

131 ``Exception`` are returned. 

132 

133 Returns 

134 ------- 

135 locales : list of strings 

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

137 For example:: 

138 

139 locale.setlocale(locale.LC_ALL, locale_string) 

140 

141 On error will return an empty list (no locale available, e.g. Windows) 

142 

143 """ 

144 if platform.system() in ("Linux", "Darwin"): 

145 raw_locales = subprocess.check_output(["locale", "-a"]) 

146 else: 

147 # Other platforms e.g. windows platforms don't define "locale -a" 

148 # Note: is_platform_windows causes circular import here 

149 return [] 

150 

151 try: 

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

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

154 # extract what we can and then rejoin. 

155 split_raw_locales = raw_locales.split(b"\n") 

156 out_locales = [] 

157 for x in split_raw_locales: 

158 try: 

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

160 except UnicodeError: 

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

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

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

164 # a few special characters and when there is an 

165 # extensive list of installed locales. 

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

167 

168 except TypeError: 

169 pass 

170 

171 if prefix is None: 

172 return _valid_locales(out_locales, normalize) 

173 

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

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

176 return _valid_locales(found, normalize)