Plinx 0.0.1__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.
- Plinx-0.0.1.dist-info/LICENSE +8 -0
- Plinx-0.0.1.dist-info/METADATA +110 -0
- Plinx-0.0.1.dist-info/RECORD +12 -0
- Plinx-0.0.1.dist-info/WHEEL +5 -0
- Plinx-0.0.1.dist-info/top_level.txt +1 -0
- plinx/__init__.py +1 -0
- plinx/applications.py +192 -0
- plinx/methods.py +18 -0
- plinx/middleware.py +81 -0
- plinx/response.py +48 -0
- plinx/status_codes.py +36 -0
- plinx/utils.py +13 -0
@@ -0,0 +1,8 @@
|
|
1
|
+
Copyright © 2025 Dhaval Savalia
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
8
|
+
|
@@ -0,0 +1,110 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: Plinx
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: Plinx is an experimental, minimalistic, and extensible web framework and ORM written in Python.
|
5
|
+
Home-page: https://github.com/dhavalsavalia/plinx
|
6
|
+
Author: Dhaval Savalia
|
7
|
+
Author-email: coder@dhavalsavalia.com
|
8
|
+
License: MIT
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
10
|
+
Classifier: Programming Language :: Python
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
13
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
14
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
15
|
+
Requires-Python: >=3.11.0
|
16
|
+
Description-Content-Type: text/markdown
|
17
|
+
License-File: LICENSE
|
18
|
+
|
19
|
+
|
20
|
+
# Plinx
|
21
|
+
|
22
|
+

|
23
|
+

