Incorrect `bad-override` for APIView.permission_classes in Django REST Framework
Describe the Bug
Pyrefly reports a bad-override error when overriding permission_classes in Django REST Framework APIView subclasses.
Example:
from rest_framework.views import APIView
from rest_framework.permissions import BasePermission
class HasRolePermission(BasePermission):
allowed_roles = []
class IsAdmin(HasRolePermission):
allowed_roles = ["admin"]
class UserSearchAPIView(APIView):
permission_classes = [IsAdmin] # only admins can use it
class ProfileView(APIView):
permission_classes = [IsAuthenticated]
Pyrefly output:
ERROR Class member `UserSearchAPIView.permission_classes` overrides parent class `APIView` in an inconsistent manner [bad-override]
ERROR Class member `ProfileView.permission_classes` overrides parent class `APIView` in an inconsistent manner [bad-override]
`ProfileView.permission_classes` has type `list[type[IsAuthenticated]]`, which is not consistent with `bool | dict[str, str] | dict[str, None] | int | list[str] | list[Any] | list[Unknown] | str | Unknown | None` in `APIView.permission_classes` (the type of read-write attributes cannot be changed)
Django REST Framework allows permission_classes to be a list of permission classes (list[type[BasePermission]]), which is exactly what my code uses.
Sandbox Link
No response
(Only applicable for extension issues) IDE Information
No response
I think this is effectively a contextual typing problem: for attributes defined in class bodies, we currently will use the assigned expression's inferred type even if an ancestor has an explicit annotation.
Because lists are invariant, this causes a type error; we probably should rethink this.
In the meantime, you can probably work around the problem by adding an annotation list[Any] to the attribute giving you problems