Releases: MaanavKrishna/PyWeb
Release list
PyWeb 0.4.3
A review release: bug fixes, security hardening and speed-ups. No API
removals; one new setting (PYWEB_TRUST_PROXY).
Fixed
- Pages showed stale data when a module-level value (
ITEMS = []) was
changed by an@serverfunction: the compiler copied its starting
value into browser code, and the page re-rendered with it. Values
that code changes now come from the server on each request. db.transaction(): a statement error that your code caught rolled
back the transaction's earlier writes while later ones still
committed. Errors inside a transaction now leave it to
transaction()(all or nothing).db.stream()inside a transaction waited for a second connection
(a 10 second hang with SQLite in memory); it now uses the
transaction's.pyweb servesent SVG, images, fonts, JSON and WebAssembly as
application/octet-stream(browsers don't show an SVG sent that way).- Your own files in
static/(such asapp.css) were cached by
browsers for a year, so deploys didn't show up for returning
visitors. They're now revalidated with an ETag (a304when
unchanged); content-hashed files,?v=URLs and npm packages are
still cached for a year. - Client-side navigation could show a page prefetched before a server
call changed its data. async defserver functions ignoredrpc_timeout.- The ASGI adapter kept only the last of repeated request headers, so
HTTP/2 clients sending each cookie separately lost all but one. pyweb dts: type aliases were never written, members on one line
({ a: string; b?: number }) produced invalid Python, and optional
fields before required ones made the stub fail to import.- Very long upload filenames are shortened (keeping the extension)
instead of failing to save.
Security
- RPC rate limits used the
X-Forwarded-Forheader, which any client
can set (so the limit was easy to get around), and put every visitor
without a session cookie in one shared bucket. They now use the
connection's address; behind a reverse proxy setPYWEB_TRUST_PROXY=1
(or the number of proxies) to use the address your proxy saw. auth.require_sessionandauth.login_responseonly redirect to
paths on your site (//evil.examplebecomes/); the new
auth.safe_next()does the same for your own?next=handling.- Magic-link tokens are signed separately from session cookies, so one
can't be used as the other. Links issued before the upgrade stop
working (they last 15 minutes).
Performance
- HTML, JavaScript, CSS, JSON and SVG are gzipped for browsers that
accept it (the shared runtime goes from 46 KB to 15 KB); static files
are compressed once and kept. - Memory no longer grows without bound in long-running servers: the
rate limiter forgets idle callers, the realtime bus forgets channels
quiet for an hour, the in-memory cache drops expired entries, and
the job queue forgets jobs an hour after they finish. - The job queue runs jobs on a pool of 8 threads instead of one thread
per job, andwait()returns as soon as the job finishes. pyweb devno longer re-scans virtualenvs in the project folder
every 0.4 s, and reloads whenpyweb.lockchanges (after
pyweb add).
PyWeb 0.4.2
Added
- VS Code extension: PyWeb: Run App (a run button on
.pyweb
files, Ctrl+F5), New App from any template, Check App, Add
npm Package, Install or Update PyWeb and Set Up AI Assistant
(MCP), which adds the PyWeb MCP server to.vscode/mcp.json. - VS Code extension: closing tags are added as you type, Emmet works in
markup, new snippets (layout,error,stream,livequery,npm,
mount), a status item that shows the PyWeb version in use, and an
icon.
Fixed
- VS Code extension: the language server failed with
spawn python ENOENTwhen no Python interpreter was selected. The
extension now looks for a Python with PyWeb (the selected
interpreter,pyweb,py,python3,python), offers to install
PyWeb when it's missing, and restarts when you change interpreter.
PyWeb 0.4.1
Added
- MCP server:
pyweb_routesmaps every page (route, path and query
parameters with types and defaults, title and head tags, layouts,
live-data variables), the layouts, error pages and server functions. - MCP server:
pyweb_packagesadds, removes or lists npm packages for
browser code, with each package's exported names and thenpm(...)
lines to bind them. - MCP server:
add_ai_featureandmake_data_liveprompts. - MCP server:
pyweb_callreturns every chunk a streaming function
yields;pyweb_renderreports the title, head tags and the layouts that
rendered;pyweb_checkreports layouts, live variables, npm packages
and streaming functions. - The AI guide (
pyweb_guide,AGENTS.md) has sections with working
examples for multi-page apps, streaming and AI, live data and npm
packages. - A new README with screenshots, and docs on publishing the VS Code
extension.
Fixed
- Markup right after a
defline that ends in a comment
(def Page(q: str = ""): # ...) failed to compile.
PyWeb 0.4.0
Apps that are bigger, livelier and smarter: npm packages without
Node.js, multi-page apps with layouts and client-side navigation,
streaming for AI features, and page data that follows the database.
Added
- npm packages without Node.js.
pyweb add chart.js/autodownloads a
package from the npm registry, checks its sha512 checksum, follows its
imports from the browser entry point and copies only the files it
needs (and its dependencies) intostatic/vendor/, pinning versions in
pyweb.lock.pyweb addwith no arguments reinstalls from the lock;
pyweb removetakes a package out. Browser code binds exports with
Chart = npm("chart.js/auto")ornpm("pkg", "Export"). Calling a
class constructs it and keyword arguments become an options object.
Each page imports only the packages it uses, through an import map
that the Content Security Policy allows by hash. CommonJS-only and
Node.js-only packages are refused with an explanation, and an
uninstalled package is a compile error that names the command to run. ref={el}sets a page variable to an element, in time for
on_mount, for libraries that draw into the page.- Changing an attribute of a package's object (
chart.value = 5)
re-renders whatever shows it. - Web components (custom elements from npm) work in markup.
pyweb lsp: hover shows an npm binding's installed version and
TypeScript signature; completion offers installed packages in
npm("and a module's exports aftername..- New docs page: npm packages.
- Layouts.
@app.layoutwraps every page (or, with
@app.layout("/admin"), pages under a prefix) in shared markup;
{children}marks where the page goes. Layouts nest, have their own
server code, state and handlers, and pages can opt out with
layout=Noneor pick one withlayout="Name". - Client-side navigation. Links between pages fetch the next page's
HTML and swap it in below the layouts both pages share, so layout
state (an open menu, a player) survives. Links are prefetched on
hover or focus, back/forward restore the scroll position, and anything
unusual falls back to a full page load.navigate("/path")from
pyweb.browserdoes the same from browser code;on_unmountruns
when a page is left;App(client_nav=False)turns it off. - Current links. Links to the current page get
aria-current="page"and links to its parent sections
aria-current="true", on the server and after each navigation. - Head tags.
@app.page(description=..., image=..., noindex=...)and
head(title=..., description=...)in page code add description, Open
Graph and Twitter tags;App(base_url=...)adds canonical URLs and
absolute image links. - Error pages in
.pyweb.@app.error(404)/@app.error(500)pages
takepath,status,messageorrequest_idand use root layouts. - Typed query parameters. Page parameters that aren't in the route
are read from the query string and converted by annotation (int,
float,bool,list[...]); bad or missing values answer 400. - New docs page: Layouts & navigation.
- Streaming server functions. A
@serverfunction thatyields
sends each value as it's produced (NDJSON over the same RPC endpoint);
browser code reads it withasync for.stream.cancel()or leaving the
loop aborts the request and closes the generator on the server, so an
upstream AI call stops too. Errors raised part-way arrive as
RPCError. Works withasync defgenerators, underpyweb serve,
pyweb devand ASGI, andTestClient.rpcreturns the streamed values. <Markdown text={...} />(frompyweb) renders Markdown to safe
HTML on the server and updates it in the browser as the text changes,
with the same rules in both places. Raw HTML is shown as text and
links only accept http(s), mailto and relative URLs.ai-chattemplate and example: a streaming chat with Stop and
Markdown replies that talks to Anthropic, any OpenAI-compatible server
(OpenAI, Ollama, vLLM, ...) or a built-in demo model, chosen by
environment variables.pyweb new NAME --template ai-chat.- New docs page: Building AI apps.
- Live data.
rows = live(db, "select ...", params)in a page or
layout renders the rows and keeps them current: writes through
pyweb.db(andpyweb.models) announce their table after commit, each
distinct query re-runs once per change (bursts coalesced) and sends
rows to every page showing it only when they changed. Pages follow a
signed feed;db.notify("table")covers writes made elsewhere. With
RedisBusit works across processes, and any process can take over a
query from the page's signed description. - New docs page: Live data.
watch(lambda: value, handler)(frompyweb.browser) runs a
handler whenever a value changes, for work outside markup such as
redrawing a chart or saving a draft.pyweb addapplies packages'browserfield (browser versions of
files, modules turned off), so packages such as ethers install.- New examples: dashboard (live queries and a Chart.js chart from
npm), site (layouts, navigation, query parameters, a 404 page) and
ai-chat. The playground has the site and AI chat examples. - New docs page: Recipe: wallets & web3 (wallet sign-in with ethers and
eth-account). - A layout's
{children}can sit on a line of its own.
Fixed
- Subscriptions and cleanups set up in
on_mountnow end when the page
is left (they were never stopped).
Changed
- The experimental
pyweb.livemodule (in-memory live tables) is now
pyweb.livetable;pyweb.liveis the new live-query function. pyweb.npm(TypeScript declarations to dataclasses) is now
pyweb.dts, and its command ispyweb dts.pyweb buildno longer
writes an esm.sh import map; packages come frompyweb.lock.- The default Content Security Policy adds
worker-src 'self' blob:so
packages can start Web Workers. - The starter stylesheet styles Markdown and chat bubbles.
- The browser runtime is about 14 KB gzipped (was about 11 KB), for
client-side navigation and npm support. - New website design: a colour system where blue means the browser and
amber the server, self-hosted Inter / Bricolage Grotesque / JetBrains
Mono, dark code windows, and a landing page that shows each line of an
app with where it runs (worked out by the compiler) next to the real
compiled output. - The example apps and the
pyweb newstylesheet use the new palette.
PyWeb 0.3.0
Faster pages that feel like an app, apps that span several files, and
tools for editors, the browser and AI assistants.
Added
- Hydration. Pages now adopt the server-rendered DOM instead of
rebuilding it: nodes are kept, focus and text typed before the script
loads survive, and typed values reach their variables. When the server
HTML doesn't match, the page falls back to a client render and logs
a warning. The page root carriesdata-pw-mode="hydrated"or
"rendered". - Live updates.
publish(name, data)in server code pushes to every
page that calledsubscribe(channel_feed, handler), over Server-Sent
Events with a polling fallback. Feeds come fromchannel(name)while
rendering: signed, expiring, and positioned so no message published
after the render is missed.pyweb dev,pyweb serveand the ASGI
adapter stream;realtime.use_bus()shares the bus (e.g. Redis)
between processes. The chat example uses it instead of polling. - Multi-file apps.
from widgets import Card, save, COLORimports
components,@serverfunctions and constants fromwidgets.pyweb(or
ui/cards.pywebasui.cards). Each file keeps its own names in the
generated JavaScript, imported files run as real modules on the
server,pyweb buildcopies them intodist/, and errors name the
file they're in. Pages stay in the app file; circular imports and
duplicate server-function names are compile errors. - Editor support.
pyweb lspis a language server (stdio, standard
library only): compile errors and security warnings as you type,
hover showing where each name runs and why, completion for components,
props and tags, go to definition across files, and an outline. The new
VS Code extension (editors/vscode) adds highlighting and snippets and
starts it; releases attach a.vsix. Setup for Neovim and Helix is in
the command-line docs. - Playground on the website: edit an app and run it in the browser,
with the real compiler, server rendering,@serverfunctions,
sessions and live updates running on Pyodide. Shows the compiled
JavaScript and what runs where, reports errors with a fix, and shares
code as links. - MCP tools
pyweb_screenshot(open a page in headless Chromium,
run click/fill/press steps, get a screenshot, the page text, console
errors and hydration status) andpyweb_test(run the app's pytest
tests and report failures). New apps include a startertest_app.py. python -m pyweb.bench --max-ssr-ms N --max-compile-ms Nfails when
a benchmark exceeds its budget; CI runs it.
Removed
- The old
pyweb.lsphelper functions (complete,DocumentState,
complete_*,diagnostics_for_compile_error,hover(compiled, name),
boundary_lens); they described an earlier API and no editor used them.
Changed
import pywebno longer needs thesqlite3module (it's imported when
a SQLite database is opened), and password hashing falls back to a
pure-Python PBKDF2 on Pythons without OpenSSL. Both make PyWeb run on
Pyodide./__pyweb/eventsand/__pyweb/pollrequire a signed?feed=; the
unauthenticated?channel=form is gone, so channels are no longer
readable by anyone who guesses a name.- Server rendering caches compiled template expressions; rendering a
page is about twice as fast. - Generated page modules pass element children as a function and wrap
every child in a call ($t,$dyn), so nodes are created in document
order.
PyWeb 0.2.0
Tools for building PyWeb apps with AI assistants.
Added
pyweb mcp: a Model Context Protocol server (stdio, standard library
only) for Claude Code, Cursor, Claude Desktop, VS Code and other MCP
clients. Tools:pyweb_guide,pyweb_new_app,pyweb_check(errors
with line numbers and fix hints),pyweb_inspect,pyweb_compiled,
pyweb_renderandpyweb_call(session cookies persist between calls);
resources for the guide and templates; abuild_pyweb_appprompt.
Tested against the official MCP Python SDK.- An AI-oriented guide to writing
.pywebapps, shipped in the package. pyweb new --template blank|counter|todo|blog|auth|chat; new projects
includeAGENTS.mdandCLAUDE.mdfor coding agents.- The docs site publishes
llms.txtandllms-full.txt, and has a new
"AI assistants & MCP" page.
PyWeb 0.1.0
The first public release, published on PyPI as pyweb-stack. The
unpublished prototype was labelled "1.0" but only the simplest counter
pattern worked in a browser; this release rebuilds the compiler, runtime
and server around a design that works end to end, and is verified in a
real browser.
Language and compiler
.pywebparser with a character scanner: tags and expressions may span
lines, braces and strings nest inside{...},elifchains, HTML
comments, and exact line numbers (the Python half keeps the source's
line count; this also fixes crashes on Python 3.10/3.11).- Python → JavaScript translation of a documented subset with Python
semantics (truthiness,==on containers, negative indexing, floor
division/modulo, string/list/dict/set methods, Python exceptions).
Unsupported code is a compile error withfile:line. Verified
differentially against CPython. - State inference: signals (mutated by handlers or bound), computeds,
constants; initial values from literals, the browser, or the server.
Mutations (append, item assignment,del, …) are copy-on-write. - Components with props, defaults and
children;on_mounthook;
page titles;App(title=, stylesheets=, lang=). @servercalls from handlers compile to awaited typed RPC; async
propagates through handler calls.- Only values browser code reads are serialised; values derived from
server data are computed on the server. Secret-looking names read by
browser code are rejected.
Runtime
- New browser runtime: dependency-tracked signals, cached computeds,
owned effects with disposal, batching; keyed lists and conditionals in
marker-bounded regions; two-way binding for text, number, checkbox,
radio and select;javascript:URL blocking; RPC client with typed
errors.
Server
- Pages render per request with real server values;
request,
session,redirect,NotFound; typed route parameters. @serverfunctions are registered automatically indev,serve,
tests and ASGI.- New ASGI adapter:
pyweb.asgi.create_app. - RPC: JSON-only and same-origin checks, rate limiting, timeouts that
actually return on time, async functions, JSON conversion for
dataclasses/datetimes/Decimals. - Content-Security-Policy and security headers on HTML responses.
Tooling
pyweb dev: live reload and an in-browser compile-error overlay.pyweb buildproduces a self-containeddist/(source, manifest with
gzip sizes, hashed assets, working Dockerfile);pyweb serveruns it.- Token-aware JS minifier (the previous one altered string literals).
pyweb.testing.TestClientandpyweb.testing.servefor tests.- Clean
file:line: messagecompile errors from the CLI.
Data, auth, jobs
?placeholders work on Postgres and MySQL; lazy connection pools.- WebAuthn: challenge, origin and sign-count checks; DER signatures; uses
cryptographywhen installed. - Redis bus and queue work against real Redis.
Fixed
- Migrations: the
pyweb_migrationsjournal table can now be created on
MySQL (VARCHAR(255)key instead ofTEXT). pyweb servecrashed on startup (logger misconfiguration).- Production builds referenced a hashed runtime the page modules never
imported. - Installed packages were missing the browser runtime file.
transaction()did not roll back on Postgres/MySQL pools.sqlite:///file.dbpointed at the filesystem root.RedisBus.since()duplicated and mis-ordered messages;
RedisQueue.drain(timeout=0)blocked forever.- Jobs raising
TypeErrorran twice. Query.order_by()ignored-fieldand did not validate a single field.- The deploy Dockerfile ran a non-existent
serve --dirflag.
Removed
- Modules the framework no longer uses:
pyweb.components(pure-Python
element API),pyweb.routing(unused router),pyweb.reactive
(server-sideSignal/Computed/Effect/live), and
pyweb.compiler.placement(superseded by the compiler's boundary checks). - The
@browserand@shareddecorators, which had no effect. pyweb.compiler.rpc.client_stub/server_handlerand unused AST classes.docs/BUGLOG.md(it described the prototype's internals) and the committed
website/dist/; the Pages workflow builds the site.- The regex-based codegen, the virtual test client that only recorded
clicks, documentation "snippets" that did not use PyWeb, island and
streaming-SSR string helpers without runtime support, and the no-op
--security-scanflag.