|
24
|
+
|
25
|
+
**Plinx** is an experimental, minimalistic, and extensible WSGI-based web framework and ORM written in Python.
|
26
|
+
It is designed to be simple, fast, and easy to extend, making it ideal for rapid prototyping and educational purposes.
|
27
|
+
|
28
|
+
---
|
29
|
+
|
30
|
+
## Features
|
31
|
+
|
32
|
+
- 🚀 Minimal and fast web framework
|
33
|
+
- 🛣️ Intuitive routing system
|
34
|
+
- 🧩 Extensible middleware support
|
35
|
+
- 🧪 Simple, readable codebase for learning and hacking
|
36
|
+
- 📝 Type hints and modern Python best practices
|
37
|
+
|
38
|
+
---
|
39
|
+
|
40
|
+
## Installation
|
41
|
+
|
42
|
+
Install directly from the git source:
|
43
|
+
|
44
|
+
```bash
|
45
|
+
pip install git+https://github.com/dhavalsavalia/plinx.git
|
46
|
+
```
|
47
|
+
|
48
|
+
---
|
49
|
+
|
50
|
+
## Quickstart
|
51
|
+
|
52
|
+
Create a simple web application in seconds:
|
53
|
+
|
54
|
+
```python
|
55
|
+
from plinx import Plinx
|
56
|
+
|
57
|
+
app = Plinx()
|
58
|
+
|
59
|
+
@app.route("/")
|
60
|
+
def index(request, response):
|
61
|
+
response.text = "Hello, world!"
|
62
|
+
```
|
63
|
+
|
64
|
+
Run your app (example, assuming you have an ASGI server like `uvicorn`):
|
65
|
+
|
66
|
+
```bash
|
67
|
+
uvicorn myapp:app
|
68
|
+
```
|
69
|
+
|
70
|
+
## Testing
|
71
|
+
|
72
|
+
Use [pytest](https://docs.pytest.org/en/latest/) to unit test this framework.
|
73
|
+
|
74
|
+
```bash
|
75
|
+
pytest --cov=.
|
76
|
+
```
|
77
|
+
|
78
|
+
---
|
79
|
+
|
80
|
+
## Roadmap
|
81
|
+
|
82
|
+
- [x] Web Framework
|
83
|
+
- [x] Routing
|
84
|
+
- [x] Explicit Routing Methods (GET, POST, etc.)
|
85
|
+
- [x] Parameterized Routes
|
86
|
+
- [x] Class Based Routes
|
87
|
+
- [x] Django-like Routes
|
88
|
+
- [x] Middleware Support
|
89
|
+
- [ ] ORM
|
90
|
+
|
91
|
+
---
|
92
|
+
|
93
|
+
## Contributing
|
94
|
+
|
95
|
+
Contributions are welcome! Please open issues or submit pull requests for improvements, bug fixes, or new features.
|
96
|
+
|
97
|
+
---
|
98
|
+
|
99
|
+
## License
|
100
|
+
|
101
|
+
This project is licensed under the MIT License. See [LICENSE](LICENSE) for details.
|
102
|
+
|
103
|
+
---
|
104
|
+
|
105
|
+
## Author & Contact
|
106
|
+
|
107
|
+
Created and maintained by [Dhaval Savalia](https://github.com/dhavalsavalia).
|
108
|
+
For questions or opportunities, feel free to reach out via [LinkedIn](https://www.linkedin.com/in/dhavalsavalia/) or open an issue.
|
109
|
+
|
110
|
+
---
|
@@ -0,0 +1,12 @@
|
|
1
|
+
plinx/__init__.py,sha256=oMho3a3JJZllxtj4lLwS96pwWRBvctDJRFww5lfqlBs,32
|
2
|
+
plinx/applications.py,sha256=Oy2pO1zyfKWyYXeJ8AuzdoAab6XJThNHDbOdpKWA5-8,5957
|
3
|
+
plinx/methods.py,sha256=CEaousqNXbgdyaEL0BdOtAWh1p03jAaAFzXJkY9xII0,294
|
4
|
+
plinx/middleware.py,sha256=OiNDhKWpmpKkAIbSWxRzP_tQXSyfDToos-MQMDPSUWM,2161
|
5
|
+
plinx/response.py,sha256=oDZPF9V76HhqdUZLzSmoLw_q-GG9hc5krfCBgNm3OfU,1460
|
6
|
+
plinx/status_codes.py,sha256=V7vX7_ujYIaVjO3I3eVUnuNKiTXMSov56wJt8tvMbAM,700
|
7
|
+
plinx/utils.py,sha256=WaU_j7YXsdlZh3M33B2vXjRPAU26lBeQ5sV2ZasXVwE,352
|
8
|
+
Plinx-0.0.1.dist-info/LICENSE,sha256=MljMjTJD6oOY41eZWLDZ4x8FrTUcl7ElKxWIXJXUwLM,1066
|
9
|
+
Plinx-0.0.1.dist-info/METADATA,sha256=N-vDBHtG4pV2LKumS4yh40_IYzKKh8PgRf1G3Umw_Q0,2568
|
10
|
+
Plinx-0.0.1.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
11
|
+
Plinx-0.0.1.dist-info/top_level.txt,sha256=U_4P3aFsTEhhvfiE3sVbJKAG9Fp4IPC5d6zpttayAZw,6
|
12
|
+
Plinx-0.0.1.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
plinx
|
plinx/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
from .applications import Plinx
|
plinx/applications.py
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
import inspect
|
2
|
+
from typing import Callable, Dict, Iterable, Tuple
|
3
|
+
from wsgiref.types import StartResponse, WSGIEnvironment
|
4
|
+
|
5
|
+
from parse import parse
|
6
|
+
from requests import Session as RequestsSession
|
7
|
+
from webob import Request
|
8
|
+
from wsgiadapter import WSGIAdapter as RequestsWSGIAdapter
|
9
|
+
|
10
|
+
from .methods import HTTPMethods
|
11
|
+
from .middleware import Middleware
|
12
|
+
from .response import PlinxResponse as Response
|
13
|
+
from .status_codes import StatusCodes
|
14
|
+
from .utils import handle_404
|
15
|
+
|
16
|
+
|
17
|
+
class Plinx:
|
18
|
+
def __init__(self):
|
19
|
+
self.routes: Dict[str, Tuple[HTTPMethods, Callable]] = {}
|
20
|
+
self.exception_handler = None
|
21
|
+
self.middleware = Middleware(self)
|
22
|
+
|
23
|
+
self._method_decorators = {}
|
24
|
+
for method in HTTPMethods:
|
25
|
+
self._method_decorators[method.name.lower()] = (
|
26
|
+
self._create_method_decorator(method)
|
27
|
+
)
|
28
|
+
|
29
|
+
def __call__(
|
30
|
+
self,
|
31
|
+
environ: WSGIEnvironment,
|
32
|
+
start_response: StartResponse,
|
33
|
+
) -> Iterable[bytes]:
|
34
|
+
"""
|
35
|
+
Entrypoint for the WSGI application.
|
36
|
+
:param environ: The WSGI environment.
|
37
|
+
:param start_response: The WSGI callable.
|
38
|
+
:return: The response body produced by the middleware.
|
39
|
+
"""
|
40
|
+
return self.middleware(environ, start_response)
|
41
|
+
|
42
|
+
def add_route(
|
43
|
+
self,
|
44
|
+
path: str,
|
45
|
+
handler: Callable,
|
46
|
+
method: HTTPMethods = HTTPMethods.GET,
|
47
|
+
):
|
48
|
+
"""
|
49
|
+
Add a route to the application. Django-like syntax.
|
50
|
+
:param path: The path to register.
|
51
|
+
:param handler: The handler to register.
|
52
|
+
:return:
|
53
|
+
"""
|
54
|
+
if path in self.routes:
|
55
|
+
raise RuntimeError(f"Route '{path}' is already registered.")
|
56
|
+
|
57
|
+
self.routes[path] = (method, handler)
|
58
|
+
|
59
|
+
def route(
|
60
|
+
self,
|
61
|
+
path: str,
|
62
|
+
):
|
63
|
+
"""
|
64
|
+
Register a route with the given path.
|
65
|
+
:param path: The path to register.
|
66
|
+
:return:
|
67
|
+
"""
|
68
|
+
|
69
|
+
def wrapper(handler):
|
70
|
+
self.add_route(path, handler)
|
71
|
+
return handler
|
72
|
+
|
73
|
+
return wrapper
|
74
|
+
|
75
|
+
def __getattr__(
|
76
|
+
self,
|
77
|
+
name: str,
|
78
|
+
):
|
79
|
+
"""Allow access to HTTP method decorators like app.get, app.post etc."""
|
80
|
+
if name in self._method_decorators:
|
81
|
+
return self._method_decorators[name]
|
82
|
+
raise RuntimeError(
|
83
|
+
f"'{self.__class__.__name__}' object has no attribute '{name}'"
|
84
|
+
)
|
85
|
+
|
86
|
+
def _create_method_decorator(self, method: HTTPMethods):
|
87
|
+
"""
|
88
|
+
Creates a decorator for registering routes with a specific HTTP method.
|
89
|
+
:param method: The HTTP method enum value
|
90
|
+
:return: Decorator function
|
91
|
+
"""
|
92
|
+
|
93
|
+
def decorator(path: str):
|
94
|
+
def wrapper(handler):
|
95
|
+
self.add_route(path, handler, method)
|
96
|
+
return handler
|
97
|
+
|
98
|
+
return wrapper
|
99
|
+
|
100
|
+
return decorator
|
101
|
+
|
102
|
+
def handle_request(
|
103
|
+
self,
|
104
|
+
request: Request,
|
105
|
+
) -> Response:
|
106
|
+
"""
|
107
|
+
Handle the given request and return the response.
|
108
|
+
:param request: The request object.
|
109
|
+
:return: The response object.
|
110
|
+
"""
|
111
|
+
response: Response = Response()
|
112
|
+
|
113
|
+
handler_definition, kwargs = self.find_handler(request, response)
|
114
|
+
|
115
|
+
try:
|
116
|
+
if handler_definition is not None:
|
117
|
+
method, handler = handler_definition
|
118
|
+
|
119
|
+
# Handle CBVs
|
120
|
+
if inspect.isclass(handler):
|
121
|
+
handler = getattr(
|
122
|
+
handler(),
|
123
|
+
request.method.lower(),
|
124
|
+
None,
|
125
|
+
)
|
126
|
+
# only allow methods that are defined in the class
|
127
|
+
if handler is None:
|
128
|
+
response.status_code = StatusCodes.METHOD_NOT_ALLOWED.value
|
129
|
+
response.text = "Method Not Allowed"
|
130
|
+
return response
|
131
|
+
|
132
|
+
if inspect.isfunction(handler):
|
133
|
+
# Handle function-based views
|
134
|
+
if request.method != method.value:
|
135
|
+
response.status_code = StatusCodes.METHOD_NOT_ALLOWED.value
|
136
|
+
response.text = "Method Not Allowed"
|
137
|
+
return response
|
138
|
+
|
139
|
+
handler(request, response, **kwargs)
|
140
|
+
|
141
|
+
except Exception as e:
|
142
|
+
if self.exception_handler:
|
143
|
+
self.exception_handler(request, response, e)
|
144
|
+
else:
|
145
|
+
response.status_code = StatusCodes.INTERNAL_SERVER_ERROR.value
|
146
|
+
response.text = str(e)
|
147
|
+
|
148
|
+
return response
|
149
|
+
|
150
|
+
def find_handler(
|
151
|
+
self,
|
152
|
+
request: Request,
|
153
|
+
response: Response,
|
154
|
+
) -> Tuple[Callable, dict | None] | Tuple[None, None]:
|
155
|
+
"""
|
156
|
+
Find the handler for the given request.
|
157
|
+
If no handler is found, set the response status code to 404
|
158
|
+
and return None.
|
159
|
+
|
160
|
+
:param request: The request object.
|
161
|
+
:param response: The response object.
|
162
|
+
:return: A tuple containing the handler and the named parameters.
|
163
|
+
If no handler is found, return tuple[None, None].
|
164
|
+
"""
|
165
|
+
for path, handler in self.routes.items():
|
166
|
+
parse_result = parse(path, request.path)
|
167
|
+
if parse_result is not None:
|
168
|
+
return handler, parse_result.named
|
169
|
+
|
170
|
+
handle_404(response)
|
171
|
+
return None, None
|
172
|
+
|
173
|
+
def add_exception_handler(
|
174
|
+
self,
|
175
|
+
exception_handler,
|
176
|
+
):
|
177
|
+
self.exception_handler = exception_handler
|
178
|
+
|
179
|
+
def add_middleware(
|
180
|
+
self,
|
181
|
+
middleware_cls: type[Middleware],
|
182
|
+
):
|
183
|
+
"""
|
184
|
+
Add a middleware class to the application.
|
185
|
+
:param middleware_cls: The middleware class to add.
|
186
|
+
"""
|
187
|
+
self.middleware.add(middleware_cls)
|
188
|
+
|
189
|
+
def test_session(self, base_url="http://testserver"):
|
190
|
+
session = RequestsSession()
|
191
|
+
session.mount(prefix=base_url, adapter=RequestsWSGIAdapter(self))
|
192
|
+
return session
|
plinx/methods.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
|
4
|
+
class HTTPMethods(Enum):
|
5
|
+
"""HTTP methods for the API."""
|
6
|
+
|
7
|
+
# Safe methods
|
8
|
+
GET = "GET"
|
9
|
+
HEAD = "HEAD"
|
10
|
+
|
11
|
+
# Idempotent methods
|
12
|
+
PUT = "PUT"
|
13
|
+
DELETE = "DELETE"
|
14
|
+
OPTIONS = "OPTIONS"
|
15
|
+
|
16
|
+
# Non-idempotent methods
|
17
|
+
POST = "POST"
|
18
|
+
PATCH = "PATCH"
|
plinx/middleware.py
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
from webob import Request, Response
|
2
|
+
|
3
|
+
|
4
|
+
class Middleware:
|
5
|
+
"""
|
6
|
+
Middleware base class for all middleware classes.
|
7
|
+
This class is used to create middleware for the Plinx application.
|
8
|
+
Middleware classes should inherit from this class and implement the
|
9
|
+
`process_request` and `process_response` methods.
|
10
|
+
"""
|
11
|
+
|
12
|
+
def __init__(
|
13
|
+
self,
|
14
|
+
app,
|
15
|
+
):
|
16
|
+
"""
|
17
|
+
Middleware base class for all middleware classes.
|
18
|
+
:param app: The WSGI application.
|
19
|
+
"""
|
20
|
+
self.app = app
|
21
|
+
|
22
|
+
def __call__(
|
23
|
+
self,
|
24
|
+
environ: dict,
|
25
|
+
start_response: callable,
|
26
|
+
):
|
27
|
+
"""
|
28
|
+
Entrypoint for the WSGI middleware since it is now entrypoint for the WSGI application.
|
29
|
+
:param environ: The WSGI environment.
|
30
|
+
:param start_response: The WSGI callable.
|
31
|
+
:return: The response body.
|
32
|
+
"""
|
33
|
+
request = Request(environ)
|
34
|
+
|
35
|
+
response = self.app.handle_request(request)
|
36
|
+
|
37
|
+
return response(environ, start_response)
|
38
|
+
|
39
|
+
def add(
|
40
|
+
self,
|
41
|
+
middleware_cls,
|
42
|
+
):
|
43
|
+
"""
|
44
|
+
Add a middleware class to the application.
|
45
|
+
:param middleware_cls: The middleware class to add.
|
46
|
+
"""
|
47
|
+
self.app = middleware_cls(self.app)
|
48
|
+
|
49
|
+
def process_request(
|
50
|
+
self,
|
51
|
+
request: Request,
|
52
|
+
):
|
53
|
+
"""
|
54
|
+
Process the request before it is passed to the application.
|
55
|
+
:param request: The request object.
|
56
|
+
"""
|
57
|
+
pass # pragma: no cover
|
58
|
+
|
59
|
+
def process_response(
|
60
|
+
self,
|
61
|
+
request: Request,
|
62
|
+
response: Response,
|
63
|
+
):
|
64
|
+
"""
|
65
|
+
Process the response after it is passed to the application.
|
66
|
+
:param request: The request object.
|
67
|
+
:param response: The response object.
|
68
|
+
"""
|
69
|
+
pass # pragma: no cover
|
70
|
+
|
71
|
+
def handle_request(self, request: Request):
|
72
|
+
"""
|
73
|
+
Handle the incoming request.
|
74
|
+
:param request: The request object.
|
75
|
+
:return: The response object.
|
76
|
+
"""
|
77
|
+
self.process_request(request)
|
78
|
+
response = self.app.handle_request(request)
|
79
|
+
self.process_response(request, response)
|
80
|
+
|
81
|
+
return response
|
plinx/response.py
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
import json
|
2
|
+
from typing import Iterable
|
3
|
+
from wsgiref.types import StartResponse, WSGIEnvironment
|
4
|
+
|
5
|
+
from webob import Response as WebObResponse
|
6
|
+
|
7
|
+
|
8
|
+
class PlinxResponse:
|
9
|
+
def __init__(self):
|
10
|
+
self.json = None
|
11
|
+
self.text = None
|
12
|
+
self.content_type = None
|
13
|
+
self.body = b""
|
14
|
+
self.status_code = 200
|
15
|
+
self.headers = {}
|
16
|
+
|
17
|
+
def __call__(
|
18
|
+
self,
|
19
|
+
environ: WSGIEnvironment,
|
20
|
+
start_response: StartResponse,
|
21
|
+
) -> Iterable[bytes]:
|
22
|
+
"""
|
23
|
+
Entrypoint for the WSGI application.
|
24
|
+
:param environ: The WSGI environment.
|
25
|
+
:param start_response: The WSGI callable.
|
26
|
+
:return: The response body produced by the middleware.
|
27
|
+
"""
|
28
|
+
|
29
|
+
self.set_body_and_content_type()
|
30
|
+
|
31
|
+
response = WebObResponse(
|
32
|
+
body=self.body,
|
33
|
+
content_type=self.content_type,
|
34
|
+
status=self.status_code,
|
35
|
+
headers=self.headers,
|
36
|
+
)
|
37
|
+
return response(environ, start_response)
|
38
|
+
|
39
|
+
def set_body_and_content_type(self):
|
40
|
+
if self.json is not None:
|
41
|
+
self.body = json.dumps(self.json).encode("UTF-8")
|
42
|
+
self.content_type = "application/json"
|
43
|
+
elif self.text is not None:
|
44
|
+
self.body = self.text.encode("utf-8") if isinstance(self.text, str) else self.text
|
45
|
+
self.content_type = "text/plain"
|
46
|
+
|
47
|
+
if self.content_type is not None:
|
48
|
+
self.headers["Content-Type"] = self.content_type
|
plinx/status_codes.py
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
|
3
|
+
|
4
|
+
class StatusCodes(Enum):
|
5
|
+
"""Status codes for the API."""
|
6
|
+
|
7
|
+
# 2XX
|
8
|
+
OK = 200
|
9
|
+
CREATED = 201
|
10
|
+
ACCEPTED = 202
|
11
|
+
NO_CONTENT = 204
|
12
|
+
|
13
|
+
# 3XX
|
14
|
+
NOT_MODIFIED = 304
|
15
|
+
|
16
|
+
# 4XX
|
17
|
+
BAD_REQUEST = 400
|
18
|
+
UNAUTHORIZED = 401
|
19
|
+
FORBIDDEN = 403
|
20
|
+
NOT_FOUND = 404
|
21
|
+
METHOD_NOT_ALLOWED = 405
|
22
|
+
NOT_ACCEPTABLE = 406
|
23
|
+
CONFLICT = 409
|
24
|
+
GONE = 410
|
25
|
+
PRECONDITION_FAILED = 412
|
26
|
+
UNSUPPORTED_MEDIA_TYPE = 415
|
27
|
+
TOO_MANY_REQUESTS = 429
|
28
|
+
UNAVAILABLE_FOR_LEGAL_REASONS = 451
|
29
|
+
|
30
|
+
# 5XX
|
31
|
+
INTERNAL_SERVER_ERROR = 500
|
32
|
+
NOT_IMPLEMENTED = 501
|
33
|
+
BAD_GATEWAY = 502
|
34
|
+
SERVICE_UNAVAILABLE = 503
|
35
|
+
GATEWAY_TIMEOUT = 504
|
36
|
+
HTTP_VERSION_NOT_SUPPORTED = 505
|
plinx/utils.py
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
from webob import Response
|
2
|
+
|
3
|
+
from plinx.status_codes import StatusCodes
|
4
|
+
|
5
|
+
|
6
|
+
def handle_404(response: Response) -> None:
|
7
|
+
"""
|
8
|
+
Set the response status code to 404 and return None.
|
9
|
+
:param response:
|
10
|
+
:return:
|
11
|
+
"""
|
12
|
+
response.status_code = StatusCodes.NOT_FOUND.value
|
13
|
+
response.text = StatusCodes.NOT_FOUND.name.replace("_", " ").title()
|