python-openpublictransport 0.1.3__tar.gz → 0.1.5__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.
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/PKG-INFO +1 -1
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/pyproject.toml +4 -1
- python_openpublictransport-0.1.5/src/openpublictransport/__init__.py +12 -0
- python_openpublictransport-0.1.5/src/openpublictransport/exceptions.py +9 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/national_rail.py +5 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/nta.py +7 -4
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/otp_base.py +6 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/rejseplanen.py +5 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/rmv.py +12 -2
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/trafiklab.py +10 -6
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/trias_base.py +6 -0
- python_openpublictransport-0.1.5/src/openpublictransport/py.typed +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/python_openpublictransport.egg-info/PKG-INFO +1 -1
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/python_openpublictransport.egg-info/SOURCES.txt +2 -0
- python_openpublictransport-0.1.3/src/openpublictransport/__init__.py +0 -5
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/setup.cfg +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/const.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/models.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/parsers.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/__init__.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/avv.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/base.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/beg.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/bsvg.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/bvg.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/db.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/ding.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/efa_base.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/fptf_base.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/gtfsde.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/hvv.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/kvv.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/mvv.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/nvbw.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/nwl.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/oebb.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/otp.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/otp_custom.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/rvv.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/sbb.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/transitous.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/vagfr.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/vbn.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/vgn.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/vrn.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/vrr.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/vvo.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/openpublictransport/providers/vvs.py +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/python_openpublictransport.egg-info/dependency_links.txt +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/python_openpublictransport.egg-info/requires.txt +0 -0
- {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.5}/src/python_openpublictransport.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-openpublictransport"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.5"
|
|
8
8
|
description = "Python library for public transport APIs (EFA, OTP2, TRIAS, FPTF, HAFAS, GTFS-RT)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -25,5 +25,8 @@ dev = [
|
|
|
25
25
|
[tool.setuptools.packages.find]
|
|
26
26
|
where = ["src"]
|
|
27
27
|
|
|
28
|
+
[tool.setuptools.package-data]
|
|
29
|
+
openpublictransport = ["py.typed"]
|
|
30
|
+
|
|
28
31
|
[tool.pytest.ini_options]
|
|
29
32
|
asyncio_mode = "auto"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""python-openpublictransport — public transport API library."""
|
|
2
|
+
|
|
3
|
+
from .exceptions import AuthenticationError
|
|
4
|
+
from .providers import get_all_provider_ids, get_provider, get_provider_class, register_provider
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"get_provider",
|
|
8
|
+
"get_provider_class",
|
|
9
|
+
"get_all_provider_ids",
|
|
10
|
+
"register_provider",
|
|
11
|
+
"AuthenticationError",
|
|
12
|
+
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Exceptions for python-openpublictransport."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AuthenticationError(Exception):
|
|
5
|
+
"""Raised when API authentication fails (HTTP 401 or 403).
|
|
6
|
+
|
|
7
|
+
Signals that the configured API key is invalid or expired.
|
|
8
|
+
Callers should prompt the user to re-enter credentials rather than retrying.
|
|
9
|
+
"""
|
|
@@ -14,6 +14,7 @@ from zoneinfo import ZoneInfo
|
|
|
14
14
|
import aiohttp
|
|
15
15
|
|
|
16
16
|
from ..const import PROVIDER_NATIONAL_RAIL
|
|
17
|
+
from ..exceptions import AuthenticationError
|
|
17
18
|
from ..models import UnifiedDeparture
|
|
18
19
|
from .base import BaseProvider
|
|
19
20
|
|
|
@@ -118,6 +119,10 @@ class NationalRailProvider(BaseProvider):
|
|
|
118
119
|
},
|
|
119
120
|
timeout=aiohttp.ClientTimeout(total=15),
|
|
120
121
|
) as resp:
|
|
122
|
+
if resp.status in (401, 403):
|
|
123
|
+
raise AuthenticationError(
|
|
124
|
+
f"{self.provider_name}: authentication failed (HTTP {resp.status}) — check API key"
|
|
125
|
+
)
|
|
121
126
|
if resp.status != 200:
|
|
122
127
|
_LOGGER.warning("%s: HTTP %s for CRS %s", self.provider_name, resp.status, crs)
|
|
123
128
|
return None
|
|
@@ -6,6 +6,8 @@ from datetime import datetime, timedelta, timezone
|
|
|
6
6
|
from typing import Any, Dict, List, Optional, Union
|
|
7
7
|
from zoneinfo import ZoneInfo
|
|
8
8
|
|
|
9
|
+
from ..exceptions import AuthenticationError
|
|
10
|
+
|
|
9
11
|
import aiohttp
|
|
10
12
|
from aiohttp import ClientConnectorError
|
|
11
13
|
|
|
@@ -217,13 +219,14 @@ class NTAProvider(BaseProvider):
|
|
|
217
219
|
elif response.status == 404:
|
|
218
220
|
_LOGGER.warning("NTA API endpoint not found (404)")
|
|
219
221
|
return None
|
|
220
|
-
elif response.status
|
|
222
|
+
elif response.status in (401, 403):
|
|
221
223
|
if self.api_key_secondary and current_api_key == self.api_key:
|
|
222
|
-
_LOGGER.info("NTA
|
|
224
|
+
_LOGGER.info("NTA primary key failed (HTTP %s), trying secondary...", response.status)
|
|
223
225
|
current_api_key = self.api_key_secondary
|
|
224
226
|
continue
|
|
225
|
-
|
|
226
|
-
|
|
227
|
+
raise AuthenticationError(
|
|
228
|
+
f"NTA: authentication failed (HTTP {response.status}) — check API key(s)"
|
|
229
|
+
)
|
|
227
230
|
elif response.status >= 500:
|
|
228
231
|
_LOGGER.warning(
|
|
229
232
|
"NTA API server error (status %s) on attempt %d/%d",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
from datetime import datetime, timezone
|
|
6
|
+
|
|
7
|
+
from ..exceptions import AuthenticationError
|
|
6
8
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
7
9
|
from urllib.parse import quote
|
|
8
10
|
from zoneinfo import ZoneInfo
|
|
@@ -86,6 +88,10 @@ class OTPBaseProvider(BaseProvider):
|
|
|
86
88
|
return await resp.json()
|
|
87
89
|
if resp.status == 204:
|
|
88
90
|
return None
|
|
91
|
+
if resp.status in (401, 403):
|
|
92
|
+
raise AuthenticationError(
|
|
93
|
+
f"{self.provider_name}: authentication failed (HTTP {resp.status}) — check API key"
|
|
94
|
+
)
|
|
89
95
|
_LOGGER.warning("%s OTP %s → HTTP %s", self.provider_name, url, resp.status)
|
|
90
96
|
except aiohttp.ClientError as exc:
|
|
91
97
|
_LOGGER.warning("%s OTP request failed: %s", self.provider_name, exc)
|
|
@@ -12,6 +12,7 @@ from zoneinfo import ZoneInfo
|
|
|
12
12
|
import aiohttp
|
|
13
13
|
|
|
14
14
|
from ..const import PROVIDER_REJSEPLANEN
|
|
15
|
+
from ..exceptions import AuthenticationError
|
|
15
16
|
from ..models import UnifiedDeparture
|
|
16
17
|
from .base import BaseProvider
|
|
17
18
|
|
|
@@ -103,6 +104,10 @@ class RejseplanenProvider(BaseProvider):
|
|
|
103
104
|
)
|
|
104
105
|
try:
|
|
105
106
|
async with self.session.get(url, timeout=aiohttp.ClientTimeout(total=15)) as resp:
|
|
107
|
+
if resp.status in (401, 403):
|
|
108
|
+
raise AuthenticationError(
|
|
109
|
+
f"{self.provider_name}: authentication failed (HTTP {resp.status}) — check API key"
|
|
110
|
+
)
|
|
106
111
|
if resp.status != 200:
|
|
107
112
|
_LOGGER.warning("%s: HTTP %s for station %s", self.provider_name, resp.status, station_id)
|
|
108
113
|
return None
|
|
@@ -6,6 +6,8 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
6
6
|
from urllib.parse import quote
|
|
7
7
|
from zoneinfo import ZoneInfo
|
|
8
8
|
|
|
9
|
+
from ..exceptions import AuthenticationError
|
|
10
|
+
|
|
9
11
|
import aiohttp
|
|
10
12
|
|
|
11
13
|
from ..const import PROVIDER_RMV
|
|
@@ -126,8 +128,10 @@ class RMVProvider(BaseProvider):
|
|
|
126
128
|
if isinstance(departures, dict):
|
|
127
129
|
departures = [departures]
|
|
128
130
|
return {"stopEvents": departures}
|
|
129
|
-
elif response.status
|
|
130
|
-
|
|
131
|
+
elif response.status in (401, 403):
|
|
132
|
+
raise AuthenticationError(
|
|
133
|
+
f"RMV: authentication failed (HTTP {response.status}) — check API key"
|
|
134
|
+
)
|
|
131
135
|
else:
|
|
132
136
|
_LOGGER.warning("RMV API returned status %s", response.status)
|
|
133
137
|
except aiohttp.ClientError as e:
|
|
@@ -264,8 +268,14 @@ class RMVProvider(BaseProvider):
|
|
|
264
268
|
}
|
|
265
269
|
)
|
|
266
270
|
return results
|
|
271
|
+
elif response.status in (401, 403):
|
|
272
|
+
raise AuthenticationError(
|
|
273
|
+
f"RMV: authentication failed (HTTP {response.status}) — check API key"
|
|
274
|
+
)
|
|
267
275
|
else:
|
|
268
276
|
_LOGGER.error("RMV API returned status %s", response.status)
|
|
277
|
+
except AuthenticationError:
|
|
278
|
+
raise
|
|
269
279
|
except Exception as e:
|
|
270
280
|
_LOGGER.error("Error searching RMV stops: %s", e)
|
|
271
281
|
|
|
@@ -7,6 +7,8 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
7
7
|
from urllib.parse import quote
|
|
8
8
|
from zoneinfo import ZoneInfo
|
|
9
9
|
|
|
10
|
+
from ..exceptions import AuthenticationError
|
|
11
|
+
|
|
10
12
|
import aiohttp
|
|
11
13
|
from aiohttp import ClientConnectorError
|
|
12
14
|
|
|
@@ -131,9 +133,10 @@ class TrafiklabProvider(BaseProvider):
|
|
|
131
133
|
elif response.status == 404:
|
|
132
134
|
_LOGGER.warning("Trafiklab API endpoint not found (404)")
|
|
133
135
|
return None
|
|
134
|
-
elif response.status
|
|
135
|
-
|
|
136
|
-
|
|
136
|
+
elif response.status in (401, 403):
|
|
137
|
+
raise AuthenticationError(
|
|
138
|
+
f"Trafiklab: authentication failed (HTTP {response.status}) — check API key"
|
|
139
|
+
)
|
|
137
140
|
elif response.status >= 500:
|
|
138
141
|
_LOGGER.warning(
|
|
139
142
|
"Trafiklab API server error (status %s) on attempt %d/%d",
|
|
@@ -243,9 +246,10 @@ class TrafiklabProvider(BaseProvider):
|
|
|
243
246
|
results.append(result)
|
|
244
247
|
|
|
245
248
|
return results
|
|
246
|
-
elif response.status
|
|
247
|
-
|
|
248
|
-
|
|
249
|
+
elif response.status in (401, 403):
|
|
250
|
+
raise AuthenticationError(
|
|
251
|
+
f"Trafiklab: authentication failed (HTTP {response.status}) — check API key"
|
|
252
|
+
)
|
|
249
253
|
elif response.status == 404:
|
|
250
254
|
_LOGGER.warning("Trafiklab API endpoint not found (404)")
|
|
251
255
|
return []
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from typing import Any, Dict, List, Optional, Union
|
|
6
|
+
|
|
7
|
+
from ..exceptions import AuthenticationError
|
|
6
8
|
from xml.etree import ElementTree as ET
|
|
7
9
|
from zoneinfo import ZoneInfo
|
|
8
10
|
|
|
@@ -140,6 +142,10 @@ class TRIASBaseProvider(BaseProvider):
|
|
|
140
142
|
if response.status == 200:
|
|
141
143
|
text = await response.text()
|
|
142
144
|
return ET.fromstring(text)
|
|
145
|
+
elif response.status in (401, 403):
|
|
146
|
+
raise AuthenticationError(
|
|
147
|
+
f"{self.provider_name}: authentication failed (HTTP {response.status}) — check API key"
|
|
148
|
+
)
|
|
143
149
|
else:
|
|
144
150
|
_LOGGER.warning("%s TRIAS API returned status %s", self.provider_name, response.status)
|
|
145
151
|
except aiohttp.ClientError as e:
|
|
File without changes
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
pyproject.toml
|
|
2
2
|
src/openpublictransport/__init__.py
|
|
3
3
|
src/openpublictransport/const.py
|
|
4
|
+
src/openpublictransport/exceptions.py
|
|
4
5
|
src/openpublictransport/models.py
|
|
5
6
|
src/openpublictransport/parsers.py
|
|
7
|
+
src/openpublictransport/py.typed
|
|
6
8
|
src/openpublictransport/providers/__init__.py
|
|
7
9
|
src/openpublictransport/providers/avv.py
|
|
8
10
|
src/openpublictransport/providers/base.py
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|