Fynor is a lightweight WSGI web application framework.
Project description
Fynor
Fynor is a Python Web Framework built for learning purposes. The plan is to learn how frameworks are built by implementing their features, writing blog posts about them and keeping the codebase as simple as possible.
It is a WSGI framework and can be used with any WSGI application server such as Gunicorn.
Inspiration
I was inspired to make a web framework after reading Jakhongir Rakhmonov' s blog post about how he built a web framework and became an open source maintainer. He wrote about how thrilling the experience has been for him so I decided I would give it a try as well. Thank you much, Jakhongir. Go check out Alcazar by Jakhongir Rakhmonov. If you like him, show some love by staring his repos.
Quick Start
Install it:
pip install fynor
Basic Usage:
# app.py
from fynor import Fynor
app = Fynor()
@app.route("/")
def home(req, resp):
resp.text = "Hello, this is a home page."
@app.route("/about")
def about_page(req, resp):
resp.text = "Hello, this is an about page."
@app.route("/{age:d}")
def tell_age(req, resp, age):
resp.text = f"Your age is {age}"
@app.route("/{name:l}")
class GreetingHandler:
def get(self, req, resp, name):
resp.text = f"Hello, {name}"
@app.route("/show/template")
def handler_with_template(req, resp):
resp.html = app.template("example.html", context={"title": "Awesome Framework", "body": "welcome to the future!"})
@app.route("/json")
def json_handler(req, resp):
resp.json = {"this": "is JSON"}
@app.route("/custom")
def custom_response(req, resp):
resp.body = b'any other body'
resp.content_type = "text/plain"
Start:
gunicorn app:app
Handlers
If you use class based handlers, only the methods that you implement will be allowed:
@app.route("/{name:l}")
class GreetingHandler:
def get(self, req, resp, name):
resp.text = f"Hello, {name}"
This handler will only allow GET requests. That is, POST and others will be rejected. The same thing can be done
with
function based handlers in the following way:
@app.route("/", allowed_methods=["get"])
def home(req, resp):
resp.text = "Hello, this is a home page."
Note that if you specify methods for class based handlers, they will be ignored.
Templates
@app.route("/show/template")
def handler_with_template(req, resp):
resp.html = app.template("example.html", context={"title": "Awesome Framework", "body": "welcome to the future!"})
Static Files
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{title}}</title>
<link href="static/main.css" rel="stylesheet" type="text/css">
</head>
<body>
<h1>{{body}}</h1>
<p>This is a paragraph</p>
</body>
</html>
Custom Exception Handler
Sometimes, depending on the exception raised, you may want to do a certain action. For such cases, you can register an exception handler:
def on_exception(req, resp, exception):
if isinstance(exception, HTTPError):
if exception.status == 404:
resp.text = "Unfortunately the thing you were looking for was not found"
else:
resp.text = str(exception)
else:
# unexpected exceptions
if app.debug:
debug_exception_handler(req, resp, exception)
else:
print("These unexpected exceptions should be logged.")
app = Fynor(debug=False)
app.add_exception_handler(on_exception)
This exception handler will catch 404 HTTPErrors and change the text
to "Unfortunately the thing you were looking for was not found". For other HTTPErrors, it will simply
show the exception message. If the raised exception is not an HTTPError and if debug is set to True, it will show the
exception and its traceback. Otherwise, it will log it.
Middleware
You can create custom middleware classes by inheriting from the fynor.middleware.Middleware class and override its
two methods
that are called before and after each request:
from fynor import Fynor
from fynor.middleware import Middleware
app = Fynor()
class SimpleCustomMiddleware(Middleware):
def process_request(self, req):
print("Before dispatch", req.url)
def process_response(self, req, res):
print("After dispatch", req.url)
app.add_middleware(SimpleCustomMiddleware)
Features
- WSGI compatible
- Parameterized and basic routing
- Class based handlers
- Test Client
- Support for templates
- Support for static files
- Custom exception handler
- Middleware
Note
It is extremely raw and will hopefully keep improving. If you are interested in knowing how a particular feature is implemented in other frameworks, please open an issue and we will hopefully implement and explain it.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fynor-0.1.2.tar.gz.
File metadata
- Download URL: fynor-0.1.2.tar.gz
- Upload date:
- Size: 6.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a6992ec80fbb86934af1ea58fb6f7ac220eaeb0ae44536c48f8c6706e87c39db
|
|
| MD5 |
3cbcd12e7d09726eab9dc2f9ba8a93d6
|
|
| BLAKE2b-256 |
bc7d972aa92189ce20deb1b37fb3c39a2b00a3c9d7dde8b77310e4ad5640f9e3
|
File details
Details for the file fynor-0.1.2-py3-none-any.whl.
File metadata
- Download URL: fynor-0.1.2-py3-none-any.whl
- Upload date:
- Size: 6.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77c84a1d48d1ec109a1bfd184b7675f9f13dd27c52140cb8797fd8dc2ae90da7
|
|
| MD5 |
de99bed0749f8866a85a28b34538d60b
|
|
| BLAKE2b-256 |
79abcdd9c6bad04bd1b1d55e40cde873214c3e00c59f1cb7e11356b53012f976
|