astrox-python 0.1.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.
- astrox/__init__.py +49 -0
- astrox/_http.py +504 -0
- astrox/access.py +173 -0
- astrox/components/__init__.py +202 -0
- astrox/components/_axes.py +508 -0
- astrox/components/_common.py +141 -0
- astrox/components/_constraints.py +138 -0
- astrox/components/_objects.py +167 -0
- astrox/components/_positions.py +673 -0
- astrox/components/_rotations.py +115 -0
- astrox/components/_sensors.py +134 -0
- astrox/components/_vgt.py +337 -0
- astrox/coverage/__init__.py +41 -0
- astrox/coverage/_core.py +552 -0
- astrox/coverage/_fom.py +125 -0
- astrox/coverage/coverage_time.py +83 -0
- astrox/coverage/number_of_assets.py +160 -0
- astrox/coverage/response_time.py +160 -0
- astrox/coverage/revisit_time.py +160 -0
- astrox/coverage/simple_coverage.py +152 -0
- astrox/exceptions.py +92 -0
- astrox/lighting.py +121 -0
- astrox/orbits.py +499 -0
- astrox/propagator.py +1072 -0
- astrox/rocket.py +82 -0
- astrox_python-0.1.0.dist-info/METADATA +82 -0
- astrox_python-0.1.0.dist-info/RECORD +29 -0
- astrox_python-0.1.0.dist-info/WHEEL +4 -0
- astrox_python-0.1.0.dist-info/licenses/LICENSE +21 -0
astrox/__init__.py
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Python interface for the ASTROX Web API.
|
|
2
|
+
|
|
3
|
+
Functions are organized by domain modules and can use package-level
|
|
4
|
+
configuration without requiring explicit client management:
|
|
5
|
+
|
|
6
|
+
import astrox
|
|
7
|
+
from astrox import access, coverage, components, lighting, orbits, propagator
|
|
8
|
+
|
|
9
|
+
astrox.configure(base_url="http://custom:8765", timeout=120)
|
|
10
|
+
orbit = orbits.keplerian(...)
|
|
11
|
+
period_s, position = propagator.j2(
|
|
12
|
+
start="2026-01-01T00:00:00Z",
|
|
13
|
+
stop="2026-01-01T01:00:00Z",
|
|
14
|
+
orbit_epoch="2026-01-01T00:00:00Z",
|
|
15
|
+
orbit=orbit,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
For advanced configuration, instantiate Client directly:
|
|
19
|
+
|
|
20
|
+
client = astrox.Client(timeout=60)
|
|
21
|
+
|
|
22
|
+
Raw route access is available for advanced callers:
|
|
23
|
+
|
|
24
|
+
result = astrox.raw.post("/Propagator/J2", json={...})
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
28
|
+
|
|
29
|
+
from astrox import access, coverage, components, lighting, orbits, propagator, rocket
|
|
30
|
+
from astrox._http import Client, configure, get_session, raw
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
__version__ = version("astrox-python")
|
|
34
|
+
except PackageNotFoundError:
|
|
35
|
+
__version__ = "0.0.0+unknown"
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"Client",
|
|
39
|
+
"configure",
|
|
40
|
+
"get_session",
|
|
41
|
+
"access",
|
|
42
|
+
"coverage",
|
|
43
|
+
"components",
|
|
44
|
+
"lighting",
|
|
45
|
+
"orbits",
|
|
46
|
+
"propagator",
|
|
47
|
+
"rocket",
|
|
48
|
+
"raw",
|
|
49
|
+
]
|
astrox/_http.py
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
"""HTTP client with retry mechanism and ContextVar-based session management."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import time
|
|
7
|
+
from contextvars import ContextVar
|
|
8
|
+
from typing import Any, TypeVar
|
|
9
|
+
|
|
10
|
+
import requests
|
|
11
|
+
|
|
12
|
+
from astrox import exceptions
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T")
|
|
15
|
+
|
|
16
|
+
# Default configuration
|
|
17
|
+
DEFAULT_BASE_URL = "http://astrox.cn:8765"
|
|
18
|
+
DEFAULT_TIMEOUT = 30.0
|
|
19
|
+
DEFAULT_MAX_RETRIES = 3
|
|
20
|
+
DEFAULT_RETRY_DELAY = 1.0 # seconds
|
|
21
|
+
|
|
22
|
+
# ContextVar for thread-safe default client management
|
|
23
|
+
_default_session: ContextVar[Client | None] = ContextVar("session", default=None)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _join_url(base_url: str, endpoint: str) -> str:
|
|
27
|
+
return f"{base_url.rstrip('/')}/{endpoint.lstrip('/')}"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _default_headers() -> dict[str, str]:
|
|
31
|
+
return {
|
|
32
|
+
"Accept": "application/json",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _json_payload(data: Any) -> Any:
|
|
37
|
+
model_dump_json = getattr(data, "model_dump_json", None)
|
|
38
|
+
if callable(model_dump_json):
|
|
39
|
+
return json.loads(model_dump_json(by_alias=True, exclude_none=True))
|
|
40
|
+
if isinstance(data, (list, tuple)):
|
|
41
|
+
return [_json_payload(item) for item in data]
|
|
42
|
+
if isinstance(data, dict):
|
|
43
|
+
return {key: _json_payload(value) for key, value in data.items()}
|
|
44
|
+
return data
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _validation_errors(exc: Exception) -> list[Any]:
|
|
48
|
+
errors = getattr(exc, "errors", None)
|
|
49
|
+
if callable(errors):
|
|
50
|
+
return errors()
|
|
51
|
+
return []
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _validate_response_model(response_model: type[T], result: Any) -> T:
|
|
55
|
+
model_validate = getattr(response_model, "model_validate", None)
|
|
56
|
+
if not callable(model_validate):
|
|
57
|
+
raise exceptions.AstroxValidationError(
|
|
58
|
+
message="response_model must provide model_validate()",
|
|
59
|
+
errors=[],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
return model_validate(result)
|
|
64
|
+
except Exception as exc:
|
|
65
|
+
raise exceptions.AstroxValidationError(
|
|
66
|
+
message=f"Failed to validate response: {exc}",
|
|
67
|
+
errors=_validation_errors(exc),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _make_request(
|
|
72
|
+
endpoint: str,
|
|
73
|
+
json_body: Any,
|
|
74
|
+
*,
|
|
75
|
+
method: str = "POST",
|
|
76
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
77
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
78
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
79
|
+
retry_delay: float = DEFAULT_RETRY_DELAY,
|
|
80
|
+
session: requests.Session | None = None,
|
|
81
|
+
params: dict[str, Any] | None = None,
|
|
82
|
+
headers: dict[str, str] | None = None,
|
|
83
|
+
**request_kwargs: Any,
|
|
84
|
+
) -> Any:
|
|
85
|
+
"""
|
|
86
|
+
Make an HTTP request to the API with retry mechanism.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
endpoint: API endpoint (e.g., "/Coverage/ComputeCoverage")
|
|
90
|
+
json_body: Optional JSON request payload
|
|
91
|
+
method: HTTP method
|
|
92
|
+
base_url: Base URL for the API
|
|
93
|
+
timeout: Request timeout in seconds
|
|
94
|
+
max_retries: Maximum number of retry attempts
|
|
95
|
+
retry_delay: Initial delay between retries (exponential backoff)
|
|
96
|
+
session: Optional requests.Session to use
|
|
97
|
+
params: Optional query parameters
|
|
98
|
+
headers: Optional extra headers
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Parsed JSON response
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
AstroxAPIError: If IsSuccess=false in response
|
|
105
|
+
AstroxHTTPError: If HTTP status code indicates error
|
|
106
|
+
AstroxTimeoutError: If request times out
|
|
107
|
+
AstroxConnectionError: If connection fails after all retries
|
|
108
|
+
"""
|
|
109
|
+
url = _join_url(base_url, endpoint)
|
|
110
|
+
use_session = session or requests.Session()
|
|
111
|
+
request_headers = _default_headers()
|
|
112
|
+
if headers:
|
|
113
|
+
request_headers.update(headers)
|
|
114
|
+
json_data = _json_payload(json_body)
|
|
115
|
+
|
|
116
|
+
last_exception = None
|
|
117
|
+
|
|
118
|
+
total_attempts = max_retries + 1
|
|
119
|
+
|
|
120
|
+
for attempt in range(total_attempts):
|
|
121
|
+
try:
|
|
122
|
+
response = use_session.request(
|
|
123
|
+
method.upper(),
|
|
124
|
+
url,
|
|
125
|
+
json=json_data,
|
|
126
|
+
headers=request_headers,
|
|
127
|
+
timeout=timeout,
|
|
128
|
+
params=params,
|
|
129
|
+
**request_kwargs,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Check HTTP status
|
|
133
|
+
if response.status_code >= 400:
|
|
134
|
+
# Don't retry client errors (4xx), only server errors (5xx)
|
|
135
|
+
if response.status_code < 500:
|
|
136
|
+
raise exceptions.AstroxHTTPError(
|
|
137
|
+
status_code=response.status_code,
|
|
138
|
+
message=response.text or response.reason,
|
|
139
|
+
endpoint=endpoint,
|
|
140
|
+
response=response,
|
|
141
|
+
)
|
|
142
|
+
# Server error - will retry
|
|
143
|
+
last_exception = exceptions.AstroxHTTPError(
|
|
144
|
+
status_code=response.status_code,
|
|
145
|
+
message=response.text or response.reason,
|
|
146
|
+
endpoint=endpoint,
|
|
147
|
+
response=response,
|
|
148
|
+
)
|
|
149
|
+
if attempt < total_attempts - 1:
|
|
150
|
+
time.sleep(retry_delay * (2**attempt))
|
|
151
|
+
continue
|
|
152
|
+
raise last_exception
|
|
153
|
+
|
|
154
|
+
# Parse JSON response
|
|
155
|
+
if (
|
|
156
|
+
response.status_code == 204
|
|
157
|
+
or getattr(response, "content", None) == b""
|
|
158
|
+
):
|
|
159
|
+
return None
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
result = response.json()
|
|
163
|
+
except json.JSONDecodeError as e:
|
|
164
|
+
raise exceptions.AstroxAPIError(
|
|
165
|
+
message=f"Failed to parse JSON response: {e}",
|
|
166
|
+
endpoint=endpoint,
|
|
167
|
+
response=response,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Check API-level success (if response has IsSuccess field)
|
|
171
|
+
if isinstance(result, dict) and "IsSuccess" in result:
|
|
172
|
+
if not result.get("IsSuccess"):
|
|
173
|
+
message = result.get("Message", "Unknown error")
|
|
174
|
+
raise exceptions.AstroxAPIError(
|
|
175
|
+
message=message,
|
|
176
|
+
endpoint=endpoint,
|
|
177
|
+
response=response,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
return result
|
|
181
|
+
|
|
182
|
+
except requests.Timeout:
|
|
183
|
+
last_exception = exceptions.AstroxTimeoutError(
|
|
184
|
+
endpoint=endpoint,
|
|
185
|
+
timeout=timeout,
|
|
186
|
+
)
|
|
187
|
+
if attempt < total_attempts - 1:
|
|
188
|
+
time.sleep(retry_delay * (2**attempt))
|
|
189
|
+
continue
|
|
190
|
+
raise last_exception
|
|
191
|
+
|
|
192
|
+
except requests.ConnectionError as e:
|
|
193
|
+
last_exception = exceptions.AstroxConnectionError(
|
|
194
|
+
message=f"Failed to connect to API: {e}",
|
|
195
|
+
original_error=e,
|
|
196
|
+
)
|
|
197
|
+
if attempt < total_attempts - 1:
|
|
198
|
+
time.sleep(retry_delay * (2**attempt))
|
|
199
|
+
continue
|
|
200
|
+
raise last_exception
|
|
201
|
+
|
|
202
|
+
except requests.RequestException as e:
|
|
203
|
+
last_exception = exceptions.AstroxConnectionError(
|
|
204
|
+
message=f"Request failed: {e}",
|
|
205
|
+
original_error=e,
|
|
206
|
+
)
|
|
207
|
+
if attempt < total_attempts - 1:
|
|
208
|
+
time.sleep(retry_delay * (2**attempt))
|
|
209
|
+
continue
|
|
210
|
+
raise last_exception
|
|
211
|
+
|
|
212
|
+
# Should not reach here, but just in case
|
|
213
|
+
if last_exception:
|
|
214
|
+
raise last_exception
|
|
215
|
+
raise exceptions.AstroxConnectionError(
|
|
216
|
+
message="Request failed after all retries",
|
|
217
|
+
original_error=None,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def post(
|
|
222
|
+
endpoint: str,
|
|
223
|
+
data: Any,
|
|
224
|
+
response_model: type[T] | None = None,
|
|
225
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
226
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
227
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
228
|
+
retry_delay: float = DEFAULT_RETRY_DELAY,
|
|
229
|
+
session: requests.Session | None = None,
|
|
230
|
+
) -> T | dict[str, Any]:
|
|
231
|
+
"""
|
|
232
|
+
Make a POST request and optionally parse response into a model object.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
endpoint: API endpoint
|
|
236
|
+
data: Request payload
|
|
237
|
+
response_model: Optional model_validate-compatible class for response
|
|
238
|
+
base_url: Base URL for the API
|
|
239
|
+
timeout: Request timeout in seconds
|
|
240
|
+
max_retries: Maximum number of retry attempts
|
|
241
|
+
retry_delay: Initial delay between retries
|
|
242
|
+
session: Optional requests.Session to use
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
Parsed response model if response_model provided, else dict
|
|
246
|
+
|
|
247
|
+
Raises:
|
|
248
|
+
AstroxValidationError: If response validation fails
|
|
249
|
+
(Plus all exceptions from _make_request)
|
|
250
|
+
"""
|
|
251
|
+
result = _make_request(
|
|
252
|
+
endpoint=endpoint,
|
|
253
|
+
json_body=data,
|
|
254
|
+
base_url=base_url,
|
|
255
|
+
timeout=timeout,
|
|
256
|
+
max_retries=max_retries,
|
|
257
|
+
retry_delay=retry_delay,
|
|
258
|
+
session=session,
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
if response_model is None:
|
|
262
|
+
return result
|
|
263
|
+
|
|
264
|
+
return _validate_response_model(response_model, result)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class Client:
|
|
268
|
+
"""Client for the ASTROX API with retry mechanism.
|
|
269
|
+
|
|
270
|
+
Wraps the low-level _make_request() function in a class-based interface
|
|
271
|
+
with configurable connection parameters.
|
|
272
|
+
|
|
273
|
+
Example:
|
|
274
|
+
>>> client = Client(timeout=60)
|
|
275
|
+
>>> result = client.raw.post("/Propagator/J2", json={...})
|
|
276
|
+
|
|
277
|
+
>>> # Global configuration
|
|
278
|
+
>>> configure(base_url="http://custom:8765", timeout=120)
|
|
279
|
+
>>> # All subsequent calls use this configuration
|
|
280
|
+
"""
|
|
281
|
+
|
|
282
|
+
def __init__(
|
|
283
|
+
self,
|
|
284
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
285
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
286
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
287
|
+
retry_delay: float = DEFAULT_RETRY_DELAY,
|
|
288
|
+
):
|
|
289
|
+
"""Initialize HTTP client.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
base_url: Base URL for the API
|
|
293
|
+
timeout: Request timeout in seconds
|
|
294
|
+
max_retries: Maximum number of retry attempts
|
|
295
|
+
retry_delay: Initial delay between retries (exponential backoff)
|
|
296
|
+
"""
|
|
297
|
+
self.base_url = base_url
|
|
298
|
+
self.timeout = timeout
|
|
299
|
+
self.max_retries = max_retries
|
|
300
|
+
self.retry_delay = retry_delay
|
|
301
|
+
self._session = requests.Session()
|
|
302
|
+
self.raw = RawClient(self)
|
|
303
|
+
|
|
304
|
+
def request(
|
|
305
|
+
self,
|
|
306
|
+
method: str,
|
|
307
|
+
endpoint: str,
|
|
308
|
+
*,
|
|
309
|
+
json: Any = None,
|
|
310
|
+
params: dict[str, Any] | None = None,
|
|
311
|
+
headers: dict[str, str] | None = None,
|
|
312
|
+
timeout: float | None = None,
|
|
313
|
+
max_retries: int | None = None,
|
|
314
|
+
retry_delay: float | None = None,
|
|
315
|
+
**request_kwargs: Any,
|
|
316
|
+
) -> Any:
|
|
317
|
+
"""Make a raw JSON request to an API endpoint."""
|
|
318
|
+
return _make_request(
|
|
319
|
+
endpoint=endpoint,
|
|
320
|
+
json_body=json,
|
|
321
|
+
method=method,
|
|
322
|
+
base_url=self.base_url,
|
|
323
|
+
timeout=timeout if timeout is not None else self.timeout,
|
|
324
|
+
max_retries=max_retries if max_retries is not None else self.max_retries,
|
|
325
|
+
retry_delay=retry_delay if retry_delay is not None else self.retry_delay,
|
|
326
|
+
session=self._session,
|
|
327
|
+
params=params,
|
|
328
|
+
headers=headers,
|
|
329
|
+
**request_kwargs,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
def post(
|
|
333
|
+
self,
|
|
334
|
+
endpoint: str,
|
|
335
|
+
data: Any,
|
|
336
|
+
response_model: type[T] | None = None,
|
|
337
|
+
params: dict[str, Any] | None = None,
|
|
338
|
+
) -> T | dict[str, Any]:
|
|
339
|
+
"""Make POST request to API endpoint.
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
endpoint: API endpoint (e.g., "/Propagator/J2")
|
|
343
|
+
data: Request payload
|
|
344
|
+
response_model: Optional model_validate-compatible class for response validation
|
|
345
|
+
params: Optional query parameters
|
|
346
|
+
|
|
347
|
+
Returns:
|
|
348
|
+
Parsed response model if response_model provided, else dict
|
|
349
|
+
|
|
350
|
+
Raises:
|
|
351
|
+
AstroxAPIError: If IsSuccess=false in response
|
|
352
|
+
AstroxHTTPError: If HTTP status code indicates error
|
|
353
|
+
AstroxTimeoutError: If request times out
|
|
354
|
+
AstroxConnectionError: If connection fails after all retries
|
|
355
|
+
AstroxValidationError: If response validation fails
|
|
356
|
+
"""
|
|
357
|
+
result = self.request("POST", endpoint, json=data, params=params)
|
|
358
|
+
|
|
359
|
+
if response_model is None:
|
|
360
|
+
return result
|
|
361
|
+
|
|
362
|
+
return _validate_response_model(response_model, result)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class RawClient:
|
|
366
|
+
"""Advanced raw route access bound to a client or the default client."""
|
|
367
|
+
|
|
368
|
+
def __init__(self, client: Client | None = None) -> None:
|
|
369
|
+
self._client = client
|
|
370
|
+
|
|
371
|
+
def _target(self) -> Client:
|
|
372
|
+
return self._client or get_session()
|
|
373
|
+
|
|
374
|
+
def request(
|
|
375
|
+
self,
|
|
376
|
+
method: str,
|
|
377
|
+
endpoint: str,
|
|
378
|
+
*,
|
|
379
|
+
json: Any = None,
|
|
380
|
+
params: dict[str, Any] | None = None,
|
|
381
|
+
headers: dict[str, str] | None = None,
|
|
382
|
+
timeout: float | None = None,
|
|
383
|
+
max_retries: int | None = None,
|
|
384
|
+
retry_delay: float | None = None,
|
|
385
|
+
client: Client | None = None,
|
|
386
|
+
**request_kwargs: Any,
|
|
387
|
+
) -> Any:
|
|
388
|
+
target = client or self._target()
|
|
389
|
+
return target.request(
|
|
390
|
+
method,
|
|
391
|
+
endpoint,
|
|
392
|
+
json=json,
|
|
393
|
+
params=params,
|
|
394
|
+
headers=headers,
|
|
395
|
+
timeout=timeout,
|
|
396
|
+
max_retries=max_retries,
|
|
397
|
+
retry_delay=retry_delay,
|
|
398
|
+
**request_kwargs,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
def get(
|
|
402
|
+
self,
|
|
403
|
+
endpoint: str,
|
|
404
|
+
*,
|
|
405
|
+
params: dict[str, Any] | None = None,
|
|
406
|
+
headers: dict[str, str] | None = None,
|
|
407
|
+
timeout: float | None = None,
|
|
408
|
+
max_retries: int | None = None,
|
|
409
|
+
retry_delay: float | None = None,
|
|
410
|
+
client: Client | None = None,
|
|
411
|
+
**request_kwargs: Any,
|
|
412
|
+
) -> Any:
|
|
413
|
+
return self.request(
|
|
414
|
+
"GET",
|
|
415
|
+
endpoint,
|
|
416
|
+
params=params,
|
|
417
|
+
headers=headers,
|
|
418
|
+
timeout=timeout,
|
|
419
|
+
max_retries=max_retries,
|
|
420
|
+
retry_delay=retry_delay,
|
|
421
|
+
client=client,
|
|
422
|
+
**request_kwargs,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
def post(
|
|
426
|
+
self,
|
|
427
|
+
endpoint: str,
|
|
428
|
+
*,
|
|
429
|
+
json: Any = None,
|
|
430
|
+
params: dict[str, Any] | None = None,
|
|
431
|
+
headers: dict[str, str] | None = None,
|
|
432
|
+
timeout: float | None = None,
|
|
433
|
+
max_retries: int | None = None,
|
|
434
|
+
retry_delay: float | None = None,
|
|
435
|
+
client: Client | None = None,
|
|
436
|
+
**request_kwargs: Any,
|
|
437
|
+
) -> Any:
|
|
438
|
+
return self.request(
|
|
439
|
+
"POST",
|
|
440
|
+
endpoint,
|
|
441
|
+
json=json,
|
|
442
|
+
params=params,
|
|
443
|
+
headers=headers,
|
|
444
|
+
timeout=timeout,
|
|
445
|
+
max_retries=max_retries,
|
|
446
|
+
retry_delay=retry_delay,
|
|
447
|
+
client=client,
|
|
448
|
+
**request_kwargs,
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
raw = RawClient()
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def get_session() -> Client:
|
|
456
|
+
"""Get the current default session, creating one if needed.
|
|
457
|
+
|
|
458
|
+
Returns:
|
|
459
|
+
Client instance (either existing default or newly created)
|
|
460
|
+
|
|
461
|
+
Example:
|
|
462
|
+
>>> sess = get_session()
|
|
463
|
+
>>> result = sess.raw.post("/Propagator/J2", json={...})
|
|
464
|
+
"""
|
|
465
|
+
sess = _default_session.get()
|
|
466
|
+
if sess is None:
|
|
467
|
+
sess = Client()
|
|
468
|
+
_default_session.set(sess)
|
|
469
|
+
return sess
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def configure(
|
|
473
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
474
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
475
|
+
max_retries: int = DEFAULT_MAX_RETRIES,
|
|
476
|
+
retry_delay: float = DEFAULT_RETRY_DELAY,
|
|
477
|
+
) -> Client:
|
|
478
|
+
"""Configure the default session globally.
|
|
479
|
+
|
|
480
|
+
Args:
|
|
481
|
+
base_url: Base URL for the API
|
|
482
|
+
timeout: Request timeout in seconds
|
|
483
|
+
max_retries: Maximum number of retry attempts
|
|
484
|
+
retry_delay: Initial delay between retries
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
Configured Client instance
|
|
488
|
+
|
|
489
|
+
Example:
|
|
490
|
+
>>> import astrox
|
|
491
|
+
>>> astrox.configure(base_url="http://custom:8765", timeout=120)
|
|
492
|
+
>>> # All subsequent calls use this configuration
|
|
493
|
+
>>> from astrox import orbits, propagator
|
|
494
|
+
>>> orbit = orbits.keplerian(...)
|
|
495
|
+
>>> period_s, position = propagator.j2(..., orbit=orbit)
|
|
496
|
+
"""
|
|
497
|
+
sess = Client(
|
|
498
|
+
base_url=base_url,
|
|
499
|
+
timeout=timeout,
|
|
500
|
+
max_retries=max_retries,
|
|
501
|
+
retry_delay=retry_delay,
|
|
502
|
+
)
|
|
503
|
+
_default_session.set(sess)
|
|
504
|
+
return sess
|