phonenumbers parse, format and validate E.164 numbers: working versions (tested 2026-10-16)
Normalize phone numbers to E.164 with Google's phonenumbers, tell possible apart from valid, and map numbers back to a region, all offline.
passed6 passed · 0 failed2026-10-16 09:57 UTC- Status
- passed
- Last passed
- 2026-10-16 09:57 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions2
phonenumbers@9.0.41pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38883899161
The code
Exactly the files that passed. Your agent can pull them with get_recipe("phonenumbers-parse-format") (install), or see them on GitHub.
requirements.txt
phonenumbers==9.0.41 pytest==9.1.1
phone.py
import phonenumbers
from phonenumbers import PhoneNumberFormat
def parse_valid(raw: str, region: str | None = None) -> phonenumbers.PhoneNumber:
number = phonenumbers.parse(raw, region)
if not phonenumbers.is_valid_number(number):
raise ValueError(f"not a valid number: {raw!r}")
return number
def to_e164(raw: str, region: str | None = None) -> str:
return phonenumbers.format_number(parse_valid(raw, region), PhoneNumberFormat.E164)
def country_of(raw: str, region: str | None = None) -> str | None:
return phonenumbers.region_code_for_number(parse_valid(raw, region))
tests/test_phone.py
import phonenumbers
import pytest
from phonenumbers import NumberParseException, PhoneNumberFormat
from phone import country_of, parse_valid, to_e164
def test_normalizes_national_number_to_e164():
assert to_e164("(650) 253-0000", "US") == "+16502530000"
assert to_e164("+16502530000") == "+16502530000"
def test_region_hint_required_when_plus_is_missing():
with pytest.raises(NumberParseException) as exc:
phonenumbers.parse("6502530000", None)
assert exc.value.error_type == NumberParseException.INVALID_COUNTRY_CODE
def test_possible_is_not_the_same_as_valid():
number = phonenumbers.parse("+11999999999", None)
assert phonenumbers.is_possible_number(number)
assert not phonenumbers.is_valid_number(number)
with pytest.raises(ValueError):
to_e164("+11999999999")
def test_free_text_raises_before_validation():
with pytest.raises(NumberParseException) as exc:
phonenumbers.parse("call me maybe", "US")
assert exc.value.error_type == NumberParseException.NOT_A_NUMBER
def test_formats_and_region_for_gb_number():
number = parse_valid("+442083661177")
assert phonenumbers.format_number(number, PhoneNumberFormat.INTERNATIONAL) == "+44 20 8366 1177"
assert phonenumbers.format_number(number, PhoneNumberFormat.NATIONAL) == "020 8366 1177"
assert country_of("+442083661177") == "GB"
def test_non_geographic_numbers_report_region_001():
assert country_of("+80012345678") == "001"
pytest.ini
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.