Anvil Full Stack Web with Nothing but Python | Meredydd Luff

This video features Meredydd Luff at DjangoCon Europe 2021 in Online.

Anvil Full Stack Web with Nothing but Python | Meredydd Luff
0:55:37
Published June 27, 2021
760 views

Building a modern web app requires so much: HTML, CSS, JS, Python, SQL, React, Bootstrap, Webpack...and Django, of course ;). What if we could build a better abstraction?

Our answer is Anvil: a full-stack Python environment where everything is a Python object, from your UI components to your database rows. In this talk, I'll walk you through how and why we constructed this new approach to the web.

We'll start with a question: Why is web programming hard? It's because your data takes so many forms: database rows, Python objects, JSON on REST, JS objects, HTML DOM, and finally pixels. Most of a web developer's job is translating between these awkwardly different representations. Frameworks like Django help, but now you have a stack of leaky abstractions: web frameworks, ORMs, JS frameworks, CSS frameworks, build tools... These frameworks help you go faster, but they double the amount you need to know!

So I'll show our stab at an answer: A framework where everything is a Python object, requests to the server are function calls, and Python is a browser-side language. I'll talk about running Python in the browser. I'll talk about full-stack autocompletion. There will even be live coding.

And then I'll talk about how it breaks. Because every abstraction breaks, and this abstraction is bigger than most. So I'll touch on what it means to go down with the ship, to use an ejector seat, or to open an escape hatch.

Join me for a rollicking tour of a new way to approach the web!

Summary

Meredydd Luff argues that conventional web development forces the same data through too many representations—database rows, Python objects, JSON, JavaScript objects, HTML, and CSS—creating repetitive translation work and complex framework “magic.” Anvil addresses this by making the application Python throughout: Python drives the browser UI, server functions are callable directly from the client, and database rows can pass from storage to the interface without being converted to JSON. He demonstrates building and deploying a database-backed application, then explains the Python-to-JavaScript compiler’s suspension mechanism for turning blocking Python-style calls into non-blocking browser code, along with Anvil’s client-side autocomplete. He also says Anvil supports escape hatches such as external Django code, ordinary HTTP APIs, custom CSS, and JavaScript libraries, so developers can adopt it incrementally rather than abandoning existing systems.

Key takeaways

  • Traditional web stacks repeatedly translate data between databases, Python, JSON, JavaScript, HTML, and CSS, which increases complexity and duplicated effort.
  • Anvil lets developers build browser interfaces, server functions, and database-backed applications using Python objects and function calls rather than separate JavaScript and JSON layers.
  • Anvil compiles Python to JavaScript and uses suspension objects to preserve ordinary blocking-style Python code while running asynchronously in the browser.
  • Its editor provides autocomplete across UI components, database columns, and server functions by analyzing Python code and shared application metadata.
  • Anvil can integrate with existing Django or other external code, HTTP APIs, custom CSS, and JavaScript libraries when a full migration is not practical.

Summarised automatically from the transcript.

Transcript

8,876 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:07

Speaker 1: My name is Meredith and I am here to talk about doing all your web development in Python. And now you might think I'm a few years late to this concept. Not only do we have Python web frameworks, we have whole conferences about them. But there is a lot more to the web than just server-side frameworks. And most of the pain comes from things that Django can't help you with. Let me explain. Let's think about the web from the perspective of your data. So if you're writing a web application , your data probably starts off as a row in a database accessed by SQL.

0:53

Speaker 1: It of course won't stay there. You will pull it out of that database and transform it into objects in your server-side Python code accessed by methods and attributes. But of course you will then turn around and immediately re-represent that data as JSON over HTTP. So you know, large number of endpoints, really weirdly limited set of verbs, get, post, put, delete. But on the other end of that HTTP connection. . . Is a JavaScript application, which will be reconstituting those payloads into JavaScript objects with their own methods and their own attributes Which it will then turn around and render into HTML DOM, which is a different set of objects entirely, and then use CSS

1:39

Speaker 1: to render into pixels. And That is an awful lot of representations of your data. And indeed, most of the day-to-day work of web development is converting your data between these different representations. And this is tedious and repetitive, and so naturally we like good lazy programmers want to automate it, and so we invent web frameworks. But the problem is the tools we invent to help us do that translation. They speed us up, but they are often magic, and I don't necessarily say that in a good way. So to take an example, let's take a friendly look at the Django ORM, which presumably

2:27

Speaker 1: you guys are mostly familiar with. So Django ORM automates the translation of those bottom two layers of the stack. So instead of writing SQL statements like this against your database, here you know we're joining two tables together and applying a filter and so on. Instead you can write this Python expression. And this is great. It means that you don't have to suddenly depart and start writing source code in another programming language. And it does have a bunch of other automation as well, but the process of turning this Python expression into that SQL is I mean it 's quite deep and implicit.

3:13

Speaker 1: And I mean what we've what this really is is twisting or one might even say abusing the semantics of Python in order to represent the semantics of that SQL statement. And so to understand how this works properly and to use it at an advanced level, you're going to have to understand Python. and SQL and how the Django ORM does that transformation in order to have a good idea of how to drive it. And This, you know, it does like this speeds up the typing, certainly. Uh but

3:59

Speaker 1: We've actually increased the number of some uh the the amount of semantics that we need to understand. And that's probably not a problem if you did this once. But of course, that's not what we do. We have translation layers like this in all layers of our stack and This causes us to have a pretty bad day. We've got these ORMs that help us translate database rows into server-side objects. We've got web frameworks helping us represent those service those server-side objects over HTTP, of course, Django bundles. These two two things together, but that's far from all. On the other side, we've got JavaScript frameworks like Angulus Resources helping us reassemble those objects into JavaScript, those HTTP payloads into JavaScript objects. We've got templating engines like React.

