invesytoolbox 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- invesytoolbox/__init__.py +13 -0
- invesytoolbox/itb_data.py +538 -0
- invesytoolbox/itb_date_time.py +923 -0
- invesytoolbox/itb_email_phone.py +288 -0
- invesytoolbox/itb_html.py +66 -0
- invesytoolbox/itb_locales.py +532 -0
- invesytoolbox/itb_restricted_python.py +54 -0
- invesytoolbox/itb_security.py +139 -0
- invesytoolbox/itb_text_name.py +478 -0
- invesytoolbox/itb_www.py +88 -0
- invesytoolbox-0.1.0.dist-info/METADATA +58 -0
- invesytoolbox-0.1.0.dist-info/RECORD +15 -0
- invesytoolbox-0.1.0.dist-info/WHEEL +5 -0
- invesytoolbox-0.1.0.dist-info/licenses/LICENSE.txt +28 -0
- invesytoolbox-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
_package_dir = Path(__file__).resolve().parent
|
|
5
|
+
|
|
6
|
+
for _file in sorted(_package_dir.glob('itb_*.py')):
|
|
7
|
+
_module = importlib.import_module(f'.{_file.stem}', package=__name__)
|
|
8
|
+
_module_all = getattr(_module, '__all__', None)
|
|
9
|
+
_names = _module_all if _module_all is not None else [
|
|
10
|
+
name for name in dir(_module) if not name.startswith('_')
|
|
11
|
+
]
|
|
12
|
+
for _name in _names:
|
|
13
|
+
globals()[_name] = getattr(_module, _name)
|
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
itb_data
|
|
4
|
+
========
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'any_2boolean',
|
|
9
|
+
'create_vcard',
|
|
10
|
+
'dict_2datatypes',
|
|
11
|
+
'dict_2unicode',
|
|
12
|
+
'dict_from_dict_list',
|
|
13
|
+
'dictlist_2datatypes',
|
|
14
|
+
'sort_dictlist',
|
|
15
|
+
'value_2datatype',
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
import datetime
|
|
19
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
20
|
+
|
|
21
|
+
import DateTime
|
|
22
|
+
import pendulum
|
|
23
|
+
import vobject
|
|
24
|
+
from dateutil.parser import parse
|
|
25
|
+
from .itb_date_time import convert_datetime, get_dateformat, str_to_date, str_to_DT
|
|
26
|
+
from .itb_email_phone import process_phonenumber
|
|
27
|
+
|
|
28
|
+
DataType = Union[str, int, float, bool, datetime.date, datetime.datetime, datetime.time, DateTime.DateTime, pendulum.DateTime]
|
|
29
|
+
|
|
30
|
+
comma_float_chars = set(',1234567890')
|
|
31
|
+
time_chars = set('1234567890:')
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def any_2boolean(
|
|
35
|
+
value: Any
|
|
36
|
+
) -> bool:
|
|
37
|
+
""" return a boolean for any value
|
|
38
|
+
|
|
39
|
+
This function recognizes strings and bytes that signify False, such as "False" or "0"
|
|
40
|
+
"""
|
|
41
|
+
if not value:
|
|
42
|
+
return False
|
|
43
|
+
elif isinstance(value, (str, bytes)):
|
|
44
|
+
if value.lower() in ('0', 'false', b'0', b'false'):
|
|
45
|
+
return False
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def dict_from_dict_list(
|
|
50
|
+
dict_list: List[dict],
|
|
51
|
+
key: str,
|
|
52
|
+
single_value: Optional[str] = None,
|
|
53
|
+
include_key: bool = False
|
|
54
|
+
) -> Dict[str, Any]:
|
|
55
|
+
""" Create a dictionary from a list of dictionaries
|
|
56
|
+
|
|
57
|
+
:param dict_list: the list of dictionaries to be converted. Each item must be a dict.
|
|
58
|
+
:param key: the key of the dictionaries to use as key for the new dictionary.
|
|
59
|
+
The values for this key must be unique across the list. Key must exist in every dict.
|
|
60
|
+
:param single_value: if provided (as string), only this value from each dict is kept.
|
|
61
|
+
It must exist in every dict if specified.
|
|
62
|
+
:param include_key: if True, the value used as key is also included in the value dict.
|
|
63
|
+
:raises ValueError: if inputs are invalid or required keys are missing.
|
|
64
|
+
:return: dictionary keyed by the values of ``key``
|
|
65
|
+
"""
|
|
66
|
+
if not isinstance(dict_list, list):
|
|
67
|
+
raise ValueError('dict_list must be a list.')
|
|
68
|
+
if not dict_list:
|
|
69
|
+
return {}
|
|
70
|
+
if not isinstance(key, str) or not key:
|
|
71
|
+
raise ValueError('key must be a non-empty string.')
|
|
72
|
+
if single_value is not None and (not isinstance(single_value, str) or not single_value):
|
|
73
|
+
raise ValueError('single_value must be a non-empty string or None.')
|
|
74
|
+
|
|
75
|
+
# Validate each dict and check key presence and uniqueness
|
|
76
|
+
seen_keys = set()
|
|
77
|
+
for idx, d in enumerate(dict_list):
|
|
78
|
+
if not isinstance(d, dict):
|
|
79
|
+
raise ValueError(f'All items in dict_list must be dicts, but item at index {idx} is {type(d).__name__}.')
|
|
80
|
+
if key not in d:
|
|
81
|
+
raise ValueError(f'Key \'{key}\' must be present in every dict, but missing in item at index {idx}.')
|
|
82
|
+
if single_value is not None and single_value not in d:
|
|
83
|
+
raise ValueError(f'single_value \'{single_value}\' must be present in every dict, but missing in item at index {idx}.')
|
|
84
|
+
dict_key = d[key]
|
|
85
|
+
if dict_key in seen_keys:
|
|
86
|
+
raise ValueError(f'Key \'{key}\' values must be unique, but \'{dict_key}\' is duplicated.')
|
|
87
|
+
seen_keys.add(dict_key)
|
|
88
|
+
|
|
89
|
+
dl = [dict(d) for d in dict_list] # we copy the list because we will change the dictionaries (except for single_value)
|
|
90
|
+
|
|
91
|
+
new_dict = {}
|
|
92
|
+
|
|
93
|
+
for dictionary in dl:
|
|
94
|
+
if single_value:
|
|
95
|
+
new_dict[dictionary.pop(key)] = dictionary.get(single_value)
|
|
96
|
+
elif include_key:
|
|
97
|
+
new_dict[dictionary[key]] = dictionary
|
|
98
|
+
else:
|
|
99
|
+
# slick: pop the id and use it as key for newDict in one line
|
|
100
|
+
new_dict[dictionary.pop(key)] = dictionary
|
|
101
|
+
|
|
102
|
+
return new_dict
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def create_vcard(
|
|
106
|
+
data: Dict[str, str],
|
|
107
|
+
returning: Literal['vcard', 'pretty'] = 'vcard'
|
|
108
|
+
) -> Optional[str]:
|
|
109
|
+
""" create a vCard from a dictionary
|
|
110
|
+
|
|
111
|
+
.. note:: The serialized vcard has Windows line-breaks, which is fine, I guess
|
|
112
|
+
.. note:: colons in notes are escaped in vobject
|
|
113
|
+
|
|
114
|
+
:param data: keys:
|
|
115
|
+
|
|
116
|
+
- prename
|
|
117
|
+
- surname
|
|
118
|
+
- email
|
|
119
|
+
- phone
|
|
120
|
+
- street
|
|
121
|
+
- city
|
|
122
|
+
- region (eg. federal state)
|
|
123
|
+
- zipcode
|
|
124
|
+
- country
|
|
125
|
+
|
|
126
|
+
:param returning: ``'vcard'`` (serialized) or ``'pretty'`` (pretty-printed)
|
|
127
|
+
:return: vCard string, or None if no name is provided
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
prename = data.get('prename', '')
|
|
131
|
+
surname = data.get('surname', '')
|
|
132
|
+
if not prename and not surname:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
vcard = vobject.vCard()
|
|
136
|
+
vcard.add('n')
|
|
137
|
+
vcard.n.value = vobject.vcard.Name(
|
|
138
|
+
family=surname,
|
|
139
|
+
given=prename
|
|
140
|
+
)
|
|
141
|
+
vcard.add('fn')
|
|
142
|
+
vcard.fn.value = f'{prename} {surname}'.strip()
|
|
143
|
+
vcard.add('email')
|
|
144
|
+
vcard.email.value = data.get('email', '')
|
|
145
|
+
vcard.email.type_param = 'INTERNET'
|
|
146
|
+
vcard.add('tel')
|
|
147
|
+
phone = process_phonenumber(
|
|
148
|
+
data.get('phone', ''),
|
|
149
|
+
empty_if_invalid=True
|
|
150
|
+
)
|
|
151
|
+
if phone:
|
|
152
|
+
vcard.tel.value = phone
|
|
153
|
+
vcard.tel.type_param = ['CELL', 'VOICE']
|
|
154
|
+
vcard.add('adr')
|
|
155
|
+
vcard.adr.type_param = ['HOME', 'pref']
|
|
156
|
+
vcard.adr.value = vobject.vcard.Address(
|
|
157
|
+
street=data.get('street', ''),
|
|
158
|
+
city=data.get('city', ''),
|
|
159
|
+
region=data.get('region', ''),
|
|
160
|
+
code=data.get('zipcode', ''),
|
|
161
|
+
country=data.get('country', '')
|
|
162
|
+
)
|
|
163
|
+
vcard.add('note')
|
|
164
|
+
vcard.note.value = data.get('note', '')
|
|
165
|
+
|
|
166
|
+
if returning == 'pretty':
|
|
167
|
+
return vcard.prettyPrint()
|
|
168
|
+
else:
|
|
169
|
+
return vcard.serialize()
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def dict_2unicode(
|
|
173
|
+
d: Dict[Union[str, bytes], Any],
|
|
174
|
+
encoding: str = 'utf-8'
|
|
175
|
+
) -> Dict[str, Any]:
|
|
176
|
+
""" Convert all keys and values in a dictionary from bytes to unicode
|
|
177
|
+
|
|
178
|
+
All keys and values are changed from bytes to unicode, if applicable.
|
|
179
|
+
Other data types are left unchanged, including other compound data types.
|
|
180
|
+
|
|
181
|
+
:param d: dictionary
|
|
182
|
+
:param encoding: default is utf-8
|
|
183
|
+
:return: dictionary with all bytes decoded to strings
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
k.decode(encoding) if isinstance(k, bytes) else k:
|
|
188
|
+
(v.decode(encoding) if isinstance(v, bytes) else v)
|
|
189
|
+
for k, v in d.items()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def dict_2datatypes(
|
|
194
|
+
d: dict,
|
|
195
|
+
metadata: Optional[Dict[str, str]] = None,
|
|
196
|
+
convert_keys: bool = False,
|
|
197
|
+
convert_to_unicode: bool = False,
|
|
198
|
+
dt: str = 'datetime',
|
|
199
|
+
fmt: Optional[str] = None,
|
|
200
|
+
timezone: str = 'local'
|
|
201
|
+
) -> Dict[Any, Any]:
|
|
202
|
+
""" Convert all data of a dictionary to specific (json metadata) or guessed types
|
|
203
|
+
|
|
204
|
+
:param d: dictionary
|
|
205
|
+
:param metadata: dictionary containing types for the keys:
|
|
206
|
+
- boolean
|
|
207
|
+
- int
|
|
208
|
+
- float
|
|
209
|
+
- date
|
|
210
|
+
- datetime: date and time
|
|
211
|
+
- time: conversion or not based on dt
|
|
212
|
+
:param convert_keys: if True, keys are also converted via value_2datatype
|
|
213
|
+
:param convert_to_unicode: convert all bytes to unicode in the process
|
|
214
|
+
:param dt: - datetime or dt
|
|
215
|
+
- DateTime or DT
|
|
216
|
+
- ignore or string
|
|
217
|
+
- pendulum
|
|
218
|
+
- default: datetime, because it's Python's default
|
|
219
|
+
:param fmt: format for datetime parsing
|
|
220
|
+
:param timezone: default is 'local', other options are 'UTC' or 'Europe/Vienna'
|
|
221
|
+
:return: dictionary with values converted to their proper types
|
|
222
|
+
"""
|
|
223
|
+
if metadata is None:
|
|
224
|
+
metadata = {}
|
|
225
|
+
|
|
226
|
+
if convert_keys:
|
|
227
|
+
return {
|
|
228
|
+
value_2datatype(
|
|
229
|
+
value=key,
|
|
230
|
+
convert_to_unicode=convert_to_unicode,
|
|
231
|
+
dt=dt,
|
|
232
|
+
fmt=fmt
|
|
233
|
+
): value_2datatype(
|
|
234
|
+
value=val,
|
|
235
|
+
typ=metadata.get(key),
|
|
236
|
+
convert_to_unicode=convert_to_unicode,
|
|
237
|
+
dt=dt,
|
|
238
|
+
fmt=fmt,
|
|
239
|
+
timezone=timezone
|
|
240
|
+
)
|
|
241
|
+
for key, val in d.items()
|
|
242
|
+
} # dict comprehension
|
|
243
|
+
else:
|
|
244
|
+
return {
|
|
245
|
+
key: value_2datatype(
|
|
246
|
+
value=val,
|
|
247
|
+
typ=metadata.get(key),
|
|
248
|
+
convert_to_unicode=convert_to_unicode,
|
|
249
|
+
dt=dt,
|
|
250
|
+
fmt=fmt,
|
|
251
|
+
timezone=timezone
|
|
252
|
+
)
|
|
253
|
+
for key, val in d.items()
|
|
254
|
+
} # dict comprehension
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def dictlist_2datatypes(
|
|
258
|
+
dictlist: List[Dict[Any, Any]],
|
|
259
|
+
metadata: Optional[Dict[Any, Any]] = None,
|
|
260
|
+
convert_keys: bool = False,
|
|
261
|
+
convert_to_unicode: bool = False,
|
|
262
|
+
dt: str = 'datetime',
|
|
263
|
+
fmt: Optional[str] = None,
|
|
264
|
+
timezone: str = 'local'
|
|
265
|
+
) -> List[dict]:
|
|
266
|
+
""" Convert all data of a list of dictionaries to specific (json metadata) or guessed types
|
|
267
|
+
|
|
268
|
+
:param dictlist: list of dictionaries
|
|
269
|
+
:param metadata: dictionary containing types for the keys:
|
|
270
|
+
- boolean
|
|
271
|
+
- int
|
|
272
|
+
- float
|
|
273
|
+
- date
|
|
274
|
+
- datetime: date and time
|
|
275
|
+
- time: conversion or not based on dt
|
|
276
|
+
:param convert_keys: if True, keys are also converted via value_2datatype
|
|
277
|
+
:param convert_to_unicode: convert all bytes to unicode in the process
|
|
278
|
+
:param dt: - datetime or dt
|
|
279
|
+
- DateTime or DT
|
|
280
|
+
- ignore or string
|
|
281
|
+
- pendulum
|
|
282
|
+
- default: datetime, because it's Python's default
|
|
283
|
+
:param fmt: format for datetime parsing
|
|
284
|
+
:param timezone: default is 'local', other options are 'UTC' or 'Europe/Vienna'
|
|
285
|
+
:return: list of dictionaries with values converted to their proper types
|
|
286
|
+
"""
|
|
287
|
+
return [
|
|
288
|
+
dict_2datatypes(
|
|
289
|
+
d=dic,
|
|
290
|
+
metadata=metadata,
|
|
291
|
+
convert_keys=convert_keys,
|
|
292
|
+
convert_to_unicode=convert_to_unicode,
|
|
293
|
+
dt=dt,
|
|
294
|
+
fmt=fmt,
|
|
295
|
+
timezone=timezone
|
|
296
|
+
)
|
|
297
|
+
for dic
|
|
298
|
+
in dictlist
|
|
299
|
+
]
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def sort_dictlist(
|
|
303
|
+
dictlist: List[dict],
|
|
304
|
+
keys: Union[str, List[str]],
|
|
305
|
+
reverse: bool = False
|
|
306
|
+
) -> List[dict]:
|
|
307
|
+
""" Sorts a list of dictionaries
|
|
308
|
+
|
|
309
|
+
:param dictlist: list of dictionaries
|
|
310
|
+
:param keys: sort by these keys
|
|
311
|
+
:param reverse: True reverses the sorting order
|
|
312
|
+
|
|
313
|
+
:return: sorted list of dictionaries
|
|
314
|
+
|
|
315
|
+
.. deprecated:: 0.0.14
|
|
316
|
+
install package multisort and use it instead
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
if isinstance(keys, str):
|
|
320
|
+
if ',' in keys:
|
|
321
|
+
keys = keys.replace(
|
|
322
|
+
' ', ''
|
|
323
|
+
).split(',')
|
|
324
|
+
else:
|
|
325
|
+
keys = [keys]
|
|
326
|
+
|
|
327
|
+
if len(keys) == 1:
|
|
328
|
+
key = keys[0]
|
|
329
|
+
sorted_dictlist = sorted(
|
|
330
|
+
dictlist,
|
|
331
|
+
key=lambda i: i[key],
|
|
332
|
+
reverse=reverse
|
|
333
|
+
)
|
|
334
|
+
else:
|
|
335
|
+
sorted_dictlist = sorted(
|
|
336
|
+
dictlist,
|
|
337
|
+
key=lambda i: [i[key] for key in keys],
|
|
338
|
+
reverse=reverse
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
return sorted_dictlist
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def value_2datatype(
|
|
345
|
+
value: Any,
|
|
346
|
+
typ: Optional[str] = None,
|
|
347
|
+
convert_to_unicode: bool = False,
|
|
348
|
+
encoding: str = 'utf-8',
|
|
349
|
+
dt: str = 'datetime',
|
|
350
|
+
fmt: Optional[str] = None,
|
|
351
|
+
timezone: str = 'local'
|
|
352
|
+
) -> Optional[DataType]:
|
|
353
|
+
""" Convert a value to a datatype
|
|
354
|
+
|
|
355
|
+
this function has two modes:
|
|
356
|
+
|
|
357
|
+
1. a type is provided for the conversion
|
|
358
|
+
2. it makes educated guesses in converting a string
|
|
359
|
+
|
|
360
|
+
:param value:
|
|
361
|
+
:param typ: type for the conversion
|
|
362
|
+
:param convert_to_unicode: convert all bytes to unicode in the process
|
|
363
|
+
:param encoding: if bytes are present, use this encoding to convert them to unicode
|
|
364
|
+
:param dt: - datetime or dt
|
|
365
|
+
- DateTime or DT
|
|
366
|
+
- ignore or string
|
|
367
|
+
- pendulum
|
|
368
|
+
- default: datetime, because it's Python's default
|
|
369
|
+
:param timezone: default is 'local', other options are 'UTC' or 'Europe/Vienna'
|
|
370
|
+
:return: value converted to the appropriate type
|
|
371
|
+
"""
|
|
372
|
+
if typ:
|
|
373
|
+
if typ == 'ignore':
|
|
374
|
+
return value
|
|
375
|
+
elif typ == 'string':
|
|
376
|
+
return str(value)
|
|
377
|
+
elif typ == 'boolean':
|
|
378
|
+
return any_2boolean(value=value)
|
|
379
|
+
elif typ == 'int':
|
|
380
|
+
try:
|
|
381
|
+
return int(value)
|
|
382
|
+
except ValueError:
|
|
383
|
+
return value
|
|
384
|
+
elif typ == 'float':
|
|
385
|
+
if isinstance(value, str):
|
|
386
|
+
value = value.replace(',', '.')
|
|
387
|
+
try:
|
|
388
|
+
return float(value)
|
|
389
|
+
except (ValueError, TypeError):
|
|
390
|
+
return value
|
|
391
|
+
elif typ == 'date':
|
|
392
|
+
if dt in ('datetime', 'dt'):
|
|
393
|
+
return convert_datetime(
|
|
394
|
+
value,
|
|
395
|
+
convert_to='date',
|
|
396
|
+
fmt=fmt
|
|
397
|
+
)
|
|
398
|
+
elif dt in ('DateTime', 'DT'):
|
|
399
|
+
if ' ' in value:
|
|
400
|
+
value = value.split()[0] # get rid of time
|
|
401
|
+
return DateTime.DateTime(
|
|
402
|
+
value,
|
|
403
|
+
datefmt='international'
|
|
404
|
+
) + 0.5 # to avoid day-shifting due to timezones, use 12am
|
|
405
|
+
elif dt == 'pendulum':
|
|
406
|
+
if fmt:
|
|
407
|
+
p_dt = pendulum.from_format(value, fmt)
|
|
408
|
+
else:
|
|
409
|
+
p_dt = pendulum.parse(value)
|
|
410
|
+
|
|
411
|
+
if p_dt.timezone_name == 'UTC' and timezone == 'local':
|
|
412
|
+
return p_dt.in_tz(pendulum.local_timezone())
|
|
413
|
+
elif timezone:
|
|
414
|
+
return p_dt.in_tz(timezone)
|
|
415
|
+
return p_dt.date()
|
|
416
|
+
elif dt in ('ignore', 'string'):
|
|
417
|
+
return value
|
|
418
|
+
else:
|
|
419
|
+
raise ValueError(f'invalid value {dt} for argument dt')
|
|
420
|
+
elif typ == 'datetime':
|
|
421
|
+
if dt in ('datetime', 'dt'):
|
|
422
|
+
if fmt:
|
|
423
|
+
return datetime.datetime.strptime(value, fmt)
|
|
424
|
+
return convert_datetime(value, convert_to='datetime')
|
|
425
|
+
elif dt in ('DateTime', 'DT'):
|
|
426
|
+
value = value.strip()
|
|
427
|
+
try:
|
|
428
|
+
return DateTime.DateTime(
|
|
429
|
+
value,
|
|
430
|
+
datefmt='international'
|
|
431
|
+
)
|
|
432
|
+
except DateTime.interfaces.SyntaxError:
|
|
433
|
+
return value
|
|
434
|
+
elif dt == 'pendulum':
|
|
435
|
+
if fmt:
|
|
436
|
+
p_dt = pendulum.from_format(value, fmt)
|
|
437
|
+
else:
|
|
438
|
+
p_dt = pendulum.parse(value)
|
|
439
|
+
|
|
440
|
+
if p_dt.timezone_name == 'UTC' and timezone == 'local':
|
|
441
|
+
return p_dt.in_tz(pendulum.local_timezone())
|
|
442
|
+
elif timezone:
|
|
443
|
+
return p_dt.in_tz(timezone)
|
|
444
|
+
return p_dt
|
|
445
|
+
elif dt in ('ignore', 'string'):
|
|
446
|
+
return value
|
|
447
|
+
else:
|
|
448
|
+
raise ValueError(f'invalid value {dt} for argument dt')
|
|
449
|
+
elif 'time' in typ:
|
|
450
|
+
if 'datetime' in typ or dt in ('datetime', 'dt'):
|
|
451
|
+
parsed_dt = parse(value)
|
|
452
|
+
return parsed_dt.time()
|
|
453
|
+
else:
|
|
454
|
+
return value # DT: return the string
|
|
455
|
+
|
|
456
|
+
else:
|
|
457
|
+
if isinstance(value, (bool, int, float)):
|
|
458
|
+
return value
|
|
459
|
+
|
|
460
|
+
original_value = value
|
|
461
|
+
if isinstance(value, bytes):
|
|
462
|
+
value = value.decode(encoding)
|
|
463
|
+
|
|
464
|
+
if isinstance(value, str):
|
|
465
|
+
value = value.strip()
|
|
466
|
+
|
|
467
|
+
if value == 'False':
|
|
468
|
+
return False
|
|
469
|
+
|
|
470
|
+
if value == 'True':
|
|
471
|
+
return True
|
|
472
|
+
|
|
473
|
+
try:
|
|
474
|
+
if (
|
|
475
|
+
':' in value
|
|
476
|
+
and set(value).issubset(time_chars)
|
|
477
|
+
and (
|
|
478
|
+
len(value) == 5 # hh:mm
|
|
479
|
+
or len(value) == 8 # hh:mm:ss
|
|
480
|
+
)
|
|
481
|
+
):
|
|
482
|
+
if dt in ('datetime', 'dt'):
|
|
483
|
+
return parse(value).time()
|
|
484
|
+
else:
|
|
485
|
+
return value
|
|
486
|
+
elif get_dateformat(
|
|
487
|
+
datestring=value,
|
|
488
|
+
checkonly=True
|
|
489
|
+
):
|
|
490
|
+
if dt in ('datetime', 'dt'):
|
|
491
|
+
return str_to_date(
|
|
492
|
+
datestring=value
|
|
493
|
+
)
|
|
494
|
+
elif dt in ('DateTime', 'DT'):
|
|
495
|
+
return str_to_DT(
|
|
496
|
+
datestring=value
|
|
497
|
+
)
|
|
498
|
+
elif dt == 'pendulum':
|
|
499
|
+
if fmt:
|
|
500
|
+
p_dt = pendulum.from_format(value, fmt)
|
|
501
|
+
else:
|
|
502
|
+
p_dt = pendulum.parse(value)
|
|
503
|
+
|
|
504
|
+
if p_dt.timezone_name == 'UTC' and timezone == 'local':
|
|
505
|
+
return p_dt.in_tz(pendulum.local_timezone())
|
|
506
|
+
elif timezone:
|
|
507
|
+
return p_dt.in_tz(timezone)
|
|
508
|
+
return p_dt
|
|
509
|
+
else:
|
|
510
|
+
return convert_datetime(
|
|
511
|
+
date=value,
|
|
512
|
+
convert_to=dt
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
except Exception:
|
|
516
|
+
# in case it looks like a date/time string but isn't parseable
|
|
517
|
+
pass
|
|
518
|
+
|
|
519
|
+
try:
|
|
520
|
+
return int(value)
|
|
521
|
+
except (ValueError, TypeError):
|
|
522
|
+
pass
|
|
523
|
+
|
|
524
|
+
if set(value).issubset(comma_float_chars):
|
|
525
|
+
value = value.replace(',', '.')
|
|
526
|
+
|
|
527
|
+
try:
|
|
528
|
+
return float(value)
|
|
529
|
+
except (ValueError, TypeError):
|
|
530
|
+
pass
|
|
531
|
+
|
|
532
|
+
if (
|
|
533
|
+
not convert_to_unicode
|
|
534
|
+
and isinstance(value, str)
|
|
535
|
+
): # potentially restore bytes
|
|
536
|
+
value = original_value
|
|
537
|
+
|
|
538
|
+
return value
|