CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-phonenumberslite

Python library for parsing, formatting, storing and validating international phone numbers with reduced memory footprint

Pending
Overview
Eval results
Files

utility-functions.mddocs/

Utility Functions

Helper functions for normalizing, converting, and analyzing phone numbers. These utilities support data processing, string manipulation, and number comparison operations.

Capabilities

String Normalization

Normalize phone number strings by removing or converting unwanted characters to standard formats.

def normalize_digits_only(number: str) -> str:
    """
    Normalize string to contain only digits.
    
    Removes all non-digit characters from the input string.
    
    Parameters:
    - number: String that may contain phone number with formatting
    
    Returns:
    String containing only digit characters (0-9)
    """

def normalize_diallable_chars_only(number: str) -> str:
    """
    Normalize string to contain only diallable characters.
    
    Keeps digits and diallable symbols like +, *, #, but removes 
    formatting characters like spaces, dashes, parentheses.
    
    Parameters:
    - number: Phone number string with potential formatting
    
    Returns:
    String with only diallable characters
    """

Usage Examples:

import phonenumbers

# Remove all formatting
formatted_number = "+44 (20) 8366-1177"
digits_only = phonenumbers.normalize_digits_only(formatted_number)
print(digits_only)  # "442083661177"

# Keep diallable characters
number_with_extension = "+44 20 8366 1177 ext. 123"
diallable = phonenumbers.normalize_diallable_chars_only(number_with_extension)
print(diallable)  # "+442083661177ext123"

Alpha Character Conversion

Convert alphabetic characters in phone numbers to their corresponding digits (vanity numbers).

def convert_alpha_characters_in_number(number: str) -> str:
    """
    Convert alphabetic characters to digits using phone keypad mapping.
    
    Maps letters to digits according to standard phone keypad:
    ABC=2, DEF=3, GHI=4, JKL=5, MNO=6, PQRS=7, TUV=8, WXYZ=9
    
    Parameters:
    - number: Phone number string that may contain letters
    
    Returns:
    String with letters converted to corresponding digits
    """

def is_alpha_number(number: str) -> bool:
    """
    Check if a phone number string contains alphabetic characters.
    
    Parameters:
    - number: Phone number string to check
    
    Returns:
    True if the string contains 3 or more letters, False otherwise
    """

Usage Examples:

import phonenumbers

# Convert vanity number
vanity_number = "1-800-FLOWERS"
numeric = phonenumbers.convert_alpha_characters_in_number(vanity_number)
print(numeric)  # "1-800-3569377"

# Check for alpha characters
has_alpha = phonenumbers.is_alpha_number("1-800-CALL-NOW")
print(has_alpha)  # True

no_alpha = phonenumbers.is_alpha_number("1-800-555-1234")
print(no_alpha)  # False

Number Comparison

Compare phone numbers to determine if they represent the same number and the quality of the match.

def is_number_match(first_number: PhoneNumber, second_number: PhoneNumber) -> MatchType:
    """
    Compare two phone numbers and return the type of match.
    
    Parameters:
    - first_number: First PhoneNumber object to compare
    - second_number: Second PhoneNumber object to compare
    
    Returns:
    MatchType indicating the quality of the match:
    - EXACT_MATCH: Numbers are identical in all respects
    - NSN_MATCH: National significant numbers match exactly
    - SHORT_NSN_MATCH: One NSN is a shorter version of the other
    - NO_MATCH: Numbers are different
    - NOT_A_NUMBER: One or both numbers are invalid
    """

Usage Examples:

import phonenumbers
from phonenumbers import MatchType

# Parse two representations of the same number
number1 = phonenumbers.parse("+442083661177")
number2 = phonenumbers.parse("020 8366 1177", "GB")

match_result = phonenumbers.is_number_match(number1, number2)

if match_result == MatchType.EXACT_MATCH:
    print("Numbers are exactly the same")
elif match_result == MatchType.NSN_MATCH:
    print("Numbers have the same national significant number")
elif match_result == MatchType.SHORT_NSN_MATCH:
    print("One number is a shorter version of the other")
elif match_result == MatchType.NO_MATCH:
    print("Numbers are different")

# Compare with extensions
number_with_ext = phonenumbers.parse("+442083661177", keep_raw_input=True)
number_with_ext.extension = "123"
number_without_ext = phonenumbers.parse("+442083661177")

match_with_extension = phonenumbers.is_number_match(number_with_ext, number_without_ext)
print(f"Match with extension: {match_with_extension}")

National Significant Number Extraction

Extract the national significant number portion from a parsed phone number.

def national_significant_number(numobj: PhoneNumber) -> str:
    """
    Get the national significant number from a PhoneNumber object.
    
    The national significant number is the portion of the phone number
    that is dialed within the country, excluding the country code but
    including area codes and the subscriber number.
    
    Parameters:
    - numobj: PhoneNumber object to extract NSN from
    
    Returns:
    String containing the national significant number
    """

Area Code and Destination Code Analysis

Analyze the structure of phone numbers to determine area code and destination code lengths.

def length_of_geographical_area_code(numobj: PhoneNumber) -> int:
    """
    Get the length of the geographical area code for a phone number.
    
    Parameters:
    - numobj: PhoneNumber object to analyze
    
    Returns:
    Length of the area code, or 0 if not applicable or determinable
    """

def length_of_national_destination_code(numobj: PhoneNumber) -> int:
    """
    Get the length of the national destination code (area code + carrier code).
    
    Parameters:
    - numobj: PhoneNumber object to analyze
    
    Returns:
    Length of the national destination code
    """

Mobile Token Handling

Work with mobile tokens used in certain countries before the area code.

