Repository: django/channels

Issue:
ERROR: object.__init__() takes no parameters
Hello guys when I try to open connection from my frontend to my socket this error I get can you help me?

```
2020-12-05 15:00:37,612 ERROR    Exception inside application: object.__init__() takes no parameters
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/site-packages/channels/routing.py", line 71, in __call__
    return await application(scope, receive, send)
  File "/usr/local/lib/python3.6/site-packages/channels/sessions.py", line 47, in __call__
    return await self.inner(dict(scope, cookies=cookies), receive, send)
  File "/usr/local/lib/python3.6/site-packages/channels/sessions.py", line 254, in __call__
    return await self.inner(wrapper.scope, receive, wrapper.send)
  File "/usr/local/lib/python3.6/site-packages/channels/auth.py", line 181, in __call__
    return await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.6/site-packages/channels/middleware.py", line 26, in __call__
    return await self.inner(scope, receive, send)
  File "/usr/local/lib/python3.6/site-packages/channels/routing.py", line 160, in __call__
    send,
  File "/usr/local/lib/python3.6/site-packages/asgiref/compatibility.py", line 33, in new_application
    instance = application(scope)
  File "/usr/local/lib/python3.6/site-packages/channels/generic/websocket.py", line 159, in __init__
    super().__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters
```


Discussion:
Hi @lubomirhadziev. 

```
File "/usr/local/lib/python3.6/site-packages/channels/generic/websocket.py", line 159, in init
super().init(*args, **kwargs)
TypeError: object.init() takes no parameters
```

https://github.com/django/channels/blob/c7ea04f42cabd695334db97dae637a3945c496a0/channels/generic/websocket.py#L150-L159

Good, yes, that's a bug. `AsyncConsumer` doesn't declare `__init__()` so... 

Thanks for the report. 

