surfdataverse 4.2.0__tar.gz → 4.2.2__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.3
2
2
  Name: surfdataverse
3
- Version: 4.2.0
3
+ Version: 4.2.2
4
4
  Summary: A Python package for ionysis Microsoft Dataverse integration
5
5
  Keywords: dataverse,microsoft,crm,api
6
6
  Author: ionysis
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "surfdataverse"
7
- version = "4.2.0"
7
+ version = "4.2.2"
8
8
  description = "A Python package for ionysis Microsoft Dataverse integration"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -200,32 +200,33 @@ class DataverseClient:
200
200
 
201
201
 
202
202
  class MsalStorageCredential:
203
- """Azure TokenCredential backed by MSAL.
204
-
205
- If the user already authenticated for Dataverse in this session, acquiring a
206
- storage token is silent (no browser popup). Otherwise the browser opens once.
207
- """
203
+ """Azure TokenCredential over one shared MSAL app + on-disk cache.
204
+ First resource opens a browser; every later resource — and every restart,
205
+ because the cache is persisted here is silent."""
208
206
 
209
207
  def __init__(self, client_id: str, authority: str):
210
- token_cache, _ = get_token_cache()
208
+ self._cache, self._cache_file = get_token_cache()
211
209
  self._app = msal.PublicClientApplication(
212
- client_id, authority=authority, token_cache=token_cache
210
+ client_id, authority=authority, token_cache=self._cache
213
211
  )
214
212
 
215
213
  def get_token(self, *scopes, **kwargs) -> AccessToken:
216
- scope = list(scopes) if scopes else [kwargs.get("scope")]
214
+ scope = list(scopes) if scopes else [kwargs["scope"]]
217
215
  result = None
218
- accounts = self._app.get_accounts()
219
- if accounts:
220
- result = self._app.acquire_token_silent(scopes=scope, account=accounts[0])
216
+ if accounts := self._app.get_accounts():
217
+ result = self._app.acquire_token_silent(scope, account=accounts[0])
221
218
  if not result:
222
219
  result = self._app.acquire_token_interactive(scope)
223
220
  if "access_token" not in result:
224
- raise RuntimeError(
225
- f"Blob storage authentication failed: {result.get('error_description', result.get('error'))}"
221
+ raise AuthenticationError(
222
+ f"Authentication failed for {scope}: "
223
+ f"{result.get('error_description', result.get('error'))}"
226
224
  )
227
- expires_on = int(time.time()) + result.get("expires_in", 3600)
228
- return AccessToken(result["access_token"], expires_on)
225
+ if self._cache.has_state_changed: # persist for ALL consumers
226
+ self._cache_file.write_text(self._cache.serialize())
227
+ return AccessToken(
228
+ result["access_token"], int(time.time()) + result.get("expires_in", 3600)
229
+ )
229
230
 
230
231
 
231
232
  class DataverseBase:
File without changes