panther 3.1.3__tar.gz → 3.1.5__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 (70) hide show
  1. {panther-3.1.3 → panther-3.1.5}/PKG-INFO +2 -2
  2. {panther-3.1.3 → panther-3.1.5}/panther/__init__.py +1 -1
  3. {panther-3.1.3 → panther-3.1.5}/panther/_load_configs.py +68 -47
  4. {panther-3.1.3 → panther-3.1.5}/panther/_utils.py +5 -3
  5. {panther-3.1.3 → panther-3.1.5}/panther/app.py +4 -1
  6. {panther-3.1.3 → panther-3.1.5}/panther/authentications.py +4 -1
  7. {panther-3.1.3 → panther-3.1.5}/panther/background_tasks.py +4 -1
  8. {panther-3.1.3 → panther-3.1.5}/panther/base_request.py +2 -4
  9. {panther-3.1.3 → panther-3.1.5}/panther/base_websocket.py +4 -1
  10. {panther-3.1.3 → panther-3.1.5}/panther/caching.py +4 -1
  11. {panther-3.1.3 → panther-3.1.5}/panther/cli/monitor_command.py +8 -2
  12. {panther-3.1.3 → panther-3.1.5}/panther/cli/utils.py +13 -5
  13. {panther-3.1.3 → panther-3.1.5}/panther/configs.py +4 -0
  14. {panther-3.1.3 → panther-3.1.5}/panther/db/queries/queries.py +15 -3
  15. {panther-3.1.3 → panther-3.1.5}/panther/db/utils.py +19 -3
  16. panther-3.1.5/panther/logging.py +68 -0
  17. {panther-3.1.3 → panther-3.1.5}/panther/main.py +64 -48
  18. {panther-3.1.3 → panther-3.1.5}/panther/middlewares/redis.py +7 -2
  19. panther-3.1.5/panther/monitoring.py +41 -0
  20. {panther-3.1.3 → panther-3.1.5}/panther/panel/apis.py +5 -10
  21. {panther-3.1.3 → panther-3.1.5}/panther/request.py +4 -1
  22. {panther-3.1.3 → panther-3.1.5}/panther/routings.py +4 -2
  23. {panther-3.1.3 → panther-3.1.5}/panther/utils.py +7 -1
  24. {panther-3.1.3 → panther-3.1.5}/panther.egg-info/PKG-INFO +2 -2
  25. {panther-3.1.3 → panther-3.1.5}/panther.egg-info/SOURCES.txt +2 -2
  26. {panther-3.1.3 → panther-3.1.5}/panther.egg-info/requires.txt +1 -1
  27. {panther-3.1.3 → panther-3.1.5}/setup.py +1 -1
  28. {panther-3.1.3 → panther-3.1.5}/tests/test_run.py +1 -35
  29. {panther-3.1.3 → panther-3.1.5}/tests/test_utils.py +32 -55
  30. panther-3.1.3/panther/logger.py +0 -89
  31. panther-3.1.3/panther/middlewares/monitoring.py +0 -26
  32. {panther-3.1.3 → panther-3.1.5}/LICENSE +0 -0
  33. {panther-3.1.3 → panther-3.1.5}/README.md +0 -0
  34. {panther-3.1.3 → panther-3.1.5}/panther/cli/__init__.py +0 -0
  35. {panther-3.1.3 → panther-3.1.5}/panther/cli/create_command.py +0 -0
  36. {panther-3.1.3 → panther-3.1.5}/panther/cli/main.py +0 -0
  37. {panther-3.1.3 → panther-3.1.5}/panther/cli/run_command.py +0 -0
  38. {panther-3.1.3 → panther-3.1.5}/panther/cli/template.py +0 -0
  39. {panther-3.1.3 → panther-3.1.5}/panther/db/__init__.py +0 -0
  40. {panther-3.1.3 → panther-3.1.5}/panther/db/connection.py +0 -0
  41. {panther-3.1.3 → panther-3.1.5}/panther/db/models.py +0 -0
  42. {panther-3.1.3 → panther-3.1.5}/panther/db/queries/__init__.py +0 -0
  43. {panther-3.1.3 → panther-3.1.5}/panther/db/queries/mongodb_queries.py +0 -0
  44. {panther-3.1.3 → panther-3.1.5}/panther/db/queries/pantherdb_queries.py +0 -0
  45. {panther-3.1.3 → panther-3.1.5}/panther/exceptions.py +0 -0
  46. {panther-3.1.3 → panther-3.1.5}/panther/file_handler.py +0 -0
  47. {panther-3.1.3 → panther-3.1.5}/panther/middlewares/__init__.py +0 -0
  48. {panther-3.1.3 → panther-3.1.5}/panther/middlewares/base.py +0 -0
  49. {panther-3.1.3 → panther-3.1.5}/panther/middlewares/db.py +0 -0
  50. {panther-3.1.3 → panther-3.1.5}/panther/panel/__init__.py +0 -0
  51. {panther-3.1.3 → panther-3.1.5}/panther/panel/urls.py +0 -0
  52. {panther-3.1.3 → panther-3.1.5}/panther/panel/utils.py +0 -0
  53. {panther-3.1.3 → panther-3.1.5}/panther/permissions.py +0 -0
  54. {panther-3.1.3 → panther-3.1.5}/panther/response.py +0 -0
  55. {panther-3.1.3 → panther-3.1.5}/panther/status.py +0 -0
  56. {panther-3.1.3 → panther-3.1.5}/panther/test.py +0 -0
  57. {panther-3.1.3 → panther-3.1.5}/panther/throttling.py +0 -0
  58. {panther-3.1.3 → panther-3.1.5}/panther/websocket.py +0 -0
  59. {panther-3.1.3 → panther-3.1.5}/panther.egg-info/dependency_links.txt +0 -0
  60. {panther-3.1.3 → panther-3.1.5}/panther.egg-info/entry_points.txt +0 -0
  61. {panther-3.1.3 → panther-3.1.5}/panther.egg-info/top_level.txt +0 -0
  62. {panther-3.1.3 → panther-3.1.5}/pyproject.toml +0 -0
  63. {panther-3.1.3 → panther-3.1.5}/setup.cfg +0 -0
  64. {panther-3.1.3 → panther-3.1.5}/tests/test_background_tasks.py +0 -0
  65. {panther-3.1.3 → panther-3.1.5}/tests/test_mongodb.py +0 -0
  66. {panther-3.1.3 → panther-3.1.5}/tests/test_pantherdb.py +0 -0
  67. {panther-3.1.3 → panther-3.1.5}/tests/test_request_methods.py +0 -0
  68. {panther-3.1.3 → panther-3.1.5}/tests/test_routing.py +0 -0
  69. {panther-3.1.3 → panther-3.1.5}/tests/test_simple_requests.py +0 -0
  70. {panther-3.1.3 → panther-3.1.5}/tests/test_simple_responses.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 3.1.3
3
+ Version: 3.1.5
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
@@ -18,7 +18,7 @@ Requires-Dist: bson~=0.5
18
18
  Requires-Dist: httptools~=0.6
19
19
  Requires-Dist: pantherdb~=1.3
20
20
  Requires-Dist: pydantic~=2.1
21
- Requires-Dist: redis~=5.0
21
+ Requires-Dist: redis==5.0.1
22
22
  Requires-Dist: rich~=13.5
23
23
  Requires-Dist: uvicorn~=0.23
24
24
  Requires-Dist: watchfiles~=0.19
@@ -1,6 +1,6 @@
1
1
  from panther.main import Panther # noqa: F401
2
2
 
3
- __version__ = '3.1.3'
3
+ __version__ = '3.1.5'
4
4
 
5
5
 
6
6
  def version():
@@ -1,5 +1,4 @@
1
1
  import ast
2
- import os
3
2
  import platform
4
3
  import sys
5
4
  from datetime import timedelta
@@ -11,7 +10,6 @@ from pydantic._internal._model_construction import ModelMetaclass
11
10
  from panther._utils import import_class
12
11
  from panther.configs import JWTConfig, config
13
12
  from panther.exceptions import PantherException
14
- from panther.middlewares import BaseMiddleware
15
13
  from panther.routings import finalize_urls, flatten_urls
16
14
  from panther.throttling import Throttling
17
15
 
@@ -73,6 +71,8 @@ def load_default_cache_exp(configs: dict, /) -> timedelta | None:
73
71
 
74
72
  def load_middlewares(configs: dict, /) -> list:
75
73
  """Collect The Middlewares & Set db_engine If One Of Middlewares Was For DB"""
74
+ from panther.middlewares import BaseMiddleware
75
+
76
76
  middlewares = []
77
77
 
78
78
  for path, data in configs.get('MIDDLEWARES', []):
@@ -113,52 +113,73 @@ def collect_all_models() -> list[dict]:
113
113
  """Collecting all models for panel APIs"""
114
114
  from panther.db.models import Model
115
115
 
116
- collected_models = []
117
-
118
- for root, _, files in os.walk(config['base_dir']):
119
- # Traverse through each directory
120
- for f in files:
121
- # Traverse through each file of directory
122
- if f == 'models.py':
123
- slash = '\\' if platform.system() == 'Windows' else '/'
124
-
125
- # If the file was "models.py" read it
126
- file_path = f'{root}{slash}models.py'
127
- with Path(file_path).open() as file:
128
- # Parse the file with ast
129
- node = ast.parse(file.read())
130
- for n in node.body:
131
- # Find classes in each element of files' body
132
- if type(n) is ast.ClassDef and n.bases:
133
- class_path = (
134
- file_path.removesuffix(f'{slash}models.py')
135
- .removeprefix(f'{config["base_dir"]}{slash}')
136
- .replace(slash, '.')
137
- )
138
- # We don't need to import the package classes
139
- if class_path.find('site-packages') == -1:
140
- # Import the class to check his parents and siblings
141
- klass = import_class(f'{class_path}.models.{n.name}')
142
-
143
- collected_models.extend(
144
- [
145
- {
146
- 'name': n.name,
147
- 'path': file_path,
148
- 'class': klass,
149
- 'app': class_path.split('.'),
150
- }
151
- for parent in klass.__mro__
152
- if parent is Model
153
- ]
154
- )
155
- return collected_models
156
-
157
-
158
- def load_urls(configs: dict, /, urls: dict | None) -> dict:
116
+ # Just load all the python files from 'base_dir',
117
+ # so Model.__subclasses__ can find all the subclasses
118
+ slash = '\\' if platform.system() == 'Windows' else '/'
119
+ python_files = [
120
+ f for f in config['base_dir'].rglob('*.py')
121
+ if not f.name.startswith('_') and 'site-packages' not in f.parents._parts
122
+ ]
123
+ for file in python_files:
124
+ # Analyse the file
125
+ with Path(file).open() as f:
126
+ node = ast.parse(f.read())
127
+
128
+ model_imported = False
129
+ panther_imported = False
130
+ panther_called = False
131
+ for n in node.body:
132
+ match n:
133
+
134
+ # from panther.db import Model
135
+ case ast.ImportFrom(module='panther.db', names=[ast.alias(name='Model')]):
136
+ model_imported = True
137
+
138
+ # from panther.db.models import ..., Model, ...
139
+ case ast.ImportFrom(module='panther.db.models', names=[*names]):
140
+ try:
141
+ next(v for v in names if v.name == 'Model')
142
+ model_imported = True
143
+ except StopIteration:
144
+ pass
145
+
146
+ # from panther import Panther, ...
147
+ case ast.ImportFrom(module='panther', names=[ast.alias(name='Panther'), *_]):
148
+ panther_imported = True
149
+
150
+ # from panther import ..., Panther
151
+ case ast.ImportFrom(module='panther', names=[*_, ast.alias(name='Panther')]):
152
+ panther_imported = True
153
+
154
+ # ... = Panther(...)
155
+ case ast.Assign(value=ast.Call(func=ast.Name(id='Panther'))):
156
+ panther_called = True
157
+
158
+ # Panther() should not be called in the file and Model() should be imported,
159
+ # We check the import of the Panther to make sure he is calling the panther.Panther and not any Panther
160
+ if panther_imported and panther_called or not model_imported:
161
+ continue
162
+
163
+ # Load the module
164
+ dotted_f = str(file).removeprefix(f'{config["base_dir"]}{slash}').removesuffix('.py').replace(slash, '.')
165
+ import_module(dotted_f)
166
+
167
+ return [
168
+ {
169
+ 'name': m.__name__,
170
+ 'module': m.__module__,
171
+ 'class': m
172
+ } for m in Model.__subclasses__() if m.__module__ != 'panther.db.models'
173
+ ]
174
+
175
+
176
+ def load_urls(configs: dict, /, urls: dict | None) -> tuple[dict, dict]:
177
+ """
178
+ Return tuple of all urls (as a flat dict) and (as a nested dict)
179
+ """
159
180
  if isinstance(urls, dict):
160
181
  collected_urls = flatten_urls(urls)
161
- return finalize_urls(collected_urls)
182
+ return collected_urls, finalize_urls(collected_urls)
162
183
 
163
184
  if (url_routing := configs.get('URLs')) is None:
164
185
  raise _exception_handler(field='URLs', error='is required.')
@@ -182,7 +203,7 @@ def load_urls(configs: dict, /, urls: dict | None) -> dict:
182
203
  raise _exception_handler(field='URLs', error='should point to a dict.')
183
204
 
184
205
  collected_urls = flatten_urls(imported_urls)
185
- return finalize_urls(collected_urls)
206
+ return collected_urls, finalize_urls(collected_urls)
186
207
 
187
208
 
188
209
  def load_panel_urls() -> dict:
@@ -1,4 +1,5 @@
1
1
  import importlib
2
+ import logging
2
3
  import re
3
4
  from collections.abc import Callable
4
5
  from traceback import TracebackException
@@ -8,7 +9,9 @@ import orjson as json
8
9
 
9
10
  from panther import status
10
11
  from panther.file_handler import File
11
- from panther.logger import logger
12
+
13
+
14
+ logger = logging.getLogger('panther')
12
15
 
13
16
 
14
17
  async def _http_response_start(send: Callable, /, headers: dict, status_code: int) -> None:
