Skip to content

Commit 4c61c33

Browse files
committed
Register short metadata with PhoneMetadata
1 parent 16f97a5 commit 4c61c33

4 files changed

Lines changed: 70 additions & 28 deletions

File tree

python/phonenumbers/phonemetadata.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,13 @@ class PhoneMetadata(UnicodeMixin, ImmutableMixin):
214214
# - a function which loads the region's metadata
215215
# - None, to indicate that the metadata is already loaded
216216
_region_available = {} # ISO 3166-1 alpha 2 => function or None
217-
# Likewise for non-geo country calling codes
217+
# Likewise for short number metadata.
218+
_short_region_available = {} # ISO 3166-1 alpha 2 => function or None
219+
# Likewise for non-geo country calling codes.
218220
_country_code_available = {} # country calling code (as int) => function or None
219221

220222
_region_metadata = {} # ISO 3166-1 alpha 2 => PhoneMetadata
223+
_short_region_metadata = {} # ISO 3166-1 alpha 2 => PhoneMetadata
221224
# A mapping from a country calling code for a non-geographical entity to
222225
# the PhoneMetadata for that country calling code. Examples of the country
223226
# calling codes include 800 (International Toll Free Service) and 808
@@ -233,6 +236,15 @@ def metadata_for_region(kls, region_code, default=None):
233236
kls._region_available[region_code] = None
234237
return kls._region_metadata.get(region_code, default)
235238

239+
@classmethod
240+
def short_metadata_for_region(kls, region_code, default=None):
241+
loader = kls._short_region_available.get(region_code, None)
242+
if loader is not None:
243+
# Region short number metadata is available but has not yet been loaded. Do so now.
244+
loader(region_code)
245+
kls._short_region_available[region_code] = None
246+
return kls._short_region_metadata.get(region_code, default)
247+
236248
@classmethod
237249
def metadata_for_nongeo_region(kls, country_code, default=None):
238250
loader = kls._country_code_available.get(country_code, None)
@@ -253,6 +265,10 @@ def metadata_for_region_or_calling_code(kls, country_calling_code, region_code):
253265
def register_region_loader(kls, region_code, loader):
254266
kls._region_available[region_code] = loader
255267

268+
@classmethod
269+
def register_short_region_loader(kls, region_code, loader):
270+
kls._short_region_available[region_code] = loader
271+
256272
@classmethod
257273
def register_nongeo_region_loader(kls, country_code, loader):
258274
kls._country_code_available[country_code] = loader
@@ -300,6 +316,7 @@ def __init__(self,
300316
main_country_for_code=False,
301317
leading_digits=None,
302318
leading_zero_possible=False,
319+
short_data=False,
303320
register=True):
304321
# The general_desc contains information which is a superset of
305322
# descriptions for all types of phone numbers. If any element is
@@ -463,11 +480,17 @@ def __init__(self,
463480
# that calling code will use the same setting.
464481
self.leading_zero_possible = leading_zero_possible # bool
465482

483+
# Record whether this metadata is for short numbers or normal numbers.
484+
self.short_data = short_data # bool
485+
466486
if register:
467487
# Register this instance with the relevant class-wide map
468488
if self.id == REGION_CODE_FOR_NON_GEO_ENTITY:
469489
kls_map = PhoneMetadata._country_code_metadata
470490
id = self.country_code
491+
elif self.short_data:
492+
kls_map = PhoneMetadata._short_region_metadata
493+
id = self.id
471494
else:
472495
kls_map = PhoneMetadata._region_metadata
473496
id = self.id
@@ -530,5 +553,7 @@ def __unicode__(self):
530553
result += ",\n leading_digits='%s'" % self.leading_digits
531554
if self.leading_zero_possible:
532555
result += ",\n leading_zero_possible=True"
556+
if self.short_data:
557+
result += ",\n short_data=True"
533558
result += u")"
534559
return result

python/phonenumbers/phonenumberutil.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
# Import auto-generated data structures
4242
try:
4343
from .data import _COUNTRY_CODE_TO_REGION_CODE
44+
from .shortdata import _AVAILABLE_REGION_CODES as SUPPORTED_SHORT_REGIONS
4445
except ImportError: # pragma no cover
4546
# Before the generated code exists, the data/ directory is empty.
4647
# The generation process imports this module, creating a circular
@@ -51,6 +52,7 @@
5152
os.path.basename(sys.argv[0]) == "buildgeocodingdata.py"):
5253
print >> sys.stderr, "Failed to import generated data (but OK as during autogeneration)"
5354
_COUNTRY_CODE_TO_REGION_CODE = {1: ("US",)}
55+
SUPPORTED_SHORT_REGIONS = []
5456
else:
5557
raise
5658

