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.
Files changed (184) hide show
  1. wesktop-0.4.4/.rlsbl/changes/.validated +1 -0
  2. wesktop-0.4.4/.rlsbl/changes/0.4.3.jsonl +4 -0
  3. wesktop-0.4.4/.rlsbl/changes/0.4.3.md +5 -0
  4. wesktop-0.4.4/.rlsbl/changes/0.4.4.jsonl +3 -0
  5. wesktop-0.4.4/.rlsbl/changes/0.4.4.md +5 -0
  6. wesktop-0.4.4/.rlsbl/releases/v0.4.3.toml +3 -0
  7. wesktop-0.4.4/.rlsbl/releases/v0.4.4.toml +3 -0
  8. {wesktop-0.4.2 → wesktop-0.4.4}/.selfdoc/hashes/hashes.json +3 -3
  9. {wesktop-0.4.2 → wesktop-0.4.4}/.strictcli/schema.json +1 -1
  10. {wesktop-0.4.2 → wesktop-0.4.4}/CHANGELOG.md +12 -0
  11. {wesktop-0.4.2 → wesktop-0.4.4}/PKG-INFO +1 -1
  12. {wesktop-0.4.2 → wesktop-0.4.4}/docs/cli-index.md +1 -1
  13. {wesktop-0.4.2 → wesktop-0.4.4}/pyproject.toml +1 -1
  14. {wesktop-0.4.2 → wesktop-0.4.4}/selfdoc.json +2 -2
  15. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/__init__.py +9 -0
  16. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/desktop.py +42 -0
  17. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/server.py +4 -4
  18. wesktop-0.4.4/tests/test_gui_backend.py +155 -0
  19. {wesktop-0.4.2 → wesktop-0.4.4}/uv.lock +1 -1
  20. wesktop-0.4.2/.rlsbl/changes/.validated +0 -1
  21. {wesktop-0.4.2 → wesktop-0.4.4}/.claude/settings.json +0 -0
  22. {wesktop-0.4.2 → wesktop-0.4.4}/.github/workflows/ci.yml +0 -0
  23. {wesktop-0.4.2 → wesktop-0.4.4}/.github/workflows/publish.yml +0 -0
  24. {wesktop-0.4.2 → wesktop-0.4.4}/.gitignore +0 -0
  25. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.claude/settings.json +0 -0
  26. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.github/workflows/ci.yml +0 -0
  27. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.github/workflows/publish.yml +0 -0
  28. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.gitignore +0 -0
  29. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/changes/unreleased.jsonl +0 -0
  30. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/hooks/post-release.sh +0 -0
  31. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +0 -0
  32. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +0 -0
  33. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/lint/go.toml +0 -0
  34. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/lint/npm.toml +0 -0
  35. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/.rlsbl/lint/python.toml +0 -0
  36. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/CHANGELOG.md +0 -0
  37. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/CLAUDE.md +0 -0
  38. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/bases/LICENSE +0 -0
  39. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.1.0.jsonl +0 -0
  40. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.1.0.md +0 -0
  41. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.1.1.jsonl +0 -0
  42. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.1.1.md +0 -0
  43. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.2.0.jsonl +0 -0
  44. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.2.0.md +0 -0
  45. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.2.1.jsonl +0 -0
  46. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.2.1.md +0 -0
  47. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.0.jsonl +0 -0
  48. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.0.md +0 -0
  49. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.1.jsonl +0 -0
  50. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.1.md +0 -0
  51. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.2.jsonl +0 -0
  52. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.3.2.md +0 -0
  53. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.0.jsonl +0 -0
  54. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.0.md +0 -0
  55. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.1.jsonl +0 -0
  56. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.1.md +0 -0
  57. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.2.jsonl +0 -0
  58. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/0.4.2.md +0 -0
  59. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/changes/unreleased.jsonl +0 -0
  60. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/config.json +0 -0
  61. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/hashes.json +0 -0
  62. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/hooks/post-release.sh +0 -0
  63. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/hooks/pre-checks.sh +0 -0
  64. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/hooks/pre-release.sh +0 -0
  65. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/lint/go.toml +0 -0
  66. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/lint/npm.toml +0 -0
  67. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/lint/python.toml +0 -0
  68. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/unreleased.toml +0 -0
  69. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.3.0.toml +0 -0
  70. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.3.1.toml +0 -0
  71. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.3.2.toml +0 -0
  72. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.4.0.toml +0 -0
  73. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.4.1.toml +0 -0
  74. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/releases/v0.4.2.toml +0 -0
  75. {wesktop-0.4.2 → wesktop-0.4.4}/.rlsbl/version +0 -0
  76. {wesktop-0.4.2 → wesktop-0.4.4}/CLAUDE.md +0 -0
  77. {wesktop-0.4.2 → wesktop-0.4.4}/LICENSE +0 -0
  78. {wesktop-0.4.2 → wesktop-0.4.4}/README.md +0 -0
  79. {wesktop-0.4.2 → wesktop-0.4.4}/bin/cli.js +0 -0
  80. {wesktop-0.4.2 → wesktop-0.4.4}/docs/api.md +0 -0
  81. {wesktop-0.4.2 → wesktop-0.4.4}/docs/cli-config.md +0 -0
  82. {wesktop-0.4.2 → wesktop-0.4.4}/docs/cli-diagnose.md +0 -0
  83. {wesktop-0.4.2 → wesktop-0.4.4}/docs/gen-index.md +0 -0
  84. {wesktop-0.4.2 → wesktop-0.4.4}/docs/index.md +0 -0
  85. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-__main__.md +0 -0
  86. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-asgi.md +0 -0
  87. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-audit.md +0 -0
  88. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-auth.md +0 -0
  89. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-cli.md +0 -0
  90. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-config.md +0 -0
  91. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-desktop.md +0 -0
  92. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-dev.md +0 -0
  93. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-di.md +0 -0
  94. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-entries.md +0 -0
  95. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-error_log.md +0 -0
  96. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-features.md +0 -0
  97. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-logging.md +0 -0
  98. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp.md +0 -0
  99. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-ask_user.md +0 -0
  100. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-deployment.md +0 -0
  101. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-filesystem.md +0 -0
  102. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-git.md +0 -0
  103. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-review.md +0 -0
  104. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools-testing.md +0 -0
  105. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-mcp_tools.md +0 -0
  106. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-middleware.md +0 -0
  107. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-sdui.md +0 -0
  108. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-server.md +0 -0
  109. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-sse.md +0 -0
  110. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-tasks.md +0 -0
  111. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop-testing.md +0 -0
  112. {wesktop-0.4.2 → wesktop-0.4.4}/docs/src-wesktop.md +0 -0
  113. {wesktop-0.4.2 → wesktop-0.4.4}/package.json +0 -0
  114. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/__main__.py +0 -0
  115. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/asgi.py +0 -0
  116. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/audit.py +0 -0
  117. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/auth.py +0 -0
  118. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/cli.py +0 -0
  119. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/config.py +0 -0
  120. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/dev.py +0 -0
  121. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/di.py +0 -0
  122. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/entries.py +0 -0
  123. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/error_log.py +0 -0
  124. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/features.py +0 -0
  125. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/logging.py +0 -0
  126. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp.py +0 -0
  127. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/__init__.py +0 -0
  128. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/ask_user.py +0 -0
  129. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/deployment.py +0 -0
  130. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/filesystem.py +0 -0
  131. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/git.py +0 -0
  132. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/review.py +0 -0
  133. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/mcp_tools/testing.py +0 -0
  134. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/middleware.py +0 -0
  135. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/sdui.py +0 -0
  136. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/sse.py +0 -0
  137. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/tasks.py +0 -0
  138. {wesktop-0.4.2 → wesktop-0.4.4}/src/wesktop/testing.py +0 -0
  139. {wesktop-0.4.2 → wesktop-0.4.4}/tests/__init__.py +0 -0
  140. {wesktop-0.4.2 → wesktop-0.4.4}/tests/conftest.py +0 -0
  141. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_asgi.py +0 -0
  142. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_cli.py +0 -0
  143. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_desktop.py +0 -0
  144. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_dev.py +0 -0
  145. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_entries.py +0 -0
  146. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_followup.py +0 -0
  147. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_import.py +0 -0
  148. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase1.py +0 -0
  149. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase2.py +0 -0
  150. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase3.py +0 -0
  151. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase4.py +0 -0
  152. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase5.py +0 -0
  153. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase6.py +0 -0
  154. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase7.py +0 -0
  155. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_phase8.py +0 -0
  156. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_server.py +0 -0
  157. {wesktop-0.4.2 → wesktop-0.4.4}/tests/test_sse.py +0 -0
  158. {wesktop-0.4.2 → wesktop-0.4.4}/todo/.done/claudetimeline-migration-decisions.md +0 -0
  159. {wesktop-0.4.2 → wesktop-0.4.4}/todo/.done/codehome-ct-migration-plan.md +0 -0
  160. {wesktop-0.4.2 → wesktop-0.4.4}/todo/.done/pywebview-gi-venv-isolation.md +0 -0
  161. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_debug.py +0 -0
  162. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/01_finder_initial.png +0 -0
  163. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/01_initial.png +0 -0
  164. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/01_initial_loaded.png +0 -0
  165. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_bookmarks.png +0 -0
  166. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_console_page.png +0 -0
  167. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_dashboard.png +0 -0
  168. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_finder.png +0 -0
  169. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_manage.png +0 -0
  170. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_semantic_map.png +0 -0
  171. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/02_timeline.png +0 -0
  172. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_dashboard.png +0 -0
  173. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_dashboard_final.png +0 -0
  174. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_finder_docker.png +0 -0
  175. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_finder_search.png +0 -0
  176. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_finder_searched.png +0 -0
  177. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_manage.png +0 -0
  178. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_semantic_map.png +0 -0
  179. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/03_timeline.png +0 -0
  180. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/04_palette.png +0 -0
  181. {wesktop-0.4.2 → wesktop-0.4.4}/todo/crawl_screenshots/initial_load.png +0 -0
  182. {wesktop-0.4.2 → wesktop-0.4.4}/todo/headless-crawl-diagnose.md +0 -0
  183. {wesktop-0.4.2 → wesktop-0.4.4}/todo/platform-vision.md +0 -0
  184. {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,5 @@
1
+ ## 0.4.3
2
+
3
+ ### Fixes
4
+
5
+ - **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.
@@ -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}
@@ -0,0 +1,5 @@
1
+ ## 0.4.4
2
+
3
+ ### Features
4
+
5
+ - **Feature.** `ensure_gui_backend()` finds and loads system PyGObject in isolated venvs. Called automatically by `run()`.
@@ -0,0 +1,3 @@
1
+ bump = "patch"
2
+ include = ["pypi"]
3
+ exclude = []
@@ -0,0 +1,3 @@
1
+ bump = "patch"
2
+ include = ["pypi"]
3
+ exclude = []
@@ -12,7 +12,7 @@
12
12
  "description": "3d5281d2398fdfd3797661b5a27cbbf1bcfb7672e0dc7593edfe887fde57c9c4"
