thinwrap-location
thinwrap-location for Python — unified facade across 6 providers for routing, matrix, geocoding, and isochrone, with built-in polyline utilities. Zero runtime deps (stdlib urllib), synchronous, stateless, BYO transport.
Python constraints
- Runtime dependencies
- Zero runtime dependencies
- BYO HTTP client
- Transport protocol: any object with send(HttpRequest) -> HttpResponse, passed as transport=
- Provenance
- PEP 740 attestations (PyPI Trusted Publishing)
- Minimum runtime
- Python 3.10+
- Truly zero dependencies — the standard library urllib.request is the only HTTP surface, and AWS SigV4 (SES, SNS, Bedrock) / JWT signing (FCM RS256, APNs ES256) are implemented in pure Python on hashlib and hmac, with no cryptography package.
- Synchronous by design. For async or pooled HTTP, inject an httpx or requests based Transport; the library never imports either.
- The default transport never follows redirects — a 3xx surfaces as an error rather than silently re-sending auth headers to another host.
30-second install
pip install thinwrap-location2-minute end-to-end example
Compute a route between two waypoints through any supported provider. The example below uses Mapbox; switching providers means changing the config class — input and output shape stay identical.
import os
from thinwrap.location import Routing, RoutingOptions, MapboxConfig, LatLng, ConnectorError
r = Routing(MapboxConfig(access_token=os.environ["MAPBOX_TOKEN"]))
try:
res = r.route(RoutingOptions(waypoints=[
LatLng(37.7749, -122.4194),
LatLng(37.3382, -121.8863),
]))
print(f"{res.total_distance_meters / 1000:.1f} km, {res.total_duration_seconds / 60:.0f} min")
print(res.polyline) # Google precision-5 encoded
except ConnectorError as e:
print(e.provider_code.value, e.provider_message)Same code works for all supported routing providers — swap to GoogleConfig, HereConfig,EsriConfig, TomTomConfig, orOsrmConfig(base_url=…). Output normalizes to a singleRoutingResult shape across every provider.
Supported providers
| Provider | Operations | Auth | README |
|---|---|---|---|
google Google Maps Platform | routing, matrix, geocoding | query | docs |
mapbox Mapbox | routing, matrix, geocoding, isochrone | query | docs |
here HERE | routing, matrix, geocoding, isochrone | header | docs |
esri ESRI ArcGIS | routing, matrix, geocoding, isochrone | bearer | docs |
tomtom TomTom | routing, matrix, geocoding, isochrone | query | docs |
osrm OSRM (self-hosted) | routing, matrix | header | docs |
Migration paths
Built-in polyline utilities
Every routing connector emits a Google precision-5 encoded polyline onres.polyline. Decode, re-encode, or convert HERE's flex-polyline / ESRI's path geometry without a third-party library:
from thinwrap.location import decode_polyline, encode_polyline, decode_flex_polyline, encode_esri_paths
points = decode_polyline(res.polyline) # list[LatLng]
re_encoded = encode_polyline(points) # back to precision-5
here = decode_flex_polyline("BFoz5...") # HERE flex-polyline
esri = encode_esri_paths(paths) # ESRI-JSON {paths, spatialReference}The four polyline primitives are the only encode/decode exports — the same four as every sibling, standard library only.
Operation coverage
Provider × operation gaps below are architectural, not bugs:
- OSRM ships only
routing+matrix— geocoding and isochrone are out of scope by design for OSRM. BuildingGeocoding(OsrmConfig(…))raisesValueErrorstraight away. - Google does not expose a public isochrone API.
Bring your own transport
The default transport is standard-library urllib: it never follows redirects and refuses cleartext http:// URLs. To reach a self-hosted OSRM over plain HTTP, or to usehttpx/requests, pass any object withsend(HttpRequest) -> HttpResponse astransport=. The wrapper holds no state — the HERE/TomTom async-matrix submit-poll-retrieve cycle stays hidden inside a singlematrix call.
Cross-language siblings
The TypeScript (/packages/location-ts), PHP (/packages/location-php), and Go (/packages/location-go) variants ship the same facade shape and provider set — cross-language parity is a design goal, not a hard release gate.