4:45

Speaker 1: uh or possibly ginger too or possibly both of them in the same application different places helping us translate those objects and represent render them into HTML DOM we've got CSS frameworks like Bootstrap helping us uh represent that DOM as pixels. And all of these are that slightly dodgy type of magic that abuses the semantics of one layer to represent one of the adjacent layers. And so you end up needing to know everything that's in those blue boxes and everything that's in those orange boxes in order to get anything done at all. Well, we're Pythonistas here. How does that stack up against the Zen of Python? Simple is better than complex? I mean that was not a simple system.

5:32

Speaker 1: There is an enormous number of moving parts there. And in order to make development faster, we just add more moving parts. Here's another one. If the implementation is hard to explain, it's a bad idea. Well again, the insides of JavaScript frameworks are famously gnarly because they are. You need to understand. Both levels of representation and how the framework's doing its thing, and how the framework accomplishes doing that translation in any kind of performant way, you need to understand all of that. Which, according to this Cohen, this Python Cohen would say it's a bad idea, and I'd be inclined to agree. Here's a famous one.

6:18

Speaker 1: There should be one and preferably only one way to do it. Do I need to say more at this point or or or can we just wince and move on? So Looking out over the amount of pain that this imposes upon people doing development for the web. We set out to ask, okay, so how could we do web development better? And because if you look at this, just building a new web framework isn't actually going to solve the problem. Because if you just swap out one of these orange boxes, you haven't changed the biggest problem, which is that you need to translate between all these different representations and you still need all these other frameworks on top.

7:07

Speaker 1: So we said, well. This isn't working. Let's see if we can replace this stack with one coherent representation, and that we chose Python. So we set out to construct a web framework in which everything is a Python object. So we built a user interface toolkit that lets you drive your UI as Python objects. We run Python in the web browser. And now, uh, instead of generating code in a different programming language and then having it styled by a third programming language in order to render your UI. you just manipulate some Python objects.

7:53

Speaker 1: Python on the server, of course, but if you've got Python on the server and Python on the client, you don't need to mash all your data into JSON anymore. You can just make a function call. We even did our own ORM, built a database with uh fairly opinionated uh semantics about database rows that makes them act like Python objects. And the advantage of this is that you can then pass a database row or sorry a piece of data all the way from your database to your front-end UI without having to change its representation at all. And it turns out that this can really help you develop applications. But that's all theoretical. Let me show you what that looks like in practice.

8:49

Speaker 1: Okay. Good news, the screen share works. Bad news I'm in the wrong place for the camera. Hello. So here we are in the Anvil editor and I'm going to show you how to build an application with Anvil. I'm going to Build a fairly simple sort of hello world application that greets the user by name. So here this here is a web page, and this here is a toolbox of things I can put onto that web page. So if I want to put a title on the page, I take a label component and try and drop it up to the title here and then I can change its text property. And that's how I can put some text onto my page. All right, let's be a little bit more interactive. I'm gonna uh give the user a form to enter their name and

9:36

Speaker 1: them so I put a card on there I put a label on here and prompted prompting the user to enter their name uh I'm then going to add a text box in here so they can answer that question. I'm going to call this name box. Every component has a variable name so we can access it from code. And I'm going to put a button here for them to click when they're done. Make that a little bit more prominent. And you can see what's going to happen. They're going to enter their name, they're going to click a button, and a message is going to appear. So the final thing we need is a label for that message to appear on. I'm going to call it message label because too much creativity in programming is frowned upon. We can edit some more text properties. We can make it centered, we can make the font big, and so on.

10:24

Speaker 1: So I have just built a web page. And obviously that's an awful lot faster than it would have been with HTML and CSS. But the really cool thing is that everything I've built here is backed by Python. So if I double-click this button, I'm now editing the Python code that runs when that button gets clicked. And you can see all the things I dragged and dropped onto the page are now available as variables in my Python code Which means that if I want to set the text on that message label based on the text of the name box, I go self. message label dot text equals hello and then self. namebox. text. And that's it. If I run it, I

11:11

Speaker 1: have an interactive application. It's actually already live on the web. Let's give it a nice public UL URL URL. Oh. There we go. Hello DJE, save you from typing. So you go to hello-dje. angle. app And this app is already live on the internet. So uh you're watching this live. If you don't believe me, go check it out. So We've just built and deployed a web application entirely in Python. And that was pretty quick. Everything you've seen so far is actually executed. Client side.

11:56

Speaker 1: We compile this Python to JavaScript and run it in the web browser, which is great for snappy responses, but obviously any serious application is going to need some server-side code as well. And so let's add some. So we can add a server module. Now, this is a Python module that runs on the Anvil server. Perfectly ordinary Python module, perfectly ordinary Python function But what I can do is I can tag this function andvil. server. callable. Let me sorry, zoom that in a little bit for you. There we go. I can tag this function andvil. server.

12:41

Speaker 1: callable. And what that says is this function that's running on the server, I should be able to call it from the browser. So if I go back to my browser code, When the button gets clicked, as well as updating the UI, I go amvil. server. call, I call that say hello function. You can see it wants an argument which is the name, so I can pass in the text from that name box. And now if I run this code. You can see we've got some output in the logs, and that print statement is now running on the server, driven by that user interface I just Built in Python. And now we're really in business because of course there's limits to what you can do here.

13:26

