volcano-sdk-python 0.11.0__py3-none-any.whl → 0.11.2__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.
@@ -196,7 +196,7 @@ def _store(
196
196
  def forget(api_url: str, authorization: str, name: str) -> None:
197
197
  """Drop one cached resolution that turned out to be stale."""
198
198
  with _lock:
199
- _entries.pop((api_url, authorization, name), None)
199
+ _ = _entries.pop((api_url, authorization, name), None)
200
200
 
201
201
 
202
202
  def clear() -> None:
@@ -9,22 +9,31 @@ from typing import TYPE_CHECKING
9
9
  from ._lock_renewer import renewal_delay as _calculate_renewal_delay
10
10
 
11
11
  if TYPE_CHECKING:
12
+ from _thread import LockType
13
+
12
14
  from .models import LockLease
13
15
 
14
16
  MAX_LOCK_LIFETIME_SECONDS = 7_776_000
15
17
  LOSS_POLL_INTERVAL_SECONDS = 1.0
16
- SUSPEND_AWARE_CLOCK_ID = getattr(time, "CLOCK_BOOTTIME", None)
17
18
  _LEASE_EXPIRED = "lock lease expired before renewal completed"
18
19
  _NO_SAFE_RENEWAL_WINDOW = "lock renewal returned no safe lease window"
19
20
 
20
21
 
22
+ def _suspend_aware_clock_id(value: object) -> int | None:
23
+ return value if isinstance(value, int) else None
24
+
25
+
26
+ _clock_id_value: object = getattr(time, "CLOCK_BOOTTIME", None)
27
+ SUSPEND_AWARE_CLOCK_ID = _suspend_aware_clock_id(_clock_id_value)
28
+
29
+
21
30
  class _FallbackClock:
22
31
  """Combine monotonic progress with suspend-aware wall time."""
23
32
 
24
33
  def __init__(self) -> None:
25
- self._lock = threading.Lock()
26
- self._monotonic = time.monotonic()
27
- self._value = time.time()
34
+ self._lock: LockType = threading.Lock()
35
+ self._monotonic: float = time.monotonic()
36
+ self._value: float = time.time()
28
37
 
29
38
  def __call__(self) -> float:
30
39
  with self._lock:
@@ -38,11 +47,11 @@ class _FallbackClock:
38
47
  _FALLBACK_CLOCK = _FallbackClock()
39
48
 
40
49
 
41
- def _lease_now() -> float:
42
- clock_gettime = getattr(time, "clock_gettime", None)
50
+ def lease_now() -> float:
51
+ clock_gettime: object = getattr(time, "clock_gettime", None)
43
52
  if clock_gettime is None or SUSPEND_AWARE_CLOCK_ID is None:
44
53
  return _FALLBACK_CLOCK()
45
- return float(clock_gettime(SUSPEND_AWARE_CLOCK_ID))
54
+ return float(time.clock_gettime(SUSPEND_AWARE_CLOCK_ID))
46
55
 
47
56
 
48
57
  class LockGuard:
@@ -57,15 +66,17 @@ class LockGuard:
57
66
  lease_started_at: float | None = None,
58
67
  ) -> None:
59
68
  """Track one acquired lease against its local monotonic deadline."""
60
- self._state_lock = threading.Lock()
61
- self._lease = lease
62
- self._ttl = ttl
63
- self._absolute_deadline = started_at + MAX_LOCK_LIFETIME_SECONDS
69
+ self._state_lock: LockType = threading.Lock()
70
+ self._lease: LockLease = lease
71
+ self._ttl: int = ttl
72
+ self._absolute_deadline: float = started_at + MAX_LOCK_LIFETIME_SECONDS
64
73
  # A successful retry renews the TTL, not the maximum ownership lifetime.
65
74
  lease_started_at = started_at if lease_started_at is None else lease_started_at
