pyrest-model-client 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyrest_model_client/__init__.py +13 -0
- pyrest_model_client/base.py +45 -0
- pyrest_model_client/client.py +50 -0
- pyrest_model_client-0.0.1.dist-info/METADATA +120 -0
- pyrest_model_client-0.0.1.dist-info/RECORD +8 -0
- pyrest_model_client-0.0.1.dist-info/WHEEL +5 -0
- pyrest_model_client-0.0.1.dist-info/licenses/LICENSE +21 -0
- pyrest_model_client-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
from dotenv import load_dotenv
|
2
|
+
|
3
|
+
from pyrest_model_client.client import RequestClient, build_header
|
4
|
+
from pyrest_model_client.base import BaseAPIModel, set_client
|
5
|
+
|
6
|
+
load_dotenv()
|
7
|
+
|
8
|
+
__all__ = [
|
9
|
+
"RequestClient",
|
10
|
+
"build_header",
|
11
|
+
"BaseAPIModel",
|
12
|
+
"set_client",
|
13
|
+
]
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from typing import ClassVar
|
2
|
+
|
3
|
+
from pydantic import BaseModel
|
4
|
+
|
5
|
+
from pyrest_model_client.client import RequestClient
|
6
|
+
|
7
|
+
|
8
|
+
def set_client(new_client: RequestClient):
|
9
|
+
"""Set the global client instance for all API models."""
|
10
|
+
global client
|
11
|
+
client = new_client
|
12
|
+
|
13
|
+
|
14
|
+
class BaseAPIModel(BaseModel):
|
15
|
+
id: int | str | None = None
|
16
|
+
_resource_path: ClassVar[str] = ""
|
17
|
+
|
18
|
+
def save(self) -> None:
|
19
|
+
data = self.model_dump(exclude_unset=True)
|
20
|
+
if self.id:
|
21
|
+
response = client.put(f"/{self._resource_path}/{self.id}", json=data)
|
22
|
+
else:
|
23
|
+
response = client.post(f"/{self._resource_path}", json=data)
|
24
|
+
response.raise_for_status()
|
25
|
+
self.id = response.json()["id"]
|
26
|
+
|
27
|
+
def delete(self) -> None:
|
28
|
+
if not self.id:
|
29
|
+
raise ValueError("Cannot delete unsaved resource.")
|
30
|
+
response = client.delete(f"/{self._resource_path}/{self.id}")
|
31
|
+
response.raise_for_status()
|
32
|
+
|
33
|
+
@classmethod
|
34
|
+
def load(cls, resource_id: str):
|
35
|
+
response = client.get(f"/{cls._resource_path}/{resource_id}")
|
36
|
+
if response.status_code == 404:
|
37
|
+
raise ValueError(f"{cls.__name__} not found.")
|
38
|
+
response.raise_for_status()
|
39
|
+
return cls(**response.json())
|
40
|
+
|
41
|
+
@classmethod
|
42
|
+
def find(cls):
|
43
|
+
response = client.get(f"/{cls._resource_path}")
|
44
|
+
response.raise_for_status()
|
45
|
+
return [cls(**item) for item in response.json()]
|
@@ -0,0 +1,50 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
|
6
|
+
def build_header(token: str, authorization_type: str = "Token", content_type: str = "application/json") -> dict:
|
7
|
+
return {
|
8
|
+
"Content-Type": content_type,
|
9
|
+
"Authorization": f"{authorization_type} {token}",
|
10
|
+
}
|
11
|
+
|
12
|
+
|
13
|
+
class RequestClient:
|
14
|
+
def __init__(self, header: dict, base_url: str = "http://localhost:8000"):
|
15
|
+
self.base_url = base_url
|
16
|
+
self.client = httpx.Client(base_url=self.base_url)
|
17
|
+
self.set_credentials(header=header)
|
18
|
+
|
19
|
+
def set_credentials(self, header: dict):
|
20
|
+
self.client.headers.update(header)
|
21
|
+
|
22
|
+
def request(self, method: str, endpoint: str, **kwargs) -> httpx.Response:
|
23
|
+
if not endpoint.startswith("http://") and not endpoint.startswith("https://"):
|
24
|
+
if not endpoint.startswith("/"): # Ensure endpoint starts with a slash if it's a path (not a full URL)
|
25
|
+
endpoint = "/" + endpoint
|
26
|
+
|
27
|
+
if not endpoint.endswith("/"): # Ensure endpoint ends with a slash
|
28
|
+
endpoint = endpoint + "/"
|
29
|
+
|
30
|
+
response = self.client.request(method, endpoint, **kwargs)
|
31
|
+
response.raise_for_status()
|
32
|
+
return response
|
33
|
+
|
34
|
+
def get(self, endpoint: str, params: Optional[dict] = None) -> httpx.Response:
|
35
|
+
if params is None:
|
36
|
+
params = {}
|
37
|
+
return self.request("GET", endpoint, params=params)
|
38
|
+
|
39
|
+
def post(self, endpoint: str, json: Optional[dict] = None) -> httpx.Response:
|
40
|
+
if json is None:
|
41
|
+
json = {}
|
42
|
+
return self.request("POST", endpoint, json=json)
|
43
|
+
|
44
|
+
def put(self, endpoint: str, json: Optional[dict] = None) -> httpx.Response:
|
45
|
+
if json is None:
|
46
|
+
json = {}
|
47
|
+
return self.request("PUT", endpoint, json=json)
|
48
|
+
|
49
|
+
def delete(self, endpoint: str) -> httpx.Response:
|
50
|
+
return self.request("DELETE", endpoint)
|
@@ -0,0 +1,120 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: pyrest-model-client
|
3
|
+
Version: 0.0.1
|
4
|
+
Summary: A simple, flexible Python HTTP client and API modeling toolkit built on httpx and pydantic.
|
5
|
+
Home-page: https://github.com/aviz92/pyrest-model-client
|
6
|
+
Author: Avi Zaguri
|
7
|
+
Author-email:
|
8
|
+
Project-URL: Repository, https://github.com/aviz92/pyrest-model-client
|
9
|
+
Requires-Python: >=3.11
|
10
|
+
Description-Content-Type: text/markdown
|
11
|
+
License-File: LICENSE
|
12
|
+
Requires-Dist: setuptools
|
13
|
+
Requires-Dist: dotenv
|
14
|
+
Requires-Dist: wheel
|
15
|
+
Requires-Dist: colorlog
|
16
|
+
Requires-Dist: pytest
|
17
|
+
Requires-Dist: pathlib
|
18
|
+
Requires-Dist: httpx
|
19
|
+
Requires-Dist: pydantic
|
20
|
+
Requires-Dist: custom-python-logger
|
21
|
+
Requires-Dist: pytest-plugins
|
22
|
+
Requires-Dist: python-simple-email-sender
|
23
|
+
Requires-Dist: python-vault
|
24
|
+
Requires-Dist: PyYAML
|
25
|
+
Requires-Dist: pytest-asyncio
|
26
|
+
Dynamic: author
|
27
|
+
Dynamic: description
|
28
|
+
Dynamic: description-content-type
|
29
|
+
Dynamic: home-page
|
30
|
+
Dynamic: license-file
|
31
|
+
Dynamic: project-url
|
32
|
+
Dynamic: requires-dist
|
33
|
+
Dynamic: requires-python
|
34
|
+
Dynamic: summary
|
35
|
+
|
36
|
+
# python-requests-client
|
37
|
+
|
38
|
+
A simple, flexible Python HTTP client and API modeling toolkit built on top of [httpx](https://www.python-httpx.org/) and [pydantic](https://docs.pydantic.dev/). Easily integrate robust API requests and resource models into your Python projects.
|
39
|
+
|
40
|
+
---
|
41
|
+
|
42
|
+
## 🚀 Features
|
43
|
+
- **Model-driven**: Define and interact with API resources as Python classes.
|
44
|
+
- **Easy HTTP Requests**: Simple `RequestClient` for GET, POST, PUT, DELETE with automatic header and base URL management.
|
45
|
+
- **Pydantic API Models**: Define resource models with CRUD helpers (`save`, `delete`, `load`, `find`).
|
46
|
+
- **Global Client Setup**: Set a global API client for all models with `set_client()`.
|
47
|
+
- **Type Safety**: All models use Pydantic for validation and serialization.
|
48
|
+
- **Extensible**: Easily create new models for any RESTful resource.
|
49
|
+
|
50
|
+
---
|
51
|
+
|
52
|
+
## 📦 Installation
|
53
|
+
```bash
|
54
|
+
pip install python-requests-client
|
55
|
+
```
|
56
|
+
|
57
|
+
---
|
58
|
+
|
59
|
+
## 🔧 Usage
|
60
|
+
|
61
|
+
### 1. Define Your Models
|
62
|
+
|
63
|
+
```python
|
64
|
+
from pyrest_model_client.base import BaseAPIModel
|
65
|
+
from typing import ClassVar
|
66
|
+
|
67
|
+
|
68
|
+
class User(BaseAPIModel):
|
69
|
+
name: str
|
70
|
+
email: str
|
71
|
+
_resource_path: ClassVar[str] = "user"
|
72
|
+
|
73
|
+
|
74
|
+
class Environment(BaseAPIModel):
|
75
|
+
name: str
|
76
|
+
_resource_path: ClassVar[str] = "environment"
|
77
|
+
```
|
78
|
+
|
79
|
+
### 2. Initialize the Client
|
80
|
+
|
81
|
+
```python
|
82
|
+
from pyrest_model_client import RequestClient, build_header, set_client
|
83
|
+
from example_usage.models.user import User
|
84
|
+
from example_usage.models.environment import Environment
|
85
|
+
|
86
|
+
set_client(
|
87
|
+
new_client=RequestClient(
|
88
|
+
base_url="http://localhost:8000",
|
89
|
+
header=build_header(token="YOUR_API_TOKEN")
|
90
|
+
)
|
91
|
+
)
|
92
|
+
|
93
|
+
# Create and save a new user
|
94
|
+
e = User(name="Alice", email="alice@example.com")
|
95
|
+
e.save()
|
96
|
+
|
97
|
+
# Update and save
|
98
|
+
e.name = "Alice Smith"
|
99
|
+
e.save()
|
100
|
+
|
101
|
+
# Find all environments
|
102
|
+
environments = Environment.find()
|
103
|
+
print(environments)
|
104
|
+
|
105
|
+
# Load a specific user by ID
|
106
|
+
user = User.load(resource_id="123")
|
107
|
+
|
108
|
+
# Delete a user
|
109
|
+
user.delete()
|
110
|
+
```
|
111
|
+
|
112
|
+
---
|
113
|
+
|
114
|
+
## 🤝 Contributing
|
115
|
+
Contributions are welcome! Please fork the repo, create a branch, and submit a pull request.
|
116
|
+
|
117
|
+
---
|
118
|
+
|
119
|
+
## 📄 License
|
120
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
@@ -0,0 +1,8 @@
|
|
1
|
+
pyrest_model_client/__init__.py,sha256=4CE5p2_nPxK7yAElL32waZFhvw-mBrJv2Slx-8UzoF0,270
|
2
|
+
pyrest_model_client/base.py,sha256=4mbheCcAi8cveq7ceUfREBvwBRNSZF1jVdGXREOjNsU,1420
|
3
|
+
pyrest_model_client/client.py,sha256=ke167iQBPg0pUFmVirJREHLF8AIw5cqAvQ2cJa1mw-M,1838
|
4
|
+
pyrest_model_client-0.0.1.dist-info/licenses/LICENSE,sha256=cSikHY6SZFsPZSBizCDAJ0-Bjjzxt-JtX6TVbKxwimo,1067
|
5
|
+
pyrest_model_client-0.0.1.dist-info/METADATA,sha256=UJ6EQMWMLZ_aYmiyt2XSRESnsgU5bHDIlZQX-C-DBnA,3078
|
6
|
+
pyrest_model_client-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
7
|
+
pyrest_model_client-0.0.1.dist-info/top_level.txt,sha256=HDtId78s9PTvfjerF5g7kRRe5Z91lgXlPr56w0E7wdU,20
|
8
|
+
pyrest_model_client-0.0.1.dist-info/RECORD,,
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Avi Zaguri
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1 @@
|
|
1
|
+
pyrest_model_client
|