[clang] Fix all mypy --strict errors in clang python binding (PR #101784)
DeinAlptraum wrote: @TsXor thank you for your work! I am generally all in favor of typing annotations, and I like that `ctyped` adds a stronger connection between the Python function definitions and the C library functions in terms of types. That said, this PR is pretty massive and grows the Python bindings by about 800 lines of code (ignoring tests), which imo is quite a lot just to pass the strict type check. There are also a lot of refactoring and other changes in this PR that, while generally welcome, seem unrelated and should be factored out. In general, multiple smaller PRs are preferred over something of this size, to have clearer boundaries and ease reviewing. For reference, I'd like to point out that I've also opened a PR for strict typing in #78114, which has been under review for a while. With that out of the way: I'm still a very new contributor and since I've opened a PR for essentially the exact same thing, don't feel comfortable deciding on how to proceed with this. I'm summoning @Endilll: what are your thoughts on this? 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
[clang] [libclang] Fix symbol version of `getBinaryOpcode` functions (PR #101820)
https://github.com/DeinAlptraum closed https://github.com/llvm/llvm-project/pull/101820 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang] Move getBinaryOpcode library functions to LLVM 19 (PR #101820)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/101820 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang] Move getBinaryOpcode library functions to LLVM 19 (PR #101820)
https://github.com/DeinAlptraum created https://github.com/llvm/llvm-project/pull/101820 None >From aff16c3b3a0cb7065a5ac143ae06850bbbeb666a Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 3 Aug 2024 13:43:41 +0100 Subject: [PATCH] [libclang] Move getBinaryOpcode library functions to LLVM 19 --- clang/tools/libclang/libclang.map | 8 ++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clang/tools/libclang/libclang.map b/clang/tools/libclang/libclang.map index 91c329b5765d4..371fe512ce71c 100644 --- a/clang/tools/libclang/libclang.map +++ b/clang/tools/libclang/libclang.map @@ -54,8 +54,6 @@ LLVM_13 { clang_Cursor_Evaluate; clang_Cursor_getArgument; clang_Cursor_getBriefCommentText; -clang_Cursor_getBinaryOpcode; -clang_Cursor_getBinaryOpcodeStr; clang_Cursor_getCXXManglings; clang_Cursor_getCommentRange; clang_Cursor_getMangling; @@ -430,6 +428,12 @@ LLVM_17 { clang_getCursorUnaryOperatorKind; }; +LLVM_19 { + global: +clang_Cursor_getBinaryOpcode; +clang_Cursor_getBinaryOpcodeStr; +}; + # Example of how to add a new symbol version entry. If you do add a new symbol # version, please update the example to depend on the version you added. # LLVM_X { ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang] Move getBinaryOpcode library functions to LLVM 19 (PR #101820)
https://github.com/DeinAlptraum milestoned https://github.com/llvm/llvm-project/pull/101820 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
@@ -54,6 +54,8 @@ LLVM_13 { clang_Cursor_Evaluate; clang_Cursor_getArgument; clang_Cursor_getBriefCommentText; +clang_Cursor_getBinaryOpcode; +clang_Cursor_getBinaryOpcodeStr; DeinAlptraum wrote: I will open a fix PR rightaway https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
@@ -54,6 +54,8 @@ LLVM_13 { clang_Cursor_Evaluate; clang_Cursor_getArgument; clang_Cursor_getBriefCommentText; +clang_Cursor_getBinaryOpcode; +clang_Cursor_getBinaryOpcodeStr; DeinAlptraum wrote: This is the `LLVM_13` section of the file. >From the top of the file: ``` # If you add a symbol to this file, make sure to add it with the correct # version. For example, if the LLVM main branch is LLVM 14.0.0, add new # symbols with the version LLVM_14. # On platforms where versions scripts are not used, this file will be used to # generate a list of exports for libclang.so ``` I'm not familiar with how the bindings actually interact with the C++ side, so I don't know what kinds of problems this would cause. Does this need to be backported for 19.x? @Endilll are you familiar with this? https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix bug in `SourceRange.__contains__`, add tests (PR #101802)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/101802 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix bug in `SourceRange.__contains__`, add tests (PR #101802)
https://github.com/DeinAlptraum created https://github.com/llvm/llvm-project/pull/101802 This resolves #22617 and #52827 >From 444d9480cf9629c81ae26636922af20db5bd52bf Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 3 Aug 2024 09:28:02 +0100 Subject: [PATCH] [libclang/python] Fix bug in SourceRange.__contains__, add tests --- clang/bindings/python/clang/cindex.py | 4 ++ .../python/tests/cindex/test_source_range.py | 56 +++ 2 files changed, 60 insertions(+) create mode 100644 clang/bindings/python/tests/cindex/test_source_range.py diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index c251c46a04adf..5fd7cc6481073 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -386,6 +386,10 @@ def __contains__(self, other): # same file, in between lines if self.start.line < other.line < self.end.line: return True +# between columns in one-liner range +elif self.start.line == other.line == self.end.line: +if self.start.column <= other.column <= self.end.column: +return True elif self.start.line == other.line: # same file first line if self.start.column <= other.column: diff --git a/clang/bindings/python/tests/cindex/test_source_range.py b/clang/bindings/python/tests/cindex/test_source_range.py new file mode 100644 index 0..9f76848e89020 --- /dev/null +++ b/clang/bindings/python/tests/cindex/test_source_range.py @@ -0,0 +1,56 @@ +import unittest + +from clang.cindex import SourceLocation, SourceRange + +from .util import get_tu + + +def create_location(tu, line, column): +return SourceLocation.from_position(tu, tu.get_file(tu.spelling), line, column) + + +def create_range(tu, line1, column1, line2, column2): +return SourceRange.from_locations( +create_location(tu, line1, column1), create_location(tu, line2, column2) +) + + +class TestSourceRange(unittest.TestCase): +def test_contains(self): +tu = get_tu( +"""a +a +a +a""" +) + +l13 = create_location(tu, 1, 3) +l21 = create_location(tu, 2, 1) +l22 = create_location(tu, 2, 2) +l23 = create_location(tu, 2, 3) +l24 = create_location(tu, 2, 4) +l25 = create_location(tu, 2, 5) +l33 = create_location(tu, 3, 3) +l31 = create_location(tu, 3, 1) +r22_24 = create_range(tu, 2, 2, 2, 4) +r23_23 = create_range(tu, 2, 3, 2, 3) +r24_32 = create_range(tu, 2, 4, 3, 2) +r14_32 = create_range(tu, 1, 4, 3, 2) + +assert l13 not in r22_24 # Line before start +assert l21 not in r22_24 # Column before start +assert l22 in r22_24 # Colum on start +assert l23 in r22_24 # Column in range +assert l24 in r22_24 # Column on end +assert l25 not in r22_24 # Column after end +assert l33 not in r22_24 # Line after end + +assert l23 in r23_23 # In one-column range + +assert l23 not in r24_32 # Outside range in first line +assert l33 not in r24_32 # Outside range in last line +assert l25 in r24_32 # In range in first line +assert l31 in r24_32 # In range in last line + +assert l21 in r14_32 # In range at start of center line +assert l25 in r14_32 # In range at end of center line ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix `get_exception_specification_kind` (PR #101548)
DeinAlptraum wrote: Could you merge this for me? On related note, do you think my contributions would count for "a track record of submitting high quality patches", i.e. should I apply for commit access? https://github.com/llvm/llvm-project/pull/101548 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix get_exception_specification_kind (PR #101548)
DeinAlptraum wrote: I can't think of any more genereal approach that prevents this sort of problem. Type checking would help with this (e.g. giving an error when an unknown attribute is called on an object) though it wouldn't have worked in this specific case... otherwise nothing to do but add more tests I guess. https://github.com/llvm/llvm-project/pull/101548 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix get_exception_specification_kind (PR #101548)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/101548 >From e7e66f0f90d7fb9adf3d3f546becd87c7527fa4c Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Thu, 1 Aug 2024 20:01:34 +0100 Subject: [PATCH 1/2] [libclang/python] Fix get_exception_specification_kind --- clang/bindings/python/clang/cindex.py| 2 +- .../cindex/test_exception_specification_kind.py | 12 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 2038ef6045c7d..c251c46a04adf 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -2654,7 +2654,7 @@ def get_exception_specification_kind(self): the ExceptionSpecificationKind enumeration. """ return ExceptionSpecificationKind.from_id( -conf.lib.clang.getExceptionSpecificationType(self) +conf.lib.clang_getExceptionSpecificationType(self) ) @property diff --git a/clang/bindings/python/tests/cindex/test_exception_specification_kind.py b/clang/bindings/python/tests/cindex/test_exception_specification_kind.py index 8e2a6b5c50223..e4742db31adbe 100644 --- a/clang/bindings/python/tests/cindex/test_exception_specification_kind.py +++ b/clang/bindings/python/tests/cindex/test_exception_specification_kind.py @@ -13,7 +13,7 @@ def find_function_declarations(node, declarations=[]): if node.kind == clang.cindex.CursorKind.FUNCTION_DECL: -declarations.append((node.spelling, node.exception_specification_kind)) +declarations.append(node) for child in node.get_children(): declarations = find_function_declarations(child, declarations) return declarations @@ -33,4 +33,12 @@ def test_exception_specification_kind(self): ("square2", ExceptionSpecificationKind.BASIC_NOEXCEPT), ("square3", ExceptionSpecificationKind.COMPUTED_NOEXCEPT), ] -self.assertListEqual(declarations, expected) +from_cursor = [ +(node.spelling, node.exception_specification_kind) for node in declarations +] +from_type = [ +(node.spelling, node.type.get_exception_specification_kind()) +for node in declarations +] +self.assertListEqual(from_cursor, expected) +self.assertListEqual(from_type, expected) >From 446507880b0204a7b7941826c1fcaab467c36109 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 2 Aug 2024 08:06:49 +0100 Subject: [PATCH 2/2] Add release note --- clang/docs/ReleaseNotes.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ba70b138a04c4..8101d4327cd47 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -299,6 +299,7 @@ Sanitizers Python Binding Changes -- +- Fixed an issue that led to crashes when calling ``Type.get_exception_specification_kind``. OpenMP Support -- ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix get_exception_specification_kind (PR #101548)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/101548 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix get_exception_specification_kind (PR #101548)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/101548 >From e7e66f0f90d7fb9adf3d3f546becd87c7527fa4c Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Thu, 1 Aug 2024 20:01:34 +0100 Subject: [PATCH] [libclang/python] Fix get_exception_specification_kind --- clang/bindings/python/clang/cindex.py| 2 +- .../cindex/test_exception_specification_kind.py | 12 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 2038ef6045c7d..c251c46a04adf 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -2654,7 +2654,7 @@ def get_exception_specification_kind(self): the ExceptionSpecificationKind enumeration. """ return ExceptionSpecificationKind.from_id( -conf.lib.clang.getExceptionSpecificationType(self) +conf.lib.clang_getExceptionSpecificationType(self) ) @property diff --git a/clang/bindings/python/tests/cindex/test_exception_specification_kind.py b/clang/bindings/python/tests/cindex/test_exception_specification_kind.py index 8e2a6b5c50223..e4742db31adbe 100644 --- a/clang/bindings/python/tests/cindex/test_exception_specification_kind.py +++ b/clang/bindings/python/tests/cindex/test_exception_specification_kind.py @@ -13,7 +13,7 @@ def find_function_declarations(node, declarations=[]): if node.kind == clang.cindex.CursorKind.FUNCTION_DECL: -declarations.append((node.spelling, node.exception_specification_kind)) +declarations.append(node) for child in node.get_children(): declarations = find_function_declarations(child, declarations) return declarations @@ -33,4 +33,12 @@ def test_exception_specification_kind(self): ("square2", ExceptionSpecificationKind.BASIC_NOEXCEPT), ("square3", ExceptionSpecificationKind.COMPUTED_NOEXCEPT), ] -self.assertListEqual(declarations, expected) +from_cursor = [ +(node.spelling, node.exception_specification_kind) for node in declarations +] +from_type = [ +(node.spelling, node.type.get_exception_specification_kind()) +for node in declarations +] +self.assertListEqual(from_cursor, expected) +self.assertListEqual(from_type, expected) ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix get_exception_specification_kind (PR #101548)
https://github.com/DeinAlptraum created https://github.com/llvm/llvm-project/pull/101548 This fixes a bug with `get_exception_ specification_kind`. The function did not work before. Also add a test that confirms that it works now. >From 7e5ff53e8dc56e15f84c3868a25a779be54a22f0 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Thu, 1 Aug 2024 20:01:34 +0100 Subject: [PATCH] [libclang/python] Fix get_exception_specification_kind --- clang/bindings/python/clang/cindex.py | 2 +- .../tests/cindex/test_exception_specification_kind.py | 7 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 2038ef6045c7d..c251c46a04adf 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -2654,7 +2654,7 @@ def get_exception_specification_kind(self): the ExceptionSpecificationKind enumeration. """ return ExceptionSpecificationKind.from_id( -conf.lib.clang.getExceptionSpecificationType(self) +conf.lib.clang_getExceptionSpecificationType(self) ) @property diff --git a/clang/bindings/python/tests/cindex/test_exception_specification_kind.py b/clang/bindings/python/tests/cindex/test_exception_specification_kind.py index 8e2a6b5c50223..6653c22eaf9cd 100644 --- a/clang/bindings/python/tests/cindex/test_exception_specification_kind.py +++ b/clang/bindings/python/tests/cindex/test_exception_specification_kind.py @@ -13,7 +13,7 @@ def find_function_declarations(node, declarations=[]): if node.kind == clang.cindex.CursorKind.FUNCTION_DECL: -declarations.append((node.spelling, node.exception_specification_kind)) +declarations.append(node) for child in node.get_children(): declarations = find_function_declarations(child, declarations) return declarations @@ -33,4 +33,7 @@ def test_exception_specification_kind(self): ("square2", ExceptionSpecificationKind.BASIC_NOEXCEPT), ("square3", ExceptionSpecificationKind.COMPUTED_NOEXCEPT), ] -self.assertListEqual(declarations, expected) +from_cursor = [(node.spelling, node.exception_specification_kind) for node in declarations] +from_type = [(node.spelling, node.type.get_exception_specification_kind()) for node in declarations] +self.assertListEqual(from_cursor, expected) +self.assertListEqual(from_type, expected) ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
DeinAlptraum wrote: I'm planning to do one last split that contains all "logic" changes, after which this PR should contain only actual typing annotations and nothing else. Will open a PR for that other change one of these days https://github.com/llvm/llvm-project/pull/78114 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/78114 >From 04641f7ea15382df2d43fecc32bd1b699e0b2481 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 12 Jul 2024 15:08:06 +0100 Subject: [PATCH] [libclang/python] Add strict typing to clang Python bindings --- clang/bindings/python/clang/cindex.py | 654 ++ 1 file changed, 364 insertions(+), 290 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 2038ef6045c7d..32ed0920ff31e 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -71,8 +71,11 @@ from typing import ( Any, Callable, +cast as Tcast, Generic, +Iterator, Optional, +Sequence, Type as TType, TypeVar, TYPE_CHECKING, @@ -81,14 +84,19 @@ if TYPE_CHECKING: from ctypes import _Pointer +from io import TextIOWrapper from typing_extensions import Protocol, TypeAlias StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +InMemoryFile: TypeAlias = ( +"tuple[TUnion[str, os.PathLike[Any]], TUnion[str, TextIOWrapper]]" +) LibFunc: TypeAlias = TUnion[ "tuple[str, Optional[list[Any]]]", "tuple[str, Optional[list[Any]], Any]", "tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", ] +CObjP: TypeAlias = _Pointer[Any] TSeq = TypeVar("TSeq", covariant=True) @@ -147,7 +155,7 @@ def b(x: str | bytes) -> bytes: # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p: TType[_Pointer[Any]] = POINTER(c_void_p) +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -183,7 +191,7 @@ class TranslationUnitSaveError(Exception): # Indicates that the translation unit was somehow invalid. ERROR_INVALID_TU = 3 -def __init__(self, enumeration, message): +def __init__(self, enumeration: int, message: str): assert isinstance(enumeration, int) if enumeration < 1 or enumeration > 3: @@ -255,23 +263,25 @@ class SourceLocation(Structure): """ _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] -_data = None +_data: tuple[File | None, int, int, int] | None = None -def _get_instantiation(self): +def _get_instantiation(self) -> tuple[File | None, int, int, int]: if self._data is None: f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() conf.lib.clang_getInstantiationLocation( self, byref(f), byref(l), byref(c), byref(o) ) if f: -f = File(f) +file = File(f) else: -f = None -self._data = (f, int(l.value), int(c.value), int(o.value)) +file = None +self._data = (file, int(l.value), int(c.value), int(o.value)) return self._data @staticmethod -def from_position(tu, file, line, column): +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. @@ -279,7 +289,7 @@ def from_position(tu, file, line, column): return conf.lib.clang_getLocation(tu, file, line, column) # type: ignore [no-any-return] @staticmethod -def from_offset(tu, file, offset): +def from_offset(tu: TranslationUnit, file: File, offset: int) -> SourceLocation: """Retrieve a SourceLocation from a given character offset. tu -- TranslationUnit file belongs to @@ -289,37 +299,39 @@ def from_offset(tu, file, offset): return conf.lib.clang_getLocationForOffset(tu, file, offset) # type: ignore [no-any-return] @property -def file(self): +def file(self) -> File | None: """Get the file represented by this source location.""" return self._get_instantiation()[0] @property -def line(self): +def line(self) -> int: """Get the line represented by this source location.""" return self._get_instantiation()[1] @property -def column(self): +def column(self) -> int: """Get the column represented by this source location.""" return self._get_instantiation()[2] @property -def offset(self): +def offset(self) -> int: """Get the file offset represented by this source location.""" return self._get_instantiation()[3] @property -def is_in_system_header(self): +def is_in_system_header(self) -> bool: """Returns true if the given source location is in a system header.""" return conf.lib.clang_Location_isInSystemHeader(self) # type:
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
@@ -255,71 +263,75 @@ class SourceLocation(Structure): """ _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] -_data = None +_data: tuple[File | None, int, int, int] | None = None -def _get_instantiation(self): +def _get_instantiation(self) -> tuple[File | None, int, int, int]: if self._data is None: f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() conf.lib.clang_getInstantiationLocation( self, byref(f), byref(l), byref(c), byref(o) ) if f: -f = File(f) +file = File(f) else: -f = None -self._data = (f, int(l.value), int(c.value), int(o.value)) +file = None +self._data = (file, int(l.value), int(c.value), int(o.value)) return self._data @staticmethod -def from_position(tu, file, line, column): +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) +return conf.lib.clang_getLocation(tu, file, line, column) # type: ignore [no-any-return] @staticmethod -def from_offset(tu, file, offset): +def from_offset(tu: TranslationUnit, file: File, offset: int) -> SourceLocation: """Retrieve a SourceLocation from a given character offset. tu -- TranslationUnit file belongs to file -- File instance to obtain offset from offset -- Integer character offset within file """ -return conf.lib.clang_getLocationForOffset(tu, file, offset) +return conf.lib.clang_getLocationForOffset(tu, file, offset) # type: ignore [no-any-return] DeinAlptraum wrote: I've opened a PR separating these at #101310 https://github.com/llvm/llvm-project/pull/78114 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] type-ignore `Any` returns from library calls (PR #101310)
https://github.com/DeinAlptraum created https://github.com/llvm/llvm-project/pull/101310 On its own, this change leads to _more_ strict typing errors as the functions are mostly not annotated so far, so the `# type: ignore`s are reported as Unused. This is part of the work leading up to #78114 though, and one of the bigger parts factored out from it, so these will later lead to less strict typing errors as the functions are annotated with return types. >From 7bf4412849774be51c170584089bde8010d0814a Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Wed, 31 Jul 2024 10:27:09 +0100 Subject: [PATCH] [libclang/python] type-ignore `Any` returns from library calls --- clang/bindings/python/clang/cindex.py | 194 +- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 72509a8a54f28..60723e1aaeff7 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -276,7 +276,7 @@ def from_position(tu, file, line, column): 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) +return conf.lib.clang_getLocation(tu, file, line, column) # type: ignore [no-any-return] @staticmethod def from_offset(tu, file, offset): @@ -286,7 +286,7 @@ def from_offset(tu, file, offset): file -- File instance to obtain offset from offset -- Integer character offset within file """ -return conf.lib.clang_getLocationForOffset(tu, file, offset) +return conf.lib.clang_getLocationForOffset(tu, file, offset) # type: ignore [no-any-return] @property def file(self): @@ -311,10 +311,10 @@ def offset(self): @property def is_in_system_header(self): """Returns true if the given source location is in a system header.""" -return conf.lib.clang_Location_isInSystemHeader(self) +return conf.lib.clang_Location_isInSystemHeader(self) # type: ignore [no-any-return] def __eq__(self, other): -return conf.lib.clang_equalLocations(self, other) +return conf.lib.clang_equalLocations(self, other) # type: ignore [no-any-return] def __ne__(self, other): return not self.__eq__(other) @@ -347,7 +347,7 @@ class SourceRange(Structure): # object. @staticmethod def from_locations(start, end): -return conf.lib.clang_getRange(start, end) +return conf.lib.clang_getRange(start, end) # type: ignore [no-any-return] @property def start(self): @@ -355,7 +355,7 @@ def start(self): Return a SourceLocation representing the first character within a source range. """ -return conf.lib.clang_getRangeStart(self) +return conf.lib.clang_getRangeStart(self) # type: ignore [no-any-return] @property def end(self): @@ -363,10 +363,10 @@ def end(self): Return a SourceLocation representing the last character within a source range. """ -return conf.lib.clang_getRangeEnd(self) +return conf.lib.clang_getRangeEnd(self) # type: ignore [no-any-return] def __eq__(self, other): -return conf.lib.clang_equalRanges(self, other) +return conf.lib.clang_equalRanges(self, other) # type: ignore [no-any-return] def __ne__(self, other): return not self.__eq__(other) @@ -429,15 +429,15 @@ def __del__(self): @property def severity(self): -return conf.lib.clang_getDiagnosticSeverity(self) +return conf.lib.clang_getDiagnosticSeverity(self) # type: ignore [no-any-return] @property def location(self): -return conf.lib.clang_getDiagnosticLocation(self) +return conf.lib.clang_getDiagnosticLocation(self) # type: ignore [no-any-return] @property def spelling(self): -return conf.lib.clang_getDiagnosticSpelling(self) +return conf.lib.clang_getDiagnosticSpelling(self) # type: ignore [no-any-return] @property def ranges(self) -> NoSliceSequence[SourceRange]: @@ -451,7 +451,7 @@ def __len__(self) -> int: def __getitem__(self, key: int) -> SourceRange: if key >= len(self): raise IndexError -return conf.lib.clang_getDiagnosticRange(self.diag, key) +return conf.lib.clang_getDiagnosticRange(self.diag, key) # type: ignore [no-any-return] return RangeIterator(self) @@ -494,17 +494,17 @@ def __getitem__(self, key: int) -> Diagnostic: @property def category_number(self): """The category number for this diagnostic or 0 if unavailable.""" -return conf.lib.clang_getDiagnosticCategory(self) +return conf.lib.clang_getDiagnosticCategory(self) # type: ignore
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/78114 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Factor out unsaved files processing (PR #101308)
https://github.com/DeinAlptraum created https://github.com/llvm/llvm-project/pull/101308 Factor out the processing of unsaved files into its own function as suggested by @Endilll [here](https://github.com/llvm/llvm-project/pull/78114/files#r1697730196) >From 8780961064341bbbfd6cf97d9c6d5a4f7d012d49 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Wed, 31 Jul 2024 10:10:33 +0100 Subject: [PATCH] [libclang/python] Factor out unsaved files processing --- clang/bindings/python/clang/cindex.py | 47 ++- 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 72509a8a54f28..f669c279aab6d 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -3011,6 +3011,20 @@ class TranslationUnit(ClangObject): # into the set of code completions returned from this translation unit. PARSE_INCLUDE_BRIEF_COMMENTS_IN_CODE_COMPLETION = 128 +@staticmethod +def process_unsaved_files(unsaved_files) -> Array[_CXUnsavedFile] | None: +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 = contents.read() +binary_contents = b(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, args=None, unsaved_files=None, options=0, index=None @@ -3067,16 +3081,7 @@ def from_source( if len(args) > 0: args_array = (c_char_p * len(args))(*[b(x) for x in args]) -unsaved_array = None -if len(unsaved_files) > 0: -unsaved_array = (_CXUnsavedFile * len(unsaved_files))() -for i, (name, contents) in enumerate(unsaved_files): -if hasattr(contents, "read"): -contents = contents.read() -contents = b(contents) -unsaved_array[i].name = b(os.fspath(name)) -unsaved_array[i].contents = contents -unsaved_array[i].length = len(contents) +unsaved_array = cls.process_unsaved_files(unsaved_files) ptr = conf.lib.clang_parseTranslationUnit( index, @@ -3257,16 +3262,7 @@ def reparse(self, unsaved_files=None, options=0): if unsaved_files is None: unsaved_files = [] -unsaved_files_array = 0 -if len(unsaved_files): -unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() -for i, (name, contents) in enumerate(unsaved_files): -if hasattr(contents, "read"): -contents = contents.read() -contents = b(contents) -unsaved_files_array[i].name = b(os.fspath(name)) -unsaved_files_array[i].contents = contents -unsaved_files_array[i].length = len(contents) +unsaved_files_array = self.process_unsaved_files(unsaved_files) ptr = conf.lib.clang_reparseTranslationUnit( self, len(unsaved_files), unsaved_files_array, options ) @@ -3329,16 +3325,7 @@ def codeComplete( if unsaved_files is None: unsaved_files = [] -unsaved_files_array = 0 -if len(unsaved_files): -unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() -for i, (name, contents) in enumerate(unsaved_files): -if hasattr(contents, "read"): -contents = contents.read() -contents = b(contents) -unsaved_files_array[i].name = b(os.fspath(name)) -unsaved_files_array[i].contents = contents -unsaved_files_array[i].length = len(contents) +unsaved_files_array = self.process_unsaved_files(unsaved_files) ptr = conf.lib.clang_codeCompleteAt( self, os.fspath(path), ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
@@ -3257,21 +3323,21 @@ def reparse(self, unsaved_files=None, options=0): if unsaved_files is None: unsaved_files = [] -unsaved_files_array = 0 +unsaved_files_array: int | Array[_CXUnsavedFile] = 0 if len(unsaved_files): unsaved_files_array = (_CXUnsavedFile * len(unsaved_files))() for i, (name, contents) in enumerate(unsaved_files): if hasattr(contents, "read"): contents = contents.read() -contents = b(contents) +binary_contents = b(contents) DeinAlptraum wrote: Assigning an incompatible type to a variable of a different type is a type error. Changing `binary_contents` back to `contents` produces a type error like this on that line: `clang/cindex.py:3332: error: Incompatible types in assignment (expression has type "bytes", variable has type "str | TextIOWrapper")` Good point with the repetition though, I'll make separate PRs for these to collect this into a common function. https://github.com/llvm/llvm-project/pull/78114 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
DeinAlptraum wrote: I rebased this on the other changes, so there's mostly relatively simple annotations left now. Still a few hundred of them though. @Endilll do you want me to split this further, or is this fine? https://github.com/llvm/llvm-project/pull/78114 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/78114 >From 6f93e30dc7fdeaa18ae181c15aaed257c902d4c8 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 12 Jul 2024 15:08:06 +0100 Subject: [PATCH] [libclang/python] Add strict typing to clang Python bindings --- clang/bindings/python/clang/cindex.py | 868 ++ 1 file changed, 471 insertions(+), 397 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 72509a8a54f28..f876b07bf6ae5 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -71,8 +71,11 @@ from typing import ( Any, Callable, +cast as Tcast, Generic, +Iterator, Optional, +Sequence, Type as TType, TypeVar, TYPE_CHECKING, @@ -81,14 +84,19 @@ if TYPE_CHECKING: from ctypes import _Pointer +from io import TextIOWrapper from typing_extensions import Protocol, TypeAlias StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +InMemoryFile: TypeAlias = ( +"tuple[TUnion[str, os.PathLike[Any]], TUnion[str, TextIOWrapper]]" +) LibFunc: TypeAlias = TUnion[ "tuple[str, Optional[list[Any]]]", "tuple[str, Optional[list[Any]], Any]", "tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", ] +CObjP: TypeAlias = _Pointer[Any] TSeq = TypeVar("TSeq", covariant=True) @@ -147,7 +155,7 @@ def b(x: str | bytes) -> bytes: # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p: TType[_Pointer[Any]] = POINTER(c_void_p) +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -183,7 +191,7 @@ class TranslationUnitSaveError(Exception): # Indicates that the translation unit was somehow invalid. ERROR_INVALID_TU = 3 -def __init__(self, enumeration, message): +def __init__(self, enumeration: int, message: str): assert isinstance(enumeration, int) if enumeration < 1 or enumeration > 3: @@ -255,71 +263,75 @@ class SourceLocation(Structure): """ _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] -_data = None +_data: tuple[File | None, int, int, int] | None = None -def _get_instantiation(self): +def _get_instantiation(self) -> tuple[File | None, int, int, int]: if self._data is None: f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() conf.lib.clang_getInstantiationLocation( self, byref(f), byref(l), byref(c), byref(o) ) if f: -f = File(f) +file = File(f) else: -f = None -self._data = (f, int(l.value), int(c.value), int(o.value)) +file = None +self._data = (file, int(l.value), int(c.value), int(o.value)) return self._data @staticmethod -def from_position(tu, file, line, column): +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) +return conf.lib.clang_getLocation(tu, file, line, column) # type: ignore [no-any-return] @staticmethod -def from_offset(tu, file, offset): +def from_offset(tu: TranslationUnit, file: File, offset: int) -> SourceLocation: """Retrieve a SourceLocation from a given character offset. tu -- TranslationUnit file belongs to file -- File instance to obtain offset from offset -- Integer character offset within file """ -return conf.lib.clang_getLocationForOffset(tu, file, offset) +return conf.lib.clang_getLocationForOffset(tu, file, offset) # type: ignore [no-any-return] @property -def file(self): +def file(self) -> File | None: """Get the file represented by this source location.""" return self._get_instantiation()[0] @property -def line(self): +def line(self) -> int: """Get the line represented by this source location.""" return self._get_instantiation()[1] @property -def column(self): +def column(self) -> int: """Get the column represented by this source location.""" return self._get_instantiation()[2] @property -def offset(self): +def offset(self) -> int: """Get the file offset represented by this source location.""" return self._get_instantiation()[3] @property -def is_in_system_header(self): +def is_in_system_header(self) -> bool:
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/78114 >From 883bda054bb6a70edd2f2d738ffccff3a0b47a35 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 12 Jul 2024 15:08:06 +0100 Subject: [PATCH] [libclang/python] Add strict typing to clang Python bindings --- clang/bindings/python/clang/cindex.py | 869 ++ 1 file changed, 472 insertions(+), 397 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 72509a8a54f28..bc680fdc2c24c 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -71,24 +71,33 @@ from typing import ( Any, Callable, +cast as Tcast, Generic, +Iterator, Optional, +Sequence, Type as TType, TypeVar, TYPE_CHECKING, Union as TUnion, ) +from typing_extensions import Protocol, TypeAlias if TYPE_CHECKING: from ctypes import _Pointer +from io import TextIOWrapper from typing_extensions import Protocol, TypeAlias StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +InMemoryFile: TypeAlias = ( +"tuple[TUnion[str, os.PathLike[Any]], TUnion[str, TextIOWrapper]]" +) LibFunc: TypeAlias = TUnion[ "tuple[str, Optional[list[Any]]]", "tuple[str, Optional[list[Any]], Any]", "tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", ] +CObjP: TypeAlias = _Pointer[Any] TSeq = TypeVar("TSeq", covariant=True) @@ -147,7 +156,7 @@ def b(x: str | bytes) -> bytes: # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p: TType[_Pointer[Any]] = POINTER(c_void_p) +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -183,7 +192,7 @@ class TranslationUnitSaveError(Exception): # Indicates that the translation unit was somehow invalid. ERROR_INVALID_TU = 3 -def __init__(self, enumeration, message): +def __init__(self, enumeration: int, message: str): assert isinstance(enumeration, int) if enumeration < 1 or enumeration > 3: @@ -255,71 +264,75 @@ class SourceLocation(Structure): """ _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] -_data = None +_data: tuple[File | None, int, int, int] | None = None -def _get_instantiation(self): +def _get_instantiation(self) -> tuple[File | None, int, int, int]: if self._data is None: f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() conf.lib.clang_getInstantiationLocation( self, byref(f), byref(l), byref(c), byref(o) ) if f: -f = File(f) +file = File(f) else: -f = None -self._data = (f, int(l.value), int(c.value), int(o.value)) +file = None +self._data = (file, int(l.value), int(c.value), int(o.value)) return self._data @staticmethod -def from_position(tu, file, line, column): +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) +return conf.lib.clang_getLocation(tu, file, line, column) # type: ignore [no-any-return] @staticmethod -def from_offset(tu, file, offset): +def from_offset(tu: TranslationUnit, file: File, offset: int) -> SourceLocation: """Retrieve a SourceLocation from a given character offset. tu -- TranslationUnit file belongs to file -- File instance to obtain offset from offset -- Integer character offset within file """ -return conf.lib.clang_getLocationForOffset(tu, file, offset) +return conf.lib.clang_getLocationForOffset(tu, file, offset) # type: ignore [no-any-return] @property -def file(self): +def file(self) -> File | None: """Get the file represented by this source location.""" return self._get_instantiation()[0] @property -def line(self): +def line(self) -> int: """Get the line represented by this source location.""" return self._get_instantiation()[1] @property -def column(self): +def column(self) -> int: """Get the column represented by this source location.""" return self._get_instantiation()[2] @property -def offset(self): +def offset(self) -> int: """Get the file offset represented by this source location.""" return self._get_instantiation()[3] @property -def
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/78114 >From d16056310b86aaa98011cb8551c38365f0e7748f Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 12 Jul 2024 15:08:06 +0100 Subject: [PATCH] [libclang/python] Add strict typing to clang Python bindings --- clang/bindings/python/clang/cindex.py | 868 ++ 1 file changed, 471 insertions(+), 397 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 72509a8a54f28..a7dd797dc4498 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -71,24 +71,33 @@ from typing import ( Any, Callable, +cast as Tcast, Generic, +Iterator, Optional, +Sequence, Type as TType, TypeVar, TYPE_CHECKING, Union as TUnion, ) +from typing_extensions import Protocol, TypeAlias if TYPE_CHECKING: from ctypes import _Pointer +from io import TextIOWrapper from typing_extensions import Protocol, TypeAlias StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +InMemoryFile: TypeAlias = ( +"tuple[TUnion[str, os.PathLike[Any]], TUnion[str, TextIOWrapper]]" +) LibFunc: TypeAlias = TUnion[ "tuple[str, Optional[list[Any]]]", "tuple[str, Optional[list[Any]], Any]", "tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", ] +CObjP: TypeAlias = _Pointer[Any] TSeq = TypeVar("TSeq", covariant=True) @@ -147,7 +156,7 @@ def b(x: str | bytes) -> bytes: # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p: TType[_Pointer[Any]] = POINTER(c_void_p) +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -183,7 +192,7 @@ class TranslationUnitSaveError(Exception): # Indicates that the translation unit was somehow invalid. ERROR_INVALID_TU = 3 -def __init__(self, enumeration, message): +def __init__(self, enumeration: int, message: str): assert isinstance(enumeration, int) if enumeration < 1 or enumeration > 3: @@ -255,71 +264,75 @@ class SourceLocation(Structure): """ _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] -_data = None +_data: tuple[File | None, int, int, int] | None = None -def _get_instantiation(self): +def _get_instantiation(self) -> tuple[File | None, int, int, int]: if self._data is None: f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() conf.lib.clang_getInstantiationLocation( self, byref(f), byref(l), byref(c), byref(o) ) if f: -f = File(f) +file = File(f) else: -f = None -self._data = (f, int(l.value), int(c.value), int(o.value)) +file = None +self._data = (file, int(l.value), int(c.value), int(o.value)) return self._data @staticmethod -def from_position(tu, file, line, column): +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) +return conf.lib.clang_getLocation(tu, file, line, column) # type: ignore [no-any-return] @staticmethod -def from_offset(tu, file, offset): +def from_offset(tu: TranslationUnit, file: File, offset: int) -> SourceLocation: """Retrieve a SourceLocation from a given character offset. tu -- TranslationUnit file belongs to file -- File instance to obtain offset from offset -- Integer character offset within file """ -return conf.lib.clang_getLocationForOffset(tu, file, offset) +return conf.lib.clang_getLocationForOffset(tu, file, offset) # type: ignore [no-any-return] @property -def file(self): +def file(self) -> File | None: """Get the file represented by this source location.""" return self._get_instantiation()[0] @property -def line(self): +def line(self) -> int: """Get the line represented by this source location.""" return self._get_instantiation()[1] @property -def column(self): +def column(self) -> int: """Get the column represented by this source location.""" return self._get_instantiation()[2] @property -def offset(self): +def offset(self) -> int: """Get the file offset represented by this source location.""" return self._get_instantiation()[3] @property -def
[clang] [libclang/python] Add strict typing to clang Python bindings (#76664) (PR #78114)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/78114 >From 717c43a4c027681fa957ed35a365400c129ff0b1 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 12 Jul 2024 15:08:06 +0100 Subject: [PATCH] [libclang/python] Add strict typing to clang Python bindings --- clang/bindings/python/clang/cindex.py | 865 ++ 1 file changed, 467 insertions(+), 398 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 72509a8a54f28..fb44082242325 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -71,24 +71,33 @@ from typing import ( Any, Callable, +cast as Tcast, Generic, +Iterator, Optional, +Sequence, Type as TType, TypeVar, TYPE_CHECKING, Union as TUnion, ) +from typing_extensions import Protocol, TypeAlias if TYPE_CHECKING: from ctypes import _Pointer +from io import TextIOWrapper from typing_extensions import Protocol, TypeAlias StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +InMemoryFile: TypeAlias = ( +"tuple[TUnion[str, os.PathLike[Any]], TUnion[str, TextIOWrapper]]" +) LibFunc: TypeAlias = TUnion[ "tuple[str, Optional[list[Any]]]", "tuple[str, Optional[list[Any]], Any]", "tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", ] +CObjP: TypeAlias = _Pointer[Any] TSeq = TypeVar("TSeq", covariant=True) @@ -136,7 +145,6 @@ def from_param(cls, param: str | bytes | None) -> c_interop_string: def to_python_string(x: c_interop_string, *args: Any) -> str | None: return x.value - def b(x: str | bytes) -> bytes: if isinstance(x, bytes): return x @@ -147,7 +155,7 @@ def b(x: str | bytes) -> bytes: # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p: TType[_Pointer[Any]] = POINTER(c_void_p) +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -183,7 +191,7 @@ class TranslationUnitSaveError(Exception): # Indicates that the translation unit was somehow invalid. ERROR_INVALID_TU = 3 -def __init__(self, enumeration, message): +def __init__(self, enumeration: int, message: str): assert isinstance(enumeration, int) if enumeration < 1 or enumeration > 3: @@ -255,71 +263,75 @@ class SourceLocation(Structure): """ _fields_ = [("ptr_data", c_void_p * 2), ("int_data", c_uint)] -_data = None +_data: tuple[File | None, int, int, int] | None = None -def _get_instantiation(self): +def _get_instantiation(self) -> tuple[File | None, int, int, int]: if self._data is None: f, l, c, o = c_object_p(), c_uint(), c_uint(), c_uint() conf.lib.clang_getInstantiationLocation( self, byref(f), byref(l), byref(c), byref(o) ) if f: -f = File(f) +file = File(f) else: -f = None -self._data = (f, int(l.value), int(c.value), int(o.value)) +file = None +self._data = (file, int(l.value), int(c.value), int(o.value)) return self._data @staticmethod -def from_position(tu, file, line, column): +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) +return conf.lib.clang_getLocation(tu, file, line, column) # type: ignore [no-any-return] @staticmethod -def from_offset(tu, file, offset): +def from_offset(tu: TranslationUnit, file: File, offset: int) -> SourceLocation: """Retrieve a SourceLocation from a given character offset. tu -- TranslationUnit file belongs to file -- File instance to obtain offset from offset -- Integer character offset within file """ -return conf.lib.clang_getLocationForOffset(tu, file, offset) +return conf.lib.clang_getLocationForOffset(tu, file, offset) # type: ignore [no-any-return] @property -def file(self): +def file(self) -> File | None: """Get the file represented by this source location.""" return self._get_instantiation()[0] @property -def line(self): +def line(self) -> int: """Get the line represented by this source location.""" return self._get_instantiation()[1] @property -def column(self): +def column(self) -> int: """Get the column represented by this source location."""
[clang] [Clang][Sema] Remove duplicate check in if-condition (PR #101070)
DeinAlptraum wrote: @zyn0217 I hope you're the right person to ask for a review here? https://github.com/llvm/llvm-project/pull/101070 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [Clang][Sema] Remove duplicate check in if-condition (PR #101070)
https://github.com/DeinAlptraum created https://github.com/llvm/llvm-project/pull/101070 Resolves #101041 >From 521082f25bc42104fd436a412b2de2edb60b7b0e Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Mon, 29 Jul 2024 20:24:15 +0100 Subject: [PATCH] [Clang][Sema] Remove duplicate check in if-condition --- clang/lib/Sema/SemaOpenMP.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp index 9c80b3eec914c..1d378e6b830ae 100644 --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -23087,8 +23087,7 @@ OMPClause *SemaOpenMP::ActOnOpenMPDoacrossClause( if (DSAStack->getCurrentDirective() == OMPD_ordered && DepType != OMPC_DOACROSS_source && DepType != OMPC_DOACROSS_sink && DepType != OMPC_DOACROSS_sink_omp_cur_iteration && - DepType != OMPC_DOACROSS_source_omp_cur_iteration && - DepType != OMPC_DOACROSS_source) { + DepType != OMPC_DOACROSS_source_omp_cur_iteration) { Diag(DepLoc, diag::err_omp_unexpected_clause_value) << "'source' or 'sink'" << getOpenMPClauseName(OMPC_doacross); return nullptr; ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Export all enums (PR #100941)
DeinAlptraum wrote: This may be my fault, I don't remember if I pulled main before branching for this PR, especially seeing how my last PR also had unrelated test failures https://github.com/llvm/llvm-project/pull/100941 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/98745 >From 00631fc559197d2bc6bfa9e8ccdae47f33926a37 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 13 Jul 2024 14:12:34 +0100 Subject: [PATCH 1/2] [libclang/python] Fix some type errors, add type annotations --- clang/bindings/python/clang/cindex.py | 195 +++--- .../tests/cindex/test_code_completion.py | 22 +- .../python/tests/cindex/test_comment.py | 4 +- 3 files changed, 130 insertions(+), 91 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index be024da5e005c..6c70588c28ffa 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -43,7 +43,7 @@ Most object information is exposed using properties, when the underlying API call is efficient. """ -from __future__ import absolute_import, division, print_function +from __future__ import annotations # TODO # @@ -64,48 +64,81 @@ from ctypes import * -import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) + +if TYPE_CHECKING: +from ctypes import _Pointer +from typing_extensions import Protocol, TypeAlias + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: +... + +def __getitem__(self, key: int) -> TSeq: +... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" @property -def value(self): -if super(c_char_p, self).value is None: +def value(self) -> str | None: # type: ignore [override] +val = super(c_char_p, self).value +if val is None: return None -return super(c_char_p, self).value.decode("utf8") +return val.decode("utf8") @classmethod -def from_param(cls, param): +def from_param(cls, param: str | bytes | None) -> c_interop_string: if isinstance(param, str): return cls(param) if isinstance(param, bytes): return cls(param) if param is None: # Support passing null to C functions expecting char arrays -return None +return cls(param) raise TypeError( "Cannot convert '{}' to '{}'".format(type(param).__name__, cls.__name__) ) @staticmethod -def to_python_string(x, *args): +def to_python_string(x: c_interop_string, *args: Any) -> str | None: return x.value -def b(x): +def b(x: str | bytes) -> bytes: if isinstance(x, bytes): return x return x.encode("utf8") @@ -115,9 +148,7 @@ def b(x): # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p = POINTER(c_void_p) - -callbacks = {} +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -169,8 +200,11 @@ def __init__(self, enumeration, message): ### Structures and Utility Classes ### +TInstance = TypeVar("TInstance") +TResult = TypeVar("TResult") + -class CachedProperty: +class CachedProperty(Generic[TInstance, TResult]): """Decorator that lazy-loads the value of a property. The first time the property is accessed, the original property function is @@ -178,16 +212,20 @@ class CachedProperty: property, replacing the original method. """ -def __init__(self, wrapped): +def __init__(self, wrapped: Callable[[TInstance], TResult]): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: pass -def __get__(self, instance, instance_type=None): +def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: if instance is None: -return self +property_name = self.wrapped.__name__ +class_name = instance_type.__name__ +raise TypeError( +f"'{property_name}' is not a
[clang] [libclang/python] Export all enums (PR #100941)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/100941 >From 4b1322b8add0a1189f0f1cbf5583841f3a591f0c Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sun, 28 Jul 2024 18:30:35 +0100 Subject: [PATCH] [libclang/python] Export all enums --- clang/bindings/python/clang/cindex.py | 5 + 1 file changed, 5 insertions(+) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index be024da5e005c..d9009a8666338 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -4077,6 +4077,7 @@ def function_exists(self, name): conf = Config() __all__ = [ +"AccessSpecifier", "AvailabilityKind", "BinaryOperator", "Config", @@ -4087,12 +4088,16 @@ def function_exists(self, name): "CursorKind", "Cursor", "Diagnostic", +"ExceptionSpecificationKind", "File", "FixIt", "Index", "LinkageKind", +"RefQualifierKind", "SourceLocation", "SourceRange", +"StorageClass", +"TemplateArgumentKind", "TLSKind", "TokenKind", "Token", ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Export all enums (PR #100941)
DeinAlptraum wrote: Could you also merge please? (or are you waiting for something else?) https://github.com/llvm/llvm-project/pull/100941 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Export all enums (PR #100941)
DeinAlptraum wrote: @Endilll can I ask you for a review again? https://github.com/llvm/llvm-project/pull/100941 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Export all enums (PR #100941)
https://github.com/DeinAlptraum created https://github.com/llvm/llvm-project/pull/100941 This resolves #48212 and also adds the remaining unexposed Enums >From c4007832c8ed7cdb56aceebcf61b24ecb75f2aa4 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sun, 28 Jul 2024 18:30:35 +0100 Subject: [PATCH] [libclang/python] Export all enums --- clang/bindings/python/clang/cindex.py | 5 + 1 file changed, 5 insertions(+) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index be024da5e005c..d9009a8666338 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -4077,6 +4077,7 @@ def function_exists(self, name): conf = Config() __all__ = [ +"AccessSpecifier", "AvailabilityKind", "BinaryOperator", "Config", @@ -4087,12 +4088,16 @@ def function_exists(self, name): "CursorKind", "Cursor", "Diagnostic", +"ExceptionSpecificationKind", "File", "FixIt", "Index", "LinkageKind", +"RefQualifierKind", "SourceLocation", "SourceRange", +"StorageClass", +"TemplateArgumentKind", "TLSKind", "TokenKind", "Token", ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
DeinAlptraum wrote: Since the release branching is done, I've rebased on main to fix the release notes https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/98745 >From c64b124ccc22cd9f92b0a55f60ec92d7101d0048 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 13 Jul 2024 14:12:34 +0100 Subject: [PATCH 1/2] [libclang/python] Fix some type errors, add type annotations --- clang/bindings/python/clang/cindex.py | 195 +++--- .../tests/cindex/test_code_completion.py | 22 +- .../python/tests/cindex/test_comment.py | 4 +- 3 files changed, 130 insertions(+), 91 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index be024da5e005c..6c70588c28ffa 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -43,7 +43,7 @@ Most object information is exposed using properties, when the underlying API call is efficient. """ -from __future__ import absolute_import, division, print_function +from __future__ import annotations # TODO # @@ -64,48 +64,81 @@ from ctypes import * -import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) + +if TYPE_CHECKING: +from ctypes import _Pointer +from typing_extensions import Protocol, TypeAlias + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: +... + +def __getitem__(self, key: int) -> TSeq: +... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" @property -def value(self): -if super(c_char_p, self).value is None: +def value(self) -> str | None: # type: ignore [override] +val = super(c_char_p, self).value +if val is None: return None -return super(c_char_p, self).value.decode("utf8") +return val.decode("utf8") @classmethod -def from_param(cls, param): +def from_param(cls, param: str | bytes | None) -> c_interop_string: if isinstance(param, str): return cls(param) if isinstance(param, bytes): return cls(param) if param is None: # Support passing null to C functions expecting char arrays -return None +return cls(param) raise TypeError( "Cannot convert '{}' to '{}'".format(type(param).__name__, cls.__name__) ) @staticmethod -def to_python_string(x, *args): +def to_python_string(x: c_interop_string, *args: Any) -> str | None: return x.value -def b(x): +def b(x: str | bytes) -> bytes: if isinstance(x, bytes): return x return x.encode("utf8") @@ -115,9 +148,7 @@ def b(x): # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p = POINTER(c_void_p) - -callbacks = {} +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -169,8 +200,11 @@ def __init__(self, enumeration, message): ### Structures and Utility Classes ### +TInstance = TypeVar("TInstance") +TResult = TypeVar("TResult") + -class CachedProperty: +class CachedProperty(Generic[TInstance, TResult]): """Decorator that lazy-loads the value of a property. The first time the property is accessed, the original property function is @@ -178,16 +212,20 @@ class CachedProperty: property, replacing the original method. """ -def __init__(self, wrapped): +def __init__(self, wrapped: Callable[[TInstance], TResult]): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: pass -def __get__(self, instance, instance_type=None): +def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: if instance is None: -return self +property_name = self.wrapped.__name__ +class_name = instance_type.__name__ +raise TypeError( +f"'{property_name}' is not a
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
DeinAlptraum wrote: Got it, thank you! https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
DeinAlptraum wrote: @nikic could you explain why? https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
DeinAlptraum wrote: I added breaking change notes where I thought them appropriate, could you merge if you are happy with this? Regarding the relase: I believe I've taken care not to break anything (besides things noted in breaking change notes) so hopefully not much need for that. If any unexpected breakages do come to my attention, I will try to get that in though. This PR is also probably the last step before #78114, so that one can hopefully still be picked onto `releases/19.x` so that we have the entire "strict typing" thing in the release. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -66,46 +66,77 @@ import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) +from typing_extensions import Protocol, TypeAlias + +if TYPE_CHECKING: +from ctypes import _Pointer + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: ... +def __getitem__(self, key: int) -> TSeq: ... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" DeinAlptraum wrote: I just checked the `c_interop_string` usage again, and this type is apparently only used for converting strings that are fed _into_ the C interface functions, never returned. So no breaking change after all. Does make me wonder why it has a `__str__` though. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -200,13 +236,16 @@ class _CXString(Structure): _fields_ = [("spelling", c_char_p), ("free", c_int)] -def __del__(self): +def __del__(self) -> None: conf.lib.clang_disposeString(self) @staticmethod -def from_result(res, fn=None, args=None): +def from_result(res: _CXString, fn: Any = None, args: Any = None) -> str: assert isinstance(res, _CXString) -return conf.lib.clang_getCString(res) +pystr: str | None = conf.lib.clang_getCString(res) +if pystr is None: +return "" +return pystr DeinAlptraum wrote: The interface changes, isn't that a breaking change in every case? I.e. if you were checking for empty results before by doing `is None`, that doesn't work anymore. You would now have to change that to `== ""`. I've added a breaking change note for this, but happy to remove this again if I'm missing something here. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/98745 >From c0216b8a50f7ccc6ee24db69802055c3942a183e Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 13 Jul 2024 14:12:34 +0100 Subject: [PATCH 1/2] [libclang/python] Fix some type errors, add type annotations --- clang/bindings/python/clang/cindex.py | 195 +++--- .../tests/cindex/test_code_completion.py | 22 +- .../python/tests/cindex/test_comment.py | 4 +- 3 files changed, 130 insertions(+), 91 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 1d3ab89190407..e8b7c006b6679 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -43,7 +43,7 @@ Most object information is exposed using properties, when the underlying API call is efficient. """ -from __future__ import absolute_import, division, print_function +from __future__ import annotations # TODO # @@ -64,48 +64,81 @@ from ctypes import * -import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) + +if TYPE_CHECKING: +from ctypes import _Pointer +from typing_extensions import Protocol, TypeAlias + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: +... + +def __getitem__(self, key: int) -> TSeq: +... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" @property -def value(self): -if super(c_char_p, self).value is None: +def value(self) -> str | None: # type: ignore [override] +val = super(c_char_p, self).value +if val is None: return None -return super(c_char_p, self).value.decode("utf8") +return val.decode("utf8") @classmethod -def from_param(cls, param): +def from_param(cls, param: str | bytes | None) -> c_interop_string: if isinstance(param, str): return cls(param) if isinstance(param, bytes): return cls(param) if param is None: # Support passing null to C functions expecting char arrays -return None +return cls(param) raise TypeError( "Cannot convert '{}' to '{}'".format(type(param).__name__, cls.__name__) ) @staticmethod -def to_python_string(x, *args): +def to_python_string(x: c_interop_string, *args: Any) -> str | None: return x.value -def b(x): +def b(x: str | bytes) -> bytes: if isinstance(x, bytes): return x return x.encode("utf8") @@ -115,9 +148,7 @@ def b(x): # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p = POINTER(c_void_p) - -callbacks = {} +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -169,8 +200,11 @@ def __init__(self, enumeration, message): ### Structures and Utility Classes ### +TInstance = TypeVar("TInstance") +TResult = TypeVar("TResult") + -class CachedProperty: +class CachedProperty(Generic[TInstance, TResult]): """Decorator that lazy-loads the value of a property. The first time the property is accessed, the original property function is @@ -178,16 +212,20 @@ class CachedProperty: property, replacing the original method. """ -def __init__(self, wrapped): +def __init__(self, wrapped: Callable[[TInstance], TResult]): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: pass -def __get__(self, instance, instance_type=None): +def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: if instance is None: -return self +property_name = self.wrapped.__name__ +class_name = instance_type.__name__ +raise TypeError( +f"'{property_name}' is not a
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
DeinAlptraum wrote: \*Ping* @Endilll do you have time to take a look at this? https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
DeinAlptraum wrote: @Endilll I am once again asking for your support This PR collects several of the changes towards #78114 that I assume might need some discussion. I tried to leave PR comments with explanations in each of those places. @boomanaiden154 @linux4life798 review from you would also be appreciated! https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/98745 >From c0216b8a50f7ccc6ee24db69802055c3942a183e Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 13 Jul 2024 14:12:34 +0100 Subject: [PATCH] [libclang/python] Fix some type errors, add type annotations --- clang/bindings/python/clang/cindex.py | 195 +++--- .../tests/cindex/test_code_completion.py | 22 +- .../python/tests/cindex/test_comment.py | 4 +- 3 files changed, 130 insertions(+), 91 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 1d3ab89190407..e8b7c006b6679 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -43,7 +43,7 @@ Most object information is exposed using properties, when the underlying API call is efficient. """ -from __future__ import absolute_import, division, print_function +from __future__ import annotations # TODO # @@ -64,48 +64,81 @@ from ctypes import * -import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) + +if TYPE_CHECKING: +from ctypes import _Pointer +from typing_extensions import Protocol, TypeAlias + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: +... + +def __getitem__(self, key: int) -> TSeq: +... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" @property -def value(self): -if super(c_char_p, self).value is None: +def value(self) -> str | None: # type: ignore [override] +val = super(c_char_p, self).value +if val is None: return None -return super(c_char_p, self).value.decode("utf8") +return val.decode("utf8") @classmethod -def from_param(cls, param): +def from_param(cls, param: str | bytes | None) -> c_interop_string: if isinstance(param, str): return cls(param) if isinstance(param, bytes): return cls(param) if param is None: # Support passing null to C functions expecting char arrays -return None +return cls(param) raise TypeError( "Cannot convert '{}' to '{}'".format(type(param).__name__, cls.__name__) ) @staticmethod -def to_python_string(x, *args): +def to_python_string(x: c_interop_string, *args: Any) -> str | None: return x.value -def b(x): +def b(x: str | bytes) -> bytes: if isinstance(x, bytes): return x return x.encode("utf8") @@ -115,9 +148,7 @@ def b(x): # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p = POINTER(c_void_p) - -callbacks = {} +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -169,8 +200,11 @@ def __init__(self, enumeration, message): ### Structures and Utility Classes ### +TInstance = TypeVar("TInstance") +TResult = TypeVar("TResult") + -class CachedProperty: +class CachedProperty(Generic[TInstance, TResult]): """Decorator that lazy-loads the value of a property. The first time the property is accessed, the original property function is @@ -178,16 +212,20 @@ class CachedProperty: property, replacing the original method. """ -def __init__(self, wrapped): +def __init__(self, wrapped: Callable[[TInstance], TResult]): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: pass -def __get__(self, instance, instance_type=None): +def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: if instance is None: -return self +property_name = self.wrapped.__name__ +class_name = instance_type.__name__ +raise TypeError( +f"'{property_name}' is not a static
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -64,48 +64,81 @@ from ctypes import * -import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) +from typing_extensions import Protocol, TypeAlias + +if TYPE_CHECKING: +from ctypes import _Pointer + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: +... + +def __getitem__(self, key: int) -> TSeq: +... DeinAlptraum wrote: This defines the proper interface for all the classes with "Iterator" in their name. None of them are actually iterators in terms of the Python protocol `Iterator`, as they are expected to support `__len__` and __getitem__`. They're the closest to the `Sequence` protocol, but without the support for slicing. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/98745 >From c9e439d18ccc0749153e211d796ae202554cb229 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 13 Jul 2024 14:12:34 +0100 Subject: [PATCH] [libclang/python] Fix some type errors, add type annotations --- clang/bindings/python/clang/cindex.py | 195 +++--- .../tests/cindex/test_code_completion.py | 22 +- .../python/tests/cindex/test_comment.py | 4 +- 3 files changed, 130 insertions(+), 91 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 1d3ab89190407..e71cdcdddcc48 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -43,7 +43,7 @@ Most object information is exposed using properties, when the underlying API call is efficient. """ -from __future__ import absolute_import, division, print_function +from __future__ import annotations # TODO # @@ -64,48 +64,81 @@ from ctypes import * -import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) +from typing_extensions import Protocol, TypeAlias + +if TYPE_CHECKING: +from ctypes import _Pointer + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: +... + +def __getitem__(self, key: int) -> TSeq: +... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" @property -def value(self): -if super(c_char_p, self).value is None: +def value(self) -> str | None: # type: ignore [override] +val = super(c_char_p, self).value +if val is None: return None -return super(c_char_p, self).value.decode("utf8") +return val.decode("utf8") @classmethod -def from_param(cls, param): +def from_param(cls, param: str | bytes | None) -> c_interop_string: if isinstance(param, str): return cls(param) if isinstance(param, bytes): return cls(param) if param is None: # Support passing null to C functions expecting char arrays -return None +return cls(param) raise TypeError( "Cannot convert '{}' to '{}'".format(type(param).__name__, cls.__name__) ) @staticmethod -def to_python_string(x, *args): +def to_python_string(x: c_interop_string, *args: Any) -> str | None: return x.value -def b(x): +def b(x: str | bytes) -> bytes: if isinstance(x, bytes): return x return x.encode("utf8") @@ -115,9 +148,7 @@ def b(x): # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p = POINTER(c_void_p) - -callbacks = {} +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -169,8 +200,11 @@ def __init__(self, enumeration, message): ### Structures and Utility Classes ### +TInstance = TypeVar("TInstance") +TResult = TypeVar("TResult") + -class CachedProperty: +class CachedProperty(Generic[TInstance, TResult]): """Decorator that lazy-loads the value of a property. The first time the property is accessed, the original property function is @@ -178,16 +212,20 @@ class CachedProperty: property, replacing the original method. """ -def __init__(self, wrapped): +def __init__(self, wrapped: Callable[[TInstance], TResult]): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: pass -def __get__(self, instance, instance_type=None): +def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: if instance is None: -return self +property_name = self.wrapped.__name__ +class_name = instance_type.__name__ +raise TypeError( +f"'{property_name}' is not a static
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -2318,25 +2356,25 @@ def kind(self): """Return the kind of this type.""" return TypeKind.from_id(self._kind_id) -def argument_types(self): +def argument_types(self) -> NoSliceSequence[Type]: """Retrieve a container for the non-variadic arguments for this type. The returned object is iterable and indexable. Each item in the container is a Type instance. """ -class ArgumentsIterator(collections.abc.Sequence): DeinAlptraum wrote: This is a type-error since `ArgumentsIterator` doesn't actually implement the `Sequence` protocol: it does not support slicing. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/98745 >From 2c31f3fe5d232381b868e96158be6f2acf7da1c6 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 13 Jul 2024 14:12:34 +0100 Subject: [PATCH] [libclang/python] Fix some type errors, add type annotations --- clang/bindings/python/clang/cindex.py | 192 +++--- .../tests/cindex/test_code_completion.py | 22 +- .../python/tests/cindex/test_comment.py | 4 +- 3 files changed, 127 insertions(+), 91 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 1d3ab89190407..9b50192068213 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -43,7 +43,7 @@ Most object information is exposed using properties, when the underlying API call is efficient. """ -from __future__ import absolute_import, division, print_function +from __future__ import annotations # TODO # @@ -64,48 +64,78 @@ from ctypes import * -import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) +from typing_extensions import Protocol, TypeAlias + +if TYPE_CHECKING: +from ctypes import _Pointer + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: ... +def __getitem__(self, key: int) -> TSeq: ... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" @property -def value(self): -if super(c_char_p, self).value is None: +def value(self) -> str | None: # type: ignore [override] +val = super(c_char_p, self).value +if val is None: return None -return super(c_char_p, self).value.decode("utf8") +return val.decode("utf8") @classmethod -def from_param(cls, param): +def from_param(cls, param: str | bytes | None) -> c_interop_string: if isinstance(param, str): return cls(param) if isinstance(param, bytes): return cls(param) if param is None: # Support passing null to C functions expecting char arrays -return None +return cls(param) raise TypeError( "Cannot convert '{}' to '{}'".format(type(param).__name__, cls.__name__) ) @staticmethod -def to_python_string(x, *args): +def to_python_string(x: c_interop_string, *args: Any) -> str | None: return x.value -def b(x): +def b(x: str | bytes) -> bytes: if isinstance(x, bytes): return x return x.encode("utf8") @@ -115,9 +145,7 @@ def b(x): # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p = POINTER(c_void_p) - -callbacks = {} +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -169,8 +197,11 @@ def __init__(self, enumeration, message): ### Structures and Utility Classes ### +TInstance = TypeVar("TInstance") +TResult = TypeVar("TResult") + -class CachedProperty: +class CachedProperty(Generic[TInstance, TResult]): """Decorator that lazy-loads the value of a property. The first time the property is accessed, the original property function is @@ -178,16 +209,20 @@ class CachedProperty: property, replacing the original method. """ -def __init__(self, wrapped): +def __init__(self, wrapped: Callable[[TInstance], TResult]): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: pass -def __get__(self, instance, instance_type=None): +def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: if instance is None: -return self +property_name = self.wrapped.__name__ +class_name = instance_type.__name__ +raise TypeError( +f"'{property_name}' is not a static attribute of '{class_name}'" +
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -169,25 +198,32 @@ def __init__(self, enumeration, message): ### Structures and Utility Classes ### +TInstance = TypeVar("TInstance") +TResult = TypeVar("TResult") + -class CachedProperty: +class CachedProperty(Generic[TInstance, TResult]): """Decorator that lazy-loads the value of a property. The first time the property is accessed, the original property function is executed. The value it returns is set as the new value of that instance's property, replacing the original method. """ -def __init__(self, wrapped): +def __init__(self, wrapped: Callable[[TInstance], TResult]): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: pass -def __get__(self, instance, instance_type=None): +def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: if instance is None: -return self +property_name = self.wrapped.__name__ +class_name = instance_type.__name__ +raise TypeError( +f"'{property_name}' is not a static attribute of '{class_name}'" +) DeinAlptraum wrote: `CachedProperty` is a decorator used as a replacement for `@property` where the actual function might include heavier computations, so after the first call this caches the result of a decorated function as an attribute of the instance it belongs to. The `if instance is None` case occurs only when trying to call this statically, i.e. when the decorated function is called on the class it belongs to, instead of on an instance. In that case the existing implementation returns the `CachedProperty` object, which... doesn't seem to make any sense. That object itself is useless, it is an implementation detail that the user is not supposed to see, so I can't think of a reason why this would return `self`. This would also make the type annotation more complicated, so I changed this to throw an error instead, when trying to access a property as if it were a static attribute https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -3895,28 +3932,28 @@ def register_function(lib, item, ignore_errors): func.errcheck = item[3] -def register_functions(lib, ignore_errors): +def register_functions(lib: CDLL, ignore_errors: bool) -> None: """Register function prototypes with a libclang library instance. This must be called as part of library instantiation so Python knows how to call out to the shared library. """ -def register(item): -return register_function(lib, item, ignore_errors) +def register(item: LibFunc) -> None: +register_function(lib, item, ignore_errors) DeinAlptraum wrote: `return` removed since `register_function()` doesn't return anything anyway. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -66,46 +66,77 @@ import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) +from typing_extensions import Protocol, TypeAlias + +if TYPE_CHECKING: +from ctypes import _Pointer + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: ... +def __getitem__(self, key: int) -> TSeq: ... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" DeinAlptraum wrote: This violated the `__str__` magic method definition, as it is not allowed to return `None`. I guess this is also worth a "potentially breaking change"? https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -66,46 +66,77 @@ import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) +from typing_extensions import Protocol, TypeAlias + +if TYPE_CHECKING: +from ctypes import _Pointer + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: ... +def __getitem__(self, key: int) -> TSeq: ... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" @property -def value(self): -if super(c_char_p, self).value is None: +def value(self) -> str | None: # type: ignore [override] +val = super(c_char_p, self).value +if val is None: return None -return super(c_char_p, self).value.decode("utf8") +return val.decode("utf8") DeinAlptraum wrote: This violates the superclass' interface, but I highly doubt that we actually want to return `bytes` here as the superclass mandates, so I had to `type: ignore` this. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -115,9 +146,7 @@ def b(x): # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p = POINTER(c_void_p) - -callbacks = {} DeinAlptraum wrote: `callbacks` could have been a `TypedDict`, but these are always a mouthful and I don't see a reason to have this dict in the first place, so I removed it. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -200,13 +236,16 @@ class _CXString(Structure): _fields_ = [("spelling", c_char_p), ("free", c_int)] -def __del__(self): +def __del__(self) -> None: conf.lib.clang_disposeString(self) @staticmethod -def from_result(res, fn=None, args=None): +def from_result(res: _CXString, fn: Any = None, args: Any = None) -> str: assert isinstance(res, _CXString) -return conf.lib.clang_getCString(res) +pystr: str | None = conf.lib.clang_getCString(res) +if pystr is None: +return "" +return pystr DeinAlptraum wrote: `conf.lib.clang_getCString` may sometimes (though seemingly rarely) return `None` instead of a Python `str`. `_CXString.from_result()` is used in a lot of places throughout this file, and while parts of it seem to be aware of this (e.g. doc string saying that this may return `None`) other places are not (e.g. calling `len` on the return value). Many parts of the interface also directly return the result of this function, and having to check for `None` in all of these places seems potentially impractical, so I went for returning empty strings instead of `None` instead. But due to the inconsistent usage throughout this file, I'm not sure how far this corresponds more or less to the original intention. https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -66,46 +66,77 @@ import collections.abc import os +import sys DeinAlptraum wrote: This is used for `sys.stdout.flush()` in this file, and leads to a type error. I don't know why this isn't already imported https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
@@ -43,7 +43,7 @@ Most object information is exposed using properties, when the underlying API call is efficient. """ -from __future__ import absolute_import, division, print_function DeinAlptraum wrote: All of these are already activated by default since Python 3.0. `annotations` is necessary though, for some of the type annotation features used in this PR https://github.com/llvm/llvm-project/pull/98745 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Fix some type errors, add type annotations (PR #98745)
https://github.com/DeinAlptraum created https://github.com/llvm/llvm-project/pull/98745 This fixes a few of the more debatable type errors, and adds related annotations, as the next step towards #76664. >From 06483bd3e398dd9dba757904e622d5dd1b37db77 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Sat, 13 Jul 2024 14:12:34 +0100 Subject: [PATCH] [libclang/python] Fix some type errors, add type annotations --- clang/bindings/python/clang/cindex.py | 137 +++--- .../tests/cindex/test_code_completion.py | 22 +-- .../python/tests/cindex/test_comment.py | 4 +- 3 files changed, 100 insertions(+), 63 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index 1d3ab89190407..469d602b2a642 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -43,7 +43,7 @@ Most object information is exposed using properties, when the underlying API call is efficient. """ -from __future__ import absolute_import, division, print_function +from __future__ import annotations # TODO # @@ -66,46 +66,77 @@ import collections.abc import os +import sys from enum import Enum +from typing import ( +Any, +Callable, +Generic, +Optional, +Type as TType, +TypeVar, +TYPE_CHECKING, +Union as TUnion, +) +from typing_extensions import Protocol, TypeAlias + +if TYPE_CHECKING: +from ctypes import _Pointer + +StrPath: TypeAlias = TUnion[str, os.PathLike[str]] +LibFunc: TypeAlias = TUnion[ +"tuple[str, Optional[list[Any]]]", +"tuple[str, Optional[list[Any]], Any]", +"tuple[str, Optional[list[Any]], Any, Callable[..., Any]]", +] +CObjP: TypeAlias = _Pointer[Any] + +TSeq = TypeVar("TSeq", covariant=True) + +class NoSliceSequence(Protocol[TSeq]): +def __len__(self) -> int: ... +def __getitem__(self, key: int) -> TSeq: ... + # Python 3 strings are unicode, translate them to/from utf8 for C-interop. class c_interop_string(c_char_p): -def __init__(self, p=None): +def __init__(self, p: str | bytes | None = None): if p is None: p = "" if isinstance(p, str): p = p.encode("utf8") super(c_char_p, self).__init__(p) -def __str__(self): -return self.value +def __str__(self) -> str: +return self.value or "" @property -def value(self): -if super(c_char_p, self).value is None: +def value(self) -> str | None: # type: ignore [override] +val = super(c_char_p, self).value +if val is None: return None -return super(c_char_p, self).value.decode("utf8") +return val.decode("utf8") @classmethod -def from_param(cls, param): +def from_param(cls, param: str | bytes | None) -> c_interop_string: if isinstance(param, str): return cls(param) if isinstance(param, bytes): return cls(param) if param is None: # Support passing null to C functions expecting char arrays -return None +return cls(param) raise TypeError( "Cannot convert '{}' to '{}'".format(type(param).__name__, cls.__name__) ) @staticmethod -def to_python_string(x, *args): +def to_python_string(x: c_interop_string, *args: Any) -> str | None: return x.value -def b(x): +def b(x: str | bytes) -> bytes: if isinstance(x, bytes): return x return x.encode("utf8") @@ -115,9 +146,7 @@ def b(x): # object. This is a problem, because it means that from_parameter will see an # integer and pass the wrong value on platforms where int != void*. Work around # this by marshalling object arguments as void**. -c_object_p = POINTER(c_void_p) - -callbacks = {} +c_object_p: TType[CObjP] = POINTER(c_void_p) ### Exception Classes ### @@ -169,8 +198,11 @@ def __init__(self, enumeration, message): ### Structures and Utility Classes ### +TInstance = TypeVar("TInstance") +TResult = TypeVar("TResult") + -class CachedProperty: +class CachedProperty(Generic[TInstance, TResult]): """Decorator that lazy-loads the value of a property. The first time the property is accessed, the original property function is @@ -178,16 +210,20 @@ class CachedProperty: property, replacing the original method. """ -def __init__(self, wrapped): +def __init__(self, wrapped: Callable[[TInstance], TResult]): self.wrapped = wrapped try: self.__doc__ = wrapped.__doc__ except: pass -def __get__(self, instance, instance_type=None): +def __get__(self, instance: TInstance, instance_type: Any = None) -> TResult: if instance is None: -return self +property_name = self.wrapped.__name__ +class_name = instance_type.__name__ +raise
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
DeinAlptraum wrote: LGTM! https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
@@ -1820,6 +1820,18 @@ def availability(self): return AvailabilityKind.from_id(self._availability) +@property +def binary_operator(self): +""" +Retrieves the opcode if this cursor points to a binary operator +:return: +""" + +if not hasattr(self, "_binopcode"): +self._binopcode = conf.lib.clang_Cursor_getBinaryOpcode(self) DeinAlptraum wrote: You're right, I missed that - that's also necessary so you get `BinaryOperator` when doing `from cindex import *` https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
https://github.com/DeinAlptraum commented: The Python changes look good to me, just one part that might need changes. Can someone approve the CI workflows so we also get the results for the bindings tests to confirm? https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
@@ -1820,6 +1820,18 @@ def availability(self): return AvailabilityKind.from_id(self._availability) +@property +def binary_operator(self): +""" +Retrieves the opcode if this cursor points to a binary operator +:return: +""" + +if not hasattr(self, "_binopcode"): +self._binopcode = conf.lib.clang_Cursor_getBinaryOpcode(self) DeinAlptraum wrote: Have you checked that the tests pass? I am not completely sure about the inner workings of this, but it seems to me that all `conf.lib` functions should be registered with the lib object by adding an entry for the function in the `functionlist` object at the end of the file. For comparison, see e.g. `clang_Cursor_getOffsetOfField` https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: CI is done, so could you merge this? Thanks for the reviews and support @Endilll! https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] Retrieve BinaryOperator::getOpcode and BinaryOperator::getOpcodeStr via libclang and its python interface (PR #98489)
DeinAlptraum wrote: For a quick overview of the changes you'll need to make after #95608 in the Python bindings: - remove `_kind`, `_name_map` and `__repr__()` from the `BinaryOperator` definition - declare all variants of `BinaryOperator` within the class definition, i.e. `Invalid = 0` instead of `BinaryOperator.Invalid = BinaryOperator(0)` - add `BinaryOperator` to the list of enum kinds tested in `clang/bindings/python/tests/cindex/test_enums.py` https://github.com/llvm/llvm-project/pull/98489 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/95608 >From 35bfcfbc69ee812c59350440b7b15c5e23ad1307 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 14 Jun 2024 22:12:09 +0100 Subject: [PATCH 1/6] [libclang/python] Refactor enum usage Use Python's builtin enum class instead of writing our own. This is preparation for strict typing in PR #78114 --- clang/bindings/python/clang/cindex.py | 1670 - .../python/tests/cindex/test_enums.py | 14 +- 2 files changed, 768 insertions(+), 916 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index b3d51e4d2a668..aacfc333723c4 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -68,6 +68,7 @@ import collections.abc import os +from enum import Enum # Python 3 strings are unicode, translate them to/from utf8 for C-interop. @@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None def __repr__(self): return "%s.%s" % ( -self.__class__, +self.__class__.__name__, self.name, ) @@ -665,14 +640,10 @@ class CursorKind(BaseEnumeration): A CursorKind describes the kind of entity that a cursor points to. """ -# The required BaseEnumeration declarations. -_kinds = [] -_name_map = None - @staticmethod def get_all_kinds(): """Return all CursorKind enumeration instances.""" -return [x for x in CursorKind._kinds if not x is None] +return list(CursorKind) def is_declaration(self): """Test if this is a declaration kind.""" @@ -710,822 +681,820 @@ def is_unexposed(self): """Test if this is an unexposed kind.""" return conf.lib.clang_isUnexposed(self) -def __repr__(self): -return "CursorKind.%s" % (self.name,) - -### -# Declaration Kinds +### +# Declaration Kinds -# A declaration whose specific kind is not exposed via this interface. -# -# Unexposed declarations have the same operations as any other kind of -# declaration; one can extract their location information, spelling, find their -# definitions, etc. However, the specific kind of the declaration is not -# reported. -CursorKind.UNEXPOSED_DECL = CursorKind(1) +# A declaration whose specific kind is not exposed via this interface. +# +# Unexposed declarations have the same operations as any other kind of +# declaration; one can extract their location information, spelling, find +# their definitions, etc. However, the specific kind of the declaration is +# not reported. +UNEXPOSED_DECL = 1 -# A C or C++ struct. -CursorKind.STRUCT_DECL = CursorKind(2) +# A C or C++ struct. +STRUCT_DECL = 2 -# A C or C++ union. -CursorKind.UNION_DECL = CursorKind(3) +# A C or C++ union. +UNION_DECL = 3 -# A C++ class. -CursorKind.CLASS_DECL = CursorKind(4) +# A C++ class. +CLASS_DECL = 4 -# An enumeration. -CursorKind.ENUM_DECL = CursorKind(5) +# An enumeration. +ENUM_DECL = 5 -# A field (in C) or non-static data member (in C++) in a struct, union, or C++ -# class. -CursorKind.FIELD_DECL = CursorKind(6) +# A field (in C) or non-static data member (in C++) in a struct, union, or +# C++ class.
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: I added a release note to the `Clang Python Bindings Potentially Breaking Changes` section https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/95608 >From 35bfcfbc69ee812c59350440b7b15c5e23ad1307 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 14 Jun 2024 22:12:09 +0100 Subject: [PATCH 1/5] [libclang/python] Refactor enum usage Use Python's builtin enum class instead of writing our own. This is preparation for strict typing in PR #78114 --- clang/bindings/python/clang/cindex.py | 1670 - .../python/tests/cindex/test_enums.py | 14 +- 2 files changed, 768 insertions(+), 916 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index b3d51e4d2a668..aacfc333723c4 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -68,6 +68,7 @@ import collections.abc import os +from enum import Enum # Python 3 strings are unicode, translate them to/from utf8 for C-interop. @@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None def __repr__(self): return "%s.%s" % ( -self.__class__, +self.__class__.__name__, self.name, ) @@ -665,14 +640,10 @@ class CursorKind(BaseEnumeration): A CursorKind describes the kind of entity that a cursor points to. """ -# The required BaseEnumeration declarations. -_kinds = [] -_name_map = None - @staticmethod def get_all_kinds(): """Return all CursorKind enumeration instances.""" -return [x for x in CursorKind._kinds if not x is None] +return list(CursorKind) def is_declaration(self): """Test if this is a declaration kind.""" @@ -710,822 +681,820 @@ def is_unexposed(self): """Test if this is an unexposed kind.""" return conf.lib.clang_isUnexposed(self) -def __repr__(self): -return "CursorKind.%s" % (self.name,) - -### -# Declaration Kinds +### +# Declaration Kinds -# A declaration whose specific kind is not exposed via this interface. -# -# Unexposed declarations have the same operations as any other kind of -# declaration; one can extract their location information, spelling, find their -# definitions, etc. However, the specific kind of the declaration is not -# reported. -CursorKind.UNEXPOSED_DECL = CursorKind(1) +# A declaration whose specific kind is not exposed via this interface. +# +# Unexposed declarations have the same operations as any other kind of +# declaration; one can extract their location information, spelling, find +# their definitions, etc. However, the specific kind of the declaration is +# not reported. +UNEXPOSED_DECL = 1 -# A C or C++ struct. -CursorKind.STRUCT_DECL = CursorKind(2) +# A C or C++ struct. +STRUCT_DECL = 2 -# A C or C++ union. -CursorKind.UNION_DECL = CursorKind(3) +# A C or C++ union. +UNION_DECL = 3 -# A C++ class. -CursorKind.CLASS_DECL = CursorKind(4) +# A C++ class. +CLASS_DECL = 4 -# An enumeration. -CursorKind.ENUM_DECL = CursorKind(5) +# An enumeration. +ENUM_DECL = 5 -# A field (in C) or non-static data member (in C++) in a struct, union, or C++ -# class. -CursorKind.FIELD_DECL = CursorKind(6) +# A field (in C) or non-static data member (in C++) in a struct, union, or +# C++ class.
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: Hmm what do you think should be mentioned in a release note @Endilll ? Since this is just refactoring, if I did everything correctly, users of the bindings should not notice any difference (except for different error messages). So just mention refactoring of enums in the bindings? https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
@@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None DeinAlptraum wrote: Thank you for the feedback! I've removed the `try`-`except` block now so we're just using the `Enum` error message Irrelevant now, but regarding `from None`: This effectively removes the previous exception context. Since we are re-raising an exception here, we would get both the exception we raised ourselves _and_ the exception that we caught here. E.g. currently we get ``` >>> cindex.TokenKind.from_id(5) Traceback (most recent call last): File "", line 1, in File "/data/shared/programming/repos/llvm-project/clang/bindings/python/clang/cindex.py", line 589, in from_id raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None ValueError: Unknown TokenKind 5 ``` and without the `from None` we would get ``` >>> cindex.TokenKind.from_id(5) Traceback (most recent call last): File "/data/shared/programming/repos/llvm-project/clang/bindings/python/clang/cindex.py", line 587, in from_id return cls(id) ^^^ File "/usr/lib/python3.12/enum.py", line 757, in __call__ return cls.__new__(cls, value) ^^^ File "/usr/lib/python3.12/enum.py", line 1171, in __new__ raise ve_exc ValueError: 5 is not a valid TokenKind During handling of the above exception, another exception occurred: Traceback (most recent call last): File "", line 1, in File "/data/shared/programming/repos/llvm-project/clang/bindings/python/clang/cindex.py", line 589, in from_id raise ValueError("Unknown %s %d" % (cls.__name__, id)) ValueError: Unknown TokenKind 5 ``` https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/95608 >From 35bfcfbc69ee812c59350440b7b15c5e23ad1307 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 14 Jun 2024 22:12:09 +0100 Subject: [PATCH 1/4] [libclang/python] Refactor enum usage Use Python's builtin enum class instead of writing our own. This is preparation for strict typing in PR #78114 --- clang/bindings/python/clang/cindex.py | 1670 - .../python/tests/cindex/test_enums.py | 14 +- 2 files changed, 768 insertions(+), 916 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index b3d51e4d2a668..aacfc333723c4 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -68,6 +68,7 @@ import collections.abc import os +from enum import Enum # Python 3 strings are unicode, translate them to/from utf8 for C-interop. @@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None def __repr__(self): return "%s.%s" % ( -self.__class__, +self.__class__.__name__, self.name, ) @@ -665,14 +640,10 @@ class CursorKind(BaseEnumeration): A CursorKind describes the kind of entity that a cursor points to. """ -# The required BaseEnumeration declarations. -_kinds = [] -_name_map = None - @staticmethod def get_all_kinds(): """Return all CursorKind enumeration instances.""" -return [x for x in CursorKind._kinds if not x is None] +return list(CursorKind) def is_declaration(self): """Test if this is a declaration kind.""" @@ -710,822 +681,820 @@ def is_unexposed(self): """Test if this is an unexposed kind.""" return conf.lib.clang_isUnexposed(self) -def __repr__(self): -return "CursorKind.%s" % (self.name,) - -### -# Declaration Kinds +### +# Declaration Kinds -# A declaration whose specific kind is not exposed via this interface. -# -# Unexposed declarations have the same operations as any other kind of -# declaration; one can extract their location information, spelling, find their -# definitions, etc. However, the specific kind of the declaration is not -# reported. -CursorKind.UNEXPOSED_DECL = CursorKind(1) +# A declaration whose specific kind is not exposed via this interface. +# +# Unexposed declarations have the same operations as any other kind of +# declaration; one can extract their location information, spelling, find +# their definitions, etc. However, the specific kind of the declaration is +# not reported. +UNEXPOSED_DECL = 1 -# A C or C++ struct. -CursorKind.STRUCT_DECL = CursorKind(2) +# A C or C++ struct. +STRUCT_DECL = 2 -# A C or C++ union. -CursorKind.UNION_DECL = CursorKind(3) +# A C or C++ union. +UNION_DECL = 3 -# A C++ class. -CursorKind.CLASS_DECL = CursorKind(4) +# A C++ class. +CLASS_DECL = 4 -# An enumeration. -CursorKind.ENUM_DECL = CursorKind(5) +# An enumeration. +ENUM_DECL = 5 -# A field (in C) or non-static data member (in C++) in a struct, union, or C++ -# class. -CursorKind.FIELD_DECL = CursorKind(6) +# A field (in C) or non-static data member (in C++) in a struct, union, or +# C++ class.
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: *Ping\* @Endilll how should we proceed with this? https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: @Endilll do you think this needs a second review? If not, could you merge this? Since I don't have commit access https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
@@ -10,26 +10,6 @@ class TestTokenKind(unittest.TestCase): -def test_constructor(self): DeinAlptraum wrote: So the existing "custom" enum implementation included a sort of constructor, as we see in this test, that takes a number and name and then adds that as a new variant to the existing enum. No such constructor exists for the builtin `Enum` class, so there isn't really an equivalent for this test under the new implementation. We could test that it is impossible for the user to add their own variants dynamically at runtime by doing something like `TokenKind.NEW_VARIANT = 6`, but that doesn't really seem like our problem, and was also possible under the old implementation Your comment did however raise another question for me: while we've covered that there are no duplicate names with different IDs, we haven't covered that there are no duplicate IDs with different names, and this is indeed possible in the enum class, and not covered by our current tests so I just added one. https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/95608 >From 35bfcfbc69ee812c59350440b7b15c5e23ad1307 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 14 Jun 2024 22:12:09 +0100 Subject: [PATCH 1/3] [libclang/python] Refactor enum usage Use Python's builtin enum class instead of writing our own. This is preparation for strict typing in PR #78114 --- clang/bindings/python/clang/cindex.py | 1670 - .../python/tests/cindex/test_enums.py | 14 +- 2 files changed, 768 insertions(+), 916 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index b3d51e4d2a668..aacfc333723c4 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -68,6 +68,7 @@ import collections.abc import os +from enum import Enum # Python 3 strings are unicode, translate them to/from utf8 for C-interop. @@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None def __repr__(self): return "%s.%s" % ( -self.__class__, +self.__class__.__name__, self.name, ) @@ -665,14 +640,10 @@ class CursorKind(BaseEnumeration): A CursorKind describes the kind of entity that a cursor points to. """ -# The required BaseEnumeration declarations. -_kinds = [] -_name_map = None - @staticmethod def get_all_kinds(): """Return all CursorKind enumeration instances.""" -return [x for x in CursorKind._kinds if not x is None] +return list(CursorKind) def is_declaration(self): """Test if this is a declaration kind.""" @@ -710,822 +681,820 @@ def is_unexposed(self): """Test if this is an unexposed kind.""" return conf.lib.clang_isUnexposed(self) -def __repr__(self): -return "CursorKind.%s" % (self.name,) - -### -# Declaration Kinds +### +# Declaration Kinds -# A declaration whose specific kind is not exposed via this interface. -# -# Unexposed declarations have the same operations as any other kind of -# declaration; one can extract their location information, spelling, find their -# definitions, etc. However, the specific kind of the declaration is not -# reported. -CursorKind.UNEXPOSED_DECL = CursorKind(1) +# A declaration whose specific kind is not exposed via this interface. +# +# Unexposed declarations have the same operations as any other kind of +# declaration; one can extract their location information, spelling, find +# their definitions, etc. However, the specific kind of the declaration is +# not reported. +UNEXPOSED_DECL = 1 -# A C or C++ struct. -CursorKind.STRUCT_DECL = CursorKind(2) +# A C or C++ struct. +STRUCT_DECL = 2 -# A C or C++ union. -CursorKind.UNION_DECL = CursorKind(3) +# A C or C++ union. +UNION_DECL = 3 -# A C++ class. -CursorKind.CLASS_DECL = CursorKind(4) +# A C++ class. +CLASS_DECL = 4 -# An enumeration. -CursorKind.ENUM_DECL = CursorKind(5) +# An enumeration. +ENUM_DECL = 5 -# A field (in C) or non-static data member (in C++) in a struct, union, or C++ -# class. -CursorKind.FIELD_DECL = CursorKind(6) +# A field (in C) or non-static data member (in C++) in a struct, union, or +# C++ class.
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
@@ -10,26 +10,6 @@ class TestTokenKind(unittest.TestCase): -def test_constructor(self): DeinAlptraum wrote: thNot really: - `test_constructor`: this tests the ability to add enum variants "on the fly", which is not possible with the Python stdlib's `Enum` class - `test_bad_register`: the `Enum` class tests for this on initialization, so if you define a duplicate `Enum` variant, you will get a failure on import already. This makes the test both a) effectively covered by that and b) you couldn't import the module to run such a test anyway in a case where there's a duplicate - `test_unknown_value`: this is covered in `test_from_id` in `test_enums.py` since I added the `TokenKind` class to the test there https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: \*Ping* @boomanaiden154 @linux4life798 a review/feedback would be appreciated! https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: @Endilll done with the TokenKind refactoring, so this is ready to go from my side. Regarding reviewers, can you recommend any others? Since I assume I'll open a couple more PRs for the Python bindings after this https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/95608 >From 35bfcfbc69ee812c59350440b7b15c5e23ad1307 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 14 Jun 2024 22:12:09 +0100 Subject: [PATCH 1/2] [libclang/python] Refactor enum usage Use Python's builtin enum class instead of writing our own. This is preparation for strict typing in PR #78114 --- clang/bindings/python/clang/cindex.py | 1670 - .../python/tests/cindex/test_enums.py | 14 +- 2 files changed, 768 insertions(+), 916 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index b3d51e4d2a668..aacfc333723c4 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -68,6 +68,7 @@ import collections.abc import os +from enum import Enum # Python 3 strings are unicode, translate them to/from utf8 for C-interop. @@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None def __repr__(self): return "%s.%s" % ( -self.__class__, +self.__class__.__name__, self.name, ) @@ -665,14 +640,10 @@ class CursorKind(BaseEnumeration): A CursorKind describes the kind of entity that a cursor points to. """ -# The required BaseEnumeration declarations. -_kinds = [] -_name_map = None - @staticmethod def get_all_kinds(): """Return all CursorKind enumeration instances.""" -return [x for x in CursorKind._kinds if not x is None] +return list(CursorKind) def is_declaration(self): """Test if this is a declaration kind.""" @@ -710,822 +681,820 @@ def is_unexposed(self): """Test if this is an unexposed kind.""" return conf.lib.clang_isUnexposed(self) -def __repr__(self): -return "CursorKind.%s" % (self.name,) - -### -# Declaration Kinds +### +# Declaration Kinds -# A declaration whose specific kind is not exposed via this interface. -# -# Unexposed declarations have the same operations as any other kind of -# declaration; one can extract their location information, spelling, find their -# definitions, etc. However, the specific kind of the declaration is not -# reported. -CursorKind.UNEXPOSED_DECL = CursorKind(1) +# A declaration whose specific kind is not exposed via this interface. +# +# Unexposed declarations have the same operations as any other kind of +# declaration; one can extract their location information, spelling, find +# their definitions, etc. However, the specific kind of the declaration is +# not reported. +UNEXPOSED_DECL = 1 -# A C or C++ struct. -CursorKind.STRUCT_DECL = CursorKind(2) +# A C or C++ struct. +STRUCT_DECL = 2 -# A C or C++ union. -CursorKind.UNION_DECL = CursorKind(3) +# A C or C++ union. +UNION_DECL = 3 -# A C++ class. -CursorKind.CLASS_DECL = CursorKind(4) +# A C++ class. +CLASS_DECL = 4 -# An enumeration. -CursorKind.ENUM_DECL = CursorKind(5) +# An enumeration. +ENUM_DECL = 5 -# A field (in C) or non-static data member (in C++) in a struct, union, or C++ -# class. -CursorKind.FIELD_DECL = CursorKind(6) +# A field (in C) or non-static data member (in C++) in a struct, union, or +# C++ class.
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum updated https://github.com/llvm/llvm-project/pull/95608 >From 35bfcfbc69ee812c59350440b7b15c5e23ad1307 Mon Sep 17 00:00:00 2001 From: Jannick Kremer Date: Fri, 14 Jun 2024 22:12:09 +0100 Subject: [PATCH 1/2] [libclang/python] Refactor enum usage Use Python's builtin enum class instead of writing our own. This is preparation for strict typing in PR #78114 --- clang/bindings/python/clang/cindex.py | 1670 - .../python/tests/cindex/test_enums.py | 14 +- 2 files changed, 768 insertions(+), 916 deletions(-) diff --git a/clang/bindings/python/clang/cindex.py b/clang/bindings/python/clang/cindex.py index b3d51e4d2a668..aacfc333723c4 100644 --- a/clang/bindings/python/clang/cindex.py +++ b/clang/bindings/python/clang/cindex.py @@ -68,6 +68,7 @@ import collections.abc import os +from enum import Enum # Python 3 strings are unicode, translate them to/from utf8 for C-interop. @@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None def __repr__(self): return "%s.%s" % ( -self.__class__, +self.__class__.__name__, self.name, ) @@ -665,14 +640,10 @@ class CursorKind(BaseEnumeration): A CursorKind describes the kind of entity that a cursor points to. """ -# The required BaseEnumeration declarations. -_kinds = [] -_name_map = None - @staticmethod def get_all_kinds(): """Return all CursorKind enumeration instances.""" -return [x for x in CursorKind._kinds if not x is None] +return list(CursorKind) def is_declaration(self): """Test if this is a declaration kind.""" @@ -710,822 +681,820 @@ def is_unexposed(self): """Test if this is an unexposed kind.""" return conf.lib.clang_isUnexposed(self) -def __repr__(self): -return "CursorKind.%s" % (self.name,) - -### -# Declaration Kinds +### +# Declaration Kinds -# A declaration whose specific kind is not exposed via this interface. -# -# Unexposed declarations have the same operations as any other kind of -# declaration; one can extract their location information, spelling, find their -# definitions, etc. However, the specific kind of the declaration is not -# reported. -CursorKind.UNEXPOSED_DECL = CursorKind(1) +# A declaration whose specific kind is not exposed via this interface. +# +# Unexposed declarations have the same operations as any other kind of +# declaration; one can extract their location information, spelling, find +# their definitions, etc. However, the specific kind of the declaration is +# not reported. +UNEXPOSED_DECL = 1 -# A C or C++ struct. -CursorKind.STRUCT_DECL = CursorKind(2) +# A C or C++ struct. +STRUCT_DECL = 2 -# A C or C++ union. -CursorKind.UNION_DECL = CursorKind(3) +# A C or C++ union. +UNION_DECL = 3 -# A C++ class. -CursorKind.CLASS_DECL = CursorKind(4) +# A C++ class. +CLASS_DECL = 4 -# An enumeration. -CursorKind.ENUM_DECL = CursorKind(5) +# An enumeration. +ENUM_DECL = 5 -# A field (in C) or non-static data member (in C++) in a struct, union, or C++ -# class. -CursorKind.FIELD_DECL = CursorKind(6) +# A field (in C) or non-static data member (in C++) in a struct, union, or +# C++ class.
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: @Endilll are you taking a look at this, and/or should I ask other reviewers? https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
DeinAlptraum wrote: @Endilll can I ask you for a review again? As a next step towards the python-bindings strict typing PR, this one captures all the enum refactoring changes necessary towards that goal. Don't be scared by the LoC changed: 90% of that is just indentation changes :) https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
@@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None def __repr__(self): return "%s.%s" % ( -self.__class__, +self.__class__.__name__, DeinAlptraum wrote: This `__repr__` was effectively unused before as each subclass defined its own. This change is necessary so that all subclasses still return exactly the same representation as they did before https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
@@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] - @classmethod def from_id(cls, id): -if id < 0 or id >= len(cls._kinds) or cls._kinds[id] is None: -raise ValueError("Unknown template argument kind %d" % id) -return cls._kinds[id] +try: +return cls(id) +except ValueError: +raise ValueError("Unknown %s %d" % (cls.__name__, id)) from None DeinAlptraum wrote: In hindsight, should the change of error message go into a separate PR? The one we had before, `Unknown template argument kind`, doesn't make much sense since this is the base class of not just `TemplateArgumentKind`. If we don't consider changing error messages as a breaking change, then we could also remove the try-except part entirely since `Enum` already throws a meanginful error, looking like this: `ValueError: 703 is not a valid CursorKind` https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
@@ -31,17 +31,9 @@ class TestCursorKind(unittest.TestCase): def test_from_id(self): """Check that kinds can be constructed from valid IDs""" for enum in self.enums: -self.assertEqual(enum.from_id(2), enum._kinds[2]) +self.assertEqual(enum.from_id(2), enum(2)) +max_value = max([variant.value for variant in enum]) with self.assertRaises(ValueError): -enum.from_id(len(enum._kinds)) +enum.from_id(max_value + 1) with self.assertRaises(ValueError): enum.from_id(-1) - -def test_unique_kinds(self): -"""Check that no kind name has been used multiple times""" -for enum in self.enums: -for id in range(len(enum._kinds)): -try: -enum.from_id(id).name -except ValueError: -pass DeinAlptraum wrote: This test is effectively pointless now, since the enum class errors out as soon as the module is loaded in case there are duplicate enum variants https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum commented: There is also `TokenKind`: this one does not currently inherit from `BaseEnumeration` and is defined somewhat differently, having all its variants and their IDs as a dictionary in `enumerations.py`. This seems quite arbitrary to me, is there any reason it is done this way? Otherwise I would also move this to `cindex.py` as another subclass of `BaseEnumeration` https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
https://github.com/DeinAlptraum edited https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [libclang/python] Refactor enum usage (PR #95608)
@@ -611,51 +612,25 @@ def register(value, name): ### Cursor Kinds ### -class BaseEnumeration: +class BaseEnumeration(Enum): """ Common base class for named enumerations held in sync with Index.h values. - -Subclasses must define their own _kinds and _name_map members, as: -_kinds = [] -_name_map = None -These values hold the per-subclass instances and value-to-name mappings, -respectively. - """ -def __init__(self, value): -if value >= len(self.__class__._kinds): -self.__class__._kinds += [None] * (value - len(self.__class__._kinds) + 1) -if self.__class__._kinds[value] is not None: -raise ValueError( -"{0} value {1} already loaded".format(str(self.__class__), value) -) -self.value = value -self.__class__._kinds[value] = self -self.__class__._name_map = None def from_param(self): return self.value -@property -def name(self): -"""Get the enumeration name of this cursor kind.""" -if self._name_map is None: -self._name_map = {} -for key, value in self.__class__.__dict__.items(): -if isinstance(value, self.__class__): -self._name_map[value] = key -return self._name_map[self] DeinAlptraum wrote: The `Enum` class already provides a `name` attribute that follows the exact same format as we had before https://github.com/llvm/llvm-project/pull/95608 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits