Python Django Channels выдает ошибку при запуске на компьютере коллег, но не на моем компьютере, что идет не так?

Я столкнулся со следующей ошибкой.

Странность этой ошибки в том, что она не возникает на моем компьютере с windows 11, а возникает только на компьютере windows 11 коллеги и на удаленном сервере debian linux, который я установил специально для того, чтобы проверить, будет ли подобная ошибка возникать и в Linux.

На всех компьютерах одинаковый код (git синхронизирован), одинаковые библиотеки (pip freeze и venv), одинаковая версия python (кроме linux-сервера)

Я не могу выяснить происхождение и не могу найти никакой информации о подобной ошибке на StackOverflow или любом другом сайте. Самое странное, что это работает на моем собственном компьютере!

Traceback (most recent call last):
  File "/usr/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.11/threading.py", line 975, in run
    self._target(*self._args, **self._kwargs)
  File "/srv/<redacted>/venv/lib/python3.11/site-packages/django/utils/autoreload.py", line 64, in wrapper
    fn(*args, **kwargs)
  File "/srv/<redacted>/venv/lib/python3.11/site-packages/daphne/management/commands/runserver.py", line 128, in inner_run
    application=self.get_application(options),
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/srv/<redacted>/venv/lib/python3.11/site-packages/daphne/management/commands/runserver.py", line 153, in get_application
    return ASGIStaticFilesHandler(get_default_application())
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/srv/<redacted>/venv/lib/python3.11/site-packages/daphne/management/commands/runserver.py", line 31, in get_default_application
    raise ImproperlyConfigured("Cannot import ASGI_APPLICATION module %r" % path)
django.core.exceptions.ImproperlyConfigured: Cannot import ASGI_APPLICATION module 'config.asgi'

Эта ошибка очень бесполезна, поэтому после дополнительной отладки мы выяснили, что причиной сбоя является:

ImportError: cannot import name 'DEFAULT_CHANNEL_LAYER' from 'channels'

Мой компьютер: Windows 11 - Python 3.12.1 (работает здесь)

Мой коллега: Windows 11 - Python 3.12.1 (здесь не работает)

Сервер Linux (текущая трассировка): Debian - Python 3.11.2 (здесь тоже не работает)

Вероятно, это не связано с несоответствием версии python, потому что у моего коллеги тоже есть такая проблема, и он использует ту же версию, что и я.

Я хотел бы добавить, что используемые каналы django - это относительно чистая установка. Поэтому я считаю, что проблема заключается в настройке. Мы также используем Django Rest Framework, Django Tenants & Django Tenant User в качестве основных библиотек.

Ошибка возникает только при использовании ASGI-сервера daphne вместо стандартного WSGI, и он падает только при импорте потребителя, в противном случае он не падает.

Релевантный код:

asgi.py

import os

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from django.core.asgi import get_asgi_application
from django.urls import path
from config.middleware import TokenAuthMiddleware


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')
# Initialize Django ASGI application early to ensure the AppRegistry
# is populated before importing code that may import ORM models.
django_asgi_app = get_asgi_application()

from apps.chats.consumers import MyWebSocketConsumer

application = ProtocolTypeRouter({
    "http": django_asgi_app,
    "websocket": TokenAuthMiddleware(
        URLRouter(
            [
                path("ws/test/", MyWebSocketConsumer.as_asgi()),
            ]
        )
    ),
})

config/middleware.py

from django.contrib.auth.models import AnonymousUser
from rest_framework.authtoken.models import Token
from channels.db import database_sync_to_async
from channels.middleware import BaseMiddleware

@database_sync_to_async
def get_user(token_key):
    try:
        token = Token.objects.get(key=token_key)
        return token.user
    except Token.DoesNotExist:
        return AnonymousUser()

class TokenAuthMiddleware(BaseMiddleware):
    def __init__(self, inner):
        super().__init__(inner)

    async def __call__(self, scope, receive, send):
        try:
            token_key = dict(scope['headers']).get(b'authorization').decode('ascii').split(" ")[1]
        except ValueError:
            token_key = None
        print("test3")
        print(token_key)
        scope['user'] = AnonymousUser() if token_key is None else await get_user(token_key)
        return await super().__call__(scope, receive, send)

apps/chats/consumers.py

import json
from channels.generic.websocket import JsonWebsocketConsumer
from django_tenants.utils import get_tenant_model

from apps.public.models import Domain
from django_tenants.utils import tenant_context

Tenant = get_tenant_model()

class MyWebSocketConsumer(JsonWebsocketConsumer):    
    def connect(self):
        user = self.scope['user']
        
        tenant_domain = dict(self.scope['headers']).get(b'host').decode('ascii').split(":")[0]
        tenant_domain_obj = Domain.objects.get(domain=tenant_domain)
        tenant = Tenant.objects.filter(domains=tenant_domain_obj).first()
        
        with tenant_context(tenant):
            if user.is_authenticated:
                user_chats = user.chats_followed.all() 
                                
                self.groups = [f"chat_{chat.id}" for chat in user_chats]

                # Subscribe the WebSocket connection to all the user's chat groups
                for group_name in self.groups:
                    self.channel_layer.group_add(group_name, self.channel_name)

                # Accept the WebSocket connection
                self.accept()

    def disconnect(self, close_code):
        # Remove the WebSocket connection from the group
        for group_name in self.groups:
            self.channel_layer.group_discard(group_name, self.channel_name)

    def receive_json(self, content):
        # This is called when a message is received from the client
        message = content.get("message", "")
        chat_id = content.get("chat_id")
        
        print(message)

        # Broadcast the message to the specific chat group
        if chat_id:
            group_name = f"chat_{chat_id}"
            self.channel_layer.group_send(
                group_name,
                {
                    "type": "chat_message",
                    "chat_id": chat_id,
                    "message": message,
                }
            )

    def chat_message(self, event):
        message = event['message']

        # Send the message to the WebSocket
        self.send(text_data=json.dumps({"message": message}))

Я сам в полном недоумении, почему на моем компьютере она работает, а на чужом - нет.

  • Я проверил переменные окружения (они в порядке)
  • Я проверил версию python
  • Я проверил установленные пакеты
  • Я проверил версии ОС

все одно и то же, что идет не так.

Вернуться на верх