Speaker 1: The limits what you compile to JavaScript, you're not going to be you know loading files, files from the file system, you're not going to be doing ordinary TensorFlow, but here this is a real CPython environment. You can do whatever you like. So, you know, you want to talk to a database, import Psycho PG2, go. You want to do some numerical computation? Import NumPy. You want to do some machine learning? Import PyTorch TensorFlow, knock yourself out. In practice, of course, the first thing everybody asks is, so how do I set up a database? And so we have a built-in database for you. It's called the Data Table Service. And I'm it's it's backed by Postgres, so you can stick uh gigabytes into this thing if you need to. I'm going to be a little bit less uh ambitious. I'm just going to make a table for all the people who

14:12

Speaker 1: enter their names. So we're going to want a text column for the name and then let's have a date time column for when it was it was entered. And that's it, I've just built my my database. So this table is apptables. visitors. It's accessible from my code just like uh those buttons and text boxes and so on. one were. So if I go back to my server code, as well as printing this message into the logs, I can take apptables. visitors, I can add a row to it, I can fill out the name column with the name that's been passed in, I can fill out the when column with Date time. now, which of course I need to import. And now if I run this

15:01

Speaker 1: Enter a couple of names. If I go to the database, I can see that those names have been entered. So I've just built and deployed a database backed web application with nothing but Python in a really really short period of time. A reminder to anybody who fancies graffitiing on the screen, hello dash dje. anvil. app. Keep it as a code of contact, please. I'm going to show you one more thing. before uh we move on uh and take you behind the scenes, uh which is how to display data uh on my user interface. So The first thing I'm going to do is of course give ourselves a way of getting that data to the UI. So I'm going to make a new callable function

15:47

Speaker 1: that gets the visitors uh and it's going to just return app tables. visitors. search which is actually just returns a lazy iterator over in this case every row in the database in that table, sorry. And then on the user interface, I thought I first need to give myself somewhere to show it. So I'm going to put another card on my screen. I'm going to put a data grid on that card and I'm going to set a a set of a column. There's a column for name. That's going to pull the name key out of every element. I'm going to pull that set of a column for when which is going to be pull out the when key and we don't need that third column at all and we can adjust these and shrink this column down a little bit Okay, now inside this data grid

16:34

Speaker 1: is a component called a repeating panel. And what that does is it has a property called items. This again it's a this is a Python object, so it's got a Python attribute called items. that you can set to any iterable object in Python. And when you do that, it will instantiate this chunk of user interface. for every element in that iterator. So what I can do is here in the constructor, I can set the items of that repeating panel to be the return value of that get visitors function. So just like that, I'm now displaying some data on the screen.

17:20

Speaker 1: Hello DJ and Samuel. Good on you. So we've just built a database-backed uh read and write web application and again and deployed it on the web with nothing but Python. I'm going to do one more thing before we go behind the scenes. Because this is kind of ugly. You can see what's happened, right? It's just, it's pulled out those keys from each row and it has called stir on them, which is a very sensible default. But uh This is uh you know the default stir of a date time object is fairly grungy. So let's go in and change what appears in that when column. So instead of the default behavior, I'm going to put a label in here. I'm going to add a data binding so that the text of this label is set to this Python expression.

18:07

Speaker 1: And here, self. item is the Python object that is the row from the visitors table. We've passed it all the way from the server to the client. And I can pull out uh the when column uh with ordinary square bracket lookups and it is of course a date time object which means that I can stir-of-time it And so now when I run this, we have some slightly nicely, uh slightly more nicely uh formatted dates. Again, this application is completely live, hellodja. amble. app, go check it out. So I hope you've enjoyed that little uh whistle stops

18:53

Speaker 1: tour of uh how an AMBL application is built and how some of it works. Let me go take you behind the scenes So that demonstration I hope gave you a feel for what it means to make your entire application one representation which is say Python objects and some of the power that that can give you I'm gonna spend a little while now taking you behind the scenes into how some of that

19:38

Speaker 1: is uh how into how some of that actually works. I should mention that the framework is open source, so if you have any other questions and you don't get a chance to ask them in the Q<unk>A, you can of course just check the source code. Okay, so I'm gonna the first thing I'm going to talk about is uh one you probably noticed fairly early on, which is that we're we're compiling Python to JavaScript here. here, which is obviously pretty essential for running uh for running an application uh in the browser and still writing it in Python, the browser only really understands JavaScript. So we use a compiler called Sculpt, which is a Python to JavaScript compiler.

20:25

Speaker 1: So you feed it a Python expression, it will produce a JavaScript exp uh JavaScript code that does the same thing. You feed it a Python function and it will produce honestly the ugliest JavaScript function you have ever seen, but it will follow the semantics of that Python function. Now I'm gonna share uh I'd like to tell you a little story about the first major modification I made to Sculpt, uh which was about blocking and non-blocking code. Because JavaScript, in its infinite wisdom, is a 100% non-blocking language. And which means that uh you can't sort of wait for something, uh wait for some long-running interaction before. returning from a function, you have to return from a function or the routes will hang.

21:13

Speaker 1: And although, and I know the uh that async is what all the cool kids are using at the moment, but still Most idiomatic Python APIs and certainly most of the ones we want to be showing to beginners or novices look something a little bit like this. You know, you do some you go ask a database about something, you get a return value, and then you do some computation on it. And if you do that in JavaScript, it ends up looking a little bit like this. I count one, two, three nested callbacks and an awful lot of spaghetti. Now you might say that's unfair to JavaScript. I might say I copied and pasted that from the first play page of the Postgres JavaScript API documentation. You might say, fine, that's still old-fashioned. These days we all use promises and async await.

