Skip to content

Commit bd5c3d9

Browse files
committed
Merge code changes from upstream r516
1 parent 965579f commit bd5c3d9

4 files changed

Lines changed: 314 additions & 26 deletions

File tree

python/phonenumbers/asyoutypeformatter.py

Lines changed: 79 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@
3333
from .phonenumberutil import _PLUS_SIGN, _PLUS_CHARS_PATTERN
3434
from .phonenumberutil import _extract_country_code, region_code_for_country_code
3535
from .phonenumberutil import country_code_for_region
36+
from .phonenumberutil import _formatting_rule_has_first_group_only
3637

38+
# Character used when appropriate to separate a prefix, such as a long NDD or
39+
# a country calling code, from the national number.
40+
_SEPARATOR_BEFORE_NATIONAL_NUMBER = u' '
3741
_EMPTY_METADATA = PhoneMetadata(id=u"", international_prefix=u"NA", register=False)
3842

3943
# A pattern that is used to match character classes in regular expressions. An
@@ -46,6 +50,10 @@
4650
# two-digit number, since the phone number can be as long as 15 digits.
4751
_STANDALONE_DIGIT_PATTERN = re.compile(u"\\d(?=[^,}][^,}])")
4852

53+
# A set of characters that, if found in a national prefix formatting rules, are an indicator to
54+
# us that we should separate the national prefix from the number when formatting.
55+
_NATIONAL_PREFIX_SEPARATORS_PATTERN = re.compile("[- ]")
56+
4957
# A pattern that is used to determine if a number_format under
5058
# available_formats is eligible to be used by the AYTF. It is eligible when
5159
# the format element under number_format contains groups of the dollar sign
@@ -107,6 +115,10 @@ def _maybe_create_new_template(self):
107115
return False
108116
if self._create_formatting_template(number_format):
109117
self._current_formatting_pattern = pattern
118+
if number_format.national_prefix_formatting_rule is None:
119+
self._should_add_space_after_national_prefix = False
120+
else:
121+
self._should_add_space_after_national_prefix = bool(_NATIONAL_PREFIX_SEPARATORS_PATTERN.search(number_format.national_prefix_formatting_rule))
110122
# With a new formatting template, the matched position using
111123
# the old template needs to be reset.
112124
self._last_match_position = 0
@@ -120,14 +132,16 @@ def _maybe_create_new_template(self):
120132
return False
121133

