wesktop 0.1.1__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 (59) hide show
  1. wesktop-0.1.1/.claude/settings.json +3 -0
  2. wesktop-0.1.1/.github/workflows/ci.yml +20 -0
  3. wesktop-0.1.1/.github/workflows/publish.yml +18 -0
  4. wesktop-0.1.1/.gitignore +24 -0
  5. wesktop-0.1.1/.rlsbl/bases/.claude/settings.json +3 -0
  6. wesktop-0.1.1/.rlsbl/bases/.github/workflows/ci.yml +21 -0
  7. wesktop-0.1.1/.rlsbl/bases/.github/workflows/publish.yml +18 -0
  8. wesktop-0.1.1/.rlsbl/bases/.gitignore +15 -0
  9. wesktop-0.1.1/.rlsbl/bases/.rlsbl/changes/unreleased.jsonl +0 -0
  10. wesktop-0.1.1/.rlsbl/bases/.rlsbl/hooks/post-release.sh +8 -0
  11. wesktop-0.1.1/.rlsbl/bases/.rlsbl/hooks/pre-checks.sh +5 -0
  12. wesktop-0.1.1/.rlsbl/bases/.rlsbl/hooks/pre-release.sh +8 -0
  13. wesktop-0.1.1/.rlsbl/bases/.rlsbl/lint/go.toml +17 -0
  14. wesktop-0.1.1/.rlsbl/bases/.rlsbl/lint/npm.toml +19 -0
  15. wesktop-0.1.1/.rlsbl/bases/.rlsbl/lint/python.toml +25 -0
  16. wesktop-0.1.1/.rlsbl/bases/CHANGELOG.md +5 -0
  17. wesktop-0.1.1/.rlsbl/bases/CLAUDE.md +20 -0
  18. wesktop-0.1.1/.rlsbl/bases/LICENSE +21 -0
  19. wesktop-0.1.1/.rlsbl/changes/.validated +1 -0
  20. wesktop-0.1.1/.rlsbl/changes/0.1.0.jsonl +11 -0
  21. wesktop-0.1.1/.rlsbl/changes/0.1.0.md +7 -0
  22. wesktop-0.1.1/.rlsbl/changes/0.1.1.jsonl +1 -0
  23. wesktop-0.1.1/.rlsbl/changes/0.1.1.md +3 -0
  24. wesktop-0.1.1/.rlsbl/changes/unreleased.jsonl +0 -0
  25. wesktop-0.1.1/.rlsbl/config.json +5 -0
  26. wesktop-0.1.1/.rlsbl/hashes.json +15 -0
  27. wesktop-0.1.1/.rlsbl/hooks/post-release.sh +8 -0
  28. wesktop-0.1.1/.rlsbl/hooks/pre-checks.sh +5 -0
  29. wesktop-0.1.1/.rlsbl/hooks/pre-release.sh +8 -0
  30. wesktop-0.1.1/.rlsbl/lint/go.toml +17 -0
  31. wesktop-0.1.1/.rlsbl/lint/npm.toml +19 -0
  32. wesktop-0.1.1/.rlsbl/lint/python.toml +25 -0
  33. wesktop-0.1.1/.rlsbl/version +1 -0
  34. wesktop-0.1.1/.selfdoc/hashes/hashes.json +10 -0
  35. wesktop-0.1.1/CHANGELOG.md +15 -0
  36. wesktop-0.1.1/CLAUDE.md +77 -0
  37. wesktop-0.1.1/LICENSE +21 -0
  38. wesktop-0.1.1/PKG-INFO +12 -0
  39. wesktop-0.1.1/README.md +1 -0
  40. wesktop-0.1.1/bin/cli.js +16 -0
  41. wesktop-0.1.1/docs/api.md +138 -0
  42. wesktop-0.1.1/docs/index.md +97 -0
  43. wesktop-0.1.1/package.json +29 -0
  44. wesktop-0.1.1/pyproject.toml +26 -0
  45. wesktop-0.1.1/selfdoc.json +7 -0
  46. wesktop-0.1.1/src/wesktop/__init__.py +86 -0
  47. wesktop-0.1.1/src/wesktop/asgi.py +404 -0
  48. wesktop-0.1.1/src/wesktop/desktop.py +39 -0
  49. wesktop-0.1.1/src/wesktop/entries.py +297 -0
  50. wesktop-0.1.1/src/wesktop/server.py +167 -0
  51. wesktop-0.1.1/src/wesktop/sse.py +119 -0
  52. wesktop-0.1.1/tests/__init__.py +0 -0
  53. wesktop-0.1.1/tests/test_asgi.py +841 -0
  54. wesktop-0.1.1/tests/test_desktop.py +108 -0
  55. wesktop-0.1.1/tests/test_entries.py +193 -0
  56. wesktop-0.1.1/tests/test_import.py +2 -0
  57. wesktop-0.1.1/tests/test_server.py +87 -0
  58. wesktop-0.1.1/tests/test_sse.py +261 -0
  59. wesktop-0.1.1/uv.lock +515 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ "hooks": {}
