Skip to main content

Python Web Framework build for learning purposes

Project description

Fastango: Python Web Framework

Logo

purpose

PyPI - Version

Fastango is a Python web framework built for learning purposes.

It's a WSGI framework and can be used with any WSGI application server such as Gunicorn.

Installation

pip install fastango-v1

How to use it

Basic usage:

from fastango_v1.app import FastangoApp
from fastango_v1.middleware import Middleware

app = FastangoApp()


@app.route('/home')
def home(request, response):
    response.text = "Hello from the Home Page"


@app.route('/about', allowed_methods=['put'])
def about(request, response):
    response.text = "Hello from the About Page"


@app.route('/hello/{name}')
def greating(request, response, name):
    response.text = f"Hello {name}"


@app.route('/class/')
class TestView:

    def get(self, request, response, **kwargs):
        response.text = "Class Method GET"

    def post(self, request, response, **kwargs):
        response.text = "Class Method POST"


def gret_route(request, response, name):
    response.text = f"Hello New route"


app.add_route("hello-new-route", gret_route)


@app.route("/json")
def get_template(request, respose):
    respose.json = {"key": "some json"}

Unit Tests

The recommend way of writing unit tests is with [pytest]:
import pytest
from fastango_v1.middleware import Middleware


def test_basic_route_adding(app):
    @app.route("/home")
    def home(req, res):
        res.text = "Hello from home"


def test_duplicate_routes_throws_exception(app):
    @app.route("/home")
    def home(req, res):
        res.text = "Hello from home"

    with pytest.raises(AssertionError):
        @app.route("/home")
        def home2(req, res):
            res.text = "Hello from home"


def test_requests_can_be_sent_by_test_client(app, test_client):
    @app.route("/home")
    def home(req, res):
        res.text = "Hello from home"

    response = test_client.get('http://testserver/home')

    assert response.text == "Hello from home"


def test_parametrized_routing(app, test_client):
    @app.route('/hello/{name}')
    def greating(request, response, name):
        response.text = f"Hello {name}"

    assert test_client.get('http://testserver/hello/Mirmux').text == "Hello Mirmux"
    assert test_client.get('http://testserver/hello/Jack').text == "Hello Jack"


def test_default_response(test_client):
    response = test_client.get('http://testserver/noneexists')

    assert response.text == 'Not found!'
    assert response.status_code == 404


def test_class_based_get(app, test_client):
    @app.route("/books")
    class Books:
        def get(self, req, res):
            res.text = "Books page"

    assert test_client.get("http://testserver/books").text == "Books page"


def test_class_based_post(app, test_client):
    @app.route("/books")
    class Books:
        def post(self, req, res):
            res.text = "Created book!"

    assert test_client.post("http://testserver/books").text == "Created book!"


def test_class_based_not_allowed_method(app, test_client):
    @app.route("/books")
    class Books:
        def post(self, req, res):
            res.text = "Created book!"

    assert test_client.get("http://testserver/books").text == "Method Not Allowed."


def test_add_another_method_route(app, test_client):
    def gret_route(request, response):
        response.text = f"Hello New route"

    app.add_route("/hello_route", gret_route)

    assert test_client.get("http://testserver/hello_route").text == "Hello New route"


def test_template_handler(app, test_client):
    @app.route("/test-template")
    def get_template(request, respose):
        respose.body = app.template(
            "home.html",
            context={"new_title": "Best Title", "new_body": "Best Body"}
        )

    response = test_client.get('http://testserver/test-template')

    assert "Best Title" in response.text
    assert "Best Body" in response.text

    assert "text/html" in response.headers['Content-Type']


def test_custom_exception_handler(app, test_client):
    @app.route("/exception")
    def exception_throwing_handler(req, resp):
        raise AssertionError('some exception')

    def on_exception(req, resp, exc):
        resp.text = "Something went wrong!"

    app.add_exception_handler(on_exception)

    response = test_client.get('http://testserver/exception')

    assert response.text == "Something went wrong!"


def test_non_existent_static_file(test_client):
    assert test_client.get("http://testserver/static/nonexistent.css").status_code == 404


def test_serving_static_file(test_client):
    response = test_client.get("http://testserver/static/test.css")

    assert response.text == "body {background-color: chocolate; }"


