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
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1from functools import total_ordering
3import phonenumbers
4from django.conf import settings
5from django.core import validators
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 """
16 format_map = {
17 "E164": phonenumbers.PhoneNumberFormat.E164,
18 "INTERNATIONAL": phonenumbers.PhoneNumberFormat.INTERNATIONAL,
19 "NATIONAL": phonenumbers.PhoneNumberFormat.NATIONAL,
20 "RFC3966": phonenumbers.PhoneNumberFormat.RFC3966,
21 }
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
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
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__()
54 def is_valid(self):
55 """
56 Whether the number supplied is actually valid.
58 :return: ``True`` when the phone number is valid.
59 :rtype: bool
60 """
61 return phonenumbers.is_valid_number(self)
63 def format_as(self, format):
64 return phonenumbers.format_number(self, format)
66 @property
67 def as_international(self):
68 return self.format_as(phonenumbers.PhoneNumberFormat.INTERNATIONAL)
70 @property
71 def as_e164(self):
72 return self.format_as(phonenumbers.PhoneNumberFormat.E164)
74 @property
75 def as_national(self):
76 return self.format_as(phonenumbers.PhoneNumberFormat.NATIONAL)
78 @property
79 def as_rfc3966(self):
80 return self.format_as(phonenumbers.PhoneNumberFormat.RFC3966)
82 def __len__(self):
83 return len(str(self))
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
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
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 )
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)
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)
136 def __hash__(self):
137 return hash(str(self))
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
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 )