Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/phonenumber_field/phonenumber.py: 38%

91 statements  

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

1from functools import total_ordering 

2 

3import phonenumbers 

4from django.conf import settings 

5from django.core import validators 

6 

7 

8@total_ordering 

9class PhoneNumber(phonenumbers.PhoneNumber): 

10 """ 

11 A extended version of phonenumbers.PhoneNumber that provides 

12 some neat and more pythonic, easy to access methods. This makes using a 

13 PhoneNumber instance much easier, especially in templates and such. 

14 """ 

15 

16 format_map = { 

17 "E164": phonenumbers.PhoneNumberFormat.E164, 

18 "INTERNATIONAL": phonenumbers.PhoneNumberFormat.INTERNATIONAL, 

19 "NATIONAL": phonenumbers.PhoneNumberFormat.NATIONAL, 

20 "RFC3966": phonenumbers.PhoneNumberFormat.RFC3966, 

21 } 

22 

23 @classmethod 

24 def from_string(cls, phone_number, region=None): 

25 """ 

26 :arg str phone_number: parse this :class:`str` as a phone number. 

27 :keyword str region: 2-letter country code as defined in ISO 3166-1. 

28 When not supplied, defaults to :setting:`PHONENUMBER_DEFAULT_REGION` 

29 """ 

30 phone_number_obj = cls() 

31 if region is None: 31 ↛ 33line 31 didn't jump to line 33, because the condition on line 31 was never false

32 region = getattr(settings, "PHONENUMBER_DEFAULT_REGION", None) 

33 phonenumbers.parse( 

34 number=phone_number, 

35 region=region, 

36 keep_raw_input=True, 

37 numobj=phone_number_obj, 

38 ) 

39 return phone_number_obj 

40 

41 def __str__(self): 

42 if self.is_valid(): 

43 format_string = getattr(settings, "PHONENUMBER_DEFAULT_FORMAT", "E164") 

44 fmt = self.format_map[format_string] 

45 return self.format_as(fmt) 

46 else: 

47 return self.raw_input 

48 

49 def __repr__(self): 

50 if not self.is_valid(): 

51 return f"Invalid{type(self).__name__}(raw_input={self.raw_input})" 

52 return super().__repr__() 

53 

54 def is_valid(self): 

55 """ 

56 Whether the number supplied is actually valid. 

57 

58 :return: ``True`` when the phone number is valid. 

59 :rtype: bool 

60 """ 

61 return phonenumbers.is_valid_number(self) 

62 

63 def format_as(self, format): 

64 return phonenumbers.format_number(self, format) 

65 

66 @property 

67 def as_international(self): 

68 return self.format_as(phonenumbers.PhoneNumberFormat.INTERNATIONAL) 

69 

70 @property 

71 def as_e164(self): 

72 return self.format_as(phonenumbers.PhoneNumberFormat.E164) 

73 

74 @property 

75 def as_national(self): 

76 return self.format_as(phonenumbers.PhoneNumberFormat.NATIONAL) 

77 

78 @property 

79 def as_rfc3966(self): 

80 return self.format_as(phonenumbers.PhoneNumberFormat.RFC3966) 

81 

82 def __len__(self): 

83 return len(str(self)) 

84 

85 def __eq__(self, other): 

86 """ 

87 Override parent equality because we store only string representation 

88 of phone number, so we must compare only this string representation 

89 """ 

90 if other in validators.EMPTY_VALUES: 

91 return False 

92 elif isinstance(other, str): 

93 default_region = getattr(settings, "PHONENUMBER_DEFAULT_REGION", None) 

94 other = to_python(other, region=default_region) 

95 elif isinstance(other, type(self)): 

96 # Nothing to do. Good to compare. 

97 pass 

98 elif isinstance(other, phonenumbers.PhoneNumber): 

99 # The parent class of PhoneNumber does not have .is_valid(). 

100 # We need to make it match ours. 

101 old_other = other 

102 other = type(self)() 

103 other.merge_from(old_other) 

104 else: 

105 return False 

106 

107 format_string = getattr(settings, "PHONENUMBER_DB_FORMAT", "E164") 

108 fmt = self.format_map[format_string] 

109 self_str = self.format_as(fmt) if self.is_valid() else self.raw_input 

110 other_str = other.format_as(fmt) if other.is_valid() else other.raw_input 

111 return self_str == other_str 

112 

113 def __lt__(self, other): 

114 if isinstance(other, phonenumbers.PhoneNumber): 

115 old_other = other 

116 other = type(self)() 

117 other.merge_from(old_other) 

118 elif not isinstance(other, type(self)): 

119 raise TypeError( 

120 "'<' not supported between instances of " 

121 "'%s' and '%s'" % (type(self).__name__, type(other).__name__) 

122 ) 

123 

124 invalid = None 

125 if not self.is_valid(): 

126 invalid = self 

127 elif not other.is_valid(): 

128 invalid = other 

129 if invalid is not None: 

130 raise ValueError("Invalid phone number: %r" % invalid) 

131 

132 format_string = getattr(settings, "PHONENUMBER_DB_FORMAT", "E164") 

133 fmt = self.format_map[format_string] 

134 return self.format_as(fmt) < other.format_as(fmt) 

135 

136 def __hash__(self): 

137 return hash(str(self)) 

138 

139 

140def to_python(value, region=None): 

141 if value in validators.EMPTY_VALUES: # None or '' 

142 phone_number = value 

143 elif isinstance(value, str): 143 ↛ 149line 143 didn't jump to line 149, because the condition on line 143 was never false

144 try: 

145 phone_number = PhoneNumber.from_string(phone_number=value, region=region) 

146 except phonenumbers.NumberParseException: 

147 # the string provided is not a valid PhoneNumber. 

148 phone_number = PhoneNumber(raw_input=value) 

149 elif isinstance(value, PhoneNumber): 

150 phone_number = value 

151 elif isinstance(value, phonenumbers.PhoneNumber): 

152 phone_number = PhoneNumber() 

153 phone_number.merge_from(value) 

154 else: 

155 raise TypeError("Can't convert %s to PhoneNumber." % type(value).__name__) 

156 return phone_number 

157 

158 

159def validate_region(region): 

160 if ( 160 ↛ 164line 160 didn't jump to line 164

161 region is not None 

162 and region not in phonenumbers.phonenumberutil.SUPPORTED_REGIONS 

163 ): 

164 raise ValueError( 

165 "“%s” is not a valid region code. Choices are %r" 

166 % (region, phonenumbers.phonenumberutil.SUPPORTED_REGIONS) 

167 )