Repository: celery/celery

Issue:
chain.apply ignores kwargs
On master@a29a0fe56 I have added the following test to test_canvas.py in the test_chain class:

```python
    def test_kwargs_apply(self):
        x = chain(self.add.s(), self.add.s(8), self.add.s(10))
        res = x.apply(kwargs={'x': 1, 'y': 1}).get()
        assert res == 20
```

It fails and describes the observed behavior also mentioned earlier in https://github.com/celery/celery/issues/2695

Pytest output for the failure:

```
―――――――――――――――――――――――――――――――――――――――――――――――――― test_chain.test_kwargs_apply ―――――――――――――――――――――――――――――――――――――――――――――――――――

self = <t.unit.tasks.test_canvas.test_chain instance at 0x7f2725208560>

    def test_kwargs_apply(self):
        x = chain(self.add.s(), self.add.s(8), self.add.s(10))
>       res = x.apply(kwargs={'x': 1, 'y': 1}).get()

t/unit/tasks/test_canvas.py:446: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
celery/canvas.py:707: in apply
    ???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <EagerResult: 7b72e2aa-0455-45ae-9df9-429ecd1faf6a>, timeout = None, propagate = True, disable_sync_subtasks = True
kwargs = {}

    def get(self, timeout=None, propagate=True,
            disable_sync_subtasks=True, **kwargs):
        if disable_sync_subtasks:
            assert_will_not_block()
    
        if self.successful():
            return self.result
        elif self.state in states.PROPAGATE_STATES:
            if propagate:
>               raise self.result
E               TypeError: add() takes exactly 2 arguments (0 given)

celery/result.py:995: TypeError
------------------------------------------------------ Captured stderr call -------------------------------------------------------
[2018-08-03 14:23:40,008: ERROR/MainProcess] Task t.unit.tasks.test_canvas.add[7b72e2aa-0455-45ae-9df9-429ecd1faf6a] raised unexpected: TypeError('add() takes exactly 2 arguments (0 given)',)
Traceback (most recent call last):
  File "/home/developer/celery/app/trace.py", line 382, in trace_task
    R = retval = fun(*args, **kwargs)
TypeError: add() takes exactly 2 arguments (0 given)
```


Discussion:

Resolving patch:
diff --git a/celery/canvas.py b/celery/canvas.py
--- a/celery/canvas.py
+++ b/celery/canvas.py
@@ -701,11 +701,11 @@ def prepare_steps(self, args, kwargs, tasks,
         return tasks, results
 
     def apply(self, args=(), kwargs={}, **options):
-        last, fargs = None, args
+        last, (fargs, fkwargs) = None, (args, kwargs)
         for task in self.tasks:
-            res = task.clone(fargs).apply(
+            res = task.clone(fargs, fkwargs).apply(
                 last and (last.get(),), **dict(self.options, **options))
-            res.parent, last, fargs = last, res, None
+            res.parent, last, (fargs, fkwargs) = last, res, (None, None)
         return last
 
     @property


Test patch:
diff --git a/t/unit/tasks/test_canvas.py b/t/unit/tasks/test_canvas.py
--- a/t/unit/tasks/test_canvas.py
+++ b/t/unit/tasks/test_canvas.py
@@ -441,6 +441,11 @@ def test_apply(self):
         assert res.parent.parent.get() == 8
         assert res.parent.parent.parent is None
 
+    def test_kwargs_apply(self):
+        x = chain(self.add.s(), self.add.s(8), self.add.s(10))
+        res = x.apply(kwargs={'x': 1, 'y': 1}).get()
+        assert res == 20
+
     def test_single_expresion(self):
         x = chain(self.add.s(1, 2)).apply()
         assert x.get() == 3