Coverage for jutil/parse.py: 77%

51 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-10-07 16:40 -0500

1import logging 

2from datetime import datetime, time, date 

3from typing import Optional, Any 

4from django.core.exceptions import ValidationError 

5from django.utils.translation import gettext as _ 

6import pytz 

7from django.utils.dateparse import parse_datetime as django_parse_datetime 

8from django.utils.dateparse import parse_date as django_parse_date 

9 

10logger = logging.getLogger(__name__) 

11 

12TRUE_VALUES = ( 

13 "true", 

14 "1", 

15 "yes", 

16 "on", 

17 "enabled", 

18) 

19 

20FALSE_VALUES = ( 

21 "false", 

22 "0", 

23 "no", 

24 "off", 

25 "disabled", 

26 "none", 

27 "null", 

28) 

29 

30 

31def parse_bool(v: str) -> bool: 

32 """ 

33 Parses boolean value 

34 :param v: Input string 

35 :return: bool 

36 """ 

37 s = str(v).lower() 

38 if s in TRUE_VALUES: 

39 return True 

40 if s in FALSE_VALUES: 

41 return False 

42 raise ValidationError(_("%(value)s is not one of the available choices") % {"value": v}) 

43 

44 

45def parse_datetime(v: str, tz: Any = None) -> datetime: 

46 """ 

47 Parses ISO date/datetime string to timezone-aware datetime. 

48 Supports YYYY-MM-DD date strings where time part is missing. 

49 Returns always timezone-aware datetime (assumes UTC if timezone missing). 

50 :param v: Input string to parse 

51 :param tz: Default pytz timezone or if None then use UTC as default 

52 :return: datetime with timezone 

53 """ 

54 try: 

55 t = django_parse_datetime(v) 

56 if t is None: 

57 t_date: Optional[date] = django_parse_date(v) 

58 if t_date is None: 58 ↛ 60line 58 didn't jump to line 60, because the condition on line 58 was never false

59 raise ValidationError(_("“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.") % {"value": v}) 

60 t = datetime.combine(t_date, time()) 

61 if tz is None: 61 ↛ 63line 61 didn't jump to line 63, because the condition on line 61 was never false

62 tz = pytz.utc 

63 return t if t.tzinfo else tz.localize(t) 

64 except Exception as err: 

65 raise ValidationError(_("“%(value)s” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.") % {"value": v}) from err 

66 

67 

68def parse_bool_or_none(v: str) -> Optional[bool]: 

69 """ 

70 Parses boolean value, or returns None if parsing fails. 

71 :param v: Input string 

72 :return: bool or None 

73 """ 

74 s = str(v).lower() 

75 if s in TRUE_VALUES: 

76 return True 

77 if s in FALSE_VALUES: 

78 return False 

79 return None 

80 

81 

82def parse_datetime_or_none(v: str, tz: Any = None) -> Optional[datetime]: 

83 """ 

84 Parses ISO date/datetime string to timezone-aware datetime. 

85 Supports YYYY-MM-DD date strings where time part is missing. 

86 Returns timezone-aware datetime (assumes UTC if timezone missing) or None if parsing fails. 

87 :param v: Input string to parse 

88 :param tz: Default pytz timezone or if None then use UTC as default 

89 :return: datetime with timezone or None 

90 """ 

91 try: 

92 t = django_parse_datetime(v) 

93 if t is None: 

94 t_date: Optional[date] = django_parse_date(v) 

95 if t_date is None: 95 ↛ 97line 95 didn't jump to line 97, because the condition on line 95 was never false

96 return None 

97 t = datetime.combine(t_date, time()) 

98 if tz is None: 98 ↛ 100line 98 didn't jump to line 100, because the condition on line 98 was never false

99 tz = pytz.utc 

100 return t if t.tzinfo else tz.localize(t) 

101 except Exception: 

102 return None