The python framework, providing quick way to build client-side API wrappers. Use type annotations, to build requests, with little or no implementation
Source code is made available under the MIT License.
API Wrapper should follow these principles:
- Provide sync and async code versions
- Validate data before accessing the API. Because better outcome is catching exception, thrown due to wrong data, than getting json explaining reason of error.
- Handle QPS (Queries per second) limits.
To follow these principles, you either to violate DRY or have to maintain bad code architecture. Sensei is tool to avoid these issues.
Example of OOP code.
from typing import Annotated, Any, Self
from sensei import Router, Query, Path, APIModel, Header, Args, pascal_case, format_str, RateLimit
router = Router('https://reqres.in/api', rate_limit=RateLimit(5, 1))
@router.model()
class BaseModel(APIModel):
def __finalize_json__(self, json: dict[str, Any]) -> dict[str, Any]:
return json['data']
def __prepare_args__(self, args: Args) -> Args:
args.headers['X-Token'] = 'secret_token'
return args
def __header_case__(self, s: str) -> str:
return pascal_case(s)
class User(BaseModel):
email: str
id: int
first_name: str
last_name: str
avatar: str
@classmethod
@router.get('/users')
def query(
cls,
page: Annotated[int, Query(1)],
per_page: Annotated[int, Query(3, le=7)],
bearer_token: Annotated[str, Header('secret', le=10)],
) -> list[Self]:
...
@classmethod
@router.get('/users/{id_}')
def get(cls, id_: Annotated[int, Path(alias='id')]) -> Self:
...
@router.delete('/users/{id_}')
def delete(self) -> Self:
...
@delete.prepare
def _delete_in(self, args: Args) -> Args:
url = args.url
url = format_str(url, {'id_': self.id})
args.url = url
return args
users = User.query(per_page=7)
user_id = users[0].id
user = User.get(user_id)
print(user == users[0])
Example of functional style.
from typing import Annotated
from sensei import Router, Path, APIModel
router = Router('https://pokeapi.co/api/v2/')
class Pokemon(APIModel):
name: str
id: int
height: int
weight: int
types: list
@router.get('/pokemon/{pokemon_name}')
def get_pokemon(
pokemon_name: Annotated[str, Path()],
) -> Pokemon:
...
pokemon = get_pokemon(pokemon_name="pikachu")
print(pokemon)
To install sensei
from PyPi, you can use that:
pip install sensei
To install sensei
from GitHub, use that:
pip install git+https://github.com/CrocoFactory/sensei.git