swl 5.0.0__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 (121) hide show
  1. swl-5.0.0/.flake8 +23 -0
  2. swl-5.0.0/.github/workflows/publish.yml +39 -0
  3. swl-5.0.0/.github/workflows/test.yml +22 -0
  4. swl-5.0.0/.gitignore +8 -0
  5. swl-5.0.0/CHANGELOG.md +90 -0
  6. swl-5.0.0/CLAUDE.md +72 -0
  7. swl-5.0.0/LICENSE +21 -0
  8. swl-5.0.0/PKG-INFO +146 -0
  9. swl-5.0.0/README.md +118 -0
  10. swl-5.0.0/doc/htmx-migration.md +138 -0
  11. swl-5.0.0/doc/license.conf +8 -0
  12. swl-5.0.0/doc/reference.md +610 -0
  13. swl-5.0.0/example/README.md +309 -0
  14. swl-5.0.0/example/pyproject.toml +25 -0
  15. swl-5.0.0/example/swl_tasks/__init__.py +18 -0
  16. swl-5.0.0/example/swl_tasks/__main__.py +37 -0
  17. swl-5.0.0/example/swl_tasks/app.py +138 -0
  18. swl-5.0.0/example/swl_tasks/app_test.py +240 -0
  19. swl-5.0.0/example/swl_tasks/static/tasks.css +46 -0
  20. swl-5.0.0/example/swl_tasks/static/tasks.js +69 -0
  21. swl-5.0.0/example/swl_tasks/store.py +100 -0
  22. swl-5.0.0/example/swl_tasks/templates/tasks/detail.j2 +39 -0
  23. swl-5.0.0/example/swl_tasks/templates/tasks/form.j2 +37 -0
  24. swl-5.0.0/example/swl_tasks/templates/tasks/list.j2 +53 -0
  25. swl-5.0.0/example/swl_tasks/templates/tasks/login.j2 +40 -0
  26. swl-5.0.0/example/swl_tasks/templates/tasks/menu.j2 +26 -0
  27. swl-5.0.0/example/swl_tasks/templates/tasks/page.j2 +102 -0
  28. swl-5.0.0/example/swl_tasks/templates/tasks/style_switch.j2 +30 -0
  29. swl-5.0.0/example/swl_tasks/templates/tasks/tab_details.j2 +32 -0
  30. swl-5.0.0/example/swl_tasks/templates/tasks/tab_history.j2 +25 -0
  31. swl-5.0.0/example/swl_tasks/views.py +238 -0
  32. swl-5.0.0/pyproject.toml +59 -0
  33. swl-5.0.0/setup.cfg +4 -0
  34. swl-5.0.0/swl/.gitignore +1 -0
  35. swl-5.0.0/swl/__init__.py +26 -0
  36. swl-5.0.0/swl/_version.py +24 -0
  37. swl-5.0.0/swl/actions.py +150 -0
  38. swl-5.0.0/swl/actions_test.py +245 -0
  39. swl-5.0.0/swl/auth_test.py +121 -0
  40. swl-5.0.0/swl/breadcrumbs.py +52 -0
  41. swl-5.0.0/swl/config.py +66 -0
  42. swl-5.0.0/swl/dispatcher.py +46 -0
  43. swl-5.0.0/swl/dispatcher_test.py +45 -0
  44. swl-5.0.0/swl/frameworks.py +179 -0
  45. swl-5.0.0/swl/frameworks_test.py +122 -0
  46. swl-5.0.0/swl/passwd.py +186 -0
  47. swl-5.0.0/swl/passwd_test.py +208 -0
  48. swl-5.0.0/swl/resources/__init__.py +14 -0
  49. swl-5.0.0/swl/resources/bootstrap-web-resources.json +22 -0
  50. swl-5.0.0/swl/resources/bulma-web-resources.json +18 -0
  51. swl-5.0.0/swl/resources/swl-web-resources.json +17 -0
  52. swl-5.0.0/swl/resources/tabler-web-resources.json +22 -0
  53. swl-5.0.0/swl/resources/web_resources.py +64 -0
  54. swl-5.0.0/swl/resources/web_resources_test.py +81 -0
  55. swl-5.0.0/swl/responses.py +212 -0
  56. swl-5.0.0/swl/responses_test.py +258 -0
  57. swl-5.0.0/swl/routes_endpoint.py +135 -0
  58. swl-5.0.0/swl/routes_endpoint_test.py +123 -0
  59. swl-5.0.0/swl/session/__init__.py +15 -0
  60. swl-5.0.0/swl/session/sessionauthbackend.py +30 -0
  61. swl-5.0.0/swl/session/sessionsecretkey.py +28 -0
  62. swl-5.0.0/swl/session/sessionsecretkey_test.py +23 -0
  63. swl-5.0.0/swl/shutdown.py +40 -0
  64. swl-5.0.0/swl/sort/__init__.py +14 -0
  65. swl-5.0.0/swl/sort/info.py +76 -0
  66. swl-5.0.0/swl/sort/info_test.py +81 -0
  67. swl-5.0.0/swl/static/swl/lang/de_DE.json +16 -0
  68. swl-5.0.0/swl/static/swl/lang/en_US.json +16 -0
  69. swl-5.0.0/swl/static/swl/lang/fr_FR.json +16 -0
  70. swl-5.0.0/swl/static/swl/lang/nl_NL.json +16 -0
  71. swl-5.0.0/swl/static/swl/lang.js +37 -0
  72. swl-5.0.0/swl/static/swl/seecr-logo-zonder.png +0 -0
  73. swl-5.0.0/swl/static/swl/swl-base.css +145 -0
  74. swl-5.0.0/swl/static/swl/swl-run.js +21 -0
  75. swl-5.0.0/swl/static/swl/swl.css +412 -0
  76. swl-5.0.0/swl/static/swl/swl.js +535 -0
  77. swl-5.0.0/swl/static/swl/theme.js +63 -0
  78. swl-5.0.0/swl/static/swl/websocket.js +62 -0
  79. swl-5.0.0/swl/swl.py +171 -0
  80. swl-5.0.0/swl/swl_test.py +100 -0
  81. swl-5.0.0/swl/templates/swl/account.j2 +22 -0
  82. swl-5.0.0/swl/templates/swl/account_password.j2 +42 -0
  83. swl-5.0.0/swl/templates/swl/alert.j2 +16 -0
  84. swl-5.0.0/swl/templates/swl/base.j2 +59 -0
  85. swl-5.0.0/swl/templates/swl/breadcrumbs.j2 +33 -0
  86. swl-5.0.0/swl/templates/swl/card.j2 +34 -0
  87. swl-5.0.0/swl/templates/swl/contextmenu.j2 +5 -0
  88. swl-5.0.0/swl/templates/swl/dialog.j2 +29 -0
  89. swl-5.0.0/swl/templates/swl/footer.j2 +25 -0
  90. swl-5.0.0/swl/templates/swl/inputs.j2 +60 -0
  91. swl-5.0.0/swl/templates/swl/kickstart.j2 +13 -0
  92. swl-5.0.0/swl/templates/swl/modal-content.j2 +39 -0
  93. swl-5.0.0/swl/templates/swl/modal.j2 +20 -0
  94. swl-5.0.0/swl/templates/swl/placeholder.j2 +35 -0
  95. swl-5.0.0/swl/templates/swl/toast.j2 +4 -0
  96. swl-5.0.0/swl/templates/swl/users.j2 +56 -0
  97. swl-5.0.0/swl/templates/swl/users_list.j2 +59 -0
  98. swl-5.0.0/swl/test/__init__.py +12 -0
  99. swl-5.0.0/swl/test/js-test/swl.test.js +509 -0
  100. swl-5.0.0/swl/test/server.py +96 -0
  101. swl-5.0.0/swl/test/views/__init__.py +16 -0
  102. swl-5.0.0/swl/test/views/change.py +26 -0
  103. swl-5.0.0/swl/test/views/misc.py +120 -0
  104. swl-5.0.0/swl/test/views/modal.py +38 -0
  105. swl-5.0.0/swl/test/views/rehydrate.py +54 -0
  106. swl-5.0.0/swl/test_utils.py +117 -0
  107. swl-5.0.0/swl/userbase.py +233 -0
  108. swl-5.0.0/swl/userbase_test.py +213 -0
  109. swl-5.0.0/swl/utils.py +36 -0
  110. swl-5.0.0/swl/utils_test.py +36 -0
  111. swl-5.0.0/swl/uvicorn_app.py +41 -0
  112. swl-5.0.0/swl/views/__init__.py +16 -0
  113. swl-5.0.0/swl/views/account.py +178 -0
  114. swl-5.0.0/swl/views/account_test.py +289 -0
  115. swl-5.0.0/swl.egg-info/PKG-INFO +146 -0
  116. swl-5.0.0/swl.egg-info/SOURCES.txt +119 -0
  117. swl-5.0.0/swl.egg-info/dependency_links.txt +1 -0
  118. swl-5.0.0/swl.egg-info/requires.txt +12 -0
  119. swl-5.0.0/swl.egg-info/scm_file_list.json +115 -0
  120. swl-5.0.0/swl.egg-info/scm_version.json +8 -0
  121. swl-5.0.0/swl.egg-info/top_level.txt +1 -0