122134
def _get_available_formats(self, leading_three_digits):
123-
if (self._is_international_formatting and
135+
if (self._is_complete_number and
124136
len(self._current_metadata.intl_number_format) > 0):
125137
format_list = self._current_metadata.intl_number_format
126138
else:
127139
format_list = self._current_metadata.number_format
128140
for this_format in format_list:
129-
if self._is_format_eligible(this_format.format):
130-
self._possible_formats.append(this_format)
141+
if (self._is_complete_number or this_format.national_prefix_optional_when_formatting or
142+
_formatting_rule_has_first_group_only(this_format.national_prefix_formatting_rule)):
143+
if self._is_format_eligible(this_format.format):
144+
self._possible_formats.append(this_format)
131145
self._narrow_down_possible_formats(leading_three_digits)
132146

133147
def _is_format_eligible(self, format):
@@ -207,6 +221,7 @@ def _clear(self):
207221
# inserted). For example, this can contain IDD, country code, and/or
208222
# NDD, etc.
209223
self._prefix_before_national_number = ""
224+
self._should_add_space_after_national_prefix = False
210225
# This contains the national prefix that has been extracted. It
211226
# contains only digits without formatting.
212227
self._national_prefix_extracted = ""
@@ -225,7 +240,11 @@ def _clear(self):
225240
# most recently invoked, as found in the original sequence of
226241
# characters the user entered.
227242
self._original_position = 0
228-
self._is_international_formatting = False
243+
# This is set to true when we know the user is entering a full
244+
# national significant number, since we have either detected a
245+
# national prefix or an international dialing prefix. When this is
246+
# true, we will no longer use local number formatting patterns.
247+
self._is_complete_number = False
229248
self._is_expecting_country_calling_code = False
230249
self._possible_formats = []
231250

@@ -281,8 +300,11 @@ def input_digit(self, next_char, remember_position=False):
281300
return self._current_output
282301
elif self._able_to_extract_longer_ndd():
283302
# Add an additional space to separate long NDD and national
284-
# significant number for readability.
285-
self._prefix_before_national_number += " "
303+
# significant number for readability. We don't set
304+
# should_add_space_after_national_prefix to True, since we don't
305+
# want this to change later when we choose formatting
306+
# templates.
307+
self._prefix_before_national_number += _SEPARATOR_BEFORE_NATIONAL_NUMBER
286308
self._current_output = self._attempt_to_choose_pattern_with_prefix_extracted()
287309
return self._current_output
288310

@@ -310,8 +332,7 @@ def input_digit(self, next_char, remember_position=False):
310332
self._current_output = self._prefix_before_national_number + self._national_number
311333
return self._current_output
312334

313-
if len(self._possible_formats) > 0:
314-
# The formatting pattern is already chosen.
335+
if len(self._possible_formats) > 0: # The formatting pattern is already chosen.
315336
temp_national_number = self._input_digit_helper(next_char)
316337
# See if the accrued digits can be formatted properly already. If
317338
# not, use the results from input_digit_helper, which does
@@ -325,7 +346,7 @@ def input_digit(self, next_char, remember_position=False):
325346
self._current_output = self._input_accrued_national_number()
326347
return self._current_output
327348
if self._able_to_format:
328-
self._current_output = self._prefix_before_national_number + temp_national_number
349+
self._current_output = self._append_national_number(temp_national_number)
329350
return self._current_output
330351
else:
331352
self._current_output = self._accrued_input
@@ -362,11 +383,18 @@ def _is_digit_or_leading_plus_sign(self, next_char):
362383
fullmatch(_PLUS_CHARS_PATTERN, next_char)))
363384

364385
def _attempt_to_format_accrued_digits(self):
365-
for num_format in self._possible_formats:
366-
num_re = re.compile(num_format.pattern)
386+
"""Check to see if there is an exact pattern match for these digits. If so, we should use this
387+
instead of any other formatting template whose leadingDigitsPattern also matches the input.
388+
"""
389+
for number_format in self._possible_formats:
390+
num_re = re.compile(number_format.pattern)
367391
if fullmatch(num_re, self._national_number):
368-
formatted_number = re.sub(num_re, num_format.format, self._national_number)
369-
return self._prefix_before_national_number + formatted_number
392+
if number_format.national_prefix_formatting_rule is None:
393+
self._should_add_space_after_national_prefix = False
394+
else:
395+
self._should_add_space_after_national_prefix = bool(_NATIONAL_PREFIX_SEPARATORS_PATTERN.search(number_format.national_prefix_formatting_rule))
396+
formatted_number = re.sub(num_re, number_format.format, self._national_number)
397+
return self._append_national_number(formatted_number)
370398
return ""
371399

372400
def get_remembered_position(self):
@@ -385,6 +413,23 @@ def get_remembered_position(self):
385413
current_output_index += 1
386414
return current_output_index
387415

