postali-api 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.
- postali_api-0.1.0/.gitignore +8 -0
- postali_api-0.1.0/LICENSE +21 -0
- postali_api-0.1.0/PKG-INFO +195 -0
- postali_api-0.1.0/README.md +165 -0
- postali_api-0.1.0/pyproject.toml +54 -0
- postali_api-0.1.0/src/postali/__init__.py +65 -0
- postali_api-0.1.0/src/postali/_client.py +236 -0
- postali_api-0.1.0/src/postali/_errors.py +42 -0
- postali_api-0.1.0/src/postali/_models.py +256 -0
- postali_api-0.1.0/src/postali/_normalize.py +40 -0
- postali_api-0.1.0/src/postali/_version.py +1 -0
- postali_api-0.1.0/src/postali/py.typed +0 -0
- postali_api-0.1.0/tests/test_client.py +341 -0
- postali_api-0.1.0/tests/test_live.py +59 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Postali <hi@postali.app>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: postali-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Cliente oficial de la API gratuita de códigos postales de Postali (México, Colombia, España). Sin dependencias.
|
|
5
|
+
Project-URL: Homepage, https://postali.app
|
|
6
|
+
Project-URL: Documentation, https://postali.app/api/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/gomflo/postali-py
|
|
8
|
+
Project-URL: Issues, https://github.com/gomflo/postali-py/issues
|
|
9
|
+
Author-email: Postali <hi@postali.app>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api,codigo postal,colombia,colonias,cp,códigos postales,geonames,mexico,postal code,sepomex,spain,zip code
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Natural Language :: Spanish
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
25
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.9
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# postali
|
|
32
|
+
|
|
33
|
+
Cliente oficial para Python de la **API gratuita de códigos postales de [Postali](https://postali.app)**: México, Colombia y España.
|
|
34
|
+
|
|
35
|
+
- Sin API key, sin registro, sin cuota mensual.
|
|
36
|
+
- Cero dependencias: sólo la biblioteca estándar (`urllib`). Python 3.9+.
|
|
37
|
+
- Respuestas tipadas como `dataclasses` inmutables (`py.typed` incluido).
|
|
38
|
+
- Restaura el cero inicial que se pierde en Excel, un CSV o `int()` (`6700` → `"06700"`).
|
|
39
|
+
|
|
40
|
+
**Documentación de la API:** https://postali.app/api/docs · **Sitio:** https://postali.app
|
|
41
|
+
|
|
42
|
+
## Instalación
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install postali-api
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
El paquete se instala como `postali-api` y se importa como `postali`.
|
|
49
|
+
|
|
50
|
+
## Uso rápido
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from postali import Postali
|
|
54
|
+
|
|
55
|
+
postali = Postali("mx")
|
|
56
|
+
print(postali.cp("06700").asentamientos[0].nombre) # Roma Norte
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Ejemplos
|
|
60
|
+
|
|
61
|
+
### Autocompletar la colonia en un formulario de dirección
|
|
62
|
+
|
|
63
|
+
Un endpoint de tu backend (aquí con Flask) que el formulario llama cuando el usuario termina de escribir el CP:
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from flask import Flask, jsonify
|
|
67
|
+
from postali import Postali, PostaliError
|
|
68
|
+
|
|
69
|
+
app = Flask(__name__)
|
|
70
|
+
postali = Postali("mx")
|
|
71
|
+
|
|
72
|
+
@app.get("/direccion/<cp>")
|
|
73
|
+
def direccion(cp: str):
|
|
74
|
+
try:
|
|
75
|
+
r = postali.cp(cp) # "6700" también funciona
|
|
76
|
+
except PostaliError as e:
|
|
77
|
+
if e.code in ("not_found", "invalid_cp"):
|
|
78
|
+
return jsonify(error="CP no encontrado"), 404
|
|
79
|
+
raise
|
|
80
|
+
return jsonify(
|
|
81
|
+
estado=r.estado,
|
|
82
|
+
municipio=r.municipio,
|
|
83
|
+
colonias=[a.nombre for a in r.asentamientos],
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
¿Buscas por nombre de colonia en lugar de CP?
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
for hit in postali.search("roma norte", limit=5).results:
|
|
91
|
+
print(hit.cp, hit.nombre, hit.municipio, hit.estado)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Validar un CP
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
if not postali.validate("06700"): # ValidateResult es truthy si el CP existe
|
|
98
|
+
raise ValueError("Ese código postal no existe.")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`validate` lanza `PostaliError` con `code == "invalid_cp"` si el texto ni siquiera tiene formato de CP (p. ej. `"abc"`), sin hacer la petición.
|
|
102
|
+
|
|
103
|
+
### Limpiar una columna de CPs (CSV / pandas)
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
r = postali.bulk(["06700", "44100", 6700, "abc"])
|
|
107
|
+
for item in r.results: # mismo orden que la entrada
|
|
108
|
+
print(item.cp, item.valid, item.municipio)
|
|
109
|
+
# 06700 True Cuauhtémoc · 44100 True Guadalajara · 06700 True Cuauhtémoc · abc False None
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`bulk` normaliza cada código, marca los que no tienen formato válido como `valid=False` y parte la lista en lotes de 100.
|
|
113
|
+
|
|
114
|
+
### Colombia y España
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
Postali("co").cp("050001").municipio # 'Medellín'
|
|
118
|
+
Postali("es").cp(8001).municipio # "08001" → 'Barcelona'
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## API
|
|
122
|
+
|
|
123
|
+
`Postali(country="mx", *, base_url="https://postali.app", timeout=10.0, user_agent=...)`
|
|
124
|
+
|
|
125
|
+
| Método | Endpoint | Devuelve |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| `cp(codigo)` | `GET /api/v1/{country}/cp/{codigo}` | `CpResult` |
|
|
128
|
+
| `validate(codigo)` | `GET /api/v1/{country}/validate/{codigo}` | `ValidateResult` |
|
|
129
|
+
| `search(q, limit=None)` | `GET /api/v1/{country}/search?q=` | `SearchResult` |
|
|
130
|
+
| `estados()` | `GET /api/v1/{country}/estados` | `EstadosResult` |
|
|
131
|
+
| `estado(slug)` | `GET /api/v1/{country}/estado/{slug}` | `Estado` |
|
|
132
|
+
| `municipios(estado_slug)` | `GET /api/v1/{country}/estado/{slug}/municipios` | `MunicipiosResult` |
|
|
133
|
+
| `municipio(estado_slug, municipio_slug)` | `GET /api/v1/{country}/municipio/{estado}/{municipio}` | `MunicipioResult` |
|
|
134
|
+
| `bulk(codigos)` | `POST /api/v1/{country}/bulk` | `BulkResult` |
|
|
135
|
+
|
|
136
|
+
"Estado" es el nivel 1 de cada país: estado en México, departamento en Colombia, provincia en España. Los campos de cada resultado se llaman igual que en el JSON de la API.
|
|
137
|
+
|
|
138
|
+
### Errores
|
|
139
|
+
|
|
140
|
+
Todo error de la API o de red se lanza como `PostaliError`, con `code`, `status`, `message` y `docs_url`:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from postali import PostaliError
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
postali.cp("00000")
|
|
147
|
+
except PostaliError as e:
|
|
148
|
+
print(e.code, e.status, e.message) # not_found 404 No se encontró el recurso solicitado.
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
| `code` | Cuándo |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `invalid_cp` | El CP no tiene el formato del país |
|
|
154
|
+
| `invalid_query` | Falta `q`, está vacía o hay un parámetro inválido |
|
|
155
|
+
| `not_found` | El CP, estado o municipio no existe |
|
|
156
|
+
| `rate_limited` | Demasiadas peticiones desde tu IP |
|
|
157
|
+
| `internal_error` | Error del servidor (5xx) |
|
|
158
|
+
| `timeout` | Se superó el `timeout` |
|
|
159
|
+
| `network_error` | No se pudo conectar |
|
|
160
|
+
| `http_error` | Otra respuesta no exitosa |
|
|
161
|
+
|
|
162
|
+
### Normalización de CPs
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from postali import normalize_cp
|
|
166
|
+
|
|
167
|
+
normalize_cp("6700") # '06700'
|
|
168
|
+
normalize_cp(" 76 148 ") # '76148'
|
|
169
|
+
normalize_cp("50001", "co") # '050001'
|
|
170
|
+
normalize_cp("670") # None (incompleto)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Se añade como máximo un cero, y nunca delante de otro cero: ningún CP de México, Colombia o España empieza por `00`.
|
|
174
|
+
|
|
175
|
+
## Datos y atribución
|
|
176
|
+
|
|
177
|
+
Datos: Sepomex vía [Postali](https://postali.app) / GeoNames ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)) para CO y ES.
|
|
178
|
+
|
|
179
|
+
## English
|
|
180
|
+
|
|
181
|
+
`postali` is the official zero-dependency Python client (stdlib `urllib`, Python 3.9+) for the free [Postali](https://postali.app) postal-code API covering Mexico, Colombia and Spain. No API key required.
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from postali import Postali
|
|
185
|
+
r = Postali("mx").cp("06700") # "mx" | "co" | "es"
|
|
186
|
+
print(r.estado, r.municipio, [a.nombre for a in r.asentamientos])
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Responses are typed frozen dataclasses; errors raise `PostaliError` with `code` (`invalid_cp`, `not_found`, `rate_limited`, `timeout`…), `status` and `docs_url`. Postal codes that lost their leading zero (`6700`) are restored automatically. API reference: https://postali.app/api/docs
|
|
190
|
+
|
|
191
|
+
Data: Sepomex via Postali / GeoNames (CC BY 4.0) for CO and ES.
|
|
192
|
+
|
|
193
|
+
## Licencia
|
|
194
|
+
|
|
195
|
+
MIT © Postali
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# postali
|
|
2
|
+
|
|
3
|
+
Cliente oficial para Python de la **API gratuita de códigos postales de [Postali](https://postali.app)**: México, Colombia y España.
|
|
4
|
+
|
|
5
|
+
- Sin API key, sin registro, sin cuota mensual.
|
|
6
|
+
- Cero dependencias: sólo la biblioteca estándar (`urllib`). Python 3.9+.
|
|
7
|
+
- Respuestas tipadas como `dataclasses` inmutables (`py.typed` incluido).
|
|
8
|
+
- Restaura el cero inicial que se pierde en Excel, un CSV o `int()` (`6700` → `"06700"`).
|
|
9
|
+
|
|
10
|
+
**Documentación de la API:** https://postali.app/api/docs · **Sitio:** https://postali.app
|
|
11
|
+
|
|
12
|
+
## Instalación
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install postali-api
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
El paquete se instala como `postali-api` y se importa como `postali`.
|
|
19
|
+
|
|
20
|
+
## Uso rápido
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from postali import Postali
|
|
24
|
+
|
|
25
|
+
postali = Postali("mx")
|
|
26
|
+
print(postali.cp("06700").asentamientos[0].nombre) # Roma Norte
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Ejemplos
|
|
30
|
+
|
|
31
|
+
### Autocompletar la colonia en un formulario de dirección
|
|
32
|
+
|
|
33
|
+
Un endpoint de tu backend (aquí con Flask) que el formulario llama cuando el usuario termina de escribir el CP:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from flask import Flask, jsonify
|
|
37
|
+
from postali import Postali, PostaliError
|
|
38
|
+
|
|
39
|
+
app = Flask(__name__)
|
|
40
|
+
postali = Postali("mx")
|
|
41
|
+
|
|
42
|
+
@app.get("/direccion/<cp>")
|
|
43
|
+
def direccion(cp: str):
|
|
44
|
+
try:
|
|
45
|
+
r = postali.cp(cp) # "6700" también funciona
|
|
46
|
+
except PostaliError as e:
|
|
47
|
+
if e.code in ("not_found", "invalid_cp"):
|
|
48
|
+
return jsonify(error="CP no encontrado"), 404
|
|
49
|
+
raise
|
|
50
|
+
return jsonify(
|
|
51
|
+
estado=r.estado,
|
|
52
|
+
municipio=r.municipio,
|
|
53
|
+
colonias=[a.nombre for a in r.asentamientos],
|
|
54
|
+
)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
¿Buscas por nombre de colonia en lugar de CP?
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
for hit in postali.search("roma norte", limit=5).results:
|
|
61
|
+
print(hit.cp, hit.nombre, hit.municipio, hit.estado)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Validar un CP
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
if not postali.validate("06700"): # ValidateResult es truthy si el CP existe
|
|
68
|
+
raise ValueError("Ese código postal no existe.")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`validate` lanza `PostaliError` con `code == "invalid_cp"` si el texto ni siquiera tiene formato de CP (p. ej. `"abc"`), sin hacer la petición.
|
|
72
|
+
|
|
73
|
+
### Limpiar una columna de CPs (CSV / pandas)
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
r = postali.bulk(["06700", "44100", 6700, "abc"])
|
|
77
|
+
for item in r.results: # mismo orden que la entrada
|
|
78
|
+
print(item.cp, item.valid, item.municipio)
|
|
79
|
+
# 06700 True Cuauhtémoc · 44100 True Guadalajara · 06700 True Cuauhtémoc · abc False None
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`bulk` normaliza cada código, marca los que no tienen formato válido como `valid=False` y parte la lista en lotes de 100.
|
|
83
|
+
|
|
84
|
+
### Colombia y España
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
Postali("co").cp("050001").municipio # 'Medellín'
|
|
88
|
+
Postali("es").cp(8001).municipio # "08001" → 'Barcelona'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## API
|
|
92
|
+
|
|
93
|
+
`Postali(country="mx", *, base_url="https://postali.app", timeout=10.0, user_agent=...)`
|
|
94
|
+
|
|
95
|
+
| Método | Endpoint | Devuelve |
|
|
96
|
+
|---|---|---|
|
|
97
|
+
| `cp(codigo)` | `GET /api/v1/{country}/cp/{codigo}` | `CpResult` |
|
|
98
|
+
| `validate(codigo)` | `GET /api/v1/{country}/validate/{codigo}` | `ValidateResult` |
|
|
99
|
+
| `search(q, limit=None)` | `GET /api/v1/{country}/search?q=` | `SearchResult` |
|
|
100
|
+
| `estados()` | `GET /api/v1/{country}/estados` | `EstadosResult` |
|
|
101
|
+
| `estado(slug)` | `GET /api/v1/{country}/estado/{slug}` | `Estado` |
|
|
102
|
+
| `municipios(estado_slug)` | `GET /api/v1/{country}/estado/{slug}/municipios` | `MunicipiosResult` |
|
|
103
|
+
| `municipio(estado_slug, municipio_slug)` | `GET /api/v1/{country}/municipio/{estado}/{municipio}` | `MunicipioResult` |
|
|
104
|
+
| `bulk(codigos)` | `POST /api/v1/{country}/bulk` | `BulkResult` |
|
|
105
|
+
|
|
106
|
+
"Estado" es el nivel 1 de cada país: estado en México, departamento en Colombia, provincia en España. Los campos de cada resultado se llaman igual que en el JSON de la API.
|
|
107
|
+
|
|
108
|
+
### Errores
|
|
109
|
+
|
|
110
|
+
Todo error de la API o de red se lanza como `PostaliError`, con `code`, `status`, `message` y `docs_url`:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
from postali import PostaliError
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
postali.cp("00000")
|
|
117
|
+
except PostaliError as e:
|
|
118
|
+
print(e.code, e.status, e.message) # not_found 404 No se encontró el recurso solicitado.
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
| `code` | Cuándo |
|
|
122
|
+
|---|---|
|
|
123
|
+
| `invalid_cp` | El CP no tiene el formato del país |
|
|
124
|
+
| `invalid_query` | Falta `q`, está vacía o hay un parámetro inválido |
|
|
125
|
+
| `not_found` | El CP, estado o municipio no existe |
|
|
126
|
+
| `rate_limited` | Demasiadas peticiones desde tu IP |
|
|
127
|
+
| `internal_error` | Error del servidor (5xx) |
|
|
128
|
+
| `timeout` | Se superó el `timeout` |
|
|
129
|
+
| `network_error` | No se pudo conectar |
|
|
130
|
+
| `http_error` | Otra respuesta no exitosa |
|
|
131
|
+
|
|
132
|
+
### Normalización de CPs
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from postali import normalize_cp
|
|
136
|
+
|
|
137
|
+
normalize_cp("6700") # '06700'
|
|
138
|
+
normalize_cp(" 76 148 ") # '76148'
|
|
139
|
+
normalize_cp("50001", "co") # '050001'
|
|
140
|
+
normalize_cp("670") # None (incompleto)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Se añade como máximo un cero, y nunca delante de otro cero: ningún CP de México, Colombia o España empieza por `00`.
|
|
144
|
+
|
|
145
|
+
## Datos y atribución
|
|
146
|
+
|
|
147
|
+
Datos: Sepomex vía [Postali](https://postali.app) / GeoNames ([CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)) para CO y ES.
|
|
148
|
+
|
|
149
|
+
## English
|
|
150
|
+
|
|
151
|
+
`postali` is the official zero-dependency Python client (stdlib `urllib`, Python 3.9+) for the free [Postali](https://postali.app) postal-code API covering Mexico, Colombia and Spain. No API key required.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from postali import Postali
|
|
155
|
+
r = Postali("mx").cp("06700") # "mx" | "co" | "es"
|
|
156
|
+
print(r.estado, r.municipio, [a.nombre for a in r.asentamientos])
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Responses are typed frozen dataclasses; errors raise `PostaliError` with `code` (`invalid_cp`, `not_found`, `rate_limited`, `timeout`…), `status` and `docs_url`. Postal codes that lost their leading zero (`6700`) are restored automatically. API reference: https://postali.app/api/docs
|
|
160
|
+
|
|
161
|
+
Data: Sepomex via Postali / GeoNames (CC BY 4.0) for CO and ES.
|
|
162
|
+
|
|
163
|
+
## Licencia
|
|
164
|
+
|
|
165
|
+
MIT © Postali
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.24"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "postali-api"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Cliente oficial de la API gratuita de códigos postales de Postali (México, Colombia, España). Sin dependencias."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Postali", email = "hi@postali.app" }]
|
|
14
|
+
keywords = [
|
|
15
|
+
"codigo postal", "códigos postales", "cp", "sepomex", "colonias",
|
|
16
|
+
"postal code", "zip code", "mexico", "colombia", "spain", "geonames", "api",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Natural Language :: Spanish",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python :: 3",
|
|
24
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
25
|
+
"Programming Language :: Python :: 3.9",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
"Programming Language :: Python :: 3.14",
|
|
31
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
32
|
+
"Topic :: Software Development :: Libraries",
|
|
33
|
+
"Typing :: Typed",
|
|
34
|
+
]
|
|
35
|
+
dependencies = []
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://postali.app"
|
|
39
|
+
Documentation = "https://postali.app/api/docs"
|
|
40
|
+
Repository = "https://github.com/gomflo/postali-py"
|
|
41
|
+
Issues = "https://github.com/gomflo/postali-py/issues"
|
|
42
|
+
|
|
43
|
+
[tool.hatch.version]
|
|
44
|
+
path = "src/postali/_version.py"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/postali"]
|
|
48
|
+
|
|
49
|
+
[tool.hatch.build.targets.sdist]
|
|
50
|
+
include = ["src/postali", "tests", "README.md", "LICENSE", "pyproject.toml"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
markers = ["live: pruebas contra la API real (POSTALI_LIVE=1)"]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""Cliente oficial de la API gratuita de códigos postales de Postali.
|
|
2
|
+
|
|
3
|
+
México, Colombia y España. Sin API key, sin dependencias.
|
|
4
|
+
|
|
5
|
+
>>> from postali import Postali
|
|
6
|
+
>>> Postali("mx").cp("06700").asentamientos[0].nombre
|
|
7
|
+
'Roma Norte'
|
|
8
|
+
|
|
9
|
+
Documentación de la API: https://postali.app/api/docs
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from ._client import (
|
|
13
|
+
BULK_MAX,
|
|
14
|
+
DEFAULT_BASE_URL,
|
|
15
|
+
DEFAULT_TIMEOUT,
|
|
16
|
+
DEFAULT_USER_AGENT,
|
|
17
|
+
SEARCH_MAX_LENGTH,
|
|
18
|
+
Postali,
|
|
19
|
+
)
|
|
20
|
+
from ._errors import DOCS_URL, PostaliError
|
|
21
|
+
from ._models import (
|
|
22
|
+
Asentamiento,
|
|
23
|
+
BulkItem,
|
|
24
|
+
BulkResult,
|
|
25
|
+
Colonia,
|
|
26
|
+
CpResult,
|
|
27
|
+
Estado,
|
|
28
|
+
EstadosResult,
|
|
29
|
+
MunicipioItem,
|
|
30
|
+
MunicipioResult,
|
|
31
|
+
MunicipiosResult,
|
|
32
|
+
SearchHit,
|
|
33
|
+
SearchResult,
|
|
34
|
+
ValidateResult,
|
|
35
|
+
)
|
|
36
|
+
from ._normalize import COUNTRIES, CP_LENGTH, normalize_cp
|
|
37
|
+
from ._version import __version__
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"Postali",
|
|
41
|
+
"PostaliError",
|
|
42
|
+
"normalize_cp",
|
|
43
|
+
"COUNTRIES",
|
|
44
|
+
"CP_LENGTH",
|
|
45
|
+
"BULK_MAX",
|
|
46
|
+
"SEARCH_MAX_LENGTH",
|
|
47
|
+
"DEFAULT_BASE_URL",
|
|
48
|
+
"DEFAULT_TIMEOUT",
|
|
49
|
+
"DEFAULT_USER_AGENT",
|
|
50
|
+
"DOCS_URL",
|
|
51
|
+
"Asentamiento",
|
|
52
|
+
"BulkItem",
|
|
53
|
+
"BulkResult",
|
|
54
|
+
"Colonia",
|
|
55
|
+
"CpResult",
|
|
56
|
+
"Estado",
|
|
57
|
+
"EstadosResult",
|
|
58
|
+
"MunicipioItem",
|
|
59
|
+
"MunicipioResult",
|
|
60
|
+
"MunicipiosResult",
|
|
61
|
+
"SearchHit",
|
|
62
|
+
"SearchResult",
|
|
63
|
+
"ValidateResult",
|
|
64
|
+
"__version__",
|
|
65
|
+
]
|