Metadata-Version: 2.4
Name: retejo
Version: 0.0.4
Summary: Modern python http client
Author-email: Ivan Kirpichnikov <mmssvvvv570@gmail.com>
License: MIT
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development
Classifier: Topic :: System :: Networking
Classifier: Topic :: System :: Distributed Computing
Classifier: Typing :: Typed
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: Environment :: Web Environment
Classifier: Framework :: AsyncIO
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: requests
Requires-Dist: requests<3.0.0,>=2.27.0; extra == "requests"
Provides-Extra: aiohttp
Requires-Dist: aiohttp<4.0.0,>=3.11.0; extra == "aiohttp"
Provides-Extra: adaptix
Requires-Dist: adaptix<4.0.0,>=3.0.0b1; extra == "adaptix"
Provides-Extra: integrations
Requires-Dist: retejo[aiohttp,requests]; extra == "integrations"
Requires-Dist: retejo[adaptix]; extra == "integrations"
Provides-Extra: types
Requires-Dist: types-requests~=2.32.4.20250611; extra == "types"
Provides-Extra: lint
Requires-Dist: mypy==1.16.0; extra == "lint"
Requires-Dist: ruff==0.11.13; extra == "lint"
Requires-Dist: basedmypy==2.10.0; extra == "lint"
Provides-Extra: dev
Requires-Dist: retejo[integrations,lint,types]; extra == "dev"
Dynamic: license-file

# Retejo

## Функции

 - **Валидация с помощью adaptix(и не только):** Используйте библиотеку **adaptix** для парсинга ответов. Вы можете с легкостью заменить **adaptix** на **pydantic**.
 - **Интуитивно понятный интерфейс:** Полностью типизированный клиент упрощает процесс разработки, устроняя множество ошибок ещё до запуска кода.
 - **Интеграции:** **Retejo** поддерживает интеграцию со такими клиентами как: **requests** и **aiohttp**.

## Установка

Вы можете установить **retejo** с помощью **pip**:

```bash
pip install retejo[requests, adaptix]
pip install retejo[aiohttp, adaptix]
```

или **uv**:

```bash
uv pip install retejo[requests, adaptix]
uv pip install retejo[aiohttp, adaptix]
```


## Написание кода.

Для начала нужно объявить модель, например, dataclass.

```python
@dataclass
class Post:
    id: int
    title: str
    body: str
    user_id: int

@dataclass
class PostId:
    id: int
```

Далее объявить метод.

В **retejo** каждый метод наследуется от базового класса Method. При наследовании в Method передается модель, которая описывает ответ метода.

Так же, нужно указать часть пусти к ендпоинту и тип метода.

```python
class GetPost(Method[Post]):
    __url__ = "posts/{id}"
    __method__ = "get"

    id: UrlVar[int]

class CreatePost(Method[PostId]):
    __url__ = "posts"
    __method__ = "post"

    user_id: Body[int]
    title: Body[str]
    body: Body[str]
```

Теперь клиент. В super передаем базовую ссылку.

Так же переопределяем логику парсинга ответа.
За большей информацией по adaptix обратитесь к [его документации](https://adaptix.readthedocs.io/).

```python
class Client(RequestsAdaptixClient):
    def __init__(self) -> None:
        super().__init__("https://jsonplaceholder.typicode.com/")

    @override
    def init_response_factory(self) -> Factory:
        return Retort(
            recipe=[
                # поля в ответе вида camelCase
                # будут конвертированы в lower_case.
                name_mapping(
                    name_style=NameStyle.CAMEL,
                ),
            ]
        )
```

Далее нужно привязать методы к клиенту. Для этого используется функция bind_method.

```python
class Client(RequestsAdaptixClient):
    # ...
    get_post = bind_method(GetPost)
    create_post = bind_method(CreatePost)
```

Вы наверное подумаете, что из за этого не будет работать типизация. Наоборот, поведение будет как у метода, которая принимает аргументы для инициализации метода и возвращает ответ.

Например строка.

```python
get_post = bind_method(GetPost)
```

Эквивалентно следующему.

```python
def get_post(self, id: int) -> Post:
    return self.send_method(
        GetPost(
            id=id,
        ),
    )
```

Типизация работает на все 100%.

И, конечно же, использование клиента.

```python
client = Client()
created_post = client.create_post(
    user_id=1,
    title="Title",
    body="Body"
)
got_post = client.get_post(created_post.id)
client.close()
```

Весь код.
```python
@dataclass
class Post:
    id: int
    title: str
    body: str
    user_id: int

@dataclass
class PostId:
    id: int

class GetPost(Method[Post]):
    __url__ = "posts/{id}"
    __method__ = "get"

    id: UrlVar[int]

class CreatePost(Method[PostId]):
    __url__ = "posts"
    __method__ = "post"

    user_id: Body[int]
    title: Body[str]
    body: Body[str]


class Client(RequestsAdaptixClient):
    def __init__(self) -> None:
        super().__init__(base_url="https://jsonplaceholder.typicode.com/")

    @override
    def init_response_factory(self) -> Factory:
        return Retort(
            recipe=[
                name_mapping(name_style=NameStyle.CAMEL),
            ]
        )

    get_post = bind_method(GetPost)
    create_post = bind_method(CreatePost)


client = Client()
created_post = client.create_post(
    user_id=1,
    title="Title",
    body="Body"
)
got_post = client.get_post(created_post.id)
client.close()
```

## Асинхронность.

Для того, чтобы использовать асинхронный подход, нужно:

1. Установить retejo с aiohttp.

    ```bash
    pip install retejo[aiohttp, adaptix]
    ```

2. Наследоваться от AiohttpAdaptixClient.

3. Вызывать все методы клиента асинхронно.

    ```py
    class Client(AiohttpAdaptixClient):
        def __init__(self) -> None:
            super().__init__(base_url="https://jsonplaceholder.typicode.com/")

    @override
    def init_response_factory(self) -> Factory:
        return Retort(
            recipe=[
                name_mapping(name_style=NameStyle.CAMEL),
            ]
        )

        get_post = bind_method(GetPost)
        create_post = bind_method(CreatePost)


    client = Client()
    created_post = await client.create_post(
        user_id=1,
        title="Title",
        body="Body"
    )
    got_post = await client.get_post(created_post.id)
    client.close()
    ```


## Маркеры

**Retejo** предоставляет несколько базовых маркеров для ваших методов:

- **Body** - параметр должен передано в **тело запроса**.

- **File** - параметр является **файлом**.

- **Header** - параметр должен передано в **заголовки запроса**.

- **QueryParam** - параметр должен передано в **параметры запроса**.

- **UrlVar** - параметр используется для **форматирования ссылки**.

- **Omittable** - этот флаг комбинируется с выше перечисленными. 

    Если значение параметра является объект **Omitted**, то это поле не попадет в запрос.

Пример использования.

```python
class AddModel(Method[Any]):
    __url__ = "user/{user_id}/models"
    __method__ = "post"

    user_id: UrlVar[int]
    model: Body[str]
    access_token: Header[str]
    number: Body[Omittable[str]] = Omitted()


class Client(RequestsAdaptixClient):
    def __init__(self) -> None:
        super().__init__("")

    @override
    def init_markers_factories(self) -> MarkersFactorties:
        factories = super().init_markers_factories()
        factories[HeaderMarker] =  Retort(
            recipe=[
                dumper(
                    P[AddModel].access_token,
                    lambda x: f"Bearer {x}",
                ),
                name_mapping(
                    AddModel,
                    map={
                        "access_token": "Authorization",
                    },
                ),
            ],
        )
        return factories

    add_model = bind_method(AddModel)
```
