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.
@@ -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(api_url: str, authorization: str, name: str) -> None:
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((api_url, authorization, name), CachedOutcome(None), NEGATIVE_TTL_SECONDS)
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(_UNKNOWN_FUNCTION, status=_HTTP_NOT_FOUND)
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
- if int(resolved.status_code) == _HTTP_NOT_FOUND:
233
- _function_resolution.store_missing(api_url, authorization, name)
234
- payload = response_payload(resolved, _HTTP_SUCCESS_MIN)
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
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="secret",
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(email="user@example.com", password="secret")
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-secret",
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 active user session when present, then a configured service key,
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", password="secret", sign_in_when_allowed=True
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 without changing tokens
346
- or emitting an auth-state event. Previously returned sessions remain immutable.
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. If another authentication operation replaces the session
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
- After parsing the returned fragment into a `Session`, validate and adopt it atomically:
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, pass the returned and stored states to the SDK so it rejects login CSRF before
477
- exchanging the one-time code:
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. Deleting that token's own session clears local
572
- credentials, including when the request outcome is uncertain; deleting another session preserves
573
- them. If another authentication operation replaces the session before deletion finishes, the method
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 without changing the anonymous user's ID or current session:
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="secure-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-secret",
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 using the access token and clears local state.
616
- Supply `refresh_token` with `access_token` 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).
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 failure clears the session that initiated the request. Server and
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, and callback failures cannot interrupt auth
664
- operations. Unsubscribing prevents queued and future delivery; a callback already selected for
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; on HTTP 401,
677
- the SDK can refresh once and revoke that same session without adopting the renewed credentials.
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=_gC3LY6x71NtJc_o9d9eq3N27Z-T_Un3p2-XRu2-Cqs,5088
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=bk523Ss80nREwMD1wk6_pO2XKqOIXqDKylq0fQvMmb0,12540
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.3.dist-info/METADATA,sha256=AmqFIPDJDOmZgEWKZUIn0jW8EK4ITcvAG92gKMDl1Mk,36008
779
- volcano_sdk_python-0.9.3.dist-info/WHEEL,sha256=THafob7ofN-NsuMN7Mg4qZyHaQI7KkD-QlcQatYhXPo,87
780
- volcano_sdk_python-0.9.3.dist-info/licenses/LICENSE,sha256=AIHJy-5MFqM8fHfuGFTTb9a8rvJGypcVgN4aZ-7XTWw,11387
781
- volcano_sdk_python-0.9.3.dist-info/RECORD,,
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,,