416+
def _append_national_number(self, national_number):
417+
"""Combines the national number with any prefix (IDD/+ and country
418+
code or national prefix) that was collected. A space will be inserted
419+
between them if the current formatting template indicates this to be
420+
suitable.
421+
"""
422+
prefix_before_nn_len = len(self._prefix_before_national_number)
423+
if (self._should_add_space_after_national_prefix and prefix_before_nn_len > 0 and
424+
self._prefix_before_national_number[-1] != _SEPARATOR_BEFORE_NATIONAL_NUMBER):
425+
# We want to add a space after the national prefix if the national
426+
# prefix formatting rule indicates that this would normally be
427+
# done, with the exception of the case where we already appended a
428+
# space because the NDD was surprisingly long.
429+
return self._prefix_before_national_number + _SEPARATOR_BEFORE_NATIONAL_NUMBER + national_number
430+
else:
431+
return self._prefix_before_national_number + national_number
432+
388433
def _attempt_to_choose_formatting_pattern(self):
389434
"""Attempts to set the formatting template and returns a string which
390435
contains the formatted version of the digits entered so far."""
@@ -397,7 +442,7 @@ def _attempt_to_choose_formatting_pattern(self):
397442
else:
398443
return self._accrued_input
399444
else:
400-
return self._prefix_before_national_number + self._national_number
445+
return self._append_national_number(self._national_number)
401446

402447
def _input_accrued_national_number(self):
403448
"""Invokes input_digit_helper on each digit of the national number
@@ -408,18 +453,30 @@ def _input_accrued_national_number(self):
408453
for ii in xrange(length_of_national_number):
409454
temp_national_number = self._input_digit_helper(self._national_number[ii])
410455
if self._able_to_format:
411-
return self._prefix_before_national_number + temp_national_number
456+
return self._append_national_number(temp_national_number)
412457
else:
413458
return self._accrued_input
414459
else:
415460
return self._prefix_before_national_number
416461

462+
def _is_nanpa_number_with_national_prefix(self):
463+
"""Returns true if the current country is a NANPA country and the
464+
national number begins with the national prefix.
465+
"""
466+
# For NANPA numbers beginning with 1[2-9], treat the 1 as the national
467+
# prefix. The reason is that national significant numbers in NANPA
468+
# always start with [2-9] after the national prefix. Numbers
469+
# beginning with 1[01] can only be short/emergency numbers, which
470+
# don't need the national prefix.
471+
return (self._current_metadata.country_code == 1 and self._national_number[0] == '1' and
472+
self._national_number[1] != '0' and self._national_number[1] != '1')
473+
417474
def _remove_national_prefix_from_national_number(self):
418475
start_of_national_number = 0
419-
if self._current_metadata.country_code == 1 and self._national_number[0] == '1':
476+
if self._is_nanpa_number_with_national_prefix():
420477
start_of_national_number = 1
421-
self._prefix_before_national_number += "1 "
422-
self._is_international_formatting = True
478+
self._prefix_before_national_number += "1" + _SEPARATOR_BEFORE_NATIONAL_NUMBER
479+
self._is_complete_number = True
423480
elif self._current_metadata.national_prefix_for_parsing is not None:
424481
npp_re = re.compile(self._current_metadata.national_prefix_for_parsing)
425482
m = npp_re.match(self._national_number)
@@ -428,7 +485,7 @@ def _remove_national_prefix_from_national_number(self):
428485
# formatting rules instead of national ones, because national
429486
# formatting rules could contain local formatting rules for
430487
# numbers entered without area code.
431-
self._is_international_formatting = True
488+
self._is_complete_number = True
432489
start_of_national_number = m.end()
433490
self._prefix_before_national_number += self._national_number[:start_of_national_number]
434491
national_prefix = self._national_number[:start_of_national_number]
@@ -446,12 +503,12 @@ def _attempt_to_extract_idd(self):
446503
international_prefix = re.compile("\\" + _PLUS_SIGN + "|" + self._current_metadata.international_prefix)
447504
idd_match = international_prefix.match(self._accrued_input_without_formatting)
448505
if idd_match:
449-
self._is_international_formatting = True
506+
self._is_complete_number = True
450507
start_of_country_calling_code = idd_match.end()
451508
self._national_number = self._accrued_input_without_formatting[start_of_country_calling_code:]
452509
self._prefix_before_national_number = self._accrued_input_without_formatting[:start_of_country_calling_code]
453510
if self._accrued_input_without_formatting[0] != _PLUS_SIGN:
454-
self._prefix_before_national_number += " "
511+
self._prefix_before_national_number += _SEPARATOR_BEFORE_NATIONAL_NUMBER
455512
return True
456513
return False
457514

@@ -478,7 +535,7 @@ def _attempt_to_extract_ccc(self):
478535
self._current_metadata = _get_metadata_for_region(new_region_code)
479536

480537
self._prefix_before_national_number += str(country_code)
481-
self._prefix_before_national_number += " "
538+
self._prefix_before_national_number += _SEPARATOR_BEFORE_NATIONAL_NUMBER
482539
return True
483540

484541
def _normalize_and_accrue_digits_and_plus_sign(self, next_char, remember_position):

python/phonenumbers/geocoder.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@
4343
# See the License for the specific language governing permissions and
4444
# limitations under the License.
4545

46-
from .phonenumberutil import format_number, PhoneNumberFormat, is_valid_number
47-
from .phonenumberutil import region_code_for_number
46+
from .phonenumberutil import format_number, PhoneNumberFormat, number_type
47+
from .phonenumberutil import region_code_for_number, PhoneNumberType
4848
try:
4949
from .geodata import GEOCODE_DATA, GEOCODE_LONGEST_PREFIX
5050
from .geodata.locale import LOCALE_DATA
@@ -177,7 +177,8 @@ def description_for_valid_number(numobj, lang, script=None, region=None):
177177
or even just "United States".
178178
179179
This function assumes the validity of the number passed in has already
180-
been checked.
180+
been checked, and that the number is suitable for geocoding. We consider
181+
fixed-line and mobile numbers possible candidates for geocoding.
181182
182183
Arguments:
183184
numobj -- A valid PhoneNumber object for which we want to get a text
@@ -227,10 +228,19 @@ def description_for_number(numobj, lang, script=None, region=None):
227228
228229
Returns a text description in the given language code, for the given phone
229230
number, or an empty string if no description is available."""
230-
if not is_valid_number(numobj):
231+
ntype = number_type(numobj)
232+
if ntype == PhoneNumberType.UNKNOWN:
231233
return ""
234+
elif not _can_be_geocoded(ntype):
235+
return country_name_for_number(numobj, lang, script, region)
232236
return description_for_valid_number(numobj, lang, script, region)
233237

