vessel-api-python 1.3.0__py3-none-any.whl → 2.0.0__py3-none-any.whl
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.
- vessel_api_python/__init__.py +9 -64
- vessel_api_python/_client.py +0 -4
- vessel_api_python/_constants.py +1 -1
- vessel_api_python/_errors.py +49 -1
- vessel_api_python/_models.py +50 -396
- vessel_api_python/_services.py +2145 -351
- vessel_api_python/_transport.py +5 -2
- {vessel_api_python-1.3.0.dist-info → vessel_api_python-2.0.0.dist-info}/METADATA +80 -16
- vessel_api_python-2.0.0.dist-info/RECORD +13 -0
- {vessel_api_python-1.3.0.dist-info → vessel_api_python-2.0.0.dist-info}/WHEEL +1 -1
- vessel_api_python-1.3.0.dist-info/RECORD +0 -13
- {vessel_api_python-1.3.0.dist-info → vessel_api_python-2.0.0.dist-info}/licenses/LICENSE +0 -0
vessel_api_python/__init__.py
CHANGED
|
@@ -13,28 +13,15 @@ from ._constants import DEFAULT_BASE_URL, VERSION
|
|
|
13
13
|
from ._errors import (
|
|
14
14
|
VesselAPIError,
|
|
15
15
|
VesselAuthError,
|
|
16
|
+
VesselForbiddenError,
|
|
16
17
|
VesselNotFoundError,
|
|
18
|
+
VesselPaymentRequiredError,
|
|
17
19
|
VesselRateLimitError,
|
|
18
20
|
VesselServerError,
|
|
19
21
|
)
|
|
20
22
|
from ._iterator import AsyncIterator, SyncIterator
|
|
21
23
|
from ._models import (
|
|
22
24
|
MODU,
|
|
23
|
-
BroadcastStation,
|
|
24
|
-
Classification,
|
|
25
|
-
ClassificationCertificate,
|
|
26
|
-
ClassificationCondition,
|
|
27
|
-
ClassificationDimensions,
|
|
28
|
-
ClassificationHull,
|
|
29
|
-
ClassificationIdentification,
|
|
30
|
-
ClassificationInfo,
|
|
31
|
-
ClassificationMachinery,
|
|
32
|
-
ClassificationOwner,
|
|
33
|
-
ClassificationPurpose,
|
|
34
|
-
ClassificationResponse,
|
|
35
|
-
ClassificationSurvey,
|
|
36
|
-
ClassificationVessel,
|
|
37
|
-
ClassificationYard,
|
|
38
25
|
DGPSStation,
|
|
39
26
|
DGPSStationsWithinLocationResponse,
|
|
40
27
|
FindDGPSStationsResponse,
|
|
@@ -44,22 +31,11 @@ from ._models import (
|
|
|
44
31
|
FindRadioBeaconsResponse,
|
|
45
32
|
FindVesselsResponse,
|
|
46
33
|
GeoJSON,
|
|
47
|
-
Inspection,
|
|
48
|
-
InspectionDeficiency,
|
|
49
|
-
InspectionDetail,
|
|
50
|
-
InspectionDetailRecord,
|
|
51
|
-
InspectionDetailResponseData,
|
|
52
|
-
InspectionRecord,
|
|
53
|
-
InspectionsResponseData,
|
|
54
34
|
LightAid,
|
|
55
35
|
LightAidsWithinLocationResponse,
|
|
56
36
|
MarineCasualtiesResponse,
|
|
57
37
|
MarineCasualty,
|
|
58
38
|
MODUsWithinLocationResponse,
|
|
59
|
-
Navtex,
|
|
60
|
-
NavtexMessagesResponse,
|
|
61
|
-
Ownership,
|
|
62
|
-
OwnershipResponseData,
|
|
63
39
|
Port,
|
|
64
40
|
PortCountry,
|
|
65
41
|
PortEvent,
|
|
@@ -72,23 +48,21 @@ from ._models import (
|
|
|
72
48
|
RadioBeacon,
|
|
73
49
|
RadioBeaconsWithinLocationResponse,
|
|
74
50
|
ResolutionMeta,
|
|
75
|
-
TypesInspectionDetailResponse,
|
|
76
|
-
TypesInspectionsResponse,
|
|
77
|
-
TypesOwnershipResponse,
|
|
78
51
|
Vessel,
|
|
79
52
|
VesselEmission,
|
|
80
53
|
VesselEmissionsResponse,
|
|
81
54
|
VesselETA,
|
|
82
55
|
VesselETAResponse,
|
|
83
56
|
VesselFormerName,
|
|
84
|
-
VesselOwnership,
|
|
85
57
|
VesselPosition,
|
|
86
58
|
VesselPositionResponse,
|
|
87
59
|
VesselPositionsResponse,
|
|
88
60
|
VesselReference,
|
|
89
61
|
VesselResponse,
|
|
62
|
+
VesselSearchMeta,
|
|
90
63
|
VesselsWithinLocationResponse,
|
|
91
64
|
)
|
|
65
|
+
from ._services import MultiFilter
|
|
92
66
|
|
|
93
67
|
__all__ = [
|
|
94
68
|
# Clients
|
|
@@ -97,7 +71,9 @@ __all__ = [
|
|
|
97
71
|
# Errors
|
|
98
72
|
"VesselAPIError",
|
|
99
73
|
"VesselAuthError",
|
|
74
|
+
"VesselForbiddenError",
|
|
100
75
|
"VesselNotFoundError",
|
|
76
|
+
"VesselPaymentRequiredError",
|
|
101
77
|
"VesselRateLimitError",
|
|
102
78
|
"VesselServerError",
|
|
103
79
|
# Iterators
|
|
@@ -106,27 +82,15 @@ __all__ = [
|
|
|
106
82
|
# Constants
|
|
107
83
|
"VERSION",
|
|
108
84
|
"DEFAULT_BASE_URL",
|
|
85
|
+
# Typing helpers
|
|
86
|
+
"MultiFilter",
|
|
109
87
|
# Models — Shared / helper
|
|
110
88
|
"GeoJSON",
|
|
111
89
|
"PortCountry",
|
|
112
90
|
"PortReference",
|
|
113
91
|
"VesselReference",
|
|
114
92
|
"VesselFormerName",
|
|
115
|
-
"BroadcastStation",
|
|
116
93
|
"ResolutionMeta",
|
|
117
|
-
# Models — Classification sub-models
|
|
118
|
-
"ClassificationCertificate",
|
|
119
|
-
"ClassificationCondition",
|
|
120
|
-
"ClassificationDimensions",
|
|
121
|
-
"ClassificationHull",
|
|
122
|
-
"ClassificationIdentification",
|
|
123
|
-
"ClassificationInfo",
|
|
124
|
-
"ClassificationMachinery",
|
|
125
|
-
"ClassificationOwner",
|
|
126
|
-
"ClassificationPurpose",
|
|
127
|
-
"ClassificationSurvey",
|
|
128
|
-
"ClassificationVessel",
|
|
129
|
-
"ClassificationYard",
|
|
130
94
|
# Models — Vessel
|
|
131
95
|
"Vessel",
|
|
132
96
|
"VesselResponse",
|
|
@@ -135,27 +99,10 @@ __all__ = [
|
|
|
135
99
|
"VesselPositionsResponse",
|
|
136
100
|
"MarineCasualty",
|
|
137
101
|
"MarineCasualtiesResponse",
|
|
138
|
-
"Classification",
|
|
139
|
-
"ClassificationResponse",
|
|
140
102
|
"VesselEmission",
|
|
141
103
|
"VesselEmissionsResponse",
|
|
142
104
|
"VesselETA",
|
|
143
105
|
"VesselETAResponse",
|
|
144
|
-
# Models — Inspection
|
|
145
|
-
"InspectionRecord",
|
|
146
|
-
"Inspection",
|
|
147
|
-
"InspectionDeficiency",
|
|
148
|
-
"InspectionDetailRecord",
|
|
149
|
-
"InspectionDetail",
|
|
150
|
-
"InspectionsResponseData",
|
|
151
|
-
"InspectionDetailResponseData",
|
|
152
|
-
"TypesInspectionsResponse",
|
|
153
|
-
"TypesInspectionDetailResponse",
|
|
154
|
-
# Models — Ownership
|
|
155
|
-
"VesselOwnership",
|
|
156
|
-
"Ownership",
|
|
157
|
-
"OwnershipResponseData",
|
|
158
|
-
"TypesOwnershipResponse",
|
|
159
106
|
# Models — Port
|
|
160
107
|
"Port",
|
|
161
108
|
"PortResponse",
|
|
@@ -164,6 +111,7 @@ __all__ = [
|
|
|
164
111
|
"PortEventsResponse",
|
|
165
112
|
"PortInboundResponse",
|
|
166
113
|
# Models — Search
|
|
114
|
+
"VesselSearchMeta",
|
|
167
115
|
"FindVesselsResponse",
|
|
168
116
|
"FindPortsResponse",
|
|
169
117
|
"DGPSStation",
|
|
@@ -181,7 +129,4 @@ __all__ = [
|
|
|
181
129
|
"LightAidsWithinLocationResponse",
|
|
182
130
|
"MODUsWithinLocationResponse",
|
|
183
131
|
"RadioBeaconsWithinLocationResponse",
|
|
184
|
-
# Models — Navtex
|
|
185
|
-
"Navtex",
|
|
186
|
-
"NavtexMessagesResponse",
|
|
187
132
|
]
|
vessel_api_python/_client.py
CHANGED
|
@@ -24,14 +24,12 @@ from ._constants import (
|
|
|
24
24
|
from ._services import (
|
|
25
25
|
AsyncEmissionsService,
|
|
26
26
|
AsyncLocationService,
|
|
27
|
-
AsyncNavtexService,
|
|
28
27
|
AsyncPortEventsService,
|
|
29
28
|
AsyncPortsService,
|
|
30
29
|
AsyncSearchService,
|
|
31
30
|
AsyncVesselsService,
|
|
32
31
|
EmissionsService,
|
|
33
32
|
LocationService,
|
|
34
|
-
NavtexService,
|
|
35
33
|
PortEventsService,
|
|
36
34
|
PortsService,
|
|
37
35
|
SearchService,
|
|
@@ -91,7 +89,6 @@ class VesselClient:
|
|
|
91
89
|
self.emissions = EmissionsService(self._client)
|
|
92
90
|
self.search = SearchService(self._client)
|
|
93
91
|
self.location = LocationService(self._client)
|
|
94
|
-
self.navtex = NavtexService(self._client)
|
|
95
92
|
|
|
96
93
|
def close(self) -> None:
|
|
97
94
|
"""Close the underlying HTTP client and release resources."""
|
|
@@ -150,7 +147,6 @@ class AsyncVesselClient:
|
|
|
150
147
|
self.emissions = AsyncEmissionsService(self._client)
|
|
151
148
|
self.search = AsyncSearchService(self._client)
|
|
152
149
|
self.location = AsyncLocationService(self._client)
|
|
153
|
-
self.navtex = AsyncNavtexService(self._client)
|
|
154
150
|
|
|
155
151
|
async def aclose(self) -> None:
|
|
156
152
|
"""Close the underlying HTTP client and release resources."""
|
vessel_api_python/_constants.py
CHANGED
vessel_api_python/_errors.py
CHANGED
|
@@ -13,6 +13,10 @@ class VesselAPIError(Exception):
|
|
|
13
13
|
status_code: The HTTP status code from the response.
|
|
14
14
|
message: A human-readable error message.
|
|
15
15
|
body: The raw response body bytes, available for re-parsing.
|
|
16
|
+
code: The API's machine-readable error code (e.g. ``insufficient_credits``,
|
|
17
|
+
``feature_not_available``), or None if the response carried none.
|
|
18
|
+
Branch on this rather than on ``message``, which may be reworded.
|
|
19
|
+
error_type: The API's error type (e.g. ``payment_required_error``), or None.
|
|
16
20
|
"""
|
|
17
21
|
|
|
18
22
|
def __init__(
|
|
@@ -20,10 +24,14 @@ class VesselAPIError(Exception):
|
|
|
20
24
|
status_code: int,
|
|
21
25
|
message: str,
|
|
22
26
|
body: bytes = b"",
|
|
27
|
+
code: str | None = None,
|
|
28
|
+
error_type: str | None = None,
|
|
23
29
|
) -> None:
|
|
24
30
|
self.status_code = status_code
|
|
25
31
|
self.message = message
|
|
26
32
|
self.body = body
|
|
33
|
+
self.code = code
|
|
34
|
+
self.error_type = error_type
|
|
27
35
|
super().__init__(f"vesselapi: {message} (status {status_code})")
|
|
28
36
|
|
|
29
37
|
@property
|
|
@@ -41,11 +49,39 @@ class VesselAPIError(Exception):
|
|
|
41
49
|
"""True if the error is a 401 Unauthorized response."""
|
|
42
50
|
return self.status_code == 401
|
|
43
51
|
|
|
52
|
+
@property
|
|
53
|
+
def is_payment_required(self) -> bool:
|
|
54
|
+
"""True if the error is a 402 Payment Required response.
|
|
55
|
+
|
|
56
|
+
Returned when a satellite-backed call is made with no satellite credits
|
|
57
|
+
left. Retry without ``filter_sat`` to serve stored data instead, or top
|
|
58
|
+
up the balance.
|
|
59
|
+
"""
|
|
60
|
+
return self.status_code == 402
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def is_forbidden(self) -> bool:
|
|
64
|
+
"""True if the error is a 403 Forbidden response.
|
|
65
|
+
|
|
66
|
+
Distinct from 401: the key is valid but not permitted. Check ``code`` to
|
|
67
|
+
tell a temporarily suspended key from a feature that is not on the
|
|
68
|
+
caller's plan.
|
|
69
|
+
"""
|
|
70
|
+
return self.status_code == 403
|
|
71
|
+
|
|
44
72
|
|
|
45
73
|
class VesselAuthError(VesselAPIError):
|
|
46
74
|
"""Raised on 401 Unauthorized responses."""
|
|
47
75
|
|
|
48
76
|
|
|
77
|
+
class VesselPaymentRequiredError(VesselAPIError):
|
|
78
|
+
"""Raised on 402 Payment Required responses (satellite credits exhausted)."""
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class VesselForbiddenError(VesselAPIError):
|
|
82
|
+
"""Raised on 403 Forbidden responses (key suspended, or feature not on plan)."""
|
|
83
|
+
|
|
84
|
+
|
|
49
85
|
class VesselNotFoundError(VesselAPIError):
|
|
50
86
|
"""Raised on 404 Not Found responses."""
|
|
51
87
|
|
|
@@ -76,6 +112,8 @@ def error_from_response(
|
|
|
76
112
|
|
|
77
113
|
Raises:
|
|
78
114
|
VesselAuthError: On 401 responses.
|
|
115
|
+
VesselPaymentRequiredError: On 402 responses (satellite credits exhausted).
|
|
116
|
+
VesselForbiddenError: On 403 responses (suspended key, or feature not on plan).
|
|
79
117
|
VesselNotFoundError: On 404 responses.
|
|
80
118
|
VesselRateLimitError: On 429 responses.
|
|
81
119
|
VesselServerError: On 5xx responses.
|
|
@@ -86,6 +124,8 @@ def error_from_response(
|
|
|
86
124
|
|
|
87
125
|
# Try to extract a human-readable message from the response body.
|
|
88
126
|
msg = _status_text(status_code)
|
|
127
|
+
code: str | None = None
|
|
128
|
+
error_type: str | None = None
|
|
89
129
|
if body:
|
|
90
130
|
try:
|
|
91
131
|
data = json.loads(body)
|
|
@@ -98,6 +138,10 @@ def error_from_response(
|
|
|
98
138
|
flat_msg = data.get("message") if isinstance(data, dict) else None
|
|
99
139
|
if flat_msg and isinstance(flat_msg, str):
|
|
100
140
|
msg = flat_msg
|
|
141
|
+
# The API also sends machine-readable discriminators. Expose them:
|
|
142
|
+
# branching on `message` breaks whenever the wording changes.
|
|
143
|
+
code = _get_nested(data, "error", "code")
|
|
144
|
+
error_type = _get_nested(data, "error", "type")
|
|
101
145
|
# If both fail, msg stays as HTTP status text.
|
|
102
146
|
# Raw body is always available in VesselAPIError.body.
|
|
103
147
|
except (json.JSONDecodeError, TypeError, AttributeError):
|
|
@@ -107,6 +151,10 @@ def error_from_response(
|
|
|
107
151
|
error_cls: type[VesselAPIError]
|
|
108
152
|
if status_code == 401:
|
|
109
153
|
error_cls = VesselAuthError
|
|
154
|
+
elif status_code == 402:
|
|
155
|
+
error_cls = VesselPaymentRequiredError
|
|
156
|
+
elif status_code == 403:
|
|
157
|
+
error_cls = VesselForbiddenError
|
|
110
158
|
elif status_code == 404:
|
|
111
159
|
error_cls = VesselNotFoundError
|
|
112
160
|
elif status_code == 429:
|
|
@@ -116,7 +164,7 @@ def error_from_response(
|
|
|
116
164
|
else:
|
|
117
165
|
error_cls = VesselAPIError
|
|
118
166
|
|
|
119
|
-
raise error_cls(status_code=status_code, message=msg, body=body)
|
|
167
|
+
raise error_cls(status_code=status_code, message=msg, body=body, code=code, error_type=error_type)
|
|
120
168
|
|
|
121
169
|
|
|
122
170
|
def _get_nested(data: dict[str, object] | list[object] | None, *keys: str) -> str | None:
|