Python library for parsing, formatting, storing and validating international phone numbers with reduced memory footprint
—
Comprehensive validation functions to check if phone numbers are valid, possible, or match specific criteria. These functions provide detailed validation information and support various validation levels.
Determine if phone numbers are valid according to official numbering plan rules and formatting requirements.
def is_valid_number(numobj: PhoneNumber) -> bool:
"""
Check if a phone number is valid.
A valid number is one that is both possible (correct length) and
follows the numbering rules for its region and type.
Parameters:
- numobj: PhoneNumber object to validate
Returns:
True if the number is valid, False otherwise
"""
def is_valid_number_for_region(numobj: PhoneNumber, region_code: str) -> bool:
"""
Check if a phone number is valid for a specific region.
Parameters:
- numobj: PhoneNumber object to validate
- region_code: Two-letter region code to validate against
Returns:
True if valid for the specified region, False otherwise
"""Usage Examples:
import phonenumbers
# Parse and validate various numbers
numbers = [
"+442083661177", # Valid UK number
"+1234567890", # Invalid country code
"+44208366", # Too short
"+442083661177123", # Too long
]
for number_str in numbers:
try:
number = phonenumbers.parse(number_str)
is_valid = phonenumbers.is_valid_number(number)
print(f"{number_str}: {'Valid' if is_valid else 'Invalid'}")
except phonenumbers.NumberParseException:
print(f"{number_str}: Parse failed")
# Region-specific validation
number = phonenumbers.parse("+442083661177")
print(f"Valid for GB: {phonenumbers.is_valid_number_for_region(number, 'GB')}")
print(f"Valid for US: {phonenumbers.is_valid_number_for_region(number, 'US')}")Check if numbers have valid lengths and basic formatting, which is less strict than full validation.
def is_possible_number(numobj: PhoneNumber) -> bool:
"""
Check if a phone number is possible (has valid length).
This is a quicker check than full validation - it only verifies
that the number length is appropriate for the region.
Parameters:
- numobj: PhoneNumber object to check
Returns:
True if the number could possibly be valid, False otherwise
"""
def is_possible_number_string(number: str, region_code: str) -> bool:
"""
Check if a number string is possible without full parsing.
Parameters:
- number: Phone number string to check
- region_code: Region code for parsing context
Returns:
True if the string could represent a valid number, False otherwise
"""Get detailed information about why a number might not be possible, with specific reasons for failure.
def is_possible_number_with_reason(numobj: PhoneNumber) -> ValidationResult:
"""
Check number possibility with detailed reason for the result.
Parameters:
- numobj: PhoneNumber object to analyze
Returns:
ValidationResult indicating specific possibility status:
- IS_POSSIBLE: Number has valid length
- IS_POSSIBLE_LOCAL_ONLY: Valid for local dialing only
- INVALID_COUNTRY_CODE: Country code is not recognized
- TOO_SHORT: Number is shorter than valid lengths
- TOO_LONG: Number is longer than valid lengths
- INVALID_LENGTH: Length doesn't match any valid pattern
"""
def is_possible_number_for_type(numobj: PhoneNumber,
phone_type: PhoneNumberType) -> bool:
"""
Check if number is possible for a specific phone number type.
Parameters:
- numobj: PhoneNumber object to check
- phone_type: Specific type to validate against (MOBILE, FIXED_LINE, etc.)
Returns:
True if possible for the specified type, False otherwise
"""
def is_possible_number_for_type_with_reason(numobj: PhoneNumber,
phone_type: PhoneNumberType) -> ValidationResult:
"""
Check type-specific possibility with detailed reason.
Parameters:
- numobj: PhoneNumber object to analyze
- phone_type: Phone number type to check against
Returns:
ValidationResult with specific reason for the validation outcome
"""Usage Examples:
import phonenumbers
from phonenumbers import PhoneNumberType, ValidationResult
number = phonenumbers.parse("+44208366", "GB") # Too short
# Basic possibility check
print(f"Is possible: {phonenumbers.is_possible_number(number)}")
# Detailed analysis
result = phonenumbers.is_possible_number_with_reason(number)
if result == ValidationResult.TOO_SHORT:
print("Number is too short")
elif result == ValidationResult.TOO_LONG:
print("Number is too long")
elif result == ValidationResult.IS_POSSIBLE:
print("Number is possible")
# Type-specific checking
mobile_number = phonenumbers.parse("+447700900123", "GB")
is_mobile_possible = phonenumbers.is_possible_number_for_type(
mobile_number, PhoneNumberType.MOBILE
)
print(f"Possible as mobile: {is_mobile_possible}")Determine what type of phone number (mobile, fixed-line, toll-free, etc.) a given number represents.
def number_type(numobj: PhoneNumber) -> PhoneNumberType:
"""
Determine the type of a phone number.
Parameters:
- numobj: PhoneNumber object to classify
Returns:
PhoneNumberType indicating the number's classification:
- FIXED_LINE: Traditional landline number
- MOBILE: Mobile/cellular number
- FIXED_LINE_OR_MOBILE: Cannot distinguish (e.g., US numbers)
- TOLL_FREE: Free-to-caller service number
- PREMIUM_RATE: Premium-rate service number
- SHARED_COST: Shared cost between caller and receiver
- VOIP: Voice over IP number
- PERSONAL_NUMBER: Personal numbering service
- PAGER: Pager number
- UAN: Universal Access Number (company number)
- VOICEMAIL: Voicemail access number
- UNKNOWN: Cannot determine type
"""Analyze whether numbers are tied to specific geographic locations.
def is_number_geographical(numobj: PhoneNumber) -> bool:
"""
Check if a phone number is geographical (tied to a location).
Parameters:
- numobj: PhoneNumber object to analyze
Returns:
True if the number is associated with a geographic area, False otherwise
"""
def is_number_type_geographical(phone_type: PhoneNumberType) -> bool:
"""
Check if a phone number type is generally geographical.
Parameters:
- phone_type: PhoneNumberType to check
Returns:
True if numbers of this type are typically geographical
"""Check if numbers can be dialed internationally from other countries.
def can_be_internationally_dialled(numobj: PhoneNumber) -> bool:
"""
Check if a number can be dialed internationally.
Some numbers (like emergency services) cannot be reached from abroad.
Parameters:
- numobj: PhoneNumber object to check
Returns:
True if the number can be dialed from other countries, False otherwise
"""Attempt to truncate overly long numbers to valid lengths.
def truncate_too_long_number(numobj: PhoneNumber) -> PhoneNumber:
"""
Truncate a number that is too long to a valid length.
Parameters:
- numobj: PhoneNumber that may be too long
Returns:
PhoneNumber truncated to valid length, or original if already valid
"""import phonenumbers
from phonenumbers import PhoneNumberType, ValidationResult
def validate_phone_number(number_string, region=None):
"""Comprehensive phone number validation with detailed feedback."""
try:
# Parse the number
number = phonenumbers.parse(number_string, region)
# Basic validation
is_valid = phonenumbers.is_valid_number(number)
is_possible = phonenumbers.is_possible_number(number)
# Detailed possibility analysis
possibility_reason = phonenumbers.is_possible_number_with_reason(number)
# Number type classification
num_type = phonenumbers.number_type(number)
# Geographic analysis
is_geographic = phonenumbers.is_number_geographical(number)
# International dialing capability
can_dial_intl = phonenumbers.can_be_internationally_dialled(number)
return {
'number': number,
'is_valid': is_valid,
'is_possible': is_possible,
'possibility_reason': possibility_reason,
'type': num_type,
'is_geographic': is_geographic,
'can_dial_internationally': can_dial_intl,
'formatted': {
'e164': phonenumbers.format_number(number, phonenumbers.PhoneNumberFormat.E164),
'international': phonenumbers.format_number(number, phonenumbers.PhoneNumberFormat.INTERNATIONAL),
'national': phonenumbers.format_number(number, phonenumbers.PhoneNumberFormat.NATIONAL)
}
}
except phonenumbers.NumberParseException as e:
return {
'error': str(e),
'error_type': e.error_type
}
# Example usage
result = validate_phone_number("+442083661177")
if 'error' not in result:
print(f"Valid: {result['is_valid']}")
print(f"Type: {result['type']}")
print(f"Geographic: {result['is_geographic']}")
print(f"E164: {result['formatted']['e164']}")import phonenumbers
def validate_number_list(numbers, default_region="US"):
"""Validate a list of phone numbers with summary statistics."""
results = {
'valid': [],
'invalid': [],
'unparseable': [],
'types': {}
}
for number_str in numbers:
try:
number = phonenumbers.parse(number_str, default_region)
if phonenumbers.is_valid_number(number):
results['valid'].append(number_str)
num_type = phonenumbers.number_type(number)
results['types'][num_type] = results['types'].get(num_type, 0) + 1
else:
results['invalid'].append(number_str)
except phonenumbers.NumberParseException:
results['unparseable'].append(number_str)
return results
# Example usage
test_numbers = [
"+1-800-555-1234", # Valid US toll-free
"+44 20 8366 1177", # Valid UK fixed-line
"+1-555-123", # Invalid (too short)
"not-a-number" # Unparseable
]
results = validate_number_list(test_numbers)
print(f"Valid: {len(results['valid'])}")
print(f"Invalid: {len(results['invalid'])}")
print(f"Unparseable: {len(results['unparseable'])}")
print(f"Types found: {results['types']}")Install with Tessl CLI
npx tessl i tessl/pypi-phonenumberslite