kiba-core 0.5.3.dev53__tar.gz → 0.5.3.dev55__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.
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/CHANGELOG.md +1 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/PKG-INFO +1 -1
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/authorizer.py +2 -1
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/middleware/exception_handling_middleware.py +2 -4
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/exceptions.py +43 -1
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/requester/requester.py +1 -1
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/kiba_core.egg-info/PKG-INFO +1 -1
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/pyproject.toml +1 -1
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/uv.lock +393 -374
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/.github/pull_request_template.md +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/.github/workflows/deploy.yml +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/.github/workflows/pull-request.yml +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/.github/workflows/release.yml +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/.gitignore +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/Dockerfile +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/README.md +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/api_request.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/api_response.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/default_routes.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/health.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/json_route.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/middleware/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/middleware/database_connection_middleware.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/middleware/logging_middleware.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/middleware/server_headers_middleware.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/streaming_json_route.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/aws_requester.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/caching/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/caching/cache.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/caching/dict_cache.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/caching/file_cache.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/http/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/http/basic_authentication.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/http/jwt.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/http/rest_method.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/logging.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/notifications/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/notifications/discord_client.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/notifications/notification_client.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/notifications/slack_client.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/py.typed +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/queues/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/queues/aqs.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/queues/cosmos.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/queues/message_queue.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/queues/message_queue_processor.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/queues/model.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/queues/sql.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/queues/sqs.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/requester/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/s3_manager.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/service_client.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/store/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/store/database.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/store/retriever.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/store/saver.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/async_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/chain_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/date_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/dict_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/file_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/hashing_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/http_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/json_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/list_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/string_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/typing_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/url_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/util/value_holder.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/web3/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/web3/eth_client.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/web3/multicall3.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/kiba_core.egg-info/SOURCES.txt +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/kiba_core.egg-info/dependency_links.txt +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/kiba_core.egg-info/requires.txt +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/kiba_core.egg-info/top_level.txt +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/makefile +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/setup.cfg +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/api/test_authorizer.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/api/test_json_route.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/api/test_streaming_json_route.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/caching/test_dict_cache.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/caching/test_file_cache.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_async_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_chain_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_date_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_dict_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_file_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_json_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_list_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_string_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/util/test_url_util.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/web3/__init__.py +0 -0
- {kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/tests/web3/test_rest_eth_client.py +0 -0
|
@@ -34,6 +34,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
|
34
34
|
- [MINOR] Added `blockNumber` to `EthClient.multicall` function
|
|
35
35
|
- [MINOR] Added `SqlMessageQueue` and `CosmosMessageQueue` to support database backed queues
|
|
36
36
|
- [MINOR] Added `shouldAllowFailures` to `EthClient.multicall` to allow individual calls to fail without reverting the whole batch
|
|
37
|
+
- [MINOR] Added `KibaException.from_headers`/`outgoing_headers` hooks so exceptions can be built from and emit response headers; used by `TooManyRequestsException` (`Retry-After`, now also parsed automatically by `Requester` from 429 responses) and `RedirectException` (`Location`/`Cache-Control`), and applied generically by `ExceptionHandlingMiddleware`
|
|
37
38
|
|
|
38
39
|
### Changed
|
|
39
40
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import functools
|
|
2
|
+
import hmac
|
|
2
3
|
import typing
|
|
3
4
|
from collections.abc import AsyncIterator
|
|
4
5
|
|
|
@@ -101,7 +102,7 @@ class StaticTokenAuthorizer(TokenAuthorizer):
|
|
|
101
102
|
self._token = token
|
|
102
103
|
|
|
103
104
|
async def validate_token(self, token: str) -> None:
|
|
104
|
-
if token
|
|
105
|
+
if not hmac.compare_digest(token, self._token):
|
|
105
106
|
raise ForbiddenException(message='AUTH_INVALID')
|
|
106
107
|
|
|
107
108
|
|
{kiba_core-0.5.3.dev53 → kiba_core-0.5.3.dev55}/core/api/middleware/exception_handling_middleware.py
RENAMED
|
@@ -20,6 +20,7 @@ class ExceptionHandlingMiddleware(BaseHTTPMiddleware):
|
|
|
20
20
|
@staticmethod
|
|
21
21
|
def _convert_exception(exception: KibaException) -> Response:
|
|
22
22
|
response = JSONResponse(status_code=exception.statusCode, content=exception.to_dict())
|
|
23
|
+
response.headers.update(exception.outgoing_headers())
|
|
23
24
|
return response
|
|
24
25
|
|
|
25
26
|
async def dispatch(self, request: Request, call_next: RequestResponseEndpoint) -> Response:
|
|
@@ -27,10 +28,7 @@ class ExceptionHandlingMiddleware(BaseHTTPMiddleware):
|
|
|
27
28
|
response = await call_next(request)
|
|
28
29
|
except RedirectException as exception:
|
|
29
30
|
response = Response(status_code=exception.statusCode)
|
|
30
|
-
response.headers
|
|
31
|
-
# TODO(krishan711): clean this up after major version bump (since shouldAddCacheHeader field will always exist)
|
|
32
|
-
if not hasattr(exception, 'shouldAddCacheHeader') or exception.shouldAddCacheHeader:
|
|
33
|
-
response.headers['Cache-Control'] = f'max-age={60 * 60 * 24 * 365}'
|
|
31
|
+
response.headers.update(exception.outgoing_headers())
|
|
34
32
|
except ClientException as exception:
|
|
35
33
|
if self.shouldSquashClientExceptions:
|
|
36
34
|
logging.error(f'{exception.exceptionType} occurred: {exception.message}')
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import datetime
|
|
4
|
+
import email.utils
|
|
3
5
|
from collections.abc import Mapping
|
|
4
6
|
|
|
5
7
|
from core.util.typing_util import JsonObject
|
|
@@ -18,6 +20,15 @@ class KibaException(Exception): # noqa: N818
|
|
|
18
20
|
return exception
|
|
19
21
|
return KibaException(message=str(exception), statusCode=statusCode, exceptionType=exception.__class__.__name__)
|
|
20
22
|
|
|
23
|
+
@classmethod
|
|
24
|
+
def from_headers(cls, message: str | None, statusCode: int, headers: Mapping[str, str]) -> KibaException: # noqa: ARG003
|
|
25
|
+
# NOTE(krishan711): default ignores headers; subclasses that need response headers (e.g. Retry-After, Location) should override
|
|
26
|
+
return cls(message=message)
|
|
27
|
+
|
|
28
|
+
def outgoing_headers(self) -> Mapping[str, str]:
|
|
29
|
+
# NOTE(krishan711): default has no extra headers; subclasses that need to set response headers (e.g. Retry-After, Location) should override
|
|
30
|
+
return {}
|
|
31
|
+
|
|
21
32
|
def to_dict(self) -> JsonObject:
|
|
22
33
|
return {
|
|
23
34
|
'exceptionType': self.exceptionType,
|
|
@@ -194,9 +205,34 @@ class PreconditionRequiredException(ClientException):
|
|
|
194
205
|
|
|
195
206
|
|
|
196
207
|
class TooManyRequestsException(ClientException):
|
|
197
|
-
def __init__(self, message: str | None = None) -> None:
|
|
208
|
+
def __init__(self, message: str | None = None, retryAfterSeconds: int | None = None) -> None:
|
|
198
209
|
message = message or 'Too Many Requests'
|
|
199
210
|
super().__init__(message=message, statusCode=429)
|
|
211
|
+
self.retryAfterSeconds = retryAfterSeconds
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def _parse_retry_after_header(value: str | None) -> int | None:
|
|
215
|
+
if not value:
|
|
216
|
+
return None
|
|
217
|
+
if value.strip().isdigit():
|
|
218
|
+
return int(value.strip())
|
|
219
|
+
try:
|
|
220
|
+
retryDate = email.utils.parsedate_to_datetime(value)
|
|
221
|
+
except (TypeError, ValueError):
|
|
222
|
+
return None
|
|
223
|
+
if retryDate.tzinfo is None:
|
|
224
|
+
retryDate = retryDate.replace(tzinfo=datetime.UTC)
|
|
225
|
+
remainingSeconds = int((retryDate - datetime.datetime.now(datetime.UTC)).total_seconds())
|
|
226
|
+
return max(0, remainingSeconds)
|
|
227
|
+
|
|
228
|
+
@classmethod
|
|
229
|
+
def from_headers(cls, message: str | None, statusCode: int, headers: Mapping[str, str]) -> TooManyRequestsException: # noqa: ARG003
|
|
230
|
+
return cls(message=message, retryAfterSeconds=cls._parse_retry_after_header(headers.get('Retry-After')))
|
|
231
|
+
|
|
232
|
+
def outgoing_headers(self) -> Mapping[str, str]:
|
|
233
|
+
if self.retryAfterSeconds is None:
|
|
234
|
+
return {}
|
|
235
|
+
return {'Retry-After': str(self.retryAfterSeconds)}
|
|
200
236
|
|
|
201
237
|
|
|
202
238
|
class RequestHeaderFieldsTooLargeException(ClientException):
|
|
@@ -394,6 +430,12 @@ class RedirectException(KibaException):
|
|
|
394
430
|
self.location = location
|
|
395
431
|
self.shouldAddCacheHeader = shouldAddCacheHeader
|
|
396
432
|
|
|
433
|
+
def outgoing_headers(self) -> Mapping[str, str]:
|
|
434
|
+
headers = {'Location': self.location}
|
|
435
|
+
if self.shouldAddCacheHeader:
|
|
436
|
+
headers['Cache-Control'] = f'max-age={60 * 60 * 24 * 365}'
|
|
437
|
+
return headers
|
|
438
|
+
|
|
397
439
|
|
|
398
440
|
class MovedPermanentlyRedirectException(RedirectException):
|
|
399
441
|
def __init__(self, location: str, message: str | None = None, shouldAddCacheHeader: bool | None = True) -> None:
|
|
@@ -143,7 +143,7 @@ class Requester:
|
|
|
143
143
|
message = httpxResponse.headers['www-authenticate']
|
|
144
144
|
if HTTP_EXCEPTIONS_MAP.get(httpxResponse.status_code) is not None:
|
|
145
145
|
exceptionCls = HTTP_EXCEPTIONS_MAP[httpxResponse.status_code]
|
|
146
|
-
exception: KibaException = exceptionCls(message=message)
|
|
146
|
+
exception: KibaException = exceptionCls.from_headers(message=message, statusCode=httpxResponse.status_code, headers=httpxResponse.headers)
|
|
147
147
|
else:
|
|
148
148
|
exception = ResponseException(message=message, statusCode=httpxResponse.status_code, headers=httpxResponse.headers)
|
|
149
149
|
raise exception
|