panther 1.3.2__py3-none-any.whl → 1.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
panther/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from .main import Panther
2
2
 
3
- __version__ = '1.3.2'
3
+ __version__ = '1.5.0'
4
4
 
5
5
 
6
6
  def version():
panther/cli/template.py CHANGED
@@ -65,6 +65,8 @@ USER_MODEL = 'panther.db.models.User'
65
65
 
66
66
  MONITORING = True
67
67
 
68
+ LOG_QUERIES = True
69
+
68
70
  URLs = 'core/urls.py'
69
71
  """ % datetime.now().date().isoformat()
70
72
 
panther/configs.py CHANGED
@@ -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': True,
32
+ 'monitoring': False,
33
+ 'log_queries': False,
32
34
  'secret_key': None,
33
35
  'urls': {},
34
36
  'middlewares': [],
panther/db/connection.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from redis import Redis
2
2
  from pantherdb import PantherDB
3
3
  from panther.configs import config
4
+ from panther.cli.utils import error
4
5
 
5
6
 
6
7
  class Singleton(object):
@@ -19,9 +20,8 @@ class DBSession(Singleton):
19
20
  if db_url:
20
21
  self._db_name = db_url[:db_url.find(':')]
21
22
  match self._db_name:
22
- # case 'mongodb':
23
- # # TODO: Check pymongo installed or not
24
- # self._create_mongodb_session(db_url)
23
+ case 'mongodb':
24
+ self._create_mongodb_session(db_url)
25
25
  case 'pantherdb':
26
26
  self._create_pantherdb_session(db_url[12:])
27
27
  case _:
@@ -37,8 +37,12 @@ class DBSession(Singleton):
37
37
  return self._db_name
38
38
 
39
39
  def _create_mongodb_session(self, db_url: str) -> None:
40
- from pymongo import MongoClient
41
- from pymongo.database import Database
40
+ try:
41
+ from pymongo import MongoClient
42
+ from pymongo.database import Database
43
+ except ImportError:
44
+ error('No module named "pymongo"\n\nHint: Try to install with "pip install pymongo"')
45
+ raise ImportError(error)
42
46
  self._client: MongoClient = MongoClient(db_url)
43
47
  self._session: Database = self._client.get_database()
44
48
 
@@ -56,7 +60,6 @@ class RedisConnection(Singleton, Redis):
56
60
  is_connected: bool = False
57
61
 
58
62
  def __init__(self, host: str | None = None, port: int | None = None, **kwargs):
59
- # TODO: Check redis installed or not
60
63
  if host and port:
61
64
  super().__init__(host=host, port=port, **kwargs)
62
65
  self.is_connected = True
@@ -1,102 +1,85 @@
1
- from typing import TypeVar
2
-
3
- import bson
1
+ from typing import Self
4
2
 
5
3
  from panther.db.connection import db # NOQA: F401
6
- from panther.db.utils import clean_object_id_in_dicts, merge_dicts, query_logger
7
-
8
- # TODO: Not sure about this bounding
9
- T = TypeVar('T', bound='BaseMongoDBQuery')
4
+ from panther.exceptions import DBException
5
+ from panther.db.utils import clean_object_id_in_dicts, merge_dicts
10
6
 
11
7
 
12
8
  class BaseMongoDBQuery:
13
9
 
14
10
  @classmethod
15
- @query_logger
16
- def get_one(cls: type[T], _data: dict = None, /, **kwargs) -> T:
17
- clean_object_id_in_dicts(_data, kwargs)
18
- _query = merge_dicts(_data, kwargs)
19
- obj = eval(f'db.session.{cls.__name__}.find_one(_query)')
20
- return cls(**obj) if obj else None
11
+ def _merge(cls, *args) -> dict:
12
+ clean_object_id_in_dicts(*args)
13
+ # TODO: Convert "id" to "_id"
14
+ return merge_dicts(*args)
21
15
 
16
+ # # # # # Find # # # # #
22
17
  @classmethod
23
- @query_logger
24
- def count(cls, _data: dict = None, /, **kwargs) -> int:
25
- clean_object_id_in_dicts(_data, kwargs)
26
- _query = merge_dicts(_data, kwargs)
27
- return eval(f'db.session.{cls.__name__}.count_documents(_query)')
18
+ def find_one(cls, _data: dict = None, /, **kwargs) -> Self | None:
19
+ _query = cls._merge(_data, kwargs)
20
+ if document := eval(f'db.session.{cls.__name__}.find_one(_query)'):
21
+ return cls(**document)
28
22
 
29
23
  @classmethod
30
- @query_logger
31
- def list(cls, _data: dict = None, /, **kwargs):
32
- clean_object_id_in_dicts(_data, kwargs)
33
- _query = merge_dicts(_data, kwargs)
34
- result = eval(f'db.session.{cls.__name__}.find(_query)')
35
- return [cls(**obj) for obj in result]
24
+ def find(cls, _data: dict = None, /, **kwargs) -> list[Self]:
25
+ _query = cls._merge(_data, kwargs)
26
+ documents = eval(f'db.session.{cls.__name__}.find(_query)')
27
+ return [cls(**document) for document in documents]
36
28
 
29
+ # # # # # Insert # # # # #
37
30
  @classmethod
38
- @query_logger
39
- def create(cls, _data: dict = None, **kwargs) -> bson.objectid.ObjectId:
40
- _query = merge_dicts(_data, kwargs)
41
- return eval(f'db.session.{cls.__name__}.insert_one(_query)').inserted_id
31
+ def insert_one(cls, _data: dict = None, **kwargs) -> Self:
32
+ document = cls._merge(_data, kwargs)
33
+ document['id'] = eval(f'db.session.{cls.__name__}.insert_one(document)').inserted_id
34
+ return cls(**document)
42
35
 
43
- @query_logger
44
- def delete(self) -> bool:
36
+ @classmethod
37
+ def insert_many(cls, _data: dict = None, **kwargs) -> Self:
38
+ raise DBException('insert_many() is not supported while using MongoDB.')
39
+
40
+ # # # # # Delete # # # # #
41
+ def delete(self) -> None:
45
42
  _filter = {'_id': self._id}
46
- result = eval(f'db.session.{self.__class__.__name__}.delete_one(_filter)')
47
- return bool(result.deleted_count)
43
+ eval(f'db.session.{self.__class__.__name__}.delete_one(_filter)')
48
44
 
49
45
  @classmethod
50
- @query_logger
51
- def delete_one(cls, **kwargs) -> bool:
52
- clean_object_id_in_dicts(kwargs)
53
- result = eval(f'db.session.{cls.__name__}.delete_one(kwargs)')
46
+ def delete_one(cls, _data: dict = None, /, **kwargs) -> bool:
47
+ _query = cls._merge(_data, kwargs)
48
+ result = eval(f'db.session.{cls.__name__}.delete_one(_query)')
54
49
  return bool(result.deleted_count)
55
50
 
56
51
  @classmethod
57
- @query_logger
58
- def delete_many(cls, **kwargs) -> int:
59
- clean_object_id_in_dicts(kwargs)
60
- result = eval(f'db.session.{cls.__name__}.delete_many(kwargs)')
52
+ def delete_many(cls, _data: dict = None, /, **kwargs) -> int:
53
+ _query = cls._merge(_data, kwargs)
54
+ result = eval(f'db.session.{cls.__name__}.delete_many(_query)')
61
55
  return result.deleted_count
62
56
 
63
- @query_logger
64
- def update(self, _data: dict = None, **kwargs) -> dict:
65
- for field, value in (_data or kwargs).items():
66
- if hasattr(self, field):
67
- setattr(self, field, value)
57
+ # # # # # Update # # # # #
58
+ def update(self, **kwargs) -> None:
59
+ for field, value in kwargs.items():
60
+ setattr(self, field, value)
68
61
  _filter = {'_id': self._id}
69
62
  _update = {'$set': kwargs}
70
- return eval(f'db.session.{self.__class__.__name__}.update_one(_filter, _update)')
63
+ eval(f'db.session.{self.__class__.__name__}.update_one(_filter, _update)')
71
64
 
72
65
  @classmethod
73
- @query_logger
74
- def update_one(cls, _filter, _data: dict = None, /, **kwargs) -> dict:
66
+ def update_one(cls, _filter, _data: dict = None, /, **kwargs) -> bool:
75
67
  clean_object_id_in_dicts(_filter)
76
-
77
68
  _update = {'$set': kwargs | {}}
78
69
  if isinstance(_data, dict):
79
70
  _data['$set'] = _data.get('$set', {}) | (kwargs or {})
80
71
 
81
- return eval(f'db.session.{cls.__name__}.update_one(_filter, _data | _update)')
82
-
83
- @classmethod
84
- @query_logger
85
- def update_many(cls, _filter, **kwargs) -> dict:
86
- _update = {'$set': kwargs}
87
- return eval(f'db.session.{cls.__name__}.update_many(_filter, _update)')
72
+ result = eval(f'db.session.{cls.__name__}.update_one(_filter, _data | _update)')
73
+ return bool(result.updated_count)
88
74
 
89
75
  @classmethod
90
- @query_logger
91
- def increment(cls, _filter, **kwargs):
92
- _update = {'$inc': kwargs}
93
- return eval(f'db.session.{cls.__name__}.update_many({_filter}, {_update})')
76
+ def update_many(cls, _filter, _data: dict = None, /, **kwargs) -> int:
77
+ _update = {'$set': cls._merge(_data, kwargs)}
78
+ result = eval(f'db.session.{cls.__name__}.update_many(_filter, _update)')
79
+ return result.updated_count
94
80
 
81
+ # # # # # Other # # # # #
95
82
  @classmethod
96
- @query_logger
97
- def get_or_create(cls, **kwargs) -> tuple[bool, any]:
98
- obj = cls.get_one(**kwargs)
99
- if obj:
100
- return False, obj
101
- else:
102
- return True, cls.create(**kwargs)
83
+ def count(cls, _data: dict = None, /, **kwargs) -> int:
84
+ _query = cls._merge(_data, kwargs)
85
+ return eval(f'db.session.{cls.__name__}.count_documents(_query)')
@@ -1,43 +1,21 @@
1
- import operator
2
- from functools import reduce
3
1
  from typing import Self
4
- from pydantic import ValidationError
5
2
 
6
3
  from panther.db.connection import db
4
+ from panther.db.utils import merge_dicts
7
5
  from panther.exceptions import DBException
8
6
 
9
7
 
10
8
  class BasePantherDBQuery:
11
9
 
12
- @classmethod
13
- def validate_data(cls, data: dict, is_updating: bool = False) -> Self | None:
14
- """
15
- *. Validate the input of user with its class
16
- *. If is_updating is True & exception happens but the message was empty
17
- Then return nothing (we don't need the obj on update())
18
- & we don't have access to the obj after exception ...
19
- """
20
- try:
21
- obj = cls(**data)
22
- except ValidationError as validation_error:
23
- error = ', '.join(
24
- '{field}="{error}"'.format(field=e['loc'][0], error=e['msg'])
25
- for e in validation_error.errors() if not is_updating or e['type'] != 'value_error.missing')
26
- if error:
27
- message = f'{cls.__name__}({error})'
28
- raise DBException(message)
29
- else:
30
- return obj
31
-
32
10
  @classmethod
33
11
  def _merge(cls, *args) -> dict:
34
12
  # TODO: Convert "id" to "_id"
35
- return reduce(operator.ior, filter(None, args), {})
13
+ return merge_dicts(*args)
36
14
 
37
15
  # # # # # Find # # # # #
38
16
  @classmethod
39
17
  def find_one(cls, _data: dict = None, /, **kwargs) -> Self | None:
40
- if document := db.session.collection(cls.__name__).first(**cls._merge(_data, kwargs)):
18
+ if document := db.session.collection(cls.__name__).find_one(**cls._merge(_data, kwargs)):
41
19
  return cls(**document)
42
20
 
43
21
  @classmethod
@@ -48,13 +26,12 @@ class BasePantherDBQuery:
48
26
  # # # # # Insert # # # # #
49
27
  @classmethod
50
28
  def insert_one(cls, _data: dict = None, **kwargs) -> Self:
51
- obj = cls.validate_data(kwargs)
52
- obj.id = db.session.collection(cls.__name__).insert_one(**cls._merge(_data, kwargs))['_id']
53
- return obj
29
+ document = db.session.collection(cls.__name__).insert_one(**cls._merge(_data, kwargs))
30
+ return cls(**document)
54
31
 
55
32
  @classmethod
56
33
  def insert_many(cls, _data: dict = None, /, **kwargs):
57
- raise DBException('increment() is not supported while using SlarkDB.')
34
+ raise DBException('insert_many() is not supported while using PantherDB.')
58
35
 
59
36
  # # # # # Delete # # # # #
60
37
  def delete(self) -> None:
@@ -70,7 +47,8 @@ class BasePantherDBQuery:
70
47
 
71
48
  # # # # # Update # # # # #
72
49
  def update(self, **kwargs) -> None:
73
- self.validate_data(kwargs, is_updating=True)
50
+ for field, value in kwargs.items():
51
+ setattr(self, field, value)
74
52
  db.session.collection(self.__class__.__name__).update_one({'_id': self.id}, **kwargs)
75
53
 
76
54
  @classmethod
@@ -82,16 +60,6 @@ class BasePantherDBQuery:
82
60
  return db.session.collection(cls.__name__).update_many(_filter, **cls._merge(_data, kwargs))
83
61
 
84
62
  # # # # # Other # # # # #
85
- @classmethod
86
- def first(cls, _data: dict = None, /, **kwargs) -> Self | None:
87
- if document := db.session.collection(cls.__name__).first(**cls._merge(_data, kwargs)):
88
- return cls(**document)
89
-
90
- @classmethod
91
- def last(cls, _data: dict = None, /, **kwargs) -> Self | None:
92
- if document := db.session.collection(cls.__name__).last(**cls._merge(_data, kwargs)):
93
- return cls(**document)
94
-
95
63
  @classmethod
96
64
  def count(cls, _data: dict = None, /, **kwargs) -> int:
97
65
  return db.session.collection(cls.__name__).count(**cls._merge(_data, kwargs))
@@ -1,8 +1,12 @@
1
1
  from typing import Self
2
2
 
3
+ from pydantic import ValidationError
4
+
5
+ from panther.configs import config
6
+ from panther.db.utils import log_query
3
7
  from panther.db.queries.mongodb_queries import BaseMongoDBQuery
4
8
  from panther.db.queries.pantherdb_queries import BasePantherDBQuery
5
- from panther.configs import config
9
+ from panther.exceptions import DBException
6
10
 
7
11
  if config['db_engine'] == 'pantherdb':
8
12
  BaseQuery = BasePantherDBQuery
@@ -16,8 +20,25 @@ __all__ = (
16
20
 
17
21
  class Query(BaseQuery):
18
22
 
23
+ @classmethod
24
+ def validate_data(cls, data: dict, is_updating: bool = False) -> Self | None:
25
+ """
26
+ *. Validate the input of user with its class
27
+ *. If is_updating is True & exception happens but the message was empty
28
+ """
29
+ try:
30
+ cls(**data)
31
+ except ValidationError as validation_error:
32
+ error = ', '.join(
33
+ '{field}="{error}"'.format(field=e['loc'][0], error=e['msg'])
34
+ for e in validation_error.errors() if not is_updating or e['type'] != 'value_error.missing')
35
+ if error:
36
+ message = f'{cls.__name__}({error})'
37
+ raise DBException(message)
38
+
19
39
  # # # # # Find # # # # #
20
40
  @classmethod
41
+ @log_query
21
42
  def find_one(cls, _data: dict = None, /, **kwargs) -> Self | None:
22
43
  """
23
44
  example:
@@ -27,6 +48,7 @@ class Query(BaseQuery):
27
48
  return super().find_one(_data, **kwargs)
28
49
 
29
50
  @classmethod
51
+ @log_query
30
52
  def find(cls, _data: dict = None, /, **kwargs) -> list[Self]:
31
53
  """
32
54
  example:
@@ -37,19 +59,23 @@ class Query(BaseQuery):
37
59
 
38
60
  # # # # # Insert # # # # #
39
61
  @classmethod
62
+ @log_query
40
63
  def insert_one(cls, _data: dict = None, /, **kwargs) -> Self:
41
64
  """
42
65
  example:
43
66
  >>> from example.app.models import User
44
67
  >>> User.insert_one(name='Ali', age=24, ...)
45
68
  """
69
+ cls.validate_data(kwargs)
46
70
  return super().insert_one(_data, **kwargs)
47
71
 
48
72
  @classmethod
73
+ @log_query
49
74
  def insert_many(cls, _data: dict = None, **kwargs):
50
75
  return super().insert_many(_data, **kwargs)
51
76
 
52
77
  # # # # # Delete # # # # #
78
+ @log_query
53
79
  def delete(self) -> None:
54
80
  """
55
81
  example:
@@ -60,6 +86,7 @@ class Query(BaseQuery):
60
86
  return super().delete()
61
87
 
62
88
  @classmethod
89
+ @log_query
63
90
  def delete_one(cls, **kwargs) -> bool:
64
91
  """
65
92
  example:
@@ -69,6 +96,7 @@ class Query(BaseQuery):
69
96
  return super().delete_one(**kwargs)
70
97
 
71
98
  @classmethod
99
+ @log_query
72
100
  def delete_many(cls, **kwargs) -> int:
73
101
  """
74
102
  example:
@@ -78,6 +106,7 @@ class Query(BaseQuery):
78
106
  return super().delete_many(**kwargs)
79
107
 
80
108
  # # # # # Update # # # # #
109
+ @log_query
81
110
  def update(self, **kwargs) -> None:
82
111
  """
83
112
  example:
@@ -85,9 +114,11 @@ class Query(BaseQuery):
85
114
  >>> user = User.find_one(name='Ali')
86
115
  >>> user.update(name='Saba')
87
116
  """
117
+ self.validate_data(kwargs, is_updating=True)
88
118
  return super().update(**kwargs)
89
119
 
90
120
  @classmethod
121
+ @log_query
91
122
  def update_one(cls, _filter, _data: dict = None, /, **kwargs) -> bool:
92
123
  """
93
124
  example:
@@ -98,6 +129,7 @@ class Query(BaseQuery):
98
129
  return super().update_one(_filter, _data, **kwargs)
99
130
 
100
131
  @classmethod
132
+ @log_query
101
133
  def update_many(cls, _filter, **kwargs) -> int:
102
134
  """
103
135
  example:
@@ -108,29 +140,34 @@ class Query(BaseQuery):
108
140
 
109
141
  # # # # # Other # # # # #
110
142
  @classmethod
111
- def first(cls, _data: dict = None, /, **kwargs) -> Self | None:
143
+ @log_query
144
+ def last(cls, _data: dict = None, /, **kwargs) -> Self | None:
112
145
  """
113
- It works same as find_one()
114
146
  example:
115
147
  >>> from example.app.models import User
116
- >>> User.first(name='Ali')
148
+ >>> user = User.last(name='Ali')
117
149
  """
118
- return super().first(_data, **kwargs)
150
+ return super().last(_data, **kwargs)
119
151
 
120
152
  @classmethod
121
- def last(cls, _data: dict = None, /, **kwargs) -> Self | None:
153
+ @log_query
154
+ def count(cls, _data: dict = None, /, **kwargs) -> int:
122
155
  """
123
156
  example:
124
157
  >>> from example.app.models import User
125
- >>> User.last(name='Ali')
158
+ >>> User.count(name='Ali')
126
159
  """
127
- return super().last(_data, **kwargs)
160
+ return super().count(_data, **kwargs)
128
161
 
129
162
  @classmethod
130
- def count(cls, _data: dict = None, /, **kwargs) -> int:
163
+ @log_query
164
+ def find_or_insert(cls, **kwargs) -> tuple[bool, any]:
131
165
  """
132
166
  example:
133
167
  >>> from example.app.models import User
134
- >>> User.count(name='Ali')
168
+ >>> user = User.find_or_insert(name='Ali')
135
169
  """
136
- return super().count(_data, **kwargs)
170
+ if obj := cls.find_one(**kwargs):
171
+ return False, obj
172
+ else:
173
+ return True, cls.insert_one(**kwargs)
panther/db/utils.py CHANGED
@@ -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 logger
7
+ from panther.logger import query_logger
8
8
 
9
9
 
10
- def query_logger(func):
10
+ def log_query(func):
11
11
  def log(*args, **kwargs):
12
- if config['monitoring'] is False:
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
- logger.info(f'\033[1mQuery -->\033[0m {class_name}.{func.__name__}() --> {(end - start) * 1_000:.2} ms')
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
 
panther/logger.py CHANGED
@@ -18,6 +18,7 @@ class LogConfig(BaseModel):
18
18
  DEFAULT_LOG_FORMAT: str = '%(levelprefix)s | %(asctime)s | %(message)s'
19
19
  FILE_LOG_FORMAT: str = '%(asctime)s | %(message)s'
20
20
  LOG_LEVEL: str = 'DEBUG'
21
+ MAX_FILE_SIZE: int = 1024 * 1024 * 100 # 100 MB
21
22
 
22
23
  version = 1
23
24
  disable_existing_loggers = False
@@ -39,15 +40,21 @@ class LogConfig(BaseModel):
39
40
  'formatter': 'file_formatter',
40
41
  'filename': LOGS_DIR / 'monitoring.log',
41
42
  'class': 'logging.handlers.RotatingFileHandler',
42
- 'maxBytes': 1024 * 1024 * 100, # 100 MB,
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,
43
51
  'backupCount': 3,
44
52
  },
