From b1eb395f5d3a3e9232ec0f9f51bc241e7bc8e29b Mon Sep 17 00:00:00 2001 From: Misty Date: Sat, 2 Oct 2021 14:12:04 +0800 Subject: [PATCH 01/14] Support simple c++ inherit cases --- ctypeslib/clang2py.py | 8 ++++++-- ctypeslib/codegen/cursorhandler.py | 15 ++++++++++++++- ctypeslib/codegen/handler.py | 12 ++++++++---- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/ctypeslib/clang2py.py b/ctypeslib/clang2py.py index ee674bdc..3c6bf165 100755 --- a/ctypeslib/clang2py.py +++ b/ctypeslib/clang2py.py @@ -295,8 +295,12 @@ def windows_dlls(option, opt, value, parser): # Preload libraries # [Library(name, mode=RTLD_GLOBAL) for name in options.preload] - - translate_files(inputs.files, outputs.stream, cfg) + try: + translate_files(inputs.files, outputs.stream, cfg) + except: + # return non-zero exit status in case of an unhandled exception + traceback.print_exc() + sys.exit(1) return 0 diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index 46fc5d0c..2848af72 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -630,6 +630,10 @@ def _record_decl(self, cursor, _output_type, num=None): # FIXME: lets ignore bases for now. # bases = attrs.get("bases", "").split() # that for cpp ? bases = [] # FIXME: support CXX + for c in cursor.get_children(): + if c.kind == CursorKind.CXX_BASE_SPECIFIER: + bases.append(self.get_registered(self.get_unique_name(c))) + log.debug("got base class %s", c.displayname) size = cursor.type.get_size() align = cursor.type.get_align() if size == -2: # @@ -830,9 +834,13 @@ def _fixup_record(self, s): log.debug('FIXUP_STRUCT: no members') s.members = [] return - if s.size == 0: + if s.size == 0 or (s.size == 1 and len(s.members) == 0): log.debug('FIXUP_STRUCT: struct has size %d', s.size) return + if len(s.members) == 0 and len(s.bases) > 0: + log.debug('FIXUP_STRUCT: derived struct without new member') + return + # try to fix bitfields without padding first self._fixup_record_bitfields_type(s) # No need to lookup members in a global var. @@ -840,6 +848,11 @@ def _fixup_record(self, s): members = [] member = None offset = 0 + for b in s.bases: + offset += b.size * 8 + if s.size * 8 == offset: + log.debug('FIXUP_STRUCT: struct has size %d equals to base size', s.size) + return padding_nb = 0 member = None prev_member = None diff --git a/ctypeslib/codegen/handler.py b/ctypeslib/codegen/handler.py index de30fb37..5d8f3d77 100644 --- a/ctypeslib/codegen/handler.py +++ b/ctypeslib/codegen/handler.py @@ -127,8 +127,10 @@ def get_unique_name(self, cursor): return '' # covers most cases name = cursor.spelling + if cursor.kind == CursorKind.CXX_BASE_SPECIFIER: + name = cursor.type.spelling # if its a record decl or field decl and its type is unnamed - if cursor.spelling == '': + if name == '': # a unnamed object at the root TU if (cursor.semantic_parent and cursor.semantic_parent.kind == CursorKind.TRANSLATION_UNIT): @@ -144,11 +146,13 @@ def get_unique_name(self, cursor): #code.interact(local=locals()) return '' if cursor.kind in [CursorKind.STRUCT_DECL,CursorKind.UNION_DECL, - CursorKind.CLASS_DECL]: + CursorKind.CLASS_DECL, CursorKind.CXX_BASE_SPECIFIER]: names= {CursorKind.STRUCT_DECL: 'struct', CursorKind.UNION_DECL: 'union', - CursorKind.CLASS_DECL: 'class', - CursorKind.TYPE_REF: ''} + CursorKind.CLASS_DECL: 'struct', + CursorKind.TYPE_REF: '', + CursorKind.CXX_BASE_SPECIFIER: 'struct' + } name = '%s_%s'%(names[cursor.kind],name) log.debug('get_unique_name: name "%s"',name) return name From 0e8468e774874ac54ff9a741876451afde41539c Mon Sep 17 00:00:00 2001 From: Misty Date: Sat, 12 Feb 2022 04:31:24 +0800 Subject: [PATCH 02/14] fix base class related problems - when base class is typedef, current implementation failed to find the referenced type - base class's body is wrongly outputted after child class's inheritance declaration, causing error _field_ is final --- ctypeslib/codegen/codegenerator.py | 6 +++++- ctypeslib/codegen/cursorhandler.py | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 0291f72f..3cf391e6 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -612,8 +612,12 @@ def StructureHead(self, head, inline=False): log.debug("Head start for %s inline:%s", head.name, inline) for struct in head.struct.bases: self._generate(struct.get_head()) + # we MUST generate the structure body before inheritance happens + # or ctypes will tell us _field_ is final, cannot be changed + self._generate(struct.get_body(), inline) # add dependencies - self.more[struct] = True + #self.more[struct] = True + basenames = [self.type_name(b) for b in head.struct.bases] if basenames: # method_names = [m.name for m in head.struct.members if type(m) is typedesc.Method] diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index 2848af72..befa9528 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -632,7 +632,17 @@ def _record_decl(self, cursor, _output_type, num=None): bases = [] # FIXME: support CXX for c in cursor.get_children(): if c.kind == CursorKind.CXX_BASE_SPECIFIER: - bases.append(self.get_registered(self.get_unique_name(c))) + base_class_name = c.type.spelling + for n in ['struct_' + base_class_name, 'union_' + base_class_name]: + if self.is_registered(n): + bases.append(self.get_registered(n)) + break + else: + typedef_typ: typedesc.Typedef = self.get_registered(base_class_name) + while isinstance(typedef_typ, typedesc.Typedef): + typedef_typ = typedef_typ.typ + bases.append(typedef_typ) + log.debug("got base class %s", c.displayname) size = cursor.type.get_size() align = cursor.type.get_align() From 44010551f34e571419d463baa75332df6f041cdf Mon Sep 17 00:00:00 2001 From: Misty Date: Sun, 13 Feb 2022 02:32:03 +0800 Subject: [PATCH 03/14] Support namespace & add missing location --- ctypeslib/codegen/cursorhandler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index befa9528..ebe64f20 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -96,6 +96,11 @@ def INIT_LIST_EXPR(self, cursor): # now fixed by TranslationUnit.PARSE_SKIP_FUNCTION_BODIES COMPOUND_STMT = ClangHandler._do_nothing + @log_entity + def NAMESPACE(self, cursor): + for child in cursor.get_children(): + self.parse_cursor(child) # FIXME, where is the starElement + ################################ # TYPE REFERENCES handlers @@ -691,6 +696,9 @@ def _record_decl(self, cursor, _output_type, num=None): declared_instance = True else: obj = self.get_registered(name) + if cursor.is_definition(): + self.set_location(obj, cursor) + self.set_comment(obj, cursor) declared_instance = False # capture members declaration members = [] From 7643845b365280cfd1c4ba9ae13cb9e442ed6f05 Mon Sep 17 00:00:00 2001 From: Misty Date: Mon, 14 Feb 2022 01:29:42 +0800 Subject: [PATCH 04/14] Properly support extern global variable --- ctypeslib/codegen/codegenerator.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 3cf391e6..9f3c7330 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -337,12 +337,25 @@ def Variable(self, tp): self.print_comment(tp) # 2021-02 give me a test case for this. it breaks all extern variables otherwise. - if tp.extern and self.find_library_with_func(tp): + # if tp.extern and self.find_library_with_func(tp): + if tp.extern: dll_library = self.find_library_with_func(tp) + if not dll_library: + class LibraryStub: + _filepath = "FIXME_STUB" + _name = "FIXME_STUB" + dll_library = LibraryStub() + self._generate(tp.typ) # calling convention does not matter for in_dll... libname = self.get_sharedlib(dll_library, "cdecl") - print("%s = (%s).in_dll(%s, '%s')" % (tp.name, self.type_name(tp.typ), libname, tp.name), file=self.stream) + #print("%s = (%s).in_dll(%s, '%s')" % (tp.name, self.type_name(tp.typ), libname, tp.name), file=self.stream) + decl = "{tp} = ({type_name}).in_dll({libname}, '{tp}') if getattr({libname}, '{tp}', None) else None".format( + tp=tp.name, + type_name=self.type_name(tp.typ), + libname=libname, + ) + print(decl, file=self.stream) self.names.append(tp.name) # wtypes.h contains IID_IProcessInitControl, for example return From a38ee8475f443d288381d278c2c0ee91dc786d19 Mon Sep 17 00:00:00 2001 From: Misty Date: Thu, 17 Feb 2022 05:20:25 +0800 Subject: [PATCH 05/14] Disable canonicalization of pointer types to improve readability --- ctypeslib/codegen/typehandler.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ctypeslib/codegen/typehandler.py b/ctypeslib/codegen/typehandler.py index 2320a6a4..6a954504 100644 --- a/ctypeslib/codegen/typehandler.py +++ b/ctypeslib/codegen/typehandler.py @@ -123,7 +123,8 @@ def POINTER(self, _cursor_type): # # we shortcut to canonical typedefs and to pointee canonical defs comment = None - _type = _cursor_type.get_pointee().get_canonical() + # _type = _cursor_type.get_pointee().get_canonical() + _type = _cursor_type.get_pointee() _p_type_name = self.get_unique_name(_type) # get pointer size size = _cursor_type.get_size() # not size of pointee From 781ab02183b215a1b6d46e44170b0fcde841442d Mon Sep 17 00:00:00 2001 From: Misty Date: Fri, 18 Feb 2022 02:21:43 +0800 Subject: [PATCH 06/14] Treat FUNCTIONPROTO as pointer itself due to canonicalization disabled and we use CFUNCTYPE to represent function --- ctypeslib/codegen/typehandler.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ctypeslib/codegen/typehandler.py b/ctypeslib/codegen/typehandler.py index 6a954504..41c0a1e4 100644 --- a/ctypeslib/codegen/typehandler.py +++ b/ctypeslib/codegen/typehandler.py @@ -125,6 +125,11 @@ def POINTER(self, _cursor_type): comment = None # _type = _cursor_type.get_pointee().get_canonical() _type = _cursor_type.get_pointee() + if _type.get_canonical().kind == TypeKind.FUNCTIONPROTO: + # in python there's no pure function proto for ctypes, + # in code generator, functionproto will emit CFUNCTYPE, which is func ptr + # so pointer to function proto should actually be directly functionproto + return self.parse_cursor_type(_type) _p_type_name = self.get_unique_name(_type) # get pointer size size = _cursor_type.get_size() # not size of pointee From 3bf9c97a55e8bbecf748538b59ca663b578e6f70 Mon Sep 17 00:00:00 2001 From: Misty Date: Fri, 18 Feb 2022 02:25:32 +0800 Subject: [PATCH 07/14] Add get_cfield helper method to Structure to retrive ctypes value of field --- ctypeslib/data/structure_type.tpl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ctypeslib/data/structure_type.tpl b/ctypeslib/data/structure_type.tpl index 5c08ffbf..ed5ee27b 100644 --- a/ctypeslib/data/structure_type.tpl +++ b/ctypeslib/data/structure_type.tpl @@ -49,6 +49,18 @@ class Structure(ctypes.Structure, AsDictMixin): args.update(kwds) super(Structure, self).__init__(**args) + def get_cfield(self, field): + fieldType = None + for fn, ftype in self._fields_: + if fn == field: + fieldType = ftype + break + else: + raise AttributeError('Field %s not found' % field) + + fieldAttr = getattr(self.__class__, field) + return fieldType.from_buffer(self, fieldAttr.offset) + @classmethod def _field_names_(cls): if hasattr(cls, '_fields_'): From 3d69cff0fc6456d0791ade65179a05f40f570a53 Mon Sep 17 00:00:00 2001 From: Misty Date: Mon, 21 Mar 2022 06:36:05 +0800 Subject: [PATCH 08/14] Fix stub flag of library for extern global variables --- ctypeslib/codegen/codegenerator.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 9f3c7330..c9961fa9 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -340,15 +340,17 @@ def Variable(self, tp): # if tp.extern and self.find_library_with_func(tp): if tp.extern: dll_library = self.find_library_with_func(tp) + is_stub = False if not dll_library: class LibraryStub: _filepath = "FIXME_STUB" _name = "FIXME_STUB" dll_library = LibraryStub() + is_stub = True self._generate(tp.typ) # calling convention does not matter for in_dll... - libname = self.get_sharedlib(dll_library, "cdecl") + libname = self.get_sharedlib(dll_library, "cdecl", stub=is_stub) #print("%s = (%s).in_dll(%s, '%s')" % (tp.name, self.type_name(tp.typ), libname, tp.name), file=self.stream) decl = "{tp} = ({type_name}).in_dll({libname}, '{tp}') if getattr({libname}, '{tp}', None) else None".format( tp=tp.name, From 63b1ae91a27d68f12f7e70c4f95267c2acd558d3 Mon Sep 17 00:00:00 2001 From: Misty Date: Mon, 21 Mar 2022 18:48:23 +0800 Subject: [PATCH 09/14] Allowing on-demand header definition inclusion --- ctypeslib/codegen/codegenerator.py | 18 ++++++++++++++++-- ctypeslib/codegen/config.py | 3 +++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index c9961fa9..9db5a0da 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -32,7 +32,9 @@ def __init__(self, output, cfg): self.stream = StringIO() self.imports = StringIO() self.cfg = cfg + self.generate_locations = cfg.generate_locations + self.exclude_location = cfg.exclude_location self.generate_comments = cfg.generate_comments self.generate_docstrings = cfg.generate_docstrings self.known_symbols = cfg.known_symbols or {} @@ -914,6 +916,11 @@ def _generate(self, item, *args): """ wraps execution of specific methods.""" if item in self.done: return + if self.exclude_location: + if item not in self.more: + if item.location and not item.location[0].endswith(self.parser.tu.spelling): + return + # verbose output with location. if self.generate_locations and item.location: print("# %s:%d" % item.location, file=self.stream) @@ -943,9 +950,10 @@ def generate_all(self, items): def generate_items(self, items): # items = set(items) loops = 0 - while items: + self.more = collections.OrderedDict() + while True: loops += 1 - self.more = collections.OrderedDict() + #self.more = collections.OrderedDict() self.generate_all(items) # items |= self.more , but keeping ordering @@ -953,10 +961,16 @@ def generate_items(self, items): [items.append(k) for k in self.more.keys() if k not in _s] # items -= self.done, but keep ordering + # more -= self.done _done = self.done.keys() for i in list(items): if i in _done: items.remove(i) + if i in self.more: + self.more.pop(i) + + if not self.more: + break return loops diff --git a/ctypeslib/codegen/config.py b/ctypeslib/codegen/config.py index b2334178..1ff1b617 100644 --- a/ctypeslib/codegen/config.py +++ b/ctypeslib/codegen/config.py @@ -17,6 +17,8 @@ class CodegenConfig: generate_docstrings: bool = False # include source file location in comments generate_locations: bool = False + # on-demand include definitions outside of source file, only used definitions will be included + exclude_location: bool = False # do not include declaration defined outside of the source files filter_location: bool = True # dll to be loaded before all others (to resolve symbols) @@ -45,6 +47,7 @@ def parse_options(self, options): self.generate_comments = options.generate_comments self.generate_docstrings = options.generate_docstrings self.generate_locations = options.generate_locations + self.exclude_location = options.exclude_includes self.filter_location = not options.generate_includes self.preloaded_dlls = options.preload # List exported symbols from libraries From 8012c16741d2f3fb7084713826dbbdb24ca88db6 Mon Sep 17 00:00:00 2001 From: Misty Date: Mon, 28 Mar 2022 15:33:05 +0800 Subject: [PATCH 10/14] Fixing exclude_location's dependency issue --- ctypeslib/codegen/codegenerator.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 9db5a0da..7e6faeef 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -916,11 +916,6 @@ def _generate(self, item, *args): """ wraps execution of specific methods.""" if item in self.done: return - if self.exclude_location: - if item not in self.more: - if item.location and not item.location[0].endswith(self.parser.tu.spelling): - return - # verbose output with location. if self.generate_locations and item.location: print("# %s:%d" % item.location, file=self.stream) @@ -954,7 +949,14 @@ def generate_items(self, items): while True: loops += 1 #self.more = collections.OrderedDict() - self.generate_all(items) + items_to_gen = [] + for item in items: + if self.exclude_location: + if item not in self.more: + if item.location and not item.location[0].endswith(self.parser.tu.spelling): + continue + items_to_gen.append(item) + self.generate_all(items_to_gen) # items |= self.more , but keeping ordering _s = set(items) From d05adcbd870743b0a5053655f57ddf2e3bfc8e38 Mon Sep 17 00:00:00 2001 From: Misty Date: Mon, 28 Mar 2022 15:33:31 +0800 Subject: [PATCH 11/14] Allow in_dll without actual DLL (as None when not present) --- ctypeslib/codegen/codegenerator.py | 2 +- ctypeslib/data/structure_type.tpl | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index 7e6faeef..d109bc47 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -354,7 +354,7 @@ class LibraryStub: # calling convention does not matter for in_dll... libname = self.get_sharedlib(dll_library, "cdecl", stub=is_stub) #print("%s = (%s).in_dll(%s, '%s')" % (tp.name, self.type_name(tp.typ), libname, tp.name), file=self.stream) - decl = "{tp} = ({type_name}).in_dll({libname}, '{tp}') if getattr({libname}, '{tp}', None) else None".format( + decl = "{tp} = ctypes_in_dll({type_name}, {libname}, '{tp}')".format( tp=tp.name, type_name=self.type_name(tp.typ), libname=libname, diff --git a/ctypeslib/data/structure_type.tpl b/ctypeslib/data/structure_type.tpl index ed5ee27b..aa83eb48 100644 --- a/ctypeslib/data/structure_type.tpl +++ b/ctypeslib/data/structure_type.tpl @@ -116,4 +116,10 @@ class Structure(ctypes.Structure, AsDictMixin): class Union(ctypes.Union, AsDictMixin): pass +def ctypes_in_dll(typ, dll, name): + try: + return typ.in_dll(dll, name) + except (ValueError, TypeError): + return None + From e1fe4a0be670958e66a8037362e3258c344f5b92 Mon Sep 17 00:00:00 2001 From: Misty Date: Mon, 28 Mar 2022 15:35:11 +0800 Subject: [PATCH 12/14] Handle multiple declaration across multiple header --- ctypeslib/codegen/cursorhandler.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ctypeslib/codegen/cursorhandler.py b/ctypeslib/codegen/cursorhandler.py index ebe64f20..ec9a2c99 100644 --- a/ctypeslib/codegen/cursorhandler.py +++ b/ctypeslib/codegen/cursorhandler.py @@ -699,6 +699,11 @@ def _record_decl(self, cursor, _output_type, num=None): if cursor.is_definition(): self.set_location(obj, cursor) self.set_comment(obj, cursor) + else: + # Correctly handle multiple-time declaration in multiple header + # FIXME: test case like this: struct TypeA; struct TypeA; struct TypeA {int a;} + log.debug('cursor %s is not on a definition, and is declared multiple times', name) + return obj declared_instance = False # capture members declaration members = [] From 8ae4cccf17bca97b00ab29ae9602656c3e18ce9f Mon Sep 17 00:00:00 2001 From: Misty Date: Mon, 27 Jun 2022 03:13:24 +0800 Subject: [PATCH 13/14] Support forcibly exclude all object outside of source files --- ctypeslib/clang2py.py | 5 +++++ ctypeslib/codegen/codegenerator.py | 5 +++++ ctypeslib/codegen/config.py | 3 +++ 3 files changed, 13 insertions(+) diff --git a/ctypeslib/clang2py.py b/ctypeslib/clang2py.py index 3c6bf165..70a67402 100755 --- a/ctypeslib/clang2py.py +++ b/ctypeslib/clang2py.py @@ -241,6 +241,11 @@ def windows_dlls(option, opt, value, parser): default=False, help="Parse object in sources files only. Ignore includes") + parser.add_argument("-X", "--force-exclude-includes", + action="store_true", + default=False, + help="Forcibly disable generation for all object outside sources files.") + parser.add_argument("--show-ids", dest="showIDs", help="Don't compute cursor IDs (very slow)", default=False) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index d109bc47..e520e423 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -35,6 +35,7 @@ def __init__(self, output, cfg): self.generate_locations = cfg.generate_locations self.exclude_location = cfg.exclude_location + self.force_exclude_location = cfg.force_exclude_location self.generate_comments = cfg.generate_comments self.generate_docstrings = cfg.generate_docstrings self.known_symbols = cfg.known_symbols or {} @@ -916,6 +917,10 @@ def _generate(self, item, *args): """ wraps execution of specific methods.""" if item in self.done: return + if self.force_exclude_location: + if item.location and not item.location[0].endswith(self.parser.tu.spelling): + return + # verbose output with location. if self.generate_locations and item.location: print("# %s:%d" % item.location, file=self.stream) diff --git a/ctypeslib/codegen/config.py b/ctypeslib/codegen/config.py index 1ff1b617..25bcfe05 100644 --- a/ctypeslib/codegen/config.py +++ b/ctypeslib/codegen/config.py @@ -19,6 +19,8 @@ class CodegenConfig: generate_locations: bool = False # on-demand include definitions outside of source file, only used definitions will be included exclude_location: bool = False + # Forcibly exclude ALL definitions that located outside of source file + force_exclude_location: bool = False # do not include declaration defined outside of the source files filter_location: bool = True # dll to be loaded before all others (to resolve symbols) @@ -48,6 +50,7 @@ def parse_options(self, options): self.generate_docstrings = options.generate_docstrings self.generate_locations = options.generate_locations self.exclude_location = options.exclude_includes + self.force_exclude_location = options.force_exclude_includes self.filter_location = not options.generate_includes self.preloaded_dlls = options.preload # List exported symbols from libraries From 0604a4a8b5e5b939f0353cc463416bda2a367048 Mon Sep 17 00:00:00 2001 From: Misty Date: Mon, 27 Jun 2022 04:19:53 +0800 Subject: [PATCH 14/14] Better location detection for exclude_location options --- ctypeslib/codegen/codegenerator.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/ctypeslib/codegen/codegenerator.py b/ctypeslib/codegen/codegenerator.py index e520e423..10bcba47 100644 --- a/ctypeslib/codegen/codegenerator.py +++ b/ctypeslib/codegen/codegenerator.py @@ -913,12 +913,25 @@ def FundamentalType(self, _type): ######## + def _get_location(self, item): + location = item.location + if not location: + if isinstance(item, typedesc.StructureBody) or isinstance(item, typedesc.StructureHead): + location = item.struct.location + elif isinstance(item, typedesc.EnumValue): + location = item.enumeration.location + elif isinstance(item, typedesc.PointerType): + location = item.typ.location + + return location + def _generate(self, item, *args): """ wraps execution of specific methods.""" if item in self.done: return if self.force_exclude_location: - if item.location and not item.location[0].endswith(self.parser.tu.spelling): + location = self._get_location(item) + if location and not location[0].endswith(self.parser.tu.spelling): return # verbose output with location. @@ -958,7 +971,8 @@ def generate_items(self, items): for item in items: if self.exclude_location: if item not in self.more: - if item.location and not item.location[0].endswith(self.parser.tu.spelling): + location = self._get_location(item) + if location and not location[0].endswith(self.parser.tu.spelling): continue items_to_gen.append(item) self.generate_all(items_to_gen)