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.
Metadata
Release files for gotime 2026.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| gotime-2026.1.0.tar.gz | 40.5 kB | With provenance | Details for gotime-2026.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| gotime-2026.1.0-py3-none-any.whl | Python 3 | none | any | Details for gotime-2026.1.0-py3-none-any.whl |
Total release size: 71.0 kB
Release files / gotime-2026.1.0.tar.gz
| Download URL | gotime-2026.1.0.tar.gz |
|---|---|
| Size | 40.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c882241bc059a068394a8f89dd9777b9d5cd02787f6bc2ea3280e0e49618d7b7
|
|
BLAKE2b-256 checksum How to use checksums |
66fbd6d67921ccaffae4dee4c9b923a5f2783594c9dd56a6c73ee54c8466a989
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 22, 2026.
Transparency logRelease files / gotime-2026.1.0-py3-none-any.whl
| Download URL | gotime-2026.1.0-py3-none-any.whl |
|---|---|
| Size | 30.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d73d0202e9c43db2309c14d3656e4c4f5e68ff6c98f86ff55f38aa8ee0862160
|
|
BLAKE2b-256 checksum How to use checksums |
7da93ac86f9370933765789da24f037a5e97b7fe35008d35e585587c40a86374
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 22, 2026.
Transparency logSecurity for gotime 2026.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for gotime-2026.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 22, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.