• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

trolldbois / ctypeslib / 13391439098

18 Feb 2025 01:15PM UTC coverage: 83.553% (-2.0%) from 85.503%
13391439098

push

github

trolldbois
update some doc

2032 of 2432 relevant lines covered (83.55%)

0.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.59
/ctypeslib/codegen/codegenerator.py
1
"""
2
Create ctypes wrapper code for abstract type descriptions.
3
Type descriptions are collections of typedesc instances.
4
"""
5

6
from __future__ import print_function
1✔
7
from __future__ import unicode_literals
1✔
8

9
import collections
1✔
10
import ctypes
1✔
11
import logging
1✔
12
import os
1✔
13
import pkgutil
1✔
14
import sys
1✔
15
import textwrap
1✔
16
import io
1✔
17
from io import StringIO
1✔
18

19
from clang.cindex import TypeKind
1✔
20

21
from ctypeslib.codegen import clangparser
1✔
22
from ctypeslib.codegen import config
1✔
23
from ctypeslib.codegen import typedesc
1✔
24
from ctypeslib.codegen import util
1✔
25
from ctypeslib.library import Library
1✔
26

27
log = logging.getLogger("codegen")
1✔
28

29

30
class Generator:
1✔
31
    def __init__(self, output, cfg):
1✔
32
        self.output = output
1✔
33
        self.stream = StringIO()
1✔
34
        self.imports = StringIO()
1✔
35
        self.cfg = cfg
1✔
36
        self.generate_locations = cfg.generate_locations
1✔
37
        self.generate_comments = cfg.generate_comments
1✔
38
        self.generate_docstrings = cfg.generate_docstrings
1✔
39
        self.known_symbols = cfg.known_symbols or {}
1✔
40
        self.preloaded_dlls = cfg.preloaded_dlls or []
1✔
41
        if cfg.searched_dlls is None:
1✔
42
            self.searched_dlls = []
×
43
        else:
44
            self.searched_dlls = cfg.searched_dlls
1✔
45

46
        # we use collections.OrderedDict() to keep ordering
47
        self.done = collections.OrderedDict()  # type descriptions that have been generated
1✔
48
        self.names = list()  # names that have been generated
1✔
49
        self.more = collections.OrderedDict()
1✔
50
        self.macros = 0
1✔
51
        self.cross_arch_code_generation = cfg.cross_arch
1✔
52
        # what record dependency were generated
53
        self.head_generated = set()
1✔
54
        self.body_generated = set()
1✔
55

56
    # pylint: disable=method-hidden
57
    def enable_fundamental_type_wrappers(self):
1✔
58
        """
59
        If a type is a int128, a long_double_t or a void, some placeholders need
60
        to be in the generated code to be valid.
61
        """
62
        self.enable_fundamental_type_wrappers = lambda: True
1✔
63
        headers = pkgutil.get_data("ctypeslib", "data/fundamental_type_name.tpl").decode()
