otdf-python 0.4.0__py3-none-any.whl → 0.4.1__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.
Files changed (52) hide show
  1. otdf_python/__init__.py +1 -2
  2. otdf_python/__main__.py +1 -2
  3. otdf_python/address_normalizer.py +8 -10
  4. otdf_python/aesgcm.py +8 -0
  5. otdf_python/assertion_config.py +21 -0
  6. otdf_python/asym_crypto.py +18 -22
  7. otdf_python/auth_headers.py +7 -6
  8. otdf_python/autoconfigure_utils.py +22 -6
  9. otdf_python/cli.py +5 -5
  10. otdf_python/collection_store.py +13 -0
  11. otdf_python/collection_store_impl.py +5 -0
  12. otdf_python/config.py +13 -0
  13. otdf_python/connect_client.py +1 -0
  14. otdf_python/constants.py +2 -0
  15. otdf_python/crypto_utils.py +4 -0
  16. otdf_python/dpop.py +3 -5
  17. otdf_python/ecc_constants.py +12 -14
  18. otdf_python/ecc_mode.py +7 -2
  19. otdf_python/ecdh.py +24 -25
  20. otdf_python/eckeypair.py +5 -0
  21. otdf_python/header.py +5 -0
  22. otdf_python/invalid_zip_exception.py +6 -2
  23. otdf_python/kas_client.py +48 -55
  24. otdf_python/kas_connect_rpc_client.py +16 -19
  25. otdf_python/kas_info.py +4 -3
  26. otdf_python/kas_key_cache.py +10 -9
  27. otdf_python/key_type.py +4 -0
  28. otdf_python/key_type_constants.py +4 -11
  29. otdf_python/manifest.py +24 -0
  30. otdf_python/nanotdf.py +34 -24
  31. otdf_python/nanotdf_ecdsa_struct.py +5 -9
  32. otdf_python/nanotdf_type.py +12 -0
  33. otdf_python/policy_binding_serializer.py +6 -4
  34. otdf_python/policy_info.py +6 -0
  35. otdf_python/policy_object.py +8 -0
  36. otdf_python/policy_stub.py +2 -0
  37. otdf_python/resource_locator.py +22 -13
  38. otdf_python/sdk.py +49 -57
  39. otdf_python/sdk_builder.py +58 -41
  40. otdf_python/sdk_exceptions.py +11 -1
  41. otdf_python/symmetric_and_payload_config.py +6 -0
  42. otdf_python/tdf.py +47 -10
  43. otdf_python/tdf_reader.py +10 -13
  44. otdf_python/tdf_writer.py +5 -0
  45. otdf_python/token_source.py +4 -3
  46. otdf_python/version.py +5 -0
  47. otdf_python/zip_reader.py +10 -2
  48. otdf_python/zip_writer.py +11 -0
  49. {otdf_python-0.4.0.dist-info → otdf_python-0.4.1.dist-info}/METADATA +1 -1
  50. {otdf_python-0.4.0.dist-info → otdf_python-0.4.1.dist-info}/RECORD +52 -52
  51. {otdf_python-0.4.0.dist-info → otdf_python-0.4.1.dist-info}/WHEEL +1 -1
  52. {otdf_python-0.4.0.dist-info → otdf_python-0.4.1.dist-info}/licenses/LICENSE +0 -0
otdf_python/sdk.py CHANGED
@@ -1,6 +1,4 @@
1
- """
2
- Python port of the main SDK class for OpenTDF platform interaction.
3
- """
1
+ """The main SDK class for OpenTDF platform interaction."""
4
2
 
5
3
  from contextlib import AbstractContextManager
6
4
  from io import BytesIO
@@ -13,13 +11,10 @@ from otdf_python.tdf import TDF, TDFReader, TDFReaderConfig
13
11
 
14
12
 
15
13
  class KAS(AbstractContextManager):
16
- """
17
- KAS (Key Access Service) interface to define methods related to key access and management.
18
- """
14
+ """KAS (Key Access Service) interface to define methods related to key access and management."""
19
15
 
20
16
  def get_public_key(self, kas_info: Any) -> Any:
21
- """
22
- Retrieves the public key from the KAS for RSA operations.
17
+ """Retrieve the public key from KAS for RSA operations.
23
18
  If the public key is cached, returns the cached value.
24
19
  Otherwise, makes a request to the KAS.
25
20
 
@@ -31,6 +26,7 @@ class KAS(AbstractContextManager):
31
26
 
32
27
  Raises:
33
28
  SDKException: If there's an error retrieving the public key
29
+
34
30
  """
35
31
  # Delegate to the underlying KAS client which handles authentication properly
36
32
  return self._kas_client.get_public_key(kas_info)
@@ -42,14 +38,14 @@ class KAS(AbstractContextManager):
42
38
  sdk_ssl_verify=True,
43
39
  use_plaintext=False,
44
40
  ):
45
- """
46
- Initialize the KAS client
41
+ """Initialize the KAS client.
47
42
 
48
43
  Args:
49
44
  platform_url: URL of the platform
50
45
  token_source: Function that returns an authentication token
51
46
  sdk_ssl_verify: Whether to verify SSL certificates
52
47
  use_plaintext: Whether to use plaintext HTTP connections instead of HTTPS
48
+
53
49
  """
54
50
  from .kas_client import KASClient
55
51
 
@@ -64,8 +60,7 @@ class KAS(AbstractContextManager):
64
60
  self._use_plaintext = use_plaintext
65
61
 
66
62
  def get_ec_public_key(self, kas_info: Any, curve: Any) -> Any:
67
- """
68
- Retrieves the EC public key from the KAS.
63
+ """Retrieve the EC public key from KAS.
69
64
 
70
65
  Args:
71
66
  kas_info: KASInfo object containing the URL
@@ -73,6 +68,7 @@ class KAS(AbstractContextManager):
73
68
 
74
69
  Returns:
75
70
  Updated KASInfo object with KID and PublicKey populated
71
+
76
72
  """
77
73
  # Set algorithm to "ec:<curve>"
78
74
  from copy import copy
@@ -82,8 +78,7 @@ class KAS(AbstractContextManager):
82
78
  return self.get_public_key(kas_info_copy)
83
79
 
84
80
  def unwrap(self, key_access: Any, policy: str, session_key_type: Any) -> bytes:
85
- """
86
- Unwraps the key using the KAS.
81
+ """Unwraps the key using the KAS.
87
82
 
88
83
  Args:
89
84
  key_access: KeyAccess object containing the wrapped key
@@ -92,6 +87,7 @@ class KAS(AbstractContextManager):
92
87
 
93
88
  Returns:
94
89
  Unwrapped key as bytes
90
+
95
91
  """
96
92
  return self._kas_client.unwrap(key_access, policy, session_key_type)
97
93
 
@@ -104,8 +100,7 @@ class KAS(AbstractContextManager):
104
100
  kas_private_key: str | None = None,
105
101
  mock: bool = False,
106
102
  ) -> bytes:
107
- """
108
- Unwraps the NanoTDF key using the KAS. If mock=True, performs local unwrap using the private key (for tests).
103
+ """Unwraps the NanoTDF key using the KAS. If mock=True, performs local unwrap using the private key (for tests).
109
104
 
110
105
  Args:
111
106
  curve: EC curve used
@@ -117,6 +112,7 @@ class KAS(AbstractContextManager):
117
112
 
118
113
  Returns:
119
114
  Unwrapped key as bytes
115
+
120
116
  """
121
117
  if mock and wrapped_key and kas_private_key:
122
118
  from .asym_crypto import AsymDecryption
@@ -128,16 +124,16 @@ class KAS(AbstractContextManager):
128
124
  raise NotImplementedError("KAS unwrap_nanotdf not implemented.")
129
125
 
130
126
  def get_key_cache(self) -> Any:
131
- """
132
- Returns the KAS key cache.
127
+ """Return the KAS key cache.
133
128
 
134
129
  Returns:
135
130
  The KAS key cache object
131
+
136
132
  """
137
133
  return self._kas_client.get_key_cache()
138
134
 
139
135
  def close(self):
140
- """Closes resources associated with the KAS interface"""
136
+ """Close resources associated with KAS interface."""
141
137
  pass
142
138
 
143
139
  def __exit__(self, exc_type, exc_val, exc_tb):
@@ -145,16 +141,15 @@ class KAS(AbstractContextManager):
145
141
 
146
142
 
147
143
  class SDK(AbstractContextManager):
144
+ """SDK for OpenTDF platform interaction."""
145
+
148
146
  def new_tdf_config(
149
147
  self,
150
148
  attributes: list[str] | None = None,
151
149
  kas_info_list: list[KASInfo] | None = None,
152
150
  **kwargs,
153
151
  ) -> TDFConfig:
154
- """
155
- Create a TDFConfig with default kas_info_list from the SDK's platform_url.
156
- """
157
-
152
+ """Create a TDFConfig with default kas_info_list from the SDK's platform_url."""
158
153
  if self.platform_url is None:
159
154
  raise SDKException("Cannot create TDFConfig: SDK platform_url is not set.")
160
155
 
@@ -214,19 +209,16 @@ class SDK(AbstractContextManager):
214
209
  """
215
210
 
216
211
  class Services(AbstractContextManager):
217
- """
218
- The Services interface provides access to various platform service clients and KAS.
219
- """
212
+ """The Services interface provides access to various platform service clients and KAS."""
220
213
 
221
214
  def kas(self) -> KAS:
222
- """
223
- Returns the KAS client for key access operations.
215
+ """Return the KAS client for key access operations.
224
216
  This should be implemented to return an instance of KAS.
225
217
  """
226
218
  raise NotImplementedError
227
219
 
228
220
  def close(self):
229
- """Closes resources associated with the services"""
221
+ """Close resources associated with the services."""
230
222
  pass
231
223
 
232
224
  def __exit__(self, exc_type, exc_val, exc_tb):
@@ -239,14 +231,14 @@ class SDK(AbstractContextManager):
239
231
  ssl_verify: bool = True,
240
232
  use_plaintext: bool = False,
241
233
  ):
242
- """
243
- Initializes a new SDK instance.
234
+ """Initialize a new SDK instance.
244
235
 
245
236
  Args:
246
237
  services: The services interface implementation
247
238
  platform_url: Optional platform base URL
248
239
  ssl_verify: Whether to verify SSL certificates (default: True)
249
240
  use_plaintext: Whether to use HTTP instead of HTTPS (default: False)
241
+
250
242
  """
251
243
  self.services = services
252
244
  self.platform_url = platform_url
@@ -254,20 +246,20 @@ class SDK(AbstractContextManager):
254
246
  self._use_plaintext = use_plaintext
255
247
 
256
248
  def __exit__(self, exc_type, exc_val, exc_tb):
257
- """Clean up resources when exiting context manager"""
249
+ """Clean up resources when exiting context manager."""
258
250
  self.close()
259
251
 
260
252
  def close(self):
261
- """Close the SDK and release resources"""
253
+ """Close the SDK and release resources."""
262
254
  if hasattr(self.services, "close"):
263
255
  self.services.close()
264
256
 
265
257
  def get_services(self) -> "SDK.Services":
266
- """Returns the services interface"""
258
+ """Return the services interface."""
267
259
  return self.services
268
260
 
269
261
  def get_platform_url(self) -> str | None:
270
- """Returns the platform URL if set"""
262
+ """Return the platform URL if set."""
271
263
  return self.platform_url
272
264
 
273
265
  def load_tdf(
@@ -275,8 +267,7 @@ class SDK(AbstractContextManager):
275
267
  tdf_data: bytes | BinaryIO | BytesIO,
276
268
  config: TDFReaderConfig | None = None,
277
269
  ) -> TDFReader:
278
- """
279
- Loads a TDF from the provided data, optionally according to the config.
270
+ """Load a TDF from the provided data, optionally according to config.
280
271
 
281
272
  Args:
282
273
  tdf_data: The TDF data as bytes, file object, or BytesIO
@@ -287,6 +278,7 @@ class SDK(AbstractContextManager):
287
278
 
288
279
  Raises:
289
280
  SDKException: If there's an error loading the TDF
281
+
290
282
  """
291
283
  tdf = TDF(self.services)
292
284
  if config is None:
@@ -300,8 +292,7 @@ class SDK(AbstractContextManager):
300
292
  config,
301
293
  output_stream: BinaryIO | None = None,
302
294
  ):
303
- """
304
- Creates a TDF with the provided payload.
295
+ """Create a TDF with the provided payload.
305
296
 
306
297
  Args:
307
298
  payload: The payload data as bytes, file object, or BytesIO
@@ -313,6 +304,7 @@ class SDK(AbstractContextManager):
313
304
 
314
305
  Raises:
315
306
  SDKException: If there's an error creating the TDF
307
+
316
308
  """
317
309
  tdf = TDF(self.services)
318
310
  return tdf.create_tdf(payload, config, output_stream)
@@ -320,8 +312,7 @@ class SDK(AbstractContextManager):
320
312
  def create_nano_tdf(
321
313
  self, payload: bytes | BytesIO, output_stream: BinaryIO, config: "NanoTDFConfig"
322
314
  ) -> int:
323
- """
324
- Creates a NanoTDF with the provided payload.
315
+ """Create a NanoTDF with the provided payload.
325
316
 
326
317
  Args:
327
318
  payload: The payload data as bytes or BytesIO
@@ -333,6 +324,7 @@ class SDK(AbstractContextManager):
333
324
 
334
325
  Raises:
335
326
  SDKException: If there's an error creating the NanoTDF
327
+
336
328
  """
337
329
  nano_tdf = NanoTDF(self.services)
338
330
  return nano_tdf.create_nano_tdf(payload, output_stream, config)
@@ -343,8 +335,7 @@ class SDK(AbstractContextManager):
343
335
  output_stream: BinaryIO,
344
336
  config: NanoTDFConfig,
345
337
  ) -> None:
346
- """
347
- Reads a NanoTDF and writes the payload to the output stream.
338
+ """Read a NanoTDF and write the payload to the output stream.
348
339
 
349
340
  Args:
350
341
  nano_tdf_data: The NanoTDF data as bytes or BytesIO
@@ -353,20 +344,21 @@ class SDK(AbstractContextManager):
353
344
 
354
345
  Raises:
355
346
  SDKException: If there's an error reading the NanoTDF
347
+
356
348
  """
357
349
  nano_tdf = NanoTDF(self.services)
358
350
  nano_tdf.read_nano_tdf(nano_tdf_data, output_stream, config)
359
351
 
360
352
  @staticmethod
361
353
  def is_tdf(data: bytes | BinaryIO) -> bool:
362
- """
363
- Checks if the provided data is a TDF.
354
+ """Check if the provided data is a TDF.
364
355
 
365
356
  Args:
366
357
  data: The data to check
367
358
 
368
359
  Returns:
369
360
  bool: True if the data is a TDF, False otherwise
361
+
370
362
  """
371
363
  import zipfile
372
364
  from io import BytesIO
@@ -383,54 +375,54 @@ class SDK(AbstractContextManager):
383
375
 
384
376
  # Exception classes - SDK-specific exceptions that can occur during operations
385
377
  class SplitKeyException(SDKException):
386
- """Thrown when the SDK encounters an error related to split key operations"""
378
+ """Throw when SDK encounters error related to split key operations."""
387
379
 
388
380
  pass
389
381
 
390
382
  class DataSizeNotSupported(SDKException):
391
- """Thrown when the user attempts to create a TDF with a size larger than the maximum size"""
383
+ """Throw when user attempts to create TDF larger than maximum size."""
392
384
 
393
385
  pass
394
386
 
395
387
  class KasInfoMissing(SDKException):
396
- """Thrown during TDF creation when no KAS information is present"""
388
+ """Throw during TDF creation when no KAS information is present."""
397
389
 
398
390
  pass
399
391
 
400
392
  class KasPublicKeyMissing(SDKException):
401
- """Thrown during encryption when the SDK cannot retrieve the public key for a KAS"""
393
+ """Throw during encryption when SDK cannot retrieve public key for KAS."""
402
394
 
403
395
  pass
404
396
 
405
397
  class TamperException(SDKException):
406
- """Base class for exceptions related to signature mismatches"""
398
+ """Base class for exceptions related to signature mismatches."""
407
399
 
408
400
  def __init__(self, error_message: str):
401
+ """Initialize tamper exception."""
409
402
  super().__init__(f"[tamper detected] {error_message}")
410
403
 
411
404
  class RootSignatureValidationException(TamperException):
412
- """Thrown when the root signature validation fails"""
413
-
414
- pass
405
+ """Throw when root signature validation fails."""
415
406
 
416
407
  class SegmentSignatureMismatch(TamperException):
417
- """Thrown when a segment signature does not match the expected value"""
408
+ """Throw when segment signature does not match expected value."""
418
409
 
419
410
  pass
420
411
 
421
412
  class KasBadRequestException(SDKException):
422
- """Thrown when the KAS returns a bad request response"""
413
+ """Throw when KAS returns bad request response."""
423
414
 
424
415
  pass
425
416
 
426
417
  class KasAllowlistException(SDKException):
427
- """Thrown when the KAS allowlist check fails"""
418
+ """Throw when KAS allowlist check fails."""
428
419
 
429
420
  pass
430
421
 
431
422
  class AssertionException(SDKException):
432
- """Thrown when an assertion validation fails"""
423
+ """Throw when an assertion validation fails."""
433
424
 
434
425
  def __init__(self, error_message: str, assertion_id: str):
426
+ """Initialize exception."""
435
427
  super().__init__(error_message)
436
428
  self.assertion_id = assertion_id
@@ -1,5 +1,5 @@
1
- """
2
- Python port of the SDKBuilder class for OpenTDF platform interaction.
1
+ """SDKBuilder class for OpenTDF platform interaction.
2
+
3
3
  Provides methods to configure and build SDK instances.
4
4
  """
5
5
 
@@ -19,6 +19,8 @@ logger = logging.getLogger(__name__)
19
19
 
20
20
  @dataclass
21
21
  class OAuthConfig:
22
+ """OAuth configuration."""
23
+
22
24
  client_id: str
23
25
  client_secret: str
24
26
  grant_type: str = "client_credentials"
@@ -28,9 +30,7 @@ class OAuthConfig:
28
30
 
29
31
 
30
32
  class SDKBuilder:
31
- """
32
- A builder class for creating instances of the SDK class.
33
- """
33
+ """A builder class for creating instances of the SDK class."""
34
34
 
35
35
  PLATFORM_ISSUER = "platform_issuer"
36
36
 
@@ -38,6 +38,7 @@ class SDKBuilder:
38
38
  _platform_url = None
39
39
 
40
40
  def __init__(self):
41
+ """Initialize SDK builder."""
41
42
  self.platform_endpoint: str | None = None
42
43
  self.issuer_endpoint: str | None = None
43
44
  self.oauth_config: OAuthConfig | None = None
@@ -49,29 +50,33 @@ class SDKBuilder:
49
50
 
50
51
  @staticmethod
51
52
  def new_builder() -> "SDKBuilder":
52
- """
53
- Creates a new SDKBuilder instance.
53
+ """Create a new SDKBuilder instance.
54
+
54
55
  Returns:
55
56
  SDKBuilder: A new builder instance
57
+
56
58
  """
57
59
  return SDKBuilder()
58
60
 
59
61
  @staticmethod
60
62
  def get_platform_url() -> str | None:
61
- """
62
- Gets the last set platform URL.
63
+ """Get the last set platform URL.
64
+
63
65
  Returns:
64
66
  str | None: The platform URL or None if not set
67
+
65
68
  """
66
69
  return SDKBuilder._platform_url
67
70
 
68
71
  def ssl_context_from_directory(self, certs_dir_path: str) -> "SDKBuilder":
69
- """
70
- Add SSL Context with trusted certs from certDirPath
72
+ """Add SSL context with trusted certs from certDirPath.
73
+
71
74
  Args:
72
75
  certs_dir_path: Path to a directory containing .pem or .crt trusted certs
76
+
73
77
  Returns:
74
78
  self: The builder instance for chaining
79
+
75
80
  """
76
81
  self.cert_paths = []
77
82
 
@@ -91,13 +96,14 @@ class SDKBuilder:
91
96
  return self
92
97
 
93
98
  def client_secret(self, client_id: str, client_secret: str) -> "SDKBuilder":
94
- """
95
- Sets client credentials for OAuth 2.0 client_credentials grant.
99
+ """Set client credentials for OAuth 2.0 client_credentials grant.
100
+
96
101
  Args:
97
102
  client_id: The OAuth client ID
98
103
  client_secret: The OAuth client secret
99
104
  Returns:
100
105
  self: The builder instance for chaining
106
+
101
107
  """
102
108
  self.oauth_config = OAuthConfig(
103
109
  client_id=client_id, client_secret=client_secret
@@ -105,12 +111,13 @@ class SDKBuilder:
105
111
  return self
106
112
 
107
113
  def set_platform_endpoint(self, endpoint: str) -> "SDKBuilder":
108
- """
109
- Sets the OpenTDF platform endpoint URL.
114
+ """Set the OpenTDF platform endpoint URL.
115
+
110
116
  Args:
111
117
  endpoint: The platform endpoint URL
112
118
  Returns:
113
119
  self: The builder instance for chaining
120
+
114
121
  """
115
122
  # Normalize the endpoint URL
116
123
  if endpoint and not (
@@ -127,12 +134,13 @@ class SDKBuilder:
127
134
  return self
128
135
 
129
136
  def set_issuer_endpoint(self, issuer: str) -> "SDKBuilder":
130
- """
131
- Sets the OpenID Connect issuer endpoint URL.
137
+ """Set the OpenID Connect issuer endpoint URL.
138
+
132
139
  Args:
133
140
  issuer: The issuer endpoint URL
134
141
  Returns:
135
142
  self: The builder instance for chaining
143
+
136
144
  """
137
145
  # Normalize the issuer URL
138
146
  if issuer and not (
@@ -146,12 +154,13 @@ class SDKBuilder:
146
154
  def use_insecure_plaintext_connection(
147
155
  self, use_plaintext: bool = True
148
156
  ) -> "SDKBuilder":
149
- """
150
- Configures whether to use plain text (HTTP) connection instead of HTTPS.
157
+ """Configure whether to use plain text (HTTP) instead of HTTPS.
158
+
151
159
  Args:
152
160
  use_plaintext: Whether to use plain text connection
153
161
  Returns:
154
162
  self: The builder instance for chaining
163
+
155
164
  """
156
165
  self.use_plaintext = use_plaintext
157
166
 
@@ -168,12 +177,13 @@ class SDKBuilder:
168
177
  return self
169
178
 
170
179
  def use_insecure_skip_verify(self, skip_verify: bool = True) -> "SDKBuilder":
171
- """
172
- Configures whether to skip SSL verification.
180
+ """Configure whether to skip SSL verification.
181
+
173
182
  Args:
174
183
  skip_verify: Whether to skip SSL verification
175
184
  Returns:
176
185
  self: The builder instance for chaining
186
+
177
187
  """
178
188
  self.insecure_skip_verify = skip_verify
179
189
 
@@ -184,21 +194,23 @@ class SDKBuilder:
184
194
  return self
185
195
 
186
196
  def bearer_token(self, token: str) -> "SDKBuilder":
187
- """
188
- Sets a bearer token to use for authorization.
197
+ """Set a bearer token to use for authorization.
198
+
189
199
  Args:
190
200
  token: The bearer token
191
201
  Returns:
192
202
  self: The builder instance for chaining
203
+
193
204
  """
194
205
  self.auth_token = token
195
206
  return self
196
207
 
197
208
  def _discover_token_endpoint_from_platform(self) -> None:
198
- """
199
- Discover token endpoint using OpenTDF platform configuration.
209
+ """Discover token endpoint using OpenTDF platform configuration.
210
+
200
211
  Raises:
201
212
  AutoConfigureException: If discovery fails
213
+
202
214
  """
203
215
  if not self.platform_endpoint or not self.oauth_config:
204
216
  return
@@ -232,12 +244,13 @@ class SDKBuilder:
232
244
  self._discover_token_endpoint_from_issuer(platform_issuer)
233
245
 
234
246
  def _discover_token_endpoint_from_issuer(self, issuer_url: str) -> None:
235
- """
236
- Discover token endpoint using OIDC discovery from issuer.
247
+ """Discover token endpoint using OIDC discovery from issuer.
248
+
237
249
  Args:
238
250
  issuer_url: The issuer URL to use for discovery
239
251
  Raises:
240
252
  AutoConfigureException: If discovery fails
253
+
241
254
  """
242
255
  if not self.oauth_config:
243
256
  return
@@ -260,10 +273,11 @@ class SDKBuilder:
260
273
  )
261
274
 
262
275
  def _discover_token_endpoint(self) -> None:
263
- """
264
- Discover the token endpoint using available configuration.
276
+ """Discover the token endpoint using available configuration.
277
+
265
278
  Raises:
266
279
  AutoConfigureException: If discovery fails
280
+
267
281
  """
268
282
  # Try platform endpoint first
269
283
  if self.platform_endpoint:
@@ -283,7 +297,7 @@ class SDKBuilder:
283
297
  pass
284
298
  raise AutoConfigureException(
285
299
  f"Error during token endpoint discovery: {e!s}"
286
- )
300
+ ) from e
287
301
 
288
302
  # Fall back to explicit issuer endpoint
289
303
  if self.issuer_endpoint:
@@ -297,12 +311,13 @@ class SDKBuilder:
297
311
  )
298
312
 
299
313
  def _get_token_from_client_credentials(self) -> str:
300
- """
301
- Obtains an OAuth token using client credentials.
314
+ """Obtain an OAuth token using client credentials.
315
+
302
316
  Returns:
303
- str: The access token
317
+ str: The OAuth access token
304
318
  Raises:
305
319
  AutoConfigureException: If token acquisition fails
320
+
306
321
  """
307
322
  if not self.oauth_config:
308
323
  raise AutoConfigureException("OAuth configuration is not set")
@@ -341,15 +356,18 @@ class SDKBuilder:
341
356
  )
342
357
 
343
358
  except Exception as e:
344
- raise AutoConfigureException(f"Error during token acquisition: {e!s}")
359
+ raise AutoConfigureException(
360
+ f"Error during token acquisition: {e!s}"
361
+ ) from e
345
362
 
346
363
  def _create_services(self) -> SDK.Services:
347
- """
348
- Creates service client instances.
364
+ """Create service client instances.
365
+
349
366
  Returns:
350
367
  SDK.Services: The service client instances
351
368
  Raises:
352
369
  AutoConfigureException: If service creation fails
370
+
353
371
  """
354
372
  # For now, return a simple implementation of Services
355
373
  # In a real implementation, this would create actual service clients
@@ -364,9 +382,7 @@ class SDKBuilder:
364
382
  self._builder = builder_instance
365
383
 
366
384
  def kas(self) -> KAS:
367
- """
368
- Returns the KAS interface with SSL verification settings.
369
- """
385
+ """Return the KAS interface with SSL verification settings."""
370
386
  platform_url = SDKBuilder.get_platform_url()
371
387
 
372
388
  # Create a token source function that can refresh tokens
@@ -394,12 +410,13 @@ class SDKBuilder:
394
410
  return ServicesImpl(self)
395
411
 
396
412
  def build(self) -> SDK:
397
- """
398
- Builds and returns an SDK instance with the configured properties.
413
+ """Build and return an SDK instance with configured properties.
414
+
399
415
  Returns:
400
416
  SDK: The configured SDK instance
401
417
  Raises:
402
418
  AutoConfigureException: If the build fails
419
+
403
420
  """
404
421
  if not self.platform_endpoint:
405
422
  raise AutoConfigureException("Platform endpoint is not set")
@@ -1,16 +1,26 @@
1
+ """SDK-specific exception classes."""
2
+
3
+
1
4
  class SDKException(Exception):
5
+ """Base SDK exception class."""
6
+
2
7
  def __init__(self, message, reason=None):
8
+ """Initialize exception."""
3
9
  super().__init__(message)
4
10
  self.reason = reason
5
11
 
6
12
 
7
13
  class AutoConfigureException(SDKException):
14
+ """Exception for SDK auto-configuration failures."""
15
+
8
16
  def __init__(self, message, cause=None):
17
+ """Initialize exception."""
9
18
  super().__init__(message, cause)
10
19
 
11
20
 
12
21
  class KASBadRequestException(SDKException):
13
- """Thrown when the KAS returns a bad request response or other client request errors."""
22
+ """Exception for KAS bad request or client errors."""
14
23
 
15
24
  def __init__(self, message):
25
+ """Initialize exception."""
16
26
  super().__init__(message)
@@ -1,10 +1,16 @@
1
+ """Symmetric encryption and payload configuration."""
2
+
3
+
1
4
  class SymmetricAndPayloadConfig:
5
+ """Symmetric and payload configuration."""
6
+
2
7
  def __init__(
3
8
  self,
4
9
  cipher_type: int = 0,
5
10
  signature_ecc_mode: int = 0,
6
11
  has_signature: bool = True,
7
12
  ):
13
+ """Initialize symmetric and payload configuration."""
8
14
  self.cipher_type = cipher_type
9
15
  self.signature_ecc_mode = signature_ecc_mode
10
16
  self.has_signature = has_signature