swl-5.0.0/.flake8 ADDED
@@ -0,0 +1,23 @@
1
+ [flake8]
2
+ exclude =
3
+ # No need to traverse our git directory
4
+ .git,
5
+ # There's no value in checking cache directories
6
+ __pycache__,
7
+ # This contains our built documentation
8
+ build,
9
+ # This contains builds of flake8 that we don't want to check
10
+ dist,
11
+ # This contains the virtual environment
12
+ env,
13
+ extend-ignore =
14
+ # E266 too many leading '#' for block comment
15
+ E266,
16
+ # docstring
17
+ D10
18
+ per-file-ignores =
19
+ swl/*_test.py:F401,F811
20
+ # Ignore unused imports in __init__
21
+ swl/*/__init__.py:F401,F403
22
+ swl/__init__.py:F401,F403
23
+ max-line-length = 90
@@ -0,0 +1,39 @@
1
+ name: Publish to PyPI
2
+
3
+ # Publishes when a MAJOR.MINOR.PATCH tag (e.g. 0.6.4) is pushed. The tag
4
+ # becomes the package version via setuptools_scm.
5
+ on:
6
+ push:
7
+ tags: ["[0-9]+.[0-9]+.[0-9]+"]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v5
14
+ with:
15
+ fetch-depth: 0
16
+ - uses: actions/setup-python@v6
17
+ with:
18
+ python-version: "3.13"
19
+ - run: pip install build
20
+ - run: python -m build
21
+ - uses: actions/upload-artifact@v4
22
+ with:
23
+ name: dist
24
+ path: dist/
25
+
26
+ publish:
27
+ needs: build
28
+ runs-on: ubuntu-latest
29
+ environment:
30
+ name: pypi
31
+ url: https://pypi.org/project/swl/
32
+ permissions:
33
+ id-token: write # PyPI trusted publishing, no API token needed
34
+ steps:
35
+ - uses: actions/download-artifact@v4
36
+ with:
37
+ name: dist
38
+ path: dist/
39
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,22 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
14
+ steps:
15
+ - uses: actions/checkout@v5
16
+ with:
17
+ fetch-depth: 0 # setuptools_scm needs the tags
18
+ - uses: actions/setup-python@v6
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - run: pip install -e . -e example/
22
+ - run: python -m pytest swl example/swl_tasks
swl-5.0.0/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ env/
2
+ *.egg-info/
3
+ dist/
4
+ build/
5
+ swl-tasks-data/
6
+ *.swp
7
+ .DS_store
8
+ doc/swl-reference.pdf
swl-5.0.0/CHANGELOG.md ADDED
@@ -0,0 +1,90 @@
1
+ # Changelog
2
+
3
+ ## 5.0.0 (2026-10-29)
4
+
5
+ SWL moves its browser side to [htmx 4](https://four.htmx.org). The Python API (actions,
6
+ `register()`, `swl.success()` / `swl.fail()`) stays the same, but templates and JavaScript have
7
+ to be converted: `doc/htmx-migration.md` lists every change.
8
+
9
+ SWL is now released under the MIT License (it was GPL v2 or later), and is published on
10
+ [GitHub](https://github.com/seecr/swl) and [PyPI](https://pypi.org/project/swl/).
11
+
12
+ ### Breaking
13
+
14
+ - **htmx instead of SWL's own jQuery code.**
15
+ - The `data-swl-*` attributes are replaced by `hx-*` attributes; `data-swl-validate` and
16
+ `data-swl-on-changed` stay. See the table in `doc/htmx-migration.md`.
17
+ - jQuery is no longer loaded.
18
+ - `rehydrate.js` is gone, and `swl.js` no longer exports `hydrate`, `start_hydrating`,
19
+ `rehydrate_placeholder`, `arguments_from_elem` and `swl_arguments_from_elem`.
20
+ - **The response is HTML plus headers instead of JSON.**
21
+ - Hydrated content arrives as `<hx-partial>` elements, and the other instructions as an
22
+ `HX-Trigger` header.
23
+ - An empty response is `204`; `swl.fail()` is `422`.
24
+ - Tests use `swl.test_utils.assert_success()` and `assert_fail()` instead of
25
+ `response.json()`.
26
+ - **`hydrate()` data can no longer be an action url** that is fetched next. Return a placeholder
27
+ in the HTML instead.
28
+ - **Placeholders post only their own values** (`hx-vals` and `hx-include`), not the fields inside
29
+ or around them.
30
+ - **The modal, message dialog, toast and context menu are native HTML.** The modal and dialog
31
+ are `<dialog>` elements; the toast and context menu are popovers. They no longer need
32
+ Bootstrap's JavaScript.
33
+ - `swl/modal-content.j2` uses `swl-modal-*` classes instead of Bootstrap's `modal-header` /
34
+ `modal-body`.
35
+ - JavaScript that listened to `shown.bs.modal` / `hidden.bs.modal` uses `swl-modal-shown` /
36
+ the dialog's `close` event.
37
+ - Put context menu items in a `<div class="swl-menu">`.
38
+ - A page with its own Bootstrap modal markup (`<div id="swl-modal" class="modal">`) still
39
+ works.
40
+ - **Web resources come from cdn.jsdelivr.net**, with integrity hashes: htmx 4.0.0, Bootstrap
41
+ 5.3.8 (was 5.3.0), Bootstrap Icons 1.13.1 (was 1.11.3), Tabler 1.6.1 and Bulma 1.0.4.
42
+ - `urls.cdn` now defaults to `https://cdn.jsdelivr.net` (was `https://cdn.seecr.nl`).
43
+ - `swl/resources/default-web-resources.json` is renamed to `bootstrap-web-resources.json`.
44
+ - **The account and user screens have a new layout.** They have captions above the fields, an
45
+ avatar, and toasts after a change. An application that styled their old Bootstrap markup should
46
+ check them.
47
+
48
+ ### Added
49
+
50
+ - **Choice of CSS framework**: `ui.framework` is `"bootstrap"` (the default), `"tabler"`,
51
+ `"bulma"` or `"swl"`.
52
+ - `"swl"` uses SWL's own `swl-base.css`, with no framework at all.
53
+ - The choice sets the web resources, and `SWL.ui`: CSS classes for inputs, buttons and tables
54
+ in that framework.
55
+ - `paths.resources` still replaces the resources with your own file.
56
+ - **Theme variables** (`--swl-accent`, `--swl-bg`, ...) for SWL's own parts. They follow
57
+ Bootstrap's, Tabler's or Bulma's colours.
58
+ - **Layout classes in `swl.css`** that work with every framework: `swl-container`, `swl-nav`,
59
+ `swl-columns`, `swl-card`, `swl-tabs`, `swl-stack`, `swl-row`, `swl-field`, `swl-list`,
60
+ `swl-link`, `swl-menu` and more.
61
+ - **Light and dark colours**: `/static/swl/theme.js` with `swl_theme.set("light" | "dark" |
62
+ "auto")`, for every framework.
63
+ - **`FrameworkCookieMiddleware`**: lets a visitor choose the framework with a cookie, for demos
64
+ and style guides.
65
+ - **`placeholder()` macro** (`swl/placeholder.j2`) for elements that load and reload
66
+ themselves.
67
+ - **`swl.success().trigger(event, **detail)`**: fire your own JavaScript event.
68
+ - **`data-swl-close`** on a button closes the modal. `swl/modal-content.j2` has an optional
69
+ `modal_footer` block.
70
+ - **Deep links.**
71
+ - `kickstart` now waits until the page's placeholders have loaded, so it can rebuild nested
72
+ screens.
73
+ - Its `kwargs` may be a dict and are escaped safely.
74
+ - `doc/reference.md` explains deep links: `set_url` plus a page route.
75
+ - **An example application** (`example/`, a task list) with a guided walkthrough. Run it with
76
+ `--framework swl | bootstrap | tabler | bulma`.
77
+ - **Documentation**: `README.md`, `doc/reference.md` (with debugging and troubleshooting) and
78
+ `doc/htmx-migration.md`.
79
+
80
+ ### Fixed
81
+
82
+ - Failing requests (HTTP errors, network errors) are reported in SWL's dialog, never swapped into
83
+ the page. A request that ends on a redirect, for example to the login page after the session
84
+ expired, becomes a page load.
85
+ - A function called by name (`call`, `data-swl-validate`, the modal's `call`) that is missing or
86
+ fails is reported, and does not stop the rest of the response.
87
+ - The context menu posts the element's values and the click position correctly.
88
+ - `open_modal(call=...)` runs the function once per opening.
89
+ - A second toast while the first is still showing no longer causes an error.
90
+ - `SWL.globals` referred to an attribute that does not exist.
swl-5.0.0/CLAUDE.md ADDED
@@ -0,0 +1,72 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ # SWL (Seecr Web Lib)
6
+
7
+ Python library for interactive web apps: Starlette on the server, Jinja templates, htmx 4 in the
8
+ browser. MIT license. Maintained by Seecr (Johan Jonkers, Thijs Janssen).
9
+
10
+ ## State of the project
11
+
12
+ - The browser side is htmx 4 (it was jQuery with `data-swl-*` attributes up to 4.3.0; a breaking
13
+ change). `doc/htmx-migration.md` describes converting an application.
14
+ - SWL's modal/dialog are native `<dialog>`, toast and context menu are popovers, styled by
15
+ `swl.css` with `--swl-*` variables; SWL needs no framework JavaScript. `"ui.framework"`:
16
+ "bootstrap" (default), "tabler" (Bootstrap-based), "bulma" or "swl" (SWL's own `swl-base.css`,
17
+ no framework) picks the web resources and `SWL.ui` (classes for inputs/buttons, see
18
+ `swl/frameworks.py`). Layout classes (swl-nav, swl-columns, swl-card, swl-tabs) live in swl.css.
19
+ The example runs with each: `python -m swl_tasks --framework bulma`.
20
+ Pico CSS was considered and rejected (no longer maintained).
21
+
22
+ ## Where things are
23
+
24
+ - `swl/swl.py`, `swl/actions.py`: app setup, action registration (`__actions__`, `register(name)`
25
+ gives `/action/<md5>` urls; actions need a logged-in user unless `authenticated=False`).
26
+ - `swl/responses.py`: `swl.success()` / `swl.fail()` builder. Output = HTML with `<hx-partial>`
27
+ elements (from `hydrate`) + an `HX-Trigger` header with SWL events (`swl-call`, `swl-rehydrate`,
28
+ `swl-modal-open/-close`, `swl-contextmenu`, `swl-toast`, `swl-reset-form`, `swl-error`, ...),
29
+ `HX-Redirect`, `HX-Replace-Url`. Empty response = 204, fail = 422 (+ `HX-Reswap: none`).
30
+ - `swl/static/swl/swl.js`: browser side (event handlers, dialogs via `swl.ui`, `swl_action`,
31
+ `swl.rehydrate`, `swl.kickstart`, toasts); `swl-run.js` boots it; `websocket.js` = live updates
32
+ (`data-swl-on-changed` placeholders reload when the dispatcher signals a change).
33
+ - `swl/templates/swl/`: `base.j2` (loads htmx + htmx-config meta), `placeholder.j2` (macro),
34
+ `kickstart.j2`, `modal-content.j2`, account/users views (`swl/views/account.py`).
35
+ - `swl/resources/<framework>-web-resources.json`: CDN scripts/styles per framework, all from
36
+ cdn.jsdelivr.net with SRI hashes (htmx is `htmx.org@4.0.0`); `web_resources.py` loads them.
37
+ - `swl/test_utils.py`: `assert_success(response)` (returns the events), `assert_fail(response, msg)`.
38
+ - `doc/reference.md`: full reference (deep links, tabs, debugging); `doc/htmx-migration.md`.
39
+ - `example/`: task-list example app (`swl_tasks`), README walks one click end to end.
40
+ Run: `pip install -e example && python -m swl_tasks`, login demo / demo-password.
41
+
42
+ ## Key conventions (htmx 4 specifics that bite)
43
+
44
+ - Attributes only inherit when written as `hx-vals:inherited` / `hx-confirm:inherited`.
45
+ - Non-form elements (placeholders, links) post only `hx-vals` + `hx-include`, plus `placeholder=<id>`.
46
+ - `HX-Trigger` events fire after the swap; the source may be detached, so listen on `document`.
47
+ - Recipes for rehydrate live on the element (`hx-post` + `hx-vals`), not in sessionStorage.
48
+ - Deep links: actions call `set_url(...)`, the page route renders that screen; `kickstart` only for
49
+ state only an action can produce (it waits until placeholders settle: 100 ms quiet, max 5 s).
50
+ - Values from users/urls into `hx-vals` or kickstart: always `|tojson`.
51
+ - Bootstrap tabs: `hx-trigger="show.bs.tab"`, plus `load, ` on the initially active tab. Without
52
+ Bootstrap: `<nav class="swl-tabs">` with `aria-selected` buttons (see `doc/reference.md`).
53
+
54
+ ## Testing
55
+
56
+ - Python: `pytest swl example/swl_tasks` (tests are `*_test.py` next to the code; needs
57
+ `pip install -e . -e example/`). pytest runs with `-n5` (xdist) and `asyncio_mode = auto` from
58
+ `pyproject.toml`; for one test use `pytest -n0 swl/responses_test.py::test_name`.
59
+ - JavaScript: QUnit tests in `swl/test/js-test/swl.test.js`, served by `python -m swl.test.server`
60
+ (open http://localhost:8000/). Headless: run that page in Playwright.
61
+ - Style: flake8 (`.flake8`, max line length 90), isort with the black profile.
62
+
63
+ ## Working agreements
64
+
65
+ - Python >= 3.11; all dependencies come from PyPI. `env/` is a local virtualenv, ignored.
66
+ - Versions come from git tags via `setuptools_scm` (tags like `5.0.0`); `swl/_version.py` is
67
+ generated and git-ignored. Pushing a version tag to GitHub publishes that version to PyPI
68
+ (`.github/workflows/publish.yml`), and PyPI never accepts a version twice. Don't push tags
69
+ unless asked. `.github/workflows/test.yml` runs the tests on every push.
70
+ - Never push or publish to PyPI; that is the maintainers' decision. Commit messages are prefixed
71
+ `JJ: `. No `Claude-Session:` lines in commit messages.
72
+ - Keep changes on a branch; report behaviour changes and anything left undone.
swl-5.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-2026 Seecr (Seek You Too B.V.) https://seecr.nl
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.
swl-5.0.0/PKG-INFO ADDED
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: swl
3
+ Version: 5.0.0
4
+ Summary: Seecr Web Lib makes developing webbased apps easier
5
+ Author-email: Thijs Janssen <thijs@seecr.nl>, Johan Jonkers <johan@seecr.nl>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/seecr/swl
8
+ Project-URL: Source, https://github.com/seecr/swl
9
+ Project-URL: Changelog, https://github.com/seecr/swl/blob/main/CHANGELOG.md
10
+ Keywords: swl
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: argon2-cffi<26,>=25.1.0
16
+ Requires-Dist: httpx2<3,>=2.16.0
17
+ Requires-Dist: pytest<10,>=9.2.0
18
+ Requires-Dist: pytest-asyncio<2,>=1.4.1
19
+ Requires-Dist: pytest-xdist<4,>=3.8.0
20
+ Requires-Dist: seecr-stdlib<1,>=0.6.5
21
+ Requires-Dist: seecr-testtools<1,>=0.4.3
22
+ Requires-Dist: setuptools-scm<11,>=10.3.4
23
+ Requires-Dist: starlette<2,>=1.10.0
24
+ Requires-Dist: starlette[full]
25
+ Requires-Dist: uvicorn<1,>=0.54.0
26
+ Requires-Dist: uvicorn[standard]
27
+ Dynamic: license-file
28
+
29
+ # SWL (Seecr Web Lib)
30
+
31
+ [![Tests](https://github.com/seecr/swl/actions/workflows/test.yml/badge.svg)](https://github.com/seecr/swl/actions/workflows/test.yml)
32
+ [![PyPI](https://img.shields.io/pypi/v/swl.svg)](https://pypi.org/project/swl/)
33
+
34
+ Interactive web applications in Python, without a JavaScript frontend.
35
+
36
+ SWL is a small layer on top of [Starlette](https://www.starlette.io),
37
+ [Jinja](https://jinja.palletsprojects.com) and [htmx 4](https://four.htmx.org). The server renders
38
+ HTML; an action answers with a short list of instructions ("put this HTML there, reload that list,
39
+ close the dialog, show a toast") that SWL carries out in the browser. You write Python and
40
+ templates. There is no build step, and no second application to keep in sync.
41
+
42
+ It is made for the software around data: admin screens, editors, review tools, form builders and
43
+ dashboards. These are lots of forms, lists, dialogs and tabs, used every day by people who need
44
+ them to work.
45
+
46
+ ## A taste
47
+
48
+ ```jinja
49
+ {% from "swl/placeholder.j2" import placeholder %}
50
+
51
+ <form hx-post="{{ register('note_add') }}">
52
+ <input name="text" class="{{ SWL.ui.input }}">
53
+ <button type="submit" class="{{ SWL.ui.button_primary }}">Add</button>
54
+ </form>
55
+
56
+ {{ placeholder("notes", register('notes_list')) }}
57
+ ```
58
+
59
+ ```python
60
+ async def notes_list(request, swl, db, **kwargs):
61
+ html = swl.template("notes/list.j2").render(notes=db.notes())
62
+ return swl.success().hydrate("notes", html)
63
+
64
+
65
+ async def note_add(request, swl, db, **kwargs):
66
+ async with request.form() as form:
67
+ text = form.get("text", "").strip()
68
+ if not text:
69
+ return swl.fail().message("A note needs some text")
70
+ db.add_note(text)
71
+ return swl.success().reset_form().rehydrate("notes").toast("Note added")
72
+
73
+
74
+ __actions__ = [notes_list, note_add]
75
+ ```
76
+
77
+ `note_add` doesn't render the list. It asks the list to reload itself, so `notes_list` stays the
78
+ only place that knows how the list looks. And because a response is data, you test an action
79
+ without a browser.
80
+
81
+ ## What you get
82
+
83
+ - **Actions**: async Python functions, registered by module, with login and authorisation checks.
84
+ A template gets an action's url with `register("name")`.
85
+ - **A response builder**: `hydrate`, `rehydrate`, `open_modal`, `close_modal`,
86
+ `open_contextmenu`, `toast`, `call` (a JavaScript function), `set_url`, `redirect`,
87
+ `trigger`, and `swl.fail().message(...)` for errors.
88
+ - **Placeholders and rehydrate**: parts of the page that load themselves and can be reloaded by
89
+ name from any action.
90
+ - **Live updates**: a placeholder can reload when the data it shows changes, also in other
91
+ browser tabs and for other users (over a websocket).
92
+ - **Screens you can reload and link to**: actions keep the url up to date, and a page route
93
+ renders the same screen from it.
94
+ - **Ready-made parts**: a modal, confirm and error dialogs, toasts and context menus, all
95
+ plain HTML (`<dialog>`, popover). There are also login, "my account" and user management
96
+ screens.
97
+ - **Your choice of CSS framework**: `ui.framework` is `"bootstrap"` (the default), `"tabler"`,
98
+ `"bulma"` or `"swl"`, SWL's own small stylesheet with no framework at all. Light and dark
99
+ colours work with each of them.
100
+
101
+ ## Install
102
+
103
+ ```sh
104
+ pip install swl
105
+ ```
106
+
107
+ Or from a checkout: `pip install -e .`. SWL needs Python 3.11 or newer. Its dependencies, including `seecr-stdlib`, come from PyPI.
108
+
109
+ ## Try the example
110
+
111
+ The example application is a small task list. It uses nearly everything: dialogs, search as you
112
+ type, tabs, a context menu, live updates between browser tabs, screens that survive a reload,
113
+ and switches for the CSS framework and dark mode.
114
+
115
+ ```sh
116
+ pip install -e .
117
+ pip install -e example/
118
+ python -m swl_tasks
119
+ ```
120
+
121
+ Open <http://127.0.0.1:8000/> and log in as **demo** / **demo-password**.
122
+
123
+ ## Documentation
124
+
125
+ - [`example/README.md`](example/README.md): a guided walk through the example, from one click to
126
+ the updated list. Start here.
127
+ - [`doc/reference.md`](doc/reference.md): everything SWL provides, for looking things up. Also
128
+ covers debugging and troubleshooting.
129
+ - [`doc/htmx-migration.md`](doc/htmx-migration.md): upgrading an application from SWL 4 (jQuery,
130
+ `data-swl-*` attributes) to the htmx version.
131
+ - [`CHANGELOG.md`](CHANGELOG.md): what changed.
132
+
133
+ ## Tests
134
+
135
+ ```sh
136
+ pytest swl example/swl_tasks
137
+ ```
138
+
139
+ The JavaScript tests (QUnit) run in a browser: `python -m swl.test.server`, then open
140
+ <http://localhost:8000/>.
141
+
142
+ ## License
143
+
144
+ SWL is released under the MIT License; see [LICENSE](LICENSE).
145
+
146
+ Copyright © Seecr (Seek You Too B.V.), <https://seecr.nl>.
swl-5.0.0/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # SWL (Seecr Web Lib)
2
+
3
+ [![Tests](https://github.com/seecr/swl/actions/workflows/test.yml/badge.svg)](https://github.com/seecr/swl/actions/workflows/test.yml)
4
+ [![PyPI](https://img.shields.io/pypi/v/swl.svg)](https://pypi.org/project/swl/)
5
+
6
+ Interactive web applications in Python, without a JavaScript frontend.
7
+
8
+ SWL is a small layer on top of [Starlette](https://www.starlette.io),
9
+ [Jinja](https://jinja.palletsprojects.com) and [htmx 4](https://four.htmx.org). The server renders
10
+ HTML; an action answers with a short list of instructions ("put this HTML there, reload that list,
11
+ close the dialog, show a toast") that SWL carries out in the browser. You write Python and
12
+ templates. There is no build step, and no second application to keep in sync.
13
+
14
+ It is made for the software around data: admin screens, editors, review tools, form builders and
15
+ dashboards. These are lots of forms, lists, dialogs and tabs, used every day by people who need
16
+ them to work.
17
+
18
+ ## A taste
19
+
20
+ ```jinja
21
+ {% from "swl/placeholder.j2" import placeholder %}
22
+
23
+ <form hx-post="{{ register('note_add') }}">
24
+ <input name="text" class="{{ SWL.ui.input }}">
25
+ <button type="submit" class="{{ SWL.ui.button_primary }}">Add</button>
26
+ </form>
27
+
28
+ {{ placeholder("notes", register('notes_list')) }}
29
+ ```
30
+
31
+ ```python
32
+ async def notes_list(request, swl, db, **kwargs):
33
+ html = swl.template("notes/list.j2").render(notes=db.notes())
34
+ return swl.success().hydrate("notes", html)
35
+
36
+
37
+ async def note_add(request, swl, db, **kwargs):
38
+ async with request.form() as form:
39
+ text = form.get("text", "").strip()
40
+ if not text:
41
+ return swl.fail().message("A note needs some text")
42
+ db.add_note(text)
43
+ return swl.success().reset_form().rehydrate("notes").toast("Note added")
44
+
45
+
46
+ __actions__ = [notes_list, note_add]
47
+ ```
48
+
49
+ `note_add` doesn't render the list. It asks the list to reload itself, so `notes_list` stays the
50
+ only place that knows how the list looks. And because a response is data, you test an action
51
+ without a browser.
52
+
53
+ ## What you get
54
+
55
+ - **Actions**: async Python functions, registered by module, with login and authorisation checks.
56
+ A template gets an action's url with `register("name")`.
57
+ - **A response builder**: `hydrate`, `rehydrate`, `open_modal`, `close_modal`,
58
+ `open_contextmenu`, `toast`, `call` (a JavaScript function), `set_url`, `redirect`,
59
+ `trigger`, and `swl.fail().message(...)` for errors.
60
+ - **Placeholders and rehydrate**: parts of the page that load themselves and can be reloaded by
61
+ name from any action.
62
+ - **Live updates**: a placeholder can reload when the data it shows changes, also in other
63
+ browser tabs and for other users (over a websocket).
64
+ - **Screens you can reload and link to**: actions keep the url up to date, and a page route
65
+ renders the same screen from it.
66
+ - **Ready-made parts**: a modal, confirm and error dialogs, toasts and context menus, all
67
+ plain HTML (`<dialog>`, popover). There are also login, "my account" and user management
68
+ screens.
69
+ - **Your choice of CSS framework**: `ui.framework` is `"bootstrap"` (the default), `"tabler"`,
70
+ `"bulma"` or `"swl"`, SWL's own small stylesheet with no framework at all. Light and dark
71
+ colours work with each of them.
72
+
73
+ ## Install
74
+
75
+ ```sh
76
+ pip install swl
77
+ ```
78
+
79
+ Or from a checkout: `pip install -e .`. SWL needs Python 3.11 or newer. Its dependencies, including `seecr-stdlib`, come from PyPI.
80
+
81
+ ## Try the example
82
+
83
+ The example application is a small task list. It uses nearly everything: dialogs, search as you
84
+ type, tabs, a context menu, live updates between browser tabs, screens that survive a reload,
85
+ and switches for the CSS framework and dark mode.
86
+
87
+ ```sh
88
+ pip install -e .
89
+ pip install -e example/
90
+ python -m swl_tasks
91
+ ```
92
+
93
+ Open <http://127.0.0.1:8000/> and log in as **demo** / **demo-password**.
94
+
95
+ ## Documentation
96
+
97
+ - [`example/README.md`](example/README.md): a guided walk through the example, from one click to
98
+ the updated list. Start here.
99
+ - [`doc/reference.md`](doc/reference.md): everything SWL provides, for looking things up. Also
100
+ covers debugging and troubleshooting.
101
+ - [`doc/htmx-migration.md`](doc/htmx-migration.md): upgrading an application from SWL 4 (jQuery,
102
+ `data-swl-*` attributes) to the htmx version.
103
+ - [`CHANGELOG.md`](CHANGELOG.md): what changed.
104
+
105
+ ## Tests
106
+
107
+ ```sh
108
+ pytest swl example/swl_tasks
109
+ ```
110
+
111
+ The JavaScript tests (QUnit) run in a browser: `python -m swl.test.server`, then open
112
+ <http://localhost:8000/>.
113
+
114
+ ## License
115
+
116
+ SWL is released under the MIT License; see [LICENSE](LICENSE).
117
+
118
+ Copyright © Seecr (Seek You Too B.V.), <https://seecr.nl>.