summaryrefslogtreecommitdiff
path: root/tests/functional/a/assigning/assigning_non_slot.py
blob: e27af79ac7d6a24acff6184046887cc079ae6125 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
""" Checks assigning attributes not found in class slots
will trigger assigning-non-slot warning.
"""
# pylint: disable=too-few-public-methods, missing-docstring, import-error, redundant-u-string-prefix, unnecessary-dunder-call
# pylint: disable=attribute-defined-outside-init

from collections import deque

from missing import Unknown

class Empty:
    """ empty """

class Bad:
    """ missing not in slots. """

    __slots__ = ['member']

    def __init__(self):
        self.missing = 42 # [assigning-non-slot]

class Bad2:
    """ missing not in slots """
    __slots__ = [deque.__name__, 'member']

    def __init__(self):
        self.deque = 42
        self.missing = 42 # [assigning-non-slot]

class Bad3(Bad):
    """ missing not found in slots """

    __slots__ = ['component']

    def __init__(self):
        self.component = 42
        self.member = 24
        self.missing = 42 # [assigning-non-slot]
        super().__init__()

class Good(Empty):
    """ missing not in slots, but Empty doesn't
    specify __slots__.
    """
    __slots__ = ['a']

    def __init__(self):
        self.missing = 42

class Good2:
    """ Using __dict__ in slots will be safe. """

    __slots__ = ['__dict__', 'comp']

    def __init__(self):
        self.comp = 4
        self.missing = 5

class PropertyGood:
    """ Using properties is safe. """

    __slots__ = ['tmp', '_value']

    @property
    def test(self):
        return self._value

    @test.setter
    def test(self, value):
        # pylint: disable=attribute-defined-outside-init
        self._value = value

    def __init__(self):
        self.test = 42

class PropertyGood2:
    """ Using properties in the body of the class is safe. """
    __slots__ = ['_value']

    def _getter(self):
        return self._value

    def _setter(self, value):
        # pylint: disable=attribute-defined-outside-init
        self._value = value

    test = property(_getter, _setter)

    def __init__(self):
        self.test = 24

class UnicodeSlots:
    """Using unicode objects in __slots__ is okay.

    On Python 3.3 onward, u'' is equivalent to '',
    so this test should be safe for both versions.
    """
    __slots__ = (u'first', u'second')

    def __init__(self):
        self.first = 42
        self.second = 24


class DataDescriptor:
    def __init__(self, name, default=''):
        self.__name = name
        self.__default = default

    def __get__(self, inst, cls):
        return getattr(inst, self.__name, self.__default)

    def __set__(self, inst, value):
        setattr(inst, self.__name, value)


class NonDataDescriptor:
    def __get__(self, inst, cls):
        return 42


class SlotsWithDescriptor:
    __slots__ = ['_err']
    data_descriptor = DataDescriptor('_err')
    non_data_descriptor = NonDataDescriptor()
    missing_descriptor = Unknown()


def dont_emit_for_descriptors():
    inst = SlotsWithDescriptor()
    # This should not emit, because attr is
    # a data descriptor
    inst.data_descriptor = 'foo'
    inst.non_data_descriptor = 'lala'


class ClassWithSlots:
    __slots__ = ['foobar']


class ClassReassigningDunderClass:
    __slots__ = ['foobar']

    def release(self):
        self.__class__ = ClassWithSlots


class ClassReassingingInvalidLayoutClass:
    __slots__ = []

    def release(self):
        self.__class__ = ClassWithSlots  # [assigning-non-slot]
        self.test = 'test'  # [assigning-non-slot]


# pylint: disable=attribute-defined-outside-init
class ClassHavingUnknownAncestors(Unknown):
    __slots__ = ['yo']

    def test(self):
        self.not_yo = 42


# pylint: disable=wrong-import-order, wrong-import-position
from typing import (
    Generic,
    TypeVar,
)

TypeT = TypeVar('TypeT')


class Cls(Generic[TypeT]):
    """ Simple class with slots """
    __slots__ = ['value']

    def __init__(self, value):
        self.value = value


class ClassDefiningSetattr:
    __slots__ = ["foobar"]

    def __init__(self):
        self.foobar = {}

    def __setattr__(self, name, value):
        if name == "foobar":
            super().__setattr__(name, value)
        else:
            self.foobar[name] = value


class ClassWithParentDefiningSetattr(ClassDefiningSetattr):
    __slots__ = []


def dont_emit_for_defined_setattr():
    inst = ClassDefiningSetattr()
    # This should not emit because we can't reason about what happens with
    # classes defining __setattr__
    inst.non_existent = "non-existent"

    child = ClassWithParentDefiningSetattr()
    child.non_existent = "non-existent"

class ColorCls:
    __slots__ = ()
    COLOR = "red"


class Child(ColorCls):
    __slots__ = ()


repro = Child()
Child.COLOR = "blue"

class MyDescriptor:
    """Basic descriptor."""

    def __get__(self, instance, owner):
        return 42

    def __set__(self, instance, value):
        pass


# Regression test from https://github.com/pylint-dev/pylint/issues/6001
class Base:
    __slots__ = ()

    attr2 = MyDescriptor()


class Repro(Base):
    __slots__ = ()


repro = Repro()
repro.attr2 = "anything"