queueio 0.5.0__tar.gz → 0.7.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.
- queueio-0.7.0/.github/workflows/docs.yml +31 -0
- {queueio-0.5.0 → queueio-0.7.0}/.gitignore +3 -0
- {queueio-0.5.0 → queueio-0.7.0}/CHANGELOG.md +32 -1
- queueio-0.7.0/CNAME +1 -0
- {queueio-0.5.0 → queueio-0.7.0}/PKG-INFO +10 -5
- {queueio-0.5.0 → queueio-0.7.0}/README.md +4 -4
- queueio-0.7.0/docs_overrides/assets/images/favicon.svg +1 -0
- queueio-0.7.0/docs_overrides/assets/stylesheets/extra.css +7 -0
- queueio-0.7.0/docs_overrides/partials/javascripts/palette.html +47 -0
- queueio-0.7.0/logo.svg +1 -0
- queueio-0.7.0/mkdocs.yml +41 -0
- queueio-0.5.0/principles.md → queueio-0.7.0/philosophy.md +1 -1
- {queueio-0.5.0 → queueio-0.7.0}/pyproject.toml +18 -3
- queueio-0.7.0/pyrightconfig.json +5 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/__init__.py +2 -2
- {queueio-0.5.0 → queueio-0.7.0}/queueio/__main__.py +26 -47
- queueio-0.7.0/queueio/backend.py +18 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/broker.py +7 -13
- {queueio-0.5.0 → queueio-0.7.0}/queueio/broker_test.py +9 -16
- queueio-0.7.0/queueio/django/README.md +82 -0
- queueio-0.7.0/queueio/django/__init__.py +17 -0
- queueio-0.7.0/queueio/django/apps.py +6 -0
- queueio-0.7.0/queueio/django/djp.py +6 -0
- queueio-0.7.0/queueio/django/management/commands/runserver.py +41 -0
- queueio-0.7.0/queueio/django/management/commands/runserver_plus.py +9 -0
- queueio-0.7.0/queueio/django/runserver_test.py +63 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/freethreading_test.py +5 -1
- {queueio-0.5.0 → queueio-0.7.0}/queueio/journal.py +0 -6
- {queueio-0.5.0 → queueio-0.7.0}/queueio/monitor.py +2 -2
- queueio-0.7.0/queueio/pika/__init__.py +31 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/pika/broker.py +27 -23
- queueio-0.7.0/queueio/pika/broker_test.py +18 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/pika/journal.py +13 -12
- queueio-0.7.0/queueio/pika/journal_test.py +15 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/pika/receiver.py +1 -0
- queueio-0.7.0/queueio/psycopg/README.md +136 -0
- queueio-0.7.0/queueio/psycopg/__init__.py +0 -0
- queueio-0.7.0/queueio/psycopg/broker-queries.md +129 -0
- queueio-0.7.0/queueio/psycopg/journal-queries.md +172 -0
- queueio-0.7.0/queueio/psycopg/journal.py +97 -0
- queueio-0.7.0/queueio/psycopg/journal_test.py +40 -0
- queueio-0.7.0/queueio/psycopg/receiver-queries.md +254 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/queueio.py +53 -40
- queueio-0.7.0/queueio/queueio_test.py +309 -0
- queueio-0.7.0/queueio/samples/__init__.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/samples/basic_test.py +2 -4
- queueio-0.7.0/queueio/samples/django/__init__.py +0 -0
- queueio-0.7.0/queueio/samples/django/apps.py +6 -0
- queueio-0.7.0/queueio/samples/django/job.py +29 -0
- queueio-0.7.0/queueio/samples/django/models.py +3 -0
- queueio-0.7.0/queueio/samples/django/settings.py +19 -0
- queueio-0.7.0/queueio/samples/django/settings_djp.py +24 -0
- queueio-0.7.0/queueio/samples/django/urls.py +7 -0
- queueio-0.7.0/queueio/samples/django_test.py +98 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/samples/expanded_test.py +2 -4
- {queueio-0.5.0 → queueio-0.7.0}/queueio/samples/priority_test.py +2 -4
- {queueio-0.5.0 → queueio-0.7.0}/queueio/samples/queuevar_test.py +2 -4
- queueio-0.7.0/queueio/stub/__init__.py +21 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/stub/broker.py +19 -12
- queueio-0.7.0/queueio/stub/broker_test.py +15 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/stub/journal.py +11 -4
- queueio-0.7.0/queueio/stub/journal_test.py +12 -0
- queueio-0.5.0/.claude/settings.local.json +0 -9
- queueio-0.5.0/pyrightconfig.json +0 -4
- queueio-0.5.0/queueio/pika/broker_test.py +0 -23
- queueio-0.5.0/queueio/pika/journal_test.py +0 -20
- queueio-0.5.0/queueio/queueio_test.py +0 -558
- queueio-0.5.0/queueio/stub/broker_test.py +0 -22
- queueio-0.5.0/queueio/stub/journal_test.py +0 -19
- {queueio-0.5.0 → queueio-0.7.0}/.devcontainer/devcontainer.json +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/.devcontainer/docker-compose.yml +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/.github/workflows/build.yml +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/.github/workflows/publish.yml +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/LICENSE +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/dill.pyi +0 -0
- {queueio-0.5.0 → queueio-0.7.0/docs_overrides/assets/images}/logo.svg +0 -0
- /queueio-0.5.0/queueio/pika/__init__.py → /queueio-0.7.0/docs_overrides/partials/footer.html +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/conftest.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/consumer.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/continuation.py +0 -0
- {queueio-0.5.0/queueio/samples → queueio-0.7.0/queueio/django/management}/__init__.py +0 -0
- {queueio-0.5.0/queueio/stub → queueio-0.7.0/queueio/django/management/commands}/__init__.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/event.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/gather.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/id.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0/queueio}/invocation.md +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/invocation.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/journal_test.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/message.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/pause.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/pika/threadsafe.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/queue.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/queue_test.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/queuespec.md +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/queuespec.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/queuespec_test.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/queuevar.md +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/queuevar.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/receiver.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/registry.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/result.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/routine.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/samples/basic.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/samples/expanded.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/samples/priority.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/samples/queuevar.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/select.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/stream.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/stub/receiver.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/suspension.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/thread.py +0 -0
- {queueio-0.5.0 → queueio-0.7.0}/queueio/worker.py +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Docs
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
pages: write
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: pages
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
deploy:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment:
|
|
19
|
+
name: github-pages
|
|
20
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: astral-sh/setup-uv@v5
|
|
24
|
+
- run: uv python install 3.14t
|
|
25
|
+
- run: uv sync --group dev
|
|
26
|
+
- run: uv run mkdocs build --strict
|
|
27
|
+
- uses: actions/upload-pages-artifact@v3
|
|
28
|
+
with:
|
|
29
|
+
path: site
|
|
30
|
+
- id: deployment
|
|
31
|
+
uses: actions/deploy-pages@v4
|
|
@@ -8,6 +8,35 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com).
|
|
|
8
8
|
[Unreleased]
|
|
9
9
|
------------
|
|
10
10
|
|
|
11
|
+
[0.7.0] - 2026-03-09
|
|
12
|
+
--------------------
|
|
13
|
+
|
|
14
|
+
### Breaking Changes
|
|
15
|
+
|
|
16
|
+
- The `pika` configuration key in `[tool.queueio]` has been renamed to `broker`.
|
|
17
|
+
- The `QUEUEIO_PIKA` environment variable has been renamed to `QUEUEIO_BROKER`.
|
|
18
|
+
- Removed `Broker.create(queue)`, in favor of `Broker.sync([queue])`.
|
|
19
|
+
- Removed `Broker.delete(queue)`, without replacement.
|
|
20
|
+
- Removed `QueueIO.create(queue)`, in favor of `QueueIO.sync([queue])`.
|
|
21
|
+
- Removed `QueueIO.delete(queue)`, without replacement.
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- Added `Broker.sync()` to back `queueio sync`.
|
|
26
|
+
- Added `QueueIO.sync()` to back `queueio sync`.
|
|
27
|
+
- `Backend` ABC with `broker()` and `journal()` methods
|
|
28
|
+
that return independent context managers with their own lifecycles.
|
|
29
|
+
|
|
30
|
+
[0.6.0] - 2026-02-16
|
|
31
|
+
--------------------
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
|
|
35
|
+
- Django integration (`queueio.django`) with `runserver` lifecycle management.
|
|
36
|
+
- [djp](https://djp.readthedocs.io/) support for automatic `INSTALLED_APPS` registration.
|
|
37
|
+
- [django-cmd](https://pypi.org/project/django-cmd/) integration for settings discovery.
|
|
38
|
+
- Documentation site.
|
|
39
|
+
|
|
11
40
|
[0.5.0] - 2026-02-08
|
|
12
41
|
--------------------
|
|
13
42
|
|
|
@@ -96,7 +125,9 @@ Thank you to Nick Anderegg for allowing me to use the queueio name for this proj
|
|
|
96
125
|
- The queuespec syntax to `queue run` to consume multiple queues with shared capacity.
|
|
97
126
|
- `queueio monitor` command to monitor activity in the queueio system.
|
|
98
127
|
|
|
99
|
-
[Unreleased]: https://github.com/ryanhiebert/queueio/compare/tag/0.
|
|
128
|
+
[Unreleased]: https://github.com/ryanhiebert/queueio/compare/tag/0.7.0...HEAD
|
|
129
|
+
[0.7.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.6.0...tag/0.7.0
|
|
130
|
+
[0.6.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.5.0...tag/0.6.0
|
|
100
131
|
[0.5.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.4.0...tag/0.5.0
|
|
101
132
|
[0.4.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.3.0...tag/0.4.0
|
|
102
133
|
[0.3.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.2.2...tag/0.3.0
|
queueio-0.7.0/CNAME
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
queueio.dev
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: queueio
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Python background queues with an async twist
|
|
5
5
|
Author-email: Ryan Hiebert <ryan@ryanhiebert.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -18,6 +18,11 @@ Requires-Dist: dill>=0.3.9
|
|
|
18
18
|
Requires-Dist: pika>=1.3.2
|
|
19
19
|
Requires-Dist: textual>=3.2.0
|
|
20
20
|
Requires-Dist: typer>=0.15.4
|
|
21
|
+
Provides-Extra: django
|
|
22
|
+
Requires-Dist: django-cmd>=3.0; extra == 'django'
|
|
23
|
+
Requires-Dist: django>=6.0; extra == 'django'
|
|
24
|
+
Provides-Extra: psycopg
|
|
25
|
+
Requires-Dist: psycopg>=3.3; extra == 'psycopg'
|
|
21
26
|
Description-Content-Type: text/markdown
|
|
22
27
|
|
|
23
28
|

|
|
@@ -68,17 +73,17 @@ Add the configuration to your `pyproject.toml`:
|
|
|
68
73
|
|
|
69
74
|
```toml
|
|
70
75
|
[tool.queueio]
|
|
71
|
-
# Configure
|
|
72
|
-
|
|
76
|
+
# Configure the broker (currently supports amqp:// URIs)
|
|
77
|
+
broker = "amqp://guest:guest@localhost:5672/"
|
|
73
78
|
# Register the modules that the worker should load to find your routines
|
|
74
79
|
register = ["basic"]
|
|
75
80
|
```
|
|
76
81
|
|
|
77
|
-
The
|
|
82
|
+
The broker configuration can be overridden with an environment variable
|
|
78
83
|
to allow a project to be deployed in multiple environments.
|
|
79
84
|
|
|
80
85
|
```sh
|
|
81
|
-
|
|
86
|
+
QUEUEIO_BROKER='amqp://guest:guest@localhost:5672/'
|
|
82
87
|
```
|
|
83
88
|
|
|
84
89
|
Sync the queues to the broker:
|
|
@@ -46,17 +46,17 @@ Add the configuration to your `pyproject.toml`:
|
|
|
46
46
|
|
|
47
47
|
```toml
|
|
48
48
|
[tool.queueio]
|
|
49
|
-
# Configure
|
|
50
|
-
|
|
49
|
+
# Configure the broker (currently supports amqp:// URIs)
|
|
50
|
+
broker = "amqp://guest:guest@localhost:5672/"
|
|
51
51
|
# Register the modules that the worker should load to find your routines
|
|
52
52
|
register = ["basic"]
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
The
|
|
55
|
+
The broker configuration can be overridden with an environment variable
|
|
56
56
|
to allow a project to be deployed in multiple environments.
|
|
57
57
|
|
|
58
58
|
```sh
|
|
59
|
-
|
|
59
|
+
QUEUEIO_BROKER='amqp://guest:guest@localhost:5672/'
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
Sync the queues to the broker:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg width="128" height="128" viewBox="100 400 520 720" xmlns="http://www.w3.org/2000/svg"><path d="M338.012,467.371c55.9,-7.825 112.888,7.725 154.194,46.346l1.633,-34.906l110.412,0.348l0.429,621.113l-119.094,0.729l0.14,-84.292c0.581,-38.062 0.669,-76.146 0.262,-114.229c-26.765,20.792 -52.321,33.083 -86.125,38.792c-59.756,9.646 -120.892,-4.896 -169.906,-40.417c-123.606,-89.792 -124.921,-294.833 -5.21,-388.29c33.813,-26.398 71.26,-39.381 113.265,-45.194Zm19.171,112.15c-155.729,31.208 -124.015,270.292 35.14,252.375c31.623,-7.125 63.613,-22.396 78.979,-52c48.587,-93.646 0.254,-206.665 -114.119,-200.375Z" fill="#0384d3"/></svg>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
(function () {
|
|
3
|
+
// Remove any cached palette so Material always re-evaluates media queries
|
|
4
|
+
try {
|
|
5
|
+
var key = __md_scope.pathname + ".__palette";
|
|
6
|
+
localStorage.removeItem(key);
|
|
7
|
+
} catch (e) {}
|
|
8
|
+
|
|
9
|
+
// Prevent Material from persisting palette choices
|
|
10
|
+
var origSet = __md_set;
|
|
11
|
+
__md_set = function (k) {
|
|
12
|
+
if (k === "__palette") return;
|
|
13
|
+
return origSet.apply(this, arguments);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function apply() {
|
|
17
|
+
var inputs = document.querySelectorAll("[data-md-color-media]");
|
|
18
|
+
for (var i = 0; i < inputs.length; i++) {
|
|
19
|
+
var media = inputs[i].getAttribute("data-md-color-media");
|
|
20
|
+
if (media && matchMedia(media).matches) {
|
|
21
|
+
document.body.setAttribute(
|
|
22
|
+
"data-md-color-scheme",
|
|
23
|
+
inputs[i].getAttribute("data-md-color-scheme"),
|
|
24
|
+
);
|
|
25
|
+
document.body.setAttribute(
|
|
26
|
+
"data-md-color-primary",
|
|
27
|
+
inputs[i].getAttribute("data-md-color-primary"),
|
|
28
|
+
);
|
|
29
|
+
document.body.setAttribute(
|
|
30
|
+
"data-md-color-accent",
|
|
31
|
+
inputs[i].getAttribute("data-md-color-accent"),
|
|
32
|
+
);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (document.body) apply();
|
|
39
|
+
document.addEventListener("DOMContentLoaded", apply);
|
|
40
|
+
|
|
41
|
+
// Listen on both light and dark queries for maximum compatibility
|
|
42
|
+
var dark = matchMedia("(prefers-color-scheme: dark)");
|
|
43
|
+
var light = matchMedia("(prefers-color-scheme: light)");
|
|
44
|
+
dark.addEventListener("change", apply);
|
|
45
|
+
light.addEventListener("change", apply);
|
|
46
|
+
})();
|
|
47
|
+
</script>
|
queueio-0.7.0/logo.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 3750 1250" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M338.012,467.371c55.9,-7.825 112.888,7.725 154.194,46.346l1.633,-34.906l110.412,0.348l0.429,621.113l-119.094,0.729l0.14,-84.292c0.581,-38.062 0.669,-76.146 0.262,-114.229c-26.765,20.792 -52.321,33.083 -86.125,38.792c-59.756,9.646 -120.892,-4.896 -169.906,-40.417c-123.606,-89.792 -124.921,-294.833 -5.21,-388.29c33.813,-26.398 71.26,-39.381 113.265,-45.194Zm19.171,112.15c-155.729,31.208 -124.015,270.292 35.14,252.375c31.623,-7.125 63.613,-22.396 78.979,-52c48.587,-93.646 0.254,-206.665 -114.119,-200.375Z" style="fill:#0384d3;"/><path d="M3268.646,460.052c134.458,-6.86 249.125,96.36 256.396,230.802c7.271,134.438 -95.604,249.417 -230.021,257.104c-135,7.708 -250.583,-95.708 -257.896,-230.729c-7.292,-135.021 96.458,-250.287 231.521,-257.177Zm2.479,120.469c-68.125,5.438 -118.958,65.021 -113.625,133.146c5.354,68.125 64.854,119.063 133,113.813c68.271,-5.271 119.312,-64.917 113.958,-133.167c-5.354,-68.271 -65.083,-119.229 -133.333,-113.792Z" style="fill:#f36c17;"/><path d="M2429.667,469.504c12.625,-1.277 25.333,-1.637 38,-1.077c58.917,2.092 114.5,27.933 154.062,71.644c52.979,58.929 65.167,125.804 61.271,202.013c-112.188,0.062 -236.083,-2.688 -347.25,0.688c33.104,99.271 122.188,114.187 212.771,76.75c14.417,-5.958 26.458,-14.292 39.333,-23.104l19.854,17.875c17.688,15.375 35.479,30.604 53.417,45.667c-45.021,45.646 -88.25,70.958 -151.542,80.979c-136.083,21.812 -272.917,-55.979 -289.583,-199.979c-16.167,-139.792 69.271,-254.075 209.667,-271.454Zm6.521,105.933c-53.875,11.125 -79.604,36.646 -97.854,87l131.833,0l93.062,-0.021c-20.5,-65.292 -58.521,-91.863 -127.042,-86.979Z" style="fill:#0384d3;"/><path d="M1393.312,469.496c13.946,-1.45 27.981,-1.844 41.985,-1.177c124.958,6.165 211.754,108.598 215.608,230.931c0.454,14.458 -0.396,28.729 -1.135,43.146c-113.421,0.146 -233.358,-1.979 -346.019,0.917c35.075,119.604 166.117,114.438 252.931,54.021l19.162,16.812l52.948,45.75c-93.298,94.875 -235.873,114.75 -348.002,38.292c-108.408,-73.938 -123.404,-237.167 -46.944,-338.815c39.348,-52.308 95.038,-80.494 159.465,-89.877Zm11.287,106.067c-52.808,10.479 -81.34,36.708 -98.487,87.188l128.262,-0.188l95.665,0.021c-20.037,-66.396 -57.262,-91.356 -125.44,-87.021Z" style="fill:#0384d3;"/><path d="M2024.729,479.54l116.958,-0.354l0.021,138.523l-0.167,311.458l-108.396,-0.312c-0.146,-11.854 -0.792,-24.396 -1.208,-36.292c-9.125,8.25 -18.333,16.771 -28.583,23.604c-67.708,45.271 -173.667,36.542 -231.077,-21.188c-20.035,-20.333 -34.302,-45.604 -41.333,-73.271c-11.271,-42.875 -8.121,-116.021 -8.058,-162.333l-0.331,-180.123l117.487,0.158c0.854,63.542 -0.438,127.694 0.25,191.319c0.729,34.896 -1.833,68.792 3.146,103.437c9.833,72.146 114.625,83.875 158.271,37.771c31.292,-33.042 23.438,-107.792 23.417,-150.979l-0.396,-181.419Z" style="fill:#0384d3;fill-rule:nonzero;"/><path d="M994.065,479.233l117.913,-0.075l0.004,138.404l0.092,311.167c-36.994,-0.792 -73.996,-1.208 -111,-1.229l-0.89,-34.312c-37.987,35.167 -67.31,49.958 -120.646,51.5c-48.3,1.417 -96.542,-13.354 -131.633,-47.625c-21.715,-21.229 -37.135,-48.062 -44.563,-77.542c-9.981,-38.771 -7.425,-111.354 -7.448,-154.083l-0.317,-186.194l116.79,0.156l0.242,182.517c0.017,35.979 -2.15,80.188 3.8,114.854c14.915,86.896 161.917,80.75 174.977,-4.979c5.375,-35.271 3.119,-75.542 3.006,-111.167l-0.327,-181.392Z" style="fill:#0384d3;fill-rule:nonzero;"/><path d="M2915.604,464.727c6.417,0.619 11.042,0.294 16.167,4.017c2.625,12.019 1.146,114.131 1.083,136.027l0.208,324.417l-125.042,0.062l-0.167,-39.146l0.188,-258.479c-20.562,0.042 -39.271,0.104 -59.875,1.292c30.375,-33.896 68,-66.692 100.604,-99.329c20.229,-20.242 46.75,-51.537 66.833,-68.86Z" style="fill:#f36c17;fill-rule:nonzero;"/><path d="M2859.583,240.567c42.292,-5.871 81.313,23.763 87.021,66.09c5.688,42.329 -24.104,81.225 -66.437,86.752c-42.125,5.496 -80.75,-24.088 -86.417,-66.175c-5.667,-42.085 23.771,-80.831 65.833,-86.667Z" style="fill:#f36c17;fill-rule:nonzero;"/></svg>
|
queueio-0.7.0/mkdocs.yml
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
site_name: queueio
|
|
2
|
+
site_description: Python background queues with an async twist
|
|
3
|
+
site_url: https://queueio.dev/
|
|
4
|
+
repo_url: https://github.com/ryanhiebert/queueio
|
|
5
|
+
repo_name: ryanhiebert/queueio
|
|
6
|
+
|
|
7
|
+
docs_dir: .
|
|
8
|
+
|
|
9
|
+
theme:
|
|
10
|
+
name: material
|
|
11
|
+
custom_dir: docs_overrides
|
|
12
|
+
logo: assets/images/logo.svg
|
|
13
|
+
favicon: assets/images/favicon.svg
|
|
14
|
+
palette:
|
|
15
|
+
- media: "(prefers-color-scheme: light)"
|
|
16
|
+
scheme: default
|
|
17
|
+
primary: white
|
|
18
|
+
- media: "(prefers-color-scheme: dark)"
|
|
19
|
+
scheme: slate
|
|
20
|
+
primary: black
|
|
21
|
+
|
|
22
|
+
markdown_extensions:
|
|
23
|
+
- admonition
|
|
24
|
+
- pymdownx.highlight
|
|
25
|
+
- pymdownx.superfences
|
|
26
|
+
|
|
27
|
+
extra_css:
|
|
28
|
+
- assets/stylesheets/extra.css
|
|
29
|
+
|
|
30
|
+
plugins:
|
|
31
|
+
- search
|
|
32
|
+
- same-dir
|
|
33
|
+
|
|
34
|
+
nav:
|
|
35
|
+
- Home: README.md
|
|
36
|
+
- Philosophy: philosophy.md
|
|
37
|
+
- Invocation: queueio/invocation.md
|
|
38
|
+
- QueueSpec: queueio/queuespec.md
|
|
39
|
+
- QueueVar: queueio/queuevar.md
|
|
40
|
+
- Django: queueio/django/README.md
|
|
41
|
+
- Changelog: CHANGELOG.md
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "queueio"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.7.0"
|
|
4
4
|
description = "Python background queues with an async twist"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
7
|
-
homepage = "https://
|
|
7
|
+
homepage = "https://queueio.dev/"
|
|
8
8
|
repository = "https://github.com/ryanhiebert/queueio"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name = "Ryan Hiebert", email = "ryan@ryanhiebert.com" },
|
|
@@ -27,18 +27,32 @@ dependencies = [
|
|
|
27
27
|
"typer>=0.15.4",
|
|
28
28
|
]
|
|
29
29
|
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
django = ["django>=6.0", "django-cmd>=3.0"]
|
|
32
|
+
psycopg = ["psycopg>=3.3"]
|
|
33
|
+
|
|
34
|
+
[project.entry-points.djp]
|
|
35
|
+
queueio = "queueio.django.djp"
|
|
36
|
+
|
|
30
37
|
[project.scripts]
|
|
31
38
|
queueio = "queueio.__main__:app"
|
|
32
39
|
|
|
33
40
|
[dependency-groups]
|
|
34
41
|
dev = [
|
|
35
42
|
"basedpyright>=1.32.1",
|
|
43
|
+
"django-extensions",
|
|
44
|
+
"djp",
|
|
45
|
+
"queueio[django]",
|
|
46
|
+
"queueio[psycopg]",
|
|
47
|
+
"mkdocs-material>=9.7.1",
|
|
48
|
+
"mkdocs-same-dir>=0.1.3",
|
|
36
49
|
"pytest>=8.4.1",
|
|
37
50
|
"pytest-cov>=6.0.0",
|
|
38
51
|
"pytest-timeout>=2.4.0",
|
|
39
52
|
"ruff>=0.9.6",
|
|
40
53
|
"textual-dev>=1.7.0",
|
|
41
54
|
"types-pika>=1.2.0b1",
|
|
55
|
+
"psycopg>=3.3.3",
|
|
42
56
|
]
|
|
43
57
|
|
|
44
58
|
[tool.ruff.lint]
|
|
@@ -54,10 +68,11 @@ register = [
|
|
|
54
68
|
"queueio.samples.priority",
|
|
55
69
|
"queueio.samples.queuevar",
|
|
56
70
|
]
|
|
57
|
-
|
|
71
|
+
broker = "amqp://localhost:5672"
|
|
58
72
|
|
|
59
73
|
[tool.pytest.ini_options]
|
|
60
74
|
timeout = 30
|
|
75
|
+
norecursedirs = ["site"]
|
|
61
76
|
|
|
62
77
|
[tool.coverage.run]
|
|
63
78
|
branch = true
|
|
@@ -2,7 +2,7 @@ from contextlib import contextmanager
|
|
|
2
2
|
|
|
3
3
|
from .gather import gather as gather
|
|
4
4
|
from .pause import pause as pause
|
|
5
|
-
from .queueio import QueueIO as
|
|
5
|
+
from .queueio import QueueIO as QueueIO
|
|
6
6
|
from .queueio import priority as priority
|
|
7
7
|
from .queuevar import QueueVar as QueueVar
|
|
8
8
|
from .registry import routine as routine
|
|
@@ -10,5 +10,5 @@ from .registry import routine as routine
|
|
|
10
10
|
|
|
11
11
|
@contextmanager
|
|
12
12
|
def activate():
|
|
13
|
-
with
|
|
13
|
+
with QueueIO.default() as queueio, queueio.activate():
|
|
14
14
|
yield
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from contextlib import suppress
|
|
2
1
|
from typing import Annotated
|
|
3
2
|
|
|
4
3
|
import typer
|
|
@@ -32,8 +31,7 @@ app.add_typer(
|
|
|
32
31
|
@routine_app.command("list")
|
|
33
32
|
def routine_list():
|
|
34
33
|
"""Show all registered routines."""
|
|
35
|
-
|
|
36
|
-
try:
|
|
34
|
+
with QueueIO.default() as queueio:
|
|
37
35
|
routines = queueio.routines()
|
|
38
36
|
|
|
39
37
|
if not routines:
|
|
@@ -53,8 +51,6 @@ def routine_list():
|
|
|
53
51
|
print(f"{'-' * name_width}-+-{'-' * path_width}")
|
|
54
52
|
for routine, path in zip(routines, function_paths, strict=False):
|
|
55
53
|
print(f"{routine.name:<{name_width}} | {path:<{path_width}}")
|
|
56
|
-
finally:
|
|
57
|
-
queueio.shutdown()
|
|
58
54
|
|
|
59
55
|
|
|
60
56
|
@app.command(rich_help_panel="Commands")
|
|
@@ -64,17 +60,16 @@ def monitor(raw: bool = False):
|
|
|
64
60
|
Show a live view of queueio activity. Use --raw for detailed event output.
|
|
65
61
|
"""
|
|
66
62
|
if raw:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
finally:
|
|
75
|
-
queueio.shutdown()
|
|
63
|
+
with QueueIO.default() as queueio:
|
|
64
|
+
events = queueio.subscribe({object})
|
|
65
|
+
try:
|
|
66
|
+
while True:
|
|
67
|
+
print(events.get())
|
|
68
|
+
except KeyboardInterrupt:
|
|
69
|
+
print("Shutting down gracefully.")
|
|
76
70
|
else:
|
|
77
|
-
|
|
71
|
+
with QueueIO.default() as queueio:
|
|
72
|
+
Monitor(queueio).run()
|
|
78
73
|
|
|
79
74
|
|
|
80
75
|
@app.command(rich_help_panel="Commands")
|
|
@@ -94,8 +89,8 @@ def run(
|
|
|
94
89
|
The worker will process invocations from the specified queue,
|
|
95
90
|
as many at a time as specified by the concurrency.
|
|
96
91
|
"""
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
with QueueIO.default() as queueio:
|
|
93
|
+
Worker(queueio, queuespec)()
|
|
99
94
|
|
|
100
95
|
|
|
101
96
|
@app.command(rich_help_panel="Commands")
|
|
@@ -103,14 +98,13 @@ def sync(
|
|
|
103
98
|
recreate: Annotated[
|
|
104
99
|
bool,
|
|
105
100
|
typer.Option(
|
|
106
|
-
help="
|
|
107
|
-
"WARNING: This will lose
|
|
101
|
+
help="Recreate all resources for the broker and journal. "
|
|
102
|
+
"WARNING: This will lose pending messages.",
|
|
108
103
|
),
|
|
109
104
|
] = False,
|
|
110
105
|
):
|
|
111
|
-
"""Sync
|
|
112
|
-
|
|
113
|
-
try:
|
|
106
|
+
"""Sync resources for the broker and journal."""
|
|
107
|
+
with QueueIO.default() as queueio:
|
|
114
108
|
routines = queueio.routines()
|
|
115
109
|
|
|
116
110
|
if not routines:
|
|
@@ -119,33 +113,21 @@ def sync(
|
|
|
119
113
|
|
|
120
114
|
queues = sorted({routine.queue for routine in routines})
|
|
121
115
|
|
|
122
|
-
print(f"
|
|
123
|
-
if recreate:
|
|
124
|
-
for queue in queues:
|
|
125
|
-
print(f" Recreating queue: {queue}")
|
|
126
|
-
with suppress(Exception):
|
|
127
|
-
queueio.delete(queue=queue)
|
|
128
|
-
|
|
129
|
-
failed = []
|
|
116
|
+
print(f"Discovered queues for {len(routines)} routine(s):")
|
|
130
117
|
for queue in queues:
|
|
131
|
-
print(f"
|
|
132
|
-
try:
|
|
133
|
-
queueio.create(queue=queue)
|
|
134
|
-
except Exception:
|
|
135
|
-
failed.append(queue)
|
|
118
|
+
print(f" {queue}")
|
|
136
119
|
|
|
137
|
-
|
|
120
|
+
try:
|
|
121
|
+
queueio.sync(queues, recreate=recreate)
|
|
122
|
+
except Exception:
|
|
138
123
|
print(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
f"WARNING: This will lose any pending messages in those queues."
|
|
124
|
+
"\nError: Failed to sync resources.\n"
|
|
125
|
+
"Re-run with --recreate to recreate them.\n"
|
|
126
|
+
"WARNING: This will lose pending messages."
|
|
143
127
|
)
|
|
144
|
-
raise typer.Exit(1)
|
|
128
|
+
raise typer.Exit(1) from None
|
|
145
129
|
|
|
146
130
|
print(f"Successfully synced {len(queues)} queue(s)")
|
|
147
|
-
finally:
|
|
148
|
-
queueio.shutdown()
|
|
149
131
|
|
|
150
132
|
|
|
151
133
|
@queue_app.command("purge")
|
|
@@ -164,8 +146,7 @@ def queue_purge(
|
|
|
164
146
|
This will remove all pending messages from the given queues.
|
|
165
147
|
Use with caution as this operation cannot be undone.
|
|
166
148
|
"""
|
|
167
|
-
|
|
168
|
-
try:
|
|
149
|
+
with QueueIO.default() as queueio:
|
|
169
150
|
queue_list = [q.strip() for q in queues.split(",") if q.strip()]
|
|
170
151
|
if not queue_list:
|
|
171
152
|
print("Error: No valid queue names provided")
|
|
@@ -176,8 +157,6 @@ def queue_purge(
|
|
|
176
157
|
queueio.purge(queue=queue)
|
|
177
158
|
|
|
178
159
|
print(f"Successfully purged {len(queue_list)} queue(s)")
|
|
179
|
-
finally:
|
|
180
|
-
queueio.shutdown()
|
|
181
160
|
|
|
182
161
|
|
|
183
162
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
from contextlib import AbstractContextManager
|
|
4
|
+
|
|
5
|
+
from .broker import Broker
|
|
6
|
+
from .journal import Journal
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Backend(ABC):
|
|
10
|
+
"""A backend provides context-managed access to a broker and journal."""
|
|
11
|
+
|
|
12
|
+
@abstractmethod
|
|
13
|
+
def broker(self) -> AbstractContextManager[Broker]:
|
|
14
|
+
raise NotImplementedError("Subclasses must implement this method.")
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def journal(self) -> AbstractContextManager[Journal]:
|
|
18
|
+
raise NotImplementedError("Subclasses must implement this method.")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from abc import ABC
|
|
2
2
|
from abc import abstractmethod
|
|
3
|
+
from collections.abc import Iterable
|
|
3
4
|
|
|
4
5
|
from .queuespec import QueueSpec
|
|
5
6
|
from .receiver import Receiver
|
|
@@ -13,10 +14,13 @@ class Broker(ABC):
|
|
|
13
14
|
at-least-once delivery.
|
|
14
15
|
"""
|
|
15
16
|
|
|
16
|
-
@classmethod
|
|
17
17
|
@abstractmethod
|
|
18
|
-
def
|
|
19
|
-
"""
|
|
18
|
+
def sync(self, queues: Iterable[str], *, recreate: bool = False):
|
|
19
|
+
"""Ensure the given queues are ready to use.
|
|
20
|
+
|
|
21
|
+
If recreate is True, destroy and recreate all resources,
|
|
22
|
+
losing any pending messages.
|
|
23
|
+
"""
|
|
20
24
|
raise NotImplementedError("Subclasses must implement this method.")
|
|
21
25
|
|
|
22
26
|
@abstractmethod
|
|
@@ -24,16 +28,6 @@ class Broker(ABC):
|
|
|
24
28
|
"""Enqueue a message."""
|
|
25
29
|
raise NotImplementedError("Subclasses must implement this method.")
|
|
26
30
|
|
|
27
|
-
@abstractmethod
|
|
28
|
-
def create(self, *, queue: str):
|
|
29
|
-
"""Create a queue if it doesn't exist."""
|
|
30
|
-
raise NotImplementedError("Subclasses must implement this method.")
|
|
31
|
-
|
|
32
|
-
@abstractmethod
|
|
33
|
-
def delete(self, *, queue: str):
|
|
34
|
-
"""Delete a queue."""
|
|
35
|
-
raise NotImplementedError("Subclasses must implement this method.")
|
|
36
|
-
|
|
37
31
|
@abstractmethod
|
|
38
32
|
def purge(self, *, queue: str):
|
|
39
33
|
"""Purge all messages from the queue."""
|
|
@@ -60,7 +60,7 @@ class BaseBrokerTest:
|
|
|
60
60
|
@pytest.mark.timeout(2)
|
|
61
61
|
def test_prefetch_limits_message_consumption(self, broker):
|
|
62
62
|
"""Verify that prefetch parameter limits message consumption."""
|
|
63
|
-
broker.
|
|
63
|
+
broker.sync(["test-queue"])
|
|
64
64
|
broker.purge(queue="test-queue")
|
|
65
65
|
|
|
66
66
|
# Enqueue more messages than prefetch limit
|
|
@@ -93,7 +93,7 @@ class BaseBrokerTest:
|
|
|
93
93
|
@pytest.mark.timeout(2)
|
|
94
94
|
def test_suspend_resume_affects_prefetch_capacity(self, broker):
|
|
95
95
|
"""Verify suspending messages frees capacity and resuming reduces it."""
|
|
96
|
-
broker.
|
|
96
|
+
broker.sync(["test-queue"])
|
|
97
97
|
broker.purge(queue="test-queue")
|
|
98
98
|
|
|
99
99
|
# Enqueue messages
|
|
@@ -131,7 +131,7 @@ class BaseBrokerTest:
|
|
|
131
131
|
@pytest.mark.timeout(2)
|
|
132
132
|
def test_complete_message_frees_prefetch_capacity(self, broker):
|
|
133
133
|
"""Verify completing messages frees up capacity."""
|
|
134
|
-
broker.
|
|
134
|
+
broker.sync(["test-queue"])
|
|
135
135
|
broker.purge(queue="test-queue")
|
|
136
136
|
|
|
137
137
|
# Enqueue more messages than prefetch limit
|
|
@@ -171,7 +171,7 @@ class BaseBrokerTest:
|
|
|
171
171
|
@pytest.mark.timeout(2)
|
|
172
172
|
def test_multiple_receivers_independent_prefetch_limits(self, broker):
|
|
173
173
|
"""Verify multiple receivers operate with independent prefetch limits."""
|
|
174
|
-
broker.
|
|
174
|
+
broker.sync(["test-queue"])
|
|
175
175
|
broker.purge(queue="test-queue")
|
|
176
176
|
|
|
177
177
|
# Enqueue enough messages for both receivers
|
|
@@ -223,8 +223,7 @@ class BaseBrokerTest:
|
|
|
223
223
|
@skip_if_unsupported("supports_multiple_queues")
|
|
224
224
|
def test_receive_supports_multiple_queues(self, broker):
|
|
225
225
|
"""Verify broker can receive from multiple queues."""
|
|
226
|
-
broker.
|
|
227
|
-
broker.create(queue="queue2")
|
|
226
|
+
broker.sync(["queue1", "queue2"])
|
|
228
227
|
broker.purge(queue="queue1")
|
|
229
228
|
broker.purge(queue="queue2")
|
|
230
229
|
|
|
@@ -260,10 +259,7 @@ class BaseBrokerTest:
|
|
|
260
259
|
def test_multiple_queues_with_mixed_empty_and_filled(self, broker):
|
|
261
260
|
"""Verify broker handles multiple queues with some empty, some with messages."""
|
|
262
261
|
|
|
263
|
-
broker.
|
|
264
|
-
broker.create(queue="filled")
|
|
265
|
-
broker.create(queue="empty2")
|
|
266
|
-
broker.create(queue="also_filled")
|
|
262
|
+
broker.sync(["empty1", "filled", "empty2", "also_filled"])
|
|
267
263
|
broker.purge(queue="empty1")
|
|
268
264
|
broker.purge(queue="filled")
|
|
269
265
|
broker.purge(queue="empty2")
|
|
@@ -308,8 +304,7 @@ class BaseBrokerTest:
|
|
|
308
304
|
probability in the round-robin selection.
|
|
309
305
|
"""
|
|
310
306
|
|
|
311
|
-
broker.
|
|
312
|
-
broker.create(queue="normal_queue")
|
|
307
|
+
broker.sync(["priority_queue", "normal_queue"])
|
|
313
308
|
broker.purge(queue="priority_queue")
|
|
314
309
|
broker.purge(queue="normal_queue")
|
|
315
310
|
|
|
@@ -410,9 +405,7 @@ class BaseBrokerTest:
|
|
|
410
405
|
the message, not just cycle by a fixed amount.
|
|
411
406
|
"""
|
|
412
407
|
|
|
413
|
-
broker.
|
|
414
|
-
broker.create(queue="queue1")
|
|
415
|
-
broker.create(queue="queue2")
|
|
408
|
+
broker.sync(["empty", "queue1", "queue2"])
|
|
416
409
|
broker.purge(queue="empty")
|
|
417
410
|
broker.purge(queue="queue1")
|
|
418
411
|
broker.purge(queue="queue2")
|
|
@@ -473,7 +466,7 @@ class BaseBrokerTest:
|
|
|
473
466
|
@pytest.mark.timeout(2)
|
|
474
467
|
def test_higher_priority_consumed_first(self, broker):
|
|
475
468
|
"""Verify that higher priority messages are consumed before lower ones."""
|
|
476
|
-
broker.
|
|
469
|
+
broker.sync(["test-queue"])
|
|
477
470
|
broker.purge(queue="test-queue")
|
|
478
471
|
|
|
479
472
|
# Enqueue low priority first, then high priority
|