meatie


Namemeatie JSON
Version 0.1.3 PyPI version JSON
download
home_pagehttps://github.com/pmateusz/meatie
SummaryMeatie is a Python typed REST client library that eliminates the need for boilerplate code when integrating with external APIs. The library generates code for calling a REST API based on method signatures annotated with type hints. Ribeye abstracts away mechanics related to HTTP communication, such as building URLs, encoding query parameters, parsing, and dumping Pydantic models. With some modest additional configuration effort, generated HTTP clients offer rate limiting, retries, and caching.
upload_time2024-03-27 09:59:34
maintainerNone
docs_urlNone
authorpmateusz
requires_python>=3.9
licenseBSD-3-Clause
keywords http-client metaprogramming rest http api requests httpx aiohttp pydantic type-hints
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Meatie: Generate methods for calling REST APIs using type hints

[![GitHub Test Badge][1]][2] [![codecov.io][3]][4] [![pypi.org][5]][6] [![versions][7]][8]
[![downloads][9]][10] [![License][11]][12]

[1]: https://github.com/pmateusz/meatie/actions/workflows/ci.yaml/badge.svg "GitHub CI Badge"

[2]: https://github.com/pmateusz/meatie/actions/workflows/ci.yaml "GitHub Actions Page"

[3]: https://codecov.io/gh/pmateusz/meatie/branch/master/graph/badge.svg?branch=master "Coverage Badge"

[4]: https://codecov.io/gh/pmateusz/meatie?branch=master "Codecov site"

[5]: https://img.shields.io/pypi/v/meatie.svg "Pypi Latest Version Badge"

[6]: https://pypi.python.org/pypi/meatie "Pypi site"

[7]:https://img.shields.io/pypi/pyversions/meatie.svg

[8]: https://github.com/pmateusz/meatie

[9]: https://static.pepy.tech/badge/meatie

[10]: https://pepy.tech/project/meatie

[11]: https://img.shields.io/github/license/pmateusz/meatie "License Badge"

[12]: https://opensource.org/license/bsd-3-clause "License"

Meatie is a Python metaprogramming library that eliminates the need for boilerplate code when integrating with REST
APIs. The library generates code for calling a REST API based on method signatures annotated with type hints. Meatie
abstracts away mechanics related to HTTP communication, such as building URLs, encoding query parameters, serializing
and deserializing request and response body. With some modest additional configuration, generated methods provide rate
limiting, retries, and caching. Meatie works with major HTTP client libraries (request, httpx, aiohttp). It offers
integration with Pydantic V1 and V2. The minimum officially supported version is Python 3.9.

## TL;DR

Generate HTTP clients using type annotations.

```python
from typing import Annotated
from aiohttp import ClientSession
from meatie import api_ref, endpoint
from meatie_aiohttp import Client
from meatie_example.store import Product, Basket, BasketQuote  # Pydantic models


class OnlineStore(Client):
    def __init__(self, session: ClientSession) -> None:
        super().__init__(session)

    @endpoint("/api/v1/products")
    async def get_products(self) -> list[Product]:
        # Sends an HTTP GET request and parses response's body using Pydantic to list[Product]
        ...

    @endpoint("/api/v1/quote/request")
    async def post_request_quote(self, basket: Annotated[Basket, api_ref("body")]) -> BasketQuote:
        # Dumps a Pydantic model :basket to JSON and sends it as payload of an HTTP POST request.
        ...

    @endpoint("/api/v1/quote/{quote_id}/accept")
    async def post_accept_quote(self, quote_id: int) -> None:
        # URLs can reference method parameters. Parameters not referenced in the URL are sent as HTTP query params.
        ...
```

### HTTP Client Support

Meatie supports leading HTTP client libraries: `requests`, `httpx`, and `aiohttp`.

#### Requests

