GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( b15539...e299b1 )
by Kaloyan
01:59
created

Processor.import_file()   C

Complexity

Conditions 7

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 11.1032

Importance

Changes 0
Metric Value
cc 7
dl 0
loc 32
ccs 9
cts 16
cp 0.5625
crap 11.1032
rs 5.5
c 0
b 0
f 0
1
2 1
import os
3 1
from collections import OrderedDict
4 1
from pyfranca import franca_parser, ast
5
6
7 1
class ProcessorException(Exception):
8
9 1
    def __init__(self, message):
0 ignored issues
show
Bug introduced by
The __init__ method of the super-class Exception is not called.

It is generally advisable to initialize the super-class by calling its __init__ method:

class SomeParent:
    def __init__(self):
        self.x = 1

class SomeChild(SomeParent):
    def __init__(self):
        # Initialize the super class
        SomeParent.__init__(self)
Loading history...
10 1
        self.message = message
11
12 1
    def __str__(self):
13 1
        return self.message
14
15
16 1
class Processor:
17
    """
18
    Franca IDL processor.
19
    """
20
21 1
    def __init__(self):
22
        """
23
        Constructor.
24
        """
25
        # Default package paths.
26 1
        self.package_paths = ["."]
27 1
        self.files = {}
28 1
        self.packages = {}
29
30 1
    @staticmethod
31
    def basename(namespace):
32
        """
33
        Extract the type or namespace name from a Franca FQN.
34
        """
35
        dot = namespace.rfind(".")
36
        if dot == -1:
37
            return namespace
38
        else:
39
            return namespace[dot + 1:]
40
41 1
    @staticmethod
42
    def packagename(namespace):
43
        """
44
        Extract the package name from a Franca FQN.
45
        """
46
        dot = namespace.rfind(".")
47
        if dot == -1:
48
            return None
49
        else:
50
            return namespace[0:dot]
51
52
    # TODO: Remove?
53 1
    def resolve_fqn(self, fqn):
54
        package_fqn = fqn
55
        while True:
56
            package_fqn = self.packagename(package_fqn)
57
            if package_fqn is None:
58
                return None
59
            if package_fqn in self.packages:
60
                break
61
        package = self.packages[package_fqn]
62
        namespace_name = fqn[len(package_fqn)+1:]
63
        if namespace_name not in package:
64
            return None
65
        namespace = package[namespace_name]
66
        return package, namespace
67
68 1
    @staticmethod
69
    def resolve(namespace, name):
70
        """
71
        Resolve type references.
72
73
        :param namespace: context ast.Namespace object.
74
        :param name: name string.
75
        :return: Dereferenced ast.Type object.
76
        """
77
        # TODO: relative and FQN imports
78
        # FIXME: Circular references
79 1
        if not isinstance(namespace, ast.Namespace) or \
80
                not isinstance(name, str):
81
            raise ValueError("Unexpected input.")
82
        # Look in the type's namespace
83 1
        if name in namespace:
84 1
            return namespace[name]
85
        # Look in other type collections in the type's package
86 1
        for typecollection in namespace.package.typecollections.values():
87 1
            if name in typecollection:
88 1
                return typecollection[name]
89
        # Look in imports
90 1
        for package_import in namespace.package.imports:
91 1
            if package_import.namespace_reference:
92
                # Look in namespaces imported in the type's package
93 1
                if name in package_import.namespace_reference:
94 1
                    return package_import.namespace_reference[name]
95
            else:
96
                # Look in typecollections of packages imported in the type's
97
                #   package using FQNs.
98
                # FIXME: FQNs
99
                for typecollection in \
100
                        package_import.package_reference.typecollections:
101
                    if name in typecollection:
102
                        return typecollection[name]
103
        # Give up
104 1
        raise ProcessorException(
105
            "Unresolved reference \"{}\".".format(name))
106
107 1
    def _udpate_complextype_references(self, name):
108
        """
109
        Update type references in a complex type.
110
111
        :param name: ast.ComplexType object.
112
        """
