 Hello everyone, thank you for having me here. It's a pleasure and honor. So we're gonna be talking a little bit about modern Python through fast API and friends. So first, a little bit about me. Who am I? I'm Sebastian Ramirez or Tiangolo. I'm in Berlin, Germany, but I'm actually from Colombia in South America. That's why the accent. You can find me on GitHub, LinkedIn and Twitter here. I'm currently a staff software engineer at Forto. Forto is doing a lot of advanced machine learning and deep learning and specific stuff for to improve the customer experience in space. And on top of that, I'm also doing some external consulting with some other things. And I created fast API, diaper and SQL model and a bunch of other officer's projects. So we're gonna be talking about modern Python. And when I say modern Python, I just mean the currently supported versions. So Python 3.6 and above, like previous versions are already reached the end of life. So this is just like the currently supported versions by Python. And that's what we're gonna talk about. We're gonna talk a little bit about f-strings, then a bunch about type annotations and then a little bit about async and away to performance and community. And we're gonna do it with a tour using fast API, diaper, SQL model and all their friends. So first, why am I giving you this keynote? I created fast API. And if you don't know fast API is a web API framework that provides high performance, easy to learn. It's fast code and it's ready for production. It currently has 35,000 GitHub stars and it's been growing about 1,000 stars per month or something like that. It's currently used by Microsoft or Netflix and a bunch of other companies. And the performance is in the top rank of what you can get with Python. And in the last official Python developer survey, fast API was the third most used Python framework right after Flask and Django, which is very cool. And in the latest Stack Overflow Developer Survey that covers a bunch of frameworks, languages and tools. It was in the category of web frameworks that includes frontend and everything. It was the third most loved technology. Right, the third most loved web framework, right on top of React and Dj, so it's super cool. A little bit about fast API and why am I talking to you here? So now let's actually start talking about modern Python. Let's talk a little bit about fStrengths. Maybe some of you already know about fStrengths. Let's just recap it quickly for those of you that haven't seen it. So we have these data structures here. We have some recipes that have some name. One is the crunchy frog and the other one is the albatross. And they have some ingredients, each one of them. And these ingredients are just some strengths. And we have a default price that is one. Now we have this function, get a recipe that will take a name, which is the name of a recipe and a quantity. And this quantity has a full value of one. So this is an optional argument. And then we just extract each specific recipe by the name and then we return that recipe. That's all we are doing. And then we are giving this message saying like, hey, this is the recipe for this thing. And we are formatting this string here saying, we put the name called braces inside of the string. Then we pass the keyword argument to format saying, hey, this is the name that should go inside of the string where that other name is there. And then the value that it's going to have is the value of this variable name that comes from the function parameter. And if we run it, then it works as we would expect. So we can get the recipe for the crunchy frog and we can see that it's a message. This is the recipe for crunchy dash frog. We have some ingredients and the total is one. And if we change the quantity, it also works. And we get the total is two. Nice, that works. But if we check it closely, we can see that the name here, this information about the name is actually duplicated. We are repeating information in the code in three places. We have name here, then also name here and then also name here. This name refers to this name here. And then this name refers to the one that comes from the parameter in the function. But actually, we can just change this string to be an F string just by putting the F before that. And now Python will know that it has to put inside of these code braces where we have this name. It would just put the variable with that same variable that is taken from outside inside of the string. So in here, it will put the name that comes from the parameter. And we are just saving a bunch of duplication here. And that's it. And it's actually quite fast, the strings are faster. So that's very cool. And if we call it, it just works the same and if we pass the quantity, it just keeps working the same. Pretty simple, but it's nice to know. And now we can avoid having a bunch of duplicated and with stuff and we can simplify the stuff and make happiness better. Nice. Now let's actually switch to type annotations. And we're gonna talk a lot about type annotations and type hints. So what are these type hints and like, what is the deal with them? Let's say that now we don't want to print just crunchy frog and say this is the recipe for crunchy dash frog. We want to instead with a space here and we want to make this in the first C, we want it to be in capital and the F we want it to be in capital F. We want it to look like a name of a recipe, not just like an ID. But then we try to get out the completion to change this name and we don't get anything. And if you are not great at reading documentation and then memorizing each one of the possible methods. So if you are not a robot, then you will probably benefit from getting out the completion but we can't get out the completion here. The way this code is written. But we're totally fine without completion and type hints. We have lived our whole life without them. So yeah, we can deal with that, right? But we can actually change the annotation here in the function to say, to tell the editor, hey, this parameter name, this is a string. This should be a string. And that's it. This is all the change that we have to do. And now suddenly we have all the completion inside of the editor because now the editor will know that this parameter name is an actual string. So we can get out the completion for that. And because it knows that the result of name that replaces also a string, we can still get out the completion for the rest. So now we don't have to memorize or add the possible method. We can just use the auto completion to change replace the dash with a space and then make it in type of case. We don't have to remember what was the exact name. We can just use auto completion to get that. So we get auto completion and type checks by using type annotations. These type annotations or type hints are what powers auto completion and inline errors in the editor. And this is how the final title looks like. Now we put this title inside of the F string. So now when we run that code, we pass crunchy dash frog, which is the idea of the recipe. And now we get a message saying, hey, this is the recipe for crunchy frog. And this looks like a nice stack. And we use auto completion and type annotations to do that quickly and easily. Now let's say that we don't always want to have a quantity of one. Maybe someone only wants to get the recipe to not actually get one plate of this specific recipe. So let's say that we just changed the quantity to be nine. Yeah, it looks like a simple refactor. We just do that. But then if we call that function, now we certainly have an error. And the error says that we cannot multiply an integer with none. So default price was one, but now quantity is known. So we are multiplying one times none. This is an error. The annoying thing is that we have to run the program to actually see this error after the code was running. We didn't see the error where we were typing the code. We just had to run the code to see the error. But now let's say that instead of just saying quantity equals none, we tell the editor, hey, quantity is of type string. We tell the editor, what is the type of this parameter? And by default, this is none. And now, suddenly, because the editor now knows what is the type of this parameter. So the type of this variable inside of this code. Now we can get this inline error. And if we check it, it will tell us, hey, you're trying to multiply something that is a literal one. So this is an integer and none. And you cannot multiply an integer with none. So the editor is telling us right away that we have an error in the code without us having to run the code. And it's telling us exactly in the place where we have to change something. Exactly the place that has some problem. So these type annotations or type genes are protecting us from a bunch of bugs because they are detecting those bugs early. Like we, in many cases, we don't even consider them bugs because we just realize that there's an error and we just fix it even before running it for the first time. And let's see now, how can we fix that? Now that we see that there's an error, how can we improve that? So let's instead of returning the data right away, we're gonna create the result first and then we're gonna check if the quantity is none. Then we actually do the operation and we put the top out in this key. Now we are having another error here. So what's happening here? Let's check that. So the thing is that the editor is trying to be too smart and is trying to guess what is the type of these results. So the editor thinks that this result is a dictionary that has key strings like this message and ingredients and as values, the editor thinks that this dictionary has strings or lists of strings or sequences of strings. So the editor thinks that it cannot contain values that are integers because it's just trying to be too smart about it, which is normally great. But in this case, it's telling us something that is not really an error because we want a dictionary that has any type of value. So we can just tell the editor, hey, don't try to guess too hard. This is just a dictionary of anything. Don't try to guess what are the keys and where the value is, this is just a dictionary. And now the editor say like, okay, you don't want me to try to guess too hard. I'm just gonna guess that is just gonna take it as a dictionary of anything. And now we don't have the error here and we can just simply put the integer inside of this value. And let's check this with a little more detail. So the quantity is of type integer, but the full value is none. So the editor actually knows that quantity is an integer or none. And inside in this point, at this point, it is actually, it will be an integer or it will be none. But then where we are inside of the if block where we are checking quantity is not none. So inside of this if block, quantity can only be an integer because we already checked that it is not none. And the editor is smart enough to know that this is a new block checking if it is none. So the type that this can have inside of the if block has to be an integer. So it won't complain, hey, this quantity will be none or will be an integer because the editor already knows that by this point it has to be an integer. So it's actually pretty smart about this type annotations and how we use them internally. Now we are saying that quantity is an integer but by default is none. This actually doesn't make that much sense because quite quantity can also be none but we are not saying it here. So we can be a little bit more rigorous with that and more explicit and we can import front typing import optional and then we can declare quantity is an optional integer. This actually means that a quantity can be an integer or it can be none. This is what this optional thing means that it will be none. And we use it with square brackets like this ones. And this module typing, this is in the standard Python library, this is the standard way of declaring type annotations and not declaring that something will be this type or it will also be none. So nice. Now we are a little bit more explicit about what is the type of this quantity. So with type hints or type annotations we get a lot of help from the editor, other condition, in-line errors and all that stuff. And without that hints, we have just to deal with everything ourselves and wait for the errors to pop up once we are running the code. Some of the people behind these type annotations or type hints, this guy started the MyPy project and MyPy is the original language that was then converted to what was included in Python itself. So it actually started outside of Python and now it's part of Python. These type annotations started by the project that this person started. And then these other two guys have also been helping a lot with MyPy itself and also improving a lot of the type annotations in Python and how to declare more sophisticated things and how to simplify and improve how these type annotations work. And I just wanted to show you that this is just normal people behind the scenes doing a bunch of these advancements that we can then take advantage of and then we can use. Now we just talk a little bit about my Python with type annotations but now using fast API. So we are gonna have the same data structure. We have the same default price. We have the same function with type annotations but now we're gonna import from fast API. We import the fast API class and now we create an instance of this fast API class which is this app object and now we use this app to decorate the function and we are telling fast API, hey, whenever someone sends an HTTP get request so this is an HTTP get operation or get method and whenever that request goes to this specific path so it goes to slash, recipes slash and then this name, this is a path parameter. And you can see that we are using the same curly braces that we use for F strings. It's just that this one is not an extreme because we don't have an F here and because we don't have any variable name outside of it but we still use the same curly braces so we don't have to learn a new syntax. It's just the same syntax that we already use for that. But the thing is that fast API will take whatever comes at this point, at this place in the URL and this is a path parameter so it will read whatever is in here and we put it in this variable name. And then because this quantity is not part of the path fast API will read the quantity from the query parameters and it will expect to have an integer by default. It's gonna be just, no, nice. So to convert this code that we had we just added these three lines and now we have a full web API to get the recipes. So the more fast API code you write the less API code you write because most of the code will actually be about the business logic about the important parts of your application and adding fast API on top is just very little code that you have to actually write for fast API itself. And because we are using all these based on type annotations we get all the completion everywhere for everything just the same as we would with standard Python. So we can be happy as this cat without the completion. And we also get error detections or inline errors like this one. Again, because this is just based on standard type annotations. Now, if we run that application and we run that fast API web application just as I showed you the code and we go to slash docs we get this automatic interactive API documentation showing us the whole API. So we can see that we have a get request that goes to slash recipes slash and then this name is a path parameter. We can see that this name comes from the path that it's a string and that is required. And we also have the quantity which comes from the query parameters and this is an integer. And this is not required because we declare it as having by default none. And we can actually execute this request. So this is shown in the browser but we can actually execute. This is not only documenting the API this is actually interactive. We can interact with the API right from the browser. If we click execute then we're gonna get a response from the API that is running their lab. We can get the recipe for the Crunchy Frog with all the ingredients and all the stuff. So we just write some basic code and the good guy fast API will make a docs user interface for us. Now let's see, this is not only doing data documentation this is also doing data validation. So we say that quantity is an integer and comes from the query parameters. Now what happens if we pass a string like name? This is not really an integer. The user interface won't even let us send this request because it knows that it has to be an integer. But what if we try to cheat and send the request through the command line? Now we are sending hey quantity, this is nine. Now fast API will validate the data using the same standard type annotations and we're just gonna give us a nice error like this one that if we format it, it will look like this JSON. There is not only validating data and telling us hey this data is invalid is also telling us exactly how and where. So it's telling us hey in the query parameter quantity, the value is not a valid integer. So we were expecting an integer in this quantity and that's not what we received. So fast API without valid data is gonna protect our application from running within valid data. And when the data is actually valid and conforms to the shapes that we declared with these type annotations then it will all just work and just be executed right as we wrote it with the standard type annotations. So we are also getting automatic data conversion it's not only validation. If you see this quantity comes from the query parameters that means that if this is the URL slash recipes this crunchy frog, this is the path parameter that comes in here. So this crunchy frog is the one that will be in that name variable. And then this quantity, this is the query parameter that goes after the question mark. This quantity has a value of two but if you see this is a URL so this is just a very long string. Like it's naturally just a string and what is gonna be receiving server is just a long string. Fast API is gonna see that this quantity is declared as an integer and it's gonna convert the data from this string in the URL to the type that we declared in our application. So the code that we write is actually gonna receive an integer as we declared it. It's not gonna receive some string with the number two and it's gonna receive an actual integer. I can confirm that because then it's gonna execute that and it's gonna multiply by the other thing and then we get the total here. This is an integer which is what we are sending back based on the input which was also an integer. Nice, so this is all based on type hints. It's all based on the same standard type annotations or type hints. We're getting data validation, data documentation, serialization, all the stuff. And this is because Fast API is based on standards. So Fast API is based on Open API which internally is JSON schema on OAuth 2 for the authentication and all the stuff and because it's based on these standards we can get automatic API talks like this one that we see here. So this documentation is not part of Fast API itself. This is what is integrated in Fast API but this is actually provided by a tool called Swagger UI and Swagger is also based on Open API. So we get various integrations with a bunch of other tools including client generators, client code generators and things like that just because this is all based on standards for the API. And because we are using standard type annotations for everything, we get automatic data conversion. Well, because we use the standard type annotations we get auto completion in the nerves and all the stuff that comes just by default in Python but then using the same standard type annotations we get automatic data conversion and data validation thanks to how Fast API works underneath and the tools that it uses underneath that we're gonna see a little bit more in a second. But now it's actually talked about more in Python with type annotations but this time with Typer. Now we're not gonna build a Web API. We're gonna build a command line interface application something to run from the command line from the shell. So now instead of importing Typer, we're gonna import, sorry, instead of importing Fast API, we're gonna import Typer. And now we create an app which is an instance of this Typer class. And now again we just decorate the function with app.command. So this is the way that we type it. Hey, whenever this app is executed in the command line this is the command I want you to run this get recipe thing. And now we are not returning something in our request so instead we're gonna print on the screen with Typer.exe. So we are just printing this result which is this JSON variable or JSON code. So we're just adding a little bit of Typer sal to the same function based on type annotations. And now if we execute it, we can just run it and then we are gonna see that we get an automatic help option that we can get, we can run to get information about this command line application that we built and we also get automatic data validation. For example, this name was required and we didn't pass it. Now if we actually pass the name, current you from, we get the response that we were expecting. And now because we declared that quantity was an optional argument, then this is gonna be interpreted as a command line option. So we can declare dash dash quantity and then pass the actual integer here. This is two and then we get the response back. So this again is all based on the same standard Python type annotations. And we also get a data validation. So if we pass quantity nine instead of two, then it's also gonna validate it and say, hey, this is an invalid value for quantity because we were expecting an integer and this is not an integer, this is just a string. So again, we get data documentation, validation and serialization all based on the same standard Python type annotations. But we also get auto completion in the terminal in the shell. If you create a package with Typer or if you use Typer CLI, which is this little tool, then you can install auto completion in the shell. And then whenever someone is running it, you can, for example, type dash dash q and then hit the tab key in the keyboard and it will auto complete what are the possible options. In this case, it's just quantity. So it's gonna auto complete quantity there. And you can get auto completion for sub commands and for arguments and options and all the stuff. And this auto completion works in bash, fish, C shell and even PowerShell which is actually quite complex to achieve. So you can get auto completion for the users of your command line interface application for features like using tech, which is very cool. So now you're building command interface applications with Typer just by using again the same standard Python type and notations. Now let's see how we can integrate it with some of the friends. So now we're gonna integrate Typer with Rich. So we import from Rich, we import the table, we also import this console class, then we create a console object and now we create a table. And then for each one of these ingredients in the recipe and all the stuff that we were wanting to do, we are gonna add a row with each grid and then we're gonna print that table using this console. And then we're just gonna print also the total that we wanted in the terminal using the same console. And now suddenly we have this very nicely formatted output shown information in the terminal just by using Rich integrated with Typer. So you can combine Typer and Rich to build very powerful command line interface applications using modern Python again. Now some of the people behind these tools, David Lord is the maintainer of click. Click is the library that does everything underneath of Typer. Typer is just a thin layer of click. It's like the actual thing that is doing all the work behind the scenes. And Rich was created by Will and Will recently decided to go full open source. So he's gonna, Will probably gonna see a lot of advancements and new features and tools from him, which is super cool. And again, as I wanted to show you, this is just normal people as always. This is just a bunch of tools built by very normal people just trying to help others. Now let's see, we can do more in Python with type annotations using fast API and now we're gonna use pydantic. So fast API is built on top of pydantic. Pydantic is the tool that does all the data validations, utilization and documentation underneath. It's all the tool that does all the data stuff underneath. So we can import from pydantic, we can import this class base model and then we can create a class that inherits from it. So we're creating a class food and then we're declaring that this has a name which is a string and then it has some ingredients which is a list of strings. Now, again, this is the standard type annotations that we will use. And if you have ever used data classes which is also part of the summer library, this is very similar, it's mainly the same syntax. This list comes from typing which is the same model that we used before. So again, this is the standard way in Python to declare that something is a list of strings. We don't have to learn some new classes from pydantic or some new types or something like that. We just use the standard Python type annotations to declare all this stuff. And then we just use this class food we put it in the parameters of the fast API function here. Now this is an HTTP POST operation or POST method and goes to slash food slash. And then we're gonna receive this parameter food which is of type food. So again, this is the standard way to declare that these parameters should be an instance of the class food. So we are telling fast API, hey, I want you to give me an instance of this class food here. And fast API will do that and will provide us an instance and will validate the data to make sure that we actually get a valid instance of this class food. And because this is an instance that we're gonna access the attributes like food.name and food.ingredients and of course get out the completion and all the stuff. So this food.name was declared here as a string and this food.ingredients was declared here as a list of strings. And if we check the interactive API documentation, it extracts the information from this pydantic mode to show us what is the example, what is the way the data that we want to receive. So name is a string and ingredients is at least a list of strings. And if we try to execute that, it's gonna actually send the data to the server. So we can say something with a name which is a string and then some ingredients and then the server is just gonna reply back and it's gonna, we're gonna interact live there from the browser with our API, which is super cool. And we also can do data validation with these pydantic modes. So let's see, this is actually valid JSON. So this is JSON, but the data is not exactly what we declared that we wanted to receive. So the name is a string, this is valid. The ingredients is a list or like a JSON array is the equivalent of a Python list. And the first item is a string. So up to here, this is valid. But then the next item, this is not a string. This is a JSON object or the equivalent in Python dictionary. So this is actually not valid. If we try to send this request, then Fast API is gonna do data validation using pydantic with that pydantic model that we declared. And it's gonna tell us exactly where the problem is saying, hey, in the body that you send in that request, inside of the body in the ingredients, the item at index one. So index zero is the first item, index one is the second item, a string type was expected and that's not what we received. So we are doing it, getting data validation even for deep nested data structures based on the pydantic model that we declared. Also notice that this is not some data shape that Fast API declared, this is a data shape that we declared, a data model that we declared with pydantic and we're getting validation for that. So what if we want to have some nested data? Now let's say that we don't want to receive a single order item, we want to receive a list of food items. So now we can put this, we can declare again, using the same standard type validation, this is the standard way to declare that something should be a list of instances of food and we're putting this in this parameter of orders. Now orders is gonna be an actual list of food instances. So we can iterate for each food instance in these orders. And then we can use that internally. So if we want to put JSON inside of JSON, we can just do that. We can build any complex arbitrary nested data structure that we want and we just use standard type notations with Python and pydantic to declare the shapes that we want to receive. And then Fast API will do all the data validation, serialization and documentation for us. And because we are using all this stuff based on the standard Python type annotations and using the standard type annotations very rigorously, we can get out the completion everywhere. So check this out. We are in a double nested for loop and inside of that we still get out the completion for each one of these ingredients. This is actually quite difficult to achieve without using standard type annotations. And this works because it knows that the ingredient is a string, but how does it know that? So it knows that orders is a list of instances of food. So for each food item in these orders, the editor will know that food is an instance of food. And then because we declared that food had some ingredients, then if we did it for each ingredient inside of this food code ingredients, the editor will know that this ingredient is also a string. And because it knows that it's a string, it can provide it to completion for strings in this ingredient. So we get out the completion even for this deeply complex nested data structures. And we get out the completion everywhere. For our data shapes, for the shapes that we declared and that we need. We also get automatic error detection or inland errors because we are using the same type annotations. Again, this is a double nested for loop and the editor is still being able to know that, hey, you're trying to do this operation between a string and an integer and this is not valid. You have to compare one to the other or something like that. Now, some of the people behind the scenes, as I have been showing you, Fast API is built on top of by Dante for doing all the data parts and Starlet for doing all the web parts. By Dante was created by Samuel Covey, Starlet was created by Tom Christy. And also David Montaghi is another contributor to by Dante and Fast API, so the search mentions here. And again, this is just normal people, just building stuff and trying to help others. Now, please actually see how we can combine all this with SQL model. Using again type annotations with modern writing. So SQL model is a library that I released two or three weeks ago for two helping with interacting with databases using the same standard Python type annotations. So here we're creating, hey, this is a class, this is a model that is a hero model and it inherits from SQL model. And we're saying, hey, this is a table. This should be a table in the database. It has an ID, which is an integer. By default, it's known because we want the ID to be generated by the database, not by Python. And we are saying that this is a primary key. Then we also say that we have a name, which is a string. And this has to have a string. This cannot be none because this is not optional. Secret name is also a string. It has to be provided, cannot be none. Then age is an integer, but it can be none. So in the database, this can be null. Then we just create this engine. In this case, we are using SQLite. Would we could as well just use Postgres SQL or MySQL or MariaDB or anything else SQL provided by SQL Alchemy, which is what powers SQL model underneath. But I'll show you that in a bit. So we just create this engine. And then with our app, with fast API application, we use the on startup event. And in this event, we're gonna create all the data here. You could do this in a different way. For example, with migrations, but for this simple example, you can just create the database and the tables here. And then because this class that we created, this hero, the trick is that this class hero is a SQL model model. So the model built with SQL model is both a pydantic model and SQL Alchemy model. So it's a model that declares data shapes just like pydantic, but also declares the data in the database. So we can use it again here in the type annotations or here, which is the standard place where we put in fast API to say what is the shape that we're gonna return in the response. Then we can create a session here. And then because this is also a database model, we can just add it directly to the database session. Then we can commit and then we can refresh the data from the database and then we can just return that same thing. We don't even have to convert this to some other type. We can just return it. And then fast API is just gonna use that. Now, let's actually also create something to read all the possible heroes that we have in the database. We can again use the same type annotations like to describe that this is a list of heroes. This is the response model that we are using here. And this is the standard place to put it in fast API. This is not put here in the type annotations for some specific reasons that you can see in the documentation. But now we can declare that this is a list of heroes. And then we can create the session here. We can select all the heroes in the database and then we can just return those heroes. And now if we go to interactive API documentation, we can see that we have the path operation, the this route to get all the heroes and we have the one to create heroes. And because this model created with SQL model is both a Python model and a model for the database, we can again get the automatic information about the shape that we are expecting to receive. So IDs will be an integer, name is a string, secret name is a string, and H will be an integer. So we can have the database and data models without duplicating information and all based in standard Python type annotations. And because we are using these standard Python type annotations, we will get that to complete in line errors and everything in the editor. Now some of the people behind SQL models and some of the tools behind SQL models. SQL model is built on top of Pydantic and SQL alchemy. Pydantic does all the data validation, documentation and serialization and SQL alchemy does all the SQL parts. SQL alchemy is the most used library for interacting with SQL databases in Python. And SQL model just adds a thin layer on top of Pydantic and SQL alchemy. So again, this is just normal people building great tools for us all to work with. Now let's change a little bit the subject and talk a little bit about modern Python with async and await. So what is the deal with async and await? So here's the thing, Python running in the backend, running in a server is actually a very fast system. You might have heard that, oh, Python is a slow programming language. Yes, it is slowly if you compare it to C and if you are building a kernel for an operating system, you will probably not do it in Python. But for everything else, you will probably build it with Python because Python is actually very decent for any other task. And particularly, if you are building web applications, the problem is not the server and how fast the code is. The problem is the network. Python is very fast when you compare it with the network. So if the network is super slow, how can we improve things here? So here's the first idea. What if we have one single backend in Python working with multiple clients and serving multiple clients? So that way, even though each network is super slow, at least we are serving multiple clients at the same time. And of course, this network is not related to Python. This network will be slow in any programming language. It's just how the network is. So cool, this idea sounds nice, but when we tried to use it in Python directly, the way that it would work just by default, you would actually not look that much like this. It would look more like one of these lines to paste stuff in the grocery store. So before this person here can buy their newspaper and groceries, they have to wait for this other dude to buy this drink. And this dude maybe is already a little bit drunk, so we'll take some time to get the money and we'll take some time to pay. And during all that process, in there in the front in the cashier, this dude just has to wait, wait and be waiting there because we're just all waiting for this one to finish. So it looks more or less like this. There's one client that sends one request that goes slowly through the network. Then it finally arrives to the backend. It's processed super fast in the backend. Then the response is sent through the network and it's slowly going back to the client. I know this will have to happen here before the next client can start their transaction with the database and start their request. So each client is actually waiting in line for the previous one to finish. So here's the idea. What if, well, before the API, this is what our API app has to do. It just has to be waiting for the network to receive and send all the messages. But this, now here's the idea. Well, we have a waiter that goes table to table, taking the orders, and then it goes delivering them whatever it is that they request. And it looks more or less like this. And this is where async and wait come to the, like show up in this. Let's say that each one of these claimings, it's sit in a table. So the waiter, which is just part of Python and it's just this mechanism with async and wait, the waiter will go to each one of the tables and will say, hey, what do you want to order? And then it will take the order and it will send the order, give it the order to our backend, to our code, which is our code written with async and wait. And then the code will be prepared up, but then this waiter doesn't have to wait for that to be ready to deliver the order. The waiter can already be asking the next client, what do they want? And then, for example, it will end up at this client and this might be my sister that is still deciding if she wants to eat chicken or fish and it will just take some time. So the waiter can see like, hey, you know what, just keep checking the menu. I will ask the next client. So the waiter can go to the client here and then the client can decide what they want to order and meanwhile, some of these dishes are already done. So the waiter can start sending the dishes to the clients while we are still waiting for some of them to decide what they want or because the network is very slow. So this is what async and wait provides, something like a waiter. And our application can be running all the time preparing dishes in the kitchen. Now, how this looks like in code. So in this case, we are using an example with HTTPX which is a great library for interacting with HTTP with to call other APIs. And this library, we can, well, we create the function for past API. We use now async because we want to use our wait internally and to use a wait, we need to declare the function with async and then we create this async client. And now this async client has this method.get and this will just send a get HTTP request. And then this client, the way that this method is defined in the internal code of client is that this method was defined also with async. And when it is defined with async, we have to call it with this keyword await. So to get the data from this, the result from calling this method, we have to put a wait here and then we can put the result in a variable. And this await, this is the key to everything. This await is the way that we tell that magic waiter, hey, this is a point where you might have to wait because this will be my sister deciding if she wants chicken or fish. So at this point, you might want to go and check other customers or other clients or do something else while we are waiting for this stuff to finish. So being able to define this is what allows our Python code to be executed in other different places in other functions and to be serving multiple clients, many, many clients at the same time, just by using this async and await. But of course, async and await, we need to have libraries that are compatible with this async and this await thing. For example, if we try to call a database with something that is not compatible with async and await, it will be equivalent to sending the waiter to prepare the dishes in the kitchen. That's not very efficient. So we have to be careful when we use async and await to make sure that the libraries that you use with async and await are actually compatible with async and await. But here are the good news. Fast API can be used with async and await or without async and await. So we can declare some functions like this, but then we can also, I will first, if we execute that, it will just work. We can just execute the request and then we will get the response and it will all just work just with async and await. But now we're gonna also declare some functions without async and then HTTPS is also compatible with blocking calls with non-async declarations. So in this case, we are just using get directly and then Fast API will do the smart thing and we'll actually run this stuff in a thread and everything. So the thing is that you can declare, you can write the code with just normal depth and then optimize only the things that really require the additional extra concurrency that really require to handle a lot of users of the same time and that you need it to be super fast. And then you can optimize those and put the async and await with the libraries that are compatible with async and await in those places as necessary. But then for the rest, you can just write the code very simply as this. So you can use with Fast API and HTTPS, you can just use normal depth functions or async depth and be happy. Cool, some of the people behind this async and await tools that I have been showing you. So Tom Christie is the creator of the HTTPS and Starlet and other tools. Florian Mumanca has been also commanding of HTTPS and you go doing is the author of the ASCII specification. This is the specification that Starlet and Fast API are built on top. So it's just a way to define how web servers in Python will interact using async and await. So these are like some of the people that are helping build the tools that allow these other tools and these libraries and these mechanisms to work. Now let's talk a little bit about performance with Fast API. When we talk about performance or this is talking about throughput or concurrency which is how many requests we can serve per second. So how many clients we can serve at the same time. And this is a benchmark run by Taken Power which is just a third party. They run benchmarks for a lot of programming languages and a lot of frameworks. In this case, we are filtering only by Python which are the blue lines and go lang which are the green lines. And go is a compiled language. So go has to be a lot faster than Python whenever it is used. Nevertheless, we can see that here's a block of Python frameworks for web APIs that can be faster than some of the go lang frameworks. So this is actually quite good. This is actually quite good performance and Fast API is here among this. Which one of this is the fastest? It's not necessarily that clear. This has some random noise. For example, Ubicorn is the server. This is not a framework. This is actually the server program that is running each one of those other frameworks. So Ubicorn has to be faster than anything else. So this has some random noise but still you can see that we can get on the ballpark of the best that you can get with Python. And well, Fast API is fast because it's based on top of the standard. And as you can see, we are being able to handle 15,000 requests per second, which is quite nice. So it's over 9,000 requests. Now, why is Fast API fast? Why is this performance? So Fast API doesn't really do anything fancy underneath. The Fast API is built on top of Starlet and Python. And Starlet is the web toolkit or micro framework that does all the web stuff. And Starlet is run by Ubicorn, which is the server program. And Ubicorn implements this asking specification. Starlet, of course, also implements the asking specification of Fast API because it's built on Starlet also implements the asking specification. But Ubicorn is built on top of or can be used with, by default, it uses UV loop. And UV loop is a high performance as in case you're dropping your placement. So this is a super fast way to do async and await. And UV loop is super fast because it's built on top of Python, which is a sort of compile Python language, something like that, to build C extensions for Python. Something like that. The thing is that this is a way to build super fast code that is compatible with Python and can be used from Python. Ubic loop is built using that. Ubicorn uses the UV loop and Starlet is used by Ubicorn and powers Fast API. Also, Fast API is built on top of Python. I think that was all the data validation, visualization and documentation. And Python is also built with Cython. So we get all the performance from this. So Fast API has good performance because it's built with high performance projects. And that's it. It's just a bunch of projects by a bunch of other people. Some of the people behind the scenes, Tom Christie again, the creator of Starlet, Ubicorn, HTTPS, and a bunch of other tools. Juni Silivano, this is the creator of UV loop. He's also the author of the async and await keywords in Python itself. And Stefan Benel is the maintainer of Cython. So this again, just normal people building a bunch of tools for us to work with. Now, a little bit about the community. As you can see, I have been showing you a lot of small pictures to show you that this is all built by normal people, mostly during free time and just trying to help others. And many of these maintainers might have hobbies, but in many cases, they just dedicate a bunch of time, a lot of time just to open source because this many cases just in free time. But you can actually help. For example, people translate the page of Fast API to help others be able to read it in a different language. Or you could review translations from others or you could answer questions from others in the issues or in Stack Overflow or things like that. Or you could even help with both some features. Or you could help with other projects with some of the projects that I showed you or maybe some of the projects that you are normally working with and using in your daily work. If you want to help Fast API, you can check this link. And if you want to help with the translations, you can check this link here. I encourage you to go and help the projects that you are working with and that you dependent because that's what these all depends on. And that's it. That's what I have for you. Thank you very much for having me. If you want to check the documentation for Fast API, you can check it here. And you can find me on github, LinkedIn, and Twitter. Now, I think we can check some questions. So, okay. So, thoughts on, actually, let me see. Can I move this over here so I can see it? Yes, nice. Thoughts on Psycho-PG3 with Fast API. So, Psycho-PG3, for those that don't know, is one of the drivers for interacting with Postgres SQL. And the author of Psycho-PG3 actually uses, actively uses Fast API, so he made sure that it works very well with Fast API, which is amazing. And Psycho-PG3 also has some improved support for async and await because there's also a async PG, which is super fast and also built with a Psython. So, yeah, it's a great tool and it's a very nice combination. So, let's see. What made you start the Fast API project? Did you have an instance where the solutions are available at the time who are looking for a task you wanted to accomplish? Yes, exactly that. If you want to read a little bit more about that in the documentation for Fast API, there's a section about alternatives and inspirations. And I tried to explain all the processes that I did. I tried to avoid building a new tool, a new framework for a long while, and I tried to use a lot of different combinations of plugins and tools. And at some point, I realized that there was none that was doing what I wanted based on type annotations, but having good performance, but having automatic integration with these standards and how all these tools put together. So I wanted to bring all the learnings from all the work that had been done by many other previous tools and many frameworks and even in all the languages and to put them together the best way that I put that. That's how I ended up building Fast API. So Fast API integrates other frameworks made by Bidantic and Starlet. Yes, that's correct. The development of Bidantic and Starlet is done separately. How do you plan to manage dependency issues in the future? So for example, Bidantic in its own test suite includes all the tests from Fast API. The author of Bidantic uses Fast API and is very involved with how these tools are integrated and they import and clone Fast API to run all the tests in Fast API to make sure that Bidantic actually works. And the author of Starlet is of course, also interested in having everything work with Fast API. And we're actually in constant communication. They are very nice people and I consider them internet friends sort of. So yeah, it's something that we are actually trying to build and to make sure that it actually works well. And of course, there's a lot of information in the Fast API documentation about how to pin versions to make sure that everything works correctly together. And Fast API has the versions very precisely defined to make sure that the dependencies are compatible and are correctly compatible, correctly having. Or you refer to a specific editor or plugin. So the good thing with actually with everything that I have been showing you is that it's all based on standard type annotations. This is all based on standard. This doesn't make anything fancy that is not just a standard Python. So all modern editors have great support for standard Python. The screen shows that I had where we PS code, but also Python has great support for standard type annotations. And in fact, the teams in PS code and in Python are like involved and interesting having great support for Fast API for these tools and have been doing a lot of work to make all these things work as best as possible. Is there any way to type annotations for our big Python projects? Are the tools to automate it? Yes, there are a couple of projects that you can run that you connect them to your application and you run them with your application and they save the types that they see that flow through your application to an internal database, to a SQLite internal database and then you can apply the type annotations from that SQLite database to the actual code. Sorry, to the actual code. So it's something a bit of a magical context but it can work in some cases probably in many cases. Of course, it depends on the project. If you have, for example, if you have a variable that you are overriding in a bunch of places and you are changing the type every time it's gonna be more difficult to annotate but it's also something that would probably be better refactor so that you are not changing the type of the same thing in multiple places. But yes, there are tools to do that and what I would recommend is to also adopt MyPy and include it in continuous integration because MyPy is the sort of the standard tool to check type annotations and it can be integrated with editors so that you can see the MyPy errors writing your editor in line. And you can configure MyPy to check Jallioli and to check some files and not others so that you can Jallioli increase the type annotations in multiple places. When integrating Fast API with ORMs wouldn't using context versus to store connection state cause a connection explosion problem since one thing can not handle thousands of requests. Was it going to start a subscription state? Yeah, well, so the thing is that with Fast API with ORMs, well, if you are using a, if you're using SQL argument or if you are using SQL or the documentation already shows you how to handle the sessions because it's better to handle the sessions a bit more explicitly unless you are an expert with context bars. I think it's easier to be explicit and to put them. And that's why Fast API dependencies work that way. And you can, I didn't show you any of the Fast API dependencies but there's a dependency injection system including Fast API that allows you handling all that in a very explicit way so that you can have a single session per request. But the session is actually not the same as a connection. For example, SQL Alchemy or SQL mode which is based on SQL Alchemy uses a connection pool and each session takes one of those connections and then returns a connection back to the pool when it's done. So you can have a controlled amount of connections to the database. But you can do all those things with Fast API dependencies in a way that is controlled and that is very explicit how you're handling it and to make sure that you're actually not stepping over different threads and things like that. How does Fast API decide whether the parameter that is not mentioned in the decorator integration should be a query parameter or something inside the payload. So if it's declared in the path parameter then it's just a path parameter. If it's not declared in the path parameter and it's a simple data type like an integer, a string, a flow or something like that then it's by default is taken from the query parameters. If it's a complex data type like a list or like a binding model then by default is taken from the body request. But you can configure all that and you can tell it exactly where do you want it to extract data and you can use that same mechanism to tell it to get data from headers, from cookies or from the query parameters or from the body explicitly. So you can also receive, for example, you can receive as a single string or a float in the body as JSON. A single float is like a single number. It's actually valid JSON data. So you could receive a single number as the body and you can declare that explicitly with Fast API. What is recommended? Mode of deployment of Fast API at separation that is for Wooski. So Wooski is a standard to define how servers, how application servers, so tools like Unicorn or Uwiski or things like that, run a piece of Python code built with a Wooski application like a Wooski application will be something like Flask or a Java. Fast API is not built on top of the Wooski standard with the ASSGI standard, which is more or less the same but using async and await. And the servers that you will use, the application servers will normally be Unicorn or Hypercorn or Daphne. And how you deploy in production, this is an interesting topic because the things that you want to make sure when you deploy in production is that the application is run automatically on startup. If you're running a single server and you start the server, you want it to be running automatically and you want to control how many processes are running at the same time. If you have four cores, you will probably want to have at least four processes to have one process running in each one of the cores. And then you also, so you want to have the replication. And you might also want to handle how much memory you are consuming. If you are doing machine learning, for example, that will consume a lot of memory so you don't want to have a lot of processes that they are all taking a lot of memory. Now, how you deploy that specifically depends on the specific architecture that you're using. If you are using something like Kubernetes, then you will probably want to run a single Uvicon container that is running a Uvicon process, a single process with Uvicon running your application and then replicate the amount of containers at the Kubernetes layer. But if you are running in a single server or in a simple deployment with Docker directly or with just Docker compose, then you can use the official FASTA API Docker image that runs Unicorn to execute the Uvicon and several processes at the same time based on the CPU that you have available. There is documentation for how to deploy a declaration or you could also use a third-party service like data or something like that. Or you could use, I don't know, serverless systems like the ones provided by Microsoft or AWS or Google or anything. There's a bunch of documentation about how to deploy this API. I have to update some of those things to clarify a little bit more, but there's already a lot of information on how to deploy a declaration. Yeah, so I think that's all, Sebastian. And that was really an amazing session and we are so honored to have you in Python India. And we really have a compliment for you, I think. So most of the folks are saying that, I don't know, you have a nice and sharp, nice thanks. It's really good to you. Thank you, thank you very much. It's been great to be here. Thank you very much for the invitation. It's amazing to be here, even if it's online. Yeah, thank you so much. Thank you.