• 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

85.93
/ctypeslib/codegen/handler.py
1
"""Abstract Handler with helper methods."""
2

3
from clang.cindex import CursorKind, TypeKind, Cursor
1✔
4

5
from ctypeslib.codegen import typedesc
1✔
6
from ctypeslib.codegen.util import log_entity
1✔
7

8
import logging
1✔
9
import re
1✔
10
log = logging.getLogger('handler')
1✔
11

12

13
class CursorKindException(TypeError):
1✔
14

15
    """When a child node of a VAR_DECL is parsed as an initialization value,
16
    when its not actually part of that initiwlization value."""
17
    pass
1✔
18

19

20
class InvalidDefinitionError(TypeError):
1✔
21

22
    """When a structure is invalid in the source code,  sizeof, alignof returns
23
    negatives value. We detect it and do our best."""
24
    pass
1✔
25

26

27
class InvalidTranslationUnitException(TypeError):
1✔
28

29
    """When a translation unit is invalid"""
30
    pass
1✔
31

32

33
class DuplicateDefinitionException(KeyError):
1✔
34

35
    """When we encounter a duplicate declaration/definition name."""
36
    pass
1✔
37

38

39
################################################################
40

41
class ClangHandler(object):
1✔
42

43
    """
44
    Abstract class for handlers.
45
    """
46

47
    def __init__(self, parser):
1✔
48
        self.parser = parser
1✔
49
        self._unhandled = []
1✔
50

51
    def register(self, name, obj):
1✔
52
        return self.parser.register(name, obj)
1✔
53

54
    def get_registered(self, name):
1✔
55
        return self.parser.get_registered(name)
1✔
56

57
    def is_registered(self, name):
1✔
58
        return self.parser.is_registered(name)
1✔
59

60
    def remove_registered(self, name):
1✔
61
        return self.parser.remove_registered(name)
×
62

63
    def set_location(self, obj, cursor):
1✔
64
        """ Location is also used for codegeneration ordering."""
65
        if (hasattr(cursor, 'location') and cursor.location is not None and
1✔
66
                cursor.location.file is not None):
67
            obj.location = (cursor.location.file.name, cursor.location.line)
1✔
68
        return
1✔
69

70
    def set_comment(self, obj, cursor):
1✔
71
        """ If a comment is available, add it to the typedesc."""
72
        if isinstance(obj, typedesc.T):
1✔
73
            obj.comment = cursor.brief_comment
1✔
74
        return
1✔
75

76
    def make_python_name(self, name):
1✔
77
        """Transforms an USR into a valid python name."""
78
        # FIXME see cindex.SpellingCache
79
        for k, v in [('<', '_'), ('>', '_'), ('::', '__'), (',', ''), (' ', ''),
1✔
80
                     ("$", "DOLLAR"), (".", "DOT"), ("@", "_"), (":", "_"),
81
                     ('-', '_')]:
82
            if k in name:  # template
1✔
83
                name = name.replace(k, v)
1✔
84
            # FIXME: test case ? I want this func to be neutral on C valid
85
            # names.
86
            if name.startswith("__"):
1✔
87
                return "_X" + name
×
88
        if len(name) == 0:
1✔
89
            pass
1✔
90
        elif name[0] in "01234567879":
1✔
91
            return "_" + name
×
92
        return name
1✔
93

94
    def _make_unknown_name(self, cursor, field_name):
1✔
95
        """Creates a name for unnamed type """
96
        parent = cursor.lexical_parent
1✔
97
        pname = self.get_unique_name(parent)
1✔
98
        log.debug('_make_unknown_name: Got parent get_unique_name %s',pname)
1✔
99
        # we only look at types declarations
100
        _cursor_decl = cursor.type.get_declaration()
1✔
101
        # we had the field index from the parent record, as to differenciate
102
        # between unnamed siblings of a same struct
103
        _i = 0
1✔
104
        found = False
1✔
105
        # Look at the parent fields to find myself
106
        for m in parent.get_children():
