panther 4.3.0__tar.gz → 4.3.2__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 (81) hide show
  1. {panther-4.3.0 → panther-4.3.2}/PKG-INFO +5 -4
  2. {panther-4.3.0 → panther-4.3.2}/panther/__init__.py +1 -1
  3. {panther-4.3.0 → panther-4.3.2}/panther/_load_configs.py +12 -1
  4. {panther-4.3.0 → panther-4.3.2}/panther/_utils.py +9 -9
  5. {panther-4.3.0 → panther-4.3.2}/panther/db/queries/base_queries.py +1 -1
  6. {panther-4.3.0 → panther-4.3.2}/panther/main.py +8 -11
  7. {panther-4.3.0 → panther-4.3.2}/panther/serializer.py +1 -1
  8. {panther-4.3.0 → panther-4.3.2}/panther.egg-info/PKG-INFO +5 -4
  9. {panther-4.3.0 → panther-4.3.2}/panther.egg-info/requires.txt +2 -2
  10. {panther-4.3.0 → panther-4.3.2}/setup.py +15 -10
  11. {panther-4.3.0 → panther-4.3.2}/tests/test_utils.py +54 -0
  12. {panther-4.3.0 → panther-4.3.2}/LICENSE +0 -0
  13. {panther-4.3.0 → panther-4.3.2}/README.md +0 -0
  14. {panther-4.3.0 → panther-4.3.2}/panther/app.py +0 -0
  15. {panther-4.3.0 → panther-4.3.2}/panther/authentications.py +0 -0
  16. {panther-4.3.0 → panther-4.3.2}/panther/background_tasks.py +0 -0
  17. {panther-4.3.0 → panther-4.3.2}/panther/base_request.py +0 -0
  18. {panther-4.3.0 → panther-4.3.2}/panther/base_websocket.py +0 -0
  19. {panther-4.3.0 → panther-4.3.2}/panther/caching.py +0 -0
  20. {panther-4.3.0 → panther-4.3.2}/panther/cli/__init__.py +0 -0
  21. {panther-4.3.0 → panther-4.3.2}/panther/cli/create_command.py +0 -0
  22. {panther-4.3.0 → panther-4.3.2}/panther/cli/main.py +0 -0
  23. {panther-4.3.0 → panther-4.3.2}/panther/cli/monitor_command.py +0 -0
  24. {panther-4.3.0 → panther-4.3.2}/panther/cli/run_command.py +0 -0
  25. {panther-4.3.0 → panther-4.3.2}/panther/cli/template.py +0 -0
  26. {panther-4.3.0 → panther-4.3.2}/panther/cli/utils.py +0 -0
  27. {panther-4.3.0 → panther-4.3.2}/panther/configs.py +0 -0
  28. {panther-4.3.0 → panther-4.3.2}/panther/db/__init__.py +0 -0
  29. {panther-4.3.0 → panther-4.3.2}/panther/db/connections.py +0 -0
  30. {panther-4.3.0 → panther-4.3.2}/panther/db/cursor.py +0 -0
  31. {panther-4.3.0 → panther-4.3.2}/panther/db/models.py +0 -0
  32. {panther-4.3.0 → panther-4.3.2}/panther/db/queries/__init__.py +0 -0
  33. {panther-4.3.0 → panther-4.3.2}/panther/db/queries/mongodb_queries.py +0 -0
  34. {panther-4.3.0 → panther-4.3.2}/panther/db/queries/pantherdb_queries.py +0 -0
  35. {panther-4.3.0 → panther-4.3.2}/panther/db/queries/queries.py +0 -0
  36. {panther-4.3.0 → panther-4.3.2}/panther/db/utils.py +0 -0
  37. {panther-4.3.0 → panther-4.3.2}/panther/events.py +0 -0
  38. {panther-4.3.0 → panther-4.3.2}/panther/exceptions.py +0 -0
  39. {panther-4.3.0 → panther-4.3.2}/panther/file_handler.py +0 -0
  40. {panther-4.3.0 → panther-4.3.2}/panther/generics.py +0 -0
  41. {panther-4.3.0 → panther-4.3.2}/panther/logging.py +0 -0
  42. {panther-4.3.0 → panther-4.3.2}/panther/middlewares/__init__.py +0 -0
  43. {panther-4.3.0 → panther-4.3.2}/panther/middlewares/base.py +0 -0
  44. {panther-4.3.0 → panther-4.3.2}/panther/monitoring.py +0 -0
  45. {panther-4.3.0 → panther-4.3.2}/panther/pagination.py +0 -0
  46. {panther-4.3.0 → panther-4.3.2}/panther/panel/__init__.py +0 -0
  47. {panther-4.3.0 → panther-4.3.2}/panther/panel/apis.py +0 -0
  48. {panther-4.3.0 → panther-4.3.2}/panther/panel/urls.py +0 -0
  49. {panther-4.3.0 → panther-4.3.2}/panther/panel/utils.py +0 -0
  50. {panther-4.3.0 → panther-4.3.2}/panther/permissions.py +0 -0
  51. {panther-4.3.0 → panther-4.3.2}/panther/request.py +0 -0
  52. {panther-4.3.0 → panther-4.3.2}/panther/response.py +0 -0
  53. {panther-4.3.0 → panther-4.3.2}/panther/routings.py +0 -0
  54. {panther-4.3.0 → panther-4.3.2}/panther/status.py +0 -0
  55. {panther-4.3.0 → panther-4.3.2}/panther/test.py +0 -0
  56. {panther-4.3.0 → panther-4.3.2}/panther/throttling.py +0 -0
  57. {panther-4.3.0 → panther-4.3.2}/panther/utils.py +0 -0
  58. {panther-4.3.0 → panther-4.3.2}/panther/websocket.py +0 -0
  59. {panther-4.3.0 → panther-4.3.2}/panther.egg-info/SOURCES.txt +0 -0
  60. {panther-4.3.0 → panther-4.3.2}/panther.egg-info/dependency_links.txt +0 -0
  61. {panther-4.3.0 → panther-4.3.2}/panther.egg-info/entry_points.txt +0 -0
  62. {panther-4.3.0 → panther-4.3.2}/panther.egg-info/top_level.txt +0 -0
  63. {panther-4.3.0 → panther-4.3.2}/pyproject.toml +0 -0
  64. {panther-4.3.0 → panther-4.3.2}/setup.cfg +0 -0
  65. {panther-4.3.0 → panther-4.3.2}/tests/test_authentication.py +0 -0
  66. {panther-4.3.0 → panther-4.3.2}/tests/test_background_tasks.py +0 -0
  67. {panther-4.3.0 → panther-4.3.2}/tests/test_caching.py +0 -0
  68. {panther-4.3.0 → panther-4.3.2}/tests/test_cli.py +0 -0
  69. {panther-4.3.0 → panther-4.3.2}/tests/test_database.py +0 -0
  70. {panther-4.3.0 → panther-4.3.2}/tests/test_events.py +0 -0
  71. {panther-4.3.0 → panther-4.3.2}/tests/test_generics.py +0 -0
  72. {panther-4.3.0 → panther-4.3.2}/tests/test_multipart.py +0 -0
  73. {panther-4.3.0 → panther-4.3.2}/tests/test_panel_apis.py +0 -0
  74. {panther-4.3.0 → panther-4.3.2}/tests/test_request.py +0 -0
  75. {panther-4.3.0 → panther-4.3.2}/tests/test_response.py +0 -0
  76. {panther-4.3.0 → panther-4.3.2}/tests/test_routing.py +0 -0
  77. {panther-4.3.0 → panther-4.3.2}/tests/test_run.py +0 -0
  78. {panther-4.3.0 → panther-4.3.2}/tests/test_serializer.py +0 -0
  79. {panther-4.3.0 → panther-4.3.2}/tests/test_status.py +0 -0
  80. {panther-4.3.0 → panther-4.3.2}/tests/test_throttling.py +0 -0
  81. {panther-4.3.0 → panther-4.3.2}/tests/test_websockets.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 4.3.0
