Skip to content

Decorator false positive: unsubscriptable-object, no-member, unsupported-membership-test, not-an-iterable, too-many-function-args #1694

Open

Description

Steps to reproduce

I use simple classproperty decorator:

class classproperty(classmethod):

    def __init__(self, fget):
        if isinstance(fget, (classmethod, staticmethod)):
            self.fget = lambda cls: fget.__get__(None, cls)()
        else:
            self.fget = fget
        self.cached = {}
        super(classproperty, self).__init__(self.fget)

    def __get__(self, instance, cls):
        if cls in self.cached:
            return self.cached[cls]
        value = self.cached[cls] = self.fget(cls)
        return value

Example of usages:

from collections import OrderedDict

from sqlalchemy.ext.declarative import declarative_base, declared_attr
from sqlalchemy.inspection import inspect

Base = declarative_base()

class Model(Base):
    __abstract__ = True

    def __init__(self, **kwargs):
        super(Model, self).__init__()
        self.set_attrs(**kwargs)

    def set_attrs(self, **attrs):
        for attr_name, attr_value in attrs.items():
            if attr_name in self.fields:
                setattr(self, attr_name, attr_value)

    def to_dict(self):
        result = {}
        for attr_name in self.fields:
            attr_value = getattr(self, attr_name)
            if attr_value is not None:
                result[attr_name] = attr_value
        return result

    @declared_attr
    def __tablename__(cls):
        # pylint: disable=no-self-argument
        return plural(decapitalize(cls.__name__))

    @classproperty
    def columns(cls):
        columns = inspect(cls).mapper.column_attrs
        columns = list(sorted(columns, key=lambda column: column.key))
        return columns

    @classproperty
    def fields(cls):
        fields = OrderedDict([(column.key, getattr(cls, column.key))
                              for column in cls.columns])
        return fields

    @classproperty
    def primary_key_columns(cls):
        columns = list(inspect(cls).mapper.primary_key)
        return columns

    @classproperty
    def primary_key_fields(cls):
        fields = OrderedDict([(column.key, getattr(cls, column.key))
                              for column in cls.primary_key_columns])
        return fields

Current behavior

There are a lot of false positive errors:

[E1135(unsupported-membership-test), Model.set_attrs] Value 'self.fields' doesn't support membership test
[E1133(not-an-iterable), Model.to_dict] Non-iterable value self.fields is used in an iterating context
[E1133(not-an-iterable), Model.fields] Non-iterable value cls.columns is used in an iterating context
[E1133(not-an-iterable), Model.primary_key_fields] Non-iterable value cls.primary_key_columns is used in an iterating context

Expected behavior

If I make interface class where declare expected value the errors are not raised:

class iface(object):
    columns = ()
    fields = {}
    primary_key_columns = ()
    primary_key_fields = {}

class Model(Base, iface):
    # ...

Expected behavior: no false-positive errors

pylint --version output

pylint 1.7.4,
astroid 1.5.3
Python 3.5.2 (default, Sep 14 2017, 22:51:06)
[GCC 5.4.0 20160609]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions