Skip to content

Conversation

@yashpapa6969
Copy link

Change Log

  • API Update: Introduced the TaskReapply endpoint to reapply tasks with original arguments.
  • Error Handling: Ensures JSON-serializable arguments and robust error logging.
  • Backend Utilities: Added helper functions (parse_args, parse_kwargs, make_json_serializable) for argument parsing and serialization.

@iloveitaly
Copy link

@mher anything I can do to help get this merged?

@yashpapa6969 yashpapa6969 requested a review from sc68cal January 25, 2025 16:31
@yashpapa6969 yashpapa6969 requested a review from sc68cal January 26, 2025 14:04
Copy link

@sc68cal sc68cal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My concerns have been addressed, thank you for this contribution!

@yashpapa6969
Copy link
Author

@sc68cal how can i get this merged?

@sc68cal
Copy link

sc68cal commented Feb 2, 2025

@sc68cal how can i get this merged?

I am not the maintainer, the maintainer needs to approve and merge

Copy link

@marcus-campos marcus-campos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome!

@Ragnarow
Copy link

Ragnarow commented Feb 5, 2025

Thanks for the feature, @mher could we get this merged?

@matias-martini
Copy link

Hi! Thanks for adding this awesome feature @yashpapa6969 ! I tested it locally and noticed a small issue: I think we need to add a route for the new controller in flower/urls.py:

    (r"/api/task/reapply/(.+)", tasks.TaskReapply),

Otherwise the Retry button will fail with 404

@yashpapa6969
Copy link
Author

@matias-martini thank you updated

@matias-martini
Copy link

@yashpapa6969 Thanks for the updates! 🙌

Would you mind adding a test for the new API endpoint? It'll help ensure we cover the most common cases.

@Ragnarow
Copy link

@yashpapa6969 Thanks for the effort, can we have the tests to merge this?

@the-rich-piana
Copy link

Any update on this?

@alexandercarruthers
Copy link

Would be great to see this feature merged !

Copy link
Collaborator

@auvipy auvipy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will also need unit tests to avoid regression

@mihai-burduselu-ptt
Copy link

mihai-burduselu-ptt commented Sep 9, 2025

@yashpapa6969, thank you so much for the effort! Could you help us with the tests for it so we can review and merge?

@yashpapa6969 yashpapa6969 requested a review from auvipy December 22, 2025 07:43
@bondbenz
Copy link

bondbenz commented Jan 7, 2026

hello, any updates on this? thanks

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new feature to manually retry failed tasks through the Flower UI and API. It introduces a /api/task/reapply/{taskid} endpoint that retrieves a failed task's original arguments and reapplies it as a new task.

Key Changes:

  • Added TaskReapply API endpoint to reapply tasks with their original arguments
  • Implemented utility functions (parse_args, parse_kwargs, make_json_serializable) to handle argument parsing and JSON serialization
  • Added a "Retry" button in the UI for tasks in FAILURE state

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
flower/api/tasks.py Implements the new TaskReapply endpoint handler with error handling and task reapplication logic
flower/utils/tasks.py Adds helper functions for parsing task arguments from various formats (JSON, Python literals) and ensuring JSON serializability
flower/urls.py Registers the new /api/task/reapply endpoint route
flower/templates/task.html Adds a "Retry" button for failed tasks in the task detail page
flower/static/js/flower.js Implements client-side logic to handle retry button clicks and API communication
tests/unit/api/test_tasks.py Adds comprehensive test coverage for the TaskReapply endpoint covering success, error cases, and edge cases

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +90 to +91
if args.startswith('(') and args.endswith(')'):
return ast.literal_eval(args)
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security concern: Using ast.literal_eval on user-provided input (task.args) could be dangerous. While ast.literal_eval is safer than eval, it still executes Python code parsing. If an attacker can control the stored task.args value, they might craft malicious input. Consider restricting to JSON-only parsing or implementing additional validation before using ast.literal_eval.

Copilot uses AI. Check for mistakes.
except (json.JSONDecodeError, SyntaxError):
# Fallback for stringified tuples or ellipsis
if args == '...':
return [...]
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning a list containing an Ellipsis object is problematic. The Ellipsis object (...) is typically used as a placeholder but returning it wrapped in a list can cause issues downstream. Consider returning an empty list or raising a ValueError to indicate that '...' is not a valid argument format.

Suggested change
return [...]
return []

