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.
@@ -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
  ]
@@ -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."""
@@ -1,6 +1,6 @@
1
1
  """Constants for the Vessel API Python SDK."""
2
2
 
3
- VERSION = "1.3.1"
3
+ VERSION = "2.0.0"
4
4
  DEFAULT_BASE_URL = "https://api.vesselapi.com/v1"
5
5
  DEFAULT_USER_AGENT = f"vesselapi-python/{VERSION}"
6
6
  DEFAULT_MAX_RETRIES = 3
@@ -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: