taskbadger 1.6.2__tar.gz → 1.6.3__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 (56) hide show
  1. {taskbadger-1.6.2 → taskbadger-1.6.3}/PKG-INFO +1 -1
  2. {taskbadger-1.6.2 → taskbadger-1.6.3}/pyproject.toml +1 -1
  3. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/celery.py +83 -0
  4. {taskbadger-1.6.2 → taskbadger-1.6.3}/.gitignore +0 -0
  5. {taskbadger-1.6.2 → taskbadger-1.6.3}/LICENSE +0 -0
  6. {taskbadger-1.6.2 → taskbadger-1.6.3}/README.md +0 -0
  7. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/__init__.py +0 -0
  8. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/cli/__init__.py +0 -0
  9. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/cli/basics.py +0 -0
  10. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/cli/list_tasks.py +0 -0
  11. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/cli/utils.py +0 -0
  12. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/cli/wrapper.py +0 -0
  13. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/cli_main.py +0 -0
  14. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/config.py +0 -0
  15. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/decorators.py +0 -0
  16. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/exceptions.py +0 -0
  17. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/integrations.py +0 -0
  18. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/__init__.py +0 -0
  19. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/__init__.py +0 -0
  20. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/action_endpoints/__init__.py +0 -0
  21. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/action_endpoints/action_cancel.py +0 -0
  22. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/action_endpoints/action_create.py +0 -0
  23. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/action_endpoints/action_get.py +0 -0
  24. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/action_endpoints/action_list.py +0 -0
  25. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/action_endpoints/action_partial_update.py +0 -0
  26. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/action_endpoints/action_update.py +0 -0
  27. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/task_endpoints/__init__.py +0 -0
  28. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/task_endpoints/task_cancel.py +0 -0
  29. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/task_endpoints/task_create.py +0 -0
  30. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/task_endpoints/task_get.py +0 -0
  31. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/task_endpoints/task_list.py +0 -0
  32. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/task_endpoints/task_partial_update.py +0 -0
  33. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/api/task_endpoints/task_update.py +0 -0
  34. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/client.py +0 -0
  35. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/errors.py +0 -0
  36. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/__init__.py +0 -0
  37. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/action.py +0 -0
  38. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/action_request.py +0 -0
  39. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/paginated_task_list.py +0 -0
  40. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/patched_action_request.py +0 -0
  41. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/patched_task_request.py +0 -0
  42. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/patched_task_request_tags.py +0 -0
  43. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/status_enum.py +0 -0
  44. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/task.py +0 -0
  45. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/task_request.py +0 -0
  46. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/task_request_tags.py +0 -0
  47. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/models/task_tags.py +0 -0
  48. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/py.typed +0 -0
  49. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/internal/types.py +0 -0
  50. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/mug.py +0 -0
  51. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/process.py +0 -0
  52. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/safe_sdk.py +0 -0
  53. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/sdk.py +0 -0
  54. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/systems/__init__.py +0 -0
  55. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/systems/celery.py +0 -0
  56. {taskbadger-1.6.2 → taskbadger-1.6.3}/taskbadger/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskbadger
3
- Version: 1.6.2
3
+ Version: 1.6.3
4
4
  Summary: The official Python SDK for Task Badger
5
5
  Project-URL: Changelog, https://github.com/taskbadger/taskbadger-python/releases
6
6
  Project-URL: homepage, https://taskbadger.net/
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "taskbadger"
3
- version = "1.6.2"
3
+ version = "1.6.3"
4
4
  description = "The official Python SDK for Task Badger"
5
5
  requires-python = ">=3.9"
6
6
  authors = []
@@ -212,8 +212,91 @@ def task_publish_handler(sender=None, headers=None, body=None, **kwargs):
212
212
  ctask.update_state(task_id=headers["id"], state="PENDING", meta=meta)
213
213
 
214
214
 