1✔
107
            # FIXME: make the good indices for fields
108
            log.debug('_make_unknown_name child %d %s %s %s',_i,m.kind, m.type.kind,m.location)
1✔
109
            if m.kind not in [CursorKind.STRUCT_DECL,CursorKind.UNION_DECL,
1✔
110
                              CursorKind.CLASS_DECL]:#,
111
                              #CursorKind.FIELD_DECL]:
112
                continue
1✔
113
            if m == _cursor_decl:
1✔
114
                found = True
1✔
115
                break
1✔
116
            _i+=1
1✔
117
        if not found:
1✔
118
            raise NotImplementedError("_make_unknown_name BUG %s" % cursor.location)
×
119
        # truncate parent name to remove the first part (union or struct)
120
        _premainer = '_'.join(pname.split('_')[1:])
1✔
121
        # name the anonymous record with the field name if it has one
122
        if field_name:
1✔
123
            name = '%s_%s' % (_premainer, field_name)
1✔
124
        else:
125
            name = '%s_%d' % (_premainer, _i)
1✔
126
        return name
1✔
127

128
    def get_unique_name(self, cursor, field_name=None):
1✔
129
        """get the spelling or create a unique name for a cursor"""
130
        # this gets called for both cursors and types!
131
        # so cursor.kind can be a CursorKind or a TypeKind
132
        if cursor.kind in [CursorKind.UNEXPOSED_DECL]:
1✔
133
            return ''
1✔
134
        # covers most cases
135
        name = cursor.spelling
1✔
136
        if cursor.kind == CursorKind.CXX_BASE_SPECIFIER:
1✔
137
            name = cursor.type.spelling
1✔
138
        # if it's a record decl or field decl and its type is anonymous
139
        # clang > 16 changes anonymous names to have a parenthetical name
140
        # so force it to have blank name like it did in earlier clang versions
141
        # only cursors, not types have .is_anonymous()
142
        if (isinstance(cursor.kind, CursorKind) and
1✔
143
                cursor.is_anonymous() and '(' in name):
144
            name = ''
1✔
145
        if name == '':
1✔
146
            # if cursor.is_anonymous():
147
            # a unnamed object at the root TU
148
            if (cursor.semantic_parent
1✔
149
                and cursor.semantic_parent.kind == CursorKind.TRANSLATION_UNIT):
150
                name = self.make_python_name(cursor.get_usr())
1✔
151
                log.debug('get_unique_name: root unnamed type kind %s',cursor.kind)
1✔
152
            elif cursor.kind in [CursorKind.STRUCT_DECL,CursorKind.UNION_DECL,
1✔
153
                                 CursorKind.CLASS_DECL,CursorKind.FIELD_DECL]:
154
                name = self._make_unknown_name(cursor, field_name)
1✔
155
                log.debug('Unnamed cursor type, got name %s',name)
1✔
156
            else:
157
                log.debug('Unnamed cursor, No idea what to do')
1✔
158
                #import code
159
                #code.interact(local=locals())
160
                return ''
1✔
161
        if cursor.kind in [CursorKind.STRUCT_DECL,CursorKind.UNION_DECL,
1✔
162
                                 CursorKind.CLASS_DECL, CursorKind.CXX_BASE_SPECIFIER]:
163
            names= {CursorKind.STRUCT_DECL: 'struct',
1✔
164
                    CursorKind.UNION_DECL: 'union',
165
                    CursorKind.CLASS_DECL: 'class',
166
                    CursorKind.TYPE_REF: '',
167
                    CursorKind.CXX_BASE_SPECIFIER: 'class'
168
                    }
169
            if 'unnamed at' in name:
1✔
170
                name = re.sub('[^a-zA-Z0-9]', '_', name)
×
171
            name = '%s_%s'%(names[cursor.kind],name)
1✔
172
        log.debug('get_unique_name: name "%s"',name)
1✔
173
        return name
1✔
174

175
    def is_fundamental_type(self, t):