```python
from meatie import endpoint
from meatie_requests import Client
from requests import Session
from meatie_example.store import Product


class OnlineStore(Client):
    def __init__(self) -> None:
        super().__init__(Session())

    @endpoint("https://test.store.com/api/v1/products")  # requests does not support base_url
    def get_products(self) -> list[Product]:
        ...
```

#### HTTPX

```python
from meatie import endpoint
from meatie_httpx import Client
import httpx
from meatie_example.store import Product


class OnlineStore(Client):
    def __init__(self) -> None:
        super().__init__(httpx.Client(base_url="https://test.store.com"))

    @endpoint("/api/v1/products")
    def get_products(self) -> list[Product]:
        ...
```

#### Aiohttp

```python
from aiohttp import ClientSession
from meatie import endpoint
from meatie_aiohttp import Client
from meatie_example.store import Product


class OnlineStore(Client):
    def __init__(self) -> None:
        super().__init__(ClientSession(base_url="https://test.store.com"))

    @endpoint("/api/v1/products")
    async def get_products(self) -> list[Product]:
        ...
```

### Cache

Cache result for given TTL.

```python
from aiohttp import ClientSession
from meatie import endpoint, cache, HOUR
from meatie_aiohttp import Client
from meatie_example.store import Product


class OnlineStore(Client):
    def __init__(self) -> None:
        super().__init__(ClientSession(base_url="https://test.store.com"))

    @endpoint("/api/v1/products", cache(ttl=4 * HOUR))
    async def get_products(self) -> list[Product]:
        ...
```

A cache key is built based on the URL path and query parameters. It does not include the scheme and the network
location.

By default, every instance of an HTTP client uses an independent cache. The behavior can be changed in the endpoint
definition to share cached results across all instances of the same HTTP client class.

```python
@endpoint("/api/v1/products", cache(ttl=4 * HOUR, shared=True))
async def get_products(self) -> list[Product]:
    ...
```

### Rate Limiting

Commercial-grade publicly available REST APIs enforce rate limit policies (a.k.a. throttling) to slow down fast
consumers. Consequently, a system can maintain a fair allocation of computational resources across all consumers. Rate
limit policies define the cost of calling an endpoint using credits (or tokens). Every consumer has some credit
allowance and replenishment rate. For instance, 300 credits constitute the initial budget, and one new credit unit
becomes available every second. The server rejects API calls that exceed the rate limit. Disobedient clients who
constantly violate rate limits are punished via a temporary lockout.

Meatie supports a rate limit policy definition in the endpoint description. Meatie delays the HTTP requests that exceed
the rate limit. Triggering the rate limit by the server has much more severe consequences than delaying a call on the
client that otherwise is very likely to be rejected anyway.

```python
from aiohttp import ClientSession
from meatie import endpoint, limit, Limiter, Rate
from meatie_aiohttp import Client
from meatie_example.store import Product


class OnlineStore(Client):
    def __init__(self) -> None:
        super().__init__(ClientSession(base_url="https://test.store.com"),
                         limiter=Limiter(rate=Rate(tokens_per_sec=1), capacity=300))

    @endpoint("/api/v1/products", limit(tokens=30))
    async def get_products(self) -> list[Product]:
        ...
```

### Retry

The retry mechanism is an inevitable part of a robust error-handling strategy for dealing with intermittent errors. In
the scope of HTTP integrations, reasonable candidates for a retry are HTTP response errors status 429 (Too Many
Requests) and network connectivity issues (i.e., timeout, connection reset).

Meatie enables a retry strategy in the endpoint definition and allows further customization of the strategy by plugging
in third-party functions. They control whether to make a retry attempt, for how long to wait between retries, which
sleep function to use for waiting, and whether to abort further retries.

```python
from http import HTTPStatus
from aiohttp import ClientSession
from meatie import (
    endpoint,
    retry,
    has_status,
    exponential,
    after_attempt,
)
from meatie_aiohttp import Client
from meatie_example.store import Product


class OnlineStore(Client):
    def __init__(self) -> None:
        super().__init__(ClientSession(base_url="https://test.store.com"))

    @endpoint("/api/v1/products", retry(on=has_status(HTTPStatus.TOO_MANY_REQUESTS),
                                        wait=exponential(),
                                        stop=after_attempt(5)))
    async def get_products(self) -> list[Product]:
        ...
```

Meatie provides some standard wait strategies, retry conditions, and stop conditions, such
as: `RetryOnStatusCode(status: int)` or `RetryOnExceptionType(exc_type: type[BaseException])`.

### Private Endpoints

Some REST API endpoints are private, i.e., calling them requires prior authentication to obtain a token that should be
present in the HTTP headers of a request. An alternative approach popular for backend-side integration is to sign a
request using a secret only authorized clients should know.

Meatie simplifies integration with endpoints that require authentication by marking as `Private`. Before calling such an
endpoint, the Meatie executes the `authenticate` method the HTTP client should implement. The implemementation should
obtain a token and add it to the HTTP headers of the pending request. Alternatively, the `authenticate` method should
sign the pending request using API keys.

The example below illustrates signing requests to Binance private endpoints using API keys.

```python
import hashlib
import hmac
import time
import urllib.parse
from decimal import Decimal
from typing import Optional

from aiohttp import ClientSession
from meatie import (
    endpoint,
    private,
    Request,
)
from meatie_aiohttp import Client
from pydantic import BaseModel, Field, AnyHttpUrl


class AssetWalletBalance(BaseModel):
    activate: bool
    balance: Decimal
    wallet_name: str = Field(alias="walletName")


class Binance(Client):
    def __init__(
            self,
            api_key: Optional[str] = None,
            secret: Optional[str] = None,
    ) -> None:
        super().__init__(
            ClientSession(base_url="https://api.binance.com"),
        )
        self.api_key = api_key
        self.secret = secret

    async def authenticate(self, request: Request) -> None:
        if self.api_key is None:
            raise RuntimeError("'api_key' is None")

        if self.secret is None:
            raise RuntimeError("'secret' is None")

        request.headers["X-MBX-APIKEY"] = self.api_key
        request.params["timestamp"] = int(time.monotonic() * 1000)

        query_params = urllib.parse.urlencode(request.params)
        raw_signature = hmac.new(
            self.secret.encode("utf-8"), query_params.encode("utf-8"), hashlib.sha256
        )
        signature = raw_signature.hexdigest()
        request.params["signature"] = signature

    @endpoint("/sapi/v1/asset/wallet/balance", private)
    async def get_asset_wallet_balance(self) -> list[AssetWalletBalance]:
        ...
```

### Endpoint Customizations

#### Pydantic integration is optional

Pydantic integration is entirely optional. Projects that don't use Pydantic might instead process the response body as
string, binary, or JSON. Pydantic integration becomes available when 1) Pydantic library is installed and 2) the return
type of a method marked with `@endpoint` decorator can be parsed to a Pydantic model. A type can be parsed to a Pydantic
if it inherits from BaseModel, is a data class, or a typed dictionary. The rule extends to container types. A container
could also be a Sequence of Pydantic convertible items or a Mapping in with Pydantic convertible type as values.

Return `meatie.AsyncResponse` directly.

```python
from meatie import AsyncResponse, endpoint


@endpoint("/api/v1/orders")
async def get_orders(self) -> AsyncResponse:
    ...
```

Return HTTP response payload as `bytes`.

```python
@endpoint("/api/v1/orders")
async def get_orders(self) -> bytes:
    ...
```

Return HTTP response payload as text.

```python
@endpoint("/api/v1/orders")
async def get_orders(self) -> str:
    ...
```

Return HTTP response as JSON.

```python
@endpoint("/api/v1/orders")
async def get_orders(self) -> list:
    ...
```

#### Rename query parameters

It might be more convenient to use a different name for a method parameter than the query parameter name defined by the
REST API.

