phone-utils 0.1.0__tar.gz
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-0.1.0/PKG-INFO +112 -0
- phone_utils-0.1.0/README.md +100 -0
- phone_utils-0.1.0/pyproject.toml +43 -0
- phone_utils-0.1.0/setup.cfg +4 -0
- phone_utils-0.1.0/src/phone_utils/__init__.py +6 -0
- phone_utils-0.1.0/src/phone_utils/exceptions.py +2 -0
- phone_utils-0.1.0/src/phone_utils/normalize.py +8 -0
- phone_utils-0.1.0/src/phone_utils/parser.py +50 -0
- phone_utils-0.1.0/src/phone_utils/validate.py +9 -0
- phone_utils-0.1.0/src/phone_utils/variants.py +44 -0
- phone_utils-0.1.0/src/phone_utils.egg-info/PKG-INFO +112 -0
- phone_utils-0.1.0/src/phone_utils.egg-info/SOURCES.txt +16 -0
- phone_utils-0.1.0/src/phone_utils.egg-info/dependency_links.txt +1 -0
- phone_utils-0.1.0/src/phone_utils.egg-info/requires.txt +5 -0
- phone_utils-0.1.0/src/phone_utils.egg-info/top_level.txt +1 -0
- phone_utils-0.1.0/tests/test_normalize.py +44 -0
- phone_utils-0.1.0/tests/test_validate.py +29 -0
- phone_utils-0.1.0/tests/test_variants.py +55 -0
|
@@ -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,100 @@
|
|
|
1
|
+
# phone-utils
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
## Instalação
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install phone-utils
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Uso
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from phone_utils import normalize_phone, validate_phone, generate_br_variants, InvalidPhoneError
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### Normalização
|
|
18
|
+
|
|
19
|
+
Converte qualquer formato de entrada para E.164 (`+CCDDNNNNNNNNN`):
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
normalize_phone("+55 (11) 99999-9999") # → "+5511999999999"
|
|
23
|
+
normalize_phone("5511999999999") # → "+5511999999999"
|
|
24
|
+
normalize_phone("+1 (202) 555-0123") # → "+12025550123"
|
|
25
|
+
|
|
26
|
+
normalize_phone("11999999999") # → raise InvalidPhoneError (sem DDI)
|
|
27
|
+
normalize_phone("abc") # → raise InvalidPhoneError
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Validação
|
|
31
|
+
|
|
32
|
+
Verifica a estrutura do número sem lançar exceção:
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
validate_phone("+5511999999999") # → True
|
|
36
|
+
validate_phone("+12025550123") # → True
|
|
37
|
+
validate_phone("11999999999") # → False
|
|
38
|
+
validate_phone("") # → False
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Variantes brasileiras (regra do 9º dígito)
|
|
42
|
+
|
|
43
|
+
Gera todas as representações válidas de um número brasileiro considerando a presença ou ausência do 9º dígito:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
# Com 9º dígito → gera variante sem
|
|
47
|
+
generate_br_variants("+5511999999999")
|
|
48
|
+
# → ["+5511999999999", "+551199999999"]
|
|
49
|
+
|
|
50
|
+
# Sem 9º dígito → gera variante com
|
|
51
|
+
generate_br_variants("+551199999999")
|
|
52
|
+
# → ["+5511999999999", "+551199999999"]
|
|
53
|
+
|
|
54
|
+
# Sem 9º dígito compatível (não começa com 9) → retorna só o número
|
|
55
|
+
generate_br_variants("+5511799999999")
|
|
56
|
+
# → ["+5511799999999"]
|
|
57
|
+
|
|
58
|
+
# Número internacional → retorna só o número normalizado
|
|
59
|
+
generate_br_variants("+12025550123")
|
|
60
|
+
# → ["+12025550123"]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Tratamento de erros
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from phone_utils import InvalidPhoneError
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
normalized = normalize_phone("numero-invalido")
|
|
70
|
+
except InvalidPhoneError as e:
|
|
71
|
+
print(f"Número inválido: {e}")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Formatos de entrada aceitos
|
|
75
|
+
|
|
76
|
+
| Formato | Exemplo |
|
|
77
|
+
|---|---|
|
|
78
|
+
| E.164 | `+5511999999999` |
|
|
79
|
+
| Sem `+` (com DDI) | `5511999999999` |
|
|
80
|
+
| Com máscara | `+55 (11) 99999-9999` |
|
|
81
|
+
| Com espaços | `+55 11 99999 9999` |
|
|
82
|
+
| Internacional | `+1 (202) 555-0123` |
|
|
83
|
+
|
|
84
|
+
## Escopo da biblioteca
|
|
85
|
+
|
|
86
|
+
**Faz:**
|
|
87
|
+
- Normalizar números para E.164
|
|
88
|
+
- Validar estrutura conforme regras do país
|
|
89
|
+
- Gerar variantes BR para compatibilidade com bases legadas
|
|
90
|
+
|
|
91
|
+
**Não faz:**
|
|
92
|
+
- Verificar se a linha existe ou está ativa
|
|
93
|
+
- Consultar operadoras
|
|
94
|
+
- Enriquecer dados
|
|
95
|
+
- Registrar logs
|
|
96
|
+
|
|
97
|
+
## Requisitos
|
|
98
|
+
|
|
99
|
+
- Python >= 3.10
|
|
100
|
+
- [phonenumbers](https://github.com/daviddrysdale/python-phonenumbers) >= 9.0.0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "phone-utils"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Biblioteca para normalização, validação e geração de variantes de números telefônicos."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
|
|
12
|
+
dependencies = [
|
|
13
|
+
"phonenumbers>=9.0.0"
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
authors = [
|
|
17
|
+
{ name = "Alexandre Nahuz", email = "alexandrenahuz@gmail.com" }
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.optional-dependencies]
|
|
21
|
+
dev = [
|
|
22
|
+
"pytest>=8.0.0",
|
|
23
|
+
"pytest-cov>=6.0.0"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[tool.setuptools]
|
|
27
|
+
package-dir = {"" = "src"}
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
where = ["src"]
|
|
31
|
+
|
|
32
|
+
[tool.pytest.ini_options]
|
|
33
|
+
testpaths = [
|
|
34
|
+
"tests"
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
python_files = [
|
|
38
|
+
"test_*.py"
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
addopts = [
|
|
42
|
+
"-v"
|
|
43
|
+
]
|
|
@@ -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
|
|
@@ -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,16 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/phone_utils/__init__.py
|
|
4
|
+
src/phone_utils/exceptions.py
|
|
5
|
+
src/phone_utils/normalize.py
|
|
6
|
+
src/phone_utils/parser.py
|
|
7
|
+
src/phone_utils/validate.py
|
|
8
|
+
src/phone_utils/variants.py
|
|
9
|
+
src/phone_utils.egg-info/PKG-INFO
|
|
10
|
+
src/phone_utils.egg-info/SOURCES.txt
|
|
11
|
+
src/phone_utils.egg-info/dependency_links.txt
|
|
12
|
+
src/phone_utils.egg-info/requires.txt
|
|
13
|
+
src/phone_utils.egg-info/top_level.txt
|
|
14
|
+
tests/test_normalize.py
|
|
15
|
+
tests/test_validate.py
|
|
16
|
+
tests/test_variants.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
phone_utils
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from phone_utils import normalize_phone, InvalidPhoneError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestNormalizeValid:
|
|
7
|
+
@pytest.mark.parametrize("input_phone,expected", [
|
|
8
|
+
("+5511999999999", "+5511999999999"),
|
|
9
|
+
("5511999999999", "+5511999999999"),
|
|
10
|
+
("+55 (11) 99999-9999", "+5511999999999"),
|
|
11
|
+
("+55 11 99999 9999", "+5511999999999"),
|
|
12
|
+
("+12025550123", "+12025550123"),
|
|
13
|
+
("+1 (202) 555-0123", "+12025550123"),
|
|
14
|
+
("+353871234567", "+353871234567"),
|
|
15
|
+
("+595981123456", "+595981123456"),
|
|
16
|
+
])
|
|
17
|
+
def test_valid_numbers(self, input_phone, expected):
|
|
18
|
+
assert normalize_phone(input_phone) == expected
|
|
19
|
+
|
|
20
|
+
@pytest.mark.parametrize("input_phone", [
|
|
21
|
+
"+5511999999999",
|
|
22
|
+
"+12025550123",
|
|
23
|
+
"+353871234567",
|
|
24
|
+
"+595981123456",
|
|
25
|
+
])
|
|
26
|
+
def test_idempotent(self, input_phone):
|
|
27
|
+
assert normalize_phone(normalize_phone(input_phone)) == normalize_phone(input_phone)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TestNormalizeInvalid:
|
|
31
|
+
@pytest.mark.parametrize("input_phone", [
|
|
32
|
+
"11999999999",
|
|
33
|
+
"999999999",
|
|
34
|
+
"abc",
|
|
35
|
+
"telefone123",
|
|
36
|
+
"",
|
|
37
|
+
"()-+",
|
|
38
|
+
"+999123456789",
|
|
39
|
+
"+5511",
|
|
40
|
+
"+551199999999999999999999",
|
|
41
|
+
])
|
|
42
|
+
def test_invalid_numbers_raise(self, input_phone):
|
|
43
|
+
with pytest.raises(InvalidPhoneError):
|
|
44
|
+
normalize_phone(input_phone)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from phone_utils import validate_phone
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestValidateTrue:
|
|
7
|
+
@pytest.mark.parametrize("phone", [
|
|
8
|
+
"+5511999999999",
|
|
9
|
+
"+12025550123",
|
|
10
|
+
"+353871234567",
|
|
11
|
+
"+595981123456",
|
|
12
|
+
])
|
|
13
|
+
def test_valid_returns_true(self, phone):
|
|
14
|
+
assert validate_phone(phone) is True
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestValidateFalse:
|
|
18
|
+
@pytest.mark.parametrize("phone", [
|
|
19
|
+
"11999999999",
|
|
20
|
+
"abc",
|
|
21
|
+
"",
|
|
22
|
+
"+999123456789",
|
|
23
|
+
])
|
|
24
|
+
def test_invalid_returns_false(self, phone):
|
|
25
|
+
assert validate_phone(phone) is False
|
|
26
|
+
|
|
27
|
+
def test_never_raises(self):
|
|
28
|
+
for value in [None, 123, [], {}, object()]:
|
|
29
|
+
assert validate_phone(value) is False
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
from phone_utils import generate_br_variants, InvalidPhoneError
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestBrVariants:
|
|
7
|
+
def test_with_ninth_digit(self):
|
|
8
|
+
result = generate_br_variants("+5511999999999")
|
|
9
|
+
assert result == ["+5511999999999", "+551199999999"]
|
|
10
|
+
|
|
11
|
+
def test_without_ninth_digit(self):
|
|
12
|
+
result = generate_br_variants("+551199999999")
|
|
13
|
+
assert result == ["+5511999999999", "+551199999999"]
|
|
14
|
+
|
|
15
|
+
def test_without_compatible_ninth_digit(self):
|
|
16
|
+
result = generate_br_variants("+5511799999999")
|
|
17
|
+
assert result == ["+5511799999999"]
|
|
18
|
+
|
|
19
|
+
def test_no_duplicates_with_ninth(self):
|
|
20
|
+
result = generate_br_variants("+5511999999999")
|
|
21
|
+
assert len(result) == len(set(result))
|
|
22
|
+
|
|
23
|
+
def test_no_duplicates_without_ninth(self):
|
|
24
|
+
result = generate_br_variants("+551199999999")
|
|
25
|
+
assert len(result) == len(set(result))
|
|
26
|
+
|
|
27
|
+
def test_order_with_ninth_first(self):
|
|
28
|
+
result = generate_br_variants("+5511999999999")
|
|
29
|
+
assert result[0] == "+5511999999999"
|
|
30
|
+
assert result[1] == "+551199999999"
|
|
31
|
+
|
|
32
|
+
def test_order_with_ninth_first_when_input_without(self):
|
|
33
|
+
result = generate_br_variants("+551199999999")
|
|
34
|
+
assert result[0] == "+5511999999999"
|
|
35
|
+
assert result[1] == "+551199999999"
|
|
36
|
+
|
|
37
|
+
def test_accepts_formatted_input(self):
|
|
38
|
+
result = generate_br_variants("+55 (11) 99999-9999")
|
|
39
|
+
assert result == ["+5511999999999", "+551199999999"]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestInternationalVariants:
|
|
43
|
+
def test_us_number_returns_single(self):
|
|
44
|
+
result = generate_br_variants("+12025550123")
|
|
45
|
+
assert result == ["+12025550123"]
|
|
46
|
+
|
|
47
|
+
def test_ireland_number_returns_single(self):
|
|
48
|
+
result = generate_br_variants("+353871234567")
|
|
49
|
+
assert result == ["+353871234567"]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class TestVariantsInvalid:
|
|
53
|
+
def test_invalid_raises(self):
|
|
54
|
+
with pytest.raises(InvalidPhoneError):
|
|
55
|
+
generate_br_variants("11999999999")
|