66
- self._lease_deadline = min(lease_started_at + ttl, self._absolute_deadline)
75
+ self._lease_deadline: float = min(
76
+ lease_started_at + ttl, self._absolute_deadline
77
+ )
67
78
  self._failure: Exception | None = None
68
- self._lost = threading.Event()
79
+ self._lost: threading.Event = threading.Event()
69
80
 
70
81
  @property
71
82
  def lease(self) -> LockLease:
@@ -77,7 +88,7 @@ class LockGuard:
77
88
  def lost(self) -> bool:
78
89
  """Whether renewal failed, the lease expired, or the guard closed."""
79
90
  with self._state_lock:
80
- self._expire_if_needed_locked(_lease_now())
91
+ self._expire_if_needed_locked(lease_now())
81
92
  return self._lost.is_set()
82
93
 
83
94
  def wait_lost(self, timeout: float | None = None) -> bool:
@@ -89,10 +100,10 @@ class LockGuard:
89
100
  True when the guard reports lost ownership, False on timeout.
90
101
 
91
102
  """
92
- timeout_deadline = None if timeout is None else _lease_now() + timeout
103
+ timeout_deadline = None if timeout is None else lease_now() + timeout
93
104
  while True:
94
105
  with self._state_lock:
95
- now = _lease_now()
106
+ now = lease_now()
96
107
  self._expire_if_needed_locked(now)
97
108
  if self._lost.is_set():
98
109
  return True
@@ -101,15 +112,15 @@ class LockGuard:
101
112
  self._remaining_seconds_locked(now),
102
113
  )
103
114
  if timeout_deadline is not None:
104
- timeout_remaining = timeout_deadline - _lease_now()
115
+ timeout_remaining = timeout_deadline - lease_now()
105
116
  if timeout_remaining <= 0:
106
117
  return False
107
118
  wait = min(wait, timeout_remaining)
108
- self._lost.wait(wait)
119
+ _ = self._lost.wait(wait)
109
120
 
110
121
  def replace_lease(self, lease: LockLease, *, started_at: float) -> bool:
111
122
  with self._state_lock:
112
- now = _lease_now()
123
+ now = lease_now()
113
124
  self._expire_if_needed_locked(now)
114
125
  if self._lost.is_set():
115
126
  return False
@@ -126,18 +137,18 @@ class LockGuard:
126
137
 
127
138
  def mark_lost(self, failure: Exception) -> None:
128
139
  with self._state_lock:
129
- self._expire_if_needed_locked(_lease_now())
140
+ self._expire_if_needed_locked(lease_now())
130
141
  self._mark_lost_locked(failure)
131
142
 
132
143
  def _remaining_seconds(self) -> float:
133
144
  with self._state_lock:
134
- now = _lease_now()
145
+ now = lease_now()
135
146
  self._expire_if_needed_locked(now)
136
147
  return self._remaining_seconds_locked(now)
137
148
 
138
149
  def renewal_delay(self) -> float:
139
150
  with self._state_lock:
140
- now = _lease_now()
151
+ now = lease_now()
141
152
  self._expire_if_needed_locked(now)
142
153
  if self._lost.is_set():
143
154
  return 0.0
@@ -148,12 +159,12 @@ class LockGuard:
148
159
 
149
160
  def _renewal_failure(self) -> Exception | None:
150
161
  with self._state_lock:
151
- self._expire_if_needed_locked(_lease_now())
162
+ self._expire_if_needed_locked(lease_now())
152
163
  return self._failure
153
164
 
154
165
  def _close(self) -> None:
155
166
  with self._state_lock:
156
- self._expire_if_needed_locked(_lease_now())
167
+ self._expire_if_needed_locked(lease_now())
157
168
  self._lost.set()
158
169
 
159
170
  def _mark_lost_locked(self, failure: Exception) -> None:
@@ -5,7 +5,7 @@ from __future__ import annotations
5
5
  import threading
6
6
  from typing import TYPE_CHECKING, Protocol
7
7
 
8
- from ._lock_guard import LockGuard, _lease_now
8
+ from ._lock_guard import LockGuard, lease_now
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from types import TracebackType
@@ -27,7 +27,7 @@ class LockRenewalClient(Protocol):
27
27
 
28
28
  class _RenewalFailureRecorder:
29
29
  def __init__(self, guard: LockGuard) -> None:
30
- self._guard = guard
30
+ self._guard: LockGuard = guard
31
31
 
32
32
  def __enter__(self) -> None:
33
33
  return None
@@ -56,12 +56,12 @@ class LockRenewer:
56
56
  *,
57
57
  ttl: int,
58
58
  ) -> None:
59
- self._locks = locks
60
- self._key = key
61
- self._guard = guard
62
- self._ttl = ttl
63
- self._stop = threading.Event()
64
- self._thread = threading.Thread(target=self._run, daemon=True)
59
+ self._locks: LockRenewalClient = locks
60
+ self._key: str = key
61
+ self._guard: LockGuard = guard
62
+ self._ttl: int = ttl
63
+ self._stop: threading.Event = threading.Event()
64
+ self._thread: threading.Thread = threading.Thread(target=self._run, daemon=True)
65
65
 
66
66
  def start(self) -> None:
67
67
  """Start renewing the guarded lease."""
@@ -81,11 +81,11 @@ class LockRenewer:
81
81
  return
82
82
 
83
83
  def _wait_until_renewal(self) -> bool:
84
- renew_at = _lease_now() + self._guard.renewal_delay()
84
+ renew_at = lease_now() + self._guard.renewal_delay()
85
85
  while not self._stop.is_set():
86
86
  if self._guard.lost:
87
87
  return False
88
- remaining = renew_at - _lease_now()
88
+ remaining = renew_at - lease_now()
89
89
  if remaining <= 0:
90
90
  return True
91
91
  if self._stop.wait(min(remaining, MAX_RENEWAL_WAIT_SLICE_SECONDS)):
@@ -93,7 +93,7 @@ class LockRenewer:
93
93
  return False
94
94
 
95
95
  def _renew_once(self) -> bool:
96
- started_at = _lease_now()
96
+ started_at = lease_now()
97
97
  with _RenewalFailureRecorder(self._guard):
98
98
  lease = self._locks.renew(
99
99
  self._key,
@@ -14,12 +14,12 @@ INVALID_LOG_RESPONSE = "Expected a complete log response"
14
14
  def response_values(payload: object) -> Mapping[str, object]:
15
15
  """Require a JSON object for the response envelope.
