Skip to main content

Ellar - Python ASGI web framework for building fast, efficient and scalable RESTAPIs and server-side application.

Project description

Ellar Logo

Ellar - Python ASGI web framework for building fast, efficient and scalable RESTAPIs and server-side application.

Test Coverage PyPI version PyPI version PyPI version

Introduction

Ellar is a lightweight ASGI framework for building efficient and scalable server-side python applications. It supports both OOP (Object-Oriented Programming) and FP (Functional Programming)

Ellar is based on Starlette (ASGI toolkit), a lightweight ASGI framework/toolkit well-suited for developing asynchronous web services in Python. While Ellar provides a high level of abstraction on top of Starlette, it still incorporates some of its features, as well as those of FastAPI. If you are familiar with these frameworks, you will find it easy to understand and use Ellar.

Dependencies

  • Python >= 3.7
  • Starlette
  • Injector
  • Pydantic

Features Summary

  • Pydantic integration
  • Dependency Injection (DI)
  • Templating with Jinja2
  • OpenAPI Documentation (Swagger and ReDoc)
  • Controller (MVC)
  • Guards (authentications, roles and permissions)
  • Modularization (eg: flask blueprint)
  • Websocket support
  • Session and Cookie support
  • CORS, GZip, Static Files, Streaming responses

Installation

Poetry Installation

For Poetry usages

poetry add ellar[standard]

Pip Installation

For normal pip installation

pip install ellar[standard]

NB:

Some shells may treat square braces ([ and ]) as special characters. If that's the case here, then use a quote around the characters to prevent unexpected shell expansion.

pip install "ellar[standard]"

Create a project

To create an ellar project, you need to have a pyproject.toml available on your root directory. This is necessary for ellar to store some metadata about your project.

Create a project

For Pip Users, you need to create pyproject.toml file

ellar new carsite

If you are using Poetry, at your project root directory with pyproject.toml, run the ellar create project cli command,

ellar create-project carsite

Run your project

Ellar runs UVICORN - ASGI Server under the hood.

ellar runserver --reload

--reload is to watch for file changes

Now go to http://127.0.0.1:8000 Swagger UI

For more info on Ellar CLI, click here

Create a project module

A project module is a project app defining a group of controllers or services including templates and static files. So, now we have a project created, lets add an app to the project.

ellar create-module car

Add Schema

In car/schema.py, lets add some serializer for car input and output data

from ellar.serializer import Serializer

class CarSerializer(Serializer):
    name: str
    model: str
    brand: str


class RetrieveCarSerializer(CarSerializer):
    pk: str

Add Services

In car/services.py, lets create a dummy repository CarDummyDB to manage our car data.

import typing as t
import uuid
from ellar.di import injectable, singleton_scope


@injectable(scope=singleton_scope)
class CarDummyDB:
    class CarDummyDBItem:
        pk: str

        def __init__(self, **data: t.Dict) -> None:
            self.__dict__ = data

        def __eq__(self, other):
            if isinstance(other, CarDummyDB.CarDummyDBItem):
                return self.pk == other.pk
            return self.pk == str(other)

    def __init__(self) -> None:
        self._data: t.List[CarDummyDB.CarDummyDBItem] = []

    def add_car(self, data: t.Dict) -> str:
        pk = uuid.uuid4()
        _data = dict(data)
        _data.update(pk=str(pk))
        item = self.CarDummyDBItem(**_data)
        self._data.append(item)
        return item.pk

    def list(self) -> t.List["CarDummyDB.CarDummyDBItem"]:
        return self._data

    def update(self, car_id: str, data: t.Dict) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
        idx = self._data.index(car_id)
        if idx >= 0:
            _data = dict(data)
            _data.update(pk=str(car_id))
            self._data[idx] = self.CarDummyDBItem(**_data)
            return self._data[idx]

    def get(self, car_id: str) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
        idx = self._data.index(car_id)
        if idx >= 0:
            return self._data[idx]

    def remove(self, car_id: str) -> t.Optional["CarDummyDB.CarDummyDBItem"]:
        idx = self._data.index(car_id)
        if idx >= 0:
            return self._data.pop(idx)

Add Controller

In car/controllers.py, lets create CarController

import typing as t
from ellar.common import Controller, delete, get, put, post
from ellar.core import ControllerBase
from ellar.core.exceptions import NotFound
from .schemas import CarSerializer, RetrieveCarSerializer
from .services import CarDummyDB


@Controller
class CarController(ControllerBase):
    def __init__(self, db: CarDummyDB) -> None:
        self.car_db = db

    @post("/create", response={200: str})
    async def create_cat(self, payload: CarSerializer):
        pk = self.car_db.add_car(payload.dict())
        return pk

    @put("/{car_id:str}", response={200: RetrieveCarSerializer})
    async def update_cat(self, car_id: str, payload: CarSerializer):
        car = self.car_db.update(car_id, payload.dict())
        if not car:
            raise NotFound("Item not found")
        return car

    @get("/{car_id:str}", response={200: RetrieveCarSerializer})
    async def get_car_by_id(self, car_id: str):
        car = self.car_db.get(car_id)
        if not car:
            raise NotFound('Item not found.')
        return car

    @delete("/{car_id:str}", response={204: dict})
    async def deleted_cat(self, car_id: str):
        car = self.car_db.remove(car_id)
        if not car:
            raise NotFound('Item not found.')
        return 204, {}

    @get("/", response={200: t.List[RetrieveCarSerializer]})
    async def list(self):
        return self.car_db.list()

Register Service and Controller

In car/module.py, lets register CarController and CarDummyDB

from ellar.common import Module
from ellar.core import ModuleBase
from ellar.di import Container

from .controllers import CarController
from .services import CarDummyDB


@Module(
    controllers=[CarController],
    providers=[CarDummyDB],
    routers=[],
)
class CarModule(ModuleBase):
    def register_providers(self, container: Container) -> None:
        # for more complicated provider registrations
        # container.register_instance(...)
        pass

Registering Module

Ellar is not aware of CarModule yet, so we need to add it to the modules list of ApplicationModule at the carsite/root_module.py.

from ellar.common import Module, exception_handler
from ellar.core import IHostContext, ModuleBase
from ellar.core.connection import Request
from ellar.core.response import JSONResponse, Response

from ellar.samples.modules import HomeModule
from .apps.car.module import CarModule


@Module(modules=[HomeModule, CarModule])
class ApplicationModule(ModuleBase):
    @exception_handler(404)
    def exception_404_handler(cls, context: IHostContext, exc: Exception) -> Response:
        return JSONResponse(dict(detail="Resource not found."))

Enabling OpenAPI Docs

To start up openapi, we need to go back to project folder in the server.py then add the following below.

import os

from ellar.constants import ELLAR_CONFIG_MODULE
from ellar.core.factory import AppFactory
from ellar.openapi import OpenAPIDocumentModule, OpenAPIDocumentBuilder
from .root_module import ApplicationModule

application = AppFactory.create_from_app_module(
    ApplicationModule,
    config_module=os.environ.get(
        ELLAR_CONFIG_MODULE, "carsite.config:DevelopmentConfig"
    ),
)

document_builder = OpenAPIDocumentBuilder()
document_builder.set_title('CarSite API') \
    .set_version('1.0.0') \
    .set_contact(name='Eadwin', url='https://www.yahoo.com', email='eadwin@gmail.com') \
    .set_license('MIT Licence', url='https://www.google.com')

document = document_builder.build_document(application)
module = application.install_module(OpenAPIDocumentModule, document=document)
module.setup_swagger_doc()

Now we can test our API at http://127.0.0.1:8000/docs Please ensure your server is running Swagger UI

HTML Templating

Ellar has built-in support for Jinja2, which is a popular template engine for HTML. This feature allows for easy and efficient HTML templating similar to that of Flask. Jinja2 can be used to create reusable templates, and to insert dynamic data into HTML pages. It also has support for template inheritance, control structures, and other useful features that can help to simplify and streamline the process of creating HTML templates.

<html>
  <body>
    <ul>
      {% for item in items %}
      <li>{{ item }}</li>
      {% endfor %}
    </ul>
  </body>
</html>

See the Doc for more examples.

Project Status

Project is still in development

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ellar-0.3.2.tar.gz (4.5 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ellar-0.3.2-py3-none-any.whl (236.8 kB view details)

Uploaded Python 3

File details

Details for the file ellar-0.3.2.tar.gz.

File metadata

  • Download URL: ellar-0.3.2.tar.gz
  • Upload date:
  • Size: 4.5 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.28.2

File hashes

Hashes for ellar-0.3.2.tar.gz
Algorithm Hash digest
SHA256 d8e57f62356e845eb992cd392e28f4fe677e412c7cc6eef0fce9206c7f4fbd9e
MD5 aeb108ca9f239577cd7723a5114dc51d
BLAKE2b-256 e78c8cc64ab6e983f1349272965cefcbefd80a2e6bd00deda5358b908b7e4907

See more details on using hashes here.

File details

Details for the file ellar-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: ellar-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 236.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: python-requests/2.28.2

File hashes

Hashes for ellar-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 77041694cc9ca8d11d49040618a98bba6c799be2b0563cfd94df6b4dec51389f
MD5 0da8f762ee071353319a08f346169d36
BLAKE2b-256 284e0c37f41e78cad2ee5a4cea0bb905980e87c6483c4dabebfe46c7dfb30adf

See more details on using hashes here.

Supported by

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