Completed
Pull Request — master (#2357)
by Manas
07:53
created

KeyValuePair   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 16
Duplicated Lines 0 %
Metric Value
dl 0
loc 16
rs 10
wmc 2

1 Method

Rating   Name   Duplication   Size   Complexity  
A register() 0 2 1
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
"""
17
Plugin which tells Pylint how to handle classes which define attributes using jsonschema
18
in "schema" class attribute.
19
20
Those classes dyamically assign attributes defined in the schema on the class inside the
21
constructor.
22
"""
23
24
import six
25
26
from astroid import MANAGER
27
from astroid import nodes
28
from astroid import scoped_nodes
29
30
# A list of class names for which we want to skip the checks
31
CLASS_NAME_BLACKLIST = [
32
    'ExecutionParametersAPI'
33
]
34
35
36
def register(linter):
37
    pass
38
39
40
def transform(cls):
41
    if cls.name in CLASS_NAME_BLACKLIST:
42
        return
43
44
    if cls.name.endswith('API') or 'schema' in cls.locals:
45
        # This is a class which defines attributes in "schema" variable using json schema.
46
        # Those attributes are then assigned during run time inside the constructor
47
        fqdn = cls.qname()
48
        module_name, class_name = fqdn.rsplit('.', 1)
49
50
        module = __import__(module_name, fromlist=[class_name])
51
        actual_cls = getattr(module, class_name)
52
53
        schema = actual_cls.schema
54
55
        if not isinstance(schema, dict):
56
            # Not a class we are interested in
57
            return
58
59
        properties = schema.get('properties', {})
60
        for property_name, property_data in six.iteritems(properties):
61
            property_name = property_name.replace('-', '_')  # Note: We do the same in Python code
62
            property_type = property_data.get('type', None)
63
64
            if isinstance(property_type, (list, tuple)):
65
                # Hack for attributes with multiple types (e.g. string, null)
66
                property_type = property_type[0]
67
68
            if property_type == 'object':
69
                node = nodes.Dict()
70
            elif property_type == 'array':
71
                node = nodes.List()
72
            elif property_type == 'integer':
73
                node = scoped_nodes.builtin_lookup('int')[1][0]
74
            elif property_type == 'number':
75
                node = scoped_nodes.builtin_lookup('float')[1][0]
76
            elif property_type == 'string':
77
                node = scoped_nodes.builtin_lookup('str')[1][0]
78
            elif property_type == 'boolean':
79
                node = scoped_nodes.builtin_lookup('bool')[1][0]
80
            elif property_type == 'null':
81
                node = scoped_nodes.builtin_lookup('None')[1][0]
82
            else:
83
                node = scoped_nodes.Class(property_name, None)
84
85
            cls.locals[property_name] = [node]
86
87
88
MANAGER.register_transform(scoped_nodes.Class, transform)
89