vialframe 0.1.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.
- vialframe-0.1.0/LICENSE +21 -0
- vialframe-0.1.0/PKG-INFO +82 -0
- vialframe-0.1.0/README.md +47 -0
- vialframe-0.1.0/setup.cfg +4 -0
- vialframe-0.1.0/setup.py +135 -0
- vialframe-0.1.0/vial/__init__.py +0 -0
- vialframe-0.1.0/vial/app.py +103 -0
- vialframe-0.1.0/vial/middleware.py +19 -0
- vialframe-0.1.0/vial/response.py +35 -0
- vialframe-0.1.0/vialframe.egg-info/PKG-INFO +82 -0
- vialframe-0.1.0/vialframe.egg-info/SOURCES.txt +12 -0
- vialframe-0.1.0/vialframe.egg-info/dependency_links.txt +1 -0
- vialframe-0.1.0/vialframe.egg-info/requires.txt +5 -0
- vialframe-0.1.0/vialframe.egg-info/top_level.txt +1 -0
vialframe-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abdullokh Alimov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
vialframe-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vialframe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A simple Python Web Framework for building web applications.
|
|
5
|
+
Home-page: https://github.com/Alimov-8/vial
|
|
6
|
+
Author: Abdullokh Alimov
|
|
7
|
+
Author-email: abdullokh@alimov.io
|
|
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.6
|
|
13
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
14
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
15
|
+
Requires-Python: >=3.13.2
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: webob
|
|
19
|
+
Requires-Dist: parse
|
|
20
|
+
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: Jinja2
|
|
22
|
+
Requires-Dist: whitenoise
|
|
23
|
+
Dynamic: author
|
|
24
|
+
Dynamic: author-email
|
|
25
|
+
Dynamic: classifier
|
|
26
|
+
Dynamic: description
|
|
27
|
+
Dynamic: description-content-type
|
|
28
|
+
Dynamic: home-page
|
|
29
|
+
Dynamic: license
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
Dynamic: requires-dist
|
|
32
|
+
Dynamic: requires-python
|
|
33
|
+
Dynamic: summary
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# 🧪 Vial
|
|
37
|
+
A simple Python Web Framework for building web applications.
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
**WSGI** (Web Server Gateway Interface) is simply a bridge or translator that lets Python web applications talk to web servers ([PEP 3333](https://peps.python.org/pep-3333/)).
|
|
44
|
+
In Python web development, we need WSGI because Python web applications cannot natively talk to traditional web servers like Nginx, Apache, or IIS.
|
|
45
|
+
Without a standard interface like WSGI, every web framework would need custom code for every web server, creating severe ecosystem fragmentation.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
**How WSGI Works in Vial?** When a user sends an HTTP request to application:
|
|
50
|
+
1. The WSGI Server receives the incoming HTTP request.
|
|
51
|
+
2. The server constructs the environ dictionary and calls Vial application instance.
|
|
52
|
+
3. Vial parses the environ data, routes the request to the matching controller or handler, processes view logic, and prepares a response.
|
|
53
|
+
4. Vial invokes start_response(status, headers) and returns the response body as an iterable (typically a list of bytes).
|
|
54
|
+
5. The WSGI Server formats the raw HTTP response and sends it back to the client's browser.
|
|
55
|
+
|
|
56
|
+
### Steps for Building Framework
|
|
57
|
+
1. Setup WSGI
|
|
58
|
+
2. Add request and response
|
|
59
|
+
3. Add routing
|
|
60
|
+
4. Add function-based and class-based handlers.
|
|
61
|
+
5. Add unit tests
|
|
62
|
+
6. Add Django style routing
|
|
63
|
+
7. Add templates
|
|
64
|
+
8. Add custom exception handler
|
|
65
|
+
9. Add static files
|
|
66
|
+
10. Add middlewares
|
|
67
|
+
11. Add allowed methods to function based handlers
|
|
68
|
+
12. Add custom responses for webob
|
|
69
|
+
13. Upload to PyPi
|
|
70
|
+
|
|
71
|
+
#### Resources
|
|
72
|
+
- [PEP 3333 – Python Web Server Gateway Interface](https://peps.python.org/pep-3333/)
|
|
73
|
+
- [WebOb WSGI request and response objects](https://webob.org/)
|
|
74
|
+
- [Parse strings using a specification based on the Python format() syntax.](https://pypi.org/project/parse/)
|
|
75
|
+
- [Pytest: helps you write better programs](https://docs.pytest.org/en/stable/)
|
|
76
|
+
- [Requests: HTTP for Humans](https://requests.readthedocs.io/en/latest/)
|
|
77
|
+
- [Requests for Unit Tests](https://github.com/seanbrant/requests-wsgi-adapter)
|
|
78
|
+
- [Pytest coverage](https://pypi.org/project/pytest-cov/)
|
|
79
|
+
- [Jinja is a fast, expressive, extensible templating engine.](https://jinja.palletsprojects.com/en/stable/)
|
|
80
|
+
- [Radically simplified static file serving for Python web apps](https://whitenoise.readthedocs.io/en/latest/)
|
|
81
|
+
- [Setup.py for Humans](https://github.com/navdeep-G/setup.py)
|
|
82
|
+
- [Twine is a utility for publishing Python packages to PyPI](https://twine.readthedocs.io/en/latest/)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# 🧪 Vial
|
|
2
|
+
A simple Python Web Framework for building web applications.
|
|
3
|
+
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
**WSGI** (Web Server Gateway Interface) is simply a bridge or translator that lets Python web applications talk to web servers ([PEP 3333](https://peps.python.org/pep-3333/)).
|
|
9
|
+
In Python web development, we need WSGI because Python web applications cannot natively talk to traditional web servers like Nginx, Apache, or IIS.
|
|
10
|
+
Without a standard interface like WSGI, every web framework would need custom code for every web server, creating severe ecosystem fragmentation.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
**How WSGI Works in Vial?** When a user sends an HTTP request to application:
|
|
15
|
+
1. The WSGI Server receives the incoming HTTP request.
|
|
16
|
+
2. The server constructs the environ dictionary and calls Vial application instance.
|
|
17
|
+
3. Vial parses the environ data, routes the request to the matching controller or handler, processes view logic, and prepares a response.
|
|
18
|
+
4. Vial invokes start_response(status, headers) and returns the response body as an iterable (typically a list of bytes).
|
|
19
|
+
5. The WSGI Server formats the raw HTTP response and sends it back to the client's browser.
|
|
20
|
+
|
|
21
|
+
### Steps for Building Framework
|
|
22
|
+
1. Setup WSGI
|
|
23
|
+
2. Add request and response
|
|
24
|
+
3. Add routing
|
|
25
|
+
4. Add function-based and class-based handlers.
|
|
26
|
+
5. Add unit tests
|
|
27
|
+
6. Add Django style routing
|
|
28
|
+
7. Add templates
|
|
29
|
+
8. Add custom exception handler
|
|
30
|
+
9. Add static files
|
|
31
|
+
10. Add middlewares
|
|
32
|
+
11. Add allowed methods to function based handlers
|
|
33
|
+
12. Add custom responses for webob
|
|
34
|
+
13. Upload to PyPi
|
|
35
|
+
|
|
36
|
+
#### Resources
|
|
37
|
+
- [PEP 3333 – Python Web Server Gateway Interface](https://peps.python.org/pep-3333/)
|
|
38
|
+
- [WebOb WSGI request and response objects](https://webob.org/)
|
|
39
|
+
- [Parse strings using a specification based on the Python format() syntax.](https://pypi.org/project/parse/)
|
|
40
|
+
- [Pytest: helps you write better programs](https://docs.pytest.org/en/stable/)
|
|
41
|
+
- [Requests: HTTP for Humans](https://requests.readthedocs.io/en/latest/)
|
|
42
|
+
- [Requests for Unit Tests](https://github.com/seanbrant/requests-wsgi-adapter)
|
|
43
|
+
- [Pytest coverage](https://pypi.org/project/pytest-cov/)
|
|
44
|
+
- [Jinja is a fast, expressive, extensible templating engine.](https://jinja.palletsprojects.com/en/stable/)
|
|
45
|
+
- [Radically simplified static file serving for Python web apps](https://whitenoise.readthedocs.io/en/latest/)
|
|
46
|
+
- [Setup.py for Humans](https://github.com/navdeep-G/setup.py)
|
|
47
|
+
- [Twine is a utility for publishing Python packages to PyPI](https://twine.readthedocs.io/en/latest/)
|
vialframe-0.1.0/setup.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# Note: To use the 'upload' functionality of this file, you must:
|
|
5
|
+
# $ pipenv install twine --dev
|
|
6
|
+
|
|
7
|
+
import io
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
from shutil import rmtree
|
|
11
|
+
|
|
12
|
+
from setuptools import find_packages, setup, Command
|
|
13
|
+
|
|
14
|
+
NAME = 'vialframe'
|
|
15
|
+
DESCRIPTION = 'A simple Python Web Framework for building web applications.'
|
|
16
|
+
URL = 'https://github.com/Alimov-8/vial'
|
|
17
|
+
EMAIL = 'abdullokh@alimov.io'
|
|
18
|
+
AUTHOR = 'Abdullokh Alimov'
|
|
19
|
+
REQUIRES_PYTHON = '>=3.13.2'
|
|
20
|
+
VERSION = '0.1.0'
|
|
21
|
+
|
|
22
|
+
# What packages are required for this module to be executed?
|
|
23
|
+
REQUIRED = [
|
|
24
|
+
'webob',
|
|
25
|
+
'parse',
|
|
26
|
+
'requests',
|
|
27
|
+
'Jinja2',
|
|
28
|
+
'whitenoise',
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# What packages are optional?
|
|
33
|
+
EXTRAS = {
|
|
34
|
+
# 'fancy feature': ['django'],
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# The rest you shouldn't have to touch too much :)
|
|
38
|
+
# ------------------------------------------------
|
|
39
|
+
# Except, perhaps the License and Trove Classifiers!
|
|
40
|
+
# If you do change the License, remember to change the Trove Classifier for that!
|
|
41
|
+
|
|
42
|
+
here = os.path.abspath(os.path.dirname(__file__))
|
|
43
|
+
|
|
44
|
+
# Import the README and use it as the long-description.
|
|
45
|
+
# Note: this will only work if 'README.md' is present in your MANIFEST.in file!
|
|
46
|
+
try:
|
|
47
|
+
with io.open(os.path.join(here, 'README.md'), encoding='utf-8') as f:
|
|
48
|
+
long_description = '\n' + f.read()
|
|
49
|
+
except FileNotFoundError:
|
|
50
|
+
long_description = DESCRIPTION
|
|
51
|
+
|
|
52
|
+
# Load the package's __version__.py module as a dictionary.
|
|
53
|
+
about = {}
|
|
54
|
+
if not VERSION:
|
|
55
|
+
project_slug = NAME.lower().replace("-", "_").replace(" ", "_")
|
|
56
|
+
with open(os.path.join(here, project_slug, '__version__.py')) as f:
|
|
57
|
+
exec(f.read(), about)
|
|
58
|
+
else:
|
|
59
|
+
about['__version__'] = VERSION
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class UploadCommand(Command):
|
|
63
|
+
"""Support setup.py upload."""
|
|
64
|
+
|
|
65
|
+
description = 'Build and publish the package.'
|
|
66
|
+
user_options = []
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def status(s):
|
|
70
|
+
"""Prints things in bold."""
|
|
71
|
+
print('\033[1m{0}\033[0m'.format(s))
|
|
72
|
+
|
|
73
|
+
def initialize_options(self):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
def finalize_options(self):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
def run(self):
|
|
80
|
+
try:
|
|
81
|
+
self.status('Removing previous builds…')
|
|
82
|
+
rmtree(os.path.join(here, 'dist'))
|
|
83
|
+
except OSError:
|
|
84
|
+
pass
|
|
85
|
+
|
|
86
|
+
self.status('Building Source and Wheel (universal) distribution…')
|
|
87
|
+
os.system('{0} setup.py sdist bdist_wheel --universal'.format(sys.executable))
|
|
88
|
+
|
|
89
|
+
self.status('Uploading the package to PyPI via Twine…')
|
|
90
|
+
os.system('twine upload dist/*')
|
|
91
|
+
|
|
92
|
+
self.status('Pushing git tags…')
|
|
93
|
+
os.system('git tag v{0}'.format(about['__version__']))
|
|
94
|
+
os.system('git push --tags')
|
|
95
|
+
|
|
96
|
+
sys.exit()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# Where the magic happens:
|
|
100
|
+
setup(
|
|
101
|
+
name=NAME,
|
|
102
|
+
version=about['__version__'],
|
|
103
|
+
description=DESCRIPTION,
|
|
104
|
+
long_description=long_description,
|
|
105
|
+
long_description_content_type='text/markdown',
|
|
106
|
+
author=AUTHOR,
|
|
107
|
+
author_email=EMAIL,
|
|
108
|
+
python_requires=REQUIRES_PYTHON,
|
|
109
|
+
url=URL,
|
|
110
|
+
packages=find_packages(exclude=["tests", "*.tests", "*.tests.*", "tests.*"]),
|
|
111
|
+
# If your package is a single module, use this instead of 'packages':
|
|
112
|
+
# py_modules=['mypackage'],
|
|
113
|
+
|
|
114
|
+
# entry_points={
|
|
115
|
+
# 'console_scripts': ['mycli=mymodule:cli'],
|
|
116
|
+
# },
|
|
117
|
+
install_requires=REQUIRED,
|
|
118
|
+
extras_require=EXTRAS,
|
|
119
|
+
include_package_data=True,
|
|
120
|
+
license='MIT',
|
|
121
|
+
classifiers=[
|
|
122
|
+
# Trove classifiers
|
|
123
|
+
# Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
|
124
|
+
'License :: OSI Approved :: MIT License',
|
|
125
|
+
'Programming Language :: Python',
|
|
126
|
+
'Programming Language :: Python :: 3',
|
|
127
|
+
'Programming Language :: Python :: 3.6',
|
|
128
|
+
'Programming Language :: Python :: Implementation :: CPython',
|
|
129
|
+
'Programming Language :: Python :: Implementation :: PyPy'
|
|
130
|
+
],
|
|
131
|
+
# $ setup.py publish support.
|
|
132
|
+
cmdclass={
|
|
133
|
+
'upload': UploadCommand,
|
|
134
|
+
},
|
|
135
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import inspect
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from webob import Request
|
|
6
|
+
from parse import parse
|
|
7
|
+
from wsgiadapter import WSGIAdapter
|
|
8
|
+
from jinja2 import Environment, FileSystemLoader
|
|
9
|
+
from whitenoise import WhiteNoise
|
|
10
|
+
|
|
11
|
+
from .middleware import Middleware
|
|
12
|
+
from .response import Response
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Vial:
|
|
16
|
+
def __init__(self, templates_dir="templates", static_dir="static"):
|
|
17
|
+
self.routes = dict()
|
|
18
|
+
self.exception_handler = None
|
|
19
|
+
self.template_env = Environment(
|
|
20
|
+
loader=FileSystemLoader(os.path.abspath(templates_dir))
|
|
21
|
+
)
|
|
22
|
+
self.whitenoise = WhiteNoise(self.wsgi_app, root=static_dir)
|
|
23
|
+
self.middleware = Middleware(self)
|
|
24
|
+
|
|
25
|
+
def __call__(self, environ, start_response):
|
|
26
|
+
return self.whitenoise(environ, start_response)
|
|
27
|
+
|
|
28
|
+
def wsgi_app(self, environ, start_response):
|
|
29
|
+
request = Request(environ)
|
|
30
|
+
response = self.middleware.handle_request(request)
|
|
31
|
+
return response(environ, start_response)
|
|
32
|
+
|
|
33
|
+
def handle_request(self, request):
|
|
34
|
+
response = Response()
|
|
35
|
+
handler_data, kwargs = self.find_handler(request)
|
|
36
|
+
|
|
37
|
+
if handler_data is None:
|
|
38
|
+
self.default_response(response)
|
|
39
|
+
return response
|
|
40
|
+
|
|
41
|
+
handler = handler_data["handler"]
|
|
42
|
+
allowed_methods = handler_data["allowed_methods"]
|
|
43
|
+
|
|
44
|
+
if inspect.isclass(handler):
|
|
45
|
+
handler = getattr(handler(), request.method.lower(), None)
|
|
46
|
+
|
|
47
|
+
if handler is None or request.method.lower() not in allowed_methods:
|
|
48
|
+
self.method_not_allowed(response)
|
|
49
|
+
return response
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
handler(request, response, **kwargs)
|
|
53
|
+
except Exception as e:
|
|
54
|
+
if self.exception_handler is None:
|
|
55
|
+
raise e
|
|
56
|
+
self.exception_handler(request, response, e)
|
|
57
|
+
|
|
58
|
+
return response
|
|
59
|
+
|
|
60
|
+
def find_handler(self, request):
|
|
61
|
+
for path, handler_data in self.routes.items():
|
|
62
|
+
parsed_result = parse(path, request.path)
|
|
63
|
+
if parsed_result is not None:
|
|
64
|
+
return handler_data, parsed_result.named
|
|
65
|
+
|
|
66
|
+
return None, None
|
|
67
|
+
|
|
68
|
+
def default_response(self, response):
|
|
69
|
+
response.status_code = 404
|
|
70
|
+
response.text = "Not Found."
|
|
71
|
+
|
|
72
|
+
def method_not_allowed(self, response):
|
|
73
|
+
response.status_code = 405
|
|
74
|
+
response.text = "Method Not Allowed"
|
|
75
|
+
|
|
76
|
+
def add_route(self, path, handler, allowed_methods=None):
|
|
77
|
+
assert path not in self.routes, "Duplicate route. Please change URL."
|
|
78
|
+
if allowed_methods is None:
|
|
79
|
+
allowed_methods = ["get", "post", "put", "patch", "delete", "head", "options", "connect", "trace"]
|
|
80
|
+
|
|
81
|
+
self.routes[path] = {"handler": handler, "allowed_methods": allowed_methods}
|
|
82
|
+
|
|
83
|
+
def route(self, path, allowed_methods=None):
|
|
84
|
+
def wrapper(handler):
|
|
85
|
+
self.add_route(path, handler, allowed_methods)
|
|
86
|
+
return handler
|
|
87
|
+
|
|
88
|
+
return wrapper
|
|
89
|
+
|
|
90
|
+
def template(self, template_name, context=None):
|
|
91
|
+
if context is None: context = dict()
|
|
92
|
+
return self.template_env.get_template(template_name).render(**context)
|
|
93
|
+
|
|
94
|
+
def add_exception_handler(self, exception_handler):
|
|
95
|
+
self.exception_handler = exception_handler
|
|
96
|
+
|
|
97
|
+
def add_middleware(self, middleware_cls):
|
|
98
|
+
self.middleware.add(middleware_cls)
|
|
99
|
+
|
|
100
|
+
def test_session(self):
|
|
101
|
+
session = requests.Session()
|
|
102
|
+
session.mount('http://testserver', WSGIAdapter(self))
|
|
103
|
+
return session
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
class Middleware:
|
|
2
|
+
def __init__(self, app):
|
|
3
|
+
self.app = app
|
|
4
|
+
|
|
5
|
+
def add(self, middleware_class):
|
|
6
|
+
self.app = middleware_class(self.app)
|
|
7
|
+
|
|
8
|
+
def process_request(self, request):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
def process_response(self, request, response):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
def handle_request(self, request):
|
|
15
|
+
self.process_request(request)
|
|
16
|
+
response = self.app.handle_request(request)
|
|
17
|
+
self.process_response(request, response)
|
|
18
|
+
|
|
19
|
+
return response
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
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
|
+
|
|
15
|
+
def set_body_and_content_type(self):
|
|
16
|
+
if self.json is not None:
|
|
17
|
+
self.body = json.dumps(self.json).encode()
|
|
18
|
+
self.content_type = "application/json"
|
|
19
|
+
|
|
20
|
+
if self.html is not None:
|
|
21
|
+
self.body = self.html.encode()
|
|
22
|
+
self.content_type = "text/html"
|
|
23
|
+
|
|
24
|
+
if self.text is not None:
|
|
25
|
+
self.body = self.text
|
|
26
|
+
self.content_type = "text/plain"
|
|
27
|
+
|
|
28
|
+
def __call__(self, environ, start_response):
|
|
29
|
+
self.set_body_and_content_type()
|
|
30
|
+
|
|
31
|
+
response = WebobResponse(
|
|
32
|
+
body=self.body, content_type=self.content_type, status=self.status_code,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return response(environ, start_response)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vialframe
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A simple Python Web Framework for building web applications.
|
|
5
|
+
Home-page: https://github.com/Alimov-8/vial
|
|
6
|
+
Author: Abdullokh Alimov
|
|
7
|
+
Author-email: abdullokh@alimov.io
|
|
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.6
|
|
13
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
14
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
15
|
+
Requires-Python: >=3.13.2
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Requires-Dist: webob
|
|
19
|
+
Requires-Dist: parse
|
|
20
|
+
Requires-Dist: requests
|
|
21
|
+
Requires-Dist: Jinja2
|
|
22
|
+
Requires-Dist: whitenoise
|
|
23
|
+
Dynamic: author
|
|
24
|
+
Dynamic: author-email
|
|
25
|
+
Dynamic: classifier
|
|
26
|
+
Dynamic: description
|
|
27
|
+
Dynamic: description-content-type
|
|
28
|
+
Dynamic: home-page
|
|
29
|
+
Dynamic: license
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
Dynamic: requires-dist
|
|
32
|
+
Dynamic: requires-python
|
|
33
|
+
Dynamic: summary
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# 🧪 Vial
|
|
37
|
+
A simple Python Web Framework for building web applications.
|
|
38
|
+
|
|
39
|
+

|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
**WSGI** (Web Server Gateway Interface) is simply a bridge or translator that lets Python web applications talk to web servers ([PEP 3333](https://peps.python.org/pep-3333/)).
|
|
44
|
+
In Python web development, we need WSGI because Python web applications cannot natively talk to traditional web servers like Nginx, Apache, or IIS.
|
|
45
|
+
Without a standard interface like WSGI, every web framework would need custom code for every web server, creating severe ecosystem fragmentation.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
**How WSGI Works in Vial?** When a user sends an HTTP request to application:
|
|
50
|
+
1. The WSGI Server receives the incoming HTTP request.
|
|
51
|
+
2. The server constructs the environ dictionary and calls Vial application instance.
|
|
52
|
+
3. Vial parses the environ data, routes the request to the matching controller or handler, processes view logic, and prepares a response.
|
|
53
|
+
4. Vial invokes start_response(status, headers) and returns the response body as an iterable (typically a list of bytes).
|
|
54
|
+
5. The WSGI Server formats the raw HTTP response and sends it back to the client's browser.
|
|
55
|
+
|
|
56
|
+
### Steps for Building Framework
|
|
57
|
+
1. Setup WSGI
|
|
58
|
+
2. Add request and response
|
|
59
|
+
3. Add routing
|
|
60
|
+
4. Add function-based and class-based handlers.
|
|
61
|
+
5. Add unit tests
|
|
62
|
+
6. Add Django style routing
|
|
63
|
+
7. Add templates
|
|
64
|
+
8. Add custom exception handler
|
|
65
|
+
9. Add static files
|
|
66
|
+
10. Add middlewares
|
|
67
|
+
11. Add allowed methods to function based handlers
|
|
68
|
+
12. Add custom responses for webob
|
|
69
|
+
13. Upload to PyPi
|
|
70
|
+
|
|
71
|
+
#### Resources
|
|
72
|
+
- [PEP 3333 – Python Web Server Gateway Interface](https://peps.python.org/pep-3333/)
|
|
73
|
+
- [WebOb WSGI request and response objects](https://webob.org/)
|
|
74
|
+
- [Parse strings using a specification based on the Python format() syntax.](https://pypi.org/project/parse/)
|
|
75
|
+
- [Pytest: helps you write better programs](https://docs.pytest.org/en/stable/)
|
|
76
|
+
- [Requests: HTTP for Humans](https://requests.readthedocs.io/en/latest/)
|
|
77
|
+
- [Requests for Unit Tests](https://github.com/seanbrant/requests-wsgi-adapter)
|
|
78
|
+
- [Pytest coverage](https://pypi.org/project/pytest-cov/)
|
|
79
|
+
- [Jinja is a fast, expressive, extensible templating engine.](https://jinja.palletsprojects.com/en/stable/)
|
|
80
|
+
- [Radically simplified static file serving for Python web apps](https://whitenoise.readthedocs.io/en/latest/)
|
|
81
|
+
- [Setup.py for Humans](https://github.com/navdeep-G/setup.py)
|
|
82
|
+
- [Twine is a utility for publishing Python packages to PyPI](https://twine.readthedocs.io/en/latest/)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
setup.py
|
|
4
|
+
vial/__init__.py
|
|
5
|
+
vial/app.py
|
|
6
|
+
vial/middleware.py
|
|
7
|
+
vial/response.py
|
|
8
|
+
vialframe.egg-info/PKG-INFO
|
|
9
|
+
vialframe.egg-info/SOURCES.txt
|
|
10
|
+
vialframe.egg-info/dependency_links.txt
|
|
11
|
+
vialframe.egg-info/requires.txt
|
|
12
|
+
vialframe.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
vial
|