Repository: celery/celery

Issue:
@app.task should copy __annotations__ from the task function like @functools.wraps
<!--
Please fill this template entirely and do not erase parts of it.
We reserve the right to close without a response
feature requests 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%22Issue+Type%3A+Feature+Request%22+)
  for similar or identical feature requests.
- [x] I have checked the [pull requests list](https://github.com/celery/celery/pulls?utf8=%E2%9C%93&q=is%3Apr+label%3A%22PR+Type%3A+Feature%22+)
  for existing proposed implementations of this feature.
- [x] I have checked the [commit log](https://github.com/celery/celery/commits/master)
  to find out if the if the same feature was already implemented 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

# Brief Summary
<!--
Please include a brief summary of what the feature does
and why it is needed.
-->

# Design

## Architectural Considerations
<!--
If more components other than Celery are involved,
describe them here and the effect it would have on Celery.
-->
None

## Proposed Behavior
<!--
Please describe in detail how this feature is going to behave.
Describe what happens in case of failures as well if applicable.
-->
Celery tasks created with `@app.task` should support `typing.get_type_hints(my_celery_task)`.

It can be solved by copying the `__annotations__` from the decorated function when available. This is done by `functools.wraps()` in Python 3.0+, if I understand [PEP 3107](https://www.python.org/dev/peps/pep-3107/) correctly. Without this `typing.get_type_hints(my_celery_task)` raises an exception.

It is my understanding that some other similar properties that `functools.wraps` handles are already copied: https://github.com/celery/celery/blob/d3863d909759f1fd618f2a1af1766ce54c16d39b/celery/app/base.py#L453-L454

## Proposed UI/UX
<!--
Please provide your ideas for the API, CLI options,
configuration key names etc. that will be introduced for this feature.
-->

## Diagrams
<!--
Please include any diagrams that might be relevant
to the implementation of this feature such as:
* Class Diagrams
* Sequence Diagrams
* Activity Diagrams
You can drag and drop images into the text box to attach them to this issue.
-->
N/A

## Alternatives
<!--
If you have considered any alternative implementations
describe them in detail below.
-->
None



Discussion:
we will consider this in near future
Thanks! Please let me know if help is needed for this to be implemented.
for celery 4.5 which will be python 3.6+ only
For anyone finding this before it's fixed, I have the following work-around:
```
@functools.wraps(decorated_function, assigned=('__annotations__',))
@celery_app.task(bind=True, base=base, priority=priority, name=name)
def task(): ...
```
I think that's certainly desirable for Celery 5. We can also backport to 4.x.

@davidparsson Care to help us with that?
@thedrow, I guess you're asking me although you pinged someone else. Yes, I'll give it a shot, but it might be a few weeks until I find the time.
I mentioned the wrong guy by accident. My apologies.
Edited for clarity. 

Resolving patch:
diff --git a/celery/app/base.py b/celery/app/base.py
--- a/celery/app/base.py
+++ b/celery/app/base.py
@@ -428,6 +428,7 @@ def _task_from_fun(self, fun, name=None, base=None, bind=False, **options):
                 '_decorated': True,
                 '__doc__': fun.__doc__,
                 '__module__': fun.__module__,
+                '__annotations__': fun.__annotations__,
                 '__header__': staticmethod(head_from_fun(fun, bound=bind)),
                 '__wrapped__': run}, **options))()
             # for some reason __qualname__ cannot be set in type()


Test patch:
diff --git a/t/unit/app/test_app.py b/t/unit/app/test_app.py
--- a/t/unit/app/test_app.py
+++ b/t/unit/app/test_app.py
@@ -494,6 +494,16 @@ def foo():
         finally:
             _imports.MP_MAIN_FILE = None
 
+    def test_can_get_type_hints_for_tasks(self):
+        import typing
+
+        with self.Celery() as app:
+            @app.task
+            def foo(parameter: int) -> None:
+                pass
+
+            assert typing.get_type_hints(foo) == {'parameter': int, 'return': type(None)}
+
     def test_annotate_decorator(self):
         from celery.app.task import Task