Skip to content

Commit a623ded

Browse files
committed
Merge upstream code changes from r618.
Rename carrier.description_for_* to carrier.name_for_*. Add carrier.safe_display_name(). Add is_mobile_number_portable_region().
1 parent e87eee7 commit a623ded

6 files changed

Lines changed: 104 additions & 45 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ originally owned a phone number.
144144
```pycon
145145
>>> from phonenumbers import carrier
146146
>>> ro_number = phonenumbers.parse("+40721234567", "RO")
147-
>>> print repr(carrier.description_for_number(ro_number, "en"))
147+
>>> print repr(carrier.name_for_number(ro_number, "en"))
148148
u'Vodafone'
149149
```
150150

@@ -162,8 +162,8 @@ In particular:
162162
* The geocoding metadata (which makes up around 75% of the total memory footprint) is only loaded on the first use of
163163
one of the geocoding functions (`geocoder.description_for_number`, `geocoder.description_for_valid_number`
164164
or `geocoder.country_name_for_number`).
165-
* The carrier metadata is only loaded on the first use of one of the mapping functions (`carrier.description_for_number`
166-
or `carrier.description_for_valid_number`).
165+
* The carrier metadata is only loaded on the first use of one of the mapping functions (`carrier.name_for_number`
166+
or `carrier.name_for_valid_number`).
167167
* The normal metadata for each region is only loaded on the first time that metadata for that region is needed.
168168

