bwsgi 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.
bwsgi-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Your Name
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,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include vinod *.py py.typed
bwsgi-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: bwsgi
3
+ Version: 0.1.0
4
+ Summary: Ultra-lightweight, zero-dependency Python WSGI framework
5
+ Author-email: Baraa Ahmed <baraa.runtime@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/BaraaByte/bwsgi
8
+ Project-URL: Repository, https://github.com/BaraaByte/bwsgi
9
+ Project-URL: Issues, https://github.com/BaraaByte/bwsgi/issues
10
+ Keywords: wsgi,framework,zero-dependency,serv00,freebsd,flask-like
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: POSIX :: BSD :: FreeBSD
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
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 :: Application
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: license-file
25
+
26
+ # Vinod
27
+
28
+ Ultra-lightweight, **zero-dependency** Python WSGI framework.
29
+ Flask-style routing. Django-style auth. Built on the Python Standard Library.
30
+
31
+ ## Why Vinod?
32
+
33
+ - **Zero dependencies** — pure Python stdlib
34
+ - **~5-8 MB idle RAM** — runs happily on 512MB hosts like Serv00
35
+ - **Flask-style routing** — decorator-based, familiar syntax
36
+ - **Secure auth built in** — PBKDF2 password hashing + DB-backed sessions
37
+ - **WSGI native** — works with Passenger, Gunicorn, uWSGI
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install vinod
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```python
48
+ from vinod import Vinod, Response, login_required
49
+
50
+ app = Vinod(db_path="app.db")
51
+
52
+ @app.route("/")
53
+ def home(req):
54
+ return Response.html("<h1>Hello from Vinod</h1>")
55
+
56
+ @app.route("/dashboard")
57
+ @login_required
58
+ def dashboard(req):
59
+ return Response.html(f"<h1>Hi {req.user['username']}</h1>")
60
+
61
+ if __name__ == "__main__":
62
+ app.run()
63
+ ```
64
+
65
+ ## Routing and responses
66
+
67
+ Routes accept named path parameters and one or more HTTP methods:
68
+
69
+ ```python
70
+ @app.route("/users/<username>", methods=("GET",))
71
+ def user(req, username):
72
+ return Response.json({"username": username})
73
+
74
+ profile_url = app.url_for("user", username="Jane Doe")
75
+ ```
76
+
77
+ Path values generated by `url_for` are URL-encoded. Requests to a known path
78
+ with an unsupported method receive `405 Method Not Allowed` and an `Allow`
79
+ header. Static files are served from `static/` at `/static/`; files are
80
+ streamed in chunks and paths are checked against the configured static root.
81
+
82
+ `Request` provides `query`, `headers`, `cookies`, `body`, `form()`, and `json()`.
83
+ Malformed JSON request bodies receive `400 Bad Request`.
84
+ `Response` provides `text()`, `html()`, `json()`, `redirect()`, and `file()`.
85
+
86
+ ## JSON request validation
87
+
88
+ Use `Request.validate_json()` to require a JSON object with specific required
89
+ fields and Python types. Malformed JSON and invalid values return a JSON
90
+ `400 Bad Request` response:
91
+
92
+ ```python
93
+ @app.route("/api/items", methods=("POST",))
94
+ def create_item(req):
95
+ item = req.validate_json({"name": str, "quantity": int})
96
+ return Response.json(item, "201 Created")
97
+ ```
98
+
99
+ ## Sessions and CSRF
100
+
101
+ `req.session` is a server-side dictionary persisted in SQLite and identified by
102
+ a signed, HTTP-only cookie. Set a stable secret key when creating the app so
103
+ session cookies survive process restarts:
104
+
105
+ ```python
106
+ app = Vinod(db_path="app.db", secret_key="load-this-from-your-environment")
107
+ ```
108
+
109
+ Protect state-changing routes with `@csrf_protect`. Render `req.csrf_token()`
110
+ into a hidden form field named `_csrf_token`, or send it in the
111
+ `X-Csrf-Token` header for JSON requests:
112
+
113
+ ```python
114
+ @app.route("/submit", methods=("POST",))
115
+ @csrf_protect
116
+ def submit(req):
117
+ req.session["submitted"] = True
118
+ return Response.json({"ok": True})
119
+ ```
120
+
121
+ Safe methods (`GET`, `HEAD`, `OPTIONS`, and `TRACE`) do not require a token.
122
+ Keep state-changing actions on non-safe methods and use CSRF protection for
123
+ browser-session routes.
124
+
125
+ ## Templates, middleware, and errors
126
+
127
+ Templates are UTF-8 files under `templates/` by default. `{{ name }}` values
128
+ are HTML-escaped automatically; this intentionally supports variable
129
+ substitution only, not template logic:
130
+
131
+ ```html
132
+ <h1>Hello, {{ username }}</h1>
133
+ ```
134
+
135
+ ```python
136
+ return app.render_template("hello.html", username="Ada")
137
+ ```
138
+
139
+ Register standard WSGI middleware with a factory that accepts and returns a
140
+ WSGI callable:
141
+
142
+ ```python
143
+ def request_id_middleware(next_app):
144
+ def middleware(environ, start_response):
145
+ environ["HTTP_X_REQUEST_ID"] = "example"
146
+ return next_app(environ, start_response)
147
+ return middleware
148
+
149
+ app.add_middleware(request_id_middleware)
150
+ ```
151
+
152
+ Error handlers may be registered for HTTP status codes or exception classes:
153
+
154
+ ```python
155
+ @app.errorhandler(ValueError)
156
+ def invalid_value(req):
157
+ return Response.json({"error": "invalid value"}, "400 Bad Request")
158
+ ```
159
+
160
+ ## License
161
+
162
+ MIT
bwsgi-0.1.0/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # Vinod
2
+
3
+ Ultra-lightweight, **zero-dependency** Python WSGI framework.
4
+ Flask-style routing. Django-style auth. Built on the Python Standard Library.
5
+
6
+ ## Why Vinod?
7
+
8
+ - **Zero dependencies** — pure Python stdlib
9
+ - **~5-8 MB idle RAM** — runs happily on 512MB hosts like Serv00
10
+ - **Flask-style routing** — decorator-based, familiar syntax
11
+ - **Secure auth built in** — PBKDF2 password hashing + DB-backed sessions
12
+ - **WSGI native** — works with Passenger, Gunicorn, uWSGI
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ pip install vinod
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```python
23
+ from vinod import Vinod, Response, login_required
24
+
25
+ app = Vinod(db_path="app.db")
26
+
27
+ @app.route("/")
28
+ def home(req):
29
+ return Response.html("<h1>Hello from Vinod</h1>")
30
+
31
+ @app.route("/dashboard")
32
+ @login_required
33
+ def dashboard(req):
34
+ return Response.html(f"<h1>Hi {req.user['username']}</h1>")
35
+
36
+ if __name__ == "__main__":
37
+ app.run()
38
+ ```
39
+
40
+ ## Routing and responses
41
+
42
+ Routes accept named path parameters and one or more HTTP methods:
43
+
44
+ ```python
45
+ @app.route("/users/<username>", methods=("GET",))
46
+ def user(req, username):
47
+ return Response.json({"username": username})
48
+
49
+ profile_url = app.url_for("user", username="Jane Doe")
50
+ ```
51
+
52
+ Path values generated by `url_for` are URL-encoded. Requests to a known path
53
+ with an unsupported method receive `405 Method Not Allowed` and an `Allow`
54
+ header. Static files are served from `static/` at `/static/`; files are
55
+ streamed in chunks and paths are checked against the configured static root.
56
+
57
+ `Request` provides `query`, `headers`, `cookies`, `body`, `form()`, and `json()`.
58
+ Malformed JSON request bodies receive `400 Bad Request`.
59
+ `Response` provides `text()`, `html()`, `json()`, `redirect()`, and `file()`.
60
+
61
+ ## JSON request validation
62
+
63
+ Use `Request.validate_json()` to require a JSON object with specific required
64
+ fields and Python types. Malformed JSON and invalid values return a JSON
65
+ `400 Bad Request` response:
66
+
67
+ ```python
68
+ @app.route("/api/items", methods=("POST",))
69
+ def create_item(req):
70
+ item = req.validate_json({"name": str, "quantity": int})
71
+ return Response.json(item, "201 Created")
72
+ ```
73
+
74
+ ## Sessions and CSRF
75
+
76
+ `req.session` is a server-side dictionary persisted in SQLite and identified by
77
+ a signed, HTTP-only cookie. Set a stable secret key when creating the app so
78
+ session cookies survive process restarts:
79
+
80
+ ```python
81
+ app = Vinod(db_path="app.db", secret_key="load-this-from-your-environment")
82
+ ```
83
+
84
+ Protect state-changing routes with `@csrf_protect`. Render `req.csrf_token()`
85
+ into a hidden form field named `_csrf_token`, or send it in the
86
+ `X-Csrf-Token` header for JSON requests:
87
+
88
+ ```python
89
+ @app.route("/submit", methods=("POST",))
90
+ @csrf_protect
91
+ def submit(req):
92
+ req.session["submitted"] = True
93
+ return Response.json({"ok": True})
94
+ ```
95
+
96
+ Safe methods (`GET`, `HEAD`, `OPTIONS`, and `TRACE`) do not require a token.
97
+ Keep state-changing actions on non-safe methods and use CSRF protection for
98
+ browser-session routes.
99
+
100
+ ## Templates, middleware, and errors
101
+
102
+ Templates are UTF-8 files under `templates/` by default. `{{ name }}` values
103
+ are HTML-escaped automatically; this intentionally supports variable
104
+ substitution only, not template logic:
105
+
106
+ ```html
107
+ <h1>Hello, {{ username }}</h1>
108
+ ```
109
+
110
+ ```python
111
+ return app.render_template("hello.html", username="Ada")
112
+ ```
113
+
114
+ Register standard WSGI middleware with a factory that accepts and returns a
115
+ WSGI callable:
116
+
117
+ ```python
118
+ def request_id_middleware(next_app):
119
+ def middleware(environ, start_response):
120
+ environ["HTTP_X_REQUEST_ID"] = "example"
121
+ return next_app(environ, start_response)
122
+ return middleware
123
+
124
+ app.add_middleware(request_id_middleware)
125
+ ```
126
+
127
+ Error handlers may be registered for HTTP status codes or exception classes:
128
+
129
+ ```python
130
+ @app.errorhandler(ValueError)
131
+ def invalid_value(req):
132
+ return Response.json({"error": "invalid value"}, "400 Bad Request")
133
+ ```
134
+
135
+ ## License
136
+
137
+ MIT
@@ -0,0 +1,162 @@
1
+ Metadata-Version: 2.4
2
+ Name: bwsgi
3
+ Version: 0.1.0
4
+ Summary: Ultra-lightweight, zero-dependency Python WSGI framework
5
+ Author-email: Baraa Ahmed <baraa.runtime@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/BaraaByte/bwsgi
8
+ Project-URL: Repository, https://github.com/BaraaByte/bwsgi
9
+ Project-URL: Issues, https://github.com/BaraaByte/bwsgi/issues
10
+ Keywords: wsgi,framework,zero-dependency,serv00,freebsd,flask-like
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: POSIX :: BSD :: FreeBSD
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
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 :: Application
21
+ Requires-Python: >=3.9
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Dynamic: license-file
25
+
26
+ # Vinod
27
+
28
+ Ultra-lightweight, **zero-dependency** Python WSGI framework.
29
+ Flask-style routing. Django-style auth. Built on the Python Standard Library.
30
+
31
+ ## Why Vinod?
32
+
33
+ - **Zero dependencies** — pure Python stdlib
34
+ - **~5-8 MB idle RAM** — runs happily on 512MB hosts like Serv00
35
+ - **Flask-style routing** — decorator-based, familiar syntax
36
+ - **Secure auth built in** — PBKDF2 password hashing + DB-backed sessions
37
+ - **WSGI native** — works with Passenger, Gunicorn, uWSGI
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install vinod
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```python
48
+ from vinod import Vinod, Response, login_required
49
+
50
+ app = Vinod(db_path="app.db")
51
+
52
+ @app.route("/")
53
+ def home(req):
54
+ return Response.html("<h1>Hello from Vinod</h1>")
55
+
56
+ @app.route("/dashboard")
57
+ @login_required
58
+ def dashboard(req):
59
+ return Response.html(f"<h1>Hi {req.user['username']}</h1>")
60
+
61
+ if __name__ == "__main__":
62
+ app.run()
63
+ ```
64
+
65
+ ## Routing and responses
66
+
67
+ Routes accept named path parameters and one or more HTTP methods:
68
+
69
+ ```python
70
+ @app.route("/users/<username>", methods=("GET",))
71
+ def user(req, username):
72
+ return Response.json({"username": username})
73
+
74
+ profile_url = app.url_for("user", username="Jane Doe")
75
+ ```
76
+
77
+ Path values generated by `url_for` are URL-encoded. Requests to a known path
78
+ with an unsupported method receive `405 Method Not Allowed` and an `Allow`
79
+ header. Static files are served from `static/` at `/static/`; files are
80
+ streamed in chunks and paths are checked against the configured static root.
81
+
82
+ `Request` provides `query`, `headers`, `cookies`, `body`, `form()`, and `json()`.
83
+ Malformed JSON request bodies receive `400 Bad Request`.
84
+ `Response` provides `text()`, `html()`, `json()`, `redirect()`, and `file()`.
85
+
86
+ ## JSON request validation
87
+
88
+ Use `Request.validate_json()` to require a JSON object with specific required
89
+ fields and Python types. Malformed JSON and invalid values return a JSON
90
+ `400 Bad Request` response:
91
+
92
+ ```python
93
+ @app.route("/api/items", methods=("POST",))
94
+ def create_item(req):
95
+ item = req.validate_json({"name": str, "quantity": int})
96
+ return Response.json(item, "201 Created")
97
+ ```
98
+
99
+ ## Sessions and CSRF
100
+
101
+ `req.session` is a server-side dictionary persisted in SQLite and identified by
102
+ a signed, HTTP-only cookie. Set a stable secret key when creating the app so
103
+ session cookies survive process restarts:
104
+
105
+ ```python
106
+ app = Vinod(db_path="app.db", secret_key="load-this-from-your-environment")
107
+ ```
108
+
109
+ Protect state-changing routes with `@csrf_protect`. Render `req.csrf_token()`
110
+ into a hidden form field named `_csrf_token`, or send it in the
111
+ `X-Csrf-Token` header for JSON requests:
112
+
113
+ ```python
114
+ @app.route("/submit", methods=("POST",))
115
+ @csrf_protect
116
+ def submit(req):
117
+ req.session["submitted"] = True
118
+ return Response.json({"ok": True})
119
+ ```
120
+
121
+ Safe methods (`GET`, `HEAD`, `OPTIONS`, and `TRACE`) do not require a token.
122
+ Keep state-changing actions on non-safe methods and use CSRF protection for
123
+ browser-session routes.
124
+
125
+ ## Templates, middleware, and errors
126
+
127
+ Templates are UTF-8 files under `templates/` by default. `{{ name }}` values
128
+ are HTML-escaped automatically; this intentionally supports variable
129
+ substitution only, not template logic:
130
+
131
+ ```html
132
+ <h1>Hello, {{ username }}</h1>
133
+ ```
134
+
135
+ ```python
136
+ return app.render_template("hello.html", username="Ada")
137
+ ```
138
+
139
+ Register standard WSGI middleware with a factory that accepts and returns a
140
+ WSGI callable:
141
+
142
+ ```python
143
+ def request_id_middleware(next_app):
144
+ def middleware(environ, start_response):
145
+ environ["HTTP_X_REQUEST_ID"] = "example"
146
+ return next_app(environ, start_response)
147
+ return middleware
148
+
149
+ app.add_middleware(request_id_middleware)
150
+ ```
151
+
152
+ Error handlers may be registered for HTTP status codes or exception classes:
153
+
154
+ ```python
155
+ @app.errorhandler(ValueError)
156
+ def invalid_value(req):
157
+ return Response.json({"error": "invalid value"}, "400 Bad Request")
158
+ ```
159
+
160
+ ## License
161
+
162
+ MIT
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ MANIFEST.in
3
+ README.md
4
+ pyproject.toml
5
+ bwsgi.egg-info/PKG-INFO
6
+ bwsgi.egg-info/SOURCES.txt
7
+ bwsgi.egg-info/dependency_links.txt
8
+ bwsgi.egg-info/top_level.txt
9
+ tests/test_smoke.py
10
+ vinod/__init__.py
11
+ vinod/auth.py
12
+ vinod/core.py
13
+ vinod/db.py
14
+ vinod/decorators.py
15
+ vinod/py.typed
@@ -0,0 +1 @@
1
+ vinod
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77.0.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "bwsgi"
7
+ version = "0.1.0"
8
+ description = "Ultra-lightweight, zero-dependency Python WSGI framework"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+
12
+ # Modern PEP 639 standard configurations
13
+ license = "MIT"
14
+ license-files = ["LICENSE"]
15
+
16
+ authors = [{ name = "Baraa Ahmed", email = "baraa.runtime@gmail.com" }]
17
+ keywords = ["wsgi", "framework", "zero-dependency", "serv00", "freebsd", "flask-like"]
18
+ classifiers = [
19
+ "Development Status :: 4 - Beta",
20
+ "Intended Audience :: Developers",
21
+ "Operating System :: POSIX :: BSD :: FreeBSD",
22
+ "Operating System :: POSIX :: Linux",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.9",
25
+ "Programming Language :: Python :: 3.10",
26
+ "Programming Language :: Python :: 3.11",
27
+ "Programming Language :: Python :: 3.12",
28
+ # Removed the invalid "Framework :: WSGI" line to fix the 400 Bad Request error
29
+ "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
30
+ ]
31
+ dependencies = []
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/BaraaByte/bwsgi"
35
+ Repository = "https://github.com/BaraaByte/bwsgi"
36
+ Issues = "https://github.com/BaraaByte/bwsgi/issues"
37
+
38
+ [tool.setuptools]
39
+ packages = ["vinod"]
40
+ include-package-data = true
bwsgi-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+