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.
@@ -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.
@@ -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
+ ![img.png](img.png)
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
+ ![img.png](img.png)
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/)
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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
+ ![img.png](img.png)
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,5 @@
1
+ webob
2
+ parse
3
+ requests
4
+ Jinja2
5
+ whitenoise
@@ -0,0 +1 @@
1
+ vial