github-actions[bot] wrote: <!--LLVM CODE FORMAT COMMENT: {darker}-->
:warning: Python code formatter, darker found issues in your code. :warning: <details> <summary> You can test this locally with the following command: </summary> ``````````bash darker --check --diff -r 98e4413a38f286147b863a6ead9625228ab0ec7d...c3ebad6a3447101cb307d5ca118d28d1b78b4dbe clang/bindings/python/clang/ctyped.py clang/bindings/python/tests/ctyped/__init__.py clang/bindings/python/tests/ctyped/test_stub_conversion.py clang/bindings/python/clang/cindex.py clang/bindings/python/tests/cindex/test_translation_unit.py clang/bindings/python/tests/cindex/test_type.py `````````` </details> <details> <summary> View the diff from darker here. </summary> ``````````diff --- clang/cindex.py 2024-08-03 14:40:40.000000 +0000 +++ clang/cindex.py 2024-08-03 23:15:21.593600 +0000 @@ -60,14 +60,33 @@ # o cleanup ctypes wrapping, would be nice to separate the ctypes details more # clearly, and hide from the external interface (i.e., help(cindex)). # # o implement additional SourceLocation, SourceRange, and File methods. -from ctypes import (c_byte, c_ubyte, c_short, c_ushort, c_int, c_uint, c_long, # pyright: ignore[reportUnusedImport] - c_ulong, c_longlong,c_ulonglong, c_size_t, c_ssize_t, # pyright: ignore[reportUnusedImport] - c_bool, c_char, c_wchar, c_float, c_double, c_longdouble, # pyright: ignore[reportUnusedImport] - c_char_p, c_wchar_p, c_void_p) # pyright: ignore[reportUnusedImport] +from ctypes import ( + c_byte, + c_ubyte, + c_short, + c_ushort, + c_int, + c_uint, + c_long, # pyright: ignore[reportUnusedImport] + c_ulong, + c_longlong, + c_ulonglong, + c_size_t, + c_ssize_t, # pyright: ignore[reportUnusedImport] + c_bool, + c_char, + c_wchar, + c_float, + c_double, + c_longdouble, # pyright: ignore[reportUnusedImport] + c_char_p, + c_wchar_p, + c_void_p, +) # pyright: ignore[reportUnusedImport] from ctypes import py_object, Structure, POINTER, byref, cast, cdll from .ctyped import * from .ctyped import ANNO_CONVERTIBLE, generate_metadata import os @@ -114,11 +133,11 @@ ... # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): - def __init__(self, p: 'CInteropString' = None): + def __init__(self, p: "CInteropString" = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) @@ -132,11 +151,11 @@ if val is None: return None return val.decode("utf8") @classmethod - def from_param(cls, param: 'CInteropString') -> c_interop_string: + def from_param(cls, param: "CInteropString") -> c_interop_string: if isinstance(param, str): return cls(param) if isinstance(param, bytes): return cls(param) if param is None: @@ -168,11 +187,11 @@ ### Exception Classes ### class CXError(Exception): - '''Represents C error of type enum CXErrorCode.''' + """Represents C error of type enum CXErrorCode.""" # A generic error code, no further details are available. # # Errors of this kind can get their own specific error codes in future # libclang versions. @@ -297,11 +316,15 @@ def __del__(self) -> None: conf.lib.clang_disposeString(self) @staticmethod - def from_result(res: _CXString, fn: Optional[Callable[..., _CXString]] = None, args: Optional[Tuple[Any, ...]] = None) -> str: + def from_result( + res: _CXString, + fn: Optional[Callable[..., _CXString]] = None, + args: Optional[Tuple[Any, ...]] = None, + ) -> str: assert isinstance(res, _CXString) pystr: str | None = conf.lib.clang_getCString(res) if pystr is None: return "" return pystr @@ -329,11 +352,13 @@ f = None self._data = (f, int(l.value), int(c.value), int(o.value)) return self._data @staticmethod - def from_position(tu: TranslationUnit, file: File, line: int, column: int) -> SourceLocation: + def from_position( + tu: TranslationUnit, file: File, line: int, column: int + ) -> SourceLocation: """ Retrieve the source location associated with a given file/line/column in a particular translation unit. """ return conf.lib.clang_getLocation(tu, file, line, column) @@ -445,12 +470,14 @@ return False if ( other.file is not None and self.start.file is not None and self.end.file is not None - and (other.file.name != self.start.file.name - or other.file.name != self.end.file.name) + and ( + other.file.name != self.start.file.name + or other.file.name != self.end.file.name + ) ): # same file name return False # same file, in between lines if self.start.line < other.line < self.end.line: @@ -655,11 +682,13 @@ def __del__(self) -> None: conf.lib.clang_disposeTokens(self._tu, self._memory, self._count) @staticmethod - def get_tokens(tu: TranslationUnit, extent: SourceRange) -> Generator[Token, None, None]: + def get_tokens( + tu: TranslationUnit, extent: SourceRange + ) -> Generator[Token, None, None]: """Helper method to return all tokens in an extent. This functionality is needed multiple places in this module. We define it here because it seems like a logical place. """ @@ -692,11 +721,12 @@ ### Cursor Kinds ### class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. """ - value: int # pyright: ignore[reportIncompatibleMethodOverride] + + value: int # pyright: ignore[reportIncompatibleMethodOverride] def from_param(self) -> int: return self.value @classmethod @@ -744,10 +774,11 @@ return conf.lib.clang_isReference(self) def is_expression(self) -> bool: """Test if this is an expression kind.""" return conf.lib.clang_isExpression(self) + def is_statement(self) -> bool: """Test if this is a statement kind.""" return conf.lib.clang_isStatement(self) def is_attribute(self) -> bool: @@ -2214,11 +2245,13 @@ Retrieve the width of a bitfield. """ return conf.lib.clang_getFieldDeclBitWidth(self) @staticmethod - def from_result(res: Cursor, fn: Callable[..., Cursor], args: Tuple[Any, ...]) -> Optional[Cursor]: + def from_result( + res: Cursor, fn: Callable[..., Cursor], args: Tuple[Any, ...] + ) -> Optional[Cursor]: assert isinstance(res, Cursor) # FIXME: There should just be an isNull method. if res == conf.lib.clang_getNullCursor(): return None @@ -2238,11 +2271,13 @@ res._tu = tu return res @staticmethod - def from_cursor_result(res: Cursor, fn: Callable[..., Cursor], args: Tuple[Any, ...]) -> Optional[Cursor]: + def from_cursor_result( + res: Cursor, fn: Callable[..., Cursor], args: Tuple[Any, ...] + ) -> Optional[Cursor]: assert isinstance(res, Cursor) if res == conf.lib.clang_getNullCursor(): return None res._tu = args[0]._tu @@ -2527,11 +2562,13 @@ return self.length def __getitem__(self, key: int) -> Type: # FIXME Support slice objects. - if not isinstance(key, int): # pyright: ignore[reportUnnecessaryIsInstance] + if not isinstance( + key, int + ): # pyright: ignore[reportUnnecessaryIsInstance] raise TypeError("Must supply a non-negative int.") if key < 0: raise IndexError("Only non-negative indexes are accepted.") @@ -2580,11 +2617,11 @@ @property def translation_unit(self) -> TranslationUnit: """The TranslationUnit to which this Type is associated.""" # If this triggers an AttributeError, the instance was not properly # instantiated. - return self._tu # type: ignore[no-any-return] + return self._tu # type: ignore[no-any-return] @staticmethod def from_result(res: Type, fn: Callable[..., Type], args: Tuple[Any, ...]) -> Type: assert isinstance(res, Type) @@ -2728,11 +2765,11 @@ def visitor(field: Cursor, children: List[Cursor]) -> int: assert field != conf.lib.clang_getNullCursor() # Create reference to TU so it isn't GC'd before Cursor. - field._tu = self._tu # pyright: ignore[reportPrivateUsage] + field._tu = self._tu # pyright: ignore[reportPrivateUsage] fields.append(field) return 1 # continue fields: List[Cursor] = [] conf.lib.clang_Type_visitFields(self, fields_visit_callback(visitor), fields) @@ -2751,11 +2788,11 @@ def spelling(self) -> str: """Retrieve the spelling of this Type.""" return conf.lib.clang_getTypeSpelling(self) def __eq__(self, other: object) -> bool: - if other is None: # in case user write `x.type == None` + if other is None: # in case user write `x.type == None` return False elif not isinstance(other, Type): return NotImplemented else: return conf.lib.clang_equalTypes(self, other) @@ -2774,21 +2811,25 @@ class ClangObject: """ A helper for Clang objects. This class helps act as an intermediary for the ctypes library and the Clang CIndex library. """ + obj: CObjectP _as_parameter_: CObjectP def __init__(self, obj: CObjectP): assert isinstance(obj, c_object_p) and obj self.obj = self._as_parameter_ = obj def from_param(self) -> CObjectP: return self._as_parameter_ -ClangObjectParam = Annotated[TUnion[CObjectP, ClangObject], ANNO_CONVERTIBLE, c_object_p] + +ClangObjectParam = Annotated[ + TUnion[CObjectP, ClangObject], ANNO_CONVERTIBLE, c_object_p +] class _CXUnsavedFile(Structure): """Helper for passing unsaved file arguments.""" @@ -2796,11 +2837,14 @@ name: r_char_p contents: r_char_p length: r_ulong -UnsavedFileInfo: TypeAlias = Tuple['FsPath', TUnion['StrOrBytes', 'SupportsReadStringData']] + +UnsavedFileInfo: TypeAlias = Tuple[ + "FsPath", TUnion["StrOrBytes", "SupportsReadStringData"] +] # Functions calls through the python interface are rather slow. Fortunately, # for most symboles, we do not need to perform a function call. Their spelling # never changes and is consequently provided by this spelling cache. SpellingCache = { @@ -2955,15 +2999,15 @@ @property def brief_comment(self) -> str: if conf.function_exists("clang_getCompletionBriefComment"): return conf.lib.clang_getCompletionBriefComment(self.obj) - return '' + return "" def __repr__(self) -> str: return ( - " | ".join([str(a) for a in self]) # type: ignore[attr-defined] + " | ".join([str(a) for a in self]) # type: ignore[attr-defined] + " || Priority: " + str(self.priority) + " || Availability: " + str(self.availability) + " || Brief comment: " @@ -3010,20 +3054,22 @@ if len(self) <= key: raise IndexError # FIXME: Current type stub of ctypes does not provide signature of # __getitem__ in class _Pointer. Remove this ignore when they # fixed that. - return self.results[key] # type: ignore[no-any-return] + return self.results[key] # type: ignore[no-any-return] class CodeCompletionResults(ClangObject): - obj: CPointer[CCRStructure] # type: ignore[assignment] - _as_parameter_: CPointer[CCRStructure] # type: ignore[assignment] + obj: CPointer[CCRStructure] # type: ignore[assignment] + _as_parameter_: CPointer[CCRStructure] # type: ignore[assignment] def __init__(self, ptr: CPointer[CCRStructure]): assert isinstance(ptr, POINTER(CCRStructure)) and ptr - self.obj = self._as_parameter_ = ptr # pyright: ignore[reportIncompatibleVariableOverride] + self.obj = ( + self._as_parameter_ + ) = ptr # pyright: ignore[reportIncompatibleVariableOverride] def __del__(self) -> None: conf.lib.clang_disposeCodeCompleteResults(self) @property @@ -3069,11 +3115,12 @@ def read(self, path: FsPath) -> TranslationUnit: """Load a TranslationUnit from the given AST file.""" return TranslationUnit.from_ast_file(path, self) def parse( - self, path: Optional[FsPath], + self, + path: Optional[FsPath], args: Optional[List[str]] = None, unsaved_files: Optional[List[UnsavedFileInfo]] = None, options: int = 0, ) -> TranslationUnit: """Load the translation unit from the given source code file by running @@ -3131,30 +3178,33 @@ PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION = 128 index: Index @staticmethod - def process_unsaved_files(unsaved_files: List[UnsavedFileInfo]) -> Optional[CArray[_CXUnsavedFile]]: + def process_unsaved_files( + unsaved_files: List[UnsavedFileInfo], + ) -> Optional[CArray[_CXUnsavedFile]]: unsaved_array = None if len(unsaved_files): unsaved_array = (_CXUnsavedFile * len(unsaved_files))() for i, (name, contents) in enumerate(unsaved_files): if hasattr(contents, "read"): - contents = tcast('SupportsReadStringData', contents).read() - binary_contents = b(tcast('StrOrBytes', contents)) + contents = tcast("SupportsReadStringData", contents).read() + binary_contents = b(tcast("StrOrBytes", contents)) unsaved_array[i].name = b(os.fspath(name)) unsaved_array[i].contents = binary_contents unsaved_array[i].length = len(binary_contents) return unsaved_array @classmethod def from_source( - cls, filename: Optional[FsPath], + cls, + filename: Optional[FsPath], args: Optional[List[str]] = None, unsaved_files: Optional[List[UnsavedFileInfo]] = None, options: int = 0, - index: Optional[Index] = None + index: Optional[Index] = None, ) -> Self: """Create a TranslationUnit by parsing source. This is capable of processing source code both from files on the filesystem as well as in-memory contents. @@ -3277,11 +3327,16 @@ this sequence is always the input file. Note that this method will not recursively iterate over header files included through precompiled headers. """ - def visitor(fobj: CObjectP, lptr: CPointer[SourceLocation], depth: int, includes: List[FileInclusion]) -> None: + def visitor( + fobj: CObjectP, + lptr: CPointer[SourceLocation], + depth: int, + includes: List[FileInclusion], + ) -> None: if depth > 0: loc = lptr.contents includes.append(FileInclusion(loc.file, File(fobj), loc, depth)) # Automatically adapt CIndex/ctype pointers to python objects @@ -3295,11 +3350,13 @@ def get_file(self, filename: FsPath) -> File: """Obtain a File from this translation unit.""" return File.from_name(self, filename) - def get_location(self, filename: FsPath, position: Tuple[int, int]) -> SourceLocation: + def get_location( + self, filename: FsPath, position: Tuple[int, int] + ) -> SourceLocation: """Obtain a SourceLocation for a file in this translation unit. The position can be specified by passing: - Integer file offset. Initial file offset is 0. @@ -3312,11 +3369,14 @@ return SourceLocation.from_offset(self, f, position) return SourceLocation.from_position(self, f, position[0], position[1]) _Location = TUnion[int, Tuple[int, int], SourceLocation] - def get_extent(self, filename: FsPath, locations: Tuple[_Location, _Location]) -> SourceRange: + + def get_extent( + self, filename: FsPath, locations: Tuple[_Location, _Location] + ) -> SourceRange: """Obtain a SourceRange from this translation unit. The bounds of the SourceRange must ultimately be defined by a start and end SourceLocation. For the locations argument, you can pass: @@ -3378,11 +3438,13 @@ raise IndexError return Diagnostic(diag) return DiagIterator(self) - def reparse(self, unsaved_files: Optional[List[UnsavedFileInfo]] = None, options: int = 0) -> None: + def reparse( + self, unsaved_files: Optional[List[UnsavedFileInfo]] = None, options: int = 0 + ) -> None: """ Reparse an already parsed translation unit. In-memory contents for files can be provided by passing a list of pairs as unsaved_files, the first items should be the filenames to be mapped @@ -3395,11 +3457,11 @@ unsaved_files_array = self.process_unsaved_files(unsaved_files) result = conf.lib.clang_reparseTranslationUnit( self, len(unsaved_files), unsaved_files_array, options ) if result != 0: - raise CXError(result, 'Error reparsing TranslationUnit.') + raise CXError(result, "Error reparsing TranslationUnit.") def save(self, filename: FsPath) -> None: """Saves the TranslationUnit to a file. This is equivalent to passing -emit-ast to the clang frontend. The @@ -3413,13 +3475,11 @@ TranslationUnit.diagnostics(). filename -- The path to save the translation unit to (str or PathLike). """ options = conf.lib.clang_defaultSaveOptions(self) - result = conf.lib.clang_saveTranslationUnit( - self, os.fspath(filename), options - ) + result = conf.lib.clang_saveTranslationUnit(self, os.fspath(filename), options) if result != 0: raise TranslationUnitSaveError(result, "Error saving TranslationUnit.") def codeComplete( self, @@ -3465,25 +3525,31 @@ ) if ptr: return CodeCompletionResults(ptr) return None - def get_tokens(self, locations: Optional[Tuple[SourceLocation, SourceLocation]] = None, extent: Optional[SourceRange] = None) -> Generator[Token, None, None]: + def get_tokens( + self, + locations: Optional[Tuple[SourceLocation, SourceLocation]] = None, + extent: Optional[SourceRange] = None, + ) -> Generator[Token, None, None]: """Obtain tokens in this translation unit. This is a generator for Token instances. The caller specifies a range of source code to obtain tokens for. The range can be specified as a 2-tuple of SourceLocation or as a SourceRange. If both are defined, behavior is undefined. """ - + if locations is not None: - final_extent = SourceRange.from_locations(start=locations[0], end=locations[1]) + final_extent = SourceRange.from_locations( + start=locations[0], end=locations[1] + ) elif extent is not None: final_extent = extent else: - raise ValueError('no extent given') + raise ValueError("no extent given") return TokenGroup.get_tokens(self, final_extent) class File(ClangObject): @@ -3516,13 +3582,15 @@ def __repr__(self) -> str: return "<File: %s>" % (self.name) @staticmethod - def from_result(res: CObjectP, fn: Callable[..., CObjectP], args: Tuple[Any, ...]) -> File: + def from_result( + res: CObjectP, fn: Callable[..., CObjectP], args: Tuple[Any, ...] + ) -> File: assert isinstance(res, c_object_p) - resobj = File(res) # pyright: ignore + resobj = File(res) # pyright: ignore # Copy a reference to the TranslationUnit to prevent premature GC. resobj._tu = args[0]._tu return resobj @@ -3640,11 +3708,13 @@ if not cc: raise IndexError return CompileCommand(cc, self) @staticmethod - def from_result(res: CObjectP, fn: Callable[..., CObjectP], args: Tuple[Any, ...]) -> Optional[CompileCommands]: + def from_result( + res: CObjectP, fn: Callable[..., CObjectP], args: Tuple[Any, ...] + ) -> Optional[CompileCommands]: if not res: return None return CompileCommands(res) @@ -3658,11 +3728,13 @@ def __del__(self) -> None: conf.lib.clang_CompilationDatabase_dispose(self) @staticmethod - def from_result(res: CObjectP, fn: Callable[..., CObjectP], args: Tuple[Any, ...]) -> CompilationDatabase: + def from_result( + res: CObjectP, fn: Callable[..., CObjectP], args: Tuple[Any, ...] + ) -> CompilationDatabase: if not res: raise CompilationDatabaseError(0, "CompilationDatabase loading failed") return CompilationDatabase(res) @staticmethod @@ -3737,11 +3809,11 @@ @property def cursor(self) -> Cursor: """The Cursor this Token corresponds to.""" cursor = Cursor() - cursor._tu = self._tu # pyright: ignore[reportPrivateUsage] + cursor._tu = self._tu # pyright: ignore[reportPrivateUsage] conf.lib.clang_annotateTokens(self._tu, byref(self), 1, byref(cursor)) return cursor @@ -3806,15 +3878,19 @@ # Now comes the plumbing to hook up the C library. # Register callback types -TranslationUnitIncludesCallback = Annotated[CFuncPointer, None, c_object_p, CPointer[SourceLocation], c_uint, py_object] +TranslationUnitIncludesCallback = Annotated[ + CFuncPointer, None, c_object_p, CPointer[SourceLocation], c_uint, py_object +] CursorVisitCallback = Annotated[CFuncPointer, c_int, Cursor, Cursor, py_object] FieldsVisitCallback = Annotated[CFuncPointer, c_int, Cursor, py_object] -translation_unit_includes_callback: TType[CFuncPointer] = convert_annotation(TranslationUnitIncludesCallback) +translation_unit_includes_callback: TType[CFuncPointer] = convert_annotation( + TranslationUnitIncludesCallback +) cursor_visit_callback: TType[CFuncPointer] = convert_annotation(CursorVisitCallback) fields_visit_callback: TType[CFuncPointer] = convert_annotation(FieldsVisitCallback) # Functions strictly alphabetical order. @@ -3823,26 +3899,38 @@ # - If Config.compatibility_check is set to `False`, then a function is allowed to be missing. # - If a function is missing in C library, it will not be replaced, thus causing NotImplementedError when called. # - Missing functions are given a `_missing_` attribute, you can check it with `hasattr(conf.lib.xxx, '_missing_')`. # - These stub functions are generated with a script from old data and manually corrected, so parameter names are missing. class LibclangExports: - def clang_annotateTokens(self, p1: TranslationUnit, p2: CPointerParam[Token], p3: p_ulong, p4: CPointerParam[Cursor]) -> r_long: + def clang_annotateTokens( + self, + p1: TranslationUnit, + p2: CPointerParam[Token], + p3: p_ulong, + p4: CPointerParam[Cursor], + ) -> r_long: raise NotImplementedError def clang_CompilationDatabase_dispose(self, p1: ClangObjectParam) -> r_long: raise NotImplementedError @with_errcheck(CompilationDatabase.from_result) - def clang_CompilationDatabase_fromDirectory(self, p1: CInteropString, p2: CPointerParam[c_ulong]) -> CObjectP: + def clang_CompilationDatabase_fromDirectory( + self, p1: CInteropString, p2: CPointerParam[c_ulong] + ) -> CObjectP: raise NotImplementedError @with_errcheck(CompileCommands.from_result) - def clang_CompilationDatabase_getAllCompileCommands(self, p1: ClangObjectParam) -> CObjectP: + def clang_CompilationDatabase_getAllCompileCommands( + self, p1: ClangObjectParam + ) -> CObjectP: raise NotImplementedError @with_errcheck(CompileCommands.from_result) - def clang_CompilationDatabase_getCompileCommands(self, p1: ClangObjectParam, p2: CInteropString) -> CObjectP: + def clang_CompilationDatabase_getCompileCommands( + self, p1: ClangObjectParam, p2: CInteropString + ) -> CObjectP: raise NotImplementedError def clang_CompileCommands_dispose(self, p1: CObjectP) -> r_long: raise NotImplementedError @@ -3865,14 +3953,25 @@ raise NotImplementedError def clang_CompileCommand_getNumArgs(self, p1: CObjectP) -> r_ulong: raise NotImplementedError - def clang_codeCompleteAt(self, p1: TranslationUnit, p2: CInteropString, p3: p_long, p4: p_long, p5: CPointerParam[_CXUnsavedFile], p6: p_long, p7: p_long) -> CPointer[CCRStructure]: - raise NotImplementedError - - def clang_codeCompleteGetDiagnostic(self, p1: CodeCompletionResults, p2: p_long) -> Diagnostic: + def clang_codeCompleteAt( + self, + p1: TranslationUnit, + p2: CInteropString, + p3: p_long, + p4: p_long, + p5: CPointerParam[_CXUnsavedFile], + p6: p_long, + p7: p_long, + ) -> CPointer[CCRStructure]: + raise NotImplementedError + + def clang_codeCompleteGetDiagnostic( + self, p1: CodeCompletionResults, p2: p_long + ) -> Diagnostic: raise NotImplementedError def clang_codeCompleteGetNumDiagnostics(self, p1: CodeCompletionResults) -> r_long: raise NotImplementedError @@ -3886,20 +3985,24 @@ raise NotImplementedError def clang_CXRewriter_dispose(self, p1: Rewriter) -> r_long: raise NotImplementedError - def clang_CXRewriter_insertTextBefore(self, p1: Rewriter, p2: SourceLocation, p3: CInteropString) -> r_long: + def clang_CXRewriter_insertTextBefore( + self, p1: Rewriter, p2: SourceLocation, p3: CInteropString + ) -> r_long: raise NotImplementedError def clang_CXRewriter_overwriteChangedFiles(self, p1: Rewriter) -> r_long: raise NotImplementedError def clang_CXRewriter_removeText(self, p1: Rewriter, p2: SourceRange) -> r_long: raise NotImplementedError - def clang_CXRewriter_replaceText(self, p1: Rewriter, p2: SourceRange, p3: CInteropString) -> r_long: + def clang_CXRewriter_replaceText( + self, p1: Rewriter, p2: SourceRange, p3: CInteropString + ) -> r_long: raise NotImplementedError def clang_CXRewriter_writeMainFileToStdOut(self, p1: Rewriter) -> r_long: raise NotImplementedError @@ -3970,11 +4073,13 @@ raise NotImplementedError def clang_disposeString(self, p1: _CXString) -> r_long: raise NotImplementedError - def clang_disposeTokens(self, p1: TranslationUnit, p2: CPointer[Token], p3: p_uint) -> r_long: + def clang_disposeTokens( + self, p1: TranslationUnit, p2: CPointer[Token], p3: p_uint + ) -> r_long: raise NotImplementedError def clang_disposeTranslationUnit(self, p1: TranslationUnit) -> r_long: raise NotImplementedError @@ -4027,11 +4132,13 @@ @with_errcheck(_CXString.from_result) def clang_getCompletionBriefComment(self, p1: CObjectP) -> _CXString: raise NotImplementedError - def clang_getCompletionChunkCompletionString(self, p1: CObjectP, p2: p_long) -> CObjectP: + def clang_getCompletionChunkCompletionString( + self, p1: CObjectP, p2: p_long + ) -> CObjectP: raise NotImplementedError def clang_getCompletionChunkKind(self, p1: CObjectP, p2: p_long) -> r_long: raise NotImplementedError @@ -4078,11 +4185,13 @@ @with_errcheck(Cursor.from_result) def clang_getCursorReferenced(self, p1: Cursor) -> Cursor: raise NotImplementedError - def clang_getCursorReferenceNameRange(self, p1: Cursor, p2: p_ulong, p3: p_ulong) -> SourceRange: + def clang_getCursorReferenceNameRange( + self, p1: Cursor, p2: p_ulong, p3: p_ulong + ) -> SourceRange: raise NotImplementedError @with_errcheck(Type.from_result) def clang_getCursorResultType(self, p1: Cursor) -> Type: raise NotImplementedError @@ -4126,11 +4235,13 @@ @with_errcheck(_CXString.from_result) def clang_getDiagnosticCategoryText(self, p1: Diagnostic) -> _CXString: raise NotImplementedError @with_errcheck(_CXString.from_result) - def clang_getDiagnosticFixIt(self, p1: Diagnostic, p2: p_ulong, p3: CPointerParam[SourceRange]) -> _CXString: + def clang_getDiagnosticFixIt( + self, p1: Diagnostic, p2: p_ulong, p3: CPointerParam[SourceRange] + ) -> _CXString: raise NotImplementedError def clang_getDiagnosticInSet(self, p1: CObjectP, p2: p_ulong) -> CObjectP: raise NotImplementedError @@ -4142,11 +4253,13 @@ def clang_getDiagnosticNumRanges(self, p1: Diagnostic) -> r_ulong: raise NotImplementedError @with_errcheck(_CXString.from_result) - def clang_getDiagnosticOption(self, p1: Diagnostic, p2: CPointerParam[_CXString]) -> _CXString: + def clang_getDiagnosticOption( + self, p1: Diagnostic, p2: CPointerParam[_CXString] + ) -> _CXString: raise NotImplementedError def clang_getDiagnosticRange(self, p1: Diagnostic, p2: p_ulong) -> SourceRange: raise NotImplementedError @@ -4190,20 +4303,36 @@ @with_errcheck(File.from_result) def clang_getIncludedFile(self, p1: Cursor) -> CObjectP: raise NotImplementedError - def clang_getInclusions(self, p1: TranslationUnit, p2: TranslationUnitIncludesCallback, p3: CPyObject[List[FileInclusion]]) -> r_long: - raise NotImplementedError - - def clang_getInstantiationLocation(self, p1: SourceLocation, p2: CPointerParam[CObjectP], p3: CPointerParam[c_ulong], p4: CPointerParam[c_ulong], p5: CPointerParam[c_ulong]) -> r_long: - raise NotImplementedError - - def clang_getLocation(self, p1: TranslationUnit, p2: File, p3: p_ulong, p4: p_ulong) -> SourceLocation: - raise NotImplementedError - - def clang_getLocationForOffset(self, p1: TranslationUnit, p2: File, p3: p_ulong) -> SourceLocation: + def clang_getInclusions( + self, + p1: TranslationUnit, + p2: TranslationUnitIncludesCallback, + p3: CPyObject[List[FileInclusion]], + ) -> r_long: + raise NotImplementedError + + def clang_getInstantiationLocation( + self, + p1: SourceLocation, + p2: CPointerParam[CObjectP], + p3: CPointerParam[c_ulong], + p4: CPointerParam[c_ulong], + p5: CPointerParam[c_ulong], + ) -> r_long: + raise NotImplementedError + + def clang_getLocation( + self, p1: TranslationUnit, p2: File, p3: p_ulong, p4: p_ulong + ) -> SourceLocation: + raise NotImplementedError + + def clang_getLocationForOffset( + self, p1: TranslationUnit, p2: File, p3: p_ulong + ) -> SourceLocation: raise NotImplementedError def clang_getNullCursor(self) -> Cursor: raise NotImplementedError @@ -4350,23 +4479,48 @@ raise NotImplementedError def clang_isVolatileQualifiedType(self, p1: Type) -> bool: raise NotImplementedError - def clang_parseTranslationUnit(self, p1: Index, p2: CInteropString, p3: CPointerParam[c_char_p], p4: p_long, p5: CPointerParam[_CXUnsavedFile], p6: p_long, p7: p_long) -> CObjectP: - raise NotImplementedError - - def clang_reparseTranslationUnit(self, p1: TranslationUnit, p2: p_long, p3: CPointerParam[_CXUnsavedFile], p4: p_long) -> r_long: - raise NotImplementedError - - def clang_saveTranslationUnit(self, p1: TranslationUnit, p2: CInteropString, p3: p_ulong) -> r_long: - raise NotImplementedError - - def clang_tokenize(self, p1: TranslationUnit, p2: SourceRange, p3: CPointerParam[CPointer[Token]], p4: CPointerParam[c_ulong]) -> r_long: - raise NotImplementedError - - def clang_visitChildren(self, p1: Cursor, p2: CursorVisitCallback, p3: CPyObject[List[Cursor]]) -> r_ulong: + def clang_parseTranslationUnit( + self, + p1: Index, + p2: CInteropString, + p3: CPointerParam[c_char_p], + p4: p_long, + p5: CPointerParam[_CXUnsavedFile], + p6: p_long, + p7: p_long, + ) -> CObjectP: + raise NotImplementedError + + def clang_reparseTranslationUnit( + self, + p1: TranslationUnit, + p2: p_long, + p3: CPointerParam[_CXUnsavedFile], + p4: p_long, + ) -> r_long: + raise NotImplementedError + + def clang_saveTranslationUnit( + self, p1: TranslationUnit, p2: CInteropString, p3: p_ulong + ) -> r_long: + raise NotImplementedError + + def clang_tokenize( + self, + p1: TranslationUnit, + p2: SourceRange, + p3: CPointerParam[CPointer[Token]], + p4: CPointerParam[c_ulong], + ) -> r_long: + raise NotImplementedError + + def clang_visitChildren( + self, p1: Cursor, p2: CursorVisitCallback, p3: CPyObject[List[Cursor]] + ) -> r_ulong: raise NotImplementedError def clang_Cursor_getNumArguments(self, p1: Cursor) -> r_long: raise NotImplementedError @@ -4383,14 +4537,18 @@ @with_errcheck(Type.from_result) def clang_Cursor_getTemplateArgumentType(self, p1: Cursor, p2: p_ulong) -> Type: raise NotImplementedError - def clang_Cursor_getTemplateArgumentValue(self, p1: Cursor, p2: p_ulong) -> r_longlong: - raise NotImplementedError - - def clang_Cursor_getTemplateArgumentUnsignedValue(self, p1: Cursor, p2: p_ulong) -> r_ulonglong: + def clang_Cursor_getTemplateArgumentValue( + self, p1: Cursor, p2: p_ulong + ) -> r_longlong: + raise NotImplementedError + + def clang_Cursor_getTemplateArgumentUnsignedValue( + self, p1: Cursor, p2: p_ulong + ) -> r_ulonglong: raise NotImplementedError def clang_Cursor_isAnonymous(self, p1: Cursor) -> bool: raise NotImplementedError @@ -4439,11 +4597,13 @@ @with_errcheck(Type.from_result) def clang_Type_getNamedType(self, p1: Type) -> Type: raise NotImplementedError - def clang_Type_visitFields(self, p1: Type, p2: FieldsVisitCallback, p3: CPyObject[List[Cursor]]) -> r_ulong: + def clang_Type_visitFields( + self, p1: Type, p2: FieldsVisitCallback, p3: CPyObject[List[Cursor]] + ) -> r_ulong: raise NotImplementedError class LibclangError(Exception): m: str @@ -4553,15 +4713,15 @@ raise LibclangError(msg) return library def function_exists(self, name: str) -> bool: - return not hasattr(getattr(self.lib, name), '_missing_') + return not hasattr(getattr(self.lib, name), "_missing_") def generate_metadata_debug() -> Dict[str, Dict[str, Any]]: - ''' Generate ctypes metadata for debugging purpose. ''' + """Generate ctypes metadata for debugging purpose.""" return {name: info for name, info in generate_metadata(LibclangExports, globals())} conf = Config() --- clang/ctyped.py 2024-08-03 14:41:42.000000 +0000 +++ clang/ctyped.py 2024-08-03 23:15:21.802497 +0000 @@ -1,48 +1,94 @@ -from ctypes import (CFUNCTYPE, POINTER, WINFUNCTYPE, c_bool, c_byte, c_char, - c_char_p, c_double, c_float, c_int, c_long, c_longdouble, - c_longlong, c_short, c_size_t, c_ssize_t, c_ubyte, c_uint, - c_ulong, c_ulonglong, c_ushort, c_void_p, c_wchar, - c_wchar_p, py_object) +from ctypes import ( + CFUNCTYPE, + POINTER, + WINFUNCTYPE, + c_bool, + c_byte, + c_char, + c_char_p, + c_double, + c_float, + c_int, + c_long, + c_longdouble, + c_longlong, + c_short, + c_size_t, + c_ssize_t, + c_ubyte, + c_uint, + c_ulong, + c_ulonglong, + c_ushort, + c_void_p, + c_wchar, + c_wchar_p, + py_object, +) from inspect import Parameter, signature -from typing import (TYPE_CHECKING, Any, Callable, Dict, Generator, Generic, - List, Optional, Tuple, Type, TypeVar, Union, cast) +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Generator, + Generic, + List, + Optional, + Tuple, + Type, + TypeVar, + Union, + cast, +) from typing_extensions import Annotated, ParamSpec, TypeAlias -_T = TypeVar('_T') +_T = TypeVar("_T") if TYPE_CHECKING: from ctypes import _CArgObject # pyright: ignore[reportPrivateUsage] from ctypes import _CData # pyright: ignore[reportPrivateUsage] -AnyCData = TypeVar('AnyCData', bound='_CData') +AnyCData = TypeVar("AnyCData", bound="_CData") if TYPE_CHECKING: from ctypes import Array as _Array # pyright: ignore[reportPrivateUsage] - from ctypes import \ - _FuncPointer as _FuncPointer # pyright: ignore[reportPrivateUsage] - from ctypes import \ - _Pointer as _Pointer # pyright: ignore[reportPrivateUsage] + from ctypes import ( + _FuncPointer as _FuncPointer, + ) # pyright: ignore[reportPrivateUsage] + from ctypes import _Pointer as _Pointer # pyright: ignore[reportPrivateUsage] # ctypes documentation noted implicit conversion for pointers: # "For example, you can pass compatible array instances instead of pointer # types. So, for POINTER(c_int), ctypes accepts an array of c_int:" # "In addition, if a function argument is explicitly declared to be a # pointer type (such as POINTER(c_int)) in argtypes, an object of the # pointed type (c_int in this case) can be passed to the function. ctypes # will apply the required byref() conversion in this case automatically." # also, current ctype typeshed thinks byref returns _CArgObject - _PointerCompatible: TypeAlias = Union['_CArgObject', _Pointer[AnyCData], None, _Array[AnyCData], AnyCData] - _PyObject: TypeAlias = Union['py_object[_T]', _T] + _PointerCompatible: TypeAlias = Union[ + "_CArgObject", _Pointer[AnyCData], None, _Array[AnyCData], AnyCData + ] + _PyObject: TypeAlias = Union["py_object[_T]", _T] else: # at runtime we don't really import those symbols - class _Array(Generic[AnyCData]): ... - class _Pointer(Generic[AnyCData]): ... - class _PointerCompatible(Generic[AnyCData]): ... - class _FuncPointer: ... - class _PyObject(Generic[AnyCData]): ... + class _Array(Generic[AnyCData]): + ... + + class _Pointer(Generic[AnyCData]): + ... + + class _PointerCompatible(Generic[AnyCData]): + ... + + class _FuncPointer: + ... + + class _PyObject(Generic[AnyCData]): + ... # ANNO_CONVETIBLE can be used to declare that a class have a `from_param` # method which can convert other types when used as `argtypes`. # For example: `CClass = Annotated[bytes, ANNO_CONVERTIBLE, c_class]` means @@ -100,13 +146,17 @@ p_size_t = Annotated[Union[c_size_t, int], ANNO_BASIC, c_size_t] p_ssize_t = Annotated[Union[c_ssize_t, int], ANNO_BASIC, c_ssize_t] p_float = Annotated[Union[c_float, float], ANNO_BASIC, c_float] p_double = Annotated[Union[c_double, float], ANNO_BASIC, c_double] p_longdouble = Annotated[Union[c_longdouble, float], ANNO_BASIC, c_longdouble] -p_char_p = Annotated[Union[c_char_p, _Array[c_wchar], bytes, None], ANNO_BASIC, c_char_p] -p_wchar_p = Annotated[Union[c_wchar_p, _Array[c_wchar], str, None], ANNO_BASIC, c_wchar_p] -p_void_p = Annotated[Union['_CArgObject', c_void_p, int, None], ANNO_BASIC, c_void_p] +p_char_p = Annotated[ + Union[c_char_p, _Array[c_wchar], bytes, None], ANNO_BASIC, c_char_p +] +p_wchar_p = Annotated[ + Union[c_wchar_p, _Array[c_wchar], str, None], ANNO_BASIC, c_wchar_p +] +p_void_p = Annotated[Union["_CArgObject", c_void_p, int, None], ANNO_BASIC, c_void_p] # export Pointer, PointerCompatible, Array and FuncPointer annotation CArray = Annotated[_Array[AnyCData], ANNO_ARRAY] CPointer = Annotated[_Pointer[AnyCData], ANNO_POINTER] @@ -114,163 +164,196 @@ CFuncPointer = Annotated[_FuncPointer, ANNO_CFUNC] WinFuncPointer = Annotated[_FuncPointer, ANNO_WINFUNC] CPyObject = Annotated[_PyObject[_T], ANNO_PYOBJ] -_Params = ParamSpec('_Params') -_OrigRet = TypeVar('_OrigRet') -_NewRet = TypeVar('_NewRet') - -def with_errcheck(checker: Callable[[_OrigRet, Callable[..., _OrigRet], Tuple[Any, ...]], _NewRet]) -> Callable[[Callable[_Params, _OrigRet]], Callable[_Params, _NewRet]]: - ''' Decorates a stub function with an error checker. ''' +_Params = ParamSpec("_Params") +_OrigRet = TypeVar("_OrigRet") +_NewRet = TypeVar("_NewRet") + + +def with_errcheck( + checker: Callable[[_OrigRet, Callable[..., _OrigRet], Tuple[Any, ...]], _NewRet] +) -> Callable[[Callable[_Params, _OrigRet]], Callable[_Params, _NewRet]]: + """Decorates a stub function with an error checker.""" + def decorator(wrapped: Callable[_Params, _OrigRet]) -> Callable[_Params, _NewRet]: def wrapper(*args: _Params.args, **kwargs: _Params.kwargs) -> _NewRet: raise NotImplementedError # attach original declaration and error checker to wrapper - setattr(wrapper, '_decl_errcheck_', (wrapped, checker)) + setattr(wrapper, "_decl_errcheck_", (wrapped, checker)) return wrapper return decorator + # NOTE: Actually, converter is a deprecated form of `restype`. # According to ctypes documentation: # "It is possible to assign a callable Python object that is not a ctypes # type, in this case the function is assumed to return a C int, and the # callable will be called with this integer, allowing further processing # or error checking. Using this is deprecated, for more flexible post # processing or error checking use a ctypes data type as restype and # assign a callable to the errcheck attribute." -def with_converter(converter: Callable[[int], _NewRet]) -> Callable[[Callable[_Params, r_int]], Callable[_Params, _NewRet]]: - ''' Decorates a stub function with a converter, its return type MUST be `r_int`. ''' + +def with_converter( + converter: Callable[[int], _NewRet] +) -> Callable[[Callable[_Params, r_int]], Callable[_Params, _NewRet]]: + """Decorates a stub function with a converter, its return type MUST be `r_int`.""" + def decorator(wrapped: Callable[_Params, r_int]) -> Callable[_Params, _NewRet]: def wrapper(*args: _Params.args, **kwargs: _Params.kwargs) -> _NewRet: raise NotImplementedError # attach original declaration and converter to wrapper - setattr(wrapper, '_decl_converter_', (wrapped, converter)) + setattr(wrapper, "_decl_converter_", (wrapped, converter)) return wrapper return decorator -def convert_annotation(typ: Any, global_ns: Optional[Dict[str, Any]] = None) -> Type[Any]: - ''' Convert an annotation to effective runtime type. ''' +def convert_annotation( + typ: Any, global_ns: Optional[Dict[str, Any]] = None +) -> Type[Any]: + """Convert an annotation to effective runtime type.""" if global_ns is None: global_ns = globals() if isinstance(typ, str): - try: typ = eval(typ, global_ns) + try: + typ = eval(typ, global_ns) except Exception as exc: - raise ValueError('Evaluation of delayed annotation failed!') from exc - - if not hasattr(typ, '__metadata__'): + raise ValueError("Evaluation of delayed annotation failed!") from exc + + if not hasattr(typ, "__metadata__"): return cast(Type[Any], typ) # type is Annotated ident, *detail = typ.__metadata__ if ident is ANNO_CONVERTIBLE: - ctyp, = detail + (ctyp,) = detail return cast(Type[Any], ctyp) elif ident is ANNO_ARRAY: - try: count, = detail + try: + (count,) = detail except ValueError: - raise ValueError('CArray needs to be annotated with its size') - ctyp, = typ.__args__[0].__args__ + raise ValueError("CArray needs to be annotated with its size") + (ctyp,) = typ.__args__[0].__args__ return cast(Type[Any], convert_annotation(ctyp, global_ns=global_ns) * count) elif ident is ANNO_POINTER: assert not detail - ctyp, = typ.__args__[0].__args__ - return POINTER(convert_annotation(ctyp, global_ns=global_ns)) # pyright: ignore + (ctyp,) = typ.__args__[0].__args__ + return POINTER(convert_annotation(ctyp, global_ns=global_ns)) # pyright: ignore elif ident is ANNO_CFUNC: if not detail: - raise ValueError('CFuncPointer needs to be annotated with its signature') + raise ValueError("CFuncPointer needs to be annotated with its signature") return CFUNCTYPE(*(convert_annotation(t, global_ns=global_ns) for t in detail)) elif ident is ANNO_WINFUNC: if not detail: - raise ValueError('WinFuncPointer needs to be annotated with its signature') - return WINFUNCTYPE(*(convert_annotation(t, global_ns=global_ns) for t in detail)) + raise ValueError("WinFuncPointer needs to be annotated with its signature") + return WINFUNCTYPE( + *(convert_annotation(t, global_ns=global_ns) for t in detail) + ) elif ident is ANNO_PYOBJ: assert not detail return py_object else: - raise ValueError(f'Unexpected annotated type {typ}') - - -def convert_func_decl(decl: Callable[..., Any], global_ns: Optional[Dict[str, Any]] = None) -> Dict[str, Any]: - ''' Converts a stub function to ctypes metadata. ''' + raise ValueError(f"Unexpected annotated type {typ}") + + +def convert_func_decl( + decl: Callable[..., Any], global_ns: Optional[Dict[str, Any]] = None +) -> Dict[str, Any]: + """Converts a stub function to ctypes metadata.""" if global_ns is None: global_ns = globals() result: Dict[str, Any] = {} errcheck = None converter = None while True: - if hasattr(decl, '_decl_errcheck_'): + if hasattr(decl, "_decl_errcheck_"): if errcheck is not None: - raise ValueError('duplicate errcheck in stub function') - decl, errcheck = getattr(decl, '_decl_errcheck_') + raise ValueError("duplicate errcheck in stub function") + decl, errcheck = getattr(decl, "_decl_errcheck_") continue - if hasattr(decl, '_decl_converter_'): + if hasattr(decl, "_decl_converter_"): if converter is not None: - raise ValueError('duplicate converter in stub function') - decl, converter = getattr(decl, '_decl_converter_') + raise ValueError("duplicate converter in stub function") + decl, converter = getattr(decl, "_decl_converter_") continue break sig = signature(decl) - param_annos = [p.annotation for p in sig.parameters.values() if p.name != 'self'] + param_annos = [p.annotation for p in sig.parameters.values() if p.name != "self"] if all(anno is not Parameter.empty for anno in param_annos): - result['argtypes'] = [convert_annotation(anno, global_ns=global_ns) for anno in param_annos] or None + result["argtypes"] = [ + convert_annotation(anno, global_ns=global_ns) for anno in param_annos + ] or None if sig.return_annotation is not Parameter.empty: - result['restype'] = convert_annotation(sig.return_annotation, global_ns=global_ns) - - if errcheck is not None: result['errcheck'] = errcheck - if converter is not None: result['restype'] = converter + result["restype"] = convert_annotation( + sig.return_annotation, global_ns=global_ns + ) + + if errcheck is not None: + result["errcheck"] = errcheck + if converter is not None: + result["restype"] = converter return result if TYPE_CHECKING: from ctypes import CDLL, WinDLL - _DLLT = TypeVar('_DLLT', bound=CDLL) -_LibDecl = TypeVar('_LibDecl') - - -def generate_metadata(decl_cls: Type[_LibDecl], global_ns: Optional[Dict[str, Any]] = None) -> Generator[Tuple[str, Dict[str, Any]], None, None]: - ''' Generate ctypes metadata for a stub class. ''' + + _DLLT = TypeVar("_DLLT", bound=CDLL) +_LibDecl = TypeVar("_LibDecl") + + +def generate_metadata( + decl_cls: Type[_LibDecl], global_ns: Optional[Dict[str, Any]] = None +) -> Generator[Tuple[str, Dict[str, Any]], None, None]: + """Generate ctypes metadata for a stub class.""" if global_ns is None: global_ns = globals() for name in dir(decl_cls): - if name.startswith('_'): continue + if name.startswith("_"): + continue value = getattr(decl_cls, name) - if not callable(value): continue + if not callable(value): + continue yield name, convert_func_decl(value, global_ns=global_ns) -def load_annotated_library(loader: 'Union[CDLL, WinDLL]', decl_cls: Type[_LibDecl], global_ns: Optional[Dict[str, Any]] = None) -> Tuple[_LibDecl, List[str]]: - ''' Load a library and set signature metadata according to python type hints. - `decl_cls` is a class which should only contain method declarations. - Note: you should only name `self` as `self`, the converter depends on this. - ''' +def load_annotated_library( + loader: "Union[CDLL, WinDLL]", + decl_cls: Type[_LibDecl], + global_ns: Optional[Dict[str, Any]] = None, +) -> Tuple[_LibDecl, List[str]]: + """Load a library and set signature metadata according to python type hints. + `decl_cls` is a class which should only contain method declarations. + Note: you should only name `self` as `self`, the converter depends on this. + """ if global_ns is None: global_ns = globals() result = decl_cls() missing: List[str] = [] for name, info in generate_metadata(decl_cls, global_ns=global_ns): - try: func = getattr(loader, name) + try: + func = getattr(loader, name) except AttributeError: stub = getattr(result, name) stub._missing_ = True missing.append(name) continue @@ -282,64 +365,60 @@ return result, missing __all__ = [ - 'ANNO_CONVERTIBLE', - 'AnyCData', - - 'p_bool', - 'p_char', - 'p_wchar', - 'p_byte', - 'p_ubyte', - 'p_short', - 'p_ushort', - 'p_int', - 'p_uint', - 'p_long', - 'p_ulong', - 'p_longlong', - 'p_ulonglong', - 'p_size_t', - 'p_ssize_t', - 'p_float', - 'p_double', - 'p_longdouble', - 'p_char_p', - 'p_wchar_p', - 'p_void_p', - - 'r_bool', - 'r_char', - 'r_wchar', - 'r_byte', - 'r_ubyte', - 'r_short', - 'r_ushort', - 'r_int', - 'r_uint', - 'r_long', - 'r_ulong', - 'r_longlong', - 'r_ulonglong', - 'r_size_t', - 'r_ssize_t', - 'r_float', - 'r_double', - 'r_longdouble', - 'r_char_p', - 'r_wchar_p', - 'r_void_p', - - 'CArray', - 'CPointer', - 'CPointerParam', - 'CFuncPointer', - 'WinFuncPointer', - 'CPyObject', - - 'convert_annotation', - 'with_errcheck', - 'with_converter', - 'load_annotated_library', + "ANNO_CONVERTIBLE", + "AnyCData", + "p_bool", + "p_char", + "p_wchar", + "p_byte", + "p_ubyte", + "p_short", + "p_ushort", + "p_int", + "p_uint", + "p_long", + "p_ulong", + "p_longlong", + "p_ulonglong", + "p_size_t", + "p_ssize_t", + "p_float", + "p_double", + "p_longdouble", + "p_char_p", + "p_wchar_p", + "p_void_p", + "r_bool", + "r_char", + "r_wchar", + "r_byte", + "r_ubyte", + "r_short", + "r_ushort", + "r_int", + "r_uint", + "r_long", + "r_ulong", + "r_longlong", + "r_ulonglong", + "r_size_t", + "r_ssize_t", + "r_float", + "r_double", + "r_longdouble", + "r_char_p", + "r_wchar_p", + "r_void_p", + "CArray", + "CPointer", + "CPointerParam", + "CFuncPointer", + "WinFuncPointer", + "CPyObject", + "convert_annotation", + "with_errcheck", + "with_converter", + "load_annotated_library", ] --- tests/ctyped/test_stub_conversion.py 2024-08-03 03:21:33.000000 +0000 +++ tests/ctyped/test_stub_conversion.py 2024-08-03 23:15:22.143008 +0000 @@ -7,14 +7,20 @@ from dictdiffer import diff as dictdiff # type: ignore from clang.cindex import * from clang.cindex import _CXString # pyright: ignore[reportPrivateUsage] -from clang.cindex import (CCRStructure, Rewriter, c_interop_string, c_object_p, - cursor_visit_callback, fields_visit_callback, - generate_metadata_debug, - translation_unit_includes_callback) +from clang.cindex import ( + CCRStructure, + Rewriter, + c_interop_string, + c_object_p, + cursor_visit_callback, + fields_visit_callback, + generate_metadata_debug, + translation_unit_includes_callback, +) # Functions strictly alphabetical order. # This is previous version of ctypes metadata, we check equality to this so # that we can ensure `ctyped` doesn't break anything in its conversion. @@ -304,56 +310,73 @@ ] # Sadly, ctypes provides no API to check if type is pointer or array. # Here we use regex to check type name. -arr_regex = re.compile(r'(?P<typ>[A-Za-z0-9_]+)_Array_(?P<count>[0-9]+)') -ptr_regex = re.compile(r'LP_(?P<typ>[A-Za-z0-9_]+)') +arr_regex = re.compile(r"(?P<typ>[A-Za-z0-9_]+)_Array_(?P<count>[0-9]+)") +ptr_regex = re.compile(r"LP_(?P<typ>[A-Za-z0-9_]+)") + def is_ptr_type(typ: Any): - return typ in (c_void_p, c_char_p, c_wchar_p) or ptr_regex.fullmatch(typ.__name__) is not None + return ( + typ in (c_void_p, c_char_p, c_wchar_p) + or ptr_regex.fullmatch(typ.__name__) is not None + ) + def is_arr_type(typ: Any): return arr_regex.fullmatch(typ.__name__) is not None + # If we change a c_void_p parameter to a more exact pointer types, it # should still be working. def is_void_specialization(old_type: Any, new_type: Any): return old_type == c_void_p and is_ptr_type(new_type) def old_data_to_dict(data: List[Any]): result: Dict[str, Any] = {} - result['argtypes'], *data = data - if not result['argtypes']: result['argtypes'] = None - if data: result['restype'], *data = data - else: result['restype'] = c_int - if data: result['errcheck'], *data = data + result["argtypes"], *data = data + if not result["argtypes"]: + result["argtypes"] = None + if data: + result["restype"], *data = data + else: + result["restype"] = c_int + if data: + result["errcheck"], *data = data return result def is_incompatible_diff(diff: Any): - kind, path, detail = diff # pyright: ignore[reportUnusedVariable] - if kind == 'add': return True + kind, path, detail = diff # pyright: ignore[reportUnusedVariable] + if kind == "add": + return True old_type, new_type = detail - if is_void_specialization(old_type, new_type): return False + if is_void_specialization(old_type, new_type): + return False return True class TestStubConversion(unittest.TestCase): def test_equality(self): """Ensure that ctyped does not break anything.""" - old_function_dict: Dict[str, Dict[str, Any]] = {name: old_data_to_dict(val) for name, *val in FUNCTION_LIST} + old_function_dict: Dict[str, Dict[str, Any]] = { + name: old_data_to_dict(val) for name, *val in FUNCTION_LIST + } new_function_dict = generate_metadata_debug() missing_functions = set(old_function_dict.keys()) stable_functions: Set[str] = set() for new_func in new_function_dict: if new_func in missing_functions: missing_functions.remove(new_func) stable_functions.add(new_func) - type_diff = [list(dictdiff(old_function_dict[name], new_function_dict[name])) for name in stable_functions] # type: ignore - type_break = [diffset for diffset in type_diff if diffset and any(is_incompatible_diff(diff) for diff in diffset)] # type: ignore - - self.assertTrue(not missing_functions, f'Functions {missing_functions} are missing after stub conversion!') - self.assertTrue(not type_break, f'Type break happens after stub conversion!') + type_diff = [list(dictdiff(old_function_dict[name], new_function_dict[name])) for name in stable_functions] # type: ignore + type_break = [diffset for diffset in type_diff if diffset and any(is_incompatible_diff(diff) for diff in diffset)] # type: ignore + + self.assertTrue( + not missing_functions, + f"Functions {missing_functions} are missing after stub conversion!", + ) + self.assertTrue(not type_break, f"Type break happens after stub conversion!") `````````` </details> https://github.com/llvm/llvm-project/pull/101784 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits