panther 4.2.6__tar.gz → 4.3.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.
Files changed (81) hide show
  1. {panther-4.2.6 → panther-4.3.0}/PKG-INFO +2 -1
  2. {panther-4.2.6 → panther-4.3.0}/panther/__init__.py +1 -1
  3. {panther-4.2.6 → panther-4.3.0}/panther/_load_configs.py +6 -0
  4. {panther-4.2.6 → panther-4.3.0}/panther/configs.py +2 -0
  5. {panther-4.2.6 → panther-4.3.0}/panther/main.py +1 -0
  6. {panther-4.2.6 → panther-4.3.0}/panther/response.py +37 -0
  7. {panther-4.2.6 → panther-4.3.0}/panther.egg-info/PKG-INFO +2 -1
  8. {panther-4.2.6 → panther-4.3.0}/panther.egg-info/requires.txt +1 -0
  9. {panther-4.2.6 → panther-4.3.0}/setup.py +1 -0
  10. {panther-4.2.6 → panther-4.3.0}/tests/test_response.py +35 -3
  11. {panther-4.2.6 → panther-4.3.0}/LICENSE +0 -0
  12. {panther-4.2.6 → panther-4.3.0}/README.md +0 -0
  13. {panther-4.2.6 → panther-4.3.0}/panther/_utils.py +0 -0
  14. {panther-4.2.6 → panther-4.3.0}/panther/app.py +0 -0
  15. {panther-4.2.6 → panther-4.3.0}/panther/authentications.py +0 -0
  16. {panther-4.2.6 → panther-4.3.0}/panther/background_tasks.py +0 -0
  17. {panther-4.2.6 → panther-4.3.0}/panther/base_request.py +0 -0
  18. {panther-4.2.6 → panther-4.3.0}/panther/base_websocket.py +0 -0
  19. {panther-4.2.6 → panther-4.3.0}/panther/caching.py +0 -0
  20. {panther-4.2.6 → panther-4.3.0}/panther/cli/__init__.py +0 -0
  21. {panther-4.2.6 → panther-4.3.0}/panther/cli/create_command.py +0 -0
  22. {panther-4.2.6 → panther-4.3.0}/panther/cli/main.py +0 -0
  23. {panther-4.2.6 → panther-4.3.0}/panther/cli/monitor_command.py +0 -0
  24. {panther-4.2.6 → panther-4.3.0}/panther/cli/run_command.py +0 -0
  25. {panther-4.2.6 → panther-4.3.0}/panther/cli/template.py +0 -0
  26. {panther-4.2.6 → panther-4.3.0}/panther/cli/utils.py +0 -0
  27. {panther-4.2.6 → panther-4.3.0}/panther/db/__init__.py +0 -0
  28. {panther-4.2.6 → panther-4.3.0}/panther/db/connections.py +0 -0
  29. {panther-4.2.6 → panther-4.3.0}/panther/db/cursor.py +0 -0
  30. {panther-4.2.6 → panther-4.3.0}/panther/db/models.py +0 -0
  31. {panther-4.2.6 → panther-4.3.0}/panther/db/queries/__init__.py +0 -0
  32. {panther-4.2.6 → panther-4.3.0}/panther/db/queries/base_queries.py +0 -0
  33. {panther-4.2.6 → panther-4.3.0}/panther/db/queries/mongodb_queries.py +0 -0
  34. {panther-4.2.6 → panther-4.3.0}/panther/db/queries/pantherdb_queries.py +0 -0
  35. {panther-4.2.6 → panther-4.3.0}/panther/db/queries/queries.py +0 -0
  36. {panther-4.2.6 → panther-4.3.0}/panther/db/utils.py +0 -0
  37. {panther-4.2.6 → panther-4.3.0}/panther/events.py +0 -0
  38. {panther-4.2.6 → panther-4.3.0}/panther/exceptions.py +0 -0
  39. {panther-4.2.6 → panther-4.3.0}/panther/file_handler.py +0 -0
  40. {panther-4.2.6 → panther-4.3.0}/panther/generics.py +0 -0
  41. {panther-4.2.6 → panther-4.3.0}/panther/logging.py +0 -0
  42. {panther-4.2.6 → panther-4.3.0}/panther/middlewares/__init__.py +0 -0
  43. {panther-4.2.6 → panther-4.3.0}/panther/middlewares/base.py +0 -0
  44. {panther-4.2.6 → panther-4.3.0}/panther/monitoring.py +0 -0
  45. {panther-4.2.6 → panther-4.3.0}/panther/pagination.py +0 -0
  46. {panther-4.2.6 → panther-4.3.0}/panther/panel/__init__.py +0 -0
  47. {panther-4.2.6 → panther-4.3.0}/panther/panel/apis.py +0 -0
  48. {panther-4.2.6 → panther-4.3.0}/panther/panel/urls.py +0 -0
  49. {panther-4.2.6 → panther-4.3.0}/panther/panel/utils.py +0 -0
  50. {panther-4.2.6 → panther-4.3.0}/panther/permissions.py +0 -0
  51. {panther-4.2.6 → panther-4.3.0}/panther/request.py +0 -0
  52. {panther-4.2.6 → panther-4.3.0}/panther/routings.py +0 -0
  53. {panther-4.2.6 → panther-4.3.0}/panther/serializer.py +0 -0
  54. {panther-4.2.6 → panther-4.3.0}/panther/status.py +0 -0
  55. {panther-4.2.6 → panther-4.3.0}/panther/test.py +0 -0
  56. {panther-4.2.6 → panther-4.3.0}/panther/throttling.py +0 -0
  57. {panther-4.2.6 → panther-4.3.0}/panther/utils.py +0 -0
  58. {panther-4.2.6 → panther-4.3.0}/panther/websocket.py +0 -0
  59. {panther-4.2.6 → panther-4.3.0}/panther.egg-info/SOURCES.txt +0 -0
  60. {panther-4.2.6 → panther-4.3.0}/panther.egg-info/dependency_links.txt +0 -0
  61. {panther-4.2.6 → panther-4.3.0}/panther.egg-info/entry_points.txt +0 -0
  62. {panther-4.2.6 → panther-4.3.0}/panther.egg-info/top_level.txt +0 -0
  63. {panther-4.2.6 → panther-4.3.0}/pyproject.toml +0 -0
  64. {panther-4.2.6 → panther-4.3.0}/setup.cfg +0 -0
  65. {panther-4.2.6 → panther-4.3.0}/tests/test_authentication.py +0 -0
  66. {panther-4.2.6 → panther-4.3.0}/tests/test_background_tasks.py +0 -0
  67. {panther-4.2.6 → panther-4.3.0}/tests/test_caching.py +0 -0
  68. {panther-4.2.6 → panther-4.3.0}/tests/test_cli.py +0 -0
  69. {panther-4.2.6 → panther-4.3.0}/tests/test_database.py +0 -0
  70. {panther-4.2.6 → panther-4.3.0}/tests/test_events.py +0 -0
  71. {panther-4.2.6 → panther-4.3.0}/tests/test_generics.py +0 -0
  72. {panther-4.2.6 → panther-4.3.0}/tests/test_multipart.py +0 -0
  73. {panther-4.2.6 → panther-4.3.0}/tests/test_panel_apis.py +0 -0
  74. {panther-4.2.6 → panther-4.3.0}/tests/test_request.py +0 -0
  75. {panther-4.2.6 → panther-4.3.0}/tests/test_routing.py +0 -0
  76. {panther-4.2.6 → panther-4.3.0}/tests/test_run.py +0 -0
  77. {panther-4.2.6 → panther-4.3.0}/tests/test_serializer.py +0 -0
  78. {panther-4.2.6 → panther-4.3.0}/tests/test_status.py +0 -0
  79. {panther-4.2.6 → panther-4.3.0}/tests/test_throttling.py +0 -0
  80. {panther-4.2.6 → panther-4.3.0}/tests/test_utils.py +0 -0
  81. {panther-4.2.6 → panther-4.3.0}/tests/test_websockets.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 4.2.6
