No project description provided
Project description
Aloni
Aloni 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
Aloni 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: Aloni 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: Aloni is optimized for asynchronous programming, making it perfect for IO-bound tasks.
- Minial boilerplate: Aloni adds no redundant boilerplate code. Keep your project as simple as possible.
Getting Started
Installation
Requirements
Linux or MacOS (should work on all Unix systems). It does not work on Windows because Aloni 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
Aloni works best with Poetry. Install Poetry first and follow the steps:
- Create a new Poetry project, then install Aloni:
poetry add aloni
- Create your application's module:
mkdir my_app
touch my_app/__init__.py
- Create the
app.py
(primary application file). That is the entire boilerplate code that Aloni needs to work:import my_app import aloni aloni.start(my_app).exit_after_finishing()
Invoking poetry run python ./app.py
should display something like:
usage: app.py [-h] {hello,serve} ...
Aloni 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 Aloni project. You can continue with the next steps.
Usage
Check the demo project for basic usage.
Aloni will scan your module (in this case, my_app
) for services with a role decorator and start your CLI application. That's it!
Add a new CLI command if you want to start developing something new. 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 Aloni):
from aloni.cli_foundation import Command
from aloni.role import responds_to_cli
@responds_to_cli(
name="hello",
description="Say hello!",
)
class Hello(Command):
async def respond(self) -> int:
print("Hello, World!")
return 0
You can then run it with:
python ./app.py hello
You should see:
Hello, World!
Quick Tutorials
Responding to HTTP Requests
Create a responder in your application's module. Filename and location do not matter:
from aloni.http import Responder, TextResponse
from aloni.role import responds_to_http
@responds_to_http(pattern='/ping')
class Ping(Responder):
async def respond(self) -> TextResponse:
return TextResponse("pong")
Responding with Jinja2 Templates
Place a template inside your application's templates
directory. Name it hello.j2
:
<p>Hello, world!</p>
from aloni.http import Responder, JinjaResponse
from aloni.role import responds_to_http
@responds_to_http(pattern='/hello')
class Hello(Responder):
async def respond(self) -> JinjaResponse:
return JinjaResponse('hello.j2')
Injecting Services
Create a service in your application's module. Filename and location do not matter:
from aloni.role import service
@service
class MyService:
pass
Use it in your other services:
from aloni.role import service
from .my_service import MyService
@service
class OtherService:
def __init__(self, my_service: MyService) -> None:
self.my_service = my_service
Creating Service Providers
Create a base service class in your application's module. Do not add @service
role to that class:
class MyService:
def __init__(self, foo: str) -> None:
self.foo = foo
Create service provider (again, location and filename do not matter as long as it's in your application's module):
from aloni.application_state import ApplicationState
from aloni.role import service_provider
from aloni.service_provider import ServiceProvider
from .my_service import MyService
@service_provider(provides=MyService)
class MyServiceProvider(ServiceProvider[MyService]):
def provide(self) -> MyService:
return MyService(foo="bar")
Use it in your other services:
from aloni.role import service
from .my_service import MyService
@service
class OtherService:
def __init__(self, my_service: MyService) -> None:
self.my_service = my_service
Registering Jinja Functions
Custom Jinja functions have their constructor (__init__
) arguments injected by the dependency injection container.
Arguments passed to the __call__
method are passed from a template.
from aloni.jinja_function import JinjaFunction
from aloni.role.jinja_function import jinja_function
@jinja_function(name="say_hello")
class UrlFor(JinjaFunction):
def __call__(self) -> str:
return "Hello, world!"
Then use it in a template:
{{ say_hello() }}
API Reference
HTTP Responses
All the available roles are accessible from aloni.http
module.
For example:
from aloni.http import AssetResponse
Response | Description |
---|---|
AssetResponse | Returns an asset file if it is present inside your application's assets directory |
JinjaResponse | Returns a parsed Jinja2 template if it is present inside your application's templates directory |
TextResponse | Returns a plain text response |
Available Roles
All the available roles are accessible from aloni.role
module.
For example:
from aloni.role import responds_to_http
Role | Description |
---|---|
intercepts_http_response | Allows to intercept any response returned by your http responder and convert it into a renderable response. It acts kind of like inversed middleware - instead of intercepting a request, it intercepts and modifies a response. |
responds_to_cli | Responds to CLI command |
responds_to_http | Responds to HTTP request |
service | Marks the current class as a service. Its constructor arguments will be injected from the dependency injection container |
service_provider | Registers a service provider for dependency injection. Use it to create a class that provides an instance of a different class to the dependency injection container. |
Special Thanks
- Granian for creating an awesome HTTP Python runner with excellent performance
License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.