def test_middleware_methods_are_called(app, test_client):
    process_request_called = False
    process_response_called = False

    class SimpleMiddleware(Middleware):
        def __init__(self, app):
            super(SimpleMiddleware, self).__init__(app)

        def process_request(self, req):
            nonlocal process_request_called
            process_request_called = True

        def process_response(self, req, resp):
            nonlocal process_response_called
            process_response_called = True

    app.add_middleware(SimpleMiddleware)

    @app.route("/home")
    def index(req, resp):
        resp.text = 'from handler'

    test_client.get("http://testserver/home")

    assert process_request_called is True
    assert process_response_called is True


def test_allowed_methods_for_function_based_handlers(app, test_client):
    @app.route("/home", allowed_methods=['post'])
    def home(req, resp):
        resp.text = 'Hello Allowed Method'

    resp = test_client.get("http://testserver/home")

    assert resp.status_code == 405
    assert resp.text == 'Method Not Allowed.'


def test_json_response_helper(app, test_client):
    @app.route("/json")
    def json_handler(req, resp):
        resp.json = {"name": "Fastango"}

    resp = test_client.get("http://testserver/json")
    response_data = resp.json()

    assert resp.headers['Content-Type'] == 'application/json'
    assert response_data['name'] == 'Fastango'


def test_text_response_helper(app, test_client):
    @app.route("/text")
    def text_handler(req, resp):
        resp.text = 'plain text'

    resp = test_client.get("http://testserver/text")

    assert "text/plain" in resp.headers['Content-Type']
    assert resp.text == 'plain text'


def test_html_response_helper(app, test_client):
    @app.route("/html")
    def html_handler(req, resp):
        resp.html = app.template(
            "home.html",
            context={"new_title": "Best Title", "new_body": "Best Body"}
        )

    resp = test_client.get("http://testserver/html")

    assert "Best Title" in resp.text
    assert "Best Body" in resp.text

    assert "text/html" in resp.headers['Content-Type']

Templates

The default folder for templates is templates. You can change it when initializing the main API() class:

from fastango_v1.app import FastangoApp

app = FastangoApp(template_dir="templates")


@app.route("/template")
def get_template(request, respose):
    respose.html = app.template(
        "home.html",
        context={"new_title": "Best Title", "new_body": "Best Body"}
    )

Middleware

You can add middleware like this
from fastango_v1.app import FastangoApp
from fastango_v1.middleware import Middleware

app = FastangoApp()


class LoginMiddleware(Middleware):
    def process_request(self, req):
        print("request is being called")

    def process_response(self, req, resp):
        print("response is being called")


app.add_middleware(LoginMiddleware)

Exception Handler

from fastango_v1.app import FastangoApp

app = FastangoApp()


@app.route("/exception")
def exception_throwing_handler(req, resp):
    raise AssertionError('some exception')


def on_exception(req, resp, exc):
    resp.text = str(exc)


app.add_exception_handler(on_exception)

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

fastango_v1-0.1.6.tar.gz (6.5 kB view details)

Uploaded Source

Built Distribution

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

fastango_v1-0.1.6-py2.py3-none-any.whl (5.6 kB view details)

Uploaded Python 2Python 3

File details

Details for the file fastango_v1-0.1.6.tar.gz.

File metadata

  • Download URL: fastango_v1-0.1.6.tar.gz
  • Upload date:
  • Size: 6.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.9.9

File hashes

Hashes for fastango_v1-0.1.6.tar.gz
Algorithm Hash digest
SHA256 e91f8a559c6a53d0ae98ff04a4de75e3909e8c83b9113ecd05627e4ad561f739
MD5 3d1d403006c00477da9fee6c118ec587
BLAKE2b-256 699ad4cb846b3dcf43ec955ceee23cca0d65af2eaec216d2b10d1e3cdb9e0523

See more details on using hashes here.

File details

Details for the file fastango_v1-0.1.6-py2.py3-none-any.whl.

File metadata

  • Download URL: fastango_v1-0.1.6-py2.py3-none-any.whl
  • Upload date:
  • Size: 5.6 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.9.9

File hashes

Hashes for fastango_v1-0.1.6-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 b742165b9345c7fe971b0ae7eba2d9f21c15268acc67acf0356a98e99cf23848
MD5 806f379d43b1920c5eae8610e73ccd06
BLAKE2b-256 b3b56a671472dd073801d89e5a2962bc05b02f1a8b7874e50a6cefa079350f41

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