1✔
64
        size = str(self.parser.get_ctypes_size(TypeKind.LONGDOUBLE) // 8)
1✔
65
        headers = headers.replace("__LONG_DOUBLE_SIZE__", size)
1✔
66
        print(headers, file=self.imports)
1✔
67

68
    def enable_pointer_type(self):
1✔
69
        """
70
        If a type is a pointer, a platform-independent POINTER_T type needs
71
        to be in the generated code.
72
        """
73
        # only enable if cross arch mode is on
74
        if not self.cross_arch_code_generation:
1✔
75
            return "ctypes.POINTER"
1✔
76
        self.enable_pointer_type = lambda: "POINTER_T"
1✔
77
        headers = pkgutil.get_data("ctypeslib", "data/pointer_type.tpl").decode()
1✔
78
        # assuming a LONG also has the same sizeof than a pointer.
79
        word_size = self.parser.get_ctypes_size(TypeKind.POINTER) // 8
1✔
80
        word_type = self.parser.get_ctypes_name(TypeKind.ULONG)
1✔
81
        # pylint: disable=protected-access
82
        word_char = getattr(ctypes, word_type)._type_
1✔
83
        # replacing template values
84
        headers = headers.replace("__POINTER_SIZE__", str(word_size))
1✔
85
        headers = headers.replace("__REPLACEMENT_TYPE__", word_type)
1✔
86
        headers = headers.replace("__REPLACEMENT_TYPE_CHAR__", word_char)
1✔
87
        print(headers, file=self.imports)
1✔
88
        return "POINTER_T"
1✔
89

90
    def enable_structure_type(self):
1✔
91
        """
92
        If a structure type is used, declare our ctypes.Structure extension type
93
        """
94
        self.enable_structure_type = lambda: True
1✔
95
        headers = pkgutil.get_data("ctypeslib", "data/structure_type.tpl").decode()
1✔
96
        print(headers, file=self.imports)
1✔
97

98
    def enable_string_cast(self):
1✔
99
        """
100
        If a structure type is used, declare our ctypes.Structure extension type
101
        """
102
        self.enable_string_cast = lambda: True
1✔
103
        headers = pkgutil.get_data("ctypeslib", "data/string_cast.tpl").decode()
1✔
104
        headers = headers.replace("__POINTER_TYPE__", self.enable_pointer_type())
1✔
105
        print(headers, file=self.imports)
1✔
106

107
    def generate_headers(self, parser):
1✔
108
        # fix parser in self for later use
109
        self.parser = parser
1✔
110
        headers = pkgutil.get_data("ctypeslib", "data/headers.tpl").decode()
1✔
111
        # get sizes from clang library
112
        word_size = self.parser.get_ctypes_size(TypeKind.LONG) // 8
1✔
113
        pointer_size = self.parser.get_ctypes_size(TypeKind.POINTER) // 8
1✔
114
        longdouble_size = self.parser.get_ctypes_size(TypeKind.LONGDOUBLE) // 8
1✔
115
        # replacing template values
116
        headers = headers.replace("__FLAGS__", str(self.parser.flags))
1✔
117
        headers = headers.replace("__WORD_SIZE__", str(word_size))
1✔
118
        headers = headers.replace("__POINTER_SIZE__", str(pointer_size))
1✔
119
        headers = headers.replace("__LONGDOUBLE_SIZE__", str(longdouble_size))
1✔
120
        print(headers, file=self.imports)
1✔
121

122
    def type_name(self, t, generate=True):
1✔
123
        """
124
        Returns a string containing an expression that can be used to
125
        refer to the type. Assumes the 'from ctypes import *'
126
        namespace is available.
127
        """
128
        # no Test case for these
129
        # elif isinstance(t, typedesc.Argument):
130
        # elif isinstance(t, typedesc.CvQualifiedType):
131
        # elif isinstance(t, typedesc.Variable):
132
        #   return "%s" % self.type_name(t.typ, generate)
133
        # elif isinstance(t, typedesc.Enumeration):
134
        #   return t.name
135

136
        if isinstance(t, typedesc.FundamentalType):
1✔
137
            return self.FundamentalType(t)
1✔
138
        if isinstance(t, typedesc.ArrayType):
1✔
139
            # C99 feature called the flexible array member feature.
140
            # changing this to a pointer is incorrect.
141
            # if t.size == 0:
142
            #     pointer_class = self.enable_pointer_type()
143
            #     return "%s(%s)" % (pointer_class, self.type_name(t.typ, generate))
144
            # else:
145
            return "%s * %s" % (self.type_name(t.typ, generate), t.size)
1✔
146
        if isinstance(t, typedesc.PointerType) and isinstance(t.typ, typedesc.FunctionType):
1✔
147
            return self.type_name(t.typ, generate)
1✔
148
        if isinstance(t, typedesc.PointerType):
1✔
149
            pointer_class = self.enable_pointer_type()
1✔
150
            if t.typ.name in ["c_ubyte", "c_char"]:
1✔
151
                self.enable_string_cast()
1✔
152
            return "%s(%s)" % (pointer_class, self.type_name(t.typ, generate))
1✔
153
        if isinstance(t, typedesc.FunctionType):
1✔
154
            args = [self.type_name(x, generate) for x in [t.returns] + list(t.iterArgTypes())]
1✔
155
            if "__stdcall__" in t.attributes:
1✔
156
                return "ctypes.WINFUNCTYPE(%s)" % ", ".join(args)
×
157
            else:
158
                return "ctypes.CFUNCTYPE(%s)" % ", ".join(args)
1✔
159
        # elif isinstance(t, typedesc.Structure):
160
        # elif isinstance(t, typedesc.Typedef):
161
        # elif isinstance(t, typedesc.Union):
162
        return t.name
1✔
163
        # All typedesc typedefs should be handled
164
        # raise TypeError('This typedesc should be handled %s'%(t))
165

166
    ################################################################
167

168
    _aliases = 0
1✔
169

170
    def Alias(self, alias):
1✔
171
        """Handles Aliases. No test cases yet"""
172
        # FIXME
173
        if self.generate_comments:
×
174
            self.print_comment(alias)
×
175
        print("%s = %s # alias" % (alias.name, alias.alias), file=self.stream)
×
176
        self._aliases += 1
×
177
        return
×
178

179
    _macros = 0
1✔
180

181
    def Macro(self, macro):
1✔
182
        """
183
        Handles macro. No test cases else that #defines.
184

185
        Clang will first give us the macro definition,
186
        and then later, the macro reference in code will be replaced by teh macro body.
187
        So really, there is nothing to actually generate.
188
        Just push the macro in comment, and let the rest work away
189

190
        """
191
        if macro.location is None:
1✔
192
            log.info("Ignoring %s with no location", macro.name)
×
193
            return
×
194
        if self.generate_locations:
1✔
195
            print("# %s:%s" % macro.location, file=self.stream)
×
196
        if self.generate_comments:
1✔
197
            self.print_comment(macro)
×
198

199
        # get tokens types all the way to here ?
200
        # 1. clang makes the decision on type casting and validity of data.
201
        # let's not try to be clever.
202
        # only ignore, undefined references, macro functions...
203
        # 2. or get a flag in macro that tells us if something contains undefinedIdentifier
204
        # is not code-generable ?
205
        # codegen should decide what codegen can do.
206
        if macro.args:
1✔
207
            print("# def %s%s:  # macro" % (macro.name, macro.args), file=self.stream)
1✔
208
            print("#    return %s  " % macro.body, file=self.stream)
1✔
209
        elif util.contains_undefined_identifier(macro):
1✔
210
            # we can't handle that, we comment it out
211
            if isinstance(macro.body, typedesc.UndefinedIdentifier):
1✔
212
                print("# %s = %s # macro" % (macro.name, macro.body.name), file=self.stream)
1✔
213
            else:  # we assume it's a list
214
                print("# %s = %s # macro" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream)
1✔
215
        elif isinstance(macro.body, bool):
1✔
216
            print("%s = %s # macro" % (macro.name, macro.body), file=self.stream)
1✔
217
            self.macros += 1
1✔
218
            self.names.append(macro.name)
1✔
219
        elif isinstance(macro.body, str):
1✔
220
            if macro.body == "":
1✔
221
                print("# %s = %s # macro" % (macro.name, macro.body), file=self.stream)
1✔
222
            else:
223
                body = macro.body
1✔
224
                float_value = util.from_c_float_literal(body)
1✔
225
                if float_value is not None:
1✔
226
                    body = float_value
1✔
227
                # what about integers you ask ? body token that represents token are Integer here.
228
                # either it's just a thing we gonna print, or we need to have a registered item
229
                if sum(x=='(' for x in body) != sum(x==')' for x in body):
1✔
230
                    # unbalanced parens means comment
231
                    print("# %s = %s # macro" % (macro.name, body), file=self.stream)
×
232
                else:
233
                    print("%s = %s # macro" % (macro.name, body), file=self.stream)
1✔
234
                self.macros += 1
1✔
235
                self.names.append(macro.name)
1✔
236
        # This is why we need to have token types all the way here.
237
        # but at the same time, clang does not type tokens. So we might as well guess them here too
238
        elif util.body_is_all_string_tokens(macro.body):
1✔
239
            print("%s = %s # macro" % (macro.name, " ".join([str(_) for _ in macro.body])), file=self.stream)
1✔
240
            self.macros += 1
1✔
241
            self.names.append(macro.name)
1✔
242
        else:
243
            # this might be a token list of float literal
244
            body = macro.body
1✔
245
            float_value = util.from_c_float_literal(body)
1✔
246
            if float_value is not None:
1✔
247
                body = float_value
1✔
248
            # or anything else that might be a valid python literal...
249
            print("%s = %s # macro" % (macro.name, body), file=self.stream)
1✔
250
            self.macros += 1
1✔
251
            self.names.append(macro.name)
1✔
252
        return
1✔
253

254
    _typedefs = 0
1✔
255

256
    def Typedef(self, tp):
1✔
257
        if self.generate_comments:
1✔
258
            self.print_comment(tp)
×
259
        sized_types = {
1✔
260
            "uint8_t": "c_uint8",
261
            "uint16_t": "c_uint16",
262
            "uint32_t": "c_uint32",
263
            "uint64_t": "c_uint64",
264
            "int8_t": "c_int8",
265
            "int16_t": "c_int16",
266
            "int32_t": "c_int32",
267
            "int64_t": "c_int64",
268
        }
269
        name = self.type_name(tp)  # tp.name
1✔
270
        if isinstance(tp.typ, typedesc.FundamentalType) and tp.name in sized_types:
1✔
271
            print("%s = ctypes.%s" % (name, sized_types[tp.name]), file=self.stream)
1✔
272
            self.names.append(tp.name)
1✔
273
            return
1✔
274
        if tp.typ not in self.done:
1✔
275
            # generate only declaration code for records ?
276
            # if type(tp.typ) in (typedesc.Structure, typedesc.Union):
277
            #    self._generate(tp.typ.get_head())
278
            #    self.more.add(tp.typ)
279
            # else:
280
            #    self._generate(tp.typ)
281
            self._generate(tp.typ)
1✔
282
        # generate actual typedef code.
283
        if tp.name != self.type_name(tp.typ):
1✔
284
            print("%s = %s" % (name, self.type_name(tp.typ)), file=self.stream)
1✔
285

286
            if isinstance(tp.typ, typedesc.Enumeration):
1✔
287
                print("%s__enumvalues = %s__enumvalues" % (name, self.type_name(tp.typ)), file=self.stream)
×
288
                self.names.append("%s__enumvalues" % name)
×
289

290
        self.names.append(tp.name)
1✔
291
        self._typedefs += 1
1✔
292
        return
1✔
293

294
    def _get_real_type(self, tp):
1✔
295
        # FIXME, kinda useless really.
296
        if isinstance(tp, typedesc.Typedef):
1✔
297
            if isinstance(tp.typ, typedesc.Typedef):
×
298
                raise TypeError("Nested loop in Typedef %s" % tp.name)
×
299
            return self._get_real_type(tp.typ)
×
300
        elif isinstance(tp, typedesc.CvQualifiedType):
1✔
301
            return self._get_real_type(tp.typ)
×
302
        return tp
1✔
303

304
    _arraytypes = 0
1✔
305

306
    def ArrayType(self, tp):
1✔
307
        self._generate(self._get_real_type(tp.typ))
1✔
308
        self._generate(tp.typ)
1✔
309
        self._arraytypes += 1
1✔
310

311
    _functiontypes = 0
1✔
312
    _notfound_functiontypes = 0
1✔
313

314
    def FunctionType(self, tp):
1✔
315
        self._generate(tp.returns)
1✔
316
        self.generate_all(tp.arguments)
1✔
317
        # print >> self.stream, "%s = %s # Functiontype " % (
318
        # self.type_name(tp), [self.type_name(a) for a in tp.arguments])
319
        self._functiontypes += 1
1✔
320

321
    def Argument(self, tp):
1✔
322
        self._generate(tp.typ)
1✔
323

324
    _pointertypes = 0
1✔
325

326
    def PointerType(self, tp):
1✔
327
        # print 'generate', tp.typ
328
        if isinstance(tp.typ, typedesc.PointerType):
1✔
329
            self._generate(tp.typ)
1✔
330
        elif type(tp.typ) in (typedesc.Union, typedesc.Structure):
1✔
331
            self._generate(tp.typ.get_head())
1✔
332
            self.more[tp.typ] = True
1✔
333
        elif isinstance(tp.typ, typedesc.Typedef):
1✔
334
            self._generate(tp.typ)
×
335
        else:
336
            self._generate(tp.typ)
1✔
337
        self._pointertypes += 1
1✔
338

339
    def CvQualifiedType(self, tp):
1✔
340
        self._generate(tp.typ)
×
341

342
    _variables = 0
1✔
343
    _notfound_variables = 0
1✔
344

345
    def Variable(self, tp):
1✔
346
        self._variables += 1
1✔
347
        if self.generate_comments:
1✔
348
            self.print_comment(tp)
×
349

350
        # 2021-02 give me a test case for this. it breaks all extern variables otherwise.
351
        if tp.extern and self.find_library_with_func(tp):
1✔
352
            dll_library = self.find_library_with_func(tp)
1✔
353
            self._generate(tp.typ)
1✔
354
            # calling convention does not matter for in_dll...
355
            libname = self.get_sharedlib(dll_library, "cdecl")
1✔
356
            print("%s = (%s).in_dll(%s, '%s')" % (tp.name, self.type_name(tp.typ), libname, tp.name), file=self.stream)
1✔
357
            self.names.append(tp.name)
1✔
358
            # wtypes.h contains IID_IProcessInitControl, for example
359
            return
1✔
360

361
        # Hm.  The variable MAY be a #define'd symbol that we have
362
        # artifically created, or it may be an exported variable that
363
        # is not in the libraries that we search.  Anyway, if it has
364
        # no tp.init value we can't generate code for it anyway, so we
365
        # drop it.
366
        # if tp.init is None:
367
        #    self._notfound_variables += 1
368
        #    return
369
        # el
370
        if isinstance(tp.init, typedesc.FunctionType):
1✔
371
            _args = [x for x in tp.typ.iterArgNames()]
1✔
372
            print("%s = %s # args: %s" % (tp.name, self.type_name(tp.init), _args), file=self.stream)
1✔
373
            self.names.append(tp.name)
1✔
374
            return
1✔
375
        elif isinstance(tp.typ, typedesc.PointerType) or isinstance(tp.typ, typedesc.ArrayType):
1✔
376
            if isinstance(tp.typ.typ, typedesc.FundamentalType) and (
1✔
377
                tp.typ.typ.name in ["c_ubyte", "c_char", "c_wchar"]
378
            ):
379
                # string
380
                # FIXME a char * is not a python string.
381
                # we should output a cstring() construct.
382
                init_value = repr(tp.init)
1✔
383
            elif isinstance(tp.typ.typ, typedesc.FundamentalType) and (
1✔
384
                "int" in tp.typ.typ.name or "long" in tp.typ.typ.name
385
            ):
386
                # array of number
387
                # CARE: size of elements must match size of array
388
                # init_value = repr(tp.init)
389
                init_value = "[%s]" % ",".join([str(x) for x in tp.init])
1✔
390
                # we do NOT want Variable to be described as ctypes object
391
                # when we can have a python abstraction for them.
392
                # init_value_type = self.type_name(tp.typ, False)
393
                # init_value = "(%s)(%s)"%(init_value_type,init_value)
394
            elif isinstance(tp.typ.typ, typedesc.Structure):
1✔
395
                self._generate(tp.typ.typ)
×
396
                init_value = self.type_name(tp.typ, False) + "()"
×
397
            else:
398
                if tp.init is not None:
1✔
399
                    init_value = tp.init
1✔
400
                else:
401
                    init_value = self.type_name(tp.typ, False) + "()"
×
402

403
        elif isinstance(tp.typ, typedesc.Structure):
1✔
404
            init_value = self.type_name(tp.typ, False)
1✔
405
        elif isinstance(tp.typ, typedesc.FundamentalType) and tp.typ.name in [
1✔
406
            "c_ubyte",
407
            "c_char",
408
            "c_wchar",
409
        ]:
410
            if tp.init is not None:
1✔
411
                init_value = repr(tp.init)
1✔
412
            else:
413
                init_value = "'\\x00'"
1✔
414
        else:
415
            # we want to have FundamentalType variable use the actual
416
            # type default, and not be a python ctypes object
417
            # if init_value is None:
418
            #    init_value = ''; # use default ctypes object constructor
419
            # init_value = "%s(%s)"%(self.type_name(tp.typ, False), init_value)
420
            if tp.init is not None:
1✔
421
                # TODO, check that if tp.init is a string literal
422
                #  and that there is a definition for it ?
423
                init_value = tp.init
1✔
424
            elif tp.typ.name in ["c_float", "c_double", "c_longdouble"]:
1✔
425
                init_value = 0.0
1✔
426
            else:
427
                # integers
428
                init_value = 0
1✔
429
        #
430
        # print it out
431
        print("%s = %s # Variable %s" % (tp.name, init_value, self.type_name(tp.typ, False)), file=self.stream)
1✔
432
        #
433
        self.names.append(tp.name)
1✔
434

435
    _enumvalues = 0
1✔
436

437
    def EnumValue(self, tp):
1✔
438
        # FIXME should be in parser
439
        value = int(tp.value)
1✔
440
        print("%s = %d" % (tp.name, value), file=self.stream)
1✔
441
        self.names.append(tp.name)
1✔
442
        self._enumvalues += 1
1✔
443

444
    _enumtypes = 0
1✔
445

446
    def Enumeration(self, tp):
1✔
447
        if self.generate_comments:
1✔
448
            self.print_comment(tp)
×
449
        print("", file=self.stream)
1✔
450
        if tp.name:
1✔
451
            print("# values for enumeration '%s'" % tp.name, file=self.stream)
1✔
452
        else:
453
            print("# values for unnamed enumeration", file=self.stream)
×
454
        print("%s__enumvalues = {" % tp.name, file=self.stream)
1✔
455
        for item in tp.values:
1✔
456
            print("    %s: '%s'," % (int(item.value), item.name), file=self.stream)
1✔
457
        print("}", file=self.stream)
1✔
458

459
        # Some enumerations have the same name for the enum type
460
        # and an enum value.  Excel's XlDisplayShapes is such an example.
461
        # Since we don't have separate namespaces for the type and the values,
462
        # we generate the TYPE last, overwriting the value. XXX
463
        for item in tp.values:
1✔
464
            self._generate(item)
1✔
465
        if tp.name:
1✔
466
            # Enums can be forced to occupy less space than an int when the compiler flag '-fshort-enums' is set.
467
            # The size adjustment is done when possible, depending on the values of the enum.
468
            # In any case, we should trust the enum size returned by the compiler.
469
            #
470
            # Furthermore, in order to obtain a correct (un)signed representation in Python,
471
            # the signedness of the enum is deduced from the sign of enum values.
472
            # If there is not any negative value in the enum, then the resulting ctype will be unsigned.
473
            # Sources:
474
            #   https://stackoverflow.com/a/54527229/1641819
475
            #   https://stackoverflow.com/a/56432050/1641819
476

477
            # Look for any negative value in enum
478
            has_negative = False
1✔
479
            for item in tp.values:
1✔
480
                if item.value < 0:
1✔
481
                    has_negative = True
1✔
482
                    break
1✔
483

484
            # Determine enum type depending on its size and signedness
485
            if tp.size == 1:
1✔
486
                enum_ctype = 'ctypes.c_int8' if has_negative else 'ctypes.c_uint8'
1✔
487
            elif tp.size == 2:
1✔
488
                enum_ctype = 'ctypes.c_int16' if has_negative else 'ctypes.c_uint16'
1✔
489
            elif tp.size == 4:
1✔
490
                enum_ctype = 'ctypes.c_int32' if has_negative else 'ctypes.c_uint32'
1✔
491
            elif tp.size == 8:
1✔
492
                enum_ctype = 'ctypes.c_int64' if has_negative else 'ctypes.c_uint64'
1✔
493
            else:
494
                enum_ctype = 'ctypes.c_int' if has_negative else 'ctypes.c_uint'
×
495

496
            print("%s = %s # enum" % (tp.name, enum_ctype), file=self.stream)
1✔
497
            self.names.append(tp.name)
1✔
498
        self._enumtypes += 1
1✔
499

500
    def get_undeclared_type(self, item):
1✔
501
        """
502
        Checks if a typed has already been declared in the python output
503
        or is a builtin python type.
504
        """
505
        if item.name in self.head_generated:
1✔
506
            return None
×
507
        if item in self.done:
1✔
508
            return None
1✔
509
        if isinstance(item, typedesc.FundamentalType):
×
510
            return None
×
511
        if isinstance(item, typedesc.PointerType):
×
512
            return self.get_undeclared_type(item.typ)
×
513
        if isinstance(item, typedesc.ArrayType):
×
514
            return self.get_undeclared_type(item.typ)
×
515
        # else its an undeclared structure.
516
        return item
×
517

518
    def _get_undefined_head_dependencies(self, struct):
1✔
519
        """Return head dependencies on other record types.
520
        Head dependencies is exclusive of body dependency. It's one or the other.
521
        """
522
        r = dict()
1✔
523
        for m in struct.members:
1✔
524
            if isinstance(m.type, typedesc.PointerType) and typedesc.is_record(m.type.typ):
1✔
525
                r[m.type] = None
1✔
526
        # remove all already defined heads
527
        r = [_ for _ in r if _.name not in self.head_generated]
1✔
528
        return r
1✔
529

530
    def _get_undefined_body_dependencies(self, struct):
1✔
531
        """Return head dependencies on other record types.
532
        Head dependencies is exclusive of body dependency. It's one or the other.
533
        """
534
        r = dict()
1✔
535
        for m in struct.members:
1✔
536
            if isinstance(m.type, typedesc.ArrayType) and typedesc.is_record(m.type.typ):
1✔
537
                r[m.type.typ] = None
1✔
538
            elif typedesc.is_record(m.type):
1✔
539
                r[m.type] = None
1✔
540
            elif m.type not in self.done:
1✔
541
                r[m.type] = None
1✔
542
        # remove all already defined bodies
543
        r = [_ for _ in r if _.name not in self.body_generated]
1✔
544
        return r
1✔
545

546
    _structures = 0
1✔
547

548
    def Structure(self, struct):
1✔
549
        if struct.name in self.head_generated and struct.name in self.body_generated:
1✔
550
            self.done[struct] = True
×
551
            return
×
552
        self.enable_structure_type()
1✔
553
        self._structures += 1
1✔
554
        depends = set()
1✔
555
        # We only print a empty struct.
556
        if struct.members is None:
1✔
557
            log.info("No members for: %s", struct.name)
1✔
558
            self._generate(struct.get_head(), False)
1✔
559
            return
1✔
560
        # look in bases class for dependencies
561
        # FIXME - need a real dependency graph maker
562
        # remove myself, just in case.
563
        if struct in self.done:
1✔
564
            del self.done[struct]
1✔
565
        # checks members dependencies in bases
566
        for b in struct.bases:
1✔
567
            depends.update([self.get_undeclared_type(m.type) for m in b.members])
1✔
568
        depends.discard(None)
1✔
569
        if len(depends) > 0:
1✔
570
            log.debug("Generate %s DEPENDS for Bases %s", struct.name, depends)
×
571
            for dep in depends:
×
572
                self._generate(dep)
×
573

574
        # checks members dependencies
575
        # test_record_ordering head does not mean declared. _fields_ mean declared
576
        # CPOINTER members just require a class definition
577
        # whereas members that are non pointers require a full _fields_ declaration
578
        # before this record body is defined fully
579
        # depends.update([self.get_undeclared_type(m.type)
580
        #                 for m in struct.members])
581
        # self.done[struct] = True
582
        # hard dependencies for members types that are not pointer but records
583
        # soft dependencies for members pointers to record
584
        undefined_head_dependencies = self._get_undefined_head_dependencies(struct)
1✔
585
        undefined_body_dependencies = self._get_undefined_body_dependencies(struct)
1✔
586

587
        if len(undefined_body_dependencies) == 0:
1✔
588
            if len(undefined_head_dependencies) == 0:
1✔
589
                # generate this head and body in one go
590
                # if struct.get_head() not in self.done:
591
                if struct.name not in self.head_generated:
1✔
592
                    self._generate(struct.get_head(), True)
1✔
593
                    self._generate(struct.get_body(), True)
1✔
594
                else:
595
                    self._generate(struct.get_body(), False)
1✔
596
            else:
597
                # generate this head first, to avoid recursive issue, then the dep, then this body
598
                self._generate(struct.get_head(), False)
×
599
                for dep in undefined_head_dependencies:
×
600
                    self._generate(dep)
×
601
                self._generate(struct.get_body(), False)
×
602
        else:
603
            # hard dep on defining the body of these dependencies
604
            # generate this head first, to avoid recursive issue, then the dep, then this body
605
            self._generate(struct.get_head(), False)
1✔
606
            for dep in undefined_head_dependencies:
1✔
607
                self._generate(dep)
1✔
608
            for dep in undefined_body_dependencies:
1✔
609
                self._generate(dep)
1✔
610
            for dep in undefined_body_dependencies:
1✔
611
                if isinstance(dep, typedesc.Structure):
1✔
612
                    self._generate(dep.get_body(), False)
1✔
613
            self._generate(struct.get_body(), False)
1✔
614
        # we defined ourselve
615
        self.done[struct] = True
1✔
616

617
    Union = Structure
1✔
618

619
    def StructureHead(self, head, inline=False):
1✔
620
        if head.name in self.head_generated:
1✔
621
            log.debug("Skipping - Head already generated for %s", head.name)
×
622
            return
×
623
        log.debug("Head start for %s inline:%s", head.name, inline)
1✔
624
        for struct in head.struct.bases:
1✔
625
            self._generate(struct.get_head())
1✔
626
            # add dependencies
627
            self.more[struct] = True
1✔
628
        basenames = [self.type_name(b) for b in head.struct.bases]
1✔
629
        if basenames:
1✔
630
            # method_names = [m.name for m in head.struct.members if type(m) is typedesc.Method]
631
            print(
1✔
632
                "class %s(%s):" % (head.struct.name, ", ".join(basenames)),
633
                file=self.stream,
634
            )
635
        else:
636
            # methods = [m for m in head.struct.members if type(m) is typedesc.Method]
637
            if isinstance(head.struct, typedesc.Structure):
1✔
638
                # Inherit from our ctypes.Structure extension
639
                print("class %s(Structure):" % head.struct.name, file=self.stream)
1✔
640
            elif isinstance(head.struct, typedesc.Union):
1✔
641
                print("class %s(Union):" % head.struct.name, file=self.stream)
1✔
642
        if not inline:
1✔
643
            print("    pass\n", file=self.stream)
1✔
644
        # special empty struct
645
        if inline and not head.struct.members:
1✔
646
            print("    pass\n", file=self.stream)
1✔
647
        self.names.append(head.struct.name)
1✔
648
        log.debug("Head finished for %s", head.name)
1✔
649
        self.head_generated.add(head.name)
1✔
650

651
    def StructureBody(self, body, inline=False):
1✔
652
        if body.name in self.body_generated:
1✔
653
            log.debug("Skipping - Body already generated for %s", body.name)
×
654
            return
×
655
        log.debug("Body start for %s", body.name)
1✔
656
        fields = []
1✔
657
        methods = []
1✔
658
        for m in body.struct.members:
1✔
659
            if isinstance(m, typedesc.Field):
1✔
660
                fields.append(m)
1✔
661
                # if type(m.type) is typedesc.Typedef:
662
                #    self._generate(get_real_type(m.type))
663
                # self._generate(m.type)
664
            elif isinstance(m, typedesc.Method):
×
665
                methods.append(m)
×
666
                # self._generate(m.returns)
667
                # self.generate_all(m.iterArgTypes())
668
            elif isinstance(m, typedesc.Ignored):
×
669
                pass
×
670
        # handled inline Vs dependent
671
        log.debug("body inline:%s for structure %s", inline, body.struct.name)
1✔
672
        if not inline:
1✔
673
            prefix = "%s." % body.struct.name
1✔
674
        else:
675
            prefix = "    "
1✔
676
        if methods:
1✔
677
            # XXX we have parsed the COM interface methods but should
678
            # we emit any code for them?
679
            pass
×
680
        # LXJ: we pack all the time, because clang gives a precise field offset
681
        # per target architecture. No need to defer to ctypes logic for that.
682
        if fields:
1✔
683
            print("%s_pack_ = 1 # source:%s" % (prefix, body.struct.packed), file=self.stream)
1✔
684

685
        if body.struct.bases:
1✔
686
            if len(body.struct.bases) == 1:  # its a Struct or a simple Class
1✔
687
                self._generate(body.struct.bases[0].get_body(), inline)
1✔
688
            else:  # we have a multi-parent inheritance
689
                for b in body.struct.bases:
×
690
                    self._generate(b.get_body(), inline)
×
691
        # field definition normally span several lines.
692
        # Before we generate them, we need to 'import' everything they need.
693
        # So, call type_name for each field once,
694
        for f in fields:
1✔
695
            self.type_name(f.type)
1✔
696

697
        # unnamed fields get autogenerated names "_0", "_1", "_2", "_3", ...
698
        unnamed_fields = {}
1✔
699
        for f in fields:
1✔
700
            # _anonymous_ fields are fields of type Structure or Union,
701
            # that have no name.
702
            if f.is_anonymous and isinstance(f.type, (typedesc.Structure, typedesc.Union)):
1✔
703
                # anonymous types can have a member name
704
                # un-named anonymous record come here with a name == ''
705
                if f.name == '':
1✔
706
                    unnamed_fields[f] = "_%d" % len(unnamed_fields)
1✔
707
                # otherwise, we want to keep that field's name
708
        if unnamed_fields:
1✔
709
            unnamed_fields_str = ", ".join("'%s'" % _ for _ in unnamed_fields.values())
1✔
710
            print("%s_anonymous_ = (%s,)" % (prefix, unnamed_fields_str), file=self.stream)
1✔
711
        if len(fields) > 0:
1✔
712
            print("%s_fields_ = [" % prefix, file=self.stream)
1✔
713
            if self.generate_locations and body.struct.location:
1✔
714
                print("    # %s %s" % body.struct.location, file=self.stream)
×
715
            for f in fields:
1✔
716
                fieldname = unnamed_fields.get(f, f.name)
1✔
717
                type_name = self.type_name(f.type)
1✔
718
                # handle "__" prefixed names by using a wrapper
719
                if type_name.startswith("__"):
1✔
720
                    type_name = "globals()['%s']" % type_name
1✔
721
                # a bitfield needs a triplet
722
                if f.is_bitfield is False:
1✔
723
                    print("    ('%s', %s)," % (fieldname, type_name), file=self.stream)
1✔
724
                else:
725
                    # FIXME: Python bitfield is int32 only.
726
                    # from clang.cindex import TypeKind
727
                    # print fieldname
728
                    # import code
729
                    # code.interact(local=locals())
730
                    print("    ('%s', %s, %s)," % (fieldname, self.type_name(f.type), f.bits), file=self.stream)
1✔
731
            if inline:
1✔
732
                print(prefix, end=" ", file=self.stream)
1✔
733
            print("]\n", file=self.stream)
1✔
734
        log.debug("Body finished for %s", body.name)
1✔
735
        self.body_generated.add(body.name)
1✔
736

737
    def find_library_with_func(self, func):
1✔
738
        if hasattr(func, "dllname"):
1✔
739
            return func.dllname
×
740
        name = func.name
1✔
741
        if os.name == "posix" and sys.platform == "darwin":
1✔
742
            name = "_%s" % name
×
743
        for dll in self.searched_dlls:
1✔
744
            try:
1✔
745
                getattr(dll, name)
1✔
746
            except AttributeError:
×
747
                pass
×
748
            else:
749
                return dll
1✔
750
        return None
1✔
751

752
    _c_libraries = None
1✔
753

754
    def need_CLibraries(self):
1✔
755
        # Create a '_libraries' doctionary in the generated code, if
756
        # it not yet exists. Will map library pathnames to loaded libs.
757
        if self._c_libraries is None:
1✔
758
            self._c_libraries = {}
1✔
759
            print("_libraries = {}", file=self.imports)
1✔
760

761
    _stdcall_libraries = None
1✔
762

763
    def need_WinLibraries(self):
1✔
764
        # Create a '_stdcall_libraries' doctionary in the generated code, if
765
        # it not yet exists. Will map library pathnames to loaded libs.
766
        if self._stdcall_libraries is None:
×
767
            self._stdcall_libraries = {}
×
768
            print("_stdcall_libraries = {}", file=self.imports)
×
769

770
    _dll_stub_issued = False
1✔
771

772
    def get_sharedlib(self, library, cc, stub=False):
1✔
773
        # deal with missing -l with a stub
774
        stub_comment = ""
1✔
775
        if stub and not self._dll_stub_issued:
1✔
776
            self._dll_stub_issued = True
1✔
777
            stub_comment = " FunctionFactoryStub() # "
1✔
778
            print("""class FunctionFactoryStub:
1✔
779
    def __getattr__(self, _):
780
      return ctypes.CFUNCTYPE(lambda y:y)
781
""", file=self.imports)
782
            print("# libraries['FIXME_STUB'] explanation", file=self.imports)
1✔
783
            print("# As you did not list (-l libraryname.so) a library that exports this function", file=self.imports)
1✔
784
            print("# This is a non-working stub instead. ", file=self.imports)
1✔
785
            print("# You can either re-run clan2py with -l /path/to/library.so",file=self.imports)
1✔
786
            print("# Or manually fix this by comment the ctypes.CDLL loading", file=self.imports)
1✔
787

788
        # generate windows call
789
        if cc == "stdcall":
1✔
790
            self.need_WinLibraries()
×
791
            if library._name not in self._stdcall_libraries:
×
792
                _ = "_stdcall_libraries[%r] =%s ctypes.WinDLL(%r)" % (library._name, stub_comment, library._filepath)
×
793
                print(_, file=self.imports)
×
794
                self._stdcall_libraries[library._name] = None
×
795
            return "_stdcall_libraries[%r]" % library._name
×
796

797
        # generate clinux call
798
        self.need_CLibraries()
1✔
799
        if self.preloaded_dlls != []:
1✔
800
            global_flag = ", mode=ctypes.RTLD_GLOBAL"
×
801
        else:
802
            global_flag = ""
1✔
803
        if library._name not in self._c_libraries:
1✔
804
            print("_libraries[%r] =%s ctypes.CDLL(%r%s)" % (library._name, stub_comment, library._filepath, global_flag),
1✔
805
                  file=self.imports)
806
            self._c_libraries[library._name] = None
1✔
807
        return "_libraries[%r]" % library._name
1✔
808

809
    _STRING_defined = False
1✔
810

811
    def need_STRING(self):
1✔
812
        if self._STRING_defined:
×
813
            return
×
814
        print("STRING = c_char_p", file=self.imports)
×
815
        self._STRING_defined = True
×
816
        return
×
817

818
    _WSTRING_defined = False
1✔
819

820
    def need_WSTRING(self):
1✔
821
        if self._WSTRING_defined:
×
822
            return
×
823
        print("WSTRING = c_wchar_p", file=self.imports)
×
824
        self._WSTRING_defined = True
×
825
        return
×
826

827
    def Function(self, func):
1✔
828
        # FIXME: why do we call this ? it does nothing
829
        if self.generate_comments:
1✔
830
            self.print_comment(func)
×
831
        self._generate(func.returns)
1✔
832
        self.generate_all(func.iterArgTypes())
1✔
833

834
        # useful code
835
        args = [self.type_name(a) for a in func.iterArgTypes()]
1✔
836
        cc = "cdecl"
1✔
837
        if "__stdcall__" in func.attributes:
1✔
838
            cc = "stdcall"
×
839

840
        #
841
        library = self.find_library_with_func(func)
1✔
842
        if library:
1✔
843
            libname = self.get_sharedlib(library, cc)
1✔
844
        else:
845

846
            class LibraryStub:
1✔
847
                _filepath = "FIXME_STUB"
1✔
848
                _name = "FIXME_STUB"
1✔
849

850
            libname = self.get_sharedlib(LibraryStub(), cc, stub=True)
1✔
851

852
        argnames = [a or "p%d" % (i + 1) for i, a in enumerate(func.iterArgNames())]
1✔
853

854
        if self.generate_locations and func.location:
1✔
855
            print("# %s %s" % func.location, file=self.stream)
×
856
        # Generate the function decl code
857
        print("try:", file=self.stream)
1✔
858
        print("    %s = %s.%s" % (func.name, libname, func.name), file=self.stream)
1✔
859
        print("    %s.restype = %s" % (func.name, self.type_name(func.returns)), file=self.stream)
1✔
860
        if self.generate_comments:
1✔
861
            print("# %s(%s)" % (func.name, ", ".join(argnames)), file=self.stream)
×
862
        print("    %s.argtypes = [%s]" % (func.name, ", ".join(args)), file=self.stream)
1✔
863
        print("except AttributeError:", file=self.stream)
1✔
864
        print("    pass", file=self.stream)
1✔
865

866
        if self.generate_docstrings:
1✔
867

868
            def typeString(typ):
×
869
                if hasattr(typ, "name"):
×
870
                    return typ.name
×
871
                elif hasattr(typ, "typ") and isinstance(typ, typedesc.PointerType):
×
872
                    return typeString(typ.typ) + " *"
×
873
                else:
874
                    return "unknown"
×
875

876
            argsAndTypes = zip([typeString(t) for t in func.iterArgTypes()], argnames)
×
877
            print(
×
878
                '{funcname}.__doc__ = """{ret} {funcname}({args})\n'
879
                '    {file}:{line}"""'.format(
880
                    funcname=func.name,
881
                    args=", ".join(["%s %s" % i for i in argsAndTypes]),
882
                    file=func.location[0],
883
                    line=func.location[1],
884
                    ret=typeString(func.returns),
885
                ),
886
                file=self.stream,
887
            )
888

889
        self.names.append(func.name)
1✔
890
        self._functiontypes += 1
1✔
891
        return
1✔
892

893
    def FundamentalType(self, _type):
1✔
894
        """Returns the proper ctypes class name for a fundamental type
895

896
        1) activates generation of appropriate headers for
897
        ## int128_t
898
        ## c_long_double_t
899
        2) return appropriate name for type
900
        """
901
        log.debug("HERE in FundamentalType for %s %s", _type, _type.name)
1✔
902
        if _type.name in ["None", "c_long_double_t", "c_uint128", "c_int128"]:
1✔
903
            self.enable_fundamental_type_wrappers()
1✔
904
            return _type.name
1✔
905
        return "ctypes.%s" % _type.name
1✔
906

907
    ########
908

909
    def _generate(self, item, *args):
1✔
910
        """ wraps execution of specific methods."""
911
        if item in self.done:
1✔
912
            return
1✔
913
        # verbose output with location.
914
        if self.generate_locations and item.location:
1✔
915
            print("# %s:%d" % item.location, file=self.stream)
×
916
        if self.generate_comments:
1✔
917
            self.print_comment(item)
×
918
        log.debug("generate %s, %s", item.__class__.__name__, item.name)
1✔
919
        # to avoid infinite recursion, we have to mark it as done
920
        # before actually generating the code.
921
        self.done[item] = True
1✔
922
        # go to specific treatment
923
        mth = getattr(self, type(item).__name__)
1✔
924
        mth(item, *args)
1✔
925
        return
1✔
926

927
    def print_comment(self, item):
1✔
928
        if item.comment is None:
×
929
            return
×
930
        for _ in textwrap.wrap(item.comment, 78):
×
931
            print("# %s" % _, file=self.stream)
×
932
        return
×
933

934
    def generate_all(self, items):
1✔
935
        for item in items:
1✔
936
            self._generate(item)
1✔
937
        return
1✔
938

939
    def generate_items(self, items):
1✔
940
        # items = set(items)
941
        loops = 0
1✔
942
        while items:
1✔
943
            loops += 1
1✔
944
            self.more = collections.OrderedDict()
1✔
945
            self.generate_all(items)
1✔
946

947
            # items |= self.more , but keeping ordering
948
            _s = set(items)
1✔
949
            [items.append(k) for k in self.more.keys() if k not in _s]
1✔
950

951
            # items -= self.done, but keep ordering
952
            _done = self.done.keys()
1✔
953
            for i in list(items):
1✔
954
                if i in _done:
1✔
955
                    items.remove(i)
1✔
956

957
        return loops
1✔
958

959
    def generate(self, parser, items):
1✔
960
        self.generate_headers(parser)
1✔
961
        return self.generate_code(items)
1✔
962

963
    def generate_code(self, items):
1✔
964
        print(
1✔
965
            "\n".join(
966
                ["ctypes.CDLL('%s', ctypes.RTLD_GLOBAL)" % preloaded_dll for preloaded_dll in self.preloaded_dlls]
967
            ),
968
            file=self.imports,
969
        )
970
        loops = self.generate_items(items)
1✔
971

972
        self.output.write(self.imports.getvalue())
1✔
973
        self.output.write("\n\n")
1✔
974
        self.output.write(self.stream.getvalue())
1✔
975

976
        text = "__all__ = \\"
1✔
977
        # text Wrapper doesn't work for the first line in certain cases.
978
        print(text, file=self.output)
1✔
979
        # doesn't work for the first line in certain cases.
980
        wrapper = textwrap.TextWrapper(break_long_words=False, initial_indent="    ", subsequent_indent="    ")
1✔
981
        text = "[%s]" % ", ".join([repr(str(n)) for n in sorted(self.names)])
1✔
982
        for line in wrapper.wrap(text):
1✔
983
            print(line, file=self.output)
1✔
984

985
        return loops
1✔
986

987
    def print_stats(self, stream):
1✔
988
        total = (
×
989
            self._structures
990
            + self._functiontypes
991
            + self._enumtypes
992
            + self._typedefs
993
            + self._pointertypes
994
            + self._arraytypes
995
        )
996
        print("###########################", file=stream)
×
997
        print("# Symbols defined:", file=stream)
×
998
        print("#", file=stream)
×
999
        print("# Variables:          %5d" % self._variables, file=stream)
×
1000
        print("# Struct/Unions:      %5d" % self._structures, file=stream)
×
1001
        print("# Functions:          %5d" % self._functiontypes, file=stream)
×
1002
        print("# Enums:              %5d" % self._enumtypes, file=stream)
×
1003
        print("# Enum values:        %5d" % self._enumvalues, file=stream)
×
1004
        print("# Typedefs:           %5d" % self._typedefs, file=stream)
×
1005
        print("# Pointertypes:       %5d" % self._pointertypes, file=stream)
×
1006
        print("# Arraytypes:         %5d" % self._arraytypes, file=stream)
×
1007
        print("# unknown functions:  %5d" % self._notfound_functiontypes, file=stream)
×
1008
        print("# unknown variables:  %5d" % self._notfound_variables, file=stream)
×
1009
        print("#", file=stream)
×
1010
        print("# Total symbols: %5d" % total, file=stream)
×
1011
        print("###########################", file=stream)
×
1012
        return
×
1013

1014

1015
################################################################
1016

1017
class CodeTranslator:
1✔
1018
    """
1019
    Organiser class to take C files in input, and produce python code in a standard fashion
1020
    """
1021
    def __init__(self, cfg: config.CodegenConfig):
1✔
1022
        self.cfg = cfg
1✔
1023
        self.parser = None
1✔
1024
        self.generator = None
1✔
1025
        self.items = []
1✔
1026
        self.filtered_items = []
1✔
1027

1028
    def preload_dlls(self):
1✔
1029
        # FIXME
1030
        self.cfg.preloaded_dlls = [Library(name, nm="nm") for name in self.cfg.preloaded_dlls]
1✔
1031

1032
    def make_clang_parser(self):
1✔
1033
        self.parser = clangparser.Clang_Parser(self.cfg.clang_opts)
1✔
1034
        if typedesc.Macro in self.cfg.types:
1✔
1035
            self.parser.activate_macros_parsing()
×
1036
        if self.cfg.generate_comments:
1✔
1037
            self.parser.activate_comment_parsing()
×
1038
        # FIXME
1039
        # if self.cfg.filter_location:
1040
        #     parser.filter_location(srcfiles)
1041
        return self.parser
1✔
1042

1043
    def parse_input_string(self, input_io):
1✔
1044
        if self.parser is None:
1✔
1045
            self.make_clang_parser()
1✔
1046
        self.parser.parse_string(input_io)
1✔
1047
        # get the typedesc C types items
1048
        self.items.extend(self.parser.get_result())
1✔
1049

1050
    def parse_input_file(self, src_file):
1✔
1051
        if self.parser is None:
1✔
1052
            self.make_clang_parser()
1✔
1053
        self.parser.parse(src_file)
1✔
1054
        # get the typedesc C types items
1055
        self.items.extend(self.parser.get_result())
1✔
1056

1057
    def parse_input_files(self, src_files: list):
1✔
1058
        if self.parser is None:
1✔
1059
            self.make_clang_parser()
1✔
1060
        # filter location with clang.
1061
        if self.cfg.filter_location:
1✔
1062
            self.parser.filter_location(src_files)
1✔
1063
        #
1064
        for srcfile in src_files:
1✔
1065
            # verifying that is really a file we can open
1066
            with open(srcfile):
1✔
1067
                pass
1✔
1068
            log.debug("Parsing input file %s", srcfile)
1✔
1069
            self.parser.parse(srcfile)
1✔
1070
        # get the typedesc C types items
1071
        self.items.extend(self.parser.get_result())
1✔
1072

1073
    def make_code_generator(self, output):
1✔
1074
        self.generator = Generator(output, cfg=self.cfg)
1✔
1075
        return self.generator
1✔
1076

1077
    def generate_code(self, output):
1✔
1078
        if self.generator is None:
1✔
1079
            self.make_code_generator(output)
1✔
1080
        self.filtered_items = list(self.items)
1✔
1081
        log.debug("%d items before filtering", len(self.filtered_items))
1✔
1082
        self.filter_types()
1✔
1083
        self.filter_symbols()
1✔
1084
        self.filter_expressions()
1✔
1085
        log.debug("Left with %d items after filtering", len(self.filtered_items))
1✔
1086
        loops = self.generator.generate(self.parser, self.filtered_items)
1✔
1087
        if self.cfg.verbose:
1✔
1088
            self.generator.print_stats(sys.stderr)
×
1089
            log.info("needed %d loop(s)", loops)
×
1090

1091
    def filter_types(self):
1✔
1092
        self.filtered_items = [i for i in self.filtered_items if i.__class__ in self.cfg.types]
1✔
1093

1094
    def filter_symbols(self):
1✔
1095
        if len(self.cfg.symbols) == 0:
1✔
1096
            return
1✔
1097
        todo = []
×
1098
        syms = set(self.cfg.symbols)
×
1099
        for i in self.filtered_items:
×
1100
            if i.name in syms:
×
1101
                todo.append(i)
×
1102
                syms.remove(i.name)
×
1103
            else:
1104
                log.debug("not generating {}: not a symbol".format(i.name))
×
1105
        if syms:
×
1106
            log.warning("symbols not found %s", [str(x) for x in list(syms)])
×
1107
        self.filtered_items = todo
×
1108

1109
    def filter_expressions(self):
1✔
1110
        if len(self.cfg.expressions) == 0:
1✔
1111
            return
1✔
1112
        todo = []
×
1113
        for s in self.cfg.expressions:
×
1114
            log.debug("regexp: looking for %s", s.pattern)
×
1115
            for i in self.filtered_items:
×
1116
                log.debug("regexp: i.name is %s", i.name)
×
1117
                if i.name is None:
×
1118
                    continue
×
1119
                match = s.match(i.name)
×
1120
                # if we only want complete matches:
1121
                if match and match.group() == i.name:
×
1122
                    todo.append(i)
×
1123
                    continue
×
1124
                # if we follow our own documentation,
1125
                # allow regular expression match of any part of name:
1126
                match = s.search(i.name)
×
1127
                if match:
×
1128
                    todo.append(i)
×
1129
                    continue
×
1130
        self.filtered_items = todo
×
1131

1132

1133
# easy to use API.
1134

1135
def translate(input_io, outfile=None, cfg=None):
1✔
1136
    """
1137
        Take a readable C like input readable and translate it to python.
1138
    """
1139
    cfg = cfg or config.CodegenConfig()
1✔
1140
    translator = CodeTranslator(cfg)
1✔
1141
    translator.preload_dlls()
1✔
1142
    translator.parse_input_string(input_io)
1✔
1143
    # gen python code
1144
    if outfile:
1✔
1145
        return translator.generate_code(outfile)
×
1146
    # otherwise return python
1147
    output = io.StringIO()
1✔
1148
    translator.generate_code(output)
1✔
1149
    output.seek(0)
1✔
1150
    # inject generated code in python namespace
1151
    ignore_coding = output.readline()
1✔
1152
    # exec ofi.getvalue() in namespace
1153
    output = ''.join(output.readlines())
1✔
1154
    namespace = {}
1✔
1155
    exec(output, namespace)
1✔
1156
    return util.ADict(namespace)
1✔
1157

1158

1159
def translate_files(source_files, outfile=None, cfg: config.CodegenConfig=None):
1✔
1160
    """
1161
    Translate the content of source_files in python code in outfile
1162

1163
    source_files: list of filenames or single filename
1164
    """
1165
    cfg = cfg or config.CodegenConfig()
1✔
1166
    translator = CodeTranslator(cfg)
1✔
1167
    translator.preload_dlls()
1✔
1168
    if isinstance(source_files, list):
1✔
1169
        translator.parse_input_files(source_files)
1✔
1170
    else:
1171
        translator.parse_input_file(source_files)
1✔
1172
    log.debug("Input was parsed")
1✔
1173
    if outfile:
1✔
1174
        return translator.generate_code(outfile)
1✔
1175
    # otherwise return python
1176
    output = io.StringIO()
1✔
1177
    translator.generate_code(output)
1✔
1178
    output.seek(0)
1✔
1179
    # inject generated code in python namespace
1180
    ignore_coding = output.readline()
1✔
1181
    # exec ofi.getvalue() in namespace
1182
    output = ''.join(output.readlines())
1✔
1183
    namespace = {}
1✔
1184
    exec(output, namespace)
1✔
1185
    return util.ADict(namespace)
1✔
1186

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc