Find LibreOffice's bundled Python inside a macOS app bundle - #139
Find LibreOffice's bundled Python inside a macOS app bundle#139kungsamu-ldu wants to merge 1 commit into
Conversation
_soffice_program_dirs only ever looks in soffice's own directory. That
holds on Windows and Linux, but a macOS app bundle keeps soffice in
Contents/MacOS and the bundled Python in the sibling Contents/Resources,
so discovery never finds it and --mode libreoffice fails with:
LibreOffice runtime is unavailable: compatible Python runtime was
not found.
Homebrew compounds this: its soffice on PATH is a bash wrapper that
execs into the app bundle, so shutil.which resolves to a Caskroom
command-wrappers directory outside the bundle entirely. Hence also
falling back to the standard bundle location.
Note this fixes discovery only. On current macOS the bundled interpreter
still cannot be spawned from outside LibreOffice -- it terminates with
SIGKILL (Code Signature Invalid), namespace CODESIGNING, indicator
"Launch Constraint Violation", with a valid signature and no quarantine
attribute, on a freshly reinstalled 26.2.5. So the mode remains
unavailable there unless a system Python with a working uno module is
present, which a Homebrew install does not provide. Worth knowing before
anyone spends time on the surrounding code path; happy to drop this if
you would rather gate the mode off on darwin instead.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 2 included reviews per hour; 0 remain after this review. 📝 WalkthroughWalkthroughThe change extends macOS LibreOffice discovery to include bundled Resources directories. New tests validate bundle resolution, Homebrew fallback behavior, and Linux behavior. ChangesmacOS LibreOffice bundle discovery
Priority: ⬇️ Low Estimated code review effort: 2 (Simple) | ~10 minutes Change: Bug fix Merge Risk: ⚪ Minimal · up to The macOS discovery change is covered for the intended bundle and Homebrew wrapper layouts without altering non-macOS resolution. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Full details: Description checkExplanation The description provides detailed scope, motivation, technical limitations, and test results. However, it does not follow the repository template: it omits the required Summary and Acceptance Criteria sections, does not address the listed criteria, and does not include the required validation checklist or documentation updates. Resolution Rewrite the description using the repository template. Add the Summary section with scope, motivation, and related issue/spec information. Address each Acceptance Criteria item or explain why it is not applicable. Add the required Validation checklist, including precommit status, changed-behavior tests, and documentation status. If the template is unrelated to this PR, update the repository template before merging.
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 5 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
The bug
_soffice_program_dirsonly ever looks in soffice's own directory. That holds on Windows and Linux, but a macOS app bundle keeps soffice inContents/MacOSand the bundled Python in the siblingContents/Resources, so discovery never finds it:Homebrew compounds it: the
sofficeit puts onPATHis a bash wrapper thatexecs into the app bundle, soshutil.whichresolves to a Caskroomcommand-wrappersdirectory outside the bundle entirely — hence the fallback to the standard bundle location as well.After the change, discovery succeeds from a bare
PATHlookup:Please read this before merging
This fixes discovery only — it does not make
--mode libreofficework on macOS. I want to be straight about that rather than have it look like a working path.Once found, the bundled interpreter still cannot be spawned from outside LibreOffice. It dies instantly, and the crash report gives the reason:
That is a macOS launch constraint, not a packaging accident on my machine. I ruled the alternatives out: the signature verifies (
codesign -vclean), there is nocom.apple.quarantineattribute, the binary is arm64 on an arm64 host, and it reproduces identically on a freshly reinstalled LibreOffice 26.2.5.sofficeitself runs fine — only direct invocation of the nested Python is refused._system_python_candidatesis the remaining fallback, but a Homebrew LibreOffice ships nounomodule for a system Python, so it finds nothing either.So: this patch turns a silent mis-detection into a correct one, which seemed worth having on its own, and the tests pin the platform layout. But if you would rather gate the mode off on darwin with a clear message than let it get one step further and fail elsewhere, say so and I will close this — or rework it that way, whichever you prefer.
Tests
tests/core/test_libreoffice_macos_paths.pybuilds a fake bundle undertmp_pathand covers the sibling-Resourcescase, the Homebrew-wrapper fallback, and that non-darwin behaviour is unchanged. All three patchsys.platform, so they run anywhere.927 tests pass (3 new),
mypy --strictandruff checkclean.Summary by CodeRabbit
Correction (added after further investigation)
Two things in the note above are wrong, and the second one matters a lot. Sorry for the noise — I would rather correct it in place than leave it standing.
1. Version. This reproduces on 26.8.0.3, not 26.2.5. A
brew reinstall --cask libreofficemoved the install from 26.2.5 to 26.8.0.3 (and the bundled Python from 3.12 to 3.13) mid-investigation; the constraint is present in both.2. "The mode remains unavailable there" is not true — there is a working route on macOS, and I have run it.
First, the root cause, which I could not identify before. It is a parent launch constraint that LibreOffice itself declares, slot 9 of the signature superblob (magic
fade8181). Decoded DER:So the immediate parent must be a TDF-signed binary with identifier
org.libreoffice.script— i.e.soffice. That is whycodesign -d --verbose=6shows nothing useful; at every verbosity it only prints "Has Parent Launch Constraints", and the detail has to be read out of the rawLC_CODE_SIGNATUREblob. The same constraint is onbin/python3.13,MacOS/uno,gengal,opencltest,regviewandxpdfimport— but not onsofficeorunopkg.It arrived deliberately: commit 22ab2bec "mac: add parent launch-constraint to packaged framework/helpers except for unopkg" (2025-05-27), with tdf#167080 (2025-07-04, backported to libreoffice-25-2) fixing the plist so it actually enforces. Every TDF-signed release since then carries it, so the official dmg and both casks behave identically. Nothing is misconfigured on my machine.
The route that works: run the bridge inside soffice via the Python script provider, rather than exec'ing the bundled interpreter. Launch constraints gate
exec, notdlopen—libpythonloaderlo.dylibdlopenslibpyuno.dyliband the framework's libpython straight into soffice, so the constraint never applies.I put
_libreoffice_bridge.pyin a throwaway profile'suser/Scripts/python/, changed only_resolve_context()(_libreoffice_bridge.py:219-238) touno.getComponentContext(), and left_load_document,_extract_draw_page_payload,_extract_chart_payloadand_close_document(:241-355) untouched:Full draw-page payload, ~3 s including launch — 33 shapes on
sample/flowchart/sample-shape-connector.xlsx, connectors flagged,sys.executable == /Applications/LibreOffice.app/Contents/MacOS/soffice, Python 3.13.15.One design consequence: a forwarded script URL does not execute. If a listener is already up, a second
soffice ... 'vnd.sun.star.script:...'returns 0 immediately and the provider never sees agetScript(plain--convert-toforwards fine, so it is specific to script URLs). So the persistent--acceptlistener atlibreoffice.py:593-616would have to give way to one headlesssofficeper workbook — roughly 3 s per file instead of 3 s per session — with parameters passed via a request file rather than env vars.Where that leaves this PR. The discovery fix here is still correct on its own terms, but it repairs
_resolve_python_path()— the very function a proper macOS fix would delete. So I am happy for you to close this, and I would rather you did than merge something that entrenches the wrong design. If you would like the script-provider route as a real PR instead, say the word and I will put one together; if you would rather gate the mode off on darwin with a clear message, that is a smaller change and I can do that too. Your call — I do not want to guess at the architecture you want.