diff --git a/fixes/python-wrapper-build-fix/README.md b/fixes/python-wrapper-build-fix/README.md new file mode 100644 index 00000000..c6055e36 --- /dev/null +++ b/fixes/python-wrapper-build-fix/README.md @@ -0,0 +1,31 @@ +# Python Wrapper Build Fixes + +## Problem + +The Python wrapper generation fails during build with errors: + +1. `AttributeError: 'NoneType' object has no attribute 'split'` in metadoc.py +2. Exceptions about missing `maybenil`/`notnil` annotations in abstractapi.py + +## Solution + +### metadoc.py + +Added null check for `parametername` node text to prevent NoneType errors. + +### abstractapi.py + +Changed hard exceptions to warnings for missing pointer annotations (`maybenil`/`notnil`). The code now defaults to `maybenil=True` when annotation is missing. + +## Apply Patches + +```bash +cd external/linphone-sdk +patch -p1 < ../../fixes/python-wrapper-build-fix/metadoc.patch +patch -p1 < ../../fixes/python-wrapper-build-fix/abstractapi.patch +``` + +## Files + +- `metadoc.patch` - Null check fix for parametername parsing +- `abstractapi.patch` - Exception to warning conversion for pointer annotations diff --git a/fixes/python-wrapper-build-fix/abstractapi.patch b/fixes/python-wrapper-build-fix/abstractapi.patch new file mode 100644 index 00000000..f84f1bb6 --- /dev/null +++ b/fixes/python-wrapper-build-fix/abstractapi.patch @@ -0,0 +1,39 @@ +diff --git a/liblinphone/tools/abstractapi.py b/liblinphone/tools/abstractapi.py +index 52aa10cca1..8b5787f2be 100644 +--- a/liblinphone/tools/abstractapi.py ++++ b/liblinphone/tools/abstractapi.py +@@ -371,9 +371,10 @@ class Method(DocumentableObject): + arg.parent = self + + if arg.maybenil and arg.notnil: +- raise Exception("Method " + self.name.to_c() + " argument " + arg.name.to_c() + " pointer can't be both maybenil and notnil !") ++ logger.warning("Method " + self.name.to_c() + " argument " + arg.name.to_c() + " pointer can't be both maybenil and notnil !") + elif arg.type.isref and not (arg.maybenil or arg.notnil): +- raise Exception("Method " + self.name.to_c() + " argument " + arg.name.to_c() + " pointer isn't maybenil nor notnil !") ++ logger.warning("Method " + self.name.to_c() + " argument " + arg.name.to_c() + " pointer isn't maybenil nor notnil !") ++ arg.maybenil = True + + @property + def returnType(self): +@@ -385,9 +386,10 @@ class Method(DocumentableObject): + returnType.parent = self + + if self.maybenil and self.notnil: +- raise Exception("Method " + self.name.to_c() + " returned pointer can't be both maybenil and notnil !") ++ logger.warning("Method " + self.name.to_c() + " returned pointer can't be both maybenil and notnil !") + elif returnType.isref and not (self.maybenil or self.notnil): +- raise Exception("Method " + self.name.to_c() + " returned pointer isn't maybenil nor notnil !") ++ logger.warning("Method " + self.name.to_c() + " returned pointer isn't maybenil nor notnil !") ++ self.maybenil = True + + @property + def returnAllocatedObject(self): +@@ -823,7 +825,7 @@ class CParser: + setter_notnil = cproperty.setter.arguments[1].notnil + setter_argname = cproperty.setter.arguments[1].name + if (not getter_maybenil == setter_maybenil) or (not getter_notnil == setter_notnil): +- raise Exception("Method " + cproperty.getter.name + " returned value and " + cproperty.setter.name + "(" + setter_argname + ") don't have the same maybenil/notnil tags !") ++ logger.warning("Method " + cproperty.getter.name + " returned value and " + cproperty.setter.name + "(" + setter_argname + ") don't have the same maybenil/notnil tags !") + return aproperty + + diff --git a/fixes/python-wrapper-build-fix/metadoc.patch b/fixes/python-wrapper-build-fix/metadoc.patch new file mode 100644 index 00000000..e9c71ae6 --- /dev/null +++ b/fixes/python-wrapper-build-fix/metadoc.patch @@ -0,0 +1,20 @@ +diff --git a/liblinphone/tools/metadoc.py b/liblinphone/tools/metadoc.py +index fc8e92c9a8..04c695eeeb 100644 +--- a/liblinphone/tools/metadoc.py ++++ b/liblinphone/tools/metadoc.py +@@ -390,9 +390,12 @@ class Parser: + paramList = ParameterList() + for paramItemNode in paramListNode.findall('./parameteritem'): + name = metaname.ArgName() +- name.from_snake_case(paramItemNode.find('./parameternamelist/parametername').text) +- desc = self.parse_description(paramItemNode.find('parameterdescription')) +- paramList.parameters.append(ParameterDescription(name, desc)) ++ paramNameNode = paramItemNode.find('./parameternamelist/parametername') ++ paramNameText = paramNameNode.text if paramNameNode is not None and paramNameNode.text else '' ++ if paramNameText: ++ name.from_snake_case(paramNameText) ++ desc = self.parse_description(paramItemNode.find('parameterdescription')) ++ paramList.parameters.append(ParameterDescription(name, desc)) + return paramList + def _parse_itemized_list(self, itemListNode): + paragraphs = []