16
16
 
17
- Raises:
18
- TypeError: The response envelope is not an object.
19
-
20
17
  Returns:
21
18
  The response fields without normalizing their values.
22
19
 
20
+ Raises:
21
+ TypeError: The response envelope is not an object.
22
+
23
23
  """
24
24
  if not isinstance(payload, Mapping):
25
25
  raise TypeError(INVALID_LOG_RESPONSE)
@@ -29,12 +29,12 @@ def response_values(payload: object) -> Mapping[str, object]:
29
29
  def response_data(values: Mapping[str, object]) -> tuple[Mapping[str, JSONValue], ...]:
30
30
  """Require a list of objects without coercing empty mappings into lists.
31
31
 
32
- Raises:
33
- TypeError: The rows are not a list of objects.
34
-
35
32
  Returns:
36
33
  The validated rows in their original order.
37
34
 
35
+ Raises:
36
+ TypeError: The rows are not a list of objects.
37
+
38
38
  """
39
39
  raw_data = values.get("data")
40
40
  if not isinstance(raw_data, list):
@@ -48,12 +48,12 @@ def response_data(values: Mapping[str, object]) -> tuple[Mapping[str, JSONValue]
48
48
  def search_metadata(values: Mapping[str, object]) -> tuple[int, bool, str | None]:
49
49
  """Require search pagination fields without coercing their values.
50
50
 
51
- Raises:
52
- TypeError: Required metadata is missing or has an invalid type.
53
-
54
51
  Returns:
