PyView: Real-time Python Web Apps
Episode Deep Dive
Guest introduction and background
Larry Ogrodnek is a seasoned software engineer with a deep background in distributed systems and web development. He describes himself as a "recovering Java and Scala developer" who found his way to Python and fell in love with its ecosystem. Currently, he works at a healthcare technology company called Fathom, where he deals with medical coding automation.
Larry is the creator of PyView, a project that aims to bring the server-side rendering magic of Phoenix LiveView to the Python world. His motivation stems from a desire to simplify the modern web stack, moving away from the complexity of managing separate Single Page Application (SPA) frontends and returning to a model where the server drives the state and logic, while still maintaining the rich interactivity users expect.
What to Know If You're New to Python
If you are just starting with Python, this episode dives into some advanced web development concepts. Here are a few things to keep in mind to help you follow along:
- Async and Await: The guest mentions using
asyncandawaitheavily. This is Python's syntax for writing concurrent code, allowing the server to handle many connections (like WebSockets) efficiently without waiting for each one to finish before starting the next. - WebSockets: Unlike standard HTTP requests where you ask for a page and get a response, a WebSocket is a persistent, two-way connection between your browser and the server. This is how PyView pushes updates to the screen instantly.
- Server-Side Rendering (SSR): This means the HTML for a webpage is built on the server before being sent to the browser. PyView takes this a step further by updating that HTML dynamically from the server.
- Starlette: You will hear references to Starlette, which is a lightweight implementation of the ASGI (Asynchronous Server Gateway Interface) standard. It serves as the foundation for many modern Python web frameworks, including FastAPI and PyView.
Key points and takeaways
1. The Core Philosophy of PyView: LiveView for Python The central theme of the episode is the introduction of PyView, a Python implementation of the Phoenix LiveView paradigm. The core idea is to enable developers to build rich, interactive web applications without writing client-side JavaScript. Instead of creating a separate React or Vue frontend that talks to a JSON API, you write Python code that manages the state on the server. When that state changes, the server calculates exactly what HTML needs to update and pushes those changes to the browser. This drastically reduces the "cognitive load" of switching between languages and managing two separate codebases.
2. How It Works: The "Mount" and "Render" Cycle Larry explains the lifecycle of a PyView component. It starts with a mount function, which initializes the state (a simple Python dictionary). Then, a render method uses a template to turn that state into HTML. The magic happens when an event occurs, like a user clicking a button. This event is sent over a WebSocket to the server, which runs a handler function (e.g., handle_event). The server updates the state, re-renders the view, diffs it against the previous version, and sends only the changes back to the client. This makes the payload incredibly small and the updates very fast.
3. Leveraging the Battle-Tested Phoenix JavaScript Client One of the smartest decisions Larry made with PyView was not to rewrite the client-side library. Instead, PyView implements the server-side protocol required to talk to the existing, mature Phoenix LiveView JavaScript client. This means PyView benefits from years of optimization, edge-case handling, and reliability engineering that the Elixir community has already poured into their frontend library. It allows Python developers to stand on the shoulders of giants while staying firmly in their preferred language.
4. Escaping the "JSON API Churn" A major pain point discussed is the "churn" involved in maintaining SPAs. In a traditional setup, if you want to add a new field to a form, you have to modify the database model, the API serialization layer, the API client in the frontend, and finally the frontend UI component. With PyView, because the HTML is rendered on the server where the data lives, adding a field is often just a matter of updating the template and the database query. This unification of the stack can significantly speed up development iterations.
5. PyView vs. HTMX: Stateful vs. Stateless The conversation draws an important distinction between PyView and htmx. While both aim to reduce JavaScript usage, htmx is primarily based on stateless HTTP requests, you click a button, it fires a request, and swaps HTML. PyView, however, maintains a stateful WebSocket connection. This makes PyView better suited for high-frequency updates (like a stock ticker or a chat room) or applications where the server needs to push data to the client without the user initiating an action (like a multiplayer game).
6. The "Just" Command Runner Larry shares his enthusiasm for a tool called just, which he uses to manage tasks in his projects. It is described as a modern alternative to make. It allows you to define a justfile with recipes for common tasks (like just run, just test, or just lint). Unlike make, which has idiosyncratic syntax (like requiring tabs), just is designed to be a command runner first, with support for arguments, different shells, and .env file loading. It is a great productivity booster for Python projects.
7. Templating and the Promise of PEP 750 Currently, PyView uses a template engine called ibis (created by Darren Mulholland), which feels similar to Jinja2 or Django templates. However, Larry and Michael discuss the excitement around PEP 750 (Template Strings). This proposed feature for Python would allow for tagged strings (e.g., html"<div>...</div>"), which could enable a developer experience similar to JSX in React. This would allow templates to be type-checked and validated by Python tools, potentially revolutionizing how HTML is generated in Python web frameworks.
8. Real-World Complexity: The Map Example To prove that PyView isn't just for simple counters, Larry built a map integration using Leaflet.js. The challenge was: how do you control a complex, client-side heavy library like a map from the server? He utilized a feature called "hooks" in LiveView. This allows the server to push data (like a set of coordinates) to the client, where a small snippet of JavaScript picks it up and updates the Leaflet map. This demonstrates a hybrid approach where you write minimal JS only for specific DOM interactions while keeping the business logic on the server.
9. Deployment is "Just Python" Despite the advanced real-time capabilities, deploying a PyView app is surprisingly standard. Because it is built on top of Starlette and runs via Uvicorn, it can be deployed exactly like a FastAPI or Django app. You don't need a separate Node.js server to host the frontend. You can containerize it with Docker, throw it on a VPS, or use a Platform as a Service (PaaS). The discussion highlights how using uv (the fast Python package manager) further simplifies the setup and dependency management.
10. Managing State and Sessions A critical technical detail covered is how PyView handles state. Each user's connection spawns a lightweight process (or coroutine in Python's case) on the server that holds their specific state. This is different from a standard "stateless" web app where the database is the only source of truth. Larry explains that while this uses memory on the server, modern servers can handle thousands of concurrent WebSocket connections easily. For scaling horizontally, you would use a Pub/Sub system (like Redis) to synchronize events across multiple server processes.
Interesting quotes and stories
"Building on the web is like working with the perfect clay. It’s malleable and can become almost anything. But too often, frameworks try to hide the web’s best parts away from us." -- Michael Kennedy
"I consider myself a recovering Java and Scala developer. ... I was doing a lot of distributed systems work and then I found Python and just really fell in love with the ecosystem and the community." -- Larry Ogrodnek
"Every time you change the shape of your data, you're changing it in the database. You're changing the API. You're changing the serialization. You're changing the client-side model. You're changing the client-side validation." -- Larry Ogrodnek (on the "churn" of SPAs)
"It's basically just a Starlette app. So anywhere that you can run a Python Starlette app or Uvicorn, you can run this." -- Larry Ogrodnek
Key definitions and terms
Diff/Diffing: The process of comparing two versions of a document (or data structure) to find the differences. PyView uses this to send only the changed parts of a webpage to the browser, rather than reloading the whole page.
Pub/Sub (Publish/Subscribe): A messaging pattern where senders (publishers) do not program the messages to be sent directly to specific receivers (subscribers). This is used in PyView to allow different users (or different parts of the app) to talk to each other in real-time, like in a chat room.
HEEx (HTML + Embedded Elixir): The templating language used in Phoenix LiveView. PyView implements a similar concept using Python templating to ensure it can efficiently track changes in the dynamic parts of the HTML.
+1
Latency Simulator: A tool (often built into browser dev tools or the framework) that artificially delays network requests. This is crucial for testing real-time apps to see how they "feel" for users with slow internet connections.
Learning resources
Here are some resources to learn more and go deeper into the topics discussed in this episode:
- Python Jumpstart by Building 10 Apps: If you are newer to Python and want to build a solid foundation before tackling async web frameworks, this course is the perfect starting point.
- Building Data-Driven Web Apps with Flask and SQLAlchemy: While this course uses Pyramid, it teaches the fundamental concepts of building server-side Python web applications that drive the kinds of projects discussed in this episode.
- Just Enough Python for Data Scientists: If you are coming from a data background (like Larry's work at Fathom) and want to clean up your code structure, this course helps bridge the gap between scripting and engineering.
Overall takeaway
PyView represents a compelling shift in how Python developers can build modern, interactive web applications. By porting the proven architecture of Phoenix LiveView to Python, Larry Ogrodnek has provided a way to escape the complexity of the "frontend vs. backend" divide. It empowers developers to build rich, real-time user interfaces while keeping their logic, state, and data securely on the server, all within the language they love. Whether you are building internal tools, dashboards, or full-scale products, PyView suggests a future where "Full Stack" doesn't have to mean "Full Complexity."
Links from the show
Larry Ogrodnek: hachyderm.io
pyview.rocks: pyview.rocks
Phoenix LiveView: github.com
this section: pyview.rocks
Core Concepts: pyview.rocks
Socket and Context: pyview.rocks
Event Handling: pyview.rocks
LiveComponents: pyview.rocks
Routing: pyview.rocks
Templating: pyview.rocks
HTML Templates: pyview.rocks
T-String Templates: pyview.rocks
File Uploads: pyview.rocks
Streams: pyview.rocks
Sessions & Authentication: pyview.rocks
Single-File Apps: pyview.rocks
starlette: starlette.dev
wsproto: github.com
apscheduler: github.com
t-dom project: github.com
Watch this episode on YouTube: youtube.com
Episode #535 deep-dive: talkpython.fm/535
Episode transcripts: talkpython.fm
Theme Song: Developer Rap
🥁 Served in a Flask 🎸: talkpython.fm/flasksong
---== Don't be a stranger ==---
YouTube: youtube.com/@talkpython
Bluesky: @talkpython.fm
Mastodon: @talkpython@fosstodon.org
X.com: @talkpython
Michael on Bluesky: @mkennedy.codes
Michael on Mastodon: @mkennedy@fosstodon.org
Michael on X.com: @mkennedy
Episode Transcript
Collapse transcript
00:00
00:02
00:05
00:10
00:15
00:21
00:24
00:47
00:52
00:58
00:59
01:03
01:05
01:09
01:13
01:13
01:17
01:22
01:26
01:27
01:27
01:32
01:36
01:38
01:43
01:50
01:56
02:04
02:09
02:16
02:22
02:29
02:31
02:38
02:41
02:43
02:45
02:51
02:54
02:58
03:03
03:09
03:11
03:14
03:18
03:20
03:22
03:26
03:28
03:34
03:35
03:36
03:37
03:40
03:43
03:47
03:50
04:14
04:19
04:24
04:26
04:28
04:31
04:32
04:33
04:36
04:37
04:37
04:39
04:44
04:48
04:54
05:03
05:09
05:13
05:16
05:20
05:21
05:21
05:22
05:26
05:27
05:29
05:31
05:34
05:41
05:48
05:53
05:54
05:59
06:08
06:10
06:18
06:20
06:23
06:24
06:26
06:26
06:30
06:34
06:38
06:40
06:42
06:45
06:48
06:51
06:52
06:54
06:58
06:59
07:05
07:10
07:14
07:15
07:18
07:22
07:28
07:29
07:30
07:35
07:36
07:41
07:45
07:46
07:47
07:48
07:50
07:50
07:54
07:55
08:02
08:02
08:04
08:05
08:06
08:07
08:09
08:10
08:15
08:24
08:30
08:33
08:37
08:38
08:40
08:42
08:46
08:49
08:53
08:56
08:58
09:04
09:14
09:23
09:27
09:33
09:38
09:41
09:46
09:49
09:55
10:00
10:05
10:11
10:14
10:19
10:28
10:34
10:43
10:44
11:13
11:38
11:44
11:47
11:51
12:02
12:05
12:11
12:16
12:20
12:23
12:38
12:48
12:50
12:55
12:57
13:06
13:10
13:13
13:27
13:36
13:37
13:39
13:42
13:43
13:44
13:49
13:52
13:53
13:57
13:59
14:01
14:05
14:06
14:09
14:11
14:13
14:19
14:22
14:24
14:26
14:28
14:30
14:32
14:35
14:37
14:41
14:43
14:47
14:51
14:58
15:00
15:04
15:05
15:10
15:11
15:14
15:20
15:29
15:34
15:39
15:44
15:49
15:51
15:54
15:57
16:01
16:02
16:03
16:09
16:16
16:18
16:21
16:25
16:28
16:32
16:38
16:40
16:45
16:51
16:53
16:56
16:58
17:01
17:05
17:08
17:15
17:17
17:21
17:25
17:26
17:28
17:31
17:32
17:34
17:38
17:43
17:47
17:48
17:49
17:53
17:58
18:02
18:06
18:10
18:17
18:21
18:24
18:26
18:32
18:32
18:35
18:40
18:44
18:48
18:52
18:55
18:57
19:04
19:09
19:20
19:23
19:28
19:32
19:40
19:48
19:51
19:54
19:55
20:01
20:04
20:08
20:09
20:11
20:17
20:20
20:22
20:26
20:28
20:30
20:34
20:38
20:42
20:47
20:53
20:59
20:59
21:05
21:09
21:09
21:10
21:13
21:16
21:20
21:24
21:26
21:28
21:32
21:35
21:35
21:38
21:46
21:49
21:50
21:56
22:00
22:03
22:08
22:09
22:09
22:12
22:16
22:29
22:35
22:43
22:44
22:48
22:51
22:55
22:58
23:04
23:08
23:09
23:14
23:20
23:24
23:29
23:36
23:40
23:47
23:51
23:59
24:03
24:08
24:11
24:13
24:15
24:19
24:23
24:27
24:31
24:36
24:42
24:46
24:47
24:48
24:51
24:55
24:58
25:00
25:03
25:05
25:07
25:09
25:12
25:13
25:17
25:20
25:23
25:27
25:28
25:29
25:30
25:36
25:40
25:43
25:46
25:49
25:50
25:54
25:57
25:59
26:02
26:08
26:09
26:12
26:18
26:21
26:23
26:27
26:29
26:29
26:33
26:37
26:39
26:43
26:45
26:46
26:50
26:51
26:56
26:59
27:02
27:03
27:07
27:14
27:15
27:18
27:22
27:27
27:28
27:29
27:32
27:39
27:42
27:46
27:48
27:57
27:58
28:02
28:05
28:08
28:13
28:16
28:20
28:22
28:24
28:26
28:27
28:29
28:31
28:32
28:35
28:39
28:42
28:44
28:47
28:49
28:49
28:50
28:56
29:02
29:07
29:11
29:14
29:19
29:26
29:29
29:34
29:39
29:44
29:49
29:55
30:00
30:08
30:15
30:17
30:22
30:30
30:34
30:41
30:42
30:44
30:46
30:47
30:52
30:57
31:01
31:04
31:15
31:21
31:24
31:27
31:29
31:34
31:38
31:42
31:47
31:50
31:55
32:03
32:06
32:08
32:18
32:23
32:24
32:26
32:29
32:35
32:38
32:41
32:44
32:48
32:50
32:53
32:54
32:58
32:59
33:01
33:03
33:07
33:11
33:16
33:17
33:17
33:20
33:24
33:27
33:31
33:31
33:32
33:36
33:39
33:43
33:47
33:48
33:49
33:51
33:52
33:53
33:56
34:01
34:03
34:04
34:08
34:11
34:16
34:19
34:20
34:23
34:27
34:29
34:33
34:35
34:37
34:39
34:41
34:43
34:46
34:49
34:52
34:57
35:01
35:06
35:07
35:07
35:08
35:12
35:14
35:18
35:19
35:21
35:21
35:21
35:23
35:24
35:28
35:30
35:35
35:38
35:42
35:45
35:52
35:57
36:04
36:17
36:25
36:25
36:26
36:27
36:35
36:36
36:39
36:40
36:49
36:53
36:54
36:57
37:00
37:02
37:07
37:12
37:14
37:19
37:21
37:24
37:28
37:30
37:34
37:35
37:37
37:42
37:44
37:46
37:49
37:54
37:59
37:59
38:06
38:07
38:08
38:15
38:18
38:23
38:27
38:28
38:29
38:33
38:36
38:37
38:38
38:43
38:47
38:48
38:50
38:52
38:55
38:56
39:01
39:06
39:08
39:12
39:17
39:19
39:21
39:25
39:27
39:29
39:32
39:37
39:39
39:41
39:43
39:48
39:53
39:56
39:58
40:03
40:04
40:07
40:12
40:15
40:17
40:19
40:27
40:31
40:36
40:42
40:47
40:51
40:55
40:59
41:04
41:08
41:14
41:21
41:28
41:34
41:41
41:43
41:44
41:47
41:50
41:57
41:58
42:00
42:01
42:04
42:08
42:09
42:11
42:15
42:17
42:19
42:20
42:25
42:29
42:31
42:34
42:37
42:40
42:43
42:44
42:48
42:54
42:55
42:58
43:01
43:01
43:04
43:08
43:11
43:15
43:19
43:22
43:24
43:27
43:28
43:33
43:36
43:41
43:43
43:43
43:45
43:52
43:59
44:05
44:08
44:09
44:19
44:21
44:30
44:38
44:39
44:42
44:49
44:51
44:54
44:57
44:59
45:02
45:06
45:14
45:20
45:23
45:27
45:31
45:34
45:37
45:43
45:44
45:47
45:48
45:53
45:59
46:05
46:07
46:11
46:17
46:19
46:21
46:23
46:27
46:29
46:32
46:36
46:39
46:45
46:49
46:53
46:55
46:57
47:04
47:10
47:13
47:17
47:19
47:21
47:23
47:28
47:32
47:33
47:36
47:38
47:41
47:42
47:43
47:46
47:53
47:55
48:00
48:03
48:07
48:08
48:14
48:17
48:19
48:20
48:21
48:21
48:27
48:31
48:35
48:38
48:45
48:47
48:49
48:54
49:02
49:03
49:04
49:07
49:10
49:11
49:16
49:19
49:20
49:22
49:23
49:24
49:27
49:32
49:35
49:39
49:44
49:48
49:53
49:59
50:00
50:04
50:05
50:06
50:11
50:20
50:27
50:38
50:41
50:43
50:51
50:55
50:58
50:59
51:02
51:04
51:05
51:08
51:11
51:16
51:18
51:22
51:23
51:27
51:28
51:29
51:32
51:34
51:36
51:37
51:43
51:46
51:48
51:55
51:59
52:00
52:03
52:05
52:05
52:13
52:18
52:23
52:25
52:31
52:32
52:39
52:46
52:51
52:57
53:02
53:05
53:08
53:10
53:13
53:17
53:22
53:27
53:30
53:35
53:37
53:41
53:42
53:46
53:47
53:50
53:53
53:56
54:01
54:03
54:09
54:13
54:13
54:17
54:21
54:22
54:24
54:25
54:27
54:28
54:34
54:35
54:36
54:43
54:46
54:51
54:53
55:06
55:12
55:15
55:16
55:17
55:20
55:20
55:22
55:23
55:23
55:29
55:34
55:40
55:43
55:46
55:53
55:55
56:00
56:04
56:11
56:12
56:13
56:18
56:23
56:26
56:32
56:35
56:39
56:43
56:49
56:53
56:56
56:56
56:59
57:01
57:08
57:12
57:12
57:15
57:18
57:21
57:24
57:29
57:32
57:35
57:35
57:38
57:39
57:42
57:48
57:53
57:57
58:01
58:05
58:08
58:10
58:12
58:13
58:16
58:21
58:28
58:33
58:38
58:41
58:46
58:52
58:54
59:00
59:01
59:06
59:07
59:08
59:10
59:11
59:15
59:17
59:19
59:23
59:24
59:24
59:26
59:30
59:32
59:34
59:35
59:35
59:39
59:42
59:45
59:49
59:52
59:56
59:59
01:00:02
01:00:04
01:00:06
01:00:12
01:00:16
01:00:19
01:00:23
01:00:27
01:00:29
01:00:32
01:00:35
01:00:39
01:00:41
01:00:45
01:00:47
01:00:53
01:00:55
01:01:00
01:01:07
01:01:09
01:01:10
01:01:12
01:01:13
01:01:16
01:01:19
01:01:23
01:01:27
01:01:30
01:01:32
01:01:33
01:01:35
01:01:35
01:01:36
01:01:38
01:01:40
01:01:41
01:01:42
01:01:42
01:01:43
01:01:46
01:01:49
01:01:51
01:01:52
01:01:55
01:01:58
01:01:59
01:02:04
01:02:09
01:02:10
01:02:12
01:02:14
01:02:16
01:02:17
01:02:19
01:02:20
01:02:24
01:02:30
01:02:32
01:02:36
01:02:39
01:02:40
01:02:44
01:02:46
01:02:49
01:02:52
01:02:53
01:02:57
01:02:59
01:03:03
01:03:07
01:03:10
01:03:11
01:03:17
01:03:20
01:03:22
01:03:26
01:03:27
01:03:33
01:03:35
01:03:40
01:03:41
01:03:46
01:03:47
01:03:48
01:03:54
01:03:57
01:04:01
01:04:03
01:04:06
01:04:10
01:04:12
01:04:13
01:04:18
01:04:22
01:04:24
01:04:27
01:04:30
01:04:33
01:04:34
01:04:35
01:04:42
01:04:47
01:04:51
01:04:55
01:05:00
01:05:03
01:05:16
01:05:24
01:05:30
01:05:32
01:05:36
01:05:38
01:05:39
01:05:41
01:05:44
01:05:46
01:05:50
01:05:55
01:06:00
01:06:01
01:06:02
01:06:08
01:06:09
01:06:10
01:06:11
01:06:14
01:06:19
01:06:21
01:06:21
01:06:24
01:06:25
01:06:29
01:06:31
01:06:34
01:06:37
01:06:37
01:06:38
01:06:39
01:06:39
01:06:40
01:06:41
01:06:44
01:06:45
01:06:46
01:06:48
01:06:55
01:07:01
01:07:04
01:07:06
01:07:10
01:07:11
01:07:13
01:07:14
01:07:17
01:07:20
01:07:22
01:07:23
01:07:24
01:07:35 I thought of me, and we ready to roll, upgrading the code, no fear of getting whole, we tapped
01:07:46 into that modern vibe, overcame each storm, talk Python and me, I-sync is the norm.
01:07:54 you


