panther 1.3.1__tar.gz → 1.4.0__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-1.3.1 → panther-1.4.0}/PKG-INFO +8 -14
- {panther-1.3.1 → panther-1.4.0}/README.md +7 -13
- {panther-1.3.1 → panther-1.4.0}/panther/__init__.py +1 -1
- {panther-1.3.1 → panther-1.4.0}/panther/authentications.py +5 -4
- {panther-1.3.1 → panther-1.4.0}/panther/cli/template.py +2 -0
- {panther-1.3.1 → panther-1.4.0}/panther/configs.py +3 -1
- {panther-1.3.1 → panther-1.4.0}/panther/db/queries/mongodb_queries.py +1 -13
- {panther-1.3.1 → panther-1.4.0}/panther/db/queries/pantherdb_queries.py +2 -3
- {panther-1.3.1 → panther-1.4.0}/panther/db/queries/queries.py +15 -1
- {panther-1.3.1 → panther-1.4.0}/panther/db/utils.py +4 -4
- {panther-1.3.1 → panther-1.4.0}/panther/logger.py +17 -17
- {panther-1.3.1 → panther-1.4.0}/panther/main.py +1 -0
- {panther-1.3.1 → panther-1.4.0}/panther/middlewares/monitoring.py +3 -3
- {panther-1.3.1 → panther-1.4.0}/panther.egg-info/PKG-INFO +8 -14
- {panther-1.3.1 → panther-1.4.0}/panther.egg-info/requires.txt +1 -1
- {panther-1.3.1 → panther-1.4.0}/setup.py +1 -1
- {panther-1.3.1 → panther-1.4.0}/LICENSE +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/_utils.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/app.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/caching.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/cli/__init__.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/cli/create_command.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/cli/main.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/cli/monitor_command.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/cli/run_command.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/cli/utils.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/db/__init__.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/db/connection.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/db/models.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/db/queries/__init__.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/exceptions.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/middlewares/__init__.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/middlewares/base.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/middlewares/db.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/middlewares/redis.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/request.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/response.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/routings.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/status.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther/utils.py +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther.egg-info/SOURCES.txt +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther.egg-info/dependency_links.txt +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther.egg-info/entry_points.txt +0 -0
- {panther-1.3.1 → panther-1.4.0}/panther.egg-info/top_level.txt +0 -0
- {panther-1.3.1 → panther-1.4.0}/pyproject.toml +0 -0
- {panther-1.3.1 → panther-1.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: panther
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.4.0
|
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
|
@@ -93,14 +93,10 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
93
93
|
```
|
94
94
|
|
95
95
|
- #### Run Project
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
Then
|
101
|
-
```console
|
102
|
-
$ panther run
|
103
|
-
```
|
96
|
+
Panther Uses [Uvicorn](https://github.com/encode/uvicorn) as ASGI (Asynchronous Server Gateway Interface)
|
97
|
+
```console
|
98
|
+
$ panther run
|
99
|
+
```
|
104
100
|
|
105
101
|
- #### Monitoring Requests
|
106
102
|
|
@@ -109,7 +105,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
109
105
|
```
|
110
106
|
|
111
107
|
- #### Python Shell
|
112
|
-
|
108
|
+
Panther Uses [bpython](https://bpython-interpreter.org) for shell
|
113
109
|
```console
|
114
110
|
$ panther shell
|
115
111
|
```
|
@@ -154,7 +150,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
154
150
|
**app/apis.py**:
|
155
151
|
|
156
152
|
```python
|
157
|
-
from datetime import
|
153
|
+
from datetime import datetime
|
158
154
|
|
159
155
|
from panther.app import API
|
160
156
|
from panther.configs import config
|
@@ -168,15 +164,13 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
168
164
|
return {'detail': 'Hello World'}
|
169
165
|
|
170
166
|
|
171
|
-
@API(cache=True
|
167
|
+
@API(cache=True)
|
172
168
|
async def info(request: Request):
|
173
169
|
data = {
|
174
170
|
'version': version(),
|
175
171
|
'datetime_now': datetime.now().isoformat(),
|
176
172
|
'user_agent': request.headers.user_agent,
|
177
|
-
'middlewares': config['middlewares'],
|
178
173
|
'db_engine': config['db_engine'],
|
179
|
-
'urls': config['urls'],
|
180
174
|
}
|
181
175
|
return Response(data=data, status_code=status.HTTP_202_ACCEPTED)
|
182
176
|
```
|
@@ -77,14 +77,10 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
77
77
|
```
|
78
78
|
|
79
79
|
- #### Run Project
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
Then
|
85
|
-
```console
|
86
|
-
$ panther run
|
87
|
-
```
|
80
|
+
Panther Uses [Uvicorn](https://github.com/encode/uvicorn) as ASGI (Asynchronous Server Gateway Interface)
|
81
|
+
```console
|
82
|
+
$ panther run
|
83
|
+
```
|
88
84
|
|
89
85
|
- #### Monitoring Requests
|
90
86
|
|
@@ -93,7 +89,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
93
89
|
```
|
94
90
|
|
95
91
|
- #### Python Shell
|
96
|
-
|
92
|
+
Panther Uses [bpython](https://bpython-interpreter.org) for shell
|
97
93
|
```console
|
98
94
|
$ panther shell
|
99
95
|
```
|
@@ -138,7 +134,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
138
134
|
**app/apis.py**:
|
139
135
|
|
140
136
|
```python
|
141
|
-
from datetime import
|
137
|
+
from datetime import datetime
|
142
138
|
|
143
139
|
from panther.app import API
|
144
140
|
from panther.configs import config
|
@@ -152,15 +148,13 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
152
148
|
return {'detail': 'Hello World'}
|
153
149
|
|
154
150
|
|
155
|
-
@API(cache=True
|
151
|
+
@API(cache=True)
|
156
152
|
async def info(request: Request):
|
157
153
|
data = {
|
158
154
|
'version': version(),
|
159
155
|
'datetime_now': datetime.now().isoformat(),
|
160
156
|
'user_agent': request.headers.user_agent,
|
161
|
-
'middlewares': config['middlewares'],
|
162
157
|
'db_engine': config['db_engine'],
|
163
|
-
'urls': config['urls'],
|
164
158
|
}
|
165
159
|
return Response(data=data, status_code=status.HTTP_202_ACCEPTED)
|
166
160
|
```
|
@@ -49,10 +49,11 @@ class JWTAuthentication:
|
|
49
49
|
if user_id := payload.get('user_id') is None:
|
50
50
|
raise AuthenticationException
|
51
51
|
user_model = config['user_model'] or cls.model
|
52
|
-
|
53
|
-
if user
|
54
|
-
|
55
|
-
|
52
|
+
|
53
|
+
if user := user_model.get_one(id=user_id):
|
54
|
+
return user
|
55
|
+
|
56
|
+
raise AuthenticationException
|
56
57
|
|
57
58
|
@staticmethod
|
58
59
|
def encode_jwt(user_id: int) -> str:
|
@@ -15,6 +15,7 @@ class JWTConfig:
|
|
15
15
|
class Config(TypedDict):
|
16
16
|
base_dir: Path
|
17
17
|
monitoring: bool
|
18
|
+
log_queries: bool
|
18
19
|
urls: dict
|
19
20
|
middlewares: list
|
20
21
|
reversed_middlewares: list
|
@@ -28,7 +29,8 @@ class Config(TypedDict):
|
|
28
29
|
|
29
30
|
config: Config = {
|
30
31
|
'base_dir': Path(),
|
31
|
-
'monitoring':
|
32
|
+
'monitoring': False,
|
33
|
+
'log_queries': False,
|
32
34
|
'secret_key': None,
|
33
35
|
'urls': {},
|
34
36
|
'middlewares': [],
|
@@ -3,7 +3,7 @@ from typing import TypeVar
|
|
3
3
|
import bson
|
4
4
|
|
5
5
|
from panther.db.connection import db # NOQA: F401
|
6
|
-
from panther.db.utils import clean_object_id_in_dicts, merge_dicts,
|
6
|
+
from panther.db.utils import clean_object_id_in_dicts, merge_dicts, log_query
|
7
7
|
|
8
8
|
# TODO: Not sure about this bounding
|
9
9
|
T = TypeVar('T', bound='BaseMongoDBQuery')
|
@@ -12,7 +12,6 @@ T = TypeVar('T', bound='BaseMongoDBQuery')
|
|
12
12
|
class BaseMongoDBQuery:
|
13
13
|
|
14
14
|
@classmethod
|
15
|
-
@query_logger
|
16
15
|
def get_one(cls: type[T], _data: dict = None, /, **kwargs) -> T:
|
17
16
|
clean_object_id_in_dicts(_data, kwargs)
|
18
17
|
_query = merge_dicts(_data, kwargs)
|
@@ -20,14 +19,12 @@ class BaseMongoDBQuery:
|
|
20
19
|
return cls(**obj) if obj else None
|
21
20
|
|
22
21
|
@classmethod
|
23
|
-
@query_logger
|
24
22
|
def count(cls, _data: dict = None, /, **kwargs) -> int:
|
25
23
|
clean_object_id_in_dicts(_data, kwargs)
|
26
24
|
_query = merge_dicts(_data, kwargs)
|
27
25
|
return eval(f'db.session.{cls.__name__}.count_documents(_query)')
|
28
26
|
|
29
27
|
@classmethod
|
30
|
-
@query_logger
|
31
28
|
def list(cls, _data: dict = None, /, **kwargs):
|
32
29
|
clean_object_id_in_dicts(_data, kwargs)
|
33
30
|
_query = merge_dicts(_data, kwargs)
|
@@ -35,32 +32,27 @@ class BaseMongoDBQuery:
|
|
35
32
|
return [cls(**obj) for obj in result]
|
36
33
|
|
37
34
|
@classmethod
|
38
|
-
@query_logger
|
39
35
|
def create(cls, _data: dict = None, **kwargs) -> bson.objectid.ObjectId:
|
40
36
|
_query = merge_dicts(_data, kwargs)
|
41
37
|
return eval(f'db.session.{cls.__name__}.insert_one(_query)').inserted_id
|
42
38
|
|
43
|
-
@query_logger
|
44
39
|
def delete(self) -> bool:
|
45
40
|
_filter = {'_id': self._id}
|
46
41
|
result = eval(f'db.session.{self.__class__.__name__}.delete_one(_filter)')
|
47
42
|
return bool(result.deleted_count)
|
48
43
|
|
49
44
|
@classmethod
|
50
|
-
@query_logger
|
51
45
|
def delete_one(cls, **kwargs) -> bool:
|
52
46
|
clean_object_id_in_dicts(kwargs)
|
53
47
|
result = eval(f'db.session.{cls.__name__}.delete_one(kwargs)')
|
54
48
|
return bool(result.deleted_count)
|
55
49
|
|
56
50
|
@classmethod
|
57
|
-
@query_logger
|
58
51
|
def delete_many(cls, **kwargs) -> int:
|
59
52
|
clean_object_id_in_dicts(kwargs)
|
60
53
|
result = eval(f'db.session.{cls.__name__}.delete_many(kwargs)')
|
61
54
|
return result.deleted_count
|
62
55
|
|
63
|
-
@query_logger
|
64
56
|
def update(self, _data: dict = None, **kwargs) -> dict:
|
65
57
|
for field, value in (_data or kwargs).items():
|
66
58
|
if hasattr(self, field):
|
@@ -70,7 +62,6 @@ class BaseMongoDBQuery:
|
|
70
62
|
return eval(f'db.session.{self.__class__.__name__}.update_one(_filter, _update)')
|
71
63
|
|
72
64
|
@classmethod
|
73
|
-
@query_logger
|
74
65
|
def update_one(cls, _filter, _data: dict = None, /, **kwargs) -> dict:
|
75
66
|
clean_object_id_in_dicts(_filter)
|
76
67
|
|
@@ -81,19 +72,16 @@ class BaseMongoDBQuery:
|
|
81
72
|
return eval(f'db.session.{cls.__name__}.update_one(_filter, _data | _update)')
|
82
73
|
|
83
74
|
@classmethod
|
84
|
-
@query_logger
|
85
75
|
def update_many(cls, _filter, **kwargs) -> dict:
|
86
76
|
_update = {'$set': kwargs}
|
87
77
|
return eval(f'db.session.{cls.__name__}.update_many(_filter, _update)')
|
88
78
|
|
89
79
|
@classmethod
|
90
|
-
@query_logger
|
91
80
|
def increment(cls, _filter, **kwargs):
|
92
81
|
_update = {'$inc': kwargs}
|
93
82
|
return eval(f'db.session.{cls.__name__}.update_many({_filter}, {_update})')
|
94
83
|
|
95
84
|
@classmethod
|
96
|
-
@query_logger
|
97
85
|
def get_or_create(cls, **kwargs) -> tuple[bool, any]:
|
98
86
|
obj = cls.get_one(**kwargs)
|
99
87
|
if obj:
|
@@ -1,9 +1,8 @@
|
|
1
|
-
import operator
|
2
|
-
from functools import reduce
|
3
1
|
from typing import Self
|
4
2
|
from pydantic import ValidationError
|
5
3
|
|
6
4
|
from panther.db.connection import db
|
5
|
+
from panther.db.utils import merge_dicts
|
7
6
|
from panther.exceptions import DBException
|
8
7
|
|
9
8
|
|
@@ -32,7 +31,7 @@ class BasePantherDBQuery:
|
|
32
31
|
@classmethod
|
33
32
|
def _merge(cls, *args) -> dict:
|
34
33
|
# TODO: Convert "id" to "_id"
|
35
|
-
return
|
34
|
+
return merge_dicts(*args)
|
36
35
|
|
37
36
|
# # # # # Find # # # # #
|
38
37
|
@classmethod
|
@@ -1,8 +1,9 @@
|
|
1
1
|
from typing import Self
|
2
2
|
|
3
|
+
from panther.configs import config
|
4
|
+
from panther.db.utils import log_query
|
3
5
|
from panther.db.queries.mongodb_queries import BaseMongoDBQuery
|
4
6
|
from panther.db.queries.pantherdb_queries import BasePantherDBQuery
|
5
|
-
from panther.configs import config
|
6
7
|
|
7
8
|
if config['db_engine'] == 'pantherdb':
|
8
9
|
BaseQuery = BasePantherDBQuery
|
@@ -18,6 +19,7 @@ class Query(BaseQuery):
|
|
18
19
|
|
19
20
|
# # # # # Find # # # # #
|
20
21
|
@classmethod
|
22
|
+
@log_query
|
21
23
|
def find_one(cls, _data: dict = None, /, **kwargs) -> Self | None:
|
22
24
|
"""
|
23
25
|
example:
|
@@ -27,6 +29,7 @@ class Query(BaseQuery):
|
|
27
29
|
return super().find_one(_data, **kwargs)
|
28
30
|
|
29
31
|
@classmethod
|
32
|
+
@log_query
|
30
33
|
def find(cls, _data: dict = None, /, **kwargs) -> list[Self]:
|
31
34
|
"""
|
32
35
|
example:
|
@@ -37,6 +40,7 @@ class Query(BaseQuery):
|
|
37
40
|
|
38
41
|
# # # # # Insert # # # # #
|
39
42
|
@classmethod
|
43
|
+
@log_query
|
40
44
|
def insert_one(cls, _data: dict = None, /, **kwargs) -> Self:
|
41
45
|
"""
|
42
46
|
example:
|
@@ -46,10 +50,12 @@ class Query(BaseQuery):
|
|
46
50
|
return super().insert_one(_data, **kwargs)
|
47
51
|
|
48
52
|
@classmethod
|
53
|
+
@log_query
|
49
54
|
def insert_many(cls, _data: dict = None, **kwargs):
|
50
55
|
return super().insert_many(_data, **kwargs)
|
51
56
|
|
52
57
|
# # # # # Delete # # # # #
|
58
|
+
@log_query
|
53
59
|
def delete(self) -> None:
|
54
60
|
"""
|
55
61
|
example:
|
@@ -60,6 +66,7 @@ class Query(BaseQuery):
|
|
60
66
|
return super().delete()
|
61
67
|
|
62
68
|
@classmethod
|
69
|
+
@log_query
|
63
70
|
def delete_one(cls, **kwargs) -> bool:
|
64
71
|
"""
|
65
72
|
example:
|
@@ -69,6 +76,7 @@ class Query(BaseQuery):
|
|
69
76
|
return super().delete_one(**kwargs)
|
70
77
|
|
71
78
|
@classmethod
|
79
|
+
@log_query
|
72
80
|
def delete_many(cls, **kwargs) -> int:
|
73
81
|
"""
|
74
82
|
example:
|
@@ -78,6 +86,7 @@ class Query(BaseQuery):
|
|
78
86
|
return super().delete_many(**kwargs)
|
79
87
|
|
80
88
|
# # # # # Update # # # # #
|
89
|
+
@log_query
|
81
90
|
def update(self, **kwargs) -> None:
|
82
91
|
"""
|
83
92
|
example:
|
@@ -88,6 +97,7 @@ class Query(BaseQuery):
|
|
88
97
|
return super().update(**kwargs)
|
89
98
|
|
90
99
|
@classmethod
|
100
|
+
@log_query
|
91
101
|
def update_one(cls, _filter, _data: dict = None, /, **kwargs) -> bool:
|
92
102
|
"""
|
93
103
|
example:
|
@@ -98,6 +108,7 @@ class Query(BaseQuery):
|
|
98
108
|
return super().update_one(_filter, _data, **kwargs)
|
99
109
|
|
100
110
|
@classmethod
|
111
|
+
@log_query
|
101
112
|
def update_many(cls, _filter, **kwargs) -> int:
|
102
113
|
"""
|
103
114
|
example:
|
@@ -108,6 +119,7 @@ class Query(BaseQuery):
|
|
108
119
|
|
109
120
|
# # # # # Other # # # # #
|
110
121
|
@classmethod
|
122
|
+
@log_query
|
111
123
|
def first(cls, _data: dict = None, /, **kwargs) -> Self | None:
|
112
124
|
"""
|
113
125
|
It works same as find_one()
|
@@ -118,6 +130,7 @@ class Query(BaseQuery):
|
|
118
130
|
return super().first(_data, **kwargs)
|
119
131
|
|
120
132
|
@classmethod
|
133
|
+
@log_query
|
121
134
|
def last(cls, _data: dict = None, /, **kwargs) -> Self | None:
|
122
135
|
"""
|
123
136
|
example:
|
@@ -127,6 +140,7 @@ class Query(BaseQuery):
|
|
127
140
|
return super().last(_data, **kwargs)
|
128
141
|
|
129
142
|
@classmethod
|
143
|
+
@log_query
|
130
144
|
def count(cls, _data: dict = None, /, **kwargs) -> int:
|
131
145
|
"""
|
132
146
|
example:
|
@@ -4,18 +4,18 @@ from functools import reduce
|
|
4
4
|
from time import perf_counter
|
5
5
|
|
6
6
|
from panther.configs import config
|
7
|
-
from panther.logger import
|
7
|
+
from panther.logger import query_logger
|
8
8
|
|
9
9
|
|
10
|
-
def
|
10
|
+
def log_query(func):
|
11
11
|
def log(*args, **kwargs):
|
12
|
-
if config['
|
12
|
+
if config['log_queries'] is False:
|
13
13
|
return func(*args, **kwargs)
|
14
14
|
start = perf_counter()
|
15
15
|
response = func(*args, **kwargs)
|
16
16
|
end = perf_counter()
|
17
17
|
class_name = args[0].__name__ if hasattr(args[0], '__name__') else args[0].__class__.__name__
|
18
|
-
|
18
|
+
query_logger.info(f'\033[1mQuery -->\033[0m {class_name}.{func.__name__}() --> {(end - start) * 1_000:.2} ms')
|
19
19
|
return response
|
20
20
|
return log
|
21
21
|
|
@@ -1,14 +1,6 @@
|
|
1
1
|
import os
|
2
2
|
import logging
|
3
3
|
from pydantic import BaseModel
|
4
|
-
from panther.cli.utils import error
|
5
|
-
|
6
|
-
try:
|
7
|
-
import uvicorn
|
8
|
-
del uvicorn
|
9
|
-
except ImportError:
|
10
|
-
error('No module named "uvicorn"\n\nHint: Try to install with "pip install uvicorn[standard]"')
|
11
|
-
exit()
|
12
4
|
|
13
5
|
from logging.config import dictConfig
|
14
6
|
from panther.configs import config
|
@@ -26,6 +18,7 @@ class LogConfig(BaseModel):
|
|
26
18
|
DEFAULT_LOG_FORMAT: str = '%(levelprefix)s | %(asctime)s | %(message)s'
|
27
19
|
FILE_LOG_FORMAT: str = '%(asctime)s | %(message)s'
|
28
20
|
LOG_LEVEL: str = 'DEBUG'
|
21
|
+
MAX_FILE_SIZE: int = 1024 * 1024 * 100 # 100 MB
|
29
22
|
|
30
23
|
version = 1
|
31
24
|
disable_existing_loggers = False
|
@@ -47,15 +40,21 @@ class LogConfig(BaseModel):
|
|
47
40
|
'formatter': 'file_formatter',
|
48
41
|
'filename': LOGS_DIR / 'monitoring.log',
|
49
42
|
'class': 'logging.handlers.RotatingFileHandler',
|
50
|
-
'maxBytes':
|
43
|
+
'maxBytes': MAX_FILE_SIZE, # 100 MB,
|
44
|
+
'backupCount': 3,
|
45
|
+
},
|
46
|
+
'query_file': {
|
47
|
+
'formatter': 'file_formatter',
|
48
|
+
'filename': LOGS_DIR / 'query.log',
|
49
|
+
'class': 'logging.handlers.RotatingFileHandler',
|
50
|
+
'maxBytes': MAX_FILE_SIZE, # 100 MB,
|
51
51
|
'backupCount': 3,
|
52
52
|
},
|
53
53
|
'file': {
|
54
54
|
'formatter': 'file_formatter',
|
55
|
-
|
56
|
-
'filename': LOGS_DIR / f'{config["base_dir"].name}.log',
|
55
|
+
'filename': LOGS_DIR / f'main.log',
|
57
56
|
'class': 'logging.handlers.RotatingFileHandler',
|
58
|
-
'maxBytes':
|
57
|
+
'maxBytes': MAX_FILE_SIZE, # 100 MB,
|
59
58
|
'backupCount': 3,
|
60
59
|
},
|
61
60
|
'default': {
|
@@ -73,13 +72,14 @@ class LogConfig(BaseModel):
|
|
73
72
|
'handlers': ['monitoring_file'],
|
74
73
|
'level': LOG_LEVEL,
|
75
74
|
},
|
75
|
+
'query': {
|
76
|
+
'handlers': ['default', 'query_file'],
|
77
|
+
'level': LOG_LEVEL,
|
78
|
+
},
|
76
79
|
}
|
77
80
|
|
78
81
|
|
79
82
|
dictConfig(LogConfig().dict())
|
80
83
|
logger = logging.getLogger('panther')
|
81
|
-
|
82
|
-
|
83
|
-
"""
|
84
|
-
[debug, info, warning, error, critical]
|
85
|
-
"""
|
84
|
+
query_logger = logging.getLogger('query')
|
85
|
+
monitoring_logger = logging.getLogger('monitoring')
|
@@ -117,6 +117,7 @@ class Panther:
|
|
117
117
|
|
118
118
|
# Put Variables In "config"
|
119
119
|
config['monitoring'] = self.settings.get('MONITORING', config['monitoring'])
|
120
|
+
config['log_queries'] = self.settings.get('LOG_QUERIES', config['log_queries'])
|
120
121
|
config['default_cache_exp'] = self.settings.get('DEFAULT_CACHE_EXP', config['default_cache_exp'])
|
121
122
|
config['secret_key'] = self._get_secret_key()
|
122
123
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from time import perf_counter
|
2
2
|
|
3
|
-
from panther.logger import monitoring
|
4
|
-
from panther.middlewares.base import BaseMiddleware
|
5
3
|
from panther.request import Request
|
4
|
+
from panther.logger import monitoring_logger
|
5
|
+
from panther.middlewares.base import BaseMiddleware
|
6
6
|
|
7
7
|
|
8
8
|
class Middleware(BaseMiddleware):
|
@@ -21,4 +21,4 @@ class Middleware(BaseMiddleware):
|
|
21
21
|
but we should put in middlewares chain later ...
|
22
22
|
"""
|
23
23
|
response_time = (perf_counter() - self.start_time) * 1_000
|
24
|
-
|
24
|
+
monitoring_logger.info(f'{self.log} | {response_time: .3} ms | {status_code}')
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: panther
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.4.0
|
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
|
@@ -93,14 +93,10 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
93
93
|
```
|
94
94
|
|
95
95
|
- #### Run Project
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
Then
|
101
|
-
```console
|
102
|
-
$ panther run
|
103
|
-
```
|
96
|
+
Panther Uses [Uvicorn](https://github.com/encode/uvicorn) as ASGI (Asynchronous Server Gateway Interface)
|
97
|
+
```console
|
98
|
+
$ panther run
|
99
|
+
```
|
104
100
|
|
105
101
|
- #### Monitoring Requests
|
106
102
|
|
@@ -109,7 +105,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
109
105
|
```
|
110
106
|
|
111
107
|
- #### Python Shell
|
112
|
-
|
108
|
+
Panther Uses [bpython](https://bpython-interpreter.org) for shell
|
113
109
|
```console
|
114
110
|
$ panther shell
|
115
111
|
```
|
@@ -154,7 +150,7 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
154
150
|
**app/apis.py**:
|
155
151
|
|
156
152
|
```python
|
157
|
-
from datetime import
|
153
|
+
from datetime import datetime
|
158
154
|
|
159
155
|
from panther.app import API
|
160
156
|
from panther.configs import config
|
@@ -168,15 +164,13 @@ with [https://github.com/nakabonne/ali](https://github.com/nakabonne/ali) and he
|
|
168
164
|
return {'detail': 'Hello World'}
|
169
165
|
|
170
166
|
|
171
|
-
@API(cache=True
|
167
|
+
@API(cache=True)
|
172
168
|
async def info(request: Request):
|
173
169
|
data = {
|
174
170
|
'version': version(),
|
175
171
|
'datetime_now': datetime.now().isoformat(),
|
176
172
|
'user_agent': request.headers.user_agent,
|
177
|
-
'middlewares': config['middlewares'],
|
178
173
|
'db_engine': config['db_engine'],
|
179
|
-
'urls': config['urls'],
|
180
174
|
}
|
181
175
|
return Response(data=data, status_code=status.HTTP_202_ACCEPTED)
|
182
176
|
```
|
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
|
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
|
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
|