22:00

Speaker 1: And I would say promises and async await are another example of those magic layers of abstraction that make the typing faster. But to use them at a serious advanced level, you need to understand. That there are callbacks and promises, which are kind of abstractions of callbacks, and then async await, which is syntactic sugar on top of promises. And if you don't know those things, you will hurt yourself. So really we would prefer we would prefer to be doing this stuff. But we have a Python to JavaScript compiler. What if we could convert this to this automatically? So, we check out the compiler and we start exploring.

22:46

Speaker 1: So it turns out that Sculpt has a fairly classic compiler architecture based unsurprisingly on CPython. So you uh when it's fed a uh some Python source code, it first uh tokenizes it. So would uh break it up into a bunch of substrings that are kind of useful atoms for the Python language. By the way, all the. js files here, these are the files in the sculpt source code if you want to follow along. We then walk over those tokens and parse them into what's called an abstract syntax tree, which is a tree of operations uh uh that you the way python thinks of them. So here we have an assignment and on the left hand side of that assignment we have a variable named result.

23:32

Speaker 1: And on the right hand side, we have a function call calling a function named f with an argument that's a numerical constant 42 And then we have a compiler that walks over this tree and spits out JavaScript. So clearly, if we want to transform idiomatic blocking Python into non-blocking JavaScript, this is where we're going to want focus our attentions. So bear in mind we want uh every Python function compiles to a JavaScript function, which means that we need To return. The JavaScript function can't block. So what we did was invent a new return type that the JavaScript function can return to say, yes, I'm returning because I need to, but I'm not done yet.

24:18

Speaker 1: I'm just blocked. And we call that a suspension. So we now compile compile code like this to something like this. So when you have a function call, we can We call F and then we look at the return value and we check, was that actually a suspension? And if so, then we save a suspension of ourselves. So we're executing inside some function. We will save the values of our local variables. We will save our temporaries in the middle. of computation and then we'll return all of that in a suspension that also includes a reference to the suspension we were given by F. And then we return that out of our JavaScript function. So here's an example of something you might write

25:04

Speaker 1: And here is our call stack. So we've got the JavaScript runtime, which is handling the click events, the buttons, calling button one click, which is calling that get function. That goes to the database and the database blocks. So the database returns a suspension. The get function checks the return value from that uh from its call and goes, ah, I have a suspension, so it saves its own suspension. So it saves its local variables, its temporaries, where it is within the get function, and returns that as a suspension that wraps the one from the database. Button one click does the same thing. It sees that there's a s it's been given a suspension, it saves its locals, saves its temporary, saves where it was in the function, and returns its own suspension to the JavaScript runtime which sits on it.

25:56

Speaker 1: And a few milliseconds later, which is plenty of time for a sip of water as far as the computer's concerned, the database gets back to us. And so the JavaScript code will resume the suspension. So that is a basically a different way of calling the button one click function. without uh without starting it from scratch, but saying actually resume from here. And so the button one click function uh restores all its local variables, restores its temporaries, jumps back to the right place in the function, and then resumes. the nested suspension, which then jumps back to the right place for the get function, restores its locals, restores its temporaries, and resumes the inner suspension. Which of course which returns immediately because now we have data from the database.

26:42

Speaker 1: And now we have reconstituted our call stack. All of our calls are in the right place, all of our variables are in the right place, and we are executing normally. And that is how uh Sculpt turns blocking Python into non-blocking JavaScript so that we can create idiomatic APIs. If you want to know more, check out sculpt. org. I am one of the maintainers. We are always looking for new contributors. There's a bunch of really exciting things going on. Do please come join us. So for my second piece of behind the scenes, I'd like to talk about something else you saw me uh saw in that demo, which is code completion. Now Uh Python JavaScript JavaScript compilation, that was sort of part of the the framework.

27:28

Speaker 1: This is part of the development platform you saw me. using. And if you are building any sort of developer tool, code completion is really, really important. It's really important because it gives you discoverability. It lets you work out what's in the API without having to go pour over some documentation in another tab. It gives you speed because you can just hit the tab key every few characters. It gives you confidence that what you're doing is right because there are whole classes of bugs that you could fix before you ever hit the run button. Because the autocompleter has already warned you that that probably was what you wanted. It makes it just feels good to use Now I'm probably pushing it an open door for anyone out there who's a diehard

28:14

Speaker 1: PyCharm user, but for the Vim and Emacs crowd, you can get Jedi as a plugin. Please it If you are one of those people, I I I beg you, install it, try it for a week. You will be amazed. It will change your life. Just try it for a week. Do me a favor. If you do nothing else from this presentation. Uh no, this is all great, but uh unfortunately uh Jedi itself on its own wasn't really uh enough for what we needed. Um the first reason is that uh Azle is full stack. So it We need the autocompleter to know about what's in your database, about what's in your server code, even about what's in your user interface.

28:59

Speaker 1: And you know, maybe we could have solved that problem. But the other thing about Jedi is that it of course expects us to be reading code from a file system. And you the environment you saw me using there was web-based. And when you hit the tab key and when you're typing code, there is just not enough time to go to the server. Parse a bunch of code, work out what the completions are and send them back to the uh back to the browser. Um if you ever use replit, repl. it, which is a really great tool for trying out all sorts of different programming languages. They do their Python autocompletion on the server side and it really, really shows. You are halfway through the next word before the completions from the tab key you press like half a word ago come back and it's really very difficult to use. So

29:45

