otdf-python 0.4.0__py3-none-any.whl → 0.4.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.
Files changed (96) 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 +21 -7
  9. otdf_python/cli.py +5 -5
  10. otdf_python/collection_store.py +13 -1
  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 -31
  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 +66 -55
  24. otdf_python/kas_connect_rpc_client.py +75 -38
  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 +30 -28
  31. otdf_python/nanotdf_ecdsa_struct.py +5 -11
  32. otdf_python/nanotdf_type.py +13 -1
  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 +51 -73
  39. otdf_python/sdk_builder.py +60 -47
  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.2.dist-info}/METADATA +3 -2
  50. {otdf_python-0.4.0.dist-info → otdf_python-0.4.2.dist-info}/RECORD +81 -72
  51. {otdf_python-0.4.0.dist-info → otdf_python-0.4.2.dist-info}/WHEEL +1 -1
  52. otdf_python_proto/__init__.py +2 -6
  53. otdf_python_proto/authorization/__init__.py +10 -0
  54. otdf_python_proto/authorization/authorization_connect.py +250 -0
  55. otdf_python_proto/authorization/v2/authorization_connect.py +315 -0
  56. otdf_python_proto/entityresolution/__init__.py +10 -0
  57. otdf_python_proto/entityresolution/entity_resolution_connect.py +185 -0
  58. otdf_python_proto/entityresolution/v2/entity_resolution_connect.py +185 -0
  59. otdf_python_proto/kas/__init__.py +2 -2
  60. otdf_python_proto/kas/kas_connect.py +259 -0
  61. otdf_python_proto/policy/actions/__init__.py +11 -0
  62. otdf_python_proto/policy/actions/actions_connect.py +380 -0
  63. otdf_python_proto/policy/attributes/__init__.py +11 -0
  64. otdf_python_proto/policy/attributes/attributes_connect.py +1310 -0
  65. otdf_python_proto/policy/kasregistry/__init__.py +11 -0
  66. otdf_python_proto/policy/kasregistry/key_access_server_registry_connect.py +912 -0
  67. otdf_python_proto/policy/keymanagement/__init__.py +11 -0
  68. otdf_python_proto/policy/keymanagement/key_management_connect.py +380 -0
  69. otdf_python_proto/policy/namespaces/__init__.py +11 -0
  70. otdf_python_proto/policy/namespaces/namespaces_connect.py +648 -0
  71. otdf_python_proto/policy/registeredresources/__init__.py +11 -0
  72. otdf_python_proto/policy/registeredresources/registered_resources_connect.py +770 -0
  73. otdf_python_proto/policy/resourcemapping/__init__.py +11 -0
  74. otdf_python_proto/policy/resourcemapping/resource_mapping_connect.py +790 -0
  75. otdf_python_proto/policy/subjectmapping/__init__.py +11 -0
  76. otdf_python_proto/policy/subjectmapping/subject_mapping_connect.py +851 -0
  77. otdf_python_proto/policy/unsafe/__init__.py +11 -0
  78. otdf_python_proto/policy/unsafe/unsafe_connect.py +705 -0
  79. otdf_python_proto/wellknownconfiguration/__init__.py +10 -0
  80. otdf_python_proto/wellknownconfiguration/wellknown_configuration_connect.py +124 -0
  81. otdf_python_proto/authorization/authorization_pb2_connect.py +0 -191
  82. otdf_python_proto/authorization/v2/authorization_pb2_connect.py +0 -233
  83. otdf_python_proto/entityresolution/entity_resolution_pb2_connect.py +0 -149
  84. otdf_python_proto/entityresolution/v2/entity_resolution_pb2_connect.py +0 -149
  85. otdf_python_proto/kas/kas_pb2_connect.py +0 -192
  86. otdf_python_proto/policy/actions/actions_pb2_connect.py +0 -275
  87. otdf_python_proto/policy/attributes/attributes_pb2_connect.py +0 -863
  88. otdf_python_proto/policy/kasregistry/key_access_server_registry_pb2_connect.py +0 -611
  89. otdf_python_proto/policy/keymanagement/key_management_pb2_connect.py +0 -275
  90. otdf_python_proto/policy/namespaces/namespaces_pb2_connect.py +0 -443
  91. otdf_python_proto/policy/registeredresources/registered_resources_pb2_connect.py +0 -527
  92. otdf_python_proto/policy/resourcemapping/resource_mapping_pb2_connect.py +0 -527
  93. otdf_python_proto/policy/subjectmapping/subject_mapping_pb2_connect.py +0 -569
  94. otdf_python_proto/policy/unsafe/unsafe_pb2_connect.py +0 -485
  95. otdf_python_proto/wellknownconfiguration/wellknown_configuration_pb2_connect.py +0 -107
  96. {otdf_python-0.4.0.dist-info → otdf_python-0.4.2.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,33 +124,33 @@ 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"""
141
- pass
136
+ """Close resources associated with KAS interface."""
137
+ if self._kas_client:
138
+ self._kas_client.close()
142
139
 
143
140
  def __exit__(self, exc_type, exc_val, exc_tb):
144
141
  self.close()
145
142
 
146
143
 
147
144
  class SDK(AbstractContextManager):
145
+ """SDK for OpenTDF platform interaction."""
146
+
148
147
  def new_tdf_config(
149
148
  self,
150
149
  attributes: list[str] | None = None,
151
150
  kas_info_list: list[KASInfo] | None = None,
152
151
  **kwargs,
153
152
  ) -> TDFConfig:
154
- """
155
- Create a TDFConfig with default kas_info_list from the SDK's platform_url.
156
- """
157
-
153
+ """Create a TDFConfig with default kas_info_list from the SDK's platform_url."""
158
154
  if self.platform_url is None:
159
155
  raise SDKException("Cannot create TDFConfig: SDK platform_url is not set.")
160
156
 
@@ -214,20 +210,16 @@ class SDK(AbstractContextManager):
214
210
  """
215
211
 
216
212
  class Services(AbstractContextManager):
217
- """
218
- The Services interface provides access to various platform service clients and KAS.
219
- """
213
+ """The Services interface provides access to various platform service clients and KAS."""
220
214
 
221
215
  def kas(self) -> KAS:
222
- """
223
- Returns the KAS client for key access operations.
216
+ """Return the KAS client for key access operations.
224
217
  This should be implemented to return an instance of KAS.
225
218
  """
226
219
  raise NotImplementedError
227
220
 
228
221
  def close(self):
229
- """Closes resources associated with the services"""
230
- pass
222
+ """Close resources associated with the services."""
231
223
 
232
224
  def __exit__(self, exc_type, exc_val, exc_tb):
233
225
  self.close()
@@ -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,40 @@ 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"""
387
-
388
- pass
378
+ """Throw when SDK encounters error related to split key operations."""
389
379
 
390
380
  class DataSizeNotSupported(SDKException):
391
- """Thrown when the user attempts to create a TDF with a size larger than the maximum size"""
392
-
393
- pass
381
+ """Throw when user attempts to create TDF larger than maximum size."""
394
382
 
395
383
  class KasInfoMissing(SDKException):
396
- """Thrown during TDF creation when no KAS information is present"""
397
-
398
- pass
384
+ """Throw during TDF creation when no KAS information is present."""
399
385
 
400
386
  class KasPublicKeyMissing(SDKException):
401
- """Thrown during encryption when the SDK cannot retrieve the public key for a KAS"""
402
-
403
- pass
387
+ """Throw during encryption when SDK cannot retrieve public key for KAS."""
404
388
 
405
389
  class TamperException(SDKException):
406
- """Base class for exceptions related to signature mismatches"""
390
+ """Base class for exceptions related to signature mismatches."""
407
391
 
408
392
  def __init__(self, error_message: str):
393
+ """Initialize tamper exception."""
409
394
  super().__init__(f"[tamper detected] {error_message}")
410
395
 
411
396
  class RootSignatureValidationException(TamperException):
412
- """Thrown when the root signature validation fails"""
413
-
414
- pass
397
+ """Throw when root signature validation fails."""
415
398
 
416
399
  class SegmentSignatureMismatch(TamperException):
417
- """Thrown when a segment signature does not match the expected value"""
418
-
419
- pass
400
+ """Throw when segment signature does not match expected value."""
420
401
 
421
402
  class KasBadRequestException(SDKException):
422
- """Thrown when the KAS returns a bad request response"""
423
-
424
- pass
403
+ """Throw when KAS returns bad request response."""
425
404
 
426
405
  class KasAllowlistException(SDKException):
427
- """Thrown when the KAS allowlist check fails"""
428
-
429
- pass
406
+ """Throw when KAS allowlist check fails."""
430
407
 
431
408
  class AssertionException(SDKException):
432
- """Thrown when an assertion validation fails"""
409
+ """Throw when an assertion validation fails."""
433
410
 
434
411
  def __init__(self, error_message: str, assertion_id: str):
412
+ """Initialize exception."""
435
413
  super().__init__(error_message)
436
414
  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,17 +111,16 @@ 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
- if endpoint and not (
117
- endpoint.startswith("http://") or endpoint.startswith("https://")
118
- ):
123
+ if endpoint and not (endpoint.startswith(("http://", "https://"))):
119
124
  if self.use_plaintext:
120
125
  endpoint = f"http://{endpoint}"
121
126
  else:
@@ -127,17 +132,16 @@ class SDKBuilder:
127
132
  return self
128
133
 
129
134
  def set_issuer_endpoint(self, issuer: str) -> "SDKBuilder":
130
- """
131
- Sets the OpenID Connect issuer endpoint URL.
135
+ """Set the OpenID Connect issuer endpoint URL.
136
+
132
137
  Args:
133
138
  issuer: The issuer endpoint URL
134
139
  Returns:
135
140
  self: The builder instance for chaining
141
+
136
142
  """
137
143
  # Normalize the issuer URL
138
- if issuer and not (
139
- issuer.startswith("http://") or issuer.startswith("https://")
140
- ):
144
+ if issuer and not (issuer.startswith(("http://", "https://"))):
141
145
  issuer = f"https://{issuer}"
142
146
 
143
147
  self.issuer_endpoint = issuer
@@ -146,12 +150,13 @@ class SDKBuilder:
146
150
  def use_insecure_plaintext_connection(
147
151
  self, use_plaintext: bool = True
148
152
  ) -> "SDKBuilder":
149
- """
150
- Configures whether to use plain text (HTTP) connection instead of HTTPS.
153
+ """Configure whether to use plain text (HTTP) instead of HTTPS.
154
+
151
155
  Args:
152
156
  use_plaintext: Whether to use plain text connection
153
157
  Returns:
154
158
  self: The builder instance for chaining
159
+
155
160
  """
156
161
  self.use_plaintext = use_plaintext
157
162
 
@@ -168,12 +173,13 @@ class SDKBuilder:
168
173
  return self
169
174
 
170
175
  def use_insecure_skip_verify(self, skip_verify: bool = True) -> "SDKBuilder":
171
- """
172
- Configures whether to skip SSL verification.
176
+ """Configure whether to skip SSL verification.
177
+
173
178
  Args:
174
179
  skip_verify: Whether to skip SSL verification
175
180
  Returns:
176
181
  self: The builder instance for chaining
182
+
177
183
  """
178
184
  self.insecure_skip_verify = skip_verify
179
185
 
@@ -184,21 +190,23 @@ class SDKBuilder:
184
190
  return self
185
191
 
186
192
  def bearer_token(self, token: str) -> "SDKBuilder":
187
- """
188
- Sets a bearer token to use for authorization.
193
+ """Set a bearer token to use for authorization.
194
+
189
195
  Args:
190
196
  token: The bearer token
191
197
  Returns:
192
198
  self: The builder instance for chaining
199
+
193
200
  """
194
201
  self.auth_token = token
195
202
  return self
196
203
 
197
204
  def _discover_token_endpoint_from_platform(self) -> None:
198
- """
199
- Discover token endpoint using OpenTDF platform configuration.
205
+ """Discover token endpoint using OpenTDF platform configuration.
206
+
200
207
  Raises:
201
208
  AutoConfigureException: If discovery fails
209
+
202
210
  """
203
211
  if not self.platform_endpoint or not self.oauth_config:
204
212
  return
@@ -232,12 +240,13 @@ class SDKBuilder:
232
240
  self._discover_token_endpoint_from_issuer(platform_issuer)
233
241
 
234
242
  def _discover_token_endpoint_from_issuer(self, issuer_url: str) -> None:
235
- """
236
- Discover token endpoint using OIDC discovery from issuer.
243
+ """Discover token endpoint using OIDC discovery from issuer.
244
+
237
245
  Args:
238
246
  issuer_url: The issuer URL to use for discovery
239
247
  Raises:
240
248
  AutoConfigureException: If discovery fails
249
+
241
250
  """
242
251
  if not self.oauth_config:
243
252
  return
@@ -260,10 +269,11 @@ class SDKBuilder:
260
269
  )
261
270
 
262
271
  def _discover_token_endpoint(self) -> None:
263
- """
264
- Discover the token endpoint using available configuration.
272
+ """Discover the token endpoint using available configuration.
273
+
265
274
  Raises:
266
275
  AutoConfigureException: If discovery fails
276
+
267
277
  """
268
278
  # Try platform endpoint first
269
279
  if self.platform_endpoint:
@@ -283,7 +293,7 @@ class SDKBuilder:
283
293
  pass
284
294
  raise AutoConfigureException(
285
295
  f"Error during token endpoint discovery: {e!s}"
286
- )
296
+ ) from e
287
297
 
288
298
  # Fall back to explicit issuer endpoint
289
299
  if self.issuer_endpoint:
@@ -297,12 +307,13 @@ class SDKBuilder:
297
307
  )
298
308
 
299
309
  def _get_token_from_client_credentials(self) -> str:
300
- """
301
- Obtains an OAuth token using client credentials.
310
+ """Obtain an OAuth token using client credentials.
311
+
302
312
  Returns:
303
- str: The access token
313
+ str: The OAuth access token
304
314
  Raises:
305
315
  AutoConfigureException: If token acquisition fails
316
+
306
317
  """
307
318
  if not self.oauth_config:
308
319
  raise AutoConfigureException("OAuth configuration is not set")
@@ -341,15 +352,18 @@ class SDKBuilder:
341
352
  )
342
353
 
343
354
  except Exception as e:
344
- raise AutoConfigureException(f"Error during token acquisition: {e!s}")
355
+ raise AutoConfigureException(
356
+ f"Error during token acquisition: {e!s}"
357
+ ) from e
345
358
 
346
359
  def _create_services(self) -> SDK.Services:
347
- """
348
- Creates service client instances.
360
+ """Create service client instances.
361
+
349
362
  Returns:
350
363
  SDK.Services: The service client instances
351
364
  Raises:
352
365
  AutoConfigureException: If service creation fails
366
+
353
367
  """
354
368
  # For now, return a simple implementation of Services
355
369
  # In a real implementation, this would create actual service clients
@@ -364,9 +378,7 @@ class SDKBuilder:
364
378
  self._builder = builder_instance
365
379
 
366
380
  def kas(self) -> KAS:
367
- """
368
- Returns the KAS interface with SSL verification settings.
369
- """
381
+ """Return the KAS interface with SSL verification settings."""
370
382
  platform_url = SDKBuilder.get_platform_url()
371
383
 
372
384
  # Create a token source function that can refresh tokens
@@ -394,12 +406,13 @@ class SDKBuilder:
394
406
  return ServicesImpl(self)
395
407
 
396
408
  def build(self) -> SDK:
397
- """
398
- Builds and returns an SDK instance with the configured properties.
409
+ """Build and return an SDK instance with configured properties.
410
+
399
411
  Returns:
400
412
  SDK: The configured SDK instance
401
413
  Raises:
402
414
  AutoConfigureException: If the build fails
415
+
403
416
  """
404
417
  if not self.platform_endpoint:
405
418
  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)