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

trolldbois / ctypeslib / 4773564691

pending completion
4773564691

push

github

Loic Jaquemet
try without baipp

1601 of 1912 relevant lines covered (83.73%)

50.0 hits per line

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

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

5
from __future__ import print_function
48✔
6
from __future__ import unicode_literals
48✔
7

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

18
from clang.cindex import TypeKind
48✔
19

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

26
log = logging.getLogger("codegen")
48✔
27

28

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

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

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

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

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

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

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

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

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

165
    ################################################################
166

167
    _aliases = 0
60✔
168

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

178
    _macros = 0
179

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

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

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

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

249
    _typedefs = 0
60✔
250

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

281
            if isinstance(tp.typ, typedesc.Enumeration):
60✔
282
                print("%s__enumvalues = %s__enumvalues" % (name, self.type_name(tp.typ)), file=self.stream)
60✔
283
                self.names.append("%s__enumvalues" % name)
60✔
284

285
        self.names.append(tp.name)
60✔
286
        self._typedefs += 1
60✔
287
        return
60✔
288

289
    def _get_real_type(self, tp):
60✔
290
        # FIXME, kinda useless really.
291
        if isinstance(tp, typedesc.Typedef):
60✔
292
            if isinstance(tp.typ, typedesc.Typedef):
×
293
                raise TypeError("Nested loop in Typedef %s" % tp.name)
×
294
            return self._get_real_type(tp.typ)
×
295
        elif isinstance(tp, typedesc.CvQualifiedType):
60✔
296
            return self._get_real_type(tp.typ)
×
297
        return tp
60✔
298

299
    _arraytypes = 0
60✔
300

301
    def ArrayType(self, tp):
60✔
302
        self._generate(self._get_real_type(tp.typ))
60✔
303
        self._generate(tp.typ)
60✔
304
        self._arraytypes += 1
60✔
305

306
    _functiontypes = 0
60✔
307
    _notfound_functiontypes = 0
60✔
308

309
    def FunctionType(self, tp):
60✔
310
        self._generate(tp.returns)
60✔
311
        self.generate_all(tp.arguments)
60✔
312
        # print >> self.stream, "%s = %s # Functiontype " % (
313
        # self.type_name(tp), [self.type_name(a) for a in tp.arguments])
314
        self._functiontypes += 1
60✔
315

316
    def Argument(self, tp):
60✔
317
        self._generate(tp.typ)
60✔
318

319
    _pointertypes = 0
60✔
320

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

334
    def CvQualifiedType(self, tp):
60✔
335
        self._generate(tp.typ)
×
336

337
    _variables = 0
60✔
338
    _notfound_variables = 0
60✔
339

340
    def Variable(self, tp):
60✔
341
        self._variables += 1
60✔
342
        if self.generate_comments:
60✔
343
            self.print_comment(tp)
×
344

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

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

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

430
    _enumvalues = 0
60✔
431

432
    def EnumValue(self, tp):
60✔
433
        # FIXME should be in parser
434
        value = int(tp.value)
60✔
435
        print("%s = %d" % (tp.name, value), file=self.stream)
60✔
436
        self.names.append(tp.name)
60✔
437
        self._enumvalues += 1
60✔
438

439
    _enumtypes = 0
60✔
440

441
    def Enumeration(self, tp):
60✔
442
        if self.generate_comments:
60✔
443
            self.print_comment(tp)
444
        print("", file=self.stream)
60✔
445
        if tp.name:
60✔
446
            print("# values for enumeration '%s'" % tp.name, file=self.stream)
60✔
447
        else:
448
            print("# values for unnamed enumeration", file=self.stream)
449
        print("%s__enumvalues = {" % tp.name, file=self.stream)
60✔
450
        for item in tp.values:
60✔
451
            print("    %s: '%s'," % (int(item.value), item.name), file=self.stream)
60✔
452
        print("}", file=self.stream)
60✔
453

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

472
            # Look for any negative value in enum
473
            has_negative = False
60✔
474
            for item in tp.values:
60✔
475
                if item.value < 0:
60✔
476
                    has_negative = True
60✔
477
                    break
60✔
478

479
            # Determine enum type depending on its size and signedness
480
            if tp.size == 1:
60✔
481
                enum_ctype = 'ctypes.c_int8' if has_negative else 'ctypes.c_uint8'
60✔
482
            elif tp.size == 2:
60✔
483
                enum_ctype = 'ctypes.c_int16' if has_negative else 'ctypes.c_uint16'
60✔
484
            elif tp.size == 4:
60✔
485
                enum_ctype = 'ctypes.c_int32' if has_negative else 'ctypes.c_uint32'
60✔
486
            elif tp.size == 8:
60✔
487
                enum_ctype = 'ctypes.c_int64' if has_negative else 'ctypes.c_uint64'
60✔
488
            else:
489
                enum_ctype = 'ctypes.c_int' if has_negative else 'ctypes.c_uint'
490

491
            print("%s = %s # enum" % (tp.name, enum_ctype), file=self.stream)
60✔
492
            self.names.append(tp.name)
60✔
493
        self._enumtypes += 1
