Skip to content

Commit

Permalink
Merge pull request #70 from ponytailer/use-enum-def-client-type
Browse files Browse the repository at this point in the history
use enum to define the client type
  • Loading branch information
ponytailer authored Oct 9, 2024
2 parents 768e34f + a65aa3b commit c3bad60
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 122 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Only support the json response.
```python
from pydantic import BaseModel

from pydantic_client import delete, get, post, put, pydantic_client_factory
from pydantic_client import delete, get, post, put, pydantic_client_manager
from pydantic_client import ClientConfig


Expand All @@ -33,9 +33,8 @@ class Book(BaseModel):
age: int


@pydantic_client_factory.register(
@pydantic_client_manager.register(
ClientConfig(
client_type="requests",
base_url="https://example.com",
headers={"Authorization": "Bearer abcdefg"},
timeout=10
Expand All @@ -62,7 +61,7 @@ class WebClient:
...


client = pydantic_client_factory.get_client()
client = pydantic_client_manager.get()
book: Book = client.get_book(1)

```
Expand All @@ -74,3 +73,5 @@ And see the examples to get more examples.
### v1.0.0: refactor all the code, to be simple. remove the group client.

### v1.0.1: simple to use.

### v1.0.2: use enum to define the client type.
2 changes: 1 addition & 1 deletion example/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,5 @@ def change_book(self, book_id: int) -> Book:
class AuthorProtocol:

@get("/authors")
def get_author(self) -> Author:
async def get_author(self) -> Author:
...
26 changes: 17 additions & 9 deletions example/use_config_to_init.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
from example.protocol import BookProtocol, Book, AuthorProtocol, Author
from pydantic_client import pydantic_client_factory, ClientConfig
from pydantic_client import pydantic_client_manager, ClientConfig, ClientType

if __name__ == "__main__":

def get_book():
cfg = ClientConfig(
client_type="requests",
base_url="https://example.com/api"
)
pydantic_client_manager.register(cfg)(BookProtocol)
client = pydantic_client_manager.get(BookProtocol)
book: Book = client.get_book(1, "name")
print(book)

pydantic_client_factory.register(cfg)(BookProtocol)
pydantic_client_factory.register(cfg)(AuthorProtocol)

client = pydantic_client_factory.get_client(BookProtocol)
book: Book = client.get_book(1, "name")
async def get_author():
cfg_2 = ClientConfig(
client_type=ClientType.httpx,
base_url="https://example.com/api"
)

pydantic_client_manager.register(cfg_2)(AuthorProtocol)

author_client = pydantic_client_factory.get_client(AuthorProtocol)
author: Author = author_client.get_author()
author_client = pydantic_client_manager.get(AuthorProtocol)
author: Author = await author_client.get_author()
print(author)
10 changes: 5 additions & 5 deletions pydantic_client/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from pydantic_client.decorators import delete, get, patch, post, put
from pydantic_client.factory import PydanticClientFactory
from pydantic_client.main import PydanticClient
from pydantic_client.schema.client_config import ClientConfig
from pydantic_client.main import PydanticClientManager
from pydantic_client.schema.client_config import ClientConfig, ClientType

pydantic_client_factory = PydanticClientFactory()
pydantic_client_manager = PydanticClientManager()