55
52
  The page limit, continuation flag, and optional cursor.
56
53
 
54
+ Raises:
55
+ TypeError: Required metadata is missing or has an invalid type.
56
+
57
57
  """
58
58
  limit = values.get("limit")
59
59
  has_more = values.get("has_more")
@@ -71,12 +71,12 @@ def search_metadata(values: Mapping[str, object]) -> tuple[int, bool, str | None
71
71
  def activity_total(values: Mapping[str, object]) -> int:
72
72
  """Require the activity total without treating booleans as integers.
73
73
 
74
- Raises:
75
- TypeError: The total is missing or is not an integer.
76
-
77
74
  Returns:
78
75
  The total reported by the server.
79
76
 
77
+ Raises:
78
+ TypeError: The total is missing or is not an integer.
79
+
80
80
  """
81
81
  total = values.get("total")
82
82
  if not isinstance(total, int) or isinstance(total, bool):
volcano_sdk/_session.py CHANGED
@@ -33,8 +33,8 @@ def session_id_from_access_token(access_token: str) -> str | None:
33
33
  return None
34
34
  padding = "=" * (-len(parts[1]) % 4)
35
35
  try:
36
- payload: object = json.loads(
37
- base64.urlsafe_b64decode(parts[1] + padding).decode()
36
+ payload = cast(
37
+ "object", json.loads(base64.urlsafe_b64decode(parts[1] + padding).decode())
38
38
  )
39
39
  except (ValueError, UnicodeDecodeError, RecursionError):
40
40
  return None
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, TypeVar
9
9
  from .errors import SessionChangedError, VolcanoError
10
10
 
11
11
  if TYPE_CHECKING:
12
+ from _thread import LockType
12
13
  from collections.abc import Callable
13
14
 
14
15
  from .models import Session
@@ -40,11 +41,11 @@ class SessionOperations:
40
41
  """Retain only this session's latest refresh and shared sign-out outcome."""
41
42
 
42
43
  def __init__(self, verified: Session | None = None) -> None:
43
- self._lock = Lock()
44
+ self._lock: LockType = Lock()
44
45
  self.refreshing: Future[Session] | None = None
45
46
  self.signing_out: Future[BaseException | None] | None = None