```python
from typing import Annotated
from meatie import api_ref, endpoint


@endpoint("/api/v1/orders")
async def get_orders(self, since_ms: Annotated[int, api_ref("since")]) -> list[dict]:
    ...
```

#### Define the HTTP method

There is no need to use HTTP methods as prefixes.

```python
@endpoint("/api/v1/orders", method="GET")
async def list_orders(self) -> list[dict]:
    ...
```

#### Preprocessing of HTTP requests or postprocessing HTTP responses

You may need to go beyond features provided by Meatie and extra pre-processing or post-processing steps for handling
HTTP requests and responses. For instance, you may want to employ a distributed cache using Redis or add logging for
HTTP communication.

Meatie architecture supports extensions by third-party middleware (i.e., the adapter pattern) with no modifications to
the core library. The code snippet below presents a simple processing step that passes a request unchanged to the
subsequent step in the pipeline. Similarly, once the return result becomes available, it is passed back to the previous
step in the pipeline.

```python
from typing import TypeVar
from meatie import Context

T = TypeVar


def step(ctx: Context[T]) -> T:
    """
    Middleware for synchronous HTTP clients (requests and httpx)
    """

    # ctx.request contains HTTP request

    result: T = ctx.proceed()  # move HTTP requests to the next step of the pipeline

    # ctx.response contains HTTP response
    return result
```

```python
from meatie import AsyncContext


async def step(ctx: AsyncContext[T]) -> T:
    """
    Middleware for asynchronous HTTP client (aiohttp).
    """

    return await ctx.proceed()
```

Features highlighted in the readme, such as rate limiting, client-side caching, and retries, are all implemented
following the adapter pattern. The interested reader is welcome to review their implementation in the `meatie.option`
package.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/pmateusz/meatie",
    "name": "meatie",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "http-client, metaprogramming, REST, HTTP, API, requests, httpx, aiohttp, pydantic, type-hints",
    "author": "pmateusz",
    "author_email": "pmateusz@users.noreply.github.com",
    "download_url": "https://files.pythonhosted.org/packages/2d/50/d48bf4a53ce772a6b0bdd284f173a0423b9baa98fd028bc0e0c1d2949d51/meatie-0.1.3.tar.gz",
    "platform": null,
    "description": "# Meatie: Generate methods for calling REST APIs using type hints\n\n[![GitHub Test Badge][1]][2] [![codecov.io][3]][4] [![pypi.org][5]][6] [![versions][7]][8]\n[![downloads][9]][10] [![License][11]][12]\n\n[1]: https://github.com/pmateusz/meatie/actions/workflows/ci.yaml/badge.svg \"GitHub CI Badge\"\n\n[2]: https://github.com/pmateusz/meatie/actions/workflows/ci.yaml \"GitHub Actions Page\"\n\n[3]: https://codecov.io/gh/pmateusz/meatie/branch/master/graph/badge.svg?branch=master \"Coverage Badge\"\n\n[4]: https://codecov.io/gh/pmateusz/meatie?branch=master \"Codecov site\"\n\n[5]: https://img.shields.io/pypi/v/meatie.svg \"Pypi Latest Version Badge\"\n\n[6]: https://pypi.python.org/pypi/meatie \"Pypi site\"\n\n[7]:https://img.shields.io/pypi/pyversions/meatie.svg\n\n[8]: https://github.com/pmateusz/meatie\n\n[9]: https://static.pepy.tech/badge/meatie\n\n[10]: https://pepy.tech/project/meatie\n\n[11]: https://img.shields.io/github/license/pmateusz/meatie \"License Badge\"\n\n[12]: https://opensource.org/license/bsd-3-clause \"License\"\n\nMeatie is a Python metaprogramming library that eliminates the need for boilerplate code when integrating with REST\nAPIs. The library generates code for calling a REST API based on method signatures annotated with type hints. Meatie\nabstracts away mechanics related to HTTP communication, such as building URLs, encoding query parameters, serializing\nand deserializing request and response body. With some modest additional configuration, generated methods provide rate\nlimiting, retries, and caching. Meatie works with major HTTP client libraries (request, httpx, aiohttp). It offers\nintegration with Pydantic V1 and V2. The minimum officially supported version is Python 3.9.\n\n## TL;DR\n\nGenerate HTTP clients using type annotations.\n\n```python\nfrom typing import Annotated\nfrom aiohttp import ClientSession\nfrom meatie import api_ref, endpoint\nfrom meatie_aiohttp import Client\nfrom meatie_example.store import Product, Basket, BasketQuote  # Pydantic models\n\n\nclass OnlineStore(Client):\n    def __init__(self, session: ClientSession) -> None:\n        super().__init__(session)\n\n    @endpoint(\"/api/v1/products\")\n    async def get_products(self) -> list[Product]:\n        # Sends an HTTP GET request and parses response's body using Pydantic to list[Product]\n        ...\n\n    @endpoint(\"/api/v1/quote/request\")\n    async def post_request_quote(self, basket: Annotated[Basket, api_ref(\"body\")]) -> BasketQuote:\n        # Dumps a Pydantic model :basket to JSON and sends it as payload of an HTTP POST request.\n        ...\n\n    @endpoint(\"/api/v1/quote/{quote_id}/accept\")\n    async def post_accept_quote(self, quote_id: int) -> None:\n        # URLs can reference method parameters. Parameters not referenced in the URL are sent as HTTP query params.\n        ...\n```\n\n### HTTP Client Support\n\nMeatie supports leading HTTP client libraries: `requests`, `httpx`, and `aiohttp`.\n\n#### Requests\n\n```python\nfrom meatie import endpoint\nfrom meatie_requests import Client\nfrom requests import Session\nfrom meatie_example.store import Product\n\n\nclass OnlineStore(Client):\n    def __init__(self) -> None:\n        super().__init__(Session())\n\n    @endpoint(\"https://test.store.com/api/v1/products\")  # requests does not support base_url\n    def get_products(self) -> list[Product]:\n        ...\n```\n\n#### HTTPX\n\n```python\nfrom meatie import endpoint\nfrom meatie_httpx import Client\nimport httpx\nfrom meatie_example.store import Product\n\n\nclass OnlineStore(Client):\n    def __init__(self) -> None:\n        super().__init__(httpx.Client(base_url=\"https://test.store.com\"))\n\n    @endpoint(\"/api/v1/products\")\n    def get_products(self) -> list[Product]:\n        ...\n```\n\n#### Aiohttp\n\n```python\nfrom aiohttp import ClientSession\nfrom meatie import endpoint\nfrom meatie_aiohttp import Client\nfrom meatie_example.store import Product\n\n\nclass OnlineStore(Client):\n    def __init__(self) -> None:\n        super().__init__(ClientSession(base_url=\"https://test.store.com\"))\n\n    @endpoint(\"/api/v1/products\")\n    async def get_products(self) -> list[Product]:\n        ...\n```\n\n### Cache\n\nCache result for given TTL.\n\n```python\nfrom aiohttp import ClientSession\nfrom meatie import endpoint, cache, HOUR\nfrom meatie_aiohttp import Client\nfrom meatie_example.store import Product\n\n\nclass OnlineStore(Client):\n    def __init__(self) -> None:\n        super().__init__(ClientSession(base_url=\"https://test.store.com\"))\n\n    @endpoint(\"/api/v1/products\", cache(ttl=4 * HOUR))\n    async def get_products(self) -> list[Product]:\n        ...\n```\n\nA cache key is built based on the URL path and query parameters. It does not include the scheme and the network\nlocation.\n\nBy default, every instance of an HTTP client uses an independent cache. The behavior can be changed in the endpoint\ndefinition to share cached results across all instances of the same HTTP client class.\n\n```python\n@endpoint(\"/api/v1/products\", cache(ttl=4 * HOUR, shared=True))\nasync def get_products(self) -> list[Product]:\n    ...\n```\n\n### Rate Limiting\n\nCommercial-grade publicly available REST APIs enforce rate limit policies (a.k.a. throttling) to slow down fast\nconsumers. Consequently, a system can maintain a fair allocation of computational resources across all consumers. Rate\nlimit policies define the cost of calling an endpoint using credits (or tokens). Every consumer has some credit\nallowance and replenishment rate. For instance, 300 credits constitute the initial budget, and one new credit unit\nbecomes available every second. The server rejects API calls that exceed the rate limit. Disobedient clients who\nconstantly violate rate limits are punished via a temporary lockout.\n\nMeatie supports a rate limit policy definition in the endpoint description. Meatie delays the HTTP requests that exceed\nthe rate limit. Triggering the rate limit by the server has much more severe consequences than delaying a call on the\nclient that otherwise is very likely to be rejected anyway.\n\n```python\nfrom aiohttp import ClientSession\nfrom meatie import endpoint, limit, Limiter, Rate\nfrom meatie_aiohttp import Client\nfrom meatie_example.store import Product\n\n\nclass OnlineStore(Client):\n    def __init__(self) -> None:\n        super().__init__(ClientSession(base_url=\"https://test.store.com\"),\n                         limiter=Limiter(rate=Rate(tokens_per_sec=1), capacity=300))\n\n    @endpoint(\"/api/v1/products\", limit(tokens=30))\n    async def get_products(self) -> list[Product]:\n        ...\n```\n\n### Retry\n\nThe retry mechanism is an inevitable part of a robust error-handling strategy for dealing with intermittent errors. In\nthe scope of HTTP integrations, reasonable candidates for a retry are HTTP response errors status 429 (Too Many\nRequests) and network connectivity issues (i.e., timeout, connection reset).\n\nMeatie enables a retry strategy in the endpoint definition and allows further customization of the strategy by plugging\nin third-party functions. They control whether to make a retry attempt, for how long to wait between retries, which\nsleep function to use for waiting, and whether to abort further retries.\n\n```python\nfrom http import HTTPStatus\nfrom aiohttp import ClientSession\nfrom meatie import (\n    endpoint,\n    retry,\n    has_status,\n    exponential,\n    after_attempt,\n)\nfrom meatie_aiohttp import Client\nfrom meatie_example.store import Product\n\n\nclass OnlineStore(Client):\n    def __init__(self) -> None:\n        super().__init__(ClientSession(base_url=\"https://test.store.com\"))\n\n    @endpoint(\"/api/v1/products\", retry(on=has_status(HTTPStatus.TOO_MANY_REQUESTS),\n                                        wait=exponential(),\n                                        stop=after_attempt(5)))\n    async def get_products(self) -> list[Product]:\n        ...\n```\n\nMeatie provides some standard wait strategies, retry conditions, and stop conditions, such\nas: `RetryOnStatusCode(status: int)` or `RetryOnExceptionType(exc_type: type[BaseException])`.\n\n### Private Endpoints\n\nSome REST API endpoints are private, i.e., calling them requires prior authentication to obtain a token that should be\npresent in the HTTP headers of a request. An alternative approach popular for backend-side integration is to sign a\nrequest using a secret only authorized clients should know.\n\nMeatie simplifies integration with endpoints that require authentication by marking as `Private`. Before calling such an\nendpoint, the Meatie executes the `authenticate` method the HTTP client should implement. The implemementation should\nobtain a token and add it to the HTTP headers of the pending request. Alternatively, the `authenticate` method should\nsign the pending request using API keys.\n\nThe example below illustrates signing requests to Binance private endpoints using API keys.\n\n```python\nimport hashlib\nimport hmac\nimport time\nimport urllib.parse\nfrom decimal import Decimal\nfrom typing import Optional\n\nfrom aiohttp import ClientSession\nfrom meatie import (\n    endpoint,\n    private,\n    Request,\n)\nfrom meatie_aiohttp import Client\nfrom pydantic import BaseModel, Field, AnyHttpUrl\n\n\nclass AssetWalletBalance(BaseModel):\n    activate: bool\n    balance: Decimal\n    wallet_name: str = Field(alias=\"walletName\")\n\n\nclass Binance(Client):\n    def __init__(\n            self,\n            api_key: Optional[str] = None,\n            secret: Optional[str] = None,\n    ) -> None:\n        super().__init__(\n            ClientSession(base_url=\"https://api.binance.com\"),\n        )\n        self.api_key = api_key\n        self.secret = secret\n\n    async def authenticate(self, request: Request) -> None:\n        if self.api_key is None:\n            raise RuntimeError(\"'api_key' is None\")\n\n        if self.secret is None:\n            raise RuntimeError(\"'secret' is None\")\n\n        request.headers[\"X-MBX-APIKEY\"] = self.api_key\n        request.params[\"timestamp\"] = int(time.monotonic() * 1000)\n\n        query_params = urllib.parse.urlencode(request.params)\n        raw_signature = hmac.new(\n            self.secret.encode(\"utf-8\"), query_params.encode(\"utf-8\"), hashlib.sha256\n        )\n        signature = raw_signature.hexdigest()\n        request.params[\"signature\"] = signature\n\n    @endpoint(\"/sapi/v1/asset/wallet/balance\", private)\n    async def get_asset_wallet_balance(self) -> list[AssetWalletBalance]:\n        ...\n```\n\n### Endpoint Customizations\n\n#### Pydantic integration is optional\n\nPydantic integration is entirely optional. Projects that don't use Pydantic might instead process the response body as\nstring, binary, or JSON. Pydantic integration becomes available when 1) Pydantic library is installed and 2) the return\ntype of a method marked with `@endpoint` decorator can be parsed to a Pydantic model. A type can be parsed to a Pydantic\nif it inherits from BaseModel, is a data class, or a typed dictionary. The rule extends to container types. A container\ncould also be a Sequence of Pydantic convertible items or a Mapping in with Pydantic convertible type as values.\n\nReturn `meatie.AsyncResponse` directly.\n\n```python\nfrom meatie import AsyncResponse, endpoint\n\n\n@endpoint(\"/api/v1/orders\")\nasync def get_orders(self) -> AsyncResponse:\n    ...\n```\n\nReturn HTTP response payload as `bytes`.\n\n```python\n@endpoint(\"/api/v1/orders\")\nasync def get_orders(self) -> bytes:\n    ...\n```\n\nReturn HTTP response payload as text.\n\n```python\n@endpoint(\"/api/v1/orders\")\nasync def get_orders(self) -> str:\n    ...\n```\n\nReturn HTTP response as JSON.\n\n```python\n@endpoint(\"/api/v1/orders\")\nasync def get_orders(self) -> list:\n    ...\n```\n\n#### Rename query parameters\n\nIt might be more convenient to use a different name for a method parameter than the query parameter name defined by the\nREST API.\n\n```python\nfrom typing import Annotated\nfrom meatie import api_ref, endpoint\n\n\n@endpoint(\"/api/v1/orders\")\nasync def get_orders(self, since_ms: Annotated[int, api_ref(\"since\")]) -> list[dict]:\n    ...\n```\n\n#### Define the HTTP method\n\nThere is no need to use HTTP methods as prefixes.\n\n```python\n@endpoint(\"/api/v1/orders\", method=\"GET\")\nasync def list_orders(self) -> list[dict]:\n    ...\n```\n\n#### Preprocessing of HTTP requests or postprocessing HTTP responses\n\nYou may need to go beyond features provided by Meatie and extra pre-processing or post-processing steps for handling\nHTTP requests and responses. For instance, you may want to employ a distributed cache using Redis or add logging for\nHTTP communication.\n\nMeatie architecture supports extensions by third-party middleware (i.e., the adapter pattern) with no modifications to\nthe core library. The code snippet below presents a simple processing step that passes a request unchanged to the\nsubsequent step in the pipeline. Similarly, once the return result becomes available, it is passed back to the previous\nstep in the pipeline.\n\n```python\nfrom typing import TypeVar\nfrom meatie import Context\n\nT = TypeVar\n\n\ndef step(ctx: Context[T]) -> T:\n    \"\"\"\n    Middleware for synchronous HTTP clients (requests and httpx)\n    \"\"\"\n\n    # ctx.request contains HTTP request\n\n    result: T = ctx.proceed()  # move HTTP requests to the next step of the pipeline\n\n    # ctx.response contains HTTP response\n    return result\n```\n\n```python\nfrom meatie import AsyncContext\n\n\nasync def step(ctx: AsyncContext[T]) -> T:\n    \"\"\"\n    Middleware for asynchronous HTTP client (aiohttp).\n    \"\"\"\n\n    return await ctx.proceed()\n```\n\nFeatures highlighted in the readme, such as rate limiting, client-side caching, and retries, are all implemented\nfollowing the adapter pattern. The interested reader is welcome to review their implementation in the `meatie.option`\npackage.\n",
    "bugtrack_url": null,
    "license": "BSD-3-Clause",
    "summary": "Meatie is a Python typed REST client library that eliminates the need for boilerplate code when integrating with external APIs. The library generates code for calling a REST API based on method signatures annotated with type hints. Ribeye abstracts away mechanics related to HTTP communication, such as building URLs, encoding query parameters, parsing, and dumping Pydantic models. With some modest additional configuration effort, generated HTTP clients offer rate limiting, retries, and caching.",
    "version": "0.1.3",
    "project_urls": {
        "Homepage": "https://github.com/pmateusz/meatie",
        "Repository": "https://github.com/pmateusz/meatie"
    },
    "split_keywords": [
        "http-client",
        " metaprogramming",
        " rest",
        " http",
        " api",
        " requests",
        " httpx",
        " aiohttp",
        " pydantic",
        " type-hints"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "7d0a9cdf92adaf0a9fabcfcf0745d3b15daaffb619fc5529092fff599e7f2e4a",
                "md5": "df67103a1a044561b759fed4774e62cd",
                "sha256": "af4122166e8ed13b48ff212fc748d37da1cb7168307379e376a76509bf3a7bc4"
            },
            "downloads": -1,
            "filename": "meatie-0.1.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "df67103a1a044561b759fed4774e62cd",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 51165,
            "upload_time": "2024-03-27T09:59:32",
            "upload_time_iso_8601": "2024-03-27T09:59:32.374209Z",
            "url": "https://files.pythonhosted.org/packages/7d/0a/9cdf92adaf0a9fabcfcf0745d3b15daaffb619fc5529092fff599e7f2e4a/meatie-0.1.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "2d50d48bf4a53ce772a6b0bdd284f173a0423b9baa98fd028bc0e0c1d2949d51",
                "md5": "0dfe3ec51ff146e766e9e118a14fc8f6",
                "sha256": "8a0bfab7549fbc17e1abf78bd754962143afba6931196dcefe8fa962aa428ff2"
            },
            "downloads": -1,
            "filename": "meatie-0.1.3.tar.gz",
            "has_sig": false,
            "md5_digest": "0dfe3ec51ff146e766e9e118a14fc8f6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 26347,
            "upload_time": "2024-03-27T09:59:34",
            "upload_time_iso_8601": "2024-03-27T09:59:34.687015Z",
            "url": "https://files.pythonhosted.org/packages/2d/50/d48bf4a53ce772a6b0bdd284f173a0423b9baa98fd028bc0e0c1d2949d51/meatie-0.1.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-27 09:59:34",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "pmateusz",
    "github_project": "meatie",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "meatie"
}
        
Elapsed time: 0.22236s