Signed snippets, hiccup, and a dual-eval loop. The web that should have been.
Project description
evaleval
evaleval is a tiny server-driven web pattern: signed Python expressions in forms, eval on submit, and UI patching over SSE.
The entire client.
import { Idiomorph } from 'idiomorph';
window.Idiomorph = Idiomorph;
const es = new EventSource('/sse');
es.addEventListener('exec', e => eval(e.data));
document.addEventListener('submit', async e => {
e.preventDefault();
const r = await fetch(e.target.action, { method: 'POST', body: new FormData(e.target) });
const t = await r.text();
if (t) eval(t);
});
Three endpoints. No framework.
GET / — serve the shell
GET /sse — push what you see
POST / — verify, eval
Example: evaleval-todo
A todo list in ~170 lines.
Forms are data. They carry their own signed handlers.
from evaleval import Signer, Three, Two, Selector, MORPH, APPEND, REMOVE
signer = Signer()
def add_form():
return ["form", {"action": "/", "method": "post"},
*signer.snippet_hidden("add($text)"),
["input", {"type": "text", "name": "text", "placeholder": "what needs doing?"}],
["button", {"type": "submit"}, "add"],
]
Sandbox functions return JS patch chains. The chains say how many parts they have, then become strings and disappear.
def add(text):
t = {"id": uuid.uuid4().hex[:8], "text": text, "done": False}
TODOS.append(t)
return PlainTextResponse(";".join([
Three[Selector("#add-form")][MORPH][add_form()],
Three[Selector("#todo-list")][APPEND][todo_item(t)],
Three[Selector("p.count")][MORPH][remaining_count()],
]))
def delete(todo_id):
TODOS.remove(_find(todo_id))
return PlainTextResponse(";".join([
Two[Selector(f"#todo-{todo_id}")][REMOVE],
Three[Selector("p.count")][MORPH][remaining_count()],
]))
The POST / route verifies the signature and evals the snippet. verify_snippet raises SnippetExecutionError with a status code.
from evaleval import SnippetExecutionError
@app.post("/")
async def do(request):
form = await request.form()
try:
snippet = signer.verify_snippet(form)
return eval(snippet)
except SnippetExecutionError as e:
return PlainTextResponse(e.message, status_code=e.status_code)
except Exception as e:
return PlainTextResponse(str(e), status_code=500)
eval only accepts a single expression. Multi-line snippets and return ... statements will fail with SyntaxError.
If you want side effects plus a return value, use an expression pattern like:
(print("form callback for add todo", $text), add($text))[1]
SSE pushes the initial page as JS the browser evals.
from evaleval import exec_event, shell_html, One, Eval
@app.get("/")
async def index():
return HTMLResponse(shell_html())
@app.get("/sse")
async def sse(request):
async def generate():
yield exec_event([
One[Eval("document.title = 'todos'")],
Three[Selector("body")][MORPH][["body", page()]],
])
return StreamingResponse(generate(), media_type="text/event-stream")
pip install evaleval
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file evaleval-0.2.3.tar.gz.
File metadata
- Download URL: evaleval-0.2.3.tar.gz
- Upload date:
- Size: 7.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
386c898a2712c154dc708b8f49e3ae780e22026e97b8ea99b95959cab2713c91
|
|
| MD5 |
357e7be53c4f4aa94f24166798186f15
|
|
| BLAKE2b-256 |
e3673fa66d3963b9d1aecd87d3aff45944dc485ddbc13c505accdd983d1c7dac
|
Provenance
The following attestation bundles were made for evaleval-0.2.3.tar.gz:
Publisher:
publish.yml on tommy-mor/evaleval
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
evaleval-0.2.3.tar.gz -
Subject digest:
386c898a2712c154dc708b8f49e3ae780e22026e97b8ea99b95959cab2713c91 - Sigstore transparency entry: 1143468365
- Sigstore integration time:
-
Permalink:
tommy-mor/evaleval@17721bb0dfcfdf52569d312404d1fecb8bb1c87e -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/tommy-mor
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@17721bb0dfcfdf52569d312404d1fecb8bb1c87e -
Trigger Event:
release
-
Statement type:
File details
Details for the file evaleval-0.2.3-py3-none-any.whl.
File metadata
- Download URL: evaleval-0.2.3-py3-none-any.whl
- Upload date:
- Size: 8.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f26d935af9dd7d472dd93f32a28458c9929c5762574e4502a0bc5357c2da61e
|
|
| MD5 |
b949a25ef2553fd9cc9bd4a937ec0e5c
|
|
| BLAKE2b-256 |
88e5afca13fed9352da17f2fb25050e58f0ecaa2d12b25ad0c91732e5406a12b
|
Provenance
The following attestation bundles were made for evaleval-0.2.3-py3-none-any.whl:
Publisher:
publish.yml on tommy-mor/evaleval
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
evaleval-0.2.3-py3-none-any.whl -
Subject digest:
3f26d935af9dd7d472dd93f32a28458c9929c5762574e4502a0bc5357c2da61e - Sigstore transparency entry: 1143468448
- Sigstore integration time:
-
Permalink:
tommy-mor/evaleval@17721bb0dfcfdf52569d312404d1fecb8bb1c87e -
Branch / Tag:
refs/tags/v0.2.3 - Owner: https://github.com/tommy-mor
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@17721bb0dfcfdf52569d312404d1fecb8bb1c87e -
Trigger Event:
release
-
Statement type: