pangea-sdk 5.2.0b1__py3-none-any.whl → 5.2.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.
pangea/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
- __version__ = "5.2.0beta1"
1
+ __version__ = "5.2.1"
2
2
 
3
3
  from pangea.asyncio.request import PangeaRequestAsync
4
4
  from pangea.config import PangeaConfig
pangea/asyncio/request.py CHANGED
@@ -5,12 +5,11 @@ from __future__ import annotations
5
5
  import asyncio
6
6
  import json
7
7
  import time
8
- from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast
8
+ from typing import Any, Dict, List, Optional, Sequence, Tuple, Type, Union
9
9
 
10
10
  import aiohttp
11
11
  from aiohttp import FormData
12
12
  from pydantic import BaseModel
13
- from pydantic_core import to_jsonable_python
14
13
  from typing_extensions import Any, TypeVar
15
14
 
16
15
  import pangea.exceptions as pe
@@ -56,20 +55,17 @@ class PangeaRequestAsync(PangeaRequestBase):
56
55
  if data is None:
57
56
  data = {}
58
57
 
59
- # Normalize.
60
- data = cast(dict[str, Any], to_jsonable_python(data))
61
-
62
58
  if url is None:
63
59
  url = self._url(endpoint)
64
60
 
65
61
  # Set config ID if available
66
- if self.config_id and data.get("config_id", None) is None:
67
- data["config_id"] = self.config_id
62
+ if self.config_id and data.get("config_id", None) is None: # type: ignore[union-attr]
63
+ data["config_id"] = self.config_id # type: ignore[index]
68
64
 
69
65
  self.logger.debug(
70
66
  json.dumps({"service": self.service, "action": "post", "url": url, "data": data}, default=default_encoder)
71
67
  )
72
- transfer_method = data.get("transfer_method", None)
68
+ transfer_method = data.get("transfer_method", None) # type: ignore[union-attr]
73
69
 
74
70
  if files and type(data) is dict and (transfer_method == TransferMethod.POST_URL.value):