113 1
        if isinstance(name, ast.Enumeration):
114
            # TODO: Handle extends
115
            if name.extends:
116
                self._update_type_references(name.namespace, name.extends)
117 1
        elif isinstance(name, ast.Struct):
118 1
            for field in name.fields.values():
119 1
                self._update_type_references(name.namespace, field.type)
120
            # TODO: Handle extends
121
            if name.extends:
122
                self._update_type_references(name.namespace, name.extends)
123 1
        elif isinstance(name, ast.Array):
124 1
            self._update_type_references(name.namespace, name.type)
125 1
        elif isinstance(name, ast.Map):
126 1
            self._update_type_references(name.namespace, name.key_type)
127 1
            self._update_type_references(name.namespace, name.value_type)
128
        else:
129
            assert False
130
131 1
    def _update_type_references(self, namespace, name):
132
        """
133
        Update type references in a type.
134
135
        :param namespace: ast.Namespace context.
136
        :param name: ast.Type object.
137
        """
138 1
        if isinstance(name, ast.Typedef):
139 1
            self._update_type_references(name.namespace, name.type)
140 1
        elif isinstance(name, ast.PrimitiveType):
141 1
            pass
142 1
        elif isinstance(name, ast.ComplexType):
143 1
            self._udpate_complextype_references(name)
144 1
        elif isinstance(name, ast.Reference):
145 1
            if not name.namespace:
146 1
                name.namespace = namespace
147 1
            if not name.reference:
148 1
                resolved_name = self.resolve(namespace, name.name)
149 1
                name.reference = resolved_name
150 1
        elif isinstance(name, ast.Attribute):
151 1
            self._update_type_references(name.namespace, name.type)
152 1
        elif isinstance(name, ast.Method):
153 1
            for arg in name.in_args.values():
154 1
                self._update_type_references(name.namespace, arg.type)
155 1
            for arg in name.out_args.values():
156 1
                self._update_type_references(name.namespace, arg.type)
157 1
            if isinstance(name.errors, OrderedDict):
158
                for arg in name.errors.values():
159
                    self._update_type_references(name.namespace, arg.type)
160
            else:
161
                # Errors can be a reference to an enumeration
162 1
                self._update_type_references(name.namespace, name.errors)
163
                # FIXME: Check the reference type
164 1
        elif isinstance(name, ast.Broadcast):
165 1
            for arg in name.out_args.values():
166 1
                self._update_type_references(name.namespace, arg.type)
167
        else:
168
            assert False
169
170 1
    def _update_namespace_references(self, namespace):
171
        """
172
        Update type references in a namespace.
173
174
        :param namespace: ast.Namespace object.
175
        """
176 1
        for name in namespace.typedefs.values():
177 1
            self._update_type_references(namespace, name)
178 1
        for name in namespace.enumerations.values():
179
            self._update_type_references(namespace, name)
180 1
        for name in namespace.structs.values():
181 1
            self._update_type_references(namespace, name)
182 1
        for name in namespace.arrays.values():
183 1
            self._update_type_references(namespace, name)
184 1
        for name in namespace.maps.values():
185 1
            self._update_type_references(namespace, name)
186
187 1
    def _update_interface_references(self, namespace):
188
        """
189
        Update type references in an interface.
190
191
        :param namespace: ast.Interface object.
192
        """
193 1
        self._update_namespace_references(namespace)
194 1
        for name in namespace.attributes.values():
195 1
            self._update_type_references(namespace, name)
196 1
        for name in namespace.methods.values():
197 1
            self._update_type_references(namespace, name)
198 1
        for name in namespace.broadcasts.values():
199 1
            self._update_type_references(namespace, name)
200
        # TODO: Handle extends
201 1
        if namespace.extends:
202
            self._update_type_references(namespace, namespace.extends)
203
204 1
    def _update_package_references(self, package):
205
        """
206
        Update type references in a package.
207
208
        :param package: ast.Package object.
209
        """
210 1
        for package_import in package.imports:
211 1
            assert package_import.package_reference is not None