3
+ Version: 4.3.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
@@ -19,6 +19,7 @@ Requires-Dist: pydantic~=2.7.4
19
19
  Requires-Dist: rich~=13.7.1
20
20
  Requires-Dist: uvicorn~=0.27.1
21
21
  Requires-Dist: pytz~=2024.1
22
+ Requires-Dist: Jinja2~=3.1
22
23
  Provides-Extra: full
23
24
  Requires-Dist: redis==5.0.1; extra == "full"
24
25
  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.2.6'
3
+ __version__ = '4.3.0'
4
4
 
5
5
 
6
6
  def version():
@@ -29,6 +29,7 @@ __all__ = (
29
29
  'load_user_model',
30
30
  'load_log_queries',
31
31
  'load_middlewares',
32
+ 'load_templates_dir',
32
33
  'load_auto_reformat',
33
34
  'load_background_tasks',
34
35
  'load_default_cache_exp',
@@ -82,6 +83,11 @@ def load_timezone(_configs: dict, /) -> None:
82
83
  config.TIMEZONE = timezone
83
84
 
84
85
 
86
+ def load_templates_dir(_configs: dict, /) -> None:
87
+ if templates_dir := _configs.get('TEMPLATES_DIR'):
88
+ config.TEMPLATES_DIR = templates_dir
89
+
90
+
85
91
  def load_database(_configs: dict, /) -> None:
86
92
  database_config = _configs.get('DATABASE', {})
87
93
  if 'engine' in database_config:
@@ -67,6 +67,7 @@ class Config:
67
67
  STARTUPS: list[Callable]
68
68
  SHUTDOWNS: list[Callable]
69
69
  TIMEZONE: str
70
+ TEMPLATES_DIR: str | list[str]
70
71
  AUTO_REFORMAT: bool
71
72
  QUERY_ENGINE: typing.Callable | None
72
73
  DATABASE: typing.Callable | None
@@ -110,6 +111,7 @@ default_configs = {
110
111
  'STARTUPS': [],
111
112
  'SHUTDOWNS': [],
112
113
  'TIMEZONE': 'UTC',
114
+ 'TEMPLATES_DIR': 'templates',
113
115
  'AUTO_REFORMAT': False,
114
116
  'QUERY_ENGINE': None,
115
117
  'DATABASE': None,
@@ -58,6 +58,7 @@ class Panther:
58
58
  load_throttling(self._configs_module)
59
59
  load_user_model(self._configs_module)
60
60
  load_log_queries(self._configs_module)
61
+ load_templates_dir(self._configs_module)
61
62
  load_middlewares(self._configs_module)
62
63
  load_auto_reformat(self._configs_module)
63
64
  load_background_tasks(self._configs_module)
@@ -1,11 +1,22 @@
1
1
  import asyncio
2
+ from sys import version_info
2
3
  from types import NoneType
3
4
  from typing import Generator, AsyncGenerator, Any, Type
4
5
 
6
+ if version_info >= (3, 11):
7
+ from typing import LiteralString
8
+ else:
9
+ from typing import TypeVar
10
+
11
+ LiteralString = TypeVar('LiteralString')
12
+
13
+
5
14
  import orjson as json
6
15
  from pydantic import BaseModel
16
+ from jinja2 import Environment, FileSystemLoader
7
17
 
8
18
  from panther import status
19
+ from panther.configs import config
9
20
  from panther._utils import to_async_generator
10
21
  from panther.db.cursor import Cursor
11
22
  from pantherdb import Cursor as PantherDBCursor
@@ -215,3 +226,29 @@ class PlainTextResponse(Response):
215
226
  if isinstance(self.data, bytes):
216
227
  return self.data
217
228
  return self.data.encode()
229
+
230
+
231
+ class TemplateResponse(HTMLResponse):
232
+ environment = Environment(loader=FileSystemLoader(config.TEMPLATES_DIR))
233
+
234
+ def __init__(
235
+ self,
236
+ source: str | LiteralString | NoneType = None,
237
+ path: str | NoneType = None,
238
+ context: dict | NoneType = None,
239
+ headers: dict | NoneType = None,
240
+ status_code: int = status.HTTP_200_OK,
241
+ pagination: Pagination | NoneType = None,
242
+ ):
243
+ """
244
+ :param source: should be a string
245
+ :param path: should be path of template file
246
+ :param context: should be dict of items
247
+ :param headers: should be dict of headers
248
+ :param status_code: should be int
249
+ :param pagination: instance of Pagination or None
250
+ Its template() method will be used
251
+ """
252
+
253
+ template = self.environment.get_template(path) if path is not None else self.environment.from_string(source)
254
+ super().__init__(template.render(context), headers, status_code, pagination=pagination)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: panther
3
- Version: 4.2.6
3
+ Version: 4.3.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
@@ -19,6 +19,7 @@ Requires-Dist: pydantic~=2.7.4
19
19
  Requires-Dist: rich~=13.7.1
20
20
  Requires-Dist: uvicorn~=0.27.1
21
21
  Requires-Dist: pytz~=2024.1
22
+ Requires-Dist: Jinja2~=3.1
22
23
  Provides-Extra: full
23
24
  Requires-Dist: redis==5.0.1; extra == "full"
24
25
  Requires-Dist: motor~=3.5.0; extra == "full"
@@ -4,6 +4,7 @@ pydantic~=2.7.4
4
4
  rich~=13.7.1
5
5
  uvicorn~=0.27.1
6
6
  pytz~=2024.1
7
+ Jinja2~=3.1
7
8
 
8
9
  [full]
9
10
  redis==5.0.1
@@ -56,6 +56,7 @@ setup(
56
56
  'rich~=13.7.1',
57
57
  'uvicorn~=0.27.1',
58
58
  'pytz~=2024.1',
59
+ 'Jinja2~=3.1',
59
60
  ],
60
61
  extras_require=EXTRAS_REQUIRE,
61
62
  )
@@ -2,7 +2,7 @@ from unittest import IsolatedAsyncioTestCase
2
2
 
3
3
  from panther import Panther
4
4
  from panther.app import API, GenericAPI
5
- from panther.response import Response, HTMLResponse, PlainTextResponse, StreamingResponse
5
+ from panther.response import Response, HTMLResponse, PlainTextResponse, StreamingResponse, TemplateResponse
6
6
  from panther.test import APIClient
7
7
 
8
8
 
@@ -116,6 +116,18 @@ class ReturnHTMLResponse(GenericAPI):
116
116
  return HTMLResponse('<html><head><title></title></head></html>')
117
117
 
118
118
 
119
+ @API()
120
+ async def return_template_response() -> TemplateResponse:
121
+ return TemplateResponse(source='<html><body><p>{{ content }}</p></body></html>', context={'content': 'Hello World'})
122
+
123
+
124
+ class ReturnTemplateResponse(GenericAPI):
125
+ def get(self) -> TemplateResponse:
126
+ return TemplateResponse(
127
+ source='<html><body><p>{{ content }}</p></body></html>', context={'content': 'Hello World'}
128
+ )
129
+
130
+
119
131
  @API()
120
132
  async def return_plain_response():
121
133
  return PlainTextResponse('Hello World')
@@ -160,7 +172,7 @@ urls = {
160
172
  'response-tuple': return_response_tuple,
161
173
  'html': return_html_response,
162
174
  'plain': return_plain_response,
163
-
175
+ 'template': return_template_response,
164
176
  'nothing-cls': ReturnNothing,
165
177
  'none-cls': ReturnNone,
166
178
  'dict-cls': ReturnDict,
@@ -172,8 +184,8 @@ urls = {
172
184
  'response-list-cls': ReturnResponseList,
173
185
  'response-tuple-cls': ReturnResponseTuple,
174
186
  'html-cls': ReturnHTMLResponse,
187
+ 'template-cls': ReturnTemplateResponse,
175
188
  'plain-cls': ReturnPlainResponse,
176
-
177
189
  'stream': ReturnStreamingResponse,
178
190
  'async-stream': ReturnAsyncStreamingResponse,
179
191
  'invalid-status-code': ReturnInvalidStatusCode,
@@ -406,6 +418,26 @@ class TestResponses(IsolatedAsyncioTestCase):
406
418
  assert res.headers['Access-Control-Allow-Origin'] == '*'
407
419
  assert res.headers['Content-Length'] == '41'
408
420
 
421
+ async def test_response_template(self) -> None:
422
+ res: Response = await self.client.get('template/')
423
+ assert res.status_code == 200
424
+ assert res.data == '<html><body><p>Hello World</p></body></html>'
425
+ assert res.body == b'<html><body><p>Hello World</p></body></html>'
426
+ assert set(res.headers.keys()) == {'Content-Type', 'Access-Control-Allow-Origin', 'Content-Length'}
427
+ assert res.headers['Content-Type'] == 'text/html; charset=utf-8'
428
+ assert res.headers['Access-Control-Allow-Origin'] == '*'
429
+ assert res.headers['Content-Length'] == '44'
430
+
431
+ async def test_response_template_cls(self) -> None:
432
+ res: Response = await self.client.get('template-cls/')
433
+ assert res.status_code == 200
434
+ assert res.data == '<html><body><p>Hello World</p></body></html>'
435
+ assert res.body == b'<html><body><p>Hello World</p></body></html>'
436
+ assert set(res.headers.keys()) == {'Content-Type', 'Access-Control-Allow-Origin', 'Content-Length'}
437
+ assert res.headers['Content-Type'] == 'text/html; charset=utf-8'
438
+ assert res.headers['Access-Control-Allow-Origin'] == '*'
439
+ assert res.headers['Content-Length'] == '44'
440
+
409
441
  async def test_response_plain(self):
410
442
  res = await self.client.get('plain/')
411
443
  assert res.status_code == 200
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