45
53
  'file': {
46
54
  'formatter': 'file_formatter',
47
- # TODO: Don't know but base_dir.name will return '.' here :\
48
- 'filename': LOGS_DIR / f'{config["base_dir"].name}.log',
55
+ 'filename': LOGS_DIR / f'main.log',
49
56
  'class': 'logging.handlers.RotatingFileHandler',
50
- 'maxBytes': 1024 * 1024 * 100, # 100 MB,
57
+ 'maxBytes': MAX_FILE_SIZE, # 100 MB,
51
58
  'backupCount': 3,
52
59
  },
53
60
  'default': {
@@ -65,13 +72,14 @@ class LogConfig(BaseModel):
65
72
  'handlers': ['monitoring_file'],
66
73
  'level': LOG_LEVEL,
67
74
  },
75
+ 'query': {
76
+ 'handlers': ['default', 'query_file'],
77
+ 'level': LOG_LEVEL,
78
+ },
68
79
  }
69
80
 
70
81
 
71
82
  dictConfig(LogConfig().dict())
72
83
  logger = logging.getLogger('panther')
73
- monitoring = logging.getLogger('monitoring')
74
-
75
- """
76
- [debug, info, warning, error, critical]
77
- """
84
+ query_logger = logging.getLogger('query')
85
+ monitoring_logger = logging.getLogger('monitoring')
panther/main.py CHANGED
@@ -4,12 +4,12 @@ from runpy import run_path
4
4
  from pydantic.main import ModelMetaclass
