1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
# -*- encoding: utf-8 -*-
# Software Management Providers
#
# Copyright (C) 2012-2013 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 recursively_clean_cls_values(linter, node):
"""
Values class under provider defines property values, under
nested classes that are popular target of pylint messages. Since
these are generated and known to any developper, we don't want
to be bothered by these warnings.
Supress them for any nested class recursively.
"""
# class has too few public methods
linter.disable('R0903', scope='module', line=node.fromlineno)
# class has not docstring
linter.disable('C0111', scope='module', line=node.fromlineno)
for child in node.get_children():
if isinstance(child, scoped_nodes.Class):
recursively_clean_cls_values(linter, child)
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)
# class has too many public methods
linter.disable('R0904', scope='module', line=node.fromlineno)
if '__init__' in node:
# __init__ not called for base class
linter.disable('W0231', scope='module', line=node['__init__'].fromlineno)
if ( 'Values' in node
and isinstance(node['Values'], scoped_nodes.Class)):
recursively_clean_cls_values(linter, node['Values'])
generated_methods = (
'get_instance',
'enum_instances',
'set_instance',
'delete_instance')
# implementation of generated methods may not use all arguments
for child in node.get_children():
if ( not isinstance(child, scoped_nodes.Function)
or ( child.name not in generated_methods
and not child.name.startswith('cim_method_'))):
continue
# provider method could be a function
linter.disable('R0201', scope='module', line=child.fromlineno)
for arg in child.args.get_children():
linter.disable('W0613', scope='module', line=arg.fromlineno)
class CIMProviderChecker(BaseChecker):
"""
Checks for compliance to naming conventions for python cim providers.
"""
__implements__ = IASTNGChecker
name = 'cim_provider'
msgs = {
'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."),
'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()]:
supress_cim_provider_messages(self.linter, node)
parent = node.parent
while not isinstance(parent, scoped_nodes.Module):
parent = parent.parent
clsm = _RE_PROVIDER_CLASS_NAME.match(node.name)
modm = _RE_PROVIDER_MODULE_NAME.match(parent.name)
if clsm and not modm:
self.add_message('C9905', node=node, args=parent.name)
if clsm and modm and clsm.group('prefix') != modm.group('prefix'):
self.add_message('C9906', node=node,
args=(modm.group('prefix'), clsm.group('prefix')))
if clsm and clsm.group('prefix') != 'LMI':
self.add_message('C9910', node=node, args=clsm.group('prefix'))
if 'get_providers' in parent.keys():
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)
elif node.name == "Values" and len([a for a in node.ancestors()]) == 1:
recursively_clean_cls_values(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))
|