1"""PhoneNumber object definition"""
2
3# Based on original Java code and protocol buffer:
4# resources/phonenumber.proto
5# java/src/com/google/i18n/phonenumbers/Phonenumber.java
6# Copyright (C) 2010-2011 The Libphonenumber Authors
7#
8# Licensed under the Apache License, Version 2.0 (the "License");
9# you may not use this file except in compliance with the License.
10# You may obtain a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing, software
15# distributed under the License is distributed on an "AS IS" BASIS,
16# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17# See the License for the specific language governing permissions and
18# limitations under the License.
19from .util import UnicodeMixin, ImmutableMixin, mutating_method
20from .util import to_long, unicod, rpr, force_unicode, u
21
22
23class CountryCodeSource(object):
24 """The source from which a country code is derived."""
25 # Default value returned if this is not set, because the phone number was
26 # created using parse(keep_raw_input=False).
27 UNSPECIFIED = 0
28
29 # The country_code is derived based on a phone number with a leading "+",
30 # e.g. the French number "+33 1 42 68 53 00".
31 FROM_NUMBER_WITH_PLUS_SIGN = 1
32
33 # The country_code is derived based on a phone number with a leading IDD,
34 # e.g. the French number "011 33 1 42 68 53 00", as it is dialled
35 # from US.
36 FROM_NUMBER_WITH_IDD = 5
37
38 # The country_code is derived based on a phone number without a leading
39 # "+", e.g. the French number "33 1 42 68 53 00" when default_country is
40 # supplied as France.
41 FROM_NUMBER_WITHOUT_PLUS_SIGN = 10
42
43 # The country_code is derived NOT based on the phone number itself, but
44 # from the default_country parameter provided in the parsing function by
45 # the clients. This happens mostly for numbers written in the national
46 # format (without country code). For example, this would be set when
47 # parsing the French number "01 42 68 53 00", when default_country is
48 # supplied as France.
49 FROM_DEFAULT_COUNTRY = 20
50
51 @classmethod
52 def to_string(cls, val):
53 """Return a string representation of a CountryCodeSource value"""
54 if val == CountryCodeSource.UNSPECIFIED:
55 return u("UNSPECIFIED")
56 elif val == CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN:
57 return u("FROM_NUMBER_WITH_PLUS_SIGN")
58 elif val == CountryCodeSource.FROM_NUMBER_WITH_IDD:
59 return u("FROM_NUMBER_WITH_IDD")
60 elif val == CountryCodeSource.FROM_NUMBER_WITHOUT_PLUS_SIGN:
61 return u("FROM_NUMBER_WITHOUT_PLUS_SIGN")
62 elif val == CountryCodeSource.FROM_DEFAULT_COUNTRY:
63 return u("FROM_DEFAULT_COUNTRY")
64 else:
65 return u("INVALID (%d)" % val)
66
67
68class PhoneNumber(UnicodeMixin):
69 """Class representing international telephone numbers.
70
71 This class is hand-created based on phonenumber.proto. Please refer
72 to that file for detailed descriptions of the meaning of each field.
73 """
74
75 def __init__(self,
76 country_code=None,
77 national_number=None,
78 extension=None,
79 italian_leading_zero=None,
80 number_of_leading_zeros=None,
81 raw_input=None,
82 country_code_source=CountryCodeSource.UNSPECIFIED,
83 preferred_domestic_carrier_code=None):
84 # The country calling code for this number, as defined by the
85 # International Telecommunication Union (ITU). For example, this would
86 # be 1 for NANPA countries, and 33 for France.
87 #
88 # None if not set, of type int otherwise.
89 if country_code is None:
90 self.country_code = None
91 else:
92 self.country_code = int(country_code)
93
94 # Number does not contain National(trunk) prefix.
95 # National (significant) Number is defined in International
96 # Telecommunication Union (ITU) Recommendation E.164. It is a
97 # language/country-neutral representation of a phone number at a
98 # country level. For countries which have the concept of an "area
99 # code" or "national destination code", this is included in the
100 # National (significant) Number. Although the ITU says the maximum
101 # length should be 15, we have found longer numbers in some countries
102 # e.g. Germany. Note that the National (significant) Number does not
103 # contain the National(trunk) prefix.
104 #
105 # None if not set, of type long otherwise (and so it will never
106 # contain any formatting (hypens, spaces, parentheses), nor any
107 # alphanumeric spellings).
108
109 if national_number is None:
110 self.national_number = None
111 else:
112 self.national_number = to_long(national_number)
113
114 # Extension is not standardized in ITU recommendations, except for
115 # being defined as a series of numbers with a maximum length of 40
116 # digits.
117 #
118 # When present, it is a Unicode string to accommodate for the
119 # possible use of a leading zero in the extension (organizations
120 # have complete freedom to do so, as there is no standard defined).
121 # However, only ASCII digits should be stored here.
122 self.extension = force_unicode(extension) # None or Unicode '[0-9]+'
123
124 # In some countries, the national (significant) number starts with one
125 # or more "0"s without this being a national prefix or trunk code of
126 # some kind. For example, the leading zero in the national
127 # (significant) number of an Italian phone number indicates the number
128 # is a fixed-line number. There have been plans to migrate fixed-line
129 # numbers to start with the digit two since December 2000, but it has
130 # not happened yet. See http://en.wikipedia.org/wiki/%2B39 for more
131 # details.
132 #
133 # These fields can be safely ignored (there is no need to set them)
134 # for most countries. Some limited number of countries behave like
135 # Italy - for these cases, if the leading zero(s) of a number would be
136 # retained even when dialling internationally, set this flag to true,
137 # and also set the number of leading zeros.
138 #
139 # Clients who use the parsing functionality of the i18n phone number
140 # libraries will have these fields set if necessary automatically.
141 #
142 # None if not set, of type bool otherwise:
143 if italian_leading_zero is None:
144 self.italian_leading_zero = None
145 else:
146 self.italian_leading_zero = bool(italian_leading_zero)
147
148 # None if not set, of type int otherwise.
149 if number_of_leading_zeros is None:
150 self.number_of_leading_zeros = None
151 else:
152 self.number_of_leading_zeros = int(number_of_leading_zeros)
153
154 # The next few fields are non-essential fields for a phone number.
155 # They retain extra information about the form the phone number was
156 # in when it was provided to us to parse. They can be safely
157 # ignored by most clients.
158
159 # This field is used to store the raw input string containing phone
160 # numbers before it was canonicalized by the library. For example, it
161 # could be used to store alphanumerical numbers such as
162 # "1-800-GOOG-411".
163 self.raw_input = force_unicode(raw_input) # None or Unicode string
164
165 # The source from which the country_code is derived. This is not set
166 # in the general parsing method, but in the method that parses and
167 # keeps raw_input. New fields could be added upon request.
168 self.country_code_source = country_code_source # CountryCodeSource.VALUE
169 if self.country_code_source is None: # pragma no cover
170 self.country_code_source = CountryCodeSource.UNSPECIFIED
171
172 # The carrier selection code that is preferred when calling this
173 # phone number domestically. This also includes codes that need to
174 # be dialed in some countries when calling from landlines to mobiles
175 # or vice versa. For example, in Columbia, a "3" needs to be dialed
176 # before the phone number itself when calling from a mobile phone to
177 # a domestic landline phone and vice versa.
178 #
179 # Note this is the "preferred" code, which means other codes may work
180 # as well.
181 self.preferred_domestic_carrier_code = force_unicode(preferred_domestic_carrier_code)
182 # None or Unicode string
183
184 def clear(self):
185 """Erase the contents of the object"""
186 self.country_code = None
187 self.national_number = None
188 self.extension = None
189 self.italian_leading_zero = None
190 self.number_of_leading_zeros = None
191 self.raw_input = None
192 self.country_code_source = CountryCodeSource.UNSPECIFIED
193 self.preferred_domestic_carrier_code = None
194
195 def merge_from(self, other):
196 """Merge information from another PhoneNumber object into this one."""
197 if other.country_code is not None:
198 self.country_code = other.country_code
199 if other.national_number is not None:
200 self.national_number = other.national_number
201 if other.extension is not None:
202 self.extension = other.extension
203 if other.italian_leading_zero is not None:
204 self.italian_leading_zero = other.italian_leading_zero
205 if other.number_of_leading_zeros is not None:
206 self.number_of_leading_zeros = other.number_of_leading_zeros
207 if other.raw_input is not None:
208 self.raw_input = other.raw_input
209 if other.country_code_source is not CountryCodeSource.UNSPECIFIED:
210 self.country_code_source = other.country_code_source
211 if other.preferred_domestic_carrier_code is not None:
212 self.preferred_domestic_carrier_code = other.preferred_domestic_carrier_code
213
214 def __eq__(self, other):
215 if not isinstance(other, PhoneNumber):
216 return False
217 return (self.country_code == other.country_code and
218 self.national_number == other.national_number and
219 self.extension == other.extension and
220 bool(self.italian_leading_zero) == bool(other.italian_leading_zero) and
221 self.number_of_leading_zeros == other.number_of_leading_zeros and
222 self.raw_input == other.raw_input and
223 self.country_code_source == other.country_code_source and
224 self.preferred_domestic_carrier_code == other.preferred_domestic_carrier_code)
225
226 def __ne__(self, other):
227 return not self.__eq__(other)
228
229 def __repr__(self):
230 return (unicod("%s(country_code=%s, national_number=%s, extension=%s, " +
231 "italian_leading_zero=%s, number_of_leading_zeros=%s, " +
232 "country_code_source=%s, preferred_domestic_carrier_code=%s)") %
233 (type(self).__name__,
234 self.country_code,
235 self.national_number,
236 rpr(self.extension),
237 self.italian_leading_zero,
238 self.number_of_leading_zeros,
239 self.country_code_source,
240 rpr(self.preferred_domestic_carrier_code)))
241
242 def __unicode__(self):
243 result = (unicod("Country Code: %s National Number: %s") %
244 (self.country_code, self.national_number))
245 if self.italian_leading_zero is not None:
246 result += unicod(" Leading Zero(s): %s") % self.italian_leading_zero
247 if self.number_of_leading_zeros is not None:
248 result += unicod(" Number of leading zeros: %d") % self.number_of_leading_zeros
249 if self.extension is not None:
250 result += unicod(" Extension: %s") % self.extension
251 if self.country_code_source is not CountryCodeSource.UNSPECIFIED:
252 result += unicod(" Country Code Source: %s") % self.country_code_source
253 if self.preferred_domestic_carrier_code is not None:
254 result += (unicod(" Preferred Domestic Carrier Code: %s") %
255 self.preferred_domestic_carrier_code)
256 return result
257
258
259class FrozenPhoneNumber(PhoneNumber, ImmutableMixin):
260 """Immutable version of PhoneNumber"""
261 def __hash__(self):
262 return hash((self.country_code,
263 self.national_number,
264 self.extension,
265 bool(self.italian_leading_zero),
266 self.number_of_leading_zeros,
267 self.raw_input,
268 self.country_code_source,
269 self.preferred_domestic_carrier_code))
270
271 @mutating_method
272 def __init__(self, *args, **kwargs):
273 if len(kwargs) == 0 and len(args) == 1 and isinstance(args[0], PhoneNumber):
274 # Copy constructor
275 super(FrozenPhoneNumber, self).__init__(**args[0].__dict__)
276 else:
277 super(FrozenPhoneNumber, self).__init__(*args, **kwargs)