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]