🌗 ← Índice
Data: 14/08/2026
Nível: Intermediário (Mês 2 — POO)
Uma ferramenta de terminal que consulta a Open-Meteo (API gratuita, sem chave) e mostra a previsão do tempo para uma cidade.
🌐 API gratuita: `https://api.open-meteo.com/v1/forecast?latitude=...&longitude=...`
A Open-Meteo tem um serviço de geocodificação gratuito:
https://geocoding-api.open-meteo.com/v1/search?name=Sao%20Paulo&count=1
Resposta (JSON):
{"results": [{"name": "São Paulo", "latitude": -23.55, "longitude": -46.63}]}
clima/
├── clima.py # CLI principal
├── api_clima.py # classe ClimaAPI (encapsula requests)
└── test_api_clima.py # testes
import requests
class ClimaAPI:
BASE = "https://api.open-meteo.com/v1/forecast"
def __init__(self, timeout=10):
self.timeout = timeout
def buscar_coordenadas(self, cidade):
url = "https://geocoding-api.open-meteo.com/v1/search"
resposta = requests.get(url, params={"name": cidade, "count": 1}, timeout=self.timeout)
resposta.raise_for_status()
resultados = resposta.json().get("results", [])
if not resultados:
raise ValueError(f"Cidade '{cidade}' não encontrada")
return resultados[0]["latitude"], resultados[0]["longitude"]
def previsao(self, cidade):
lat, lon = self.buscar_coordenadas(cidade)
resposta = requests.get(
self.BASE,
params={
"latitude": lat,
"longitude": lon,
"hourly": "temperature_2m",
"forecast_days": 1,
"timezone": "America/Sao_Paulo",
},
timeout=self.timeout,
)
resposta.raise_for_status()
return resposta.json()
import sys
from datetime import datetime
from api_clima import ClimaAPI
def exibir_previsao(dados):
horarios = dados["hourly"]["time"]
temperaturas = dados["hourly"]["temperature_2m"]
agora = datetime.now()
print(f"\n🌤️ Previsão para hoje — {agora.strftime('%d/%m/%Y')}\n")
for hora, temp in zip(horarios, temperaturas):
hora_obj = datetime.fromisoformat(hora)
if hora_obj.hour % 4 == 0: # mostra a cada 4h
print(f" {hora_obj.strftime('%H:%M')} → {temp:.1f}°C")
def main():
if len(sys.argv) < 2:
print("Uso: python clima.py 'Nome da Cidade'")
sys.exit(1)
cidade = " ".join(sys.argv[1:])
api = ClimaAPI()
try:
dados = api.previsao(cidade)
exibir_previsao(dados)
except ValueError as erro:
print(f"❌ {erro}")
sys.exit(1)
except requests.exceptions.RequestException as erro:
print(f"❌ Erro de rede: {erro}")
sys.exit(1)
if __name__ == "__main__":
import requests
main()
python clima.py "São Paulo"
# 🌤️ Previsão para hoje — 14/08/2026
# 00:00 → 16.2°C
# 04:00 → 14.8°C
# ...
Escreva testes para ClimaAPI:
# test_api_clima.py
import pytest
from unittest.mock import patch, MagicMock
from api_clima import ClimaAPI
def test_buscar_coordenadas_sucesso():
resposta_fake = MagicMock()
resposta_fake.json.return_value = {
"results": [{"name": "São Paulo", "latitude": -23.55, "longitude": -46.63}]
}
with patch("requests.get", return_value=resposta_fake) as mock_get:
api = ClimaAPI()
lat, lon = api.buscar_coordenadas("São Paulo")
assert lat == -23.55
assert lon == -46.63
mock_get.assert_called_once()
def test_buscar_coordenadas_nao_encontrada():
resposta_fake = MagicMock()
resposta_fake.json.return_value = {}
with patch("requests.get", return_value=resposta_fake):
api = ClimaAPI()
with pytest.raises(ValueError, match="não encontrada"):
api.buscar_coordenadas("Atlantida")
def test_previsao_monta_params():
resposta_geo = MagicMock()
resposta_geo.json.return_value = {"results": [{"latitude": -23.55, "longitude": -46.63}]}
resposta_previsao = MagicMock()
resposta_previsao.json.return_value = {"hourly": {"time": [], "temperature_2m": []}}
def fake_get(url, **kwargs):
if "geocoding" in url:
return resposta_geo
return resposta_previsao
with patch("requests.get", side_effect=fake_get) as mock_get:
api = ClimaAPI()
dados = api.previsao("São Paulo")
# a 2ª chamada deve ter forecast_days e timezone
segunda_chamada = mock_get.call_args_list[1]
params = segunda_chamada.kwargs["params"]
assert params["forecast_days"] == 1
assert "America/Sao_Paulo" in params["timezone"]
💡 Resultado esperado: 3 passed — e a CLI funciona de verdade na rede real.
Você construiu uma CLI real que consome API externa — combinando POO, requests, datetime, tratamento de erros e testes. Isso é exatamente o perfil de automação do Mês 3: ferramentas úteis para o seu dia a dia (e para as suas VPSs!).
HB Estudos · ← Voltar ao índice de Python