panther 1.7.1__tar.gz → 1.7.3__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.
Files changed (50) hide show
  1. {panther-1.7.1 → panther-1.7.3}/PKG-INFO +11 -6
  2. {panther-1.7.1 → panther-1.7.3}/README.md +10 -5
  3. {panther-1.7.1 → panther-1.7.3}/panther/__init__.py +1 -1
  4. {panther-1.7.1 → panther-1.7.3}/panther/app.py +11 -11
  5. panther-1.7.3/panther/cli/monitor_command.py +58 -0
  6. {panther-1.7.1 → panther-1.7.3}/panther/cli/template.py +27 -12
  7. {panther-1.7.1 → panther-1.7.3}/panther/db/utils.py +1 -1
  8. {panther-1.7.1 → panther-1.7.3}/panther/main.py +3 -4
  9. {panther-1.7.1 → panther-1.7.3}/panther/middlewares/redis.py +3 -11
  10. {panther-1.7.1 → panther-1.7.3}/panther.egg-info/PKG-INFO +11 -6
  11. panther-1.7.3/panther.egg-info/requires.txt +14 -0
  12. {panther-1.7.1 → panther-1.7.3}/setup.py +8 -6
  13. panther-1.7.1/panther/cli/monitor_command.py +0 -69
  14. panther-1.7.1/panther.egg-info/requires.txt +0 -12
  15. {panther-1.7.1 → panther-1.7.3}/LICENSE +0 -0
  16. {panther-1.7.1 → panther-1.7.3}/panther/_utils.py +0 -0
  17. {panther-1.7.1 → panther-1.7.3}/panther/authentications.py +0 -0
  18. {panther-1.7.1 → panther-1.7.3}/panther/caching.py +0 -0
  19. {panther-1.7.1 → panther-1.7.3}/panther/cli/__init__.py +0 -0
  20. {panther-1.7.1 → panther-1.7.3}/panther/cli/create_command.py +0 -0
  21. {panther-1.7.1 → panther-1.7.3}/panther/cli/main.py +0 -0
  22. {panther-1.7.1 → panther-1.7.3}/panther/cli/run_command.py +0 -0
  23. {panther-1.7.1 → panther-1.7.3}/panther/cli/utils.py +0 -0
  24. {panther-1.7.1 → panther-1.7.3}/panther/configs.py +0 -0
  25. {panther-1.7.1 → panther-1.7.3}/panther/db/__init__.py +0 -0
  26. {panther-1.7.1 → panther-1.7.3}/panther/db/connection.py +0 -0
  27. {panther-1.7.1 → panther-1.7.3}/panther/db/models.py +0 -0
  28. {panther-1.7.1 → panther-1.7.3}/panther/db/queries/__init__.py +0 -0
  29. {panther-1.7.1 → panther-1.7.3}/panther/db/queries/mongodb_queries.py +0 -0
  30. {panther-1.7.1 → panther-1.7.3}/panther/db/queries/pantherdb_queries.py +0 -0
  31. {panther-1.7.1 → panther-1.7.3}/panther/db/queries/queries.py +0 -0
  32. {panther-1.7.1 → panther-1.7.3}/panther/exceptions.py +0 -0
  33. {panther-1.7.1 → panther-1.7.3}/panther/logger.py +1 -1
  34. {panther-1.7.1 → panther-1.7.3}/panther/middlewares/__init__.py +0 -0
  35. {panther-1.7.1 → panther-1.7.3}/panther/middlewares/base.py +0 -0
  36. {panther-1.7.1 → panther-1.7.3}/panther/middlewares/db.py +0 -0
  37. {panther-1.7.1 → panther-1.7.3}/panther/middlewares/monitoring.py +0 -0
  38. {panther-1.7.1 → panther-1.7.3}/panther/permissions.py +0 -0
  39. {panther-1.7.1 → panther-1.7.3}/panther/request.py +0 -0
  40. {panther-1.7.1 → panther-1.7.3}/panther/response.py +0 -0
  41. {panther-1.7.1 → panther-1.7.3}/panther/routings.py +0 -0
  42. {panther-1.7.1 → panther-1.7.3}/panther/status.py +0 -0
  43. {panther-1.7.1 → panther-1.7.3}/panther/throttling.py +0 -0
  44. {panther-1.7.1 → panther-1.7.3}/panther/utils.py +0 -0
  45. {panther-1.7.1 → panther-1.7.3}/panther.egg-info/SOURCES.txt +0 -0
  46. {panther-1.7.1 → panther-1.7.3}/panther.egg-info/dependency_links.txt +0 -0
  47. {panther-1.7.1 → panther-1.7.3}/panther.egg-info/entry_points.txt +0 -0
  48. {panther-1.7.1 → panther-1.7.3}/panther.egg-info/top_level.txt +0 -0
  49. {panther-1.7.1 → panther-1.7.3}/pyproject.toml +0 -0
  50. {panther-1.7.1 → panther-1.7.3}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 1.7.1