13
13
  },
14
14
  "cli-index.md": {
15
- "content": "4b621158082758133bec3a20d1a8de97613128dc90d4d3359e50b6a97086c4da",
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": "5bf4f1a6264195aaac494e6cf89e8cf90b218a5d259f253528e27d5af643d2d3",
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": "2aa767b12b24bc8eb8dbb1d22d288780e2c742ef74db97494665f840cbb1709d",
143
+ "content": "09aa24170e09a40f949ae6d783a0d24dbb7c3d4347ca706c9e5af070e4c26cd4",
144
144
  "description": "efa694eed1f01acd89f1ccd505c0810c9820e27c53c1857edbe635bd3c5e0474"
145
145
  }
146
146
  }
@@ -33,7 +33,7 @@
33
33
  }
34
34
  },
35
35
  "name": "wesktop",
36
- "version": "0.4.1",
36
+ "version": "0.4.3",
37
37
  "help": "A Python framework for building web-based desktop applications",
38
38
  "config": true,
39
39
  "commands": {
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wesktop
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: A Python framework for building web-based desktop applications
5
5
  Author-email: smm-h <smmh72@gmail.com>
6
6
  License-Expression: MIT
@@ -12,7 +12,7 @@ order: 91
12
12
 
13
13
  A Python framework for building web-based desktop applications
14
14
 
15
- Version: 0.4.1
15
+ Version: 0.4.3
16
16
 
17
17
  ## Commands
18
18
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "wesktop"
3
- version = "0.4.2"
3
+ version = "0.4.4"
4
4
  description = "A Python framework for building web-based desktop applications"
5
5
  requires-python = ">=3.11"
6
6
  license = "MIT"
@@ -1,11 +1,11 @@
1
1
  {
2
- "version": "0.4.1",
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.1", "indexed": true}],
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
- _original = _signals.set_main_signals
283
- _signals.set_main_signals = lambda *a, **kw: None
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
@@ -826,7 +826,7 @@ wheels = [
826
826
 
827
827
  [[package]]
828
828
  name = "wesktop"
829
- version = "0.4.2"
829
+ version = "0.4.4"
830
830
  source = { editable = "." }
831
831
  dependencies = [
832
832
  { name = "bcrypt" },
@@ -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