4
- Summary: Fast & Friendly, Web Framework For Building Async APIs
3
+ Version: 4.3.2
4
+ Summary: Fast & Friendly, Web Framework For Building Async APIs
5
5
  Home-page: https://github.com/alirn76/panther
6
6
  Author: Ali RajabNezhad
7
7
  Author-email: alirn76@yahoo.com
@@ -10,16 +10,17 @@ Classifier: Operating System :: OS Independent
10
10
  Classifier: Programming Language :: Python :: 3.10
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
13
14
  Requires-Python: >=3.10
14
15
  Description-Content-Type: text/markdown
15
16
  License-File: LICENSE
16
- Requires-Dist: httptools~=0.6.1
17
17
  Requires-Dist: pantherdb~=2.1.0
18
- Requires-Dist: pydantic~=2.7.4
18
+ Requires-Dist: pydantic~=2.8.2
19
19
  Requires-Dist: rich~=13.7.1
20
20
  Requires-Dist: uvicorn~=0.27.1
21
21
  Requires-Dist: pytz~=2024.1
22
22
  Requires-Dist: Jinja2~=3.1
23
+ Requires-Dist: httptools~=0.6.1
23
24
  Provides-Extra: full
24
25
  Requires-Dist: redis==5.0.1; extra == "full"
25
26
  Requires-Dist: motor~=3.5.0; extra == "full"
@@ -1,6 +1,6 @@
1
1
  from panther.main import Panther # noqa: F401
2
2
 
3
- __version__ = '4.3.0'
3
+ __version__ = '4.3.2'
4
4
 
5
5
 
6
6
  def version():
@@ -1,9 +1,10 @@
1
1
  import logging
2
2
  import sys
3
+ import types
3
4
  from importlib import import_module
4
5
  from multiprocessing import Manager
5
6
 
6
- from panther._utils import import_class
7
+ from panther._utils import import_class, check_function_type_endpoint, check_class_type_endpoint
7
8
  from panther.background_tasks import background_tasks
8
9
  from panther.base_websocket import WebsocketConnections
9
10
  from panther.cli.utils import import_error
@@ -36,6 +37,7 @@ __all__ = (
36
37
  'load_authentication_class',
37
38
  'load_urls',
38
39
  'load_websocket_connections',
40
+ 'check_endpoints_inheritance',
39
41
  )
40
42
 
41
43
  logger = logging.getLogger('panther')
@@ -265,5 +267,14 @@ def load_websocket_connections():
265
267
  config.WEBSOCKET_CONNECTIONS = WebsocketConnections(pubsub_connection=pubsub_connection)
266
268
 
267
269
 
270
+ def check_endpoints_inheritance():
271
+ """Should be after `load_urls()`"""
272
+ for _, endpoint in config.FLAT_URLS.items():
273
+ if isinstance(endpoint, types.FunctionType):
274
+ check_function_type_endpoint(endpoint=endpoint)
275
+ else:
276
+ check_class_type_endpoint(endpoint=endpoint)
277
+
278
+
268
279
  def _exception_handler(field: str, error: str | Exception) -> PantherError:
269
280
  return PantherError(f"Invalid '{field}': {error}")
@@ -4,12 +4,13 @@ import logging
4
4
  import re
5
5
  import subprocess
6
6
  import types
7
- from typing import Any, Generator, Iterator, AsyncGenerator
8
7
  from collections.abc import Callable
9
8
  from traceback import TracebackException
9
+ from typing import Any, Generator, Iterator, AsyncGenerator
10
10
 
11
11
  from panther.exceptions import PantherError
12
12
  from panther.file_handler import File
13
+ from panther.websocket import GenericWebsocket
13
14
 
14
15
  logger = logging.getLogger('panther')
15
16
 
@@ -99,19 +100,18 @@ def reformat_code(base_dir):
99
100
  def check_function_type_endpoint(endpoint: types.FunctionType) -> Callable:
100
101
  # Function Doesn't Have @API Decorator
101
102
  if not hasattr(endpoint, '__wrapped__'):
102
- logger.critical(f'You may have forgotten to use @API() on the {endpoint.__name__}()')
103
- raise TypeError
104
- return endpoint
103
+ raise PantherError(
104
+ f'You may have forgotten to use `@API()` on the `{endpoint.__module__}.{endpoint.__name__}()`')
105
105
 
106
106
 
107
107
  def check_class_type_endpoint(endpoint: Callable) -> Callable:
108
108
  from panther.app import GenericAPI
109
109
 
110
- if not issubclass(endpoint, GenericAPI):
111
- logger.critical(f'You may have forgotten to inherit from GenericAPI on the {endpoint.__name__}()')
112
- raise TypeError
113
-
114
- return endpoint().call_method
110
+ if not issubclass(endpoint, (GenericAPI, GenericWebsocket)):
111
+ raise PantherError(
112
+ f'You may have forgotten to inherit from `panther.app.GenericAPI` or `panther.app.GenericWebsocket` '
113
+ f'on the `{endpoint.__module__}.{endpoint.__name__}()`'
114
+ )
115
115
 
116
116
 
117
117
  def async_next(iterator: Iterator):
@@ -28,7 +28,7 @@ class BaseQuery:
28
28
  def _clean_error_message(cls, validation_error: ValidationError, is_updating: bool = False) -> str:
29
29
  error = ', '.join(
30
30
  '{field}="{error}"'.format(
31
- field='.'.join(loc for loc in e['loc']),
31
+ field='.'.join(str(loc) for loc in e['loc']),
32
32
  error=e['msg']
33
33
  )
34
34
  for e in validation_error.errors()
@@ -67,6 +67,8 @@ class Panther:
67
67
  load_urls(self._configs_module, urls=self._urls)
68
68
  load_websocket_connections()
69
69
 
70
+ check_endpoints_inheritance()
71
+
70
72
  async def __call__(self, scope: dict, receive: Callable, send: Callable) -> None:
71
73
  if scope['type'] == 'lifespan':
72
74
  message = await receive()
@@ -160,19 +162,10 @@ class Panther:
160
162
  await request.read_body()
161
163
 
162
164
  # Find Endpoint
163
- _endpoint, found_path = find_endpoint(path=request.path)
164
- if _endpoint is None:
165
+ endpoint, found_path = find_endpoint(path=request.path)
166
+ if endpoint is None:
165
167
  return await self._raise(send, monitoring=monitoring, status_code=status.HTTP_404_NOT_FOUND)
166
168
 
167
- # Check Endpoint Type
168
- try:
169
- if isinstance(_endpoint, types.FunctionType):
170
- endpoint = check_function_type_endpoint(endpoint=_endpoint)
171
- else:
172
- endpoint = check_class_type_endpoint(endpoint=_endpoint)
173
- except TypeError:
174
- return await self._raise(send, monitoring=monitoring, status_code=status.HTTP_501_NOT_IMPLEMENTED)
175
-
176
169
  # Collect Path Variables
177
170
  request.collect_path_variables(found_path=found_path)
178
171
 
@@ -186,6 +179,10 @@ class Panther:
186
179
  f'Make sure to return the `request` at the end of `{middleware.__class__.__name__}.before()`')
187
180
  return await self._raise(send, monitoring=monitoring)
188
181
 
182
+ # Prepare the method
183
+ if not isinstance(endpoint, types.FunctionType):
184
+ endpoint = endpoint().call_method
185
+
189
186
  # Call Endpoint
190
187
  response = await endpoint(request=request)
191
188
 
@@ -39,7 +39,7 @@ class MetaModelSerializer:
39
39
 
40
40
  # 4. Create a serializer
