It's important to be clear about which use cases are addressed by a given system. For example, if you have compute-bound tasks that operate on non-shared data then it makes a lot of sense to distribute them via a multiprocessing system like Celery.
But if you have I/O-bound tasks that operate on shared data then it doesn't — implementing the necessary locking and communication is very hard. In this case, coroutines are simple to program and reliable, and because you're I/O-bound you're not losing anything by being single-threaded.