[PATCH 2/5] Remove our extra pylint checkers.
Chris Lumens
clumens at redhat.com
Thu Jun 4 17:16:01 UTC 2015
These are moving off into their own package that can be shared amongst our
various projects, so there's no need to keep them in anaconda.
---
tests/pylint/eintr.py | 65 ----------
tests/pylint/environ.py | 107 ----------------
tests/pylint/intl.py | 148 ----------------------
tests/pylint/markup.py | 198 -----------------------------
tests/pylint/pointless-override.py | 252 -------------------------------------
5 files changed, 770 deletions(-)
delete mode 100644 tests/pylint/eintr.py
delete mode 100644 tests/pylint/environ.py
delete mode 100644 tests/pylint/intl.py
delete mode 100644 tests/pylint/markup.py
delete mode 100644 tests/pylint/pointless-override.py
diff --git a/tests/pylint/eintr.py b/tests/pylint/eintr.py
deleted file mode 100644
index 8fae96c..0000000
--- a/tests/pylint/eintr.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# Interuptible system call pylint module
-#
-# Copyright (C) 2014 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use,
-# modify, copy, or redistribute it subject to the terms and conditions of
-# the GNU General Public License v.2, or (at your option) any later version.
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY expressed or implied, including the implied warranties of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details. You should have received a copy of the
-# GNU General Public License along with this program; if not, write to the
-# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
-# source code or documentation are not subject to the GNU General Public
-# License and may only be used or replicated with the express permission of
-# Red Hat, Inc.
-#
-# Red Hat Author(s): David Shea <dhsea at redhat.com>
-#
-
-import astroid
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import check_messages, safe_infer
-from pylint.interfaces import IAstroidChecker
-
-import os
-
-# These are all of the system calls exposed through the os module that are
-# documented in SUSv4 as *may* set EINTR. Some of them probably don't in Linux,
-# but who knows. lchmod, wait3 and wait4 aren't documented much anywhere but
-# are here just in case.
-interruptible = ("tmpfile", "close", "dup2", "fchmod", "fchown", "fstatvfs",
- "fsync", "ftruncate", "open", "read", "write", "fchdir",
- "chmod", "chown", "lchmod", "lchown", "statvfs", "wait",
- "waitpid", "wait3", "wait4")
-
-class EintrChecker(BaseChecker):
- __implements__ = (IAstroidChecker,)
- name = "retry-interruptible"
- msgs = {"W9930" : ("Found interruptible system call %s",
- "interruptible-system-call",
- "A system call that may raise EINTR is not wrapped in eintr_retry_call"),
- }
-
- @check_messages("interruptible-system-call")
- def visit_callfunc(self, node):
- if not isinstance(node, astroid.CallFunc):
- return
-
- # Skip anything not a function or not in os. os redirects most of its
- # content to an OS-dependent module, named in os.name, so check that
- # one too.
- function_node = safe_infer(node.func)
- if not isinstance(function_node, astroid.Function) or \
- function_node.root().name not in ("os", os.name):
- return
-
- if function_node.name in interruptible:
- self.add_message("interruptible-system-call", node=node, args=function_node.name)
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(EintrChecker(linter))
diff --git a/tests/pylint/environ.py b/tests/pylint/environ.py
deleted file mode 100644
index ffbed49..0000000
--- a/tests/pylint/environ.py
+++ /dev/null
@@ -1,107 +0,0 @@
-# setenv pylint module
-#
-# Copyright (C) 2015 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use,
-# modify, copy, or redistribute it subject to the terms and conditions of
-# the GNU General Public License v.2, or (at your option) any later version.
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY expressed or implied, including the implied warranties of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details. You should have received a copy of the
-# GNU General Public License along with this program; if not, write to the
-# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
-# source code or documentation are not subject to the GNU General Public
-# License and may only be used or replicated with the express permission of
-# Red Hat, Inc.
-#
-# Red Hat Author(s): David Shea <dshea at redhat.com>
-#
-
-import astroid
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import check_messages, safe_infer
-from pylint.interfaces import IAstroidChecker
-
-import os
-
-class EnvironChecker(BaseChecker):
- __implements__ = (IAstroidChecker,)
- name = "environ"
- msgs = {"W9940" : ("Found potentially unsafe modification of environment",
- "environment-modify",
- "Potentially thread-unsafe modification of environment")}
-
- def _is_environ(self, node):
- # Guess whether a node being modified is os.environ
-
- if isinstance(node, astroid.Getattr):
- if node.attrname == "environ":
- expr_node = safe_infer(node.expr)
- if isinstance(expr_node, astroid.Module) and expr_node.name == "os":
- return True
-
- # If the node being modified is just "environ" assume that it's os.environ
- if isinstance(node, astroid.Name):
- if node.name == "environ":
- return True
-
- return False
-
- @check_messages("environment-modify")
- def visit_assign(self, node):
- if not isinstance(node, astroid.Assign):
- return
-
- # Look for os.environ["WHATEVER"] = something
- for target in node.targets:
- if not isinstance(target, astroid.Subscript):
- continue
-
- if self._is_environ(target.value):
- self.add_message("environment-modify", node=node)
-
- @check_messages("environment-modify")
- def visit_callfunc(self, node):
- # Check both for uses of os.putenv and os.setenv and modifying calls
- # to the os.environ object, such as os.environ.update
-
- if not isinstance(node, astroid.CallFunc):
- return
-
- function_node = safe_infer(node.func)
- if not isinstance(function_node, (astroid.Function, astroid.BoundMethod)):
- return
-
- # If the function is from the os or posix modules, look for calls that
- # modify the environment
- if function_node.root().name in ("os", os.name) and \
- function_node.name in ("putenv", "unsetenv"):
- self.add_message("environment-modify", node=node)
-
- # Look for methods bound to the environ dict
- if isinstance(function_node, astroid.BoundMethod) and \
- isinstance(function_node.bound, astroid.Dict) and \
- function_node.bound.root().name in ("os", os.name) and \
- function_node.bound.name == "environ" and \
- function_node.name in ("clear", "pop", "popitem", "setdefault", "update"):
- self.add_message("environment-modify", node=node)
-
- @check_messages("environment-modify")
- def visit_delete(self, node):
- if not isinstance(node, astroid.Delete):
- return
-
- # Look for del os.environ["WHATEVER"]
- for target in node.targets:
- if not isinstance(target, astroid.Subscript):
- continue
-
- if self._is_environ(target.value):
- self.add_message("environment-modify", node=node)
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(EnvironChecker(linter))
diff --git a/tests/pylint/intl.py b/tests/pylint/intl.py
deleted file mode 100644
index ac27bb6..0000000
--- a/tests/pylint/intl.py
+++ /dev/null
@@ -1,148 +0,0 @@
-# I18N-related pylint module
-#
-# Copyright (C) 2013 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use,
-# modify, copy, or redistribute it subject to the terms and conditions of
-# the GNU General Public License v.2, or (at your option) any later version.
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY expressed or implied, including the implied warranties of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details. You should have received a copy of the
-# GNU General Public License along with this program; if not, write to the
-# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
-# source code or documentation are not subject to the GNU General Public
-# License and may only be used or replicated with the express permission of
-# Red Hat, Inc.
-#
-# Red Hat Author(s): Chris Lumens <clumens at redhat.com>
-#
-
-import astroid
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.strings import StringFormatChecker
-from pylint.checkers.logging import LoggingChecker
-from pylint.checkers.utils import check_messages
-from pylint.interfaces import IAstroidChecker
-
-from copy import copy
-
-translationMethods = frozenset(["_", "N_", "P_", "C_", "CN_", "CP_"])
-
-# Returns a list of the message strings for a given translation method call
-def _get_message_strings(node):
- msgstrs = []
-
- if node.func.name in ("_", "N_") and len(node.args) >= 1:
- if isinstance(node.args[0], astroid.Const):
- msgstrs.append(node.args[0].value)
- elif node.func.name in ("C_", "CN_") and len(node.args) >= 2:
- if isinstance(node.args[1], astroid.Const):
- msgstrs.append(node.args[1].value)
- elif node.func.name == "P_" and len(node.args) >= 2:
- if isinstance(node.args[0], astroid.Const):
- msgstrs.append(node.args[0].value)
- if isinstance(node.args[1], astroid.Const):
- msgstrs.append(node.args[1].value)
- elif node.func.name == "CP_" and len(node.args) >= 3:
- if isinstance(node.args[1], astroid.Const):
- msgstrs.append(node.args[1].value)
- if isinstance(node.args[2], astroid.Const):
- msgstrs.append(node.args[2].value)
-
- return msgstrs
-
-class IntlChecker(BaseChecker):
- __implements__ = (IAstroidChecker, )
- name = "internationalization"
- msgs = {"W9901": ("Found % in a call to a _() method",
- "found-percent-in-_",
- "% in a call to one of the _() methods results in incorrect translations"),
- "W9902": ("Found _ call at module/class level",
- "found-_-in-module-class",
- "Calling _ at the module or class level results in translations to the wrong language")
- }
-
- @check_messages("found-percent-in-_")
- def visit_binop(self, node):
- if node.op != "%":
- return
-
- curr = node
- while curr.parent:
- if isinstance(curr.parent, astroid.CallFunc) and getattr(curr.parent.func, "name", "") in translationMethods:
- self.add_message("W9901", node=node)
- break
-
- curr = curr.parent
-
- @check_messages("found-_-in-module-class")
- def visit_callfunc(self, node):
- # The first test skips internal functions like getattr.
- if isinstance(node.func, astroid.Name) and node.func.name == "_":
- if isinstance(node.scope(), astroid.Module) or isinstance(node.scope(), astroid.Class):
- self.add_message("W9902", node=node)
-
-# Extend LoggingChecker to check translated logging strings
-class IntlLoggingChecker(LoggingChecker):
- __implements__ = (IAstroidChecker,)
-
- name = 'intl-logging'
- msgs = {'W9903': ("Fake message for translated E/W120* checks",
- "translated-log",
- "This message is not emitted itself, but can be used to control the display of \
- logging format messages extended for translated strings")
- }
-
- options = ()
-
- @check_messages('translated-log')
- def visit_callfunc(self, node):
- if len(node.args) >= 1 and isinstance(node.args[0], astroid.CallFunc) and \
- getattr(node.args[0].func, "name", "") in translationMethods:
- for formatstr in _get_message_strings(node.args[0]):
- # Both the node and the args need to be copied so we don't replace args
- # on the original node.
- copynode = copy(node)
- copyargs = copy(node.args)
- copyargs[0] = astroid.Const(formatstr)
- copynode.args = copyargs
- LoggingChecker.visit_callfunc(self, copynode)
-
- def __init__(self, *args, **kwargs):
- LoggingChecker.__init__(self, *args, **kwargs)
-
- # Just set logging_modules to 'logging', instead of trying to take a parameter
- # like LoggingChecker
- self.config.logging_modules = ('logging',)
-
-# Extend StringFormatChecker to check translated format strings
-class IntlStringFormatChecker(StringFormatChecker):
- __implements__ = (IAstroidChecker,)
-
- name = 'intl-string'
- msgs = {'W9904': ("Fake message for translated E/W130* checks",
- "translated-format",
- "This message is not emitted itself, but can be used to control the display of \
- string format messages extended for translated strings")
- }
-
- @check_messages('translated-format')
- def visit_binop(self, node):
- if node.op != '%':
- return
-
- if isinstance(node.left, astroid.CallFunc) and getattr(node.left.func, "name", "") in translationMethods:
- for formatstr in _get_message_strings(node.left):
- # Create a copy of the node with just the message string as the format
- copynode = copy(node)
- copynode.left = astroid.Const(formatstr)
- StringFormatChecker.visit_binop(self, copynode)
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(IntlChecker(linter))
- linter.register_checker(IntlLoggingChecker(linter))
- linter.register_checker(IntlStringFormatChecker(linter))
diff --git a/tests/pylint/markup.py b/tests/pylint/markup.py
deleted file mode 100644
index 7a93880..0000000
--- a/tests/pylint/markup.py
+++ /dev/null
@@ -1,198 +0,0 @@
-# Pango markup pylint module
-#
-# Copyright (C) 2014 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use,
-# modify, copy, or redistribute it subject to the terms and conditions of
-# the GNU General Public License v.2, or (at your option) any later version.
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY expressed or implied, including the implied warranties of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details. You should have received a copy of the
-# GNU General Public License along with this program; if not, write to the
-# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
-# source code or documentation are not subject to the GNU General Public
-# License and may only be used or replicated with the express permission of
-# Red Hat, Inc.
-#
-# Red Hat Author(s): David Shea <dhsea at redhat.com>
-#
-
-import astroid
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import check_messages
-from pylint.interfaces import IAstroidChecker
-
-import sys
-import os
-
-import xml.etree.ElementTree as ET
-
-# markup_necessary not used yet
-from pangocheck import markup_nodes, is_markup, markup_match #, markup_necessary
-
-markupMethods = ["set_markup"]
-escapeMethods = ["escape_markup"]
-
-# Used for checking translations
-podicts = None
-
-i18n_funcs = ["_", "N_", "P_", "C_", "CN_", "CP_"]
-i18n_ctxt_funcs = ["C_", "CN_", "CP_"]
-
-class MarkupChecker(BaseChecker):
- __implements__ = (IAstroidChecker,)
- name = "pango-markup"
- msgs = {"W9920" : ("Found invalid pango markup",
- "invalid-markup",
- "Pango markup could not be parsed"),
- "W9921" : ("Found pango markup with invalid element %s",
- "invalid-markup-element",
- "Pango markup contains invalid elements"),
- "W9922" : ("Found % in markup with unescaped parameters",
- "unescaped-markup",
- "Parameters passed to % in markup not escaped"),
- "W9923" : ("Found invalid pango markup in %s translation",
- "invalid-translated-markup",
- "Translated Pango markup could not be parsed"),
- "W9924" : ("Found pango markup with invalid element %s in %s translation",
- "invalid-translated-markup-element",
- "Translated pango markup contains invalid elements"),
- "W9925" : ("Found mis-translated pango markup for language %s",
- "invalid-pango-translation",
- "The elements or attributes do not match between a pango markup string and its translation"),
- "W9926" : ("Found unnecessary pango markup",
- "unnecessary-markup",
- "Pango markup could be expressed as attribute list"),
- }
-
- options = (('translate-markup',
- {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>',
- 'help' : "Check translations of markup strings"
- }),
- )
-
- # Check a parsed markup string for invalid tags
- def _validate_pango_markup(self, node, root, lang=None):
- if root.tag not in markup_nodes:
- if lang:
- self.add_message("W9924", node=node, args=(root.tag, lang))
- else:
- self.add_message("W9921", node=node, args=(root.tag,))
- else:
- for child in root:
- self._validate_pango_markup(node, child)
-
- # Attempt to parse a markup string as XML
- def _validate_pango_markup_string(self, node, string, lang=None):
- try:
- # QUIS CUSTODIET IPSOS CUSTODES
- # pylint: disable=unescaped-markup
- tree = ET.fromstring("<markup>%s</markup>" % string)
-
- # Check if the markup is necessary
- # TODO: Turn this on after it's possible to actually do
- # anything about it. See https://bugzilla.gnome.org/show_bug.cgi?id=725681
- #if not markup_necessary(tree):
- # self.add_message("W9926", node=node)
- except ET.ParseError:
- if lang:
- self.add_message("W9923", node=node, args=(lang,))
- else:
- self.add_message("W9920", node=node)
- else:
- # Check that all of the elements are valid for pango
- self._validate_pango_markup(node, tree)
-
- def __init__(self, linter=None):
- BaseChecker.__init__(self, linter)
-
- @check_messages("invalid-markup", "invalid-markup-element", "unescaped-markup", "invalid-translated-markup", "invalid-translated-markup-element", "invalid-pango-translation", "unnecessary-markup")
- def visit_const(self, node):
- if not isinstance(node.value, (bytes, str)):
- return
-
- if not is_markup(node.value):
- return
-
- self._validate_pango_markup_string(node, node.value)
-
- # Check translated versions of the string if requested
- if self.config.translate_markup:
- global podicts
-
- # Check if this is a translatable string
- curr = node
- i18nFunc = None
- while curr.parent:
- if isinstance(curr.parent, astroid.CallFunc) and \
- getattr(curr.parent.func, "name", "") in i18n_funcs:
- i18nFunc = curr.parent
- break
- curr = curr.parent
-
- if i18nFunc:
- # If not done already, import polib and read the translations
- if not podicts:
- try:
- from translatepo import translate_all
- except ImportError:
- print("Unable to load po translation module")
- sys.exit(99)
- else:
- podicts = translate_all(os.path.join(os.environ.get('top_srcdir', '.'), 'po'))
-
- if i18nFunc.func.name in i18n_ctxt_funcs:
- msgctxt = i18nFunc.args[0].value
- else:
- msgctxt = None
-
- # Loop over all translations for the string
- for podict in podicts.values():
- try:
- node_values = podict.get(node.value, msgctxt)
- except KeyError:
- continue
-
- for value in node_values:
- self._validate_pango_markup_string(node, value, podict.metadata['Language'])
-
- # Check that the markup matches, roughly
- if not markup_match(node.value, value):
- self.add_message("W9925", node=node, args=(podict.metadata['Language'],))
-
- # Check if this the left side of a % operation
- curr = node
- formatOp = None
- while curr.parent:
- if isinstance(curr.parent, astroid.BinOp) and curr.parent.op == "%" and \
- curr.parent.left == curr:
- formatOp = curr.parent
- break
- curr = curr.parent
-
- # Check whether the right side of the % operation is escaped
- if formatOp:
- if isinstance(formatOp.right, astroid.CallFunc):
- if getattr(formatOp.right.func, "name", "") not in escapeMethods:
- self.add_message("W9922", node=formatOp.right)
- # If a tuple, each item in the tuple must be escaped
- elif isinstance(formatOp.right, astroid.Tuple):
- for elt in formatOp.right.elts:
- if not isinstance(elt, astroid.CallFunc) or\
- getattr(elt.func, "name", "") not in escapeMethods:
- self.add_message("W9922", node=elt)
- # If a dictionary, each value must be escaped
- elif isinstance(formatOp.right, astroid.Dict):
- for item in formatOp.right.items:
- if not isinstance(item[1], astroid.CallFunc) or\
- getattr(item[1].func, "name", "") not in escapeMethods:
- self.add_message("W9922", node=item[1])
- else:
- self.add_message("W9922", node=formatOp)
-
-def register(linter):
- """required method to auto register this checker """
- linter.register_checker(MarkupChecker(linter))
diff --git a/tests/pylint/pointless-override.py b/tests/pylint/pointless-override.py
deleted file mode 100644
index d10ba42..0000000
--- a/tests/pylint/pointless-override.py
+++ /dev/null
@@ -1,252 +0,0 @@
-# Pylint checker for pointless class attributes overrides.
-#
-# Copyright (C) 2014 Red Hat, Inc.
-#
-# This copyrighted material is made available to anyone wishing to use,
-# modify, copy, or redistribute it subject to the terms and conditions of
-# the GNU General Public License v.2, or (at your option) any later version.
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY expressed or implied, including the implied warranties of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details. You should have received a copy of the
-# GNU General Public License along with this program; if not, write to the
-# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
-# source code or documentation are not subject to the GNU General Public
-# License and may only be used or replicated with the express permission of
-# Red Hat, Inc.
-#
-# Red Hat Author(s): Anne Mulhern <amulhern at redhat.com>
-#
-
-import abc
-
-from six import add_metaclass
-
-import astroid
-
-from pylint.checkers import BaseChecker
-from pylint.checkers.utils import check_messages
-from pylint.interfaces import IAstroidChecker
-
- at add_metaclass(abc.ABCMeta)
-class PointlessData(object):
-
- _DEF_CLASS = abc.abstractproperty(doc="Class of interesting definitions.")
- message_id = abc.abstractproperty(doc="Pylint message identifier.")
-
- @classmethod
- @abc.abstractmethod
- def _retain_node(cls, node, restrict=True):
- """ Determines whether to retain a node for the analysis.
-
- :param node: an AST node
- :type node: astroid.Class
- :param restrict bool: True if results returned should be restricted
- :returns: True if the node should be kept, otherwise False
- :rtype: bool
-
- Restricted nodes are candidates for being marked as overridden.
- Only restricted nodes are put into the initial pool of candidates.
- """
- raise NotImplementedError()
-
- @staticmethod
- @abc.abstractmethod
- def _extract_value(node):
- """ Return the node that contains the assignment's value.
-
- :param node: an AST node
- :type node: astroid.Class
- :returns: the node corresponding to the value
- :rtype: bool
- """
- raise NotImplementedError()
-
- @staticmethod
- @abc.abstractmethod
- def _extract_targets(node):
- """ Generates the names being assigned to.
-
- :param node: an AST node
- :type node: astroid.Class
- :returns: a list of assignment target names
- :rtype: generator of str
- """
- raise NotImplementedError()
-
- @classmethod
- def get_data(cls, node, restrict=True):
- """ Find relevant nodes for this analysis.
-
- :param node: an AST node
- :type node: astroid.Class
- :param restrict bool: True if results returned should be restricted
-
- :rtype: generator of astroid.Class
- :returns: a generator of interesting nodes.
-
- Note that all nodes returned are guaranteed to be instances of
- some class in self._DEF_CLASS.
- """
- nodes = (n for n in node.body if isinstance(n, cls._DEF_CLASS))
- for n in nodes:
- if cls._retain_node(n, restrict):
- for name in cls._extract_targets(n):
- yield (name, cls._extract_value(n))
-
- @classmethod
- @abc.abstractmethod
- def check_equal(cls, node, other):
- """ Check whether the two nodes are considered equal.
-
- :param node: some ast node
- :param other: some ast node
-
- :rtype: bool
- :returns: True if the nodes are considered equal, otherwise False
-
- If the method returns True, the nodes are actually equal, but it
- may return False when the nodes are equal.
- """
- raise NotImplementedError()
-
-class PointlessFunctionDefinition(PointlessData):
- """ Looking for pointless function definitions. """
-
- _DEF_CLASS = astroid.Function
- message_id = "W9952"
-
- @classmethod
- def _retain_node(cls, node, restrict=True):
- return not restrict or \
- (len(node.body) == 1 and isinstance(node.body[0], astroid.Pass))
-
- @classmethod
- def check_equal(cls, node, other):
- return len(node.body) == 1 and isinstance(node.body[0], astroid.Pass) and \
- len(other.body) == 1 and isinstance(other.body[0], astroid.Pass)
-
- @staticmethod
- def _extract_value(node):
- return node
-
- @staticmethod
- def _extract_targets(node):
- yield node.name
-
-class PointlessAssignment(PointlessData):
-
- _DEF_CLASS = astroid.Assign
- message_id = "W9951"
-
- _VALUE_CLASSES = (
- astroid.Const,
- astroid.Dict,
- astroid.List,
- astroid.Tuple
- )
-
- @classmethod
- def _retain_node(cls, node, restrict=True):
- return not restrict or isinstance(node.value, cls._VALUE_CLASSES)
-
- @classmethod
- def check_equal(cls, node, other):
- if not isinstance(node, other.__class__):
- return False
- if isinstance(node, astroid.Const):
- return node.value == other.value
- if isinstance(node, (astroid.List, astroid.Tuple)):
- return len(node.elts) == len(other.elts) and \
- all(cls.check_equal(n, o) for (n, o) in zip(node.elts, other.elts))
- if isinstance(node, astroid.Dict):
- return len(node.items) == len(other.items)
- return False
-
- @staticmethod
- def _extract_value(node):
- return node.value
-
- @staticmethod
- def _extract_targets(node):
- for target in node.targets:
- yield target.name
-
-class PointlessClassAttributeOverrideChecker(BaseChecker):
- """ If the nearest definition of the class attribute in the MRO assigns
- it the same value, then the overriding definition is said to be
- pointless.
-
- The algorithm for detecting a pointless attribute override is the following.
-
- * For each class, C:
- - For each attribute assignment,
- name_1 = name_2 ... name_n = l (where l is a literal):
- * For each n in (n_1, n_2):
- - Traverse the linearization of the MRO until the first
- matching assignment n = l' is identified. If l is equal to l',
- then consider that the assignment to l in C is a
- pointless override.
-
- The algorithm for detecting a pointless method override has the same
- general structure, and the same defects discussed below.
-
- Note that this analysis is neither sound nor complete. It is unsound
- under multiple inheritance. Consider the following class hierarchy::
-
- class A(object):
- _attrib = False
-
- class B(A):
- _attrib = False
-
- class C(A):
- _attrib = True
-
- class D(B,C):
- pass
-
- In this case, starting from B, B._attrib = False would be considered
- pointless. However, for D the MRO is B, C, A, and removing the assignment
- B._attrib = False would change the inherited value of D._attrib from
- False to True.
-
- The analysis is incomplete because it will find some values unequal when
- actually they are equal.
-
- The analysis is both incomplete and unsound because it expects that
- assignments will always be made by means of the same syntax.
- """
-
- __implements__ = (IAstroidChecker,)
-
- name = "pointless class attribute override checker"
- msgs = {
- "W9951":
- (
- "Assignment to class attribute %s overrides identical assignment in ancestor.",
- "pointless-class-attribute-override",
- "Assignment to class attribute that overrides assignment in ancestor that assigns identical value has no effect."
- ),
- "W9952":
- (
- "definition of %s method overrides identical method definition in ancestor",
- "pointless-method-definition-override",
- "Overriding empty method definition with another empty method definition has no effect."
- )
- }
-
- @check_messages("W9951", "W9952")
- def visit_class(self, node):
- for checker in (PointlessAssignment, PointlessFunctionDefinition):
- for (name, value) in checker.get_data(node):
- for a in node.ancestors():
- match = next((v for (n, v) in checker.get_data(a, False) if n == name), None)
- if match is not None:
- if checker.check_equal(value, match):
- self.add_message(checker.message_id, node=value, args=(name,))
- break
-
-def register(linter):
- linter.register_checker(PointlessClassAttributeOverrideChecker(linter))
--
2.2.2
More information about the anaconda-patches
mailing list