fopost 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.
@@ -0,0 +1,15 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .eggs/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ venv/
9
+ .env
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .mypy_cache/
13
+ .coverage
14
+ htmlcov/
15
+ uv.lock
fopost-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Porter Bridge, LLC
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.
fopost-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,250 @@
1
+ Metadata-Version: 2.5
2
+ Name: fopost
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the FoPost API. Schedule and publish to 31 social platforms from your code.
5
+ Project-URL: Homepage, https://fopost.com
6
+ Project-URL: Documentation, https://fopost.com/docs
7
+ Project-URL: Repository, https://github.com/fopost/fopost-python
8
+ Project-URL: Issues, https://github.com/fopost/fopost-python/issues
9
+ Project-URL: Support, https://fopost.com/contact
10
+ Author: FoPost, Porter Bridge, LLC
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: api,fopost,instagram,linkedin,publishing,scheduling,sdk,social-media,twitter
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Developers
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: Topic :: Internet :: WWW/HTTP
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: httpx>=0.27
25
+ Requires-Dist: pydantic>=2.7
26
+ Description-Content-Type: text/markdown
27
+
28
+ # fopost
29
+
30
+ [![PyPI](https://img.shields.io/pypi/v/fopost.svg)](https://pypi.org/project/fopost/)
31
+ [![Python versions](https://img.shields.io/pypi/pyversions/fopost.svg)](https://pypi.org/project/fopost/)
32
+ [![CI](https://github.com/fopost/fopost-python/actions/workflows/ci.yml/badge.svg)](https://github.com/fopost/fopost-python/actions/workflows/ci.yml)
33
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
34
+
35
+ Official Python SDK for the [FoPost](https://fopost.com) API. Schedule and publish to 31 social platforms from your code.
36
+
37
+ ```bash
38
+ pip install fopost
39
+ ```
40
+
41
+ Requires Python 3.10 or newer. Built on `httpx` and `pydantic` v2, fully typed.
42
+
43
+ > **0.x release.** The public API is still settling and minor versions may
44
+ > contain breaking changes. Pin an exact version if that matters to you.
45
+
46
+ ## Quick start
47
+
48
+ ```python
49
+ from fopost import Fopost
50
+
51
+ client = Fopost(api_key="osk_...") # or set FOPOST_API_KEY
52
+
53
+ workspace = client.workspaces.list()[0]
54
+ accounts = client.accounts.list(workspace_id=workspace.id)
55
+
56
+ post = client.posts.create(
57
+ workspace_id=workspace.id,
58
+ content="Hello from Python",
59
+ accounts=[a.id for a in accounts],
60
+ )
61
+
62
+ client.posts.publish(post.id)
63
+ ```
64
+
65
+ `content` takes a string for a single block, or a list for a thread:
66
+
67
+ ```python
68
+ client.posts.create(
69
+ workspace_id=workspace.id,
70
+ content=[
71
+ "First post in the thread",
72
+ {
73
+ "text": "Second one, with an image",
74
+ "media": [
75
+ {"type": "image", "name": "chart.png", "url": "https://.../chart.png"},
76
+ ],
77
+ },
78
+ ],
79
+ accounts=[a.id for a in accounts],
80
+ )
81
+ ```
82
+
83
+ ## Scheduling
84
+
85
+ `status` is `"draft"` or `"scheduled"`; a scheduled post needs `schedule_at`. To send something out now, create it and call `publish`.
86
+
87
+ ```python
88
+ from datetime import datetime, timezone
89
+
90
+ client.posts.create(
91
+ workspace_id=workspace.id,
92
+ status="scheduled",
93
+ schedule_at=datetime(2026, 9, 1, 10, 0, tzinfo=timezone.utc),
94
+ content="Scheduled with the SDK",
95
+ accounts=[accounts[0].id],
96
+ )
97
+ ```
98
+
99
+ ## Pagination
100
+
101
+ `posts.list` returns one page and iterates over its items directly. `posts.iter` walks every page for you:
102
+
103
+ ```python
104
+ page = client.posts.list(workspace_id=workspace.id, status="published", per_page=50)
105
+ print(f"{page.meta.total} published posts")
106
+ for post in page:
107
+ print(post.id, post.status)
108
+
109
+ # Every post, one page fetched at a time
110
+ for post in client.posts.iter(workspace_id=workspace.id):
111
+ print(post.id)
112
+
113
+ # Or page by page, when you want the meta
114
+ for page in client.posts.iter_pages(workspace_id=workspace.id):
115
+ print(page.meta.current_page, len(page))
116
+ ```
117
+
118
+ ## AI features
119
+
120
+ ```python
121
+ balance = client.ai.credits()
122
+ print(f"{balance.credits_remaining} of {balance.credits_total} credits left")
123
+
124
+ result = client.ai.generate_caption(
125
+ current_caption="shipping a new feature",
126
+ platforms=["twitter", "linkedin"],
127
+ )
128
+ print(result.caption)
129
+ ```
130
+
131
+ `rewrite` and `repurpose_url` are wired the same way:
132
+
133
+ ```python
134
+ rewrites = client.ai.rewrite(
135
+ content="Long article-style draft...",
136
+ platforms=["twitter", "linkedin", "bluesky"],
137
+ )
138
+ for variant in rewrites.results:
139
+ print(variant.platform, variant.content)
140
+
141
+ repurposed = client.ai.repurpose_url(
142
+ url="https://example.com/blog/post",
143
+ platforms=["twitter", "linkedin", "bluesky", "threads"],
144
+ )
145
+ ```
146
+
147
+ > **API keys reach `credits` and `generate_caption`.** `rewrite` and
148
+ > `repurpose_url` currently require a signed-in dashboard session and answer
149
+ > `401` to an API key. They are here so the surface is complete once the server
150
+ > opens them up.
151
+
152
+ ## Configuration
153
+
154
+ ```python
155
+ Fopost(
156
+ api_key="osk_...", # or FOPOST_API_KEY
157
+ base_url="https://api.fopost.com/api/v1", # override for a dev server
158
+ timeout=30.0, # seconds, or an httpx.Timeout
159
+ max_retries=3, # total attempts on a 429
160
+ http_client=my_httpx_client, # bring your own transport
161
+ )
162
+ ```
163
+
164
+ | Env var | Used for |
165
+ | ------------------ | -------------------------------------------- |
166
+ | `FOPOST_API_KEY` | API key, when not passed to the constructor |
167
+
168
+ The client is a context manager, and closes its transport on exit:
169
+
170
+ ```python
171
+ with Fopost() as client:
172
+ client.posts.list(workspace_id=workspace.id)
173
+ ```
174
+
175
+ A `429` is retried automatically, waiting for the interval the API asks for in
176
+ `Retry-After` (delta-seconds or an HTTP date, capped at 60s). `max_retries`
177
+ counts total attempts, so the default of 3 means two retries.
178
+
179
+ ## Error handling
180
+
181
+ Every non-2xx response raises `FopostError` or one of its subclasses, carrying
182
+ the API's `status`, `code`, and `message`.
183
+
184
+ ```python
185
+ from fopost import Fopost, FopostError, PaymentRequiredError, RateLimitError
186
+
187
+ try:
188
+ client.posts.publish("9b2f6c1e-...")
189
+ except PaymentRequiredError as err:
190
+ print(f"Out of credits — upgrade at {err.upgrade_url}")
191
+ except RateLimitError as err:
192
+ print(f"Rate limited, retry in {err.retry_after}s")
193
+ except FopostError as err:
194
+ print(f"API {err.status} ({err.code}): {err.message}")
195
+ ```
196
+
197
+ | Status | Exception |
198
+ | ------ | ----------------------- |
199
+ | 401 | `AuthenticationError` |
200
+ | 402 | `PaymentRequiredError` |
201
+ | 403 | `PermissionDeniedError` |
202
+ | 404 | `NotFoundError` |
203
+ | 429 | `RateLimitError` |
204
+ | other | `FopostError` |
205
+
206
+ ## Resources
207
+
208
+ | Namespace | Methods |
209
+ | ------------ | -------------------------------------------------------------------------------------- |
210
+ | `posts` | `list`, `iter`, `iter_pages`, `get`, `create`, `update`, `delete`, `publish`, `cancel`, `retry`, `preflight`, `deliveries` |
211
+ | `accounts` | `list`, `get`, `health` |
212
+ | `workspaces` | `list`, `get` |
213
+ | `labels` | `list` |
214
+ | `ai` | `credits`, `generate_caption`, `rewrite`, `repurpose_url` |
215
+
216
+ For an endpoint the SDK does not wrap yet, `client.request` sends an
217
+ authenticated call and hands back the decoded body:
218
+
219
+ ```python
220
+ client.request("GET", "/analytics/summary", params={"workspace_id": workspace.id})
221
+ ```
222
+
223
+ ## Example
224
+
225
+ [`examples/create_post.py`](examples/create_post.py) creates a post against a
226
+ running API:
227
+
228
+ ```bash
229
+ export FOPOST_API_KEY=osk_...
230
+ export FOPOST_BASE_URL=http://localhost:8080/api/v1
231
+ python examples/create_post.py "Hello from the Python SDK" --publish
232
+ ```
233
+
234
+ ## Contributing
235
+
236
+ Issues and pull requests are welcome at
237
+ [fopost/fopost-python](https://github.com/fopost/fopost-python).
238
+
239
+ ```bash
240
+ uv sync --group dev
241
+ uv run pytest
242
+ uv run ruff check .
243
+ uv run mypy
244
+ ```
245
+
246
+ ## License
247
+
248
+ MIT
249
+
250
+ Questions or a problem: [fopost.com/contact](https://fopost.com/contact).
fopost-0.1.0/README.md ADDED
@@ -0,0 +1,223 @@
1
+ # fopost
2
+
3
+ [![PyPI](https://img.shields.io/pypi/v/fopost.svg)](https://pypi.org/project/fopost/)
4
+ [![Python versions](https://img.shields.io/pypi/pyversions/fopost.svg)](https://pypi.org/project/fopost/)
5
+ [![CI](https://github.com/fopost/fopost-python/actions/workflows/ci.yml/badge.svg)](https://github.com/fopost/fopost-python/actions/workflows/ci.yml)
6
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
7
+
8
+ Official Python SDK for the [FoPost](https://fopost.com) API. Schedule and publish to 31 social platforms from your code.
9
+
10
+ ```bash
11
+ pip install fopost
12
+ ```
13
+
14
+ Requires Python 3.10 or newer. Built on `httpx` and `pydantic` v2, fully typed.
15
+
16
+ > **0.x release.** The public API is still settling and minor versions may
17
+ > contain breaking changes. Pin an exact version if that matters to you.
18
+
19
+ ## Quick start
20
+
21
+ ```python
22
+ from fopost import Fopost
23
+
24
+ client = Fopost(api_key="osk_...") # or set FOPOST_API_KEY
25
+
26
+ workspace = client.workspaces.list()[0]
27
+ accounts = client.accounts.list(workspace_id=workspace.id)
28
+
29
+ post = client.posts.create(
30
+ workspace_id=workspace.id,
31
+ content="Hello from Python",
32
+ accounts=[a.id for a in accounts],
33
+ )
34
+
35
+ client.posts.publish(post.id)
36
+ ```
37
+
38
+ `content` takes a string for a single block, or a list for a thread:
39
+
40
+ ```python
41
+ client.posts.create(
42
+ workspace_id=workspace.id,
43
+ content=[
44
+ "First post in the thread",
45
+ {
46
+ "text": "Second one, with an image",
47
+ "media": [
48
+ {"type": "image", "name": "chart.png", "url": "https://.../chart.png"},
49
+ ],
50
+ },
51
+ ],
52
+ accounts=[a.id for a in accounts],
53
+ )
54
+ ```
55
+
56
+ ## Scheduling
57
+
58
+ `status` is `"draft"` or `"scheduled"`; a scheduled post needs `schedule_at`. To send something out now, create it and call `publish`.
59
+
60
+ ```python
61
+ from datetime import datetime, timezone
62
+
63
+ client.posts.create(
64
+ workspace_id=workspace.id,
65
+ status="scheduled",
66
+ schedule_at=datetime(2026, 9, 1, 10, 0, tzinfo=timezone.utc),
67
+ content="Scheduled with the SDK",
68
+ accounts=[accounts[0].id],
69
+ )
70
+ ```
71
+
72
+ ## Pagination
73
+
74
+ `posts.list` returns one page and iterates over its items directly. `posts.iter` walks every page for you:
75
+
76
+ ```python
77
+ page = client.posts.list(workspace_id=workspace.id, status="published", per_page=50)
78
+ print(f"{page.meta.total} published posts")
79
+ for post in page:
80
+ print(post.id, post.status)
81
+
82
+ # Every post, one page fetched at a time
83
+ for post in client.posts.iter(workspace_id=workspace.id):
84
+ print(post.id)
85
+
86
+ # Or page by page, when you want the meta
87
+ for page in client.posts.iter_pages(workspace_id=workspace.id):
88
+ print(page.meta.current_page, len(page))
89
+ ```
90
+
91
+ ## AI features
92
+
93
+ ```python
94
+ balance = client.ai.credits()
95
+ print(f"{balance.credits_remaining} of {balance.credits_total} credits left")
96
+
97
+ result = client.ai.generate_caption(
98
+ current_caption="shipping a new feature",
99
+ platforms=["twitter", "linkedin"],
100
+ )
101
+ print(result.caption)
102
+ ```
103
+
104
+ `rewrite` and `repurpose_url` are wired the same way:
105
+
106
+ ```python
107
+ rewrites = client.ai.rewrite(
108
+ content="Long article-style draft...",
109
+ platforms=["twitter", "linkedin", "bluesky"],
110
+ )
111
+ for variant in rewrites.results:
112
+ print(variant.platform, variant.content)
113
+
114
+ repurposed = client.ai.repurpose_url(
115
+ url="https://example.com/blog/post",
116
+ platforms=["twitter", "linkedin", "bluesky", "threads"],
117
+ )
118
+ ```
119
+
120
+ > **API keys reach `credits` and `generate_caption`.** `rewrite` and
121
+ > `repurpose_url` currently require a signed-in dashboard session and answer
122
+ > `401` to an API key. They are here so the surface is complete once the server
123
+ > opens them up.
124
+
125
+ ## Configuration
126
+
127
+ ```python
128
+ Fopost(
129
+ api_key="osk_...", # or FOPOST_API_KEY
130
+ base_url="https://api.fopost.com/api/v1", # override for a dev server
131
+ timeout=30.0, # seconds, or an httpx.Timeout
132
+ max_retries=3, # total attempts on a 429
133
+ http_client=my_httpx_client, # bring your own transport
134
+ )
135
+ ```
136
+
137
+ | Env var | Used for |
138
+ | ------------------ | -------------------------------------------- |
139
+ | `FOPOST_API_KEY` | API key, when not passed to the constructor |
140
+
141
+ The client is a context manager, and closes its transport on exit:
142
+
143
+ ```python
144
+ with Fopost() as client:
145
+ client.posts.list(workspace_id=workspace.id)
146
+ ```
147
+
148
+ A `429` is retried automatically, waiting for the interval the API asks for in
149
+ `Retry-After` (delta-seconds or an HTTP date, capped at 60s). `max_retries`
150
+ counts total attempts, so the default of 3 means two retries.
151
+
152
+ ## Error handling
153
+
154
+ Every non-2xx response raises `FopostError` or one of its subclasses, carrying
155
+ the API's `status`, `code`, and `message`.
156
+
157
+ ```python
158
+ from fopost import Fopost, FopostError, PaymentRequiredError, RateLimitError
159
+
160
+ try:
161
+ client.posts.publish("9b2f6c1e-...")
162
+ except PaymentRequiredError as err:
163
+ print(f"Out of credits — upgrade at {err.upgrade_url}")
164
+ except RateLimitError as err:
165
+ print(f"Rate limited, retry in {err.retry_after}s")
166
+ except FopostError as err:
167
+ print(f"API {err.status} ({err.code}): {err.message}")
168
+ ```
169
+
170
+ | Status | Exception |
171
+ | ------ | ----------------------- |
172
+ | 401 | `AuthenticationError` |
173
+ | 402 | `PaymentRequiredError` |
174
+ | 403 | `PermissionDeniedError` |
175
+ | 404 | `NotFoundError` |
176
+ | 429 | `RateLimitError` |
177
+ | other | `FopostError` |
178
+
179
+ ## Resources
180
+
181
+ | Namespace | Methods |
182
+ | ------------ | -------------------------------------------------------------------------------------- |
183
+ | `posts` | `list`, `iter`, `iter_pages`, `get`, `create`, `update`, `delete`, `publish`, `cancel`, `retry`, `preflight`, `deliveries` |
184
+ | `accounts` | `list`, `get`, `health` |
185
+ | `workspaces` | `list`, `get` |
186
+ | `labels` | `list` |
187
+ | `ai` | `credits`, `generate_caption`, `rewrite`, `repurpose_url` |
188
+
189
+ For an endpoint the SDK does not wrap yet, `client.request` sends an
190
+ authenticated call and hands back the decoded body:
191
+
192
+ ```python
193
+ client.request("GET", "/analytics/summary", params={"workspace_id": workspace.id})
194
+ ```
195
+
196
+ ## Example
197
+
198
+ [`examples/create_post.py`](examples/create_post.py) creates a post against a
199
+ running API:
200
+
201
+ ```bash
202
+ export FOPOST_API_KEY=osk_...
203
+ export FOPOST_BASE_URL=http://localhost:8080/api/v1
204
+ python examples/create_post.py "Hello from the Python SDK" --publish
205
+ ```
206
+
207
+ ## Contributing
208
+
209
+ Issues and pull requests are welcome at
210
+ [fopost/fopost-python](https://github.com/fopost/fopost-python).
211
+
212
+ ```bash
213
+ uv sync --group dev
214
+ uv run pytest
215
+ uv run ruff check .
216
+ uv run mypy
217
+ ```
218
+
219
+ ## License
220
+
221
+ MIT
222
+
223
+ Questions or a problem: [fopost.com/contact](https://fopost.com/contact).
@@ -0,0 +1,83 @@
1
+ """Create a post and publish it.
2
+
3
+ Point it at a local dev API:
4
+
5
+ export FOPOST_API_KEY=osk_...
6
+ export FOPOST_BASE_URL=http://localhost:8080/api/v1
7
+ python examples/create_post.py "Hello from the Python SDK"
8
+
9
+ Without --publish it stops at a draft, so you can run it against a real
10
+ workspace without anything going out.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import argparse
16
+ import os
17
+ import sys
18
+
19
+ from fopost import DEFAULT_BASE_URL, Fopost, FopostError
20
+
21
+
22
+ def main() -> int:
23
+ parser = argparse.ArgumentParser(description="Create a post with the FoPost SDK.")
24
+ parser.add_argument(
25
+ "text",
26
+ nargs="?",
27
+ default="Hello from the FoPost Python SDK",
28
+ help="post body",
29
+ )
30
+ parser.add_argument("--workspace", help="workspace id (defaults to the first one)")
31
+ parser.add_argument(
32
+ "--publish",
33
+ action="store_true",
34
+ help="publish immediately instead of leaving a draft",
35
+ )
36
+ args = parser.parse_args()
37
+
38
+ api_key = os.environ.get("FOPOST_API_KEY")
39
+ if not api_key:
40
+ print("Set FOPOST_API_KEY first.", file=sys.stderr)
41
+ return 1
42
+
43
+ base_url = os.environ.get("FOPOST_BASE_URL", DEFAULT_BASE_URL)
44
+
45
+ with Fopost(api_key=api_key, base_url=base_url) as client:
46
+ try:
47
+ workspace_id = args.workspace
48
+ if not workspace_id:
49
+ workspaces = client.workspaces.list()
50
+ if not workspaces:
51
+ print("No workspaces on this key.", file=sys.stderr)
52
+ return 1
53
+ workspace_id = workspaces[0].id
54
+ print(f"Workspace: {workspaces[0].name} ({workspace_id})")
55
+
56
+ accounts = client.accounts.list(workspace_id=workspace_id)
57
+ if not accounts:
58
+ print("No connected accounts in this workspace.", file=sys.stderr)
59
+ return 1
60
+ for account in accounts:
61
+ print(f" · {account.platform}: @{account.username}")
62
+
63
+ post = client.posts.create(
64
+ workspace_id=workspace_id,
65
+ content=args.text,
66
+ accounts=[a.id for a in accounts],
67
+ )
68
+ print(f"Created post {post.id} ({post.status})")
69
+
70
+ if args.publish:
71
+ client.posts.publish(post.id)
72
+ for delivery in client.posts.deliveries(post.id):
73
+ print(f" · {delivery.platform}: {delivery.status}")
74
+
75
+ return 0
76
+
77
+ except FopostError as err:
78
+ print(f"API error: {err}", file=sys.stderr)
79
+ return 1
80
+
81
+
82
+ if __name__ == "__main__":
83
+ raise SystemExit(main())
@@ -0,0 +1,71 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "fopost"
7
+ version = "0.1.0"
8
+ description = "Official Python SDK for the FoPost API. Schedule and publish to 31 social platforms from your code."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+ requires-python = ">=3.10"
13
+ authors = [{ name = "FoPost, Porter Bridge, LLC" }]
14
+ keywords = [
15
+ "fopost",
16
+ "social-media",
17
+ "publishing",
18
+ "scheduling",
19
+ "twitter",
20
+ "linkedin",
21
+ "instagram",
22
+ "api",
23
+ "sdk",
24
+ ]
25
+ classifiers = [
26
+ "Development Status :: 3 - Alpha",
27
+ "Intended Audience :: Developers",
28
+ "Programming Language :: Python :: 3",
29
+ "Programming Language :: Python :: 3.10",
30
+ "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ "Programming Language :: Python :: 3.13",
33
+ "Topic :: Internet :: WWW/HTTP",
34
+ "Typing :: Typed",
35
+ ]
36
+ dependencies = ["httpx>=0.27", "pydantic>=2.7"]
37
+
38
+ [project.urls]
39
+ Homepage = "https://fopost.com"
40
+ Documentation = "https://fopost.com/docs"
41
+ Repository = "https://github.com/fopost/fopost-python"
42
+ Issues = "https://github.com/fopost/fopost-python/issues"
43
+ Support = "https://fopost.com/contact"
44
+
45
+ [dependency-groups]
46
+ dev = ["pytest>=8", "respx>=0.21", "ruff>=0.6", "mypy>=1.11"]
47
+
48
+ [tool.hatch.build.targets.wheel]
49
+ packages = ["src/fopost"]
50
+
51
+ [tool.hatch.build.targets.sdist]
52
+ include = ["src/fopost", "tests", "examples", "README.md", "LICENSE"]
53
+
54
+ [tool.pytest.ini_options]
55
+ testpaths = ["tests"]
56
+ addopts = "-q"
57
+
58
+ [tool.ruff]
59
+ line-length = 100
60
+ src = ["src", "tests"]
61
+
62
+ [tool.ruff.lint]
63
+ select = ["E", "F", "I", "UP", "B"]
64
+
65
+ [tool.ruff.lint.isort]
66
+ known-first-party = ["fopost", "tests"]
67
+
68
+ [tool.mypy]
69
+ python_version = "3.10"
70
+ strict = true
71
+ files = ["src/fopost"]