Channels: Worker functionality (`runworker`) is broken in 3.0.0

Created on 3 Nov 2020  Â·  9Comments  Â·  Source: django/channels

This issue is being created with the intent of tracking and documenting efforts following the discussion on upstream project asgiref: https://github.com/django/asgiref/issues/209

The Worker that exists in Channels (used via the runworker Django management command) is based on StatelessServer in asgiref. In the latest ASGI 3.0 specification, the spec for an Application has been changed to a single callable:

coroutine application(scope, receive, send)

When the Worker handles an incoming message from the channel layer, it currently delegates the handling of the application itself to the StatelessServer:

https://github.com/django/channels/blob/3ab3f42d34da51e4d7c514c88ce6f49b722c58bf/channels/worker.py#L42

This in turn tries to instantiate the Application following the old double-callable specification, which obviously doesn't work for applications that have been upgraded to the ASGI 3.0 format. Furthermore, the underlying exception (__call__() missing 2 required positional arguments: 'receive' and 'send') is being muffled and nothing is being output in the Worker console as a result, which complicates diagnosing the issue.

As suggested by @carltongibson, I'm going to try to first create a failing test in Channels and see where that leads me.

Most helpful comment

Fixed in django/asgiref#209, available in asgiref 3.3.1.

Plus c.f #1567, for StaticFilesWrapper, which will be in Channels 3.0.2 shortly.

Thanks all for the report, fixes and testing!

All 9 comments

there exist a workaround in django/asgiref#211, you need init the consumer:

after:

    'channel': ChannelNameRouter({
        'myconsumer': MyConsumer
    })

before:

    'channel': ChannelNameRouter({
        'myconsumer': MyConsumer.as_asgi()
    })

there exist a workaround in django/asgiref#211, you need init the consumer:
...

Unfortunately checked and this workaround does not work. Still getting (__call__() missing 2 required positional arguments: 'receive' and 'send')

Only workaround working for me is downgrading channels to 2.4.0

@mlodzianck

Unfortunately checked and this workaround does not work. Still getting (__call__() missing 2 required positional arguments: 'receive' and 'send')

Stacktrace please. And your routing: that one looks like you maybe forgot to call as_asgi() somewhere.

I have the same issue

asgiref==3.3.0
channels==3.0.1
Django==3.1.3

  File "C:\Users\home\AppData\Local\Programs\Python\Python38\lib\threading.py", line 932, in _bootstrap_inner
    self.run()
  File "C:\Users\home\AppData\Local\Programs\Python\Python38\lib\threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
  File "C:\Users\home\Documents\PycharmProjects\myapp\tests\fixtures_decoder.py", line 307, in run
    run_in_loop()
  File "C:\Users\home\Documents\PycharmProjects\myapp\venv\lib\site-packages\asgiref\sync.py", line 147, in __call__
    return call_result.result()
  File "C:\Users\home\AppData\Local\Programs\Python\Python38\lib\concurrent\futures\_base.py", line 432, in result
    return self.__get_result()
  File "C:\Users\home\AppData\Local\Programs\Python\Python38\lib\concurrent\futures\_base.py", line 388, in __get_result
    raise self._exception
  File "C:\Users\home\Documents\PycharmProjects\myapp\venv\lib\site-packages\asgiref\sync.py", line 212, in main_wrap
    result = await self.awaitable(*args, **kwargs)
  File "C:\Users\home\Documents\PycharmProjects\myapp\tests\fixtures_decoder.py", line 303, in run_async
    raise task.exception()
  File "C:\Users\home\Documents\PycharmProjects\myapp\tests\fixtures_decoder.py", line 293, in run_worker
    await asyncio.create_task(self._worker.handle(), name="WORKER_HANDLE_TASK")
  File "C:\Users\home\Documents\PycharmProjects\myapp\venv\lib\site-packages\channels\worker.py", line 30, in handle
    [listener.result() for listener in listeners]
  File "C:\Users\home\Documents\PycharmProjects\myapp\venv\lib\site-packages\channels\worker.py", line 30, in <listcomp>
    [listener.result() for listener in listeners]
  File "C:\Users\home\Documents\PycharmProjects\myapp\venv\lib\site-packages\channels\worker.py", line 42, in listener
    instance_queue = self.get_or_create_application_instance(channel, scope)
  File "C:\Users\home\Documents\PycharmProjects\myapp\venv\lib\site-packages\asgiref\server.py", line 87, in get_or_create_application_instance
    application_instance = self.application(scope=scope)
TypeError: __call__() missing 2 required positional arguments: 'receive' and 'send'

routing.py:

application = ProtocolTypeRouter({
    "channel": ChannelNameRouter({
        "decoder": consumers.DecodeConsumer.as_asgi(),
    }),
})

settings.py:

WSGI_APPLICATION = 'webproject.wsgi.application'
ASGI_APPLICATION = "myapp.routing.application"

Asgiref calls the ProtocolTypeRouter instance with just scope, but the ProtocolTypeRouter __call___ method takes scope, receive and send

@primal100 can you try with this:

git clone https://github.com/lsaavedr/asgiref.git
cd asgiref
git checkout server_compatibility
pip install .

of course pip-install in your environment

regards!

Yes that works, thank you @lsaavedr

I can confirm this in indeed fixed by this PR to asgiref:
https://github.com/django/asgiref/pull/211

... and making sure ‘as_asgi()’ is used on consumers

@primal100 can you try with this:

git clone https://github.com/lsaavedr/asgiref.git
cd asgiref
git checkout server_compatibility
pip install .

of course pip-install in your environment

regards!

@mlodzianck can you check with this?

Fixed in django/asgiref#209, available in asgiref 3.3.1.

Plus c.f #1567, for StaticFilesWrapper, which will be in Channels 3.0.2 shortly.

Thanks all for the report, fixes and testing!

Was this page helpful?
0 / 5 - 0 ratings