panther 4.1.0__py3-none-any.whl → 4.1.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.
- panther/__init__.py +1 -1
- panther/_utils.py +1 -1
- panther/app.py +2 -0
- panther/base_websocket.py +5 -2
- panther/cli/monitor_command.py +1 -1
- panther/cli/run_command.py +3 -1
- panther/db/connections.py +7 -1
- panther/db/models.py +1 -1
- panther/generics.py +5 -5
- panther/logging.py +2 -2
- panther/pagination.py +5 -2
- panther/response.py +8 -1
- {panther-4.1.0.dist-info → panther-4.1.2.dist-info}/METADATA +1 -1
- {panther-4.1.0.dist-info → panther-4.1.2.dist-info}/RECORD +18 -18
- {panther-4.1.0.dist-info → panther-4.1.2.dist-info}/LICENSE +0 -0
- {panther-4.1.0.dist-info → panther-4.1.2.dist-info}/WHEEL +0 -0
- {panther-4.1.0.dist-info → panther-4.1.2.dist-info}/entry_points.txt +0 -0
- {panther-4.1.0.dist-info → panther-4.1.2.dist-info}/top_level.txt +0 -0
panther/__init__.py
CHANGED
panther/_utils.py
CHANGED
@@ -82,7 +82,7 @@ def clean_traceback_message(exception: Exception) -> str:
|
|
82
82
|
tb = TracebackException(type(exception), exception, exception.__traceback__)
|
83
83
|
stack = tb.stack.copy()
|
84
84
|
for t in stack:
|
85
|
-
if t.filename.find('site-packages/panther') != -1:
|
85
|
+
if t.filename.find('site-packages/panther') != -1 or t.filename.find('site-packages\\panther') != -1:
|
86
86
|
tb.stack.remove(t)
|
87
87
|
_traceback = list(tb.format(chain=False))
|
88
88
|
return exception if len(_traceback) == 1 else f'{exception}\n' + ''.join(_traceback)
|
panther/app.py
CHANGED
@@ -96,6 +96,8 @@ class API:
|
|
96
96
|
response = Response(data=response)
|
97
97
|
if self.output_model and response.data:
|
98
98
|
response.data = await response.apply_output_model(output_model=self.output_model)
|
99
|
+
if response.pagination:
|
100
|
+
response.data = await response.pagination.template(response.data)
|
99
101
|
|
100
102
|
# 10. Set New Response To Cache
|
101
103
|
if self.cache and self.request.method == 'GET':
|
panther/base_websocket.py
CHANGED
@@ -52,8 +52,11 @@ class WebsocketConnections(Singleton):
|
|
52
52
|
queue = self.pubsub.subscribe()
|
53
53
|
logger.info("Subscribed to 'websocket_connections' queue")
|
54
54
|
while True:
|
55
|
-
|
56
|
-
|
55
|
+
try:
|
56
|
+
received_message = queue.get()
|
57
|
+
await self._handle_received_message(received_message=received_message)
|
58
|
+
except InterruptedError:
|
59
|
+
break
|
57
60
|
else:
|
58
61
|
# We have a redis connection, so use it for pubsub
|
59
62
|
self.pubsub = self.pubsub_connection.pubsub()
|
panther/cli/monitor_command.py
CHANGED
@@ -57,7 +57,7 @@ class Monitoring:
|
|
57
57
|
|
58
58
|
# Check log file
|
59
59
|
if not self.monitoring_log_file.exists():
|
60
|
-
return f'`{self.monitoring_log_file}` file not found. (Make sure `MONITORING` is `True` in `configs`'
|
60
|
+
return f'`{self.monitoring_log_file}` file not found. (Make sure `MONITORING` is `True` in `configs` and you have at least one record)'
|
61
61
|
|
62
62
|
# Initialize Deque
|
63
63
|
self.update_rows()
|
panther/cli/run_command.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import contextlib
|
1
2
|
import os
|
2
3
|
|
3
4
|
import uvicorn
|
@@ -75,6 +76,7 @@ def run(args: list[str]) -> None:
|
|
75
76
|
command.update(_handle_commands(args))
|
76
77
|
command.update(args)
|
77
78
|
try:
|
78
|
-
|
79
|
+
with contextlib.suppress(KeyboardInterrupt):
|
80
|
+
uvicorn.run('main:app', **command)
|
79
81
|
except TypeError as e:
|
80
82
|
cli_error(e)
|
panther/db/connections.py
CHANGED
@@ -122,7 +122,13 @@ class RedisConnection(Singleton, _Redis):
|
|
122
122
|
def sync_ping(self):
|
123
123
|
from redis import Redis
|
124
124
|
|
125
|
-
Redis(host=self.host, port=self.port, **self.kwargs).ping()
|
125
|
+
Redis(host=self.host, port=self.port, socket_timeout=3, **self.kwargs).ping()
|
126
|
+
|
127
|
+
async def execute_command(self, *args, **options):
|
128
|
+
if self.is_connected:
|
129
|
+
return await super().execute_command(*args, **options)
|
130
|
+
msg = '`REDIS` is not found in `configs`'
|
131
|
+
raise ValueError(msg)
|
126
132
|
|
127
133
|
def create_connection_for_websocket(self) -> _Redis:
|
128
134
|
if not hasattr(self, 'websocket_connection'):
|
panther/db/models.py
CHANGED
@@ -58,7 +58,7 @@ class BaseUser(Model):
|
|
58
58
|
|
59
59
|
async def login(self) -> dict:
|
60
60
|
"""Return dict of access and refresh token"""
|
61
|
-
return config.AUTHENTICATION.login(self.id)
|
61
|
+
return config.AUTHENTICATION.login(str(self.id))
|
62
62
|
|
63
63
|
async def logout(self) -> dict:
|
64
64
|
return await config.AUTHENTICATION.logout(self._auth_token)
|
panther/generics.py
CHANGED
@@ -65,10 +65,10 @@ class ListAPI(GenericAPI, CursorRequired):
|
|
65
65
|
pagination: type[Pagination]
|
66
66
|
|
67
67
|
async def get(self, request: Request, **kwargs):
|
68
|
-
cursor = await self.prepare_cursor(request=request, **kwargs)
|
69
|
-
return Response(data=cursor, status_code=status.HTTP_200_OK)
|
68
|
+
cursor, pagination = await self.prepare_cursor(request=request, **kwargs)
|
69
|
+
return Response(data=cursor, pagination=pagination, status_code=status.HTTP_200_OK)
|
70
70
|
|
71
|
-
async def prepare_cursor(self, request: Request, **kwargs):
|
71
|
+
async def prepare_cursor(self, request: Request, **kwargs) -> tuple[Cursor | PantherDBCursor, Pagination | None]:
|
72
72
|
cursor = await self.cursor(request=request, **kwargs)
|
73
73
|
self._check_cursor(cursor)
|
74
74
|
|
@@ -83,9 +83,9 @@ class ListAPI(GenericAPI, CursorRequired):
|
|
83
83
|
cursor = cursor.sort(sort)
|
84
84
|
|
85
85
|
if pagination := self.process_pagination(query_params=request.query_params, cursor=cursor):
|
86
|
-
cursor =
|
86
|
+
cursor = pagination.paginate()
|
87
87
|
|
88
|
-
return cursor
|
88
|
+
return cursor, pagination
|
89
89
|
|
90
90
|
def process_filters(self, query_params: dict, cursor: Cursor | PantherDBCursor) -> dict:
|
91
91
|
_filter = {}
|
panther/logging.py
CHANGED
panther/pagination.py
CHANGED
@@ -36,7 +36,10 @@ class Pagination:
|
|
36
36
|
previous_skip = max(self.skip - self.limit, 0)
|
37
37
|
return f'?limit={self.limit}&skip={previous_skip}'
|
38
38
|
|
39
|
-
|
39
|
+
def paginate(self):
|
40
|
+
return self.cursor.skip(skip=self.skip).limit(limit=self.limit)
|
41
|
+
|
42
|
+
async def template(self, response: list):
|
40
43
|
count = await self.cursor.cls.count(self.cursor.filter)
|
41
44
|
has_next = not bool(self.limit + self.skip >= count)
|
42
45
|
|
@@ -44,5 +47,5 @@ class Pagination:
|
|
44
47
|
'count': count,
|
45
48
|
'next': self.build_next_params() if has_next else None,
|
46
49
|
'previous': self.build_previous_params() if self.skip else None,
|
47
|
-
'results':
|
50
|
+
'results': response
|
48
51
|
}
|
panther/response.py
CHANGED
@@ -10,6 +10,7 @@ from panther._utils import to_async_generator
|
|
10
10
|
from panther.db.cursor import Cursor
|
11
11
|
from pantherdb import Cursor as PantherDBCursor
|
12
12
|
from panther.monitoring import Monitoring
|
13
|
+
from panther.pagination import Pagination
|
13
14
|
|
14
15
|
ResponseDataTypes = list | tuple | set | Cursor | PantherDBCursor | dict | int | float | str | bool | bytes | NoneType | Type[BaseModel]
|
15
16
|
IterableDataTypes = list | tuple | set | Cursor | PantherDBCursor
|
@@ -24,13 +25,19 @@ class Response:
|
|
24
25
|
data: ResponseDataTypes = None,
|
25
26
|
headers: dict | None = None,
|
26
27
|
status_code: int = status.HTTP_200_OK,
|
28
|
+
pagination: Pagination | None = None,
|
27
29
|
):
|
28
30
|
"""
|
29
31
|
:param data: should be an instance of ResponseDataTypes
|
30
32
|
:param headers: should be dict of headers
|
31
33
|
:param status_code: should be int
|
34
|
+
:param pagination: instance of Pagination or None
|
35
|
+
Its template() method will be used
|
32
36
|
"""
|
33
37
|
self.headers = headers or {}
|
38
|
+
self.pagination: Pagination | None = pagination
|
39
|
+
if isinstance(data, Cursor):
|
40
|
+
data = list(data)
|
34
41
|
self.initial_data = data
|
35
42
|
self.data = self.prepare_data(data=data)
|
36
43
|
self.status_code = self.check_status_code(status_code=status_code)
|
@@ -60,7 +67,7 @@ class Response:
|
|
60
67
|
def headers(self, headers: dict):
|
61
68
|
self._headers = headers
|
62
69
|
|
63
|
-
def prepare_data(self, data:
|
70
|
+
def prepare_data(self, data: Any):
|
64
71
|
"""Make sure the response data is only ResponseDataTypes or Iterable of ResponseDataTypes"""
|
65
72
|
if isinstance(data, (int | float | str | bool | bytes | NoneType)):
|
66
73
|
return data
|
@@ -1,24 +1,24 @@
|
|
1
|
-
panther/__init__.py,sha256=
|
1
|
+
panther/__init__.py,sha256=jygFzW2zuio44KfIpTdDG3C8iAGBD_vxBn8GVGODSDk,110
|
2
2
|
panther/_load_configs.py,sha256=AVkoixkUFkBQiTmrLrwCmg0eiPW2U_Uw2EGNEGQRfnI,9281
|
3
|
-
panther/_utils.py,sha256=
|
4
|
-
panther/app.py,sha256=
|
3
|
+
panther/_utils.py,sha256=j0rwIxTf0rtcZAAD-1nGE-_bWpvinyKtnwt3uO0hMmY,4330
|
4
|
+
panther/app.py,sha256=vb4j8CKidFHD5HIfK1t96fr8URMYxkroW8dQH9SVj14,7385
|
5
5
|
panther/authentications.py,sha256=gf7BVyQ8vXKhiumJAtD0aAK7uIHWx_snbOKYAKrYuVw,5677
|
6
6
|
panther/background_tasks.py,sha256=HBYubDIiO_673cl_5fqCUP9zzimzRgRkDSkag9Msnbs,7656
|
7
7
|
panther/base_request.py,sha256=Fwwpm-9bjAZdpzSdakmSas5BD3gh1nrc6iGcBxwa_94,4001
|
8
|
-
panther/base_websocket.py,sha256=
|
8
|
+
panther/base_websocket.py,sha256=iJUIbrfnh3ZLXlmKxTswMw158eNvtBFi8RZ-aBBmc8w,10643
|
9
9
|
panther/caching.py,sha256=ltuJYdjNiAaKIs3jpO5EBpL8Y6CF1vAIQqh8J_Np10g,4098
|
10
10
|
panther/configs.py,sha256=EaLApT6nYcguBoNXBG_8n6DU6HTNxsulI2943j8UAkE,3174
|
11
11
|
panther/events.py,sha256=bxDqrfiNNBlvD03vEk2LDK4xbMzTMFVcgAjx2ein7mI,1158
|
12
12
|
panther/exceptions.py,sha256=7rHdJIES2__kqOStIqbHl3Uxask2lzKgLQlkZvvDwFA,1591
|
13
13
|
panther/file_handler.py,sha256=XnomEigCUYOaXjkH4kD1kzpUbL2i9lLnR5kerruF6BA,846
|
14
|
-
panther/generics.py,sha256=
|
15
|
-
panther/logging.py,sha256=
|
14
|
+
panther/generics.py,sha256=SIK1Wqpfb_jKKt4xJPbYIhMY0QhtbhOXS68dIW4Y0bU,6671
|
15
|
+
panther/logging.py,sha256=k__vzvSrPpr1IsA4OLrBt1JHuRUBXr7ekPlBW0-9rbM,2209
|
16
16
|
panther/main.py,sha256=UbIxwaojvY_vH9nYfBpkulRBqVEj4Lbl81Er4XW_KCY,9334
|
17
17
|
panther/monitoring.py,sha256=y1F3c8FJlnmooM-m1nSyOTa9eWq0v1nHnmw9zz-4Kls,1314
|
18
|
-
panther/pagination.py,sha256=
|
18
|
+
panther/pagination.py,sha256=ANJrEF0q1nVAfD33I4nZfUUxFcETzJb01gIhbZX3HEw,1639
|
19
19
|
panther/permissions.py,sha256=9-J5vzvEKa_PITwEVQbZZv8PG2FOu05YBlD5yMrKcfc,348
|
20
20
|
panther/request.py,sha256=F9ZiAWSse7_6moAzqdoFInUN4zTKlzijh9AdU9w3Jfw,1673
|
21
|
-
panther/response.py,sha256=
|
21
|
+
panther/response.py,sha256=Njp4zJozNic8J4ucG8Sgh-xeBZOgtoz2cfdDkJlGOWU,7582
|
22
22
|
panther/routings.py,sha256=1eqbjubLnUUEQRlz8mIF464ImvCMjyasiekHBtxEQoQ,6218
|
23
23
|
panther/serializer.py,sha256=MBT43UG8YBjp-UGaqe5-SPqQHIcDEjLAdBjHAVKyMJo,9059
|
24
24
|
panther/status.py,sha256=Gc_PnYrHfInTsZpGbqiCfDB-py1C7Rh8KMdb6Lq9Exs,3346
|
@@ -29,14 +29,14 @@ panther/websocket.py,sha256=5WLw--Oa-6kGYbeRvO79hjbd0ARFcTTF40-hO_bdjmQ,1206
|
|
29
29
|
panther/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
30
|
panther/cli/create_command.py,sha256=cVhz0VQOvEbpufFsevH9T1rYZ8T_Wsa89cpTiIVKTC0,10294
|
31
31
|
panther/cli/main.py,sha256=pCqnOTazgMhTvFHTugutIsiFXueU5kx2VmGngwAl54Q,1679
|
32
|
-
panther/cli/monitor_command.py,sha256=
|
33
|
-
panther/cli/run_command.py,sha256=
|
32
|
+
panther/cli/monitor_command.py,sha256=7N1-4W0Lu7yl5maehJowe04WH4nxZ1DumGDRATh82SQ,3139
|
33
|
+
panther/cli/run_command.py,sha256=yWcDoWC-c4ph4M5EDj0jvR9xSjh-apG5r6-NpDdArUo,2195
|
34
34
|
panther/cli/template.py,sha256=rsyKOQ0l2v3kdwmLiZxt5ecIhDzmFprCCv0uVAv7eQI,5319
|
35
35
|
panther/cli/utils.py,sha256=Jd4YQ9H6lapVktl7ZmiORt30WVmKI85xcwsY-fMRq3c,5289
|
36
36
|
panther/db/__init__.py,sha256=w9lEL0vRqb18Qx_iUJipUR_fi5GQ5uVX0DWycx14x08,50
|
37
|
-
panther/db/connections.py,sha256=
|
37
|
+
panther/db/connections.py,sha256=rps48Ic2r3SV2HD3df1OU7C4Pv8j98PVXU1O_FqF9Ak,4210
|
38
38
|
panther/db/cursor.py,sha256=jJ6bhz_Zljt3-AoeVdi563e2q3MSDJPP33WVbQk-goE,1287
|
39
|
-
panther/db/models.py,sha256=
|
39
|
+
panther/db/models.py,sha256=4g6Jm2TBTViC2ttpENzuRJyvPbvXYzEqHIZLiWekRUk,2575
|
40
40
|
panther/db/utils.py,sha256=Uxh7UebkBv4thMCfooYW1pkuorFgocsbnBZJi-hHtdY,1582
|
41
41
|
panther/db/queries/__init__.py,sha256=uF4gvBjLBJ-Yl3WLqoZEVNtHCVhFRKW3_Vi44pJxDNI,45
|
42
42
|
panther/db/queries/base_queries.py,sha256=8HhdlsSW-lgz3-IrZYfOtHNC3TBWbCNErDR4XE718AY,3764
|
@@ -49,9 +49,9 @@ panther/panel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
49
|
panther/panel/apis.py,sha256=COsbwKZyTgyHvHYbpDfusifAH9ojMS3z1KhZCt9M-Ms,2428
|
50
50
|
panther/panel/urls.py,sha256=JiV-H4dWE-m_bfaTTVxzOxTvJmOWhyLOvcbM7xU3Bn4,240
|
51
51
|
panther/panel/utils.py,sha256=0Rv79oR5IEqalqwpRKQHMn1p5duVY5mxMqDKiA5mWx4,437
|
52
|
-
panther-4.1.
|
53
|
-
panther-4.1.
|
54
|
-
panther-4.1.
|
55
|
-
panther-4.1.
|
56
|
-
panther-4.1.
|
57
|
-
panther-4.1.
|
52
|
+
panther-4.1.2.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
|
53
|
+
panther-4.1.2.dist-info/METADATA,sha256=c0gW17fnBtGxqL-u4mM6gVckd4r3nrZRsbEKeh7MAOY,6376
|
54
|
+
panther-4.1.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
55
|
+
panther-4.1.2.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
|
56
|
+
panther-4.1.2.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
|
57
|
+
panther-4.1.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|