@@ -541,6 +543,7 @@ def _normalize_diallable_chars_only(number):
541543
"""
542544
return _normalize_helper(number, _DIALLABLE_CHAR_MAPPINGS, True)
543545

546+
544547
def convert_alpha_characters_in_number(number):
545548
"""Convert alpha chars in a number to their respective digits on a keypad,
546549
but retains existing formatting."""

tools/python/buildmetadatafromxml.py

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -85,14 +85,15 @@ def u(s):
8585
METADATA_FILE_IMPORT = "from %(module)s.phonemetadata import PhoneMetadata\n"
8686
METADATA_FILE_LOOP = '''
8787
def _load_region(code):
88-
__import__("region_%s" % code, globals(), locals(),
89-
fromlist=["PHONE_METADATA_%s" % code], level=1)
88+
__import__("region_%%s" %% code, globals(), locals(),
89+
fromlist=["PHONE_METADATA_%%s" %% code], level=1)
9090
91+
for region_code in _AVAILABLE_REGION_CODES:
92+
PhoneMetadata.register_%(prefix)sregion_loader(region_code, _load_region)
93+
'''
94+
METADATA_NONGEO_FILE_LOOP = '''
9195
for country_code in _AVAILABLE_NONGEO_COUNTRY_CODES:
9296
PhoneMetadata.register_nongeo_region_loader(country_code, _load_region)
93-
94-
for region_code in _AVAILABLE_REGION_CODES:
95-
PhoneMetadata.register_region_loader(region_code, _load_region)
9697
'''
9798

9899
_COUNTRY_CODE_TO_REGION_CODE_PROLOG = '''
@@ -295,8 +296,7 @@ def __unicode__(self):
295296

296297
class XPhoneNumberDesc(UnicodeMixin):
297298
"""Parse PhoneNumberDesc object from XML element"""
298-
def __init__(self, xtag,
299-
template=None, fill_na=True):
299+
def __init__(self, xtag, template=None, fill_na=True):
300300
self.o = PhoneNumberDesc()
301301
self.o._mutable = True
302302
self.o.national_number_pattern = None
@@ -350,10 +350,10 @@ def __unicode__(self):
350350

351351
class XTerritory(UnicodeMixin):
352352
"""Parse PhoneMetadata from XML element (territory)"""
353-
def __init__(self, xterritory):
353+
def __init__(self, xterritory, short_data):
354354
# Retrieve the REQUIRED attributes
355355
id = xterritory.attrib['id']
356-
self.o = PhoneMetadata(id, register=False)
356+
self.o = PhoneMetadata(id, short_data=short_data, register=False)
357357
self.o._mutable = True
358358
if 'countryCode' in xterritory.attrib:
359359
self.o.country_code = int(xterritory.attrib['countryCode'])
@@ -461,7 +461,7 @@ def __unicode__(self):
461461

462462
class XPhoneNumberMetadata(UnicodeMixin):
463463
"""Entire collection of phone number metadata retrieved from XML"""
464-
def __init__(self, filename):
464+
def __init__(self, filename, short_data):
465465
# Load the XML data from the given filename
466466
with open(filename, "r") as infile:
467467
xtree = etree.parse(infile)
@@ -472,14 +472,15 @@ def __init__(self, filename):
472472
self.territory = {}
473473
for xterritory in xterritories:
474474
if xterritory.tag == TERRITORY_TAG:
475-
terrobj = XTerritory(xterritory)
475+
terrobj = XTerritory(xterritory, short_data)
476476
id = terrobj.identifier() # like "US" for countries, "800" for non-geo
477477
if id in self.territory:
478478
raise Exception("Duplicate entry for %s" % id)
479479
self.territory[id] = terrobj
480480
else:
481481
raise Exception("Unexpected element %s found" % xterritory.tag)
482482
self.alt_territory = None
483+
self.short_data = short_data
483484

484485
def add_alternate_formats(self, filename):
485486
"""Add phone number alternate format metadata retrieved from XML"""
@@ -546,20 +547,22 @@ def emit_metadata_py(self, datadir, module_prefix):
546547
nongeo_codes.append(country_id) # int
547548
else:
548549
country_codes.append("'%s'" % country_id) # quoted string
549-
prnt("_AVAILABLE_NONGEO_COUNTRY_CODES = [%s]" % ", ".join(nongeo_codes), file=outfile)
550550
prnt("_AVAILABLE_REGION_CODES = [%s]" % ",".join(country_codes), file=outfile)
551-
prnt(METADATA_FILE_LOOP, file=outfile)
551+
if len(nongeo_codes) > 0:
552+
prnt("_AVAILABLE_NONGEO_COUNTRY_CODES = [%s]" % ", ".join(nongeo_codes), file=outfile)
553+
register_prefix = "short_" if self.short_data else ""
554+
prnt(METADATA_FILE_LOOP % {'prefix': register_prefix}, file=outfile)
555+
if len(nongeo_codes) > 0:
556+
prnt(METADATA_NONGEO_FILE_LOOP, file=outfile)
552557

553558
if self.alt_territory is not None:
554559
for country_code in sorted(self.alt_territory.keys()):
555560
prnt("from .alt_format_%s import PHONE_ALT_FORMAT_%s" % (country_code, country_code), file=outfile)
556561
prnt("_ALT_NUMBER_FORMATS = {%s}" %
557562
", ".join(["%s: PHONE_ALT_FORMAT_%s" % (cc, cc) for cc in sorted(self.alt_territory.keys())]),
558563
file=outfile)
559-
# Emit the mapping from country code to region code
560-
prnt(_COUNTRY_CODE_TO_REGION_CODE_PROLOG, file=outfile)
561-
prnt("_COUNTRY_CODE_TO_REGION_CODE = {", file=outfile)
562-
# Build up the map
564+
565+
# Build up a map from country code (int) to list of region codes (ISO 3166-1 alpha 2)
563566
country_code_to_region_code = {}
564567
for country_id in sorted(self.territory.keys()):
565568
terrobj = self.territory[country_id]
@@ -572,24 +575,31 @@ def emit_metadata_py(self, datadir, module_prefix):
572575
else:
573576
country_code_to_region_code[country_code].append(terrobj.o.id)
574577

575-
for country_code in sorted(country_code_to_region_code.keys()):
576-
country_ids = country_code_to_region_code[country_code]
577-
prnt(' %d: ("%s",),' % (country_code, '", "'.join(country_ids)), file=outfile)
578-
prnt("}", file=outfile)
578+
# Emit the mapping from country code to region code if nonempty.
579+
if len(country_code_to_region_code.keys()) > 0:
580+
prnt(_COUNTRY_CODE_TO_REGION_CODE_PROLOG, file=outfile)
581+
prnt("_COUNTRY_CODE_TO_REGION_CODE = {", file=outfile)
582+
for country_code in sorted(country_code_to_region_code.keys()):
583+
country_ids = country_code_to_region_code[country_code]
584+
prnt(' %d: ("%s",),' % (country_code, '", "'.join(country_ids)), file=outfile)
585+
prnt("}", file=outfile)
579586

580587

581588
def _standalone(argv):
582589
"""Parse the given XML file and emit generated code."""
583590
alternate = None
591+
short_data = False
584592
try:
585-
opts, args = getopt.getopt(argv, "ha:", ("help", "alt="))
593+
opts, args = getopt.getopt(argv, "hsa:", ("help", "short", "alt="))
586594
except getopt.GetoptError:
587595
prnt(__doc__, file=sys.stderr)
588596
sys.exit(1)
589597
for opt, arg in opts:
590598
if opt in ("-h", "--help"):
591599
prnt(__doc__, file=sys.stderr)
592600
sys.exit(1)
601+
elif opt in ("-s", "--short"):
602+
short_data = True
593603
elif opt in ("-a", "--alt"):
594604
alternate = arg
595605
else:
@@ -600,7 +610,7 @@ def _standalone(argv):
600610
if len(args) != 3:
601611
prnt(__doc__, file=sys.stderr)
602612
sys.exit(1)
603-
pmd = XPhoneNumberMetadata(args[0])
613+
pmd = XPhoneNumberMetadata(args[0], short_data)
604614
if alternate is not None:
605615
pmd.add_alternate_formats(alternate)
606616
pmd.emit_metadata_py(args[1], args[2])

tools/python/makefile

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ geodata: $(PYDIR)/phonenumbers/geodata/__init__.py $(PYDIR)/tests/testgeodata/__
2626
# Generate Python files from metadata
2727
$(PYDIR)/phonenumbers/data/__init__.py: $(TOPDIR)/resources/PhoneNumberMetadata.xml $(TOPDIR)/resources/PhoneNumberAlternateFormats.xml buildmetadatafromxml.py
2828
$(PYTHON) buildmetadatafromxml.py --alt $(TOPDIR)/resources/PhoneNumberAlternateFormats.xml $(TOPDIR)/resources/PhoneNumberMetadata.xml $(PYDIR)/phonenumbers/data .
29+
$(PYDIR)/phonenumbers/shortdata/__init__.py: $(TOPDIR)/resources/ShortNumberMetadata.xml buildmetadatafromxml.py
30+
$(PYTHON) buildmetadatafromxml.py --short $(TOPDIR)/resources/ShortNumberMetadata.xml $(PYDIR)/phonenumbers/shortdata .
2931
$(PYDIR)/tests/testdata/__init__.py: $(TOPDIR)/resources/PhoneNumberMetadataForTesting.xml buildmetadatafromxml.py
3032
$(PYTHON) buildmetadatafromxml.py $(TOPDIR)/resources/PhoneNumberMetadataForTesting.xml $(PYDIR)/tests/testdata phonenumbers
31-
metadata: $(PYDIR)/phonenumbers/data/__init__.py $(PYDIR)/tests/testdata/__init__.py geodata
33+
metadata: $(PYDIR)/phonenumbers/data/__init__.py $(PYDIR)/phonenumbers/shortdata/__init__.py $(PYDIR)/tests/testdata/__init__.py geodata
3234

3335
alldata: metadata geodata locale
3436

@@ -70,7 +72,7 @@ clean:
7072
rm -rf $(PYDIR)/build $(PYDIR)/deb_dist $(PYDIR)/dist
7173

7274
metaclean:
73-
rm -rf $(PYDIR)/phonenumbers/data $(PYDIR)/tests/testdata
75+
rm -rf $(PYDIR)/phonenumbers/data $(PYDIR)/phonenumbers/shortdata $(PYDIR)/tests/testdata
7476
rm -rf $(PYDIR)/phonenumbers/geodata $(PYDIR)/tests/testgeodata
7577

7678
distclean: clean metaclean distclean3
@@ -131,9 +133,11 @@ geo3data: $(PY3DIR)/phonenumbers/geodata/__init__.py $(PY3DIR)/tests/testgeodata
131133

132134
$(PY3DIR)/phonenumbers/data/__init__.py: $(TOPDIR)/resources/PhoneNumberMetadata.xml $(TOPDIR)/resources/PhoneNumberAlternateFormats.xml buildmetadatafromxml.py
133135
$(PYTHON3) buildmetadatafromxml.py --alt $(TOPDIR)/resources/PhoneNumberAlternateFormats.xml $(TOPDIR)/resources/PhoneNumberMetadata.xml $(PY3DIR)/phonenumbers/data .
136+
$(PY3DIR)/phonenumbers/shortdata/__init__.py: $(TOPDIR)/resources/ShortNumberMetadata.xml buildmetadatafromxml.py
137+
$(PYTHON3) buildmetadatafromxml.py --short $(TOPDIR)/resources/ShortNumberMetadata.xml $(PY3DIR)/phonenumbers/shortdata .
134138
$(PY3DIR)/tests/testdata/__init__.py: $(TOPDIR)/resources/PhoneNumberMetadataForTesting.xml buildmetadatafromxml.py
135139
$(PYTHON3) buildmetadatafromxml.py $(TOPDIR)/resources/PhoneNumberMetadataForTesting.xml $(PY3DIR)/tests/testdata phonenumbers
136-
meta3data: $(PY3DIR)/phonenumbers/data/__init__.py $(PY3DIR)/tests/testdata/__init__.py geo3data
140+
meta3data: $(PY3DIR)/phonenumbers/data/__init__.py $(PY3DIR)/phonenumbers/shortdata/__init__.py $(PY3DIR)/tests/testdata/__init__.py geo3data
137141

138142
all3data: meta3data geo3data locale3
139143

@@ -154,7 +158,7 @@ clean3:
154158
rm -rf $(PY3DIR)/build $(PY3DIR)/deb_dist $(PY3DIR)/dist
155159

156160
metaclean3:
157-
rm -rf $(PY3DIR)/phonenumbers/data $(PY3DIR)/tests/testdata
161+
rm -rf $(PY3DIR)/phonenumbers/data $(PY3DIR)/phonenumbers/shortdata $(PY3DIR)/tests/testdata
158162
rm -rf $(PY3DIR)/phonenumbers/geodata $(PY3DIR)/tests/testgeodata
159163

160164
distclean3: clean3 metaclean3

0 commit comments

Comments
 (0)