From 3a6d444d3ef0175728f55b7db67d010cb5e0c3cc Mon Sep 17 00:00:00 2001 From: Maarten Breddels Date: Tue, 30 Jul 2024 16:32:33 +0200 Subject: [PATCH] feat: use solara for pyinstaller (#11) * feat: use solara for pyinstaller * rich.logging hidden import * make test run * close when tab is closed --- .github/workflows/standalone.yml | 4 ++-- jdaviz/solara.py | 15 +++++++++++++++ standalone/hooks/hook-ipyreact.py | 5 +++++ standalone/hooks/hook-ipyvuetify.py | 5 +++++ standalone/hooks/hook-solara.py | 5 +++++ standalone/hooks/hook-solara_server.py | 5 +++++ standalone/hooks/hook-solara_ui.py | 5 +++++ standalone/jdaviz-cli-entrypoint.py | 23 +++-------------------- standalone/jdaviz.spec | 2 +- standalone/test_standalone.py | 4 +--- 10 files changed, 47 insertions(+), 26 deletions(-) create mode 100644 standalone/hooks/hook-ipyreact.py create mode 100644 standalone/hooks/hook-ipyvuetify.py create mode 100644 standalone/hooks/hook-solara.py create mode 100644 standalone/hooks/hook-solara_server.py create mode 100644 standalone/hooks/hook-solara_ui.py diff --git a/.github/workflows/standalone.yml b/.github/workflows/standalone.yml index c08bef512b..2966aada00 100644 --- a/.github/workflows/standalone.yml +++ b/.github/workflows/standalone.yml @@ -17,7 +17,7 @@ defaults: jobs: build_binary_not_osx: runs-on: ${{ matrix.os }}-latest - if: (github.repository == 'spacetelescope/jdaviz' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'Build standalone'))) + # if: (github.repository == 'spacetelescope/jdaviz' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || contains(github.event.pull_request.labels.*.name, 'Build standalone'))) strategy: matrix: os: [ubuntu, windows] @@ -53,7 +53,7 @@ jobs: - name: Wait for Voila to get online uses: ifaxity/wait-on-action@a7d13170ec542bdca4ef8ac4b15e9c6aa00a6866 # v1.2.1 with: - resource: tcp:8866 + resource: tcp:8765 timeout: 60000 - name: Test standalone diff --git a/jdaviz/solara.py b/jdaviz/solara.py index 7234459b2e..906fd0b861 100644 --- a/jdaviz/solara.py +++ b/jdaviz/solara.py @@ -1,7 +1,9 @@ import os from pathlib import Path +import signal import solara +import solara.lab import ipygoldenlayout import ipysplitpanes import ipyvue @@ -16,6 +18,19 @@ jdaviz_history_verbosity = 'info' +@solara.lab.on_kernel_start +def on_kernel_start(): + # at import time, solara runs with a dummy kernel + # we simply ignore that + if "dummy" in solara.get_kernel_id(): + return + def on_kernel_close(): + # for some reason, sys.exit(0) does not work here + # see https://github.com/encode/uvicorn/discussions/1103 + signal.raise_signal(signal.SIGINT) + return on_kernel_close + + @solara.component def Page(): if config is None: diff --git a/standalone/hooks/hook-ipyreact.py b/standalone/hooks/hook-ipyreact.py new file mode 100644 index 0000000000..8a2acbaaeb --- /dev/null +++ b/standalone/hooks/hook-ipyreact.py @@ -0,0 +1,5 @@ +from PyInstaller.utils.hooks import collect_data_files, copy_metadata, collect_submodules + +hiddenimports = collect_submodules("ipyreact") +datas = collect_data_files("ipyreact") # codespell:ignore datas +datas += copy_metadata("ipyreact") # codespell:ignore datas diff --git a/standalone/hooks/hook-ipyvuetify.py b/standalone/hooks/hook-ipyvuetify.py new file mode 100644 index 0000000000..d3bee8c400 --- /dev/null +++ b/standalone/hooks/hook-ipyvuetify.py @@ -0,0 +1,5 @@ +from PyInstaller.utils.hooks import collect_data_files, copy_metadata, collect_submodules + +hiddenimports = collect_submodules("ipyvuetify") +datas = collect_data_files('ipyvuetify') +datas += copy_metadata('ipyvuetify') diff --git a/standalone/hooks/hook-solara.py b/standalone/hooks/hook-solara.py new file mode 100644 index 0000000000..0a51665040 --- /dev/null +++ b/standalone/hooks/hook-solara.py @@ -0,0 +1,5 @@ +from PyInstaller.utils.hooks import collect_data_files, copy_metadata, collect_submodules + +hiddenimports = collect_submodules("solara") +datas = collect_data_files('solara') +datas += collect_data_files('solara-ui') diff --git a/standalone/hooks/hook-solara_server.py b/standalone/hooks/hook-solara_server.py new file mode 100644 index 0000000000..227108ff6a --- /dev/null +++ b/standalone/hooks/hook-solara_server.py @@ -0,0 +1,5 @@ +from PyInstaller.utils.hooks import collect_data_files, copy_metadata, collect_submodules + +hiddenimports = collect_submodules("solara-server") +datas = collect_data_files('solara-server') +datas += copy_metadata('solara-server') diff --git a/standalone/hooks/hook-solara_ui.py b/standalone/hooks/hook-solara_ui.py new file mode 100644 index 0000000000..3747e9a718 --- /dev/null +++ b/standalone/hooks/hook-solara_ui.py @@ -0,0 +1,5 @@ +from PyInstaller.utils.hooks import collect_data_files, copy_metadata, collect_submodules + +hiddenimports = collect_submodules("solara-ui") +datas = collect_data_files('solara-ui') +datas += copy_metadata('solara-ui') diff --git a/standalone/jdaviz-cli-entrypoint.py b/standalone/jdaviz-cli-entrypoint.py index 27dde09c5c..1fdde82efb 100644 --- a/standalone/jdaviz-cli-entrypoint.py +++ b/standalone/jdaviz-cli-entrypoint.py @@ -1,23 +1,6 @@ -import sys - -def start_as_kernel(): - # similar to https://github.com/astrofrog/voila-qt-app/blob/master/voila_demo.py - import sys - - from ipykernel import kernelapp as app - app.launch_new_instance() - sys.argv = [app.__file__, sys.argv[3:]] +import jdaviz.cli if __name__ == "__main__": - # When voila starts a kernel under pyinstaller, it will use sys.executable - # (which is this entry point again) - # if called like [sys.argv[0], "-m", "ipykernel_launcher", ...] - if len(sys.argv) >= 3 and sys.argv[1] == "-m" and sys.argv[2] == "ipykernel_launcher": - # it is important that we do not import jdaviz top level - # as that would cause it to import ipywidgets before the kernel is started - start_as_kernel() - else: - import jdaviz.cli - # should change this to _main, but now it doesn't need arguments - jdaviz.cli.main(layout="") + # should change this to _main, but now it doesn't need arguments + jdaviz.cli.main(layout="") diff --git a/standalone/jdaviz.spec b/standalone/jdaviz.spec index 31e9f2f11b..11ddf9a610 100644 --- a/standalone/jdaviz.spec +++ b/standalone/jdaviz.spec @@ -26,7 +26,7 @@ a = Analysis( pathex=[], binaries=[], datas=datas, - hiddenimports=[], + hiddenimports=["rich.logging"], hookspath=["hooks"], hooksconfig={}, runtime_hooks=[], diff --git a/standalone/test_standalone.py b/standalone/test_standalone.py index 2864d6362e..916d9b328c 100644 --- a/standalone/test_standalone.py +++ b/standalone/test_standalone.py @@ -4,9 +4,7 @@ def test_voila_basics(page: Page): - page.goto("http://localhost:8866/") + page.goto("http://localhost:8765/") - # basic voila is loaded - page.locator("body.theme-light").wait_for() # when jdaviz is loaded (button at the top left) page.locator("text=Welcome to Jdaviz").wait_for()