django-queuebie 0.6.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.
Files changed (101) hide show
  1. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/CHANGES.md +6 -0
  2. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/PKG-INFO +1 -1
  3. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/features/getting-started.md +16 -0
  4. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/__init__.py +1 -1
  5. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/runner.py +14 -2
  6. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_runner.py +44 -0
  7. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.ambient-package-update/metadata.py +0 -0
  8. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.ambient-package-update/templates/snippets/content.tpl +0 -0
  9. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.ambient-package-update/templates/snippets/tagline.tpl +0 -0
  10. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.editorconfig +0 -0
  11. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  12. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  13. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  14. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  15. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.github/workflows/ci.yml +0 -0
  16. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.github/workflows/quality-gate.yml +0 -0
  17. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.github/workflows/release.yml +0 -0
  18. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.gitignore +0 -0
  19. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.pre-commit-config.yaml +0 -0
  20. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/.readthedocs.yaml +0 -0
  21. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/CODE_OF_CONDUCT.md +0 -0
  22. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/CONTRIBUTING.md +0 -0
  23. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/LICENSE.md +0 -0
  24. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/MANIFEST.in +0 -0
  25. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/README.md +0 -0
  26. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/SECURITY.md +0 -0
  27. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/Makefile +0 -0
  28. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/conf.py +0 -0
  29. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/features/changelog.rst +0 -0
  30. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/features/introduction.md +0 -0
  31. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/features/motivation.md +0 -0
  32. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/features/settings.md +0 -0
  33. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/features/setup.md +0 -0
  34. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/index.rst +0 -0
  35. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/docs/make.bat +0 -0
  36. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/manage.py +0 -0
  37. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/pyproject.toml +0 -0
  38. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/apps.py +0 -0
  39. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/database_blocker.py +0 -0
  40. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/exceptions.py +0 -0
  41. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/logger.py +0 -0
  42. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/management/__init__.py +0 -0
  43. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/management/commands/__init__.py +0 -0
  44. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/management/commands/clear_queuebie_registry.py +0 -0
  45. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/messages.py +0 -0
  46. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/registry.py +0 -0
  47. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/settings.py +0 -0
  48. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/queuebie/utils.py +0 -0
  49. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/scripts/unix/install_requirements.sh +0 -0
  50. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/scripts/unix/publish_to_pypi.sh +0 -0
  51. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/scripts/windows/install_requirements.ps1 +0 -0
  52. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/scripts/windows/publish_to_pypi.ps1 +0 -0
  53. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/settings.py +0 -0
  54. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/__init__.py +0 -0
  55. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/handlers/__init__.py +0 -0
  56. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/handlers/commands/__init__.py +0 -0
  57. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/handlers/commands/messages.py +0 -0
  58. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/handlers/commands/testapp.py +0 -0
  59. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/handlers/events/__init__.py +0 -0
  60. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/handlers/events/testapp.py +0 -0
  61. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/messages/__init__.py +0 -0
  62. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/messages/commands/__init__.py +0 -0
  63. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/messages/commands/messages.py +0 -0
  64. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/messages/commands/my_commands.py +0 -0
  65. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/messages/commands/other_commands.py +0 -0
  66. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/messages/events/__init__.py +0 -0
  67. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/messages/events/my_events.py +0 -0
  68. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/__init__.py +0 -0
  69. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/handlers/__init__.py +0 -0
  70. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/handlers/commands/__init__.py +0 -0
  71. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/handlers/commands/nested_domain.py +0 -0
  72. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/handlers/events/__init__.py +0 -0
  73. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/handlers/events/nested_domain.py +0 -0
  74. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/messages/__init__.py +0 -0
  75. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/messages/commands/__init__.py +0 -0
  76. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/messages/commands/nested_commands.py +0 -0
  77. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/messages/events/__init__.py +0 -0
  78. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/nested_domain/messages/events/nested_events.py +0 -0
  79. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/tests/__init__.py +0 -0
  80. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/tests/handlers/__init__.py +0 -0
  81. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/tests/handlers/commands/__init__.py +0 -0
  82. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/tests/handlers/commands/decoy.py +0 -0
  83. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/tests/messages/__init__.py +0 -0
  84. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/tests/messages/commands/__init__.py +0 -0
  85. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/tests/messages/commands/decoy_commands.py +0 -0
  86. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/testapp/urls.py +0 -0
  87. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/__init__.py +0 -0
  88. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/helpers/__init__.py +0 -0
  89. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/helpers/commands.py +0 -0
  90. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/helpers/events.py +0 -0
  91. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/management_commands/__init__.py +0 -0
  92. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/management_commands/test_clear_queuebie_registry.py +0 -0
  93. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_apps.py +0 -0
  94. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_database_blocker.py +0 -0
  95. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_exceptions.py +0 -0
  96. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_logger.py +0 -0
  97. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_messages.py +0 -0
  98. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_registry.py +0 -0
  99. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_settings.py +0 -0
  100. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/tests/test_utils.py +0 -0
  101. {django_queuebie-0.6.0 → django_queuebie-0.7.0}/uv.lock +0 -0
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ **0.7.0** (2026-10-05)
4
+ * `handle_message()` returns the handled messages in the order they were processed - the initial message(s)
5
+ followed by every command and event raised along the way - so the caller can tell whether its command led to the
6
+ expected event
7
+ * `handle_message()` no longer empties a list passed in by the caller
8
+
3
9
  **0.6.0** (2026-09-09)
4
10
  * Auto-discovery now walks the whole subtree of every local Django app, so `handlers/commands` and `handlers/events`
