mirror of
https://fuchsia.googlesource.com/third_party/github.com/pylint-dev/pylint
synced 2024-09-21 16:19:21 +00:00
ada4ef8855
We moved pyreverse.inspector.Project, pyreverse.inspector.project_from_files and pyreverse.inspector.interfaces. These were moved since they didn't belong in astroid and they can be better maintained inside pyreverse itself.
211 lines
6.3 KiB
Python
211 lines
6.3 KiB
Python
# Copyright (c) 2002-2013 LOGILAB S.A. (Paris, FRANCE).
|
|
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation; either version 2 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty 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.
|
|
"""
|
|
generic classes/functions for pyreverse core/extensions
|
|
"""
|
|
from __future__ import print_function
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
########### pyreverse option utils ##############################
|
|
|
|
|
|
RCFILE = '.pyreverserc'
|
|
|
|
def get_default_options():
|
|
"""
|
|
Read config file and return list of options
|
|
"""
|
|
options = []
|
|
home = os.environ.get('HOME', '')
|
|
if home:
|
|
rcfile = os.path.join(home, RCFILE)
|
|
try:
|
|
options = open(rcfile).read().split()
|
|
except IOError:
|
|
pass # ignore if no config file found
|
|
return options
|
|
|
|
def insert_default_options():
|
|
"""insert default options to sys.argv
|
|
"""
|
|
options = get_default_options()
|
|
options.reverse()
|
|
for arg in options:
|
|
sys.argv.insert(1, arg)
|
|
|
|
|
|
|
|
# astroid utilities ###########################################################
|
|
|
|
SPECIAL = re.compile('^__[A-Za-z0-9]+[A-Za-z0-9_]*__$')
|
|
PRIVATE = re.compile('^__[_A-Za-z0-9]*[A-Za-z0-9]+_?$')
|
|
PROTECTED = re.compile('^_[_A-Za-z0-9]*$')
|
|
|
|
def get_visibility(name):
|
|
"""return the visibility from a name: public, protected, private or special
|
|
"""
|
|
if SPECIAL.match(name):
|
|
visibility = 'special'
|
|
elif PRIVATE.match(name):
|
|
visibility = 'private'
|
|
elif PROTECTED.match(name):
|
|
visibility = 'protected'
|
|
|
|
else:
|
|
visibility = 'public'
|
|
return visibility
|
|
|
|
ABSTRACT = re.compile('^.*Abstract.*')
|
|
FINAL = re.compile('^[A-Z_]*$')
|
|
|
|
def is_abstract(node):
|
|
"""return true if the given class node correspond to an abstract class
|
|
definition
|
|
"""
|
|
return ABSTRACT.match(node.name)
|
|
|
|
def is_final(node):
|
|
"""return true if the given class/function node correspond to final
|
|
definition
|
|
"""
|
|
return FINAL.match(node.name)
|
|
|
|
def is_interface(node):
|
|
# bw compat
|
|
return node.type == 'interface'
|
|
|
|
def is_exception(node):
|
|
# bw compat
|
|
return node.type == 'exception'
|
|
|
|
|
|
# Helpers #####################################################################
|
|
|
|
_CONSTRUCTOR = 1
|
|
_SPECIAL = 2
|
|
_PROTECTED = 4
|
|
_PRIVATE = 8
|
|
MODES = {
|
|
'ALL' : 0,
|
|
'PUB_ONLY' : _SPECIAL + _PROTECTED + _PRIVATE,
|
|
'SPECIAL' : _SPECIAL,
|
|
'OTHER' : _PROTECTED + _PRIVATE,
|
|
}
|
|
VIS_MOD = {'special': _SPECIAL, 'protected': _PROTECTED,
|
|
'private': _PRIVATE, 'public': 0}
|
|
|
|
|
|
class FilterMixIn(object):
|
|
"""filter nodes according to a mode and nodes' visibility
|
|
"""
|
|
def __init__(self, mode):
|
|
"init filter modes"
|
|
__mode = 0
|
|
for nummod in mode.split('+'):
|
|
try:
|
|
__mode += MODES[nummod]
|
|
except KeyError as ex:
|
|
print('Unknown filter mode %s' % ex, file=sys.stderr)
|
|
self.__mode = __mode
|
|
|
|
|
|
def show_attr(self, node):
|
|
"""return true if the node should be treated
|
|
"""
|
|
visibility = get_visibility(getattr(node, 'name', node))
|
|
return not self.__mode & VIS_MOD[visibility]
|
|
|
|
|
|
class ASTWalker(object):
|
|
"""a walker visiting a tree in preorder, calling on the handler:
|
|
|
|
* visit_<class name> on entering a node, where class name is the class of
|
|
the node in lower case
|
|
|
|
* leave_<class name> on leaving a node, where class name is the class of
|
|
the node in lower case
|
|
"""
|
|
|
|
def __init__(self, handler):
|
|
self.handler = handler
|
|
self._cache = {}
|
|
|
|
def walk(self, node, _done=None):
|
|
"""walk on the tree from <node>, getting callbacks from handler"""
|
|
if _done is None:
|
|
_done = set()
|
|
if node in _done:
|
|
raise AssertionError((id(node), node, node.parent))
|
|
_done.add(node)
|
|
self.visit(node)
|
|
for child_node in node.get_children():
|
|
assert child_node is not node
|
|
self.walk(child_node, _done)
|
|
self.leave(node)
|
|
assert node.parent is not node
|
|
|
|
def get_callbacks(self, node):
|
|
"""get callbacks from handler for the visited node"""
|
|
klass = node.__class__
|
|
methods = self._cache.get(klass)
|
|
if methods is None:
|
|
handler = self.handler
|
|
kid = klass.__name__.lower()
|
|
e_method = getattr(handler, 'visit_%s' % kid,
|
|
getattr(handler, 'visit_default', None))
|
|
l_method = getattr(handler, 'leave_%s' % kid,
|
|
getattr(handler, 'leave_default', None))
|
|
self._cache[klass] = (e_method, l_method)
|
|
else:
|
|
e_method, l_method = methods
|
|
return e_method, l_method
|
|
|
|
def visit(self, node):
|
|
"""walk on the tree from <node>, getting callbacks from handler"""
|
|
method = self.get_callbacks(node)[0]
|
|
if method is not None:
|
|
method(node)
|
|
|
|
def leave(self, node):
|
|
"""walk on the tree from <node>, getting callbacks from handler"""
|
|
method = self.get_callbacks(node)[1]
|
|
if method is not None:
|
|
method(node)
|
|
|
|
|
|
class LocalsVisitor(ASTWalker):
|
|
"""visit a project by traversing the locals dictionary"""
|
|
def __init__(self):
|
|
ASTWalker.__init__(self, self)
|
|
self._visited = {}
|
|
|
|
def visit(self, node):
|
|
"""launch the visit starting from the given node"""
|
|
if node in self._visited:
|
|
return
|
|
self._visited[node] = 1 # FIXME: use set ?
|
|
methods = self.get_callbacks(node)
|
|
if methods[0] is not None:
|
|
methods[0](node)
|
|
if 'locals' in node.__dict__: # skip Instance and other proxy
|
|
for local_node in node.values():
|
|
self.visit(local_node)
|
|
if methods[1] is not None:
|
|
return methods[1](node)
|