Skip to content

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

Open
@ghost

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]

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions