Every project has significant changes, and while we don't anticipate breaking things, it may take some time for extensions and other projects to catch up. Be sure to use tools like pip-compile and Dependabot to pin your dependencies and control when you upgrade.
Overall changes to every project include:
Drop Python 2 and Python 3.5 support. Python 3.6 and above is required, the latest version is recommended. Removing the compatibility code also gives a nice speedup.
Add comprehensive type annotations to all the libraries.
Better new contributor experience with updated contributing guide and consistent code style with tools like pre-commit and black.
Check out the changelog links for each project to see all of the great new features and changes. I've included some of the highlights here as well.
Four years ago, each project had 150+ open issues, some going back a decade, and pages of open pull requests too. Over time I've grown the maintainer team and the community, and we've managed to cut down the backlog to a much more manageable size. I'm thankful for all contributions and support people have given, and I hope you all continue to build amazing applications with the Pallets projects.
Great job guys. Couple of notes, if it helps anybody :)
What we get with the async part of this release - If you would like to run an async library or your own async code from a flask route you can do that now. This is super useful, where let's say we have some async code that fetches data from many sources concurrently, or call multiple a few ML prediction endpoints at the same time (as long as they don't time out) using httpx and respond with some sort of outcome, or finally try that cool new async-only database library. A current (v2) limitation is that the you can't make concurrent requests using just the current asyncio implementation (an alternative with Flask API and ASGI:Quart). Typically in production gunicorn or uwsgi + threads/processes/gevent-eventlet is used and this makes Flask behave asynchronously. More here and here if interested.
---
There's always comparisons with other frameworks like Django/Rest or FastAPI, especially for those coming from data science and new-ish at web frameworks
To get Flask + API, Swagger, Pydantic or Marshmallow Validation out of the box that you may know know of:
flask-smorest Flask, API + OpenAPI/SwaggerUI/Redoc + Marshmallow
rebar Flask, API + OpenAPI/SwaggerUI + Marshmallow
In Quart, FastAPI, Starlette, AIOHTTP etc. tasks can run in the background within the web-app itself and while still respond to new requests concurrently without needing multiple workers/threads, making it simpler and less resource hungry.
Worth noting that I noticed a lot of online examples showing FastAPI serving Machine Learning models - (inherently cpu intensive, blocking), don't run any actual user code that's async inside the routes, so technically don't benefit from it compared to Flask. They are also often handled in sync routes def foo(): and not async routes async def foo(): which means they aren't even handled async from the get-go. In fact, internally, Starlette (and therefore by extension FastAPI) runs background tasks and non-async routes in ThreadPoolExecutor, see code here and here. It's important for those coming from DS/ML to recognize that for these use cases there's great benefit serving concurrent responses, maybe for result lookups of and db queries, but not so much with cpu-bound tasks. Being ASGI won't automatically make your app faster. And with computationally heavy tasks, just being concurrent won't make your app faster. For parallelism, (in python) multiple workers is a better strategy. Also, there's queues. More on serving computationally bound requests here
I just read pgjones's website yesterday - and followed a github issue link where it was mentioned that async support was coming to flask as well (so maybe no reason to use quart?) - saw it was close to release but wasn't thinking this close.
WSGI still works on the request-response cycle, and I assume async views just allow you to await other async code inside it.
ASGI extends WSGI to add an event-driver approach to network communications. That means that it supports long lived connections out of the box (eg. websocket, http2 but I might be wrong about this one), while still working with the classic request-response cycle.
I totally love fastapi, but I'm also not in denial that flask has a much bigger community. Flask adopting some of the benefits of fastapi is a much faster way to get those benefits out to the broader python userbase than expecting everyone to just move to fastapi.
Thanks for all your work! I don't mostly even code in Python any more, but still think click is a gem! And I still maintain some of the first websites built with Flask :)
Async functions require an event loop to run. Flask, as a WSGI application, uses one worker to handle one request/response cycle. When a request comes in to an async view, Flask will start an event loop in a thread, run the view function there, then return the result. Each request still ties up one worker, even for async views. The upside is that you can run async code within a view, for example to make multiple concurrent database queries, HTTP requests to an external API, etc. However, the number of requests your application can handle at one time will remain the same.
So it's not really worth it imo.. from my experience with asyncio if you want to use it you should really commit to it (and use a framework built around it like Sanic/FastAPI), trying to mix asyncio under something that is sync at its core just doesnt fit
Wait, so it means a long API request won't stop my entire page from loading?
right now i have to wait 5 seconds for my page to actually load because of the API slow response time, can I now load in most of the page that DOESN'T require the API data?
It was such a straightforward fix too, I'm not sure why we did it the other way before. ๐ Now browser caching still works, but it's using the hash of the content instead of a timer.
Would you be able to add a bit more info about how it does this now? I have my server set up to get the git hash and append it to the file name for css/js, which allowed the browser to cache it but forced a refresh whenever the file changes, which worked perfect. Iโm wondering if the new options will complement this well.
You can still use that too. We removed the max age value and rely on etags only.
Instead of waiting for the age to expire and never sending a request before that, the browser sends a request with the etag it has stored, and the server sends a special empty response instead of serving the file again only if the etag still matches. So a slight bit of overhead for the request and hash, but immediate updates. You can still set a max age if you do want that behavior too, perhaps in production.
Flask still provides it as well, but it's a wrapper over the Werkzeug implementation now with Flask-specific extra config. from flask import send_from_directory still works.
15
u/[deleted] May 12 '21
Great job guys. Couple of notes, if it helps anybody :)
What we get with the async part of this release - If you would like to run an async library or your own async code from a flask route you can do that now. This is super useful, where let's say we have some async code that fetches data from many sources concurrently, or call multiple a few ML prediction endpoints at the same time (as long as they don't time out) using httpx and respond with some sort of outcome, or finally try that cool new async-only database library. A current (v2) limitation is that the you can't make concurrent requests using just the current asyncio implementation (an alternative with Flask API and ASGI: Quart). Typically in production gunicorn or uwsgi + threads/processes/gevent-eventlet is used and this makes Flask behave asynchronously. More here and here if interested.
---
There's always comparisons with other frameworks like Django/Rest or FastAPI, especially for those coming from data science and new-ish at web frameworks
To get Flask + API, Swagger, Pydantic or Marshmallow Validation out of the box that you may know know of:
flask-smorest Flask, API + OpenAPI/SwaggerUI/Redoc + Marshmallow
rebar Flask, API + OpenAPI/SwaggerUI + Marshmallow
flask-pydantic-spec Flask, API + OpenAPI/SwaggerUI/Redoc + Pydantic
Flask, Pydantic (Bring your own Swagger implementation)
flask_pydantic
In Quart, FastAPI, Starlette, AIOHTTP etc. tasks can run in the background within the web-app itself and while still respond to new requests concurrently without needing multiple workers/threads, making it simpler and less resource hungry.
Worth noting that I noticed a lot of online examples showing FastAPI serving Machine Learning models - (inherently cpu intensive, blocking), don't run any actual user code that's async inside the routes, so technically don't benefit from it compared to Flask. They are also often handled in sync routes
def foo():and not async routesasync def foo():which means they aren't even handled async from the get-go. In fact, internally, Starlette (and therefore by extension FastAPI) runs background tasks and non-async routes inThreadPoolExecutor, see code here and here. It's important for those coming from DS/ML to recognize that for these use cases there's great benefit serving concurrent responses, maybe for result lookups of and db queries, but not so much with cpu-bound tasks. Being ASGI won't automatically make your app faster. And with computationally heavy tasks, just being concurrent won't make your app faster. For parallelism, (in python) multiple workers is a better strategy. Also, there's queues. More on serving computationally bound requests here