Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/dateparser/conf.py: 89%
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
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
1import hashlib
2from datetime import datetime
3from functools import wraps
5from dateparser.data.languages_info import language_order
7from .parser import date_order_chart
8from .utils import registry
11@registry
12class Settings:
13 """Control and configure default parsing behavior of dateparser.
14 Currently, supported settings are:
16 * `DATE_ORDER`
17 * `PREFER_LOCALE_DATE_ORDER`
18 * `TIMEZONE`
19 * `TO_TIMEZONE`
20 * `RETURN_AS_TIMEZONE_AWARE`
21 * `PREFER_MONTH_OF_YEAR`
22 * `PREFER_DAY_OF_MONTH`
23 * `PREFER_DATES_FROM`
24 * `RELATIVE_BASE`
25 * `STRICT_PARSING`
26 * `REQUIRE_PARTS`
27 * `SKIP_TOKENS`
28 * `NORMALIZE`
29 * `RETURN_TIME_AS_PERIOD`
30 * `PARSERS`
31 * `DEFAULT_LANGUAGES`
32 * `LANGUAGE_DETECTION_CONFIDENCE_THRESHOLD`
33 * `CACHE_SIZE_LIMIT`
34 """
36 _default = True
37 _pyfile_data = None
38 _mod_settings = dict()
40 def __init__(self, settings=None):
41 if settings:
42 self._updateall(settings.items())
43 else:
44 self._updateall(self._get_settings_from_pyfile().items())
46 @classmethod
47 def get_key(cls, settings=None):
48 if not settings:
49 return "default"
51 keys = sorted(["%s-%s" % (key, str(settings[key])) for key in settings])
52 return hashlib.md5("".join(keys).encode("utf-8")).hexdigest()
54 @classmethod
55 def _get_settings_from_pyfile(cls):
56 if not cls._pyfile_data:
57 from dateparser_data import settings
59 cls._pyfile_data = settings.settings
60 return cls._pyfile_data
62 def _updateall(self, iterable):
63 for key, value in iterable:
64 setattr(self, key, value)
66 def replace(self, mod_settings=None, **kwds):
67 for k, v in kwds.items():
68 if v is None:
69 raise TypeError('Invalid {{"{}": {}}}'.format(k, v))
71 for x in self._get_settings_from_pyfile().keys():
72 kwds.setdefault(x, getattr(self, x))
74 kwds["_default"] = False
75 if mod_settings:
76 kwds["_mod_settings"] = mod_settings
78 return self.__class__(settings=kwds)
81settings = Settings()
84def apply_settings(f):
85 @wraps(f)
86 def wrapper(*args, **kwargs):
87 mod_settings = kwargs.get("settings")
88 kwargs["settings"] = mod_settings or settings
90 if isinstance(kwargs["settings"], dict):
91 kwargs["settings"] = settings.replace(
92 mod_settings=mod_settings, **kwargs["settings"]
93 )
95 if not isinstance(kwargs["settings"], Settings):
96 raise TypeError(
97 "settings can only be either dict or instance of Settings class"
98 )
100 return f(*args, **kwargs)
102 return wrapper
105class SettingValidationError(ValueError):
106 pass
109def _check_repeated_values(setting_name, setting_value):
110 if len(setting_value) != len(set(setting_value)):
111 raise SettingValidationError(
112 'There are repeated values in the "{}" setting'.format(setting_name)
113 )
114 return
117def _check_require_part(setting_name, setting_value):
118 """Returns `True` if the provided list of parts contains valid values"""
119 invalid_values = set(setting_value) - {"day", "month", "year"}
120 if invalid_values:
121 raise SettingValidationError(
122 '"{}" setting contains invalid values: {}'.format(
123 setting_name, ", ".join(invalid_values)
124 )
125 )
126 _check_repeated_values(setting_name, setting_value)
129def _check_parsers(setting_name, setting_value):
130 """Returns `True` if the provided list of parsers contains valid values"""
131 existing_parsers = [
132 "timestamp",
133 "relative-time",
134 "custom-formats",
135 "absolute-time",
136 "no-spaces-time",
137 "negative-timestamp",
138 ] # FIXME: Extract the list of existing parsers from another place (#798)
139 unknown_parsers = set(setting_value) - set(existing_parsers)
140 if unknown_parsers:
141 raise SettingValidationError(
142 'Found unknown parsers in the "{}" setting: {}'.format(
143 setting_name, ", ".join(unknown_parsers)
144 )
145 )
146 _check_repeated_values(setting_name, setting_value)
149def _check_default_languages(setting_name, setting_value):
150 unsupported_languages = set(setting_value) - set(language_order)
151 if unsupported_languages:
152 raise SettingValidationError(
153 "Found invalid languages in the '{}' setting: {}".format(
154 setting_name, ", ".join(map(repr, unsupported_languages))
155 )
156 )
157 _check_repeated_values(setting_name, setting_value)
160def _check_between_0_and_1(setting_name, setting_value):
161 is_valid = 0 <= setting_value <= 1
162 if not is_valid:
163 raise SettingValidationError(
164 "{} is not a valid value for {}. It can take values between 0 and "
165 "1.".format(
166 setting_value,
167 setting_name,
168 )
169 )
172def check_settings(settings):
173 """
174 Check if provided settings are valid, if not it raises `SettingValidationError`.
175 Only checks for the modified settings.
176 """
177 settings_values = {
178 "DATE_ORDER": {
179 "values": tuple(date_order_chart.keys()),
180 "type": str,
181 },
182 "TIMEZONE": {
183 # we don't check invalid Timezones as they raise an error
184 "type": str,
185 },
186 "TO_TIMEZONE": {
187 # It defaults to None, but it's not allowed to use it directly
188 # "values" can take unlimited options
189 "type": str
190 },
191 "RETURN_AS_TIMEZONE_AWARE": {
192 # It defaults to 'default', but it's not allowed to use it directly
193 "type": bool
194 },
195 "PREFER_MONTH_OF_YEAR": {"values": ("current", "first", "last"), "type": str},
196 "PREFER_DAY_OF_MONTH": {"values": ("current", "first", "last"), "type": str},
197 "PREFER_DATES_FROM": {
198 "values": ("current_period", "past", "future"),
199 "type": str,
200 },
201 "RELATIVE_BASE": {
202 # "values" can take unlimited options
203 "type": datetime
204 },
205 "STRICT_PARSING": {"type": bool},
206 "REQUIRE_PARTS": {
207 # "values" covered by the 'extra_check'
208 "type": list,
209 "extra_check": _check_require_part,
210 },
211 "SKIP_TOKENS": {
212 # "values" can take unlimited options
213 "type": list,
214 },
215 "NORMALIZE": {"type": bool},
216 "RETURN_TIME_AS_PERIOD": {"type": bool},
217 "PARSERS": {
218 # "values" covered by the 'extra_check'
219 "type": list,
220 "extra_check": _check_parsers,
221 },
222 "FUZZY": {"type": bool},
223 "PREFER_LOCALE_DATE_ORDER": {"type": bool},
224 "DEFAULT_LANGUAGES": {"type": list, "extra_check": _check_default_languages},
225 "LANGUAGE_DETECTION_CONFIDENCE_THRESHOLD": {
226 "type": float,
227 "extra_check": _check_between_0_and_1,
228 },
229 "CACHE_SIZE_LIMIT": {
230 "type": int,
231 },
232 }
234 modified_settings = settings._mod_settings # check only modified settings
236 # check settings keys:
237 for setting in modified_settings:
238 if setting not in settings_values:
239 raise SettingValidationError('"{}" is not a valid setting'.format(setting))
241 for setting_name, setting_value in modified_settings.items():
242 setting_type = type(setting_value)
243 setting_props = settings_values[setting_name]
245 # check type:
246 if not isinstance(setting_value, setting_props["type"]):
247 raise SettingValidationError(
248 '"{}" must be "{}", not "{}".'.format(
249 setting_name, setting_props["type"].__name__, setting_type.__name__
250 )
251 )
253 # check values:
254 if setting_props.get("values") and setting_value not in setting_props["values"]:
255 raise SettingValidationError(
256 '"{}" is not a valid value for "{}", it should be: "{}" or "{}"'.format(
257 setting_value,
258 setting_name,
259 '", "'.join(setting_props["values"][:-1]),
260 setting_props["values"][-1],
261 )
262 )
264 # specific checks
265 extra_check = setting_props.get("extra_check")
266 if extra_check:
267 extra_check(setting_name, setting_value)