def country_mobile_token(country_calling_code: int) -> str:
    """
    Get the mobile token for a country, if applicable.
    
    Some countries use a mobile token (like '9' in Argentina) that
    appears before the area code in mobile numbers.
    
    Parameters:
    - country_calling_code: Country calling code to check
    
    Returns:
    Mobile token string, or empty string if not applicable
    """

NANPA Region Detection

Check if a region uses the North American Numbering Plan.

def is_nanpa_country(region_code: str) -> bool:
    """
    Check if a region uses the North American Numbering Plan.
    
    NANPA regions share country code +1 and similar numbering patterns.
    
    Parameters:
    - region_code: Two-letter region code to check
    
    Returns:
    True if the region is part of NANPA, False otherwise
    """

Mobile Number Portability

Check if a region supports mobile number portability.

def is_mobile_number_portable_region(region_code: str) -> bool:
    """
    Check if mobile numbers can be ported between carriers in a region.
    
    Parameters:
    - region_code: Two-letter region code to check
    
    Returns:
    True if mobile number portability is supported, False otherwise
    """

Usage Patterns

Data Cleaning Pipeline

import phonenumbers

def clean_phone_number_data(raw_numbers, default_region="US"):
    """Clean a list of raw phone number strings for processing."""
    cleaned_numbers = []
    
    for raw_number in raw_numbers:
        try:
            # Step 1: Convert alpha characters
            if phonenumbers.is_alpha_number(raw_number):
                numeric_number = phonenumbers.convert_alpha_characters_in_number(raw_number)
            else:
                numeric_number = raw_number
            
            # Step 2: Parse the number
            parsed = phonenumbers.parse(numeric_number, default_region)
            
            # Step 3: Validate
            if phonenumbers.is_valid_number(parsed):
                # Step 4: Normalize to E164 format
                e164 = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
                cleaned_numbers.append({
                    'original': raw_number,
                    'cleaned': e164,
                    'nsn': phonenumbers.national_significant_number(parsed),
                    'type': phonenumbers.number_type(parsed)
                })
                
        except phonenumbers.NumberParseException:
            # Log invalid numbers for review
            print(f"Could not parse: {raw_number}")
    
    return cleaned_numbers

# Example usage
raw_data = [
    "1-800-FLOWERS",
    "+44 (20) 8366-1177",
    "555.123.4567",
    "(555) 123-4567 ext 123"
]

cleaned = clean_phone_number_data(raw_data)
for item in cleaned:
    print(f"{item['original']} -> {item['cleaned']}")

Deduplication Using Number Matching

import phonenumbers
from phonenumbers import MatchType

def deduplicate_phone_numbers(number_strings, region="US"):
    """Remove duplicate phone numbers using intelligent matching."""
    parsed_numbers = []
    unique_numbers = []
    
    # Parse all valid numbers
    for number_str in number_strings:
        try:
            parsed = phonenumbers.parse(number_str, region)
            if phonenumbers.is_valid_number(parsed):
                parsed_numbers.append((number_str, parsed))
        except phonenumbers.NumberParseException:
            continue
    
    # Find unique numbers
    for original_str, parsed_num in parsed_numbers:
        is_duplicate = False
        
        for unique_str, unique_parsed in unique_numbers:
            match_type = phonenumbers.is_number_match(parsed_num, unique_parsed)
            
            if match_type in [MatchType.EXACT_MATCH, MatchType.NSN_MATCH]:
                is_duplicate = True
                break
        
        if not is_duplicate:
            unique_numbers.append((original_str, parsed_num))
    
    return [num_str for num_str, _ in unique_numbers]

# Example usage
numbers_with_duplicates = [
    "+1-800-555-1234",
    "1 (800) 555-1234",
    "18005551234",
    "+1-555-123-4567",
    "555.123.4567"
]

unique = deduplicate_phone_numbers(numbers_with_duplicates)
print(f"Original: {len(numbers_with_duplicates)} numbers")
print(f"Unique: {len(unique)} numbers")

Number Structure Analysis

import phonenumbers

def analyze_number_structure(number_str, region=None):
    """Analyze the internal structure of a phone number."""
    try:
        number = phonenumbers.parse(number_str, region)
        
        if not phonenumbers.is_valid_number(number):
            return {"error": "Invalid number"}
        
        nsn = phonenumbers.national_significant_number(number)
        area_code_length = phonenumbers.length_of_geographical_area_code(number)
        ndc_length = phonenumbers.length_of_national_destination_code(number)
        
        region_code = phonenumbers.region_code_for_number(number)
        is_nanpa = phonenumbers.is_nanpa_country(region_code)
        mobile_token = phonenumbers.country_mobile_token(number.country_code)
        
        return {
            'country_code': number.country_code,
            'nsn': nsn,
            'area_code_length': area_code_length,
            'ndc_length': ndc_length,
            'region': region_code,
            'is_nanpa': is_nanpa,
            'mobile_token': mobile_token,
            'is_geographic': phonenumbers.is_number_geographical(number),
            'can_dial_internationally': phonenumbers.can_be_internationally_dialled(number)
        }
        
    except phonenumbers.NumberParseException as e:
        return {"error": str(e)}

# Example usage
analysis = analyze_number_structure("+442083661177")
print(f"Country code: {analysis['country_code']}")
print(f"NSN: {analysis['nsn']}")
print(f"Area code length: {analysis['area_code_length']}")
print(f"Region: {analysis['region']}")

Install with Tessl CLI

npx tessl i tessl/pypi-phonenumberslite

docs

as-you-type-formatting.md

core-parsing-formatting.md

index.md

number-validation.md

phone-number-matching.md

region-metadata.md

short-numbers.md

utility-functions.md

tile.json