Compare commits

...

15 Commits

Author SHA1 Message Date
Sebastián Ramírez
9c3c9b6e78 🔖 Release version 0.49.0 2020-02-16 21:11:28 +01:00
Sebastián Ramírez
687871e46a 📝 Update release notes 2020-02-16 21:08:58 +01:00
Patrick McKenna
3c1803897f 🐛 Fix path encoding (#978) 2020-02-16 21:06:41 +01:00
T. Tokusumi
1c3289f115 📝 Add link: JP articles related to fastapi (#974) 2020-02-16 21:00:42 +01:00
Taras Sotnikov
35d41adc7b 📝 Fix broken link in docs (#949) 2020-02-16 20:57:29 +01:00
Sebastián Ramírez
9a3dd91030 📝 Update release notes 2020-02-16 20:55:07 +01:00
Sebastián Ramírez
017eae63b1 📝 Update release notes 2020-02-16 20:53:50 +01:00
Nikita Kolesov
8af4454251 ✏️ Fix typo (#941) 2020-02-16 20:51:18 +01:00
Sebastián Ramírez
adf252768c 📝 Update docs for dependencies with yield (#986) 2020-02-16 20:49:12 +01:00
Sebastián Ramírez
3705bdefd5 📝 Update release notes 2020-02-16 19:48:52 +01:00
Sebastián Ramírez
25e94d6344 Add mermaid.js support in Markdown fenced blocks for diagrams (#985) 2020-02-16 19:48:20 +01:00
Sebastián Ramírez
26c345b766 📝 Update release notes 2020-02-15 13:26:01 +01:00
Sebastián Ramírez
9ce50b4684 👷 Add GitHub actions to deploy to Netlify (#983) 2020-02-15 13:24:34 +01:00
Sebastián Ramírez
66954c7ded 📝 Update release notes 2020-02-12 21:37:05 +01:00
Sebastián Ramírez
e0c3519b94 Allow callables (as functools.partial) in path operations (#977) 2020-02-12 21:36:14 +01:00
16 changed files with 240 additions and 12 deletions

29
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Build and Deploy to Netlify
on:
push:
pull_request:
types: [opened, synchronize]
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: "3.7"
- name: Install Flit
run: python3.7 -m pip install flit
- name: Install docs extras
run: python3.7 -m flit install --extras doc
- name: Build MkDocs
run: python3.7 -m mkdocs build
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1.0
with:
publish-dir: './site'
production-branch: master
github-token: ${{ secrets.GITHUB_TOKEN }}
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

View File

@@ -79,6 +79,10 @@ Here's an incomplete list of some of them.
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-admin-page-improvement" class="external-link" target="_blank">【第4回】FastAPIチュートリアル: toDoアプリを作ってみよう【管理者ページ改良編】</a> by <a href="https://rightcode.co.jp/author/jun" class="external-link" target="_blank">ライトコードメディア編集部</a>
* <a href="https://qiita.com/bee2/items/0ad260ab9835a2087dae" class="external-link" target="_blank">PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
* <a href="https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9" class="external-link" target="_blank">[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
### Chinese
* <a href="https://cloud.tencent.com/developer/article/1431448" class="external-link" target="_blank">使用FastAPI框架快速构建高性能的api服务</a> by <a href="https://cloud.tencent.com/developer/user/5471722" class="external-link" target="_blank">逍遥散人</a>.

View File

@@ -1,4 +1,4 @@
Are you liking **FastAPI**?
Do you like **FastAPI**?
Would you like to help FastAPI, other users, and the author?

View File

@@ -256,7 +256,7 @@ Taken from the official Pydantic docs:
**FastAPI** is all based on Pydantic.
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/){.internal-link target=_blank}.
You will see a lot more of all this in practice in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
## Type hints in **FastAPI**
@@ -276,7 +276,7 @@ With **FastAPI** you declare parameters with type hints and you get:
* **Document** the API using OpenAPI:
* which is then used by the automatic interactive documentation user interfaces.
This might all sound abstract. Don't worry. You'll see all this in action in the [Tutorial - User Guide](tutorial/){.internal-link target=_blank}.
This might all sound abstract. Don't worry. You'll see all this in action in the [Tutorial - User Guide](tutorial/index.md){.internal-link target=_blank}.
The important thing is that by using standard Python types, in a single place (instead of adding more classes, decorators, etc), **FastAPI** will do a lot of the work for you.

View File

@@ -1,5 +1,16 @@
## Latest changes
## 0.49.0
* Fix encoding of `pathlib` paths in `jsonable_encoder`. PR [#978](https://github.com/tiangolo/fastapi/pull/978) by [@patrickmckenna](https://github.com/patrickmckenna).
* Add articles to [External Links](https://fastapi.tiangolo.com/external-links/): [PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)](https://qiita.com/bee2/items/0ad260ab9835a2087dae) and [[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する](https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9). PR [#974](https://github.com/tiangolo/fastapi/pull/974) by [@tokusumi](https://github.com/tokusumi).
* Fix broken links in docs. PR [#949](https://github.com/tiangolo/fastapi/pull/949) by [@tsotnikov](https://github.com/tsotnikov).
* Fix small typos. PR [#941](https://github.com/tiangolo/fastapi/pull/941) by [@NikitaKolesov](https://github.com/NikitaKolesov).
* Update and clarify docs for dependencies with `yield`. PR [#986](https://github.com/tiangolo/fastapi/pull/986).
* Add Mermaid JS support for diagrams in docs. Add first diagrams to [Dependencies: First Steps](https://fastapi.tiangolo.com/tutorial/dependencies/) and [Dependencies with `yield` and `HTTPException`](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-with-yield/#dependencies-with-yield-and-httpexception). PR [#985](https://github.com/tiangolo/fastapi/pull/985).
* Update CI to run docs deployment in GitHub actions. PR [#983](https://github.com/tiangolo/fastapi/pull/983).
* Allow `callable`s in *path operation functions*, like functions modified with `functools.partial`. PR [#977](https://github.com/tiangolo/fastapi/pull/977).
## 0.48.0
* Run linters first in tests to error out faster. PR [#948](https://github.com/tiangolo/fastapi/pull/948).

View File

@@ -102,6 +102,79 @@ You can have any combinations of dependencies that you want.
**FastAPI** uses them internally to achieve this.
## Dependencies with `yield` and `HTTPException`
You saw that you can use dependencies with `yield` and have `try` blocks that catch exceptions.
It might be tempting to raise an `HTTPException` or similar in the exit code, after the `yield`. But **it won't work**.
The exit code in dependencies with `yield` is executed *after* [Exception Handlers](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. There's nothing catching exceptions thrown by your dependencies in the exit code (after the `yield`).
So, if you raise an `HTTPException` after the `yield`, the default (or any custom) exception handler that catches `HTTPException`s and returns an HTTP 400 response won't be there to catch that exception anymore.
This is what allows anything set in the dependency (e.g. a DB session) to, for example, be used by background tasks.
Background tasks are run *after* the response has been sent. So there's no way to raise an `HTTPException` because there's not even a way to change the response that is *already sent*.
But if a background task creates a DB error, at least you can rollback or cleanly close the session in the dependency with `yield`, and maybe log the error or report it to a remote tracking system.
If you have some code that you know could raise an exception, do the most normal/"Pythonic" thing and add a `try` block in that section of the code.
If you have custom exceptions that you would like to handle *before* returning the response and possibly modifying the response, maybe even raising an `HTTPException`, create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
!!! tip
You can still raise exceptions including `HTTPException` *before* the `yield`. But not after.
The sequence of execution is more or less like this diagram. Time flows from top to bottom. And each column is one of the parts interacting or executing code.
```mermaid
sequenceDiagram
participant client as Client
participant handler as Exception handler
participant dep as Dep with yield
participant operation as Path Operation
participant tasks as Background tasks
Note over client,tasks: Can raise exception for dependency, handled after response is sent
Note over client,operation: Can raise HTTPException and can change the response
client ->> dep: Start request
Note over dep: Run code up to yield
opt raise
dep -->> handler: Raise HTTPException
handler -->> client: HTTP error response
dep -->> dep: Raise other exception
end
dep ->> operation: Run dependency, e.g. DB session
opt raise
operation -->> handler: Raise HTTPException
handler -->> client: HTTP error response
operation -->> dep: Raise other exception
end
operation ->> client: Return response to client
Note over client,operation: Response is already sent, can't change it anymore
opt Tasks
operation -->> tasks: Send background tasks
end
opt Raise other exception
tasks -->> dep: Raise other exception
end
Note over dep: After yield
opt Handle other exception
dep -->> dep: Handle exception, can't change response. E.g. close DB session.
end
```
!!! info
Only **one response** will be sent to the client. It might be one of the error responses or it will be the response from the *path operation*.
After one of those responses is sent, no other response can be sent.
!!! tip
This diagram shows `HTTPException`, but you could also raise any other exception for which you create a [Custom Exception Handler](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}. And that exception would be handled by that custom exception handler instead of the dependency exit code.
But if you raise an exception that is not handled by the exception handlers, it will be handled by the exit code of the dependency.
## Context Managers
### What are "Context Managers"

View File

@@ -82,6 +82,19 @@ Whenever a new request arrives, **FastAPI** will take care of:
* Get the result from your function.
* Assign that result to the parameter in your *path operation function*.
```mermaid
graph TB
common_parameters(["common_parameters"])
read_items["/items/"]
read_users["/users/"]
common_parameters --> read_items
common_parameters --> read_users
```
This way you write shared code once and **FastAPI** takes care of calling it for your *path operations*.
!!! check
Notice that you don't have to create a special class and pass it somewhere to **FastAPI** to "register" it or anything similar.
@@ -154,7 +167,39 @@ Although the hierarchical dependency injection system is very simple to define a
You can define dependencies that in turn can define dependencies themselves.
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and your dependencies) and providing (injecting) the results at each step.
In the end, a hierarchical tree of dependencies is built, and the **Dependency Injection** system takes care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting) the results at each step.
For example, let's say you have 4 API endpoints (*path operations*):
* `/items/public/`
* `/items/private/`
* `/users/{user_id}/activate`
* `/items/pro/`
then you could add different permission requirements for each of them just with dependencies and sub-dependencies:
```mermaid
graph TB
current_user(["current_user"])
active_user(["active_user"])
admin_user(["admin_user"])
paying_user(["paying_user"])
public["/items/public/"]
private["/items/private/"]
activate_user["/users/{user_id}/activate"]
pro_items["/items/pro/"]
current_user --> active_user
active_user --> admin_user
active_user --> paying_user
current_user --> public
active_user --> private
admin_user --> activate_user
paying_user --> pro_items
```
## Integrated with **OpenAPI**

View File

@@ -44,6 +44,17 @@ Then we can use the dependency with:
But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
```mermaid
graph TB
query_extractor(["query_extractor"])
query_or_cookie_extractor(["query_or_cookie_extractor"])
read_query["/items/"]
query_extractor --> query_or_cookie_extractor --> read_query
```
## Using the same dependency multiple times
If one of your dependencies is declared multiple times for the same *path operation*, for example, multiple dependencies have a common sub-dependency, **FastAPI** will know to call that sub-dependency only once per request.

View File

@@ -1,8 +1,8 @@
There are many situations in where you need to notify an error to the client that is using your API.
There are many situations in where you need to notify an error to a client that is using your API.
This client could be a browser with a frontend, the code from someone else, an IoT device, etc.
This client could be a browser with a frontend, a code from someone else, an IoT device, etc.
You could need to tell that client that:
You could need to tell the client that:
* The client doesn't have enough privileges for that operation.
* The client doesn't have access to that resource.

View File

@@ -469,6 +469,8 @@ Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in
This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request.
But you can't raise another exception from the exit code (after `yield`). See more in [Dependencies with `yield` and `HTTPException`](./dependencies/dependencies-with-yield.md#dependencies-with-yield-and-httpexception){.internal-link target=_blank}
And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy.
This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`:

View File

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

View File

@@ -1,4 +1,5 @@
from enum import Enum
from pathlib import PurePath
from types import GeneratorType
from typing import Any, Callable, Dict, List, Set, Tuple, Union
@@ -73,6 +74,8 @@ def jsonable_encoder(
)
if isinstance(obj, Enum):
return obj.value
if isinstance(obj, PurePath):
return str(obj)
if isinstance(obj, (str, int, float, type(None))):
return obj
if isinstance(obj, dict):

View File

@@ -335,9 +335,7 @@ class APIRoute(routing.Route):
self.include_in_schema = include_in_schema
self.response_class = response_class
assert inspect.isfunction(endpoint) or inspect.ismethod(
endpoint
), f"An endpoint must be a function or method"
assert callable(endpoint), f"An endpoint must be a callable"
self.dependant = get_dependant(path=self.path_format, call=self.endpoint)
for depends in self.dependencies[::-1]:
self.dependant.dependencies.insert(

View File

@@ -117,6 +117,11 @@ markdown_extensions:
- admonition
- codehilite
- extra
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_div_format
extra:
social:
@@ -137,4 +142,5 @@ extra_css:
- 'css/custom.css'
extra_javascript:
- 'https://unpkg.com/mermaid@8.4.6/dist/mermaid.min.js'
- 'js/custom.js'

View File

@@ -0,0 +1,24 @@
from functools import partial
from fastapi import FastAPI
from starlette.testclient import TestClient
def main(some_arg, q: str = None):
return {"some_arg": some_arg, "q": q}
endpoint = partial(main, "foo")
app = FastAPI()
app.get("/")(endpoint)
client = TestClient(app)
def test_partial():
response = client.get("/?q=bar")
data = response.json()
assert data == {"some_arg": "foo", "q": "bar"}

View File

@@ -1,9 +1,10 @@
from datetime import datetime, timezone
from enum import Enum
from pathlib import PurePath, PurePosixPath, PureWindowsPath
import pytest
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel, ValidationError
from pydantic import BaseModel, ValidationError, create_model
try:
from pydantic import Field
@@ -69,6 +70,19 @@ class ModelWithAlias(BaseModel):
foo: str = Field(..., alias="Foo")
@pytest.fixture(
name="model_with_path", params=[PurePath, PurePosixPath, PureWindowsPath]
)
def fixture_model_with_path(request):
class Config:
arbitrary_types_allowed = True
ModelWithPath = create_model(
"ModelWithPath", path=(request.param, ...), __config__=Config
)
return ModelWithPath(path=request.param("/foo", "bar"))
def test_encode_class():
person = Person(name="Foo")
pet = Pet(owner=person, name="Firulais")
@@ -120,3 +134,11 @@ def test_custom_encoders():
instance, custom_encoder={safe_datetime: lambda o: o.isoformat()}
)
assert encoded_instance["dt_field"] == instance.dt_field.isoformat()
def test_encode_model_with_path(model_with_path):
if isinstance(model_with_path.path, PureWindowsPath):
expected = "\\foo\\bar"
else:
expected = "/foo/bar"
assert jsonable_encoder(model_with_path) == {"path": expected}