fronta 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.
fronta-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ondrej
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.
fronta-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,226 @@
1
+ Metadata-Version: 2.4
2
+ Name: fronta
3
+ Version: 0.1.0
4
+ Summary: Distributed task queue for asyncio tasks and sandboxed processes
5
+ Keywords: task queue,postgresql,asyncio,sandbox,bubblewrap,mcp,workers
6
+ Author: Ondrej
7
+ Author-email: Ondrej <ondrej@ilcik.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ License-File: THIRD_PARTY_NOTICES.md
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Framework :: Pydantic :: 2
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Classifier: Topic :: System :: Distributed Computing
22
+ Classifier: Typing :: Typed
23
+ Requires-Dist: click>=8.1.1,<9
24
+ Requires-Dist: psycopg-pool>=3.2.8,<4
25
+ Requires-Dist: psycopg[binary,pool]>=3.2.11,<4
26
+ Requires-Dist: pydantic>=2.12,<3
27
+ Requires-Dist: pydantic-settings>=2.5,<3
28
+ Requires-Dist: fastapi>=0.110,<1 ; extra == 'server'
29
+ Requires-Dist: jinja2>=3.1,<4 ; extra == 'server'
30
+ Requires-Dist: jsonschema[format-nongpl]>=4.20,<5 ; extra == 'server'
31
+ Requires-Dist: mcp>=2.1,<3 ; extra == 'server'
32
+ Requires-Dist: uvicorn>=0.31.1,<1 ; extra == 'server'
33
+ Requires-Python: >=3.12
34
+ Project-URL: Homepage, https://github.com/dreo/fronta
35
+ Project-URL: Repository, https://github.com/dreo/fronta
36
+ Project-URL: Issues, https://github.com/dreo/fronta/issues
37
+ Project-URL: Changelog, https://github.com/dreo/fronta/blob/main/CHANGELOG.md
38
+ Provides-Extra: server
39
+ Description-Content-Type: text/markdown
40
+
41
+ # Fronta
42
+
43
+ Task queue on PostgreSQL for Python. Workers run `async def` handlers in-process and executables
44
+ in bubblewrap sandboxes; an optional server exposes the queue over REST and MCP with a small
45
+ dashboard.
46
+
47
+ Fronta keeps its tables in a `fronta` schema of your own PostgreSQL 16+ database; there is no
48
+ broker. Workers claim rows with `SELECT … FOR UPDATE SKIP LOCKED`, hold leases renewed by
49
+ heartbeats, and record every state change in one fenced transaction, so the tasks of a crashed or
50
+ stalled worker are reaped and retried while attempts remain. Priorities, scheduled runs, dedupe
51
+ keys, retries with jittered backoff and concurrency limits (per task type and per key) are enforced
52
+ in the database; attempt timeouts and cancellation by the worker.
53
+
54
+ **Status:** alpha. The API and the schema can change between minor versions before 1.0 (see
55
+ `CHANGELOG.md`). Linux, Python 3.12–3.14, PostgreSQL 16+.
56
+
57
+ ## Install
58
+
59
+ ```bash
60
+ uv add fronta # SDK + worker
61
+ uv add "fronta[server]" # + REST/MCP server and dashboard
62
+ ```
63
+
64
+ `pip install fronta` works the same. Hosts that run sandboxed processes need `bwrap` (bubblewrap),
65
+ `prlimit` (util-linux) and unprivileged user namespaces; `import fronta` and the SDK work anywhere.
66
+
67
+ ## Example
68
+
69
+ ```python
70
+ # app/tasks.py
71
+ import fronta
72
+ from pydantic import BaseModel
73
+
74
+
75
+ class Resize(BaseModel):
76
+ image_id: int
77
+ width: int
78
+
79
+
80
+ @fronta.task("resize", input=Resize, max_attempts=5, attempt_timeout=120)
81
+ async def resize(ctx: fronta.Context, job: Resize) -> dict[str, int]:
82
+ await ctx.progress({"stage": "download"})
83
+ ... # idempotent work that honors CancelledError
84
+ return {"bytes": 12345}
85
+
86
+
87
+ worker = fronta.Worker([resize])
88
+ ```
89
+
90
+ ```bash
91
+ export FRONTA_DSN=postgresql://user:pass@host/db # the role must be able to create the schema
92
+ fronta db init # creates schema `fronta`; safe to repeat
93
+ fronta worker app.tasks:worker # runs until SIGTERM/SIGINT
94
+ ```
95
+
96
+ ```python
97
+ # enqueue.py: any process that reaches the database
98
+ import asyncio
99
+
100
+ import fronta
101
+ from app.tasks import Resize, resize
102
+
103
+
104
+ async def main() -> None:
105
+ await fronta.open_pool() # once, at application start
106
+ try:
107
+ task_id = await resize.enqueue(Resize(image_id=7, width=800), priority=5, key="resize-7")
108
+ print(task_id)
109
+ finally:
110
+ await fronta.close_pool() # at application shutdown
111
+
112
+
113
+ asyncio.run(main())
114
+ ```
115
+
116
+ `enqueue(..., conn=conn)` joins your own psycopg transaction instead of using the pool. `key`
117
+ dedupes: while a task with the same key is queued or running, `enqueue` returns its id; once
118
+ that task has finished, the same key enqueues a new one.
119
+
120
+ A handler gets the validated input and a `Context` (`task_id`, `attempt`, `log`, `progress()`,
121
+ `enqueue()`, `cancelled`, `state` from the worker lifespan). It must handle
122
+ `asyncio.CancelledError` and be safe to run twice: after a lost lease the task runs again.
123
+
124
+ A sandboxed process task, with a placeholder executable:
125
+
126
+ ```python
127
+ class Convert(BaseModel):
128
+ source: str
129
+
130
+
131
+ convert = fronta.process_task(
132
+ "convert",
133
+ ["/usr/bin/convert-tool", "--from-stdin"], # reads the JSON input on stdin
134
+ input=Convert,
135
+ sandbox=fronta.Sandbox(memory_bytes=512 << 20, cpu_time_s=60, max_pids=16),
136
+ max_concurrency=4,
137
+ )
138
+ ```
139
+
140
+ The process runs in a private tmpfs `/work` without network; its result is
141
+ `{"exit_code", "stdout", "stderr", "truncated"}`. Exit code 0 means the task succeeded; anything
142
+ else fails the attempt.
143
+
144
+ ## Server
145
+
146
+ ```bash
147
+ FRONTA_SERVER_TOKEN=... fronta server # 127.0.0.1:8000
148
+ ```
149
+
150
+ REST under `/api/v1` (task types, enqueue, get, list, cancel), MCP at `/mcp`, dashboard at `/`.
151
+ The SDK only enqueues; inspection and cancellation go through the server. `FRONTA_SERVER_TOKEN`
152
+ is required (the server never runs open: even on loopback a browser could be made to cancel or
153
+ enqueue tasks); every REST and MCP request sends it as `Authorization: Bearer <token>`. Put a
154
+ TLS-terminating reverse proxy in front of it outside a private network. Endpoints, inputs and error codes:
155
+ [docs/reference.md](https://github.com/dreo/fronta/blob/main/docs/reference.md#server).
156
+
157
+ ## Deploy
158
+
159
+ One database, any number of workers, optionally a server. Each process reads `FRONTA_*`
160
+ environment variables; `FRONTA_DSN` is the only required one. Run workers under a supervisor that
161
+ restarts them: a worker exits 0 after a graceful stop and 70 when a handler ignores cancellation or
162
+ blocks the event loop.
163
+
164
+ ```ini
165
+ # /etc/systemd/system/fronta-worker.service
166
+ [Unit]
167
+ Description=Fronta worker
168
+ After=network-online.target
169
+
170
+ [Service]
171
+ User=app
172
+ WorkingDirectory=/srv/app
173
+ # FRONTA_DSN=... and other FRONTA_* variables; readable by root only (mode 0600)
174
+ EnvironmentFile=/etc/fronta/worker.env
175
+ ExecStart=/srv/app/.venv/bin/fronta worker app.tasks:worker
176
+ # SIGTERM goes to the worker only, which stops its sandboxes itself; SIGKILL to everything
177
+ KillMode=mixed
178
+ Restart=always
179
+ RestartSec=2
180
+ TimeoutStopSec=120
181
+
182
+ [Install]
183
+ WantedBy=multi-user.target
184
+ ```
185
+
186
+ `systemctl enable --now fronta-worker`. On `SIGTERM` the worker stops claiming, lets running
187
+ attempts finish for `FRONTA_GRACE_S`, then stops the rest (another grace period for cooperative
188
+ cancellation, then a kill) and records every outcome before it exits. That takes at most
189
+ `2 × FRONTA_GRACE_S + 6 × FRONTA_KILL_TIMEOUT_S` (91 s with the defaults 30 s and 5 s) unless the
190
+ database is unreachable or a sandbox cannot be killed: then the worker keeps trying rather than
191
+ lose an outcome, and systemd's `SIGKILL` at `TimeoutStopSec` ends it. That loses no data:
192
+ sandboxes die with the worker and unrecorded attempts are retried when their lease expires.
193
+
194
+ Throughput is bounded by the database's commit rate (at least two durable commits per task, plus
195
+ heartbeats and progress): about 150 no-op tasks/s in total on a laptop PostgreSQL with fsync; a
196
+ claim costs ~5 ms on a 70k-row queue. Configuration, retry policy, guarantees and the
197
+ measurements: [docs/reference.md](https://github.com/dreo/fronta/blob/main/docs/reference.md).
198
+
199
+ ## Not covered
200
+
201
+ - Exactly-once side effects: a worker stalled past its lease may still be running while the task
202
+ is retried elsewhere; the stale attempt's writes to Fronta are rejected, its other effects are not.
203
+ - Workflows, chains, or periodic tasks (only `run_at`).
204
+ - Schema migrations before 1.0: a release that changes the schema needs `fronta db init` on a
205
+ fresh schema.
206
+ - Windows or macOS workers: sandboxes are Linux-only and nothing else is tested there.
207
+
208
+ ## Development
209
+
210
+ ```bash
211
+ uv sync --all-extras
212
+ docker run -d --name fronta-test-pg -e POSTGRES_USER=fronta -e POSTGRES_PASSWORD=fronta \
213
+ -e POSTGRES_DB=fronta -p 127.0.0.1:5439:5432 postgres:16
214
+ export FRONTA_TEST_DSN=postgresql://fronta:fronta@127.0.0.1:5439/fronta
215
+ make check # lint, format, types, architecture, deps (also the git pre-commit hook)
216
+ make checkall # check + the full test suite + pip-audit
217
+ ```
218
+
219
+ CI runs `make checkall` on every pull request and push to `main`, on Python 3.12–3.14 and at the
220
+ lowest dependency versions the declared bounds allow. To release: set the version (`uv version X.Y.Z`), add the CHANGELOG
221
+ section, merge, then push the tag `vX.Y.Z` from that `main` commit; the gate runs again, the
222
+ package goes to PyPI and a GitHub release is created. `SPEC.md` is the contract.
223
+
224
+ ## License
225
+
226
+ MIT. The dashboard bundles Alpine.js (MIT); see `THIRD_PARTY_NOTICES.md`.
fronta-0.1.0/README.md ADDED
@@ -0,0 +1,186 @@
1
+ # Fronta
2
+
3
+ Task queue on PostgreSQL for Python. Workers run `async def` handlers in-process and executables
4
+ in bubblewrap sandboxes; an optional server exposes the queue over REST and MCP with a small
5
+ dashboard.
6
+
7
+ Fronta keeps its tables in a `fronta` schema of your own PostgreSQL 16+ database; there is no
8
+ broker. Workers claim rows with `SELECT … FOR UPDATE SKIP LOCKED`, hold leases renewed by
9
+ heartbeats, and record every state change in one fenced transaction, so the tasks of a crashed or
10
+ stalled worker are reaped and retried while attempts remain. Priorities, scheduled runs, dedupe
11
+ keys, retries with jittered backoff and concurrency limits (per task type and per key) are enforced
12
+ in the database; attempt timeouts and cancellation by the worker.
13
+
14
+ **Status:** alpha. The API and the schema can change between minor versions before 1.0 (see
15
+ `CHANGELOG.md`). Linux, Python 3.12–3.14, PostgreSQL 16+.
16
+
17
+ ## Install
18
+
19
+ ```bash
20
+ uv add fronta # SDK + worker
21
+ uv add "fronta[server]" # + REST/MCP server and dashboard
22
+ ```
23
+
24
+ `pip install fronta` works the same. Hosts that run sandboxed processes need `bwrap` (bubblewrap),
25
+ `prlimit` (util-linux) and unprivileged user namespaces; `import fronta` and the SDK work anywhere.
26
+
27
+ ## Example
28
+
29
+ ```python
30
+ # app/tasks.py
31
+ import fronta
32
+ from pydantic import BaseModel
33
+
34
+
35
+ class Resize(BaseModel):
36
+ image_id: int
37
+ width: int
38
+
39
+
40
+ @fronta.task("resize", input=Resize, max_attempts=5, attempt_timeout=120)
41
+ async def resize(ctx: fronta.Context, job: Resize) -> dict[str, int]:
42
+ await ctx.progress({"stage": "download"})
43
+ ... # idempotent work that honors CancelledError
44
+ return {"bytes": 12345}
45
+
46
+
47
+ worker = fronta.Worker([resize])
48
+ ```
49
+
50
+ ```bash
51
+ export FRONTA_DSN=postgresql://user:pass@host/db # the role must be able to create the schema
52
+ fronta db init # creates schema `fronta`; safe to repeat
53
+ fronta worker app.tasks:worker # runs until SIGTERM/SIGINT
54
+ ```
55
+
56
+ ```python
57
+ # enqueue.py: any process that reaches the database
58
+ import asyncio
59
+
60
+ import fronta
61
+ from app.tasks import Resize, resize
62
+
63
+
64
+ async def main() -> None:
65
+ await fronta.open_pool() # once, at application start
66
+ try:
67
+ task_id = await resize.enqueue(Resize(image_id=7, width=800), priority=5, key="resize-7")
68
+ print(task_id)
69
+ finally:
70
+ await fronta.close_pool() # at application shutdown
71
+
72
+
73
+ asyncio.run(main())
74
+ ```
75
+
76
+ `enqueue(..., conn=conn)` joins your own psycopg transaction instead of using the pool. `key`
77
+ dedupes: while a task with the same key is queued or running, `enqueue` returns its id; once
78
+ that task has finished, the same key enqueues a new one.
79
+
80
+ A handler gets the validated input and a `Context` (`task_id`, `attempt`, `log`, `progress()`,
81
+ `enqueue()`, `cancelled`, `state` from the worker lifespan). It must handle
82
+ `asyncio.CancelledError` and be safe to run twice: after a lost lease the task runs again.
83
+
84
+ A sandboxed process task, with a placeholder executable:
85
+
86
+ ```python
87
+ class Convert(BaseModel):
88
+ source: str
89
+
90
+
91
+ convert = fronta.process_task(
92
+ "convert",
93
+ ["/usr/bin/convert-tool", "--from-stdin"], # reads the JSON input on stdin
94
+ input=Convert,
95
+ sandbox=fronta.Sandbox(memory_bytes=512 << 20, cpu_time_s=60, max_pids=16),
96
+ max_concurrency=4,
97
+ )
98
+ ```
99
+
100
+ The process runs in a private tmpfs `/work` without network; its result is
101
+ `{"exit_code", "stdout", "stderr", "truncated"}`. Exit code 0 means the task succeeded; anything
102
+ else fails the attempt.
103
+
104
+ ## Server
105
+
106
+ ```bash
107
+ FRONTA_SERVER_TOKEN=... fronta server # 127.0.0.1:8000
108
+ ```
109
+
110
+ REST under `/api/v1` (task types, enqueue, get, list, cancel), MCP at `/mcp`, dashboard at `/`.
111
+ The SDK only enqueues; inspection and cancellation go through the server. `FRONTA_SERVER_TOKEN`
112
+ is required (the server never runs open: even on loopback a browser could be made to cancel or
113
+ enqueue tasks); every REST and MCP request sends it as `Authorization: Bearer <token>`. Put a
114
+ TLS-terminating reverse proxy in front of it outside a private network. Endpoints, inputs and error codes:
115
+ [docs/reference.md](https://github.com/dreo/fronta/blob/main/docs/reference.md#server).
116
+
117
+ ## Deploy
118
+
119
+ One database, any number of workers, optionally a server. Each process reads `FRONTA_*`
120
+ environment variables; `FRONTA_DSN` is the only required one. Run workers under a supervisor that
121
+ restarts them: a worker exits 0 after a graceful stop and 70 when a handler ignores cancellation or
122
+ blocks the event loop.
123
+
124
+ ```ini
125
+ # /etc/systemd/system/fronta-worker.service
126
+ [Unit]
127
+ Description=Fronta worker
128
+ After=network-online.target
129
+
130
+ [Service]
131
+ User=app
132
+ WorkingDirectory=/srv/app
133
+ # FRONTA_DSN=... and other FRONTA_* variables; readable by root only (mode 0600)
134
+ EnvironmentFile=/etc/fronta/worker.env
135
+ ExecStart=/srv/app/.venv/bin/fronta worker app.tasks:worker
136
+ # SIGTERM goes to the worker only, which stops its sandboxes itself; SIGKILL to everything
137
+ KillMode=mixed
138
+ Restart=always
139
+ RestartSec=2
140
+ TimeoutStopSec=120
141
+
142
+ [Install]
143
+ WantedBy=multi-user.target
144
+ ```
145
+
146
+ `systemctl enable --now fronta-worker`. On `SIGTERM` the worker stops claiming, lets running
147
+ attempts finish for `FRONTA_GRACE_S`, then stops the rest (another grace period for cooperative
148
+ cancellation, then a kill) and records every outcome before it exits. That takes at most
149
+ `2 × FRONTA_GRACE_S + 6 × FRONTA_KILL_TIMEOUT_S` (91 s with the defaults 30 s and 5 s) unless the
150
+ database is unreachable or a sandbox cannot be killed: then the worker keeps trying rather than
151
+ lose an outcome, and systemd's `SIGKILL` at `TimeoutStopSec` ends it. That loses no data:
152
+ sandboxes die with the worker and unrecorded attempts are retried when their lease expires.
153
+
154
+ Throughput is bounded by the database's commit rate (at least two durable commits per task, plus
155
+ heartbeats and progress): about 150 no-op tasks/s in total on a laptop PostgreSQL with fsync; a
156
+ claim costs ~5 ms on a 70k-row queue. Configuration, retry policy, guarantees and the
157
+ measurements: [docs/reference.md](https://github.com/dreo/fronta/blob/main/docs/reference.md).
158
+
159
+ ## Not covered
160
+
161
+ - Exactly-once side effects: a worker stalled past its lease may still be running while the task
162
+ is retried elsewhere; the stale attempt's writes to Fronta are rejected, its other effects are not.
163
+ - Workflows, chains, or periodic tasks (only `run_at`).
164
+ - Schema migrations before 1.0: a release that changes the schema needs `fronta db init` on a
165
+ fresh schema.
166
+ - Windows or macOS workers: sandboxes are Linux-only and nothing else is tested there.
167
+
168
+ ## Development
169
+
170
+ ```bash
171
+ uv sync --all-extras
172
+ docker run -d --name fronta-test-pg -e POSTGRES_USER=fronta -e POSTGRES_PASSWORD=fronta \
173
+ -e POSTGRES_DB=fronta -p 127.0.0.1:5439:5432 postgres:16
174
+ export FRONTA_TEST_DSN=postgresql://fronta:fronta@127.0.0.1:5439/fronta
175
+ make check # lint, format, types, architecture, deps (also the git pre-commit hook)
176
+ make checkall # check + the full test suite + pip-audit
177
+ ```
178
+
179
+ CI runs `make checkall` on every pull request and push to `main`, on Python 3.12–3.14 and at the
180
+ lowest dependency versions the declared bounds allow. To release: set the version (`uv version X.Y.Z`), add the CHANGELOG
181
+ section, merge, then push the tag `vX.Y.Z` from that `main` commit; the gate runs again, the
182
+ package goes to PyPI and a GitHub release is created. `SPEC.md` is the contract.
183
+
184
+ ## License
185
+
186
+ MIT. The dashboard bundles Alpine.js (MIT); see `THIRD_PARTY_NOTICES.md`.
@@ -0,0 +1,30 @@
1
+ # Third-party notices
2
+
3
+ ## Alpine.js 3.14.9 (vendored as `fronta/server/static/alpine.min.js`)
4
+
5
+ Source: https://cdn.jsdelivr.net/npm/alpinejs@3.14.9/dist/cdn.min.js
6
+ (https://github.com/alpinejs/alpine), SHA-256 `3ed1eed252488921df65e363d6715deb04d7f92aaedb9e52199fdf73cb1e0ad3`.
7
+ To update: download the new `dist/cdn.min.js`, replace the file, update the version and the
8
+ checksum here, and run the dashboard test (`tests/test_dashboard.py`).
9
+
10
+ MIT License
11
+
12
+ Copyright © 2019-2025 Caleb Porzio and contributors
13
+
14
+ Permission is hereby granted, free of charge, to any person obtaining a copy
15
+ of this software and associated documentation files (the "Software"), to deal
16
+ in the Software without restriction, including without limitation the rights
17
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18
+ copies of the Software, and to permit persons to whom the Software is
19
+ furnished to do so, subject to the following conditions:
20
+
21
+ The above copyright notice and this permission notice shall be included in all
22
+ copies or substantial portions of the Software.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
+ SOFTWARE.
@@ -0,0 +1,123 @@
1
+ [project]
2
+ name = "fronta"
3
+ version = "0.1.0"
4
+ description = "Distributed task queue for asyncio tasks and sandboxed processes"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Ondrej", email = "ondrej@ilcik.com" }
8
+ ]
9
+ license = "MIT"
10
+ license-files = ["LICENSE", "THIRD_PARTY_NOTICES.md"]
11
+ requires-python = ">=3.12"
12
+ keywords = ["task queue", "postgresql", "asyncio", "sandbox", "bubblewrap", "mcp", "workers"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Framework :: AsyncIO",
16
+ "Framework :: FastAPI",
17
+ "Framework :: Pydantic :: 2",
18
+ "Intended Audience :: Developers",
19
+ "Operating System :: POSIX :: Linux",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Programming Language :: Python :: 3.14",
24
+ "Topic :: System :: Distributed Computing",
25
+ "Typing :: Typed",
26
+ ]
27
+ dependencies = [
28
+ # Every floor is tested: CI runs the suite at `--resolution lowest-direct` with all extras and
29
+ # these are the versions it installs. Ceilings exclude the next major of contracts we depend
30
+ # on. `psycopg[binary]` gives an out-of-the-box install; an application that ships
31
+ # `psycopg[c]` keeps it (psycopg prefers the C implementation).
32
+ "click>=8.1.1,<9", # 8.1.0's decorators are untyped for mypy
33
+ "psycopg-pool>=3.2.8,<4", # 3.2.8: CancelledError inside connection() no longer swallowed
34
+ "psycopg[binary,pool]>=3.2.11,<4", # 3.2.4 keeps notifies across notifies() restarts; 3.2.10/11 cancel + wait fixes
35
+ "pydantic>=2.12,<3", # mcp 2.1 needs 2.12; the lowest version CI tests
36
+ "pydantic-settings>=2.5,<3",
37
+ ]
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/dreo/fronta"
41
+ Repository = "https://github.com/dreo/fronta"
42
+ Issues = "https://github.com/dreo/fronta/issues"
43
+ Changelog = "https://github.com/dreo/fronta/blob/main/CHANGELOG.md"
44
+
45
+ [project.optional-dependencies]
46
+ # `fronta server` (REST + MCP + dashboard); workers and the SDK do not need it.
47
+ server = [
48
+ "fastapi>=0.110,<1", # lifespan= and Annotated dependencies
49
+ "jinja2>=3.1,<4",
50
+ "jsonschema[format-nongpl]>=4.20,<5", # Draft 2020-12 + FORMAT_CHECKER
51
+ "mcp>=2.1,<3", # MCPServer + streamable_http_app(host=, max_request_body_size=)
52
+ "uvicorn>=0.31.1,<1", # timeout_graceful_shutdown=
53
+ ]
54
+
55
+ [project.scripts]
56
+ fronta = "fronta.cli:main"
57
+
58
+ [build-system]
59
+ requires = ["uv_build>=0.11.18,<0.12.0"]
60
+ build-backend = "uv_build"
61
+
62
+ [dependency-groups]
63
+ dev = [
64
+ "deptry>=0.25.1",
65
+ "httpx>=0.28.1",
66
+ "import-linter>=2.14",
67
+ "mypy>=2.3.1",
68
+ "pip-audit>=2.10.1",
69
+ "playwright>=1.62.0",
70
+ "pre-commit>=4.6.2",
71
+ "pytest>=9.1.1",
72
+ "pytest-asyncio>=1.4.0",
73
+ "ruff>=0.16.5",
74
+ "types-jsonschema>=4.26.0.20260518",
75
+ ]
76
+
77
+ [tool.ruff]
78
+ target-version = "py312"
79
+ line-length = 100
80
+
81
+ [tool.ruff.lint]
82
+ select = [
83
+ "E", "W", "F", "I", "S", "B", "C4", "UP", "D", "PT",
84
+ "RUF", "ANN", "SIM", "TC", "ARG", "ERA", "PL", "PTH", "DTZ", "T20",
85
+ ]
86
+ ignore = ["D1"]
87
+
88
+ [tool.ruff.lint.pydocstyle]
89
+ convention = "google"
90
+
91
+ [tool.ruff.lint.per-file-ignores]
92
+ "tests/**" = ["S101", "D", "ANN", "PLR2004"]
93
+
94
+ [tool.mypy]
95
+ strict = true
96
+ packages = ["fronta"]
97
+ mypy_path = "src"
98
+
99
+ [tool.importlinter]
100
+ root_package = "fronta"
101
+
102
+ [[tool.importlinter.contracts]]
103
+ name = "Layers: cli > server | worker > executors > sandbox | definitions > runtime > store > codec > model | config | errors"
104
+ type = "layers"
105
+ layers = [
106
+ "fronta.cli",
107
+ "fronta.server | fronta.worker",
108
+ "fronta.executors",
109
+ "fronta.sandbox | fronta.definitions",
110
+ "fronta.runtime",
111
+ "fronta.store",
112
+ "fronta.codec",
113
+ "fronta.model | fronta.config | fronta.errors",
114
+ ]
115
+
116
+ [tool.deptry]
117
+ # Add per-rule ignores only with a justifying comment.
118
+
119
+ [tool.pytest.ini_options]
120
+ asyncio_mode = "auto"
121
+ asyncio_default_fixture_loop_scope = "function"
122
+ asyncio_default_test_loop_scope = "function"
123
+ testpaths = ["tests"]
@@ -0,0 +1,58 @@
1
+ """Fronta: distributed task processing on PostgreSQL with sandboxed process execution."""
2
+
3
+ from importlib.metadata import version
4
+
5
+ from fronta.config import Settings
6
+ from fronta.definitions import Context, ProcessTaskDefinition, TaskDefinition, process_task, task
7
+ from fronta.errors import (
8
+ ConfigurationError,
9
+ FrontaError,
10
+ InputValidationError,
11
+ InvalidInput,
12
+ NonRetryableError,
13
+ NotCancellable,
14
+ PayloadTooLarge,
15
+ ProgressTooLarge,
16
+ ResultSerializationError,
17
+ SandboxError,
18
+ TaskNotFound,
19
+ UnknownTaskType,
20
+ )
21
+ from fronta.model import Backoff, Policy, Sandbox, State, TaskRow, TaskSummary, TaskTypeRow
22
+ from fronta.runtime import close_pool, configure, open_pool
23
+ from fronta.worker import Worker
24
+
25
+ __version__: str = version("fronta")
26
+
27
+ __all__ = [
28
+ "Backoff",
29
+ "ConfigurationError",
30
+ "Context",
31
+ "FrontaError",
32
+ "InputValidationError",
33
+ "InvalidInput",
34
+ "NonRetryableError",
35
+ "NotCancellable",
36
+ "PayloadTooLarge",
37
+ "Policy",
38
+ "ProcessTaskDefinition",
39
+ "ProgressTooLarge",
40
+ "ResultSerializationError",
41
+ "Sandbox",
42
+ "SandboxError",
43
+ "Settings",
44
+ "State",
45
+ "TaskDefinition",
46
+ "TaskNotFound",
47
+ "TaskRow",
48
+ "TaskSummary",
49
+ "TaskTypeRow",
50
+ "UnknownTaskType",
51
+ "Worker",
52
+ "__version__",
53
+ "close_pool",
54
+ "configure",
55
+ "open_pool",
56
+ "process_task",
57
+ "task",
58
+ ]