From ae3285b6a2a5422e408fa84799db3ae4c0021bbe Mon Sep 17 00:00:00 2001 From: Oliver Wu Date: Thu, 2 Oct 2025 15:45:53 -0400 Subject: [PATCH 1/4] Add getter methods of the python exception class The code and message should be able to return. It keeps consistent with the API of C++. The naming conversion is also followed with ACT implementation rather than python PEP 8. --- Source/buildbindingpython.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Source/buildbindingpython.go b/Source/buildbindingpython.go index a96d4da2..ed822e8c 100644 --- a/Source/buildbindingpython.go +++ b/Source/buildbindingpython.go @@ -132,6 +132,12 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w w.Writeln(" if self._message:") w.Writeln(" return '%sException ' + str(self._code) + ': '+ str(self._message)", NameSpace) w.Writeln(" return '%sException ' + str(self._code)", NameSpace) + w.Writeln(" ") + w.Writeln(" def GetErrorCode(self):") + w.Writeln(" return self._code") + w.Writeln(" ") + w.Writeln(" def GetErrorMessage(self):") + w.Writeln(" return self._message") w.Writeln("") From 8e8bec758e341fe5ab5fbd79eec2d13ad6e462a1 Mon Sep 17 00:00:00 2001 From: Oliver Wu Date: Thu, 2 Oct 2025 16:06:21 -0400 Subject: [PATCH 2/4] Add additional methods --- Source/buildbindingpython.go | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Source/buildbindingpython.go b/Source/buildbindingpython.go index ed822e8c..7695e865 100644 --- a/Source/buildbindingpython.go +++ b/Source/buildbindingpython.go @@ -138,6 +138,26 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w w.Writeln(" ") w.Writeln(" def GetErrorMessage(self):") w.Writeln(" return self._message") + w.Writeln(" ") + w.Writeln(" def GetErrorName(self):") + w.Writeln(" if self._code == ErrorCodes.SUCCESS:") + w.Writeln(" return 'SUCCESS'") + for _, errorDef := range componentdefinition.Errors.Errors { + w.Writeln(" elif self._code == ErrorCodes.%s:", errorDef.Name) + w.Writeln(" return '%s'", errorDef.Name) + } + w.Writeln(" else:") + w.Writeln(" return 'UNKNOWN'") + w.Writeln(" ") + w.Writeln(" def GetErrorDescription(self):") + w.Writeln(" if self._code == ErrorCodes.SUCCESS:") + w.Writeln(" return 'success'") + for _, errorDef := range componentdefinition.Errors.Errors { + w.Writeln(" elif self._code == ErrorCodes.%s:", errorDef.Name) + w.Writeln(" return '%s'", errorDef.Description) + } + w.Writeln(" else:") + w.Writeln(" return 'unknown error'") w.Writeln("") From 256f970e148055bbf5cfdbd6fb61e21593e7f5d8 Mon Sep 17 00:00:00 2001 From: Oliver Wu Date: Thu, 2 Oct 2025 17:28:13 -0400 Subject: [PATCH 3/4] Add properties for the exception class It ensures Cross-Language Consistency. C++: Uses methods (getErrorCode()) C#: Uses properties (ErrorCode) Java: Uses methods (getErrorCode()) Python: Can support both patterns Meanwhile, rename the methods to comply with PEP 8 --- Source/buildbindingpython.go | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/Source/buildbindingpython.go b/Source/buildbindingpython.go index 7695e865..31d36cf2 100644 --- a/Source/buildbindingpython.go +++ b/Source/buildbindingpython.go @@ -133,13 +133,16 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w w.Writeln(" return '%sException ' + str(self._code) + ': '+ str(self._message)", NameSpace) w.Writeln(" return '%sException ' + str(self._code)", NameSpace) w.Writeln(" ") - w.Writeln(" def GetErrorCode(self):") + w.Writeln(" def get_error_code(self):") + w.Writeln(" \"\"\"Returns the error code\"\"\"") w.Writeln(" return self._code") w.Writeln(" ") - w.Writeln(" def GetErrorMessage(self):") + w.Writeln(" def get_error_message(self):") + w.Writeln(" \"\"\"Returns the custom error message\"\"\"") w.Writeln(" return self._message") w.Writeln(" ") - w.Writeln(" def GetErrorName(self):") + w.Writeln(" def get_error_name(self):") + w.Writeln(" \"\"\"Returns the error name (constant name)\"\"\"") w.Writeln(" if self._code == ErrorCodes.SUCCESS:") w.Writeln(" return 'SUCCESS'") for _, errorDef := range componentdefinition.Errors.Errors { @@ -149,7 +152,8 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w w.Writeln(" else:") w.Writeln(" return 'UNKNOWN'") w.Writeln(" ") - w.Writeln(" def GetErrorDescription(self):") + w.Writeln(" def get_error_description(self):") + w.Writeln(" \"\"\"Returns the error description (human-readable)\"\"\"") w.Writeln(" if self._code == ErrorCodes.SUCCESS:") w.Writeln(" return 'success'") for _, errorDef := range componentdefinition.Errors.Errors { @@ -158,6 +162,26 @@ func buildDynamicPythonImplementation(componentdefinition ComponentDefinition, w } w.Writeln(" else:") w.Writeln(" return 'unknown error'") + w.Writeln(" ") + w.Writeln(" @property") + w.Writeln(" def error_code(self):") + w.Writeln(" \"\"\"Property to access error code\"\"\"") + w.Writeln(" return self._code") + w.Writeln(" ") + w.Writeln(" @property") + w.Writeln(" def error_message(self):") + w.Writeln(" \"\"\"Property to access custom error message\"\"\"") + w.Writeln(" return self._message") + w.Writeln(" ") + w.Writeln(" @property") + w.Writeln(" def error_name(self):") + w.Writeln(" \"\"\"Property to access error name\"\"\"") + w.Writeln(" return self.get_error_name()") + w.Writeln(" ") + w.Writeln(" @property") + w.Writeln(" def error_description(self):") + w.Writeln(" \"\"\"Property to access error description\"\"\"") + w.Writeln(" return self.get_error_description()") w.Writeln("") From f76abbd6db7aa8affdeb174eacf48a4491132165 Mon Sep 17 00:00:00 2001 From: Oliver Wu Date: Fri, 10 Oct 2025 15:37:30 -0400 Subject: [PATCH 4/4] Test the exception in python --- .../RTTI_component/Bindings/Python/RTTI.py | 74 +++++++++++++++++++ .../Examples/Python/RTTI_Example.py | 53 ++++++++++++- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/Examples/RTTI/RTTI_component/Bindings/Python/RTTI.py b/Examples/RTTI/RTTI_component/Bindings/Python/RTTI.py index 361e0e13..fba4827f 100644 --- a/Examples/RTTI/RTTI_component/Bindings/Python/RTTI.py +++ b/Examples/RTTI/RTTI_component/Bindings/Python/RTTI.py @@ -32,6 +32,80 @@ def __str__(self): if self._message: return 'RTTIException ' + str(self._code) + ': '+ str(self._message) return 'RTTIException ' + str(self._code) + + def get_error_code(self): + """Returns the error code""" + return self._code + + def get_error_message(self): + """Returns the custom error message""" + return self._message + + def get_error_name(self): + """Returns the error name (constant name)""" + if self._code == ErrorCodes.SUCCESS: + return 'SUCCESS' + elif self._code == ErrorCodes.NOTIMPLEMENTED: + return 'NOTIMPLEMENTED' + elif self._code == ErrorCodes.INVALIDPARAM: + return 'INVALIDPARAM' + elif self._code == ErrorCodes.INVALIDCAST: + return 'INVALIDCAST' + elif self._code == ErrorCodes.BUFFERTOOSMALL: + return 'BUFFERTOOSMALL' + elif self._code == ErrorCodes.GENERICEXCEPTION: + return 'GENERICEXCEPTION' + elif self._code == ErrorCodes.COULDNOTLOADLIBRARY: + return 'COULDNOTLOADLIBRARY' + elif self._code == ErrorCodes.COULDNOTFINDLIBRARYEXPORT: + return 'COULDNOTFINDLIBRARYEXPORT' + elif self._code == ErrorCodes.INCOMPATIBLEBINARYVERSION: + return 'INCOMPATIBLEBINARYVERSION' + else: + return 'UNKNOWN' + + def get_error_description(self): + """Returns the error description (human-readable)""" + if self._code == ErrorCodes.SUCCESS: + return 'success' + elif self._code == ErrorCodes.NOTIMPLEMENTED: + return 'functionality not implemented' + elif self._code == ErrorCodes.INVALIDPARAM: + return 'an invalid parameter was passed' + elif self._code == ErrorCodes.INVALIDCAST: + return 'a type cast failed' + elif self._code == ErrorCodes.BUFFERTOOSMALL: + return 'a provided buffer is too small' + elif self._code == ErrorCodes.GENERICEXCEPTION: + return 'a generic exception occurred' + elif self._code == ErrorCodes.COULDNOTLOADLIBRARY: + return 'the library could not be loaded' + elif self._code == ErrorCodes.COULDNOTFINDLIBRARYEXPORT: + return 'a required exported symbol could not be found in the library' + elif self._code == ErrorCodes.INCOMPATIBLEBINARYVERSION: + return 'the version of the binary interface does not match the bindings interface' + else: + return 'unknown error' + + @property + def error_code(self): + """Property to access error code""" + return self._code + + @property + def error_message(self): + """Property to access custom error message""" + return self._message + + @property + def error_name(self): + """Property to access error name""" + return self.get_error_name() + + @property + def error_description(self): + """Property to access error description""" + return self.get_error_description() '''Definition of binding API version ''' diff --git a/Examples/RTTI/RTTI_component/Examples/Python/RTTI_Example.py b/Examples/RTTI/RTTI_component/Examples/Python/RTTI_Example.py index 98ebaabf..55b39fee 100644 --- a/Examples/RTTI/RTTI_component/Examples/Python/RTTI_Example.py +++ b/Examples/RTTI/RTTI_component/Examples/Python/RTTI_Example.py @@ -20,6 +20,43 @@ import RTTI +def test_exception_methods(): + """Test the new exception methods using assert statements""" + print("Testing RTTI Exception Methods...") + + # Test with a non-existent library to trigger an exception + try: + wrapper = RTTI.Wrapper(libraryName="nonexistent_library") + assert False, "Should have thrown an exception!" + except RTTI.ERTTIException as e: + print("+ Successfully caught ERTTIException") + + # Test the new methods with assertions + error_code = e.get_error_code() + error_message = e.get_error_message() + error_name = e.get_error_name() + error_description = e.get_error_description() + + # Assertions for method functionality + assert error_code == RTTI.ErrorCodes.COULDNOTLOADLIBRARY, f"Expected error code 6, got {error_code}" + assert error_name == "COULDNOTLOADLIBRARY", f"Expected 'COULDNOTLOADLIBRARY', got '{error_name}'" + assert error_description == "the library could not be loaded", f"Expected library load error description, got '{error_description}'" + assert "nonexistent_library" in error_message, f"Expected library name in message, got '{error_message}'" + + # Test properties (alternative access) + assert e.error_code == error_code, "Property error_code should match method get_error_code()" + assert e.error_message == error_message, "Property error_message should match method get_error_message()" + assert e.error_name == error_name, "Property error_name should match method get_error_name()" + assert e.error_description == error_description, "Property error_description should match method get_error_description()" + + # Test string representation + exception_str = str(e) + assert "RTTIException" in exception_str, f"String representation should contain 'RTTIException', got '{exception_str}'" + assert str(error_code) in exception_str, f"String representation should contain error code, got '{exception_str}'" + + print("+ All exception method tests passed!") + + def main(): libpath = '' # TODO add the location of the shared library binary here wrapper = RTTI.Wrapper(libraryName = os.path.join(libpath, "rtti")) @@ -96,7 +133,21 @@ def main(): animal = iter.GetNextAnimal(); assert not animal if __name__ == "__main__": + # Test exception methods first (before main, as it uses a non-existent library) + print("Testing Exception Methods:") + print("-" * 30) + test_exception_methods() + print() + + # Then run the main example + print("Running Main RTTI Example:") + print("-" * 30) try: main() except RTTI.ERTTIException as e: - print(e) + print("RTTI Exception occurred:") + print(f" Error Code: {e.get_error_code()}") + print(f" Error Message: '{e.get_error_message()}'") + print(f" Error Name: {e.get_error_name()}") + print(f" Error Description: {e.get_error_description()}") + print(f" Full Exception: {e}")