committed by
GitHub
37 changed files with 2689 additions and 1076 deletions
File diff suppressed because it is too large
@ -0,0 +1,831 @@ |
|||
# 仮想環境 |
|||
|
|||
Pythonプロジェクトの作業では、**仮想環境**(または類似の仕組み)を使用し、プロジェクトごとにインストールするパッケージを分離するべきでしょう。 |
|||
|
|||
/// info | 情報 |
|||
|
|||
もし、仮想環境の概要や作成方法、使用方法について既にご存知なら、このセクションをスキップすることができます。🤓 |
|||
|
|||
/// |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
**仮想環境**は、**環境変数**とは異なります。 |
|||
|
|||
**環境変数**は、プログラムが使用できるシステム内の変数です。 |
|||
|
|||
**仮想環境**は、ファイルをまとめたディレクトリのことです。 |
|||
|
|||
/// |
|||
|
|||
/// info | 情報 |
|||
このページでは、**仮想環境**の使用方法と、そのはたらきについて説明します。 |
|||
|
|||
もし**すべてを管理するツール**(Pythonのインストールも含む)を導入する準備ができているなら、<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> をお試しください。 |
|||
|
|||
/// |
|||
|
|||
## プロジェクトの作成 |
|||
|
|||
まず、プロジェクト用のディレクトリを作成します。 |
|||
|
|||
私は通常 home/user ディレクトリの中に `code` というディレクトリを用意していて、プロジェクトごとに1つのディレクトリをその中に作成しています。 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Go to the home directory |
|||
$ cd |
|||
// Create a directory for all your code projects |
|||
$ mkdir code |
|||
// Enter into that code directory |
|||
$ cd code |
|||
// Create a directory for this project |
|||
$ mkdir awesome-project |
|||
// Enter into that project directory |
|||
$ cd awesome-project |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## 仮想環境の作成 |
|||
|
|||
Pythonプロジェクトでの**初めての**作業を開始する際には、**<abbr title="他の選択肢もありますが、これはシンプルなガイドラインです">プロジェクト内</abbr>**に仮想環境を作成してください。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
これを行うのは、**プロジェクトごとに1回だけ**です。作業のたびに行う必要はありません。 |
|||
|
|||
/// |
|||
|
|||
//// tab | `venv` |
|||
|
|||
仮想環境を作成するには、Pythonに付属している `venv` モジュールを使用できます。 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python -m venv .venv |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
/// details | このコマンドの意味 |
|||
|
|||
- `python` : `python` というプログラムを呼び出します |
|||
- `-m` : モジュールをスクリプトとして呼び出します。どのモジュールを呼び出すのか、この次に指定します |
|||
- `venv` : 通常Pythonに付随してインストールされる `venv`モジュールを使用します |
|||
- `.venv` : 仮想環境を`.venv`という新しいディレクトリに作成します |
|||
|
|||
/// |
|||
|
|||
//// |
|||
|
|||
//// tab | `uv` |
|||
|
|||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> をインストール済みなら、仮想環境を作成するために `uv` を使うこともできます。 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uv venv |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
デフォルトでは、 `uv` は `.venv` というディレクトリに仮想環境を作成します。 |
|||
|
|||
ただし、追加の引数にディレクトリ名を与えてカスタマイズすることもできます。 |
|||
|
|||
/// |
|||
|
|||
//// |
|||
|
|||
このコマンドは `.venv` というディレクトリに新しい仮想環境を作成します。 |
|||
|
|||
/// details | `.venv` またはその他の名前 |
|||
|
|||
仮想環境を別のディレクトリに作成することも可能ですが、 `.venv` と名付けるのが一般的な慣習です。 |
|||
|
|||
/// |
|||
|
|||
## 仮想環境の有効化 |
|||
|
|||
実行されるPythonコマンドやインストールされるパッケージが新しく作成した仮想環境を使用するよう、その仮想環境を有効化しましょう。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
そのプロジェクトの作業で**新しいターミナルセッション**を開始する際には、**毎回**有効化が必要です。 |
|||
|
|||
/// |
|||
|
|||
//// tab | Linux, macOS |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ source .venv/bin/activate |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows PowerShell |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ .venv\Scripts\Activate.ps1 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows Bash |
|||
|
|||
もしWindowsでBashを使用している場合 (<a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>など): |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ source .venv/Scripts/activate |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
**新しいパッケージ**を仮想環境にインストールするときには、再度**有効化**してください。 |
|||
|
|||
こうすることで、そのパッケージがインストールした**ターミナル(<abbr title="command line interface">CLI</abbr>)プログラム**を使用する場合に、仮想環境内のものが確実に使われ、グローバル環境にインストールされている別のもの(おそらく必要なものとは異なるバージョン)を誤って使用することを防ぎます。 |
|||
|
|||
/// |
|||
|
|||
## 仮想環境が有効であることを確認する |
|||
|
|||
仮想環境が有効である(前のコマンドが正常に機能した)ことを確認します。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
これは**任意**ですが、すべてが期待通りに機能し、意図した仮想環境を使用していることを**確認する**良い方法です。 |
|||
|
|||
/// |
|||
|
|||
//// tab | Linux, macOS, Windows Bash |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ which python |
|||
|
|||
/home/user/code/awesome-project/.venv/bin/python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
`.venv/bin/python` にある `python` バイナリが、プロジェクト(この場合は `awesome-project` )内に表示されていれば、正常に動作しています 🎉。 |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows PowerShell |
|||
|
|||
<div class="termy"> |
|||
|
|||
``` console |
|||
$ Get-Command python |
|||
|
|||
C:\Users\user\code\awesome-project\.venv\Scripts\python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
`.venv\Scripts\python` にある `python` バイナリが、プロジェクト(この場合は `awesome-project` )内に表示されていれば、正常に動作しています 🎉。 |
|||
|
|||
//// |
|||
|
|||
## `pip` をアップグレードする |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> を使用している場合は、 `pip` の代わりに `uv` を使ってインストールを行うため、 `pip` をアップグレードする必要はありません 😎。 |
|||
|
|||
/// |
|||
|
|||
もしパッケージのインストールに `pip`(Pythonに標準で付属しています)を使用しているなら、 `pip` を最新バージョンに**アップグレード**しましょう。 |
|||
|
|||
パッケージのインストール中に発生する想定外のエラーの多くは、最初に `pip` をアップグレードしておくだけで解決されます。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
通常、これは仮想環境を作成した直後に**一度だけ**実行します。 |
|||
|
|||
/// |
|||
|
|||
仮想環境が有効であることを(上で説明したコマンドで)確認し、アップグレードを実行しましょう: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python -m pip install --upgrade pip |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## `.gitignore` を追加する |
|||
|
|||
**Git**を使用している場合(使用するべきでしょう)、 `.gitignore` ファイルを追加して、 `.venv` 内のあらゆるファイルをGitの管理対象から除外します。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> を使用して仮想環境を作成した場合、すでにこの作業は済んでいるので、この手順をスキップできます 😎。 |
|||
|
|||
/// |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
これも、仮想環境を作成した直後に**一度だけ**実行します。 |
|||
|
|||
/// |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ echo "*" > .venv/.gitignore |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
/// details | このコマンドの意味 |
|||
|
|||
- `echo "*"` : ターミナルに `*` というテキストを「表示」しようとします。(次の部分によってその動作が少し変わります) |
|||
- `>` : `>` の左側のコマンドがターミナルに表示しようとする内容を、ターミナルには表示せず、 `>` の右側のファイルに書き込みます。 |
|||
- `.gitignore` : `*` を書き込むファイル名。 |
|||
|
|||
ここで、Gitにおける `*` は「すべて」を意味するので、このコマンドによって `.venv` ディレクトリ内のすべてがGitに無視されるようになります。 |
|||
|
|||
このコマンドは以下のテキストを持つ `.gitignore` ファイルを作成します: |
|||
|
|||
```gitignore |
|||
* |
|||
``` |
|||
|
|||
/// |
|||
|
|||
## パッケージのインストール |
|||
|
|||
仮想環境を有効化した後、その中でパッケージをインストールできます。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
プロジェクトに必要なパッケージをインストールまたはアップグレードする場合、これを**一度**実行します。 |
|||
|
|||
もし新しいパッケージを追加したり、バージョンをアップグレードする必要がある場合は、もう**一度この手順を繰り返し**ます。 |
|||
|
|||
/// |
|||
|
|||
### パッケージを直接インストールする |
|||
|
|||
急いでいて、プロジェクトのパッケージ要件を宣言するファイルを使いたくない場合、パッケージを直接インストールできます。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
プログラムが必要とするパッケージとバージョンをファイル(例えば `requirements.txt` や `pyproject.toml` )に記載しておくのは、(とても)良い考えです。 |
|||
|
|||
/// |
|||
|
|||
//// tab | `pip` |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install "fastapi[standard]" |
|||
|
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | `uv` |
|||
|
|||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> を使用できるなら: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uv pip install "fastapi[standard]" |
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
### `requirements.txt` からインストールする |
|||
|
|||
もし `requirements.txt` があるなら、パッケージのインストールに使用できます。 |
|||
|
|||
//// tab | `pip` |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install -r requirements.txt |
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | `uv` |
|||
|
|||
もし <a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">`uv`</a> を使用できるなら: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ uv pip install -r requirements.txt |
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
/// details | `requirements.txt` |
|||
|
|||
パッケージが記載された `requirements.txt` は以下のようになっています: |
|||
|
|||
```requirements.txt |
|||
fastapi[standard]==0.113.0 |
|||
pydantic==2.8.0 |
|||
``` |
|||
|
|||
/// |
|||
|
|||
## プログラムを実行する |
|||
|
|||
仮想環境を有効化した後、プログラムを実行できます。この際、仮想環境内のPythonと、そこにインストールしたパッケージが使用されます。 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ python main.py |
|||
|
|||
Hello World |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## エディタの設定 |
|||
|
|||
プロジェクトではおそらくエディタを使用するでしょう。コード補完やインラインエラーの表示ができるように、作成した仮想環境をエディタでも使えるよう設定してください。(多くの場合、自動検出されます) |
|||
|
|||
設定例: |
|||
|
|||
* <a href="https://code.visualstudio.com/docs/python/environments#_select-and-activate-an-environment" class="external-link" target="_blank">VS Code</a> |
|||
* <a href="https://www.jetbrains.com/help/pycharm/creating-virtual-environment.html" class="external-link" target="_blank">PyCharm</a> |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
この設定は通常、仮想環境を作成した際に**一度だけ**行います。 |
|||
|
|||
/// |
|||
|
|||
## 仮想環境の無効化 |
|||
|
|||
プロジェクトの作業が終了したら、その仮想環境を**無効化**できます。 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ deactivate |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
これにより、 `python` コマンドを実行しても、そのプロジェクト用(のパッケージがインストールされた)仮想環境から `python` プログラムを呼び出そうとはしなくなります。 |
|||
|
|||
## 作業準備完了 |
|||
|
|||
ここまでで、プロジェクトの作業を始める準備が整いました。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
上記の内容を理解したいですか? |
|||
|
|||
もしそうなら、以下を読み進めてください。👇🤓 |
|||
|
|||
/// |
|||
|
|||
## なぜ仮想環境? |
|||
|
|||
FastAPIを使った作業をするには、 [Python](https://www.python.org/) のインストールが必要です。 |
|||
|
|||
それから、FastAPIや、使用したいその他の**パッケージ**を**インストール**する必要があります。 |
|||
|
|||
パッケージをインストールするには、通常、Python に付属する `pip` コマンド (または同様の代替コマンド) を使用します。 |
|||
|
|||
ただし、`pip` を直接使用すると、パッケージは**グローバルなPython環境**(OS全体にインストールされたPython環境)にインストールされます。 |
|||
|
|||
### 問題点 |
|||
|
|||
では、グローバルPython環境にパッケージをインストールすることの問題点は何でしょうか? |
|||
|
|||
ある時点で、あなたは**異なるパッケージ**に依存する多くのプログラムを書くことになるでしょう。そして、これらの中には同じパッケージの**異なるバージョン**に依存するものも出てくるでしょう。😱 |
|||
|
|||
例えば、 `philosophers-stone` (賢者の石)というプロジェクトを作成するとします。このプログラムは **`harry` (ハリー)というパッケージのバージョン `1`**に依存しています。そのため、 `harry` (ハリー)をインストールする必要があります。 |
|||
|
|||
```mermaid |
|||
flowchart LR |
|||
stone(philosophers-stone) -->|requires| harry-1[harry v1] |
|||
``` |
|||
|
|||
それから、 `prisoner-of-azkaban` (アズカバンの囚人)という別のプロジェクトを作成したとします。このプロジェクトも `harry` (ハリー)に依存していますが、**`harry` (ハリー)のバージョン `3`**が必要です。 |
|||
|
|||
```mermaid |
|||
flowchart LR |
|||
azkaban(prisoner-of-azkaban) --> |requires| harry-3[harry v3] |
|||
``` |
|||
|
|||
しかし、ここで問題になるのは、もしローカルの**仮想環境**ではなくグローバル(環境)にパッケージをインストールするなら、 `harry` (ハリー)のどのバージョンをインストールするか選ばないといけないことです。 |
|||
|
|||
例えば、 `philosophers-stone` (賢者の石)を実行するには、まず `harry` (ハリー)のバージョン `1` をインストールする必要があります: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install "harry==1" |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
これにより、`harry` (ハリー)バージョン1がグローバルなPython環境にインストールされます。 |
|||
|
|||
```mermaid |
|||
flowchart LR |
|||
subgraph global[global env] |
|||
harry-1[harry v1] |
|||
end |
|||
subgraph stone-project[philosophers-stone project] |
|||
stone(philosophers-stone) -->|requires| harry-1 |
|||
end |
|||
``` |
|||
|
|||
しかし、 `prisoner-of-azkaban` (アズカバンの囚人)を実行したい場合は、`harry` (ハリー)のバージョン `1` をアンインストールし、`harry` (ハリー)のバージョン `3` をインストールし直す必要があります。(あるいは、単に`harry` (ハリー)のバージョン `3` をインストールすることで、自動的にバージョン `1` がアンインストールされます) |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ pip install "harry==3" |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
このようにして、グローバル環境への `harry` (ハリー)のバージョン `3` のインストールが完了します。 |
|||
|
|||
それから、 `philosophers-stone` (賢者の石)を再び実行しようとすると、このプログラムは `harry` (ハリー)のバージョン `1` が必要なため、**動作しなくなる**可能性があります。 |
|||
|
|||
```mermaid |
|||
flowchart LR |
|||
subgraph global[global env] |
|||
harry-1[<strike>harry v1</strike>] |
|||
style harry-1 fill:#ccc,stroke-dasharray: 5 5 |
|||
harry-3[harry v3] |
|||
end |
|||
subgraph stone-project[philosophers-stone project] |
|||
stone(philosophers-stone) -.-x|⛔️| harry-1 |
|||
end |
|||
subgraph azkaban-project[prisoner-of-azkaban project] |
|||
azkaban(prisoner-of-azkaban) --> |requires| harry-3 |
|||
end |
|||
``` |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
Pythonのパッケージでは、**新しいバージョン**で**互換性を損なう変更を避ける**よう努めるのが一般的ですが、それでも注意が必要です。すべてが正常に動作することをテストで確認してから、意図的に指定して新しいバージョンをインストールするのが良いでしょう。 |
|||
|
|||
/// |
|||
|
|||
あなたのすべての**プロジェクトが依存している**、**多数の**他の**パッケージ**が上記の問題を抱えていると想像してください。これは管理が非常に困難です。そして、**互換性のないバージョン**のパッケージを使ってプロジェクトを実行し、なぜ動作しないのか分からなくなるでしょう。 |
|||
|
|||
また、使用しているOS(Linux、Windows、macOS など)によっては、Pythonがすでにインストールされていることがあります。この場合、特定のバージョンのパッケージが**OSの動作に必要である**ことがあります。グローバル環境にパッケージをインストールすると、OSに付属するプログラムを**壊してしまう**可能性があります。 |
|||
|
|||
## パッケージのインストール先 |
|||
|
|||
Pythonをインストールしたとき、ファイルを含んだいくつかのディレクトリが作成されます。 |
|||
|
|||
これらの中には、インストールされたパッケージを保存するためのものもあります。 |
|||
|
|||
以下のコマンドを実行したとき: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
// Don't run this now, it's just an example 🤓 |
|||
$ pip install "fastapi[standard]" |
|||
---> 100% |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
FastAPIのコードを含む圧縮ファイルが、通常は [PyPI](https://pypi.org/project/fastapi/) からダウンロードされます。 |
|||
|
|||
また、FastAPIが依存する他のパッケージも**ダウンロード**されます。 |
|||
|
|||
それから、これらのファイルは**解凍**され、コンピュータのあるディレクトリに配置されます。 |
|||
|
|||
デフォルトでは、これらのファイルはPythonのインストール時に作成されるディレクトリ、つまり**グローバル環境**に配置されます。 |
|||
|
|||
## 仮想環境とは |
|||
|
|||
すべてのパッケージをグローバル環境に配置することによって生じる問題の解決策は、作業する**プロジェクトごとの仮想環境**を使用することです。 |
|||
|
|||
仮想環境は**ディレクトリ**であり、グローバル環境と非常に似ていて、一つのプロジェクトで使う特定のパッケージ群をインストールできる場所です。 |
|||
|
|||
このようにして、それぞれのプロジェクトが独自の仮想環境(`.venv` ディレクトリ)に独自のパッケージ群を持つことができます。 |
|||
|
|||
```mermaid |
|||
flowchart TB |
|||
subgraph stone-project[philosophers-stone project] |
|||
stone(philosophers-stone) --->|requires| harry-1 |
|||
subgraph venv1[.venv] |
|||
harry-1[harry v1] |
|||
end |
|||
end |
|||
subgraph azkaban-project[prisoner-of-azkaban project] |
|||
azkaban(prisoner-of-azkaban) --->|requires| harry-3 |
|||
subgraph venv2[.venv] |
|||
harry-3[harry v3] |
|||
end |
|||
end |
|||
stone-project ~~~ azkaban-project |
|||
``` |
|||
|
|||
## 仮想環境の有効化とは |
|||
|
|||
仮想環境を有効にしたとき、例えば次のコマンドを実行した場合を考えます: |
|||
|
|||
//// tab | Linux, macOS |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ source .venv/bin/activate |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows PowerShell |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ .venv\Scripts\Activate.ps1 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows Bash |
|||
|
|||
あるいは、WindowsでBashを使用している場合 (<a href="https://gitforwindows.org/" class="external-link" target="_blank">Git Bash</a>など): |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ source .venv/Scripts/activate |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
これによって、いくつかの [環境変数](environment-variables.md){.internal-link target=_blank} が作成・修正され、次に実行されるコマンドで使用できるようになります。 |
|||
|
|||
これらの環境変数のひとつに、 `PATH` 変数があります。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
`PATH` 変数についての詳細は [環境変数](environment-variables.md#path環境変数){.internal-link target=_blank} を参照してください。 |
|||
|
|||
/// |
|||
|
|||
仮想環境を有効にすると、その仮想環境のパス `.venv/bin` (LinuxとmacOS)、あるいは `.venv\Scripts` (Windows)が `PATH` 変数に追加されます。 |
|||
|
|||
その環境を有効にする前の `PATH` 変数が次のようになっているとします。 |
|||
|
|||
//// tab | Linux, macOS |
|||
|
|||
```plaintext |
|||
/usr/bin:/bin:/usr/sbin:/sbin |
|||
``` |
|||
|
|||
これは、OSが以下のディレクトリ中でプログラムを探すことを意味します: |
|||
|
|||
* `/usr/bin` |
|||
* `/bin` |
|||
* `/usr/sbin` |
|||
* `/sbin` |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows |
|||
|
|||
```plaintext |
|||
C:\Windows\System32 |
|||
``` |
|||
|
|||
これは、OSが以下のディレクトリ中でプログラムを探すことを意味します: |
|||
|
|||
* `C:\Windows\System32` |
|||
|
|||
//// |
|||
|
|||
仮想環境を有効にすると、 `PATH` 変数は次のようになります。 |
|||
|
|||
//// tab | Linux, macOS |
|||
|
|||
```plaintext |
|||
/home/user/code/awesome-project/.venv/bin:/usr/bin:/bin:/usr/sbin:/sbin |
|||
``` |
|||
|
|||
これは、OSが他のディレクトリを探すより前に、最初に以下のディレクトリ中でプログラムを探し始めることを意味します: |
|||
|
|||
```plaintext |
|||
/home/user/code/awesome-project/.venv/bin |
|||
``` |
|||
|
|||
そのため、ターミナルで `python` と入力した際に、OSはPythonプログラムを以下のパスで発見し、使用します。 |
|||
|
|||
```plaintext |
|||
/home/user/code/awesome-project/.venv/bin/python |
|||
``` |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows |
|||
|
|||
```plaintext |
|||
C:\Users\user\code\awesome-project\.venv\Scripts;C:\Windows\System32 |
|||
``` |
|||
|
|||
これは、OSが他のディレクトリを探すより前に、最初に以下のディレクトリ中でプログラムを探し始めることを意味します: |
|||
|
|||
```plaintext |
|||
C:\Users\user\code\awesome-project\.venv\Scripts |
|||
``` |
|||
|
|||
そのため、ターミナルで `python` と入力した際に、OSはPythonプログラムを以下のパスで発見し、使用します。 |
|||
|
|||
```plaintext |
|||
C:\Users\user\code\awesome-project\.venv\Scripts\python |
|||
``` |
|||
|
|||
//// |
|||
|
|||
重要な点は、仮想環境のパスを `PATH` 変数の**先頭**に配置することです。OSは利用可能な他のPythonを見つけるより**前に**、この仮想環境のPythonを見つけるようになります。このようにして、 `python` を実行したときに、他の `python` (例えばグローバル環境の `python` )ではなく、**その仮想環境の**Pythonを使用するようになります。 |
|||
|
|||
仮想環境を有効にして変更されることは他にもありますが、これが最も重要な変更のひとつです。 |
|||
|
|||
## 仮想環境の確認 |
|||
|
|||
仮想環境が有効かどうか、例えば次のように確認できます。: |
|||
|
|||
//// tab | Linux, macOS, Windows Bash |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ which python |
|||
|
|||
/home/user/code/awesome-project/.venv/bin/python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
//// tab | Windows PowerShell |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ Get-Command python |
|||
|
|||
C:\Users\user\code\awesome-project\.venv\Scripts\python |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
//// |
|||
|
|||
これは、使用される `python` プログラムが**その仮想環境の**ものであることを意味します。 |
|||
|
|||
LinuxやmacOSでは `which` を、Windows PowerShellでは `Get-Command` を使用します。 |
|||
|
|||
このコマンドの動作は、 `PATH`変数に設定された**それぞれのパスを順に**確認していき、呼ばれている `python` プログラムを探します。そして、見つかり次第そのプログラムへの**パスを表示します**。 |
|||
|
|||
最も重要なことは、 `python` が呼ばれたときに、まさにこのコマンドで確認した "`python`" が実行されることです。 |
|||
|
|||
こうして、自分が想定通りの仮想環境にいるかを確認できます。 |
|||
|
|||
/// tip | 豆知識 |
|||
|
|||
ある仮想環境を有効にし、そのPythonを使用したまま**他のプロジェクトに移動して**しまうことは簡単に起こり得ます。 |
|||
|
|||
そして、その第二のプロジェクトは動作しないでしょう。なぜなら別のプロジェクトの仮想環境の**誤ったPython**を使用しているからです。 |
|||
|
|||
そのため、どの `python` が使用されているのか確認できることは役立ちます。🤓 |
|||
|
|||
/// |
|||
|
|||
## なぜ仮想環境を無効化するのか |
|||
|
|||
例えば、`philosophers-stone` (賢者の石)というプロジェクトで作業をしていて、**その仮想環境を有効にし**、必要なパッケージをインストールしてその環境内で作業を進めているとします。 |
|||
|
|||
それから、**別のプロジェクト**、 `prisoner-of-azkaban` (アズカバンの囚人)に取り掛かろうとします。 |
|||
|
|||
そのプロジェクトディレクトリへ移動します: |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ cd ~/code/prisoner-of-azkaban |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
もし `philosophers-stone` (賢者の石)の仮想環境を無効化していないと、`python` を実行したとき、 ターミナルは `philosophers-stone` (賢者の石)のPythonを使用しようとします。 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ cd ~/code/prisoner-of-azkaban |
|||
|
|||
$ python main.py |
|||
|
|||
// Error importing sirius, it's not installed 😱 |
|||
Traceback (most recent call last): |
|||
File "main.py", line 1, in <module> |
|||
import sirius |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
しかし、その仮想環境を無効化し、 `prisoner-of-azkaban` (アズカバンの囚人)のための新しい仮想環境を有効にすれば、 `python` を実行したときに `prisoner-of-azkaban` (アズカバンの囚人)の仮想環境の Python が使用されるようになります。 |
|||
|
|||
<div class="termy"> |
|||
|
|||
```console |
|||
$ cd ~/code/prisoner-of-azkaban |
|||
|
|||
// You don't need to be in the old directory to deactivate, you can do it wherever you are, even after going to the other project 😎 |
|||
$ deactivate |
|||
|
|||
// Activate the virtual environment in prisoner-of-azkaban/.venv 🚀 |
|||
$ source .venv/bin/activate |
|||
|
|||
// Now when you run python, it will find the package sirius installed in this virtual environment ✨ |
|||
$ python main.py |
|||
|
|||
I solemnly swear 🐺 |
|||
``` |
|||
|
|||
</div> |
|||
|
|||
## 代替手段 |
|||
|
|||
これは、あらゆる仕組みを**根本から**学ぶためのシンプルな入門ガイドです。 |
|||
|
|||
仮想環境、パッケージの依存関係(requirements)、プロジェクトの管理には、多くの**代替手段**があります。 |
|||
|
|||
準備が整い、パッケージの依存関係、仮想環境など**プロジェクト全体の管理**ツールを使いたいと考えたら、<a href="https://github.com/astral-sh/uv" class="external-link" target="_blank">uv</a> を試してみることをおすすめします。 |
|||
|
|||
`uv` では以下のような多くのことができます: |
|||
|
|||
* 異なるバージョンも含めた**Python のインストール** |
|||
* プロジェクトごとの**仮想環境**の管理 |
|||
* **パッケージ**のインストール |
|||
* プロジェクトのパッケージの**依存関係やバージョン**の管理 |
|||
* パッケージとそのバージョンの、依存関係を含めた**厳密な**組み合わせを保持し、これによって、本番環境で、開発環境と全く同じようにプロジェクトを実行できる(これは**locking**と呼ばれます) |
|||
* その他のさまざまな機能 |
|||
|
|||
## まとめ |
|||
|
|||
ここまで読みすべて理解したなら、世間の多くの開発者と比べて、仮想環境について**あなたはより多くのことを知っています**。🤓 |
|||
|
|||
これらの詳細を知ることは、将来、複雑に見える何かのデバッグにきっと役立つでしょう。しかし、その頃には、あなたは**そのすべての動作を根本から**理解しているでしょう。😎 |
@ -0,0 +1,72 @@ |
|||
# Модели Header-параметров |
|||
|
|||
Если у вас есть группа связанных **header-параметров**, то вы можете объединить их в одну **Pydantic-модель**. |
|||
|
|||
Это позволит вам **переиспользовать модель** в **разных местах**, а также задать валидацию и метаданные сразу для всех параметров. 😎 |
|||
|
|||
/// note | Заметка |
|||
|
|||
Этот функционал доступен в FastAPI начиная с версии `0.115.0`. 🤓 |
|||
|
|||
/// |
|||
|
|||
## Header-параметры в виде Pydantic-модели |
|||
|
|||
Объявите нужные **header-параметры** в **Pydantic-модели** и затем аннотируйте параметр как `Header`: |
|||
|
|||
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *} |
|||
|
|||
**FastAPI** **извлечёт** данные для **каждого поля** из **заголовков** запроса и выдаст заданную вами Pydantic-модель. |
|||
|
|||
## Проверьте документацию |
|||
|
|||
Вы можете посмотреть нужные header-параметры в графическом интерфейсе сгенерированной документации по пути `/docs`: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/header-param-models/image01.png"> |
|||
</div> |
|||
|
|||
## Как запретить дополнительные заголовки |
|||
|
|||
В некоторых случаях (не особо часто встречающихся) вам может понадобиться **ограничить** заголовки, которые вы хотите получать. |
|||
|
|||
Вы можете использовать возможности конфигурации Pydantic-модели для того, чтобы запретить (`forbid`) любые дополнительные (`extra`) поля: |
|||
|
|||
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *} |
|||
|
|||
Если клиент попробует отправить **дополнительные заголовки**, то в ответ он получит **ошибку**. |
|||
|
|||
Например, если клиент попытается отправить заголовок `tool` со значением `plumbus`, то в ответ он получит ошибку, сообщающую ему, что header-параметр `tool` не разрешен: |
|||
|
|||
```json |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"type": "extra_forbidden", |
|||
"loc": ["header", "tool"], |
|||
"msg": "Extra inputs are not permitted", |
|||
"input": "plumbus", |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
## Как отключить автоматическое преобразование подчеркиваний |
|||
|
|||
Как и в случае с обычными заголовками, если у вас в именах параметров имеются символы подчеркивания, они **автоматически преобразовываются в дефис**. |
|||
|
|||
Например, если в коде есть header-параметр `save_data`, то ожидаемый HTTP-заголовок будет `save-data` и именно так он будет отображаться в документации. |
|||
|
|||
Если по каким-то причинам вам нужно отключить данное автоматическое преобразование, это можно сделать и для Pydantic-моделей для header-параметров. |
|||
|
|||
{* ../../docs_src/header_param_models/tutorial003_an_py310.py hl[19] *} |
|||
|
|||
/// warning | Внимание |
|||
|
|||
Перед тем как устанавливать для параметра `convert_underscores` значение `False`, имейте в виду, что некоторые HTTP-прокси и серверы не разрешают использовать заголовки с символами подчеркивания. |
|||
|
|||
/// |
|||
|
|||
## Резюме |
|||
|
|||
Вы можете использовать **Pydantic-модели** для объявления **header-параметров** в **FastAPI**. 😎 |
@ -0,0 +1,76 @@ |
|||
# Моделі для Cookie-параметрів |
|||
|
|||
Якщо у Вас є група **cookies** параметрів, які пов'язані між собою, Ви можете створити **Pydantic-модель**, щоб оголосити їх. 🍪 |
|||
|
|||
Це дозволить Вам повторно **використовувати модель** у **різних місцях**, а також оголосити валідацію та метадані для всіх параметрів одночасно. 😎 |
|||
|
|||
/// note | Нотатки |
|||
|
|||
Це підтримується з версії FastAPI `0.115.0`. 🤓 |
|||
|
|||
/// |
|||
|
|||
/// tip | Порада |
|||
|
|||
Ця ж техніка застосовується до `Query`, `Cookie`, та `Header`. 😎 |
|||
|
|||
/// |
|||
|
|||
## Cookie з Pydantic-моделлю |
|||
|
|||
Оголосіть **cookie-параметри**, які Вам потрібні, у **Pydantic-моделі**, а потім оголосіть параметр як `Cookie`: |
|||
|
|||
{* ../../docs_src/cookie_param_models/tutorial001_an_py310.py hl[9:12,16] *} |
|||
|
|||
**FastAPI** буде **витягувати** дані для **кожного поля** з **cookie** параметрів, отриманих у запиті, і передавати Вам Pydantic-модель, яку Ви визначили. |
|||
|
|||
## Перевірка у документації |
|||
|
|||
Ви можете побачити визначені cookie в інтерфейсі документації за адресою `/docs`: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/cookie-param-models/image01.png"> |
|||
</div> |
|||
|
|||
/// info | Інформація |
|||
|
|||
Майте на увазі, що оскільки **браузери обробляють cookie** особливим чином і "за лаштунками", вони **не** дозволяють **JavaScript** легко з ними працювати. |
|||
|
|||
Якщо Ви зайдете до **інтерфейсу документації API** за адресою `/docs`, Ви зможете побачити **документацію** для cookie у Ваших **операціях шляху**. |
|||
|
|||
Але навіть якщо Ви заповните дані й натиснете "Execute", оскільки інтерфейс документації працює з **JavaScript**, cookie не будуть відправлені, і Ви побачите **помилку**, ніби Ви не ввели жодних значень. |
|||
|
|||
/// |
|||
|
|||
## Заборона додаткових cookie |
|||
|
|||
У деяких спеціальних випадках (ймовірно, не дуже поширених) Ви можете захотіти **обмежити** список cookie, які хочете отримувати. |
|||
|
|||
Ваша API тепер має можливість контролювати власну <abbr title="Це жарт, якщо що. Це не має нічого спільного зі згодою на використання cookie, але це кумедно, що навіть API тепер може відхиляти бідні cookie. Ловіть печиво. 🍪">згоду на cookie</abbr>. 🤪🍪 |
|||
|
|||
Ви можете використовувати налаштування моделі Pydantic, щоб `заборонити` будь-які `додаткові` поля: |
|||
|
|||
{* ../../docs_src/cookie_param_models/tutorial002_an_py39.py hl[10] *} |
|||
|
|||
Якщо клієнт спробує надіслати якісь **додаткові cookie**, він отримає відповідь з **помилкою**. |
|||
|
|||
Бідні банери cookie, які так старанно намагаються отримати Вашу згоду, щоб <abbr title="Це ще один жарт. Не звертайте уваги. Візьміть каву для свого печива. ☕">API її відхилила</abbr>. 🍪 |
|||
|
|||
Наприклад, якщо клієнт спробує надіслати cookie `santa_tracker` зі значенням `good-list-please`, він отримає відповідь з помилкою, яка повідомить, що <abbr title="Санта не схвалює відсутність cookie. 🎅 Гаразд, більше жартів не буде.">cookie `santa_tracker` не дозволено</abbr>: |
|||
|
|||
```json |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"type": "extra_forbidden", |
|||
"loc": ["cookie", "santa_tracker"], |
|||
"msg": "Extra inputs are not permitted", |
|||
"input": "good-list-please", |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
## Підсумок |
|||
|
|||
Ви можете використовувати **Pydantic-моделі** для оголошення <abbr title="Отримайте останнє печиво перед тим, як піти. 🍪">cookie</abbr> у FastAPI. 😎 |
@ -0,0 +1,58 @@ |
|||
# Моделі Параметрів Заголовків |
|||
|
|||
Якщо у Вас є група пов’язаних параметрів заголовків, Ви можете створити **Pydantic модель** для їх оголошення. |
|||
|
|||
Це дозволить Вам повторно **використовувати модель** в **різних місцях**, а також оголосити валідації та метадані для всіх параметрів одночасно. 😎 |
|||
|
|||
/// note | Нотатки |
|||
|
|||
Ця можливість підтримується починаючи з версії FastAPI `0.115.0`. 🤓 |
|||
|
|||
/// |
|||
|
|||
## Параметри Заголовків з Використанням Pydantic Model |
|||
|
|||
Оголосіть потрібні **параметри заголовків** у **Pydantic моделі**, а потім оголосіть параметр як `Header`: |
|||
|
|||
{* ../../docs_src/header_param_models/tutorial001_an_py310.py hl[9:14,18] *} |
|||
|
|||
FastAPI буде витягувати дані для кожного поля з заголовків у запиті та передавати їх у створену Вами Pydantic модель. |
|||
|
|||
**FastAPI** буде **витягувати** дані для **кожного поля** з **заголовків** у запиті та передавати їх у створену Вами Pydantic модель. |
|||
|
|||
## Перевірка в Документації |
|||
|
|||
Ви можете побачити необхідні заголовки в інтерфейсі документації за адресою `/docs`: |
|||
|
|||
<div class="screenshot"> |
|||
<img src="/img/tutorial/header-param-models/image01.png"> |
|||
</div> |
|||
|
|||
## Заборона Додаткових Заголовків |
|||
|
|||
У деяких особливих випадках (ймовірно, не дуже поширених) Ви можете захотіти **обмежити** заголовки, які хочете отримати. |
|||
|
|||
Ви можете використати конфігурацію моделі Pydantic, щоб `заборонити` будь-які `додаткові` поля: |
|||
|
|||
{* ../../docs_src/header_param_models/tutorial002_an_py310.py hl[10] *} |
|||
|
|||
Якщо клієнт спробує надіслати **додаткові заголовки**, він отримає **помилку** у відповіді. |
|||
|
|||
Наприклад, якщо клієнт спробує надіслати заголовок `tool` зі значенням `plumbus`, він отримає **помилку** з повідомленням про те, що параметр заголовка `tool` не дозволений: |
|||
|
|||
```json |
|||
{ |
|||
"detail": [ |
|||
{ |
|||
"type": "extra_forbidden", |
|||
"loc": ["header", "tool"], |
|||
"msg": "Extra inputs are not permitted", |
|||
"input": "plumbus", |
|||
} |
|||
] |
|||
} |
|||
``` |
|||
|
|||
## Підсумок |
|||
|
|||
Ви можете використовувати **Pydantic моделі** для оголошення **заголовків** у **FastAPI**. 😎 |
@ -0,0 +1,120 @@ |
|||
# Метадані та URL-адреси документації |
|||
|
|||
Ви можете налаштувати кілька конфігурацій метаданих у Вашому додатку **FastAPI**. |
|||
|
|||
## Метадані для API |
|||
|
|||
Ви можете встановити такі поля, які використовуються в специфікації OpenAPI та в автоматично згенерованих інтерфейсах документації API: |
|||
|
|||
| Параметр | Тип | Опис | |
|||
|------------|------|-------------| |
|||
| `title` | `str` | Назва API. | |
|||
| `summary` | `str` | Короткий опис API. <small>Доступно з OpenAPI 3.1.0, FastAPI 0.99.0.</small> | |
|||
| `description` | `str` | Більш детальний опис API. Може використовувати Markdown. | |
|||
| `version` | `string` | Версія API. Це версія Вашого додатка, а не OpenAPI. Наприклад, `2.5.0`. | |
|||
| `terms_of_service` | `str` | URL до умов використання API. Якщо вказано, має бути у форматі URL. | |
|||
| `contact` | `dict` | Інформація для контакту з API. Може містити кілька полів. <details><summary><code>contact</code> поля</summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Опис</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td>Ім'я контактної особи або організації.</td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL з інформацією для контакту. Повинен бути у форматі URL.</td></tr><tr><td><code>email</code></td><td><code>str</code></td><td>Email контактної особи або організації. Повинен бути у форматі електронної пошти.</td></tr></tbody></table></details> | |
|||
| `license_info` | `dict` | Інформація про ліцензію для API. Може містити кілька полів. <details><summary><code>license_info</code> поля</summary><table><thead><tr><th>Параметр</th><th>Тип</th><th>Опис</th></tr></thead><tbody><tr><td><code>name</code></td><td><code>str</code></td><td><strong>ОБОВ'ЯЗКОВО</strong> (якщо встановлено <code>license_info</code>). Назва ліцензії для API.</td></tr><tr><td><code>identifier</code></td><td><code>str</code></td><td>Ліцензійний вираз за <a href="https://spdx.org/licenses/" class="external-link" target="_blank">SPDX</a> для API. Поле <code>identifier</code> взаємовиключне з полем <code>url</code>. <small>Доступно з OpenAPI 3.1.0, FastAPI 0.99.0.</small></td></tr><tr><td><code>url</code></td><td><code>str</code></td><td>URL до ліцензії, яка використовується для API. Повинен бути у форматі URL.</td></tr></tbody></table></details> | |
|||
|
|||
Ви можете налаштувати їх наступним чином: |
|||
|
|||
{* ../../docs_src/metadata/tutorial001.py hl[3:16, 19:32] *} |
|||
|
|||
/// tip | Підказка |
|||
|
|||
У полі `description` можна використовувати Markdown, і він буде відображатися у результаті. |
|||
|
|||
/// |
|||
|
|||
З цією конфігурацією автоматична документація API виглядатиме так: |
|||
|
|||
<img src="/img/tutorial/metadata/image01.png"> |
|||
|
|||
## Ідентифікатор ліцензії |
|||
|
|||
З початку використання OpenAPI 3.1.0 та FastAPI 0.99.0 Ви також можете налаштувати `license_info` за допомогою `identifier` замість `url`. |
|||
|
|||
Наприклад: |
|||
|
|||
{* ../../docs_src/metadata/tutorial001_1.py hl[31] *} |
|||
|
|||
## Метадані для тегів |
|||
|
|||
Ви також можете додати додаткові метадані для різних тегів, які використовуються для групування операцій шляхів, за допомогою параметра `openapi_tags`. |
|||
|
|||
Він приймає список, який містить один словник для кожного тега. |
|||
|
|||
Кожен словник може містити: |
|||
|
|||
* `name` (**обов'язково**): `str` з тією ж назвою тегу, яку Ви використовуєте у параметрі `tags` у Ваших *операціях шляху* та `APIRouter`s. |
|||
* `description`: `str` з коротким описом тегу. Може містити Markdown і буде відображено в інтерфейсі документації. |
|||
* `externalDocs`: `dict` який описує зовнішню документацію з такими полями: |
|||
* `description`: `str` з коротким описом зовнішньої документації. |
|||
* `url` (**обов'язково**): `str`з URL-адресою зовнішньої документації. |
|||
|
|||
### Створення метаданих для тегів |
|||
|
|||
Спробуймо це на прикладі з тегами для `users` та `items`. |
|||
|
|||
Створіть метадані для своїх тегів і передайте їх у параметр `openapi_tags`: |
|||
|
|||
{* ../../docs_src/metadata/tutorial004.py hl[3:16,18] *} |
|||
|
|||
Зверніть увагу, що в описах можна використовувати Markdown, наприклад, "login" буде показано жирним шрифтом (**login**), а "fancy" буде показано курсивом (_fancy_). |
|||
|
|||
/// tip | Порада |
|||
|
|||
Не обов'язково додавати метадані для всіх тегів, які Ви використовуєте. |
|||
|
|||
/// |
|||
|
|||
### Використання тегів |
|||
|
|||
Використовуйте параметр `tags` зі своїми *операціями шляху* (і `APIRouter`) для призначення їх до різних тегів: |
|||
|
|||
{* ../../docs_src/metadata/tutorial004.py hl[21,26] *} |
|||
|
|||
/// info | Інформація |
|||
|
|||
Детальніше про теги читайте в розділі [Конфігурація шляхів операцій](path-operation-configuration.md#tags){.internal-link target=_blank}. |
|||
|
|||
/// |
|||
|
|||
### Перевірка документації |
|||
|
|||
Якщо Ви зараз перевірите документацію, вона покаже всі додаткові метадані: |
|||
|
|||
<img src="/img/tutorial/metadata/image02.png"> |
|||
|
|||
### Порядок тегів |
|||
|
|||
Порядок кожного словника метаданих тегу також визначає порядок відображення в інтерфейсі документації. |
|||
|
|||
Наприклад, хоча `users` мав би йти після `items` в алфавітному порядку, він відображається перед ними, оскільки ми додали його метадані як перший словник у списку. |
|||
|
|||
## URL для OpenAPI |
|||
|
|||
За замовчуванням схема OpenAPI надається за адресою `/openapi.json`. |
|||
|
|||
Але Ви можете налаштувати це за допомогою параметра `openapi_url`. |
|||
|
|||
Наприклад, щоб налаштувати його на `/api/v1/openapi.json`: |
|||
|
|||
{* ../../docs_src/metadata/tutorial002.py hl[3] *} |
|||
|
|||
Якщо Ви хочете повністю вимкнути схему OpenAPI, Ви можете встановити `openapi_url=None`, це також вимкне інтерфейси документації, які її використовують. |
|||
|
|||
## URL-адреси документації |
|||
|
|||
Ви можете налаштувати два інтерфейси користувача для документації, які включені: |
|||
|
|||
* **Swagger UI**: доступний за адресою `/docs`. |
|||
* Ви можете змінити його URL за допомогою параметра `docs_url`. |
|||
* Ви можете вимкнути його, встановивши `docs_url=None`. |
|||
* **ReDoc**: доступний за адресою `/redoc`. |
|||
* Ви можете змінити його URL за допомогою параметра `redoc_url`. |
|||
* Ви можете вимкнути його, встановивши `redoc_url=None`. |
|||
|
|||
Наприклад, щоб налаштувати Swagger UI на `/documentation` і вимкнути ReDoc: |
|||
|
|||
{* ../../docs_src/metadata/tutorial003.py hl[3] *} |
@ -0,0 +1,100 @@ |
|||
# Статус коди Відповідей |
|||
|
|||
Так само як Ви можете вказати модель відповіді, Ви також можете оголосити HTTP код статусу для відповіді за допомогою параметра `status_code` в будь-якій з *операцій шляху*: |
|||
|
|||
* `@app.get()` |
|||
* `@app.post()` |
|||
* `@app.put()` |
|||
* `@app.delete()` |
|||
* тощо. |
|||
|
|||
{* ../../docs_src/response_status_code/tutorial001.py hl[6] *} |
|||
|
|||
/// note | Нотатка |
|||
|
|||
Зверніть увагу, що `status_code` є параметром методу "декоратора" (`get`, `post` і т.д.), а не Вашої *функції операції шляху*, як усі інші параметри та тіло запиту. |
|||
|
|||
/// |
|||
|
|||
Параметр `status_code` приймає число, яке відповідає HTTP коду статусу. |
|||
|
|||
/// info | Інформація |
|||
`status_code` також може отримувати значення з `IntEnum`, наприклад, з Python <a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a>. |
|||
|
|||
/// |
|||
|
|||
Він буде: |
|||
|
|||
* Повертати вказаний код статусу у відповіді. |
|||
* Документувати його як такий у схемі OpenAPI (і, таким чином, в інтерфейсі користувача): |
|||
|
|||
<img src="/img/tutorial/response-status-code/image01.png"> |
|||
|
|||
/// note | Нотатка |
|||
|
|||
Деякі коди відповіді (див. наступний розділ) вказують, що відповідь не має тіла. |
|||
|
|||
FastAPI знає про це і створить OpenAPI документацію, яка вказує, що тіла відповіді немає. |
|||
|
|||
/// |
|||
|
|||
## Про HTTP статус коди |
|||
|
|||
/// note | Нотатка |
|||
|
|||
Якщо Ви вже знаєте, що таке HTTP коди статусу, переходьте до наступного розділу. |
|||
|
|||
/// |
|||
|
|||
В HTTP Ви надсилаєте числовий код статусу з 3 цифр як частину відповіді. |
|||
|
|||
Ці коди статусу мають пов’язану назву для їх розпізнавання, але найважливішою частиною є саме число. |
|||
|
|||
Коротко: |
|||
|
|||
* **`100 - 199`** "Інформаційні" відповіді. Ви рідко використовуєте їх напряму. Відповіді з такими кодами не можуть мати тіла. |
|||
* **`200 - 299`** "Успішні" відповіді. Це ті, які Ви використовуватимете найчастіше. |
|||
* `200` - код за замовчуванням, який означає, що все пройшло "OK". |
|||
* Інший приклад – `201`, "Created" (створено). Його зазвичай використовують після створення нового запису в базі даних. |
|||
* Особливий випадок – `204`, "No Content" (немає вмісту). Ця відповідь використовується, коли немає даних для повернення клієнту, тому відповідь не повинна мати тіла. |
|||
* **`300 - 399`** "Перенаправлення". Відповіді з цими кодами можуть мати або не мати тіла, за винятком `304`, "Not Modified" (не змінено), яка не повинна мати тіла. |
|||
* **`400 - 499`** "Помилка клієнта". Це другий тип, який Ви, ймовірно, будете використовувати найчастіше. |
|||
* Приклад `404`, "Not Found" (не знайдено). |
|||
* Для загальних помилок клієнта можна використовувати `400`. |
|||
* `500 - 599` "Помилки сервера". Ви майже ніколи не використовуєте їх напряму. Якщо в коді Вашого застосунку або на сервері щось пішло не так, автоматично буде повернено один із цих кодів статусу. |
|||
|
|||
/// tip | Порада |
|||
|
|||
Щоб дізнатися більше про кожен код статусу і призначення кожного з них, перегляньте документацію <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network">MDN</abbr> про HTTP коди статусу</a>. |
|||
|
|||
/// |
|||
|
|||
## Легкий спосіб запам'ятати назви |
|||
|
|||
Розглянемо ще раз попередній приклад: |
|||
|
|||
{* ../../docs_src/response_status_code/tutorial001.py hl[6] *} |
|||
|
|||
`201` - це код статусу для "Created" (створено). |
|||
|
|||
Але Вам не потрібно запам'ятовувати, що означає кожен із цих кодів. |
|||
|
|||
Ви можете використовувати зручні змінні з `fastapi.status` |
|||
|
|||
{* ../../docs_src/response_status_code/tutorial002.py hl[1,6] *} |
|||
|
|||
Ці змінні просто для зручності. Вони містять ті ж самі числа, але Ви можете скористатися автозаповненням в редакторі: |
|||
|
|||
<img src="/img/tutorial/response-status-code/image02.png"> |
|||
|
|||
/// note | Технічні деталі |
|||
|
|||
Ви також можете використати `from starlette import status`. |
|||
|
|||
**FastAPI** надає ті ж самі змінні `starlette.status` як `fastapi.status`, просто для зручності розробника. Однак вони походять безпосередньо зі Starlette. |
|||
|
|||
/// |
|||
|
|||
## Зміна значення за замовчуванням |
|||
|
|||
Далі, у Посібнику для досвідчених користувачів{.internal-link target=_blank}, Ви дізнаєтесь, як повернути інший код статусу, ніж той, який Ви оголосили тут. |
@ -0,0 +1,19 @@ |
|||
from typing import List, Union |
|||
|
|||
from fastapi import FastAPI, Header |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class CommonHeaders(BaseModel): |
|||
host: str |
|||
save_data: bool |
|||
if_modified_since: Union[str, None] = None |
|||
traceparent: Union[str, None] = None |
|||
x_tag: List[str] = [] |
|||
|
|||
|
|||
@app.get("/items/") |
|||
async def read_items(headers: CommonHeaders = Header(convert_underscores=False)): |
|||
return headers |
@ -0,0 +1,22 @@ |
|||
from typing import List, Union |
|||
|
|||
from fastapi import FastAPI, Header |
|||
from pydantic import BaseModel |
|||
from typing_extensions import Annotated |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class CommonHeaders(BaseModel): |
|||
host: str |
|||
save_data: bool |
|||
if_modified_since: Union[str, None] = None |
|||
traceparent: Union[str, None] = None |
|||
x_tag: List[str] = [] |
|||
|
|||
|
|||
@app.get("/items/") |
|||
async def read_items( |
|||
headers: Annotated[CommonHeaders, Header(convert_underscores=False)], |
|||
): |
|||
return headers |
@ -0,0 +1,21 @@ |
|||
from typing import Annotated |
|||
|
|||
from fastapi import FastAPI, Header |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class CommonHeaders(BaseModel): |
|||
host: str |
|||
save_data: bool |
|||
if_modified_since: str | None = None |
|||
traceparent: str | None = None |
|||
x_tag: list[str] = [] |
|||
|
|||
|
|||
@app.get("/items/") |
|||
async def read_items( |
|||
headers: Annotated[CommonHeaders, Header(convert_underscores=False)], |
|||
): |
|||
return headers |
@ -0,0 +1,21 @@ |
|||
from typing import Annotated, Union |
|||
|
|||
from fastapi import FastAPI, Header |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class CommonHeaders(BaseModel): |
|||
host: str |
|||
save_data: bool |
|||
if_modified_since: Union[str, None] = None |
|||
traceparent: Union[str, None] = None |
|||
x_tag: list[str] = [] |
|||
|
|||
|
|||
@app.get("/items/") |
|||
async def read_items( |
|||
headers: Annotated[CommonHeaders, Header(convert_underscores=False)], |
|||
): |
|||
return headers |
@ -0,0 +1,17 @@ |
|||
from fastapi import FastAPI, Header |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class CommonHeaders(BaseModel): |
|||
host: str |
|||
save_data: bool |
|||
if_modified_since: str | None = None |
|||
traceparent: str | None = None |
|||
x_tag: list[str] = [] |
|||
|
|||
|
|||
@app.get("/items/") |
|||
async def read_items(headers: CommonHeaders = Header(convert_underscores=False)): |
|||
return headers |
@ -0,0 +1,19 @@ |
|||
from typing import Union |
|||
|
|||
from fastapi import FastAPI, Header |
|||
from pydantic import BaseModel |
|||
|
|||
app = FastAPI() |
|||
|
|||
|
|||
class CommonHeaders(BaseModel): |
|||
host: str |
|||
save_data: bool |
|||
if_modified_since: Union[str, None] = None |
|||
traceparent: Union[str, None] = None |
|||
x_tag: list[str] = [] |
|||
|
|||
|
|||
@app.get("/items/") |
|||
async def read_items(headers: CommonHeaders = Header(convert_underscores=False)): |
|||
return headers |
@ -1,4 +1,4 @@ |
|||
# For mkdocstrings and tests |
|||
httpx >=0.23.0,<0.28.0 |
|||
# For linting and generating docs versions |
|||
ruff ==0.9.4 |
|||
ruff ==0.11.2 |
|||
|
@ -1 +1 @@ |
|||
pydantic-ai==0.0.15 |
|||
pydantic-ai==0.0.30 |
|||
|
@ -0,0 +1,285 @@ |
|||
import importlib |
|||
|
|||
import pytest |
|||
from dirty_equals import IsDict |
|||
from fastapi.testclient import TestClient |
|||
from inline_snapshot import snapshot |
|||
|
|||
from tests.utils import needs_py39, needs_py310 |
|||
|
|||
|
|||
@pytest.fixture( |
|||
name="client", |
|||
params=[ |
|||
"tutorial003", |
|||
pytest.param("tutorial003_py39", marks=needs_py39), |
|||
pytest.param("tutorial003_py310", marks=needs_py310), |
|||
"tutorial003_an", |
|||
pytest.param("tutorial003_an_py39", marks=needs_py39), |
|||
pytest.param("tutorial003_an_py310", marks=needs_py310), |
|||
], |
|||
) |
|||
def get_client(request: pytest.FixtureRequest): |
|||
mod = importlib.import_module(f"docs_src.header_param_models.{request.param}") |
|||
|
|||
client = TestClient(mod.app) |
|||
return client |
|||
|
|||
|
|||
def test_header_param_model(client: TestClient): |
|||
response = client.get( |
|||
"/items/", |
|||
headers=[ |
|||
("save_data", "true"), |
|||
("if_modified_since", "yesterday"), |
|||
("traceparent", "123"), |
|||
("x_tag", "one"), |
|||
("x_tag", "two"), |
|||
], |
|||
) |
|||
assert response.status_code == 200 |
|||
assert response.json() == { |
|||
"host": "testserver", |
|||
"save_data": True, |
|||
"if_modified_since": "yesterday", |
|||
"traceparent": "123", |
|||
"x_tag": ["one", "two"], |
|||
} |
|||
|
|||
|
|||
def test_header_param_model_no_underscore(client: TestClient): |
|||
response = client.get( |
|||
"/items/", |
|||
headers=[ |
|||
("save-data", "true"), |
|||
("if-modified-since", "yesterday"), |
|||
("traceparent", "123"), |
|||
("x-tag", "one"), |
|||
("x-tag", "two"), |
|||
], |
|||
) |
|||
assert response.status_code == 422 |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"detail": [ |
|||
IsDict( |
|||
{ |
|||
"type": "missing", |
|||
"loc": ["header", "save_data"], |
|||
"msg": "Field required", |
|||
"input": { |
|||
"host": "testserver", |
|||
"traceparent": "123", |
|||
"x_tag": [], |
|||
"accept": "*/*", |
|||
"accept-encoding": "gzip, deflate", |
|||
"connection": "keep-alive", |
|||
"user-agent": "testclient", |
|||
"save-data": "true", |
|||
"if-modified-since": "yesterday", |
|||
"x-tag": "two", |
|||
}, |
|||
} |
|||
) |
|||
| IsDict( |
|||
# TODO: remove when deprecating Pydantic v1 |
|||
{ |
|||
"type": "value_error.missing", |
|||
"loc": ["header", "save_data"], |
|||
"msg": "field required", |
|||
} |
|||
) |
|||
] |
|||
} |
|||
) |
|||
|
|||
|
|||
def test_header_param_model_defaults(client: TestClient): |
|||
response = client.get("/items/", headers=[("save_data", "true")]) |
|||
assert response.status_code == 200 |
|||
assert response.json() == { |
|||
"host": "testserver", |
|||
"save_data": True, |
|||
"if_modified_since": None, |
|||
"traceparent": None, |
|||
"x_tag": [], |
|||
} |
|||
|
|||
|
|||
def test_header_param_model_invalid(client: TestClient): |
|||
response = client.get("/items/") |
|||
assert response.status_code == 422 |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"detail": [ |
|||
IsDict( |
|||
{ |
|||
"type": "missing", |
|||
"loc": ["header", "save_data"], |
|||
"msg": "Field required", |
|||
"input": { |
|||
"x_tag": [], |
|||
"host": "testserver", |
|||
"accept": "*/*", |
|||
"accept-encoding": "gzip, deflate", |
|||
"connection": "keep-alive", |
|||
"user-agent": "testclient", |
|||
}, |
|||
} |
|||
) |
|||
| IsDict( |
|||
# TODO: remove when deprecating Pydantic v1 |
|||
{ |
|||
"type": "value_error.missing", |
|||
"loc": ["header", "save_data"], |
|||
"msg": "field required", |
|||
} |
|||
) |
|||
] |
|||
} |
|||
) |
|||
|
|||
|
|||
def test_header_param_model_extra(client: TestClient): |
|||
response = client.get( |
|||
"/items/", headers=[("save_data", "true"), ("tool", "plumbus")] |
|||
) |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"host": "testserver", |
|||
"save_data": True, |
|||
"if_modified_since": None, |
|||
"traceparent": None, |
|||
"x_tag": [], |
|||
} |
|||
) |
|||
|
|||
|
|||
def test_openapi_schema(client: TestClient): |
|||
response = client.get("/openapi.json") |
|||
assert response.status_code == 200, response.text |
|||
assert response.json() == snapshot( |
|||
{ |
|||
"openapi": "3.1.0", |
|||
"info": {"title": "FastAPI", "version": "0.1.0"}, |
|||
"paths": { |
|||
"/items/": { |
|||
"get": { |
|||
"summary": "Read Items", |
|||
"operationId": "read_items_items__get", |
|||
"parameters": [ |
|||
{ |
|||
"name": "host", |
|||
"in": "header", |
|||
"required": True, |
|||
"schema": {"type": "string", "title": "Host"}, |
|||
}, |
|||
{ |
|||
"name": "save_data", |
|||
"in": "header", |
|||
"required": True, |
|||
"schema": {"type": "boolean", "title": "Save Data"}, |
|||
}, |
|||
{ |
|||
"name": "if_modified_since", |
|||
"in": "header", |
|||
"required": False, |
|||
"schema": IsDict( |
|||
{ |
|||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|||
"title": "If Modified Since", |
|||
} |
|||
) |
|||
| IsDict( |
|||
# TODO: remove when deprecating Pydantic v1 |
|||
{ |
|||
"type": "string", |
|||
"title": "If Modified Since", |
|||
} |
|||
), |
|||
}, |
|||
{ |
|||
"name": "traceparent", |
|||
"in": "header", |
|||
"required": False, |
|||
"schema": IsDict( |
|||
{ |
|||
"anyOf": [{"type": "string"}, {"type": "null"}], |
|||
"title": "Traceparent", |
|||
} |
|||
) |
|||
| IsDict( |
|||
# TODO: remove when deprecating Pydantic v1 |
|||
{ |
|||
"type": "string", |
|||
"title": "Traceparent", |
|||
} |
|||
), |
|||
}, |
|||
{ |
|||
"name": "x_tag", |
|||
"in": "header", |
|||
"required": False, |
|||
"schema": { |
|||
"type": "array", |
|||
"items": {"type": "string"}, |
|||
"default": [], |
|||
"title": "X Tag", |
|||
}, |
|||
}, |
|||
], |
|||
"responses": { |
|||
"200": { |
|||
"description": "Successful Response", |
|||
"content": {"application/json": {"schema": {}}}, |
|||
}, |
|||
"422": { |
|||
"description": "Validation Error", |
|||
"content": { |
|||
"application/json": { |
|||
"schema": { |
|||
"$ref": "#/components/schemas/HTTPValidationError" |
|||
} |
|||
} |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
} |
|||
}, |
|||
"components": { |
|||
"schemas": { |
|||
"HTTPValidationError": { |
|||
"properties": { |
|||
"detail": { |
|||
"items": { |
|||
"$ref": "#/components/schemas/ValidationError" |
|||
}, |
|||
"type": "array", |
|||
"title": "Detail", |
|||
} |
|||
}, |
|||
"type": "object", |
|||
"title": "HTTPValidationError", |
|||
}, |
|||
"ValidationError": { |
|||
"properties": { |
|||
"loc": { |
|||
"items": { |
|||
"anyOf": [{"type": "string"}, {"type": "integer"}] |
|||
}, |
|||
"type": "array", |
|||
"title": "Location", |
|||
}, |
|||
"msg": {"type": "string", "title": "Message"}, |
|||
"type": {"type": "string", "title": "Error Type"}, |
|||
}, |
|||
"type": "object", |
|||
"required": ["loc", "msg", "type"], |
|||
"title": "ValidationError", |
|||
}, |
|||
} |
|||
}, |
|||
} |
|||
) |
Loading…
Reference in new issue