75
71
  requests_response = await self._full_post_presigned_url(
@@ -186,7 +182,20 @@ class PangeaRequestAsync(PangeaRequestBase):
186
182
  if resp.status < 200 or resp.status >= 300:
187
183
  raise pe.PresignedUploadError(f"presigned PUT failure: {resp.status}", await resp.text())
188
184
 
189
- async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
185
+ async def download_file(self, url: str, filename: str | None = None) -> AttachedFile:
186
+ """
187
+ Download file
188
+
189
+ Download a file from the specified URL and save it with the given
190
+ filename.
191
+
192
+ Args:
193
+ url: URL of the file to download
194
+ filename: Name to save the downloaded file as. If not provided, the
195
+ filename will be determined from the Content-Disposition header or
196
+ the URL.
197
+ """
198
+
190
199
  self.logger.debug(
191
200
  json.dumps(
192
201
  {
@@ -174,14 +174,16 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
174
174
  verbose: Optional[bool] = None,
175
175
  ) -> PangeaResponse[LogResult]:
176
176
  """
177
- Log an entry
177
+ Log an event
178
178
 
179
179
  Create a log entry in the Secure Audit Log.
180
+
180
181
  Args:
181
182
  event (dict[str, Any]): event to be logged
182
183
  verify (bool, optional): True to verify logs consistency after response.
183
184
  sign_local (bool, optional): True to sign event with local key.
184
185
  verbose (bool, optional): True to get a more verbose response.
186
+
185
187
  Raises:
186
188
  AuditException: If an audit based api exception happens
187
189
  PangeaAPIException: If an API Error happens
@@ -192,13 +194,7 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
192
194
  Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#log-an-entry).
193
195
 
194
196
  Examples:
195
- try:
196
- log_response = audit.log({"message"="Hello world"}, verbose=False)
197
- print(f"Response. Hash: {log_response.result.hash}")
198
- except pe.PangeaAPIException as e:
199
- print(f"Request Error: {e.response.summary}")
200
- for err in e.errors:
201
- print(f"\\t{err.detail} \\n")
197
+ response = await audit.log_event({"message": "hello world"}, verbose=True)
202
198
  """
203
199
 
204
200
  input = self._get_log_request(event, sign_local=sign_local, verify=verify, verbose=verbose)
@@ -632,9 +628,9 @@ class AuditAsync(ServiceBaseAsync, AuditBase):
632
628
  if pub_root is not None:
633
629
  self.pub_roots[tree_size] = pub_root
634
630
 
635
- await self.fix_consistency_proofs(tree_sizes)
631
+ await self._fix_consistency_proofs(tree_sizes)
636
632
 
637
- async def fix_consistency_proofs(self, tree_sizes: Iterable[int]):
633
+ async def _fix_consistency_proofs(self, tree_sizes: Iterable[int]) -> None:
638
634
  # on very rare occasions, the consistency proof in Arweave may be wrong
639
635
  # override it with the proof from pangea (not the root hash, just the proof)
640
636
  for tree_size in tree_sizes:
@@ -1,9 +1,12 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
3
 
4
+ from __future__ import annotations
5
+
4
6
  from typing import Any, Dict, List, Optional
5
7
 
6
8
  from pangea.asyncio.services.base import ServiceBaseAsync
9
+ from pangea.config import PangeaConfig
7
10
  from pangea.response import PangeaResponse
8
11
  from pangea.services.authz import (
9
12
  CheckRequest,
@@ -36,8 +39,8 @@ class AuthZAsync(ServiceBaseAsync):
36
39
 
37
40
  Examples:
38
41
  import os
42
+ from pangea.asyncio.services import AuthZAsync
39
43
  from pangea.config import PangeaConfig
40
- from pangea.services import AuthZ
41
44
 
42
45
  PANGEA_TOKEN = os.getenv("PANGEA_AUTHZ_TOKEN")
43
46
 
@@ -49,7 +52,25 @@ class AuthZAsync(ServiceBaseAsync):
49
52
 
50
53
  service_name = "authz"
51
54
 
52
- def __init__(self, token: str, config=None, logger_name="pangea", config_id: Optional[str] = None):
55
+ def __init__(
56
+ self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
57
+ ) -> None:
58
+ """
59
+ AuthZ client
60
+
61
+ Initializes a new AuthZ client.
62
+
63
+ Args:
64
+ token: Pangea API token.
65
+ config: Configuration.
66
+ logger_name: Logger name.
67
+ config_id: Configuration ID.
68
+
69
+ Examples:
70
+ config = PangeaConfig(domain="aws.us.pangea.cloud")
71
+ authz = AuthZAsync(token="pangea_token", config=config)
72
+ """
73
+
53
74
  super().__init__(token, config, logger_name, config_id=config_id)
54
75
 
55
76
  async def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
@@ -1,8 +1,11 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
+ from __future__ import annotations
3
4
 
4
5
  from typing import Dict, Optional, Type, Union
5
6
 
7
+ from typing_extensions import override
8
+
6
9
  from pangea.asyncio.request import PangeaRequestAsync
7
10
  from pangea.exceptions import AcceptedRequestException
8
11
  from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult
@@ -23,6 +26,7 @@ class ServiceBaseAsync(ServiceBase):
23
26
 
24
27
  return self._request
25
28
 
29
+ @override
26
30
  async def poll_result( # type: ignore[override]
27
31
  self,
28
32
  exception: Optional[AcceptedRequestException] = None,
@@ -36,7 +40,8 @@ class ServiceBaseAsync(ServiceBase):
36
40
  Returns request's result that has been accepted by the server
37
41
 
38
42
  Args:
39
- exception (AcceptedRequestException): Exception raise by SDK on the call that is been processed.
43
+ exception: Exception that was previously raised by the SDK on a call
44
+ that is being processed.
40
45
 
41
46
  Returns:
42
47
  PangeaResponse
@@ -58,7 +63,21 @@ class ServiceBaseAsync(ServiceBase):
58
63
  else:
59
64
  raise AttributeError("Need to set exception, response or request_id")
60
65
 
61
- async def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile: # type: ignore[override]
66
+ @override
67
+ async def download_file(self, url: str, filename: str | None = None) -> AttachedFile: # type: ignore[override]
68
+ """
69
+ Download file
70
+
71
+ Download a file from the specified URL and save it with the given
72
+ filename.
73
+
74
+ Args:
75
+ url: URL of the file to download
76
+ filename: Name to save the downloaded file as. If not provided, the
77
+ filename will be determined from the Content-Disposition header or
78
+ the URL.
79
+ """
80
+
62
81
  return await self.request.download_file(url=url, filename=filename)
63
82
 
64
83
  async def close(self):
@@ -790,6 +790,7 @@ class UserIntelAsync(ServiceBaseAsync):
790
790
  verbose: Optional[bool] = None,
791
791
  raw: Optional[bool] = None,
792
792
  provider: Optional[str] = None,
793
+ cursor: Optional[str] = None,
793
794
  ) -> PangeaResponse[m.UserBreachedResult]:
794
795
  """
795
796
  Look up breached users
@@ -808,6 +809,7 @@ class UserIntelAsync(ServiceBaseAsync):
808
809
  verbose (bool, optional): Echo the API parameters in the response
809
810
  raw (bool, optional): Include raw data from this provider
810
811
  provider (str, optional): Use reputation data from this provider: "crowdstrike"
812
+ cursor (str, optional): A token given in the raw response from SpyCloud. Post this back to paginate results
811
813
 
812
814
  Raises:
813
815
  PangeaAPIException: If an API Error happens
@@ -835,6 +837,7 @@ class UserIntelAsync(ServiceBaseAsync):
835
837
  end=end,
836
838
  verbose=verbose,
837
839
  raw=raw,
840
+ cursor=cursor,
838
841
  )
839
842
  return await self.request.post(
840
843
  "v1/user/breached", m.UserBreachedResult, data=input.model_dump(exclude_none=True)
@@ -65,6 +65,8 @@ class RedactAsync(ServiceBaseAsync):
65
65
  rulesets: Optional[List[str]] = None,
66
66
  return_result: Optional[bool] = None,
67
67
  redaction_method_overrides: Optional[m.RedactionMethodOverrides] = None,
68
+ llm_request: Optional[bool] = None,
69
+ vault_parameters: Optional[m.VaultParameters] = None,
68
70
  ) -> PangeaResponse[m.RedactResult]:
69
71
  """
70
72
  Redact
@@ -81,6 +83,8 @@ class RedactAsync(ServiceBaseAsync):
81
83
  rulesets (list[str], optional): An array of redact rulesets short names
82
84
  return_result(bool, optional): Setting this value to false will omit the redacted result only returning count
83
85
  redaction_method_overrides: A set of redaction method overrides for any enabled rule. These methods override the config declared methods
86
+ llm_request: Boolean flag to enable FPE redaction for LLM requests
87
+ vault_parameters: A set of vault parameters to use for redaction
84
88
 
85
89
  Raises:
86
90
  PangeaAPIException: If an API Error happens
@@ -101,6 +105,8 @@ class RedactAsync(ServiceBaseAsync):
101
105
  rulesets=rulesets,
102
106
  return_result=return_result,
103
107
  redaction_method_overrides=redaction_method_overrides,
108
+ llm_request=llm_request,
109
+ vault_parameters=vault_parameters,
104
110
  )
105
111
  return await self.request.post("v1/redact", m.RedactResult, data=input.model_dump(exclude_none=True))
106
112
 
@@ -114,6 +120,8 @@ class RedactAsync(ServiceBaseAsync):
114
120
  rulesets: Optional[List[str]] = None,
115
121
  return_result: Optional[bool] = None,
116
122
  redaction_method_overrides: Optional[m.RedactionMethodOverrides] = None,
123
+ llm_request: bool | None = None,
124
+ vault_parameters: m.VaultParameters | None = None,
117
125
  ) -> PangeaResponse[m.StructuredResult]:
118
126
  """
119
127
  Redact structured
@@ -134,6 +142,8 @@ class RedactAsync(ServiceBaseAsync):
134
142
  rulesets (list[str], optional): An array of redact rulesets short names
135
143
  return_result(bool, optional): Setting this value to false will omit the redacted result only returning count
136
144
  redaction_method_overrides: A set of redaction method overrides for any enabled rule. These methods override the config declared methods
145
+ llm_request: Boolean flag to enable FPE redaction for LLM requests
146
+ vault_parameters: A set of vault parameters to use for redaction
137
147
 
138
148
  Raises:
139
149
  PangeaAPIException: If an API Error happens
@@ -161,6 +171,8 @@ class RedactAsync(ServiceBaseAsync):
161
171
  rulesets=rulesets,
162
172
  return_result=return_result,
163
173
  redaction_method_overrides=redaction_method_overrides,
174
+ llm_request=llm_request,
175
+ vault_parameters=vault_parameters,
164
176
  )
165
177
  return await self.request.post(
166
178
  "v1/redact_structured", m.StructuredResult, data=input.model_dump(exclude_none=True)
@@ -1,10 +1,13 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
+ from __future__ import annotations
4
+
3
5
  import io
4
6
  from typing import List, Optional, Tuple
5
7
 
6
8
  import pangea.services.sanitize as m
7
9
  from pangea.asyncio.services.base import ServiceBaseAsync
10
+ from pangea.config import PangeaConfig
8
11
  from pangea.response import PangeaResponse, TransferMethod
9
12
  from pangea.utils import FileUploadParams, get_file_upload_params
10
13
 
@@ -16,17 +19,38 @@ class SanitizeAsync(ServiceBaseAsync):
16
19
  import os
17
20
 
18
21
  # Pangea SDK
22
+ from pangea.asyncio.services import SanitizeAsync
19
23
  from pangea.config import PangeaConfig
20
- from pangea.asyncio.services import Sanitize
21
24
 
22
25
  PANGEA_SANITIZE_TOKEN = os.getenv("PANGEA_SANITIZE_TOKEN")
23
26
  config = PangeaConfig(domain="pangea.cloud")
24
27
 
25
- sanitize = Sanitize(token=PANGEA_SANITIZE_TOKEN, config=config)
28
+ sanitize = SanitizeAsync(token=PANGEA_SANITIZE_TOKEN, config=config)
26
29
  """
27
30
 
28
31
  service_name = "sanitize"
29
32
 
33
+ def __init__(
34
+ self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
35
+ ) -> None:
36
+ """
37
+ Sanitize client
38
+
39
+ Initializes a new Sanitize client.
40
+
41
+ Args:
42
+ token: Pangea API token.
43
+ config: Configuration.
44
+ logger_name: Logger name.
45
+ config_id: Configuration ID.
46
+
47
+ Examples:
48
+ config = PangeaConfig(domain="aws.us.pangea.cloud")
49
+ authz = SanitizeAsync(token="pangea_token", config=config)
50
+ """
51
+
52
+ super().__init__(token, config, logger_name, config_id=config_id)
53
+
30
54
  async def sanitize(
31
55
  self,
32
56
  transfer_method: TransferMethod = TransferMethod.POST_URL,
@@ -7,16 +7,38 @@ from typing import Dict, List, Optional, Tuple, Union
7
7
 
8
8
  import pangea.services.share.share as m
9
9
  from pangea.asyncio.services.base import ServiceBaseAsync
10
+ from pangea.config import PangeaConfig
10
11
  from pangea.response import PangeaResponse, TransferMethod
11
12
  from pangea.services.share.file_format import FileFormat
12
13
  from pangea.utils import get_file_size, get_file_upload_params
13
14
 
14
15
 
15
16
  class ShareAsync(ServiceBaseAsync):
16
- """Share service client."""
17
+ """Secure Share service client."""
17
18
 
18
19
  service_name = "share"
19
20
 
21
+ def __init__(
22
+ self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
23
+ ) -> None:
24
+ """
25
+ Secure Share client
26
+
27
+ Initializes a new Secure Share client.
28
+
29
+ Args:
30
+ token: Pangea API token.
31
+ config: Configuration.
32
+ logger_name: Logger name.
33
+ config_id: Configuration ID.
34
+
35
+ Examples:
36
+ config = PangeaConfig(domain="aws.us.pangea.cloud")
37
+ authz = ShareAsync(token="pangea_token", config=config)
38
+ """
39
+
40
+ super().__init__(token, config, logger_name, config_id=config_id)
41
+
20
42
  async def buckets(self) -> PangeaResponse[m.BucketsResult]:
21
43
  """
22
44
  Buckets
pangea/deep_verify.py CHANGED
@@ -263,8 +263,14 @@ def main():
263
263
  audit = init_audit(args.token, args.domain)
264
264
  errors = deep_verify(audit, args.file)
265
265
 
266
- print("\n\nTotal errors:")
266
+ print("\n\nWarnings:")
267
+ val = errors["not_persisted"]
268
+ print(f"\tnot_persisted: {val}")
269
+
270
+ print("\nTotal errors:")
267
271
  for key, val in errors.items():
272
+ if key == "not_persisted":
273
+ continue
268
274
  print(f"\t{key.title()}: {val}")
269
275
  print()
270
276
 
pangea/dump_audit.py CHANGED
@@ -63,11 +63,12 @@ def dump_before(audit: Audit, output: io.TextIOWrapper, start: datetime) -> int:
63
63
  cnt = 0
64
64
  if search_res.result and search_res.result.count > 0:
65
65
  leaf_index = search_res.result.events[0].leaf_index
66
- for row in reversed(search_res.result.events):
67
- if row.leaf_index != leaf_index:
68
- break
69
- dump_event(output, row, search_res)
70
- cnt += 1
66
+ if leaf_index is not None:
67
+ for row in reversed(search_res.result.events):
68
+ if row.leaf_index != leaf_index:
69
+ break
70
+ dump_event(output, row, search_res)
71
+ cnt += 1
71
72
  print(f"Dumping before... {cnt} events")
72
73
  return cnt
73
74
 
@@ -89,7 +90,7 @@ def dump_after(audit: Audit, output: io.TextIOWrapper, start: datetime, last_eve
89
90
  cnt = 0
90
91
  if search_res.result and search_res.result.count > 0:
91
92
  leaf_index = search_res.result.events[0].leaf_index
92
- if leaf_index == last_leaf_index:
93
+ if leaf_index is not None and leaf_index == last_leaf_index:
93
94
  start_idx: int = 1 if last_event_hash == search_res.result.events[0].hash else 0
94
95
  for row in search_res.result.events[start_idx:]:
95
96
  if row.leaf_index != leaf_index:
@@ -124,7 +125,7 @@ def dump_page(
124
125
  msg = f"Dumping... {search_res.result.count} events"
125
126
 
126
127
  if search_res.result.count <= 1:
127
- return end, 0 # type: ignore[return-value]
128
+ return end, 0, True, "", 0
128
129
 
129
130
  offset = 0
130
131
  result_id = search_res.result.id
pangea/request.py CHANGED
@@ -6,13 +6,12 @@ import copy
6
6
  import json
7
7
  import logging
8
8
  import time
9
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Tuple, Type, Union, cast
9
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Tuple, Type, Union
10
10
 
11
11
  import requests
12
12
  from pydantic import BaseModel
13
- from pydantic_core import to_jsonable_python
14
13
  from requests.adapters import HTTPAdapter, Retry
15
- from requests_toolbelt import MultipartDecoder # type: ignore
14
+ from requests_toolbelt import MultipartDecoder # type: ignore[import-untyped]
16
15
  from typing_extensions import TypeVar
17
16
 
18
17
  import pangea
@@ -233,9 +232,6 @@ class PangeaRequest(PangeaRequestBase):
233
232
  if data is None:
234
233
  data = {}
235
234
 
236
- # Normalize.
237
- data = cast(dict[str, Any], to_jsonable_python(data))
238
-
239
235
  if url is None:
240
236
  url = self._url(endpoint)
241
237
 
@@ -406,7 +402,20 @@ class PangeaRequest(PangeaRequestBase):
406
402
 
407
403
  return self._check_response(pangea_response)
408
404
 
409
- def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
405
+ def download_file(self, url: str, filename: str | None = None) -> AttachedFile:
406
+ """
407
+ Download file
408
+
409
+ Download a file from the specified URL and save it with the given
410
+ filename.
411
+
412
+ Args:
413
+ url: URL of the file to download
414
+ filename: Name to save the downloaded file as. If not provided, the
415
+ filename will be determined from the Content-Disposition header or
416
+ the URL.
417
+ """
418
+
410
419
  self.logger.debug(
411
420
  json.dumps(
412
421
  {
@@ -1,11 +1,9 @@
1
1
  from .audit.audit import Audit
2
2
  from .authn.authn import AuthN
3
3
  from .authz import AuthZ
4
- from .data_guard import DataGuard
5
4
  from .embargo import Embargo
6
5
  from .file_scan import FileScan
7
6
  from .intel import DomainIntel, FileIntel, IpIntel, UrlIntel, UserIntel
8
- from .prompt_guard import PromptGuard
9
7
  from .redact import Redact
10
8
  from .sanitize import Sanitize
11
9
  from .share.share import Share
@@ -492,7 +492,7 @@ class Audit(ServiceBase, AuditBase):
492
492
  verbose: Optional[bool] = None,
493
493
  ) -> PangeaResponse[LogResult]:
494
494
  """
495
- Log an entry
495
+ Log an event
496
496
 
497
497
  Create a log entry in the Secure Audit Log.
498
498
 
@@ -501,6 +501,7 @@ class Audit(ServiceBase, AuditBase):
501
501
  verify (bool, optional): True to verify logs consistency after response.
502
502
  sign_local (bool, optional): True to sign event with local key.
503
503
  verbose (bool, optional): True to get a more verbose response.
504
+
504
505
  Raises:
505
506
  AuditException: If an audit based api exception happens
506
507
  PangeaAPIException: If an API Error happens
@@ -511,13 +512,7 @@ class Audit(ServiceBase, AuditBase):
511
512
  Available response fields can be found in our [API documentation](https://pangea.cloud/docs/api/audit#/v1/log).
512
513
 
513
514
  Examples:
514
- try:
515
- log_response = audit.log({"message": "hello world"}, verbose=True)
516
- print(f"Response. Hash: {log_response.result.hash}")
517
- except pe.PangeaAPIException as e:
518
- print(f"Request Error: {e.response.summary}")
519
- for err in e.errors:
520
- print(f"\\t{err.detail} \\n")
515
+ response = audit.log_event({"message": "hello world"}, verbose=True)
521
516
  """
522
517
 
523
518
  input = self._get_log_request(event, sign_local=sign_local, verify=verify, verbose=verbose)
@@ -961,9 +956,9 @@ class Audit(ServiceBase, AuditBase):
961
956
  if pub_root is not None:
962
957
  self.pub_roots[tree_size] = pub_root
963
958
 
964
- self.fix_consistency_proofs(tree_sizes)
959
+ self._fix_consistency_proofs(tree_sizes)
965
960
 
966
- def fix_consistency_proofs(self, tree_sizes: Iterable[int]):
961
+ def _fix_consistency_proofs(self, tree_sizes: Iterable[int]) -> None:
967
962
  # on very rare occasions, the consistency proof in Arweave may be wrong
968
963
  # override it with the proof from pangea (not the root hash, just the proof)
969
964
  for tree_size in tree_sizes:
pangea/services/authz.py CHANGED
@@ -1,9 +1,11 @@
1
1
  # Copyright 2022 Pangea Cyber Corporation
2
2
  # Author: Pangea Cyber Corporation
3
+ from __future__ import annotations
3
4
 
4
5
  import enum
5
6
  from typing import Any, Dict, List, Optional, Union
6
7
 
8
+ from pangea.config import PangeaConfig
7
9
  from pangea.response import APIRequestModel, APIResponseModel, PangeaResponse, PangeaResponseResult
8
10
  from pangea.services.base import ServiceBase
9
11
 
@@ -171,7 +173,25 @@ class AuthZ(ServiceBase):
171
173
 
172
174
  service_name = "authz"
173
175
 
174
- def __init__(self, token: str, config=None, logger_name="pangea", config_id: Optional[str] = None):
176
+ def __init__(
177
+ self, token: str, config: PangeaConfig | None = None, logger_name: str = "pangea", config_id: str | None = None
178
+ ) -> None:
179
+ """
180
+ AuthZ client
181
+
182
+ Initializes a new AuthZ client.
183
+
184
+ Args:
185
+ token: Pangea API token.
186
+ config: Configuration.
187
+ logger_name: Logger name.
188
+ config_id: Configuration ID.
189
+
190
+ Examples:
191
+ config = PangeaConfig(domain="aws.us.pangea.cloud")
192
+ authz = AuthZ(token="pangea_token", config=config)
193
+ """
194
+
175
195
  super().__init__(token, config, logger_name, config_id=config_id)
176
196
 
177
197
  def tuple_create(self, tuples: List[Tuple]) -> PangeaResponse[TupleCreateResult]:
pangea/services/base.py CHANGED
@@ -80,7 +80,8 @@ class ServiceBase(object):
80
80
  Returns request's result that has been accepted by the server
81
81
 
82
82
  Args:
83
- exception (AcceptedRequestException): Exception raise by SDK on the call that is been processed.
83
+ exception: Exception that was previously raised by the SDK on a call
84
+ that is being processed.
84
85
 
85
86
  Returns:
86
87
  PangeaResponse
@@ -100,5 +101,18 @@ class ServiceBase(object):
100
101
  else:
101
102
  raise AttributeError("Need to set exception, response or request_id")
102
103
 
103
- def download_file(self, url: str, filename: Optional[str] = None) -> AttachedFile:
104
+ def download_file(self, url: str, filename: str | None = None) -> AttachedFile:
105
+ """
106
+ Download file
107
+
108
+ Download a file from the specified URL and save it with the given
109
+ filename.
110
+
111
+ Args:
112
+ url: URL of the file to download
113
+ filename: Name to save the downloaded file as. If not provided, the
114
+ filename will be determined from the Content-Disposition header or
115
+ the URL.
116
+ """
117
+
104
118
  return self.request.download_file(url=url, filename=filename)