postpeer 0.1.0__tar.gz
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.
- postpeer-0.1.0/.github/workflows/ci.yml +75 -0
- postpeer-0.1.0/.github/workflows/publish.yml +34 -0
- postpeer-0.1.0/.gitignore +12 -0
- postpeer-0.1.0/CHANGELOG.md +7 -0
- postpeer-0.1.0/LICENSE +17 -0
- postpeer-0.1.0/PKG-INFO +213 -0
- postpeer-0.1.0/README.md +177 -0
- postpeer-0.1.0/openapi-python.config.mjs +8 -0
- postpeer-0.1.0/openapi.json +7470 -0
- postpeer-0.1.0/package.json +17 -0
- postpeer-0.1.0/pnpm-lock.yaml +386 -0
- postpeer-0.1.0/pyproject.toml +95 -0
- postpeer-0.1.0/scripts/__init__.py +1 -0
- postpeer-0.1.0/scripts/fetch-spec.mjs +10 -0
- postpeer-0.1.0/scripts/generate_resources.py +483 -0
- postpeer-0.1.0/src/postpeer/__init__.py +40 -0
- postpeer-0.1.0/src/postpeer/_generated/pydantic_gen.py +2061 -0
- postpeer-0.1.0/src/postpeer/_transport.py +224 -0
- postpeer-0.1.0/src/postpeer/_version.py +1 -0
- postpeer-0.1.0/src/postpeer/client.py +131 -0
- postpeer-0.1.0/src/postpeer/errors.py +125 -0
- postpeer-0.1.0/src/postpeer/py.typed +1 -0
- postpeer-0.1.0/src/postpeer/resources/__init__.py +3 -0
- postpeer-0.1.0/src/postpeer/resources/_generated.py +2107 -0
- postpeer-0.1.0/src/postpeer/types.py +3 -0
- postpeer-0.1.0/tests/conftest.py +7 -0
- postpeer-0.1.0/tests/test_client.py +360 -0
- postpeer-0.1.0/tests/test_generation.py +61 -0
- postpeer-0.1.0/uv.lock +1350 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: astral-sh/setup-uv@v6
|
|
22
|
+
with:
|
|
23
|
+
version: "0.9.26"
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
enable-cache: true
|
|
26
|
+
- run: uv sync --frozen --extra dev
|
|
27
|
+
- run: uv run ruff check .
|
|
28
|
+
- run: uv run ruff format --check .
|
|
29
|
+
- run: uv run mypy
|
|
30
|
+
- run: uv run pytest
|
|
31
|
+
|
|
32
|
+
generated:
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- uses: pnpm/action-setup@v4
|
|
37
|
+
with:
|
|
38
|
+
version: 10.30.1
|
|
39
|
+
- uses: actions/setup-node@v4
|
|
40
|
+
with:
|
|
41
|
+
node-version: 22
|
|
42
|
+
cache: pnpm
|
|
43
|
+
- uses: astral-sh/setup-uv@v6
|
|
44
|
+
with:
|
|
45
|
+
version: "0.9.26"
|
|
46
|
+
python-version: "3.13"
|
|
47
|
+
enable-cache: true
|
|
48
|
+
- run: pnpm install --frozen-lockfile
|
|
49
|
+
- run: uv sync --frozen --extra dev
|
|
50
|
+
- run: pnpm generate
|
|
51
|
+
- name: Verify generated SDK is current
|
|
52
|
+
run: git diff --exit-code -- openapi.json src/postpeer/_generated src/postpeer/resources/_generated.py
|
|
53
|
+
|
|
54
|
+
package:
|
|
55
|
+
runs-on: ubuntu-latest
|
|
56
|
+
needs: [test, generated]
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
- uses: astral-sh/setup-uv@v6
|
|
60
|
+
with:
|
|
61
|
+
version: "0.9.26"
|
|
62
|
+
python-version: "3.13"
|
|
63
|
+
enable-cache: true
|
|
64
|
+
- run: uv sync --frozen --extra dev
|
|
65
|
+
- run: uv build
|
|
66
|
+
- run: uv run twine check dist/*
|
|
67
|
+
- name: Verify wheel import
|
|
68
|
+
run: |
|
|
69
|
+
uv venv --python 3.13 .wheel-venv
|
|
70
|
+
uv pip install --python .wheel-venv/bin/python dist/*.whl
|
|
71
|
+
.wheel-venv/bin/python -c "from postpeer import PostPeer, AsyncPostPeer"
|
|
72
|
+
- uses: actions/upload-artifact@v4
|
|
73
|
+
with:
|
|
74
|
+
name: python-distributions
|
|
75
|
+
path: dist/
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
if: startsWith(github.event.release.tag_name, 'v')
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
environment:
|
|
15
|
+
name: pypi
|
|
16
|
+
url: https://pypi.org/project/postpeer/
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
id-token: write
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v4
|
|
22
|
+
- uses: astral-sh/setup-uv@v6
|
|
23
|
+
with:
|
|
24
|
+
version: "0.9.26"
|
|
25
|
+
python-version: "3.13"
|
|
26
|
+
enable-cache: true
|
|
27
|
+
- run: uv sync --frozen --extra dev
|
|
28
|
+
- run: uv build
|
|
29
|
+
- run: uv run twine check dist/*
|
|
30
|
+
- name: Verify tag matches package version
|
|
31
|
+
run: |
|
|
32
|
+
PACKAGE_VERSION=$(uv run python -c "from postpeer import __version__; print(__version__)")
|
|
33
|
+
test "v${PACKAGE_VERSION}" = "${{ github.event.release.tag_name }}"
|
|
34
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
postpeer-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 PostPeer
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
postpeer-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: postpeer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The official Python SDK for the PostPeer API
|
|
5
|
+
Project-URL: Homepage, https://postpeer.dev
|
|
6
|
+
Project-URL: Documentation, https://docs.postpeer.dev
|
|
7
|
+
Project-URL: Repository, https://github.com/PostPeer-API/postpeer-python
|
|
8
|
+
Project-URL: Issues, https://github.com/PostPeer-API/postpeer-python/issues
|
|
9
|
+
Author: PostPeer
|
|
10
|
+
License: Apache-2.0
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api,postpeer,sdk,social-media
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: httpx<1,>=0.27
|
|
24
|
+
Requires-Dist: pydantic[email]<3,>=2
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
27
|
+
Requires-Dist: hatchling>=1.27; extra == 'dev'
|
|
28
|
+
Requires-Dist: mypy>=1.13; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest-cov>=6; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
32
|
+
Requires-Dist: respx>=0.22; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.9; extra == 'dev'
|
|
34
|
+
Requires-Dist: twine>=6; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# PostPeer Python SDK
|
|
38
|
+
|
|
39
|
+
The official Python SDK for the [PostPeer API](https://postpeer.dev), generated
|
|
40
|
+
from PostPeer's OpenAPI contract with [Hey API](https://heyapi.dev/).
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install postpeer
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Python 3.10 or newer is required.
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from postpeer import PostPeer
|
|
54
|
+
|
|
55
|
+
client = PostPeer() # Reads POSTPEER_API_KEY
|
|
56
|
+
|
|
57
|
+
post = client.posts.create(
|
|
58
|
+
content="Hello from PostPeer!",
|
|
59
|
+
platforms=[{"platform": "twitter", "accountId": "integration-id"}],
|
|
60
|
+
publish_now=True,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
print(post.post_id)
|
|
64
|
+
client.close()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The client can also be used as a context manager:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
with PostPeer(api_key="your-access-key") as client:
|
|
71
|
+
profiles = client.profiles.list()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Async usage
|
|
75
|
+
|
|
76
|
+
`AsyncPostPeer` has the same resources and method names:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import asyncio
|
|
80
|
+
|
|
81
|
+
from postpeer import AsyncPostPeer
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async def main() -> None:
|
|
85
|
+
async with AsyncPostPeer() as client:
|
|
86
|
+
posts = await client.posts.list(status="scheduled")
|
|
87
|
+
print(posts)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
asyncio.run(main())
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
import httpx
|
|
97
|
+
|
|
98
|
+
from postpeer import PostPeer
|
|
99
|
+
|
|
100
|
+
transport = httpx.Client(http2=True)
|
|
101
|
+
client = PostPeer(
|
|
102
|
+
api_key="your-access-key",
|
|
103
|
+
base_url="https://api.postpeer.dev",
|
|
104
|
+
timeout=60.0,
|
|
105
|
+
max_retries=2,
|
|
106
|
+
default_headers={"X-Application": "my-app"},
|
|
107
|
+
http_client=transport,
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Each SDK instance is isolated. An injected HTTPX client remains owned by the
|
|
112
|
+
caller and is not closed by the SDK.
|
|
113
|
+
|
|
114
|
+
## Resources
|
|
115
|
+
|
|
116
|
+
The resource hierarchy matches the official Node.js SDK:
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
client.health.check()
|
|
120
|
+
client.health.verify_access_key()
|
|
121
|
+
|
|
122
|
+
client.connect.get_oauth_url(platform="linkedin")
|
|
123
|
+
client.connect.linkedin.get_selection(token="...")
|
|
124
|
+
client.connect.integrations.list()
|
|
125
|
+
|
|
126
|
+
client.profiles.create(name="Marketing")
|
|
127
|
+
client.apps.list()
|
|
128
|
+
client.notifications.list()
|
|
129
|
+
client.platforms.list()
|
|
130
|
+
|
|
131
|
+
client.posts.create(content="Hello", platforms=[...], publish_now=True)
|
|
132
|
+
client.posts.list(status="scheduled")
|
|
133
|
+
client.posts.get(post_id="...")
|
|
134
|
+
client.posts.delete(post_id="...")
|
|
135
|
+
client.posts.scheduled.list()
|
|
136
|
+
client.posts.scheduled.cancel(post_id="...")
|
|
137
|
+
|
|
138
|
+
client.media.upload(filename="image.png", mime_type="image/png")
|
|
139
|
+
client.analytics.get()
|
|
140
|
+
client.usage.get()
|
|
141
|
+
client.pinterest.get_boards(account_id="...")
|
|
142
|
+
client.tiktok.get_creator_info(account_id="...")
|
|
143
|
+
client.ai.write(description="A launch announcement", platforms=["linkedin"])
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Parameters use Python `snake_case`; the SDK serializes their OpenAPI aliases on
|
|
147
|
+
the wire. Responses are validated Pydantic v2 models. All generated models and
|
|
148
|
+
enums are available from `postpeer.types`.
|
|
149
|
+
|
|
150
|
+
## Errors
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from postpeer import NotFoundError, RateLimitError
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
client.posts.get(post_id="missing")
|
|
157
|
+
except NotFoundError as error:
|
|
158
|
+
print(error.status, error.request_id, error.message)
|
|
159
|
+
except RateLimitError:
|
|
160
|
+
print("PostPeer rate limit reached")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The SDK exports typed API, connection, timeout, and response-validation errors.
|
|
164
|
+
|
|
165
|
+
## Retries and request options
|
|
166
|
+
|
|
167
|
+
GET, PUT, and DELETE requests retry transient connection failures, 408, 409,
|
|
168
|
+
429, and 5xx responses by default. POST and PATCH are not automatically retried,
|
|
169
|
+
because replaying an ambiguous request could publish or create a resource twice.
|
|
170
|
+
|
|
171
|
+
Use `RequestOptions` for an explicit per-call override:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from postpeer import RequestOptions
|
|
175
|
+
|
|
176
|
+
client.posts.create(
|
|
177
|
+
content="Hello",
|
|
178
|
+
platforms=[...],
|
|
179
|
+
publish_now=True,
|
|
180
|
+
_request_options=RequestOptions(
|
|
181
|
+
timeout=30.0,
|
|
182
|
+
max_retries=2,
|
|
183
|
+
retry_non_idempotent=True,
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Updating generated code
|
|
189
|
+
|
|
190
|
+
The OpenAPI specification and package releases are intentionally separate:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
pnpm spec:fetch
|
|
194
|
+
pnpm generate
|
|
195
|
+
uv run ruff format .
|
|
196
|
+
uv run ruff check .
|
|
197
|
+
uv run mypy
|
|
198
|
+
uv run pytest
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
`openapi.json`, `src/postpeer/_generated`, and
|
|
202
|
+
`src/postpeer/resources/_generated.py` must be committed together. Generated
|
|
203
|
+
files must not be edited manually.
|
|
204
|
+
|
|
205
|
+
Hey API 0.0.24 currently generates the Pydantic models. A deterministic
|
|
206
|
+
PostPeer-owned generator emits the sync and async resources until Hey API's
|
|
207
|
+
Python runtime is mature enough to replace it.
|
|
208
|
+
|
|
209
|
+
## Versioning
|
|
210
|
+
|
|
211
|
+
PostPeer uses conservative pre-1.0 versioning. Compatible additions remain in
|
|
212
|
+
`0.1.x`; a breaking SDK change moves to `0.2.0`; `1.0.0` is reserved for a
|
|
213
|
+
stable public contract.
|
postpeer-0.1.0/README.md
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# PostPeer Python SDK
|
|
2
|
+
|
|
3
|
+
The official Python SDK for the [PostPeer API](https://postpeer.dev), generated
|
|
4
|
+
from PostPeer's OpenAPI contract with [Hey API](https://heyapi.dev/).
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install postpeer
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Python 3.10 or newer is required.
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from postpeer import PostPeer
|
|
18
|
+
|
|
19
|
+
client = PostPeer() # Reads POSTPEER_API_KEY
|
|
20
|
+
|
|
21
|
+
post = client.posts.create(
|
|
22
|
+
content="Hello from PostPeer!",
|
|
23
|
+
platforms=[{"platform": "twitter", "accountId": "integration-id"}],
|
|
24
|
+
publish_now=True,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
print(post.post_id)
|
|
28
|
+
client.close()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The client can also be used as a context manager:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
with PostPeer(api_key="your-access-key") as client:
|
|
35
|
+
profiles = client.profiles.list()
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Async usage
|
|
39
|
+
|
|
40
|
+
`AsyncPostPeer` has the same resources and method names:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import asyncio
|
|
44
|
+
|
|
45
|
+
from postpeer import AsyncPostPeer
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def main() -> None:
|
|
49
|
+
async with AsyncPostPeer() as client:
|
|
50
|
+
posts = await client.posts.list(status="scheduled")
|
|
51
|
+
print(posts)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
asyncio.run(main())
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Configuration
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import httpx
|
|
61
|
+
|
|
62
|
+
from postpeer import PostPeer
|
|
63
|
+
|
|
64
|
+
transport = httpx.Client(http2=True)
|
|
65
|
+
client = PostPeer(
|
|
66
|
+
api_key="your-access-key",
|
|
67
|
+
base_url="https://api.postpeer.dev",
|
|
68
|
+
timeout=60.0,
|
|
69
|
+
max_retries=2,
|
|
70
|
+
default_headers={"X-Application": "my-app"},
|
|
71
|
+
http_client=transport,
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Each SDK instance is isolated. An injected HTTPX client remains owned by the
|
|
76
|
+
caller and is not closed by the SDK.
|
|
77
|
+
|
|
78
|
+
## Resources
|
|
79
|
+
|
|
80
|
+
The resource hierarchy matches the official Node.js SDK:
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
client.health.check()
|
|
84
|
+
client.health.verify_access_key()
|
|
85
|
+
|
|
86
|
+
client.connect.get_oauth_url(platform="linkedin")
|
|
87
|
+
client.connect.linkedin.get_selection(token="...")
|
|
88
|
+
client.connect.integrations.list()
|
|
89
|
+
|
|
90
|
+
client.profiles.create(name="Marketing")
|
|
91
|
+
client.apps.list()
|
|
92
|
+
client.notifications.list()
|
|
93
|
+
client.platforms.list()
|
|
94
|
+
|
|
95
|
+
client.posts.create(content="Hello", platforms=[...], publish_now=True)
|
|
96
|
+
client.posts.list(status="scheduled")
|
|
97
|
+
client.posts.get(post_id="...")
|
|
98
|
+
client.posts.delete(post_id="...")
|
|
99
|
+
client.posts.scheduled.list()
|
|
100
|
+
client.posts.scheduled.cancel(post_id="...")
|
|
101
|
+
|
|
102
|
+
client.media.upload(filename="image.png", mime_type="image/png")
|
|
103
|
+
client.analytics.get()
|
|
104
|
+
client.usage.get()
|
|
105
|
+
client.pinterest.get_boards(account_id="...")
|
|
106
|
+
client.tiktok.get_creator_info(account_id="...")
|
|
107
|
+
client.ai.write(description="A launch announcement", platforms=["linkedin"])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Parameters use Python `snake_case`; the SDK serializes their OpenAPI aliases on
|
|
111
|
+
the wire. Responses are validated Pydantic v2 models. All generated models and
|
|
112
|
+
enums are available from `postpeer.types`.
|
|
113
|
+
|
|
114
|
+
## Errors
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from postpeer import NotFoundError, RateLimitError
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
client.posts.get(post_id="missing")
|
|
121
|
+
except NotFoundError as error:
|
|
122
|
+
print(error.status, error.request_id, error.message)
|
|
123
|
+
except RateLimitError:
|
|
124
|
+
print("PostPeer rate limit reached")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The SDK exports typed API, connection, timeout, and response-validation errors.
|
|
128
|
+
|
|
129
|
+
## Retries and request options
|
|
130
|
+
|
|
131
|
+
GET, PUT, and DELETE requests retry transient connection failures, 408, 409,
|
|
132
|
+
429, and 5xx responses by default. POST and PATCH are not automatically retried,
|
|
133
|
+
because replaying an ambiguous request could publish or create a resource twice.
|
|
134
|
+
|
|
135
|
+
Use `RequestOptions` for an explicit per-call override:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from postpeer import RequestOptions
|
|
139
|
+
|
|
140
|
+
client.posts.create(
|
|
141
|
+
content="Hello",
|
|
142
|
+
platforms=[...],
|
|
143
|
+
publish_now=True,
|
|
144
|
+
_request_options=RequestOptions(
|
|
145
|
+
timeout=30.0,
|
|
146
|
+
max_retries=2,
|
|
147
|
+
retry_non_idempotent=True,
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Updating generated code
|
|
153
|
+
|
|
154
|
+
The OpenAPI specification and package releases are intentionally separate:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pnpm spec:fetch
|
|
158
|
+
pnpm generate
|
|
159
|
+
uv run ruff format .
|
|
160
|
+
uv run ruff check .
|
|
161
|
+
uv run mypy
|
|
162
|
+
uv run pytest
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`openapi.json`, `src/postpeer/_generated`, and
|
|
166
|
+
`src/postpeer/resources/_generated.py` must be committed together. Generated
|
|
167
|
+
files must not be edited manually.
|
|
168
|
+
|
|
169
|
+
Hey API 0.0.24 currently generates the Pydantic models. A deterministic
|
|
170
|
+
PostPeer-owned generator emits the sync and async resources until Hey API's
|
|
171
|
+
Python runtime is mature enough to replace it.
|
|
172
|
+
|
|
173
|
+
## Versioning
|
|
174
|
+
|
|
175
|
+
PostPeer uses conservative pre-1.0 versioning. Compatible additions remain in
|
|
176
|
+
`0.1.x`; a breaking SDK change moves to `0.2.0`; `1.0.0` is reserved for a
|
|
177
|
+
stable public contract.
|