During the next days, I will be sharing here, in small batches, a short summary of my keynote talk at @PyConChina.
Let's explore some "Modern Python through FastAPI and friends" πππ
Hey! π This is me. I work at @explosion_ai. We build very cool stuff.
And I created FastAPI and Typer (that's why they invited me π
).
fastapi.tiangolo.com
typer.tiangolo.com
If you don't know, FastAPI is a web API framework, it has been growing, and a bunch of organizations are using it. π
And you get quite decent performance (more on that later). π
I'll share some ideas about modern Python, so, the currently supported versions (3.6 and above).
π f-strings (a bit)
π§ Type annotations (a lot)
β± async / await
β‘ Performance
π₯ Community
I'll show you FastAPI, Typer, and friends as examples.
The first Twitter batch (today) will be about f-strings. π
This is the shortest/simplest part, we have to start somewhere. π€·
Let's take this example code. All on the same file.
To format the "message", we have to repeat the text "name" 3 times.
That's a lot of code repetition. π
We can reduce code repetition using an f-string, just a string with an "f" before the first quote, with variables inside. π€
We just need to make sure the variables are available right outside the f-string.
And if we call it, it just works, as normally.
And we saved a lot of code repetition. π
...that's it for this first batch. I'll add more here during the next days. π
Part 2: Let's talk more about Modern Python! πππ
This time, about type annotations, also called "type hints" and why they are awesome. π§
I'll use the same example from the beginning.
We don't want the title "crunchy-frog", we want "Crunchy Frog".
There's a method to change one string for another (e.g. dash for space). What's the method name? "sub"? β
Let's trigger autocomplete... oh, no, it doesn't work π©
But we can just annotate the "name" with a str type π§
And suddenly we have autocompletion! π
The editor is smart enough to know that the result is also a str. So it can keep giving us autocompletion. π
And with that help, we will probably get the code right on the first try more often. π
There it is, the delicious "Crunchy Frog". πΈ
Now, as we don't know if everyone really wants one, more, or just the recipe, let's refactor "quantity" to default to None.
Yeah, that's simple, right? π€·
Oh, no! There's an error on line 23! π±
It seems we can't multiply 1 * None π€¦
That's fine. It's an easy/common mistake to make. What is not cool is that we don't notice it π¨ until we run π¨ the code.
But we can add a type annotation of int, we can even keep the default of None. π§
That change is easy enough.
And with just that change, the editor and tools can infer that the variable "quantity" can be an int or None.
And will show us the error right there in the code β¨before we run it β¨.
Now that we know what could go wrong, we can simply fix it. π
Let's create the data first...
...and add the total later.
What's that error now? The editor and tools see we are using type annotations and try to be even smarter. π€
So they infer the type of the dict from the existing data, using the last key (string) and value (a list of strings).
We can tell the editor and tools "don't try so hard to infer the type, this is a dict of whatever, don't bother about the values". π€·
We do that by annotating the "result" as a plain dict.
Also see that the editor and tools are smart enough to know that quantity could be an int, or also None. π€
But even better, inside an if statement checking that it is not None, the editor and tools will know that the only possible type (in that block) is int (not None). π§β¨
And we can be even more explicit/formal/strict and annotate the variable as an "Optional" int. That means it can be an int or None. π€
Here are some of the people that did a lot of the work behind these type annotations and tools, so that we can use them now in Modern Python. πβ¨
@JukkaLeh
@ILevkivskyi
@msully4321
That's it for today. I'll share more on Modern Python here tomorrow. π
Part 3: In our exploration of Modern Python, let's make a web API with type annotations, using FastAPI. π
Let's extend the previous example.
Just import and create a FastAPI app, and decorate the function. β¨
The path "/recipes/{name}" means "name" is a path parameter.
Of course, thanks to type annotations, we still have autocompletion. π§
And again, thanks to type annotations, we still have type error checks. π―
And FastAPI uses the standard type annotations to create an API with interactive documentation for us. π
It's β¨ interactive β¨, we can send requests live and receive the responses.
And we get automatic data validation for those type annotations. π‘
If we pass something that is not an integer, the UI complains.
Of course, even if a client "cheats" and sends a request from the CLI, FastAPI will validate the data and send a nice validation error. π‘
If we pass valid data, the "2" will still go in the URL, and the URL is naturally really just a long string.
But FastAPI is smart enough to also do data conversion from those type annotations. π§ͺ
...so, our code (and here, the response) will see an actual integer where it was declared, instead of the string "2". π§ͺ
FastAPI uses the standard type annotations combined with several standards, so we get that nice automatic UI (provided by @SwaggerApi). π¨
And thanks to the same type annotations, we get autocompletion and type checks. β¨
That's it for today. More Modern Python later! ππ
Part 4: More modern Python! ππ
Let's use the standard type annotations to create a command line interface (CLI) application with Typer.
Again, the same code example...
We just import typer, create an app, and decorate the same function, with the same type annotations. β¨
This time, we don't want to only "return", we also want to print on screen with typer.echo() to see the result.
Typer will use that to create a nice CLI, that detects required parameters, and includes an automatic --help option. β¨
Typer will automatically create CLI Arguments from the type annotations. π
It will also automatically create CLI Options, like --quantity, when they have default values.
Typer will use the standard type annotations to do data validation and data conversion. π§
When a CLI app created with Typer is installed, the user will have shell completion. β¨
In this example, it's an app also called typer.
But what we print is not looking very nice. Let's make it pretty with Rich. π¨
We just create a Rich table and print with Rich's console.
And now we have a very nice terminal output with our data. π
Typer is based on Click, it's used for everything underneath.
Typer mainly adds the layer to handle it all from standard type annotations. π§
Click is maintained by @davidism.
And Rich was created by @willmcgugan.
More about Typer: typer.tiangolo.com
Part 5: Let's talk more about modern Python with type annotations. π§
With FastAPI and Pydantic.
Pydantic is what powers all the data handling underneath in FastAPI, and it's great as a standalone library as well. π
We can use Pydantic to create a class using standard type annotations, pretty much like with dataclasses. π€
Then use that Pydantic class (model) to declare the body of a request in our API with FastAPI. π
The automatic API documentation will show the new data shape (schema) defined with the Pydantic class. π¨
As the automatic API docs are interactive, you can send actual requests. π¬
...and get the response as expected.
What happens if a client sends invalid data inside of the contents of the JSON message? π¨
FastAPI will use Pydantic to detect it and return a nice and clear error to the client. π‘
If we want deeply nested data structures, again, we just use standard type annotations. π§
Because we are using standard type annotations, autocomplete will keep working. Even for these complex and deeply nested data structures. π§
(Notice this is a double for loop). πͺ
We also get type error checks in the editor, again, thanks to standard type annotations. β¨
Some of the people behind these tools. π₯
FastAPI is built on top of @samuel_colvin's Pydantic for the data handling, and @_tomchristie's Starlette for the web parts.
David Montague also helped a lot, so he deserves a special mention. π€
That's it for today! π
Part 6: Let's talk about Modern Python with async and await! π
This is what drives all the performance in terms of concurrency (things executed during the same time range). πͺ
So, concurrency ends up being the number of users served per second, by the same code and process. π
So what's the deal with concurrency?
Python is a super-fast language... when you compare it to the network. π
The network is so slow that it becomes the bottleneck, and the specific language is less important. π’
The first idea to improve this: let's serve multiple (slow) network clients with the same (fast) server code.
That's a nice idea, at least in theory. π‘
But in reality, the way standard Python works (without async and await) is like a grocery shop line.
Each client has to finish their process, slowly counting coins, etc. before the next client can start their request. πͺ
So, in diagrams, it looks like this. Each client has to wait for the previous one to finish everything (including the slow network) before being able to start sending their request. π©
With async and await, Python has something like an internal waiter. π
Each client sits at a table, and orders their food, even though the kitchen hasn't finished preparing the food for several previous clients. π¨βπ³
The important thing is that with a waiter, the kitchen is preparing food non-stop. π
Python is using the processor as much as possible, always computing some request, instead of waiting for the slow network. πͺ
In code, using FastAPI, you create the function to handle a request using async def.
And then, inside, you use await to mark the "await"-compatible points that need to be awaited (that are slow). π€
Here, using the awesome HTTPX. π
And of course, everything works as normal. The UI docs still work. π¨
And you can get responses as normal.
But this time, being able to handle some extra thousands of users per second. π
A caveat: the libraries have to be async/await compatible and use async def for any awaitable function. π¨
And the internal code shouldn't do slow computations without await-compatibility.
Good news: it's optional with FastAPI and HTTPX. So you don't have to use async/await. β¨
Some people behind these tools π₯:
@_tomchristie, creator of Starlette and HTTPX
@florimondmanca, co-maintainer of HTTPX
@andrewgodwin, author of ASGI, the spec to use async/await in Python for the web
In terms of concurrency, FastAPI has good performance. π
This graph is from a well-done third-party benchmark by @TFBenchmarks.
Green lines are Go, blue lines are Python.
So, FastAPI gives you pretty decent performance for an interpreted language. π
Why is FastAPI fast?
It's built on top of Starlette, normally run with Uvicorn, that uses Uvloop, a high-performance drop-in replacement for asyncio (it powers all the async/await stuff).
And Uvloop and Pydantic are both built with Cython. A sort-of compiled Python. π
Some of the people behind these tools π₯:
Again @_tomchristie, creator of Uvicorn (and Starlette, HTTPX, etc)
@1st1 creator of Uvloop
Stefan Behnel, the current maintainer of Cython
I have been showing you a bunch of people. π₯
I want to highlight that all this work, all these tools, are built and done by normal people like you and me.
Mostly done during our free time, just trying to help others. π
For example, I don't speak Chinese.
But the FastAPI community in China has contributed a lot of translations of the FastAPI docs. π
Here are some of the people that help a lot with the FastAPI community in China. π
For example, @phy25 is one of the top-3 FastAPI Experts. π
And @waynerv77 is currently the top (external) contributor to FastAPI, for translating a lot of the FastAPI docs. π
And you can help too! π€ (and this doesn't apply only to China).
Help FastAPI: fastapi.tiangolo.com/help-fastapi/
Help with FastAPI translations: fastapi.tiangolo.com/contributing/#β¦
Or go and help with some of the other projects I have been mentioning! πͺ
Thanks for exploring some Modern Python through FastAPI and friends with me! ππ
You can check the FastAPI docs at fastapi.tiangolo.com
End. π
Share this Scrolly Tale with your friends.
A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.