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.
Aloni will take only a few minutes to set up, and it might amaze you and change how you develop apps. :)
- 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.
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: emmett-framework/granian#330).
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:importmy_appimportalonialoni.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 exitCongratulations! You have installed the Aloni project. You can continue with the next steps.
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):
fromaloni.cli_foundationimportCommandfromaloni.roleimportresponds_to_cli@responds_to_cli(name="hello",description="Say hello!",)classHello(Command):
asyncdefrespond(self) ->int:
print("Hello, World!")
return0You can then run it with:
python ./app.py helloYou should see:
Hello, World!
Create a responder in your application's module. Filename and location do not matter:
fromaloni.httpimportResponder, TextResponsefromaloni.roleimportresponds_to_http@responds_to_http(pattern='/ping')classPing(Responder):
asyncdefrespond(self) ->TextResponse:
returnTextResponse("pong")Place a template inside your application's templates directory. Name it hello.j2:
<p>Hello, world!</p>fromaloni.httpimportResponder, JinjaResponsefromaloni.roleimportresponds_to_http@responds_to_http(pattern='/hello')classHello(Responder):
asyncdefrespond(self) ->JinjaResponse:
returnJinjaResponse('hello.j2')Create a service in your application's module. Filename and location do not matter:
fromaloni.roleimportservice@serviceclassMyService:
passUse it in your other services:
fromaloni.roleimportservicefrom .my_serviceimportMyService@serviceclassOtherService:
def__init__(self, my_service: MyService) ->None:
self.my_service=my_serviceCreate a base service class in your application's module. Do not add @service role to that class:
classMyService:
def__init__(self, foo: str) ->None:
self.foo=fooCreate service provider (again, location and filename do not matter as long as it's in your application's module):
fromaloni.application_stateimportApplicationStatefromaloni.roleimportservice_providerfromaloni.service_providerimportServiceProviderfrom .my_serviceimportMyService@service_provider(provides=MyService)classMyServiceProvider(ServiceProvider[MyService]):
defprovide(self) ->MyService:
returnMyService(foo="bar")Use it in your other services:
fromaloni.roleimportservicefrom .my_serviceimportMyService@serviceclassOtherService:
def__init__(self, my_service: MyService) ->None:
self.my_service=my_serviceCustom Jinja functions have their constructor (__init__) arguments injected by the dependency injection container.
Arguments passed to the __call__ method are passed from a template.
fromaloni.jinja_functionimportJinjaFunctionfromaloni.role.jinja_functionimportjinja_function@jinja_function(name="say_hello")classUrlFor(JinjaFunction):
def__call__(self) ->str:
return"Hello, world!"Then use it in a template:
{{ say_hello() }}All the available roles are accessible from aloni.http module.
For example:
fromaloni.httpimportAssetResponse| 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 |
All the available roles are accessible from aloni.role module.
For example:
fromaloni.roleimportresponds_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. |
- Granian for creating an awesome HTTP Python runner with excellent performance
This project is licensed under the MIT License - see the LICENSE file for details.