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 CHANGED
@@ -1,6 +1,6 @@
1
1
  from panther.main import Panther # noqa: F401
2
2
 
3
- __version__ = '4.1.0'
3
+ __version__ = '4.1.2'
4
4
 
5
5
 
6
6
  def version():
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
- received_message = queue.get()
56
- await self._handle_received_message(received_message=received_message)
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()
@@ -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()
@@ -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
- uvicorn.run('main:app', **command)
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 = await pagination.paginate()
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
@@ -66,8 +66,8 @@ LOGGING = {
66
66
  },
67
67
  'uvicorn.error': {
68
68
  'handlers': ['default'],
69
- 'level': 'WARNING',
69
+ 'level': 'INFO',
70
70
  'propagate': False,
71
71
  },
72
72
  }
73
- }
73
+ }
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
- async def paginate(self):
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': self.cursor.skip(skip=self.skip).limit(limit=self.limit)
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: any):
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 4.1.0
3
+ Version: 4.1.2
4
4
  Summary: Fast & Friendly, Web Framework For Building Async APIs
5
5
  Home-page: https://github.com/alirn76/panther
6
6
  Author: Ali RajabNezhad
@@ -1,24 +1,24 @@
1
- panther/__init__.py,sha256=y4niTNbN1Jwrm8eXNSrzLlVOGoajNgm3E6I1fs2i7QA,110
1
+ panther/__init__.py,sha256=jygFzW2zuio44KfIpTdDG3C8iAGBD_vxBn8GVGODSDk,110
2
2
  panther/_load_configs.py,sha256=AVkoixkUFkBQiTmrLrwCmg0eiPW2U_Uw2EGNEGQRfnI,9281
3
- panther/_utils.py,sha256=xeVR0yHvczhXv2XXrpoa6SHpGTDTFxNxiemXTdbsqjM,4279
4
- panther/app.py,sha256=a0QLpxM5fwLyWDHZjVaYrLw0_Rfx3p2NnZWcfyCzwf4,7267
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=hJN_ItUGLpk0QMWrExlDHQahiu7hYgc_jVvHWxqqpq4,10547
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=HGbCGvHEpbWaY4MrtI_Wphkm9YEGR8PV_X9uERSRGVI,6576
15
- panther/logging.py,sha256=t0nQXsSIwIxShqFnjRGp6lhO4Ybf1SnwJraDSTqMHFM,2211
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=efpsWMgLBaTWXhnhMAf6fyIrGTmVOFbmHpX03GgEJh0,1574
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=G0Ychc3Tp5UANtmst7o4npOrmdca6kKC_HYYoeYWLUQ,7266
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=aK70JyQz7-NXBYR4Ed4YQwtR55ypks9vaTK_h7Ck5kE,3105
33
- panther/cli/run_command.py,sha256=X_T9jP_Z8X_fm9S4LSoR6ARsPp4rCTMi1E5c7QWREjM,2120
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=P2mNO_WHJmQxwCL3VgZ-5AN97yx_cMHF2MevZyxlZDc,3959
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=BjBOdTyByDWs3S2V0leERd_47ZhoTbH306TIp-0Wgo8,2570
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.0.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
53
- panther-4.1.0.dist-info/METADATA,sha256=oxZtMCsV7y5Hdhejw9V3NTrXaOof5nz0jLlvHDeE2pA,6376
54
- panther-4.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
55
- panther-4.1.0.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
56
- panther-4.1.0.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
57
- panther-4.1.0.dist-info/RECORD,,
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,,