Skip to content

Commit 302b5b0

Browse files
ponytailerCopilot
andauthored
Dev2.0 (#121)
* feat: Implement refactored client structure with multiple HTTP client support (#102) * Create example.py add examples * refactor client * feat: Implement refactored client structure with multiple HTTP client support (#103) * Create example.py add examples * refactor client * fix tests * Initial plan (#105) * Create comprehensive README.md documentation for pydantic-client library (#104) * Initial plan * Create comprehensive README.md with complete documentation Co-authored-by: ponytailer <6038418+ponytailer@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ponytailer <6038418+ponytailer@users.noreply.github.com> * Update README.md * Add support for Pydantic models, form data, and custom headers in decorators (#107) * Initial plan * Add support for Pydantic models, form data, and custom headers in decorators Co-authored-by: ponytailer <6038418+ponytailer@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ponytailer <6038418+ponytailer@users.noreply.github.com> * Switch from Poetry to uv and simplify decorators (#120) * Initial plan * Switch from Poetry to uv for dependency management Co-authored-by: ponytailer <6038418+ponytailer@users.noreply.github.com> * Simplify decorators: extract common logic, improve type hints and documentation Co-authored-by: ponytailer <6038418+ponytailer@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ponytailer <6038418+ponytailer@users.noreply.github.com> * fix lint * support api2tools * install dev deps * fix tests deps --------- Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: ponytailer <6038418+ponytailer@users.noreply.github.com>
1 parent 4441b8e commit 302b5b0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+2299
-3024
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,21 +12,25 @@ jobs:
1212
strategy:
1313
matrix:
1414
os: [ubuntu-latest, windows-latest, macos-latest]
15-
python-version: ["3.9", "3.10", "3.11", "3.12"]
15+
python-version: ["3.10", "3.11", "3.12", "3.13"]
1616
fail-fast: true
1717

1818
steps:
19-
- uses: actions/checkout@v2
20-
- name: Set up Python
21-
uses: actions/setup-python@v2
19+
- uses: actions/checkout@v4
20+
- name: Install uv
21+
uses: astral-sh/setup-uv@v4
2222
with:
23-
python-version: ${{ matrix.python-version }}
23+
version: "latest"
24+
- name: Set up Python
25+
run: uv python install ${{ matrix.python-version }}
26+
- name: Install Dependencies
27+
run: uv sync --all-extras
2428
- name: Install Dependencies
25-
run: python -m pip install -r requirements.txt
29+
run: uv run pip install -r requirements.txt
2630
- name: Test
27-
run: python -m pytest tests/ --cov=pydantic_client --cov-report=term-missing:skip-covered --cov-report=xml
31+
run: uv run pytest tests/ --cov=pydantic_client --cov-report=term-missing:skip-covered --cov-report=xml
2832
- name: Upload coverage
29-
uses: codecov/codecov-action@v1
33+
uses: codecov/codecov-action@v4
3034

3135
automerge:
3236
needs: test

.github/workflows/python-publish.yml

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,14 @@ jobs:
2222
id-token: write
2323

2424
steps:
25-
- uses: actions/checkout@v3
26-
- name: Set up Python
27-
uses: actions/setup-python@v3
25+
- uses: actions/checkout@v4
26+
- name: Install uv
27+
uses: astral-sh/setup-uv@v4
2828
with:
29-
python-version: '3.x'
30-
- name: Install dependencies
31-
run: |
32-
python -m pip install --upgrade pip
33-
pip install build
29+
version: "latest"
30+
- name: Set up Python
31+
run: uv python install 3.x
3432
- name: Build package
35-
run: python -m build
33+
run: uv build
3634
- name: Publish package
37-
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
38-
with:
39-
user: __token__
40-
password: ${{ secrets.PYPI_TOKEN }}
35+
uses: pypa/gh-action-pypi-publish@release/v1

README.md

Lines changed: 181 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -1,147 +1,217 @@
1-
# pydantic-client
1+
# Pydantic Client
22

33
[![codecov](https://codecov.io/gh/ponytailer/pydantic-client/branch/main/graph/badge.svg?token=CZX5V1YP22)](https://codecov.io/gh/ponytailer/pydantic-client) [![Upload Python Package](https://github.com/ponytailer/pydantic-client/actions/workflows/python-publish.yml/badge.svg)](https://github.com/ponytailer/pydantic-client/actions/workflows/python-publish.yml)
4+
[![PyPI version](https://badge.fury.io/py/pydantic-client.svg)](https://badge.fury.io/py/pydantic-client)
5+
[![Python Version](https://img.shields.io/pypi/pyversions/pydantic-client.svg)](https://pypi.org/project/pydantic-client/)
6+
[![Downloads](https://pepy.tech/badge/pydantic-client)](https://pepy.tech/project/pydantic-client)
7+
[![License](https://img.shields.io/github/license/ponytailer/pydantic-client.svg)](https://github.com/ponytailer/pydantic-client/blob/main/LICENSE)
48

5-
Http client base pydantic with requests, aiohttp and httpx, support the json response or file(bytes).
69

10+
A flexible HTTP client library that leverages Pydantic models for request/response handling, supporting both synchronous and asynchronous operations.
711

8-
#### If you like this project, please star it.
12+
## Features
913

10-
### How to install
14+
- 🔥 **Type-safe**: Full type hints with Pydantic models for request/response validation
15+
- 🚀 **Multiple HTTP backends**: Choose from `requests`, `aiohttp`, or `httpx`
16+
-**Async/Sync support**: Work with both synchronous and asynchronous HTTP operations
17+
- 🎯 **Decorator-based API**: Clean, intuitive API definition with decorators
18+
- 📝 **OpenAPI/Swagger support**: Generate client code from OpenAPI specifications
19+
- 🛡️ **Automatic validation**: Request/response validation with Pydantic models
20+
- 🔧 **Flexible configuration**: Easy client configuration with headers, timeouts, and more
1121

12-
> only support `requests`:
13-
>> pip install pydantic-client
22+
## Installation
1423

15-
> support `aiohttp` and `requests`:
16-
>> pip install "pydantic-client[aiohttp]"
17-
18-
> support `httpx(async)` and `requests`:
19-
>> pip install "pydantic-client[httpx]"
20-
21-
> support all:
22-
>> pip install "pydantic-client[all]"
24+
```bash
25+
pip install pydantic-client
26+
```
2327

24-
### How to use
28+
## Quick Start
2529

2630
```python
2731
from pydantic import BaseModel
32+
from pydantic_client import RequestsWebClient, get, post
2833

29-
from pydantic_client import delete, get, post, put, pydantic_client_manager
30-
from pydantic_client import ClientConfig
31-
32-
33-
class Book(BaseModel):
34+
# Define your response models
35+
class UserResponse(BaseModel):
36+
id: int
3437
name: str
35-
age: int
38+
email: str
3639

40+
class CreateUser(BaseModel):
41+
name: str
42+
email: str
43+
44+
45+
# Create your API client
46+
class MyAPIClient(RequestsWebClient):
47+
def __init__(self):
48+
super().__init__(
49+
base_url="https://api.example.com",
50+
headers={"Authorization": "Bearer token"}
51+
)
52+
53+
@get("users/{user_id}")
54+
def get_user(self, user_id: int) -> UserResponse:
55+
pass
56+
57+
@post("users")
58+
def create_user(self, user: CreateUser) -> UserResponse:
59+
pass
60+
61+
# Use the client
62+
client = MyAPIClient()
63+
user = client.get_user(user_id=123)
64+
65+
user_body = CreateUser(name="john", email="123@gmail.com")
66+
user = client.create_user(user_body)
67+
```
3768

38-
@pydantic_client_manager.register(
39-
ClientConfig(
40-
base_url="https://example.com",
41-
headers={"Authorization": "Bearer abcdefg"},
42-
timeout=10
43-
)
44-
)
45-
class WebClient:
46-
47-
@get("/books/{book_id}?query={query}")
48-
def get_book(self, book_id: int, query: str) -> Book:
49-
...
69+
## Available Clients
5070

51-
@post("/books", form_body=True)
52-
def create_book_form(self, book: Book) -> Book:
53-
""" will post the form with book"""
54-
...
71+
- `RequestsWebClient`: Synchronous client using the requests library
72+
- `AiohttpWebClient`: Asynchronous client using aiohttp
73+
- `HttpxWebClient`: Asynchronous client using httpx
5574

56-
@put("/books/{book_id}")
57-
def change_book(self, book_id: int, book: Book) -> Book:
58-
"""will put the json body"""
59-
...
75+
## HTTP Method Decorators
6076

61-
@delete("/books/{book_id}")
62-
def change_book(self, book_id: int, request_headers: dict = None) -> Book:
63-
...
77+
The library provides decorators for common HTTP methods:
6478

79+
- `@get(path)`
80+
- `@post(path)`
81+
- `@put(path)`
82+
- `@patch(path)`
83+
- `@delete(path)`
6584

66-
client = pydantic_client_manager.get()
67-
# will get the book with book_id=1
68-
book: Book = client.get_book(1)
85+
## Path Parameters
6986

70-
# request_headers will overwrite the headers in client_config
71-
client.change_book(1, request_headers={"Authorization": "Bearer abcdefg"})
87+
Path parameters are automatically extracted from the URL template and matched with method arguments:
7288

89+
```python
90+
@get("users/{user_id}/posts/{post_id}")
91+
def get_user_post(self, user_id: int, post_id: int) -> PostResponse:
92+
pass
7393
```
7494

75-
And see the examples.
76-
95+
## Request Parameters
7796

78-
<details>
79-
<summary> Change Log </summary>
97+
- For GET and DELETE methods, remaining arguments are sent as query parameters
98+
- For POST, PUT, and PATCH methods, remaining arguments are sent in the request body as JSON
8099

81-
### v1.0.7: genearte client and schema code from swagger
100+
## Configuration
82101

83-
```shell
84-
pydantic-client parse your-swagger-file.yaml
85-
```
86-
you will get the code from output, above:
102+
You can initialize clients with custom configurations:
87103

88104
```python
89-
from typing import Optional
90-
from pydantic import BaseModel
91-
92-
93-
class FunctionAnalysisRequest(BaseModel):
94-
project_name: str
95-
struct_code: str
96-
struct_level_code: str
97-
struct_name: Optional[str]
98-
function_type: str
99-
100-
101-
102-
class HTTPValidationError(BaseModel):
103-
detail: list[str]
104-
105-
106-
107-
class LoseEffectRequest(BaseModel):
108-
struct_name: Optional[str]
109-
function_description: Optional[str]
110-
111-
112-
113-
class LoseEffectResponse(BaseModel):
114-
failure_type_code: Optional[str]
115-
failure_description: Optional[str]
116-
117-
118-
class WebClient:
119-
120-
@get("/fmea-agent/v1/hello")
121-
def get_fmea_agent_v1_hello(self):
122-
...
123-
124-
@post("/fmea-agent/v1/function_analysis")
125-
def post_fmea_agent_v1_function_analysis(self, function_analysis_request: FunctionAnalysisRequest):
126-
...
105+
client = MyAPIClient(
106+
base_url="https://api.example.com",
107+
headers={"Custom-Header": "value"},
108+
timeout=30 # in seconds
109+
)
127110

128-
@post("/fmea-agent/v1/lose_effect_analysis/{lose_id}")
129-
def post_fmea_agent_v1_lose_effect_analysis(self, lose_id: int, name: Optional[str], lose_effect_request: LoseEffectRequest):
130-
...
111+
# Or using configuration dictionary
112+
config = {
113+
"base_url": "https://api.example.com",
114+
"headers": {"Custom-Header": "value"},
115+
"timeout": 30
116+
}
117+
client = MyAPIClient.from_config(config)
131118
```
132119

133-
134-
### v1.0.5: support file response type.
135-
136-
```python
137-
from pydantic_client.schema.file import File
138-
from pydantic_client import post
139-
140-
@post("/download")
141-
def download_file(self) -> File:
142-
# you will get the bytes content of the file
143-
...
120+
## Api to Agno Tools.
144121

145122
```
123+
from pydantic import BaseModel
124+
from typing import List
146125
147-
</details>
126+
127+
class User(BaseModel):
128+
id: int
129+
name: str
130+
email: str
131+
132+
133+
class PetAPIClient(BaseClient):
134+
@get("/pets/{id}", agno_tool=True)
135+
def get_pet(self, id: int) -> dict:
136+
"""Get pet details by ID
137+
138+
:param id: The ID of the pet to retrieve
139+
"""
140+
pass
141+
142+
@post("/pets", agno_tool=True, tool_description="Create a new pet")
143+
def create_pet(self, name: str, type: str) -> dict:
144+
"""Create a new pet
145+
146+
:param name: Name of the pet
147+
:param type: Type of pet (dog, cat, etc.)
148+
"""
149+
pass
150+
151+
@get("/users", agno_tool=True)
152+
def list_users(self, limit: int = 10) -> List[User]:
153+
"""List all users
154+
155+
:param limit: Maximum number of users to return
156+
"""
157+
pass
158+
159+
160+
# Get Agno tools
161+
tools = PetAPIClient.get_agno_tools()
162+
"""
163+
[
164+
{
165+
"name": "get_pet",
166+
"description": "Get pet details by ID",
167+
"parameters": {
168+
"id": {
169+
"type": "integer",
170+
"description": "The ID of the pet to retrieve",
171+
"required": true
172+
}
173+
}
174+
},
175+
{
176+
"name": "create_pet",
177+
"description": "Create a new pet",
178+
"parameters": {
179+
"name": {
180+
"type": "string",
181+
"description": "Name of the pet",
182+
"required": true
183+
},
184+
"type": {
185+
"type": "string",
186+
"description": "Type of pet (dog, cat, etc.)",
187+
"required": true
188+
}
189+
}
190+
},
191+
{
192+
"name": "list_users",
193+
"description": "List all users",
194+
"parameters": {
195+
"limit": {
196+
"type": "integer",
197+
"description": "Maximum number of users to return",
198+
"required": false
199+
}
200+
}
201+
}
202+
]
203+
"""
204+
205+
206+
207+
## Type Safety
208+
209+
The library automatically validates responses against Pydantic models when specified as return types in the method definitions.
210+
211+
## Error Handling
212+
213+
HTTP errors are raised as exceptions by the underlying HTTP client libraries. Make sure to handle these appropriately in your application.
214+
215+
---
216+
217+
⭐ If you like this project, please star it on GitHub!

0 commit comments

Comments
 (0)