0
# Utilities
1
2
Helper functions for string processing, normalization, example number generation, and metadata access. These utilities provide advanced functionality for specialized use cases and integration with other systems.
3
4
## Capabilities
5
6
### String Processing and Normalization
7
8
Functions to clean, normalize, and process phone number strings before parsing or after formatting.
9
10
```python { .api }
11
def normalize_digits_only(number: str, keep_non_digits: bool = False) -> str:
12
"""
13
Extract and normalize only digits from string.
14
15
Parameters:
16
- number: Input string to normalize
17
- keep_non_digits: Whether to preserve non-digit characters
18
19
Returns:
20
String containing only digits (0-9) or digits plus non-digits if keep_non_digits=True
21
"""
22
23
def normalize_diallable_chars_only(number: str) -> str:
24
"""
25
Normalize string to contain only diallable characters.
26
27
Parameters:
28
- number: Input string to normalize
29
30
Returns:
31
String containing only characters that can be dialed (digits, +, *, #, etc.)
32
"""
33
34
def convert_alpha_characters_in_number(number: str) -> str:
35
"""
36
Convert alphabetic characters to digits (e.g., ABC → 222).
37
38
Parameters:
39
- number: Phone number string potentially containing letters
40
41
Returns:
42
String with letters converted to corresponding digits
43
"""
44
45
def is_alpha_number(number: str) -> bool:
46
"""
47
Check if string could be an alphanumeric phone number.
48
49
Parameters:
50
- number: String to check for alphanumeric phone number pattern
51
52
Returns:
53
True if the string appears to be an alphanumeric phone number
54
"""
55
```
56
57
**Usage Examples:**
58
59
```python
60
# Normalize digits from messy input
61
messy_input = "Call (555) 123-HELP or +1.555.987.6543 ext.123"
62
digits_only = phonenumbers.normalize_digits_only(messy_input)
63
print(digits_only) # "555123555987654312"
64
65
# Keep some non-digits for context
66
partial_normalize = phonenumbers.normalize_digits_only("555-123-HELP", keep_non_digits=True)
67
print(partial_normalize) # "555-123-"
68
69
# Normalize to diallable characters
70
diallable = phonenumbers.normalize_diallable_chars_only("Call +1 (555) 123-4567!")
71
print(diallable) # "+15551234567"
72
73
# Convert letters to numbers (phone keypad mapping)
74
alpha_number = "1-800-FLOWERS"
75
numeric = phonenumbers.convert_alpha_characters_in_number(alpha_number)
76
print(numeric) # "1-800-3569377"
77
78
# Letter conversion examples
79
conversions = [
80
"CALL-HOME", # 2255-4663
81
"555-HELP", # 555-4357
82
"1-800-GOT-JUNK", # 1-800-468-5865
83
"TEXT-TAXI" # 8398-8294
84
]
85
86
for alpha in conversions:
87
numeric = phonenumbers.convert_alpha_characters_in_number(alpha)
88
print(f"{alpha} -> {numeric}")
89
90
# Check if string is alphanumeric phone number
91
test_strings = [
92
"1-800-FLOWERS", # True
93
"555-HELP", # True
94
"CALL-ME", # True
95
"Hello World", # False
96
"123-456-7890", # False (no letters)
97
"" # False
98
]
99
100
for test_str in test_strings:
101
is_alpha = phonenumbers.is_alpha_number(test_str)
102
print(f"'{test_str}' is alphanumeric: {is_alpha}")
103
```
104
105
### Example Number Generation
106
107
Functions to generate example phone numbers for testing, documentation, and user interface mockups.
108
109
```python { .api }
110
def example_number(region_code: str) -> PhoneNumber | None:
111
"""
112
Get example phone number for region.
113
114
Parameters:
115
- region_code: Region identifier (e.g., "US", "GB")
116
117
Returns:
118
Example PhoneNumber for the region, None if no example available
119
"""
120
121
def example_number_for_type(region_code: str | None, num_type: int) -> PhoneNumber | None:
122
"""
123
Get example number of specified type for region.
124
125
Parameters:
126
- region_code: Region identifier, None for non-geographical numbers
127
- num_type: PhoneNumberType value (MOBILE, FIXED_LINE, etc.)
128
129
Returns:
130
Example PhoneNumber of the specified type, None if unavailable
131
"""
132
133
def example_number_for_non_geo_entity(country_calling_code: int) -> PhoneNumber | None:
134
"""
135
Get example number for non-geographical country code.
136
137
Parameters:
138
- country_calling_code: Non-geographical country calling code
139
140
Returns:
141
Example PhoneNumber for the non-geographical entity
142
"""
143
144
def invalid_example_number(region_code: str) -> PhoneNumber | None:
145
"""
146
Get example of invalid number for testing purposes.
147
148
Parameters:
149
- region_code: Region identifier
150
151
Returns:
152
Example invalid PhoneNumber for testing validation logic
153
"""
154
```
155
156
**Usage Examples:**
157
158
```python
159
# Generate example numbers for different regions
160
regions = ["US", "GB", "DE", "FR", "JP", "AU"]
161
162
print("Example numbers by region:")
163
for region in regions:
164
example = phonenumbers.example_number(region)
165
if example:
166
formatted = phonenumbers.format_number(example, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
167
print(f"{region}: {formatted}")
168
169
# Generate examples by number type
170
types_to_test = [
171
(phonenumbers.PhoneNumberType.FIXED_LINE, "Fixed Line"),
172
(phonenumbers.PhoneNumberType.MOBILE, "Mobile"),
173
(phonenumbers.PhoneNumberType.TOLL_FREE, "Toll Free"),
174
(phonenumbers.PhoneNumberType.PREMIUM_RATE, "Premium Rate")
175
]
176
177
print("\nUS examples by type:")
178
for num_type, type_name in types_to_test:
179
example = phonenumbers.example_number_for_type("US", num_type)
180
if example:
181
formatted = phonenumbers.format_number(example, phonenumbers.PhoneNumberFormat.NATIONAL)
182
print(f"{type_name}: {formatted}")
183
184
# Non-geographical examples
185
non_geo_codes = [800, 808, 870, 878, 881, 882, 883, 888]
186
print("\nNon-geographical examples:")
187
for code in non_geo_codes:
188
example = phonenumbers.example_number_for_non_geo_entity(code)
189
if example:
190
formatted = phonenumbers.format_number(example, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
191
print(f"Country code {code}: {formatted}")
192
193
# Invalid examples for testing
194
print("\nInvalid examples for testing:")
195
test_regions = ["US", "GB", "DE"]
196
for region in test_regions:
197
invalid = phonenumbers.invalid_example_number(region)
198
if invalid:
199
# These should fail validation
200
is_valid = phonenumbers.is_valid_number(invalid)
201
formatted = phonenumbers.format_number(invalid, phonenumbers.PhoneNumberFormat.E164)
202
print(f"{region} invalid: {formatted} (valid: {is_valid})")
203
204
# Generate test data for unit tests
205
def generate_test_data(region, include_invalid=True):
206
"""Generate comprehensive test data for a region."""
207
test_data = {
208
"region": region,
209
"general_example": None,
210
"by_type": {},
211
"invalid_example": None
212
}
213
214
# General example
215
general = phonenumbers.example_number(region)
216
if general:
217
test_data["general_example"] = {
218
"number": general,
219
"e164": phonenumbers.format_number(general, phonenumbers.PhoneNumberFormat.E164),
220
"national": phonenumbers.format_number(general, phonenumbers.PhoneNumberFormat.NATIONAL),
221
"international": phonenumbers.format_number(general, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
222
}
223
224
# Examples by type
225
all_types = [
226
phonenumbers.PhoneNumberType.FIXED_LINE,
227
phonenumbers.PhoneNumberType.MOBILE,
228
phonenumbers.PhoneNumberType.TOLL_FREE,
229
phonenumbers.PhoneNumberType.PREMIUM_RATE,
230
phonenumbers.PhoneNumberType.SHARED_COST,
231
phonenumbers.PhoneNumberType.VOIP,
232
phonenumbers.PhoneNumberType.PERSONAL_NUMBER,
233
phonenumbers.PhoneNumberType.PAGER,
234
phonenumbers.PhoneNumberType.UAN,
235
phonenumbers.PhoneNumberType.VOICEMAIL
236
]
237
238
for num_type in all_types:
239
example = phonenumbers.example_number_for_type(region, num_type)
240
if example:
241
test_data["by_type"][num_type] = {
242
"number": example,
243
"formatted": phonenumbers.format_number(example, phonenumbers.PhoneNumberFormat.E164)
244
}
245
246
# Invalid example
247
if include_invalid:
248
invalid = phonenumbers.invalid_example_number(region)
249
if invalid:
250
test_data["invalid_example"] = {
251
"number": invalid,
252
"formatted": phonenumbers.format_number(invalid, phonenumbers.PhoneNumberFormat.E164)
253
}
254
255
return test_data
256
257
# Generate test data for US
258
us_test_data = generate_test_data("US")
259
print(f"\nGenerated test data for US:")
260
print(f"- General example: {us_test_data['general_example']['e164'] if us_test_data['general_example'] else 'None'}")
261
print(f"- Number types with examples: {len(us_test_data['by_type'])}")
262
print(f"- Has invalid example: {us_test_data['invalid_example'] is not None}")
263
```
264
265
### Metadata and Constants Access
266
267
Access to global constants and metadata about supported regions and capabilities.
268
269
```python { .api }
270
SUPPORTED_REGIONS: set[str] # Set of all supported region codes
271
272
SUPPORTED_SHORT_REGIONS: list[str] # List of regions with short number support
273
274
COUNTRY_CODE_TO_REGION_CODE: dict[int, tuple[str, ...]] # Map country codes to regions
275
276
COUNTRY_CODES_FOR_NON_GEO_REGIONS: set[int] # Non-geographical country codes
277
278
UNKNOWN_REGION: str # Constant for unknown region ("ZZ")
279
280
REGION_CODE_FOR_NON_GEO_ENTITY: str # Region code for non-geographical numbers ("001")
281
282
NON_DIGITS_PATTERN: Pattern[str] # Regex pattern matching non-digit characters
283
284
__version__: str # Package version string
285
```
286
287
**Usage Examples:**
288
289
```python
290
# Access global constants
291
print(f"Library version: {phonenumbers.__version__}")
292
print(f"Unknown region code: {phonenumbers.UNKNOWN_REGION}")
293
print(f"Non-geo region code: {phonenumbers.REGION_CODE_FOR_NON_GEO_ENTITY}")
294
295
# Supported regions information
296
all_regions = phonenumbers.SUPPORTED_REGIONS
297
print(f"Total supported regions: {len(all_regions)}")
298
print(f"Sample regions: {sorted(list(all_regions))[:10]}")
299
300
# Check if specific regions are supported
301
regions_to_check = ["US", "GB", "CA", "AU", "XX", "ZZ"]
302
for region in regions_to_check:
303
is_supported = region in phonenumbers.SUPPORTED_REGIONS
304
print(f"Region {region} supported: {is_supported}")
305
306
# Short number support
307
short_regions = phonenumbers.SUPPORTED_SHORT_REGIONS
308
print(f"\nShort numbers supported in {len(short_regions)} regions")
309
print(f"Short number regions: {short_regions[:10]}") # First 10
310
311
# Country code mappings
312
print(f"\nCountry code mappings (sample):")
313
sample_codes = [1, 44, 33, 49, 81]
314
for code in sample_codes:
315
regions = phonenumbers.COUNTRY_CODE_TO_REGION_CODE.get(code, ())
316
print(f"Country code {code}: {regions}")
317
318
# Non-geographical country codes
319
non_geo_codes = phonenumbers.COUNTRY_CODES_FOR_NON_GEO_REGIONS
320
print(f"\nNon-geographical country codes: {sorted(non_geo_codes)}")
321
322
# Pattern matching with NON_DIGITS_PATTERN
323
import re
324
test_strings = ["123-456-7890", "abc123def", "+1 (555) 123-4567", ""]
325
for test_str in test_strings:
326
non_digits = phonenumbers.NON_DIGITS_PATTERN.findall(test_str)
327
print(f"'{test_str}' non-digits: {non_digits}")
328
```
329
330
### Advanced Utility Functions
331
332
Specialized utility functions for advanced use cases and system integration.
333
334
```python { .api }
335
def supported_calling_codes() -> set[int]:
336
"""Get set of all supported country calling codes."""
337
338
def supported_types_for_region(region_code: str) -> set[int]:
339
"""Get supported phone number types for region."""
340
341
def supported_types_for_non_geo_entity(country_code: int) -> set[int]:
342
"""Get supported types for non-geographical entities."""
343
```
344
345
**Usage Examples:**
346
347
```python
348
# Get all supported calling codes
349
all_codes = phonenumbers.supported_calling_codes()
350
print(f"All supported country codes ({len(all_codes)}):")
351
print(f"Range: {min(all_codes)} to {max(all_codes)}")
352
print(f"Sample codes: {sorted(list(all_codes))[:20]}")
353
354
# Analyze regional capabilities
355
def analyze_region_capabilities(region_code):
356
"""Analyze what number types are supported in a region."""
357
if region_code not in phonenumbers.SUPPORTED_REGIONS:
358
return f"Region {region_code} is not supported"
359
360
supported_types = phonenumbers.supported_types_for_region(region_code)
361
362
type_names = {
363
phonenumbers.PhoneNumberType.FIXED_LINE: "Fixed Line",
364
phonenumbers.PhoneNumberType.MOBILE: "Mobile",
365
phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE: "Fixed Line or Mobile",
366
phonenumbers.PhoneNumberType.TOLL_FREE: "Toll Free",
367
phonenumbers.PhoneNumberType.PREMIUM_RATE: "Premium Rate",
368
phonenumbers.PhoneNumberType.SHARED_COST: "Shared Cost",
369
phonenumbers.PhoneNumberType.VOIP: "VoIP",
370
phonenumbers.PhoneNumberType.PERSONAL_NUMBER: "Personal Number",
371
phonenumbers.PhoneNumberType.PAGER: "Pager",
372
phonenumbers.PhoneNumberType.UAN: "Universal Access Number",
373
phonenumbers.PhoneNumberType.VOICEMAIL: "Voicemail"
374
}
375
376
capabilities = []
377
for type_code in supported_types:
378
type_name = type_names.get(type_code, f"Type {type_code}")
379
capabilities.append(type_name)
380
381
return {
382
"region": region_code,
383
"supported_types": sorted(capabilities),
384
"count": len(capabilities)
385
}
386
387
# Analyze several regions
388
regions_to_analyze = ["US", "GB", "DE", "JP", "IN", "BR"]
389
for region in regions_to_analyze:
390
analysis = analyze_region_capabilities(region)
391
if isinstance(analysis, dict):
392
print(f"\n{region} capabilities ({analysis['count']} types):")
393
for capability in analysis['supported_types']:
394
print(f" - {capability}")
395
396
# Non-geographical entity analysis
397
non_geo_codes = [800, 808, 870, 878, 881, 882, 883, 888]
398
print(f"\nNon-geographical entity capabilities:")
399
for code in non_geo_codes:
400
supported_types = phonenumbers.supported_types_for_non_geo_entity(code)
401
if supported_types:
402
print(f"Country code {code}: {len(supported_types)} supported types")
403
404
# Create comprehensive capability matrix
405
def create_capability_matrix(regions=None):
406
"""Create a matrix showing which capabilities are available in which regions."""
407
if regions is None:
408
regions = ["US", "GB", "DE", "FR", "JP", "AU", "CA", "IN"]
409
410
all_types = [
411
phonenumbers.PhoneNumberType.FIXED_LINE,
412
phonenumbers.PhoneNumberType.MOBILE,
413
phonenumbers.PhoneNumberType.TOLL_FREE,
414
phonenumbers.PhoneNumberType.PREMIUM_RATE,
415
phonenumbers.PhoneNumberType.SHARED_COST,
416
phonenumbers.PhoneNumberType.VOIP
417
]
418
419
type_names = ["Fixed", "Mobile", "Toll Free", "Premium", "Shared", "VoIP"]
420
421
matrix = {}
422
for region in regions:
423
if region in phonenumbers.SUPPORTED_REGIONS:
424
supported = phonenumbers.supported_types_for_region(region)
425
matrix[region] = [num_type in supported for num_type in all_types]
426
427
return matrix, type_names
428
429
capability_matrix, type_headers = create_capability_matrix()
430
431
print(f"\nCapability Matrix:")
432
print(f"{'Region':<8}", end="")
433
for header in type_headers:
434
print(f"{header:<10}", end="")
435
print()
436
437
print("-" * (8 + len(type_headers) * 10))
438
439
for region, capabilities in capability_matrix.items():
440
print(f"{region:<8}", end="")
441
for has_capability in capabilities:
442
mark = "✓" if has_capability else "✗"
443
print(f"{mark:<10}", end="")
444
print()
445
```
446
447
### System Integration Helpers
448
449
Utility functions to help integrate phonenumbers with other systems and frameworks.
450
451
**Usage Examples:**
452
453
```python
454
# Create validation helpers for web forms
455
def create_phone_validator(region_code):
456
"""Create a phone number validator function for a specific region."""
457
def validate_phone(phone_string):
458
try:
459
parsed = phonenumbers.parse(phone_string, region_code)
460
return {
461
"valid": phonenumbers.is_valid_number(parsed),
462
"possible": phonenumbers.is_possible_number(parsed),
463
"formatted": phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164),
464
"type": phonenumbers.number_type(parsed),
465
"region": phonenumbers.region_code_for_number(parsed)
466
}
467
except phonenumbers.NumberParseException as e:
468
return {
469
"valid": False,
470
"error": str(e),
471
"error_type": e.error_type
472
}
473
474
return validate_phone
475
476
# Create validators for different regions
477
us_validator = create_phone_validator("US")
478
uk_validator = create_phone_validator("GB")
479
480
# Test validation
481
test_numbers = [
482
"(555) 123-4567",
483
"+1 555 123 4567",
484
"020 8366 1177",
485
"+44 20 8366 1177",
486
"invalid"
487
]
488
489
for number in test_numbers:
490
us_result = us_validator(number)
491
uk_result = uk_validator(number)
492
493
print(f"\n'{number}':")
494
print(f" US context: {'Valid' if us_result.get('valid') else 'Invalid'}")
495
print(f" UK context: {'Valid' if uk_result.get('valid') else 'Invalid'}")
496
497
# Database storage helper
498
def prepare_for_storage(phone_string, region_code=None):
499
"""Prepare phone number for database storage."""
500
try:
501
parsed = phonenumbers.parse(phone_string, region_code)
502
503
if not phonenumbers.is_valid_number(parsed):
504
return None # Don't store invalid numbers
505
506
return {
507
"e164": phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164),
508
"national": phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.NATIONAL),
509
"international": phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.INTERNATIONAL),
510
"country_code": parsed.country_code,
511
"national_number": parsed.national_number,
512
"region": phonenumbers.region_code_for_number(parsed),
513
"type": phonenumbers.number_type(parsed),
514
"extension": parsed.extension
515
}
516
except phonenumbers.NumberParseException:
517
return None
518
519
# Test storage preparation
520
storage_tests = [
521
("(555) 123-4567", "US"),
522
("+44 20 8366 1177", None),
523
("invalid", "US")
524
]
525
526
for phone, region in storage_tests:
527
stored_data = prepare_for_storage(phone, region)
528
if stored_data:
529
print(f"\n'{phone}' -> Storage data:")
530
print(f" E164: {stored_data['e164']}")
531
print(f" Region: {stored_data['region']}")
532
print(f" Type: {stored_data['type']}")
533
else:
534
print(f"\n'{phone}' -> Not suitable for storage")
535
536
# Bulk processing helper
537
def bulk_process_numbers(phone_list, region_code=None, format_type=None):
538
"""Process a list of phone numbers efficiently."""
539
if format_type is None:
540
format_type = phonenumbers.PhoneNumberFormat.E164
541
542
results = []
543
544
for phone_string in phone_list:
545
try:
546
parsed = phonenumbers.parse(phone_string, region_code)
547
548
result = {
549
"input": phone_string,
550
"valid": phonenumbers.is_valid_number(parsed),
551
"formatted": phonenumbers.format_number(parsed, format_type),
552
"region": phonenumbers.region_code_for_number(parsed),
553
"type": phonenumbers.number_type(parsed)
554
}
555
556
results.append(result)
557
558
except phonenumbers.NumberParseException as e:
559
results.append({
560
"input": phone_string,
561
"valid": False,
562
"error": str(e),
563
"error_type": e.error_type
564
})
565
566
return results
567
568
# Test bulk processing
569
phone_batch = [
570
"(555) 123-4567",
571
"+44 20 8366 1177",
572
"1-800-555-0199",
573
"invalid-number",
574
"+33 1 42 68 53 00"
575
]
576
577
batch_results = bulk_process_numbers(phone_batch, "US", phonenumbers.PhoneNumberFormat.INTERNATIONAL)
578
579
print(f"\nBulk processing results:")
580
for result in batch_results:
581
status = "✓" if result.get("valid") else "✗"
582
formatted = result.get("formatted", "N/A")
583
print(f"{status} {result['input']} -> {formatted}")
584
```