Skip to main content

Fynor is a lightweight WSGI web application framework.

Project description

Fynor

purpose

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fynor-0.1.2.tar.gz (6.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fynor-0.1.2-py3-none-any.whl (6.5 kB view details)

Uploaded Python 3

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

Hashes for fynor-0.1.2.tar.gz
Algorithm Hash digest
SHA256 a6992ec80fbb86934af1ea58fb6f7ac220eaeb0ae44536c48f8c6706e87c39db
MD5 3cbcd12e7d09726eab9dc2f9ba8a93d6
BLAKE2b-256 bc7d972aa92189ce20deb1b37fb3c39a2b00a3c9d7dde8b77310e4ad5640f9e3

See more details on using hashes here.

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

Hashes for fynor-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 77c84a1d48d1ec109a1bfd184b7675f9f13dd27c52140cb8797fd8dc2ae90da7
MD5 de99bed0749f8866a85a28b34538d60b
BLAKE2b-256 79abcdd9c6bad04bd1b1d55e40cde873214c3e00c59f1cb7e11356b53012f976

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page