wesktop 0.4.2__tar.gz → 0.4.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wesktop-0.4.4/.rlsbl/changes/.validated +1 -0
- wesktop-0.4.4/.rlsbl/changes/0.4.3.jsonl +4 -0
- wesktop-0.4.4/.rlsbl/changes/0.4.3.md +5 -0
- wesktop-0.4.4/.rlsbl/changes/0.4.4.jsonl +3 -0
- wesktop-0.4.4/.rlsbl/changes/0.4.4.md +5 -0
- wesktop-0.4.4/.rlsbl/releases/v0.4.3.toml +3 -0
- wesktop-0.4.4/.rlsbl/releases/v0.4.4.toml +3 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.selfdoc/hashes/hashes.json +3 -3
- {wesktop-0.4.2 → wesktop-0.4.4}/.strictcli/schema.json +1 -1
- {wesktop-0.4.2 → wesktop-0.4.4}/CHANGELOG.md +12 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/PKG-INFO +1 -1
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/cli-index.md +1 -1
- {wesktop-0.4.2 → wesktop-0.4.4}/pyproject.toml +1 -1
- {wesktop-0.4.2 → wesktop-0.4.4}/selfdoc.json +2 -2
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/__init__.py +9 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/desktop.py +42 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/server.py +4 -4
- wesktop-0.4.4/tests/test_gui_backend.py +155 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/uv.lock +1 -1
- wesktop-0.4.2/.rlsbl/changes/.validated +0 -1
- {wesktop-0.4.2 → wesktop-0.4.4}/.claude/settings.json +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.github/workflows/ci.yml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.github/workflows/publish.yml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.gitignore +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.claude/settings.json +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.github/workflows/ci.yml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.github/workflows/publish.yml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.gitignore +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/changes/unreleased.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/hooks/post-release.sh +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/lint/go.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/lint/npm.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/CHANGELOG.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/CLAUDE.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/LICENSE +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.1.0.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.1.0.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.1.1.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.1.1.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.2.0.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.2.0.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.2.1.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.2.1.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.0.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.0.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.1.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.1.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.2.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.2.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.0.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.0.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.1.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.1.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.2.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.2.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/unreleased.jsonl +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/config.json +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/hashes.json +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/hooks/post-release.sh +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/hooks/pre-checks.sh +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/hooks/pre-release.sh +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/lint/go.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/lint/npm.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/lint/python.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/unreleased.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.3.0.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.3.1.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.3.2.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.4.0.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.4.1.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.4.2.toml +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/version +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/CLAUDE.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/LICENSE +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/README.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/bin/cli.js +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/api.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/cli-config.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/cli-diagnose.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/gen-index.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/index.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-__main__.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-asgi.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-audit.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-auth.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-cli.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-config.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-desktop.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-dev.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-di.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-entries.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-error_log.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-features.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-logging.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-ask_user.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-deployment.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-filesystem.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-git.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-review.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-testing.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-middleware.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-sdui.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-server.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-sse.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-tasks.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-testing.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/package.json +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/__main__.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/asgi.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/audit.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/auth.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/cli.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/config.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/dev.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/di.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/entries.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/error_log.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/features.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/logging.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/__init__.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/ask_user.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/deployment.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/filesystem.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/git.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/review.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/testing.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/middleware.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/sdui.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/sse.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/tasks.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/testing.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/__init__.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/conftest.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_asgi.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_cli.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_desktop.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_dev.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_entries.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_followup.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_import.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase1.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase2.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase3.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase4.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase5.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase6.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase7.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase8.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_server.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_sse.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/.done/claudetimeline-migration-decisions.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/.done/codehome-ct-migration-plan.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/.done/pywebview-gi-venv-isolation.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_debug.py +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/01_finder_initial.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/01_initial.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/01_initial_loaded.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_bookmarks.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_console_page.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_dashboard.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_finder.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_manage.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_semantic_map.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_timeline.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_dashboard.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_dashboard_final.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_finder_docker.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_finder_search.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_finder_searched.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_manage.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_semantic_map.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_timeline.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/04_palette.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/initial_load.png +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/headless-crawl-diagnose.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/platform-vision.md +0 -0
- {wesktop-0.4.2 → wesktop-0.4.4}/todo/route-metadata-api.md +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
6a9c578efc84dad1ba896d5ff0187f37d445f76f
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{"commits":["2f8324d5c49da11628b1efb016ccfc9680747141"],"user_facing":false}
|
|
2
|
+
{"commits":["ad39a610a91db62ce6642c8cba5e68db4c7f8327"],"user_facing":true,"description":"**Fix.** `serve(foreground=False)` signal handler patch now survives thread start race condition. Previous fix was ineffective because the noop was restored before the daemon thread executed startup.","type":"fix"}
|
|
3
|
+
{"commits":["7b12c3c937671b5ed6b5c83a06fbb9cefd722e4a"],"user_facing":false}
|
|
4
|
+
{"commits":["ecd619cf4ce0539d740a8e4880811f9efea01920"],"user_facing":false}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{"commits":["2816ad70db01fb893b36bfd47832845ca890a6ed"],"user_facing":true,"description":"**Feature.** `ensure_gui_backend()` finds and loads system PyGObject in isolated venvs. Called automatically by `run()`.","type":"feature"}
|
|
2
|
+
{"commits":["d2eb0dcc8a74e8245486abbf03ae723fc3f39182"],"user_facing":false}
|
|
3
|
+
{"commits":["4ff7529eb66e6ecc20ccbf3402b522d56b20dbcb"],"user_facing":false}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"description": "3d5281d2398fdfd3797661b5a27cbbf1bcfb7672e0dc7593edfe887fde57c9c4"
|
|
13
13
|
},
|
|
14
14
|
"cli-index.md": {
|
|
15
|
-
"content": "
|
|
15
|
+
"content": "58ebe7861b04a95005551cf2c5a6fd07a918232f6cdff036bb167fef50b27b34",
|
|
16
16
|
"description": "eb7d99d2e046110a9cad0e28ea4636e1fa5269bf53c7727a6385e3962beedd53"
|
|
17
17
|
},
|
|
18
18
|
"en/api.md": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"description": "a845d0e6199b15cb03e8f3be172de7d59ea3076039bb2dee4af03a1178f1780f"
|
|
57
57
|
},
|
|
58
58
|
"src-wesktop-desktop.md": {
|
|
59
|
-
"content": "
|
|
59
|
+
"content": "a8692b5a12c2c71fca09c1e083072ee028c39fc1531b6b14e34b720819ec5b34",
|
|
60
60
|
"description": "e9839f6cb1270a463ba16d41b5afd1020800331b819d5ebb1f6a67cd21a83340"
|
|
61
61
|
},
|
|
62
62
|
"src-wesktop-dev.md": {
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
"description": "8f9b1d5078c4c523c5e346f478af2d571065613cb8e562a71d1c9c3ddeaccc73"
|
|
141
141
|
},
|
|
142
142
|
"src-wesktop.md": {
|
|
143
|
-
"content": "
|
|
143
|
+
"content": "09aa24170e09a40f949ae6d783a0d24dbb7c3d4347ca706c9e5af070e4c26cd4",
|
|
144
144
|
"description": "efa694eed1f01acd89f1ccd505c0810c9820e27c53c1857edbe635bd3c5e0474"
|
|
145
145
|
}
|
|
146
146
|
}
|
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
# Changelog
|
|
4
4
|
|
|
5
|
+
## 0.4.4
|
|
6
|
+
|
|
7
|
+
### Features
|
|
8
|
+
|
|
9
|
+
- **Feature.** `ensure_gui_backend()` finds and loads system PyGObject in isolated venvs. Called automatically by `run()`.
|
|
10
|
+
|
|
11
|
+
## 0.4.3
|
|
12
|
+
|
|
13
|
+
### Fixes
|
|
14
|
+
|
|
15
|
+
- **Fix.** `serve(foreground=False)` signal handler patch now survives thread start race condition. Previous fix was ineffective because the noop was restored before the daemon thread executed startup.
|
|
16
|
+
|
|
5
17
|
## 0.4.2
|
|
6
18
|
|
|
7
19
|
### Fixes
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.4.
|
|
2
|
+
"version": "0.4.3",
|
|
3
3
|
"language": "python",
|
|
4
4
|
"source": ["src/wesktop/"],
|
|
5
5
|
"docs": "docs/",
|
|
6
6
|
"output": "docs/_build/",
|
|
7
7
|
"base_url": "https://wesktop.smmh.dev",
|
|
8
|
-
"versions": [{"version": "0.4.
|
|
8
|
+
"versions": [{"version": "0.4.3", "indexed": true}],
|
|
9
9
|
"locales": [{"code": "en", "label": "English", "default": true}],
|
|
10
10
|
"deploy": {
|
|
11
11
|
"provider": "cloudflare-pages",
|
|
@@ -193,6 +193,8 @@ __all__ = [
|
|
|
193
193
|
"ServerStatus",
|
|
194
194
|
"run",
|
|
195
195
|
"dev",
|
|
196
|
+
# desktop
|
|
197
|
+
"ensure_gui_backend",
|
|
196
198
|
# features
|
|
197
199
|
"FeatureFlags",
|
|
198
200
|
# audit
|
|
@@ -263,6 +265,13 @@ __all__ = [
|
|
|
263
265
|
]
|
|
264
266
|
|
|
265
267
|
|
|
268
|
+
def ensure_gui_backend() -> bool:
|
|
269
|
+
"""Make pywebview's GUI backend importable in isolated venvs."""
|
|
270
|
+
from wesktop.desktop import ensure_gui_backend as _ensure_gui_backend
|
|
271
|
+
|
|
272
|
+
return _ensure_gui_backend()
|
|
273
|
+
|
|
274
|
+
|
|
266
275
|
def run(
|
|
267
276
|
target: str | Callable,
|
|
268
277
|
*,
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import glob
|
|
5
6
|
import logging
|
|
7
|
+
import os
|
|
6
8
|
import signal
|
|
9
|
+
import sys
|
|
7
10
|
import webbrowser
|
|
8
11
|
from pathlib import Path
|
|
9
12
|
from typing import Callable
|
|
@@ -11,6 +14,42 @@ from typing import Callable
|
|
|
11
14
|
log = logging.getLogger(__name__)
|
|
12
15
|
|
|
13
16
|
|
|
17
|
+
def ensure_gui_backend() -> bool:
|
|
18
|
+
"""Make pywebview's GUI backend importable in isolated venvs.
|
|
19
|
+
|
|
20
|
+
If gi (PyGObject) is not importable, searches common system
|
|
21
|
+
site-packages locations and adds the first one found to sys.path.
|
|
22
|
+
Returns True if a backend is available, False otherwise.
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
import gi # noqa: F401
|
|
26
|
+
|
|
27
|
+
return True
|
|
28
|
+
except ImportError:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
# gi not in venv -- search system site-packages
|
|
32
|
+
patterns = [
|
|
33
|
+
"/usr/lib64/python3.*/site-packages",
|
|
34
|
+
"/usr/lib/python3.*/site-packages",
|
|
35
|
+
"/usr/lib/python3/dist-packages", # Debian/Ubuntu
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
for pattern in patterns:
|
|
39
|
+
for path in sorted(glob.glob(pattern), reverse=True):
|
|
40
|
+
gi_path = os.path.join(path, "gi")
|
|
41
|
+
if os.path.isdir(gi_path) and path not in sys.path:
|
|
42
|
+
sys.path.insert(0, path)
|
|
43
|
+
try:
|
|
44
|
+
import gi # noqa: F401
|
|
45
|
+
|
|
46
|
+
return True
|
|
47
|
+
except ImportError:
|
|
48
|
+
sys.path.remove(path)
|
|
49
|
+
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
|
|
14
53
|
def _has_gui_backend() -> bool:
|
|
15
54
|
"""Probe whether pywebview can load a GUI backend (GTK or Qt).
|
|
16
55
|
|
|
@@ -107,6 +146,9 @@ def run(
|
|
|
107
146
|
reload=reload,
|
|
108
147
|
)
|
|
109
148
|
|
|
149
|
+
# Make system PyGObject visible in isolated venvs before importing webview
|
|
150
|
+
ensure_gui_backend()
|
|
151
|
+
|
|
110
152
|
# Late import so headless mode (serve) has no pywebview dependency
|
|
111
153
|
try:
|
|
112
154
|
import webview
|
|
@@ -278,15 +278,15 @@ def serve(
|
|
|
278
278
|
# daemon threads. Signal handling is unnecessary here -- the daemon
|
|
279
279
|
# thread dies when the main thread exits.
|
|
280
280
|
from granian import _signals
|
|
281
|
+
from granian.server import common as _granian_common
|
|
281
282
|
|
|
282
|
-
|
|
283
|
-
_signals.set_main_signals =
|
|
283
|
+
_noop = lambda *a, **kw: None
|
|
284
|
+
_signals.set_main_signals = _noop
|
|
285
|
+
_granian_common.set_main_signals = _noop
|
|
284
286
|
|
|
285
287
|
thread = threading.Thread(target=server.serve, daemon=True)
|
|
286
288
|
thread.start()
|
|
287
289
|
|
|
288
|
-
_signals.set_main_signals = _original
|
|
289
|
-
|
|
290
290
|
log.info("%s started in background on %s", name, url)
|
|
291
291
|
return url
|
|
292
292
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Tests for ensure_gui_backend() -- system PyGObject discovery for isolated venvs."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import builtins
|
|
6
|
+
import importlib
|
|
7
|
+
import os
|
|
8
|
+
import socket
|
|
9
|
+
import sys
|
|
10
|
+
from unittest.mock import MagicMock, patch
|
|
11
|
+
|
|
12
|
+
from wesktop.desktop import ensure_gui_backend
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _free_port() -> int:
|
|
16
|
+
"""Find an ephemeral port that is currently free."""
|
|
17
|
+
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
18
|
+
s.bind(("127.0.0.1", 0))
|
|
19
|
+
return s.getsockname()[1]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_ensure_gui_backend_gi_already_importable() -> None:
|
|
23
|
+
"""When gi is already importable, returns True without touching sys.path."""
|
|
24
|
+
mock_gi = MagicMock()
|
|
25
|
+
original_path = sys.path.copy()
|
|
26
|
+
|
|
27
|
+
with patch.dict(sys.modules, {"gi": mock_gi}):
|
|
28
|
+
result = ensure_gui_backend()
|
|
29
|
+
|
|
30
|
+
assert result is True
|
|
31
|
+
assert sys.path == original_path
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_ensure_gui_backend_finds_system_gi() -> None:
|
|
35
|
+
"""When gi is not in the venv, finds it via system site-packages glob."""
|
|
36
|
+
original_import = builtins.__import__
|
|
37
|
+
call_count = 0
|
|
38
|
+
|
|
39
|
+
def fake_import(name, *args, **kwargs):
|
|
40
|
+
nonlocal call_count
|
|
41
|
+
if name == "gi":
|
|
42
|
+
call_count += 1
|
|
43
|
+
if call_count == 1:
|
|
44
|
+
# First attempt (venv) fails
|
|
45
|
+
raise ImportError("no gi in venv")
|
|
46
|
+
# Second attempt (after sys.path modification) succeeds
|
|
47
|
+
return MagicMock()
|
|
48
|
+
return original_import(name, *args, **kwargs)
|
|
49
|
+
|
|
50
|
+
fake_system_path = "/usr/lib64/python3.12/site-packages"
|
|
51
|
+
|
|
52
|
+
with (
|
|
53
|
+
patch("builtins.__import__", side_effect=fake_import),
|
|
54
|
+
patch("wesktop.desktop.glob.glob", return_value=[fake_system_path]),
|
|
55
|
+
patch("os.path.isdir", return_value=True),
|
|
56
|
+
):
|
|
57
|
+
# Ensure our fake path is not already in sys.path
|
|
58
|
+
original_path = sys.path.copy()
|
|
59
|
+
try:
|
|
60
|
+
result = ensure_gui_backend()
|
|
61
|
+
assert result is True
|
|
62
|
+
assert fake_system_path in sys.path
|
|
63
|
+
finally:
|
|
64
|
+
# Clean up sys.path
|
|
65
|
+
if fake_system_path in sys.path:
|
|
66
|
+
sys.path.remove(fake_system_path)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_ensure_gui_backend_returns_false_when_gi_nowhere() -> None:
|
|
70
|
+
"""When gi is not available anywhere, returns False."""
|
|
71
|
+
original_import = builtins.__import__
|
|
72
|
+
|
|
73
|
+
def fake_import(name, *args, **kwargs):
|
|
74
|
+
if name == "gi":
|
|
75
|
+
raise ImportError("no gi anywhere")
|
|
76
|
+
return original_import(name, *args, **kwargs)
|
|
77
|
+
|
|
78
|
+
with (
|
|
79
|
+
patch("builtins.__import__", side_effect=fake_import),
|
|
80
|
+
patch("wesktop.desktop.glob.glob", return_value=[]),
|
|
81
|
+
):
|
|
82
|
+
result = ensure_gui_backend()
|
|
83
|
+
|
|
84
|
+
assert result is False
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_ensure_gui_backend_skips_paths_without_gi_dir() -> None:
|
|
88
|
+
"""Paths that exist but don't contain a gi/ directory are skipped."""
|
|
89
|
+
original_import = builtins.__import__
|
|
90
|
+
|
|
91
|
+
def fake_import(name, *args, **kwargs):
|
|
92
|
+
if name == "gi":
|
|
93
|
+
raise ImportError("no gi")
|
|
94
|
+
return original_import(name, *args, **kwargs)
|
|
95
|
+
|
|
96
|
+
fake_path = "/usr/lib64/python3.12/site-packages"
|
|
97
|
+
|
|
98
|
+
with (
|
|
99
|
+
patch("builtins.__import__", side_effect=fake_import),
|
|
100
|
+
patch("wesktop.desktop.glob.glob", return_value=[fake_path]),
|
|
101
|
+
patch("os.path.isdir", return_value=False),
|
|
102
|
+
):
|
|
103
|
+
result = ensure_gui_backend()
|
|
104
|
+
|
|
105
|
+
assert result is False
|
|
106
|
+
assert fake_path not in sys.path
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_run_calls_ensure_gui_backend_before_webview() -> None:
|
|
110
|
+
"""run() calls ensure_gui_backend() before importing webview."""
|
|
111
|
+
call_order: list[str] = []
|
|
112
|
+
|
|
113
|
+
original_ensure = ensure_gui_backend
|
|
114
|
+
|
|
115
|
+
def tracking_ensure():
|
|
116
|
+
call_order.append("ensure_gui_backend")
|
|
117
|
+
return True
|
|
118
|
+
|
|
119
|
+
port = _free_port()
|
|
120
|
+
|
|
121
|
+
with (
|
|
122
|
+
patch("wesktop.desktop.ensure_gui_backend", side_effect=tracking_ensure),
|
|
123
|
+
patch("wesktop.desktop._has_gui_backend", return_value=True),
|
|
124
|
+
patch("webview.start"),
|
|
125
|
+
patch("webview.create_window"),
|
|
126
|
+
patch("wesktop.server.serve", return_value=f"http://127.0.0.1:{port}"),
|
|
127
|
+
):
|
|
128
|
+
from wesktop.desktop import run
|
|
129
|
+
|
|
130
|
+
run("myapp:app", host="127.0.0.1", port=port)
|
|
131
|
+
|
|
132
|
+
assert "ensure_gui_backend" in call_order
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_ensure_gui_backend_removes_path_on_failed_import() -> None:
|
|
136
|
+
"""If adding a system path still doesn't make gi importable, the path is removed."""
|
|
137
|
+
original_import = builtins.__import__
|
|
138
|
+
|
|
139
|
+
def fake_import(name, *args, **kwargs):
|
|
140
|
+
if name == "gi":
|
|
141
|
+
raise ImportError("no gi")
|
|
142
|
+
return original_import(name, *args, **kwargs)
|
|
143
|
+
|
|
144
|
+
fake_path = "/usr/lib64/python3.12/site-packages"
|
|
145
|
+
original_sys_path = sys.path.copy()
|
|
146
|
+
|
|
147
|
+
with (
|
|
148
|
+
patch("builtins.__import__", side_effect=fake_import),
|
|
149
|
+
patch("wesktop.desktop.glob.glob", return_value=[fake_path]),
|
|
150
|
+
patch("os.path.isdir", return_value=True),
|
|
151
|
+
):
|
|
152
|
+
result = ensure_gui_backend()
|
|
153
|
+
|
|
154
|
+
assert result is False
|
|
155
|
+
assert fake_path not in sys.path
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
dadb8c9c66ead3a0767827bbb13dd9dca62321a1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|