Skip to content

Commit 2edbaf3

Browse files
committed
Django channels stubs
1 parent a4d3f6f commit 2edbaf3

27 files changed

+615
-0
lines changed

pyrightconfig.stricter.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"stubs/braintree",
3434
"stubs/caldav",
3535
"stubs/cffi",
36+
"stubs/channels",
3637
"stubs/click-web",
3738
"stubs/corus",
3839
"stubs/dateparser",

stubs/channels/METADATA.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
version = "4.*"
2+
upstream_repository = "https://github.com/django/channels"
3+
requires = ["django-stubs", "asgiref"]
4+
requires_python = ">=3.10"
5+
6+
[tool.stubtest]
7+
skip = true # due to the need of django mypy plugin config, it should be skipped.
8+
stubtest_requirements = ["daphne"]

stubs/channels/channels/__init__.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
__version__: str
2+
DEFAULT_CHANNEL_LAYER: str

stubs/channels/channels/apps.pyi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from django.apps import AppConfig
2+
from django.utils.functional import _StrOrPromise
3+
4+
class ChannelsConfig(AppConfig):
5+
name: str = ...
6+
verbose_name: _StrOrPromise = ...

stubs/channels/channels/auth.pyi

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from typing import Any
2+
3+
from asgiref.typing import ASGIReceiveCallable, ASGISendCallable
4+
from channels.middleware import BaseMiddleware
5+
from django.contrib.auth.backends import BaseBackend
6+
from django.contrib.auth.base_user import AbstractBaseUser
7+
from django.contrib.auth.models import AnonymousUser
8+
from django.utils.functional import LazyObject
9+
10+
from .consumer import _ChannelScope, _LazySession
11+
from .db import database_sync_to_async
12+
from .utils import _ChannelApplication
13+
14+
@database_sync_to_async
15+
def get_user(scope: _ChannelScope) -> AbstractBaseUser | AnonymousUser: ...
16+
@database_sync_to_async
17+
def login(scope: _ChannelScope, user: AbstractBaseUser, backend: BaseBackend | None = ...) -> None: ...
18+
@database_sync_to_async
19+
def logout(scope: _ChannelScope) -> None: ...
20+
def _get_user_session_key(session: _LazySession) -> Any: ...
21+
22+
class UserLazyObject(AbstractBaseUser, LazyObject):
23+
def _setup(self) -> None: ...
24+
25+
class AuthMiddleware(BaseMiddleware):
26+
def populate_scope(self, scope: _ChannelScope) -> None: ...
27+
async def resolve_scope(self, scope: _ChannelScope) -> None: ...
28+
async def __call__(
29+
self, scope: _ChannelScope, receive: ASGIReceiveCallable, send: ASGISendCallable
30+
) -> _ChannelApplication: ...
31+
32+
def AuthMiddlewareStack(inner: _ChannelApplication) -> _ChannelApplication: ...

