phone-utils 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- phone_utils/__init__.py +6 -0
- phone_utils/exceptions.py +2 -0
- phone_utils/normalize.py +8 -0
- phone_utils/parser.py +50 -0
- phone_utils/validate.py +9 -0
- phone_utils/variants.py +44 -0
- phone_utils-0.1.0.dist-info/METADATA +112 -0
- phone_utils-0.1.0.dist-info/RECORD +10 -0
- phone_utils-0.1.0.dist-info/WHEEL +5 -0
- phone_utils-0.1.0.dist-info/top_level.txt +1 -0
phone_utils/__init__.py
ADDED
phone_utils/normalize.py
ADDED
phone_utils/parser.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import phonenumbers
|
|
2
|
+
|
|
3
|
+
from .exceptions import InvalidPhoneError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def parse_phone(phone: str) -> phonenumbers.PhoneNumber:
|
|
7
|
+
parsed = _try_parse(phone)
|
|
8
|
+
if not phonenumbers.is_valid_number(parsed):
|
|
9
|
+
raise InvalidPhoneError(f"Invalid phone number: {phone!r}")
|
|
10
|
+
return parsed
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parse_phone_lenient(phone: str) -> phonenumbers.PhoneNumber:
|
|
14
|
+
"""Parse accepting legacy BR formats; 12-digit BR numbers get the 9th digit auto-added."""
|
|
15
|
+
parsed = _try_parse(phone)
|
|
16
|
+
|
|
17
|
+
if parsed.country_code == 55:
|
|
18
|
+
e164 = phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
|
|
19
|
+
digits = e164[1:] # strip "+"
|
|
20
|
+
if len(digits) == 12:
|
|
21
|
+
with_ninth = "+" + digits[:4] + "9" + digits[4:]
|
|
22
|
+
try:
|
|
23
|
+
candidate = phonenumbers.parse(with_ninth, None)
|
|
24
|
+
if phonenumbers.is_valid_number(candidate):
|
|
25
|
+
return candidate
|
|
26
|
+
except phonenumbers.NumberParseException:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
if not phonenumbers.is_possible_number(parsed):
|
|
30
|
+
raise InvalidPhoneError(f"Invalid phone number: {phone!r}")
|
|
31
|
+
return parsed
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _try_parse(phone: str) -> phonenumbers.PhoneNumber:
|
|
35
|
+
if not isinstance(phone, str) or not phone.strip():
|
|
36
|
+
raise InvalidPhoneError(f"Invalid phone number: {phone!r}")
|
|
37
|
+
|
|
38
|
+
cleaned = phone.strip()
|
|
39
|
+
candidates = [cleaned]
|
|
40
|
+
if not cleaned.startswith("+"):
|
|
41
|
+
candidates.append("+" + cleaned)
|
|
42
|
+
|
|
43
|
+
last_exc = None
|
|
44
|
+
for candidate in candidates:
|
|
45
|
+
try:
|
|
46
|
+
return phonenumbers.parse(candidate, None)
|
|
47
|
+
except phonenumbers.NumberParseException as exc:
|
|
48
|
+
last_exc = exc
|
|
49
|
+
|
|
50
|
+
raise InvalidPhoneError(f"Invalid phone number: {phone!r}") from last_exc
|
phone_utils/validate.py
ADDED
phone_utils/variants.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import phonenumbers
|
|
2
|
+
|
|
3
|
+
from .exceptions import InvalidPhoneError
|
|
4
|
+
from .normalize import normalize_phone
|
|
5
|
+
from .parser import parse_phone_lenient
|
|
6
|
+
|
|
7
|
+
_BR_COUNTRY_CODE = 55
|
|
8
|
+
_BR_DDI = "+55"
|
|
9
|
+
_BR_TOTAL_WITH_NINTH = 13
|
|
10
|
+
_BR_TOTAL_WITHOUT_NINTH = 12
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def generate_br_variants(phone: str) -> list[str]:
|
|
14
|
+
normalized = _normalize_lenient(phone)
|
|
15
|
+
|
|
16
|
+
if not normalized.startswith(_BR_DDI):
|
|
17
|
+
return [normalized]
|
|
18
|
+
|
|
19
|
+
digits = normalized[1:] # strip "+"
|
|
20
|
+
ddd = digits[2:4] # area code after country code "55"
|
|
21
|
+
local = digits[4:] # digits after DDD
|
|
22
|
+
total_digits = len(digits)
|
|
23
|
+
|
|
24
|
+
if total_digits == _BR_TOTAL_WITH_NINTH and local.startswith("9"):
|
|
25
|
+
without_ninth = f"+55{ddd}{local[1:]}"
|
|
26
|
+
return [normalized, without_ninth]
|
|
27
|
+
|
|
28
|
+
if total_digits == _BR_TOTAL_WITHOUT_NINTH and local.startswith("9"):
|
|
29
|
+
with_ninth = f"+55{ddd}9{local}"
|
|
30
|
+
return [with_ninth, normalized]
|
|
31
|
+
|
|
32
|
+
return [normalized]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _normalize_lenient(phone: str) -> str:
|
|
36
|
+
"""Normalize to E.164; falls back to lenient parsing only for legacy BR formats."""
|
|
37
|
+
try:
|
|
38
|
+
return normalize_phone(phone)
|
|
39
|
+
except InvalidPhoneError:
|
|
40
|
+
pass
|
|
41
|
+
parsed = parse_phone_lenient(phone)
|
|
42
|
+
if parsed.country_code != _BR_COUNTRY_CODE:
|
|
43
|
+
raise InvalidPhoneError(f"Invalid phone number: {phone!r}")
|
|
44
|
+
return phonenumbers.format_number(parsed, phonenumbers.PhoneNumberFormat.E164)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phone-utils
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Biblioteca para normalização, validação e geração de variantes de números telefônicos.
|
|
5
|
+
Author-email: Alexandre Nahuz <alexandrenahuz@gmail.com>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: phonenumbers>=9.0.0
|
|
9
|
+
Provides-Extra: dev
|
|
10
|
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
|
11
|
+
Requires-Dist: pytest-cov>=6.0.0; extra == "dev"
|
|
12
|
+
|
|
13
|
+
# phone-utils
|
|
14
|
+
|
|
15
|
+
Biblioteca Python para normalização, validação e geração de variantes de números de telefone internacionais, com suporte especial à regra do 9º dígito brasileiro.
|
|
16
|
+
|
|
17
|
+
## Instalação
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install phone-utils
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Uso
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from phone_utils import normalize_phone, validate_phone, generate_br_variants, InvalidPhoneError
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Normalização
|
|
30
|
+
|
|
31
|
+
Converte qualquer formato de entrada para E.164 (`+CCDDNNNNNNNNN`):
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
normalize_phone("+55 (11) 99999-9999") # → "+5511999999999"
|
|
35
|
+
normalize_phone("5511999999999") # → "+5511999999999"
|
|
36
|
+
normalize_phone("+1 (202) 555-0123") # → "+12025550123"
|
|
37
|
+
|
|
38
|
+
normalize_phone("11999999999") # → raise InvalidPhoneError (sem DDI)
|
|
39
|
+
normalize_phone("abc") # → raise InvalidPhoneError
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Validação
|
|
43
|
+
|
|
44
|
+
Verifica a estrutura do número sem lançar exceção:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
validate_phone("+5511999999999") # → True
|
|
48
|
+
validate_phone("+12025550123") # → True
|
|
49
|
+
validate_phone("11999999999") # → False
|
|
50
|
+
validate_phone("") # → False
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Variantes brasileiras (regra do 9º dígito)
|
|
54
|
+
|
|
55
|
+
Gera todas as representações válidas de um número brasileiro considerando a presença ou ausência do 9º dígito:
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
# Com 9º dígito → gera variante sem
|
|
59
|
+
generate_br_variants("+5511999999999")
|
|
60
|
+
# → ["+5511999999999", "+551199999999"]
|
|
61
|
+
|
|
62
|
+
# Sem 9º dígito → gera variante com
|
|
63
|
+
generate_br_variants("+551199999999")
|
|
64
|
+
# → ["+5511999999999", "+551199999999"]
|
|
65
|
+
|
|
66
|
+
# Sem 9º dígito compatível (não começa com 9) → retorna só o número
|
|
67
|
+
generate_br_variants("+5511799999999")
|
|
68
|
+
# → ["+5511799999999"]
|
|
69
|
+
|
|
70
|
+
# Número internacional → retorna só o número normalizado
|
|
71
|
+
generate_br_variants("+12025550123")
|
|
72
|
+
# → ["+12025550123"]
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Tratamento de erros
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from phone_utils import InvalidPhoneError
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
normalized = normalize_phone("numero-invalido")
|
|
82
|
+
except InvalidPhoneError as e:
|
|
83
|
+
print(f"Número inválido: {e}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Formatos de entrada aceitos
|
|
87
|
+
|
|
88
|
+
| Formato | Exemplo |
|
|
89
|
+
|---|---|
|
|
90
|
+
| E.164 | `+5511999999999` |
|
|
91
|
+
| Sem `+` (com DDI) | `5511999999999` |
|
|
92
|
+
| Com máscara | `+55 (11) 99999-9999` |
|
|
93
|
+
| Com espaços | `+55 11 99999 9999` |
|
|
94
|
+
| Internacional | `+1 (202) 555-0123` |
|
|
95
|
+
|
|
96
|
+
## Escopo da biblioteca
|
|
97
|
+
|
|
98
|
+
**Faz:**
|
|
99
|
+
- Normalizar números para E.164
|
|
100
|
+
- Validar estrutura conforme regras do país
|
|
101
|
+
- Gerar variantes BR para compatibilidade com bases legadas
|
|
102
|
+
|
|
103
|
+
**Não faz:**
|
|
104
|
+
- Verificar se a linha existe ou está ativa
|
|
105
|
+
- Consultar operadoras
|
|
106
|
+
- Enriquecer dados
|
|
107
|
+
- Registrar logs
|
|
108
|
+
|
|
109
|
+
## Requisitos
|
|
110
|
+
|
|
111
|
+
- Python >= 3.10
|
|
112
|
+
- [phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) >= 9.0.0
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
phone_utils/__init__.py,sha256=YEEV8uoHKdxDkYVLb1IvwQNn2eCrYoXVT-uVEYZ7Gug,255
|
|
2
|
+
phone_utils/exceptions.py,sha256=t4d-ktkGeHKn-bi4RHetP4CthvJ1BoJbkpoq8wx52mM,45
|
|
3
|
+
phone_utils/normalize.py,sha256=wPZ4z1RmLVrc1jyNZ5SepySmuRGeYVe08iT5wjBttZE,210
|
|
4
|
+
phone_utils/parser.py,sha256=pWSIEygwzQTBA5Uxn7132uv4VtFF4pbrzTnFq429yFE,1726
|
|
5
|
+
phone_utils/validate.py,sha256=FSjqv47ff-7ZfOvz6kBSme_lfgklwsYwiumHZqq11Jg,184
|
|
6
|
+
phone_utils/variants.py,sha256=PKzxcnQkC1oAjGUGAS_ThSwAh7JF1V-Ybkz6zwmU1jw,1395
|
|
7
|
+
phone_utils-0.1.0.dist-info/METADATA,sha256=Q9jHDO_ovLhgjy-njuUCyXfdbA-mXAZ-aZJ4YMNUa14,3182
|
|
8
|
+
phone_utils-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
9
|
+
phone_utils-0.1.0.dist-info/top_level.txt,sha256=ID1jdHUG0gGyNoQ7QzgEdYnp7qmtOMN8PLPnTvfLRvw,12
|
|
10
|
+
phone_utils-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
phone_utils
|