__all__ = [
"pydantic_client_factory",
"pydantic_client_manager",
"ClientConfig",
"ClientType",
"patch",
"get",
"post",
Expand Down
37 changes: 0 additions & 37 deletions pydantic_client/factory.py

This file was deleted.

81 changes: 29 additions & 52 deletions pydantic_client/main.py
Original file line number Diff line number Diff line change
@@ -1,56 +1,33 @@
from typing import Type

from pydantic_client.clients.abstract_client import AbstractClient
from pydantic_client.container import container
from pydantic_client.schema.client_config import ClientConfig


class PydanticClient:
"""
class User(BaseModel):
id: int
name: str
class WebClient:
@get("/users/{user_id}")
def get_user(self, user_id: int) -> User:
...
web_client = PydanticClient.from_toml(...)
.bind_client(RequestsClient)
.bind_protocol(WebClient)
.build()
# or
client: WebClient = PydanticClient(
ClientConfig(
base_url="https://example.com",
headers={"Authorization": "Bearer abcdefg"},
timeout=10
)
).bind_client(RequestsClient)
.bind_protocol(WebClient)
.build()
user: User = web_client.get_user(123)
"""

def __init__(self, config: ClientConfig):
self.config = config
self.client = None
self.protocol = None

def bind_client(self, client_class: Type[AbstractClient]):
self.client = client_class(self.config)
return self

def bind_protocol(self, protocol_class, *args, **kwargs):
self.protocol = protocol_class(*args, **kwargs)
return self

def build(self):
container.bind_protocol(self.protocol, self.client)
return self.protocol
class PydanticClientManager:
def __init__(self):
# proto_class
self.pydantic_clients = {}
# self.config = config

def register(
self,
client_config: ClientConfig
):
def wrapper(protocol_class):
web_client = client_config.get_client()(client_config)
protocol = protocol_class()
container.bind_protocol(protocol, web_client)
self.pydantic_clients[protocol_class] = protocol
return protocol_class

return wrapper

def get(self, protocol_class=None):
if len(self.pydantic_clients) == 1:
return self.pydantic_clients[list(self.pydantic_clients.keys())[0]]
if not protocol_class:
raise ValueError(
"Multiple clients exists, protocol_class is required")
client = self.pydantic_clients.get(protocol_class)
if not client:
raise ValueError(f"client for {protocol_class} not found")
return client
15 changes: 11 additions & 4 deletions pydantic_client/schema/client_config.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
import logging
from typing import Dict, Any, Optional, Literal
from enum import Enum
from typing import Dict, Any, Optional

from pydantic import BaseModel

logger = logging.getLogger(__name__)


class ClientType(str, Enum):
requests = "requests"
httpx = "httpx"
aiohttp = "aiohttp"


class ClientConfig(BaseModel):
# unique name for the client
name: Optional[str] = None
# requests, httpx, aiohttp
client_type: Literal["requests", "httpx", "aiohttp"] = "requests"
client_type: ClientType = ClientType.requests
base_url: str
headers: Dict[str, Any] = {}
# only httpx support http2
http2: bool = False
timeout: Optional[int] = None

def get_client(self):
if self.client_type == "requests":
if self.client_type.value == "requests":
from pydantic_client.clients import RequestsClient
return RequestsClient
elif self.client_type == "httpx":
elif self.client_type.value == "httpx":
from pydantic_client.clients import HttpxClient
return HttpxClient
else:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pydantic-client"
version = "1.0.1"
version = "1.0.2"
description = "Http client base pydantic, with requests or aiohttp"
authors = ["ponytailer <[email protected]>"]
readme = "README.md"
Expand Down
18 changes: 9 additions & 9 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from pydantic_client import delete, get, patch, post, put, \
ClientConfig, pydantic_client_factory
ClientConfig, pydantic_client_manager, ClientType
from tests.book import Book

server_url = "http://localhost:12098"
Expand All @@ -12,19 +12,19 @@
)

config_2 = ClientConfig(
client_type="httpx",
client_type=ClientType.httpx,
base_url=server_url,
timeout=10
)

config_3 = ClientConfig(
client_type="aiohttp",
client_type=ClientType.aiohttp,
base_url=server_url,
timeout=10
)


@pydantic_client_factory.register(config_1)
@pydantic_client_manager.register(config_1)
class R:

@get("/books/{book_id}?query={query}")
Expand Down Expand Up @@ -58,7 +58,7 @@ def patch_book(self, book_id: int, book: Book) -> Book:
...


@pydantic_client_factory.register(config_3)
@pydantic_client_manager.register(config_3)
class AsyncR:
@get("/books/{book_id}?query={query}")
async def get_book(self, book_id: int, query: str) -> Book:
Expand Down Expand Up @@ -91,7 +91,7 @@ async def patch_book(self, book_id: int, book: Book) -> Book:
...


@pydantic_client_factory.register(config_2)
@pydantic_client_manager.register(config_2)
class HttpxR(AsyncR):
...

Expand Down Expand Up @@ -127,8 +127,8 @@ def start_server():

@pytest.fixture
def clients(fastapi_server_url):
client_1 = pydantic_client_factory.get_client(R)
client_2 = pydantic_client_factory.get_client(AsyncR)
client_3 = pydantic_client_factory.get_client(HttpxR)
client_1 = pydantic_client_manager.get(R)
client_2 = pydantic_client_manager.get(AsyncR)
client_3 = pydantic_client_manager.get(HttpxR)

yield client_1, client_2, client_3

0 comments on commit c3bad60

Please sign in to comment.