# sensei
<a href="https://pypi.org/project/sensei/">
<h1 align="center">
<img alt="Logo Banner" src="https://raw.githubusercontent.com/CrocoFactory/.github/main/branding/sensei/logo/bookmark_transparent.svg" width="300">
</h1><br>
</a>
*Build robust HTTP Requests and best API clients with minimal implementation*
[![Python versions](https://img.shields.io/pypi/pyversions/sensei?color=%23F94526)](https://pypi.org/project/sensei/)
[![PyPi Version](https://img.shields.io/pypi/v/sensei?color=%23F94526)](https://pypi.org/project/sensei/)
[![Coverage](https://raw.githubusercontent.com/CrocoFactory/sensei/main/badges/coverage.svg)](https://pypi.org/project/sensei/)
The Python framework that provides a quick way to build robust HTTP requests and best API clients. Use type hints, to build requests, with
little or no implementation.
---
**Documentation:** [https://sensei.crocofactory.dev](https://sensei.crocofactory.dev)
**Source code:** [https://github.com/CrocoFactory/sensei](https://github.com/CrocoFactory/sensei)
---
<a href="https://pypi.org/project/sensei/">
<p align="center">
<img alt="Mindmap" src="https://raw.githubusercontent.com/CrocoFactory/sensei/main/assets/mindmap.svg" height="350px">
</p><br>
</a>
There are key features provided by `sensei`:
- **Fast:** Do not write any request-handling code, dedicate responsibility to the function's interface(signature) 🚀
- **Short:** Avoid code duplication 🧹
- **Sync/Async:** Implement sync and async quickly, without headaches ⚡
- **Robust:** Auto validation data before and after request 🛡️️
Table of Contents:
1. [First Request](#first-request)
2. [Comparison](#comparison)
3. [OOP Style](#oop-style)
4. [Installing](#installing)
## First Request
Do you want to see the simplest and most robust HTTP Request? He's already here!
```python
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
@router.get('/pokemon/{name}')
def get_pokemon(name: Annotated[str, Path(max_length=300)]) -> Pokemon:
pass
pokemon = get_pokemon(name="pikachu")
print(pokemon) # Pokemon(name='pikachu' id=25 height=4 weight=60)
```
Didn't it seem to you that the function doesn't contain the code? **Sensei writes it instead of you!**
Moreover, Sensei abstracts away much of the manual work, letting developers focus on function signatures while the framework
handles the API logic and data validation. This enables a declarative style for your apps.
The example of [First Request](#first-request) demonstrates a simple and robust HTTP request using the Sensei framework.
Here's the key breakdown of the process:
#### 1. Importing Dependencies:
- `Router` manages API endpoints and routing.
- `Path` specifies and validates route parameters.
- `APIModel` defines models for structuring API responses (similar to `pydantic.BaseModel`).
#### 2. Creating the Router:
The `Router` is initialized with the base URL of the *PokéAPI*. All subsequent requests will use this as the base path.
#### 3. Defining the Model:
The `Pokemon` class represents the data structure for a Pokémon, with fields like `name`, `id`, `height`, and `weight`.
It inherits from `APIModel`, which provides validation and serialization.
#### 4. Creating the Endpoint:
The `get_pokemon` function is a routed function decorated with `@router.get`, defining a GET request for
`/pokemon/{name}`.
This uses `Annotated` to ensure that `name` is a string and adheres to the validation rule (max length of 300).
#### 5. Making the Request:
By calling `get_pokemon(name="pikachu")`, Sensei automatically handles validation, makes the HTTP request,
and maps the API response into the `Pokemon` model. The code omits the function body since Sensei handles calls through
the function's signature.
## Comparison
**Sensei** 👍: It provides a high level of abstraction. Sensei simplifies creating API wrappers, offering decorators for
easy routing, data validation, and automatic mapping of API responses to models. This reduces boilerplate and improves
code readability and maintainability.
**Bare HTTP Client** 👎: A bare HTTP client like `requests` or `httpx` requires manually managing requests,
handling response parsing, data validation, and error handling. You have to write repetitive code for each endpoint.
## OOP Style
There is a wonderful OOP approach proposed by Sensei:
```python
class User(APIModel):
email: EmailStr
id: PositiveInt
first_name: str
last_name: str
avatar: AnyHttpUrl
@classmethod
@router.get('/users')
def query(
cls,
page: Annotated[int, Query()] = 1,
per_page: Annotated[int, Query(le=7)] = 3
) -> list[Self]:
pass
@classmethod
@router.get('/users/{id_}')
def get(cls, id_: Annotated[int, Path(alias='id')]) -> Self:
pass
@router.post('/token')
def login(self) -> str:
pass
@login.prepare
def _login_in(self, args: Args) -> Args:
args.json_['email'] = self.email
return args
@login.finalize
def _login_out(self, response: Response) -> str:
return response.json()['token']
user = User.get(1)
user.login() # User(id=1, email="john@example.com", first_name="John", ...)
```
When Sensei doesn't know how to handle a request, you can do it yourself, using preprocessing as `prepare` and
postprocessing as `finalize`
## Installing
To install `sensei` from PyPi, you can use that:
```shell
pip install sensei
```
To install `sensei` from GitHub, use that:
```shell
pip install git+https://github.com/CrocoFactory/sensei.git
```
Raw data
{
"_id": null,
"home_page": null,
"name": "sensei",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.9",
"maintainer_email": null,
"keywords": "api, client, api-client, api-wrapper, python-client, http-client, rest-api, pydantic, httpx, http-requests, requests",
"author": "Alexey ",
"author_email": "axbelenkov@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/9a/da/a34ef185a4803cd1b28749f7b718c9a9ef10a0b51cbd2d064331b6e9a4c8/sensei-0.1.1.post3.tar.gz",
"platform": null,
"description": "# sensei\n<a href=\"https://pypi.org/project/sensei/\">\n<h1 align=\"center\">\n<img alt=\"Logo Banner\" src=\"https://raw.githubusercontent.com/CrocoFactory/.github/main/branding/sensei/logo/bookmark_transparent.svg\" width=\"300\">\n</h1><br>\n</a>\n\n*Build robust HTTP Requests and best API clients with minimal implementation*\n\n[![Python versions](https://img.shields.io/pypi/pyversions/sensei?color=%23F94526)](https://pypi.org/project/sensei/)\n[![PyPi Version](https://img.shields.io/pypi/v/sensei?color=%23F94526)](https://pypi.org/project/sensei/)\n[![Coverage](https://raw.githubusercontent.com/CrocoFactory/sensei/main/badges/coverage.svg)](https://pypi.org/project/sensei/)\n\nThe Python framework that provides a quick way to build robust HTTP requests and best API clients. Use type hints, to build requests, with\nlittle or no implementation.\n\n---\n\n**Documentation:** [https://sensei.crocofactory.dev](https://sensei.crocofactory.dev)\n\n**Source code:** [https://github.com/CrocoFactory/sensei](https://github.com/CrocoFactory/sensei)\n\n---\n\n<a href=\"https://pypi.org/project/sensei/\">\n<p align=\"center\">\n<img alt=\"Mindmap\" src=\"https://raw.githubusercontent.com/CrocoFactory/sensei/main/assets/mindmap.svg\" height=\"350px\">\n</p><br>\n</a>\n \nThere are key features provided by `sensei`:\n\n- **Fast:** Do not write any request-handling code, dedicate responsibility to the function's interface(signature) \ud83d\ude80\n- **Short:** Avoid code duplication \ud83e\uddf9 \n- **Sync/Async:** Implement sync and async quickly, without headaches \u26a1\n- **Robust:** Auto validation data before and after request \ud83d\udee1\ufe0f\ufe0f\n\nTable of Contents:\n1. [First Request](#first-request)\n2. [Comparison](#comparison)\n3. [OOP Style](#oop-style)\n4. [Installing](#installing)\n\n## First Request\n\nDo you want to see the simplest and most robust HTTP Request? He's already here!\n\n```python\nfrom typing import Annotated\nfrom sensei import Router, Path, APIModel\n\nrouter = Router('https://pokeapi.co/api/v2/')\n\n\nclass Pokemon(APIModel):\n name: str\n id: int\n height: int\n weight: int\n\n\n@router.get('/pokemon/{name}')\ndef get_pokemon(name: Annotated[str, Path(max_length=300)]) -> Pokemon:\n pass\n\n\npokemon = get_pokemon(name=\"pikachu\")\nprint(pokemon) # Pokemon(name='pikachu' id=25 height=4 weight=60)\n```\n\nDidn't it seem to you that the function doesn't contain the code? **Sensei writes it instead of you!** \n\nMoreover, Sensei abstracts away much of the manual work, letting developers focus on function signatures while the framework\nhandles the API logic and data validation. This enables a declarative style for your apps.\n\nThe example of [First Request](#first-request) demonstrates a simple and robust HTTP request using the Sensei framework.\nHere's the key breakdown of the process:\n\n#### 1. Importing Dependencies:\n\n- `Router` manages API endpoints and routing.\n- `Path` specifies and validates route parameters.\n- `APIModel` defines models for structuring API responses (similar to `pydantic.BaseModel`).\n\n#### 2. Creating the Router:\n\nThe `Router` is initialized with the base URL of the *Pok\u00e9API*. All subsequent requests will use this as the base path.\n\n#### 3. Defining the Model:\n\nThe `Pokemon` class represents the data structure for a Pok\u00e9mon, with fields like `name`, `id`, `height`, and `weight`.\nIt inherits from `APIModel`, which provides validation and serialization.\n\n#### 4. Creating the Endpoint:\n\nThe `get_pokemon` function is a routed function decorated with `@router.get`, defining a GET request for\n`/pokemon/{name}`.\nThis uses `Annotated` to ensure that `name` is a string and adheres to the validation rule (max length of 300).\n\n#### 5. Making the Request:\n\nBy calling `get_pokemon(name=\"pikachu\")`, Sensei automatically handles validation, makes the HTTP request,\nand maps the API response into the `Pokemon` model. The code omits the function body since Sensei handles calls through\nthe function's signature.\n\n## Comparison\n\n**Sensei** \ud83d\udc4d: It provides a high level of abstraction. Sensei simplifies creating API wrappers, offering decorators for \neasy routing, data validation, and automatic mapping of API responses to models. This reduces boilerplate and improves \ncode readability and maintainability.\n\n**Bare HTTP Client** \ud83d\udc4e: A bare HTTP client like `requests` or `httpx` requires manually managing requests, \nhandling response parsing, data validation, and error handling. You have to write repetitive code for each endpoint.\n\n## OOP Style\n\nThere is a wonderful OOP approach proposed by Sensei:\n\n```python\nclass User(APIModel):\n email: EmailStr\n id: PositiveInt\n first_name: str\n last_name: str\n avatar: AnyHttpUrl\n\n @classmethod\n @router.get('/users')\n def query(\n cls,\n page: Annotated[int, Query()] = 1,\n per_page: Annotated[int, Query(le=7)] = 3\n ) -> list[Self]:\n pass\n\n @classmethod\n @router.get('/users/{id_}')\n def get(cls, id_: Annotated[int, Path(alias='id')]) -> Self: \n pass\n\n @router.post('/token')\n def login(self) -> str: \n pass\n\n @login.prepare\n def _login_in(self, args: Args) -> Args:\n args.json_['email'] = self.email\n return args\n\n @login.finalize\n def _login_out(self, response: Response) -> str:\n return response.json()['token']\n\nuser = User.get(1)\nuser.login() # User(id=1, email=\"john@example.com\", first_name=\"John\", ...)\n```\n\nWhen Sensei doesn't know how to handle a request, you can do it yourself, using preprocessing as `prepare` and \npostprocessing as `finalize`\n\n## Installing\nTo install `sensei` from PyPi, you can use that:\n\n```shell\npip install sensei\n```\n\nTo install `sensei` from GitHub, use that:\n\n```shell\npip install git+https://github.com/CrocoFactory/sensei.git\n```",
"bugtrack_url": null,
"license": "MIT",
"summary": "The Python framework that provides a quick way to build robust HTTP requests and best API clients. Use type hints, to build requests, with little or no implementation.",
"version": "0.1.1.post3",
"project_urls": null,
"split_keywords": [
"api",
" client",
" api-client",
" api-wrapper",
" python-client",
" http-client",
" rest-api",
" pydantic",
" httpx",
" http-requests",
" requests"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "007a3fec0d43b621f25c1e3a840d1a6f4e163b6b57eb9b68eea5a14b07749d1a",
"md5": "6a9823ca46ee5f33e2fc6cc3a023cb34",
"sha256": "2bbd8ab1ab8093abafad1f9ee2832b9fa3a990c7b51146f2b3d31fa7b745bdce"
},
"downloads": -1,
"filename": "sensei-0.1.1.post3-py3-none-any.whl",
"has_sig": false,
"md5_digest": "6a9823ca46ee5f33e2fc6cc3a023cb34",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.9",
"size": 39861,
"upload_time": "2024-11-21T20:58:03",
"upload_time_iso_8601": "2024-11-21T20:58:03.061466Z",
"url": "https://files.pythonhosted.org/packages/00/7a/3fec0d43b621f25c1e3a840d1a6f4e163b6b57eb9b68eea5a14b07749d1a/sensei-0.1.1.post3-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "9adaa34ef185a4803cd1b28749f7b718c9a9ef10a0b51cbd2d064331b6e9a4c8",
"md5": "4e557ebed2765ab75309a7893f53daeb",
"sha256": "66b47a863371c47acb3be3b4d3c1f735bb55aeb06c2003647fa2ced721f335e7"
},
"downloads": -1,
"filename": "sensei-0.1.1.post3.tar.gz",
"has_sig": false,
"md5_digest": "4e557ebed2765ab75309a7893f53daeb",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.9",
"size": 76429,
"upload_time": "2024-11-21T20:58:06",
"upload_time_iso_8601": "2024-11-21T20:58:06.987809Z",
"url": "https://files.pythonhosted.org/packages/9a/da/a34ef185a4803cd1b28749f7b718c9a9ef10a0b51cbd2d064331b6e9a4c8/sensei-0.1.1.post3.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-11-21 20:58:06",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "sensei"
}