MahmudCore 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.
- MahmudCore/__init__.py +0 -0
- MahmudCore/api.py +127 -0
- MahmudCore/middleware.py +27 -0
- MahmudCore/response.py +40 -0
- mahmudcore-0.0.1.dist-info/METADATA +49 -0
- mahmudcore-0.0.1.dist-info/RECORD +8 -0
- mahmudcore-0.0.1.dist-info/WHEEL +5 -0
- mahmudcore-0.0.1.dist-info/top_level.txt +1 -0
MahmudCore/__init__.py
ADDED
|
File without changes
|
MahmudCore/api.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import inspect
|
|
4
|
+
|
|
5
|
+
from parse import parse
|
|
6
|
+
from webob import Request
|
|
7
|
+
from .response import Response
|
|
8
|
+
from whitenoise import WhiteNoise
|
|
9
|
+
from requests import Session as RequestsSession
|
|
10
|
+
from jinja2 import Environment, FileSystemLoader
|
|
11
|
+
from wsgiadapter import WSGIAdapter as RequestsWSGIAdapter
|
|
12
|
+
|
|
13
|
+
from .middleware import Middleware
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class API:
|
|
17
|
+
def __init__(self, templates_dir="templates", static_dir="static"):
|
|
18
|
+
self.routes = {}
|
|
19
|
+
self.exception_handler = None
|
|
20
|
+
|
|
21
|
+
self.templates_env = Environment(
|
|
22
|
+
loader=FileSystemLoader(os.path.abspath(templates_dir))
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
self.whitenoise = WhiteNoise(self.wsgi_app, root=static_dir)
|
|
26
|
+
self.middleware = Middleware(self)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def __call__(self, environ, start_response):
|
|
30
|
+
path_info = environ["PATH_INFO"]
|
|
31
|
+
|
|
32
|
+
if path_info.startswith("/static"):
|
|
33
|
+
environ["PATH_INFO"] = path_info[len("/static"):]
|
|
34
|
+
return self.whitenoise(environ, start_response)
|
|
35
|
+
|
|
36
|
+
return self.middleware(environ, start_response)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def add_middleware(self, middleware_cls):
|
|
40
|
+
self.middleware.add(middleware_cls)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def wsgi_app(self, environ, start_response):
|
|
44
|
+
request = Request(environ)
|
|
45
|
+
response = self.handle_request(request)
|
|
46
|
+
return response(environ, start_response)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def add_route(self, path, handler, allowed_methods=None):
|
|
50
|
+
assert path not in self.routes, f"Route '{path}' already exists."
|
|
51
|
+
|
|
52
|
+
if allowed_methods is None:
|
|
53
|
+
allowed_methods = ["get", "post", "put", "patch", "delete", "options"]
|
|
54
|
+
|
|
55
|
+
self.routes[path] = {
|
|
56
|
+
"handler": handler,
|
|
57
|
+
"allowed_methods": [method.lower() for method in allowed_methods]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def route(self, path, allowed_methods=None):
|
|
62
|
+
def wrapper(handler):
|
|
63
|
+
self.add_route(path, handler, allowed_methods) # Reuse add_route logic
|
|
64
|
+
return handler
|
|
65
|
+
return wrapper
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def find_handler(self, request_path):
|
|
69
|
+
for path, handler_data in self.routes.items():
|
|
70
|
+
parse_result = parse(path, request_path)
|
|
71
|
+
if parse_result is not None:
|
|
72
|
+
return handler_data, parse_result.named
|
|
73
|
+
return None, None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def handle_request(self, request):
|
|
77
|
+
response = Response()
|
|
78
|
+
handler_data, kwargs = self.find_handler(request_path=request.path)
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
if handler_data is not None:
|
|
82
|
+
handler = handler_data["handler"]
|
|
83
|
+
allowed_methods = handler_data["allowed_methods"]
|
|
84
|
+
|
|
85
|
+
if inspect.isclass(handler):
|
|
86
|
+
# class-based handler — pick method by HTTP verb
|
|
87
|
+
handler = getattr(handler(), request.method.lower(), None)
|
|
88
|
+
if handler is None:
|
|
89
|
+
raise AttributeError("Method not allowed", request.method)
|
|
90
|
+
else:
|
|
91
|
+
# function-based handler — check allowed methods list
|
|
92
|
+
if request.method.lower() not in allowed_methods:
|
|
93
|
+
raise AttributeError("Method not allowed", request.method)
|
|
94
|
+
|
|
95
|
+
handler(request, response, **kwargs)
|
|
96
|
+
|
|
97
|
+
else:
|
|
98
|
+
self.default_response(response)
|
|
99
|
+
|
|
100
|
+
except Exception as e:
|
|
101
|
+
if self.exception_handler is None:
|
|
102
|
+
raise e
|
|
103
|
+
else:
|
|
104
|
+
self.exception_handler(request, response, e)
|
|
105
|
+
|
|
106
|
+
return response
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def default_response(self, response):
|
|
110
|
+
response.status_code = 404
|
|
111
|
+
response.text = "Request Not found"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def add_exception_handler(self, exception_handler):
|
|
115
|
+
self.exception_handler = exception_handler
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def template(self, template_name, context=None):
|
|
119
|
+
if context is None:
|
|
120
|
+
context = {}
|
|
121
|
+
return self.templates_env.get_template(template_name).render(**context)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_session(self, base_url="http://127.0.0.1:8082"):
|
|
125
|
+
session = RequestsSession()
|
|
126
|
+
session.mount(prefix=base_url, adapter=RequestsWSGIAdapter(self))
|
|
127
|
+
return session
|
MahmudCore/middleware.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
from webob import Request
|
|
3
|
+
|
|
4
|
+
class Middleware:
|
|
5
|
+
def __init__(self, app):
|
|
6
|
+
self.app = app
|
|
7
|
+
|
|
8
|
+
def __call__(self, environ, start_response):
|
|
9
|
+
request = Request(environ)
|
|
10
|
+
response = self.handle_request(request)
|
|
11
|
+
return response(environ, start_response)
|
|
12
|
+
|
|
13
|
+
def add(self, middleware_cls):
|
|
14
|
+
self.app = middleware_cls(self.app)
|
|
15
|
+
|
|
16
|
+
def process_request(self, req):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def process_response(self, req, resp):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
def handle_request(self, request):
|
|
23
|
+
self.process_request(request)
|
|
24
|
+
response = self.app.handle_request(request)
|
|
25
|
+
self.process_response(request, response)
|
|
26
|
+
|
|
27
|
+
return response
|
MahmudCore/response.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
|
|
2
|
+
import json
|
|
3
|
+
from webob import Response as WebObResponse
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Response:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.json = None
|
|
9
|
+
self.html = None
|
|
10
|
+
self.text = None
|
|
11
|
+
self.content_type = None
|
|
12
|
+
self.body = b''
|
|
13
|
+
self.status_code = 200
|
|
14
|
+
self.headers = {}
|
|
15
|
+
|
|
16
|
+
def __call__(self, environ, start_response):
|
|
17
|
+
self.set_body_and_content_type()
|
|
18
|
+
|
|
19
|
+
response = WebObResponse(
|
|
20
|
+
body=self.body, content_type=self.content_type, status=self.status_code
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Apply any custom headers
|
|
24
|
+
for key, value in self.headers.items():
|
|
25
|
+
response.headers[key] = value
|
|
26
|
+
|
|
27
|
+
return response(environ, start_response)
|
|
28
|
+
|
|
29
|
+
def set_body_and_content_type(self):
|
|
30
|
+
if self.json is not None:
|
|
31
|
+
self.body = json.dumps(self.json).encode("UTF-8")
|
|
32
|
+
self.content_type = "application/json"
|
|
33
|
+
|
|
34
|
+
if self.html is not None:
|
|
35
|
+
self.body = self.html.encode()
|
|
36
|
+
self.content_type = "text/html"
|
|
37
|
+
|
|
38
|
+
if self.text is not None:
|
|
39
|
+
self.body = self.text.encode()
|
|
40
|
+
self.content_type = "text/plain"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: MahmudCore
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: MahmudCore — a Python web framework built from scratch for learning core web framework concepts
|
|
5
|
+
Home-page: https://github.com/dear-mahmud-bd/python-framework-from-scratch
|
|
6
|
+
Author: Md. Mahmudul Hasan
|
|
7
|
+
Author-email: dearmahmud.bd@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: wsgi web framework python learning
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Education
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
23
|
+
Requires-Python: >=3.8.0
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
Requires-Dist: webob==1.8.9
|
|
26
|
+
Requires-Dist: parse==1.21.1
|
|
27
|
+
Requires-Dist: requests==2.33.1
|
|
28
|
+
Requires-Dist: requests-wsgi-adapter==0.4.1
|
|
29
|
+
Requires-Dist: Jinja2==3.1.6
|
|
30
|
+
Requires-Dist: whitenoise==6.12.0
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest; extra == "dev"
|
|
33
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
34
|
+
Requires-Dist: gunicorn; extra == "dev"
|
|
35
|
+
Requires-Dist: waitress; extra == "dev"
|
|
36
|
+
Dynamic: author
|
|
37
|
+
Dynamic: author-email
|
|
38
|
+
Dynamic: classifier
|
|
39
|
+
Dynamic: description
|
|
40
|
+
Dynamic: description-content-type
|
|
41
|
+
Dynamic: home-page
|
|
42
|
+
Dynamic: keywords
|
|
43
|
+
Dynamic: license
|
|
44
|
+
Dynamic: provides-extra
|
|
45
|
+
Dynamic: requires-dist
|
|
46
|
+
Dynamic: requires-python
|
|
47
|
+
Dynamic: summary
|
|
48
|
+
|
|
49
|
+
MahmudCore — a Python web framework built from scratch for learning core web framework concepts
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
MahmudCore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
MahmudCore/api.py,sha256=956yIUPgYuD-A1GsyNFx020lN3M8yvTFwOHC_OlZSGQ,4268
|
|
3
|
+
MahmudCore/middleware.py,sha256=NmDJkDplkoZ9GnRJQiKL755GirL9TaWg2VYYIbSbU8k,706
|
|
4
|
+
MahmudCore/response.py,sha256=DWAunMjSeMkxog5wgRS-EpmSvY_80rHxmeru6tMNnQM,1179
|
|
5
|
+
mahmudcore-0.0.1.dist-info/METADATA,sha256=TiFo8134Crih7yGnHlFj1_uoy8O7gp971xGVYWtd6gM,1883
|
|
6
|
+
mahmudcore-0.0.1.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
7
|
+
mahmudcore-0.0.1.dist-info/top_level.txt,sha256=4HnjvoEI6xLK5fd6-faX2kYdqRgKvBvKFbgPGvD7a8U,11
|
|
8
|
+
mahmudcore-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MahmudCore
|