Speaker 1: uh we set out to build our own in JavaScript, which would seem like a particularly huge uh challenge Except we already had a Python JavaScript compiler knocking around and you've already seen how it can be used to parse Python code. So what we do is if you're in the Anvil editor and you hit the tab key, we replace where your cursor is with a magic symbol and just a string of random letters and then we feed that Python module into the sculpt parser which generates an abstract syntax tree and then we can walk over that syntax tree which is the easy part

30:32

Speaker 1: You go over, you walk in, you see, oh well, here's a function definition. It has arguments called self and event args. Well they they're they're local variables now. Walk some further. Oh, here's an assignment. We're creating we 're assigning to a local variable called X. I guess you know there's something called X in our scope now. Walk some more. Oh, we've run across this magic cursor symbol. Well, what do we know that's in scope right now? When we offer those things to autocomplete. Of course, we're not restricted to uh Completing local variables, you can complete all sorts. Here I am completing columns in a row from a database table. And actually we've walked over a lot of things to get there.

31:17

Speaker 1: So we've looked at what happens if you, you know, what happens if you get the user's attribute from app tables, and then what happens if you get the search attribute from from that, and then what happens if you call that, and then what happens if you iterate over that, and then what happens if you do a square bracket lookup on those? But that's that's okay. Computers are good at sort of following chains of inferences and you can just walk your way through the types. Something else that we autocomplete is of course server client communication. And this is some one again another advantage of having everything in the same language is that you can do things like this. So here I'm calling a server function called say hello and I'm passing an a dictionary as an argument and what we do is we

32:04

Speaker 1: As the autocompleter walks over that, it notices, oh hey, I'm calling emble. server. call, I'm calling a server function called say hello. Let me just remember the types of those arguments. And then next time we are parsing the say hello function, we go, oh wait, this is a server function called say hello. I know what arguments this gets called. With and I can pull those uh out of my memory and drop them into the local scope and autocomplete them. And again, this is the sort of thing that is tremendously difficult if you are building. five different programs that talk to each other because what sort of you know what what types are coming from your server code when you make a REST API core

32:49

Speaker 1: is, I mean, formally undecidable, and you you know, your JavaScript tooling has no idea what your Django model is doing on the other end. But if you have everything in one representation, then everything is a Python program, everything is a Python object, and standard autocompletion will be able to auto complete across cloud and server. You can auto-complete an awful lot about Python objects, which leads to a rather philosophical question about what exactly a type is. And you might say that the type of a Python object is its formal type, its class. But of course. That's not really the whole truth because Python's a very dynamic language.

33:36

Speaker 1: You can add uh attributes to objects at runtime. Heck, even different dictionary instances have different keys and we've seen we want to complete some of those. So what we do is we actually duct type our autocomplete. As far as the AML Autocompleter is concerned, these two dictionaries are two different types. Obviously, they share a lot of their implementation. They share a lot of uh their methods and so on, but because we track them as different types, we can offer completions like this. Two different types auditably differently. So, I hope you've enjoyed that little taste of some of how it works under the hood. I could obviously go on for days about this, and if you want to dig dig in further in QA, then I'm very happy to.

34:24

Speaker 1: But Before we get to that, I'd like to talk a little bit about the design philosophy that underlies what you've just seen. All right, there we go.

34:39

Speaker 2: Yeah.

34:40

Speaker 1: Okay, I am live. Um I would like to start with an apology for that one. Uh that I have never done that and that talk ran uh ran to the minute to 32 minutes in practice. Oh well, that's what I get All right, so questions please.

34:56

Speaker 2: So it was fascinating. Thank you very much for this. But I'm going to play the devil's advocate here. So my question is why Python and why not JavaScript? Because then you don't have to do the compilation. It would seem to me that it would be the most natural choice to make.

35:20

Speaker 1: So I mean I think there are sort of two answers to that one. One is that uh Python is the language of the people who are currently suffering most from the problems with the web, which is to say that if you know JavaScript, if you've learned JavaScript, you've probably already learned web development The people who are most frustrated by the fact that they can write code and then they need to put it online for somebody else to use and they can't, you know, and and they can't do that because to do that required learning four new different pro four different programming languages and another few frameworks on top. Those people are mostly speaking Python.

36:06

Speaker 1: the you know the the effort of doing that cross-compilation is definitely worth it for them. Uh the Other practical thing is that we wanted to create a sort of island of sanity. Uh the web is a very uh It's very easy to end up mixing in all sorts of horrors. And if you are using JavaScript as Especially if you're an obvious, you're going to go on Stack Overflow and ask, well, how do I do such and such in JavaScript? You're going to get an answer that involves touching the DOM. And then suddenly your abstraction is blown to smithereens and none of the guarantees that we are looking to provide can be provided. So yes, uh uh a couple of different reasons that add up to the same thing.

36:53

Speaker 1: Someone else with a question

36:58

Speaker 3: I I do. I mean actually that there's one um I don't know if you saw it, there's one in in the um there was one in the chat while you were doing the talk from someone else. So I'll maybe I should mention that in case it gets missed, but that was is the Anvil um Uh interface itself, another way is the Anvil web control panel built in Anvil?

37:23

Speaker 1: Uh it is not. Uh it it because there's a sort of um uh Uh th there's a bootstrapping problem there and we built it before uh we could build and all. Um it's Yeah, uh it is sort of part of the the un underpinning stuff, so we unfortunately have to deal with uh the usual array of uh web development horrors to do it. Uh there is a little dent in my desk next to my keyboard marked Uh I am enduring this so other people don't have to.

37:59

Speaker 3: Um And I had a question of my own about the way into this because I I've invested a lot in, as I know many have, into learning Django and learning various parts of it. So for example, I'm reasonably Happy and comfortable with the ORM. Um is there a pathway into using Anvil that allows me to use bits of it at a time. Is there a kind of progressive integration pathway?

38:30

Speaker 1: Absolutely.

38:31

Speaker 3: Rather than having to go all at once.

38:33

Speaker 1: Yes, there's well there's limits to what I could fit into that time and apparently those limits were were more severe than my rehearsals had indicated. Um The uh there are so there's I I didn't manage to discuss everything, but um there are There are tools for integrating code that's not all entirely inside, for example, Java uh Anvil 's serverless environment. So you can be running code on your local machine. You can then use something called the Uplink, which will integrate it with your uh Anvil uh server code and you can uh you can access all the APIs. that you would be able to do from those functions in yellow, you can do that from the code that's running outside Anvil.

39:19

Speaker 1: So that's one option. So you can just have something that 's running even on your own machine with a chunk of the Django ORM, but you could integrate with that ammo. server. callable. The alternatives, you can of course uh drive the anvil uh application built with the anvil front end uh off ordinary HTTP requests. So you could build your back end with something like Django and then still be able to build the front end uh with the UI toolkit you saw me using. Now obviously each of these involves abandoning or partially abandoning some of the things that we have built to try and simplify things. So if you're using code that is running outside the serverless environment, well now you're responsible for the DevOps.

40:04

Speaker 1: of keeping that up up and running. Maybe that's a problem for you, maybe it's not. If you want to set up your own HTTP API and then drive a front end off that, Well, now you're responsible for mashing everything into JSON and rendering your data. But again, if that's what you have, an existing code base or an existing base of expertise that makes that the most logical choice for you, we are certainly not going to stand in your way. uh there is a uh we have a general philosophical commitment to escape patches which is to say that you should always be able to get out of the Anvil abstraction and do what you need to do with the um with the underlying system , whether that is browser APIs, JavaScript libraries, code running outside. REST APIs, you name it, you shouldn't be forced into this abstraction, even though we think that for most people, most of the time, it will be an improvement.

40:56

Speaker 1: Oh I see. That was uh the um uh loud swarm. I was thinking why why is Calvin in here? Uh right, someone else with a question?

41:10

Speaker 4: Yeah, I I got a question. Uh first first thanks uh it's it's an amazing product and also very very amazing uh presentation uh skills that uh Really like that. My my question is um how how about you showed some of the UI components that you have. Um how how about um Is it possible to to customize them, to customize the design?

41:34

Speaker 1: Yes and yes. On two separate levels. One is that you can of you can drop down into the theme if you want If you w want to drop down and edit the, for example, the CSS. So you saw me the very first thing I did was I picked a theme, and those themes are defined obviously with HTML and CSS. And you can go in and edit those and change, change the theme to look like, you know, to style your components as you want. And there are, you know, you can tag your components, give them CSS classes, that sort of thing. So that's one uh one option. Uh the other is um uh if you want to incorporate an entirely third-party component, so you want to build something in JavaScript or use a JavaScript API then you can absolutely import that

42:19

Speaker 1: and then you can drive that JavaScript from Anvil. There's actually a Python to JavaScript bridge. You can go from uh the importamble. js. window and you've got the JavaScript window object so you can pull you know functions and classes. So my colleague Brooke recently did a demo where she Use the Mapbox JavaScript library if you're familiar with that, which is a really cool mapping widget, and integrated that and drove that entirely from Python and built an interactive mapping application with it. So yes, you can you the answer is yes, you can define things on you can customize them uh in a couple of ways. And of course, once you've done that, you can then bundle that up as a custom component. And anyone else can add a dependency on your app and the custom component you've built will appear in the Anvil toolbox you can they can drag and drop onto their screen.

43:05

Speaker 1: So only one person needs to get their hands dirty with the JavaScript or CSS.

43:10

Speaker 4: Okay, cool. So that sounds good. Thanks.

43:15

Speaker 1: Uh somebody else has uh video unmuted themselves. Do you did you want to ask a question? Hello, yes. We can't hear you. We can't hear you. You're muted. You remain muted Video calls never change. We've done been doing this for a year and we still still can't hear. Oh there we go.

43:39

Speaker 5: Okay, great. Um what do you think of trans

43:41

Speaker 1: you're really really really muffled.

43:42

Speaker 5: What do you think of Trend?

43:43

Speaker 1: No, still can't hear you.

43:45

Speaker 5: I'll type Type it in. Thank you.

43:47

Speaker 1: Okay, we have a question being typed into the chat. So I'm going to have to learn where the chat is in Jitsi. Oh, I found it. If someone else has a question before then while uh while he's typing, uh jump right in. No, okay. Hello.

44:10

Speaker 6: Hello?

44:11

Speaker 1: Hello, Pablo. Hello, Pablo.

44:13

Speaker 6: Hi. First uh congratulations. That was an awesome topic and a very good presentation. I really like everything that you showed and I think technically it's quite an achievement, so congratulations on that side. I just have like more maybe a philosophical question around this Um I've been working with Python for many years and I love the language. Um and I o I understand the pain you're mentioning about not ha all these other languages that we have to handle. And my question is Wouldn't this uh isolate Python developers into not growing outside our comfort zone?

44:53

Speaker 1: So That is a very interesting uh philosophical question. I think the answer is the answer has to do with what do you think the purpose of growing outside your comfort zone is. If you would just like to experience new languages and new ways of doing things, there's nothing stopping you doing that. But are you looking for personal growth or are you looking to get something done? Because uh if you're looking to get something done, then the argument's an awful lot easier. There's just less effort and less pain. Um if you are looking to branch out and you know dip your toes into something like uh uh JavaScript another front end development, uh then you you can. I mean you can do that either

45:39

Speaker 1: you can either you know go play with React the side project or you could incrementally go, actually here's this thing I would but I think I could make my app my work incrementally better by going and building this thing custom in JavaScript. You can, as I was just talking about, jump out and run write components or use components from JavaScript. uh without uh without having to compromise the abstractions you're using for the rest of your code. So I don't think I I think this ends up having the same answers to the questions about uh whether it locks people into this particular idea because There there is no lock in, the gates are open. You can go and expand your mind by inflicting pain on yourself if you want to.

46:23

Speaker 6: Okay, great. Thank you.

46:26

Speaker 1: All right. Christopher uh Lovki asks, uh what do you think of transcript? Oh boy, yes, transcript is and so for people who aren't familiar with it, transcript is another of those um uh it is a different Python to JavaScript compiler. It has a very different philosophical purpose. It was very different with a different approach. For one thing, it doesn't actually compile Python. It compiles something Python-like and it explicitly compromises the semantics of what you can use. so that it produces a pretty much one-to-one thing with JavaScript. So if you're if you're used to low-level object meddling or dunder stuff in uh in Python, you're gonna have a bit of a uh you're gonna run stub your toe in transcript. Um It's also kind of designed to be used as part of a traditional pipeline. So it's it produces. js files that you will then bundle up and include in your application, a very traditional web

47:13

Speaker 1: dev um uh stack, which means that it it it falls into the I don't know trap is the right word, but it definitely does that thing I was talking about about swapping out one of the blue boxes. It swaps out the blue box mic java like marked JavaScript for the blue box mic marked Python But doesn't actually solve the main pain of web development, which is converting all your data between all these different layers of your stack. And so it's transcript is really neat. But it I I think it hasn't taken off hugely because it doesn't actually solve the problem. It just introduces you know it it doesn't produce enough uh pain relief to justify the extra awkward corners produced by the fact that all the APIs you're using are still

47:59

Speaker 1: will still have been written for JavaScript. and the uh the code you're writing isn't quite exactly Python, and so neither set of documentation really explains what you want, and it sort of ends up stranded in no man's land where it 's not quite worth that effort or worth worth the effort it takes uh for the benefit it gives. That's my personal opinion. Um my colleague Sean actually did a write-up um for our blog of several different Python in the browser implementations and the trade-off profiles of each, and I will drop a link to that in the chat because uh I can uh rabbit on about this one for ages. Let me just find the there we go. Python in the browser. Python

48:44

Speaker 1: read the there we go. Right. It's The Python in the browser talk is I 've linked there. Alright. Anyone else have questions?

49:02

Speaker 3: Yes, I do. I'd like to know um whether you how complete you consider because you've got I think you've got the whole stack as you illustrated at the beginning. But perhaps there are other parts of it that you didn't mention or ways that you plan to go deeper with this pi pythonization of the entire web stack. Well cleanse.

49:27

Speaker 1: I don't have I'm I'm trying to think or whether there is uh a particular piece of the stack that we don't think we've sufficiently pythonized because I think we've covered basically uh the you the user interface to the database. I mean obviously we'd want to we'd like to increase our coverage in each of these layers. So for example, to have more to be able to do more things from uh within Python in the browser rather than you know there's certain there's always there's always going to be a certain point past which you have to go out of the escape hatch and uh use some JavaScript. But the we would like to push that frontier back further, so

50:15

Speaker 1: expand the amount of the the amount of stuff you can do without having to leave anything that is Python semantics and auto-completed and so on. There is I mean we we on the back end, I'm not sure it's so much Pythonized, but the the back end is is acquiring it is acquiring capabilities. And obviously there's an awful lot I didn't have a chance to talk about, so there's a whole bunch of like um We have built-in libraries for things like doing user authentication. So your you know password management and hashing and authenticating correctly and not being vulnerable to session fixation, all of those sorts of things. Uh that that's not so much Pythonization as creating reusable components for the web. And that's not something that's unfamiliar to anyone in the Django ecosystem.

51:03

Speaker 1: But the idea is to have a bunch of high quality off-the-shelf ways to solve the incidental complexity of building your application uh so that you can focus on the you know the The stereotype is your web application, which is a user interface and a five-line for loop. And we would like to asymptotically approach the goal of being able to build the user interface and write the five-line for loop and then be done. All right. Now you had a follow up.

51:40

Speaker 7: Yeah. Uh what about like more complex uh project scenarios where you like you have uh celery bread is or all all those yes sometimes Bigger setups we you you need uh so from the the the deploy uh point of view or the creating the this in whole environment around your web application. Yeah, and we're making everything work together.

52:10

Speaker 1: Yes, oh well and the answer to that and just about everything else is you do it the Python way. So if you want if you would like to drive salary from then you you know you can use that from Python. And we actually we have uh built-in things for running, for example, scheduled tasks or background tasks, you can do that within the Anvil framework. But if there's something you want to do outside it, you you can just grab it and use it. In terms of deployments, I was doing everything in development mode, obviously, where every change I made was live reflected of that URL. You can do things like selecting versions to publish. Uh and again, if you want to get arbitrarily advanced, you check the code out of Git and pip install Anvil App Server and stand up, you know, uh uh standalone server or a dev production staging

52:56

Speaker 1: or a whole bunch of other you know configurations ultimately if you need to If you have a workflow that is opinionated about how it deals with source code, then uh you can deal with an anvil application as source code. Once again, we attempt to make the common tasks easy and you should definitely watch this space because we will be releasing a bunch of stuff that is particularly geared towards making deployments nicer in the next couple of months.

53:28

Speaker 7: Cool.

53:33

Speaker 1: All right. Anybody else? Yes, Chris.

53:37

Speaker 5: Hi. Can you say something about running a debugger in the browser? Is that at all possible?

53:42

Speaker 1: Uh so uh you can oh gosh. Uh you can uh use the JavaScript debugger on the generated JavaScript, but I I do warn you it's ugly. Um a uh yes, a stepping debugger is not a thing that is currently available in the production version of Anvil.

54:07

Speaker 5: Also let me give you a compliment on your first few slides saying about how just complex the different layers of I just thought that was brilliant. You might want to turn that into a like a five-minute lightning talk and just get that out to the world. Really good. Thank you.

54:20

Speaker 1: Uh I have done that at PyCon US and I will probably do it again.

54:23

Speaker 5: Super.

54:26

Speaker 1: But thank you very much. Right. Anybody else? Okay. Oh no, no, you you did I couldn't find it.

54:37

Speaker 5: I've got uh I think this is just fascinating. You're kind of a guiding light for me. Um are you doing a sprint this weekend?

54:44

Speaker 1: Uh uh uh we are not. I am uh sleeping and enjoying the sunshine this weekend.

54:50

Speaker 5: It's sun for the first time. Have fun.

54:52

Speaker 1: Yes, it really is. Uh I don't know where you're based.

54:55

Speaker 5: Poland.

54:56

Speaker 1: Oh, I see, right, yes. Uh uh uh here in the UK, um, we have sun for the first and as always with the UK, possibly the last time this summer. So it's it it it's very important to get the barbecue out.

55:08

Speaker 5: Yeah, make it while the sun shines. Have fun.

55:11

Speaker 1: Thanks. All right. If there is nobody else, then I think I'm gonna declare this QA session done and uh let you know that you know if you would like to get in touch with me, I mean My name is fairly easily Googleable. My email address is mereda. anvil. works. Please drop me a line. Thank you all very much.

Questions this talk answers

How can I build a full-stack web app using only Python?

Anvil uses Python objects throughout the stack: Python-based UI components in the browser, Python server code, and database rows that behave like Python objects. This avoids repeatedly translating data between SQL, server objects, JSON, JavaScript, HTML, and CSS.

Discussed at 7:07

How do I connect a button to Python code in Anvil?

Each UI component has a Python-accessible name, and a button click handler can update other components directly—for example, setting a label’s text from the contents of a text box. The resulting application can be run and deployed to a public web URL.

Discussed at 10:24

How do I call server-side Python from an Anvil browser app?

Define a normal Python function in a server module and mark it with `@anvil.server.callable`. Browser code can then invoke it with `anvil.server.call`, passing arguments such as the text entered by the user.

Discussed at 11:56

What can I do in Anvil’s server-side Python environment?

Anvil’s server code runs in a real CPython environment, so it can access databases and use Python packages such as NumPy, PyTorch, TensorFlow, or Psycopg2. The browser-side Python compiled to JavaScript has more restrictions, such as not being able to load files from the server filesystem.

Discussed at 13:26

How do I set up a database table in Anvil?

Anvil provides a built-in Data Table Service backed by PostgreSQL. You create a table and define columns—such as a text name column and a datetime column—then access it from Python through `app_tables`.

Discussed at 14:12

How do I display database rows in an Anvil UI?

A server function can return an iterable of rows from an Anvil table, and a repeating panel or data grid can use that iterable as its `items` property. Anvil then creates the UI for each row, with columns bound to the row’s fields.

Discussed at 15:47

How does Anvil run Python code in the browser?

Anvil compiles browser-side Python to JavaScript using the Skulpt compiler. This lets UI code run client-side for responsive interactions while keeping the application source in Python.

Discussed at 19:38

How does Anvil turn blocking Python into non-blocking JavaScript?

The compiler represents a paused operation as a suspension containing the current function’s locals, temporaries, and position. When the asynchronous operation finishes, the runtime restores those suspended frames and resumes the Python call stack where it left off.

Discussed at 24:18

How does Anvil’s Python code autocomplete work?

The editor parses the Python source into an abstract syntax tree in JavaScript, walks the tree to determine what is in scope, and offers matching completions. Because the application is represented as Python, it can also infer database columns and server-function arguments across the client-server boundary.

Discussed at 29:45

Why use Python instead of JavaScript for full-stack web development?

The target users are largely Python developers who are frustrated by having to learn JavaScript and several additional web technologies just to deploy an application. Python also provides an isolated abstraction that prevents accidental dependencies on low-level DOM and JavaScript details.

Discussed at 35:20

Can I integrate Anvil with an existing Django application?

Yes. You can run existing code, including parts of a Django-based system, outside Anvil and connect it with Anvil through the Uplink, or use Django as an HTTP backend for an Anvil-built front end. These approaches give up some of Anvil’s managed simplicity and may leave you responsible for DevOps or JSON APIs.

Discussed at 39:19

Can I customize Anvil’s UI components and use JavaScript widgets?

Yes. You can edit themes and CSS, add classes, or import third-party JavaScript components through Anvil’s Python-to-JavaScript bridge. Customized components can then be packaged so other Anvil apps can add them from the toolbox.

Discussed at 41:34

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos from DjangoCon Europe