volcano-sdk-python 0.9.3__py3-none-any.whl → 0.9.4__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.
- volcano_sdk/_function_resolution.py +15 -2
- volcano_sdk/functions.py +11 -5
- {volcano_sdk_python-0.9.3.dist-info → volcano_sdk_python-0.9.4.dist-info}/METADATA +47 -28
- {volcano_sdk_python-0.9.3.dist-info → volcano_sdk_python-0.9.4.dist-info}/RECORD +6 -6
- {volcano_sdk_python-0.9.3.dist-info → volcano_sdk_python-0.9.4.dist-info}/WHEEL +0 -0
- {volcano_sdk_python-0.9.3.dist-info → volcano_sdk_python-0.9.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -12,8 +12,12 @@ from __future__ import annotations
|
|
|
12
12
|
import threading
|
|
13
13
|
import time
|
|
14
14
|
from dataclasses import dataclass
|
|
15
|
+
from typing import TYPE_CHECKING
|
|
15
16
|
from urllib.parse import urlsplit
|
|
16
17
|
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from .errors import NotFoundError
|
|
20
|
+
|
|
17
21
|
MAX_ENTRIES = 1024
|
|
18
22
|
NEGATIVE_TTL_SECONDS = 30.0
|
|
19
23
|
|
|
@@ -41,6 +45,9 @@ class CachedOutcome:
|
|
|
41
45
|
"""A cached resolve result: a resolution, or a remembered miss when None."""
|
|
42
46
|
|
|
43
47
|
resolution: FunctionResolution | None
|
|
48
|
+
message: str = "Function was not found"
|
|
49
|
+
code: str | None = None
|
|
50
|
+
retry_after: int | None = None
|
|
44
51
|
|
|
45
52
|
|
|
46
53
|
@dataclass(frozen=True, slots=True)
|
|
@@ -122,13 +129,19 @@ def store(
|
|
|
122
129
|
_store((api_url, authorization, name), CachedOutcome(resolution), ttl_seconds)
|
|
123
130
|
|
|
124
131
|
|
|
125
|
-
def store_missing(
|
|
132
|
+
def store_missing(
|
|
133
|
+
api_url: str, authorization: str, name: str, error: NotFoundError
|
|
134
|
+
) -> None:
|
|
126
135
|
"""Remember briefly that a name does not resolve.
|
|
127
136
|
|
|
128
137
|
A caller retrying an unknown name in a loop would otherwise re-ask the
|
|
129
138
|
server on every attempt.
|
|
130
139
|
"""
|
|
131
|
-
_store(
|
|
140
|
+
_store(
|
|
141
|
+
(api_url, authorization, name),
|
|
142
|
+
CachedOutcome(None, str(error), error.code, error.retry_after),
|
|
143
|
+
NEGATIVE_TTL_SECONDS,
|
|
144
|
+
)
|
|
132
145
|
|
|
133
146
|
|
|
134
147
|
def _store(
|
volcano_sdk/functions.py
CHANGED
|
@@ -28,7 +28,6 @@ _INVALID_FUNCTION_NAME = (
|
|
|
28
28
|
)
|
|
29
29
|
_INVALID_FUNCTION_RESPONSE = "Expected a complete function response"
|
|
30
30
|
_INVALID_FUNCTION_PAYLOAD = "Function payload must be a mapping"
|
|
31
|
-
_UNKNOWN_FUNCTION = "Function was not found"
|
|
32
31
|
_HTTP_SUCCESS_MIN = 200
|
|
33
32
|
_HTTP_SUCCESS_MAX = 300
|
|
34
33
|
_HTTP_NOT_FOUND = 404
|
|
@@ -214,7 +213,12 @@ class Functions:
|
|
|
214
213
|
if cached is None:
|
|
215
214
|
return None
|
|
216
215
|
if cached.resolution is None:
|
|
217
|
-
raise NotFoundError(
|
|
216
|
+
raise NotFoundError(
|
|
217
|
+
cached.message,
|
|
218
|
+
status=_HTTP_NOT_FOUND,
|
|
219
|
+
code=cached.code,
|
|
220
|
+
retry_after=cached.retry_after,
|
|
221
|
+
)
|
|
218
222
|
return cached.resolution
|
|
219
223
|
|
|
220
224
|
def _resolve_uncached(
|
|
@@ -229,9 +233,11 @@ class Functions:
|
|
|
229
233
|
authorization=authorization,
|
|
230
234
|
name=name,
|
|
231
235
|
)
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
236
|
+
try:
|
|
237
|
+
payload = response_payload(resolved, _HTTP_SUCCESS_MIN)
|
|
238
|
+
except NotFoundError as error:
|
|
239
|
+
_function_resolution.store_missing(api_url, authorization, name, error)
|
|
240
|
+
raise
|
|
235
241
|
resolution = self._resolution(payload, api_url)
|
|
236
242
|
_function_resolution.store(
|
|
237
243
|
api_url, authorization, name, resolution, self._cache_ttl(payload)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: volcano-sdk-python
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.4
|
|
4
4
|
Summary: Official Python SDK for Volcano
|
|
5
5
|
Project-URL: Homepage, https://volcano.dev
|
|
6
6
|
Project-URL: Repository, https://github.com/Kong/volcano-sdk-python
|
|
@@ -26,6 +26,9 @@ Use the Volcano Python SDK to access authentication, databases, storage,
|
|
|
26
26
|
functions, locks, logs, and realtime events. Requires Python 3.11 or later.
|
|
27
27
|
|
|
28
28
|
Start with the [Python quickstart](https://github.com/Kong/volcano-sdk-python/blob/main/docs/README.md).
|
|
29
|
+
See the [functions guide](https://github.com/Kong/volcano-sdk-python/blob/main/docs/functions.md) for invocation identity and response handling.
|
|
30
|
+
|
|
31
|
+
See [Authentication](https://github.com/Kong/volcano-sdk-python/blob/main/docs/authentication.md) for account, session, email, and OAuth workflows.
|
|
29
32
|
|
|
30
33
|
## Install
|
|
31
34
|
|
|
@@ -54,13 +57,15 @@ client = VolcanoClient(
|
|
|
54
57
|
|
|
55
58
|
sign_up = client.auth.sign_up(
|
|
56
59
|
email="new-user@example.com",
|
|
57
|
-
password="
|
|
60
|
+
password="correct-horse-battery-staple",
|
|
58
61
|
metadata={"display_name": "New User"},
|
|
59
62
|
)
|
|
60
63
|
if sign_up.confirmation_required:
|
|
61
64
|
print(sign_up.message)
|
|
62
65
|
|
|
63
|
-
session = client.auth.sign_in(
|
|
66
|
+
session = client.auth.sign_in(
|
|
67
|
+
email="user@example.com", password="correct-horse-battery-staple"
|
|
68
|
+
)
|
|
64
69
|
current_session = client.auth.get_session()
|
|
65
70
|
assert current_session == session
|
|
66
71
|
|
|
@@ -68,7 +73,7 @@ user = client.auth.get_user()
|
|
|
68
73
|
assert user.id == session.user_id
|
|
69
74
|
|
|
70
75
|
updated_user = client.auth.update_user(
|
|
71
|
-
password="new-
|
|
76
|
+
password="new-correct-horse-battery-staple",
|
|
72
77
|
metadata={"display_name": "Grace", "avatar": None},
|
|
73
78
|
)
|
|
74
79
|
assert updated_user.id == session.user_id
|
|
@@ -218,7 +223,7 @@ The request goes to the function's own domain rather than to `api_url`, so an
|
|
|
218
223
|
egress rule that allows only the API host will block it; the resolved endpoint
|
|
219
224
|
is cached for the lifetime the platform gives it. Deployments with no public
|
|
220
225
|
function domain invoke through the API host instead.
|
|
221
|
-
It uses the
|
|
226
|
+
It uses the current session token, including a supplied `access_token`, then a configured service key,
|
|
222
227
|
then the anonymous key. An anonymous key can invoke a public function without a
|
|
223
228
|
user session; the function receives no user identity. The immutable result
|
|
224
229
|
includes the response body, status, headers, and `X-Volcano-Version`. The body
|
|
@@ -227,7 +232,6 @@ JSON arrays become immutable tuples. Invalid JSON is returned as text. A
|
|
|
227
232
|
function's own non-2xx response is returned when Volcano confirms it ran;
|
|
228
233
|
non-success platform HTTP responses raise typed SDK errors.
|
|
229
234
|
|
|
230
|
-
|
|
231
235
|
Function resolution and invocation recover from a platform HTTP 401 before dispatch:
|
|
232
236
|
the SDK refreshes the captured session and retries the rejected request once.
|
|
233
237
|
Concurrent calls share successful recovery. Replacing or signing out that session
|
|
@@ -326,7 +330,9 @@ The signup acknowledgement is identical for new and existing email addresses. Pa
|
|
|
326
330
|
|
|
327
331
|
```python
|
|
328
332
|
result = client.auth.sign_up(
|
|
329
|
-
email="new-user@example.com",
|
|
333
|
+
email="new-user@example.com",
|
|
334
|
+
password="correct-horse-battery-staple",
|
|
335
|
+
sign_in_when_allowed=True,
|
|
330
336
|
)
|
|
331
337
|
session = result.session # None when no follow-up sign-in ran.
|
|
332
338
|
```
|
|
@@ -342,21 +348,23 @@ request. It is cached data, not proof of authentication; use `auth.get_user()`
|
|
|
342
348
|
to fetch the server-validated profile. Existing three-field `Session` construction
|
|
343
349
|
still works, with `user=None`. An adopted snapshot must have the same user ID.
|
|
344
350
|
Successful `get_user()`, `update_user()`, `convert_anonymous()`, and
|
|
345
|
-
`confirm_email_change()` calls update that local snapshot
|
|
346
|
-
|
|
351
|
+
`confirm_email_change()` calls update that local snapshot. Automatic HTTP 401 recovery
|
|
352
|
+
can rotate credentials and emit `TOKEN_REFRESHED`; the profile update itself does not. Previously returned sessions remain immutable.
|
|
347
353
|
Profile identity checks compare UUID values; the session retains its original
|
|
348
354
|
user ID spelling, including in the cached snapshot.
|
|
349
355
|
|
|
350
356
|
`get_user()` sends the active access token to Volcano and returns an immutable, server-validated
|
|
351
357
|
profile with the complete public AuthUser fields. Profile timestamps are timezone-aware `datetime`
|
|
352
358
|
values, and nested user and application metadata are immutable. The request updates the cached
|
|
353
|
-
profile without changing credentials
|
|
359
|
+
profile without changing credentials unless HTTP 401 recovery requires a refresh.
|
|
360
|
+
Successful recovery rotates credentials and emits `TOKEN_REFRESHED`. If another authentication operation replaces the session
|
|
354
361
|
while the request is in flight, `get_user()` raises `SessionChangedError` instead of returning a
|
|
355
362
|
profile for stale credentials.
|
|
356
363
|
|
|
357
364
|
`update_user()` updates the current user's password, metadata, or both. Metadata is a shallow patch:
|
|
358
365
|
omitted keys remain unchanged, and setting a key to `None` removes it. The method returns the same
|
|
359
|
-
immutable profile type as `get_user()` and updates the cached profile without changing credentials.
|
|
366
|
+
immutable profile type as `get_user()` and updates the cached profile without changing credentials unless HTTP 401 recovery requires a refresh.
|
|
367
|
+
Successful recovery rotates credentials and emits `TOKEN_REFRESHED`.
|
|
360
368
|
It also rejects a response if another authentication operation replaces the session while the
|
|
361
369
|
update is in flight.
|
|
362
370
|
|
|
@@ -444,7 +452,9 @@ hosted_url = client.auth.get_hosted_auth_url(
|
|
|
444
452
|
```
|
|
445
453
|
|
|
446
454
|
Store `hosted_state` in the user's signed server-side session before redirecting to `hosted_url`.
|
|
447
|
-
|
|
455
|
+
In the callback, atomically fetch and delete the stored state before validation,
|
|
456
|
+
even if validation or adoption fails. Reject a missing or already-consumed state.
|
|
457
|
+
After parsing the returned fragment into a `Session`, validate and adopt it:
|
|
448
458
|
|
|
449
459
|
```python
|
|
450
460
|
session = client.auth.adopt_hosted_auth_session(
|
|
@@ -473,8 +483,9 @@ authorization_url = client.auth.sign_in_with_oauth(
|
|
|
473
483
|
```
|
|
474
484
|
|
|
475
485
|
Store `oauth_state` in the user's signed server-side session, then redirect the user to the returned
|
|
476
|
-
URL. In the callback,
|
|
477
|
-
|
|
486
|
+
URL. In the callback, atomically fetch and delete the stored nonce as `stored_oauth_state`;
|
|
487
|
+
reject a missing or already-consumed nonce. Pass the returned and consumed states to
|
|
488
|
+
the SDK so it rejects login CSRF before exchanging the one-time code:
|
|
478
489
|
|
|
479
490
|
```python
|
|
480
491
|
session = client.auth.exchange_oauth_code(
|
|
@@ -568,9 +579,11 @@ Revoke one session by ID:
|
|
|
568
579
|
client.auth.delete_session(session_id="00000000-0000-4000-8000-000000000099")
|
|
569
580
|
```
|
|
570
581
|
|
|
571
|
-
The request uses the current access token.
|
|
572
|
-
credentials
|
|
573
|
-
|
|
582
|
+
The request uses the current access token. When its JWT contains a readable UUID `session_id`,
|
|
583
|
+
deleting that session clears local credentials even if the request outcome is uncertain.
|
|
584
|
+
Without that identifier, the SDK cannot recognize self-deletion. Deleting another session does not
|
|
585
|
+
itself clear local state. HTTP 401 recovery can rotate credentials and emit `TOKEN_REFRESHED`;
|
|
586
|
+
a server-rejected refresh clears the captured session before the operation raises. If another authentication operation replaces the session before deletion finishes, the method
|
|
574
587
|
raises `SessionChangedError` instead of clearing the replacement or acknowledging a stale result.
|
|
575
588
|
|
|
576
589
|
Create an anonymous account and make its tokens the current session:
|
|
@@ -582,12 +595,12 @@ session = client.auth.sign_in_anonymously(metadata={"device": "mobile"})
|
|
|
582
595
|
Anonymous sign-ins must be enabled for the project. Convert the account before signing out if the
|
|
583
596
|
user needs to recover it later.
|
|
584
597
|
|
|
585
|
-
Attach email credentials
|
|
598
|
+
Attach email credentials while preserving the anonymous user's ID:
|
|
586
599
|
|
|
587
600
|
```python
|
|
588
601
|
user = client.auth.convert_anonymous(
|
|
589
602
|
email="user@example.com",
|
|
590
|
-
password="
|
|
603
|
+
password="a-long-example-password-2026",
|
|
591
604
|
metadata={"display_name": "Ada"},
|
|
592
605
|
)
|
|
593
606
|
```
|
|
@@ -599,7 +612,7 @@ Set a new password with the recovery token from that email:
|
|
|
599
612
|
```python
|
|
600
613
|
client.auth.reset_password(
|
|
601
614
|
token="recovery-token",
|
|
602
|
-
new_password="new-
|
|
615
|
+
new_password="new-correct-horse-battery-staple",
|
|
603
616
|
)
|
|
604
617
|
```
|
|
605
618
|
|
|
@@ -610,10 +623,13 @@ when the reset flow completes.
|
|
|
610
623
|
To start with only a supplied user access token, pass `access_token` to
|
|
611
624
|
`VolcanoClient`. Construction makes no request and leaves `refresh_token`,
|
|
612
625
|
`user_id`, and `user` as `None` until supplied or validated by the server.
|
|
613
|
-
`get_user()` validates and caches the profile without changing credentials.
|
|
626
|
+
`get_user()` validates and caches the profile without changing credentials unless HTTP 401 recovery requires a refresh.
|
|
627
|
+
Successful recovery rotates credentials and emits `TOKEN_REFRESHED`.
|
|
614
628
|
Without a refresh token, `refresh_session()` raises `AuthenticationError` and
|
|
615
|
-
`sign_out()` revokes the server session
|
|
616
|
-
|
|
629
|
+
`sign_out()` clears local state and revokes the server session when the access JWT
|
|
630
|
+
contains a readable UUID `session_id`.
|
|
631
|
+
Supplied credentials require both a refresh token and an access JWT with a readable UUID
|
|
632
|
+
`session_id` to enable refresh. See the [token bootstrap example](https://github.com/Kong/volcano-sdk-python/blob/main/docs/README.md#use-a-supplied-access-token).
|
|
617
633
|
|
|
618
634
|
Copy a complete native session into another client's memory:
|
|
619
635
|
|
|
@@ -638,7 +654,8 @@ assert client.auth.get_session() is refreshed
|
|
|
638
654
|
```
|
|
639
655
|
|
|
640
656
|
On success, `refresh_session()` replaces the in-memory session and returns the immutable new
|
|
641
|
-
snapshot. An authentication
|
|
657
|
+
snapshot. An authentication rejection from the refresh endpoint clears the captured session.
|
|
658
|
+
Missing refresh credentials, failed session-continuity checks, server errors, and
|
|
642
659
|
transport failures preserve it, and a late response never replaces a newer session. The SDK does
|
|
643
660
|
not persist sessions.
|
|
644
661
|
|
|
@@ -660,8 +677,8 @@ subscription.unsubscribe()
|
|
|
660
677
|
Registration queues `INITIAL_SESSION`. It normally arrives before registration returns, but an
|
|
661
678
|
existing notification dispatch may deliver it afterward. Successful session creation, refresh, and
|
|
662
679
|
local clearing emit `SIGNED_IN`, `TOKEN_REFRESHED`, and `SIGNED_OUT`. Callbacks are delivered locally
|
|
663
|
-
in transition order after the state lock is released
|
|
664
|
-
|
|
680
|
+
in transition order after the state lock is released. Ordinary callback `Exception` failures are
|
|
681
|
+
isolated; exceptions such as `KeyboardInterrupt` propagate after the session transition has committed. Unsubscribing prevents queued and future delivery; a callback already selected for
|
|
665
682
|
delivery may finish after `unsubscribe()` returns. The SDK does not broadcast between processes or
|
|
666
683
|
persist sessions.
|
|
667
684
|
|
|
@@ -673,8 +690,10 @@ assert client.auth.get_session() is None
|
|
|
673
690
|
```
|
|
674
691
|
|
|
675
692
|
Sign-out uses the refresh token directly when the SDK received both credentials together from
|
|
676
|
-
sign-in or a validated refresh. Supplied credentials use the access-token session
|
|
677
|
-
the SDK can refresh once and revoke that
|
|
693
|
+
sign-in or a validated refresh. Supplied credentials use the access-token session when its JWT
|
|
694
|
+
contains a readable UUID `session_id`; on HTTP 401, the SDK can refresh once and revoke that
|
|
695
|
+
same session without adopting the renewed credentials. Without that identifier, sign-out uses
|
|
696
|
+
the supplied refresh token, or only clears local state if no refresh token is available.
|
|
678
697
|
Calling `sign_out()` without a session succeeds without a request. A revocation failure is raised
|
|
679
698
|
after the captured local session is cleared. Sign-out waits for an already-running refresh and uses its validated credentials.
|
|
680
699
|
Later refresh attempts raise `SessionChangedError` without a request. Concurrent sign-out calls
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
volcano_sdk/__init__.py,sha256=rPSFMLjFbxeQvSRkL1Ee8MsNWArIhs8fgNFgtDuMNzI,1954
|
|
2
|
-
volcano_sdk/_function_resolution.py,sha256=
|
|
2
|
+
volcano_sdk/_function_resolution.py,sha256=g04npZTP6h2ePEAnpJryP889l5erCfFkcLloBV84eTs,5387
|
|
3
3
|
volcano_sdk/_lock_guard.py,sha256=VhHTgBc4e5EDj8j15vVIhdBOTp6fvD628x3hVBeF39o,5794
|
|
4
4
|
volcano_sdk/_lock_renewer.py,sha256=tFo1ZeRiPKB5VSbW61uoMpNR67ZDYyMEe-dCjpAM3As,841
|
|
5
5
|
volcano_sdk/_lock_worker.py,sha256=QhSmwR0WcwgT-7ueJMlbCogLqOakQ5eRAtyWV74fgV0,3057
|
|
@@ -12,7 +12,7 @@ volcano_sdk/client.py,sha256=CJJnf_iTcXnyY2Y57LXJVAn39mTUsepblUg9op-Thzo,12283
|
|
|
12
12
|
volcano_sdk/connection_string.py,sha256=LlTZigsrCF4IfAqQ8SDTWJv35hnnzNu1MfeVv5JW-Kk,2331
|
|
13
13
|
volcano_sdk/database.py,sha256=p_5OaqlBjKAtIUfKQCflXM_CGqokrVmpOMsp6KHiJ8A,9493
|
|
14
14
|
volcano_sdk/errors.py,sha256=ffs66FIN_jhyh5SMj7KdXb-zVV3DSgVK6xJEPNA5h9Y,1613
|
|
15
|
-
volcano_sdk/functions.py,sha256=
|
|
15
|
+
volcano_sdk/functions.py,sha256=31OWRkzBAFjvgynErRFfgjhvzA_sIfv73gz7gYX3G-0,12645
|
|
16
16
|
volcano_sdk/locks.py,sha256=eSkidfHQ_wml5-1LT2hBtHxeALP6E1cGYK0FzHjbrNk,9241
|
|
17
17
|
volcano_sdk/logs.py,sha256=lxORRQBvsnJ0BdMau19MXrIeig5_mEucQSyJ4F1CZxU,4666
|
|
18
18
|
volcano_sdk/models.py,sha256=n5EdzkWzmOstXsuFlvfGOILRemQAqG2P4VCLMelW8SA,8690
|
|
@@ -775,7 +775,7 @@ volcano_sdk/_generated/models/usage_data_point.py,sha256=d4U08JbVTiIMM_l2BmRILL1
|
|
|
775
775
|
volcano_sdk/_generated/models/variable.py,sha256=tvGDOMv77gW6j5skufnfrlNAhzCzA1F7nuPCQWx79yY,5926
|
|
776
776
|
volcano_sdk/_generated/models/variable_deploy_source.py,sha256=G_aCiFHzQx8WhJFs0GvYZEFclfTrfkDvd3kT1e3vty8,463
|
|
777
777
|
volcano_sdk/_generated/models/variable_status.py,sha256=THSsKBSiAYG4kAtBzGA4TZqxjgx8hdL0Q4qCdZfOj00,391
|
|
778
|
-
volcano_sdk_python-0.9.
|
|
779
|
-
volcano_sdk_python-0.9.
|
|
780
|
-
volcano_sdk_python-0.9.
|
|
781
|
-
volcano_sdk_python-0.9.
|
|
778
|
+
volcano_sdk_python-0.9.4.dist-info/METADATA,sha256=RxgSj08eiYMbgWjVj-CXOia39awfCLKsyjRmytGlwmY,37824
|
|
779
|
+
volcano_sdk_python-0.9.4.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
|
|
780
|
+
volcano_sdk_python-0.9.4.dist-info/licenses/LICENSE,sha256=AIHJy-5MFqM8fHfuGFTTb9a8rvJGypcVgN4aZ-7XTWw,11387
|
|
781
|
+
volcano_sdk_python-0.9.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|