mypy django rest framework - Unsupported left operand type when multiple permission classes are used

972 views Asked by At

I am integrating mypy on a existing codebase(using django, drf frameworks).

Sample code in view.py:

from rest_framework.permissions import IsAdminUser, IsAuthenticatedOrReadOnly

@api_view()
@permission_classes([IsAuthenticatedOrReadOnly | IsAdminUser])
def listAbc(request):
    queryset = ...
    serializer = ...
    return Response(serializer.data)

Result:

$ mypy
error: Unsupported left operand type for | ("Type[IsAuthenticatedOrReadOnly]")

Plugins used:

$ pip list | grep stubs
django-stubs                    1.2.0
djangorestframework-stubs       1.0.0

mypy configuration file (mypy.ini):

[mypy]

plugins =
    mypy_django_plugin.main, mypy_drf_plugin.main

;ignore_missing_imports = True
files=**/*.py

[mypy-*.migrations.*]
ignore_errors = True

[mypy.plugins.django-stubs]
django_settings_module = project.settings

Checked with both mypy (0.720 and 0.740).

What could be the issue here ? Since the operation '|' is not recognized by mypy, I suspect the metaclass BasePermissionMetaclass (containing the operations) is not added BasePermission during mypy evaluation. I assume just installing djangorestframework-stubs and configuring the same in mypy.ini is enough.

1

There are 1 answers

0
A_Safarzadeh On

The issue if because mypy doesn't understand the | operator being used between two permission classes in Django rest framework. This is a limitation of the mypy and type stubs for Django and DRF, atleast until now(probably gonna get solved some day).

The | operator is overloaded in DRF's BasePermission class to mean "or", allowing multiple permissions to be combined, But mypy doesn't recognize this operator overloading, so we have the error!

Instead of using the | operator, you can create a custom permission class that encapsulates the logic of the two permission classes. For example:

from rest_framework.permissions import BasePermission, IsAdminUser, IsAuthenticatedOrReadOnly

class CustomPermission(BasePermission):
    def has_permission(self, request, view):
        return IsAuthenticatedOrReadOnly().has_permission(request, view) or IsAdminUser().has_permission(request, view)

@api_view()
@permission_classes([CustomPermission])
def listAbc(request):
    queryset = ...
    serializer = ...
    return Response(serializer.data)

The question is like 4 years old, just came across and thought it may be helpful for someone searching it!