Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/utils/formats.py: 13%

137 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1import datetime 

2import decimal 

3import functools 

4import re 

5import unicodedata 

6from importlib import import_module 

7 

8from django.conf import settings 

9from django.utils import dateformat, numberformat 

10from django.utils.functional import lazy 

11from django.utils.translation import check_for_language, get_language, to_locale 

12 

13# format_cache is a mapping from (format_type, lang) to the format string. 

14# By using the cache, it is possible to avoid running get_format_modules 

15# repeatedly. 

16_format_cache = {} 

17_format_modules_cache = {} 

18 

19ISO_INPUT_FORMATS = { 

20 "DATE_INPUT_FORMATS": ["%Y-%m-%d"], 

21 "TIME_INPUT_FORMATS": ["%H:%M:%S", "%H:%M:%S.%f", "%H:%M"], 

22 "DATETIME_INPUT_FORMATS": [ 

23 "%Y-%m-%d %H:%M:%S", 

24 "%Y-%m-%d %H:%M:%S.%f", 

25 "%Y-%m-%d %H:%M", 

26 "%Y-%m-%d", 

27 ], 

28} 

29 

30 

31FORMAT_SETTINGS = frozenset( 

32 [ 

33 "DECIMAL_SEPARATOR", 

34 "THOUSAND_SEPARATOR", 

35 "NUMBER_GROUPING", 

36 "FIRST_DAY_OF_WEEK", 

37 "MONTH_DAY_FORMAT", 

38 "TIME_FORMAT", 

39 "DATE_FORMAT", 

40 "DATETIME_FORMAT", 

41 "SHORT_DATE_FORMAT", 

42 "SHORT_DATETIME_FORMAT", 

43 "YEAR_MONTH_FORMAT", 

44 "DATE_INPUT_FORMATS", 

45 "TIME_INPUT_FORMATS", 

46 "DATETIME_INPUT_FORMATS", 

47 ] 

48) 

49 

50 

51def reset_format_cache(): 

52 """Clear any cached formats. 

53 

54 This method is provided primarily for testing purposes, 

55 so that the effects of cached formats can be removed. 

56 """ 

57 global _format_cache, _format_modules_cache 

58 _format_cache = {} 

59 _format_modules_cache = {} 

60 

61 

62def iter_format_modules(lang, format_module_path=None): 

63 """Find format modules.""" 

64 if not check_for_language(lang): 

65 return 

66 

67 if format_module_path is None: 

68 format_module_path = settings.FORMAT_MODULE_PATH 

69 

70 format_locations = [] 

71 if format_module_path: 

72 if isinstance(format_module_path, str): 

73 format_module_path = [format_module_path] 

74 for path in format_module_path: 

75 format_locations.append(path + ".%s") 

76 format_locations.append("django.conf.locale.%s") 

77 locale = to_locale(lang) 

78 locales = [locale] 

79 if "_" in locale: 

80 locales.append(locale.split("_")[0]) 

81 for location in format_locations: 

82 for loc in locales: 

83 try: 

84 yield import_module("%s.formats" % (location % loc)) 

85 except ImportError: 

86 pass 

87 

88 

89def get_format_modules(lang=None): 

90 """Return a list of the format modules found.""" 

91 if lang is None: 

92 lang = get_language() 

93 if lang not in _format_modules_cache: 

94 _format_modules_cache[lang] = list( 

95 iter_format_modules(lang, settings.FORMAT_MODULE_PATH) 

96 ) 

97 return _format_modules_cache[lang] 

98 

99 

100def get_format(format_type, lang=None, use_l10n=None): 

101 """ 

102 For a specific format type, return the format for the current 

103 language (locale). Default to the format in the settings. 

104 format_type is the name of the format, e.g. 'DATE_FORMAT'. 

105 

106 If use_l10n is provided and is not None, it forces the value to 

107 be localized (or not), overriding the value of settings.USE_L10N. 

108 """ 

109 use_l10n = use_l10n or ( 

110 use_l10n is None 

111 and ( 

112 settings._USE_L10N_INTERNAL 

113 if hasattr(settings, "_USE_L10N_INTERNAL") 

114 else settings.USE_L10N 

115 ) 

116 ) 

117 if use_l10n and lang is None: 

118 lang = get_language() 

119 cache_key = (format_type, lang) 

120 try: 

121 return _format_cache[cache_key] 

122 except KeyError: 

123 pass 

124 

125 # The requested format_type has not been cached yet. Try to find it in any 

126 # of the format_modules for the given lang if l10n is enabled. If it's not 

127 # there or if l10n is disabled, fall back to the project settings. 

128 val = None 

129 if use_l10n: 

130 for module in get_format_modules(lang): 

131 val = getattr(module, format_type, None) 

132 if val is not None: 

133 break 

134 if val is None: 

135 if format_type not in FORMAT_SETTINGS: 

136 return format_type 

137 val = getattr(settings, format_type) 

138 elif format_type in ISO_INPUT_FORMATS: 

139 # If a list of input formats from one of the format_modules was 

140 # retrieved, make sure the ISO_INPUT_FORMATS are in this list. 

141 val = list(val) 

142 for iso_input in ISO_INPUT_FORMATS.get(format_type, ()): 

143 if iso_input not in val: 

144 val.append(iso_input) 

145 _format_cache[cache_key] = val 

146 return val 

147 

148 

149get_format_lazy = lazy(get_format, str, list, tuple) 

150 

151 

152def date_format(value, format=None, use_l10n=None): 

153 """ 

154 Format a datetime.date or datetime.datetime object using a 

155 localizable format. 

156 

157 If use_l10n is provided and is not None, that will force the value to 

158 be localized (or not), overriding the value of settings.USE_L10N. 

159 """ 

160 return dateformat.format( 

161 value, get_format(format or "DATE_FORMAT", use_l10n=use_l10n) 

162 ) 

163 

164 

165def time_format(value, format=None, use_l10n=None): 

166 """ 

167 Format a datetime.time object using a localizable format. 

168 

169 If use_l10n is provided and is not None, it forces the value to 

170 be localized (or not), overriding the value of settings.USE_L10N. 

171 """ 

172 return dateformat.time_format( 

173 value, get_format(format or "TIME_FORMAT", use_l10n=use_l10n) 

174 ) 

175 

176 

177def number_format(value, decimal_pos=None, use_l10n=None, force_grouping=False): 

178 """ 

179 Format a numeric value using localization settings. 

180 

181 If use_l10n is provided and is not None, it forces the value to 

182 be localized (or not), overriding the value of settings.USE_L10N. 

183 """ 

184 use_l10n = use_l10n or ( 

185 use_l10n is None 

186 and ( 

187 settings._USE_L10N_INTERNAL 

188 if hasattr(settings, "_USE_L10N_INTERNAL") 

189 else settings.USE_L10N 

190 ) 

191 ) 

192 lang = get_language() if use_l10n else None 

193 return numberformat.format( 

194 value, 

195 get_format("DECIMAL_SEPARATOR", lang, use_l10n=use_l10n), 

196 decimal_pos, 

197 get_format("NUMBER_GROUPING", lang, use_l10n=use_l10n), 

198 get_format("THOUSAND_SEPARATOR", lang, use_l10n=use_l10n), 

199 force_grouping=force_grouping, 

200 use_l10n=use_l10n, 

201 ) 

202 

203 

204def localize(value, use_l10n=None): 

205 """ 

206 Check if value is a localizable type (date, number...) and return it 

207 formatted as a string using current locale format. 

208 

209 If use_l10n is provided and is not None, it forces the value to 

210 be localized (or not), overriding the value of settings.USE_L10N. 

211 """ 

212 if isinstance(value, str): # Handle strings first for performance reasons. 

213 return value 

214 elif isinstance(value, bool): # Make sure booleans don't get treated as numbers 

215 return str(value) 

216 elif isinstance(value, (decimal.Decimal, float, int)): 

217 if use_l10n is False: 

218 return str(value) 

219 return number_format(value, use_l10n=use_l10n) 

220 elif isinstance(value, datetime.datetime): 

221 return date_format(value, "DATETIME_FORMAT", use_l10n=use_l10n) 

222 elif isinstance(value, datetime.date): 

223 return date_format(value, use_l10n=use_l10n) 

224 elif isinstance(value, datetime.time): 

225 return time_format(value, "TIME_FORMAT", use_l10n=use_l10n) 

226 return value 

227 

228 

229def localize_input(value, default=None): 

230 """ 

231 Check if an input value is a localizable type and return it 

232 formatted with the appropriate formatting string of the current locale. 

233 """ 

234 if isinstance(value, str): # Handle strings first for performance reasons. 

235 return value 

236 elif isinstance(value, bool): # Don't treat booleans as numbers. 

237 return str(value) 

238 elif isinstance(value, (decimal.Decimal, float, int)): 

239 return number_format(value) 

240 elif isinstance(value, datetime.datetime): 

241 format = default or get_format("DATETIME_INPUT_FORMATS")[0] 

242 format = sanitize_strftime_format(format) 

243 return value.strftime(format) 

244 elif isinstance(value, datetime.date): 

245 format = default or get_format("DATE_INPUT_FORMATS")[0] 

246 format = sanitize_strftime_format(format) 

247 return value.strftime(format) 

248 elif isinstance(value, datetime.time): 

249 format = default or get_format("TIME_INPUT_FORMATS")[0] 

250 return value.strftime(format) 

251 return value 

252 

253 

254@functools.lru_cache() 

255def sanitize_strftime_format(fmt): 

256 """ 

257 Ensure that certain specifiers are correctly padded with leading zeros. 

258 

259 For years < 1000 specifiers %C, %F, %G, and %Y don't work as expected for 

260 strftime provided by glibc on Linux as they don't pad the year or century 

261 with leading zeros. Support for specifying the padding explicitly is 

262 available, however, which can be used to fix this issue. 

263 

264 FreeBSD, macOS, and Windows do not support explicitly specifying the 

265 padding, but return four digit years (with leading zeros) as expected. 

266 

267 This function checks whether the %Y produces a correctly padded string and, 

268 if not, makes the following substitutions: 

269 

270 - %C → %02C 

271 - %F → %010F 

272 - %G → %04G 

273 - %Y → %04Y 

274 

275 See https://bugs.python.org/issue13305 for more details. 

276 """ 

277 if datetime.date(1, 1, 1).strftime("%Y") == "0001": 

278 return fmt 

279 mapping = {"C": 2, "F": 10, "G": 4, "Y": 4} 

280 return re.sub( 

281 r"((?:^|[^%])(?:%%)*)%([CFGY])", 

282 lambda m: r"%s%%0%s%s" % (m[1], mapping[m[2]], m[2]), 

283 fmt, 

284 ) 

285 

286 

287def sanitize_separators(value): 

288 """ 

289 Sanitize a value according to the current decimal and 

290 thousand separator setting. Used with form field input. 

291 """ 

292 if isinstance(value, str): 

293 parts = [] 

294 decimal_separator = get_format("DECIMAL_SEPARATOR") 

295 if decimal_separator in value: 

296 value, decimals = value.split(decimal_separator, 1) 

297 parts.append(decimals) 

298 if settings.USE_THOUSAND_SEPARATOR: 

299 thousand_sep = get_format("THOUSAND_SEPARATOR") 

300 if ( 

301 thousand_sep == "." 

302 and value.count(".") == 1 

303 and len(value.split(".")[-1]) != 3 

304 ): 

305 # Special case where we suspect a dot meant decimal separator 

306 # (see #22171). 

307 pass 

308 else: 

309 for replacement in { 

310 thousand_sep, 

311 unicodedata.normalize("NFKD", thousand_sep), 

312 }: 

313 value = value.replace(replacement, "") 

314 parts.append(value) 

315 value = ".".join(reversed(parts)) 

316 return value