215
+ def _maybe_create_task(signal_sender):
216
+ """Create a TaskBadger task if one doesn't exist yet.
217
+
218
+ This handles cases where before_task_publish didn't fire or was skipped:
219
+ - Eager mode (before_task_publish doesn't fire)
220
+ - Canvas primitives like map/starmap/chunks (fire for celery.* tasks)
221
+ """
222
+ # Check if task was already created FIRST (before accessing Badger)
223
+ # This avoids initializing thread-local Badger state for tasks like celery.ping
224
+ task_id = _get_taskbadger_task_id(signal_sender.request)
225
+ if task_id:
226
+ return
227
+
228
+ task_name = signal_sender.name
229
+
230
+ # Skip built-in celery tasks that we don't track (like celery.ping)
231
+ # Only handle celery.map and celery.starmap specially
232
+ if task_name.startswith("celery.") and task_name not in ("celery.map", "celery.starmap"):
233
+ return
234
+
235
+ # For non-canvas tasks, only create if there was an explicit intent to track
236
+ # (indicated by taskbadger_track header). This prevents creating tasks when
237
+ # Badger wasn't configured at publish time but has stale config in worker.
238
+ headers = signal_sender.request.headers or {}
239
+ is_canvas_task = task_name in ("celery.map", "celery.starmap")
240
+ if not is_canvas_task and not headers.get("taskbadger_track"):
241
+ return
242
+
243
+ # NOW it's safe to check Badger configuration
244
+ if not Badger.is_configured():
245
+ return
246
+
247
+ celery_system = Badger.current.settings.get_system_by_id("celery")
248
+ data = None
249
+ inner_task = None
250
+
251
+ # Handle celery.map and celery.starmap - extract the inner task name
252
+ if task_name in ("celery.map", "celery.starmap"):
253
+ canvas_type = task_name.split(".")[-1] # "map" or "starmap"
254
+ inner_task_info = signal_sender.request.kwargs.get("task")
255
+ if inner_task_info:
256
+ # inner_task_info can be a dict (serialized signature) or a Signature object
257
+ if isinstance(inner_task_info, dict):
258
+ task_name = inner_task_info.get("task", task_name)
259
+ elif hasattr(inner_task_info, "name"):
260
+ task_name = inner_task_info.name
261
+ # Get the actual task class to check if it uses Task base
262
+ inner_task = celery.current_app.tasks.get(task_name)
263
+ items = signal_sender.request.kwargs.get("it", [])
264
+ # Convert to list if needed for counting and potential recording
265
+ items_list = list(items) if not isinstance(items, (list, tuple)) else items
266
+ item_count = len(items_list)
267
+ # Append canvas type and item count to task name
268
+ task_name = f"{task_name} ({canvas_type} {item_count})"
269
+ data = {"canvas_type": signal_sender.name, "item_count": item_count}
270
+
271
+ # Include task items if record_task_args is enabled
272
+ if celery_system and celery_system.record_task_args:
273
+ try:
274
+ _, _, value = serialization.dumps({"items": items_list}, serializer="json")
275
+ items_data = json.loads(value)
276
+ data["celery_task_items"] = items_data["items"]
277
+ except Exception:
278
+ log.warning("Error serializing canvas items for task '%s'", task_name)
279
+
280
+ # Check if we should track this task
281
+ auto_track = celery_system and celery_system.track_task(task_name)
282
+ # Check if the task (or inner task for map/starmap) uses our Task base class
283
+ task_to_check = inner_task if inner_task else signal_sender
284
+ manual_track = isinstance(task_to_check, Task)
285
+ if not manual_track and not auto_track:
286
+ return
287
+
288
+ enter_session()
289
+
290
+ task = create_task_safe(task_name, status=StatusEnum.PENDING, data=data)
291
+ if task:
292
+ # Store the task ID in the request so _update_task can find it
293
+ signal_sender.request.update({TB_TASK_ID: task.id})
294
+ safe_get_task.cache.set((task.id,), task)
295
+
296
+
215
297
  @task_prerun.connect
216
298
  def task_prerun_handler(sender=None, **kwargs):
299
+ _maybe_create_task(sender)
217
300
  _update_task(sender, StatusEnum.PROCESSING)
218
301
 
219
302
 
File without changes
File without changes
File without changes
File without changes
File without changes