stablesea 0.1.0__tar.gz

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.
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: stablesea
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Stable Sea Terminal API
5
+ Author: Stable Sea
6
+ License: MIT
7
+ Project-URL: Homepage, https://stablesea.com
8
+ Project-URL: Documentation, https://docs.stablesea.com
9
+ Project-URL: Repository, https://github.com/stablesea/stablesea-python
10
+ Keywords: stablesea,api,shipping,terminal
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.8
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Requires-Python: >=3.8
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: httpx>=0.24.0
23
+ Provides-Extra: dev
24
+ Requires-Dist: pytest>=7.0; extra == "dev"
25
+ Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
26
+
27
+ # Stable Sea Python SDK
28
+
29
+ Official Python client for the [Stable Sea Terminal API](https://stablesea.com), for managing organizations, quotes, orders, and API keys.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install stablesea
35
+ ```
36
+
37
+ Or install from source:
38
+
39
+ ```bash
40
+ pip install /path/to/sdk/python
41
+ ```
42
+
43
+ ## Quickstart
44
+
45
+ ```python
46
+ from stablesea import StableseaClient
47
+
48
+ # Initialize with your API key (sandbox by default)
49
+ client = StableseaClient(api_key="sk_your_api_key")
50
+
51
+ # List organizations
52
+ organizations = client.organizations.list()
53
+
54
+ # Create an organization
55
+ org = client.organizations.create(
56
+ name="Acme Corp",
57
+ contact={"email": "john@acme.com", "first_name": "John", "last_name": "Doe"},
58
+ idempotency_key="unique-key-123",
59
+ )
60
+
61
+ # Create a quote (organization-scoped)
62
+ quote = client.quotes.create(
63
+ organization_id="org_01k2cm4r59e5z8k5ggrbbxjcwy",
64
+ offering_id="off_01k4qph5ezfsga7fkvbygsqq93",
65
+ payin_amount="100",
66
+ idempotency_key="quote-key-456",
67
+ )
68
+
69
+ # Create an order from the quote
70
+ order = client.orders.create(
71
+ organization_id="org_01k2cm4r59e5z8k5ggrbbxjcwy",
72
+ quote_id=quote["id"],
73
+ idempotency_key="order-key-789",
74
+ )
75
+
76
+ # List API keys
77
+ api_keys = client.api_keys.list()
78
+
79
+ # Create a new API key
80
+ new_key = client.api_keys.create(
81
+ name="Production API",
82
+ environment="PRODUCTION",
83
+ permission_level="STANDARD",
84
+ )
85
+ # Store new_key["api_key"] securely - it's only returned once!
86
+
87
+ # List liquidity providers
88
+ providers = client.liquidity_providers.list()
89
+
90
+ # Get exchange rate for a provider
91
+ rate = client.liquidity_providers.get_exchange_rate("ALPHA")
92
+
93
+ client.close()
94
+ ```
95
+
96
+ ## Async Support
97
+
98
+ For async/await support, use `AsyncStableseaClient`:
99
+
100
+ ```python
101
+ import asyncio
102
+ from stablesea import AsyncStableseaClient
103
+
104
+ async def main():
105
+ async with AsyncStableseaClient(api_key="sk_your_api_key") as client:
106
+ orgs = await client.organizations.list()
107
+ quote = await client.quotes.create(
108
+ organization_id="org_...",
109
+ offering_id="off_...",
110
+ payin_amount="100",
111
+ idempotency_key="unique-key",
112
+ )
113
+ print(orgs, quote)
114
+
115
+ asyncio.run(main())
116
+ ```
117
+
118
+ ## Configuration
119
+
120
+ | Parameter | Default | Description |
121
+ |------------|------------------------------------|--------------------------------|
122
+ | `api_key` | (required) | Your Stable Sea API key |
123
+ | `base_url` | `https://api-sandbox.stablesea.com/v1` | API base URL (use production URL for live) |
124
+ | `timeout` | `30.0` | Request timeout in seconds |
125
+
126
+ ## API Resources
127
+
128
+ | Resource | Methods |
129
+ |----------|---------|
130
+ | `client.organizations` | `list()`, `create()`, `get()` |
131
+ | `client.quotes` | `create()`, `get()` |
132
+ | `client.orders` | `create()`, `list()`, `get()` |
133
+ | `client.api_keys` | `list()`, `create()`, `rotate()`, `revoke()` |
134
+ | `client.liquidity_providers` | `list()`, `get_exchange_rate()` |
135
+ | `client.offerings` | `create()`, `get()` |
136
+ | `client.external_payment_instruments` | `list()`, `create()`, `get()`, `archive()` |
137
+
138
+ ## Error Handling
139
+
140
+ ```python
141
+ from stablesea import StableseaClient, APIError
142
+
143
+ client = StableseaClient(api_key="sk_...")
144
+ try:
145
+ client.organizations.create(...)
146
+ except APIError as e:
147
+ print(f"API error {e.status_code}: {e.message}")
148
+ ```
149
+
150
+ ## License
151
+
152
+ MIT
@@ -0,0 +1,126 @@
1
+ # Stable Sea Python SDK
2
+
3
+ Official Python client for the [Stable Sea Terminal API](https://stablesea.com), for managing organizations, quotes, orders, and API keys.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install stablesea
9
+ ```
10
+
11
+ Or install from source:
12
+
13
+ ```bash
14
+ pip install /path/to/sdk/python
15
+ ```
16
+
17
+ ## Quickstart
18
+
19
+ ```python
20
+ from stablesea import StableseaClient
21
+
22
+ # Initialize with your API key (sandbox by default)
23
+ client = StableseaClient(api_key="sk_your_api_key")
24
+
25
+ # List organizations
26
+ organizations = client.organizations.list()
27
+
28
+ # Create an organization
29
+ org = client.organizations.create(
30
+ name="Acme Corp",
31
+ contact={"email": "john@acme.com", "first_name": "John", "last_name": "Doe"},
32
+ idempotency_key="unique-key-123",
33
+ )
34
+
35
+ # Create a quote (organization-scoped)
36
+ quote = client.quotes.create(
37
+ organization_id="org_01k2cm4r59e5z8k5ggrbbxjcwy",
38
+ offering_id="off_01k4qph5ezfsga7fkvbygsqq93",
39
+ payin_amount="100",
40
+ idempotency_key="quote-key-456",
41
+ )
42
+
43
+ # Create an order from the quote
44
+ order = client.orders.create(
45
+ organization_id="org_01k2cm4r59e5z8k5ggrbbxjcwy",
46
+ quote_id=quote["id"],
47
+ idempotency_key="order-key-789",
48
+ )
49
+
50
+ # List API keys
51
+ api_keys = client.api_keys.list()
52
+
53
+ # Create a new API key
54
+ new_key = client.api_keys.create(
55
+ name="Production API",
56
+ environment="PRODUCTION",
57
+ permission_level="STANDARD",
58
+ )
59
+ # Store new_key["api_key"] securely - it's only returned once!
60
+
61
+ # List liquidity providers
62
+ providers = client.liquidity_providers.list()
63
+
64
+ # Get exchange rate for a provider
65
+ rate = client.liquidity_providers.get_exchange_rate("ALPHA")
66
+
67
+ client.close()
68
+ ```
69
+
70
+ ## Async Support
71
+
72
+ For async/await support, use `AsyncStableseaClient`:
73
+
74
+ ```python
75
+ import asyncio
76
+ from stablesea import AsyncStableseaClient
77
+
78
+ async def main():
79
+ async with AsyncStableseaClient(api_key="sk_your_api_key") as client:
80
+ orgs = await client.organizations.list()
81
+ quote = await client.quotes.create(
82
+ organization_id="org_...",
83
+ offering_id="off_...",
84
+ payin_amount="100",
85
+ idempotency_key="unique-key",
86
+ )
87
+ print(orgs, quote)
88
+
89
+ asyncio.run(main())
90
+ ```
91
+
92
+ ## Configuration
93
+
94
+ | Parameter | Default | Description |
95
+ |------------|------------------------------------|--------------------------------|
96
+ | `api_key` | (required) | Your Stable Sea API key |
97
+ | `base_url` | `https://api-sandbox.stablesea.com/v1` | API base URL (use production URL for live) |
98
+ | `timeout` | `30.0` | Request timeout in seconds |
99
+
100
+ ## API Resources
101
+
102
+ | Resource | Methods |
103
+ |----------|---------|
104
+ | `client.organizations` | `list()`, `create()`, `get()` |
105
+ | `client.quotes` | `create()`, `get()` |
106
+ | `client.orders` | `create()`, `list()`, `get()` |
107
+ | `client.api_keys` | `list()`, `create()`, `rotate()`, `revoke()` |
108
+ | `client.liquidity_providers` | `list()`, `get_exchange_rate()` |
109
+ | `client.offerings` | `create()`, `get()` |
110
+ | `client.external_payment_instruments` | `list()`, `create()`, `get()`, `archive()` |
111
+
112
+ ## Error Handling
113
+
114
+ ```python
115
+ from stablesea import StableseaClient, APIError
116
+
117
+ client = StableseaClient(api_key="sk_...")
118
+ try:
119
+ client.organizations.create(...)
120
+ except APIError as e:
121
+ print(f"API error {e.status_code}: {e.message}")
122
+ ```
123
+
124
+ ## License
125
+
126
+ MIT
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "stablesea"
7
+ version = "0.1.0"
8
+ description = "Python SDK for the Stable Sea Terminal API"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.8"
12
+ authors = [
13
+ { name = "Stable Sea" }
14
+ ]
15
+ keywords = ["stablesea", "api", "shipping", "terminal"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ ]
27
+ dependencies = [
28
+ "httpx>=0.24.0",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "pytest>=7.0",
34
+ "pytest-asyncio>=0.21",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://stablesea.com"
39
+ Documentation = "https://docs.stablesea.com"
40
+ Repository = "https://github.com/stablesea/stablesea-python"
41
+
42
+ [tool.setuptools.packages.find]
43
+ where = ["."]
44
+ include = ["stablesea*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,14 @@
1
+ """Stable Sea Python SDK."""
2
+
3
+ from stablesea.async_client import AsyncStableseaClient
4
+ from stablesea.client import StableseaClient
5
+ from stablesea.errors import APIError, StableseaError
6
+
7
+ __all__ = [
8
+ "StableseaClient",
9
+ "AsyncStableseaClient",
10
+ "StableseaError",
11
+ "APIError",
12
+ ]
13
+
14
+ __version__ = "0.1.0"
@@ -0,0 +1,155 @@
1
+ """HTTP client with Bearer auth."""
2
+
3
+ from typing import Any, Dict, Optional
4
+
5
+ import httpx
6
+
7
+
8
+ class HTTPClient:
9
+ """Low-level HTTP client with Bearer token authentication."""
10
+
11
+ def __init__(
12
+ self,
13
+ api_key: str,
14
+ base_url: str = "https://api-sandbox.stablesea.com/v1",
15
+ timeout: float = 30.0,
16
+ http_client: Optional[httpx.Client] = None,
17
+ ):
18
+ self.base_url = base_url.rstrip("/")
19
+ self.api_key = api_key
20
+ self.timeout = timeout
21
+ self._client = http_client or httpx.Client(
22
+ base_url=self.base_url,
23
+ headers={
24
+ "Authorization": f"Bearer {api_key}",
25
+ "Content-Type": "application/json",
26
+ "Accept": "application/json",
27
+ },
28
+ timeout=timeout,
29
+ )
30
+
31
+ def request(
32
+ self,
33
+ method: str,
34
+ path: str,
35
+ *,
36
+ json: Optional[Dict[str, Any]] = None,
37
+ params: Optional[Dict[str, Any]] = None,
38
+ headers: Optional[Dict[str, str]] = None,
39
+ ) -> Dict[str, Any]:
40
+ """Make an HTTP request and return parsed JSON."""
41
+ merged_headers = dict(headers or {})
42
+ resp = self._client.request(
43
+ method,
44
+ path,
45
+ json=json,
46
+ params=params,
47
+ headers=merged_headers or None,
48
+ )
49
+ return self._handle_response(resp)
50
+
51
+ def _handle_response(self, resp: httpx.Response) -> Dict[str, Any]:
52
+ from stablesea.errors import APIError
53
+
54
+ if resp.status_code >= 400:
55
+ try:
56
+ body = resp.json()
57
+ msg = body.get("message", resp.text or f"HTTP {resp.status_code}")
58
+ err_code = body.get("error")
59
+ except Exception:
60
+ msg = resp.text or f"HTTP {resp.status_code}"
61
+ err_code = None
62
+ raise APIError(
63
+ message=msg,
64
+ error_code=err_code,
65
+ status_code=resp.status_code,
66
+ response=resp,
67
+ )
68
+ if resp.status_code in (200, 201) and resp.content:
69
+ return resp.json()
70
+ return {}
71
+
72
+ def close(self) -> None:
73
+ """Close the HTTP client."""
74
+ self._client.close()
75
+
76
+ def __enter__(self) -> "HTTPClient":
77
+ return self
78
+
79
+ def __exit__(self, *args: object) -> None:
80
+ self.close()
81
+
82
+
83
+ class AsyncHTTPClient:
84
+ """Async HTTP client with Bearer token authentication."""
85
+
86
+ def __init__(
87
+ self,
88
+ api_key: str,
89
+ base_url: str = "https://api-sandbox.stablesea.com/v1",
90
+ timeout: float = 30.0,
91
+ http_client: Optional[httpx.AsyncClient] = None,
92
+ ):
93
+ self.base_url = base_url.rstrip("/")
94
+ self.api_key = api_key
95
+ self.timeout = timeout
96
+ self._client = http_client or httpx.AsyncClient(
97
+ base_url=self.base_url,
98
+ headers={
99
+ "Authorization": f"Bearer {api_key}",
100
+ "Content-Type": "application/json",
101
+ "Accept": "application/json",
102
+ },
103
+ timeout=timeout,
104
+ )
105
+
106
+ async def request(
107
+ self,
108
+ method: str,
109
+ path: str,
110
+ *,
111
+ json: Optional[Dict[str, Any]] = None,
112
+ params: Optional[Dict[str, Any]] = None,
113
+ headers: Optional[Dict[str, str]] = None,
114
+ ) -> Dict[str, Any]:
115
+ """Make an HTTP request and return parsed JSON."""
116
+ merged_headers = dict(headers or {})
117
+ resp = await self._client.request(
118
+ method,
119
+ path,
120
+ json=json,
121
+ params=params,
122
+ headers=merged_headers or None,
123
+ )
124
+ return self._handle_response(resp)
125
+
126
+ def _handle_response(self, resp: httpx.Response) -> Dict[str, Any]:
127
+ from stablesea.errors import APIError
128
+
129
+ if resp.status_code >= 400:
130
+ try:
131
+ body = resp.json()
132
+ msg = body.get("message", resp.text or f"HTTP {resp.status_code}")
133
+ err_code = body.get("error")
134
+ except Exception:
135
+ msg = resp.text or f"HTTP {resp.status_code}"
136
+ err_code = None
137
+ raise APIError(
138
+ message=msg,
139
+ error_code=err_code,
140
+ status_code=resp.status_code,
141
+ response=resp,
142
+ )
143
+ if resp.status_code in (200, 201) and resp.content:
144
+ return resp.json()
145
+ return {}
146
+
147
+ async def close(self) -> None:
148
+ """Close the HTTP client."""
149
+ await self._client.aclose()
150
+
151
+ async def __aenter__(self) -> "AsyncHTTPClient":
152
+ return self
153
+
154
+ async def __aexit__(self, *args: object) -> None:
155
+ await self.close()
@@ -0,0 +1,80 @@
1
+ """Async Stable Sea API client."""
2
+
3
+ from stablesea._http import AsyncHTTPClient
4
+ from stablesea.resources.async_resources import (
5
+ AsyncApiKeysResource,
6
+ AsyncExternalPaymentInstrumentsResource,
7
+ AsyncLiquidityProvidersResource,
8
+ AsyncOfferingsResource,
9
+ AsyncOrganizationsResource,
10
+ AsyncOrdersResource,
11
+ AsyncQuotesResource,
12
+ )
13
+
14
+
15
+ class AsyncStableseaClient:
16
+ """Async Python client for the Stable Sea Terminal API.
17
+
18
+ Uses Bearer token authentication. Supports async/await for non-blocking requests.
19
+
20
+ Example:
21
+ >>> async with AsyncStableseaClient(api_key="sk_...") as client:
22
+ ... orgs = await client.organizations.list()
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ api_key: str,
28
+ base_url: str = "https://api-sandbox.stablesea.com/v1",
29
+ timeout: float = 30.0,
30
+ ):
31
+ """Initialize the async client.
32
+
33
+ Args:
34
+ api_key: Your Stable Sea API key (Bearer token)
35
+ base_url: API base URL (default: sandbox)
36
+ timeout: Request timeout in seconds
37
+ """
38
+ self._http = AsyncHTTPClient(
39
+ api_key=api_key,
40
+ base_url=base_url,
41
+ timeout=timeout,
42
+ )
43
+
44
+ @property
45
+ def organizations(self) -> AsyncOrganizationsResource:
46
+ return AsyncOrganizationsResource(self._http)
47
+
48
+ @property
49
+ def quotes(self) -> AsyncQuotesResource:
50
+ return AsyncQuotesResource(self._http)
51
+
52
+ @property
53
+ def orders(self) -> AsyncOrdersResource:
54
+ return AsyncOrdersResource(self._http)
55
+
56
+ @property
57
+ def api_keys(self) -> AsyncApiKeysResource:
58
+ return AsyncApiKeysResource(self._http)
59
+
60
+ @property
61
+ def liquidity_providers(self) -> AsyncLiquidityProvidersResource:
62
+ return AsyncLiquidityProvidersResource(self._http)
63
+
64
+ @property
65
+ def offerings(self) -> AsyncOfferingsResource:
66
+ return AsyncOfferingsResource(self._http)
67
+
68
+ @property
69
+ def external_payment_instruments(self) -> AsyncExternalPaymentInstrumentsResource:
70
+ return AsyncExternalPaymentInstrumentsResource(self._http)
71
+
72
+ async def close(self) -> None:
73
+ """Close the HTTP client."""
74
+ await self._http.close()
75
+
76
+ async def __aenter__(self) -> "AsyncStableseaClient":
77
+ return self
78
+
79
+ async def __aexit__(self, *args: object) -> None:
80
+ await self.close()
@@ -0,0 +1,96 @@
1
+ """Stable Sea API client."""
2
+
3
+ from typing import Optional
4
+
5
+ from stablesea._http import HTTPClient
6
+ from stablesea.resources import (
7
+ ApiKeysResource,
8
+ ExternalPaymentInstrumentsResource,
9
+ LiquidityProvidersResource,
10
+ OfferingsResource,
11
+ OrganizationsResource,
12
+ OrdersResource,
13
+ QuotesResource,
14
+ )
15
+
16
+
17
+ class StableseaClient:
18
+ """Python client for the Stable Sea Terminal API.
19
+
20
+ Uses Bearer token authentication. All requests include the API key
21
+ in the Authorization header.
22
+
23
+ Example:
24
+ >>> client = StableseaClient(api_key="sk_...")
25
+ >>> orgs = client.organizations.list()
26
+ >>> quote = client.quotes.create(
27
+ ... organization_id="org_...",
28
+ ... offering_id="off_...",
29
+ ... payin_amount="100",
30
+ ... idempotency_key="unique-key-123"
31
+ ... )
32
+ """
33
+
34
+ def __init__(
35
+ self,
36
+ api_key: str,
37
+ base_url: str = "https://api-sandbox.stablesea.com/v1",
38
+ timeout: float = 30.0,
39
+ ):
40
+ """Initialize the client.
41
+
42
+ Args:
43
+ api_key: Your Stable Sea API key (Bearer token)
44
+ base_url: API base URL (default: sandbox)
45
+ timeout: Request timeout in seconds
46
+ """
47
+ self._http = HTTPClient(
48
+ api_key=api_key,
49
+ base_url=base_url,
50
+ timeout=timeout,
51
+ )
52
+
53
+ @property
54
+ def organizations(self) -> OrganizationsResource:
55
+ """Organizations: list, create, get."""
56
+ return OrganizationsResource(self._http)
57
+
58
+ @property
59
+ def quotes(self) -> QuotesResource:
60
+ """Quotes: create, get (organization-scoped)."""
61
+ return QuotesResource(self._http)
62
+
63
+ @property
64
+ def orders(self) -> OrdersResource:
65
+ """Orders: create, list, get (organization-scoped)."""
66
+ return OrdersResource(self._http)
67
+
68
+ @property
69
+ def api_keys(self) -> ApiKeysResource:
70
+ """API Keys: list, create, rotate, revoke."""
71
+ return ApiKeysResource(self._http)
72
+
73
+ @property
74
+ def liquidity_providers(self) -> LiquidityProvidersResource:
75
+ """Liquidity providers: list, get exchange rate."""
76
+ return LiquidityProvidersResource(self._http)
77
+
78
+ @property
79
+ def offerings(self) -> OfferingsResource:
80
+ """Offerings: create, get (organization-scoped)."""
81
+ return OfferingsResource(self._http)
82
+
83
+ @property
84
+ def external_payment_instruments(self) -> ExternalPaymentInstrumentsResource:
85
+ """External payment instruments (organization-scoped)."""
86
+ return ExternalPaymentInstrumentsResource(self._http)
87
+
88
+ def close(self) -> None:
89
+ """Close the HTTP client and release resources."""
90
+ self._http.close()
91
+
92
+ def __enter__(self) -> "StableseaClient":
93
+ return self
94
+
95
+ def __exit__(self, *args: object) -> None:
96
+ self.close()