3
+ Version: 1.7.3
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
@@ -14,23 +14,27 @@ Description-Content-Type: text/markdown
14
14
  Provides-Extra: full
15
15
  License-File: LICENSE
16
16
 
17
-
18
17
  ## Panther
19
18
  <b>Is A Fast & Friendly, Web Framework For Building Async APIs With Python 3.11+</b>
20
19
 
20
+ <p align="center">
21
+ <img src="https://github.com/AliRn76/panther/raw/master/docs/docs/images/logo.png" alt="logo" style="width: 200px">
22
+ </p>
23
+
21
24
  >_Full Documentation_ -> [https://pantherpy.github.io](https://pantherpy.github.io)
22
25
  >
23
26
  >_PyPI_ -> [https://pypi.org/project/panther/](https://pypi.org/project/panther/)
24
27
 
25
28
  ---
26
29
 
27
- ### Features
30
+ ### Why Use Panther ?
28
31
  - Document-oriented Databases ODM ([PantherDB](https://pypi.org/project/pantherdb/), MongoDB)
29
32
  - Visual API Monitoring (In Terminal)
30
- - Cache APIs (In Memory, In Redis)
33
+ - Caching for APIs (In Memory, In Redis)
31
34
  - Built-in Authentication Classes (Customizable)
32
35
  - Built-in Permission Classes (Customizable)
33
36
  - Handle Custom Middlewares
37
+ - Handle Custom Throttling
34
38
  ---
35
39
 
36
40
  ### Benchmark
@@ -150,13 +154,14 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
150
154
  **app/apis.py**:
151
155
 
152
156
  ```python
153
- from datetime import datetime
157
+ from datetime import datetime, timedelta
154
158
 
155
159
  from panther.app import API
156
160
  from panther.configs import config
157
161
  from panther import version, status
158
162
  from panther.request import Request
159
163
  from panther.response import Response
164
+ from panther.throttling import Throttling
160
165
 
161
166
 
162
167
  @API()
@@ -164,7 +169,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
164
169
  return {'detail': 'Hello World'}
165
170
 
166
171
 
167
- @API(cache=True)
172
+ @API(cache=True, throttling=Throttling(rate=5, duration=timedelta(minutes=1)))
168
173
  async def info(request: Request):
169
174
  data = {
170
175
  'version': version(),
@@ -1,20 +1,24 @@
1
-
2
1
  ## Panther
3
2
  <b>Is A Fast & Friendly, Web Framework For Building Async APIs With Python 3.11+</b>
4
3
 
4
+ <p align="center">
5
+ <img src="https://github.com/AliRn76/panther/raw/master/docs/docs/images/logo.png" alt="logo" style="width: 200px">
6
+ </p>
7
+
5
8
  >_Full Documentation_ -> [https://pantherpy.github.io](https://pantherpy.github.io)
6
9
  >
7
10
  >_PyPI_ -> [https://pypi.org/project/panther/](https://pypi.org/project/panther/)
8
11
 
9
12
  ---
10
13
 
11
- ### Features
14
+ ### Why Use Panther ?
12
15
  - Document-oriented Databases ODM ([PantherDB](https://pypi.org/project/pantherdb/), MongoDB)
13
16
  - Visual API Monitoring (In Terminal)
14
- - Cache APIs (In Memory, In Redis)
17
+ - Caching for APIs (In Memory, In Redis)
15
18
  - Built-in Authentication Classes (Customizable)
16
19
  - Built-in Permission Classes (Customizable)
17
20
  - Handle Custom Middlewares
21
+ - Handle Custom Throttling
18
22
  ---
19
23
 
20
24
  ### Benchmark
@@ -134,13 +138,14 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
134
138
  **app/apis.py**:
135
139
 
136
140
  ```python
137
- from datetime import datetime
141
+ from datetime import datetime, timedelta
138
142
 
139
143
  from panther.app import API
140
144
  from panther.configs import config
141
145
  from panther import version, status
142
146
  from panther.request import Request
143
147
  from panther.response import Response
148
+ from panther.throttling import Throttling
144
149
 
145
150
 
146
151
  @API()
@@ -148,7 +153,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
148
153
  return {'detail': 'Hello World'}
149
154
 
150
155
 
151
- @API(cache=True)
156
+ @API(cache=True, throttling=Throttling(rate=5, duration=timedelta(minutes=1)))
152
157
  async def info(request: Request):
153
158
  data = {
154
159
  'version': version(),
@@ -1,6 +1,6 @@
1
1
  from .main import Panther
2
2
 
3
- __version__ = '1.7.1'
3
+ __version__ = '1.7.3'
4
4
 
5
5
 
6
6
  def version():
@@ -41,44 +41,44 @@ class API:
41
41
  async def wrapper(*args, **kwargs):
42
42
  self.request: Request = kwargs.pop('request') # NOQA: Non-self attribute could not be type hinted
43
43
 
44
- # Handle Authentication
44
+ # 1. Authentication
45
45
  self.handle_authentications()
46
46
 
47
- # Handle Throttling
47
+ # 2. Throttling
48
48
  self.handle_throttling()
49
49
 
50
- # Handle Authentication
50
+ # 3. Permissions
51
51
  self.handle_permissions()
52
52
 
53
- # Validate Input
53
+ # 4. Validate Input
54
54
  self.validate_input()
55
55
 
56
- # Validate Path Variables
56
+ # 5. Validate Path Variables
57
57
  self.validate_path_variables(func, kwargs)
58
58
 
59
- # Get Cached Response
59
+ # 6. Get Cached Response
60
60
  if self.cache and self.request.method == 'GET':
61
61
  if cached := get_cached_response_data(request=self.request):
62
62
  return Response(data=cached.data, status_code=cached.status_code)
63
63
 
64
- # Put Request In kwargs
64
+ # 7. Put Request In kwargs
65
65
  if req_arg := [k for k, v in func.__annotations__.items() if v == Request]:
66
66
  kwargs[req_arg[0]] = self.request
67
67
 
68
- # Call Endpoint
68
+ # 8. Call Endpoint
69
69
  response = await func(**kwargs)
70
70
 
71
- # Clean Output
71
+ # 9. Clean Output
72
72
  if not isinstance(response, Response):
73
73
  response = Response(data=response)
74
74
  data = self.serialize_response_data(data=response._data) # NOQA: Access to a protected member
75
75
  response.set_data(data)
76
76
 
77
- # Set New Response To Cache
77
+ # 10. Set New Response To Cache
78
78
  if self.cache and self.request.method == 'GET':
79
79
  set_cache_response(request=self.request, response=response, cache_exp_time=self.cache_exp_time)
80
80
 
81
- # Warning CacheExpTime
81
+ # 11. Warning CacheExpTime
82
82
  if self.cache_exp_time and self.cache is False:
83
83
  logger.warning('"cache_exp_time" won\'t work while "cache" is False')
84
84
 
@@ -0,0 +1,58 @@
1
+ import os
2
+ from collections import deque
3
+
4
+ from rich.console import Group
5
+ from rich.layout import Layout
6
+ from rich.align import Align
7
+ from rich.panel import Panel
8
+ from rich.table import Table
9
+ from watchfiles import watch
10
+ from rich.live import Live
11
+ from rich import box
12
+
13
+
14
+ def monitor() -> None:
15
+ monitoring_log_file = 'logs/monitoring.log'
16
+
17
+ def _generate_table(rows: deque):
18
+ layout = Layout()
19
+
20
+ rows = list(rows)
21
+ _, lines = os.get_terminal_size()
22
+
23
+ table = Table(box=box.MINIMAL_DOUBLE_HEAD)
24
+ table.add_column('Datetime', justify='center', style='magenta', no_wrap=True)
25
+ table.add_column('Method', justify='center', style='cyan')
26
+ table.add_column('Path', justify='center', style='cyan')
27
+ table.add_column('Client', justify='center', style='cyan')
28
+ table.add_column('Response Time', justify='center', style='blue')
29
+ table.add_column('Status Code', justify='center', style='blue')
30
+
31
+ for row in rows[-lines:]: # It will give us "lines" last lines of "rows"
32
+ table.add_row(*row)
33
+ layout.update(table)
34
+
35
+ return Panel(
36
+ Align.center(Group(table)),
37
+ box=box.ROUNDED,
38
+ padding=(1, 2),
39
+ title='Monitoring',
40
+ border_style='bright_blue',
41
+ )
42
+
43
+ with open(monitoring_log_file) as f:
44
+ f.readlines() # Set cursor at the end of file
45
+
46
+ _, init_lines_count = os.get_terminal_size()
47
+ messages = deque(maxlen=init_lines_count - 10) # Save space for header and footer
48
+
49
+ with Live(_generate_table(messages), auto_refresh=False, vertical_overflow='visible', screen=True) as live:
50
+ try:
51
+ for _ in watch(monitoring_log_file):
52
+ data = f.readline().split('|')
53
+ # 2023-03-24 01:42:52 | GET | /user/317/ | 127.0.0.1:48856 | 0.0366 ms | 200
54
+ messages.append(data)
55
+ live.update(_generate_table(messages))
56
+ live.refresh()
57
+ except KeyboardInterrupt:
58
+ pass
@@ -10,15 +10,16 @@ from panther.configs import config
10
10
  from panther import version, status
11
11
  from panther.request import Request
12
12
  from panther.response import Response
13
+ from app.throttling import InfoThrottling
13
14
 
14
15
 
15
16
  @API()
16
- async def hello_world():
17
+ async def hello_world_api():
17
18
  return {'detail': 'Hello World'}
18
19
 
19
20
 
20
- @API(cache=True)
21
- async def info(request: Request):
21
+ @API(cache=True, throttling=InfoThrottling)
22
+ async def info_api(request: Request):
22
23
  data = {
23
24
  'version': version(),
24
25
  'datetime_now': datetime.now().isoformat(),
@@ -34,11 +35,18 @@ models_py = """from panther.db import Model
34
35
  serializers_py = """from pydantic import BaseModel as Serializer
35
36
  """
36
37
 
37
- app_urls_py = """from app.apis import hello_world, info
38
+ throttling_py = """from datetime import timedelta
39
+ from panther.throttling import Throttling
40
+
41
+
42
+ InfoThrottling = Throttling(rate=5, duration=timedelta(minutes=1))
43
+ """
44
+
45
+ app_urls_py = """from app.apis import hello_world_api, info_api
38
46
 
39
47
  urls = {
40
- '': hello_world,
41
- 'info/': info,
48
+ '': hello_world_api,
49
+ 'info/': info_api,
42
50
  }
43
51
  """
44
52
 
@@ -56,23 +64,29 @@ env = load_env(BASE_DIR / '.env')
56
64
  DB_NAME = env['DB_NAME']
57
65
  SECRET_KEY = env['SECRET_KEY']
58
66
 
59
-
67
+ # # # More Info: Https://PantherPy.GitHub.io/middlewares/
60
68
  MIDDLEWARES = [
61
69
  ('panther.middlewares.db.Middleware', {'url': f'pantherdb://{BASE_DIR}/{DB_NAME}.pantherdb'}),
62
70
  ]
63
71
 
64
72
  USER_MODEL = 'panther.db.models.User'
65
73
 
74
+ # # # More Info: Https://PantherPy.GitHub.io/authentications/
75
+ AUTHENTICATION = 'panther.authentications.JWTAuthentication'
76
+
77
+ # # # More Info: Https://PantherPy.GitHub.io/monitoring/
66
78
  MONITORING = True
67
79
 
80
+ # # # More Info: Https://PantherPy.GitHub.io/log_queries/
68
81
  LOG_QUERIES = True
69
82
 
83
+ # # # More Info: Https://PantherPy.GitHub.io/throttling/
84
+ THROTTLING = Throttling(rate=60, duration=timedelta(minutes=1))
85
+
86
+ # # # More Info: Https://PantherPy.GitHub.io/urls/
70
87
  URLs = 'core/urls.py'
71
88
  """ % datetime.now().date().isoformat()
72
89
 
73
- middlewares = """from panther.middlewares import BaseMiddleware
74
- """
75
-
76
90
  env = """
77
91
  SECRET_KEY = '%s'
78
92
 
@@ -94,8 +108,9 @@ urls = {
94
108
  git_ignore = """__pycache__/
95
109
  .venv/
96
110
  .idea/
97
- .env
98
111
  logs/
112
+
113
+ .env
99
114
  *.pantherdb
100
115
  """
101
116
 
@@ -107,11 +122,11 @@ Template = {
107
122
  'apis.py': apis_py,
108
123
  'models.py': models_py,
109
124
  'serializers.py': serializers_py,
125
+ 'throttling.py': throttling_py,
110
126
  'urls.py': app_urls_py,
111
127
  },
112
128
  'core': {
113
129
  'configs.py': configs_py,
114
- 'middlewares.py': middlewares,
115
130
  'urls.py': urls_py,
116
131
  },
117
132
  'main.py': main_py,
@@ -26,7 +26,7 @@ def clean_object_id(_id: bson.ObjectId | str) -> bson.ObjectId:
26
26
  try:
27
27
  return bson.ObjectId(_id)
28
28
  except Exception:
29
- raise bson.errors.InvalidId
29
+ raise bson.errors.InvalidId # NOQA: Py Unresolved References
30
30
 
31
31
 
32
32
  def clean_object_id_in_dicts(*args):
@@ -180,11 +180,10 @@ class Panther:
180
180
  if path.find('panther.middlewares.db.Middleware') != -1:
181
181
  config['db_engine'] = data['url'].split(':')[0]
182
182
 
183
- # noinspection PyPep8Naming
184
- Middleware = import_class(path)
183
+ Middleware = import_class(path) # NOQA: Py Pep8 Naming
185
184
  if not issubclass(Middleware, BaseMiddleware):
186
185
  logger.critical(f'{Middleware} is not a sub class of BaseMiddleware.')
187
186
  continue
188
- # noinspection PyArgumentList
189
- middlewares.append(Middleware(**data))
187
+
188
+ middlewares.append(Middleware(**data)) # NOQA: Py Argument List
190
189
  return middlewares
@@ -20,17 +20,9 @@ class Middleware(BaseMiddleware):
20
20
  self.kwargs['host'] = '127.0.0.1'
21
21
 
22
22
  def validate_port(self):
23
- if port := self.kwargs.get('port'):
24
- if isinstance(port, str):
25
- try:
26
- self.kwargs['port'] = int(port)
27
- except ValueError:
28
- logger.critical('Redis "port" should be number')
29
-
30
- elif not isinstance(port, int):
31
- logger.critical('Redis "port" is not valid.')
32
- else:
33
- self.kwargs['port'] = 6379
23
+ port = self.kwargs.setdefault('port', 6379)
24
+ if not isinstance(port, int):
25
+ logger.critical('Redis "port" is not valid.')
34
26
 
35
27
  async def before(self, request: Request) -> Request:
36
28
  self.redis = RedisConnection(**self.kwargs)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 1.7.1
3
+ Version: 1.7.3
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
@@ -14,23 +14,27 @@ Description-Content-Type: text/markdown
14
14
  Provides-Extra: full
15
15
  License-File: LICENSE
16
16
 
17
-
18
17
  ## Panther
19
18
  <b>Is A Fast & Friendly, Web Framework For Building Async APIs With Python 3.11+</b>
20
19
 
20
+ <p align="center">
21
+ <img src="https://github.com/AliRn76/panther/raw/master/docs/docs/images/logo.png" alt="logo" style="width: 200px">
22
+ </p>
23
+
21
24
  >_Full Documentation_ -> [https://pantherpy.github.io](https://pantherpy.github.io)
22
25
  >
23
26
  >_PyPI_ -> [https://pypi.org/project/panther/](https://pypi.org/project/panther/)
24
27
 
25
28
  ---
26
29
 
27
- ### Features
30
+ ### Why Use Panther ?
28
31
  - Document-oriented Databases ODM ([PantherDB](https://pypi.org/project/pantherdb/), MongoDB)
29
32
  - Visual API Monitoring (In Terminal)
30
- - Cache APIs (In Memory, In Redis)
33
+ - Caching for APIs (In Memory, In Redis)
31
34
  - Built-in Authentication Classes (Customizable)
32
35
  - Built-in Permission Classes (Customizable)
33
36
  - Handle Custom Middlewares
37
+ - Handle Custom Throttling
34
38
  ---
35
39
 
36
40
  ### Benchmark
@@ -150,13 +154,14 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
150
154
  **app/apis.py**:
151
155
 
152
156
  ```python
153
- from datetime import datetime
157
+ from datetime import datetime, timedelta
154
158
 
155
159
  from panther.app import API
156
160
  from panther.configs import config
157
161
  from panther import version, status
158
162
  from panther.request import Request
159
163
  from panther.response import Response
164
+ from panther.throttling import Throttling
160
165
 
161
166
 
162
167
  @API()
@@ -164,7 +169,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
164
169
  return {'detail': 'Hello World'}
165
170
 
166
171
 
167
- @API(cache=True)
172
+ @API(cache=True, throttling=Throttling(rate=5, duration=timedelta(minutes=1)))
168
173
  async def info(request: Request):
169
174
  data = {
170
175
  'version': version(),
@@ -0,0 +1,14 @@
1
+ bpython>=0.24
2
+ bson>=0.5.10
3
+ httptools>=0.5.0
4
+ pantherdb>=1.2.2
5
+ pydantic>=1.10.7
6
+ redis>=4.5.3
7
+ rich>=13.3.2
8
+ uvicorn>=0.21.1
9
+ uvloop>=0.17.0
10
+ watchfiles>=0.18.1
11
+
12
+ [full]
13
+ python-jose>=3.3.0
14
+ pymongo>=4.3.3
@@ -43,14 +43,16 @@ setup(
43
43
  'panther': ['cli/*'],
44
44
  },
45
45
  install_requires=[
46
- 'pantherdb>=1.2.0',
47
- 'pydantic>=1.10.5',
48
- 'watchfiles>=0.18.1',
49
- 'uvicorn[standard]',
50
46
  'bpython>=0.24',
51
47
  'bson>=0.5.10',
52
- 'redis>=4.0.2',
53
- 'rich>=13.3.1',
48
+ 'httptools>=0.5.0',
49
+ 'pantherdb>=1.2.2',
50
+ 'pydantic>=1.10.7',
51
+ 'redis>=4.5.3',
52
+ 'rich>=13.3.2',
53
+ 'uvicorn>=0.21.1',
54
+ 'uvloop>=0.17.0',
55
+ 'watchfiles>=0.18.1',
54
56
  ],
55
57
  extras_require=EXTRAS_REQUIRE,
56
58
  )
@@ -1,69 +0,0 @@
1
- import os
2
- from collections import deque
3
-
4
- from rich import box
5
- from rich.align import Align
6
- from rich.console import Console, Group
7
- from rich.layout import Layout
8
- from rich.live import Live
9
- from rich.panel import Panel
10
- from rich.table import Table
11
- from watchfiles import watch
12
-
13
- from panther.cli.utils import cli_error
14
-
15
-
16
- def monitor() -> None:
17
- def _generate_table(rows):
18
- layout = Layout()
19
- console = Console()
20
-
21
- rows = list(rows)
22
- n_rows = os.get_terminal_size()[1]
23
-
24
- while n_rows >= 0:
25
- table = Table(box=box.MINIMAL_DOUBLE_HEAD)
26
- table.add_column('Datetime', justify='center', style='magenta', no_wrap=True)
27
- table.add_column('Method', justify='center', style='cyan')
28
- table.add_column('Path', justify='center', style='cyan')
29
- table.add_column('Client', justify='center', style='cyan')
30
- table.add_column('Response Time', justify='center', style='blue')
31
- table.add_column('Status Code', justify='center', style='blue')
32
-
33
- for row in rows[-n_rows:]:
34
- table.add_row(*row)
35
- layout.update(table)
36
- render_map = layout.render(console, console.options)
37
-
38
- if len(render_map[layout].render[-1]) > 2:
39
- n_rows -= 1 # The table is overflowing
40
- else:
41
- break
42
-
43
- return Panel(
44
- Align.center(Group(table)),
45
- box=box.ROUNDED,
46
- padding=(1, 2),
47
- title='Monitoring',
48
- border_style='bright_blue',
49
- )
50
-
51
- try:
52
- with open('logs/monitoring.log') as f:
53
- f.readlines()
54
- width, height = os.get_terminal_size()
55
- messages = deque(maxlen=height - 8) # Save space for header and footer
56
-
57
- with Live(_generate_table(messages), auto_refresh=False, vertical_overflow='visible', screen=True) as live:
58
- # TODO: Is it only watch logs/monitoring.log or the whole directory ?
59
- for _ in watch('logs/monitoring.log'):
60
- data = f.readline().split('|')
61
- messages.append(data)
62
- live.update(_generate_table(messages))
63
- live.refresh()
64
-
65
- except FileNotFoundError:
66
- cli_error("Monitor Log File Does Not Exists.\n\nHint: Make sure 'Monitor' is True in 'core/configs' "
67
- "or you are in a correct directory.")
68
- except KeyboardInterrupt:
69
- pass
@@ -1,12 +0,0 @@
1
- pantherdb>=1.2.0
2
- pydantic>=1.10.5
3
- watchfiles>=0.18.1
4
- uvicorn[standard]
5
- bpython>=0.24
6
- bson>=0.5.10
7
- redis>=4.0.2
8
- rich>=13.3.1
9
-
10
- [full]
11
- python-jose>=3.3.0
12
- pymongo>=4.3.3
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -2,8 +2,8 @@ import os
2
2
  import logging
3
3
  from pydantic import BaseModel
4
4
 
5
- from logging.config import dictConfig
6
5
  from panther.configs import config
6
+ from logging.config import dictConfig
7
7
 
8
8
 
9
9
  LOGS_DIR = config['base_dir'] / 'logs'
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes