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.
- {panther-3.1.3 → panther-3.1.5}/PKG-INFO +2 -2
- {panther-3.1.3 → panther-3.1.5}/panther/__init__.py +1 -1
- {panther-3.1.3 → panther-3.1.5}/panther/_load_configs.py +68 -47
- {panther-3.1.3 → panther-3.1.5}/panther/_utils.py +5 -3
- {panther-3.1.3 → panther-3.1.5}/panther/app.py +4 -1
- {panther-3.1.3 → panther-3.1.5}/panther/authentications.py +4 -1
- {panther-3.1.3 → panther-3.1.5}/panther/background_tasks.py +4 -1
- {panther-3.1.3 → panther-3.1.5}/panther/base_request.py +2 -4
- {panther-3.1.3 → panther-3.1.5}/panther/base_websocket.py +4 -1
- {panther-3.1.3 → panther-3.1.5}/panther/caching.py +4 -1
- {panther-3.1.3 → panther-3.1.5}/panther/cli/monitor_command.py +8 -2
- {panther-3.1.3 → panther-3.1.5}/panther/cli/utils.py +13 -5
- {panther-3.1.3 → panther-3.1.5}/panther/configs.py +4 -0
- {panther-3.1.3 → panther-3.1.5}/panther/db/queries/queries.py +15 -3
- {panther-3.1.3 → panther-3.1.5}/panther/db/utils.py +19 -3
- panther-3.1.5/panther/logging.py +68 -0
- {panther-3.1.3 → panther-3.1.5}/panther/main.py +64 -48
- {panther-3.1.3 → panther-3.1.5}/panther/middlewares/redis.py +7 -2
- panther-3.1.5/panther/monitoring.py +41 -0
- {panther-3.1.3 → panther-3.1.5}/panther/panel/apis.py +5 -10
- {panther-3.1.3 → panther-3.1.5}/panther/request.py +4 -1
- {panther-3.1.3 → panther-3.1.5}/panther/routings.py +4 -2
- {panther-3.1.3 → panther-3.1.5}/panther/utils.py +7 -1
- {panther-3.1.3 → panther-3.1.5}/panther.egg-info/PKG-INFO +2 -2
- {panther-3.1.3 → panther-3.1.5}/panther.egg-info/SOURCES.txt +2 -2
- {panther-3.1.3 → panther-3.1.5}/panther.egg-info/requires.txt +1 -1
- {panther-3.1.3 → panther-3.1.5}/setup.py +1 -1
- {panther-3.1.3 → panther-3.1.5}/tests/test_run.py +1 -35
- {panther-3.1.3 → panther-3.1.5}/tests/test_utils.py +32 -55
- panther-3.1.3/panther/logger.py +0 -89
- panther-3.1.3/panther/middlewares/monitoring.py +0 -26
- {panther-3.1.3 → panther-3.1.5}/LICENSE +0 -0
- {panther-3.1.3 → panther-3.1.5}/README.md +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/cli/__init__.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/cli/create_command.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/cli/main.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/cli/run_command.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/cli/template.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/db/__init__.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/db/connection.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/db/models.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/db/queries/__init__.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/db/queries/mongodb_queries.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/db/queries/pantherdb_queries.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/exceptions.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/file_handler.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/middlewares/__init__.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/middlewares/base.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/middlewares/db.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/panel/__init__.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/panel/urls.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/panel/utils.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/permissions.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/response.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/status.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/test.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/throttling.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther/websocket.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther.egg-info/dependency_links.txt +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther.egg-info/entry_points.txt +0 -0
- {panther-3.1.3 → panther-3.1.5}/panther.egg-info/top_level.txt +0 -0
- {panther-3.1.3 → panther-3.1.5}/pyproject.toml +0 -0
- {panther-3.1.3 → panther-3.1.5}/setup.cfg +0 -0
- {panther-3.1.3 → panther-3.1.5}/tests/test_background_tasks.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/tests/test_mongodb.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/tests/test_pantherdb.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/tests/test_request_methods.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/tests/test_routing.py +0 -0
- {panther-3.1.3 → panther-3.1.5}/tests/test_simple_requests.py +0 -0
- {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
|
+
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
|
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,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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
for f in
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
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
|
-
|
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)
|
87
|
+
def user(self):
|
90
88
|
return self._user
|
91
89
|
|
92
|
-
def set_user(self, user
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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:
|