diff options
author | Michal Minar <miminar@redhat.com> | 2012-11-28 10:24:59 +0100 |
---|---|---|
committer | Michal Minar <miminar@redhat.com> | 2012-11-28 10:24:59 +0100 |
commit | 148b5b7d18c449b87b1103c05612ff2a2fe83233 (patch) | |
tree | 44ec8759acb37b79bae705550e37a8cb7885b20c /tools/pylint/plugins/cim_provider_checker.py | |
parent | 084ed5a5ffe8dccd0436e880449a6b39863690a4 (diff) | |
download | openlmi-providers-148b5b7d18c449b87b1103c05612ff2a2fe83233.tar.gz openlmi-providers-148b5b7d18c449b87b1103c05612ff2a2fe83233.tar.xz openlmi-providers-148b5b7d18c449b87b1103c05612ff2a2fe83233.zip |
added pylintlmi checker for python source checking
pylintlmi uses pylint (python source code checking utility)
- giving it custom configuration for openlmi project and
plugins with additional checks
for usage see src/software/README
renamed directory "src/software/providers" to
"src/software/openlmi/software"
* allows installation to custom PYTHONPATH together with openlmi-python egg
* that also allows running pylint on sources in devel directory without
messages like:
W0403: 28,0: Relative import 'openlmi.software.core', should be
'openlmi.software.openlmi.software.core'
E0611: 28,0: No name 'software' in module 'openlmi'
F0401: 28,0: Unable to import 'openlmi.software.core'
Diffstat (limited to 'tools/pylint/plugins/cim_provider_checker.py')
-rw-r--r-- | tools/pylint/plugins/cim_provider_checker.py | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/tools/pylint/plugins/cim_provider_checker.py b/tools/pylint/plugins/cim_provider_checker.py new file mode 100644 index 0000000..253cb9d --- /dev/null +++ b/tools/pylint/plugins/cim_provider_checker.py @@ -0,0 +1,149 @@ +# -*- encoding: utf-8 -*- +# Software Management Providers +# +# Copyright (C) 2012 Red Hat, Inc. All rights reserved. +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Authors: Michal Minar <miminar@redhat.com> +""" +Pylint checker for CIM provider modules and classes. +""" + +import re +from logilab.astng import node_classes # pylint: disable=W0403 +from logilab.astng import scoped_nodes # pylint: disable=W0403 +from pylint.interfaces import IASTNGChecker +from pylint.checkers import BaseChecker + +_RE_PROVIDER_CLASS_NAME = re.compile( + r'^(?P<prefix>[A-Z][a-zA-Z0-9]*)_(?P<name>[A-Z][a-zA-Z]*)$') +_RE_PROVIDER_MODULE_NAME = re.compile( + r'^((?P<parent>.*)\.)?(?P<basename>(?P<prefix>[A-Z][a-zA-Z0-9]*)' + r'_(?P<name>[A-Z][a-zA-Z]*))$') +_RE_COMMON_MODULE_NAME = re.compile( + r'^([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)$') + +def supress_cim_provider_messages(linter, node): + """ + Supress some warnings for CIMProvider2 subclass. + @param node is a subclass of CIMProvider2 + """ + assert isinstance(node, scoped_nodes.Class) + if ( 'Values' in node + and isinstance(node['Values'], scoped_nodes.Class)): + linter.disable('R0903', scope='module', line=node['Values'].lineno) + linter.disable('C0111', scope='module', line=node['Values'].lineno) + for child in node['Values'].get_children(): + if not isinstance(child, scoped_nodes.Class): + continue + linter.disable('R0903', scope='module', line=child.lineno) + linter.disable('C0111', scope='module', line=child.lineno) + +class CIMProviderChecker(BaseChecker): + """ + Checks for compliance to naming conventions for python cim providers. + """ + + __implements__ = IASTNGChecker + + name = 'cim_provider' + msgs = { + 'C9904': ('Invalid provider class name %s', + "Class name representing cim provider should be in inform " + "<prefix>_<name>. Where <prefix> and <name> should be both " + "written in CamelCase."), + 'C9905': ('Invalid provider module name %s', + "Module containing cim provider(s) should be named as " + "<prefix>_<name>. Where both <prefix> and <name> are " + "CamelCased strings."), + 'C9906': ('Prefixes of module and class does not match: %s != %s', + "Module prefix has to match provider class prefix."), + 'E9907': ("Missing get_providers function in module %s", + "Provider module must contain get_providers function."), + 'W9908': ("get_providers in module %s is not a function", + "get_providers should be a callable function."), + 'W9909': ("Missing provider name \"%s\" in providers dictionary.", + "Function get_providers returns providers association" + " dictionary, that must include all module's provider" + " classes."), + 'C9910': ("Prefix different from LMI: %s", + "All OpenLMI CIM classes have LMI_ ORGID prefix."), + 'C9911': ("Invalid module name: %s", + "All modules, that does not declare cim provider class" + " should be named according to this regexp:" + " '^(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$'."), + } + + # this is important so that your checker is executed before others + priority = -2 + + def visit_class(self, node): + """ + Check every class, which inherits from + pywbem.cim_provider2.CIMProvider2. + """ + if "CIMProvider2" in [a.name for a in node.ancestors()]: + clsm = _RE_PROVIDER_CLASS_NAME.match(node.name) + if not clsm: + self.add_message('C9904', node=node, args=node.name) + parent = node.parent + while not isinstance(parent, scoped_nodes.Module): + parent = parent.parent + modm = _RE_PROVIDER_MODULE_NAME.match(parent.name) + if not modm: + self.add_message('C9905', node=node, args=parent.name) + if not clsm: + return + if clsm.group('prefix') != modm.group('prefix'): + self.add_message('C9906', node=node, + args=(modm.group('prefix'), clsm.group('prefix'))) + if clsm.group('prefix') != 'LMI': + self.add_message('C9910', node=node, args=clsm.group('prefix')) + if not 'get_providers' in parent.keys(): + self.add_message('E9907', node=parent, args=parent.name) + return + getprovs = parent['get_providers'] + if not isinstance(getprovs, scoped_nodes.Function): + self.add_message('W9908', node=getprovs, args=parent.name) + ret = getprovs.last_child() + if not isinstance(ret, node_classes.Return): + return + dictionary = ret.get_children().next() + if not isinstance(dictionary, node_classes.Dict): + return + if not node.name in [i[0].value for i in dictionary.items]: + self.add_message('W9909', node=getprovs, args=node.name) + supress_cim_provider_messages(self.linter, node) + + def visit_module(self, node): + """ + Check for invalid module name. + """ + modm = _RE_PROVIDER_MODULE_NAME.match(node.name) + if modm: + if not _RE_COMMON_MODULE_NAME.match(node.name): + for child in node.get_children(): + if ( isinstance(child, scoped_nodes.Class) + and 'CIMProvider2' in [ + a.name for a in child.ancestors()]): + break + else: + self.add_message('C9911', node=node, args=node.name) + +def register(linter): + """required method to auto register our checker""" + linter.register_checker(CIMProviderChecker(linter)) + |