diff --git a/.gitea/workflows/tests.yml b/.gitea/workflows/tests.yml index f8daeed..863f122 100644 --- a/.gitea/workflows/tests.yml +++ b/.gitea/workflows/tests.yml @@ -15,7 +15,7 @@ jobs: container: catthehacker/ubuntu:act-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v3 diff --git a/README.md b/README.md index a8910dd..f428978 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Small module that makes your journey with RMV REST API somehow easier. Based ful ## Requirements * RMV API key (Get it [here](https://opendata.rmv.de/site/start.html)) -* Python 3.8+ +* Python 3.9+ * git (Only for installation from source) ## Installation diff --git a/pyproject.toml b/pyproject.toml index ac2b3b5..c1d16bf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ authors = [{ name = "Profitroll", email = "profitroll@end-play.xyz" }] maintainers = [{ name = "Profitroll", email = "profitroll@end-play.xyz" }] description = "Small module that makes your journey with RMV REST API somehow easier." readme = "README.md" -requires-python = ">=3.8" +requires-python = ">=3.9" license = { text = "MIT" } classifiers = [ "Development Status :: 3 - Alpha", @@ -17,7 +17,6 @@ classifiers = [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -46,7 +45,7 @@ speed = { file = "requirements/speed.txt" } where = ["src"] [tool.black] -target-version = ['py38', 'py39', 'py310', 'py311', 'py312'] +target-version = ['py39', 'py310', 'py311', 'py312'] line-length = 94 [tool.isort] @@ -66,7 +65,7 @@ show_error_codes = true [tool.pylint.main] extension-pkg-whitelist = ["ujson"] -py-version = 3.8 +py-version = 3.9 [tool.coverage.run] source = ["pyrmv"] diff --git a/requirements/_.txt b/requirements/_.txt index 317f56d..dc66d84 100644 --- a/requirements/_.txt +++ b/requirements/_.txt @@ -1,3 +1,3 @@ requests~=2.32.3 -xmltodict~=0.13.0 -isodate~=0.6.1 \ No newline at end of file +xmltodict~=0.14.0 +isodate~=0.7.0 \ No newline at end of file diff --git a/requirements/dev.txt b/requirements/dev.txt index 5cafd9f..54babc4 100644 --- a/requirements/dev.txt +++ b/requirements/dev.txt @@ -1,10 +1,10 @@ -black~=24.8.0 +black~=25.1.0 isort==5.13.2 -mypy~=1.11.2 -pylint==3.2.7 -pytest-cov~=5.0.0 +mypy~=1.15.0 +pylint==3.3.7,<3.4.0 +pytest-cov~=6.1.0 pytest~=8.3.2 -tox==4.18.1 +tox==4.25.0 types-ujson~=5.10.0.20240515 # Disabled async libraries for now diff --git a/requirements/dist.txt b/requirements/dist.txt index cf431e7..66339e7 100644 --- a/requirements/dist.txt +++ b/requirements/dist.txt @@ -1,2 +1,2 @@ -build==1.2.2 -twine~=5.1.1 +build==1.2.2.post1 +twine~=6.1.0 diff --git a/src/pyrmv/__init__.py b/src/pyrmv/__init__.py index e331cda..e25c38e 100644 --- a/src/pyrmv/__init__.py +++ b/src/pyrmv/__init__.py @@ -21,7 +21,7 @@ trip = client.trip_find(origin_id=origin.id, dest_id=destination.id) """ __name__ = "pyrmv" -__version__ = "0.4.0" +__version__ = "0.5.0" __license__ = "MIT License" __author__ = "Profitroll" diff --git a/src/pyrmv/classes/board.py b/src/pyrmv/classes/board.py index c3b69cc..85bf9fb 100644 --- a/src/pyrmv/classes/board.py +++ b/src/pyrmv/classes/board.py @@ -1,10 +1,10 @@ from datetime import datetime from typing import Any, List, Mapping, Union -from pyrmv.classes.journey import Journey -from pyrmv.classes.message import Message -from pyrmv.classes.stop import Stop -from pyrmv.utility import ref_upgrade +from ..classes.journey import Journey +from ..classes.message import Message +from ..classes.stop import Stop +from ..utility import ref_upgrade class LineArrival: diff --git a/src/pyrmv/classes/client.py b/src/pyrmv/classes/client.py index e9c9e72..51400ff 100644 --- a/src/pyrmv/classes/client.py +++ b/src/pyrmv/classes/client.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from typing import List, OrderedDict, Union -from pyrmv.classes import ( +from ..classes import ( BoardArrival, BoardDeparture, Journey, @@ -10,7 +10,7 @@ from pyrmv.classes import ( StopTrip, Trip, ) -from pyrmv.enums import ( +from ..enums import ( AffectedJourneyMode, AffectedJourneyStopMode, BoardArrivalType, @@ -22,15 +22,15 @@ from pyrmv.enums import ( SearchMode, SelectionMode, ) -from pyrmv.raw import board_arrival as raw_board_arrival -from pyrmv.raw import board_departure as raw_board_departure -from pyrmv.raw import him_search as raw_him_search -from pyrmv.raw import journey_detail as raw_journey_detail -from pyrmv.raw import stop_by_coords as raw_stop_by_coords -from pyrmv.raw import stop_by_name as raw_stop_by_name -from pyrmv.raw import trip_find as raw_trip_find -from pyrmv.raw import trip_recon as raw_trip_recon -from pyrmv.utility import find_exception +from ..raw import board_arrival as raw_board_arrival +from ..raw import board_departure as raw_board_departure +from ..raw import him_search as raw_him_search +from ..raw import journey_detail as raw_journey_detail +from ..raw import stop_by_coords as raw_stop_by_coords +from ..raw import stop_by_name as raw_stop_by_name +from ..raw import trip_find as raw_trip_find +from ..raw import trip_recon as raw_trip_recon +from ..utility import find_exception try: from typing import Literal @@ -150,7 +150,7 @@ class Client: boardType=board_type.code, ) - find_exception(board_raw) + find_exception(board_raw.copy()) return BoardArrival( board_raw, @@ -219,7 +219,7 @@ class Client: boardType=board_type.code, ) - find_exception(board_raw) + find_exception(board_raw.copy()) return BoardDeparture( board_raw, @@ -343,7 +343,7 @@ class Client: minprio=priority_min, ) - find_exception(messages_raw) + find_exception(messages_raw.copy()) if "Message" in messages_raw: messages.extend(Message(message) for message in messages_raw["Message"]) @@ -402,7 +402,7 @@ class Client: toIdx=to_index, ) - find_exception(journey_raw) + find_exception(journey_raw.copy()) return Journey(journey_raw) @@ -481,7 +481,7 @@ class Client: locationSelectionMode=selection_mode, # type: ignore ) - find_exception(stops_raw) + find_exception(stops_raw.copy()) if "stopLocationOrCoordLocation" in stops_raw: for stop in stops_raw["stopLocationOrCoordLocation"]: @@ -536,7 +536,7 @@ class Client: accessId=self.access_id, inputString=query, lang=lang.code, maxNo=1 ) - find_exception(stops_raw) + find_exception(stops_raw.copy()) if len(stops_raw["stopLocationOrCoordLocation"]) <= 0: return None @@ -642,7 +642,7 @@ class Client: filterMode=filter_mode.code, ) - find_exception(stops_raw) + find_exception(stops_raw.copy()) if "stopLocationOrCoordLocation" in stops_raw: for stop in stops_raw["stopLocationOrCoordLocation"]: @@ -833,7 +833,7 @@ class Client: withFreq=frequency, ) - find_exception(trips_raw) + find_exception(trips_raw.copy()) if "Trip" in trips_raw: trips.extend(Trip(trip) for trip in trips_raw["Trip"]) @@ -913,7 +913,7 @@ class Client: trafficMessages=messages, ) - find_exception(trips_raw) + find_exception(trips_raw.copy()) if "Trip" in trips_raw: trips.extend(Trip(trip) for trip in trips_raw["Trip"]) diff --git a/src/pyrmv/classes/journey.py b/src/pyrmv/classes/journey.py index 45ea7e7..c9e5d0d 100644 --- a/src/pyrmv/classes/journey.py +++ b/src/pyrmv/classes/journey.py @@ -1,8 +1,8 @@ from typing import Any, List, Mapping, Union -from pyrmv.classes.message import Message -from pyrmv.classes.stop import Stop -from pyrmv.utility import ref_upgrade +from ..classes.message import Message +from ..classes.stop import Stop +from ..utility import ref_upgrade class Journey: diff --git a/src/pyrmv/classes/leg.py b/src/pyrmv/classes/leg.py index 968694a..c8b52aa 100644 --- a/src/pyrmv/classes/leg.py +++ b/src/pyrmv/classes/leg.py @@ -3,9 +3,9 @@ from typing import Any, List, Mapping, Union from isodate import Duration, parse_duration -from pyrmv.classes.gis import Gis -from pyrmv.classes.message import Message -from pyrmv.classes.stop import StopTrip +from ..classes.gis import Gis +from ..classes.message import Message +from ..classes.stop import StopTrip class Leg: diff --git a/src/pyrmv/classes/message.py b/src/pyrmv/classes/message.py index b08dfe5..09dbeed 100644 --- a/src/pyrmv/classes/message.py +++ b/src/pyrmv/classes/message.py @@ -3,7 +3,7 @@ from typing import Any, List, Mapping, Union from isodate import Duration, parse_duration -from pyrmv.classes.stop import Stop +from ..classes.stop import Stop class Url: @@ -59,9 +59,9 @@ class Message: self.channels.extend(Channel(channel) for channel in data["channel"]) self.id: str = data["id"] self.active: bool = data["act"] - self.head: str = data["head"] - self.lead: str = data["lead"] - self.text: str = data["text"] + self.head: str = "" if "head" not in data else data["head"] + self.lead: str = "" if "lead" not in data else data["lead"] + self.text: str = "" if "text" not in data else data["text"] self.company: Union[str, None] = data.get("company") self.category: Union[str, None] = data.get("category") self.priority: Union[int, None] = data.get("priority") @@ -81,8 +81,10 @@ class Message: f"{data['validToDate']} {data['validToTime']}", "%Y-%m-%d %H:%M:%S" ) ) - self.date_start_alt: str = data["altStart"] - self.date_end_alt: str = data["altEnd"] + self.date_start_alt: Union[str, None] = ( + None if "altStart" not in data else data["altStart"] + ) + self.date_end_alt: Union[str, None] = None if "altEnd" not in data else data["altEnd"] self.time_modified: Union[datetime, None] = ( None if "modDate" not in data or "modTime" not in data diff --git a/src/pyrmv/classes/platform_type.py b/src/pyrmv/classes/platform_type.py index a1afe49..e3ae518 100644 --- a/src/pyrmv/classes/platform_type.py +++ b/src/pyrmv/classes/platform_type.py @@ -1,6 +1,6 @@ from typing import Any, Mapping, Union -from pyrmv.enums.platform_type_type import PlatformTypeType +from ..enums.platform_type_type import PlatformTypeType class PlatformType: diff --git a/src/pyrmv/classes/trip.py b/src/pyrmv/classes/trip.py index 9bab704..e62c6dc 100644 --- a/src/pyrmv/classes/trip.py +++ b/src/pyrmv/classes/trip.py @@ -3,8 +3,8 @@ from typing import List, Union from isodate import Duration, parse_duration -from pyrmv.classes.leg import Leg -from pyrmv.classes.stop import StopTrip +from ..classes.leg import Leg +from ..classes.stop import StopTrip class Trip: @@ -19,8 +19,10 @@ class Trip: self.index: int = data["idx"] self.id: str = data["tripId"] self.ctx_recon: str = data["ctxRecon"] - self.duration: Union[Duration, timedelta] = parse_duration(data["duration"]) - self.real_time_duration: Union[Duration, timedelta] = ( + self.duration: Union[Duration, timedelta, None] = ( + None if "duration" not in data else parse_duration(data["duration"]) + ) + self.real_time_duration: Union[Duration, timedelta, None] = ( None if "rtDuration" not in data else parse_duration(data["rtDuration"]) ) self.checksum: str = data["checksum"] diff --git a/src/pyrmv/enums/auto_name.py b/src/pyrmv/enums/auto_name.py index d0c07cf..b7a552f 100644 --- a/src/pyrmv/enums/auto_name.py +++ b/src/pyrmv/enums/auto_name.py @@ -3,7 +3,7 @@ from enum import Enum -from pyrmv.const import PRODUCTS +from ..const import PRODUCTS class AutoName(Enum): diff --git a/src/pyrmv/raw/board_arrival.py b/src/pyrmv/raw/board_arrival.py index 1e34527..b6674ec 100644 --- a/src/pyrmv/raw/board_arrival.py +++ b/src/pyrmv/raw/board_arrival.py @@ -64,7 +64,7 @@ def board_arrival( payload = {} headers = {"Accept": "application/json"} if json else {"Accept": "application/xml"} - for var, val in locals().items(): + for var, val in locals().copy().items(): if str(var) == "date": if val != None: if isinstance(val, datetime): diff --git a/src/pyrmv/raw/board_departure.py b/src/pyrmv/raw/board_departure.py index e3cef29..dafe1fc 100644 --- a/src/pyrmv/raw/board_departure.py +++ b/src/pyrmv/raw/board_departure.py @@ -65,7 +65,7 @@ def board_departure( payload = {} headers = {"Accept": "application/json"} if json else {"Accept": "application/xml"} - for var, val in locals().items(): + for var, val in locals().copy().items(): if str(var) == "date": if val != None: if isinstance(val, datetime): diff --git a/src/pyrmv/raw/him_search.py b/src/pyrmv/raw/him_search.py index c8911cf..535a8e6 100644 --- a/src/pyrmv/raw/him_search.py +++ b/src/pyrmv/raw/him_search.py @@ -4,7 +4,7 @@ from typing import OrderedDict, Union from requests import get from xmltodict import parse as xmlparse -from pyrmv.utility import weekdays_bitmask +from ..utility import weekdays_bitmask try: from typing import Literal @@ -97,7 +97,7 @@ def him_search( payload = {} headers = {"Accept": "application/json"} if json else {"Accept": "application/xml"} - for var, val in locals().items(): + for var, val in locals().copy().items(): if str(var) in {"dateB", "dateE"}: if val != None: if isinstance(val, datetime): diff --git a/src/pyrmv/raw/journey_detail.py b/src/pyrmv/raw/journey_detail.py index 81235c2..39236d8 100644 --- a/src/pyrmv/raw/journey_detail.py +++ b/src/pyrmv/raw/journey_detail.py @@ -55,7 +55,7 @@ def journey_detail( payload = {} headers = {"Accept": "application/json"} if json else {"Accept": "application/xml"} - for var, val in locals().items(): + for var, val in locals().copy().items(): if str(var) == "rtMode": if val != None: payload["rtMode"] = val.upper() diff --git a/src/pyrmv/raw/stop_by_coords.py b/src/pyrmv/raw/stop_by_coords.py index b8c7f05..b4fbdf4 100644 --- a/src/pyrmv/raw/stop_by_coords.py +++ b/src/pyrmv/raw/stop_by_coords.py @@ -54,7 +54,7 @@ def stop_by_coords( payload = {} headers = {"Accept": "application/json"} if json else {"Accept": "application/xml"} - for var, val in locals().items(): + for var, val in locals().copy().items(): if str(var) == "stopType": if val != None: payload["type"] = val.upper() diff --git a/src/pyrmv/raw/stop_by_name.py b/src/pyrmv/raw/stop_by_name.py index 5355a0b..5ac372c 100644 --- a/src/pyrmv/raw/stop_by_name.py +++ b/src/pyrmv/raw/stop_by_name.py @@ -65,7 +65,7 @@ def stop_by_name( payload = {} headers = {"Accept": "application/json"} if json else {"Accept": "application/xml"} - for var, val in locals().items(): + for var, val in locals().copy().items(): if str(var) == "inputString": if val != None: payload["input"] = val diff --git a/src/pyrmv/raw/trip_find.py b/src/pyrmv/raw/trip_find.py index 8465074..887b2cd 100644 --- a/src/pyrmv/raw/trip_find.py +++ b/src/pyrmv/raw/trip_find.py @@ -201,7 +201,7 @@ def trip_find( payload = {} headers = {"Accept": "application/json"} if json else {"Accept": "application/xml"} - for var, val in locals().items(): + for var, val in locals().copy().items(): if str(var) == "date": if val != None: if isinstance(val, datetime): diff --git a/src/pyrmv/raw/trip_recon.py b/src/pyrmv/raw/trip_recon.py index e916e1e..ccb0941 100644 --- a/src/pyrmv/raw/trip_recon.py +++ b/src/pyrmv/raw/trip_recon.py @@ -87,7 +87,7 @@ def trip_recon( payload = {} headers = {"Accept": "application/json"} if json else {"Accept": "application/xml"} - for var, val in locals().items(): + for var, val in locals().copy().items(): if str(var) == "date": if val != None: if isinstance(val, datetime): diff --git a/src/pyrmv/utility/find_exception.py b/src/pyrmv/utility/find_exception.py index eb18ae9..23e4247 100644 --- a/src/pyrmv/utility/find_exception.py +++ b/src/pyrmv/utility/find_exception.py @@ -1,4 +1,4 @@ -from pyrmv.errors import ( +from ..errors import ( ApiAuthError, ApiFormatError, ApiParamError, @@ -26,6 +26,7 @@ from pyrmv.errors import ( SvcNoResultError, SvcProductError, SvcSearchError, + SvcParamError, UnknownError, ) @@ -86,6 +87,9 @@ def find_exception(data: dict): elif data["errorCode"] == "SVC_CTX": raise SvcContextError() + elif data["errorCode"] == "SVC_PARAM": + raise SvcParamError(errorCode=data["errorCode"], errorText=data["errorText"]) + elif data["errorCode"] == "SVC_NO_RESULT": raise SvcNoResultError() diff --git a/tox.ini b/tox.ini index e35f504..65b63f3 100644 --- a/tox.ini +++ b/tox.ini @@ -1,11 +1,10 @@ [tox] -minversion = 3.8.0 -envlist = py38, py39, py310, py311, py312 +minversion = 3.9.0 +envlist = py39, py310, py311, py312 isolated_build = true [gh-actions] -python = - 3.8: py38 +python = 3.9: py39 3.10: py310 3.11: py311