Pylint: False positive on "has no member" in django rest framework

Created on 3 Apr 2019  路  15Comments  路  Source: PyCQA/pylint

Steps to reproduce

  1. pip install django==2.1.7 djangorestframework==3.9.2 pylint-django==2.0.6
  2. using given file below, run pylint on it
from rest_framework import mixins


class ListModelMixin:  # Not used in the first test, skip it at first read.
    """
    List a queryset.
    """

    def list(self, request, *args, **kwargs):
        return 42


class ReadOnlyModelViewSet(mixins.RetrieveModelMixin, mixins.ListModelMixin):
    """
    A viewset that provides default `list()` and `retrieve()` actions.
    """


class UserViewSet(ReadOnlyModelViewSet):
    def list(self, request, *args, **kwargs):
        print("foo")
        return super().list(request, *args, **kwargs)

Current behavior

E1101: Super of 'UserViewSet' has no 'list' member (no-member)

Expected behavior

In the parents of ReadOnlyModelViewSet there's mixins.ListModelMixin which contains a list method, so I don't expect to get this error.

Strangely, if we remove "mixins" so it use the fake one I implemented above, the error dissapear. A bit like if pylint/astroid can't find the mixins module, or find a wrong one, or I don't know.

pylint --version output

pylint 2.3.1
astroid 2.2.5
Python 3.7.3rc1 (default, Mar 13 2019, 11:01:15) 
[GCC 8.3.0]

I'll gladly help a bit more on resolving this one, but I don't really know what can I check, is there some tools to see maybe what's pylint seeing? Only thing I checked is the right file is opened by the process:

```openat(AT_FDCWD, "/home/mdk/.venvs/3.7.3/lib/python3.7/site-packages/rest_framework/mixins.py", O_RDONLY|O_CLOEXEC) = 3

which contains literally:

class ListModelMixin(object):
"""
List a queryset.
"""
def list(self, request, args, *kwargs):
queryset = self.filter_queryset(self.get_queryset())

    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    serializer = self.get_serializer(queryset, many=True)
    return Response(serializer.data)

```

bug false-positive topic-inference

Most helpful comment

@teknodan you meant return super().test(), in method.

I'm able to reproduce it:

printf "class ClassA(object):\n    pass\n\nclass ClassB(object):\n    def test(self):\n        return True\n" > base.py
printf "import base\n\n\nclass ClassAB(base.ClassA, base.ClassB):\n    def method(self):\n        return super().test()  # E1101: Super of 'ClassAB' has no 'test' member (no-member)\n" > derived.py
cat base.py

```python
class ClassA(object):
pass

class ClassB(object):
def test(self):
return True

```bash
cat derived.py

```python
import base

class ClassAB(base.ClassA, base.ClassB):
def method(self):
return super().test() # E1101: Super of 'ClassAB' has no 'test' member (no-member)

```bash
pylint --version
pylint 2.3.1
astroid 2.2.5
Python 3.5.3 (default, Sep 27 2018, 17:25:39) 
[GCC 6.3.0 20170516]

```bash
python --version
Python 3.5.3

```bash
$ pylint derived.py 
************* Module derived
[...]
derived.py:5:15: E1101: Super of 'ClassAB' has no 'test' member (no-member)

Same with

$ pylint --version
pylint 2.6.0
astroid 2.4.2
Python 3.8.4 (default, Jul 14 2020, 09:37:22) 
[GCC 9.3.0]

All 15 comments

I have the same issue.

Django              2.2.3
djangorestframework 3.9.4
pylint-django       2.0.10
pylint              2.3.1

The code

class ExampleViewSet(ModelViewSet):
    def perform_destroy(self, instance):
        instance.file.delete(save=False)
        super().perform_destroy(instance)

The error message

pylint Error no-member: Super of 'SampleViewSet' has no 'perform_destroy' member

The code related

The definition of ModelViewSet is in rest_framework/viewsets.py.

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass

The method perform_destroy is in rest_framework/mixins.py:

class DestroyModelMixin(object):
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

Conclusion

The pylint is wrong.

This is not a django-specific problem.

It will occur in a situation where:

  • a class inherits from two base classes
  • the base classes are in a separate file from the derived class
  • the base _module_ is imported, rather than the specific classes (and the derived class is declared class ClassAB(module.ClassA, module.ClassB) and not class ClassAB(ClassA, ClassB) )
  • the method that cannot be found is in the second base class (Class B)

base.py

class ClassA(object):
    pass

class ClassB(object):
    def test(self):
        return True

derived.py

import base

class ClassAB(base.ClassA, base.ClassB):
    def method(self):
        super().test()  # E1101: Super of 'ClassAB' has no 'test' member (no-member)

Calling ClassAB().method() returns True, but pylint thinks the test() method is missing.

It occurs with super() and super(ClassAB, self). Also, no difference if the files are within a package or not.

Unclear if OS (tested on Fedora 29) or python version (tested on 3.7) affect the result.

$ pylint --version
pylint 2.3.1
astroid 2.2.5
Python 3.7.4 (default, Jul  9 2019, 16:48:28) 
[GCC 8.3.1 20190223 (Red Hat 8.3.1-2)]

@teknodan you meant return super().test(), in method.

I'm able to reproduce it:

printf "class ClassA(object):\n    pass\n\nclass ClassB(object):\n    def test(self):\n        return True\n" > base.py
printf "import base\n\n\nclass ClassAB(base.ClassA, base.ClassB):\n    def method(self):\n        return super().test()  # E1101: Super of 'ClassAB' has no 'test' member (no-member)\n" > derived.py
cat base.py

```python
class ClassA(object):
pass

class ClassB(object):
def test(self):
return True

```bash
cat derived.py

```python
import base

class ClassAB(base.ClassA, base.ClassB):
def method(self):
return super().test() # E1101: Super of 'ClassAB' has no 'test' member (no-member)

```bash
pylint --version
pylint 2.3.1
astroid 2.2.5
Python 3.5.3 (default, Sep 27 2018, 17:25:39) 
[GCC 6.3.0 20170516]

```bash
python --version
Python 3.5.3

```bash
$ pylint derived.py 
************* Module derived
[...]
derived.py:5:15: E1101: Super of 'ClassAB' has no 'test' member (no-member)

Same with

$ pylint --version
pylint 2.6.0
astroid 2.4.2
Python 3.8.4 (default, Jul 14 2020, 09:37:22) 
[GCC 9.3.0]

Thanks folks, I can reproduce the same issue. Did not manage yet to investigate why it's happening though, but looks like an inference bug.

can repro

inheriting from viewsets.ModelViewSet
def perform_destroy(self, instance):
...
return super().perform_destroy(instance)
has no 'perform_destroy' memberpylint(no-member)

I think I have the same issue, it is not related to Django but to yappi library, and there is no multiple inheritance, only a very straightforward call of the superclass method.

import yappi

stats = yappi.get_func_stats()

stats.save()
$ pylint -E yappi_test.py 
************* Module yappi_test
yappi_test.py:5:0: E1101: Instance of 'YStats' has no 'save' member (no-member)



md5-f356afe87508c0a68cab5dd7832513cf



pylint 2.4.4
astroid 2.3.3
Python 3.6.9 (default, Nov  7 2019, 10:44:02) 
[GCC 8.3.0]

Same problem here: inheriting from rest_framework.viewsets.ModelViewSet and overriding destroy leads to no-member error.

Same problem here.

class DebuggingAPIView(viewsets.ModelViewSet):
    queryset = Debugger.objects.all()
    serializer_class = DebuggerSerializer
    def create(self, request, *args, **kwargs):
        print("create method of debugging endpoint is called")
        return super(DebuggingAPIView, self).create(request, *args, **kwargs)

    def list(self, request, *args, **kwargs):
        print("list method of debugging endpoint is called")
        return super(DebuggingAPIView, self).list(request, *args, **kwargs)

    def retrieve(self, request, *args, **kwargs):
        print("retrieve method of debugging endpoint is called")
        return super(DebuggingAPIView, self).retrieve(request, *args, **kwargs)

    def update(self, request, *args, **kwargs):
        print("update method of debugging endpoint is called")
        return super(DebuggingAPIView, self).update(request, *args, **kwargs)

    def partial_update(self, request, *args, **kwargs):
        print("partial update method of debugging endpoint is called")
        return super(DebuggingAPIView, self).partial_update(request, *args, **kwargs)

    def destroy(self, request, *args, **kwargs):
        print("destroy method of debugging endpoint is called")
        return super(DebuggingAPIView, self).destroy(request, *args, **kwargs)

where class viewsets.ModelViewSet(mixins.CreateModelMixin, mixins.RetrieveModelMixin, ...):.

Pylint is failed to detect multiple inheritance and shows error for no-member.
Only the inheritance's method is fine (create in my case.)

@pistex thanks for the report. Do you think it is possible to have a simpler snippet that reproduces the bug without using django?

@hippo91 is https://github.com/PyCQA/pylint/issues/2854#issuecomment-520865608 the simpler snippet that reproduce it without external dependencies you need?

@JulienPalard that's it! Sorry for not having seen it. Thanks.

Having the same issue, was this not fixed?

@ahaq0 no, it's not fixed yet (issue is still open) and there's no PR to propose a fix. If you feel it try it :) the road is long, but the way is clear ;)

Duplicate of #3901

Do one of this issues should be closed?

@Randomneo thanks for noticing this.
In fact this issue should be fixed by PyCQA/astroid#844.
I'm closing it.

Was this page helpful?
0 / 5 - 0 ratings