<localInspection language="Python" shortName="PyAbstractClassInspection" suppressId="PyAbstractClass" displayName="Class must implement all abstract methods" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyAbstractClassInspection"/>
<localInspection language="Python" shortName="PyPep8NamingInspection" suppressId="PyPep8Naming" displayName="PEP 8 naming convention violation" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyPep8NamingInspection"/>
<localInspection language="Python" shortName="PyAssignmentToLoopOrWithParameterInspection" suppressId="PyAssignmentToLoopOrWithParameter" displayName="Assignment to 'for' loop or 'with' statement parameter" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyAssignmentToLoopOrWithParameterInspection"/>
-
+ <localInspection language="Python" shortName="PyDunderSlotsInspection" suppressId="PyDunderSlots" displayName="Definition of __slots__ in a class" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyDunderSlotsInspection"/>
<localInspection language="Python" shortName="PyMissingTypeHintsInspection" suppressId="PyMissingTypeHints" displayName="Missing type hinting for function definition" groupKey="INSP.GROUP.python" enabledByDefault="false" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyMissingTypeHintsInspection"/>
<defaultLiveTemplatesProvider implementation="com.jetbrains.python.codeInsight.liveTemplates.PyDefaultLiveTemplatesProvider"/>
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.inspections
+
+import com.intellij.codeInspection.LocalInspectionToolSession
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.psi.PsiElementVisitor
+import com.jetbrains.python.PyNames
+import com.jetbrains.python.psi.*
+import com.jetbrains.python.psi.impl.PyPsiUtils
+
+class PyDunderSlotsInspection : PyInspection() {
+
+ override fun buildVisitor(holder: ProblemsHolder,
+ isOnTheFly: Boolean,
+ session: LocalInspectionToolSession): PsiElementVisitor = Visitor(holder, session)
+
+ private class Visitor(holder: ProblemsHolder, session: LocalInspectionToolSession) : PyInspectionVisitor(holder, session) {
+
+ override fun visitPyClass(node: PyClass?) {
+ if (node != null && LanguageLevel.forElement(node).isAtLeast(LanguageLevel.PYTHON30)) {
+ val slots = findSlotsValue(node)
+
+ when (slots) {
+ is PySequenceExpression -> slots
+ .elements
+ .asSequence()
+ .filterIsInstance<PyStringLiteralExpression>()
+ .forEach {
+ processSlot(node, it)
+ }
+ is PyStringLiteralExpression -> processSlot(node, slots)
+ }
+ }
+ }
+
+ private fun findSlotsValue(pyClass: PyClass): PyExpression? {
+ val target = pyClass.findClassAttribute(PyNames.SLOTS, false, myTypeEvalContext) as? PyTargetExpression
+ val value = target?.findAssignedValue()
+
+ return PyPsiUtils.flattenParens(value)
+ }
+
+ private fun processSlot(pyClass: PyClass, slot: PyStringLiteralExpression) {
+ val name = slot.stringValue
+
+ if (pyClass.findClassAttribute(name, false, myTypeEvalContext) != null) {
+ registerProblem(slot, "'$name' in __slots__ conflicts with class variable")
+ }
+ }
+ }
+}
+
--- /dev/null
+# PY-20280: one slot in list
+class Foo(object):
+ __slots__ = [<warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>]
+ foo = 1
+
+
+# PY-20280: one slot in tuple
+class Foo(object):
+ __slots__ = (<warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>)
+ foo = 1
+
+
+# PY-20280: one slot
+class Foo(object):
+ __slots__ = <warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>
+ foo = 1
+
+
+# PY-20280: two slots in list
+class Foo(object):
+ __slots__ = [<warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>, 'bar']
+ foo = 1
+
+
+# PY-20280: two slots in tuple
+class Foo(object):
+ __slots__ = (<warning descr="'foo' in __slots__ conflicts with class variable">'foo'</warning>, 'bar')
+ foo = 1
+
+
+# PY-20280: slots in base and class variable in derived
+class Base(object):
+ __slots__ = 'foo'
+
+class Derived(Base):
+ foo = 1
+
+
+# PY-20280: class variable in base and slots in derived
+class Base(object):
+ foo = 1
+
+class Derived(Base):
+ __slots__ = 'foo'
+
+
+# PY-20280: slots in base and derived, class variable in derived
+class Base(object):
+ __slots__ = 'foo'
+
+class Derived(Base):
+ __slots__ = 'bar'
+ foo = 1
+
+
+# PY-20280: slots in base and derived, class variable in base
+class Base(object):
+ __slots__ = 'bar'
+ foo = 1
+
+class Derived(Base):
+ __slots__ = 'foo'
\ No newline at end of file