summaryrefslogtreecommitdiffstats
path: root/src/cepis/__init__.py
blob: bc98d2948b795befcf0999685276157f49308a8e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
"""Python client for the public CEP API at `cep.is <https://cep.is>`_.

Quick use — for a script, the module-level functions are enough::

    import cepis

    address = cepis.lookup("01001-000")
    print(address.localidade, address.uf)   # São Paulo SP

In an application — build a :class:`Client` once and reuse it::

    from cepis import Client

    client = Client(timeout=5.0, total_timeout=15.0)
    address = client.lookup("01001000")

Pointing at a local instance (the API repo brings one up with ``just up``)::

    client = Client(base_url="http://localhost:8080")

Errors are explicit and split by decision — whether retrying helps or not::

    from cepis import CEPNotFound, CepisError

    try:
        address = cepis.lookup(cep_from_form)
    except CEPNotFound:
        ...   # the CEP does not exist: ask the user for another
    except CepisError:
        ...   # network, server, rate limit: retry later

Note on field names: the API surface is English, but the **data fields keep
the API's own Portuguese names** (``logradouro``, ``bairro``, ``localidade``).
What you read in the JSON is what you access on the object.

The module-level functions share one default client. It is built once because
:class:`Client` keeps no state between calls and is thread-safe. Do not mutate
it: it applies process-wide, including to third-party libraries that also use
this module. Anyone needing different settings should build their own.
"""

from __future__ import annotations

__version__ = "0.1.0"

from .client import DEFAULT_URL, MAX_BATCH, Client, normalize_cep
from .errors import (
    CepisError,
    CEPNotFound,
    InvalidCEP,
    InvalidResponse,
    NetworkError,
    RateLimited,
    ServerError,
)
from .models import Address, Geo, Health

__all__ = [
    "DEFAULT_URL",
    "MAX_BATCH",
    "Address",
    "CEPNotFound",
    "CepisError",
    "Client",
    "Geo",
    "Health",
    "InvalidCEP",
    "InvalidResponse",
    "NetworkError",
    "RateLimited",
    "ServerError",
    "__version__",
    "health",
    "lookup",
    "lookup_many",
    "normalize_cep",
]

_default = Client()


def lookup(cep: str) -> Address:
    """Look up a CEP using the default configuration.

    Shorthand for ``Client().lookup(cep)``. See :meth:`Client.lookup`.
    """
    return _default.lookup(cep)


def lookup_many(ceps: list[str]) -> list[Address | None]:
    """Look up many CEPs in one batch, using the default configuration.

    Shorthand for :meth:`Client.lookup_many` — read that one, especially the
    warning that batches carry no coordinate.
    """
    return _default.lookup_many(ceps)


def health() -> Health:
    """Query the API's health using the default configuration.

    Shorthand for :meth:`Client.health`.
    """
    return _default.health()