Hi Jean-Michel,

the patch is attached. I have not tested it, and it is vibe-coded with anthropics Agent claude 4.8

you need to do:
mkdir apso-fix && cd apso-fix
unzip /path/to/apso-1.3.2.oxt
patch -p1 < apso-1.3.2-aoo417-uri-compat.patch   # from here, the diff targets python/apso.py
zip -r ../apso-1.3.2-aoo417.oxt .

I hope this helps if you have issues please reach out.

Am 13.10.2026 um 16:56 schrieb Jean-Michel COSTE via dev:
Hello Peter,

Le 13/10/2026 à 07:12, [email protected] a écrit :
Thanks a lot.

So the issue is that we APSO uses an internal API that is not promised to stay the same. The extension broke because we added our own  in-app Python editor and refactored the API to work properly.
I had forgotten about this menu, whose buttons were disabled in version 4.1.16...
So way forward:
I can provide a patch that fixes APSO that it works again. it is a one liner.
If it is possible, Thanks
Can you maybe test our own Editor and tell us what you need so we can add features?

I see—simple and effective.

APSO doesn't have a built-in editor; you have to specify which one to use. Personally, I use Geany for its autocompletion, syntax highlighting, and templates.

I don't know if it's worth developing these features in OpenOffice?
I am not an hardcore user. Shame is that I have only very limited use of AOO. So if I want that features we ship are used I need to listen to you. There is a huge rift between the community and the view Developers that still exist. So if those are features you think important, maybe the build in AOO editor should offer it. Not only for python, but for all scripting languages. I will note them down for a time later.
all the best
peter
--- a/python/apso.py
+++ b/python/apso.py
@@ -371,6 +371,21 @@
         n = dialog.execute()
         dialog.dispose()
         return n
+
+
+def _node_uri(node):
+    """ Return a script BrowseNode's storage URI as a string.
+
+    APSO targets the LibreOffice python script provider, where
+    BrowseNode.uri is a plain string attribute.  Apache OpenOffice 4.1.17
+    turned it into a *method* (pythonscript.py: "Use the parent URI to
+    dynamically derive ours, so renaming the parent doesn't break our
+    URI"), so bare attribute access yields the bound method and breaks
+    every downstream string op.  Call it when callable, read it otherwise,
+    so one build works on both AOO 4.1.17+ and LibreOffice / older AOO.
+    """
+    uri = node.uri
+    return uri() if callable(uri) else uri
 
 
 def join_url(base, name, name_encode=True):
@@ -641,12 +656,12 @@
 
         tree_node = self.tree_get_selected_node()
         node = self._node_get(tree_node)
-        if hasattr(node, 'uri') and node.uri.startswith(self.DOC_PROTOCOL):
+        if hasattr(node, 'uri') and 
_node_uri(node).startswith(self.DOC_PROTOCOL):
             try:
-                tempfile = tempfiles[node.uri]
+                tempfile = tempfiles[_node_uri(node)]
                 sfa = node.provCtx.sfa
                 if sfa.exists(tempfile):
-                    sfa.copy(tempfile, node.uri)
+                    sfa.copy(tempfile, _node_uri(node))
                     logger.info("tempfile copied back to embedded container.")
             except KeyError:
                 pass
@@ -689,7 +704,7 @@
             elif isinstance(node, pythonscript.FileBrowseNode):
                 states = [False, False, self.ENABLE_EDIT, False,
                           True, True, False, True, False, self.ENABLE_SHELL]
-                if node.uri.startswith(self.DOC_PROTOCOL):
+                if _node_uri(node).startswith(self.DOC_PROTOCOL):
                     states[3] = True
                     states[5] = False
                     states[6] = True
@@ -785,7 +800,7 @@
         """ Substitute script file in documents. """
         if not isinstance(node, pythonscript.FileBrowseNode):
             return
-        if not node.uri.startswith(self.DOC_PROTOCOL):
+        if not _node_uri(node).startswith(self.DOC_PROTOCOL):
             return
         url = FileOpenDialog(self.ctx,
                              default="$(user)/Scripts/python",
@@ -796,7 +811,7 @@
             if not sfa.exists(url):
                 raise ErrorAsMessage(RR.resolvestring('msg08') + url)
             try:
-                sfa.copy(url, node.uri)
+                sfa.copy(url, _node_uri(node))
             except Exception as e:
                 raise ErrorAsMessage(str(e))
 
@@ -810,7 +825,7 @@
         # if name is not None or not name == "":
         if name.strip():
             if isinstance(node, pythonscript.FileBrowseNode):
-                uri = node.uri
+                uri = _node_uri(node)
             elif isinstance(node, pythonscript.DirBrowseNode):
                 uri = node.rootUrl
             new_uri = join_url(base_url(uri), name)
@@ -826,7 +841,11 @@
                 sfa.move(uri, new_uri)
                 tree_node.setDisplayValue(name)
                 node.name = name
-                node.uri = new_uri
+                # AOO >= 4.1.17 derives uri() from the (already-updated)
+                # name, so only the LO/old-AOO string attribute needs
+                # setting; assigning on AOO would shadow the method.
+                if not callable(node.uri):
+                    node.uri = new_uri
             except Exception as e:
                 raise ErrorAsMessage(str(e))
 
@@ -850,7 +869,7 @@
             self.exec_create_file(self._get_tree_node("document"),
                                   doc_node, node.getName())
         try:
-            sfa.copy(node.uri, doc_uri)
+            sfa.copy(_node_uri(node), doc_uri)
         except Exception as e:
             raise ErrorAsMessage(str(e))
 
@@ -862,7 +881,7 @@
                              filters=((RR.resolvestring('msg06'), 
"*.py"),)).execute()
         if url:
             sfa = node.provCtx.sfa
