# artless-core

![PyPI Version](https://img.shields.io/pypi/v/artless-core)
![Development Status](https://img.shields.io/badge/status-3%20--%20Beta-blue)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/artless-core)
[![Downloads](https://static.pepy.tech/badge/artless-core)](https://pepy.tech/project/artless-core)
![PyPI - License](https://img.shields.io/pypi/l/artless-core)

The artless and ultralight web framework for building minimal APIs and apps.

## Motivation

An extremely minimalistic framework was needed to create the same minimalistic applications. Those "micro" frameworks like `Flask`, `Pyramid`, `CherryPie`, etc - turned out to be not micro at all). Even a single-module `Bottle` turned out to be a "monster" of 4 thousand LOC and supporting compatibility with version 2.7.

Therefore, it was decided to sketch out our own simple, minimally necessary implementation of the WSGI and ASGI library for building simple APIs and apps.

## Why artless-core?

* 🪶 Tiny: Single module, no dependencies, less then 500 LOC
* ⚡ Fast: Optimized pure Python with async support
* 🧩 Simple: Intuitive API with type hints
* ✅ Tested: 100% coverage
* 🐍 Modern: Python 3.11+ only

## Quickstart

### Installation

``` shellsession
$ pip install artless-core
```

### WSGI Example

``` python
from artless_core import WSGIApp, Request, Response, plain


def say_hello(request: Request, name: str) -> Response:
    return plain(f"Hello, {name}!")


def create_application() -> WSGIApp:
    app = WSGIApp()
    app.set_routes([("GET", r"^/hello/(?P<name>\w+)$", say_hello)])
    return app


application = create_application()
```

Run with Gunicorn:

``` shellsession
$ gunicorn app
[2025-01-11 16:34:19 +0300] [62111] [INFO] Starting gunicorn 23.0.0
[2025-01-11 16:34:19 +0300] [62111] [INFO] Listening at: http://127.0.0.1:8000 (62111)
[2025-01-11 16:34:19 +0300] [62111] [INFO] Using worker: sync
[2025-01-11 16:34:19 +0300] [62155] [INFO] Booting worker with pid: 62155
```

Check it:

``` shellsession
$ curl http://127.0.0.1:8000/hello/Bro
Hello, Bro!
```

Need more? See [wsgi examples](https://git.peterbro.su/peter/py3-artless-core/src/branch/master/examples/wsgi).

### ASGI Example

``` python
from artless_core import ASGIApp, Request, Response, plain


async def say_hello(request: Request, name: str) -> Response:
    return plain(f"Hello, {name}!")


def create_application() -> ASGIApp:
    app = ASGIApp()
    app.set_routes([("GET", r"^/hello/(?P<name>\w+)$", say_hello)])
    return app


application = create_application()
```

Run with Uvicorn:

``` shellsession
$ uvicorn app:application
INFO:     Started server process [62683]
INFO:     Waiting for application startup.
INFO:     ASGI 'lifespan' protocol appears unsupported.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```

Check it:

``` shellsession
$ curl http://127.0.0.1:8000/hello/Bro
Hello, Bro!
```

Need more? See [asgi examples](https://git.peterbro.su/peter/py3-artless-core/src/branch/master/examples/asgi).

## Limitations

* ❌ No `WebSockets`
* ❌ No `multipart/form-data` support.
* ❌ No `Cookies` support.
* ❌ No builtin models, ORM, template engine, form serialisation and other.
* ❌ No built-in protections, such as: `CSRF`, `XSS`, `clickjacking` and other.

## Benchmarks results

See more details in [benchmarks/README.md](benchmarks/README.md).

## WSGI application

Single worker:

| Framework   | RPS (mean)  |
|-------------|-------------|
| Falcon      | 1839.46     |
| **Artless** | **1803.65** |
| Bottle      | 1516.69     |
| Flask       | 1549.90     |
| Django      | 1386.77     |


Multiple workers:

| Framework   | RPS (mean)  |
|-------------|-------------|
| **Artless** | **3461.03** |
| Falcon      | 3458.39     |
| Bottle      | 3331.41     |
| Flask       | 3018.25     |
| Django      | 2240.39     |

## ASGI application

Single worker:

| Framework   | RPS (mean)  |
|-------------|-------------|
| Blacksheep  | 3456.86     |
| Falcon      | 3338.41     |
| **Artless** | **3320.35** |
| FastAPI     | 2191.76     |
| Django      | 1160.94     |
| Flask       | 777.81      |

Multiple workers:

| Framework   | RPS (mean)  |
|-------------|-------------|
| Falcon      | 5393.27     |
| Blacksheep  | 5382.32     |
| **Artless** | **5332.50** |
| FastAPI     | 3594.12     |
| Django      | 2050.55     |
| Flask       | 1627.59     |

## Memory usage

WSGI application:

| Patient     | RSS Mem (MB) |
|-------------|--------------|
| **Artless** | **10.668**   |
| Bottle      | 14.8164      |
| Falcon      | 26.9219      |
| Flask       | 31.6406      |
| Django      | 43.5742      |

ASGI application:

| Patient     | RSS Mem (MB) |
|-------------|--------------|
| **Artless** | **25.6797**  |
| Flask       | 34.1367      |
| Falcon      | 35.2461      |
| Blacksheep  | 40.4883      |
| Django      | 47.0352      |
| Fastapi     | 53.1992      |

## Roadmap

- [x] Add benchmarks.
- [x] Add Async/ASGI support.
- [ ] Add plugins support.
- [ ] Add middlewares support.
- [ ] Add more complex examples.
- [ ] Add test clients (for WSGI and ASGI).

## Related projects

* [artless-template](https://pypi.org/project/artless-template/) - the artless and small template library for server-side rendering.