@@ -42,8 +45,7 @@ async def http_response(
42
45
  elif status_code == status.HTTP_204_NO_CONTENT or body == b'null':
43
46
  body = None
44
47
 
45
- if monitoring is not None:
46
- await monitoring.after(status_code=status_code)
48
+ await monitoring.after(status_code)
47
49
 
48
50
  await _http_response_start(send, headers=headers, status_code=status_code)
49
51
  await _http_response_body(send, body=body)
@@ -1,6 +1,7 @@
1
1
  import functools
2
2
  from collections.abc import Callable
3
3
  from datetime import datetime, timedelta
4
+ import logging
4
5
  from typing import Literal
5
6
 
6
7
  from orjson import JSONDecodeError
@@ -18,7 +19,6 @@ from panther.exceptions import (
18
19
  MethodNotAllowed,
19
20
  ThrottlingException,
20
21
  )
21
- from panther.logger import logger
22
22
  from panther.request import Request
23
23
  from panther.response import Response
24
24
  from panther.throttling import Throttling, throttling_storage
@@ -27,6 +27,9 @@ from panther.utils import round_datetime
27
27
  __all__ = ('API', 'GenericAPI')
28
28
 
29
29
 
30
+ logger = logging.getLogger('panther')
31
+
32
+
30
33
  class API:
31
34
  def __init__(
32
35
  self,
@@ -1,5 +1,6 @@
1
1
  import time
2
2
  from abc import abstractmethod
3
+ import logging
3
4
  from typing import Literal
4
5
 
5
6
  from jose import JWTError, jwt
@@ -7,10 +8,12 @@ from jose import JWTError, jwt
7
8
  from panther.configs import config
8
9
  from panther.db.models import BaseUser, IDType, Model
9
10
  from panther.exceptions import AuthenticationException
10
- from panther.logger import logger
11
11
  from panther.request import Request
12
12
 
13
13
 
14
+ logger = logging.getLogger('panther')
15
+
16
+
14
17
  class BaseAuthentication:
15
18
  @classmethod
16
19
  @abstractmethod
@@ -1,12 +1,12 @@
1
1
  import asyncio
2
2
  import datetime
3
+ import logging
3
4
  import sys
4
5
  import time
5
6
  from threading import Thread
6
7
  from typing import Callable, Literal
7
8
 
8
9
  from panther._utils import is_function_async
9
- from panther.logger import logger
10
10
  from panther.utils import Singleton
11
11
 
12
12
 
@@ -16,6 +16,9 @@ __all__ = (
16
16
  )
17
17
 
18
18
 
19
+ logger = logging.getLogger('panther')
20
+
21
+
19
22
  if sys.version_info.minor >= 11:
20
23
  from typing import Self
21
24
  else:
@@ -2,8 +2,6 @@ from collections import namedtuple
2
2
  from collections.abc import Callable
3
3
  from dataclasses import dataclass
4
4
 
5
- from panther.db import Model
6
-
7
5
 
8
6
  @dataclass(frozen=True)
9
7
  class Headers:
@@ -86,8 +84,8 @@ class BaseRequest:
86
84
  return self.scope['scheme']
87
85
 
88
86
  @property
89
- def user(self) -> Model:
87
+ def user(self):
90
88
  return self._user
91
89
 
92
- def set_user(self, user: Model) -> None:
90
+ def set_user(self, user) -> None:
93
91
  self._user = user
@@ -2,6 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import asyncio
4
4
  import contextlib
5
+ import logging
5
6
  from typing import TYPE_CHECKING
6
7
 
7
8
  import orjson as json
@@ -10,13 +11,15 @@ from panther import status
10
11
  from panther._utils import generate_ws_connection_id
11
12
  from panther.base_request import BaseRequest
12
13
  from panther.configs import config
13
- from panther.logger import logger
14
14
  from panther.utils import Singleton
15
15
 
16
16
  if TYPE_CHECKING:
17
17
  from redis import Redis
18
18
 
19
19
 
20
+ logger = logging.getLogger('panther')
21
+
22
+
20
23
  class WebsocketConnections(Singleton):
21
24
  def __init__(self):
22
25
  self.connections = {}
@@ -1,16 +1,19 @@
1
1
  from collections import namedtuple
2
2
  from datetime import timedelta
3
+ import logging
3
4
  from types import NoneType
4
5
 
5
6
  import orjson as json
6
7
 
7
8
  from panther.configs import config
8
9
  from panther.db.connection import redis
9
- from panther.logger import logger
10
10
  from panther.request import Request
11
11
  from panther.response import Response, ResponseDataTypes
12
12
  from panther.utils import generate_hash_value_from_string
13
13
 
14
+
15
+ logger = logging.getLogger('panther')
16
+
14
17
  caches = {}
15
18
  CachedResponse = namedtuple('Cached', ['data', 'status_code'])
16
19
 
@@ -12,9 +12,12 @@ from rich.panel import Panel
12
12
  from rich.table import Table
13
13
  from watchfiles import watch
14
14
 
15
+ from panther.cli.utils import cli_error
16
+ from panther.configs import config
17
+
15
18
 
16
19
  def monitor() -> None:
17
- monitoring_log_file = 'logs/monitoring.log'
20
+ monitoring_log_file = Path(config['base_dir'] / 'logs' / 'monitoring.log')
18
21
 
19
22
  def _generate_table(rows: deque) -> Panel:
20
23
  layout = Layout()
@@ -42,7 +45,10 @@ def monitor() -> None:
42
45
  border_style='bright_blue',
43
46
  )
44
47
 
45
- with Path(monitoring_log_file).open() as f:
48
+ if not monitoring_log_file.exists():
49
+ return cli_error('Monitoring file not found. (You need at least one monitoring record for this action)')
50
+
51
+ with monitoring_log_file.open() as f:
46
52
  f.readlines() # Set cursor at the end of file
47
53
 
48
54
  _, init_lines_count = os.get_terminal_size()
@@ -1,7 +1,10 @@
1
- from panther.db.connection import redis
2
- from panther.logger import logger
1
+ import logging
3
2
  from rich import print as rprint
4
3
 
4
+
5
+ logger = logging.getLogger('panther')
6
+
7
+
5
8
  logo = r"""│ ____ __ __ │
6
9
  │ /\ _`\ /\ \__/\ \ │
7
10
  │ \ \ \L\ \ __ ___\ \ ,_\ \ \___ __ _ __ │
@@ -78,21 +81,26 @@ def print_uvicorn_help_message():
78
81
  def print_info(config: dict):
79
82
  mo = config['monitoring']
80
83
  lq = config['log_queries']
81
- rc = redis.is_connected
82
84
  bt = config['background_tasks']
85
+ ws = config['has_ws']
83
86
  bd = '{0:<39}'.format(str(config['base_dir']))
84
87
  if len(bd) > 39:
85
88
  bd = f'{bd[:36]}...'
86
89
 
90
+ if config['monitoring']:
91
+ monitor = '│ * Run "panther monitor" in another session for Monitoring│'
92
+ else:
93
+ monitor = f'│{58 * " "}│'
94
+
87
95
  info_message = f"""
88
96
  ╭{58 * '─'}╮
89
97
  {logo}│{58 * ' '}│
90
- │ │
91
98
  │ Monitoring: {mo} \t │
92
99
  │ Log Queries: {lq} \t │
93
- │ Redis Is Connected: {rc} \t │
94
100
  │ Background Tasks: {bt} \t │
101
+ │ Websocket: {ws} \t │
95
102
  │ Base directory: {bd}│
103
+ {monitor}
96
104
  ╰{58 * '─'}╯
97
105
  """
98
106
  rprint(info_message)
@@ -41,10 +41,12 @@ class Config(TypedDict):
41
41
  authentication: ModelMetaclass | None
42
42
  jwt_config: JWTConfig | None
43
43
  models: list[dict]
44
+ flat_urls: dict
44
45
  urls: dict
45
46
  db_engine: str
46
47
  websocket_connections: any # type: WebsocketConnections
47
48
  background_tasks: bool
49
+ has_ws: bool
48
50
 
49
51
 
50
52
  config: Config = {
@@ -60,8 +62,10 @@ config: Config = {
60
62
  'authentication': None,
61
63
  'jwt_config': None,
62
64
  'models': [],
65
+ 'flat_urls': {},
63
66
  'urls': {},
64
67
  'db_engine': '',
65
68
  'websocket_connections': None,
66
69
  'background_tasks': False,
70
+ 'has_ws': False,
67
71
  }
@@ -6,7 +6,7 @@ from panther import status
6
6
  from panther.configs import config
7
7
  from panther.db.queries.mongodb_queries import BaseMongoDBQuery
8
8
  from panther.db.queries.pantherdb_queries import BasePantherDBQuery
9
- from panther.db.utils import log_query
9
+ from panther.db.utils import log_query, check_connection
10
10
  from panther.exceptions import APIException, DBException
11
11
 
12
12
  BaseQuery = BasePantherDBQuery if config['db_engine'] == 'pantherdb' else BaseMongoDBQuery
@@ -55,6 +55,7 @@ class Query(BaseQuery):
55
55
 
56
56
  # # # # # Find # # # # #
57
57
  @classmethod
58
+ @check_connection
58
59
  @log_query
59
60
  def find_one(cls, _data: dict | None = None, /, **kwargs) -> Self | None:
60
61
  """
@@ -66,6 +67,7 @@ class Query(BaseQuery):
66
67
  return super().find_one(_data, **kwargs)
67
68
 
68
69
  @classmethod
70
+ @check_connection
69
71
  @log_query
70
72
  def find(cls, _data: dict | None = None, /, **kwargs) -> list[Self]:
71
73
  """
@@ -77,6 +79,7 @@ class Query(BaseQuery):
77
79
  return super().find(_data, **kwargs)
78
80
 
79
81
  @classmethod
82
+ @check_connection
80
83
  @log_query
81
84
  def first(cls, _data: dict | None = None, /, **kwargs) -> Self | None:
82
85
  """
@@ -89,6 +92,7 @@ class Query(BaseQuery):
89
92
  return super().first(_data, **kwargs)
90
93
 
91
94
  @classmethod
95
+ @check_connection
92
96
  @log_query
93
97
  def last(cls, _data: dict | None = None, /, **kwargs) -> Self | None:
94
98
  """
@@ -101,6 +105,7 @@ class Query(BaseQuery):
101
105
 
102
106
  # # # # # Count # # # # #
103
107
  @classmethod
108
+ @check_connection
104
109
  @log_query
105
110
  def count(cls, _data: dict | None = None, /, **kwargs) -> int:
106
111
  """
@@ -113,6 +118,7 @@ class Query(BaseQuery):
113
118
 
114
119
  # # # # # Insert # # # # #
115
120
  @classmethod
121
+ @check_connection
116
122
  @log_query
117
123
  def insert_one(cls, _data: dict | None = None, /, **kwargs) -> Self:
118
124
  """
@@ -125,12 +131,14 @@ class Query(BaseQuery):
125
131
  return super().insert_one(_data, **kwargs)
126
132
 
127
133
  @classmethod
134
+ @check_connection
128
135
  @log_query
129
136
  def insert_many(cls, _data: dict | None = None, /, **kwargs):
130
137
  msg = 'insert_many() is not supported yet.'
131
138
  raise DBException(msg)
132
139
 
133
140
  # # # # # Delete # # # # #
141
+ @check_connection
134
142
  @log_query
135
143
  def delete(self) -> None:
136
144
  """
@@ -143,6 +151,7 @@ class Query(BaseQuery):
143
151
  return super().delete()
144
152
 
145
153
  @classmethod
154
+ @check_connection
146
155
  @log_query
147
156
  def delete_one(cls, _data: dict | None = None, /, **kwargs) -> bool:
148
157
  """
@@ -154,6 +163,7 @@ class Query(BaseQuery):
154
163
  return super().delete_one(_data, **kwargs)
155
164
 
156
165
  @classmethod
166
+ @check_connection
157
167
  @log_query
158
168
  def delete_many(cls, _data: dict | None = None, /, **kwargs) -> int:
159
169
  """
@@ -165,6 +175,7 @@ class Query(BaseQuery):
165
175
  return super().delete_many(_data, **kwargs)
166
176
 
167
177
  # # # # # Update # # # # #
178
+ @check_connection
168
179
  @log_query
169
180
  def update(self, **kwargs) -> None:
170
181
  """
@@ -178,6 +189,7 @@ class Query(BaseQuery):
178
189
  return super().update(**kwargs)
179
190
 
180
191
  @classmethod
192
+ @check_connection
181
193
  @log_query
182
194
  def update_one(cls, _filter: dict, _data: dict | None = None, /, **kwargs) -> bool:
183
195
  """
@@ -190,6 +202,7 @@ class Query(BaseQuery):
190
202
  return super().update_one(_filter, _data, **kwargs)
191
203
 
192
204
  @classmethod
205
+ @check_connection
193
206
  @log_query
194
207
  def update_many(cls, _filter: dict, _data: dict | None = None, /, **kwargs) -> int:
195
208
  """
@@ -203,7 +216,6 @@ class Query(BaseQuery):
203
216
 
204
217
  # # # # # Other # # # # #
205
218
  @classmethod
206
- @log_query
207
219
  def find_or_insert(cls, **kwargs) -> tuple[bool, any]:
208
220
  """
209
221
  Example:
@@ -216,7 +228,6 @@ class Query(BaseQuery):
216
228
  return True, cls.insert_one(**kwargs)
217
229
 
218
230
  @classmethod
219
- @log_query
220
231
  def find_one_or_raise(cls, **kwargs) -> Self:
221
232
  """
222
233
  Example:
@@ -232,6 +243,7 @@ class Query(BaseQuery):
232
243
  status_code=status.HTTP_404_NOT_FOUND,
233
244
  )
234
245
 
246
+ @check_connection
235
247
  @log_query
236
248
  def save(self, **kwargs) -> None:
237
249
  """
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import operator
2
3
  from functools import reduce
3
4
  from time import perf_counter
@@ -5,23 +6,38 @@ from time import perf_counter
5
6
  import bson
6
7
 
7
8
  from panther.configs import config
8
- from panther.logger import query_logger
9
+
10
+
11
+ logger = logging.getLogger('query')
9
12
 
10
13
 
11
14
  def log_query(func):
12
15
  def log(*args, **kwargs):
16
+ # Check Database Connection
17
+ if config['db_engine'] == '':
18
+ msg = "You don't have active database connection, Check your middlewares"
19
+ raise NotImplementedError(msg)
20
+
13
21
  if config['log_queries'] is False:
14
22
  return func(*args, **kwargs)
15
23
  start = perf_counter()
16
24
  response = func(*args, **kwargs)
17
25
  end = perf_counter()
18
26
  class_name = args[0].__name__ if hasattr(args[0], '__name__') else args[0].__class__.__name__
19
- query_logger.info(f'\033[1mQuery -->\033[0m {class_name}.{func.__name__}() --> {(end - start) * 1_000:.2} ms')
27
+ logger.info(f'\033[1mQuery -->\033[0m {class_name}.{func.__name__}() --> {(end - start) * 1_000:.2} ms')
20
28
  return response
21
-
22
29
  return log
23
30
 
24
31
 
32
+ def check_connection(func):
33
+ def wrapper(*args, **kwargs):
34
+ if config['db_engine'] == '':
35
+ msg = "You don't have active database connection, Check your middlewares"
36
+ raise NotImplementedError(msg)
37
+ return func(*args, **kwargs)
38
+ return wrapper
39
+
40
+
25
41
  def prepare_id_for_query(*args, is_mongo: bool = False):
26
42
  for d in args:
27
43
  if d is None: