python-openpublictransport 0.1.3__tar.gz → 0.1.4__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.
Files changed (50) hide show
  1. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/PKG-INFO +1 -1
  2. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/pyproject.toml +1 -1
  3. python_openpublictransport-0.1.4/src/openpublictransport/__init__.py +12 -0
  4. python_openpublictransport-0.1.4/src/openpublictransport/exceptions.py +9 -0
  5. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/national_rail.py +5 -0
  6. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/nta.py +7 -4
  7. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/otp_base.py +6 -0
  8. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/rejseplanen.py +5 -0
  9. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/rmv.py +12 -2
  10. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/trafiklab.py +10 -6
  11. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/trias_base.py +6 -0
  12. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/python_openpublictransport.egg-info/PKG-INFO +1 -1
  13. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/python_openpublictransport.egg-info/SOURCES.txt +1 -0
  14. python_openpublictransport-0.1.3/src/openpublictransport/__init__.py +0 -5
  15. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/setup.cfg +0 -0
  16. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/const.py +0 -0
  17. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/models.py +0 -0
  18. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/parsers.py +0 -0
  19. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/__init__.py +0 -0
  20. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/avv.py +0 -0
  21. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/base.py +0 -0
  22. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/beg.py +0 -0
  23. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/bsvg.py +0 -0
  24. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/bvg.py +0 -0
  25. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/db.py +0 -0
  26. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/ding.py +0 -0
  27. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/efa_base.py +0 -0
  28. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/fptf_base.py +0 -0
  29. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/gtfsde.py +0 -0
  30. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/hvv.py +0 -0
  31. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/kvv.py +0 -0
  32. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/mvv.py +0 -0
  33. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/nvbw.py +0 -0
  34. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/nwl.py +0 -0
  35. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/oebb.py +0 -0
  36. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/otp.py +0 -0
  37. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/otp_custom.py +0 -0
  38. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/rvv.py +0 -0
  39. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/sbb.py +0 -0
  40. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/transitous.py +0 -0
  41. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/vagfr.py +0 -0
  42. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/vbn.py +0 -0
  43. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/vgn.py +0 -0
  44. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/vrn.py +0 -0
  45. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/vrr.py +0 -0
  46. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/vvo.py +0 -0
  47. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/openpublictransport/providers/vvs.py +0 -0
  48. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/python_openpublictransport.egg-info/dependency_links.txt +0 -0
  49. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/python_openpublictransport.egg-info/requires.txt +0 -0
  50. {python_openpublictransport-0.1.3 → python_openpublictransport-0.1.4}/src/python_openpublictransport.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-openpublictransport
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Python library for public transport APIs (EFA, OTP2, TRIAS, FPTF, HAFAS, GTFS-RT)
5
5
  License: MIT
6
6
  Requires-Python: >=3.11
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-openpublictransport"
7
- version = "0.1.3"
7
+ version = "0.1.4"
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" }
@@ -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 == 401:
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 Primary API key failed (401), trying Secondary key...")
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
- _LOGGER.warning("NTA API authentication failed (401) - check API key(s)")
226
- return None
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 == 401:
130
- _LOGGER.error("RMV API: invalid API key")
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 == 401:
135
- _LOGGER.warning("Trafiklab API authentication failed (401) - check API key")
136
- return None
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 == 401:
247
- _LOGGER.error("Trafiklab API authentication failed (401) - check API key")
248
- return []
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-openpublictransport
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: Python library for public transport APIs (EFA, OTP2, TRIAS, FPTF, HAFAS, GTFS-RT)
5
5
  License: MIT
6
6
  Requires-Python: >=3.11
@@ -1,6 +1,7 @@
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
6
7
  src/openpublictransport/providers/__init__.py
@@ -1,5 +0,0 @@
1
- """python-openpublictransport — public transport API library."""
2
-
3
- from .providers import get_all_provider_ids, get_provider, get_provider_class, register_provider
4
-
5
- __all__ = ["get_provider", "get_provider_class", "get_all_provider_ids", "register_provider"]