-            sfa.copy(node.uri, url)
+            sfa.copy(_node_uri(node), url)
             # self.dialog.endDialog(1)
 
     def exec_delete(self, tree_node, node):
@@ -876,7 +895,7 @@
                           
message=RR.resolvestring('msg15').format(name)).execute()
         if n == YES:
             if isinstance(node, pythonscript.FileBrowseNode):
-                uri = node.uri
+                uri = _node_uri(node)
             elif isinstance(node, pythonscript.DirBrowseNode):
                 uri = node.rootUrl
             sfa = node.provCtx.sfa
@@ -891,16 +910,16 @@
 
     def exec_edit(self, tree_node, node):
         try:
-            if node.uri.startswith(self.DOC_PROTOCOL):
+            if _node_uri(node).startswith(self.DOC_PROTOCOL):
                 try:
-                    url = tempfiles[node.uri]
+                    url = tempfiles[_node_uri(node)]
                     sfa = node.provCtx.sfa
                     if not sfa.exists(url):
                         url = self._create_tempfile(node)
                 except KeyError:
                     url = self._create_tempfile(node)
             else:
-                url = node.uri
+                url = _node_uri(node)
             if self.settings["EditorPath"] and self._node_is_script(tree_node):
                 lineno = self.getFuncLine(url, node.funcName)
             else:
@@ -912,9 +931,9 @@
     def exec_debug(self, tree_node, node):
         """ Debug selected macro. """
         if self._node_is_script(tree_node):
-            if node.uri.startswith(self.DOC_PROTOCOL):
+            if _node_uri(node).startswith(self.DOC_PROTOCOL):
                 try:
-                    url = tempfiles[node.uri]
+                    url = tempfiles[_node_uri(node)]
                     sfa = node.provCtx.sfa
                     if not sfa.exists(url):
                         self._create_tempfile(node)
@@ -928,7 +947,7 @@
             # this line must come first
             loc = {'XSCRIPTCONTEXT': provctx.scriptContext}
             if isinstance(node, (pythonscript.FileBrowseNode, 
pythonscript.ScriptBrowseNode)):
-                module = provctx.getModuleByUrl(node.uri)
+                module = provctx.getModuleByUrl(_node_uri(node))
                 loc.update(module.__dict__)
             self.dialog.endDialog(0)
             console(loc=loc)
@@ -942,13 +961,13 @@
                 TP = self.smgr.createInstanceWithContext(
                             "com.sun.star.io.TempFile", self.ctx)
                 self.tempdir = base_url(TP.Uri)
-            path = node.uri.replace(self.DOC_PROTOCOL + ':/', '')
+            path = _node_uri(node).replace(self.DOC_PROTOCOL + ':/', '')
             filepath = join_url(self.tempdir, path)
             dirpath = '/'.join(filepath.split('/')[:-1])
             sfa = node.provCtx.sfa
             sfa.createFolder(dirpath)
-            sfa.copy(node.uri, filepath)
-            tempfiles[node.uri] = filepath
+            sfa.copy(_node_uri(node), filepath)
+            tempfiles[_node_uri(node)] = filepath
             return filepath
         except Exception as e:
             raise ErrorAsMessage("_create_tempfile\n\n"+str(e))
@@ -1080,18 +1099,18 @@
         data_model = self.tree.getModel().DataModel
         node = self._node_get(tree_node)
         if isinstance(node, pythonscript.FileBrowseNode):
-            if node.uri.startswith(self.DOC_PROTOCOL):
+            if _node_uri(node).startswith(self.DOC_PROTOCOL):
                 try:
-                    url = tempfiles[node.uri]
+                    url = tempfiles[_node_uri(node)]
                     sfa = node.provCtx.sfa
                     if sfa.exists(url):
-                        sfa.copy(url, node.uri)
+                        sfa.copy(url, _node_uri(node))
                 except KeyError:
-                    url = node.uri
+                    url = _node_uri(node)
                 except Exception as e:
                     raise ErrorAsMessage(str(e))
             else:
-                url = node.uri
+                url = _node_uri(node)
         try:
             child_nodes = node.getChildNodes()
             if isinstance(node, pythonscript.PythonScriptProvider):
@@ -1104,7 +1123,7 @@
             if n:
                 if url.startswith(self.DOC_PROTOCOL):
                     try:
-                        url = tempfiles[node.uri]
+                        url = tempfiles[_node_uri(node)]
                     except KeyError:
                         url = self._create_tempfile(node)
                 open_script(self.settings, url, e.lineno, e.offset)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to