queueio 0.5.0__tar.gz → 0.6.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.6.0/.github/workflows/docs.yml +31 -0
- {queueio-0.5.0 → queueio-0.6.0}/.gitignore +3 -0
- {queueio-0.5.0 → queueio-0.6.0}/CHANGELOG.md +12 -1
- queueio-0.6.0/CNAME +1 -0
- {queueio-0.5.0 → queueio-0.6.0}/PKG-INFO +4 -1
- queueio-0.6.0/docs_overrides/assets/images/favicon.svg +1 -0
- queueio-0.6.0/docs_overrides/assets/stylesheets/extra.css +7 -0
- queueio-0.6.0/docs_overrides/partials/javascripts/palette.html +47 -0
- queueio-0.6.0/logo.svg +1 -0
- queueio-0.6.0/mkdocs.yml +41 -0
- queueio-0.5.0/principles.md → queueio-0.6.0/philosophy.md +1 -1
- {queueio-0.5.0 → queueio-0.6.0}/pyproject.toml +14 -2
- queueio-0.6.0/queueio/django/__init__.py +17 -0
- queueio-0.6.0/queueio/django/apps.py +6 -0
- queueio-0.6.0/queueio/django/djp.py +6 -0
- queueio-0.6.0/queueio/django/management/commands/runserver.py +41 -0
- queueio-0.6.0/queueio/django/management/commands/runserver_plus.py +9 -0
- queueio-0.6.0/queueio/django/runserver_test.py +63 -0
- queueio-0.6.0/queueio/django.md +82 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/freethreading_test.py +1 -1
- queueio-0.6.0/queueio/pika/__init__.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queueio.py +4 -0
- queueio-0.6.0/queueio/samples/__init__.py +0 -0
- queueio-0.6.0/queueio/samples/django/__init__.py +0 -0
- queueio-0.6.0/queueio/samples/django/apps.py +6 -0
- queueio-0.6.0/queueio/samples/django/job.py +29 -0
- queueio-0.6.0/queueio/samples/django/models.py +3 -0
- queueio-0.6.0/queueio/samples/django/settings.py +19 -0
- queueio-0.6.0/queueio/samples/django/settings_djp.py +24 -0
- queueio-0.6.0/queueio/samples/django/urls.py +7 -0
- queueio-0.6.0/queueio/samples/django_test.py +98 -0
- queueio-0.6.0/queueio/stub/__init__.py +0 -0
- queueio-0.5.0/.claude/settings.local.json +0 -9
- {queueio-0.5.0 → queueio-0.6.0}/.devcontainer/devcontainer.json +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/.devcontainer/docker-compose.yml +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/.github/workflows/build.yml +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/.github/workflows/publish.yml +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/LICENSE +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/README.md +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/dill.pyi +0 -0
- {queueio-0.5.0 → queueio-0.6.0/docs_overrides/assets/images}/logo.svg +0 -0
- /queueio-0.5.0/queueio/pika/__init__.py → /queueio-0.6.0/docs_overrides/partials/footer.html +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/pyrightconfig.json +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/__init__.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/__main__.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/broker.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/broker_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/conftest.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/consumer.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/continuation.py +0 -0
- {queueio-0.5.0/queueio/samples → queueio-0.6.0/queueio/django/management}/__init__.py +0 -0
- {queueio-0.5.0/queueio/stub → queueio-0.6.0/queueio/django/management/commands}/__init__.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/event.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/gather.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/id.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0/queueio}/invocation.md +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/invocation.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/journal.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/journal_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/message.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/monitor.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/pause.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/pika/broker.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/pika/broker_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/pika/journal.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/pika/journal_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/pika/receiver.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/pika/threadsafe.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queue.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queue_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queueio_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queuespec.md +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queuespec.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queuespec_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queuevar.md +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/queuevar.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/receiver.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/registry.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/result.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/routine.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/samples/basic.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/samples/basic_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/samples/expanded.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/samples/expanded_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/samples/priority.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/samples/priority_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/samples/queuevar.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/samples/queuevar_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/select.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/stream.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/stub/broker.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/stub/broker_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/stub/journal.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/stub/journal_test.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/stub/receiver.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/suspension.py +0 -0
- {queueio-0.5.0 → queueio-0.6.0}/queueio/thread.py +0 -0
- {queueio-0.5.0 → queueio-0.6.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,16 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com).
|
|
|
8
8
|
[Unreleased]
|
|
9
9
|
------------
|
|
10
10
|
|
|
11
|
+
[0.6.0] - 2026-02-16
|
|
12
|
+
--------------------
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- Django integration (`queueio.django`) with `runserver` lifecycle management.
|
|
17
|
+
- [djp](https://djp.readthedocs.io/) support for automatic `INSTALLED_APPS` registration.
|
|
18
|
+
- [django-cmd](https://pypi.org/project/django-cmd/) integration for settings discovery.
|
|
19
|
+
- Documentation site.
|
|
20
|
+
|
|
11
21
|
[0.5.0] - 2026-02-08
|
|
12
22
|
--------------------
|
|
13
23
|
|
|
@@ -96,7 +106,8 @@ Thank you to Nick Anderegg for allowing me to use the queueio name for this proj
|
|
|
96
106
|
- The queuespec syntax to `queue run` to consume multiple queues with shared capacity.
|
|
97
107
|
- `queueio monitor` command to monitor activity in the queueio system.
|
|
98
108
|
|
|
99
|
-
[Unreleased]: https://github.com/ryanhiebert/queueio/compare/tag/0.
|
|
109
|
+
[Unreleased]: https://github.com/ryanhiebert/queueio/compare/tag/0.6.0...HEAD
|
|
110
|
+
[0.6.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.5.0...tag/0.6.0
|
|
100
111
|
[0.5.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.4.0...tag/0.5.0
|
|
101
112
|
[0.4.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.3.0...tag/0.4.0
|
|
102
113
|
[0.3.0]: https://github.com/ryanhiebert/queueio/compare/tag/0.2.2...tag/0.3.0
|
queueio-0.6.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.6.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,9 @@ 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'
|
|
21
24
|
Description-Content-Type: text/markdown
|
|
22
25
|
|
|
23
26
|

|
|
@@ -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.6.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.6.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.md
|
|
41
|
+
- Changelog: CHANGELOG.md
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "queueio"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.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,12 +27,23 @@ 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
|
+
|
|
33
|
+
[project.entry-points.djp]
|
|
34
|
+
queueio = "queueio.django.djp"
|
|
35
|
+
|
|
30
36
|
[project.scripts]
|
|
31
37
|
queueio = "queueio.__main__:app"
|
|
32
38
|
|
|
33
39
|
[dependency-groups]
|
|
34
40
|
dev = [
|
|
35
41
|
"basedpyright>=1.32.1",
|
|
42
|
+
"django-extensions",
|
|
43
|
+
"djp",
|
|
44
|
+
"queueio[django]",
|
|
45
|
+
"mkdocs-material>=9.7.1",
|
|
46
|
+
"mkdocs-same-dir>=0.1.3",
|
|
36
47
|
"pytest>=8.4.1",
|
|
37
48
|
"pytest-cov>=6.0.0",
|
|
38
49
|
"pytest-timeout>=2.4.0",
|
|
@@ -58,6 +69,7 @@ pika = "amqp://localhost:5672"
|
|
|
58
69
|
|
|
59
70
|
[tool.pytest.ini_options]
|
|
60
71
|
timeout = 30
|
|
72
|
+
norecursedirs = ["site"]
|
|
61
73
|
|
|
62
74
|
[tool.coverage.run]
|
|
63
75
|
branch = true
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import tomllib
|
|
2
|
+
from importlib import import_module
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def _find_pyproject() -> Path | None:
|
|
7
|
+
for path in [cwd := Path.cwd(), *cwd.parents]:
|
|
8
|
+
candidate = path / "pyproject.toml"
|
|
9
|
+
if candidate.is_file():
|
|
10
|
+
return candidate
|
|
11
|
+
return None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _queueio_django_config() -> dict:
|
|
15
|
+
if pyproject := _find_pyproject():
|
|
16
|
+
with pyproject.open("rb") as f:
|
|
17
|
+
config = tomllib.load(f)
|
|
18
|
+
return config.get("tool", {}).get("queueio", {}).get("django", {})
|
|
19
|
+
return {}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _resolve_base_command(config: dict):
|
|
23
|
+
runserver_app = config.get("runserver", "django.core")
|
|
24
|
+
module = import_module(f"{runserver_app}.management.commands.runserver")
|
|
25
|
+
return module.Command
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _build_command():
|
|
29
|
+
base = _resolve_base_command(_queueio_django_config())
|
|
30
|
+
|
|
31
|
+
class Command(base):
|
|
32
|
+
def inner_run(self, *args, **options):
|
|
33
|
+
from queueio import activate
|
|
34
|
+
|
|
35
|
+
with activate():
|
|
36
|
+
super().inner_run(*args, **options)
|
|
37
|
+
|
|
38
|
+
return Command
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
Command = _build_command()
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sysconfig
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from .management.commands.runserver import _build_command
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.mark.parametrize(
|
|
10
|
+
("runserver_app", "expected_module"),
|
|
11
|
+
[
|
|
12
|
+
pytest.param(
|
|
13
|
+
None,
|
|
14
|
+
"django.core.management.commands.runserver",
|
|
15
|
+
id="default",
|
|
16
|
+
),
|
|
17
|
+
pytest.param(
|
|
18
|
+
"django.contrib.staticfiles",
|
|
19
|
+
"django.contrib.staticfiles.management.commands.runserver",
|
|
20
|
+
id="staticfiles",
|
|
21
|
+
),
|
|
22
|
+
pytest.param(
|
|
23
|
+
"daphne",
|
|
24
|
+
"daphne.management.commands.runserver",
|
|
25
|
+
id="daphne",
|
|
26
|
+
marks=pytest.mark.skipif(
|
|
27
|
+
bool(sysconfig.get_config_var("Py_GIL_DISABLED")),
|
|
28
|
+
reason="zope.interface re-enables the GIL on free-threaded builds",
|
|
29
|
+
),
|
|
30
|
+
),
|
|
31
|
+
],
|
|
32
|
+
)
|
|
33
|
+
def test_runserver(tmp_path, runserver_app, expected_module):
|
|
34
|
+
config = "[tool.queueio]\n"
|
|
35
|
+
if runserver_app is not None:
|
|
36
|
+
config = f'[tool.queueio.django]\nrunserver = "{runserver_app}"\n'
|
|
37
|
+
|
|
38
|
+
pyproject = tmp_path / "pyproject.toml"
|
|
39
|
+
pyproject.write_text(config)
|
|
40
|
+
|
|
41
|
+
original_cwd = os.getcwd()
|
|
42
|
+
os.chdir(tmp_path)
|
|
43
|
+
try:
|
|
44
|
+
Cmd = _build_command()
|
|
45
|
+
from importlib import import_module
|
|
46
|
+
|
|
47
|
+
expected = import_module(expected_module)
|
|
48
|
+
assert issubclass(Cmd, expected.Command)
|
|
49
|
+
finally:
|
|
50
|
+
os.chdir(original_cwd)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_runserver_plus():
|
|
54
|
+
from django.conf import settings
|
|
55
|
+
|
|
56
|
+
if not settings.configured:
|
|
57
|
+
settings.configure()
|
|
58
|
+
|
|
59
|
+
from django_extensions.management.commands import runserver_plus as expected
|
|
60
|
+
|
|
61
|
+
from .management.commands.runserver_plus import Command
|
|
62
|
+
|
|
63
|
+
assert issubclass(Command, expected.Command)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Django Integration
|
|
2
|
+
|
|
3
|
+
## Settings
|
|
4
|
+
|
|
5
|
+
When running `queueio run`,
|
|
6
|
+
Django isn't started automatically.
|
|
7
|
+
If your routines use the Django ORM or other Django features,
|
|
8
|
+
queueio needs to call `django.setup()` before importing routine modules.
|
|
9
|
+
|
|
10
|
+
The `queueio[django]` extra includes
|
|
11
|
+
[django-cmd](https://pypi.org/project/django-cmd/),
|
|
12
|
+
which reads `tool.django.settings` from `pyproject.toml`
|
|
13
|
+
and sets `DJANGO_SETTINGS_MODULE` automatically:
|
|
14
|
+
|
|
15
|
+
```toml
|
|
16
|
+
[tool.django]
|
|
17
|
+
settings = "myproject.settings"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
If `DJANGO_SETTINGS_MODULE` is set in the environment
|
|
21
|
+
(whether by `django-cmd` or directly),
|
|
22
|
+
queueio calls `django.setup()` before registering routines.
|
|
23
|
+
|
|
24
|
+
## Runserver
|
|
25
|
+
|
|
26
|
+
!!! warning "Development server only"
|
|
27
|
+
|
|
28
|
+
The runserver integration only affects the `runserver` management command.
|
|
29
|
+
Production servers like gunicorn need their own lifecycle hooks.
|
|
30
|
+
|
|
31
|
+
queueio ships a Django app
|
|
32
|
+
that overrides the `runserver` management command
|
|
33
|
+
to wrap the server in `with activate():`,
|
|
34
|
+
giving you automatic lifecycle management
|
|
35
|
+
of the broker connection and background threads
|
|
36
|
+
during development.
|
|
37
|
+
|
|
38
|
+
### Setup
|
|
39
|
+
|
|
40
|
+
If you use [djp](https://djp.readthedocs.io/),
|
|
41
|
+
`queueio.django` is added to `INSTALLED_APPS` automatically.
|
|
42
|
+
|
|
43
|
+
Otherwise, add it manually:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
INSTALLED_APPS = [
|
|
47
|
+
"queueio.django",
|
|
48
|
+
# ...
|
|
49
|
+
]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This must come **after** whichever app provides the `runserver` command
|
|
53
|
+
you want to chain with
|
|
54
|
+
(e.g. `daphne`, `django.contrib.staticfiles`),
|
|
55
|
+
because Django resolves management commands by last-app-wins order.
|
|
56
|
+
|
|
57
|
+
### Custom provider
|
|
58
|
+
|
|
59
|
+
If you use a custom `runserver` provider
|
|
60
|
+
like `django.contrib.staticfiles` or `daphne`,
|
|
61
|
+
tell queueio which app to chain with:
|
|
62
|
+
|
|
63
|
+
```toml
|
|
64
|
+
[tool.queueio.django]
|
|
65
|
+
runserver = "daphne"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The value is the app label (or dotted module path).
|
|
69
|
+
queueio resolves it to `{value}.management.commands.runserver`
|
|
70
|
+
and subclasses that command.
|
|
71
|
+
If omitted, it defaults to Django's built-in `runserver`.
|
|
72
|
+
|
|
73
|
+
### `runserver_plus`
|
|
74
|
+
|
|
75
|
+
There is also a built-in `runserver_plus` command
|
|
76
|
+
that wraps [django-extensions](https://django-extensions.readthedocs.io/)' `runserver_plus`
|
|
77
|
+
with `activate()`.
|
|
78
|
+
This is available automatically
|
|
79
|
+
when `django-extensions` is installed
|
|
80
|
+
and `queueio.django` is in `INSTALLED_APPS`.
|
|
81
|
+
|
|
82
|
+
A complete working example is in `queueio/samples/django/`.
|
|
@@ -22,7 +22,7 @@ def test_all_queueio_modules_preserve_freethreading():
|
|
|
22
22
|
for _, modname, _ in pkgutil.walk_packages(
|
|
23
23
|
queueio.__path__, f"{queueio.__name__}."
|
|
24
24
|
):
|
|
25
|
-
if not modname.endswith("_test"):
|
|
25
|
+
if not modname.endswith("_test") and ".django" not in modname:
|
|
26
26
|
importlib.import_module(modname)
|
|
27
27
|
|
|
28
28
|
assert sys._is_gil_enabled() is False
|
|
File without changes
|
|
@@ -11,6 +11,7 @@ from typing import Self
|
|
|
11
11
|
|
|
12
12
|
from .broker import Broker
|
|
13
13
|
from .consumer import Consumer
|
|
14
|
+
from .django import setup as _django_setup
|
|
14
15
|
from .invocation import Invocation
|
|
15
16
|
from .journal import Journal
|
|
16
17
|
from .message import Message
|
|
@@ -105,6 +106,9 @@ class QueueIO:
|
|
|
105
106
|
|
|
106
107
|
def __register_routines(self):
|
|
107
108
|
"""Load routine modules from pyproject.toml."""
|
|
109
|
+
for hook in [_django_setup]:
|
|
110
|
+
hook()
|
|
111
|
+
|
|
108
112
|
config = self.__config()
|
|
109
113
|
modules = config.get("register", [])
|
|
110
114
|
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from django.http import JsonResponse
|
|
3
|
+
|
|
4
|
+
from queueio import routine
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Job(models.Model):
|
|
8
|
+
status = models.CharField(max_length=20, default="pending")
|
|
9
|
+
objects = models.Manager() # pyright: ignore[reportAssignmentType]
|
|
10
|
+
|
|
11
|
+
class Meta:
|
|
12
|
+
app_label = "queueio_sample"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@routine(name="update_job", queue="django")
|
|
16
|
+
def update_job(job_id: int):
|
|
17
|
+
job = Job.objects.get(pk=job_id)
|
|
18
|
+
job.status = "complete"
|
|
19
|
+
job.save()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def index(request):
|
|
23
|
+
if request.method == "POST":
|
|
24
|
+
job = Job.objects.create()
|
|
25
|
+
update_job(job.pk).submit()
|
|
26
|
+
return JsonResponse({"job_id": job.pk, "status": job.status})
|
|
27
|
+
|
|
28
|
+
job = Job.objects.latest("pk")
|
|
29
|
+
return JsonResponse({"job_id": job.pk, "status": job.status})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
DEBUG = True
|
|
2
|
+
|
|
3
|
+
SECRET_KEY = "insecure-example-key"
|
|
4
|
+
|
|
5
|
+
INSTALLED_APPS = [
|
|
6
|
+
"queueio.django",
|
|
7
|
+
"queueio.samples.django",
|
|
8
|
+
]
|
|
9
|
+
|
|
10
|
+
DATABASES = {
|
|
11
|
+
"default": {
|
|
12
|
+
"ENGINE": "django.db.backends.sqlite3",
|
|
13
|
+
"NAME": "db.sqlite3",
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
18
|
+
|
|
19
|
+
ROOT_URLCONF = "queueio.samples.django.urls"
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import djp
|
|
2
|
+
|
|
3
|
+
DEBUG = True
|
|
4
|
+
|
|
5
|
+
SECRET_KEY = "insecure-example-key"
|
|
6
|
+
|
|
7
|
+
INSTALLED_APPS = [
|
|
8
|
+
"queueio.samples.django",
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
DATABASES = {
|
|
12
|
+
"default": {
|
|
13
|
+
"ENGINE": "django.db.backends.sqlite3",
|
|
14
|
+
"NAME": "db.sqlite3",
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
19
|
+
|
|
20
|
+
ROOT_URLCONF = "queueio.samples.django.urls"
|
|
21
|
+
|
|
22
|
+
MIDDLEWARE = []
|
|
23
|
+
|
|
24
|
+
djp.settings(globals())
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import socket
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
import time
|
|
7
|
+
from urllib.request import Request
|
|
8
|
+
from urllib.request import urlopen
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _wait_for_port(port, timeout=10):
|
|
14
|
+
deadline = time.monotonic() + timeout
|
|
15
|
+
while time.monotonic() < deadline:
|
|
16
|
+
try:
|
|
17
|
+
with socket.create_connection(("127.0.0.1", port), timeout=0.1):
|
|
18
|
+
return
|
|
19
|
+
except OSError:
|
|
20
|
+
time.sleep(0.1)
|
|
21
|
+
raise TimeoutError(f"Port {port} not ready")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.mark.timeout(10)
|
|
25
|
+
@pytest.mark.parametrize(
|
|
26
|
+
"settings",
|
|
27
|
+
[
|
|
28
|
+
pytest.param("queueio.samples.django.settings", id="explicit"),
|
|
29
|
+
pytest.param("queueio.samples.django.settings_djp", id="djp"),
|
|
30
|
+
],
|
|
31
|
+
)
|
|
32
|
+
def test_django(tmp_path, settings):
|
|
33
|
+
env = {**os.environ, "DJANGO_SETTINGS_MODULE": settings}
|
|
34
|
+
|
|
35
|
+
pyproject = tmp_path / "pyproject.toml"
|
|
36
|
+
pyproject.write_text(
|
|
37
|
+
f"[tool.django]\n"
|
|
38
|
+
f'settings = "{settings}"\n'
|
|
39
|
+
f"\n"
|
|
40
|
+
f"[tool.queueio]\n"
|
|
41
|
+
f'register = ["queueio.samples.django.job"]\n'
|
|
42
|
+
f'pika = "amqp://localhost:5672"\n'
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
subprocess.run(
|
|
46
|
+
[sys.executable, "-m", "django", "migrate", "--run-syncdb"],
|
|
47
|
+
cwd=str(tmp_path),
|
|
48
|
+
env=env,
|
|
49
|
+
check=True,
|
|
50
|
+
capture_output=True,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
subprocess.run(
|
|
54
|
+
[sys.executable, "-m", "queueio", "sync"],
|
|
55
|
+
cwd=str(tmp_path),
|
|
56
|
+
env=env,
|
|
57
|
+
check=True,
|
|
58
|
+
capture_output=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
server = subprocess.Popen(
|
|
62
|
+
[sys.executable, "-m", "django", "runserver", "--noreload", "127.0.0.1:8642"],
|
|
63
|
+
cwd=str(tmp_path),
|
|
64
|
+
env=env,
|
|
65
|
+
stdout=subprocess.PIPE,
|
|
66
|
+
stderr=subprocess.PIPE,
|
|
67
|
+
)
|
|
68
|
+
worker = subprocess.Popen(
|
|
69
|
+
[sys.executable, "-m", "queueio", "run", "django=1"],
|
|
70
|
+
cwd=str(tmp_path),
|
|
71
|
+
env=env,
|
|
72
|
+
stdout=subprocess.PIPE,
|
|
73
|
+
stderr=subprocess.PIPE,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
_wait_for_port(8642)
|
|
78
|
+
|
|
79
|
+
urlopen(Request("http://127.0.0.1:8642/", method="POST"))
|
|
80
|
+
|
|
81
|
+
while True:
|
|
82
|
+
assert worker.poll() is None, (
|
|
83
|
+
f"Worker exited early with code {worker.returncode}"
|
|
84
|
+
)
|
|
85
|
+
response = urlopen("http://127.0.0.1:8642/")
|
|
86
|
+
data = json.loads(response.read())
|
|
87
|
+
if data["status"] == "complete":
|
|
88
|
+
break
|
|
89
|
+
time.sleep(0.1)
|
|
90
|
+
finally:
|
|
91
|
+
for proc in [server, worker]:
|
|
92
|
+
if proc.poll() is None:
|
|
93
|
+
proc.terminate()
|
|
94
|
+
try:
|
|
95
|
+
proc.wait(timeout=5)
|
|
96
|
+
except subprocess.TimeoutExpired:
|
|
97
|
+
proc.kill()
|
|
98
|
+
proc.wait()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
/queueio-0.5.0/queueio/pika/__init__.py → /queueio-0.6.0/docs_overrides/partials/footer.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|