238+
239+
def _can_be_geocoded(ntype):
240+
return (ntype == PhoneNumberType.FIXED_LINE or
241+
ntype == PhoneNumberType.MOBILE or
242+
ntype == PhoneNumberType.FIXED_LINE_OR_MOBILE)
243+
234244
if __name__ == '__main__': # pragma no cover
235245
import doctest
236246
doctest.testmod()

python/phonenumbers/phonenumberutil.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,11 @@ def _create_extn_pattern(single_extn_symbols):
324324
_FG_PATTERN = re.compile("\\$FG")
325325
_CC_PATTERN = re.compile("\\$CC")
326326

327+
# A pattern that is used to determine if the national prefix formatting rule
328+
# has the first group only, i.e., does not start with the national
329+
# prefix. Note that the pattern explicitly allows for unbalanced parentheses.
330+
_FIRST_GROUP_ONLY_PREFIX_PATTERN = re.compile("\\(?\\\\1\\)?")
331+
327332

328333
class PhoneNumberFormat(object):
329334
"""
@@ -680,6 +685,16 @@ def _normalize_helper(number, replacements, remove_non_matches):
680685
return u''.join(normalized_number)
681686

682687

688+
def _formatting_rule_has_first_group_only(national_prefix_formatting_rule):
689+
"""Helper function to check if the national prefix formatting rule has the
690+
first group only, i.e., does not start with the national prefix.
691+
"""
692+
if national_prefix_formatting_rule is None:
693+
return False
694+
return bool(fullmatch(_FIRST_GROUP_ONLY_PREFIX_PATTERN,
695+
national_prefix_formatting_rule))
696+
697+
683698
def _is_valid_region_code(region_code):
684699
"""Helper function to check region code is not unknown or None"""
685700
if region_code is None:

0 commit comments

Comments
 (0)