3
+ }
@@ -0,0 +1,20 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - uses: astral-sh/setup-uv@v7
18
+ - run: uv python install ${{ matrix.python-version }}
19
+ - run: uv sync
20
+ - run: uv run python -c "import wesktop"
@@ -0,0 +1,18 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write
10
+
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v6
16
+ - uses: astral-sh/setup-uv@v7
17
+ - run: uv build
18
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,24 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .venv/
8
+ .pytest_cache/
9
+ *.egg
10
+ .uv/
11
+
12
+ node_modules/
13
+ *.pyc
14
+ *.log
15
+ .DS_Store
16
+ coverage/
17
+ .rlsbl-notes-*.tmp
18
+ .rlsbl/lock
19
+ .credentials.json
20
+ .*-cache.json
21
+ .env
22
+ .env.local
23
+ *.local-only
24
+ docs/_build/
@@ -0,0 +1,3 @@
1
+ {
2
+ "hooks": {}
3
+ }
@@ -0,0 +1,21 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ # requires-python: >= {{pypi.minRequiredPython}}
15
+ python-version: ["3.12", "3.13", "3.14"]
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+ - uses: astral-sh/setup-uv@v7
19
+ - run: uv python install ${{ matrix.python-version }}
20
+ - run: uv sync
21
+ - run: uv run python -c "import wesktop"
@@ -0,0 +1,18 @@
1
+ name: Publish
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ permissions:
8
+ contents: read
9
+ id-token: write
10
+
11
+ jobs:
12
+ publish:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v6
16
+ - uses: astral-sh/setup-uv@v7
17
+ - run: uv build
18
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,15 @@
1
+ node_modules/
2
+ __pycache__/
3
+ *.pyc
4
+ *.log
5
+ .DS_Store
6
+ coverage/
7
+ dist/
8
+ *.egg-info/
9
+ .rlsbl-notes-*.tmp
10
+ .rlsbl/lock
11
+ .credentials.json
12
+ .*-cache.json
13
+ .env
14
+ .env.local
15
+ *.local-only
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ # Post-release hook. Runs after a successful release (non-fatal).
3
+ # Environment: RLSBL_VERSION is set to the released version.
4
+ # Customize this for your project (e.g., local install, deploy, notify).
5
+
6
+ set -euo pipefail
7
+
8
+ echo "Post-release: v$RLSBL_VERSION"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ # This hook runs BEFORE built-in pre-release checks (tests, lint).
4
+ # Use it for setup tasks: starting services, setting env vars, etc.
5
+ # Built-in checks run after this hook. Custom validation goes in pre-release.sh.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ # Project-specific pre-release checks.
4
+ # Built-in checks (tests, lint) run automatically before this hook.
5
+ # Add custom validation here, e.g.:
6
+ # - Check for uncommitted documentation
7
+ # - Verify external service connectivity
8
+ # - Run integration tests not covered by the test suite
@@ -0,0 +1,17 @@
1
+ [forbidden-imports]
2
+ modules = [
3
+ "net/http",
4
+ "github.com/spf13/cobra",
5
+ "github.com/urfave/cli",
6
+ ]
7
+
8
+ [stdout]
9
+ enabled = true
10
+ ignore = []
11
+
12
+ [entry-point]
13
+ enabled = true
14
+ ignore = []
15
+
16
+ [files]
17
+ exclude = []
@@ -0,0 +1,19 @@
1
+ [forbidden-imports]
2
+ modules = [
3
+ "express",
4
+ "koa",
5
+ "hono",
6
+ "commander",
7
+ "yargs",
8
+ ]
9
+
10
+ [stdout]
11
+ enabled = true
12
+ ignore = []
13
+
14
+ [entry-point]
15
+ enabled = true
16
+ ignore = []
17
+
18
+ [files]
19
+ exclude = []
@@ -0,0 +1,25 @@
1
+ [forbidden-imports]
2
+ modules = [
3
+ "argparse",
4
+ "click",
5
+ "typer",
6
+ "flask",
7
+ "fastapi",
8
+ "django",
9
+ "uvicorn",
10
+ "granian",
11
+ "starlette",
12
+ "tornado",
13
+ "bottle",
14
+ ]
15
+
16
+ [stdout]
17
+ enabled = true
18
+ ignore = []
19
+
20
+ [entry-point]
21
+ enabled = true
22
+ ignore = []
23
+
24
+ [files]
25
+ exclude = []
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial release
@@ -0,0 +1,20 @@
1
+ # wesktop
2
+
3
+ ## Release workflow
4
+
5
+ This project uses [rlsbl](https://github.com/smm-h/rlsbl) for release orchestration.
6
+
7
+ - Update CHANGELOG.md with a `## X.Y.Z` entry describing changes
8
+ - Run `rlsbl release [patch|minor|major]` to bump version and create a GitHub Release
9
+ - CI handles publishing automatically via the publish workflow
10
+ - Never publish manually — always use `rlsbl release`
11
+ - Configure Trusted Publishing on pypi.org for automated PyPI releases
12
+ - Use `rlsbl release --dry-run` to preview a release without making changes
13
+
14
+ ## Conventions
15
+
16
+ - No tokens or secrets in command-line arguments (use env vars or config files)
17
+ - All file writes to shared state should be atomic (write to tmp, then rename)
18
+ - External calls (APIs, CLI tools) must have timeouts and graceful fallbacks
19
+ - Use `npm link` (npm) or `uv pip install -e .` (Python) for local development
20
+ - CI runs smoke tests on every push; manual testing for UI/UX changes
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 smm-h
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ 3540d09fa802bb88537ce076800b9b0b2801ef79
@@ -0,0 +1,11 @@
1
+ {"commits":["d00e0a314abbc6720b06864ad8a4014e653e9a33","53b83ce974becab9c7d3d20debd682d3abf9297d","0949e1ab49d7d24616f8173cadb6a646cd04bfba"],"user_facing":false}
2
+ {"commits":["97e0ad7fe9f96f3a815b3f6138cd8dbd57c365df","3da18c78e2074da14a555ef30d8656b4b2c38ba2","4fe003a36934d1b53642c549a9e68936a279053f"],"user_facing":false}
3
+ {"commits":["128679ea96ada6f978bf01aa78cab828694e503e","a4b37f708c9822b0658d592010a1b0bbde5df001","b12f712986de331350ba98daf2ec54db58fddeef"],"user_facing":true,"description":"**Initial release.** ASGI router, SSE broadcaster, and extracted server lifecycle.","type":"feature"}
4
+ {"commits":["e6f466e1857c268c80f66a150d27b1e8fc0a064f","48d0d63b608e0f58ac817a4c322be5e28132f51b","e03976716edb4de95ba55122e0d00ed4ae8807de","ceb985a298b9d8b0510f6b49b6505203870450b9"],"user_facing":true,"description":"**Desktop integration.** pywebview, desktop entry creation, and public API exports.","type":"feature"}
5
+ {"commits":["679c1bfcbc6db58d7143a807de6fa9ee4921d979","797fc29feda110bc8893439e2652d1118ef8cfa5"],"user_facing":false}
6
+ {"commits":["a453cf8830b6d86e5d9bf32d540e069a5432adaf"],"user_facing":false}
7
+ {"commits":["2ad42e3dc9e0153ed992771f48b60f5255d8db1b"],"user_facing":false}
8
+ {"commits":["1e2274bfe5ae37f4b1649ee8a9c0e6228539baff"],"user_facing":false}
9
+ {"commits":["7c4c6b89d705bad412bd89037cb61715a0932585","01edd549dd66b46d2907a8d3f36d44f969de71c2"],"user_facing":true,"description":"**npm shim.** Install via `npm i -g @smmh/wesktop` for a global `wesktop` CLI that delegates to Python.","type":"feature"}
10
+ {"commits":["2c16194bae86e9398bd7083561c2aa53c37402a3"],"user_facing":false}
11
+ {"commits":["8d7e8fd69b946abd58bebb51a60d55c0778d5366"],"user_facing":false}
@@ -0,0 +1,7 @@
1
+ ## 0.1.0
2
+
3
+ ### Features
4
+
5
+ - **Initial release.** ASGI router, SSE broadcaster, and extracted server lifecycle.
6
+ - **Desktop integration.** pywebview, desktop entry creation, and public API exports.
7
+ - **npm shim.** Install via `npm i -g @smmh/wesktop` for a global `wesktop` CLI that delegates to Python.
@@ -0,0 +1 @@
1
+ {"commits":["e814207a9b87c4b742c70c2adf59246c38fdf804"],"user_facing":false}
@@ -0,0 +1,3 @@
1
+ ## 0.1.1
2
+
3
+ - No user-facing changes.
File without changes
@@ -0,0 +1,5 @@
1
+ {
2
+ "targets": [
3
+ "pypi"
4
+ ]
5
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ ".github/workflows/ci.yml": "c1c40dd67db0a91b305570028a0579ec03c747da0dd187f31de80a099b6fb516",
3
+ ".github/workflows/publish.yml": "55e2dba57d17218c2887ba90051d31aee15067cc0b69c8adebc2deab07aef76b",
4
+ "CHANGELOG.md": "7a6ec46141007c18090a0ff325e2dd68cd51588c4e0b5af7b580365e5eaaca85",
5
+ ".gitignore": "ce5391c115e249532a3bc27098546bb1ff2a32410b53b0d59ad7b54e765c0a04",
6
+ "LICENSE": "e958892abc1dd5d991fca6d528e695d335f25773d373d2e0be5f4a9507d8f8d5",
7
+ ".rlsbl/hooks/pre-checks.sh": "60c57d4563263124530e6f33aca77550ea2161ad3b33b3ba6ad1c03fea9eb6e2",
8
+ ".rlsbl/hooks/pre-release.sh": "b2dc17c243d0b1a2c1e23c1c25db6f94878596df81dc76c41b32d95f740a7d44",
9
+ ".rlsbl/hooks/post-release.sh": "b455f8511e0b2655509ecf5dcb0ab7da5bb7c961f47910ff8e00cab5bd51f833",
10
+ ".claude/settings.json": "78922a784ee78e9e50587e93628cd3b9d4dfbe49087adc4514e6781cea38cbb9",
11
+ ".rlsbl/lint/python.toml": "9cfbcef2e010d5fad243437bf2fd8df54a1fae70ee0762f9d11da70fe207501c",
12
+ ".rlsbl/lint/go.toml": "807d5cf760baad6d571a5bfd43cb163efe356f7462ba9f1918d72de45f62339f",
13
+ ".rlsbl/lint/npm.toml": "baa36014e65923d8cc5c8b83c77fa651da79351627570476402da66840027645",
14
+ ".rlsbl/changes/unreleased.jsonl": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
15
+ }
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ # Post-release hook. Runs after a successful release (non-fatal).
3
+ # Environment: RLSBL_VERSION is set to the released version.
4
+ # Customize this for your project (e.g., local install, deploy, notify).
5
+
6
+ set -euo pipefail
7
+
8
+ echo "Post-release: v$RLSBL_VERSION"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ # This hook runs BEFORE built-in pre-release checks (tests, lint).
4
+ # Use it for setup tasks: starting services, setting env vars, etc.
5
+ # Built-in checks run after this hook. Custom validation goes in pre-release.sh.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ # Project-specific pre-release checks.
4
+ # Built-in checks (tests, lint) run automatically before this hook.
5
+ # Add custom validation here, e.g.:
6
+ # - Check for uncommitted documentation
7
+ # - Verify external service connectivity
8
+ # - Run integration tests not covered by the test suite
@@ -0,0 +1,17 @@
1
+ [forbidden-imports]
2
+ modules = [
3
+ "net/http",
4
+ "github.com/spf13/cobra",
5
+ "github.com/urfave/cli",
6
+ ]
7
+
8
+ [stdout]
9
+ enabled = true
10
+ ignore = []
11
+
12
+ [entry-point]
13
+ enabled = true
14
+ ignore = []
15
+
16
+ [files]
17
+ exclude = []
@@ -0,0 +1,19 @@
1
+ [forbidden-imports]
2
+ modules = [
3
+ "express",
4
+ "koa",
5
+ "hono",
6
+ "commander",
7
+ "yargs",
8
+ ]
9
+
10
+ [stdout]
11
+ enabled = true
12
+ ignore = []
13
+
14
+ [entry-point]
15
+ enabled = true
16
+ ignore = []
17
+
18
+ [files]
19
+ exclude = []
@@ -0,0 +1,25 @@
1
+ [forbidden-imports]
2
+ modules = [
3
+ "argparse",
4
+ "click",
5
+ "typer",
6
+ "flask",
7
+ "fastapi",
8
+ "django",
9
+ "uvicorn",
10
+ "granian",
11
+ "starlette",
12
+ "tornado",
13
+ "bottle",
14
+ ]
15
+
16
+ [stdout]
17
+ enabled = true
18
+ ignore = []
19
+
20
+ [entry-point]
21
+ enabled = true
22
+ ignore = []
23
+
24
+ [files]
25
+ exclude = []
@@ -0,0 +1 @@
1
+ 0.36.0
@@ -0,0 +1,10 @@
1
+ {
2
+ "api.md": {
3
+ "content": "7a853df7d84d377b3026c46995c42747190bea4ef53a1b496c255dfeb09e77e2",
4
+ "description": "1c49db4136318e727bafdf1f66c2c763db8914038f6e1f5c31b680aef4687ed2"
5
+ },
6
+ "index.md": {
7
+ "content": "448279a5ee5b07cf889a65674ac848328f2ee88f40c6f3be4f8a0bf9087335fd",
8
+ "description": "e11c3c93cacc51bdb50b42c2b50d609a9f0f03665eeef92ade3c01df5b86bb5f"
9
+ }
10
+ }
@@ -0,0 +1,15 @@
1
+ <!-- Generated by rlsbl from .rlsbl/changes/ — do not edit -->
2
+
3
+ # Changelog
4
+
5
+ ## 0.1.1
6
+
7
+ - No user-facing changes.
8
+
9
+ ## 0.1.0
10
+
11
+ ### Features
12
+
13
+ - **Initial release.** ASGI router, SSE broadcaster, and extracted server lifecycle.
14
+ - **Desktop integration.** pywebview, desktop entry creation, and public API exports.
15
+ - **npm shim.** Install via `npm i -g @smmh/wesktop` for a global `wesktop` CLI that delegates to Python.
@@ -0,0 +1,77 @@
1
+ # wesktop
2
+
3
+ A Python framework for building web-based desktop applications. Uses granian (Rust-based ASGI server), pywebview (native OS windows), and msgspec (fast serialization).
4
+
5
+ ## Architecture
6
+
7
+ wesktop was extracted from ProductEngine as a reusable library. It provides four layers:
8
+
9
+ 1. **ASGI micro-router** (`asgi.py`) -- minimal HTTP routing with `{param}` placeholders, response type dispatch (JSON, text, HTML, bytes, streaming), static file serving, SPA fallback, WebSocket route registry, middleware chain, and async lifespan support. Zero external dependencies beyond msgspec for JSON encoding.
10
+
11
+ 2. **SSE broadcaster** (`sse.py`) -- typed server-sent events with per-client async queues. Event types must be registered before broadcast (strict mode). Disconnected clients are pruned automatically when their queue fills up.
12
+
13
+ 3. **Server lifecycle** (`server.py`) -- granian integration with PID file management, port availability checks, process group leadership (so worker subprocesses die with the parent), and signal handling. Supports both blocking (`start_server`) and background-thread (`start_server_in_background`) modes.
14
+
15
+ 4. **Desktop window** (`desktop.py`) -- pywebview integration. Starts the server in a background thread, opens a native OS window, and blocks until the window is closed. Late-imports pywebview so headless mode has no GUI dependency.
16
+
17
+ 5. **Desktop entries** (`entries.py`) -- cross-platform desktop shortcut creation and removal (Linux `.desktop` files, macOS `.app` bundles, Windows Start Menu shortcuts via COM or PowerShell).
18
+
19
+ ## Module Layout
20
+
21
+ ```
22
+ src/wesktop/
23
+ __init__.py Public API: re-exports from all modules, __version__, __all__
24
+ asgi.py ASGI router, request/response types, app factory, WebSocket registry
25
+ sse.py SSE Broadcaster and sse_route helper
26
+ server.py Granian lifecycle: PID files, port checks, start/stop
27
+ desktop.py pywebview window: start server + open native window
28
+ entries.py Cross-platform desktop entry creation/removal
29
+ ```
30
+
31
+ ## Dependencies
32
+
33
+ | Package | Why |
34
+ |---------|-----|
35
+ | `granian` | Rust-based ASGI server. Faster than uvicorn for production, handles process management internally. |
36
+ | `pywebview` | Opens native OS windows (WebKit on Linux/macOS, Edge WebView2 on Windows). Avoids Electron overhead. |
37
+ | `msgspec` | Fast JSON serialization/deserialization. Used in the ASGI layer for request parsing and response encoding. |
38
+
39
+ Dev dependencies: `pytest`, `httpx` (for async test client).
40
+
41
+ ## Consumer Patterns
42
+
43
+ wesktop is a library, not a CLI tool. Consumers bring their own entry point and call either:
44
+
45
+ - `wesktop.run("myapp:app")` -- desktop mode (server + native window)
46
+ - `wesktop.serve("myapp:app")` -- headless mode (server only)
47
+
48
+ The `target` argument is an ASGI import path (e.g., `"myapp.web:app"`). Consumers create a `Router`, register routes, call `create_app()`, and then pass the module path to `run()` or `serve()`.
49
+
50
+ ## Public API
51
+
52
+ All public symbols are importable from `wesktop` directly:
53
+
54
+ - **Router, Request** -- routing and request handling
55
+ - **JSONResponse, TextResponse, HTMLResponse, BytesResponse, StreamResponse** -- response types
56
+ - **create_app** -- ASGI app factory
57
+ - **add_ws_route** -- WebSocket route registration
58
+ - **Broadcaster, sse_route** -- SSE support
59
+ - **create_entry, remove_entry** -- desktop shortcut management
60
+ - **run, serve** -- entry points
61
+ - **__version__** -- package version from metadata
62
+
63
+ ## Development
64
+
65
+ ```bash
66
+ uv run pytest # Run test suite
67
+ selfdoc build # Build documentation
68
+ selfdoc serve # Serve documentation locally
69
+ ```
70
+
71
+ ## Project Structure
72
+
73
+ ```
74
+ src/wesktop/ Source code
75
+ tests/ Test suite
76
+ docs/ Documentation (selfdoc templates)
77
+ ```
wesktop-0.1.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 smm-h
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
wesktop-0.1.1/PKG-INFO ADDED
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: wesktop
3
+ Version: 0.1.1
4
+ Summary: A Python framework for building web-based desktop applications
5
+ Author-email: smm-h <smmh72@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: asgi,desktop,granian,gui,pywebview,rlsbl,web
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: granian>=2.7.4
11
+ Requires-Dist: msgspec>=0.21.1
12
+ Requires-Dist: pywebview>=6.2.1
@@ -0,0 +1 @@
1
+ # wesktop
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ const { execFileSync, spawnSync } = require("child_process");
5
+
6
+ try {
7
+ execFileSync("python3", ["--version"], { stdio: "pipe" });
8
+ } catch {
9
+ console.error("wesktop requires Python 3.11+. Install from https://python.org/");
10
+ process.exit(1);
11
+ }
12
+
13
+ const result = spawnSync("python3", ["-m", "wesktop", ...process.argv.slice(2)], {
14
+ stdio: "inherit",
15
+ });
16
+ process.exit(result.status ?? 1);