212 1
            if package_import.namespace:
213
                # Namespace import
214 1
                package_reference = package_import.package_reference
215 1
                if not package_import.namespace.endswith(".*"):
216
                    raise ProcessorException(
217
                        "Invalid namespace import {}.".format(
218
                            package_import.namespace))
219 1
                namespace_name = \
220
                    package_import.namespace[len(package_reference.name)+1:-2]
221
                # Update namespace reference
222 1
                if namespace_name in package_reference:
223 1
                    namespace = package_reference[namespace_name]
224 1
                    package_import.namespace_reference = namespace
225
                else:
226 1
                    raise ProcessorException(
227
                        "Namespace \"{}\" not found.".format(
228
                            package_import.namespace))
229
            else:
230
                # Model import
231
                assert package_import.namespace_reference is None
232 1
        for namespace in package.typecollections:
233 1
            self._update_namespace_references(
234
                package.typecollections[namespace])
235 1
        for namespace in package.interfaces:
236 1
            self._update_interface_references(
237
                package.interfaces[namespace])
238
239 1
    def import_package(self, fspec, package, references=None):
240
        """
241
        Import an ast.Package into the processor.
242
243
        :param fspec: File specification of the package.
244
        :param package: ast.Package object.
245
        :param references: A list of package references.
246
        """
247 1
        if not isinstance(package, ast.Package):
248
            ValueError("Expected ast.Package as input.")
249 1
        if not references:
250 1
            references = []
251
        # Check for circular package dependencies.
252 1
        if package.name in references:
253
            raise ProcessorException(
254
                "Circular dependency for package \"{}\".".format(package.name))
255
        # Check whether package is already imported
256 1
        if package.name in self.packages:
257 1
            if fspec != self.packages[package.name].file:
258 1
                raise ProcessorException(
259
                    "Package \"{}\" defined in multiple files.".format(
260
                        package.name))
261
            else:
262
                return
263
        # Register the package in the processor.
264 1
        self.packages[package.name] = package
265 1
        self.files[fspec] = package
266
        # Process package imports
267 1
        for package_import in package.imports:
268 1
            imported_package = self.import_file(
269
                package_import.file, references + [package.name])
270
            # Update import reference
271 1
            package_import.package_reference = imported_package
272
        # Update type references
273 1
        self._update_package_references(package)
274
275 1
    def import_string(self, fspec, fidl, references=None):
276
        """
277
        Parse an FIDL string and import it into the processor as package.
278
279
        :param fspec: File specification of the package.
280
        :param fidl: FIDL string.
281
        :param references: A list of package references.
282
        :return: The parsed ast.Package.
283
        """
284
        # Parse the string.
285 1
        parser = franca_parser.Parser()
286 1
        package = parser.parse(fidl)
287
        # Import the package in the processor.
288 1
        self.import_package(fspec, package, references)
289 1
        return package
290
291 1
    def import_file(self, fspec, references=None):
292
        """
293
        Parse an FIDL file and import it into the processor as package.
294
295
        :param fspec: File specification.
296
        :param references: A list of package references.
297
        :return: The parsed ast.Package.
298
        """
299 1
        if fspec in self.files:
300
            # File already loaded.
301 1
            return self.files[fspec]
302 1
        if not os.path.exists(fspec):
303 1
            if os.path.isabs(fspec):
304
                # Absolute specification
305
                raise ProcessorException(
306
                    "Model \"{}\" not found.".format(fspec))
307
            else:
308
                # Relative specification - check in the package path list.
309 1
                for path in self.package_paths:
310 1
                    temp_fspec = os.path.join(path, fspec)
311 1
                    if os.path.exists(temp_fspec):
312
                        fspec = temp_fspec
313
                        break
314
                else:
315 1
                    raise ProcessorException(
316
                        "Model \"{}\" not found.".format(fspec))
317
        # Parse the file.
318
        parser = franca_parser.Parser()
319
        package = parser.parse_file(fspec)
320
        # Import the package in the processor.
321
        self.import_package(fspec, package, references)
322
        return package
323