5
11
  directories may live in sub-packages instead of only at the app root
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: django-queuebie
3
- Version: 0.6.0
3
+ Version: 0.7.0
4
4
  Summary: A simple and synchronous message queue for commands and events for Django
5
5
  Project-URL: Homepage, https://github.com/ambient-innovation/django-queuebie/
6
6
  Project-URL: Documentation, https://django-queuebie.readthedocs.io/en/latest/index.html
@@ -102,3 +102,19 @@ handle_message(
102
102
  )
103
103
  )
104
104
  ```
105
+
106
+ `handle_message()` returns every message it handled, in the order they were processed: the message(s) you passed in,
107
+ followed by all commands and events the handlers returned along the way. Use it to find out what your command led to,
108
+ for example when a command handler can decide to do nothing and return `None`:
109
+
110
+ ```python
111
+ handled = handle_message(BuyProduct(...))
112
+
113
+ if any(isinstance(message, ProductBought) for message in handled):
114
+ messages.success(request, "Thank you for your purchase.")
115
+ else:
116
+ messages.error(request, "The product could not be bought.")
117
+ ```
118
+
119
+ The returned list tells you which handlers ran, not that their changes are committed. If you call `handle_message()`
120
+ inside an outer `transaction.atomic()` block, that outer transaction can still roll everything back.
@@ -1,6 +1,6 @@
1
1
  """A simple and synchronous message queue for commands and events for Django"""
2
2
 
3
- __version__ = "0.6.0"
3
+ __version__ = "0.7.0"
4
4
 
5
5
  from queuebie.registry import MessageRegistry
6
6
 
@@ -11,8 +11,16 @@ from queuebie.messages import Command, Event, Message
11
11
  from queuebie.settings import get_queuebie_strict_mode
12
12
 
13
13
 
14
- def handle_message(messages: Message | list[Message]) -> None:
15
- queue: list[Message] = messages if isinstance(messages, list) else [messages]
14
+ def handle_message(messages: Message | list[Message]) -> list[Message]:
15
+ """
16
+ Process the given message(s) and every message their handlers return, until the queue is drained.
17
+
18
+ Returns all handled messages in the order they were processed - the initial message(s) first, followed by
19
+ everything raised along the way. The caller can inspect it to learn what its command led to, e.g. whether a
20
+ handler returned an event or bailed out with `None`.
21
+ """
22
+ # Copy the input, so draining the queue doesn't empty the caller's list
23
+ queue: list[Message] = list(messages) if isinstance(messages, list) else [messages]
16
24
 
17
25
  for message in queue:
18
26
  if not isinstance(message, (Command, Event)):
@@ -21,9 +29,11 @@ def handle_message(messages: Message | list[Message]) -> None:
21
29
  # Run auto-registry
22
30
  message_registry.autodiscover()
23
31
 
32
+ handled: list[Message] = []
24
33
  with transaction.atomic():
25
34
  while queue:
26
35
  message = queue.pop(0)
36
+ handled.append(message)
27
37
  if isinstance(message, Command):
28
38
  handler_list = message_registry.command_dict.get(message.module_path(), [])
29
39
  block_db_access = False
@@ -34,6 +44,8 @@ def handle_message(messages: Message | list[Message]) -> None:
34
44
  new_messages = _process_message(handler_list=handler_list, message=message, block_db_access=block_db_access)
35
45
  queue.extend(new_messages)
36
46
 
47
+ return handled
48
+
37
49
 
38
50
  def _process_message(*, handler_list: list, message: [Command, Event], block_db_access: bool) -> list[Message]:
39
51
  """
@@ -136,3 +136,47 @@ def test_handle_message_atomic_works(*args):
136
136
  handle_message([CreateUser(username="username"), RaiseRuntimeError(error_msg="Something is broken.")])
137
137
 
138
138
  assert User.objects.filter(username="username").exists() is False
139
+
140
+
141
+ @pytest.mark.django_db
142
+ def test_handle_message_returns_handled_messages_in_order():
143
+ command = DoSomething(my_var=1)
144
+
145
+ handled = handle_message(messages=command)
146
+
147
+ assert len(handled) == 2 # noqa: PLR2004
148
+ assert handled[0] is command
149
+ assert isinstance(handled[1], SomethingHappened)
150
+ assert handled[1].other_var == 2 # noqa: PLR2004
151
+
152
+
153
+ @pytest.mark.django_db
154
+ def test_handle_message_returns_only_initial_message_if_handler_returns_none():
155
+ command = CriticalCommand(my_var=1)
156
+
157
+ handled = handle_message(messages=command)
158
+
159
+ assert handled == [command]
160
+
161
+
162
+ @pytest.mark.django_db
163
+ def test_handle_message_returns_all_initial_messages_of_list():
164
+ command = DoSomething(my_var=1)
165
+ event = SomethingHappened(other_var=5)
166
+
167
+ handled = handle_message(messages=[command, event])
168
+
169
+ assert handled[0] is command
170
+ assert handled[1] is event
171
+ assert isinstance(handled[2], SomethingHappened)
172
+ assert handled[2].other_var == 2 # noqa: PLR2004
173
+ assert len(handled) == 3 # noqa: PLR2004
174
+
175
+
176
+ @pytest.mark.django_db
177
+ def test_handle_message_does_not_empty_passed_list():
178
+ message_list = [CriticalCommand(my_var=1)]
179
+
180
+ handle_message(messages=message_list)
181
+
182
+ assert len(message_list) == 1
File without changes