Skip to content

ninja.streaming.SSE and JSONL are not Generic[T] - mypy reports "type is not generic and not indexable" #1713

@adonig

Description

@adonig

Describe the bug

The streaming type containers introduced in v1.6 (ninja.streaming.SSE,
ninja.streaming.JSONL) support SSE[Schema] syntax at runtime via
__class_getitem__, but the underlying StreamFormat base class does not
inherit from typing.Generic. As a result, mypy (and any other type checker
that doesn't special-case __class_getitem__) rejects the documented usage:

error: The type "type[SSE]" is not generic and not indexable  [misc]

This forces users with strict mypy configurations to add # type: ignore[misc]
to every streaming endpoint, which weakens the type-safety story that
django-ninja otherwise has.

Versions

  • django-ninja: 1.6.2
  • mypy: 1.20.0
  • Python: 3.14

Reproduction

from ninja import NinjaAPI, Schema
from ninja.streaming import SSE

api = NinjaAPI()


class Event(Schema):
    message: str


@api.get("/events", response=SSE[Event])  # mypy error here
async def stream(request):
    yield {"message": "hello"}

Run mypy on the file:

error: The type "type[SSE]" is not generic and not indexable  [misc]

Runtime works correctly - only static type checking fails.

Root cause

In ninja/streaming.py:

class StreamFormat:
    media_type: str

    def __class_getitem__(cls, item_type: type) -> "_StreamAlias":
        return _StreamAlias(cls, item_type)

__class_getitem__ enables the runtime syntax, but mypy only treats a class
as subscriptable when it inherits from typing.Generic[T] (or one of the
special-cased stdlib generics).

Suggested fix

Make StreamFormat generic over the item type. The change is small and
shouldn't affect runtime behavior:

from typing import Generic, TypeVar

T = TypeVar("T")


class StreamFormat(Generic[T]):
    media_type: str
    # ... rest unchanged

SSE and JSONL then inherit the generic parameter automatically, and
SSE[Event] becomes a valid parameterized type for mypy.

Happy to send a PR if this approach is acceptable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions