Repository: celery/celery

Issue:
4.4 silently changes the Inspect API
<!--
Please fill this template entirely and do not erase parts of it.
We reserve the right to close without a response
bug reports which are incomplete.
-->
# Checklist
<!--
To check an item on the list replace [ ] with [x].
-->

- [x] I have checked the [issues list](https://github.com/celery/celery/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3A%22Category%3A+Documentation%22+)
  for similar or identical bug reports.
- [x] I have checked the [pull requests list](https://github.com/celery/celery/pulls?q=is%3Apr+label%3A%22Category%3A+Documentation%22)
  for existing proposed fixes.
- [x] I have checked the [commit log](https://github.com/celery/celery/commits/master)
  to find out if the bug was already fixed in the master branch.
- [x] I have included all related issues and possible duplicate issues in this issue
      (If there are none, check this box anyway).

## Related Issues and Possible Duplicates
<!--
Please make sure to search and mention any related issues
or possible duplicates to this issue as requested by the checklist above.

This may or may not include issues in other repositories that the Celery project
maintains or other repositories that are dependencies of Celery.

If you don't know how to mention issues, please refer to Github's documentation
on the subject: https://help.github.com/en/articles/autolinked-references-and-urls#issues-and-pull-requests
-->

#### Related Issues

- None

#### Possible Duplicates

- None

# Description
<!--
Please describe what's missing or incorrect about our documentation.
Include links and/or screenshots which will aid us to resolve the issue.
-->

Prior to Celery 4.4.0, the celery.app.control.Inspect.scheduled() API used to return "args" and "kwargs" as strings which could be parsed using ast.literal_eval. Now, they seem to be a list and dict respectively:

 {'id': 'd82058c7-12b0-4914-8381-2fef9ad874a9', 'name': '...', 'args': ['time', 'sleep', 5], 'kwargs': {}, 'type': '...', }

# Suggestions
<!-- Please provide us suggestions for how to fix the documentation -->

I assume this is a deliberate design change; it would have been helpful to add this breaking change to the release notes!




Discussion:
I admit the release notes are not complete as I released the 4.4.0 quite hastily before the holiday season starts. thanks for the report
I think this issue is related to: https://groups.google.com/g/celery-users/c/1F61iEj-YFI/m/N7d64AalDAAJ

https://github.com/celery/celery/blob/8492b75c579564c2af5c2be75fe4b2118ebd0cd1/celery/worker/request.py#L558-L559

args and kwargs are passed directly. So correctly working when using JSON serialization.
But when using pickle serialization, it causes error.

```
kombu.exceptions.EncodeError: Object of type **** is not JSON serializable
```

## Reproduction

`celery -A repro worker` and `python repro.py` in separated two shells.

```python
import time

from celery import Celery


app = Celery('example')
app.conf.update(
    backend_url='redis://localhost:6379',
    broker_url='redis://localhost:6379',
    result_backend='redis://localhost:6379',
    task_serializer='pickle',
    accept_content=['pickle', 'json'],
)


@app.task(name='task1')
def task1(*args, **kwargs):
    print('Start', args, kwargs)
    time.sleep(30)
    print('Finish', args, kwargs)


def main():
    task1.delay({1, 2, 3})  # something not JSON serializable.

    # inspect queue items
    inspected = app.control.inspect()
    active_tasks = inspected.active()
    print(active_tasks)


if __name__ == '__main__':
    main()
```
> I think this issue is related to: https://groups.google.com/g/celery-users/c/1F61iEj-YFI/m/N7d64AalDAAJ
> 
> https://github.com/celery/celery/blob/8492b75c579564c2af5c2be75fe4b2118ebd0cd1/celery/worker/request.py#L558-L559
> 
> args and kwargs are passed directly. So correctly working when using JSON serialization.
> But when using pickle serialization, it causes error.
> 
> ```
> kombu.exceptions.EncodeError: Object of type **** is not JSON serializable
> ```
> 
> ## Reproduction
> 
> `celery -A repro worker` and `python repro.py` in separated two shells.
> 
> ```python
> import time
> 
> from celery import Celery
> 
> 
> app = Celery('example')
> app.conf.update(
>     backend_url='redis://localhost:6379',
>     broker_url='redis://localhost:6379',
>     result_backend='redis://localhost:6379',
>     task_serializer='pickle',
>     accept_content=['pickle', 'json'],
> )
> 
> 
> @app.task(name='task1')
> def task1(*args, **kwargs):
>     print('Start', args, kwargs)
>     time.sleep(30)
>     print('Finish', args, kwargs)
> 
> 
> def main():
>     task1.delay({1, 2, 3})  # something not JSON serializable.
> 
>     # inspect queue items
>     inspected = app.control.inspect()
>     active_tasks = inspected.active()
>     print(active_tasks)
> 
> 
> if __name__ == '__main__':
>     main()
> ```

would you mind finding a possible fix?
I think there is a possible fix like this https://github.com/celery/celery/pull/6567
We need to understand how we should fix this issue either via PR #6567 or via my comment: https://github.com/celery/celery/pull/6567#issuecomment-788008337

Resolving patch:
diff --git a/celery/app/control.py b/celery/app/control.py
--- a/celery/app/control.py
+++ b/celery/app/control.py
@@ -431,7 +431,8 @@ def __init__(self, app=None):
         self.mailbox = self.Mailbox(
             app.conf.control_exchange,
             type='fanout',
-            accept=['json'],
+            accept=app.conf.accept_content,
+            serializer=app.conf.task_serializer,
             producer_pool=lazy(lambda: self.app.amqp.producer_pool),
             queue_ttl=app.conf.control_queue_ttl,
             reply_queue_ttl=app.conf.control_queue_ttl,


Test patch:
diff --git a/t/unit/app/test_control.py b/t/unit/app/test_control.py
--- a/t/unit/app/test_control.py
+++ b/t/unit/app/test_control.py
@@ -241,6 +241,12 @@ def assert_control_called_with_args(self, name, destination=None,
         self.app.control.broadcast.assert_called_with(
             name, destination=destination, arguments=args, **_options or {})
 
+    def test_serializer(self):
+        self.app.conf['task_serializer'] = 'test'
+        self.app.conf['accept_content'] = ['test']
+        assert control.Control(self.app).mailbox.serializer == 'test'
+        assert control.Control(self.app).mailbox.accept == ['test']
+
     def test_purge(self):
         self.app.amqp.TaskConsumer = Mock(name='TaskConsumer')
         self.app.control.purge()