60✔
494

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

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

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

541
    _structures = 0
60✔
542

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

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

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

612
    Union = Structure
60✔
613

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

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

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

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

732
    def find_library_with_func(self, func):
60✔
733
        if hasattr(func, "dllname"):
60✔
734
            return func.dllname
735
        name = func.name
60✔
736
        if os.name == "posix" and sys.platform == "darwin":
60✔
737
            name = "_%s" % name
738
        for dll in self.searched_dlls:
60✔
739
            try:
60✔
740
                getattr(dll, name)
60✔
741
            except AttributeError:
742
                pass
743
            else:
744
                return dll
60✔
745
        return None
60✔
746

747
    _c_libraries = None
60✔
748

749
    def need_CLibraries(self):
60✔
750
        # Create a '_libraries' doctionary in the generated code, if
751
        # it not yet exists. Will map library pathnames to loaded libs.
752
        if self._c_libraries is None:
60✔
753
            self._c_libraries = {}
60✔
754
            print("_libraries = {}", file=self.imports)
60✔
755

756
    _stdcall_libraries = None
60✔
757

758
    def need_WinLibraries(self):
60✔
759
        # Create a '_stdcall_libraries' doctionary in the generated code, if
760
        # it not yet exists. Will map library pathnames to loaded libs.
761
        if self._stdcall_libraries is None:
762
            self._stdcall_libraries = {}
763
            print("_stdcall_libraries = {}", file=self.imports)
764

765
    _dll_stub_issued = False
60✔
766

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

783
        # generate windows call
784
        if cc == "stdcall":
785
            self.need_WinLibraries()
786
            if library._name not in self._stdcall_libraries:
787
                _ = "_stdcall_libraries[%r] =%s ctypes.WinDLL(%r)" % (library._name, stub_comment, library._filepath)
788
                print(_, file=self.imports)
789
                self._stdcall_libraries[library._name] = None
790
            return "_stdcall_libraries[%r]" % library._name
791

792
        # generate clinux call
793
        self.need_CLibraries()
794
        if self.preloaded_dlls != []:
795
            global_flag = ", mode=ctypes.RTLD_GLOBAL"
796
        else:
797
            global_flag = ""
798
        if library._name not in self._c_libraries:
799
            print("_libraries[%r] =%s ctypes.CDLL(%r%s)" % (library._name, stub_comment, library._filepath, global_flag),
800
                  file=self.imports)
801
            self._c_libraries[library._name] = None
802
        return "_libraries[%r]" % library._name
803

804
    _STRING_defined = False
805

806
    def need_STRING(self):
807
        if self._STRING_defined:
808
            return
809
        print("STRING = c_char_p", file=self.imports)
810
        self._STRING_defined = True
811
        return
812

813
    _WSTRING_defined = False
814

815
    def need_WSTRING(self):
816
        if self._WSTRING_defined:
817
            return
818
        print("WSTRING = c_wchar_p", file=self.imports)
819
        self._WSTRING_defined = True
820
        return
821

822
    def Function(self, func):
823
        # FIXME: why do we call this ? it does nothing
824
        if self.generate_comments:
825
            self.print_comment(func)
826
        self._generate(func.returns)
827
        self.generate_all(func.iterArgTypes())
828

829
        # useful code
830
        args = [self.type_name(a) for a in func.iterArgTypes()]
831
        cc = "cdecl"
832
        if "__stdcall__" in func.attributes:
833
            cc = "stdcall"
834

835
        #
836
        library = self.find_library_with_func(func)
837
        if library:
838
            libname = self.get_sharedlib(library, cc)
839
        else:
840

841
            class LibraryStub:
842
                _filepath = "FIXME_STUB"
843
                _name = "FIXME_STUB"
844

845
            libname = self.get_sharedlib(LibraryStub(), cc, stub=True)
846

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

849
        if self.generate_locations and func.location:
850
            print("# %s %s" % func.location, file=self.stream)
851
        # Generate the function decl code
852
        print("%s = %s.%s" % (func.name, libname, func.name), file=self.stream)
853
        print(
854
            "%s.restype = %s" % (func.name, self.type_name(func.returns)),
855
            file=self.stream,
856
        )
857
        if self.generate_comments:
858
            print("# %s(%s)" % (func.name, ", ".join(argnames)), file=self.stream)
859
        print("%s.argtypes = [%s]" % (func.name, ", ".join(args)), file=self.stream)
860

861
        if self.generate_docstrings:
862

863
            def typeString(typ):
864
                if hasattr(typ, "name"):
865
                    return typ.name
866
                elif hasattr(typ, "typ") and isinstance(typ, typedesc.PointerType):
867
                    return typeString(typ.typ) + " *"
868
                else:
869
                    return "unknown"
870

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

884
        self.names.append(func.name)
885
        self._functiontypes += 1
886
        return
887

888
    def FundamentalType(self, _type):
889
        """Returns the proper ctypes class name for a fundamental type
890

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

902
    ########
903

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

922
    def print_comment(self, item):
923
        if item.comment is None:
924
            return
925
        for _ in textwrap.wrap(item.comment, 78):
926
            print("# %s" % _, file=self.stream)
927
        return
928

929
    def generate_all(self, items):
930
        for item in items:
931
            self._generate(item)
932
        return
933

934
    def generate_items(self, items):
935
        # items = set(items)
936
        loops = 0
937
        while items:
938
            loops += 1
939
            self.more = collections.OrderedDict()
940
            self.generate_all(items)
941

942
            # items |= self.more , but keeping ordering
943
            _s = set(items)
944
            [items.append(k) for k in self.more.keys() if k not in _s]
945

946
            # items -= self.done, but keep ordering
947
            _done = self.done.keys()
948
            for i in list(items):
949
                if i in _done:
950
                    items.remove(i)
951

952
        return loops
953

954
    def generate(self, parser, items):
955
        self.generate_headers(parser)
956
        return self.generate_code(items)
957

958
    def generate_code(self, items):
959
        print(
960
            "\n".join(
961
                ["ctypes.CDLL('%s', ctypes.RTLD_GLOBAL)" % preloaded_dll for preloaded_dll in self.preloaded_dlls]
962
            ),
963
            file=self.imports,
964
        )
965
        loops = self.generate_items(items)
966

967
        self.output.write(self.imports.getvalue())
968
        self.output.write("\n\n")
969
        self.output.write(self.stream.getvalue())
970

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

980
        return loops
981

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

1009

1010
################################################################
1011

1012
class CodeTranslator:
1013
    """
1014
    Organiser class to take C files in input, and produce python code in a standard fashion
1015
    """
1016
    def __init__(self, cfg: config.CodegenConfig):
48✔
1017
        self.cfg = cfg
48✔
1018
        self.parser = None
48✔
1019
        self.generator = None
48✔
1020
        self.items = []
48✔
1021
        self.filtered_items = []
48✔
1022

1023
    def preload_dlls(self):
48✔
1024
        # FIXME
1025
        self.cfg.preloaded_dlls = [Library(name, nm="nm") for name in self.cfg.preloaded_dlls]
48✔
1026

1027
    def make_clang_parser(self):
48✔
1028
        self.parser = clangparser.Clang_Parser(self.cfg.clang_opts)
48✔
1029
        if typedesc.Macro in self.cfg.types:
48✔
1030
            self.parser.activate_macros_parsing()
1031
        if self.cfg.generate_comments:
48✔
1032
            self.parser.activate_comment_parsing()
1033
        # FIXME
1034
        # if self.cfg.filter_location:
1035
        #     parser.filter_location(srcfiles)
1036
        return self.parser
48✔
1037

1038
    def parse_input_string(self, input_io):
48✔
1039
        if self.parser is None:
48✔
1040
            self.make_clang_parser()
48✔
1041
        self.parser.parse_string(input_io)
48✔
1042
        # get the typedesc C types items
1043
        self.items.extend(self.parser.get_result())
48✔
1044

1045
    def parse_input_file(self, src_file):
48✔
1046
        if self.parser is None:
1047
            self.make_clang_parser()
1048
        self.parser.parse(src_file)
1049
        # get the typedesc C types items
1050
        self.items.extend(self.parser.get_result())
1051

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

1068
    def make_code_generator(self, output):
48✔
1069
        self.generator = Generator(output, cfg=self.cfg)
48✔
1070
        return self.generator
48✔
1071

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

1086
    def filter_types(self):
48✔
1087
        self.filtered_items = [i for i in self.filtered_items if i.__class__ in self.cfg.types]
48✔
1088

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

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

1127

1128
# easy to use API.
1129

1130
def translate(input_io, cfg=None):
48✔
1131
    """
1132
        Take a readable C like input readable and translate it to python.
1133
    """
1134
    cfg = cfg or config.CodegenConfig()
60✔
1135
    translator = CodeTranslator(cfg)
60✔
1136
    translator.preload_dlls()
60✔
1137
    translator.parse_input_string(input_io)
60✔
1138
    # gen python code
1139
    output = io.StringIO()
60✔
1140
    translator.generate_code(output)
60✔
1141
    output.seek(0)
60✔
1142
    # inject generated code in python namespace
1143
    ignore_coding = output.readline()
60✔
1144
    # exec ofi.getvalue() in namespace
1145
    output = ''.join(output.readlines())
60✔
1146
    namespace = {}
60✔
1147
    exec(output, namespace)
60✔
1148
    return util.ADict(namespace)
60✔
1149

1150

1151
def translate_files(source_files, outfile, cfg: config.CodegenConfig):
60✔
1152
    """
1153
    Translate the content of source_files in python code in outfile
1154

1155
    source_files: list of filenames or single filename
1156
    """
1157
    translator = CodeTranslator(cfg)
60✔
1158
    translator.preload_dlls()
60✔
1159
    if isinstance(source_files, list):
60✔
1160
        translator.parse_input_files(source_files)
60✔
1161
    else:
1162
        translator.parse_input_file(source_files)
1163
    log.debug("Input was parsed")
60✔
1164
    translator.generate_code(outfile)
60✔
1165
    return
60✔
1166

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