Introducing Intention: A High-Performance RSGI Framework for Python

RMAG news

Intention is a Python framework designed to increase productivity when developing applications. Simplicity, productivity, and great developer experience are our primary goals.

It is based on an innovative Role-Based Services approach. (more info below).

It is async and thus performs well with IO-bound services (for example, anything that makes a lot of long-running HTTP calls to large language models or uses a lot of microservices and third-party services)—but not just those.

Try it out

Intention will take only a few minutes to set up, and it might amaze you and change how you develop apps. 🙂

Key Features

Effortlessly split your project into multiple files: Intention automatically combines your project files based on the roles you assign them, making it easy to manage large projects.

Dependency Injection: Manage your application’s dependencies effortlessly with a container that holds one instance of each service, ensuring efficient resource management.

Async-First: Intention is optimized for asynchronous programming, making it perfect for IO-bound tasks.

Minial boilerplate: Intention adds no redundant boilerplate code. Keep your project as simple as possible.

Installation

Requirements

Linux or MacOS (should work on all Unix systems). It does not work on Windows because Intention requires the fork multiprocessing method (which Windows does not have).

That might change in the future (see also: https://github.com/emmett-framework/granian/issues/330).

Steps

Intention works best with Poetry. Install Poetry first and follow the steps:

Create a new project. Invoke the command and fill in the project creation form:

poetry init my_app

In the project’s directory (the one with the newly created pyproject.toml) open:

poetry shell

Install Intention locally in the project:

poetry add intention

Create your application’s module:

mkdir my_app

“`shell
touch my_app/__init__.py
“`

Create the app.py (primary application file). That is the entire boilerplate code that Intention needs to work:

import my_app
import intention

intention.start(my_app).exit_after_finishing()

Invoking python ./app.py should display something like:

usage: app.py [-h] {hello,serve}

Intention CLI

positional arguments:
{serve}
serve Start the app in HTTP server mode

options:
-h, –help show this help message and exit

Congratulations! You have installed the Intention project. You can continue with the next steps.

Usage

Check the demo project for basic usage.

Intention will scan your module (in this case, my_app) for services with a role decorator and start your CLI application. That’s it!

If you want to start developing something new, add a new CLI command. Use responds_to_cli role. Add a new file in my_app/hello_command.py (file name can be anything; it’s just an example – file names and directory structure do not matter for Intention):

from intention.cli_foundation import Command
from intention.role import responds_to_cli

@responds_to_cli(
name=hello,
description=Say hello!,
)
class Hello(Command):
def respond(self) -> int:
print(Hello, World!)

return 0

You can then run it with:

python ./app.py hello

You should see:

Hello, World!

Why Intention?

Intention’s unique Role-Based Services approach allows for clear and organized code. It scans your project for services decorated as roles and automatically combines them. It takes care of all the tedious work and allows you to focus on the application architecture.

Suitable for Bigger Projects

Intention allows you to split your HTTP responders and other modules among multiple files.

You do not have to manually combine the project files. Intention treats role.* decorators as metadata and puts them together for you.

from intention.role import responds_to_http
from intention.http import Responder, JinjaResponse
from intention.http_foundation import Request

@responds_to_http(pattern=/)
class Homepage(Responder):
async def respond(self, request: Request):
return JinjaResponse(homepage.j2)

Dependency Injection

Intention allows you to inject services into your responders and other modules.

It is possible to create service providers for more advanced use cases.

from intention.role import service_provider
from intention.service_provider.service_provider import ServiceProvider
from jinja2 import Environment, PackageLoader, select_autoescape

@service_provider(provides=Environment)
class JinjaEnvironmentServiceProvider(ServiceProvider[Environment]):
def provide(self) -> Environment:
return Environment(
auto_reload=False,
enable_async=True,
loader=PackageLoader(mymodule),
autoescape=select_autoescape(),
)

Then, you can inject the service into your responders and other modules.

No further configuration is needed (just the type hint).

from intention.role import service
from jinja2 import Environment

@service
class MyService:
def __init__(self, env: Environment):
self.env = env

async def render_something(self):
return self.env.get_template(foo.j2).render()

Special Thanks

Granian for creating an awesome HTTP Python runner with excellent performance