169169
If you need to ensure that the metadata memory use is accounted for at start of day (i.e. that a subsequent on-demand

python/phonenumbers/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@
113113
region_code_for_country_code,
114114
region_codes_for_country_code,
115115
region_code_for_number,
116-
truncate_too_long_number,)
116+
truncate_too_long_number,
117+
is_mobile_number_portable_region,)
117118
from .shortnumberinfo import (ShortNumberCost,
118119
is_possible_short_number,
119120
is_possible_short_number_object,
@@ -173,6 +174,7 @@
173174
'region_codes_for_country_code',
174175
'region_code_for_number',
175176
'truncate_too_long_number',
177+
'is_mobile_number_portable_region',
176178
# end of items from phonenumberutil.py
177179
# items from shortnumberinfo.py
178180
'ShortNumberCost',

python/phonenumbers/carrier.py

Lines changed: 53 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
"""Phone number to carrier mapping functionality
22
33
>>> import phonenumbers
4-
>>> from phonenumbers.carrier import description_for_number
4+
>>> from phonenumbers.carrier import name_for_number
55
>>> ro_number = phonenumbers.parse("+40721234567", "RO")
6-
>>> str(description_for_number(ro_number, "en"))
6+
>>> str(name_for_number(ro_number, "en"))
77
'Vodafone'
8-
>>> str(description_for_number(ro_number, "fr")) # fall back to English
8+
>>> str(name_for_number(ro_number, "fr")) # fall back to English
99
'Vodafone'
1010
1111
"""
@@ -26,6 +26,8 @@
2626
# limitations under the License.
2727

2828
from .phonenumberutil import PhoneNumberType, number_type
29+
from .phonenumberutil import region_code_for_number
30+
from .phonenumberutil import is_mobile_number_portable_region
2931
from .prefix import prefix_description_for_number
3032
try:
3133
from .carrierdata import CARRIER_DATA, CARRIER_LONGEST_PREFIX
@@ -44,60 +46,89 @@
4446
raise
4547

4648

47-
def description_for_valid_number(numobj, lang, script=None, region=None):
48-
"""Return a text description of a PhoneNumber object for the given language.
49+
def name_for_valid_number(numobj, lang, script=None, region=None):
50+
"""Returns a carrier name for the given PhoneNumber object, in the
51+
language provided.
4952
50-
The description consists of the name of the carrier the number was
51-
originally allocated to, however if the country supports mobile number
52-
portability the number might not belong to the returned carrier
53-
anymore. If no mapping is found an empty string is returned.
53+
The carrier name is the one the number was originally allocated to,
54+
however if the country supports mobile number portability the number might
55+
not belong to the returned carrier anymore. If no mapping is found an
56+
empty string is returned.
5457
5558
This method assumes the validity of the number passed in has already been
5659
checked, and that the number is suitable for carrier lookup. We consider
5760
mobile and pager numbers possible candidates for carrier lookup.
5861
5962
Arguments:
60-
numobj -- The PhoneNumber object for which we want to get a text description.
63+
numobj -- The PhoneNumber object for which we want to get a carrier name.
6164
lang -- A 2-letter lowercase ISO 639-1 language code for the language in
6265
which the description should be returned (e.g. "en")
6366
script -- A 4-letter titlecase (first letter uppercase, rest lowercase)
6467
ISO script code as defined in ISO 15924, separated by an
6568
underscore (e.g. "Hant")
6669
region -- A 2-letter uppercase ISO 3166-1 country code (e.g. "GB")
6770
68-
Returns a text description in the given language code, for the given phone
69-
number, or an empty string if no description is available."""
71+
Returns a carrier name in the given language code, for the given phone
72+
number, or an empty string if no description is available.
73+
"""
7074
return prefix_description_for_number(CARRIER_DATA, CARRIER_LONGEST_PREFIX,
7175
numobj, lang, script, region)
7276

7377

74-
def description_for_number(numobj, lang, script=None, region=None):
75-
"""Return a text description of a PhoneNumber object for the given language.
78+
def name_for_number(numobj, lang, script=None, region=None):
79+
"""Returns a carrier name for the given PhoneNumber object, in the
80+
language provided.
7681
77-
The description consists of the name of the carrier the number was
78-
originally allocated to, however if the country supports mobile number
79-
portability the number might not belong to the returned carrier
80-
anymore. If no mapping is found an empty string is returned.
82+
The carrier name is the one the number was originally allocated to,
83+
however if the country supports mobile number portability the number might
84+
not belong to the returned carrier anymore. If no mapping is found an
85+
empty string is returned.
8186
8287
This function explicitly checks the validity of the number passed in
8388
8489
Arguments:
85-
numobj -- The PhoneNumber object for which we want to get a text description.
90+
numobj -- The PhoneNumber object for which we want to get a carrier name.
8691
lang -- A 2-letter lowercase ISO 639-1 language code for the language in
8792
which the description should be returned (e.g. "en")
8893
script -- A 4-letter titlecase (first letter uppercase, rest lowercase)
8994
ISO script code as defined in ISO 15924, separated by an
9095
underscore (e.g. "Hant")
9196
region -- A 2-letter uppercase ISO 3166-1 country code (e.g. "GB")
9297
93-
Returns a text description in the given language code, for the given phone
94-
number, or an empty string if no description is available."""
98+
Returns a carrier name in the given language code, for the given phone
99+
number, or an empty string if no description is available.
100+
"""
95101
ntype = number_type(numobj)
96102
if _is_mobile(ntype):
97-
return description_for_valid_number(numobj, lang, script, region)
103+
return name_for_valid_number(numobj, lang, script, region)
98104
return ""
99105

100106

107+
def safe_display_name(numobj, lang, script=None, region=None):
108+
"""Gets the name of the carrier for the given PhoneNumber object only when
109+
it is 'safe' to display to users. A carrier name is onsidered safe if the
110+
number is valid and for a region that doesn't support mobile number
111+
portability (http://en.wikipedia.org/wiki/Mobile_number_portability).
112+
113+
114+
This function explicitly checks the validity of the number passed in
115+
116+
Arguments:
117+
numobj -- The PhoneNumber object for which we want to get a carrier name.
118+
lang -- A 2-letter lowercase ISO 639-1 language code for the language in
119+
which the description should be returned (e.g. "en")
120+
script -- A 4-letter titlecase (first letter uppercase, rest lowercase)
121+
ISO script code as defined in ISO 15924, separated by an
122+
underscore (e.g. "Hant")
123+
region -- A 2-letter uppercase ISO 3166-1 country code (e.g. "GB")
124+
125+
Returns a carrier name that is safe to display to users, or the empty string.
126+
"""
127+
if is_mobile_number_portable_region(region_code_for_number(numobj)):
128+
return ""
129+
return name_for_number(numobj, lang, script, region)
130+
131+
101132
def _is_mobile(ntype):
102133
"""Checks if the supplied number type supports carrier lookup"""
103134
return (ntype == PhoneNumberType.MOBILE or

python/phonenumbers/phonenumberutil.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2702,6 +2702,21 @@ def _can_be_internationally_dialled(numobj):
27022702
return not _is_number_matching_desc(nsn, metadata.no_international_dialling)
27032703

27042704

2705+
def is_mobile_number_portable_region(region_code):
2706+
"""Returns true if the supplied region supports mobile number portability.
2707+
Returns false for invalid, unknown or regions that don't support mobile
2708+
number portability.
2709+
2710+
Arguments:
2711+
region_code -- the region for which we want to know whether it supports mobile number
2712+
portability or not.
2713+
"""
2714+
metadata = PhoneMetadata.metadata_for_region(region_code, None)
2715+
if metadata is None:
2716+
return False
2717+
return metadata.mobile_number_portable_region
2718+
2719+
27052720
class NumberParseException(UnicodeMixin, Exception):
27062721
"""Exception when attempting to parse a putative phone number"""
27072722
# Invalid country code specified

python/tests/carriertest.py

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
from phonenumbers import PhoneNumber, FrozenPhoneNumber
2323
from phonenumbers import carrier
24-
from phonenumbers.carrier import description_for_number, description_for_valid_number
24+
from phonenumbers.carrier import name_for_number, name_for_valid_number, safe_display_name
2525

2626
# Allow override library carrier metadata with the test metadata.
2727
REAL_CARRIER_DATA = carrier.CARRIER_DATA
@@ -73,37 +73,40 @@ def tearDown(self):
7373
reinstate_real_carrierdata()
7474

7575
def testGetDescriptionForMobilePortableRegion(self):
76-
self.assertEqual("British carrier", description_for_number(UK_MOBILE1, _ENGLISH))
77-
self.assertEqual(u"Brittisk operat\u00F6r", description_for_number(UK_MOBILE1, "sv", region="SE"))
78-
self.assertEqual("British carrier", description_for_number(UK_MOBILE1, _FRENCH))
76+
self.assertEqual("British carrier", name_for_number(UK_MOBILE1, _ENGLISH))
77+
self.assertEqual(u"Brittisk operat\u00F6r", name_for_number(UK_MOBILE1, "sv", region="SE"))
78+
self.assertEqual("British carrier", name_for_number(UK_MOBILE1, _FRENCH))
79+
# Returns an empty string because the UK implements mobile number portability.
80+
self.assertEqual("", safe_display_name(UK_MOBILE1, _ENGLISH))
7981

8082
def testGetDescriptionForNonMobilePortableRegion(self):
81-
self.assertEqual("Angolan carrier", description_for_number(AO_MOBILE1, _ENGLISH))
83+
self.assertEqual("Angolan carrier", name_for_number(AO_MOBILE1, _ENGLISH))
84+
self.assertEqual("Angolan carrier", safe_display_name(AO_MOBILE1, _ENGLISH))
8285

8386
def testGetDescriptionForFixedLineNumber(self):
84-
self.assertEqual("", description_for_number(AO_FIXED1, _ENGLISH))
85-
self.assertEqual("", description_for_number(UK_FIXED1, _ENGLISH))
87+
self.assertEqual("", name_for_number(AO_FIXED1, _ENGLISH))
88+
self.assertEqual("", name_for_number(UK_FIXED1, _ENGLISH))
8689
# If the carrier information is present in the files and the method
8790
# that assumes a valid number is used, a carrier is returned.
88-
self.assertEqual("Angolan fixed line carrier", description_for_valid_number(AO_FIXED2, _ENGLISH))
89-
self.assertEqual("", description_for_valid_number(UK_FIXED2, _ENGLISH))
91+
self.assertEqual("Angolan fixed line carrier", name_for_valid_number(AO_FIXED2, _ENGLISH))
92+
self.assertEqual("", name_for_valid_number(UK_FIXED2, _ENGLISH))
9093

9194
def testGetDescriptionForFixedOrMobileNumber(self):
92-
self.assertEqual("US carrier", description_for_number(US_FIXED_OR_MOBILE, _ENGLISH))
95+
self.assertEqual("US carrier", name_for_number(US_FIXED_OR_MOBILE, _ENGLISH))
9396

9497
def testGetDescriptionForPagerNumber(self):
95-
self.assertEqual("British pager", description_for_number(UK_PAGER, _ENGLISH))
98+
self.assertEqual("British pager", name_for_number(UK_PAGER, _ENGLISH))
9699

97100
def testGetDescriptionForNumberWithNoDataFile(self):
98-
self.assertEqual("", description_for_number(NUMBER_WITH_INVALID_COUNTRY_CODE, _ENGLISH))
99-
self.assertEqual("", description_for_number(INTERNATIONAL_TOLL_FREE, _ENGLISH))
100-
self.assertEqual("", description_for_valid_number(NUMBER_WITH_INVALID_COUNTRY_CODE, _ENGLISH))
101-
self.assertEqual("", description_for_valid_number(INTERNATIONAL_TOLL_FREE, _ENGLISH))
101+
self.assertEqual("", name_for_number(NUMBER_WITH_INVALID_COUNTRY_CODE, _ENGLISH))
102+
self.assertEqual("", name_for_number(INTERNATIONAL_TOLL_FREE, _ENGLISH))
103+
self.assertEqual("", name_for_valid_number(NUMBER_WITH_INVALID_COUNTRY_CODE, _ENGLISH))
104+
self.assertEqual("", name_for_valid_number(INTERNATIONAL_TOLL_FREE, _ENGLISH))
102105

103106
def testGetDescriptionForNumberWithMissingPrefix(self):
104-
self.assertEqual("", description_for_number(UK_MOBILE2, _ENGLISH))
105-
self.assertEqual("", description_for_number(AO_MOBILE2, _ENGLISH))
107+
self.assertEqual("", name_for_number(UK_MOBILE2, _ENGLISH))
108+
self.assertEqual("", name_for_number(AO_MOBILE2, _ENGLISH))
106109

107110
def testGetDescriptionForInvalidNumber(self):
108-
self.assertEqual("", description_for_number(UK_INVALID_NUMBER, _ENGLISH))
109-
self.assertEqual("", description_for_number(AO_INVALID_NUMBER, _ENGLISH))
111+
self.assertEqual("", name_for_number(UK_INVALID_NUMBER, _ENGLISH))
112+
self.assertEqual("", name_for_number(AO_INVALID_NUMBER, _ENGLISH))

python/tests/phonenumberutiltest.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2357,6 +2357,14 @@ def testIsAlphaNumber(self):
23572357
# Python version extra test
23582358
self.assertFalse(phonenumbers.is_alpha_number(""))
23592359

2360+
def testIsMobileNumberPortableRegion(self):
2361+
self.assertTrue(phonenumbers.is_mobile_number_portable_region("US"))
2362+
self.assertTrue(phonenumbers.is_mobile_number_portable_region("GB"))
2363+
self.assertFalse(phonenumbers.is_mobile_number_portable_region("AE"))
2364+
self.assertFalse(phonenumbers.is_mobile_number_portable_region("BS"))
2365+
# Python version extra test: check with bogus region
2366+
self.assertFalse(phonenumbers.is_mobile_number_portable_region("XY"))
2367+
23602368
def testMetadataEquality(self):
23612369
# Python version extra tests for equality against other types
23622370
desc1 = PhoneNumberDesc(national_number_pattern="\\d{4,8}")

0 commit comments

Comments
 (0)