41
41
  return create_model(
42
- __model_name=cls_name,
42
+ cls_name,
43
43
  __module__=namespace['__module__'],
44
44
  __validators__=namespace,
45
45
  __base__=(cls.model_serializer, BaseModel),
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 4.3.0
4
- Summary: Fast & Friendly, Web Framework For Building Async APIs
3
+ Version: 4.3.2
4
+ Summary: Fast & Friendly, Web Framework For Building Async APIs
5
5
  Home-page: https://github.com/alirn76/panther
6
6
  Author: Ali RajabNezhad
7
7
  Author-email: alirn76@yahoo.com
@@ -10,16 +10,17 @@ Classifier: Operating System :: OS Independent
10
10
  Classifier: Programming Language :: Python :: 3.10
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
13
14
  Requires-Python: >=3.10
14
15
  Description-Content-Type: text/markdown
15
16
  License-File: LICENSE
16
- Requires-Dist: httptools~=0.6.1
17
17
  Requires-Dist: pantherdb~=2.1.0
18
- Requires-Dist: pydantic~=2.7.4
18
+ Requires-Dist: pydantic~=2.8.2
19
19
  Requires-Dist: rich~=13.7.1
20
20
  Requires-Dist: uvicorn~=0.27.1
21
21
  Requires-Dist: pytz~=2024.1
22
22
  Requires-Dist: Jinja2~=3.1
23
+ Requires-Dist: httptools~=0.6.1
23
24
  Provides-Extra: full
24
25
  Requires-Dist: redis==5.0.1; extra == "full"
25
26
  Requires-Dist: motor~=3.5.0; extra == "full"
@@ -1,10 +1,10 @@
1
- httptools~=0.6.1
2
1
  pantherdb~=2.1.0
3
- pydantic~=2.7.4
2
+ pydantic~=2.8.2
4
3
  rich~=13.7.1
5
4
  uvicorn~=0.27.1
6
5
  pytz~=2024.1
7
6
  Jinja2~=3.1
7
+ httptools~=0.6.1
8
8
 
9
9
  [full]
10
10
  redis==5.0.1
@@ -1,4 +1,5 @@
1
1
  import re
2
+ import sys
2
3
 
3
4
  from setuptools import setup
4
5
 
@@ -12,6 +13,17 @@ VERSION = panther_version()
12
13
  with open('README.md', encoding='utf-8') as file:
13
14
  DESCRIPTION = file.read()
14
15
 
16
+ INSTALL_REQUIRES = [
17
+ 'pantherdb~=2.1.0',
18
+ 'pydantic~=2.8.2',
19
+ 'rich~=13.7.1',
20
+ 'uvicorn~=0.27.1',
21
+ 'pytz~=2024.1',
22
+ 'Jinja2~=3.1',
23
+ ]
24
+ if sys.version_info <= (3, 12):
25
+ INSTALL_REQUIRES.append('httptools~=0.6.1')
26
+
15
27
  EXTRAS_REQUIRE = {
16
28
  'full': [
17
29
  'redis==5.0.1',
@@ -32,7 +44,7 @@ setup(
32
44
  author='Ali RajabNezhad',
33
45
  author_email='alirn76@yahoo.com',
34
46
  url='https://github.com/alirn76/panther',
35
- description='Fast & Friendly, Web Framework For Building Async APIs',
47
+ description='Fast & Friendly, Web Framework For Building Async APIs',
36
48
  long_description=DESCRIPTION,
37
49
  long_description_content_type='text/markdown',
38
50
  include_package_data=True,
@@ -42,6 +54,7 @@ setup(
42
54
  'Programming Language :: Python :: 3.10',
43
55
  'Programming Language :: Python :: 3.11',
44
56
  'Programming Language :: Python :: 3.12',
57
+ 'Programming Language :: Python :: 3.13',
45
58
  ],
46
59
  entry_points={
47
60
  'console_scripts': ['panther=panther.cli.main:start'],
@@ -49,14 +62,6 @@ setup(
49
62
  package_data={
50
63
  'panther': ['cli/*'],
51
64
  },
52
- install_requires=[
53
- 'httptools~=0.6.1',
54
- 'pantherdb~=2.1.0',
55
- 'pydantic~=2.7.4',
56
- 'rich~=13.7.1',
57
- 'uvicorn~=0.27.1',
58
- 'pytz~=2024.1',
59
- 'Jinja2~=3.1',
60
- ],
65
+ install_requires=INSTALL_REQUIRES,
61
66
  extras_require=EXTRAS_REQUIRE,
62
67
  )
@@ -379,6 +379,60 @@ class TestLoadConfigs(TestCase):
379
379
  AUTHENTICATION = None
380
380
  SECRET_KEY = None
381
381
 
382
+ def test_check_function_endpoint_decorator(self):
383
+ with self.assertLogs(level='ERROR') as captured:
384
+ try:
385
+ Panther(name=__name__, configs=__name__, urls={'/': invalid_api})
386
+ except SystemExit:
387
+ assert True
388
+ else:
389
+ assert False
390
+
391
+ assert len(captured.records) == 1
392
+ assert captured.records[0].getMessage() == 'You may have forgotten to use `@API()` on the `tests.test_utils.invalid_api()`'
393
+
394
+ def test_check_class_endpoint_inheritance(self):
395
+ with self.assertLogs(level='ERROR') as captured:
396
+ try:
397
+ Panther(name=__name__, configs=__name__, urls={'/': InvalidAPI})
398
+ except SystemExit:
399
+ assert True
400
+ else:
401
+ assert False
402
+
403
+ assert len(captured.records) == 1
404
+ assert captured.records[0].getMessage() == (
405
+ f'You may have forgotten to inherit from `panther.app.GenericAPI` or `panther.app.GenericWebsocket` '
406
+ f'on the `tests.test_utils.InvalidAPI()`'
407
+ )
408
+
409
+ def test_check_websocket_inheritance(self):
410
+ with self.assertLogs(level='ERROR') as captured:
411
+ try:
412
+ Panther(name=__name__, configs=__name__, urls={'/': InvalidWebsocket})
413
+ except SystemExit:
414
+ assert True
415
+ else:
416
+ assert False
417
+
418
+ assert len(captured.records) == 1
419
+ assert captured.records[0].getMessage() == (
420
+ f'You may have forgotten to inherit from `panther.app.GenericAPI` or `panther.app.GenericWebsocket` '
421
+ f'on the `tests.test_utils.InvalidWebsocket()`'
422
+ )
423
+
424
+
425
+ def invalid_api():
426
+ pass
427
+
428
+
429
+ class InvalidAPI:
430
+ pass
431
+
432
+
433
+ class InvalidWebsocket:
434
+ pass
435
+
382
436
 
383
437
  class CorrectTestMiddleware(BaseMiddleware):
384
438
  pass
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
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