pangea-sdk 6.5.0b1__tar.gz → 6.6.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.
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/PKG-INFO +17 -18
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/README.md +1 -2
- pangea_sdk-6.6.0/pangea/__init__.py +7 -0
- pangea_sdk-6.6.0/pangea/_constants.py +4 -0
- pangea_sdk-6.6.0/pangea/_typing.py +30 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/__init__.py +2 -1
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/file_uploader.py +3 -2
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/request.py +66 -162
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/__init__.py +19 -3
- pangea_sdk-6.6.0/pangea/asyncio/services/ai_guard.py +196 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/audit.py +1 -301
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/authn.py +25 -8
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/base.py +21 -6
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/file_scan.py +1 -1
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/intel.py +160 -95
- pangea_sdk-6.6.0/pangea/asyncio/services/prompt_guard.py +87 -0
- pangea_sdk-6.6.0/pangea/asyncio/services/redact.py +206 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/config.py +4 -2
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/file_uploader.py +4 -1
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/request.py +91 -166
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/response.py +5 -1
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/__init__.py +19 -3
- pangea_sdk-6.6.0/pangea/services/ai_guard.py +570 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/audit/audit.py +3 -301
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/audit/models.py +1 -273
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/audit/util.py +2 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/authn/authn.py +4 -5
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/base.py +3 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/file_scan.py +3 -2
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/intel.py +187 -252
- pangea_sdk-6.6.0/pangea/services/prompt_guard.py +121 -0
- pangea_sdk-6.6.0/pangea/services/redact.py +388 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/vault/vault.py +3 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pyproject.toml +23 -21
- pangea_sdk-6.5.0b1/pangea/__init__.py +0 -15
- pangea_sdk-6.5.0b1/pangea/asyncio/services/ai_guard.py +0 -342
- pangea_sdk-6.5.0b1/pangea/asyncio/services/management.py +0 -576
- pangea_sdk-6.5.0b1/pangea/asyncio/services/prompt_guard.py +0 -194
- pangea_sdk-6.5.0b1/pangea/asyncio/services/redact.py +0 -467
- pangea_sdk-6.5.0b1/pangea/services/ai_guard.py +0 -1180
- pangea_sdk-6.5.0b1/pangea/services/management.py +0 -720
- pangea_sdk-6.5.0b1/pangea/services/prompt_guard.py +0 -309
- pangea_sdk-6.5.0b1/pangea/services/redact.py +0 -854
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/authz.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/embargo.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/sanitize.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/share.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/asyncio/services/vault.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/audit_logger.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/crypto/rsa.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/deep_verify.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/deprecated.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/dump_audit.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/exceptions.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/py.typed +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/audit/exceptions.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/audit/signing.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/authn/models.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/authz.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/embargo.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/sanitize.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/share/file_format.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/share/share.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/vault/models/asymmetric.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/vault/models/common.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/vault/models/keys.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/vault/models/secret.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/services/vault/models/symmetric.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/tools.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/utils.py +0 -0
- {pangea_sdk-6.5.0b1 → pangea_sdk-6.6.0}/pangea/verify_audit.py +0 -0
@@ -1,24 +1,25 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: pangea-sdk
|
3
|
-
Version: 6.
|
3
|
+
Version: 6.6.0
|
4
4
|
Summary: Pangea API SDK
|
5
|
-
License: MIT
|
6
5
|
Keywords: Pangea,SDK,Audit
|
7
6
|
Author: Glenn Gallien
|
8
|
-
Author-email: glenn.gallien@pangea.cloud
|
9
|
-
|
7
|
+
Author-email: Glenn Gallien <glenn.gallien@pangea.cloud>
|
8
|
+
License-Expression: MIT
|
10
9
|
Classifier: Topic :: Software Development
|
11
10
|
Classifier: Topic :: Software Development :: Libraries
|
12
|
-
Requires-Dist: aiohttp
|
13
|
-
Requires-Dist: cryptography
|
14
|
-
Requires-Dist: deprecated
|
15
|
-
Requires-Dist: google-crc32c
|
16
|
-
Requires-Dist: pydantic
|
17
|
-
Requires-Dist: python-dateutil
|
18
|
-
Requires-Dist: requests
|
19
|
-
Requires-Dist: requests-toolbelt
|
20
|
-
Requires-Dist: typing-extensions
|
21
|
-
Requires-Dist: yarl
|
11
|
+
Requires-Dist: aiohttp>=3.12.15,<4.0.0
|
12
|
+
Requires-Dist: cryptography>=45.0.6,<46.0.0
|
13
|
+
Requires-Dist: deprecated>=1.2.18,<2.0.0
|
14
|
+
Requires-Dist: google-crc32c>=1.7.1,<2.0.0
|
15
|
+
Requires-Dist: pydantic>=2.11.7,<3.0.0
|
16
|
+
Requires-Dist: python-dateutil>=2.9.0.post0,<3.0.0
|
17
|
+
Requires-Dist: requests>=2.32.5,<3.0.0
|
18
|
+
Requires-Dist: requests-toolbelt>=1.0.0,<2.0.0
|
19
|
+
Requires-Dist: typing-extensions>=4.15.0,<5.0.0
|
20
|
+
Requires-Dist: yarl>=1.20.1,<2.0.0
|
21
|
+
Requires-Python: >=3.9.2, <4.0.0
|
22
|
+
Project-URL: repository, https://github.com/pangeacyber/pangea-python
|
22
23
|
Description-Content-Type: text/markdown
|
23
24
|
|
24
25
|
<a href="https://pangea.cloud?utm_source=github&utm_medium=python-sdk" target="_blank" rel="noopener noreferrer">
|
@@ -116,8 +117,7 @@ The SDK supports the following configuration options via `PangeaConfig`:
|
|
116
117
|
Use `base_url_template` for more control over the URL, such as setting
|
117
118
|
service-specific paths. Defaults to `aws.us.pangea.cloud`.
|
118
119
|
- `request_retries` — Number of retries on the initial request.
|
119
|
-
- `request_backoff` —
|
120
|
-
- `request_timeout` — Timeout used on initial request attempts.
|
120
|
+
- `request_backoff` — A backoff factor to apply between request attempts.
|
121
121
|
- `poll_result_timeout` — Timeout used to poll results after 202 (in secs).
|
122
122
|
- `queued_retry_enabled` — Enable queued request retry support.
|
123
123
|
- `custom_user_agent` — Custom user agent to be used in the request headers.
|
@@ -243,4 +243,3 @@ It accepts multiple file formats:
|
|
243
243
|
[Beta Examples]: https://github.com/pangeacyber/pangea-python/tree/beta/examples
|
244
244
|
[Pangea Console]: https://console.pangea.cloud/
|
245
245
|
[Secure Audit Log]: https://pangea.cloud/docs/audit
|
246
|
-
|
@@ -93,8 +93,7 @@ The SDK supports the following configuration options via `PangeaConfig`:
|
|
93
93
|
Use `base_url_template` for more control over the URL, such as setting
|
94
94
|
service-specific paths. Defaults to `aws.us.pangea.cloud`.
|
95
95
|
- `request_retries` — Number of retries on the initial request.
|
96
|
-
- `request_backoff` —
|
97
|
-
- `request_timeout` — Timeout used on initial request attempts.
|
96
|
+
- `request_backoff` — A backoff factor to apply between request attempts.
|
98
97
|
- `poll_result_timeout` — Timeout used to poll results after 202 (in secs).
|
99
98
|
- `queued_retry_enabled` — Enable queued request retry support.
|
100
99
|
- `custom_user_agent` — Custom user agent to be used in the request headers.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
__version__ = "6.6.0"
|
2
|
+
|
3
|
+
from pangea.config import PangeaConfig
|
4
|
+
from pangea.file_uploader import FileUploader
|
5
|
+
from pangea.response import PangeaResponse, PangeaResponseResult, TransferMethod
|
6
|
+
|
7
|
+
__all__ = ("FileUploader", "PangeaConfig", "PangeaResponse", "PangeaResponseResult", "TransferMethod")
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections.abc import Iterator, Sequence
|
4
|
+
|
5
|
+
from typing_extensions import Any, Protocol, SupportsIndex, TypeVar, overload
|
6
|
+
|
7
|
+
T = TypeVar("T")
|
8
|
+
T_co = TypeVar("T_co", covariant=True)
|
9
|
+
|
10
|
+
|
11
|
+
class SequenceNotStr(Protocol[T_co]):
|
12
|
+
"""Sequence-like object that isn't str or bytes."""
|
13
|
+
|
14
|
+
@overload
|
15
|
+
def __getitem__(self, index: SupportsIndex, /) -> T_co: ...
|
16
|
+
|
17
|
+
@overload
|
18
|
+
def __getitem__(self, index: slice, /) -> Sequence[T_co]: ...
|
19
|
+
|
20
|
+
def __contains__(self, value: object, /) -> bool: ...
|
21
|
+
|
22
|
+
def __len__(self) -> int: ...
|
23
|
+
|
24
|
+
def __iter__(self) -> Iterator[T_co]: ...
|
25
|
+
|
26
|
+
def index(self, value: Any, start: int = ..., stop: int = ..., /) -> int: ...
|
27
|
+
|
28
|
+
def count(self, value: Any, /) -> int: ...
|
29
|
+
|
30
|
+
def __reversed__(self) -> Iterator[T_co]: ...
|
@@ -6,9 +6,10 @@ from __future__ import annotations
|
|
6
6
|
import io
|
7
7
|
import logging
|
8
8
|
|
9
|
+
from pangea import PangeaConfig, TransferMethod
|
9
10
|
from pangea.asyncio.request import PangeaRequestAsync
|
10
|
-
|
11
|
-
|
11
|
+
|
12
|
+
__all__ = ("FileUploaderAsync",)
|
12
13
|
|
13
14
|
|
14
15
|
class FileUploaderAsync:
|
@@ -10,19 +10,23 @@ import asyncio
|
|
10
10
|
import json
|
11
11
|
import time
|
12
12
|
from collections.abc import Iterable, Mapping
|
13
|
-
from
|
13
|
+
from random import random
|
14
|
+
from typing import Dict, List, Optional, Sequence, Tuple, Type, Union, cast
|
14
15
|
|
15
16
|
import aiohttp
|
16
17
|
from aiohttp import FormData
|
17
|
-
from pydantic import BaseModel
|
18
|
+
from pydantic import BaseModel
|
18
19
|
from pydantic_core import to_jsonable_python
|
19
|
-
from typing_extensions import Any,
|
20
|
+
from typing_extensions import Any, TypeAlias, TypeVar, override
|
20
21
|
|
21
22
|
import pangea.exceptions as pe
|
23
|
+
from pangea._constants import MAX_RETRY_DELAY, RETRYABLE_HTTP_CODES
|
22
24
|
from pangea.request import MultipartResponse, PangeaRequestBase
|
23
25
|
from pangea.response import AttachedFile, PangeaResponse, PangeaResponseResult, ResponseStatus, TransferMethod
|
24
26
|
from pangea.utils import default_encoder
|
25
27
|
|
28
|
+
__all__ = ("PangeaRequestAsync",)
|
29
|
+
|
26
30
|
_FileName: TypeAlias = Union[str, None]
|
27
31
|
_FileContent: TypeAlias = Union[str, bytes]
|
28
32
|
_FileContentType: TypeAlias = str
|
@@ -50,24 +54,6 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
50
54
|
be set in PangeaConfig.
|
51
55
|
"""
|
52
56
|
|
53
|
-
async def delete(self, endpoint: str) -> None:
|
54
|
-
"""
|
55
|
-
Makes a DELETE call to a Pangea endpoint.
|
56
|
-
|
57
|
-
Args:
|
58
|
-
endpoint: The Pangea API endpoint.
|
59
|
-
"""
|
60
|
-
|
61
|
-
url = self._url(endpoint)
|
62
|
-
|
63
|
-
self.logger.debug(
|
64
|
-
json.dumps({"service": self.service, "action": "delete", "url": url}, default=default_encoder)
|
65
|
-
)
|
66
|
-
|
67
|
-
requests_response = await self._http_delete(url, headers=self._headers())
|
68
|
-
await self._check_http_errors(requests_response)
|
69
|
-
|
70
|
-
@overload
|
71
57
|
async def post(
|
72
58
|
self,
|
73
59
|
endpoint: str,
|
@@ -76,60 +62,18 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
76
62
|
files: Optional[List[Tuple]] = None,
|
77
63
|
poll_result: bool = True,
|
78
64
|
url: Optional[str] = None,
|
79
|
-
*,
|
80
|
-
pangea_response: Literal[True] = True,
|
81
65
|
) -> PangeaResponse[TResult]:
|
82
|
-
"""
|
83
|
-
Makes a POST call to a Pangea Service endpoint.
|
66
|
+
"""Makes the POST call to a Pangea Service endpoint.
|
84
67
|
|
85
68
|
Args:
|
86
|
-
endpoint: The Pangea Service API endpoint.
|
87
|
-
data: The POST body payload object
|
69
|
+
endpoint(str): The Pangea Service API endpoint.
|
70
|
+
data(dict): The POST body payload object
|
88
71
|
|
89
72
|
Returns:
|
90
73
|
PangeaResponse which contains the response in its entirety and
|
91
74
|
various properties to retrieve individual fields
|
92
75
|
"""
|
93
76
|
|
94
|
-
@overload
|
95
|
-
async def post(
|
96
|
-
self,
|
97
|
-
endpoint: str,
|
98
|
-
result_class: Type[TResult],
|
99
|
-
data: str | BaseModel | Mapping[str, Any] | None = None,
|
100
|
-
files: Optional[List[Tuple]] = None,
|
101
|
-
poll_result: bool = True,
|
102
|
-
url: Optional[str] = None,
|
103
|
-
*,
|
104
|
-
pangea_response: Literal[False],
|
105
|
-
) -> TResult:
|
106
|
-
"""
|
107
|
-
Makes a POST call to a Pangea Service endpoint.
|
108
|
-
|
109
|
-
Args:
|
110
|
-
endpoint: The Pangea Service API endpoint.
|
111
|
-
data: The POST body payload object
|
112
|
-
"""
|
113
|
-
|
114
|
-
async def post(
|
115
|
-
self,
|
116
|
-
endpoint: str,
|
117
|
-
result_class: Type[TResult],
|
118
|
-
data: str | BaseModel | Mapping[str, Any] | None = None,
|
119
|
-
files: Optional[List[Tuple]] = None,
|
120
|
-
poll_result: bool = True,
|
121
|
-
url: Optional[str] = None,
|
122
|
-
*,
|
123
|
-
pangea_response: bool = True,
|
124
|
-
) -> PangeaResponse[TResult] | TResult:
|
125
|
-
"""
|
126
|
-
Makes a POST call to a Pangea Service endpoint.
|
127
|
-
|
128
|
-
Args:
|
129
|
-
endpoint: The Pangea Service API endpoint.
|
130
|
-
data: The POST body payload object
|
131
|
-
"""
|
132
|
-
|
133
77
|
if isinstance(data, BaseModel):
|
134
78
|
data = data.model_dump(exclude_none=True)
|
135
79
|
|
@@ -169,13 +113,9 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
169
113
|
|
170
114
|
await self._check_http_errors(requests_response)
|
171
115
|
|
172
|
-
if not pangea_response:
|
173
|
-
type_adapter = TypeAdapter(result_class)
|
174
|
-
return type_adapter.validate_python(await requests_response.json())
|
175
|
-
|
176
116
|
if "multipart/form-data" in requests_response.headers.get("content-type", ""):
|
177
117
|
multipart_response = await self._process_multipart_response(requests_response)
|
178
|
-
|
118
|
+
pangea_response: PangeaResponse = PangeaResponse(
|
179
119
|
requests_response,
|
180
120
|
result_class=result_class,
|
181
121
|
json=multipart_response.pangea_json,
|
@@ -188,110 +128,49 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
188
128
|
json.dumps({"service": self.service, "action": "post", "url": url, "response": json_resp})
|
189
129
|
)
|
190
130
|
|
191
|
-
|
131
|
+
pangea_response = PangeaResponse(requests_response, result_class=result_class, json=json_resp)
|
192
132
|
except aiohttp.ContentTypeError as e:
|
193
133
|
raise pe.PangeaException(
|
194
134
|
f"Failed to decode json response. {e}. Body: {await requests_response.text()}"
|
195
135
|
) from e
|
196
136
|
|
197
137
|
if poll_result:
|
198
|
-
|
138
|
+
pangea_response = await self._handle_queued_result(pangea_response)
|
199
139
|
|
200
|
-
return self._check_response(
|
140
|
+
return self._check_response(pangea_response)
|
201
141
|
|
202
|
-
|
203
|
-
|
204
|
-
self,
|
205
|
-
path: str,
|
206
|
-
result_class: Type[TResult],
|
207
|
-
check_response: bool = True,
|
208
|
-
*,
|
209
|
-
params: (
|
210
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
211
|
-
| None
|
212
|
-
) = None,
|
213
|
-
pangea_response: Literal[True] = True,
|
214
|
-
) -> PangeaResponse[TResult]:
|
215
|
-
"""
|
216
|
-
Makes the GET call to a Pangea Service endpoint.
|
142
|
+
async def get(self, path: str, result_class: Type[TResult], check_response: bool = True) -> PangeaResponse[TResult]:
|
143
|
+
"""Makes the GET call to a Pangea Service endpoint.
|
217
144
|
|
218
145
|
Args:
|
219
|
-
|
220
|
-
|
146
|
+
endpoint(str): The Pangea Service API endpoint.
|
147
|
+
path(str): Additional URL path
|
221
148
|
|
222
149
|
Returns:
|
223
150
|
PangeaResponse which contains the response in its entirety and
|
224
151
|
various properties to retrieve individual fields
|
225
152
|
"""
|
226
153
|
|
227
|
-
@overload
|
228
|
-
async def get(
|
229
|
-
self,
|
230
|
-
path: str,
|
231
|
-
result_class: Type[TResult],
|
232
|
-
check_response: bool = True,
|
233
|
-
*,
|
234
|
-
params: (
|
235
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
236
|
-
| None
|
237
|
-
) = None,
|
238
|
-
pangea_response: Literal[False] = False,
|
239
|
-
) -> TResult:
|
240
|
-
"""
|
241
|
-
Makes the GET call to a Pangea Service endpoint.
|
242
|
-
|
243
|
-
Args:
|
244
|
-
path: Additional URL path
|
245
|
-
params: Dictionary of querystring data to attach to the request
|
246
|
-
"""
|
247
|
-
|
248
|
-
async def get(
|
249
|
-
self,
|
250
|
-
path: str,
|
251
|
-
result_class: Type[TResult],
|
252
|
-
check_response: bool = True,
|
253
|
-
*,
|
254
|
-
params: (
|
255
|
-
Mapping[str | bytes | int | float, str | bytes | int | float | Iterable[str | bytes | int | float] | None]
|
256
|
-
| None
|
257
|
-
) = None,
|
258
|
-
pangea_response: bool = True,
|
259
|
-
) -> PangeaResponse[TResult] | TResult:
|
260
|
-
"""
|
261
|
-
Makes the GET call to a Pangea Service endpoint.
|
262
|
-
|
263
|
-
Args:
|
264
|
-
path: Additional URL path
|
265
|
-
params: Dictionary of querystring data to attach to the request
|
266
|
-
pangea_response: Whether or not the response body follows Pangea's
|
267
|
-
standard response schema
|
268
|
-
"""
|
269
|
-
|
270
154
|
url = self._url(path)
|
271
155
|
self.logger.debug(json.dumps({"service": self.service, "action": "get", "url": url}))
|
272
156
|
|
273
|
-
async with self.session.get(url,
|
157
|
+
async with self.session.get(url, headers=self._headers()) as requests_response:
|
274
158
|
await self._check_http_errors(requests_response)
|
275
|
-
|
276
|
-
if not pangea_response:
|
277
|
-
type_adapter = TypeAdapter(result_class)
|
278
|
-
return type_adapter.validate_python(await requests_response.json())
|
279
|
-
|
280
|
-
pangea_response_obj = PangeaResponse(
|
159
|
+
pangea_response = PangeaResponse(
|
281
160
|
requests_response, result_class=result_class, json=await requests_response.json()
|
282
161
|
)
|
283
162
|
|
284
163
|
self.logger.debug(
|
285
164
|
json.dumps(
|
286
|
-
{"service": self.service, "action": "get", "url": url, "response":
|
165
|
+
{"service": self.service, "action": "get", "url": url, "response": pangea_response.json},
|
287
166
|
default=default_encoder,
|
288
167
|
)
|
289
168
|
)
|
290
169
|
|
291
170
|
if check_response is False:
|
292
|
-
return
|
171
|
+
return pangea_response
|
293
172
|
|
294
|
-
return self._check_response(
|
173
|
+
return self._check_response(pangea_response)
|
295
174
|
|
296
175
|
async def _check_http_errors(self, resp: aiohttp.ClientResponse):
|
297
176
|
if resp.status == 503:
|
@@ -425,14 +304,6 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
425
304
|
attached_files = await self._get_attached_files(multipart_reader)
|
426
305
|
return MultipartResponse(pangea_json, attached_files) # type: ignore[arg-type]
|
427
306
|
|
428
|
-
async def _http_delete(
|
429
|
-
self,
|
430
|
-
url: str,
|
431
|
-
*,
|
432
|
-
headers: Mapping[str, str | bytes | None] = {},
|
433
|
-
) -> aiohttp.ClientResponse:
|
434
|
-
return await self.session.delete(url, headers=headers)
|
435
|
-
|
436
307
|
async def _http_post(
|
437
308
|
self,
|
438
309
|
url: str,
|
@@ -595,13 +466,46 @@ class PangeaRequestAsync(PangeaRequestBase):
|
|
595
466
|
|
596
467
|
@override
|
597
468
|
def _init_session(self) -> aiohttp.ClientSession:
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
469
|
+
return aiohttp.ClientSession(middlewares=[self._retry_middleware])
|
470
|
+
|
471
|
+
def _calculate_retry_timeout(self, remaining_retries: int) -> float:
|
472
|
+
max_retries = self.config.request_retries
|
473
|
+
nb_retries = min(max_retries - remaining_retries, 1000)
|
474
|
+
sleep_seconds = min(self.config.request_backoff * pow(2.0, nb_retries), MAX_RETRY_DELAY)
|
475
|
+
jitter = 1 - 0.25 * random()
|
476
|
+
timeout = sleep_seconds * jitter
|
477
|
+
return max(timeout, 0)
|
478
|
+
|
479
|
+
async def _retry_middleware(
|
480
|
+
self, request: aiohttp.ClientRequest, handler: aiohttp.ClientHandlerType
|
481
|
+
) -> aiohttp.ClientResponse:
|
482
|
+
max_retries = self.config.request_retries
|
483
|
+
request_ids = set[str]()
|
484
|
+
retries_taken = 0
|
485
|
+
for retries_taken in range(max_retries + 1):
|
486
|
+
remaining_retries = max_retries - retries_taken
|
487
|
+
|
488
|
+
if len(request_ids) > 0:
|
489
|
+
request.headers["X-Pangea-Retried-Request-Ids"] = ",".join(request_ids)
|
490
|
+
|
491
|
+
response = await handler(request)
|
492
|
+
|
493
|
+
request_id = response.headers.get("x-request-id")
|
494
|
+
if request_id:
|
495
|
+
request_ids.add(request_id)
|
496
|
+
|
497
|
+
if not response.ok and remaining_retries > 0 and self._should_retry(response):
|
498
|
+
await self._sleep_for_retry(retries_taken=retries_taken, max_retries=max_retries)
|
499
|
+
continue
|
500
|
+
|
501
|
+
break
|
502
|
+
|
503
|
+
return response
|
504
|
+
|
505
|
+
def _should_retry(self, response: aiohttp.ClientResponse) -> bool:
|
506
|
+
return response.status in RETRYABLE_HTTP_CODES
|
507
|
+
|
508
|
+
async def _sleep_for_retry(self, *, retries_taken: int, max_retries: int) -> None:
|
509
|
+
remaining_retries = max_retries - retries_taken
|
510
|
+
timeout = self._calculate_retry_timeout(remaining_retries)
|
511
|
+
await asyncio.sleep(timeout)
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# ruff: noqa: F401
|
2
|
-
|
3
1
|
from .ai_guard import AIGuardAsync
|
4
2
|
from .audit import AuditAsync
|
5
3
|
from .authn import AuthNAsync
|
@@ -7,9 +5,27 @@ from .authz import AuthZAsync
|
|
7
5
|
from .embargo import EmbargoAsync
|
8
6
|
from .file_scan import FileScanAsync
|
9
7
|
from .intel import DomainIntelAsync, FileIntelAsync, IpIntelAsync, UrlIntelAsync, UserIntelAsync
|
10
|
-
from .management import ManagementAsync
|
11
8
|
from .prompt_guard import PromptGuardAsync
|
12
9
|
from .redact import RedactAsync
|
13
10
|
from .sanitize import SanitizeAsync
|
14
11
|
from .share import ShareAsync
|
15
12
|
from .vault import VaultAsync
|
13
|
+
|
14
|
+
__all__ = (
|
15
|
+
"AIGuardAsync",
|
16
|
+
"AuditAsync",
|
17
|
+
"AuthNAsync",
|
18
|
+
"AuthZAsync",
|
19
|
+
"DomainIntelAsync",
|
20
|
+
"EmbargoAsync",
|
21
|
+
"FileIntelAsync",
|
22
|
+
"FileScanAsync",
|
23
|
+
"IpIntelAsync",
|
24
|
+
"PromptGuardAsync",
|
25
|
+
"RedactAsync",
|
26
|
+
"SanitizeAsync",
|
27
|
+
"ShareAsync",
|
28
|
+
"UrlIntelAsync",
|
29
|
+
"UserIntelAsync",
|
30
|
+
"VaultAsync",
|
31
|
+
)
|