We could either remove the `super()` call or add a stub implementation accepting `**kwargs`. I'd probably favour the former... 🤔 
I commented out the call to super().__init__(...) and now I get this exception:

  File "env/lib64/python3.9/site-packages/channels/staticfiles.py", line 44, in __call__
    return await self.application(scope, receive, send)
  File "env/lib64/python3.9/site-packages/channels/routing.py", line 71, in __call__
    return await application(scope, receive, send)
  File "env/lib64/python3.9/site-packages/channels/sessions.py", line 47, in __call__
    return await self.inner(dict(scope, cookies=cookies), receive, send)
  File "env/lib64/python3.9/site-packages/channels/sessions.py", line 254, in __call__
    return await self.inner(wrapper.scope, receive, wrapper.send)
  File "env/lib64/python3.9/site-packages/channels/auth.py", line 181, in __call__
    return await super().__call__(scope, receive, send)
  File "env/lib64/python3.9/site-packages/channels/middleware.py", line 26, in __call__
    return await self.inner(scope, receive, send)
  File "env/lib64/python3.9/site-packages/channels/routing.py", line 150, in __call__
    return await application(
  File "env/lib64/python3.9/site-packages/asgiref/compatibility.py", line 34, in new_application
    return await instance(receive, send)
TypeError: __call__() missing 1 required positional argument: 'send'
WebSocket DISCONNECT /ws/tester/13 [127.0.0.1:59654]

That looks like an issue not updating to the ASGI 3 signature. See the 3.0 release notes. 

Are you able to provide a reproduce here, as I'm just guessing without that. 
**Environment:**
- WSL with Ubuntu 18.04 (initially); updated to 20.04 as part of the solution
- using pipenv
- original Python version in venv: 3.6
- Python version in new venv:  3.8

Following up to help address this problem.  Early last year I had placed `channels-redis = {version = "<3"}` in the `Pipfile` to handle an error that I was getting at that time (maybe the same thing, I don't remember).  I've now removed that constraint in anticipation of upgrading from 18.04 to 20.04.  After removing this constraint and allowing channels-redis to move up to the latest version, I got the same error as first noted above in this issue (on Ubuntu 18.04).  I deleted the `__init__` call [as suggested by](https://github.com/django/channels/issues/1588#issuecomment-739475379) @carltongibson and then got the second error noted above.  I then followed the 3.0 release notes ([Upgrade to ASGI 3](https://channels.readthedocs.io/en/stable/releases/3.0.0.html#update-to-asgi-3)) to update my code.  I then got this error:
   aioredis.errors.ReplyError: ERR unknown command 'BZPOPMIN'
I discovered that the maximum Redis version on Ubuntu 18.04 is 4.0.9.  (`BZPOPMIN` is apparently a [new command in Redis 5](https://redislabs.com/blog/redis-5-0-is-here/).)  On another machine that I've already updated to 20.04 I saw that Redis is at 5.0.7, so I went ahead and updated my development WSL to 20.04 with the hope that bringing Redis to 5 would solve the `BZPOPMIN` problem.  When I first ran the app after updating to 20.04, it gave this error:  ImportError: cannot import name 'dist'.  This was a Python error and the Python version in the venv when this issue started was 3.6, so I re-created the venv with Python 3.8 (the system version for Unbuntu 20.04), and when I executed `runserver`, the app came up fine EVEN WITHOUT removing the `__init__` that caused the first error in this issue thread.  Regardless, I have created [this PR](https://github.com/django/channels/pull/1621) to remove the `super().__init__(*args, **kwargs)` calls in `WebsocketConsumer` and `AsyncWebsocketConsumer`.

Incidentally, I had to do `python3 -m pipenv xxxxxxxxx` in order to execute pipenv rather than just typing `pipenv xxxxxxxxx`.  I plan to start using that syntax since [it is a recommendation](https://snarky.ca/why-you-should-use-python-m-pip/) from one of the Python core developers.
Preface: This runs fine when deployed. It's the test cases that fail.

I am also running into this after following https://channels.readthedocs.io/en/stable/topics/testing.html#setting-up-async-tests to set up test cases for some classes that extend `WebsocketConsumer`.

* Django: 3.1.6
* Django-channels: 3.0.3

The stack trace as already stated in here is

```
Traceback (most recent call last):
  File "/home/jonas/.local/share/virtualenvs/paperless-RxYopU_u/lib/python3.6/site-packages/asgiref/sync.py", line 212, in main_wrap
    result = await self.awaitable(*args, **kwargs)
  File "/home/jonas/git/paperless/src/paperless/tests/test_websockets.py", line 11, in test_auth
    response = await communicator.get_response()
  File "/home/jonas/.local/share/virtualenvs/paperless-RxYopU_u/lib/python3.6/site-packages/channels/testing/http.py", line 42, in get_response
    response_start = await self.receive_output(timeout)
  File "/home/jonas/.local/share/virtualenvs/paperless-RxYopU_u/lib/python3.6/site-packages/asgiref/testing.py", line 78, in receive_output
    self.future.result()
  File "/home/jonas/.local/share/virtualenvs/paperless-RxYopU_u/lib/python3.6/site-packages/asgiref/compatibility.py", line 33, in new_application
    instance = application(scope)
  File "/home/jonas/.local/share/virtualenvs/paperless-RxYopU_u/lib/python3.6/site-packages/channels/generic/websocket.py", line 23, in __init__
    super().__init__(*args, **kwargs)
TypeError: object.__init__() takes no parameters
```

Probably due to `AsyncConsumer` not implementing a generic

```
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
```

constructor to pass variables passed to the constructor further down the hierarchy, but I don't know the details.

Debugging the test case reveals that at the point of failure, `WebsocketConsumer` would like to pass

```
args=[ {'type': 'http', 'http_version': '1.1', 'method': 'GET', 'path': '/test/', 'query_string': b'', 'headers': []} ]
```

to the constructor of `SyncConsumer`, which looks like a scope to me.

The specific `WebsocketConsumer` under test does not have to implement anything for this to fail:

```
class StatusConsumer(WebsocketConsumer):
    pass
```

Is there a workaround for testing consumers?
Investigating this further revealed that when running the application (with `runserver` or any ASGI server), the constructor of the `WebsocketConsumer` does not receive any arguments. With a test case as proposed by the documentation, it receives a scope object. Therefore, this might be a different issue with the testing utilities.
This has apparently something to do with `asgiref` detecting the WebsocketConsumer subclass as a "double-callable" when using the following example code from the documentation:

```python
from django.test import TestCase
from channels.testing import HttpCommunicator
from myproject.myapp.consumers import MyConsumer

class MyTests(TestCase):
    async def test_my_consumer(self):
        communicator = HttpCommunicator(MyConsumer, "GET", "/test/")
        response = await communicator.get_response()    # <-- error happens here
        self.assertEqual(response["body"], b"test response")
        self.assertEqual(response["status"], 200)
```

The relevant line is this:

https://github.com/django/asgiref/blob/2b6174c517e1b0f46b456d2fa5984ef2e1801684/asgiref/compatibility.py#L14

A "double-callable" is some legacy construct from before ASGI v3. `asgiref` is then trying to convert it to a "single-callable" by calling it (the constructor) with a scope object. That's also where the "takes no parameter" issue comes from. Some background info on that is available in https://github.com/django/asgiref/issues/78.

Feels like a different issue to what OP has posted, should I move this to a new issue?

Even more information is available in https://asgi.readthedocs.io/en/latest/specs/main.html#applications.
Oh well.

`HttpCommunicator(MyConsumer, "GET", "/test/")` from the documentation led me to believe I can simply pass in my WebsocketConsumer implementation and use that for testing. When using `WebsocketCommunicator(application)`, where `application` is the asgi application object, it works.

Still, I'll just leave all that information here, maybe that's useful for something.
I had the same issue, though found [this](https://stackoverflow.com/questions/64625473/django-channel-custom-authentication-middleware-call-missing-2-required-po) article that basically stated update routing.py to add .as_asgi() to my consumer
consumers.LivePostConsumer.as_asgi()),

It is to be fixed here:
`channels.testing.websocket.WebsocketCommunicator`

```
    def __init__(self, application, path, headers=None, subprotocols=None):
        if not isinstance(path, str):
            raise TypeError("Expected str, got {}".format(type(path)))
        parsed = urlparse(path)
        self.scope = {
            "type": "websocket",
            "path": unquote(parsed.path),
            "query_string": parsed.query.encode("utf-8"),
            "headers": headers or [],
            "subprotocols": subprotocols or [],
        }
        super().__init__(application, self.scope)
```

Scope should not be passed as described in the release notes: https://channels.readthedocs.io/en/latest/releases/3.0.0.html

```
Consumers are now ASGI 3 single-callables with the signature:

application(scope, receive, send)
For generic consumers this change should be largely transparent, but you will need to update __init__() (no longer taking the scope) and __call__() (now taking the scope) if you implemented these yourself.
```

Resolving patch:
diff --git a/channels/auth.py b/channels/auth.py
--- a/channels/auth.py
+++ b/channels/auth.py
@@ -23,7 +23,8 @@ def get_user(scope):
     Return the user model instance associated with the given scope.
     If no user is retrieved, return an instance of `AnonymousUser`.
     """
-    # postpone model import to avoid ImproperlyConfigured error before Django setup is complete.
+    # postpone model import to avoid ImproperlyConfigured error before Django
+    # setup is complete.
     from django.contrib.auth.models import AnonymousUser
 
     if "session" not in scope:
@@ -59,7 +60,8 @@ def login(scope, user, backend=None):
     """
     Persist a user id and a backend in the request.
     This way a user doesn't have to re-authenticate on every request.
-    Note that data set during the anonymous session is retained when the user logs in.
+    Note that data set during the anonymous session is retained when the user
+    logs in.
     """
     if "session" not in scope:
         raise ValueError(
@@ -112,9 +114,11 @@ def login(scope, user, backend=None):
 @database_sync_to_async
 def logout(scope):
     """
-    Remove the authenticated user's ID from the request and flush their session data.
+    Remove the authenticated user's ID from the request and flush their session
+    data.
     """
-    # postpone model import to avoid ImproperlyConfigured error before Django setup is complete.
+    # postpone model import to avoid ImproperlyConfigured error before Django
+    # setup is complete.
     from django.contrib.auth.models import AnonymousUser
 
     if "session" not in scope:
diff --git a/channels/generic/websocket.py b/channels/generic/websocket.py
--- a/channels/generic/websocket.py
+++ b/channels/generic/websocket.py
@@ -20,7 +20,6 @@ class WebsocketConsumer(SyncConsumer):
     groups = None
 
     def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
         if self.groups is None:
             self.groups = []
 
@@ -156,7 +155,6 @@ class AsyncWebsocketConsumer(AsyncConsumer):
     groups = None
 
     def __init__(self, *args, **kwargs):
-        super().__init__(*args, **kwargs)
         if self.groups is None:
             self.groups = []
 


Test patch: