Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/dateparser/languages/loader.py: 88%

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

73 statements  

1from collections import OrderedDict 

2from copy import deepcopy 

3from importlib import import_module 

4from itertools import zip_longest 

5 

6import regex as re 

7 

8from ..data import language_locale_dict, language_order 

9from .locale import Locale 

10 

11LOCALE_SPLIT_PATTERN = re.compile(r"-(?=[A-Z0-9]+$)") 

12 

13 

14def _isvalidlocale(locale): 

15 language = LOCALE_SPLIT_PATTERN.split(locale)[0] 

16 if language not in language_order: 

17 return False 

18 else: 

19 locales_list = language_locale_dict[language] 

20 if locale == language or locale in locales_list: 

21 return True 

22 else: 

23 return False 

24 

25 

26def _filter_valid_locales(locales): 

27 return [locale for locale in locales if _isvalidlocale(locale)] 

28 

29 

30def _construct_locales(languages, region): 

31 if region: 

32 possible_locales = [language + "-" + region for language in languages] 

33 locales = _filter_valid_locales(possible_locales) 

34 else: 

35 locales = languages 

36 return locales 

37 

38 

39class LocaleDataLoader: 

40 """Class that handles loading of locale instances.""" 

41 

42 _loaded_languages = {} 

43 _loaded_locales = {} 

44 

45 def get_locale_map( 

46 self, 

47 languages=None, 

48 locales=None, 

49 region=None, 

50 use_given_order=False, 

51 allow_conflicting_locales=False, 

52 ): 

53 """ 

54 Get an ordered mapping with locale codes as keys 

55 and corresponding locale instances as values. 

56 

57 :param languages: 

58 A list of language codes, e.g. ['en', 'es', 'zh-Hant']. 

59 If locales are not given, languages and region are 

60 used to construct locales to load. 

61 :type languages: list 

62 

63 :param locales: 

64 A list of codes of locales which are to be loaded, 

65 e.g. ['fr-PF', 'qu-EC', 'af-NA'] 

66 :type locales: list 

67 

68 :param region: 

69 A region code, e.g. 'IN', '001', 'NE'. 

70 If locales are not given, languages and region are 

71 used to construct locales to load. 

72 :type region: str 

73 

74 :param use_given_order: 

75 If True, the returned mapping is ordered in the order locales are given. 

76 :type use_given_order: bool 

77 

78 :param allow_conflicting_locales: 

79 if True, locales with same language and different region can be loaded. 

80 :type allow_conflicting_locales: bool 

81 

82 :return: ordered locale code to locale instance mapping 

83 """ 

84 return OrderedDict( 

85 self._load_data( 

86 languages=languages, 

87 locales=locales, 

88 region=region, 

89 use_given_order=use_given_order, 

90 allow_conflicting_locales=allow_conflicting_locales, 

91 ) 

92 ) 

93 

94 def get_locales( 

95 self, 

96 languages=None, 

97 locales=None, 

98 region=None, 

99 use_given_order=False, 

100 allow_conflicting_locales=False, 

101 ): 

102 """ 

103 Yield locale instances. 

104 

105 :param languages: 

106 A list of language codes, e.g. ['en', 'es', 'zh-Hant']. 

107 If locales are not given, languages and region are 

108 used to construct locales to load. 

109 :type languages: list 

110 

111 :param locales: 

112 A list of codes of locales which are to be loaded, 

113 e.g. ['fr-PF', 'qu-EC', 'af-NA'] 

114 :type locales: list 

115 

116 :param region: 

117 A region code, e.g. 'IN', '001', 'NE'. 

118 If locales are not given, languages and region are 

119 used to construct locales to load. 

120 :type region: str 

121 

122 :param use_given_order: 

123 If True, the returned mapping is ordered in the order locales are given. 

124 :type use_given_order: bool 

125 

126 :param allow_conflicting_locales: 

127 if True, locales with same language and different region can be loaded. 

128 :type allow_conflicting_locales: bool 

129 

130 :yield: locale instances 

131 """ 

132 for _, locale in self._load_data( 

133 languages=languages, 

134 locales=locales, 

135 region=region, 

136 use_given_order=use_given_order, 

137 allow_conflicting_locales=allow_conflicting_locales, 

138 ): 

139 yield locale 

140 

141 def get_locale(self, shortname): 

142 """ 

143 Get a locale instance. 

144 

145 :param shortname: 

146 A locale code, e.g. 'fr-PF', 'qu-EC', 'af-NA'. 

147 :type shortname: str 

148 

149 :return: locale instance 

150 """ 

151 return list(self.get_locales(locales=[shortname]))[0] 

152 

153 def _load_data( 

154 self, 

155 languages=None, 

156 locales=None, 

157 region=None, 

158 use_given_order=False, 

159 allow_conflicting_locales=False, 

160 ): 

161 locale_dict = OrderedDict() 

162 if locales: 

163 invalid_locales = [] 

164 for locale in locales: 

165 lang_reg = LOCALE_SPLIT_PATTERN.split(locale) 

166 if len(lang_reg) == 1: 

167 lang_reg.append("") 

168 locale_dict[locale] = tuple(lang_reg) 

169 if not _isvalidlocale(locale): 

170 invalid_locales.append(locale) 

171 if invalid_locales: 

172 raise ValueError( 

173 "Unknown locale(s): %s" % ", ".join(map(repr, invalid_locales)) 

174 ) 

175 

176 if not allow_conflicting_locales: 

177 if len(set(locales)) > len({t[0] for t in locale_dict.values()}): 

178 raise ValueError( 

179 "Locales should not have same language and different region" 

180 ) 

181 

182 else: 

183 if languages is None: 

184 languages = language_order 

185 unsupported_languages = set(languages) - set(language_order) 

186 if unsupported_languages: 

187 raise ValueError( 

188 "Unknown language(s): %s" 

189 % ", ".join(map(repr, unsupported_languages)) 

190 ) 

191 if region is None: 

192 region = "" 

193 locales = _construct_locales(languages, region) 

194 locale_dict.update( 

195 zip_longest( 

196 locales, tuple(zip_longest(languages, [], fillvalue=region)) 

197 ) 

198 ) 

199 

200 if not use_given_order: 

201 locale_dict = OrderedDict( 

202 sorted(locale_dict.items(), key=lambda x: language_order.index(x[1][0])) 

203 ) 

204 

205 for shortname, lang_reg in locale_dict.items(): 

206 if shortname not in self._loaded_locales: 

207 lang, reg = lang_reg 

208 if lang in self._loaded_languages: 

209 locale = Locale( 

210 shortname, language_info=deepcopy(self._loaded_languages[lang]) 

211 ) 

212 self._loaded_locales[shortname] = locale 

213 else: 

214 language_info = getattr( 

215 import_module("dateparser.data.date_translation_data." + lang), 

216 "info", 

217 ) 

218 locale = Locale(shortname, language_info=deepcopy(language_info)) 

219 self._loaded_languages[lang] = language_info 

220 self._loaded_locales[shortname] = locale 

221 yield shortname, self._loaded_locales[shortname] 

222 

223 

224default_loader = LocaleDataLoader()