vessel-api-python 1.3.1__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 -75
- 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.1.dist-info → vessel_api_python-2.0.0.dist-info}/METADATA +80 -29
- vessel_api_python-2.0.0.dist-info/RECORD +13 -0
- vessel_api_python-1.3.1.dist-info/RECORD +0 -13
- {vessel_api_python-1.3.1.dist-info → vessel_api_python-2.0.0.dist-info}/WHEEL +0 -0
- {vessel_api_python-1.3.1.dist-info → vessel_api_python-2.0.0.dist-info}/licenses/LICENSE +0 -0
vessel_api_python/__init__.py
CHANGED
|
@@ -8,44 +8,20 @@ Usage::
|
|
|
8
8
|
result = client.search.vessels(filter_name="Ever Given")
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
import warnings as _warnings
|
|
12
|
-
|
|
13
|
-
_warnings.warn(
|
|
14
|
-
"vessel-api-python 1.x is no longer maintained. Several endpoints it wraps "
|
|
15
|
-
"were retired from the service on 10 August 2026 and now return HTTP 410. "
|
|
16
|
-
"Upgrade to 2.x: pip install 'vessel-api-python>=2'. See the CHANGELOG for "
|
|
17
|
-
"the migration notes.",
|
|
18
|
-
DeprecationWarning,
|
|
19
|
-
stacklevel=2,
|
|
20
|
-
)
|
|
21
|
-
|
|
22
11
|
from ._client import AsyncVesselClient, VesselClient
|
|
23
12
|
from ._constants import DEFAULT_BASE_URL, VERSION
|
|
24
13
|
from ._errors import (
|
|
25
14
|
VesselAPIError,
|
|
26
15
|
VesselAuthError,
|
|
16
|
+
VesselForbiddenError,
|
|
27
17
|
VesselNotFoundError,
|
|
18
|
+
VesselPaymentRequiredError,
|
|
28
19
|
VesselRateLimitError,
|
|
29
20
|
VesselServerError,
|
|
30
21
|
)
|
|
31
22
|
from ._iterator import AsyncIterator, SyncIterator
|
|
32
23
|
from ._models import (
|
|
33
24
|
MODU,
|
|
34
|
-
BroadcastStation,
|
|
35
|
-
Classification,
|
|
36
|
-
ClassificationCertificate,
|
|
37
|
-
ClassificationCondition,
|
|
38
|
-
ClassificationDimensions,
|
|
39
|
-
ClassificationHull,
|
|
40
|
-
ClassificationIdentification,
|
|
41
|
-
ClassificationInfo,
|
|
42
|
-
ClassificationMachinery,
|
|
43
|
-
ClassificationOwner,
|
|
44
|
-
ClassificationPurpose,
|
|
45
|
-
ClassificationResponse,
|
|
46
|
-
ClassificationSurvey,
|
|
47
|
-
ClassificationVessel,
|
|
48
|
-
ClassificationYard,
|
|
49
25
|
DGPSStation,
|
|
50
26
|
DGPSStationsWithinLocationResponse,
|
|
51
27
|
FindDGPSStationsResponse,
|
|
@@ -55,22 +31,11 @@ from ._models import (
|
|
|
55
31
|
FindRadioBeaconsResponse,
|
|
56
32
|
FindVesselsResponse,
|
|
57
33
|
GeoJSON,
|
|
58
|
-
Inspection,
|
|
59
|
-
InspectionDeficiency,
|
|
60
|
-
InspectionDetail,
|
|
61
|
-
InspectionDetailRecord,
|
|
62
|
-
InspectionDetailResponseData,
|
|
63
|
-
InspectionRecord,
|
|
64
|
-
InspectionsResponseData,
|
|
65
34
|
LightAid,
|
|
66
35
|
LightAidsWithinLocationResponse,
|
|
67
36
|
MarineCasualtiesResponse,
|
|
68
37
|
MarineCasualty,
|
|
69
38
|
MODUsWithinLocationResponse,
|
|
70
|
-
Navtex,
|
|
71
|
-
NavtexMessagesResponse,
|
|
72
|
-
Ownership,
|
|
73
|
-
OwnershipResponseData,
|
|
74
39
|
Port,
|
|
75
40
|
PortCountry,
|
|
76
41
|
PortEvent,
|
|
@@ -83,23 +48,21 @@ from ._models import (
|
|
|
83
48
|
RadioBeacon,
|
|
84
49
|
RadioBeaconsWithinLocationResponse,
|
|
85
50
|
ResolutionMeta,
|
|
86
|
-
TypesInspectionDetailResponse,
|
|
87
|
-
TypesInspectionsResponse,
|
|
88
|
-
TypesOwnershipResponse,
|
|
89
51
|
Vessel,
|
|
90
52
|
VesselEmission,
|
|
91
53
|
VesselEmissionsResponse,
|
|
92
54
|
VesselETA,
|
|
93
55
|
VesselETAResponse,
|
|
94
56
|
VesselFormerName,
|
|
95
|
-
VesselOwnership,
|
|
96
57
|
VesselPosition,
|
|
97
58
|
VesselPositionResponse,
|
|
98
59
|
VesselPositionsResponse,
|
|
99
60
|
VesselReference,
|
|
100
61
|
VesselResponse,
|
|
62
|
+
VesselSearchMeta,
|
|
101
63
|
VesselsWithinLocationResponse,
|
|
102
64
|
)
|
|
65
|
+
from ._services import MultiFilter
|
|
103
66
|
|
|
104
67
|
__all__ = [
|
|
105
68
|
# Clients
|
|
@@ -108,7 +71,9 @@ __all__ = [
|
|
|
108
71
|
# Errors
|
|
109
72
|
"VesselAPIError",
|
|
110
73
|
"VesselAuthError",
|
|
74
|
+
"VesselForbiddenError",
|
|
111
75
|
"VesselNotFoundError",
|
|
76
|
+
"VesselPaymentRequiredError",
|
|
112
77
|
"VesselRateLimitError",
|
|
113
78
|
"VesselServerError",
|
|
114
79
|
# Iterators
|
|
@@ -117,27 +82,15 @@ __all__ = [
|
|
|
117
82
|
# Constants
|
|
118
83
|
"VERSION",
|
|
119
84
|
"DEFAULT_BASE_URL",
|
|
85
|
+
# Typing helpers
|
|
86
|
+
"MultiFilter",
|
|
120
87
|
# Models — Shared / helper
|
|
121
88
|
"GeoJSON",
|
|
122
89
|
"PortCountry",
|
|
123
90
|
"PortReference",
|
|
124
91
|
"VesselReference",
|
|
125
92
|
"VesselFormerName",
|
|
126
|
-
"BroadcastStation",
|
|
127
93
|
"ResolutionMeta",
|
|
128
|
-
# Models — Classification sub-models
|
|
129
|
-
"ClassificationCertificate",
|
|
130
|
-
"ClassificationCondition",
|
|
131
|
-
"ClassificationDimensions",
|
|
132
|
-
"ClassificationHull",
|
|
133
|
-
"ClassificationIdentification",
|
|
134
|
-
"ClassificationInfo",
|
|
135
|
-
"ClassificationMachinery",
|
|
136
|
-
"ClassificationOwner",
|
|
137
|
-
"ClassificationPurpose",
|
|
138
|
-
"ClassificationSurvey",
|
|
139
|
-
"ClassificationVessel",
|
|
140
|
-
"ClassificationYard",
|
|
141
94
|
# Models — Vessel
|
|
142
95
|
"Vessel",
|
|
143
96
|
"VesselResponse",
|
|
@@ -146,27 +99,10 @@ __all__ = [
|
|
|
146
99
|
"VesselPositionsResponse",
|
|
147
100
|
"MarineCasualty",
|
|
148
101
|
"MarineCasualtiesResponse",
|
|
149
|
-
"Classification",
|
|
150
|
-
"ClassificationResponse",
|
|
151
102
|
"VesselEmission",
|
|
152
103
|
"VesselEmissionsResponse",
|
|
153
104
|
"VesselETA",
|
|
154
105
|
"VesselETAResponse",
|
|
155
|
-
# Models — Inspection
|
|
156
|
-
"InspectionRecord",
|
|
157
|
-
"Inspection",
|
|
158
|
-
"InspectionDeficiency",
|
|
159
|
-
"InspectionDetailRecord",
|
|
160
|
-
"InspectionDetail",
|
|
161
|
-
"InspectionsResponseData",
|
|
162
|
-
"InspectionDetailResponseData",
|
|
163
|
-
"TypesInspectionsResponse",
|
|
164
|
-
"TypesInspectionDetailResponse",
|
|
165
|
-
# Models — Ownership
|
|
166
|
-
"VesselOwnership",
|
|
167
|
-
"Ownership",
|
|
168
|
-
"OwnershipResponseData",
|
|
169
|
-
"TypesOwnershipResponse",
|
|
170
106
|
# Models — Port
|
|
171
107
|
"Port",
|
|
172
108
|
"PortResponse",
|
|
@@ -175,6 +111,7 @@ __all__ = [
|
|
|
175
111
|
"PortEventsResponse",
|
|
176
112
|
"PortInboundResponse",
|
|
177
113
|
# Models — Search
|
|
114
|
+
"VesselSearchMeta",
|
|
178
115
|
"FindVesselsResponse",
|
|
179
116
|
"FindPortsResponse",
|
|
180
117
|
"DGPSStation",
|
|
@@ -192,7 +129,4 @@ __all__ = [
|
|
|
192
129
|
"LightAidsWithinLocationResponse",
|
|
193
130
|
"MODUsWithinLocationResponse",
|
|
194
131
|
"RadioBeaconsWithinLocationResponse",
|
|
195
|
-
# Models — Navtex
|
|
196
|
-
"Navtex",
|
|
197
|
-
"NavtexMessagesResponse",
|
|
198
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:
|