stackit-core 0.3.1__tar.gz → 0.4.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: stackit-core
3
- Version: v0.3.1
3
+ Version: v0.4.0
4
4
  Summary: Core functionality for the STACKIT SDK for Python
5
5
  Project-URL: Homepage, https://github.com/stackitcloud/stackit-sdk-python
6
6
  Project-URL: Issues, https://github.com/stackitcloud/stackit-sdk-python/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "stackit-core"
3
- version = "v0.3.1"
3
+ version = "v0.4.0"
4
4
  description = "Core functionality for the STACKIT SDK for Python"
5
5
  authors = [{ name = "STACKIT Developer Tools", email = "developer-tools@stackit.cloud" }]
6
6
  requires-python = ">=3.10,<4.0"
@@ -54,6 +54,8 @@ class KeyAuth(AuthBase):
54
54
  executor: ThreadPoolExecutor
55
55
  refresh_future: Optional[futures.Future]
56
56
  service_account_key: ServiceAccountKey
57
+ _stop: threading.Event
58
+ _refresh_thread: Optional[threading.Thread]
57
59
 
58
60
  def __init__(
59
61
  self,
@@ -66,6 +68,8 @@ class KeyAuth(AuthBase):
66
68
  self.executor = ThreadPoolExecutor(max_workers=1)
67
69
  self.access_token = None
68
70
  self.refresh_future = None
71
+ self._stop = threading.Event()
72
+ self._refresh_thread = None
69
73
  self.__create_initial_token()
70
74
  self.__fetch_token_from_endpoint()
71
75
  self.__start_token_refresh_task()
@@ -92,6 +96,24 @@ class KeyAuth(AuthBase):
92
96
  r.headers["Authorization"] = f"Bearer {self.access_token}"
93
97
  return r
94
98
 
99
+ def close(self) -> None:
100
+ """Stop the background token refresh and release its worker.
101
+
102
+ Call this when the instance is no longer needed, for example after
103
+ replacing it during key rotation in a long-running process. Without it
104
+ the refresh thread keeps running and keeps requesting tokens. When it
105
+ returns, no refresh is running and none will start. Safe to call more
106
+ than once.
107
+ """
108
+ # Set under the lock so the refresh loop cannot submit after shutdown.
109
+ with self.lock:
110
+ self._stop.set()
111
+ atexit.unregister(self.__shutdown)
112
+ thread = self._refresh_thread
113
+ if thread is not None and thread is not threading.current_thread():
114
+ thread.join()
115
+ self.executor.shutdown(wait=True, cancel_futures=True)
116
+
95
117
  def __create_initial_token(self) -> None:
96
118
  payload = {
97
119
  "iss": self.service_account_key.credentials.issuer,
@@ -126,9 +148,10 @@ class KeyAuth(AuthBase):
126
148
 
127
149
  def __start_token_refresh_task(self):
128
150
  def token_refresh_task():
129
- while True:
130
- time.sleep(self.TOKEN_EXPIRY_CHECK_INTERVAL.total_seconds())
151
+ while not self._stop.wait(self.TOKEN_EXPIRY_CHECK_INTERVAL.total_seconds()):
131
152
  with self.lock:
153
+ if self._stop.is_set():
154
+ return
132
155
  if self.refresh_future is not None and self.refresh_future.done():
133
156
  self.refresh_future.result()
134
157
  if self.__is_token_expired(self.access_token) and (
@@ -139,6 +162,7 @@ class KeyAuth(AuthBase):
139
162
  thread = threading.Thread(target=token_refresh_task)
140
163
  thread.daemon = True
141
164
  thread.start()
165
+ self._refresh_thread = thread
142
166
 
143
167
  def __refresh_token(self):
144
168
  with self.lock:
@@ -156,6 +180,8 @@ class KeyAuth(AuthBase):
156
180
 
157
181
  last_exception = None
158
182
  for _ in range(self.MAX_REFRESH_RETRIES):
183
+ if self._stop.is_set():
184
+ return
159
185
  try:
160
186
  response = requests.post(self.token_endpoint, data=body, timeout=self.timeout)
161
187
  response.raise_for_status()
File without changes
File without changes
File without changes
File without changes