Multi-provider driving directions and real-time traffic collector with a Typer CLI and SQL persistence.
Project description
gotime
gotime is a multi-provider driving-directions library and Typer-powered command-line tool. It gives you a single, typed API on top of the major commercial routing services — Google Maps, Bing Maps, TomTom, HERE, MapQuest, Mapbox and Azure Maps — along with a SQLAlchemy persistence layer that stores trips and raw API responses for later analysis.
Installation
pip install gotime # library + CLI
pip install "gotime[postgres]" # add psycopg2 for Postgres storage
You also need API keys for any provider you plan to use; see
docs/pricing.md for signup links and free-tier guidance.
Quickstart
export GOOGLE_MAPS_API_KEY=your-key
gotime providers list
gotime providers verify # probe every configured key
gotime query \
--origin 42.3554,-71.0654 \
--destination 42.3467,-71.0972 \
--provider google --provider tomtom
The coordinates above are public Boston landmarks (Boston Common and Fenway Park), included as a recognizable short commute for demo and testing purposes.
gotime providers verify (aliased as gotime verify-keys) issues a tiny
directions call per provider and classifies the response as ok,
missing, invalid, rate_limited, unreachable or error. Add
--provider google to narrow the probe, --json for CI consumption, and
--timeout 5 to shorten the per-call deadline.
Or drop a .env file next to where you invoke the CLI:
cp .env.example .env
$EDITOR .env # fill in the keys you have
gotime query --origin 42.36,-71.07 --destination 42.35,-71.10
Library usage
from gotime import Waypoint, query_providers
from gotime.config import load_settings
settings = load_settings()
results = query_providers(
origin=Waypoint(42.3554, -71.0654, label="home"),
destination=Waypoint(42.3467, -71.0972, label="work"),
providers=["google", "tomtom"],
settings=settings,
)
for name, result in results.items():
if hasattr(result, "duration_minutes"):
print(name, f"{result.duration_minutes:.1f} min", f"{result.distance_miles:.1f} mi")
else:
print(name, "error:", result)
Features
- Seven Tier-1 providers with a normalized
TripResultcontract. Providers that don't support a field (e.g. duration-in-traffic on Mapbox's basedrivingprofile) returnNoneso you never have to guess. - Typer CLI with rich table / JSON output and no hidden global state.
- SQLAlchemy 2.x persistence (
users,providers,locations,trips,trip_api_logs) plus Alembic migrations. Swap SQLite ↔ Postgres with a URL. - Docker-first tests with ≥ 90 % line coverage enforced in CI.
- Typed, Black-formatted, Ruff-linted Python 3.13 code (3.14 migration tracked in
docs/roadmap.md).
Repository layout
gotime/
├── src/gotime/ # library source (see docs/architecture.md)
├── tests/ # pytest-cov suite (mocks HTTP via respx)
├── docs/ # Sphinx site published to Read the Docs
├── alembic/ # DB migrations
├── Dockerfile # slim image for CI integration tests
├── docker-compose.test.yml
├── pyproject.toml # hatchling build, PEP 621 metadata
└── .github/workflows/ # CI, integration, publish, docs
Development
uv venv --python 3.13
source .venv/bin/activate
uv pip install --editable ".[dev]"
black --check src tests
ruff check src tests
pytest # pytest.ini_options sets --cov-fail-under=90
Or run everything in Docker:
docker compose --file docker-compose.test.yml up --build --abort-on-container-exit
Running CI locally
Three tiers, ordered by fidelity. Pick the one that matches what you're trying to verify.
1. Fast loop — the commands above, annotated with the .github/workflows/
step they mirror:
black --check src tests # ci.yml -> lint
ruff check src tests # ci.yml -> lint
mypy # ci.yml -> types (non-blocking)
sphinx-build -W -b html docs docs/_build/html # docs.yml
pytest # ci.yml -> test (sqlite leg)
The new changelog gate in ci.yml can be
previewed locally against the current PR's base branch:
# Mirrors ci.yml -> changelog
git diff --name-only origin/main... \
| grep --extended-regexp '^(src/gotime/|alembic/|pyproject\.toml$)' \
&& git diff --name-only origin/main... | grep --fixed-strings 'CHANGELOG.md' \
|| echo "Add a CHANGELOG entry before pushing, or apply the 'skip-changelog' label."
2. Docker fast-path — covers the Postgres leg that ci.yml's matrix runs:
docker compose --file docker-compose.test.yml up --build --abort-on-container-exit
3. Full-fidelity via act — runs the raw
.github/workflows/*.yml files:
curl --silent --show-error --location \
https://raw.githubusercontent.com/nektos/act/master/install.sh | bash
./bin/act --list
./bin/act --workflows .github/workflows/ci.yml
./bin/act --workflows .github/workflows/docs.yml
Caveats:
actneeds Docker (≈500 MB runner image).services:containers usually need--container-options "--network host".- Integration secrets (
GOOGLE_MAPS_API_KEY, …) are not auto-present — supply via--secret-file .secretsor letintegration.ymlskip (it alreadyskipifs on missing keys).
Release process
gotime uses calendar-anchored semantic versioning (YYYY.MINOR.MICRO) derived
automatically from git tags via hatch-vcs.
Version strings are never hand-edited.
The full runbook lives in docs/release.md. In short:
- Run
scripts/prepare-release.sh 2026.MINOR.MICROfrommain. It opens a PR whose only change is moving## [Unreleased]inCHANGELOG.mdto the new release section. - Merge the PR.
git pull --ff-only origin main, thengit tag --annotate --sign 2026.MINOR.MICROandgit push origin 2026.MINOR.MICRO.- Approve the
pypiGitHub Environment deployment. Everything else (build, provenance attestation, PyPI publish via OIDC, GitHub Release, install smoke-test, Read the Docs rebuild) is automated.
License
MIT — see LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gotime-2026.1.0.tar.gz.
File metadata
- Download URL: gotime-2026.1.0.tar.gz
- Upload date:
- Size: 40.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c882241bc059a068394a8f89dd9777b9d5cd02787f6bc2ea3280e0e49618d7b7
|
|
| MD5 |
602caafccf7ceecf89b631a23a9d71a6
|
|
| BLAKE2b-256 |
66fbd6d67921ccaffae4dee4c9b923a5f2783594c9dd56a6c73ee54c8466a989
|
Provenance
The following attestation bundles were made for gotime-2026.1.0.tar.gz:
Publisher:
release.yml on mgeiger/gotime
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gotime-2026.1.0.tar.gz -
Subject digest:
c882241bc059a068394a8f89dd9777b9d5cd02787f6bc2ea3280e0e49618d7b7 - Sigstore transparency entry: 1360038147
- Sigstore integration time:
-
Permalink:
mgeiger/gotime@ee26bc130de60caa0ab6f0ec1e430832ed4a3ac2 -
Branch / Tag:
refs/tags/2026.1.0 - Owner: https://github.com/mgeiger
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ee26bc130de60caa0ab6f0ec1e430832ed4a3ac2 -
Trigger Event:
push
-
Statement type:
File details
Details for the file gotime-2026.1.0-py3-none-any.whl.
File metadata
- Download URL: gotime-2026.1.0-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d73d0202e9c43db2309c14d3656e4c4f5e68ff6c98f86ff55f38aa8ee0862160
|
|
| MD5 |
41e59c0c78af4567e4b5413f56f996b6
|
|
| BLAKE2b-256 |
7da93ac86f9370933765789da24f037a5e97b7fe35008d35e585587c40a86374
|
Provenance
The following attestation bundles were made for gotime-2026.1.0-py3-none-any.whl:
Publisher:
release.yml on mgeiger/gotime
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
gotime-2026.1.0-py3-none-any.whl -
Subject digest:
d73d0202e9c43db2309c14d3656e4c4f5e68ff6c98f86ff55f38aa8ee0862160 - Sigstore transparency entry: 1360038155
- Sigstore integration time:
-
Permalink:
mgeiger/gotime@ee26bc130de60caa0ab6f0ec1e430832ed4a3ac2 -
Branch / Tag:
refs/tags/2026.1.0 - Owner: https://github.com/mgeiger
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@ee26bc130de60caa0ab6f0ec1e430832ed4a3ac2 -
Trigger Event:
push
-
Statement type: