crypticorn 2.5.0rc1__py3-none-any.whl → 2.5.0rc3__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.
@@ -63,6 +63,8 @@ class ApiErrorIdentifier(StrEnum):
63
63
  INSUFFICIENT_SCOPES = "insufficient_scopes"
64
64
  INVALID_API_KEY = "invalid_api_key"
65
65
  INVALID_BEARER = "invalid_bearer"
66
+ INVALID_DATA_REQUEST = "invalid_data"
67
+ INVALID_DATA_RESPONSE = "invalid_data_response"
66
68
  INVALID_EXCHANGE_KEY = "invalid_exchange_key"
67
69
  INVALID_MARGIN_MODE = "invalid_margin_mode"
68
70
  INVALID_PARAMETER = "invalid_parameter_provided"
@@ -263,6 +265,16 @@ class ApiError(Enum, metaclass=Fallback):
263
265
  ApiErrorType.USER_ERROR,
264
266
  ApiErrorLevel.ERROR,
265
267
  )
268
+ INVALID_DATA_REQUEST = (
269
+ ApiErrorIdentifier.INVALID_DATA_REQUEST,
270
+ ApiErrorType.USER_ERROR,
271
+ ApiErrorLevel.ERROR,
272
+ )
273
+ INVALID_DATA_RESPONSE = (
274
+ ApiErrorIdentifier.INVALID_DATA_RESPONSE,
275
+ ApiErrorType.USER_ERROR,
276
+ ApiErrorLevel.ERROR,
277
+ )
266
278
  INVALID_EXCHANGE_KEY = (
267
279
  ApiErrorIdentifier.INVALID_EXCHANGE_KEY,
268
280
  ApiErrorType.SERVER_ERROR,
@@ -489,6 +501,8 @@ class HttpStatusMapper:
489
501
  ApiError.BOT_ALREADY_DELETED: status.HTTP_409_CONFLICT,
490
502
  # Invalid Content
491
503
  ApiError.CONTENT_TYPE_ERROR: status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
504
+ ApiError.INVALID_DATA_REQUEST: status.HTTP_422_UNPROCESSABLE_ENTITY,
505
+ ApiError.INVALID_DATA_RESPONSE: status.HTTP_422_UNPROCESSABLE_ENTITY,
492
506
  # Rate Limits
493
507
  ApiError.EXCHANGE_RATE_LIMIT: status.HTTP_429_TOO_MANY_REQUESTS,
494
508
  ApiError.REQUEST_SCOPE_EXCEEDED: status.HTTP_429_TOO_MANY_REQUESTS,
@@ -1,33 +1,80 @@
1
- from typing import Optional, Dict
2
- from pydantic import BaseModel
3
- from fastapi import HTTPException as FastAPIHTTPException
4
- from crypticorn.common import ApiError
1
+ from enum import Enum
2
+ from typing import Optional, Dict, Type
3
+ from pydantic import BaseModel, Field
4
+ from fastapi import HTTPException as FastAPIHTTPException, Request, FastAPI
5
+ from fastapi.exceptions import RequestValidationError, ResponseValidationError
6
+ from fastapi.openapi.utils import get_openapi
7
+ from fastapi.responses import JSONResponse
8
+ from crypticorn.common import ApiError, ApiErrorIdentifier, ApiErrorType, ApiErrorLevel
9
+
10
+ class EnrichedDetail(BaseModel):
11
+ '''This is the detail of the exception. It is used to enrich the exception with additional information by unwrapping the ApiError into its components.'''
12
+ message: Optional[str] = Field(None, description="An additional error message")
13
+ code: ApiErrorIdentifier = Field(..., description="The unique error code")
14
+ type: ApiErrorType = Field(..., description="The type of error")
15
+ level: ApiErrorLevel = Field(..., description="The level of the error")
16
+ status_code: int = Field(..., description="The HTTP status code")
5
17
 
6
18
 
7
19
  class ExceptionDetail(BaseModel):
8
- message: str
9
- error: ApiError
20
+ '''This is the detail of the exception. Pass an ApiError to the constructor and an optional human readable message.'''
21
+ error: ApiError = Field(..., description="The unique error code")
22
+ message: Optional[str] = Field(None, description="An additional error message")
10
23
 
24
+ def enrich(self) -> EnrichedDetail:
25
+ return EnrichedDetail(
26
+ message=self.message,
27
+ code=self.error.identifier,
28
+ type=self.error.type,
29
+ level=self.error.level,
30
+ status_code=self.error.status_code,
31
+ )
11
32
 
12
33
  class HTTPException(FastAPIHTTPException):
34
+ """A custom HTTP exception wrapper around FastAPI's HTTPException.
35
+ It allows for a more structured way to handle errors, with a message and an error code. The status code is being derived from the detail's error.
36
+ The ApiError class is the source of truth for everything. If the error is not yet implemented, there are fallbacks to avoid errors while testing.
37
+ """
38
+
13
39
  def __init__(
14
40
  self,
15
- status_code: int,
16
- detail: ExceptionDetail | str,
41
+ detail: ExceptionDetail,
17
42
  headers: Optional[Dict[str, str]] = None,
18
43
  ):
19
- try:
20
- exc = (
21
- ExceptionDetail(**detail)
22
- if not isinstance(detail, ExceptionDetail)
23
- else detail
24
- )
25
- except Exception as e:
26
- exc = ExceptionDetail(
27
- message=detail,
28
- error=ApiError.UNKNOWN_ERROR,
29
- )
30
-
44
+ assert isinstance(detail, ExceptionDetail)
45
+ body = detail.enrich()
31
46
  super().__init__(
32
- status_code=status_code, detail=exc.model_dump(), headers=headers
47
+ status_code=body.status_code,
48
+ detail=body.model_dump(mode="json"),
49
+ headers=headers,
33
50
  )
51
+
52
+ async def general_handler(request: Request, exc: Exception):
53
+ '''This is the default exception handler for all exceptions.'''
54
+ body = ExceptionDetail(message=str(exc), error=ApiError.UNKNOWN_ERROR)
55
+ return JSONResponse(status_code=body.error.status_code, content=HTTPException(detail=body).detail)
56
+
57
+ async def request_validation_handler(request: Request, exc: RequestValidationError):
58
+ '''This is the exception handler for all request validation errors.'''
59
+ body = ExceptionDetail(message=str(exc), error=ApiError.INVALID_DATA_REQUEST)
60
+ return JSONResponse(status_code=body.error.status_code, content=HTTPException(detail=body).detail)
61
+
62
+ async def response_validation_handler(request: Request, exc: ResponseValidationError):
63
+ '''This is the exception handler for all response validation errors.'''
64
+ body = ExceptionDetail(message=str(exc), error=ApiError.INVALID_DATA_RESPONSE)
65
+ return JSONResponse(status_code=body.error.status_code, content=HTTPException(detail=body).detail)
66
+
67
+ async def http_handler(request: Request, exc: HTTPException):
68
+ '''This is the exception handler for HTTPExceptions. It unwraps the HTTPException and returns the detail in a flat JSON response.'''
69
+ return JSONResponse(status_code=exc.status_code, content=exc.detail)
70
+
71
+ def register_exception_handlers(app: FastAPI):
72
+ '''Utility to register serveral exception handlers in one go. Catches Exception, HTTPException and Data Validation errors and responds with a unified json body.'''
73
+ app.add_exception_handler(Exception, general_handler)
74
+ app.add_exception_handler(FastAPIHTTPException, http_handler)
75
+ app.add_exception_handler(RequestValidationError, request_validation_handler)
76
+ app.add_exception_handler(ResponseValidationError, response_validation_handler)
77
+
78
+ exception_response = {
79
+ "default": {"model": EnrichedDetail, "description": "Error response", "media_type": "application/txt"}
80
+ }
@@ -35,7 +35,7 @@ def gen_random_id(length: int = 20) -> str:
35
35
  return "".join(random.choice(charset) for _ in range(length))
36
36
 
37
37
 
38
- @deprecated(reason="Use math.isclose instead. Will be removed in a future version.")
38
+ @deprecated("Use math.isclose instead. Will be removed in a future version.")
39
39
  def is_equal(
40
40
  a: float | Decimal,
41
41
  b: float | Decimal,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: crypticorn
3
- Version: 2.5.0rc1
3
+ Version: 2.5.0rc3
4
4
  Summary: Maximise Your Crypto Trading Profits with AI Predictions
5
5
  Author-email: Crypticorn <timon@crypticorn.com>
6
6
  Project-URL: Homepage, https://crypticorn.com
@@ -62,13 +62,13 @@ crypticorn/cli/templates/auth.py,sha256=Q1TxlA7qzhjvrqp1xz1aV2vGnj3DKFNN-VSl3o0B
62
62
  crypticorn/common/__init__.py,sha256=ZR6znFCEMAJH-FuSMga0KaTpAXj3tl9Xz_4Xi820l9w,312
63
63
  crypticorn/common/auth.py,sha256=q5DwgoIzveGWXCHpGl4xMPEABsjufptTqsR92MsOi6M,7618
64
64
  crypticorn/common/enums.py,sha256=6cCwQZVdXUoN33WA8kSf4LeSZyExZcWO2ahSsgGddCs,1243
65
- crypticorn/common/errors.py,sha256=LyX32A8Bgl20DrQmeUOPYYp1KIj4uRjvha4VyTFZm0A,20102
66
- crypticorn/common/exceptions.py,sha256=lSJojYl8DiK4YJbxYGOsisJdl8WTWnxqO0ed97BsoB8,883
65
+ crypticorn/common/errors.py,sha256=sRlcNe84sJxU2ujikhjSG7eJ9GdOqOo5uv7duYDATBo,20645
66
+ crypticorn/common/exceptions.py,sha256=_KfHjEhqEx44eJeELvmXFMnCT8wWLJkj9LQj8ePJybQ,4292
67
67
  crypticorn/common/pydantic.py,sha256=pmnGYCIrLv59wZkDbvPyK9NJmgPJWW74LXTdIWSjOkY,1063
68
68
  crypticorn/common/scopes.py,sha256=MgH9sGodJfPjEqVtFsaczNmwEaGL2wuGzeTpga_ehXs,2407
69
69
  crypticorn/common/sorter.py,sha256=keRRp4u7KJk3nS2A8tMdSF8Hbc1jcsre8KdTVuetfGc,1278
70
70
  crypticorn/common/urls.py,sha256=X557WaODUqW2dECi-mOjTbmhkSpnp40fPXDdvlnBXfo,805
71
- crypticorn/common/utils.py,sha256=oB9rYfy4TeYWafItACI4x8QH1R2lT2p6HSoK1eRnQrM,2092
71
+ crypticorn/common/utils.py,sha256=crH8_p_HpuWSaH9zsxwyGRaBjgZV_5Msm6lfW028MWs,2085
72
72
  crypticorn/hive/__init__.py,sha256=hRfTlEzEql4msytdUC_04vfaHzVKG5CGZle1M-9QFgY,81
73
73
  crypticorn/hive/main.py,sha256=U4wurkxnKai2i7iiq049ah9nVzBxmexRxBdFIWfd9qE,631
74
74
  crypticorn/hive/client/__init__.py,sha256=b4XBX-fBb0vVHY6323ADasKFSbL66UDeUi0aV3-aNj8,2376
@@ -283,8 +283,8 @@ crypticorn/trade/client/models/tpsl.py,sha256=LlqzHaSA-HgQp1k4PhRckmxWNhgVZU6NgB
283
283
  crypticorn/trade/client/models/trading_action_type.py,sha256=oLVDp94VeC9kjYbgZN7dHn2t07YGGUrAkNr2PE435eM,827
284
284
  crypticorn/trade/client/models/validation_error.py,sha256=x4rR325juK4EJiFJ8l5IKp2werY8y6PWbLx_WJMxbbA,3208
285
285
  crypticorn/trade/client/models/validation_error_loc_inner.py,sha256=ZB2NbHkxhjDZ2-qK1HyvzTUnabeCdxeTjbSAHNmWq5A,5111
286
- crypticorn-2.5.0rc1.dist-info/METADATA,sha256=jaLSSOGCOkdJDTjosuOox9fpd55KSH_a8RrwQYHYIzE,6210
287
- crypticorn-2.5.0rc1.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
288
- crypticorn-2.5.0rc1.dist-info/entry_points.txt,sha256=d_xHsGvUTebPveVUK0SrpDFQ5ZRSjlI7lNCc11sn2PM,59
289
- crypticorn-2.5.0rc1.dist-info/top_level.txt,sha256=EP3NY216qIBYfmvGl0L2Zc9ItP0DjGSkiYqd9xJwGcM,11
290
- crypticorn-2.5.0rc1.dist-info/RECORD,,
286
+ crypticorn-2.5.0rc3.dist-info/METADATA,sha256=Efy7jNBKex8LUfaKHSGg9GLixf-EDUZg6T_8eP6Xzpg,6210
287
+ crypticorn-2.5.0rc3.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
288
+ crypticorn-2.5.0rc3.dist-info/entry_points.txt,sha256=d_xHsGvUTebPveVUK0SrpDFQ5ZRSjlI7lNCc11sn2PM,59
289
+ crypticorn-2.5.0rc3.dist-info/top_level.txt,sha256=EP3NY216qIBYfmvGl0L2Zc9ItP0DjGSkiYqd9xJwGcM,11
290
+ crypticorn-2.5.0rc3.dist-info/RECORD,,