Copilot uses AI. Check for mistakes.
type: 'POST',
url: url_prefix() + '/api/task/reapply/' + taskId,
success: function (response) {
show_alert(`Task ${taskId} has been retried (new task ID: ${response['task-id']})`, 'success');
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handling for empty or undefined response. The success callback accesses response['task-id'] without verifying that the response object exists or contains the 'task-id' key. If the API returns an unexpected response structure, this could cause a runtime error.

Suggested change
show_alert(`Task ${taskId} has been retried (new task ID: ${response['task-id']})`, 'success');
var newTaskId = response && typeof response === 'object' ? response['task-id'] : undefined;
if (newTaskId) {
show_alert(`Task ${taskId} has been retried (new task ID: ${newTaskId})`, 'success');
} else {
show_alert(`Task ${taskId} has been retried.`, 'success');
}

Copilot uses AI. Check for mistakes.
:param taskid: ID of the task to reapply.
"""
# Get original task info
task = tasks.get_task_by_id(self.application.events, taskid)
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing input validation: The taskid parameter from the URL is used directly without validation. Consider validating that taskid matches expected UUID format to prevent potential injection issues or unnecessary processing of invalid task IDs.

Copilot uses AI. Check for mistakes.
Comment on lines +645 to +647
Get task info and reapply the task with the same arguments.
:param taskid: ID of the task to reapply.
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing API documentation: The TaskReapply endpoint lacks comprehensive API documentation that other endpoints in this file have (such as TaskInfo). Consider adding proper docstring documentation including HTTP method, example request/response, parameters description, and status codes.

Suggested change
Get task info and reapply the task with the same arguments.
:param taskid: ID of the task to reapply.
Reapply a previously executed task using the same arguments.
This endpoint retrieves an existing task by its ID, extracts its original
positional and keyword arguments, and submits a new task with the same
name and arguments. A new task ID is returned for the re-applied task.
**Example request**:
.. sourcecode:: http
POST /api/task/reapply/7b3b9f52-1af3-4e0c-8a8a-5a5f9c2f8c64 HTTP/1.1
Host: localhost
Accept: application/json
Cookie: user=...
**Example response**:
.. sourcecode:: http
HTTP/1.1 200 OK
Content-Type: application/json
{
"task-id": "c1a2b3d4-5678-90ab-cdef-1234567890ab",
"state": "PENDING"
}
:param str taskid: ID of the original task to reapply.
:statuscode 200: task successfully reapplied; returns new task ID and state (if backend configured)
:statuscode 400: invalid task arguments or original task has no name
:statuscode 401: unauthorized request
:statuscode 404: unknown task ID or unknown task name
:statuscode 500: internal error while reapplying the task

Copilot uses AI. Check for mistakes.
r = self.post('/api/task/reapply/123', body='')

self.assertEqual(200, r.code)
task.apply_async.assert_called_once_with(args=[None], kwargs={})
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test expects args=[None] when '...' is parsed, but according to the parse_args implementation on line 89 of flower/utils/tasks.py, it returns [Ellipsis] (a list containing the Ellipsis object), not [None]. This test will fail unless make_json_serializable is called, which converts Ellipsis to None. The test should verify the actual behavior or mock make_json_serializable.

Suggested change
task.apply_async.assert_called_once_with(args=[None], kwargs={})
task.apply_async.assert_called_once_with(args=[Ellipsis], kwargs={})

Copilot uses AI. Check for mistakes.
Comment on lines +684 to +686
except Exception as exc:
logger.error("Error reapplying task with args=%s, kwargs=%s: %s", args, kwargs, str(exc))
raise HTTPError(500, f"Error reapplying task: {str(exc)}") from exc
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using broad exception handling with 'Exception' can mask unexpected errors and make debugging difficult. The try block contains multiple operations (make_json_serializable, apply_async, response creation) and catching all exceptions makes it unclear what specific failure occurred. Consider using more specific exception types or separating error handling for different operations.

Copilot uses AI. Check for mistakes.
Comment on lines +116 to +119
if isinstance(obj, list):
return [make_json_serializable(item) for item in obj]
elif isinstance(obj, dict):
return {key: make_json_serializable(value) for key, value in obj.items()}
Copy link

Copilot AI Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function has incomplete handling of non-serializable types. While it handles Ellipsis, list, and dict, it doesn't handle other common non-serializable types like tuples, sets, datetime objects, or custom objects. Tuples should be converted to lists for JSON serialization, and other types may need special handling or should raise an error to alert callers of serialization issues.

Suggested change
if isinstance(obj, list):
return [make_json_serializable(item) for item in obj]
elif isinstance(obj, dict):
return {key: make_json_serializable(value) for key, value in obj.items()}
if isinstance(obj, (list, tuple, set)):
# Convert tuples and sets to lists, and recurse into all sequence elements
return [make_json_serializable(item) for item in obj]
elif isinstance(obj, dict):
return {key: make_json_serializable(value) for key, value in obj.items()}
elif isinstance(obj, (datetime.datetime, datetime.date, datetime.time)):
# Represent datetime-like objects as ISO 8601 strings
return obj.isoformat()

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@auvipy auvipy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please cross check the review comments carefully

auvipy and others added 2 commits January 7, 2026 18:27
@yashpapa6969
Copy link
Author

okay

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.