# -*- 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 """ 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[A-Z][a-zA-Z0-9]*)_(?P[A-Z][a-zA-Z]*)$') _RE_PROVIDER_MODULE_NAME = re.compile( r'^((?P.*)\.)?(?P(?P[A-Z][a-zA-Z0-9]*)' r'_(?P[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 '__init__' in node: linter.disable('W0231', scope='module', line=node['__init__'].lineno) 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 " "_. Where and should be both " "written in CamelCase."), 'C9905': ('Invalid provider module name %s', "Module containing cim provider(s) should be named as " "_. Where both and 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))