Skip to main content

Python Web Framework built for learning purposes.

Project description


purpose travis

Alcazar

Alcazar 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 Florimond Monca'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, Florimond and of course Kenneth Reitz who in turn inspired Florimond to write a framework with his own framework Responder. Go check out both Bocadillo by Florimond Monca and Responder by Kenneth Reitz. If you like them, show some love by staring their repos.

Blog posts

Quick Start

Install it:

pip install alcazar-web-framework

Basic Usage:

# app.py
from alcazar import Alcazar

app = Alcazar()


@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("/", 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.

Unit Tests

The recommended way of writing unit tests is with pytest. There are two built in fixtures that you may want to use when writing unit tests with Alcazar. The first one is app which is an instance of the main Alcazar class:

def test_route_overlap_throws_exception(app):
    @app.route("/")
    def home(req, resp):
        resp.text = "Welcome Home."

    with pytest.raises(AssertionError):
        @app.route("/")
        def home2(req, resp):
            resp.text = "Welcome Home2."

The other one is client that you can use to send HTTP requests to your handlers. It is based on the famous requests and it should feel very familiar:

def test_parameterized_route(app, client):
    @app.route("/{name}")
    def hello(req, resp, name):
        resp.text = f"hey {name}"

    assert client.get(url("/matthew")).text == "hey matthew"

Note that there is a url() function used. It is used to generate the absolute url of the request given a relative url. Import it before usage:

from alcazar.utils.tests import url

Templates

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

app = Alcazar(templates_dir="templates_dir_name")

Then you can use HTML files in that folder like so in a handler:

@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

Just like templates, the default folder for static files is static and you can override it:

app = Alcazar(static_dir="static_dir_name")

Then you can use the files inside this folder in HTML 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 = Alcazar(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 alcazar.middleware.Middleware class and override its two methods that are called before and after each request:

from alcazar import Alcazar
from alcazar.middleware import Middleware

app = Alcazar()


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
  • Basic and parameterized 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 in a blog post.

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

alcazar-web-framework-0.0.2.tar.gz (9.6 kB view details)

Uploaded Source

Built Distribution

alcazar_web_framework-0.0.2-py3-none-any.whl (9.6 kB view details)

Uploaded Python 3

File details

Details for the file alcazar-web-framework-0.0.2.tar.gz.

File metadata

  • Download URL: alcazar-web-framework-0.0.2.tar.gz
  • Upload date:
  • Size: 9.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.2

File hashes

Hashes for alcazar-web-framework-0.0.2.tar.gz
Algorithm Hash digest
SHA256 327775c040fdc674f776ced0466d05a38a7f741dd47c2650fc25176e93c897e1
MD5 9699aeed803da998af468ff3bf29308f
BLAKE2b-256 8f7aff3696446a1ba8144368e5186c9b323ba027be4c3a29d9ecd20a8efef629

See more details on using hashes here.

File details

Details for the file alcazar_web_framework-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: alcazar_web_framework-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 9.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.6.2 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.2

File hashes

Hashes for alcazar_web_framework-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 bdab8928063d1421c6bd41077e9eb54f62a610a870e2211164c735afe05252e6
MD5 100b8d298982cf5d10d088cd44762dc9
BLAKE2b-256 ceae25f103ebad24d637ef0af8c9510c4711a3e918e675bc810d550437230a29

See more details on using hashes here.

Supported by

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