pygtfsie
A GTFS static and GTFS-Realtime engine: ingest a feed archive into SQLite, resolve every stop event to an absolute instant, and answer departure queries.
This is the engine half of a pair. The Home Assistant integration built on it lives in ha-gtfsie. Nothing here imports Home Assistant, and a CI job fails the build if anything ever does.
Why it is a separate package
The great majority of the logic in a transit-departure integration is not integration logic. It is date arithmetic, CSV tolerance, protobuf decoding and SQL -- none of which needs an event loop, a config entry or a pinned Home Assistant version to be exercised.
Splitting the engine out means its tests run on bare pytest across every supported Python in well under a second, so the parts most likely to be wrong are also the cheapest to check. The integration keeps the parts that genuinely need Home Assistant: config entries, coordinators, entities and services.
Design rules
Three rules account for most of what this library does differently, and each exists because the alternative has a known, reported failure mode.
Time is resolved at ingest, never at query time. Every (trip, stop time, service date) becomes an absolute UTC epoch integer when the feed is loaded. No
SQL statement contains date(), datetime(), strftime() or 'now'. SQLite
evaluates those in C against the process timezone in UTC, which no Python-level
clock control can reach -- so a suite that fakes the clock still gets real dates
from the database, and the disagreement surfaces as departures on the wrong day.
The service day is anchored at local noon minus twelve hours. Not midnight. Local midnight does not exist on spring-forward days in Cairo, Havana, Asunción and around 70 other zone-dates in the current decade, and it happens twice on the corresponding fall-back days. Anchoring there yields a silently shifted instant rather than an error. Noon has never been moved by any jurisdiction.
Nothing reads the current time except the caller. Every query takes a
now_utc argument. scripts/time-sweep.sh runs the whole suite at six instants
in six timezones and fails if the results differ, which is what keeps the rule
from decaying.
Layout
pygtfsie/
const.py GTFS enumerations, route type names and icons
exceptions.py one hierarchy, so a consumer can catch the library
helpers/
tz.py the only place timezone reasoning exists
text.py the only place str() is called on a raw feed value
geo.py bounding boxes and great-circle distance
logthrottle.py rate limiting for repeated messages
Later phases add store/ (schema, connections, queries), ingest/ (download,
CSV, calendar expansion, materialisation) and realtime/ (protobuf and SIRI
decoding, trip matching).
Install
pip install pygtfsie # engine only
pip install pygtfsie[realtime] # adds gtfs-realtime-bindings and protobuf
Realtime support is an extra because a consumer that only needs the static timetable should not pull in protobuf.
Development
python3 -m venv .venv
./.venv/bin/pip install -e ".[dev]"
./.venv/bin/python -m pytest -q
Before pushing anything that touches date handling:
./.venv/bin/python -m ruff check pygtfsie tests
./.venv/bin/python -m ruff format --check pygtfsie tests
scripts/time-sweep.sh # needs libfaketime
Branching
master is the release branch and is what tags are cut from. Work happens on
topic branches merged back into master.
Pipeline
The GitLab pipeline has three stages:
- lint --
ruff checkandruff format --check, plus a grep gate that fails if any module underpygtfsie/imports Home Assistant. - test -- pytest on Python 3.11, 3.12, 3.13 and 3.14.
- publish -- builds and uploads to PyPI on a
vMAJOR.MINOR.PATCHtag using PyPI Trusted Publishing (OIDC), so no API token is stored anywhere. The job iswhen: manual: tagging never publishes on its own.
Licence
Mozilla Public License 2.0. See LICENSE.
Metadata
Release files for pygtfsie 0.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 |
|---|---|---|---|---|
| pygtfsie-0.1.0.tar.gz | 106.8 kB | Without provenance | Details for pygtfsie-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pygtfsie-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for pygtfsie-0.1.0-py3-none-any.whl |
Total release size: 177.7 kB
Release files / pygtfsie-0.1.0.tar.gz
| Download URL | pygtfsie-0.1.0.tar.gz |
|---|---|
| Size | 106.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9dcb7a2747892b2e09d98a9097a461f808edb2b78359822f7c00896fce3abc1b
|
|
BLAKE2b-256 checksum How to use checksums |
bb04ac40e82f3e6624b805a343b1da717799f4399743fb00a541bc0c40584e53
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|
Release files / pygtfsie-0.1.0-py3-none-any.whl
| Download URL | pygtfsie-0.1.0-py3-none-any.whl |
|---|---|
| Size | 70.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ca0f9c59b593bb90463f473f1928a56daf82829872139e92ba5a009f5e2f9bc9
|
|
BLAKE2b-256 checksum How to use checksums |
5b507907110de7c3a22748ad901f3c0d9d9ed4e9e8ab8ce888001a9375e7f25b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.15
|
Security for pygtfsie 0.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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A 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.