stubs/channels/channels/consumer.pyi

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from collections.abc import Awaitable
2+
from typing import Any, ClassVar, Protocol
3+
4+
from asgiref.typing import ASGIReceiveCallable, ASGISendCallable, Scope, WebSocketScope
5+
from channels.auth import UserLazyObject
6+
from channels.db import database_sync_to_async
7+
from django.contrib.sessions.backends.base import SessionBase
8+
from django.utils.functional import LazyObject
9+
10+
class _LazySession(SessionBase, LazyObject): # type: ignore[misc]
11+
_wrapped: SessionBase
12+
13+
# Base ASGI Scope definition
14+
class _ChannelScope(WebSocketScope, total=False):
15+
# Channel specific
16+
channel: str
17+
url_route: dict[str, Any]
18+
path_remaining: str
19+
20+
# Auth specific
21+
cookies: dict[str, str]
22+
session: _LazySession
23+
user: UserLazyObject | None
24+
25+
def get_handler_name(message: dict[str, Any]) -> str: ...
26+
27+
class _ASGIApplicationProtocol(Protocol):
28+
consumer_class: Any
29+
consumer_initkwargs: dict[str, Any]
30+
31+
def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> Awaitable[None]: ...
32+
33+
class AsyncConsumer:
34+
_sync: ClassVar[bool] = ...
35+
channel_layer_alias: ClassVar[str] = ...
36+
37+
scope: _ChannelScope
38+
channel_layer: Any
39+
channel_name: str
40+
channel_receive: ASGIReceiveCallable
41+
base_send: ASGISendCallable
42+
43+
async def __call__(self, scope: _ChannelScope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None: ...
44+
async def dispatch(self, message: dict[str, Any]) -> None: ...
45+
async def send(self, message: dict[str, Any]) -> None: ...
46+
@classmethod
47+
def as_asgi(cls, **initkwargs: Any) -> _ASGIApplicationProtocol: ...
48+
49+
class SyncConsumer(AsyncConsumer):
50+
_sync: ClassVar[bool] = ...
51+
52+
@database_sync_to_async
53+
def dispatch(self, message: dict[str, Any]) -> None: ... # type: ignore[override]
54+
def send(self, message: dict[str, Any]) -> None: ... # type: ignore[override]

stubs/channels/channels/db.pyi

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from asyncio import BaseEventLoop
2+
from collections.abc import Callable, Coroutine
3+
from typing import Any, TypeVar
4+
from typing_extensions import ParamSpec
5+
6+
from asgiref.sync import SyncToAsync
7+
8+
_P = ParamSpec("_P")
9+
_R = TypeVar("_R")
10+
11+
class DatabaseSyncToAsync(SyncToAsync[_P, _R]):
12+
def thread_handler(self, loop: BaseEventLoop, *args: Any, **kwargs: Any) -> Any: ...
13+
14+
def database_sync_to_async(func: Callable[_P, _R]) -> Callable[_P, Coroutine[Any, Any, _R]]: ...
15+
async def aclose_old_connections() -> None: ...
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
class RequestAborted(Exception): ...
2+
class RequestTimeout(RequestAborted): ...
3+
class InvalidChannelLayerError(ValueError): ...
4+
class AcceptConnection(Exception): ...
5+
class DenyConnection(Exception): ...
6+
class ChannelFull(Exception): ...
7+
class MessageTooLarge(Exception): ...
8+
class StopConsumer(Exception): ...

stubs/channels/channels/generic/__init__.pyi

Whitespace-only changes.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from collections.abc import Iterable
2+
from typing import Any
3+
4+
from asgiref.typing import HTTPDisconnectEvent, HTTPRequestEvent, HTTPScope
5+
from channels.consumer import AsyncConsumer
6+
7+
class AsyncHttpConsumer(AsyncConsumer):
8+
body: list[bytes]
9+
scope: HTTPScope # type: ignore[assignment]
10+
11+
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
12+
async def send_headers(self, *, status: int = ..., headers: Iterable[tuple[bytes, bytes]] | None = ...) -> None: ...
13+
async def send_body(self, body: bytes, *, more_body: bool = ...) -> None: ...
14+
async def send_response(self, status: int, body: bytes, **kwargs: Any) -> None: ...
15+
async def handle(self, body: bytes) -> None: ...
16+
async def disconnect(self) -> None: ...
17+
async def http_request(self, message: HTTPRequestEvent) -> None: ...
18+
async def http_disconnect(self, message: HTTPDisconnectEvent) -> None: ...
19+
async def send(self, message: dict[str, Any]) -> None: ... # type: ignore[override]
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from typing import Any
2+
3+
from asgiref.typing import WebSocketConnectEvent, WebSocketDisconnectEvent, WebSocketReceiveEvent
4+
from channels.consumer import AsyncConsumer, SyncConsumer, _ChannelScope
5+
6+
class WebsocketConsumer(SyncConsumer):
7+
groups: list[str] | None
8+
scope: _ChannelScope
9+
channel_name: str
10+
channel_layer: Any
11+
channel_receive: Any
12+
base_send: Any
13+
14+
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
15+
def websocket_connect(self, message: WebSocketConnectEvent) -> None: ...
16+
def connect(self) -> None: ...
17+
def accept(self, subprotocol: str | None = ..., headers: list[tuple[str, str]] | None = ...) -> None: ...
18+
def websocket_receive(self, message: WebSocketReceiveEvent) -> None: ...
19+
def receive(self, text_data: str | None = ..., bytes_data: bytes | None = ...) -> None: ...
20+
def send( # type: ignore[override]
21+
self, text_data: str | None = ..., bytes_data: bytes | None = ..., close: bool = ...
22+
) -> None: ...
23+
def close(self, code: int | bool | None = ..., reason: str | None = ...) -> None: ...
24+
def websocket_disconnect(self, message: WebSocketDisconnectEvent) -> None: ...
25+
def disconnect(self, code: int) -> None: ...
26+
27+
class JsonWebsocketConsumer(WebsocketConsumer):
28+
def receive(self, text_data: str | None = ..., bytes_data: bytes | None = ..., **kwargs: Any) -> None: ...
29+
def receive_json(self, content: Any, **kwargs: Any) -> None: ...
30+
def send_json(self, content: Any, close: bool = ...) -> None: ...
31+
@classmethod
32+
def decode_json(cls, text_data: str) -> Any: ...
33+
@classmethod
34+
def encode_json(cls, content: Any) -> str: ...
35+
36+
class AsyncWebsocketConsumer(AsyncConsumer):
37+
groups: list[str] | None
38+
scope: _ChannelScope
39+
channel_name: str
40+
channel_layer: Any
41+
channel_receive: Any
42+
base_send: Any
43+
44+
def __init__(self, *args: Any, **kwargs: Any) -> None: ...
45+
async def websocket_connect(self, message: WebSocketConnectEvent) -> None: ...
46+
async def connect(self) -> None: ...
47+
async def accept(self, subprotocol: str | None = ..., headers: list[tuple[str, str]] | None = ...) -> None: ...
48+
async def websocket_receive(self, message: WebSocketReceiveEvent) -> None: ...
49+
async def receive(self, text_data: str | None = ..., bytes_data: bytes | None = ...) -> None: ...
50+
async def send( # type: ignore[override]
51+
self, text_data: str | None = ..., bytes_data: bytes | None = ..., close: bool = ...
52+
) -> None: ...
53+
async def close(self, code: int | bool | None = ..., reason: str | None = ...) -> None: ...
54+
async def websocket_disconnect(self, message: WebSocketDisconnectEvent) -> None: ...
55+
async def disconnect(self, code: int) -> None: ...
56+
57+
class AsyncJsonWebsocketConsumer(AsyncWebsocketConsumer):
58+
async def receive(self, text_data: str | None = ..., bytes_data: bytes | None = ..., **kwargs: Any) -> None: ...
59+
async def receive_json(self, content: Any, **kwargs: Any) -> None: ...
60+
async def send_json(self, content: Any, close: bool = ...) -> None: ...
61+
@classmethod
62+
async def decode_json(cls, text_data: str) -> Any: ...
63+
@classmethod
64+
async def encode_json(cls, content: Any) -> str: ...

stubs/channels/channels/layers.pyi

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import asyncio
2+
from re import Pattern
3+
from typing import Any, TypeAlias, overload
4+
from typing_extensions import deprecated
5+
6+
class ChannelLayerManager:
7+
backends: dict[str, BaseChannelLayer]
8+
9+
def __init__(self) -> None: ...
10+
def _reset_backends(self, setting: str, **kwargs: Any) -> None: ...
11+
@property
12+
def configs(self) -> dict[str, Any]: ...
13+
def make_backend(self, name: str) -> BaseChannelLayer: ...
14+
def make_test_backend(self, name: str) -> Any: ...
15+
def _make_backend(self, name: str, config: dict[str, Any]) -> BaseChannelLayer: ...
16+
def __getitem__(self, key: str) -> BaseChannelLayer: ...
17+
def __contains__(self, key: str) -> bool: ...
18+
def set(self, key: str, layer: BaseChannelLayer) -> BaseChannelLayer | None: ...
19+
20+
_ChannelCapacityPattern: TypeAlias = Pattern[str] | str
21+
_ChannelCapacityDict: TypeAlias = dict[_ChannelCapacityPattern, int]
22+
_CompiledChannelCapacity: TypeAlias = list[tuple[Pattern[str], int]]
23+
24+
class BaseChannelLayer:
25+
MAX_NAME_LENGTH: int = ...
26+
expiry: int
27+
capacity: int
28+
channel_capacity: _ChannelCapacityDict
29+
channel_name_regex: Pattern[str]
30+
group_name_regex: Pattern[str]
31+
invalid_name_error: str
32+
33+
def __init__(self, expiry: int = ..., capacity: int = ..., channel_capacity: _ChannelCapacityDict | None = ...) -> None: ...
34+
def compile_capacities(self, channel_capacity: _ChannelCapacityDict) -> _CompiledChannelCapacity: ...
35+
def get_capacity(self, channel: str) -> int: ...
36+
@overload
37+
def match_type_and_length(self, name: str) -> bool: ...
38+
@overload
39+
def match_type_and_length(self, name: Any) -> bool: ...
40+
@overload
41+
def require_valid_channel_name(self, name: str, receive: bool = ...) -> bool: ...
42+
@overload
43+
def require_valid_channel_name(self, name: Any, receive: bool = ...) -> bool: ...
44+
@overload
45+
def require_valid_group_name(self, name: str) -> bool: ...
46+
@overload
47+
def require_valid_group_name(self, name: Any) -> bool: ...
48+
@overload
49+
def valid_channel_names(self, names: list[str], receive: bool = ...) -> bool: ...
50+
@overload
51+
def valid_channel_names(self, names: list[Any], receive: bool = ...) -> bool: ...
52+
def non_local_name(self, name: str) -> str: ...
53+
async def send(self, channel: str, message: dict[str, Any]) -> None: ...
54+
async def receive(self, channel: str) -> dict[str, Any]: ...
55+
async def new_channel(self) -> str: ...
56+
async def flush(self) -> None: ...
57+
async def group_add(self, group: str, channel: str) -> None: ...
58+
async def group_discard(self, group: str, channel: str) -> None: ...
59+
async def group_send(self, group: str, message: dict[str, Any]) -> None: ...
60+
@deprecated("Use require_valid_channel_name instead.")
61+
def valid_channel_name(self, channel_name: str, receive: bool = ...) -> bool: ...
62+
@deprecated("Use require_valid_group_name instead.")
63+
def valid_group_name(self, group_name: str) -> bool: ...
64+
65+
_InMemoryQueueData: TypeAlias = tuple[float, dict[str, Any]]
66+
67+
class InMemoryChannelLayer(BaseChannelLayer):
68+
channels: dict[str, asyncio.Queue[_InMemoryQueueData]]
69+
groups: dict[str, dict[str, float]]
70+
group_expiry: int
71+
72+
def __init__(
73+
self,
74+
expiry: int = ...,
75+
group_expiry: int = ...,
76+
capacity: int = ...,
77+
channel_capacity: _ChannelCapacityDict | None = ...,
78+
**kwargs: Any,
79+
) -> None: ...
80+
81+
extensions: list[str]
82+
83+
async def send(self, channel: str, message: dict[str, Any]) -> None: ...
84+
async def receive(self, channel: str) -> dict[str, Any]: ...
85+
async def new_channel(self, prefix: str = ...) -> str: ...
86+
def _clean_expired(self) -> None: ...
87+
async def flush(self) -> None: ...
88+
async def close(self) -> None: ...
89+
def _remove_from_groups(self, channel: str) -> None: ...
90+
async def group_add(self, group: str, channel: str) -> None: ...
91+
async def group_discard(self, group: str, channel: str) -> None: ...
92+
async def group_send(self, group: str, message: dict[str, Any]) -> None: ...
93+
94+
def get_channel_layer(alias: str = ...) -> BaseChannelLayer | None: ...
95+
96+
channel_layers: ChannelLayerManager

stubs/channels/channels/management/__init__.pyi

Whitespace-only changes.

stubs/channels/channels/management/commands/__init__.pyi

Whitespace-only changes.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import logging
2+
from argparse import ArgumentParser
3+
from typing import Any
4+
5+
from channels.layers import BaseChannelLayer
6+
from channels.worker import Worker
7+
from django.core.management.base import BaseCommand
8+
9+
logger: logging.Logger
10+
11+
class Command(BaseCommand):
12+
leave_locale_alone: bool = ...
13+
worker_class: type[Worker] = ...
14+
verbosity: int
15+
channel_layer: BaseChannelLayer
16+
17+
def add_arguments(self, parser: ArgumentParser) -> None: ...
18+
def handle(
19+
self, *args: Any, application_path: str | None = ..., channels: list[str] | None = ..., layer: str = ..., **options: Any
20+
) -> None: ...
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from asgiref.typing import ASGIReceiveCallable, ASGISendCallable
2+
3+
from .consumer import _ChannelScope
4+
from .utils import _ChannelApplication
5+
6+
class BaseMiddleware:
7+
inner: _ChannelApplication
8+
9+
def __init__(self, inner: _ChannelApplication) -> None: ...
10+
async def __call__(
11+
self, scope: _ChannelScope, receive: ASGIReceiveCallable, send: ASGISendCallable
12+
) -> _ChannelApplication: ...

stubs/channels/channels/routing.pyi

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from typing import Any
2+
3+
from asgiref.typing import ASGIReceiveCallable, ASGISendCallable
4+
from django.urls.resolvers import URLPattern
5+
6+
from .consumer import _ASGIApplicationProtocol, _ChannelScope
7+
from .utils import _ChannelApplication
8+
9+
def get_default_application() -> ProtocolTypeRouter: ...
10+
11+
class ProtocolTypeRouter:
12+
application_mapping: dict[str, _ChannelApplication]
13+
14+
def __init__(self, application_mapping: dict[str, Any]) -> None: ...
15+
async def __call__(self, scope: _ChannelScope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None: ...
16+
17+
class _ExtendedURLPattern(URLPattern):
18+
callback: _ASGIApplicationProtocol | URLRouter
19+
20+
class URLRouter:
21+
_path_routing: bool = ...
22+
routes: list[_ExtendedURLPattern | URLRouter]
23+
24+
def __init__(self, routes: list[_ExtendedURLPattern | URLRouter]) -> None: ...
25+
async def __call__(self, scope: _ChannelScope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None: ...
26+
27+
class ChannelNameRouter:
28+
application_mapping: dict[str, _ChannelApplication]
29+
30+
def __init__(self, application_mapping: dict[str, _ChannelApplication]) -> None: ...
31+
async def __call__(self, scope: _ChannelScope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None: ...

stubs/channels/channels/security/__init__.pyi

Whitespace-only changes.

0 commit comments

Comments
 (0)