5
5
 
6
6
  from panther import status
7
- from panther.configs import JWTConfig, config
7
+ from panther.request import Request
8
+ from panther.response import Response
8
9
  from panther.exceptions import APIException
10
+ from panther.configs import JWTConfig, config
9
11
  from panther.middlewares.base import BaseMiddleware
10
12
  from panther.middlewares.monitoring import Middleware as MonitoringMiddleware
11
- from panther.request import Request
12
- from panther.response import Response
13
13
  from panther.routings import find_endpoint, check_urls, collect_urls, finalize_urls
14
14
  from panther._utils import http_response, import_class, read_body, collect_path_variables
15
15
 
@@ -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
- monitoring.info(f'{self.log} | {response_time: .3} ms | {status_code}')
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.2
3
+ Version: 1.5.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
@@ -14,7 +14,8 @@ Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
15
  Requires-Dist: pantherdb (>=1.2.0)
16
16
  Requires-Dist: pydantic (>=1.10.5)
17
- Requires-Dist: uvicorn (==0.20.0)
17
+ Requires-Dist: watchfiles (>=0.18.1)
18
+ Requires-Dist: uvicorn[standard]
18
19
  Requires-Dist: bpython (>=0.24)
19
20
  Requires-Dist: bson (>=0.5.10)
20
21
  Requires-Dist: redis (>=4.0.2)
@@ -34,7 +35,7 @@ Requires-Dist: pymongo (>=4.3.3) ; extra == 'full'
34
35
  ---
35
36
 
36
37
  ### Features
37
- - Document-oriented Databases ORM ([PantherDB](https://pypi.org/project/pantherdb/), MongoDB)
38
+ - Document-oriented Databases ODM ([PantherDB](https://pypi.org/project/pantherdb/), MongoDB)
38
39
  - Visual API Monitoring (In Terminal)
39
40
  - Cache APIs (In Memory, In Redis)
40
41
  - Built-in Authentication Classes (Customizable)
@@ -1,12 +1,12 @@
1
- panther/__init__.py,sha256=3wbCCpx1wHKORMYkcXcuf3nk18M7HyPkxPsnmModdlE,89
1
+ panther/__init__.py,sha256=qhYoTW71O9z3-iK8zrAILsgYWzJ_tcpZQaqQoqWIbdw,89
2
2
  panther/_utils.py,sha256=Ltev6PCrTWHTWSQcWNfHMWDof1_MXODTAcDaGwflgXo,3667
3
3
  panther/app.py,sha256=tojfQaU8x5pZa7m8XS4m9fR7D5sIe_-d96hD2ccBINo,5469
4
4
  panther/authentications.py,sha256=FCRQL3xqN1Tla2vZsOY8-qXwPhnZBFsujJ-R7CeaW7w,2350
5
5
  panther/caching.py,sha256=qCqb-IaXD1T7VU0AzJVMoXawb366ZYob9kblDUT-7sM,2452
6
- panther/configs.py,sha256=UU2ePtjM0dT6C1p63l0EE9w0FPCDpUTOHbDPOkFHJRM,926
6
+ panther/configs.py,sha256=bRGRy772Rxjb3RunEBBvfa9LCNCmgcc2TKkfbNTK0GM,975
7
7
  panther/exceptions.py,sha256=c1MeSo7_uwDuqoBcjKTFyXz17uXP5iZ3bM0Sp5Stvhw,769
8
- panther/logger.py,sha256=fb6RdDN908Em_fmbE7yDpViWn-LQ4egvbid_obBZN70,2168
9
- panther/main.py,sha256=oQu4v_c8Iip1TrXmRtxHx7j4qYiYrNimMoAXMicOZ9g,7372
8
+ panther/logger.py,sha256=1skaZg4w55mTh4pSrCohkGp-7dg62lyEIoFAJKMP5L8,2499
9
+ panther/main.py,sha256=n9Q55NOrx0KgumQOi8Ra3CzxtYPGWZHMKkX4g44e9LQ,7460
10
10
  panther/request.py,sha256=Ix1podGHCU5NlUCT9Emc79GOITKEw623ldpTw3oZm68,4383
11
11
  panther/response.py,sha256=zzfe8vsPSnydN-_peJTizlnmnawxlNbJFU9zvXppYTk,1025
12
12
  panther/routings.py,sha256=sV3xWksKYb3_95BTDOtSqouroHkB98ExoNkgWT1t7jE,4422
@@ -17,24 +17,24 @@ panther/cli/create_command.py,sha256=cUPMzXJnwsa2J1eGetWYUnbEL5GEEe5x-_wG3fA3rLg
17
17
  panther/cli/main.py,sha256=TttHOr0jA2rkLxCCtpW3ES9x4dDpq5AS32dMN0VwoW0,891
18
18
  panther/cli/monitor_command.py,sha256=wR6Ti8-2-LuLhfPzdXH77g7xlNQAL5flpBoVwu9qdoE,2458
19
19
  panther/cli/run_command.py,sha256=SdguosH7Orlh-8x8eZK_EWrv8MPnXFKzqxruTHYdHqk,1501
20
- panther/cli/template.py,sha256=XssIjmb98tECumKC0wqE4b61_tpzFi9TXv6jzGtrlFg,2297
20
+ panther/cli/template.py,sha256=KhehW5nzovQzbW75J1lpAAQcIsqucqU4IXQXpVWBj5M,2317
21
21
  panther/cli/utils.py,sha256=QKQ5_qLWtTjpLY0lZ6yt2vDlc2H0jF6HFe3F-8YNJDg,8225
22
22
  panther/db/__init__.py,sha256=lOgqqRkGMzMSS1WS8D7lTChTr9ikIB5ImRPmucOhCyc,54
23
- panther/db/connection.py,sha256=BoqTetHcWn2cmjo1ELDoFFGkGlvJw6qDuBorKSe8ru0,2097
23
+ panther/db/connection.py,sha256=E6ZRkDY1b-ns6cYBkSCl-DTYLzbRuOaLYFZvU0fauKs,2207
24
24
  panther/db/models.py,sha256=vnhFtVONqhCkTkFMOSz_rTDmAAxNcegPoxk1p5SljLc,1028
25
- panther/db/utils.py,sha256=ed_wCy8tga6CLgGjUDe-GZvXZzVrLoUxPQukJvhrdo4,1209
25
+ panther/db/utils.py,sha256=6ndrZ9reaVTqnbFhX8MjOYtcrcPSY7UywAbDquY4eEg,1219
26
26
  panther/db/queries/__init__.py,sha256=BMffHS9RbHE-AUAeT9C5uY3L-hpDh0WGRduDUQ9Kpuc,41
27
- panther/db/queries/mongodb_queries.py,sha256=CHum1VDxVHt9fqa97CCvOijxSEWycEDiibcuoq0mJow,3447
28
- panther/db/queries/pantherdb_queries.py,sha256=FjmRILq2hPAuK_NSaJ6UuCqLhUSIfH9sVoG7BsKk_hw,3864
29
- panther/db/queries/queries.py,sha256=dVjp-P5z6fgIj0jrKW2C2GHQE3Wlx_6IRvgpsg6zH6A,3900
27
+ panther/db/queries/mongodb_queries.py,sha256=uXd81Q2ukJskFDYlNfOiuCCkHuH7URyfZMkD6MUiVkk,3205
28
+ panther/db/queries/pantherdb_queries.py,sha256=Ee3Kb5WIGQ-ZDeAYFHYx_Y34f-I0RgLT0XYFEof5FLM,2505
29
+ panther/db/queries/queries.py,sha256=MD65JzBtot-2fKbePVNBF5wymsywXfTm3mJwAE2dkIk,5052
30
30
  panther/middlewares/__init__.py,sha256=7RtHuS-MfybnJc6pcBSGhi9teXNhDsnJ3n7h_cXSkJk,66
31
31
  panther/middlewares/base.py,sha256=Php29ckITeGZm6GfauFG3i61bcsb4qoU8RpPLTqsfls,240
32
32
  panther/middlewares/db.py,sha256=C_PevTIaMykJl0NaaMYEfwE_oLdSLfKW2HR9UoPN1dU,508
33
- panther/middlewares/monitoring.py,sha256=WpB_DGPBLC0OSOHCFkFc5R3FlO6PTZ83C-px1Frm254,838
33
+ panther/middlewares/monitoring.py,sha256=CbGNJ4Ja-m9Wq3Ytv7RFyd-Qj9Ft6cYqBZZncCAxccs,852
34
34
  panther/middlewares/redis.py,sha256=_XIvPAOrVl74UoiA6-Xz8hSfWoeuM2Y1e74xeyvW0I4,1126
35
- panther-1.3.2.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
36
- panther-1.3.2.dist-info/METADATA,sha256=W1d6VHYUjlE_vHl4aEFr93fKXcqXgMRBRp8XyCTMme0,5124
37
- panther-1.3.2.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
38
- panther-1.3.2.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
39
- panther-1.3.2.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
40
- panther-1.3.2.dist-info/RECORD,,
35
+ panther-1.5.0.dist-info/LICENSE,sha256=2aF1hL2aC0zRPjzUkSxJUzZbn2_uLoOkn7DHjzZni-I,1524
36
+ panther-1.5.0.dist-info/METADATA,sha256=N_WVMfNbFUZfevcMvOxEANoqN03SXZWeBlb65BLgahc,5160
37
+ panther-1.5.0.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
38
+ panther-1.5.0.dist-info/entry_points.txt,sha256=6GPxYFGuzVfNB4YpHFJvYex6iWah5_tLnirAHwj2Qsg,51
39
+ panther-1.5.0.dist-info/top_level.txt,sha256=VbBs02JGXTIoHMzsX-eLOk2MCbBZzQbLhWiYpI7xI2g,8
40
+ panther-1.5.0.dist-info/RECORD,,