Search code examples
pythonpylint

Pylint warning `W0212` with properties accessing a protected member: how to avoid?


Pylint warns for suspicious access to object's protected members. It know how to not warn when the access is from the object it‑self, however does not know how to not warn when the access is from a property of the object.

Ex.

class C(object):

    def __init__(self):
        C.__a = 0

    a = property(lambda self: self.__a)

Pylint tells “W0212 (protected-access): Access to a protected member __a of a client class”

I don't want to globally disable W0212 and I am not happy with repeatedly disabling it locally (*) for each such property definition.

Is there a known way to work around this?

(*) As in:

class C(object):

    def __init__(self):
        C.__a = 0

    a = property(lambda self: self.__a)  # pylint: disable=W0212

Margin notes

As an interesting side note, the answer I selected presents an additional benefit with the actual Pylint (may change in future versions, I can't tell): it preserves Pylint ability to check for non‑existent members, as this test shows:

class C1(object):

    member = 0


class C2(object):

    def __init__(self):
        self.__a = C1()

    def a(self):
        return self.__a

    @property
    def b(self):
        return self.__a

    c = property(lambda self: self.__a)


def test_member():

    o = C2()

    print(o.a().member)
    print(o.b.member)
    print(o.c.member)


def test_nonexistent():

    o = C2()

    print(o.a().nonexistent)
    print(o.b.nonexistent)
    print(o.c.nonexistent)

You will get a warning for print(o.a().nonexistent) and print(o.b.nonexistent) but not for print(o.c.nonexistent).


Solution

  • Seems to me that you could use a decorator and the linter probably wouldn't complain:

    class C(object):
    
        def __init__(self):
            self.__a = 0
    
        @property
        def a(self):
            return self.__a
    
        # You can use the decorator to create setters too...
        @a.setter
        def a(self, value):
            self.__a = value
    

    because then the linter can easily identify a as a method on the class. Otherwise, you're pretty much stuck with the options you listed. You can either disable a warning globally or locally or not at all -- There's no way to disable the warning only in a specific context as far as I know.