Compare commits

..

6 Commits

Author SHA1 Message Date
Sebastián Ramírez
f2bd2c44e2 🔖 Release version 0.50.0 2020-02-29 21:49:09 +01:00
Sebastián Ramírez
f0beab1778 📝 Update release notes 2020-02-29 21:48:11 +01:00
Sebastián Ramírez
95f2dc065e 📝 Add link to the Release Notes from the section about pinning versions for deployment (#1058) 2020-02-29 21:47:46 +01:00
Sebastián Ramírez
4e8080f290 📌 Upgrade Starlette version (#1057) 2020-02-29 21:28:23 +01:00
Sebastián Ramírez
fbbed6fe81 📝 Update release notes 2020-02-29 19:22:08 +01:00
Sebastián Ramírez
2d5a5d0d9e 📝 Add docs about pinning versions for deployment (#1056) 2020-02-29 19:20:18 +01:00
8 changed files with 225 additions and 17 deletions

View File

@@ -1,4 +1,98 @@
You can use <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> for deployment. It has several advantages like security, replicability, development simplicity, etc.
Deploying a **FastAPI** application is relatively easy.
There are several ways to do it depending on your specific use case and the tools that you use.
You will see more about some of the ways to do it in the next sections.
## FastAPI versions
**FastAPI** is already being used in production in many applications and systems. And the test coverage is kept at 100%. But its development is still moving quickly.
New features are added frequently, bugs are fixed regularly, and the code is still continuously improving.
That's why the current versions are still `0.x.x`, this reflects that each version could potentially have breaking changes. This follows the <a href="https://semver.org/" class="external-link" target="_blank">Semantic Versioning</a> conventions.
You can create production applications with **FastAPI** right now (and you have probably been doing it for some time), you just have to make sure that you use a version that works correctly with the rest of your code.
### Pin your `fastapi` version
The first thing you should do is to "pin" the version of **FastAPI** you are using to the specific latest version that you know works correctly for your application.
For example, let's say you are using version `0.45.0` in your app.
If you use a `requirements.txt` file you could specify the version with:
```txt
fastapi==0.45.0
```
that would mean that you would use exactly the version `0.45.0`.
Or you could also pin it with:
```txt
fastapi>=0.45.0,<0.46.0
```
that would mean that you would use the versions `0.45.0` or above, but less than `0.46.0`, for example, a version `0.45.2` would still be accepted.
If you use any other tool to manage your installations, like Poetry, Pipenv, or others, they all have a way that you can use to define specific versions for your packages.
### Available versions
You can see the available versions (e.g. to check what is the current latest) in the [Release Notes](release-notes.md){.internal-link target=_blank}.
### About versions
Following the Semantic Versioning conventions, any version below `1.0.0` could potentially add breaking changes.
FastAPI also follows the convention that any "PATCH" version change is for bug fixes and non-breaking changes.
!!! tip
The "PATCH" is the last number, for example, in `0.2.3`, the PATCH version is `3`.
So, you should be able to pin to a version like:
```txt
fastapi>=0.45.0,<0.46.0
```
Breaking changes and new features are added in "MINOR" versions.
!!! tip
The "MINOR" is the number in the middle, for example, in `0.2.3`, the MINOR version is `2`.
### Upgrading the FastAPI versions
You should add tests for your app.
With **FastAPI** it's very easy (thanks to Starlette), check the docs: [Testing](tutorial/testing.md){.internal-link target=_blank}
After you have tests, then you can upgrade the **FastAPI** version to a more recent one, and make sure that all your code is working correctly by running your tests.
If everything is working, or after you make the necessary changes, and all your tests are passing, then you can pin your `fastapi` to that new recent version.
### About Starlette
You shouldn't pin the version of `starlette`.
Different versions of **FastAPI** will use a specific newer version of Starlette.
So, you can just let **FastAPI** use the correct Starlette version.
### About Pydantic
Pydantic includes the tests for **FastAPI** with its own tests, so new versions of Pydantic (above `1.0.0`) are always compatible with FastAPI.
You can pin Pydantic to any version above `1.0.0` that works for you and below `2.0.0`.
For example:
```txt
pydantic>=1.2.0,<2.0.0
```
## Docker
In this section you'll see instructions and links to guides to know how to:
@@ -7,11 +101,7 @@ In this section you'll see instructions and links to guides to know how to:
* Set up a Docker Swarm mode cluster with automatic HTTPS, even on a simple $5 USD/month server. In about **20 min**.
* Generate and deploy a full **FastAPI** application, using your Docker Swarm cluster, with HTTPS, etc. In about **10 min**.
---
You can also easily use **FastAPI** in a standard server directly too (without Docker).
## Docker
You can use <a href="https://www.docker.com/" class="external-link" target="_blank">**Docker**</a> for deployment. It has several advantages like security, replicability, development simplicity, etc.
If you are using Docker, you can use the official Docker image:

View File

@@ -1,5 +1,15 @@
## Latest changes
## 0.50.0
* Add link to Release Notes from docs about pinning versions for deployment. PR [#1058](https://github.com/tiangolo/fastapi/pull/1058).
* Upgrade code to use the latest version of Starlette, including:
* Several bug fixes.
* Optional redirects of slashes, with or without ending in `/`.
* Events for routers, `"startup"`, and `"shutdown"`.
* PR [#1057](https://github.com/tiangolo/fastapi/pull/1057).
* Add docs about pinning FastAPI versions for deployment: [Deployment: FastAPI versions](https://fastapi.tiangolo.com/deployment/#fastapi-versions). PR [#1056](https://github.com/tiangolo/fastapi/pull/1056).
## 0.49.2
* Fix links in release notes. PR [#1052](https://github.com/tiangolo/fastapi/pull/1052) by [@sattosan](https://github.com/sattosan).

View File

@@ -1,6 +1,6 @@
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
__version__ = "0.49.2"
__version__ = "0.50.0"
from starlette.background import BackgroundTasks

View File

@@ -18,8 +18,8 @@ from fastapi.params import Depends
from fastapi.utils import warning_response_model_skip_defaults_deprecated
from starlette.applications import Starlette
from starlette.datastructures import State
from starlette.exceptions import ExceptionMiddleware, HTTPException
from starlette.middleware.errors import ServerErrorMiddleware
from starlette.exceptions import HTTPException
from starlette.middleware import Middleware
from starlette.requests import Request
from starlette.responses import HTMLResponse, JSONResponse, Response
from starlette.routing import BaseRoute
@@ -29,9 +29,9 @@ from starlette.types import Receive, Scope, Send
class FastAPI(Starlette):
def __init__(
self,
*,
debug: bool = False,
routes: List[BaseRoute] = None,
template_directory: str = None,
title: str = "FastAPI",
description: str = "",
version: str = "0.1.0",
@@ -42,19 +42,28 @@ class FastAPI(Starlette):
redoc_url: Optional[str] = "/redoc",
swagger_ui_oauth2_redirect_url: Optional[str] = "/docs/oauth2-redirect",
swagger_ui_init_oauth: Optional[dict] = None,
middleware: Sequence[Middleware] = None,
exception_handlers: Dict[Union[int, Type[Exception]], Callable] = None,
on_startup: Sequence[Callable] = None,
on_shutdown: Sequence[Callable] = None,
**extra: Dict[str, Any],
) -> None:
self.default_response_class = default_response_class
self._debug = debug
self.state = State()
self.router: routing.APIRouter = routing.APIRouter(
routes, dependency_overrides_provider=self
routes,
dependency_overrides_provider=self,
on_startup=on_startup,
on_shutdown=on_shutdown,
)
self.exception_middleware = ExceptionMiddleware(self.router, debug=debug)
self.error_middleware = ServerErrorMiddleware(
self.exception_middleware, debug=debug
self.exception_handlers = (
{} if exception_handlers is None else dict(exception_handlers)
)
self.user_middleware = [] if middleware is None else list(middleware)
self.middleware_stack = self.build_middleware_stack()
self.title = title
self.description = description
self.version = version

View File

@@ -346,9 +346,15 @@ class APIRouter(routing.Router):
dependency_overrides_provider: Any = None,
route_class: Type[APIRoute] = APIRoute,
default_response_class: Type[Response] = None,
on_startup: Sequence[Callable] = None,
on_shutdown: Sequence[Callable] = None,
) -> None:
super().__init__(
routes=routes, redirect_slashes=redirect_slashes, default=default
routes=routes,
redirect_slashes=redirect_slashes,
default=default,
on_startup=on_startup,
on_shutdown=on_shutdown,
)
self.dependency_overrides_provider = dependency_overrides_provider
self.route_class = route_class
@@ -552,6 +558,10 @@ class APIRouter(routing.Router):
self.add_websocket_route(
prefix + route.path, route.endpoint, name=route.name
)
for handler in router.on_startup:
self.add_event_handler("startup", handler)
for handler in router.on_shutdown:
self.add_event_handler("shutdown", handler)
def get(
self,

View File

@@ -32,7 +32,7 @@ classifiers = [
"Topic :: Internet :: WWW/HTTP",
]
requires = [
"starlette >=0.12.9,<=0.12.9",
"starlette ==0.13.2",
"pydantic >=0.32.2,<2.0.0"
]
description-file = "README.md"

View File

@@ -21,10 +21,12 @@ client = TestClient(app)
def test_use_empty():
with client:
response = client.get("/prefix")
assert response.status_code == 200
assert response.json() == ["OK"]
response = client.get("/prefix/")
assert response.status_code == 404
assert response.status_code == 200
assert response.json() == ["OK"]
def test_include_empty():

View File

@@ -0,0 +1,87 @@
from fastapi import APIRouter, FastAPI
from pydantic import BaseModel
from starlette.testclient import TestClient
class State(BaseModel):
app_startup: bool = False
app_shutdown: bool = False
router_startup: bool = False
router_shutdown: bool = False
sub_router_startup: bool = False
sub_router_shutdown: bool = False
state = State()
app = FastAPI()
@app.on_event("startup")
def app_startup():
state.app_startup = True
@app.on_event("shutdown")
def app_shutdown():
state.app_shutdown = True
router = APIRouter()
@router.on_event("startup")
def router_startup():
state.router_startup = True
@router.on_event("shutdown")
def router_shutdown():
state.router_shutdown = True
sub_router = APIRouter()
@sub_router.on_event("startup")
def sub_router_startup():
state.sub_router_startup = True
@sub_router.on_event("shutdown")
def sub_router_shutdown():
state.sub_router_shutdown = True
@sub_router.get("/")
def main():
return {"message": "Hello World"}
router.include_router(sub_router)
app.include_router(router)
def test_router_events():
assert state.app_startup is False
assert state.router_startup is False
assert state.sub_router_startup is False
assert state.app_shutdown is False
assert state.router_shutdown is False
assert state.sub_router_shutdown is False
with TestClient(app) as client:
assert state.app_startup is True
assert state.router_startup is True
assert state.sub_router_startup is True
assert state.app_shutdown is False
assert state.router_shutdown is False
assert state.sub_router_shutdown is False
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"message": "Hello World"}
assert state.app_startup is True
assert state.router_startup is True
assert state.sub_router_startup is True
assert state.app_shutdown is True
assert state.router_shutdown is True
assert state.sub_router_shutdown is True