46
- self._locally_cleared = False
47
- self._verified_pair = (
47
+ self._locally_cleared: bool = False
48
+ self._verified_pair: tuple[str, str | None] | None = (
48
49
  (verified.access_token, verified.refresh_token)
49
50
  if verified is not None
50
51
  else None
volcano_sdk/_transport.py CHANGED
@@ -7,7 +7,15 @@ from collections.abc import Mapping
7
7
  from dataclasses import dataclass
8
8
  from io import BytesIO
9
9
  from pathlib import PurePosixPath
10
- from typing import TYPE_CHECKING, Any, ParamSpec, Protocol, TypeVar, cast
10
+ from typing import (
11
+ TYPE_CHECKING,
12
+ Any,
13
+ ParamSpec,
14
+ Protocol,
15
+ TypeVar,
16
+ cast,
17
+ runtime_checkable,
18
+ )
11
19
  from uuid import UUID, uuid4
12
20
 
13
21
  import httpx
@@ -375,6 +383,7 @@ class _GeneratedTransportResponse:
375
383
  headers: Mapping[str, str]
376
384
 
377
385
 
386
+ @runtime_checkable
378
387
  class AuthRefreshTransport(Protocol):
379
388
  def auth_refresh(
380
389
  self,
@@ -384,6 +393,7 @@ class AuthRefreshTransport(Protocol):
384
393
  ) -> TransportResponse: ...
385
394
 
386
395
 
396
+ @runtime_checkable
387
397
  class AuthLogoutTransport(Protocol):
388
398
  def auth_logout(
389
399
  self,
@@ -393,6 +403,7 @@ class AuthLogoutTransport(Protocol):
393
403
  ) -> TransportResponse: ...
394
404
 
395
405
 
406
+ @runtime_checkable
396
407
  class AuthSignUpTransport(Protocol):
397
408
  def auth_signup(
398
409
  self,
@@ -404,6 +415,7 @@ class AuthSignUpTransport(Protocol):
404
415
  ) -> TransportResponse: ...
405
416
 
406
417
 
418
+ @runtime_checkable
407
419
  class AuthSignUpAnonymousTransport(Protocol):
408
420
  def auth_signup_anonymous(
409
421
  self,
@@ -413,6 +425,7 @@ class AuthSignUpAnonymousTransport(Protocol):
413
425
  ) -> TransportResponse: ...
414
426
 
415
427
 
428
+ @runtime_checkable
416
429
  class AuthConvertAnonymousTransport(Protocol):
417
430
  def auth_convert_anonymous(
418
431
  self,
@@ -424,6 +437,7 @@ class AuthConvertAnonymousTransport(Protocol):
424
437
  ) -> TransportResponse: ...
425
438
 
426
439
 
440
+ @runtime_checkable
427
441
  class AuthForgotPasswordTransport(Protocol):
428
442
  def auth_forgot_password(
429
443
  self,
@@ -433,6 +447,7 @@ class AuthForgotPasswordTransport(Protocol):
433
447
  ) -> TransportResponse: ...
434
448
 
435
449
 
450
+ @runtime_checkable
436
451
  class AuthConfirmEmailTransport(Protocol):
437
452
  def auth_confirm_email(
438
453
  self,
@@ -442,6 +457,7 @@ class AuthConfirmEmailTransport(Protocol):
442
457
  ) -> TransportResponse: ...
443
458
 
444
459
 
460
+ @runtime_checkable
445
461
  class AuthResetPasswordTransport(Protocol):
446
462
  def auth_reset_password(
447
463
  self,
@@ -452,6 +468,7 @@ class AuthResetPasswordTransport(Protocol):
452
468
  ) -> TransportResponse: ...
453
469
 
454
470
 
471
+ @runtime_checkable
455
472
  class AuthResendConfirmationTransport(Protocol):
456
473
  def auth_resend_confirmation(
457
474
  self,
@@ -461,6 +478,7 @@ class AuthResendConfirmationTransport(Protocol):
461
478
  ) -> TransportResponse: ...
462
479
 
463
480
 
481
+ @runtime_checkable
464
482
  class AuthRequestEmailChangeTransport(Protocol):
465
483
  def auth_request_email_change(
466
484
  self,
@@ -470,6 +488,7 @@ class AuthRequestEmailChangeTransport(Protocol):
470
488
  ) -> TransportResponse: ...
471
489
 
472
490
 
491
+ @runtime_checkable
473
492
  class AuthCancelEmailChangeTransport(Protocol):
474
493
  def auth_cancel_email_change(
475
494
  self,
@@ -478,6 +497,7 @@ class AuthCancelEmailChangeTransport(Protocol):
478
497
  ) -> TransportResponse: ...
479
498
 
480
499
 
500
+ @runtime_checkable
481
501
  class AuthConfirmEmailChangeTransport(Protocol):
482
502
  def auth_confirm_email_change(
483
503
  self,
@@ -487,6 +507,7 @@ class AuthConfirmEmailChangeTransport(Protocol):
487
507
  ) -> TransportResponse: ...
488
508
 
489
509
 
510
+ @runtime_checkable
490
511
  class AuthDeleteAllMySessionsTransport(Protocol):
491
512
  def auth_delete_all_my_sessions(
492
513
  self,
@@ -495,6 +516,7 @@ class AuthDeleteAllMySessionsTransport(Protocol):
495
516
  ) -> TransportResponse: ...
496
517
 
497
518
 
519
+ @runtime_checkable
498
520
  class AuthDeleteMySessionTransport(Protocol):
499
521
  def auth_delete_my_session(
500
522
  self,
@@ -504,6 +526,7 @@ class AuthDeleteMySessionTransport(Protocol):
504
526
  ) -> TransportResponse: ...
505
527
 
506
528
 
529
+ @runtime_checkable
507
530
  class AuthGetMySessionsTransport(Protocol):
508
531
  def auth_get_my_sessions(
509
532
  self,
@@ -514,6 +537,7 @@ class AuthGetMySessionsTransport(Protocol):
514
537
  ) -> TransportResponse: ...
515
538
 
516
539
 
540
+ @runtime_checkable
517
541
  class AuthListOAuthProvidersTransport(Protocol):
518
542
  def auth_list_oauth_providers(
519
543
  self,
@@ -522,6 +546,7 @@ class AuthListOAuthProvidersTransport(Protocol):
522
546
  ) -> TransportResponse: ...
523
547
 
524
548
 
549
+ @runtime_checkable
525
550
  class AuthOAuthAuthorizationURLTransport(Protocol):
526
551
  def auth_oauth_authorization_url(
527
552
  self,
@@ -533,6 +558,7 @@ class AuthOAuthAuthorizationURLTransport(Protocol):
533
558
  ) -> str: ...
534
559
 
535
560
 
561
+ @runtime_checkable
536
562
  class AuthOAuthExchangeTransport(Protocol):
537
563
  def auth_oauth_exchange(
538
564
  self,
@@ -543,6 +569,7 @@ class AuthOAuthExchangeTransport(Protocol):
543
569
  ) -> TransportResponse: ...
544
570
 
545
571
 
572
+ @runtime_checkable
546
573
  class AuthLinkOAuthProviderTransport(Protocol):
547
574
  def auth_link_oauth_provider(
548
575
  self,
@@ -552,6 +579,7 @@ class AuthLinkOAuthProviderTransport(Protocol):
552
579
  ) -> TransportResponse: ...
553
580
 
554
581
 
582
+ @runtime_checkable
555
583
  class AuthUnlinkOAuthProviderTransport(Protocol):
556
584
  def auth_unlink_oauth_provider(
557
585
  self,
@@ -561,6 +589,7 @@ class AuthUnlinkOAuthProviderTransport(Protocol):
561
589
  ) -> TransportResponse: ...
562
590
 
563
591
 
592
+ @runtime_checkable
564
593
  class AuthGetOAuthProviderTokenTransport(Protocol):
565
594
  def auth_get_oauth_provider_token(
566
595
  self,
@@ -570,6 +599,7 @@ class AuthGetOAuthProviderTokenTransport(Protocol):
570
599
  ) -> TransportResponse: ...
571
600
 
572
601
 
602
+ @runtime_checkable
573
603
  class AuthRefreshOAuthProviderTokenTransport(Protocol):
574
604
  def auth_refresh_oauth_provider_token(
575
605
  self,
@@ -579,6 +609,7 @@ class AuthRefreshOAuthProviderTokenTransport(Protocol):
579
609
  ) -> TransportResponse: ...
580
610
 
581
611
 
612
+ @runtime_checkable
582
613
  class AuthCallOAuthAPITransport(Protocol):
583
614
  def auth_call_oauth_api(
584
615
  self,
@@ -591,10 +622,12 @@ class AuthCallOAuthAPITransport(Protocol):
591
622
  ) -> TransportResponse: ...
592
623
 
593
624
 
625
+ @runtime_checkable
594
626
  class AuthGetUserTransport(Protocol):
595
627
  def auth_get_user(self, *, authorization: str) -> TransportResponse: ...
596
628
 
597
629
 
630
+ @runtime_checkable
598
631
  class AuthUpdateUserTransport(Protocol):
599
632
  def auth_update_user(
600
633
  self,
@@ -685,6 +718,7 @@ class Transport(Protocol):
685
718
  ) -> TransportResponse: ...
686
719
 
687
720
 
721
+ @runtime_checkable
688
722
  class AsyncDatabaseSelectTransport(Protocol):
689
723
  """Async database query capability used by cancellable realtime fetches."""
690
724