caltrain-api 1.0.1__tar.gz → 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- caltrain_api-1.1.0/.github/workflows/publish.yml +73 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/.gitignore +3 -0
- caltrain_api-1.1.0/.readthedocs.yaml +22 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/PKG-INFO +17 -1
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/README.md +10 -0
- caltrain_api-1.1.0/docs/api.rst +88 -0
- caltrain_api-1.1.0/docs/conf.py +82 -0
- caltrain_api-1.1.0/docs/index.md +26 -0
- caltrain_api-1.1.0/docs/overview.md +5 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/pyproject.toml +8 -1
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/__init__.py +9 -2
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/corridor.py +1 -1
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/models.py +6 -3
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/realtime.py +1 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/schedule.py +6 -1
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/uv.lock +698 -3
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/.python-version +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/LICENSE +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/docs/data-findings.md +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/_feedcache.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/api.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/describe.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/errors.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/src/caltrain_api/py.typed +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/__init__.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/conftest.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/fixtures/caltrain-gtfs-20261113.zip +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/fixtures/servicealerts-20261113-1145.pb +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/fixtures/siri-vm-20261113-1145.json +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/integration/__init__.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/integration/test_live.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/unit/__init__.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/unit/test_api.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/unit/test_corridor.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/unit/test_describe.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/unit/test_journeys.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/unit/test_realtime.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/unit/test_schedule.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tests/unit/test_schedule_load.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tools/one-off-explore-realtime.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tools/one-off-explore-static-gtfs.py +0 -0
- {caltrain_api-1.0.1 → caltrain_api-1.1.0}/tools/one-off-measure-report-lag.py +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Publish caltrain-api to PyPI when a version tag (vX.Y.Z) is pushed.
|
|
2
|
+
#
|
|
3
|
+
# Uses PyPI Trusted Publishing (OIDC): PyPI is configured to trust this
|
|
4
|
+
# repository + workflow + environment, so no API token is stored anywhere.
|
|
5
|
+
# PyPI side: https://pypi.org/manage/project/caltrain-api/settings/publishing/
|
|
6
|
+
# owner: jsinnott, repository: caltrain-api, workflow: publish.yml, environment: pypi
|
|
7
|
+
#
|
|
8
|
+
# Release flow (unchanged from before, minus the manual upload):
|
|
9
|
+
# bump version in pyproject.toml + src/caltrain_api/__init__.py, update the
|
|
10
|
+
# README changelog, commit, then: git tag vX.Y.Z && git push origin vX.Y.Z
|
|
11
|
+
|
|
12
|
+
name: Publish to PyPI
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
tags: ["v*"]
|
|
17
|
+
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
test:
|
|
23
|
+
name: Test (Python ${{ matrix.python }})
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
strategy:
|
|
26
|
+
matrix:
|
|
27
|
+
python: ["3.11", "3.14"] # oldest supported and newest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: astral-sh/setup-uv@v6
|
|
31
|
+
with:
|
|
32
|
+
python-version: ${{ matrix.python }}
|
|
33
|
+
- run: uv sync --extra dev
|
|
34
|
+
- run: uv run pytest -q tests/unit
|
|
35
|
+
- run: uv run mypy --strict src
|
|
36
|
+
|
|
37
|
+
build:
|
|
38
|
+
name: Build and check distributions
|
|
39
|
+
needs: test
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
steps:
|
|
42
|
+
- uses: actions/checkout@v4
|
|
43
|
+
- uses: astral-sh/setup-uv@v6
|
|
44
|
+
- name: Tag must match package version
|
|
45
|
+
run: |
|
|
46
|
+
version=$(grep -m1 '^version = ' pyproject.toml | cut -d'"' -f2)
|
|
47
|
+
init_version=$(grep -m1 '^__version__ = ' src/caltrain_api/__init__.py | cut -d'"' -f2)
|
|
48
|
+
echo "tag=$GITHUB_REF_NAME pyproject=$version __init__=$init_version"
|
|
49
|
+
test "$GITHUB_REF_NAME" = "v$version" || { echo "::error::tag $GITHUB_REF_NAME != v$version"; exit 1; }
|
|
50
|
+
test "$version" = "$init_version" || { echo "::error::__version__ $init_version != $version"; exit 1; }
|
|
51
|
+
- run: uv build
|
|
52
|
+
- run: uvx twine check --strict dist/*
|
|
53
|
+
- uses: actions/upload-artifact@v4
|
|
54
|
+
with:
|
|
55
|
+
name: dist
|
|
56
|
+
path: dist/
|
|
57
|
+
if-no-files-found: error
|
|
58
|
+
|
|
59
|
+
publish:
|
|
60
|
+
name: Publish to PyPI
|
|
61
|
+
needs: build
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
environment:
|
|
64
|
+
name: pypi
|
|
65
|
+
url: https://pypi.org/project/caltrain-api/
|
|
66
|
+
permissions:
|
|
67
|
+
id-token: write # required for Trusted Publishing; nothing else gets it
|
|
68
|
+
steps:
|
|
69
|
+
- uses: actions/download-artifact@v4
|
|
70
|
+
with:
|
|
71
|
+
name: dist
|
|
72
|
+
path: dist/
|
|
73
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Read the Docs configuration: https://docs.readthedocs.io/en/stable/config-file/v2.html
|
|
2
|
+
# Published at https://caltrain-api.readthedocs.io/
|
|
3
|
+
# "latest" follows master; "stable" and per-version builds follow vX.Y.Z tags.
|
|
4
|
+
version: 2
|
|
5
|
+
|
|
6
|
+
build:
|
|
7
|
+
os: ubuntu-24.04
|
|
8
|
+
tools:
|
|
9
|
+
python: "3.13"
|
|
10
|
+
|
|
11
|
+
sphinx:
|
|
12
|
+
configuration: docs/conf.py
|
|
13
|
+
# Same strictness as local builds: nitpicky is set in conf.py, so any
|
|
14
|
+
# warning (including a broken cross-reference) fails the build.
|
|
15
|
+
fail_on_warning: true
|
|
16
|
+
|
|
17
|
+
python:
|
|
18
|
+
install:
|
|
19
|
+
- method: pip
|
|
20
|
+
path: .
|
|
21
|
+
extra_requirements:
|
|
22
|
+
- docs
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: caltrain-api
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.1.0
|
|
4
4
|
Summary: Caltrain schedules and train positions (lat/lon, milepost, and plain-English) from GTFS and 511 real-time data.
|
|
5
5
|
Project-URL: Homepage, https://github.com/jsinnott/caltrain-api
|
|
6
6
|
Project-URL: Repository, https://github.com/jsinnott/caltrain-api
|
|
7
7
|
Project-URL: Issues, https://github.com/jsinnott/caltrain-api/issues
|
|
8
|
+
Project-URL: Documentation, https://caltrain-api.readthedocs.io/
|
|
8
9
|
Project-URL: Changelog, https://github.com/jsinnott/caltrain-api#changelog
|
|
9
10
|
Author-email: Joe Sinnott <joe@jsinnott.net>
|
|
10
11
|
License-Expression: MIT
|
|
@@ -31,12 +32,19 @@ Provides-Extra: dev
|
|
|
31
32
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
32
33
|
Requires-Dist: pytest>=8; extra == 'dev'
|
|
33
34
|
Requires-Dist: types-protobuf; extra == 'dev'
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Requires-Dist: autodoc-pydantic>=2; extra == 'docs'
|
|
37
|
+
Requires-Dist: furo; extra == 'docs'
|
|
38
|
+
Requires-Dist: myst-parser; extra == 'docs'
|
|
39
|
+
Requires-Dist: sphinx<9,>=7; extra == 'docs'
|
|
34
40
|
Description-Content-Type: text/markdown
|
|
35
41
|
|
|
36
42
|
# caltrain-api
|
|
37
43
|
|
|
38
44
|
Caltrain schedules and train positions -- latitude/longitude, milepost from SF 4th & King, and plain-English descriptions ("approaching Redwood City") -- from Caltrain's static GTFS and 511.org real-time data.
|
|
39
45
|
|
|
46
|
+
**Documentation:** https://caltrain-api.readthedocs.io/ (usage guide and full API reference).
|
|
47
|
+
|
|
40
48
|
## Install
|
|
41
49
|
|
|
42
50
|
```bash
|
|
@@ -103,10 +111,18 @@ CALTRAIN_511_API_KEY=... .venv/bin/pytest -s tests/integration # live; ~2 511
|
|
|
103
111
|
.venv/bin/mypy --strict src
|
|
104
112
|
```
|
|
105
113
|
|
|
114
|
+
API docs (Sphinx, published by Read the Docs; warnings fail the build):
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv sync --extra docs
|
|
118
|
+
.venv/bin/sphinx-build -W --keep-going -b html docs docs/_build/html && open docs/_build/html/index.html
|
|
119
|
+
```
|
|
120
|
+
|
|
106
121
|
Unit tests use a pinned GTFS feed and a captured 511 response (2026-11-13 11:45 PT) in `tests/fixtures/`. `tools/one-off-*.py` are survey/measurement scripts for the live data sources.
|
|
107
122
|
|
|
108
123
|
## Changelog
|
|
109
124
|
|
|
125
|
+
- 1.1.0 -- API documentation: Sphinx docs published at https://caltrain-api.readthedocs.io/ (`docs` extra; strict build). `ExpectedCall` is now exported from `caltrain_api` (it was already part of `TrainObservation`). Docstrings added for previously undocumented members. First release published by the GitHub Actions Trusted Publishing workflow.
|
|
110
126
|
- 1.0.1 -- Packaging for PyPI: LICENSE file, project URLs, classifiers, `py.typed` marker (type checkers now use the library's annotations), README links that work on PyPI. No code changes.
|
|
111
127
|
- 1.0.0 -- Stable release: the public API (everything exported from `caltrain_api`) is now covered by semantic versioning. No code changes from 0.2.1.
|
|
112
128
|
- 0.2.1 -- Fix: a train waiting at its origin could be placed miles down the line, because 511 reports its equipment (often finishing a previous run) before departure. Reports no longer constrain estimates before departure, and WAITING/ARRIVED apply only near the origin/destination.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Caltrain schedules and train positions -- latitude/longitude, milepost from SF 4th & King, and plain-English descriptions ("approaching Redwood City") -- from Caltrain's static GTFS and 511.org real-time data.
|
|
4
4
|
|
|
5
|
+
**Documentation:** https://caltrain-api.readthedocs.io/ (usage guide and full API reference).
|
|
6
|
+
|
|
5
7
|
## Install
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -68,10 +70,18 @@ CALTRAIN_511_API_KEY=... .venv/bin/pytest -s tests/integration # live; ~2 511
|
|
|
68
70
|
.venv/bin/mypy --strict src
|
|
69
71
|
```
|
|
70
72
|
|
|
73
|
+
API docs (Sphinx, published by Read the Docs; warnings fail the build):
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
uv sync --extra docs
|
|
77
|
+
.venv/bin/sphinx-build -W --keep-going -b html docs docs/_build/html && open docs/_build/html/index.html
|
|
78
|
+
```
|
|
79
|
+
|
|
71
80
|
Unit tests use a pinned GTFS feed and a captured 511 response (2026-11-13 11:45 PT) in `tests/fixtures/`. `tools/one-off-*.py` are survey/measurement scripts for the live data sources.
|
|
72
81
|
|
|
73
82
|
## Changelog
|
|
74
83
|
|
|
84
|
+
- 1.1.0 -- API documentation: Sphinx docs published at https://caltrain-api.readthedocs.io/ (`docs` extra; strict build). `ExpectedCall` is now exported from `caltrain_api` (it was already part of `TrainObservation`). Docstrings added for previously undocumented members. First release published by the GitHub Actions Trusted Publishing workflow.
|
|
75
85
|
- 1.0.1 -- Packaging for PyPI: LICENSE file, project URLs, classifiers, `py.typed` marker (type checkers now use the library's annotations), README links that work on PyPI. No code changes.
|
|
76
86
|
- 1.0.0 -- Stable release: the public API (everything exported from `caltrain_api`) is now covered by semantic versioning. No code changes from 0.2.1.
|
|
77
87
|
- 0.2.1 -- Fix: a train waiting at its origin could be placed miles down the line, because 511 reports its equipment (often finishing a previous run) before departure. Reports no longer constrain estimates before departure, and WAITING/ARRIVED apply only near the origin/destination.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
API reference
|
|
2
|
+
=============
|
|
3
|
+
|
|
4
|
+
Everything here is importable directly from ``caltrain_api`` and is covered by
|
|
5
|
+
semantic versioning.
|
|
6
|
+
|
|
7
|
+
.. currentmodule:: caltrain_api
|
|
8
|
+
|
|
9
|
+
Facade
|
|
10
|
+
------
|
|
11
|
+
|
|
12
|
+
Most applications need only :class:`CaltrainAPI`: it combines the timetable
|
|
13
|
+
with real-time data and returns immutable snapshots.
|
|
14
|
+
|
|
15
|
+
.. autoclass:: CaltrainAPI
|
|
16
|
+
|
|
17
|
+
Trains and positions
|
|
18
|
+
--------------------
|
|
19
|
+
|
|
20
|
+
.. autopydantic_model:: Train
|
|
21
|
+
.. autopydantic_model:: TrainPosition
|
|
22
|
+
.. autopydantic_model:: Stop
|
|
23
|
+
.. autopydantic_model:: Departure
|
|
24
|
+
.. autopydantic_model:: Journey
|
|
25
|
+
.. autopydantic_model:: Alert
|
|
26
|
+
|
|
27
|
+
Stations and track
|
|
28
|
+
------------------
|
|
29
|
+
|
|
30
|
+
.. autopydantic_model:: Station
|
|
31
|
+
.. autopydantic_model:: TrackLocation
|
|
32
|
+
.. autoclass:: Corridor
|
|
33
|
+
|
|
34
|
+
Enumerations
|
|
35
|
+
------------
|
|
36
|
+
|
|
37
|
+
.. autoclass:: Direction
|
|
38
|
+
:members:
|
|
39
|
+
:undoc-members:
|
|
40
|
+
.. autoclass:: ServiceType
|
|
41
|
+
:members:
|
|
42
|
+
:undoc-members:
|
|
43
|
+
.. autoclass:: PositionSource
|
|
44
|
+
:members:
|
|
45
|
+
:undoc-members:
|
|
46
|
+
.. autoclass:: Relation
|
|
47
|
+
:members:
|
|
48
|
+
:undoc-members:
|
|
49
|
+
.. autoclass:: TrainStatus
|
|
50
|
+
:members:
|
|
51
|
+
:undoc-members:
|
|
52
|
+
|
|
53
|
+
Timetable
|
|
54
|
+
---------
|
|
55
|
+
|
|
56
|
+
:class:`Schedule` works without an API key or (after the first download)
|
|
57
|
+
network access. :class:`CaltrainAPI` builds on it; use it directly for
|
|
58
|
+
timetable-only applications.
|
|
59
|
+
|
|
60
|
+
.. autoclass:: Schedule
|
|
61
|
+
|
|
62
|
+
Descriptions
|
|
63
|
+
------------
|
|
64
|
+
|
|
65
|
+
.. autopydantic_model:: DescriptionPolicy
|
|
66
|
+
|
|
67
|
+
Real-time sources
|
|
68
|
+
-----------------
|
|
69
|
+
|
|
70
|
+
The default source is :class:`SiriRealtimeSource`. Implement
|
|
71
|
+
:class:`RealtimeSource` to plug in another feed.
|
|
72
|
+
|
|
73
|
+
.. autoclass:: RealtimeSource
|
|
74
|
+
.. autoclass:: SiriRealtimeSource
|
|
75
|
+
.. autopydantic_model:: RealtimeSnapshot
|
|
76
|
+
.. autopydantic_model:: TrainObservation
|
|
77
|
+
.. autopydantic_model:: ExpectedCall
|
|
78
|
+
|
|
79
|
+
Errors
|
|
80
|
+
------
|
|
81
|
+
|
|
82
|
+
.. autoexception:: CaltrainError
|
|
83
|
+
.. autoexception:: ScheduleUnavailableError
|
|
84
|
+
.. autoexception:: RealtimeUnavailableError
|
|
85
|
+
.. autoexception:: RateLimitedError
|
|
86
|
+
.. autoexception:: UnknownStationError
|
|
87
|
+
.. autoexception:: UnknownTrainError
|
|
88
|
+
.. autoexception:: OffCorridorError
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Sphinx configuration for caltrain-api.
|
|
2
|
+
|
|
3
|
+
Published by Read the Docs (see .readthedocs.yaml). Build locally with the
|
|
4
|
+
same strictness -- any warning, including a broken cross-reference, fails:
|
|
5
|
+
|
|
6
|
+
uv sync --extra docs
|
|
7
|
+
.venv/bin/sphinx-build -W --keep-going -b html docs docs/_build/html
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from importlib.metadata import version as _version
|
|
11
|
+
|
|
12
|
+
project = "caltrain-api"
|
|
13
|
+
author = "Joe Sinnott"
|
|
14
|
+
copyright = "2026, Joe Sinnott"
|
|
15
|
+
release = _version("caltrain-api")
|
|
16
|
+
version = release
|
|
17
|
+
|
|
18
|
+
extensions = [
|
|
19
|
+
"sphinx.ext.autodoc",
|
|
20
|
+
"sphinx.ext.napoleon", # Google-style Args:/Returns:/Raises: sections
|
|
21
|
+
"sphinx.ext.intersphinx", # links to Python / pydantic / httpx docs
|
|
22
|
+
"sphinx.ext.viewcode", # "[source]" links
|
|
23
|
+
"myst_parser", # Markdown pages (README, data findings)
|
|
24
|
+
"sphinxcontrib.autodoc_pydantic",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
exclude_patterns = ["_build"]
|
|
28
|
+
nitpicky = True # warn on every unresolvable reference (local and Read the Docs alike)
|
|
29
|
+
source_suffix = {".rst": "restructuredtext", ".md": "markdown"}
|
|
30
|
+
|
|
31
|
+
# -- autodoc -------------------------------------------------------------------
|
|
32
|
+
autodoc_member_order = "bysource"
|
|
33
|
+
autodoc_typehints = "signature"
|
|
34
|
+
autodoc_class_signature = "separated"
|
|
35
|
+
autodoc_default_options = {"members": True, "show-inheritance": True}
|
|
36
|
+
napoleon_google_docstring = True
|
|
37
|
+
napoleon_numpy_docstring = False
|
|
38
|
+
napoleon_use_rtype = False
|
|
39
|
+
|
|
40
|
+
# Pydantic models: list fields with their descriptions; hide JSON schema,
|
|
41
|
+
# config and validator noise that callers don't need.
|
|
42
|
+
autodoc_pydantic_model_show_json = False
|
|
43
|
+
autodoc_pydantic_model_show_config_summary = False
|
|
44
|
+
autodoc_pydantic_model_show_validator_summary = False
|
|
45
|
+
autodoc_pydantic_model_show_validator_members = False
|
|
46
|
+
autodoc_pydantic_model_show_field_summary = False
|
|
47
|
+
autodoc_pydantic_model_member_order = "bysource"
|
|
48
|
+
autodoc_pydantic_field_list_validators = False
|
|
49
|
+
autodoc_pydantic_field_show_constraints = True
|
|
50
|
+
|
|
51
|
+
intersphinx_mapping = {
|
|
52
|
+
"python": ("https://docs.python.org/3", None),
|
|
53
|
+
"pydantic": ("https://docs.pydantic.dev/latest", None),
|
|
54
|
+
# httpx's docs are MkDocs, with no Sphinx inventory to link against.
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Names in rendered type annotations that have no link target. They still
|
|
58
|
+
# display; this just stops the strict (-n) build from failing on them.
|
|
59
|
+
nitpick_ignore = [
|
|
60
|
+
("py:class", "httpx.Client"),
|
|
61
|
+
("py:class", "AwareDatetime"), # pydantic.AwareDatetime, rendered unqualified
|
|
62
|
+
("py:class", "date"), # datetime.date, rendered unqualified
|
|
63
|
+
("py:class", "timedelta"), # datetime.timedelta, rendered unqualified
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _hide_private_bases(app, name, obj, options, bases):
|
|
68
|
+
"""Show the internal frozen-model base class as pydantic.BaseModel."""
|
|
69
|
+
from pydantic import BaseModel
|
|
70
|
+
|
|
71
|
+
bases[:] = [BaseModel if b.__name__ == "_Frozen" else b for b in bases]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def setup(app):
|
|
75
|
+
app.connect("autodoc-process-bases", _hide_private_bases)
|
|
76
|
+
|
|
77
|
+
# -- MyST ----------------------------------------------------------------------
|
|
78
|
+
myst_heading_anchors = 3
|
|
79
|
+
|
|
80
|
+
# -- HTML ----------------------------------------------------------------------
|
|
81
|
+
html_theme = "furo"
|
|
82
|
+
html_title = f"caltrain-api {release}"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# caltrain-api
|
|
2
|
+
|
|
3
|
+
Caltrain schedules and train positions for Python: where every train is (latitude/longitude, milepost from San Francisco 4th & King, and plain English like "approaching Redwood City"), where it should be, departures, point-to-point timetables, delays and service alerts -- from Caltrain's static GTFS feed and 511.org real-time data.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install caltrain-api
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from caltrain_api import CaltrainAPI
|
|
11
|
+
|
|
12
|
+
api = CaltrainAPI(api_key="...") # a free 511.org key; omit for timetable-only
|
|
13
|
+
for train in api.get_active_trains():
|
|
14
|
+
print(train.number, train.position.milepost, train.position.description)
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Start with the {doc}`overview` (usage, positions, data handling), then the {doc}`api` for every class and method. {doc}`data-findings` records what the data sources actually provide and how that shaped the design.
|
|
18
|
+
|
|
19
|
+
```{toctree}
|
|
20
|
+
:maxdepth: 2
|
|
21
|
+
:hidden:
|
|
22
|
+
|
|
23
|
+
overview
|
|
24
|
+
api
|
|
25
|
+
data-findings
|
|
26
|
+
```
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "caltrain-api"
|
|
7
|
-
version = "1.0
|
|
7
|
+
version = "1.1.0"
|
|
8
8
|
description = "Caltrain schedules and train positions (lat/lon, milepost, and plain-English) from GTFS and 511 real-time data."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -37,6 +37,7 @@ dependencies = [
|
|
|
37
37
|
Homepage = "https://github.com/jsinnott/caltrain-api"
|
|
38
38
|
Repository = "https://github.com/jsinnott/caltrain-api"
|
|
39
39
|
Issues = "https://github.com/jsinnott/caltrain-api/issues"
|
|
40
|
+
Documentation = "https://caltrain-api.readthedocs.io/"
|
|
40
41
|
Changelog = "https://github.com/jsinnott/caltrain-api#changelog"
|
|
41
42
|
|
|
42
43
|
[project.optional-dependencies]
|
|
@@ -45,6 +46,12 @@ dev = [
|
|
|
45
46
|
"mypy>=1.10",
|
|
46
47
|
"types-protobuf",
|
|
47
48
|
]
|
|
49
|
+
docs = [
|
|
50
|
+
"sphinx>=7,<9", # autodoc-pydantic 2.2 drops model properties/methods under Sphinx 9
|
|
51
|
+
"furo",
|
|
52
|
+
"myst-parser",
|
|
53
|
+
"autodoc-pydantic>=2",
|
|
54
|
+
]
|
|
48
55
|
|
|
49
56
|
[tool.hatch.build.targets.wheel]
|
|
50
57
|
packages = ["src/caltrain_api"]
|
|
@@ -4,7 +4,7 @@ Public API -- everything importable from ``caltrain_api`` directly is covered
|
|
|
4
4
|
by semantic versioning; submodule internals are not.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "1.0
|
|
7
|
+
__version__ = "1.1.0"
|
|
8
8
|
|
|
9
9
|
from .api import CaltrainAPI
|
|
10
10
|
from .corridor import Corridor
|
|
@@ -33,7 +33,13 @@ from .models import (
|
|
|
33
33
|
TrainPosition,
|
|
34
34
|
TrainStatus,
|
|
35
35
|
)
|
|
36
|
-
from .realtime import
|
|
36
|
+
from .realtime import (
|
|
37
|
+
ExpectedCall,
|
|
38
|
+
RealtimeSnapshot,
|
|
39
|
+
RealtimeSource,
|
|
40
|
+
SiriRealtimeSource,
|
|
41
|
+
TrainObservation,
|
|
42
|
+
)
|
|
37
43
|
from .schedule import Schedule
|
|
38
44
|
|
|
39
45
|
__all__ = [
|
|
@@ -45,6 +51,7 @@ __all__ = [
|
|
|
45
51
|
"Journey",
|
|
46
52
|
"DescriptionPolicy",
|
|
47
53
|
"Direction",
|
|
54
|
+
"ExpectedCall",
|
|
48
55
|
"OffCorridorError",
|
|
49
56
|
"PositionSource",
|
|
50
57
|
"RateLimitedError",
|
|
@@ -92,7 +92,7 @@ class _Polyline:
|
|
|
92
92
|
class Corridor:
|
|
93
93
|
"""The Caltrain line as a single measured polyline.
|
|
94
94
|
|
|
95
|
-
Obtain one from :attr
|
|
95
|
+
Obtain one from :attr:`~caltrain_api.Schedule.corridor`; constructing directly is for tests.
|
|
96
96
|
Immutable and safe to share between threads.
|
|
97
97
|
"""
|
|
98
98
|
|
|
@@ -151,12 +151,12 @@ class Stop(_Frozen):
|
|
|
151
151
|
|
|
152
152
|
@property
|
|
153
153
|
def arrival(self) -> datetime:
|
|
154
|
-
"""
|
|
154
|
+
"""The best available arrival time (expected if known, else scheduled)."""
|
|
155
155
|
return self.expected_arrival or self.scheduled_arrival
|
|
156
156
|
|
|
157
157
|
@property
|
|
158
158
|
def departure(self) -> datetime:
|
|
159
|
-
"""
|
|
159
|
+
"""The best available departure time (expected if known, else scheduled)."""
|
|
160
160
|
return self.expected_departure or self.scheduled_departure
|
|
161
161
|
|
|
162
162
|
@property
|
|
@@ -173,7 +173,7 @@ class TrainPosition(TrackLocation):
|
|
|
173
173
|
A ``TrackLocation`` plus when, how we know, and how it relates to stations.
|
|
174
174
|
``station``/``previous_station``/``next_station`` refer to *physical*
|
|
175
175
|
neighbors along the track, including stations the train does not stop at;
|
|
176
|
-
use :attr
|
|
176
|
+
use :attr:`~caltrain_api.Train.next_stop` for the next station it will serve.
|
|
177
177
|
"""
|
|
178
178
|
|
|
179
179
|
source: PositionSource
|
|
@@ -263,10 +263,12 @@ class Train(_Frozen):
|
|
|
263
263
|
|
|
264
264
|
@property
|
|
265
265
|
def origin(self) -> Station:
|
|
266
|
+
"""The station the trip starts from."""
|
|
266
267
|
return self.stops[0].station
|
|
267
268
|
|
|
268
269
|
@property
|
|
269
270
|
def destination(self) -> Station:
|
|
271
|
+
"""The station the trip ends at."""
|
|
270
272
|
return self.stops[-1].station
|
|
271
273
|
|
|
272
274
|
@property
|
|
@@ -315,6 +317,7 @@ class Journey(_Frozen):
|
|
|
315
317
|
|
|
316
318
|
@property
|
|
317
319
|
def scheduled_duration(self) -> timedelta:
|
|
320
|
+
"""Timetable duration, origin departure to destination arrival."""
|
|
318
321
|
return self.destination_stop.scheduled_arrival - self.origin_stop.scheduled_departure
|
|
319
322
|
|
|
320
323
|
@property
|
|
@@ -290,6 +290,7 @@ class SiriRealtimeSource:
|
|
|
290
290
|
return resp.content
|
|
291
291
|
|
|
292
292
|
def fetch(self) -> RealtimeSnapshot:
|
|
293
|
+
"""Fetch train data (and alerts, if due) from 511. See :meth:`RealtimeSource.fetch`."""
|
|
293
294
|
now = self._clock()
|
|
294
295
|
raw = self._get(self.VEHICLE_MONITORING_URL, "VehicleMonitoring", format="json")
|
|
295
296
|
try:
|
|
@@ -451,14 +451,17 @@ class Schedule:
|
|
|
451
451
|
|
|
452
452
|
@property
|
|
453
453
|
def feed_version(self) -> str:
|
|
454
|
+
"""The feed's ``feed_version`` from ``feed_info.txt`` (or ``"unknown"``)."""
|
|
454
455
|
return self._feed_version
|
|
455
456
|
|
|
456
457
|
@property
|
|
457
458
|
def valid_from(self) -> date:
|
|
459
|
+
"""First service date the feed covers (``feed_start_date``, else the calendar's earliest)."""
|
|
458
460
|
return self._valid_from
|
|
459
461
|
|
|
460
462
|
@property
|
|
461
463
|
def valid_until(self) -> date:
|
|
464
|
+
"""Last service date the feed covers; past it, :meth:`load` re-downloads."""
|
|
462
465
|
return self._valid_until
|
|
463
466
|
|
|
464
467
|
@property
|
|
@@ -468,10 +471,12 @@ class Schedule:
|
|
|
468
471
|
|
|
469
472
|
@property
|
|
470
473
|
def corridor(self) -> Corridor:
|
|
474
|
+
"""The measured SF -> Gilroy track used for all mileposts."""
|
|
471
475
|
return self._corridor
|
|
472
476
|
|
|
473
477
|
@property
|
|
474
478
|
def description_policy(self) -> DescriptionPolicy:
|
|
479
|
+
"""Thresholds and wording used for position descriptions."""
|
|
475
480
|
return self._policy
|
|
476
481
|
|
|
477
482
|
# -- stations -----------------------------------------------------------
|
|
@@ -690,7 +695,7 @@ class Schedule:
|
|
|
690
695
|
The relation is WAITING before the origin departure (by best-available
|
|
691
696
|
times) and ARRIVED from the destination arrival on -- in both cases
|
|
692
697
|
only within ``near_radius`` of that station -- and otherwise
|
|
693
|
-
classified by :attr
|
|
698
|
+
classified by :attr:`~caltrain_api.Schedule.description_policy`.
|
|
694
699
|
|
|
695
700
|
Args:
|
|
696
701
|
stops: The train's stops (determine AT vs PASSING and WAITING/ARRIVED).
|