1✔
176
        return (not self.is_pointer_type(t) and
1✔
177
                t.kind in self.parser.ctypes_typename.keys())
178

179
    def is_pointer_type(self, t):
1✔
180
        return t.kind == TypeKind.POINTER
1✔
181

182
    def is_array_type(self, t):
1✔
183
        return (t.kind == TypeKind.CONSTANTARRAY or
1✔
184
                t.kind == TypeKind.INCOMPLETEARRAY or
185
                t.kind == TypeKind.VARIABLEARRAY or
186
                t.kind == TypeKind.DEPENDENTSIZEDARRAY)
187

188
    def is_unexposed_type(self, t):
1✔
189
        return t.kind == TypeKind.UNEXPOSED
1✔
190

191
    def is_literal_cursor(self, t):
1✔
192
        return (t.kind == CursorKind.INTEGER_LITERAL or
×
193
                t.kind == CursorKind.FLOATING_LITERAL or
194
                t.kind == CursorKind.IMAGINARY_LITERAL or
195
                t.kind == CursorKind.STRING_LITERAL or
196
                t.kind == CursorKind.CHARACTER_LITERAL)
197

198
    def get_literal_kind_affinity(self, literal_kind):
1✔
199
        ''' return the list of fundamental types that are adequate for which
200
        this literal_kind is adequate'''
201
        if literal_kind == CursorKind.INTEGER_LITERAL:
×
202
            return [TypeKind.USHORT, TypeKind.UINT, TypeKind.ULONG,
×
203
                    TypeKind.ULONGLONG, TypeKind.UINT128,
204
                    TypeKind.SHORT, TypeKind.INT, TypeKind.LONG,
205
                    TypeKind.LONGLONG, TypeKind.INT128, ]
206
        elif literal_kind == CursorKind.STRING_LITERAL:
×
207
            return [TypeKind.CHAR16, TypeKind.CHAR32, TypeKind.CHAR_S,
×
208
                    TypeKind.SCHAR, TypeKind.WCHAR]  # DEBUG
209
        elif literal_kind == CursorKind.CHARACTER_LITERAL:
×
210
            return [TypeKind.CHAR_U, TypeKind.UCHAR]
×
211
        elif literal_kind == CursorKind.FLOATING_LITERAL:
×
212
            return [TypeKind.FLOAT, TypeKind.DOUBLE, TypeKind.LONGDOUBLE]
×
213
        elif literal_kind == CursorKind.IMAGINARY_LITERAL:
×
214
            return []
×
215
        return []
×
216

217
    def get_ctypes_name(self, typekind):
1✔
218
        return self.parser.get_ctypes_name(typekind)
1✔
219

220
    def get_ctypes_size(self, typekind):
1✔
221
        return self.parser.get_ctypes_size(typekind)
×
222

223
    def parse_cursor(self, cursor):
1✔
224
        return self.parser.parse_cursor(cursor)
1✔
225

226
    def parse_cursor_type(self, _cursor_type):
1✔
227
        return self.parser.parse_cursor_type(_cursor_type)
1✔
228

229
    ################################
230
    # do-nothing element handlers
231

232
    @log_entity
1✔
233
    def _pass_through_children(self, node, **args):
1✔
234
        for child in node.get_children():
1✔
235
            self.parser.start_element(child)
×
236
        return True
1✔
237

238
    def _do_nothing(self, node, **args):
1✔
239
        name = self.get_unique_name(node)
1✔
240
        #import code
241
        # code.interact(local=locals())
242
        log.warning('_do_nothing for %s/%s',node.kind.name, name)
1✔
243
        return True
1✔
244

245
    ###########################################
246
    # TODO FIXME: 100% cursor/type Kind coverage
247
    def __getattr__(self, name, **args):
1✔
248
        if name not in self._unhandled:
1✔
249
            log.warning('%s is not handled',name)
1✔
250
            self._unhandled.append(name)
1✔
251
        return self._do_nothing
1✔
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