mirror of https://github.com/tiangolo/fastapi.git
Merge branch 'master' into master
This commit is contained in:
commit
8a0fefc628
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
|
|
@ -86,7 +86,7 @@ jobs:
|
|||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.11"
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
|
|
@ -102,7 +102,7 @@ jobs:
|
|||
pip install git+https://${{ secrets.FASTAPI_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/mkdocstrings-python.git
|
||||
- name: Update Languages
|
||||
run: python ./scripts/docs.py update-languages
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
key: mkdocs-cards-${{ matrix.lang }}-${{ github.ref }}
|
||||
path: docs/${{ matrix.lang }}/.cache
|
||||
|
|
|
|||
|
|
@ -21,12 +21,17 @@ jobs:
|
|||
# Issue ref: https://github.com/actions/setup-python/issues/436
|
||||
# cache: "pip"
|
||||
# cache-dependency-path: pyproject.toml
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-publish
|
||||
- name: Install build dependencies
|
||||
run: pip install build
|
||||
- name: Build distribution
|
||||
run: python -m build
|
||||
- name: Publish
|
||||
uses: pypa/gh-action-pypi-publish@v1.8.11
|
||||
uses: pypa/gh-action-pypi-publish@v1.8.14
|
||||
with:
|
||||
password: ${{ secrets.PYPI_API_TOKEN }}
|
||||
- name: Dump GitHub context
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ on:
|
|||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
schedule:
|
||||
# cron every week on monday
|
||||
- cron: "0 0 * * 1"
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
|
|
@ -25,7 +28,7 @@ jobs:
|
|||
# Issue ref: https://github.com/actions/setup-python/issues/436
|
||||
# cache: "pip"
|
||||
# cache-dependency-path: pyproject.toml
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
|
|
@ -63,7 +66,7 @@ jobs:
|
|||
# Issue ref: https://github.com/actions/setup-python/issues/436
|
||||
# cache: "pip"
|
||||
# cache-dependency-path: pyproject.toml
|
||||
- uses: actions/cache@v3
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
path: ${{ env.pythonLocation }}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,537 @@
|
|||
# পাইথন এর <abbr title="একটি ভেরিয়েবল কি ধরনের ডেটা ধারণ করতে পারে।">টাইপ্স</abbr> পরিচিতি
|
||||
|
||||
Python-এ ঐচ্ছিক "টাইপ হিন্ট" (যা "টাইপ অ্যানোটেশন" নামেও পরিচিত) এর জন্য সাপোর্ট রয়েছে।
|
||||
|
||||
এই **"টাইপ হিন্ট"** বা অ্যানোটেশনগুলি এক ধরণের বিশেষ <abbr title="সিনট্যাক্স হল প্রোগ্রামিং ভাষায় কোড লেখার নিয়ম ও গঠন।">সিনট্যাক্স</abbr> যা একটি ভেরিয়েবলের <abbr title="যেমন: str, int, float, bool">টাইপ</abbr> ঘোষণা করতে দেয়।
|
||||
|
||||
ভেরিয়েবলগুলির জন্য টাইপ ঘোষণা করলে, এডিটর এবং টুলগুলি আপনাকে আরও ভালো সাপোর্ট দিতে পারে।
|
||||
|
||||
এটি পাইথন টাইপ হিন্ট সম্পর্কে একটি দ্রুত **টিউটোরিয়াল / রিফ্রেশার** মাত্র। এটি **FastAPI** এর সাথে ব্যবহার করার জন্য শুধুমাত্র ন্যূনতম প্রয়োজনীয়তা কভার করে... যা আসলে খুব একটা বেশি না।
|
||||
|
||||
**FastAPI** এই টাইপ হিন্টগুলির উপর ভিত্তি করে নির্মিত, যা এটিকে অনেক সুবিধা এবং লাভ প্রদান করে।
|
||||
|
||||
তবে, আপনি যদি কখনো **FastAPI** ব্যবহার নাও করেন, তবুও এগুলি সম্পর্কে একটু শেখা আপনার উপকারে আসবে।
|
||||
|
||||
!!! Note
|
||||
যদি আপনি একজন Python বিশেষজ্ঞ হন, এবং টাইপ হিন্ট সম্পর্কে সবকিছু জানেন, তাহলে পরবর্তী অধ্যায়ে চলে যান।
|
||||
|
||||
## প্রেরণা
|
||||
|
||||
চলুন একটি সাধারণ উদাহরণ দিয়ে শুরু করি:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
এই প্রোগ্রামটি কল করলে আউটপুট হয়:
|
||||
|
||||
```
|
||||
John Doe
|
||||
```
|
||||
|
||||
ফাংশনটি নিম্নলিখিত কাজ করে:
|
||||
|
||||
* `first_name` এবং `last_name` নেয়।
|
||||
* প্রতিটির প্রথম অক্ষরকে `title()` ব্যবহার করে বড় হাতের অক্ষরে রূপান্তর করে।
|
||||
* তাদেরকে মাঝখানে একটি স্পেস দিয়ে <abbr title="একটার পরে একটা একত্রিত করা">concatenate</abbr> করে।
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!../../../docs_src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
### এটি সম্পাদনা করুন
|
||||
|
||||
এটি একটি খুব সাধারণ প্রোগ্রাম।
|
||||
|
||||
কিন্তু এখন কল্পনা করুন যে আপনি এটি শুরু থেকে লিখছিলেন।
|
||||
|
||||
এক পর্যায়ে আপনি ফাংশনের সংজ্ঞা শুরু করেছিলেন, আপনার প্যারামিটারগুলি প্রস্তুত ছিল...
|
||||
|
||||
কিন্তু তারপর আপনাকে "সেই method কল করতে হবে যা প্রথম অক্ষরকে বড় হাতের অক্ষরে রূপান্তর করে"।
|
||||
|
||||
এটা কি `upper` ছিল? নাকি `uppercase`? `first_uppercase`? `capitalize`?
|
||||
|
||||
তারপর, আপনি পুরোনো প্রোগ্রামারের বন্ধু, এডিটর অটোকমপ্লিশনের সাহায্যে নেওয়ার চেষ্টা করেন।
|
||||
|
||||
আপনি ফাংশনের প্রথম প্যারামিটার `first_name` টাইপ করেন, তারপর একটি ডট (`.`) টাইপ করেন এবং `Ctrl+Space` চাপেন অটোকমপ্লিশন ট্রিগার করার জন্য।
|
||||
|
||||
কিন্তু, দুর্ভাগ্যবশত, আপনি কিছুই উপযোগী পান না:
|
||||
|
||||
<img src="/img/python-types/image01.png">
|
||||
|
||||
### টাইপ যোগ করুন
|
||||
|
||||
আসুন আগের সংস্করণ থেকে একটি লাইন পরিবর্তন করি।
|
||||
|
||||
আমরা ঠিক এই অংশটি পরিবর্তন করব অর্থাৎ ফাংশনের প্যারামিটারগুলি, এইগুলি:
|
||||
|
||||
```Python
|
||||
first_name, last_name
|
||||
```
|
||||
|
||||
থেকে এইগুলি:
|
||||
|
||||
```Python
|
||||
first_name: str, last_name: str
|
||||
```
|
||||
|
||||
ব্যাস।
|
||||
|
||||
এগুলিই "টাইপ হিন্ট":
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial002.py!}
|
||||
```
|
||||
|
||||
এটি ডিফল্ট ভ্যালু ঘোষণা করার মত নয় যেমন:
|
||||
|
||||
```Python
|
||||
first_name="john", last_name="doe"
|
||||
```
|
||||
|
||||
এটি একটি ভিন্ন জিনিস।
|
||||
|
||||
আমরা সমান (`=`) নয়, কোলন (`:`) ব্যবহার করছি।
|
||||
|
||||
এবং টাইপ হিন্ট যোগ করা সাধারণত তেমন কিছু পরিবর্তন করে না যা টাইপ হিন্ট ছাড়াই ঘটত।
|
||||
|
||||
কিন্তু এখন, কল্পনা করুন আপনি আবার সেই ফাংশন তৈরির মাঝখানে আছেন, কিন্তু টাইপ হিন্ট সহ।
|
||||
|
||||
একই পর্যায়ে, আপনি অটোকমপ্লিট ট্রিগার করতে `Ctrl+Space` চাপেন এবং আপনি দেখতে পান:
|
||||
|
||||
<img src="/img/python-types/image02.png">
|
||||
|
||||
এর সাথে, আপনি অপশনগুলি দেখে, স্ক্রল করতে পারেন, যতক্ষণ না আপনি এমন একটি অপশন খুঁজে পান যা কিছু মনে পরিয়ে দেয়:
|
||||
|
||||
<img src="/img/python-types/image03.png">
|
||||
|
||||
## আরও প্রেরণা
|
||||
|
||||
এই ফাংশনটি দেখুন, এটিতে ইতিমধ্যে টাইপ হিন্ট রয়েছে:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial003.py!}
|
||||
```
|
||||
|
||||
এডিটর ভেরিয়েবলগুলির টাইপ জানার কারণে, আপনি শুধুমাত্র অটোকমপ্লিশনই পান না, আপনি এরর চেকও পান:
|
||||
|
||||
<img src="/img/python-types/image04.png">
|
||||
|
||||
এখন আপনি জানেন যে আপনাকে এটি ঠিক করতে হবে, `age`-কে একটি স্ট্রিং হিসেবে রূপান্তর করতে `str(age)` ব্যবহার করতে হবে:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!../../../docs_src/python_types/tutorial004.py!}
|
||||
```
|
||||
|
||||
## টাইপ ঘোষণা
|
||||
|
||||
আপনি এতক্ষন টাইপ হিন্ট ঘোষণা করার মূল স্থানটি দেখে ফেলেছেন-- ফাংশন প্যারামিটার হিসেবে।
|
||||
|
||||
সাধারণত এটি **FastAPI** এর ক্ষেত্রেও একই।
|
||||
|
||||
### সিম্পল টাইপ
|
||||
|
||||
আপনি `str` ছাড়াও সমস্ত স্ট্যান্ডার্ড পাইথন টাইপ ঘোষণা করতে পারেন।
|
||||
|
||||
উদাহরণস্বরূপ, আপনি এগুলো ব্যবহার করতে পারেন:
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/python_types/tutorial005.py!}
|
||||
```
|
||||
|
||||
### টাইপ প্যারামিটার সহ জেনেরিক টাইপ
|
||||
|
||||
কিছু ডাটা স্ট্রাকচার অন্যান্য মান ধারণ করতে পারে, যেমন `dict`, `list`, `set` এবং `tuple`। এবং অভ্যন্তরীণ মানগুলোরও নিজেদের টাইপ থাকতে পারে।
|
||||
|
||||
এই ধরনের টাইপগুলিকে বলা হয় "**জেনেরিক**" টাইপ এবং এগুলিকে তাদের অভ্যন্তরীণ টাইপগুলি সহ ঘোষণা করা সম্ভব।
|
||||
|
||||
এই টাইপগুলি এবং অভ্যন্তরীণ টাইপগুলি ঘোষণা করতে, আপনি Python মডিউল `typing` ব্যবহার করতে পারেন। এটি বিশেষভাবে এই টাইপ হিন্টগুলি সমর্থন করার জন্য রয়েছে।
|
||||
|
||||
#### Python এর নতুন সংস্করণ
|
||||
|
||||
`typing` ব্যবহার করা সিনট্যাক্সটি Python 3.6 থেকে সর্বশেষ সংস্করণগুলি পর্যন্ত, অর্থাৎ Python 3.9, Python 3.10 ইত্যাদি সহ সকল সংস্করণের সাথে **সামঞ্জস্যপূর্ণ**।
|
||||
|
||||
Python যত এগিয়ে যাচ্ছে, **নতুন সংস্করণগুলি** এই টাইপ অ্যানোটেশনগুলির জন্য তত উন্নত সাপোর্ট নিয়ে আসছে এবং অনেক ক্ষেত্রে আপনাকে টাইপ অ্যানোটেশন ঘোষণা করতে `typing` মডিউল ইম্পোর্ট এবং ব্যবহার করার প্রয়োজন হবে না।
|
||||
|
||||
যদি আপনি আপনার প্রজেক্টের জন্য Python-এর আরও সাম্প্রতিক সংস্করণ নির্বাচন করতে পারেন, তাহলে আপনি সেই অতিরিক্ত সরলতা থেকে সুবিধা নিতে পারবেন।
|
||||
|
||||
ডক্সে রয়েছে Python-এর প্রতিটি সংস্করণের সাথে সামঞ্জস্যপূর্ণ উদাহরণগুলি (যখন পার্থক্য আছে)।
|
||||
|
||||
উদাহরণস্বরূপ, "**Python 3.6+**" মানে এটি Python 3.6 বা তার উপরে সামঞ্জস্যপূর্ণ (যার মধ্যে 3.7, 3.8, 3.9, 3.10, ইত্যাদি অন্তর্ভুক্ত)। এবং "**Python 3.9+**" মানে এটি Python 3.9 বা তার উপরে সামঞ্জস্যপূর্ণ (যার মধ্যে 3.10, ইত্যাদি অন্তর্ভুক্ত)।
|
||||
|
||||
যদি আপনি Python-এর **সর্বশেষ সংস্করণগুলি ব্যবহার করতে পারেন**, তাহলে সর্বশেষ সংস্করণের জন্য উদাহরণগুলি ব্যবহার করুন, সেগুলি আপনাকে **সর্বোত্তম এবং সহজতম সিনট্যাক্স** প্রদান করবে, যেমন, "**Python 3.10+**"।
|
||||
|
||||
#### লিস্ট
|
||||
|
||||
উদাহরণস্বরূপ, একটি ভেরিয়েবলকে `str`-এর একটি `list` হিসেবে সংজ্ঞায়িত করা যাক।
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
ভেরিয়েবলটি ঘোষণা করুন, একই কোলন (`:`) সিনট্যাক্স ব্যবহার করে।
|
||||
|
||||
টাইপ হিসেবে, `list` ব্যবহার করুন।
|
||||
|
||||
যেহেতু লিস্ট এমন একটি টাইপ যা অভ্যন্তরীণ টাইপগুলি ধারণ করে, আপনি তাদের স্কোয়ার ব্রাকেটের ভিতরে ব্যবহার করুন:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial006_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
`typing` থেকে `List` (বড় হাতের `L` দিয়ে) ইমপোর্ট করুন:
|
||||
|
||||
``` Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
ভেরিয়েবলটি ঘোষণা করুন, একই কোলন (`:`) সিনট্যাক্স ব্যবহার করে।
|
||||
|
||||
টাইপ হিসেবে, `typing` থেকে আপনার ইম্পোর্ট করা `List` ব্যবহার করুন।
|
||||
|
||||
যেহেতু লিস্ট এমন একটি টাইপ যা অভ্যন্তরীণ টাইপগুলি ধারণ করে, আপনি তাদের স্কোয়ার ব্রাকেটের ভিতরে করুন:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!> ../../../docs_src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
!!! Info
|
||||
স্কোয়ার ব্রাকেট এর ভিতরে ব্যবহৃত এইসব অভন্তরীন টাইপগুলোকে "ইন্টারনাল টাইপ" বলে।
|
||||
|
||||
এই উদাহরণে, এটি হচ্ছে `List`(অথবা পাইথন ৩.৯ বা তার উপরের সংস্করণের ক্ষেত্রে `list`) এ পাস করা টাইপ প্যারামিটার।
|
||||
|
||||
এর অর্থ হচ্ছে: "ভেরিয়েবল `items` একটি `list`, এবং এই লিস্টের প্রতিটি আইটেম একটি `str`।"
|
||||
|
||||
!!! Tip
|
||||
যদি আপনি Python 3.9 বা তার উপরে ব্যবহার করেন, আপনার `typing` থেকে `List` আমদানি করতে হবে না, আপনি সাধারণ `list` ওই টাইপের পরিবর্তে ব্যবহার করতে পারেন।
|
||||
|
||||
এর মাধ্যমে, আপনার এডিটর লিস্ট থেকে আইটেম প্রসেস করার সময় সাপোর্ট প্রদান করতে পারবে:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
|
||||
টাইপগুলি ছাড়া, এটি করা প্রায় অসম্ভব।
|
||||
|
||||
লক্ষ্য করুন যে ভেরিয়েবল `item` হল `items` লিস্টের একটি এলিমেন্ট।
|
||||
|
||||
তবুও, এডিটর জানে যে এটি একটি `str`, এবং তার জন্য সাপোর্ট প্রদান করে।
|
||||
|
||||
#### টাপল এবং সেট
|
||||
|
||||
আপনি `tuple` এবং `set` ঘোষণা করার জন্য একই প্রক্রিয়া অনুসরণ করবেন:
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial007_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
এর মানে হল:
|
||||
|
||||
* ভেরিয়েবল `items_t` হল একটি `tuple` যা ৩টি আইটেম ধারণ করে, একটি `int`, অন্য একটি `int`, এবং একটি `str`।
|
||||
* ভেরিয়েবল `items_s` হল একটি `set`, এবং এর প্রতিটি আইটেম হল `bytes` টাইপের।
|
||||
|
||||
#### ডিক্ট
|
||||
|
||||
একটি `dict` সংজ্ঞায়িত করতে, আপনি ২টি টাইপ প্যারামিটার কমা দ্বারা পৃথক করে দেবেন।
|
||||
|
||||
প্রথম টাইপ প্যারামিটারটি হল `dict`-এর কীগুলির জন্য।
|
||||
|
||||
দ্বিতীয় টাইপ প্যারামিটারটি হল `dict`-এর মানগুলির জন্য:
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial008_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
|
||||
এর মানে হল:
|
||||
|
||||
* ভেরিয়েবল `prices` হল একটি `dict`:
|
||||
* এই `dict`-এর কীগুলি হল `str` টাইপের (ধরা যাক, প্রতিটি আইটেমের নাম)।
|
||||
* এই `dict`-এর মানগুলি হল `float` টাইপের (ধরা যাক, প্রতিটি আইটেমের দাম)।
|
||||
|
||||
#### ইউনিয়ন
|
||||
|
||||
আপনি একটি ভেরিয়েবলকে এমনভাবে ঘোষণা করতে পারেন যেন তা **একাধিক টাইপের** হয়, উদাহরণস্বরূপ, একটি `int` অথবা `str`।
|
||||
|
||||
Python 3.6 এবং তার উপরের সংস্করণগুলিতে (Python 3.10 অন্তর্ভুক্ত) আপনি `typing` থেকে `Union` টাইপ ব্যবহার করতে পারেন এবং স্কোয়ার ব্র্যাকেটের মধ্যে গ্রহণযোগ্য টাইপগুলি রাখতে পারেন।
|
||||
|
||||
Python 3.10-এ একটি **নতুন সিনট্যাক্স** আছে যেখানে আপনি সম্ভাব্য টাইপগুলিকে একটি <abbr title="উল্লম্ব বারালকে 'বিটওয়াইজ বা অপারেটর' বলা হয়, কিন্তু সেই অর্থ এখানে প্রাসঙ্গিক নয়">ভার্টিকাল বার (`|`)</abbr> দ্বারা পৃথক করতে পারেন।
|
||||
|
||||
=== "Python 3.10+"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial008b_py310.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial008b.py!}
|
||||
```
|
||||
|
||||
উভয় ক্ষেত্রেই এর মানে হল যে `item` হতে পারে একটি `int` অথবা `str`।
|
||||
|
||||
#### সম্ভবত `None`
|
||||
|
||||
আপনি এমনভাবে ঘোষণা করতে পারেন যে একটি মান হতে পারে এক টাইপের, যেমন `str`, আবার এটি `None`-ও হতে পারে।
|
||||
|
||||
Python 3.6 এবং তার উপরের সংস্করণগুলিতে (Python 3.10 অনতর্ভুক্ত) আপনি `typing` মডিউল থেকে `Optional` ইমপোর্ট করে এটি ঘোষণা এবং ব্যবহার করতে পারেন।
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
`Optional[str]` ব্যবহার করা মানে হল শুধু `str` নয়, এটি হতে পারে `None`-ও, যা আপনার এডিটরকে সেই ত্রুটিগুলি শনাক্ত করতে সাহায্য করবে যেখানে আপনি ধরে নিচ্ছেন যে একটি মান সবসময় `str` হবে, অথচ এটি `None`-ও হতে পারেও।
|
||||
|
||||
`Optional[Something]` মূলত `Union[Something, None]`-এর একটি শর্টকাট, এবং তারা সমতুল্য।
|
||||
|
||||
এর মানে হল, Python 3.10-এ, আপনি টাইপগুলির ইউনিয়ন ঘোষণা করতে `Something | None` ব্যবহার করতে পারেন:
|
||||
|
||||
=== "Python 3.10+"
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!> ../../../docs_src/python_types/tutorial009_py310.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+ বিকল্প"
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial009b.py!}
|
||||
```
|
||||
|
||||
#### `Union` বা `Optional` ব্যবহার
|
||||
|
||||
যদি আপনি Python 3.10-এর নীচের সংস্করণ ব্যবহার করেন, তবে এখানে আমার খুবই **ব্যক্তিগত** দৃষ্টিভঙ্গি থেকে একটি টিপস:
|
||||
|
||||
* 🚨 `Optional[SomeType]` ব্যবহার এড়িয়ে চলুন।
|
||||
* এর পরিবর্তে ✨ **`Union[SomeType, None]` ব্যবহার করুন** ✨।
|
||||
|
||||
উভয়ই সমতুল্য এবং মূলে একই, কিন্তু আমি `Union`-এর পক্ষে সুপারিশ করব কারণ "**অপশনাল**" শব্দটি মনে হতে পারে যে মানটি ঐচ্ছিক,অথচ এটি আসলে মানে "এটি হতে পারে `None`", এমনকি যদি এটি ঐচ্ছিক না হয়েও আবশ্যিক হয়।
|
||||
|
||||
আমি মনে করি `Union[SomeType, None]` এর অর্থ আরও স্পষ্টভাবে প্রকাশ করে।
|
||||
|
||||
এটি কেবল শব্দ এবং নামের ব্যাপার। কিন্তু সেই শব্দগুলি আপনি এবং আপনার সহকর্মীরা কোড সম্পর্কে কীভাবে চিন্তা করেন তা প্রভাবিত করতে পারে।
|
||||
|
||||
একটি উদাহরণ হিসেবে, এই ফাংশনটি নিন:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009c.py!}
|
||||
```
|
||||
|
||||
`name` প্যারামিটারটি `Optional[str]` হিসেবে সংজ্ঞায়িত হয়েছে, কিন্তু এটি **অপশনাল নয়**, আপনি প্যারামিটার ছাড়া ফাংশনটি কল করতে পারবেন না:
|
||||
|
||||
```Python
|
||||
say_hi() # ওহ না, এটি একটি ত্রুটি নিক্ষেপ করবে! 😱
|
||||
```
|
||||
|
||||
`name` প্যারামিটারটি **এখনও আবশ্যিক** (নন-অপশনাল) কারণ এটির কোনো ডিফল্ট মান নেই। তবুও, `name` এর মান হিসেবে `None` গ্রহণযোগ্য:
|
||||
|
||||
```Python
|
||||
say_hi(name=None) # এটি কাজ করে, None বৈধ 🎉
|
||||
```
|
||||
|
||||
সুখবর হল, একবার আপনি Python 3.10 ব্যবহার করা শুরু করলে, আপনাকে এগুলোর ব্যাপারে আর চিন্তা করতে হবে না, যেহুতু আপনি | ব্যবহার করেই ইউনিয়ন ঘোষণা করতে পারবেন:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009c_py310.py!}
|
||||
```
|
||||
|
||||
এবং তারপর আপনাকে নামগুলি যেমন `Optional` এবং `Union` নিয়ে আর চিন্তা করতে হবে না। 😎
|
||||
|
||||
#### জেনেরিক টাইপস
|
||||
|
||||
স্কোয়ার ব্র্যাকেটে টাইপ প্যারামিটার নেওয়া এই টাইপগুলিকে **জেনেরিক টাইপ** বা **জেনেরিকস** বলা হয়, যেমন:
|
||||
|
||||
=== "Python 3.10+"
|
||||
আপনি সেই একই বিল্টইন টাইপ জেনেরিক্স হিসেবে ব্যবহার করতে পারবেন(ভিতরে টাইপ সহ স্কয়ারে ব্রাকেট দিয়ে):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
এবং Python 3.8 এর মতোই, `typing` মডিউল থেকে:
|
||||
|
||||
* `Union`
|
||||
* `Optional` (Python 3.8 এর মতোই)
|
||||
* ...এবং অন্যান্য।
|
||||
|
||||
Python 3.10-এ, `Union` এবং `Optional` জেনেরিকস ব্যবহার করার বিকল্প হিসেবে, আপনি টাইপগুলির ইউনিয়ন ঘোষণা করতে <abbr title="উল্লম্ব বারালকে 'বিটওয়াইজ বা অপারেটর' বলা হয়, কিন্তু সেই অর্থ এখানে প্রাসঙ্গিক নয়">ভার্টিকাল বার (`|`)</abbr> ব্যবহার করতে পারেন, যা ওদের থেকে অনেক ভালো এবং সহজ।
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
আপনি সেই একই বিল্টইন টাইপ জেনেরিক্স হিসেবে ব্যবহার করতে পারবেন(ভিতরে টাইপ সহ স্কয়ারে ব্রাকেট দিয়ে):
|
||||
|
||||
* `list`
|
||||
* `tuple`
|
||||
* `set`
|
||||
* `dict`
|
||||
|
||||
এবং Python 3.8 এর মতোই, `typing` মডিউল থেকে:
|
||||
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...এবং অন্যান্য।
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
* `List`
|
||||
* `Tuple`
|
||||
* `Set`
|
||||
* `Dict`
|
||||
* `Union`
|
||||
* `Optional`
|
||||
* ...এবং অন্যান্য।
|
||||
|
||||
### ক্লাস হিসেবে টাইপস
|
||||
|
||||
আপনি একটি ভেরিয়েবলের টাইপ হিসেবে একটি ক্লাস ঘোষণা করতে পারেন।
|
||||
|
||||
ধরুন আপনার কাছে `Person` নামে একটি ক্লাস আছে, যার একটি নাম আছে:
|
||||
|
||||
```Python hl_lines="1-3"
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
তারপর আপনি একটি ভেরিয়েবলকে `Person` টাইপের হিসেবে ঘোষণা করতে পারেন:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
এবং তারপর, আবার, আপনি এডিটর সাপোর্ট পেয়ে যাবেন:
|
||||
|
||||
<img src="/img/python-types/image06.png">
|
||||
|
||||
লক্ষ্য করুন যে এর মানে হল "`one_person` হল ক্লাস `Person`-এর একটি **ইন্সট্যান্স**।"
|
||||
|
||||
এর মানে এটি নয় যে "`one_person` হল **ক্লাস** যাকে বলা হয় `Person`।"
|
||||
|
||||
## Pydantic মডেল
|
||||
|
||||
[Pydantic](https://docs.pydantic.dev/) হল একটি Python লাইব্রেরি যা ডাটা ভ্যালিডেশন সম্পাদন করে।
|
||||
|
||||
আপনি ডাটার "আকার" এট্রিবিউট সহ ক্লাস হিসেবে ঘোষণা করেন।
|
||||
|
||||
এবং প্রতিটি এট্রিবিউট এর একটি টাইপ থাকে।
|
||||
|
||||
তারপর আপনি যদি কিছু মান দিয়ে সেই ক্লাসের একটি ইন্সট্যান্স তৈরি করেন-- এটি মানগুলিকে ভ্যালিডেট করবে, প্রয়োজন অনুযায়ী তাদেরকে উপযুক্ত টাইপে রূপান্তর করবে এবং আপনাকে সমস্ত ডাটা সহ একটি অবজেক্ট প্রদান করবে।
|
||||
|
||||
এবং আপনি সেই ফলাফল অবজেক্টের সাথে এডিটর সাপোর্ট পাবেন।
|
||||
|
||||
অফিসিয়াল Pydantic ডক্স থেকে একটি উদাহরণ:
|
||||
|
||||
=== "Python 3.10+"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011_py310.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python
|
||||
{!> ../../../docs_src/python_types/tutorial011.py!}
|
||||
```
|
||||
|
||||
!!! Info
|
||||
[Pydantic সম্পর্কে আরও জানতে, এর ডকুমেন্টেশন দেখুন](https://docs.pydantic.dev/)।
|
||||
|
||||
**FastAPI** মূলত Pydantic-এর উপর নির্মিত।
|
||||
|
||||
আপনি এই সমস্ত কিছুর অনেক বাস্তবসম্মত উদাহরণ পাবেন [টিউটোরিয়াল - ইউজার গাইডে](https://fastapi.tiangolo.com/tutorial/)।
|
||||
|
||||
!!! Tip
|
||||
যখন আপনি `Optional` বা `Union[Something, None]` ব্যবহার করেন এবং কোনো ডিফল্ট মান না থাকে, Pydantic-এর একটি বিশেষ আচরণ রয়েছে, আপনি Pydantic ডকুমেন্টেশনে [Required Optional fields](https://docs.pydantic.dev/latest/concepts/models/#required-optional-fields) সম্পর্কে আরও পড়তে পারেন।
|
||||
|
||||
## মেটাডাটা অ্যানোটেশন সহ টাইপ হিন্টস
|
||||
|
||||
Python-এ এমন একটি ফিচার আছে যা `Annotated` ব্যবহার করে এই টাইপ হিন্টগুলিতে **অতিরিক্ত মেটাডাটা** রাখতে দেয়।
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
Python 3.9-এ, `Annotated` স্ট্যান্ডার্ড লাইব্রেরিতে অন্তর্ভুক্ত, তাই আপনি এটি `typing` থেকে ইমপোর্ট করতে পারেন।
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial013_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
Python 3.9-এর নীচের সংস্করণগুলিতে, আপনি `Annotated`-কে `typing_extensions` থেকে ইমপোর্ট করেন।
|
||||
|
||||
এটি **FastAPI** এর সাথে ইতিমদ্ধে ইনস্টল হয়ে থাকবে।
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!> ../../../docs_src/python_types/tutorial013.py!}
|
||||
```
|
||||
|
||||
Python নিজে এই `Annotated` দিয়ে কিছুই করে না। এবং এডিটর এবং অন্যান্য টুলগুলির জন্য, টাইপটি এখনও `str`।
|
||||
|
||||
কিন্তু আপনি এই `Annotated` এর মধ্যকার জায়গাটির মধ্যে **FastAPI**-এ কীভাবে আপনার অ্যাপ্লিকেশন আচরণ করুক তা সম্পর্কে অতিরিক্ত মেটাডাটা প্রদান করার জন্য ব্যবহার করতে পারেন।
|
||||
|
||||
মনে রাখার গুরুত্বপূর্ণ বিষয় হল যে **প্রথম *টাইপ প্যারামিটার*** আপনি `Annotated`-এ পাস করেন সেটি হল **আসল টাইপ**। বাকি শুধুমাত্র অন্যান্য টুলগুলির জন্য মেটাডাটা।
|
||||
|
||||
এখন আপনার কেবল জানা প্রয়োজন যে `Annotated` বিদ্যমান, এবং এটি স্ট্যান্ডার্ড Python। 😎
|
||||
|
||||
পরবর্তীতে আপনি দেখবেন এটি কতটা **শক্তিশালী** হতে পারে।
|
||||
|
||||
!!! Tip
|
||||
এটি **স্ট্যান্ডার্ড Python** হওয়ার মানে হল আপনি আপনার এডিটরে, আপনি যে টুলগুলি কোড বিশ্লেষণ এবং রিফ্যাক্টর করার জন্য ব্যবহার করেন তাতে **সেরা সম্ভাব্য ডেভেলপার এক্সপেরিয়েন্স** পাবেন। ✨
|
||||
|
||||
এবং এছাড়াও আপনার কোড অন্যান্য অনেক Python টুল এবং লাইব্রেরিগুলির সাথে খুব সামঞ্জস্যপূর্ণ হবে। 🚀
|
||||
|
||||
## **FastAPI**-এ টাইপ হিন্টস
|
||||
|
||||
**FastAPI** এই টাইপ হিন্টগুলি ব্যবহার করে বেশ কিছু জিনিস করে।
|
||||
|
||||
**FastAPI**-এ আপনি টাইপ হিন্টগুলি সহ প্যারামিটার ঘোষণা করেন এবং আপনি পান:
|
||||
|
||||
* **এডিটর সাপোর্ট**।
|
||||
* **টাইপচেক**।
|
||||
|
||||
...এবং **FastAPI** একই ঘোষণাগুলি ব্যবহার করে:
|
||||
|
||||
* **রিকুইরেমেন্টস সংজ্ঞায়িত করে**: রিকোয়েস্ট পাথ প্যারামিটার, কুয়েরি প্যারামিটার, হেডার, বডি, ডিপেন্ডেন্সিস, ইত্যাদি থেকে।
|
||||
* **ডেটা রূপান্তর করে**: রিকোয়েস্ট থেকে প্রয়োজনীয় টাইপে ডেটা।
|
||||
* **ডেটা যাচাই করে**: প্রতিটি রিকোয়েস্ট থেকে আসা ডেটা:
|
||||
* যখন ডেটা অবৈধ হয় তখন **স্বয়ংক্রিয় ত্রুটি** গ্রাহকের কাছে ফেরত পাঠানো।
|
||||
* **API ডকুমেন্টেশন তৈরি করে**: OpenAPI ব্যবহার করে:
|
||||
* যা স্বয়ংক্রিয় ইন্টার্যাক্টিভ ডকুমেন্টেশন ইউজার ইন্টারফেস দ্বারা ব্যবহৃত হয়।
|
||||
|
||||
এই সব কিছু আপনার কাছে অস্পষ্ট মনে হতে পারে। চিন্তা করবেন না। আপনি [টিউটোরিয়াল - ইউজার গাইড](https://fastapi.tiangolo.com/tutorial/) এ এই সব কিছু প্র্যাকটিসে দেখতে পাবেন।
|
||||
|
||||
গুরুত্বপূর্ণ বিষয় হল, আপনি যদি স্ট্যান্ডার্ড Python টাইপগুলি ব্যবহার করেন, তবে আরও বেশি ক্লাস, ডেকোরেটর ইত্যাদি যোগ না করেই একই স্থানে **FastAPI** আপনার অনেক কাজ করে দিবে।
|
||||
|
||||
!!! Info
|
||||
যদি আপনি টিউটোরিয়ালের সমস্ত বিষয় পড়ে ফেলে থাকেন এবং টাইপ সম্পর্কে আরও জানতে চান, তবে একটি ভালো রিসোর্স হল [mypy এর "cheat sheet"](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html)। এই "cheat sheet" এ আপনি Python টাইপ হিন্ট সম্পর্কে বেসিক থেকে উন্নত লেভেলের ধারণা পেতে পারেন, যা আপনার কোডে টাইপ সেফটি এবং স্পষ্টতা বাড়াতে সাহায্য করবে।
|
||||
|
|
@ -10,7 +10,7 @@ Es gibt viele Tools zum Generieren von Clients aus **OpenAPI**.
|
|||
|
||||
Ein gängiges Tool ist <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>.
|
||||
|
||||
Wenn Sie ein **Frontend** erstellen, ist <a href="https://github.com/ferdikoomen/openapi-typescript-codegen" class="external-link" target="_blank">openapi-typescript-codegen</a> eine sehr interessante Alternative.
|
||||
Wenn Sie ein **Frontend** erstellen, ist <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a> eine sehr interessante Alternative.
|
||||
|
||||
## Client- und SDK-Generatoren – Sponsor
|
||||
|
||||
|
|
@ -58,14 +58,14 @@ Und dieselben Informationen aus den Modellen, die in OpenAPI enthalten sind, kö
|
|||
|
||||
Nachdem wir nun die Anwendung mit den Modellen haben, können wir den Client-Code für das Frontend generieren.
|
||||
|
||||
#### `openapi-typescript-codegen` installieren
|
||||
#### `openapi-ts` installieren
|
||||
|
||||
Sie können `openapi-typescript-codegen` in Ihrem Frontend-Code installieren mit:
|
||||
Sie können `openapi-ts` in Ihrem Frontend-Code installieren mit:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm install openapi-typescript-codegen --save-dev
|
||||
$ npm install @hey-api/openapi-ts --save-dev
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
|
@ -74,7 +74,7 @@ $ npm install openapi-typescript-codegen --save-dev
|
|||
|
||||
#### Client-Code generieren
|
||||
|
||||
Um den Client-Code zu generieren, können Sie das Kommandozeilentool `openapi` verwenden, das soeben installiert wurde.
|
||||
Um den Client-Code zu generieren, können Sie das Kommandozeilentool `openapi-ts` verwenden, das soeben installiert wurde.
|
||||
|
||||
Da es im lokalen Projekt installiert ist, könnten Sie diesen Befehl wahrscheinlich nicht direkt aufrufen, sondern würden ihn in Ihre Datei `package.json` einfügen.
|
||||
|
||||
|
|
@ -87,12 +87,12 @@ Diese könnte so aussehen:
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
|
||||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"openapi-typescript-codegen": "^0.20.1",
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -106,7 +106,7 @@ Nachdem Sie das NPM-Skript `generate-client` dort stehen haben, können Sie es a
|
|||
$ npm run generate-client
|
||||
|
||||
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
|
||||
> openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes
|
||||
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
```
|
||||
|
||||
</div>
|
||||
|
|
@ -254,12 +254,12 @@ Da das Endergebnis nun in einer Datei `openapi.json` vorliegt, würden Sie die `
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi --input ./openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
|
||||
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"openapi-typescript-codegen": "^0.20.1",
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
# Response-Statuscode
|
||||
|
||||
So wie ein Responsemodell, können Sie auch einen HTTP-Statuscode für die Response deklarieren, mithilfe des Parameters `status_code`, und zwar in jeder der *Pfadoperationen*:
|
||||
|
||||
* `@app.get()`
|
||||
* `@app.post()`
|
||||
* `@app.put()`
|
||||
* `@app.delete()`
|
||||
* usw.
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/response_status_code/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! note "Hinweis"
|
||||
Beachten Sie, dass `status_code` ein Parameter der „Dekorator“-Methode ist (`get`, `post`, usw.). Nicht der *Pfadoperation-Funktion*, so wie die anderen Parameter und der Body.
|
||||
|
||||
Dem `status_code`-Parameter wird eine Zahl mit dem HTTP-Statuscode übergeben.
|
||||
|
||||
!!! info
|
||||
Alternativ kann `status_code` auch ein `IntEnum` erhalten, so wie Pythons <a href="https://docs.python.org/3/library/http.html#http.HTTPStatus" class="external-link" target="_blank">`http.HTTPStatus`</a>.
|
||||
|
||||
Das wird:
|
||||
|
||||
* Diesen Statuscode mit der Response zurücksenden.
|
||||
* Ihn als solchen im OpenAPI-Schema dokumentieren (und somit in den Benutzeroberflächen):
|
||||
|
||||
<img src="/img/tutorial/response-status-code/image01.png">
|
||||
|
||||
!!! note "Hinweis"
|
||||
Einige Responsecodes (siehe nächster Abschnitt) kennzeichnen, dass die Response keinen Body hat.
|
||||
|
||||
FastAPI versteht das und wird in der OpenAPI-Dokumentation anzeigen, dass es keinen Responsebody gibt.
|
||||
|
||||
## Über HTTP-Statuscodes
|
||||
|
||||
!!! note "Hinweis"
|
||||
Wenn Sie bereits wissen, was HTTP-Statuscodes sind, überspringen Sie dieses Kapitel und fahren Sie mit dem nächsten fort.
|
||||
|
||||
In HTTP senden Sie als Teil der Response einen aus drei Ziffern bestehenden numerischen Statuscode.
|
||||
|
||||
Diese Statuscodes haben einen Namen zugeordnet, um sie besser zu erkennen, aber der wichtige Teil ist die Zahl.
|
||||
|
||||
Kurz:
|
||||
|
||||
* `100` und darüber stehen für „Information“. Diese verwenden Sie selten direkt. Responses mit diesen Statuscodes können keinen Body haben.
|
||||
* **`200`** und darüber stehen für Responses, die „Successful“ („Erfolgreich“) waren. Diese verwenden Sie am häufigsten.
|
||||
* `200` ist der Default-Statuscode, welcher bedeutet, alles ist „OK“.
|
||||
* Ein anderes Beispiel ist `201`, „Created“ („Erzeugt“). Wird in der Regel verwendet, wenn ein neuer Datensatz in der Datenbank erzeugt wurde.
|
||||
* Ein spezieller Fall ist `204`, „No Content“ („Kein Inhalt“). Diese Response wird verwendet, wenn es keinen Inhalt gibt, der zum Client zurückgeschickt wird, diese Response hat also keinen Body.
|
||||
* **`300`** und darüber steht für „Redirection“ („Umleitung“). Responses mit diesen Statuscodes können einen oder keinen Body haben, mit Ausnahme von `304`, „Not Modified“ („Nicht verändert“), welche keinen haben darf.
|
||||
* **`400`** und darüber stehen für „Client error“-Responses („Client-Fehler“). Auch diese verwenden Sie am häufigsten.
|
||||
* Ein Beispiel ist `404`, für eine „Not Found“-Response („Nicht gefunden“).
|
||||
* Für allgemeine Fehler beim Client können Sie einfach `400` verwenden.
|
||||
* `500` und darüber stehen für Server-Fehler. Diese verwenden Sie fast nie direkt. Wenn etwas an irgendeiner Stelle in Ihrem Anwendungscode oder im Server schiefläuft, wird automatisch einer dieser Fehler-Statuscodes zurückgegeben.
|
||||
|
||||
!!! tip "Tipp"
|
||||
Um mehr über Statuscodes zu lernen, und welcher wofür verwendet wird, lesen Sie die <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Mozilla-Entwickler-Netzwerk">MDN</abbr> Dokumentation über HTTP-Statuscodes</a>.
|
||||
|
||||
## Abkürzung, um die Namen zu erinnern
|
||||
|
||||
Schauen wir uns das vorherige Beispiel noch einmal an:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/response_status_code/tutorial001.py!}
|
||||
```
|
||||
|
||||
`201` ist der Statuscode für „Created“ („Erzeugt“).
|
||||
|
||||
Aber Sie müssen sich nicht daran erinnern, welcher dieser Codes was bedeutet.
|
||||
|
||||
Sie können die Hilfsvariablen von `fastapi.status` verwenden.
|
||||
|
||||
```Python hl_lines="1 6"
|
||||
{!../../../docs_src/response_status_code/tutorial002.py!}
|
||||
```
|
||||
|
||||
Diese sind nur eine Annehmlichkeit und enthalten dieselbe Nummer, aber auf diese Weise können Sie die Autovervollständigung Ihres Editors verwenden, um sie zu finden:
|
||||
|
||||
<img src="/img/tutorial/response-status-code/image02.png">
|
||||
|
||||
!!! note "Technische Details"
|
||||
Sie können auch `from starlette import status` verwenden.
|
||||
|
||||
**FastAPI** bietet dieselben `starlette.status`-Codes auch via `fastapi.status` an, als Annehmlichkeit für Sie, den Entwickler. Sie kommen aber direkt von Starlette.
|
||||
|
||||
## Den Defaultwert ändern
|
||||
|
||||
Später sehen Sie, im [Handbuch für fortgeschrittene Benutzer](../advanced/response-change-status-code.md){.internal-link target=_blank}, wie Sie einen anderen Statuscode zurückgeben können, als den Default, den Sie hier deklarieren.
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
⚠ 🧰 <a href="https://openapi-generator.tech/" class="external-link" target="_blank">🗄 🚂</a>.
|
||||
|
||||
🚥 👆 🏗 **🕸**, 📶 😌 🎛 <a href="https://github.com/ferdikoomen/openapi-typescript-codegen" class="external-link" target="_blank">🗄-📕-🇦🇪</a>.
|
||||
🚥 👆 🏗 **🕸**, 📶 😌 🎛 <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">🗄-📕-🇦🇪</a>.
|
||||
|
||||
## 🏗 📕 🕸 👩💻
|
||||
|
||||
|
|
@ -46,14 +46,14 @@
|
|||
|
||||
🔜 👈 👥 ✔️ 📱 ⏮️ 🏷, 👥 💪 🏗 👩💻 📟 🕸.
|
||||
|
||||
#### ❎ `openapi-typescript-codegen`
|
||||
#### ❎ `openapi-ts`
|
||||
|
||||
👆 💪 ❎ `openapi-typescript-codegen` 👆 🕸 📟 ⏮️:
|
||||
👆 💪 ❎ `openapi-ts` 👆 🕸 📟 ⏮️:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm install openapi-typescript-codegen --save-dev
|
||||
$ npm install @hey-api/openapi-ts --save-dev
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
|
@ -62,7 +62,7 @@ $ npm install openapi-typescript-codegen --save-dev
|
|||
|
||||
#### 🏗 👩💻 📟
|
||||
|
||||
🏗 👩💻 📟 👆 💪 ⚙️ 📋 ⏸ 🈸 `openapi` 👈 🔜 🔜 ❎.
|
||||
🏗 👩💻 📟 👆 💪 ⚙️ 📋 ⏸ 🈸 `openapi-ts` 👈 🔜 🔜 ❎.
|
||||
|
||||
↩️ ⚫️ ❎ 🇧🇿 🏗, 👆 🎲 🚫🔜 💪 🤙 👈 📋 🔗, ✋️ 👆 🔜 🚮 ⚫️ 🔛 👆 `package.json` 📁.
|
||||
|
||||
|
|
@ -75,12 +75,12 @@ $ npm install openapi-typescript-codegen --save-dev
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"openapi-typescript-codegen": "^0.20.1",
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ $ npm install openapi-typescript-codegen --save-dev
|
|||
$ npm run generate-client
|
||||
|
||||
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
|
||||
> openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
```
|
||||
|
||||
</div>
|
||||
|
|
@ -235,12 +235,12 @@ FastAPI ⚙️ **😍 🆔** 🔠 *➡ 🛠️*, ⚫️ ⚙️ **🛠️ 🆔**
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi --input ./openapi.json --output ./src/client --client axios"
|
||||
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"openapi-typescript-codegen": "^0.20.1",
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ sponsors:
|
|||
- - login: bump-sh
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/33217836?v=4
|
||||
url: https://github.com/bump-sh
|
||||
- login: Alek99
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/38776361?u=bd6c163fe787c2de1a26c881598e54b67e2482dd&v=4
|
||||
url: https://github.com/Alek99
|
||||
- login: porter-dev
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62078005?v=4
|
||||
url: https://github.com/porter-dev
|
||||
|
|
@ -14,6 +11,9 @@ sponsors:
|
|||
- login: zanfaruqui
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/104461687?v=4
|
||||
url: https://github.com/zanfaruqui
|
||||
- login: Alek99
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/38776361?u=bd6c163fe787c2de1a26c881598e54b67e2482dd&v=4
|
||||
url: https://github.com/Alek99
|
||||
- login: cryptapi
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/44925437?u=61369138589bc7fee6c417f3fbd50fbd38286cc4&v=4
|
||||
url: https://github.com/cryptapi
|
||||
|
|
@ -56,24 +56,21 @@ sponsors:
|
|||
- login: acsone
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7601056?v=4
|
||||
url: https://github.com/acsone
|
||||
- - login: Trivie
|
||||
- - login: owlur
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/20010787?v=4
|
||||
url: https://github.com/owlur
|
||||
- login: Trivie
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8161763?v=4
|
||||
url: https://github.com/Trivie
|
||||
- - login: americanair
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/12281813?v=4
|
||||
url: https://github.com/americanair
|
||||
- login: 84adam
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13172004?u=293f3cc6bb7e6f6ecfcdd64489a3202468321254&v=4
|
||||
url: https://github.com/84adam
|
||||
- login: CanoaPBC
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/64223768?v=4
|
||||
url: https://github.com/CanoaPBC
|
||||
- login: mainframeindustries
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/55092103?v=4
|
||||
url: https://github.com/mainframeindustries
|
||||
- login: doseiai
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/57115726?v=4
|
||||
url: https://github.com/doseiai
|
||||
- login: AccentDesign
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2429332?v=4
|
||||
url: https://github.com/AccentDesign
|
||||
|
|
@ -104,15 +101,15 @@ sponsors:
|
|||
- login: koconder
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25068?u=582657b23622aaa3dfe68bd028a780f272f456fa&v=4
|
||||
url: https://github.com/koconder
|
||||
- login: b-rad-c
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25362581?u=5bb10629f4015b62bec1f9a366675d5085551af9&v=4
|
||||
url: https://github.com/b-rad-c
|
||||
- login: ehaca
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25950317?u=cec1a3e0643b785288ae8260cc295a85ab344995&v=4
|
||||
url: https://github.com/ehaca
|
||||
- login: timlrx
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/28362229?u=9a745ca31372ee324af682715ae88ce8522f9094&v=4
|
||||
url: https://github.com/timlrx
|
||||
- login: mattmalcher
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31312775?v=4
|
||||
url: https://github.com/mattmalcher
|
||||
- login: Leay15
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32212558?u=c4aa9c1737e515959382a5515381757b1fd86c53&v=4
|
||||
url: https://github.com/Leay15
|
||||
|
|
@ -137,6 +134,12 @@ sponsors:
|
|||
- login: mjohnsey
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16784016?u=38fad2e6b411244560b3af99c5f5a4751bc81865&v=4
|
||||
url: https://github.com/mjohnsey
|
||||
- login: ashi-agrawal
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/17105294?u=99c7a854035e5398d8e7b674f2d42baae6c957f8&v=4
|
||||
url: https://github.com/ashi-agrawal
|
||||
- login: sepsi77
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/18682303?v=4
|
||||
url: https://github.com/sepsi77
|
||||
- login: wedwardbeck
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/19333237?u=1de4ae2bf8d59eb4c013f21d863cbe0f2010575f&v=4
|
||||
url: https://github.com/wedwardbeck
|
||||
|
|
@ -146,9 +149,12 @@ sponsors:
|
|||
- login: Filimoa
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/21352040?u=0be845711495bbd7b756e13fcaeb8efc1ebd78ba&v=4
|
||||
url: https://github.com/Filimoa
|
||||
- login: b-rad-c
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25362581?u=5bb10629f4015b62bec1f9a366675d5085551af9&v=4
|
||||
url: https://github.com/b-rad-c
|
||||
- login: prodhype
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/60444672?u=3f278cff25ea37ead487d7861d4a984795de819e&v=4
|
||||
url: https://github.com/prodhype
|
||||
- login: yakkonaut
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/60633704?u=90a71fd631aa998ba4a96480788f017c9904e07b&v=4
|
||||
url: https://github.com/yakkonaut
|
||||
- login: patsatsia
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/61111267?u=3271b85f7a37b479c8d0ae0a235182e83c166edf&v=4
|
||||
url: https://github.com/patsatsia
|
||||
|
|
@ -167,6 +173,9 @@ sponsors:
|
|||
- login: apitally
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/138365043?v=4
|
||||
url: https://github.com/apitally
|
||||
- login: logic-automation
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/144732884?v=4
|
||||
url: https://github.com/logic-automation
|
||||
- login: thenickben
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/40610922?u=1e907d904041b7c91213951a3cb344cd37c14aaf&v=4
|
||||
url: https://github.com/thenickben
|
||||
|
|
@ -179,12 +188,6 @@ sponsors:
|
|||
- login: dudikbender
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/53487583?u=3a57542938ebfd57579a0111db2b297e606d9681&v=4
|
||||
url: https://github.com/dudikbender
|
||||
- login: prodhype
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/60444672?u=3f278cff25ea37ead487d7861d4a984795de819e&v=4
|
||||
url: https://github.com/prodhype
|
||||
- login: yakkonaut
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/60633704?u=90a71fd631aa998ba4a96480788f017c9904e07b&v=4
|
||||
url: https://github.com/yakkonaut
|
||||
- login: tcsmith
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/989034?u=7d8d741552b3279e8f4d3878679823a705a46f8f&v=4
|
||||
url: https://github.com/tcsmith
|
||||
|
|
@ -233,9 +236,6 @@ sponsors:
|
|||
- login: mintuhouse
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/769950?u=ecfbd79a97d33177e0d093ddb088283cf7fe8444&v=4
|
||||
url: https://github.com/mintuhouse
|
||||
- login: simw
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/6322526?v=4
|
||||
url: https://github.com/simw
|
||||
- login: Rehket
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7015688?u=3afb0ba200feebbc7f958950e92db34df2a3c172&v=4
|
||||
url: https://github.com/Rehket
|
||||
|
|
@ -248,9 +248,9 @@ sponsors:
|
|||
- login: wdwinslow
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11562137?u=dc01daafb354135603a263729e3d26d939c0c452&v=4
|
||||
url: https://github.com/wdwinslow
|
||||
- login: drcat101
|
||||
- login: catherinenelson1
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11951946?u=e714b957185b8cf3d301cced7fc3ad2842122c6a&v=4
|
||||
url: https://github.com/drcat101
|
||||
url: https://github.com/catherinenelson1
|
||||
- login: zsinx6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3532625?u=ba75a5dc744d1116ccfeaaf30d41cb2fe81fe8dd&v=4
|
||||
url: https://github.com/zsinx6
|
||||
|
|
@ -276,23 +276,26 @@ sponsors:
|
|||
avatarUrl: https://avatars.githubusercontent.com/u/5300907?u=5b5452725ddb391b2caaebf34e05aba873591c3a&v=4
|
||||
url: https://github.com/ennui93
|
||||
- login: ternaus
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5481618?u=fabc8d75c921b3380126adb5a931c5da6e7db04f&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5481618?u=513a26b02a39e7a28d587cd37c6cc877ea368e6e&v=4
|
||||
url: https://github.com/ternaus
|
||||
- login: eseglem
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5920492?u=208d419cf667b8ac594c82a8db01932c7e50d057&v=4
|
||||
url: https://github.com/eseglem
|
||||
- login: Yaleesa
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/6135475?v=4
|
||||
url: https://github.com/Yaleesa
|
||||
- login: FernandoCelmer
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/6262214?u=d29fff3fd862fda4ca752079f13f32e84c762ea4&v=4
|
||||
url: https://github.com/FernandoCelmer
|
||||
- login: simw
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/6322526?v=4
|
||||
url: https://github.com/simw
|
||||
- - login: getsentry
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1396951?v=4
|
||||
url: https://github.com/getsentry
|
||||
- - login: pawamoy
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3999221?u=b030e4c89df2f3a36bc4710b925bdeb6745c9856&v=4
|
||||
url: https://github.com/pawamoy
|
||||
- login: nisutec
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25281462?u=e562484c451fdfc59053163f64405f8eb262b8b0&v=4
|
||||
url: https://github.com/nisutec
|
||||
- login: hoenie-ams
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25708487?u=cda07434f0509ac728d9edf5e681117c0f6b818b&v=4
|
||||
url: https://github.com/hoenie-ams
|
||||
|
|
@ -302,9 +305,6 @@ sponsors:
|
|||
- login: rlnchow
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/28018479?u=a93ca9cf1422b9ece155784a72d5f2fdbce7adff&v=4
|
||||
url: https://github.com/rlnchow
|
||||
- login: HosamAlmoghraby
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32025281?u=aa1b09feabccbf9dc506b81c71155f32d126cefa&v=4
|
||||
url: https://github.com/HosamAlmoghraby
|
||||
- login: dvlpjrs
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32254642?u=fbd6ad0324d4f1eb6231cf775be1c7bd4404e961&v=4
|
||||
url: https://github.com/dvlpjrs
|
||||
|
|
@ -314,9 +314,9 @@ sponsors:
|
|||
- login: bnkc
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/34930566?u=fa1dc8db3e920cf5c5636b97180a6f811fa01aaf&v=4
|
||||
url: https://github.com/bnkc
|
||||
- login: curegit
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/37978051?u=1733c322079118c0cdc573c03d92813f50a9faec&v=4
|
||||
url: https://github.com/curegit
|
||||
- login: petercool
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/37613029?u=81c525232bb35780945a68e88afd96bb2cdad9c4&v=4
|
||||
url: https://github.com/petercool
|
||||
- login: JimFawkes
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/12075115?u=dc58ecfd064d72887c34bf500ddfd52592509acd&v=4
|
||||
url: https://github.com/JimFawkes
|
||||
|
|
@ -341,12 +341,6 @@ sponsors:
|
|||
- login: SebTota
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25122511?v=4
|
||||
url: https://github.com/SebTota
|
||||
- login: nisutec
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25281462?u=e562484c451fdfc59053163f64405f8eb262b8b0&v=4
|
||||
url: https://github.com/nisutec
|
||||
- login: 0417taehyun
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/63915557?u=47debaa860fd52c9b98c97ef357ddcec3b3fb399&v=4
|
||||
url: https://github.com/0417taehyun
|
||||
- login: fernandosmither
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/66154723?u=a76a037b5d674938a75d2cff862fb6dfd63ec214&v=4
|
||||
url: https://github.com/fernandosmither
|
||||
|
|
@ -356,18 +350,15 @@ sponsors:
|
|||
- login: PelicanQ
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/77930606?v=4
|
||||
url: https://github.com/PelicanQ
|
||||
- login: tim-habitat
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/86600518?u=7389dd77fe6a0eb8d13933356120b7d2b32d7bb4&v=4
|
||||
url: https://github.com/tim-habitat
|
||||
- login: jugeeem
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/116043716?u=ae590d79c38ac79c91b9c5caa6887d061e865a3d&v=4
|
||||
url: https://github.com/jugeeem
|
||||
- login: tahmarrrr23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/138208610?u=465a46b0ff72a74252d3e3a71ac7d2f1919cda28&v=4
|
||||
url: https://github.com/tahmarrrr23
|
||||
- login: lukzmu
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/155924127?u=2e52e40b3134bef370b52bf2e40a524f307c2a05&v=4
|
||||
url: https://github.com/lukzmu
|
||||
- login: curegit
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/37978051?u=1733c322079118c0cdc573c03d92813f50a9faec&v=4
|
||||
url: https://github.com/curegit
|
||||
- login: kristiangronberg
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/42678548?v=4
|
||||
url: https://github.com/kristiangronberg
|
||||
|
|
@ -377,9 +368,6 @@ sponsors:
|
|||
- login: arrrrrmin
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43553423?u=36a3880a6eb29309c19e6cadbb173bafbe91deb1&v=4
|
||||
url: https://github.com/arrrrrmin
|
||||
- login: rbtrsv
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43938206?u=09955f324da271485a7edaf9241f449e88a1388a&v=4
|
||||
url: https://github.com/rbtrsv
|
||||
- login: mobyw
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/44370805?v=4
|
||||
url: https://github.com/mobyw
|
||||
|
|
@ -452,6 +440,9 @@ sponsors:
|
|||
- login: moonape1226
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8532038?u=d9f8b855a429fff9397c3833c2ff83849ebf989d&v=4
|
||||
url: https://github.com/moonape1226
|
||||
- login: msehnout
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9369632?u=8c988f1b008a3f601385a3616f9327820f66e3a5&v=4
|
||||
url: https://github.com/msehnout
|
||||
- login: xncbf
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9462045?u=ee91e210ae93b9cdd8f248b21cb028316cc0b747&v=4
|
||||
url: https://github.com/xncbf
|
||||
|
|
@ -489,7 +480,7 @@ sponsors:
|
|||
avatarUrl: https://avatars.githubusercontent.com/u/5250987?u=4ed9a120c89805a8aefda1cbdc0cf6512e64d1b4&v=4
|
||||
url: https://github.com/sdevkota
|
||||
- login: brizzbuzz
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5607577?u=1ffbf39f5bb8736b75c0d235707d6e8f803725c5&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5607577?u=58d5aae33bc97e52f11f334d2702e8710314b5c1&v=4
|
||||
url: https://github.com/brizzbuzz
|
||||
- login: Baghdady92
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5708590?v=4
|
||||
|
|
@ -497,7 +488,10 @@ sponsors:
|
|||
- login: jakeecolution
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/5884696?u=4a7c7883fb064b593b50cb6697b54687e6f7aafe&v=4
|
||||
url: https://github.com/jakeecolution
|
||||
- - login: danburonline
|
||||
- - login: abizovnuralem
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/33475993?u=6ce72b11a16a8232d3dd1f958f460b4735f520d8&v=4
|
||||
url: https://github.com/abizovnuralem
|
||||
- login: danburonline
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/34251194?u=94935cccfbec58083ab1e535212d54f1bf2c978a&v=4
|
||||
url: https://github.com/danburonline
|
||||
- login: sadikkuzu
|
||||
|
|
@ -506,6 +500,12 @@ sponsors:
|
|||
- login: rwxd
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/40308458?u=cd04a39e3655923be4f25c2ba8a5a07b3da3230a&v=4
|
||||
url: https://github.com/rwxd
|
||||
- login: YungBricoCoop
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/42273436?u=80470b400c416d1eabc2cc71b1efffc0e3503146&v=4
|
||||
url: https://github.com/YungBricoCoop
|
||||
- login: nlazaro
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/44237350?u=939a570fc965d93e9db1284b5acc173c1a0be4a0&v=4
|
||||
url: https://github.com/nlazaro
|
||||
- login: Patechoc
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2376641?u=23b49e9eda04f078cb74fa3f93593aa6a57bb138&v=4
|
||||
url: https://github.com/Patechoc
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
maintainers:
|
||||
- login: tiangolo
|
||||
answers: 1878
|
||||
prs: 550
|
||||
prs: 559
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1326112?u=740f11212a731f56798f558ceddb0bd07642afa7&v=4
|
||||
url: https://github.com/tiangolo
|
||||
experts:
|
||||
- login: Kludex
|
||||
count: 596
|
||||
count: 598
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: dmontagu
|
||||
|
|
@ -14,7 +14,7 @@ experts:
|
|||
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
|
||||
url: https://github.com/dmontagu
|
||||
- login: jgould22
|
||||
count: 232
|
||||
count: 235
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
|
||||
url: https://github.com/jgould22
|
||||
- login: Mause
|
||||
|
|
@ -23,7 +23,7 @@ experts:
|
|||
url: https://github.com/Mause
|
||||
- login: ycd
|
||||
count: 217
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=45aa6ef58220a3f1e8a3c3cd5f1b75a1a0a73347&v=4
|
||||
url: https://github.com/ycd
|
||||
- login: JarroVGIT
|
||||
count: 193
|
||||
|
|
@ -58,7 +58,7 @@ experts:
|
|||
avatarUrl: https://avatars.githubusercontent.com/u/653031?u=ad9838e089058c9e5a0bab94c0eec7cc181e0cd0&v=4
|
||||
url: https://github.com/falkben
|
||||
- login: JavierSanchezCastro
|
||||
count: 52
|
||||
count: 55
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
|
||||
url: https://github.com/JavierSanchezCastro
|
||||
- login: n8sty
|
||||
|
|
@ -77,10 +77,6 @@ experts:
|
|||
count: 47
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
|
||||
url: https://github.com/acidjunk
|
||||
- login: Dustyposa
|
||||
count: 45
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
|
||||
url: https://github.com/Dustyposa
|
||||
- login: adriangb
|
||||
count: 45
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
|
||||
|
|
@ -89,6 +85,14 @@ experts:
|
|||
count: 45
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16958893?u=f8be7088d5076d963984a21f95f44e559192d912&v=4
|
||||
url: https://github.com/insomnes
|
||||
- login: Dustyposa
|
||||
count: 45
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/27180793?u=5cf2877f50b3eb2bc55086089a78a36f07042889&v=4
|
||||
url: https://github.com/Dustyposa
|
||||
- login: YuriiMotov
|
||||
count: 43
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: frankie567
|
||||
count: 43
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1144727?u=c159fe047727aedecbbeeaa96a1b03ceb9d39add&v=4
|
||||
|
|
@ -129,10 +133,6 @@ experts:
|
|||
count: 25
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
|
||||
url: https://github.com/wshayes
|
||||
- login: YuriiMotov
|
||||
count: 24
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: acnebs
|
||||
count: 23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9054108?v=4
|
||||
|
|
@ -153,6 +153,10 @@ experts:
|
|||
count: 21
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/51059348?u=5fe59a56e1f2f9ccd8005d71752a8276f133ae1a&v=4
|
||||
url: https://github.com/rafsaf
|
||||
- login: hasansezertasan
|
||||
count: 20
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
|
||||
url: https://github.com/hasansezertasan
|
||||
- login: nsidnev
|
||||
count: 20
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/22559461?u=a9cc3238217e21dc8796a1a500f01b722adb082c&v=4
|
||||
|
|
@ -165,10 +169,6 @@ experts:
|
|||
count: 20
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/565544?v=4
|
||||
url: https://github.com/chris-allnutt
|
||||
- login: hasansezertasan
|
||||
count: 19
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
|
||||
url: https://github.com/hasansezertasan
|
||||
- login: retnikt
|
||||
count: 18
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/24581770?v=4
|
||||
|
|
@ -177,6 +177,10 @@ experts:
|
|||
count: 18
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/22326718?u=31ba446ac290e23e56eea8e4f0c558aaf0b40779&v=4
|
||||
url: https://github.com/zoliknemet
|
||||
- login: nkhitrov
|
||||
count: 17
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4
|
||||
url: https://github.com/nkhitrov
|
||||
- login: Hultner
|
||||
count: 17
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2669034?u=115e53df959309898ad8dc9443fbb35fee71df07&v=4
|
||||
|
|
@ -185,10 +189,6 @@ experts:
|
|||
count: 17
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1765494?u=5b1ab7c582db4b4016fa31affe977d10af108ad4&v=4
|
||||
url: https://github.com/harunyasar
|
||||
- login: nkhitrov
|
||||
count: 17
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/28262306?u=66ee21316275ef356081c2efc4ed7a4572e690dc&v=4
|
||||
url: https://github.com/nkhitrov
|
||||
- login: caeser1996
|
||||
count: 17
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16540232?u=05d2beb8e034d584d0a374b99d8826327bd7f614&v=4
|
||||
|
|
@ -199,41 +199,41 @@ experts:
|
|||
url: https://github.com/jonatasoli
|
||||
last_month_experts:
|
||||
- login: YuriiMotov
|
||||
count: 24
|
||||
count: 40
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: Kludex
|
||||
count: 17
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: JavierSanchezCastro
|
||||
count: 13
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
|
||||
url: https://github.com/JavierSanchezCastro
|
||||
- login: Kludex
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: jgould22
|
||||
count: 11
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
|
||||
url: https://github.com/jgould22
|
||||
- login: GodMoonGoodman
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/29688727?u=7b251da620d999644c37c1feeb292d033eed7ad6&v=4
|
||||
url: https://github.com/GodMoonGoodman
|
||||
- login: n8sty
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
|
||||
url: https://github.com/n8sty
|
||||
- login: flo-at
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/564288?v=4
|
||||
url: https://github.com/flo-at
|
||||
- login: estebanx64
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
|
||||
url: https://github.com/estebanx64
|
||||
- login: ahmedabdou14
|
||||
- login: omarcruzpantoja
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
|
||||
url: https://github.com/omarcruzpantoja
|
||||
- login: pythonweb2
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
|
||||
url: https://github.com/ahmedabdou14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
|
||||
url: https://github.com/pythonweb2
|
||||
- login: PhysicallyActive
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
|
||||
url: https://github.com/PhysicallyActive
|
||||
- login: VatsalJagani
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/20964366?u=43552644be05c9107c029e26d5ab3be5a1920f45&v=4
|
||||
url: https://github.com/VatsalJagani
|
||||
- login: khaledadrani
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/45245894?u=49ed5056426a149a5af29d385d8bd3847101d3a4&v=4
|
||||
url: https://github.com/khaledadrani
|
||||
- login: chrisK824
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
|
||||
|
|
@ -242,41 +242,33 @@ last_month_experts:
|
|||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/50728601?u=167c0bd655e52817082e50979a86d2f98f95b1a3&v=4
|
||||
url: https://github.com/ThirVondukr
|
||||
- login: richin13
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8370058?u=8e37a4cdbc78983a5f4b4847f6d1879fb39c851c&v=4
|
||||
url: https://github.com/richin13
|
||||
- login: hussein-awala
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/21311487?u=cbbc60943d3fedfb869e49b604020a821f589659&v=4
|
||||
url: https://github.com/hussein-awala
|
||||
- login: admo1
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14835916?v=4
|
||||
url: https://github.com/admo1
|
||||
three_months_experts:
|
||||
- login: Kludex
|
||||
count: 90
|
||||
count: 84
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: JavierSanchezCastro
|
||||
count: 28
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
|
||||
url: https://github.com/JavierSanchezCastro
|
||||
- login: jgould22
|
||||
count: 28
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
|
||||
url: https://github.com/jgould22
|
||||
- login: YuriiMotov
|
||||
count: 24
|
||||
count: 43
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: jgould22
|
||||
count: 30
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
|
||||
url: https://github.com/jgould22
|
||||
- login: JavierSanchezCastro
|
||||
count: 24
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
|
||||
url: https://github.com/JavierSanchezCastro
|
||||
- login: n8sty
|
||||
count: 12
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
|
||||
url: https://github.com/n8sty
|
||||
- login: hasansezertasan
|
||||
count: 12
|
||||
count: 8
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
|
||||
url: https://github.com/hasansezertasan
|
||||
- login: dolfinus
|
||||
|
|
@ -287,14 +279,6 @@ three_months_experts:
|
|||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2835374?u=3c3ed29aa8b09ccaf8d66def0ce82bc2f7e5aab6&v=4
|
||||
url: https://github.com/aanchlia
|
||||
- login: Ventura94
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43103937?u=ccb837005aaf212a449c374618c4339089e2f733&v=4
|
||||
url: https://github.com/Ventura94
|
||||
- login: shashstormer
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/90090313?v=4
|
||||
url: https://github.com/shashstormer
|
||||
- login: GodMoonGoodman
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/29688727?u=7b251da620d999644c37c1feeb292d033eed7ad6&v=4
|
||||
|
|
@ -307,6 +291,14 @@ three_months_experts:
|
|||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
|
||||
url: https://github.com/estebanx64
|
||||
- login: omarcruzpantoja
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
|
||||
url: https://github.com/omarcruzpantoja
|
||||
- login: fmelihh
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=f76c4460556e41a59eb624acd0cf6e342d660700&v=4
|
||||
url: https://github.com/fmelihh
|
||||
- login: ahmedabdou14
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
|
||||
|
|
@ -315,10 +307,26 @@ three_months_experts:
|
|||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
|
||||
url: https://github.com/chrisK824
|
||||
- login: fmelihh
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=f76c4460556e41a59eb624acd0cf6e342d660700&v=4
|
||||
url: https://github.com/fmelihh
|
||||
- login: pythonweb2
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
|
||||
url: https://github.com/pythonweb2
|
||||
- login: PhysicallyActive
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
|
||||
url: https://github.com/PhysicallyActive
|
||||
- login: richin13
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8370058?u=8e37a4cdbc78983a5f4b4847f6d1879fb39c851c&v=4
|
||||
url: https://github.com/richin13
|
||||
- login: VatsalJagani
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/20964366?u=43552644be05c9107c029e26d5ab3be5a1920f45&v=4
|
||||
url: https://github.com/VatsalJagani
|
||||
- login: khaledadrani
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/45245894?u=49ed5056426a149a5af29d385d8bd3847101d3a4&v=4
|
||||
url: https://github.com/khaledadrani
|
||||
- login: acidjunk
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
|
||||
|
|
@ -331,10 +339,6 @@ three_months_experts:
|
|||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/50728601?u=167c0bd655e52817082e50979a86d2f98f95b1a3&v=4
|
||||
url: https://github.com/ThirVondukr
|
||||
- login: richin13
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8370058?u=8e37a4cdbc78983a5f4b4847f6d1879fb39c851c&v=4
|
||||
url: https://github.com/richin13
|
||||
- login: hussein-awala
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/21311487?u=cbbc60943d3fedfb869e49b604020a821f589659&v=4
|
||||
|
|
@ -375,10 +379,6 @@ three_months_experts:
|
|||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/78362619?u=fe6e8d05f94d8d4c0679a4da943955a686f96177&v=4
|
||||
url: https://github.com/DJoepie
|
||||
- login: alex-pobeditel-2004
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14791483?v=4
|
||||
url: https://github.com/alex-pobeditel-2004
|
||||
- login: binbjz
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8213913?u=22b68b7a0d5bf5e09c02084c0f5f53d7503114cd&v=4
|
||||
|
|
@ -391,14 +391,6 @@ three_months_experts:
|
|||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7178184?v=4
|
||||
url: https://github.com/TarasKuzyo
|
||||
- login: kiraware
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/117554978?v=4
|
||||
url: https://github.com/kiraware
|
||||
- login: iudeen
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
|
||||
url: https://github.com/iudeen
|
||||
- login: msehnout
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9369632?u=8c988f1b008a3f601385a3616f9327820f66e3a5&v=4
|
||||
|
|
@ -415,43 +407,39 @@ three_months_experts:
|
|||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8787120?u=7028d2b3a2a26534c1806eb76c7425a3fac9732f&v=4
|
||||
url: https://github.com/garg10may
|
||||
- login: taegyunum
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16094650?v=4
|
||||
url: https://github.com/taegyunum
|
||||
six_months_experts:
|
||||
- login: Kludex
|
||||
count: 112
|
||||
count: 108
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: jgould22
|
||||
count: 66
|
||||
count: 67
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
|
||||
url: https://github.com/jgould22
|
||||
- login: JavierSanchezCastro
|
||||
count: 32
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
|
||||
url: https://github.com/JavierSanchezCastro
|
||||
- login: YuriiMotov
|
||||
count: 24
|
||||
count: 43
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: JavierSanchezCastro
|
||||
count: 35
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
|
||||
url: https://github.com/JavierSanchezCastro
|
||||
- login: n8sty
|
||||
count: 24
|
||||
count: 21
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
|
||||
url: https://github.com/n8sty
|
||||
- login: hasansezertasan
|
||||
count: 19
|
||||
count: 20
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
|
||||
url: https://github.com/hasansezertasan
|
||||
- login: WilliamStam
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/182800?v=4
|
||||
url: https://github.com/WilliamStam
|
||||
- login: iudeen
|
||||
- login: pythonweb2
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
|
||||
url: https://github.com/iudeen
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
|
||||
url: https://github.com/pythonweb2
|
||||
- login: dolfinus
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=a51b39001a2e5e7529b45826980becf786de2327&v=4
|
||||
|
|
@ -464,22 +452,14 @@ six_months_experts:
|
|||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/43103937?u=ccb837005aaf212a449c374618c4339089e2f733&v=4
|
||||
url: https://github.com/Ventura94
|
||||
- login: nymous
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
|
||||
url: https://github.com/nymous
|
||||
- login: White-Mask
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31826970?u=8625355dc25ddf9c85a8b2b0b9932826c4c8f44c&v=4
|
||||
url: https://github.com/White-Mask
|
||||
- login: chrisK824
|
||||
- login: nymous
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
|
||||
url: https://github.com/chrisK824
|
||||
- login: alex-pobeditel-2004
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14791483?v=4
|
||||
url: https://github.com/alex-pobeditel-2004
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
|
||||
url: https://github.com/nymous
|
||||
- login: shashstormer
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/90090313?v=4
|
||||
|
|
@ -496,26 +476,30 @@ six_months_experts:
|
|||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/564288?v=4
|
||||
url: https://github.com/flo-at
|
||||
- login: ebottos94
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
|
||||
url: https://github.com/ebottos94
|
||||
- login: estebanx64
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10840422?u=45f015f95e1c0f06df602be4ab688d4b854cc8a8&v=4
|
||||
url: https://github.com/estebanx64
|
||||
- login: pythonweb2
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
|
||||
url: https://github.com/pythonweb2
|
||||
- login: ahmedabdou14
|
||||
- login: omarcruzpantoja
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
|
||||
url: https://github.com/ahmedabdou14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
|
||||
url: https://github.com/omarcruzpantoja
|
||||
- login: fmelihh
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=f76c4460556e41a59eb624acd0cf6e342d660700&v=4
|
||||
url: https://github.com/fmelihh
|
||||
- login: ahmedabdou14
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
|
||||
url: https://github.com/ahmedabdou14
|
||||
- login: chrisK824
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
|
||||
url: https://github.com/chrisK824
|
||||
- login: ebottos94
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
|
||||
url: https://github.com/ebottos94
|
||||
- login: binbjz
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8213913?u=22b68b7a0d5bf5e09c02084c0f5f53d7503114cd&v=4
|
||||
|
|
@ -524,18 +508,10 @@ six_months_experts:
|
|||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16098190?u=dc70db88a7a99b764c9a89a6e471e0b7ca478a35&v=4
|
||||
url: https://github.com/theobouwman
|
||||
- login: Ryandaydev
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4292423?u=48f68868db8886fce31a1d802c1003914c6cd7c6&v=4
|
||||
url: https://github.com/Ryandaydev
|
||||
- login: sriram-kondakindi
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32274323?v=4
|
||||
url: https://github.com/sriram-kondakindi
|
||||
- login: NeilBotelho
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/39030675?u=16fea2ff90a5c67b974744528a38832a6d1bb4f7&v=4
|
||||
url: https://github.com/NeilBotelho
|
||||
- login: yinziyan1206
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
|
||||
|
|
@ -544,14 +520,54 @@ six_months_experts:
|
|||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/48502122?u=89fe3e55f3cfd15d34ffac239b32af358cca6481&v=4
|
||||
url: https://github.com/pcorvoh
|
||||
- login: osangu
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/80697064?u=de9bae685e2228bffd4e202274e1df1afaf54a0d&v=4
|
||||
url: https://github.com/osangu
|
||||
- login: PhysicallyActive
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/160476156?u=7a8e44f4a43d3bba636f795bb7d9476c9233b4d8&v=4
|
||||
url: https://github.com/PhysicallyActive
|
||||
- login: richin13
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8370058?u=8e37a4cdbc78983a5f4b4847f6d1879fb39c851c&v=4
|
||||
url: https://github.com/richin13
|
||||
- login: amacfie
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/889657?u=d70187989940b085bcbfa3bedad8dbc5f3ab1fe7&v=4
|
||||
url: https://github.com/amacfie
|
||||
- login: WSH032
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/126865849?v=4
|
||||
url: https://github.com/WSH032
|
||||
- login: MRigal
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2190327?u=557f399ee90319da7bc4a7d9274e110836b0bd60&v=4
|
||||
url: https://github.com/MRigal
|
||||
- login: VatsalJagani
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/20964366?u=43552644be05c9107c029e26d5ab3be5a1920f45&v=4
|
||||
url: https://github.com/VatsalJagani
|
||||
- login: nameer
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3931725?u=6199fb065df098fc13ac0a5e649f89672b586732&v=4
|
||||
url: https://github.com/nameer
|
||||
- login: kiraware
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/117554978?v=4
|
||||
url: https://github.com/kiraware
|
||||
- login: iudeen
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
|
||||
url: https://github.com/iudeen
|
||||
- login: khaledadrani
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/45245894?u=49ed5056426a149a5af29d385d8bd3847101d3a4&v=4
|
||||
url: https://github.com/khaledadrani
|
||||
- login: acidjunk
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
|
||||
url: https://github.com/acidjunk
|
||||
- login: shashiwtt
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/87797476?v=4
|
||||
url: https://github.com/shashiwtt
|
||||
- login: yavuzakyazici
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/148442912?u=1d2d150172c53daf82020b950c6483a6c6a77b7e&v=4
|
||||
|
|
@ -568,10 +584,6 @@ six_months_experts:
|
|||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/50728601?u=167c0bd655e52817082e50979a86d2f98f95b1a3&v=4
|
||||
url: https://github.com/ThirVondukr
|
||||
- login: richin13
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8370058?u=8e37a4cdbc78983a5f4b4847f6d1879fb39c851c&v=4
|
||||
url: https://github.com/richin13
|
||||
- login: hussein-awala
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/21311487?u=cbbc60943d3fedfb869e49b604020a821f589659&v=4
|
||||
|
|
@ -580,10 +592,6 @@ six_months_experts:
|
|||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/996689?v=4
|
||||
url: https://github.com/jcphlux
|
||||
- login: Matthieu-LAURENT39
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/91389613?v=4
|
||||
url: https://github.com/Matthieu-LAURENT39
|
||||
- login: bhumkong
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13270137?u=1490432e6a0184fbc3d5c8d1b5df553ca92e7e5b&v=4
|
||||
|
|
@ -596,95 +604,79 @@ six_months_experts:
|
|||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1032980?u=722c96b0a234752df23f04df150ef36441ceb43c&v=4
|
||||
url: https://github.com/mielvds
|
||||
- login: admo1
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/14835916?v=4
|
||||
url: https://github.com/admo1
|
||||
- login: pbasista
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1535892?u=e9a8bd5b3b2f95340cfeb4bc97886e9334911669&v=4
|
||||
url: https://github.com/pbasista
|
||||
- login: osangu
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/80697064?u=de9bae685e2228bffd4e202274e1df1afaf54a0d&v=4
|
||||
url: https://github.com/osangu
|
||||
- login: bogdan-coman-uv
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/92912507?v=4
|
||||
url: https://github.com/bogdan-coman-uv
|
||||
- login: leonidktoto
|
||||
count: 2
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/159561986?v=4
|
||||
url: https://github.com/leonidktoto
|
||||
one_year_experts:
|
||||
- login: Kludex
|
||||
count: 231
|
||||
count: 218
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: jgould22
|
||||
count: 132
|
||||
count: 133
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4335847?u=ed77f67e0bb069084639b24d812dbb2a2b1dc554&v=4
|
||||
url: https://github.com/jgould22
|
||||
- login: JavierSanchezCastro
|
||||
count: 52
|
||||
count: 55
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72013291?u=ae5679e6bd971d9d98cd5e76e8683f83642ba950&v=4
|
||||
url: https://github.com/JavierSanchezCastro
|
||||
- login: n8sty
|
||||
count: 39
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
|
||||
url: https://github.com/n8sty
|
||||
- login: YuriiMotov
|
||||
count: 24
|
||||
count: 43
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: n8sty
|
||||
count: 37
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/2964996?v=4
|
||||
url: https://github.com/n8sty
|
||||
- login: chrisK824
|
||||
count: 22
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/79946379?u=03d85b22d696a58a9603e55fbbbe2de6b0f4face&v=4
|
||||
url: https://github.com/chrisK824
|
||||
- login: hasansezertasan
|
||||
count: 19
|
||||
count: 20
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/13135006?u=99f0b0f0fc47e88e8abb337b4447357939ef93e7&v=4
|
||||
url: https://github.com/hasansezertasan
|
||||
- login: abhint
|
||||
count: 14
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4
|
||||
url: https://github.com/abhint
|
||||
- login: ahmedabdou14
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
|
||||
url: https://github.com/ahmedabdou14
|
||||
- login: nymous
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4216559?u=360a36fb602cded27273cbfc0afc296eece90662&v=4
|
||||
url: https://github.com/nymous
|
||||
- login: iudeen
|
||||
- login: ahmedabdou14
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/104530599?u=05365b155a1ff911532e8be316acfad2e0736f98&v=4
|
||||
url: https://github.com/ahmedabdou14
|
||||
- login: abhint
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
|
||||
url: https://github.com/iudeen
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/25699289?u=b5d219277b4d001ac26fb8be357fddd88c29d51b&v=4
|
||||
url: https://github.com/abhint
|
||||
- login: arjwilliams
|
||||
count: 12
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/22227620?v=4
|
||||
url: https://github.com/arjwilliams
|
||||
- login: ebottos94
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
|
||||
url: https://github.com/ebottos94
|
||||
- login: Viicos
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/65306057?u=fcd677dc1b9bef12aa103613e5ccb3f8ce305af9&v=4
|
||||
url: https://github.com/Viicos
|
||||
- login: iudeen
|
||||
count: 11
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10519440?u=2843b3303282bff8b212dcd4d9d6689452e4470c&v=4
|
||||
url: https://github.com/iudeen
|
||||
- login: WilliamStam
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/182800?v=4
|
||||
url: https://github.com/WilliamStam
|
||||
- login: yinziyan1206
|
||||
count: 10
|
||||
count: 9
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/37829370?u=da44ca53aefd5c23f346fab8e9fd2e108294c179&v=4
|
||||
url: https://github.com/yinziyan1206
|
||||
- login: mateoradman
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/48420316?u=066f36b8e8e263b0d90798113b0f291d3266db7c&v=4
|
||||
url: https://github.com/mateoradman
|
||||
- login: Viicos
|
||||
count: 7
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/65306057?u=fcd677dc1b9bef12aa103613e5ccb3f8ce305af9&v=4
|
||||
url: https://github.com/Viicos
|
||||
- login: pythonweb2
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
|
||||
url: https://github.com/pythonweb2
|
||||
- login: ebottos94
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/100039558?u=e2c672da5a7977fd24d87ce6ab35f8bf5b1ed9fa&v=4
|
||||
url: https://github.com/ebottos94
|
||||
- login: dolfinus
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4661021?u=a51b39001a2e5e7529b45826980becf786de2327&v=4
|
||||
|
|
@ -733,14 +725,14 @@ one_year_experts:
|
|||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/52145145?u=f8c9e5c8c259d248e1683fedf5027b4ee08a0967&v=4
|
||||
url: https://github.com/wu-clan
|
||||
- login: adriangb
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
|
||||
url: https://github.com/adriangb
|
||||
- login: 8thgencore
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/30128845?u=a747e840f751a1d196d70d0ecf6d07a530d412a1&v=4
|
||||
url: https://github.com/8thgencore
|
||||
- login: anthonycepeda
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
|
||||
url: https://github.com/anthonycepeda
|
||||
- login: acidjunk
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/685002?u=b5094ab4527fc84b006c0ac9ff54367bdebb2267&v=4
|
||||
|
|
@ -773,53 +765,49 @@ one_year_experts:
|
|||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/977953?u=d94445b7b87b7096a92a2d4b652ca6c560f34039&v=4
|
||||
url: https://github.com/sanzoghenzo
|
||||
- login: hochstibe
|
||||
- login: adriangb
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/48712216?u=1862e0265e06be7ff710f7dc12094250c0616313&v=4
|
||||
url: https://github.com/hochstibe
|
||||
- login: pythonweb2
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/32141163?v=4
|
||||
url: https://github.com/pythonweb2
|
||||
- login: nameer
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3931725?u=6199fb065df098fc13ac0a5e649f89672b586732&v=4
|
||||
url: https://github.com/nameer
|
||||
- login: anthonycepeda
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/72019805?u=60bdf46240cff8fca482ff0fc07d963fd5e1a27c&v=4
|
||||
url: https://github.com/anthonycepeda
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/1755071?u=612704256e38d6ac9cbed24f10e4b6ac2da74ecb&v=4
|
||||
url: https://github.com/adriangb
|
||||
- login: 9en9i
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/44907258?u=297d0f31ea99c22b718118c1deec82001690cadb&v=4
|
||||
url: https://github.com/9en9i
|
||||
- login: AlexanderPodorov
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/54511144?v=4
|
||||
url: https://github.com/AlexanderPodorov
|
||||
- login: sharonyogev
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/31185192?u=b13ea64b3cdaf3903390c555793aba4aff45c5e6&v=4
|
||||
url: https://github.com/sharonyogev
|
||||
- login: fmelihh
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=f76c4460556e41a59eb624acd0cf6e342d660700&v=4
|
||||
url: https://github.com/fmelihh
|
||||
- login: jinluyang
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/15670327?v=4
|
||||
url: https://github.com/jinluyang
|
||||
- login: mht2953658596
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/59814105?v=4
|
||||
url: https://github.com/mht2953658596
|
||||
- login: omarcruzpantoja
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/15116058?u=4b64c643fad49225d854e1aaecd1ffc6f9071a1b&v=4
|
||||
url: https://github.com/omarcruzpantoja
|
||||
- login: fmelihh
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/99879453?u=f76c4460556e41a59eb624acd0cf6e342d660700&v=4
|
||||
url: https://github.com/fmelihh
|
||||
- login: amacfie
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/889657?u=d70187989940b085bcbfa3bedad8dbc5f3ab1fe7&v=4
|
||||
url: https://github.com/amacfie
|
||||
- login: nameer
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/3931725?u=6199fb065df098fc13ac0a5e649f89672b586732&v=4
|
||||
url: https://github.com/nameer
|
||||
- login: hhartzer
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/100533792?v=4
|
||||
url: https://github.com/hhartzer
|
||||
- login: binbjz
|
||||
count: 3
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/8213913?u=22b68b7a0d5bf5e09c02084c0f5f53d7503114cd&v=4
|
||||
url: https://github.com/binbjz
|
||||
top_contributors:
|
||||
- login: nilslindemann
|
||||
count: 30
|
||||
count: 127
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/6892179?u=1dca6a22195d6cd1ab20737c0e19a4c55d639472&v=4
|
||||
url: https://github.com/nilslindemann
|
||||
- login: jaystone776
|
||||
count: 27
|
||||
count: 49
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11191137?u=299205a95e9b6817a43144a48b643346a5aac5cc&v=4
|
||||
url: https://github.com/jaystone776
|
||||
- login: waynerv
|
||||
|
|
@ -827,7 +815,7 @@ top_contributors:
|
|||
avatarUrl: https://avatars.githubusercontent.com/u/39515546?u=ec35139777597cdbbbddda29bf8b9d4396b429a9&v=4
|
||||
url: https://github.com/waynerv
|
||||
- login: tokusumi
|
||||
count: 23
|
||||
count: 24
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
|
||||
url: https://github.com/tokusumi
|
||||
- login: Kludex
|
||||
|
|
@ -870,6 +858,10 @@ top_contributors:
|
|||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
|
||||
url: https://github.com/hard-coders
|
||||
- login: alejsdev
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=1ee3a9fbef27abc9448ef5951350f99c7d76f7af&v=4
|
||||
url: https://github.com/alejsdev
|
||||
- login: KaniKim
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/19832624?u=d8ff6fca8542d22f94388cd2c4292e76e3898584&v=4
|
||||
|
|
@ -906,10 +898,6 @@ top_contributors:
|
|||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/33462923?u=0fb3d7acb316764616f11e4947faf080e49ad8d9&v=4
|
||||
url: https://github.com/batlopes
|
||||
- login: alejsdev
|
||||
count: 6
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/90076947?u=1ee3a9fbef27abc9448ef5951350f99c7d76f7af&v=4
|
||||
url: https://github.com/alejsdev
|
||||
- login: wshayes
|
||||
count: 5
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/365303?u=07ca03c5ee811eb0920e633cc3c3db73dbec1aa5&v=4
|
||||
|
|
@ -944,7 +932,7 @@ top_contributors:
|
|||
url: https://github.com/jfunez
|
||||
- login: ycd
|
||||
count: 4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=45aa6ef58220a3f1e8a3c3cd5f1b75a1a0a73347&v=4
|
||||
url: https://github.com/ycd
|
||||
- login: komtaki
|
||||
count: 4
|
||||
|
|
@ -1000,7 +988,7 @@ top_contributors:
|
|||
url: https://github.com/pawamoy
|
||||
top_reviewers:
|
||||
- login: Kludex
|
||||
count: 155
|
||||
count: 156
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/7353520?u=62adc405ef418f4b6c8caa93d3eb8ab107bc4927&v=4
|
||||
url: https://github.com/Kludex
|
||||
- login: BilalAlpaslan
|
||||
|
|
@ -1033,7 +1021,7 @@ top_reviewers:
|
|||
url: https://github.com/Laineyzhang55
|
||||
- login: ycd
|
||||
count: 45
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=45aa6ef58220a3f1e8a3c3cd5f1b75a1a0a73347&v=4
|
||||
url: https://github.com/ycd
|
||||
- login: cikay
|
||||
count: 41
|
||||
|
|
@ -1079,6 +1067,10 @@ top_reviewers:
|
|||
count: 24
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/16273730?u=095b66f243a2cd6a0aadba9a095009f8aaf18393&v=4
|
||||
url: https://github.com/LorhanSohaky
|
||||
- login: YuriiMotov
|
||||
count: 24
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: dmontagu
|
||||
count: 23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/35119617?u=540f30c937a6450812628b9592a1dfe91bbe148e&v=4
|
||||
|
|
@ -1091,10 +1083,6 @@ top_reviewers:
|
|||
count: 23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/9651103?u=95db33927bbff1ed1c07efddeb97ac2ff33068ed&v=4
|
||||
url: https://github.com/hard-coders
|
||||
- login: YuriiMotov
|
||||
count: 23
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/109919500?u=e83a39697a2d33ab2ec9bfbced794ee48bc29cec&v=4
|
||||
url: https://github.com/YuriiMotov
|
||||
- login: rjNemo
|
||||
count: 21
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/56785022?u=d5c3a02567c8649e146fcfc51b6060ccaf8adef8&v=4
|
||||
|
|
@ -1155,6 +1143,10 @@ top_reviewers:
|
|||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/6478810?u=af15d724875cec682ed8088a86d36b2798f981c0&v=4
|
||||
url: https://github.com/sh0nk
|
||||
- login: junah201
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/75025529?u=2451c256e888fa2a06bcfc0646d09b87ddb6a945&v=4
|
||||
url: https://github.com/junah201
|
||||
- login: wdh99
|
||||
count: 13
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/108172295?u=8a8fb95d5afe3e0fa33257b2aecae88d436249eb&v=4
|
||||
|
|
@ -1191,19 +1183,15 @@ top_reviewers:
|
|||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/11489395?u=4adb6986bf3debfc2b8216ae701f2bd47d73da7d&v=4
|
||||
url: https://github.com/mariacamilagl
|
||||
- login: raphaelauv
|
||||
count: 10
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/10202690?u=e6f86f5c0c3026a15d6b51792fa3e532b12f1371&v=4
|
||||
url: https://github.com/raphaelauv
|
||||
top_translations_reviewers:
|
||||
- login: s111d
|
||||
count: 143
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4954856?v=4
|
||||
url: https://github.com/s111d
|
||||
- login: Xewus
|
||||
count: 128
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/85196001?u=f8e2dc7e5104f109cef944af79050ea8d1b8f914&v=4
|
||||
url: https://github.com/Xewus
|
||||
- login: s111d
|
||||
count: 122
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/4954856?v=4
|
||||
url: https://github.com/s111d
|
||||
- login: tokusumi
|
||||
count: 104
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/41147016?u=55010621aece725aa702270b54fed829b6a1fe60&v=4
|
||||
|
|
@ -1250,7 +1238,7 @@ top_translations_reviewers:
|
|||
url: https://github.com/solomein-sv
|
||||
- login: alperiox
|
||||
count: 37
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/34214152?u=0688c1dc00988150a82d299106062c062ed1ba13&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/34214152?u=2c5acad3461d4dbc2d48371ba86cac56ae9b25cc&v=4
|
||||
url: https://github.com/alperiox
|
||||
- login: lsglucas
|
||||
count: 36
|
||||
|
|
@ -1342,7 +1330,7 @@ top_translations_reviewers:
|
|||
url: https://github.com/Attsun1031
|
||||
- login: ycd
|
||||
count: 20
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=bba5af018423a2858d49309bed2a899bb5c34ac5&v=4
|
||||
avatarUrl: https://avatars.githubusercontent.com/u/62724709?u=45aa6ef58220a3f1e8a3c3cd5f1b75a1a0a73347&v=4
|
||||
url: https://github.com/ycd
|
||||
- login: delhi09
|
||||
count: 20
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ There are many tools to generate clients from **OpenAPI**.
|
|||
|
||||
A common tool is <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>.
|
||||
|
||||
If you are building a **frontend**, a very interesting alternative is <a href="https://github.com/ferdikoomen/openapi-typescript-codegen" class="external-link" target="_blank">openapi-typescript-codegen</a>.
|
||||
If you are building a **frontend**, a very interesting alternative is <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a>.
|
||||
|
||||
## Client and SDK Generators - Sponsor
|
||||
|
||||
|
|
@ -58,14 +58,14 @@ And that same information from the models that is included in OpenAPI is what ca
|
|||
|
||||
Now that we have the app with the models, we can generate the client code for the frontend.
|
||||
|
||||
#### Install `openapi-typescript-codegen`
|
||||
#### Install `openapi-ts`
|
||||
|
||||
You can install `openapi-typescript-codegen` in your frontend code with:
|
||||
You can install `openapi-ts` in your frontend code with:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm install openapi-typescript-codegen --save-dev
|
||||
$ npm install @hey-api/openapi-ts --save-dev
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
|
@ -74,7 +74,7 @@ $ npm install openapi-typescript-codegen --save-dev
|
|||
|
||||
#### Generate Client Code
|
||||
|
||||
To generate the client code you can use the command line application `openapi` that would now be installed.
|
||||
To generate the client code you can use the command line application `openapi-ts` that would now be installed.
|
||||
|
||||
Because it is installed in the local project, you probably wouldn't be able to call that command directly, but you would put it on your `package.json` file.
|
||||
|
||||
|
|
@ -87,12 +87,12 @@ It could look like this:
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
|
||||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"openapi-typescript-codegen": "^0.20.1",
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -106,7 +106,7 @@ After having that NPM `generate-client` script there, you can run it with:
|
|||
$ npm run generate-client
|
||||
|
||||
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
|
||||
> openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios --useOptions --useUnionTypes
|
||||
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
```
|
||||
|
||||
</div>
|
||||
|
|
@ -254,12 +254,12 @@ Now as the end result is in a file `openapi.json`, you would modify the `package
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi --input ./openapi.json --output ./src/client --client axios --useOptions --useUnionTypes"
|
||||
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"openapi-typescript-codegen": "^0.20.1",
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,44 @@ hide:
|
|||
|
||||
## Latest Changes
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Fix typo in `docs/es/docs/async.md`. PR [#11400](https://github.com/tiangolo/fastapi/pull/11400) by [@fabianfalon](https://github.com/fabianfalon).
|
||||
* 📝 Update OpenAPI client generation docs to use `@hey-api/openapi-ts`. PR [#11339](https://github.com/tiangolo/fastapi/pull/11339) by [@jordanshatford](https://github.com/jordanshatford).
|
||||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/dependencies/dependencies-in-path-operation-decorators.md`. PR [#11411](https://github.com/tiangolo/fastapi/pull/11411) by [@anton2yakovlev](https://github.com/anton2yakovlev).
|
||||
* 🌐 Add Portuguese translations for `learn/index.md` `resources/index.md` `help/index.md` `about/index.md`. PR [#10807](https://github.com/tiangolo/fastapi/pull/10807) by [@nazarepiedady](https://github.com/nazarepiedady).
|
||||
* 🌐 Update Russian translations for deployments docs. PR [#11271](https://github.com/tiangolo/fastapi/pull/11271) by [@Lufa1u](https://github.com/Lufa1u).
|
||||
* 🌐 Add Bengali translations for `docs/bn/docs/python-types.md`. PR [#11376](https://github.com/tiangolo/fastapi/pull/11376) by [@imtiaz101325](https://github.com/imtiaz101325).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/security/simple-oauth2.md`. PR [#5744](https://github.com/tiangolo/fastapi/pull/5744) by [@KdHyeon0661](https://github.com/KdHyeon0661).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/help-fastapi.md`. PR [#4139](https://github.com/tiangolo/fastapi/pull/4139) by [@kty4119](https://github.com/kty4119).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/advanced/events.md`. PR [#5087](https://github.com/tiangolo/fastapi/pull/5087) by [@pers0n4](https://github.com/pers0n4).
|
||||
* 🌐 Add Japanese translation for `docs/ja/docs/tutorial/path-operation-configuration.md`. PR [#1954](https://github.com/tiangolo/fastapi/pull/1954) by [@SwftAlpc](https://github.com/SwftAlpc).
|
||||
* 🌐 Add Japanese translation for `docs/ja/docs/tutorial/request-forms-and-files.md`. PR [#1946](https://github.com/tiangolo/fastapi/pull/1946) by [@SwftAlpc](https://github.com/SwftAlpc).
|
||||
* 🌐 Add Russian translation for `docs/ru/docs/tutorial/dependencies/dependencies-with-yield.md`. PR [#10532](https://github.com/tiangolo/fastapi/pull/10532) by [@AlertRED](https://github.com/AlertRED).
|
||||
* 🌐 Add Korean translation for `docs/ko/docs/tutorial/debugging.md`. PR [#5695](https://github.com/tiangolo/fastapi/pull/5695) by [@JungWooGeon](https://github.com/JungWooGeon).
|
||||
|
||||
### Internal
|
||||
|
||||
* ⬆️ Upgrade version of typer for docs. PR [#11393](https://github.com/tiangolo/fastapi/pull/11393) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
||||
## 0.110.1
|
||||
|
||||
### Fixes
|
||||
|
||||
* 🐛 Fix parameterless `Depends()` with generics. PR [#9479](https://github.com/tiangolo/fastapi/pull/9479) by [@nzig](https://github.com/nzig).
|
||||
|
||||
### Refactors
|
||||
|
||||
* ♻️ Update mypy. PR [#11049](https://github.com/tiangolo/fastapi/pull/11049) by [@k0t3n](https://github.com/k0t3n).
|
||||
* ♻️ Simplify string format with f-strings in `fastapi/applications.py`. PR [#11335](https://github.com/tiangolo/fastapi/pull/11335) by [@igeni](https://github.com/igeni).
|
||||
|
||||
### Upgrades
|
||||
|
||||
* ⬆️ Upgrade Starlette to >=0.37.2,<0.38.0, remove Starlette filterwarning for internal tests. PR [#11266](https://github.com/tiangolo/fastapi/pull/11266) by [@nothielf](https://github.com/nothielf).
|
||||
|
||||
### Docs
|
||||
|
||||
* 📝 Tweak docs and translations links and remove old docs translations. PR [#11381](https://github.com/tiangolo/fastapi/pull/11381) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
|
@ -27,6 +61,7 @@ hide:
|
|||
|
||||
### Translations
|
||||
|
||||
* 🌐 Add German translation for `docs/de/docs/tutorial/response-status-code.md`. PR [#10357](https://github.com/tiangolo/fastapi/pull/10357) by [@nilslindemann](https://github.com/nilslindemann).
|
||||
* 🌐 Update Chinese translation for `docs/zh/docs/tutorial/query-params.md`. PR [#3480](https://github.com/tiangolo/fastapi/pull/3480) by [@jaystone776](https://github.com/jaystone776).
|
||||
* 🌐 Update Chinese translation for `docs/zh/docs/tutorial/body.md`. PR [#3481](https://github.com/tiangolo/fastapi/pull/3481) by [@jaystone776](https://github.com/jaystone776).
|
||||
* 🌐 Update Chinese translation for `docs/zh/docs/tutorial/path-params.md`. PR [#3479](https://github.com/tiangolo/fastapi/pull/3479) by [@jaystone776](https://github.com/jaystone776).
|
||||
|
|
@ -162,6 +197,12 @@ hide:
|
|||
|
||||
### Internal
|
||||
|
||||
* 👥 Update FastAPI People. PR [#11387](https://github.com/tiangolo/fastapi/pull/11387) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ⬆ Bump actions/cache from 3 to 4. PR [#10988](https://github.com/tiangolo/fastapi/pull/10988) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump pypa/gh-action-pypi-publish from 1.8.11 to 1.8.14. PR [#11318](https://github.com/tiangolo/fastapi/pull/11318) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump pillow from 10.1.0 to 10.2.0. PR [#11011](https://github.com/tiangolo/fastapi/pull/11011) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* ⬆ Bump black from 23.3.0 to 24.3.0. PR [#11325](https://github.com/tiangolo/fastapi/pull/11325) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
* 👷 Add cron to run test once a week on monday. PR [#11377](https://github.com/tiangolo/fastapi/pull/11377) by [@estebanx64](https://github.com/estebanx64).
|
||||
* ➕ Replace mkdocs-markdownextradata-plugin with mkdocs-macros-plugin. PR [#11383](https://github.com/tiangolo/fastapi/pull/11383) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Disable MkDocs insiders social plugin while an issue in MkDocs Material is handled. PR [#11373](https://github.com/tiangolo/fastapi/pull/11373) by [@tiangolo](https://github.com/tiangolo).
|
||||
* 👷 Fix logic for when to install and use MkDocs Insiders. PR [#11372](https://github.com/tiangolo/fastapi/pull/11372) by [@tiangolo](https://github.com/tiangolo).
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ Luego, el cajero / cocinero 👨🍳 finalmente regresa con tus hamburguesas
|
|||
|
||||
<img src="https://fastapi.tiangolo.com/img/async/parallel-burgers/parallel-burgers-05.png" alt="illustration">
|
||||
|
||||
Cojes tus hamburguesas 🍔 y vas a la mesa con esa persona 😍.
|
||||
Coges tus hamburguesas 🍔 y vas a la mesa con esa persona 😍.
|
||||
|
||||
Sólo las comes y listo 🍔 ⏹.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
# Path Operationの設定
|
||||
|
||||
*path operationデコレータ*を設定するためのパラメータがいくつかあります。
|
||||
|
||||
!!! warning "注意"
|
||||
これらのパラメータは*path operation関数*ではなく、*path operationデコレータ*に直接渡されることに注意してください。
|
||||
|
||||
## レスポンスステータスコード
|
||||
|
||||
*path operation*のレスポンスで使用する(HTTP)`status_code`を定義することができます。
|
||||
|
||||
`404`のように`int`のコードを直接渡すことができます。
|
||||
|
||||
しかし、それぞれの番号コードが何のためのものか覚えていない場合は、`status`のショートカット定数を使用することができます:
|
||||
|
||||
```Python hl_lines="3 17"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial001.py!}
|
||||
```
|
||||
|
||||
そのステータスコードはレスポンスで使用され、OpenAPIスキーマに追加されます。
|
||||
|
||||
!!! note "技術詳細"
|
||||
また、`from starlette import status`を使用することもできます。
|
||||
|
||||
**FastAPI** は開発者の利便性を考慮して、`fastapi.status`と同じ`starlette.status`を提供しています。しかし、これはStarletteから直接提供されています。
|
||||
|
||||
## タグ
|
||||
|
||||
`tags`パラメータを`str`の`list`(通常は1つの`str`)と一緒に渡すと、*path operation*にタグを追加できます:
|
||||
|
||||
```Python hl_lines="17 22 27"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
これらはOpenAPIスキーマに追加され、自動ドキュメントのインターフェースで使用されます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image01.png">
|
||||
|
||||
## 概要と説明
|
||||
|
||||
`summary`と`description`を追加できます:
|
||||
|
||||
```Python hl_lines="20-21"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
## docstringを用いた説明
|
||||
|
||||
説明文は長くて複数行におよぶ傾向があるので、関数<abbr title="ドキュメントに使用される関数内の最初の式(変数に代入されていない)としての複数行の文字列">docstring</abbr>内に*path operation*の説明文を宣言できます。すると、**FastAPI** は説明文を読み込んでくれます。
|
||||
|
||||
docstringに<a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a>を記述すれば、正しく解釈されて表示されます。(docstringのインデントを考慮して)
|
||||
|
||||
```Python hl_lines="19-27"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial004.py!}
|
||||
```
|
||||
|
||||
これは対話的ドキュメントで使用されます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image02.png">
|
||||
|
||||
## レスポンスの説明
|
||||
|
||||
`response_description`パラメータでレスポンスの説明をすることができます。
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! info "情報"
|
||||
`respnse_description`は具体的にレスポンスを参照し、`description`は*path operation*全般を参照していることに注意してください。
|
||||
|
||||
!!! check "確認"
|
||||
OpenAPIは*path operation*ごとにレスポンスの説明を必要としています。
|
||||
|
||||
そのため、それを提供しない場合は、**FastAPI** が自動的に「成功のレスポンス」を生成します。
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image03.png">
|
||||
|
||||
## 非推奨の*path operation*
|
||||
|
||||
*path operation*を<abbr title="非推奨、使わない方がよい">deprecated</abbr>としてマークする必要があるが、それを削除しない場合は、`deprecated`パラメータを渡します:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial006.py!}
|
||||
```
|
||||
|
||||
対話的ドキュメントでは非推奨と明記されます:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image04.png">
|
||||
|
||||
*path operations*が非推奨である場合とそうでない場合でどのように見えるかを確認してください:
|
||||
|
||||
<img src="https://fastapi.tiangolo.com/img/tutorial/path-operation-configuration/image05.png">
|
||||
|
||||
## まとめ
|
||||
|
||||
*path operationデコレータ*にパラメータを渡すことで、*path operations*のメタデータを簡単に設定・追加することができます。
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# リクエストフォームとファイル
|
||||
|
||||
`File`と`Form`を同時に使うことでファイルとフォームフィールドを定義することができます。
|
||||
|
||||
!!! info "情報"
|
||||
アップロードされたファイルやフォームデータを受信するには、まず<a href="https://andrew-d.github.io/python-multipart/" class="external-link" target="_blank">`python-multipart`</a>をインストールします。
|
||||
|
||||
例えば、`pip install python-multipart`のように。
|
||||
|
||||
## `File`と`Form`のインポート
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
## `File`と`Form`のパラメータの定義
|
||||
|
||||
ファイルやフォームのパラメータは`Body`や`Query`の場合と同じように作成します:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/request_forms_and_files/tutorial001.py!}
|
||||
```
|
||||
|
||||
ファイルとフォームフィールドがフォームデータとしてアップロードされ、ファイルとフォームフィールドを受け取ります。
|
||||
|
||||
また、いくつかのファイルを`bytes`として、いくつかのファイルを`UploadFile`として宣言することができます。
|
||||
|
||||
!!! warning "注意"
|
||||
*path operation*で複数の`File`と`Form`パラメータを宣言することができますが、JSONとして受け取ることを期待している`Body`フィールドを宣言することはできません。なぜなら、リクエストのボディは`application/json`の代わりに`multipart/form-data`を使ってエンコードされているからです。
|
||||
|
||||
これは **FastAPI** の制限ではなく、HTTPプロトコルの一部です。
|
||||
|
||||
## まとめ
|
||||
|
||||
同じリクエストでデータやファイルを受け取る必要がある場合は、`File` と`Form`を一緒に使用します。
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# 이벤트: startup과 shutdown
|
||||
|
||||
필요에 따라 응용 프로그램이 시작되기 전이나 종료될 때 실행되는 이벤트 핸들러(함수)를 정의할 수 있습니다.
|
||||
|
||||
이 함수들은 `async def` 또는 평범하게 `def`으로 선언할 수 있습니다.
|
||||
|
||||
!!! warning "경고"
|
||||
이벤트 핸들러는 주 응용 프로그램에서만 작동합니다. [하위 응용 프로그램 - 마운트](./sub-applications.md){.internal-link target=_blank}에서는 작동하지 않습니다.
|
||||
|
||||
## `startup` 이벤트
|
||||
|
||||
응용 프로그램을 시작하기 전에 실행하려는 함수를 "startup" 이벤트로 선언합니다:
|
||||
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/events/tutorial001.py!}
|
||||
```
|
||||
|
||||
이 경우 `startup` 이벤트 핸들러 함수는 단순히 몇 가지 값으로 구성된 `dict` 형식의 "데이터베이스"를 초기화합니다.
|
||||
|
||||
하나 이상의 이벤트 핸들러 함수를 추가할 수도 있습니다.
|
||||
|
||||
그리고 응용 프로그램은 모든 `startup` 이벤트 핸들러가 완료될 때까지 요청을 받지 않습니다.
|
||||
|
||||
## `shutdown` 이벤트
|
||||
|
||||
응용 프로그램이 종료될 때 실행하려는 함수를 추가하려면 `"shutdown"` 이벤트로 선언합니다:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/events/tutorial002.py!}
|
||||
```
|
||||
|
||||
이 예제에서 `shutdown` 이벤트 핸들러 함수는 `"Application shutdown"`이라는 텍스트가 적힌 `log.txt` 파일을 추가할 것입니다.
|
||||
|
||||
!!! info "정보"
|
||||
`open()` 함수에서 `mode="a"`는 "추가"를 의미합니다. 따라서 이미 존재하는 파일의 내용을 덮어쓰지 않고 새로운 줄을 추가합니다.
|
||||
|
||||
!!! tip "팁"
|
||||
이 예제에서는 파일과 상호작용 하기 위해 파이썬 표준 함수인 `open()`을 사용하고 있습니다.
|
||||
|
||||
따라서 디스크에 데이터를 쓰기 위해 "대기"가 필요한 I/O (입력/출력) 작업을 수행합니다.
|
||||
|
||||
그러나 `open()`은 `async`와 `await`을 사용하지 않기 때문에 이벤트 핸들러 함수는 `async def`가 아닌 표준 `def`로 선언하고 있습니다.
|
||||
|
||||
!!! info "정보"
|
||||
이벤트 핸들러에 관한 내용은 <a href="https://www.starlette.io/events/" class="external-link" target="_blank">Starlette 이벤트 문서</a>에서 추가로 확인할 수 있습니다.
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
* # FastAPI 지원 - 도움말 받기
|
||||
|
||||
**FastAPI** 가 마음에 드시나요?
|
||||
|
||||
FastAPI, 다른 사용자, 개발자를 응원하고 싶으신가요?
|
||||
|
||||
혹은 **FastAPI** 에 대해 도움이 필요하신가요?
|
||||
|
||||
아주 간단하게 응원할 수 있습니다 (몇 번의 클릭만으로).
|
||||
|
||||
또한 도움을 받을 수 있는 방법도 몇 가지 있습니다.
|
||||
|
||||
## 뉴스레터 구독
|
||||
|
||||
[**FastAPI와 친구** 뉴스레터](https://github.com/tiangolo/fastapi/blob/master/newsletter)를 구독하여 최신 정보를 유지할 수 있습니다{.internal-link target=_blank}:
|
||||
|
||||
- FastAPI 와 그 친구들에 대한 뉴스 🚀
|
||||
- 가이드 📝
|
||||
- 특징 ✨
|
||||
- 획기적인 변화 🚨
|
||||
- 팁과 요령 ✅
|
||||
|
||||
## 트위터에서 FastAPI 팔로우하기
|
||||
|
||||
[Follow @fastapi on **Twitter**](https://twitter.com/fastapi) 를 팔로우하여 **FastAPI** 에 대한 최신 뉴스를 얻을 수 있습니다. 🐦
|
||||
|
||||
## Star **FastAPI** in GitHub
|
||||
|
||||
GitHub에서 FastAPI에 "star"를 붙일 수 있습니다(오른쪽 상단의 star 버튼을 클릭): https://github.com/tiangolo/fastapi. ⭐️
|
||||
|
||||
스타를 늘림으로써, 다른 사용자들이 좀 더 쉽게 찾을 수 있고, 많은 사람들에게 유용한 것임을 나타낼 수 있습니다.
|
||||
|
||||
## GitHub 저장소에서 릴리즈 확인
|
||||
|
||||
GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/tiangolo/fastapi. 👀
|
||||
|
||||
여기서 "Releases only"을 선택할 수 있습니다.
|
||||
|
||||
이렇게하면, **FastAPI** 의 버그 수정 및 새로운 기능의 구현 등의 새로운 자료 (최신 버전)이 있을 때마다 (이메일) 통지를 받을 수 있습니다.
|
||||
|
||||
## 개발자와의 연결
|
||||
|
||||
개발자인 [me (Sebastián Ramírez / `tiangolo`)](https://tiangolo.com/) 와 연락을 취할 수 있습니다.
|
||||
|
||||
여러분은 할 수 있습니다:
|
||||
|
||||
- [**GitHub**에서 팔로우하기](https://github.com/tiangolo).
|
||||
- 당신에게 도움이 될 저의 다른 오픈소스 프로젝트를 확인하십시오.
|
||||
- 새로운 오픈소스 프로젝트를 만들었을 때 확인하려면 팔로우 하십시오.
|
||||
|
||||
- [**Twitter**에서 팔로우하기](https://twitter.com/tiangolo).
|
||||
- FastAPI의 사용 용도를 알려주세요 (그것을 듣는 것을 좋아합니다).
|
||||
- 발표 또는 새로운 툴 출시할 때 들으십시오.
|
||||
- [follow @fastapi on Twitter](https://twitter.com/fastapi) (별도 계정에서) 할 수 있습니다.
|
||||
|
||||
- [**Linkedin**에서의 연결](https://www.linkedin.com/in/tiangolo/).
|
||||
- 새로운 툴의 발표나 릴리스를 들을 수 있습니다 (단, Twitter를 더 자주 사용합니다 🤷♂).
|
||||
|
||||
- [**Dev.to**](https://dev.to/tiangolo) 또는 [**Medium**](https://medium.com/@tiangolo)에서 제가 작성한 내용을 읽어 보십시오(또는 팔로우).
|
||||
- 다른 기사나 아이디어들을 읽고, 제가 만들어왔던 툴에 대해서도 읽으십시오.
|
||||
- 새로운 기사를 읽기 위해 팔로우 하십시오.
|
||||
|
||||
## **FastAPI**에 대한 트윗
|
||||
|
||||
[**FastAPI**에 대해 트윗](https://twitter.com/compose/tweet?text=I'm loving @fastapi because... https://github.com/tiangolo/fastapi) 하고 FastAPI가 마음에 드는 이유를 알려주세요. 🎉
|
||||
|
||||
**FastAPI**가 어떻게 사용되고 있는지, 어떤 점이 마음에 들었는지, 어떤 프로젝트/회사에서 사용하고 있는지 등에 대해 듣고 싶습니다.
|
||||
|
||||
## FastAPI에 투표하기
|
||||
|
||||
- [Slant에서 **FastAPI** 에 대해 투표하십시오](https://www.slant.co/options/34241/~fastapi-review).
|
||||
- [AlternativeTo**FastAPI** 에 대해 투표하십시오](https://alternativeto.net/software/fastapi/).
|
||||
|
||||
## GitHub의 이슈로 다른사람 돕기
|
||||
|
||||
[존재하는 이슈](https://github.com/tiangolo/fastapi/issues)를 확인하고 그것을 시도하고 도와줄 수 있습니다. 대부분의 경우 이미 답을 알고 있는 질문입니다. 🤓
|
||||
|
||||
많은 사람들의 문제를 도와준다면, 공식적인 [FastAPI 전문가](https://github.com/tiangolo/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts) 가 될 수 있습니다{.internal-link target=_blank}. 🎉
|
||||
|
||||
## GitHub 저장소 보기
|
||||
|
||||
GitHub에서 FastAPI를 "watch"할 수 있습니다 (오른쪽 상단 watch 버튼을 클릭): https://github.com/tiangolo/fastapi. 👀
|
||||
|
||||
"Releases only" 대신 "Watching"을 선택하면 다른 사용자가 새로운 issue를 생성할 때 알림이 수신됩니다.
|
||||
|
||||
그런 다음 이런 issues를 해결 할 수 있도록 도움을 줄 수 있습니다.
|
||||
|
||||
## 이슈 생성하기
|
||||
|
||||
GitHub 저장소에 [새로운 이슈 생성](https://github.com/tiangolo/fastapi/issues/new/choose) 을 할 수 있습니다, 예를들면 다음과 같습니다:
|
||||
|
||||
- **질문**을 하거나 **문제**에 대해 질문합니다.
|
||||
- 새로운 **기능**을 제안 합니다.
|
||||
|
||||
**참고**: 만약 이슈를 생성한다면, 저는 여러분에게 다른 사람들을 도와달라고 부탁할 것입니다. 😉
|
||||
|
||||
## Pull Request를 만드십시오
|
||||
|
||||
Pull Requests를 이용하여 소스코드에 [컨트리뷰트](https://github.com/tiangolo/fastapi/blob/master/docs/en/docs/contributing.md){.internal-link target=_blank} 할 수 있습니다. 예를 들면 다음과 같습니다:
|
||||
|
||||
- 문서에서 찾은 오타를 수정할 때.
|
||||
|
||||
- FastAPI를 [편집하여](https://github.com/tiangolo/fastapi/edit/master/docs/en/data/external_links.yml) 작성했거나 찾은 문서, 비디오 또는 팟캐스트를 공유할 때.
|
||||
|
||||
- 해당 섹션의 시작 부분에 링크를 추가했는지 확인하십시오.
|
||||
|
||||
- 당신의 언어로 [문서 번역하는데](https://github.com/tiangolo/fastapi/blob/master/docs/en/docs/contributing.md#translations){.internal-link target=_blank} 기여할 때.
|
||||
|
||||
- 또한 다른 사용자가 만든 번역을 검토하는데 도움을 줄 수도 있습니다.
|
||||
|
||||
- 새로운 문서의 섹션을 제안할 때.
|
||||
|
||||
- 기존 문제/버그를 수정할 때.
|
||||
|
||||
- 새로운 feature를 추가할 때.
|
||||
|
||||
## 채팅에 참여하십시오
|
||||
|
||||
👥 [디스코드 채팅 서버](https://discord.gg/VQjSZaeJmf) 👥 에 가입하고 FastAPI 커뮤니티에서 다른 사람들과 어울리세요.
|
||||
|
||||
!!! tip 질문이 있는 경우, [GitHub 이슈 ](https://github.com/tiangolo/fastapi/issues/new/choose) 에서 질문하십시오, [FastAPI 전문가](https://github.com/tiangolo/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts) 의 도움을 받을 가능성이 높습니다{.internal-link target=_blank} .
|
||||
|
||||
```
|
||||
다른 일반적인 대화에서만 채팅을 사용하십시오.
|
||||
```
|
||||
|
||||
기존 [지터 채팅](https://gitter.im/tiangolo/fastapi) 이 있지만 채널과 고급기능이 없어서 대화를 하기가 조금 어렵기 때문에 지금은 디스코드가 권장되는 시스템입니다.
|
||||
|
||||
### 질문을 위해 채팅을 사용하지 마십시오
|
||||
|
||||
채팅은 더 많은 "자유로운 대화"를 허용하기 때문에, 너무 일반적인 질문이나 대답하기 어려운 질문을 쉽게 질문을 할 수 있으므로, 답변을 받지 못할 수 있습니다.
|
||||
|
||||
GitHub 이슈에서의 템플릿은 올바른 질문을 작성하도록 안내하여 더 쉽게 좋은 답변을 얻거나 질문하기 전에 스스로 문제를 해결할 수도 있습니다. 그리고 GitHub에서는 시간이 조금 걸리더라도 항상 모든 것에 답할 수 있습니다. 채팅 시스템에서는 개인적으로 그렇게 할 수 없습니다. 😅
|
||||
|
||||
채팅 시스템에서의 대화 또한 GitHub에서 처럼 쉽게 검색할 수 없기 때문에 대화 중에 질문과 답변이 손실될 수 있습니다. 그리고 GitHub 이슈에 있는 것만 [FastAPI 전문가](https://github.com/tiangolo/fastapi/blob/master/docs/en/docs/fastapi-people.md#experts)가 되는 것으로 간주되므로{.internal-link target=_blank} , GitHub 이슈에서 더 많은 관심을 받을 것입니다.
|
||||
|
||||
반면, 채팅 시스템에는 수천 명의 사용자가 있기 때문에, 거의 항상 대화 상대를 찾을 가능성이 높습니다. 😄
|
||||
|
||||
## 개발자 스폰서가 되십시오
|
||||
|
||||
[GitHub 스폰서](https://github.com/sponsors/tiangolo) 를 통해 개발자를 경제적으로 지원할 수 있습니다.
|
||||
|
||||
감사하다는 말로 커피를 ☕️ 한잔 사줄 수 있습니다. 😄
|
||||
|
||||
또한 FastAPI의 실버 또는 골드 스폰서가 될 수 있습니다. 🏅🎉
|
||||
|
||||
## FastAPI를 강화하는 도구의 스폰서가 되십시오
|
||||
|
||||
문서에서 보았듯이, FastAPI는 Starlette과 Pydantic 라는 거인의 어깨에 타고 있습니다.
|
||||
|
||||
다음의 스폰서가 될 수 있습니다
|
||||
|
||||
- [Samuel Colvin (Pydantic)](https://github.com/sponsors/samuelcolvin)
|
||||
- [Encode (Starlette, Uvicorn)](https://github.com/sponsors/encode)
|
||||
|
||||
------
|
||||
|
||||
감사합니다! 🚀
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
# 디버깅
|
||||
|
||||
예를 들면 Visual Studio Code 또는 PyCharm을 사용하여 편집기에서 디버거를 연결할 수 있습니다.
|
||||
|
||||
## `uvicorn` 호출
|
||||
|
||||
FastAPI 애플리케이션에서 `uvicorn`을 직접 임포트하여 실행합니다
|
||||
|
||||
```Python hl_lines="1 15"
|
||||
{!../../../docs_src/debugging/tutorial001.py!}
|
||||
```
|
||||
|
||||
### `__name__ == "__main__"` 에 대하여
|
||||
|
||||
`__name__ == "__main__"`의 주요 목적은 다음과 같이 파일이 호출될 때 실행되는 일부 코드를 갖는 것입니다.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python myapp.py
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
그러나 다음과 같이 다른 파일을 가져올 때는 호출되지 않습니다.
|
||||
|
||||
```Python
|
||||
from myapp import app
|
||||
```
|
||||
|
||||
#### 추가 세부사항
|
||||
|
||||
파일 이름이 `myapp.py`라고 가정해 보겠습니다.
|
||||
|
||||
다음과 같이 실행하면
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ python myapp.py
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
Python에 의해 자동으로 생성된 파일의 내부 변수 `__name__`은 문자열 `"__main__"`을 값으로 갖게 됩니다.
|
||||
|
||||
따라서 섹션
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
이 실행됩니다.
|
||||
|
||||
---
|
||||
|
||||
해당 모듈(파일)을 가져오면 이런 일이 발생하지 않습니다
|
||||
|
||||
그래서 다음과 같은 다른 파일 `importer.py`가 있는 경우:
|
||||
|
||||
```Python
|
||||
from myapp import app
|
||||
|
||||
# Some more code
|
||||
```
|
||||
|
||||
이 경우 `myapp.py` 내부의 자동 변수에는 값이 `"__main__"`인 변수 `__name__`이 없습니다.
|
||||
|
||||
따라서 다음 행
|
||||
|
||||
```Python
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
```
|
||||
|
||||
은 실행되지 않습니다.
|
||||
|
||||
!!! info "정보"
|
||||
자세한 내용은 <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">공식 Python 문서</a>를 확인하세요
|
||||
|
||||
## 디버거로 코드 실행
|
||||
|
||||
코드에서 직접 Uvicorn 서버를 실행하고 있기 때문에 디버거에서 직접 Python 프로그램(FastAPI 애플리케이션)을 호출할 수 있습니다.
|
||||
|
||||
---
|
||||
|
||||
예를 들어 Visual Studio Code에서 다음을 수행할 수 있습니다.
|
||||
|
||||
* "Debug" 패널로 이동합니다.
|
||||
* "Add configuration...".
|
||||
* "Python"을 선택합니다.
|
||||
* "`Python: Current File (Integrated Terminal)`" 옵션으로 디버거를 실행합니다.
|
||||
|
||||
그런 다음 **FastAPI** 코드로 서버를 시작하고 중단점 등에서 중지합니다.
|
||||
|
||||
다음과 같이 표시됩니다.
|
||||
|
||||
<img src="/img/tutorial/debugging/image01.png">
|
||||
|
||||
---
|
||||
|
||||
Pycharm을 사용하는 경우 다음을 수행할 수 있습니다
|
||||
|
||||
* "Run" 메뉴를 엽니다
|
||||
* "Debug..." 옵션을 선택합니다.
|
||||
* 그러면 상황에 맞는 메뉴가 나타납니다.
|
||||
* 디버그할 파일을 선택합니다(이 경우 `main.py`).
|
||||
|
||||
그런 다음 **FastAPI** 코드로 서버를 시작하고 중단점 등에서 중지합니다.
|
||||
|
||||
다음과 같이 표시됩니다.
|
||||
|
||||
<img src="/img/tutorial/debugging/image02.png">
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
# 패스워드와 Bearer를 이용한 간단한 OAuth2
|
||||
|
||||
이제 이전 장에서 빌드하고 누락된 부분을 추가하여 완전한 보안 흐름을 갖도록 하겠습니다.
|
||||
|
||||
## `username`와 `password` 얻기
|
||||
|
||||
**FastAPI** 보안 유틸리티를 사용하여 `username` 및 `password`를 가져올 것입니다.
|
||||
|
||||
OAuth2는 (우리가 사용하고 있는) "패스워드 플로우"을 사용할 때 클라이언트/유저가 `username` 및 `password` 필드를 폼 데이터로 보내야 함을 지정합니다.
|
||||
|
||||
그리고 사양에는 필드의 이름을 그렇게 지정해야 한다고 나와 있습니다. 따라서 `user-name` 또는 `email`은 작동하지 않습니다.
|
||||
|
||||
하지만 걱정하지 않아도 됩니다. 프런트엔드에서 최종 사용자에게 원하는 대로 표시할 수 있습니다.
|
||||
|
||||
그리고 데이터베이스 모델은 원하는 다른 이름을 사용할 수 있습니다.
|
||||
|
||||
그러나 로그인 *경로 작동*의 경우 사양과 호환되도록 이러한 이름을 사용해야 합니다(예를 들어 통합 API 문서 시스템을 사용할 수 있어야 합니다).
|
||||
|
||||
사양에는 또한 `username`과 `password`가 폼 데이터로 전송되어야 한다고 명시되어 있습니다(따라서 여기에는 JSON이 없습니다).
|
||||
|
||||
### `scope`
|
||||
|
||||
사양에는 클라이언트가 다른 폼 필드 "`scope`"를 보낼 수 있다고 나와 있습니다.
|
||||
|
||||
폼 필드 이름은 `scope`(단수형)이지만 실제로는 공백으로 구분된 "범위"가 있는 긴 문자열입니다.
|
||||
|
||||
각 "범위"는 공백이 없는 문자열입니다.
|
||||
|
||||
일반적으로 특정 보안 권한을 선언하는 데 사용됩니다. 다음을 봅시다:
|
||||
|
||||
* `users:read` 또는 `users:write`는 일반적인 예시입니다.
|
||||
* `instagram_basic`은 페이스북/인스타그램에서 사용합니다.
|
||||
* `https://www.googleapis.com/auth/drive`는 Google에서 사용합니다.
|
||||
|
||||
!!! 정보
|
||||
OAuth2에서 "범위"는 필요한 특정 권한을 선언하는 문자열입니다.
|
||||
|
||||
`:`과 같은 다른 문자가 있는지 또는 URL인지는 중요하지 않습니다.
|
||||
|
||||
이러한 세부 사항은 구현에 따라 다릅니다.
|
||||
|
||||
OAuth2의 경우 문자열일 뿐입니다.
|
||||
|
||||
## `username`과 `password`를 가져오는 코드
|
||||
|
||||
이제 **FastAPI**에서 제공하는 유틸리티를 사용하여 이를 처리해 보겠습니다.
|
||||
|
||||
### `OAuth2PasswordRequestForm`
|
||||
|
||||
먼저 `OAuth2PasswordRequestForm`을 가져와 `/token`에 대한 *경로 작동*에서 `Depends`의 의존성으로 사용합니다.
|
||||
|
||||
=== "파이썬 3.7 이상"
|
||||
|
||||
```Python hl_lines="4 76"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "파이썬 3.10 이상"
|
||||
|
||||
```Python hl_lines="2 74"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
`OAuth2PasswordRequestForm`은 다음을 사용하여 폼 본문을 선언하는 클래스 의존성입니다:
|
||||
|
||||
* `username`.
|
||||
* `password`.
|
||||
* `scope`는 선택적인 필드로 공백으로 구분된 문자열로 구성된 큰 문자열입니다.
|
||||
* `grant_type`(선택적으로 사용).
|
||||
|
||||
!!! 팁
|
||||
OAuth2 사양은 실제로 `password`라는 고정 값이 있는 `grant_type` 필드를 *요구*하지만 `OAuth2PasswordRequestForm`은 이를 강요하지 않습니다.
|
||||
|
||||
사용해야 한다면 `OAuth2PasswordRequestForm` 대신 `OAuth2PasswordRequestFormStrict`를 사용하면 됩니다.
|
||||
|
||||
* `client_id`(선택적으로 사용) (예제에서는 필요하지 않습니다).
|
||||
* `client_secret`(선택적으로 사용) (예제에서는 필요하지 않습니다).
|
||||
|
||||
!!! 정보
|
||||
`OAuth2PasswordRequestForm`은 `OAuth2PasswordBearer`와 같이 **FastAPI**에 대한 특수 클래스가 아닙니다.
|
||||
|
||||
`OAuth2PasswordBearer`는 **FastAPI**가 보안 체계임을 알도록 합니다. 그래서 OpenAPI에 그렇게 추가됩니다.
|
||||
|
||||
그러나 `OAuth2PasswordRequestForm`은 직접 작성하거나 `Form` 매개변수를 직접 선언할 수 있는 클래스 의존성일 뿐입니다.
|
||||
|
||||
그러나 일반적인 사용 사례이므로 더 쉽게 하기 위해 **FastAPI**에서 직접 제공합니다.
|
||||
|
||||
### 폼 데이터 사용하기
|
||||
|
||||
!!! 팁
|
||||
종속성 클래스 `OAuth2PasswordRequestForm`의 인스턴스에는 공백으로 구분된 긴 문자열이 있는 `scope` 속성이 없고 대신 전송된 각 범위에 대한 실제 문자열 목록이 있는 `scopes` 속성이 있습니다.
|
||||
|
||||
이 예제에서는 `scopes`를 사용하지 않지만 필요한 경우, 기능이 있습니다.
|
||||
|
||||
이제 폼 필드의 `username`을 사용하여 (가짜) 데이터베이스에서 유저 데이터를 가져옵니다.
|
||||
|
||||
해당 사용자가 없으면 "잘못된 사용자 이름 또는 패스워드"라는 오류가 반환됩니다.
|
||||
|
||||
오류의 경우 `HTTPException` 예외를 사용합니다:
|
||||
|
||||
=== "파이썬 3.7 이상"
|
||||
|
||||
```Python hl_lines="3 77-79"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "파이썬 3.10 이상"
|
||||
|
||||
```Python hl_lines="1 75-77"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
### 패스워드 확인하기
|
||||
|
||||
이 시점에서 데이터베이스의 사용자 데이터 형식을 확인했지만 암호를 확인하지 않았습니다.
|
||||
|
||||
먼저 데이터를 Pydantic `UserInDB` 모델에 넣겠습니다.
|
||||
|
||||
일반 텍스트 암호를 저장하면 안 되니 (가짜) 암호 해싱 시스템을 사용합니다.
|
||||
|
||||
두 패스워드가 일치하지 않으면 동일한 오류가 반환됩니다.
|
||||
|
||||
#### 패스워드 해싱
|
||||
|
||||
"해싱"은 일부 콘텐츠(이 경우 패스워드)를 횡설수설하는 것처럼 보이는 일련의 바이트(문자열)로 변환하는 것을 의미합니다.
|
||||
|
||||
정확히 동일한 콘텐츠(정확히 동일한 패스워드)를 전달할 때마다 정확히 동일한 횡설수설이 발생합니다.
|
||||
|
||||
그러나 횡설수설에서 암호로 다시 변환할 수는 없습니다.
|
||||
|
||||
##### 패스워드 해싱을 사용해야 하는 이유
|
||||
|
||||
데이터베이스가 유출된 경우 해커는 사용자의 일반 텍스트 암호가 아니라 해시만 갖게 됩니다.
|
||||
|
||||
따라서 해커는 다른 시스템에서 동일한 암호를 사용하려고 시도할 수 없습니다(많은 사용자가 모든 곳에서 동일한 암호를 사용하므로 이는 위험할 수 있습니다).
|
||||
|
||||
=== "P파이썬 3.7 이상"
|
||||
|
||||
```Python hl_lines="80-83"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "파이썬 3.10 이상"
|
||||
|
||||
```Python hl_lines="78-81"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
#### `**user_dict`에 대해
|
||||
|
||||
`UserInDB(**user_dict)`는 다음을 의미한다:
|
||||
|
||||
*`user_dict`의 키와 값을 다음과 같은 키-값 인수로 직접 전달합니다:*
|
||||
|
||||
```Python
|
||||
UserInDB(
|
||||
username = user_dict["username"],
|
||||
email = user_dict["email"],
|
||||
full_name = user_dict["full_name"],
|
||||
disabled = user_dict["disabled"],
|
||||
hashed_password = user_dict["hashed_password"],
|
||||
)
|
||||
```
|
||||
|
||||
!!! 정보
|
||||
`**user_dict`에 대한 자세한 설명은 [**추가 모델** 문서](../extra-models.md#about-user_indict){.internal-link target=_blank}를 다시 읽어봅시다.
|
||||
|
||||
## 토큰 반환하기
|
||||
|
||||
`token` 엔드포인트의 응답은 JSON 객체여야 합니다.
|
||||
|
||||
`token_type`이 있어야 합니다. 여기서는 "Bearer" 토큰을 사용하므로 토큰 유형은 "`bearer`"여야 합니다.
|
||||
|
||||
그리고 액세스 토큰을 포함하는 문자열과 함께 `access_token`이 있어야 합니다.
|
||||
|
||||
이 간단한 예제에서는 완전히 안전하지 않고, 동일한 `username`을 토큰으로 반환합니다.
|
||||
|
||||
!!! 팁
|
||||
다음 장에서는 패스워드 해싱 및 <abbr title="JSON Web Tokens">JWT</abbr> 토큰을 사용하여 실제 보안 구현을 볼 수 있습니다.
|
||||
|
||||
하지만 지금은 필요한 세부 정보에 집중하겠습니다.
|
||||
|
||||
=== "파이썬 3.7 이상"
|
||||
|
||||
```Python hl_lines="85"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "파이썬 3.10 이상"
|
||||
|
||||
```Python hl_lines="83"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
!!! 팁
|
||||
사양에 따라 이 예제와 동일하게 `access_token` 및 `token_type`이 포함된 JSON을 반환해야 합니다.
|
||||
|
||||
이는 코드에서 직접 수행해야 하며 해당 JSON 키를 사용해야 합니다.
|
||||
|
||||
사양을 준수하기 위해 스스로 올바르게 수행하기 위해 거의 유일하게 기억해야 하는 것입니다.
|
||||
|
||||
나머지는 **FastAPI**가 처리합니다.
|
||||
|
||||
## 의존성 업데이트하기
|
||||
|
||||
이제 의존성을 업데이트를 할 겁니다.
|
||||
|
||||
이 사용자가 활성화되어 있는 *경우에만* `current_user`를 가져올 겁니다.
|
||||
|
||||
따라서 `get_current_user`를 의존성으로 사용하는 추가 종속성 `get_current_active_user`를 만듭니다.
|
||||
|
||||
이러한 의존성 모두, 사용자가 존재하지 않거나 비활성인 경우 HTTP 오류를 반환합니다.
|
||||
|
||||
따라서 엔드포인트에서는 사용자가 존재하고 올바르게 인증되었으며 활성 상태인 경우에만 사용자를 얻습니다:
|
||||
|
||||
=== "파이썬 3.7 이상"
|
||||
|
||||
```Python hl_lines="58-66 69-72 90"
|
||||
{!> ../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
=== "파이썬 3.10 이상"
|
||||
|
||||
```Python hl_lines="55-64 67-70 88"
|
||||
{!> ../../../docs_src/security/tutorial003_py310.py!}
|
||||
```
|
||||
|
||||
!!! 정보
|
||||
여기서 반환하는 값이 `Bearer`인 추가 헤더 `WWW-Authenticate`도 사양의 일부입니다.
|
||||
|
||||
모든 HTTP(오류) 상태 코드 401 "UNAUTHORIZED"는 `WWW-Authenticate` 헤더도 반환해야 합니다.
|
||||
|
||||
베어러 토큰의 경우(지금의 경우) 해당 헤더의 값은 `Bearer`여야 합니다.
|
||||
|
||||
실제로 추가 헤더를 건너뛸 수 있으며 여전히 작동합니다.
|
||||
|
||||
그러나 여기에서는 사양을 준수하도록 제공됩니다.
|
||||
|
||||
또한 이를 예상하고 (현재 또는 미래에) 사용하는 도구가 있을 수 있으며, 현재 또는 미래에 자신 혹은 자신의 유저들에게 유용할 것입니다.
|
||||
|
||||
그것이 표준의 이점입니다 ...
|
||||
|
||||
## 확인하기
|
||||
|
||||
대화형 문서 열기: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
### 인증하기
|
||||
|
||||
"Authorize" 버튼을 눌러봅시다.
|
||||
|
||||
자격 증명을 사용합니다.
|
||||
|
||||
유저명: `johndoe`
|
||||
|
||||
패스워드: `secret`
|
||||
|
||||
<img src="/img/tutorial/security/image04.png">
|
||||
|
||||
시스템에서 인증하면 다음과 같이 표시됩니다:
|
||||
|
||||
<img src="/img/tutorial/security/image05.png">
|
||||
|
||||
### 자신의 유저 데이터 가져오기
|
||||
|
||||
이제 `/users/me` 경로에 `GET` 작업을 진행합시다.
|
||||
|
||||
다음과 같은 사용자 데이터를 얻을 수 있습니다:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"username": "johndoe",
|
||||
"email": "johndoe@example.com",
|
||||
"full_name": "John Doe",
|
||||
"disabled": false,
|
||||
"hashed_password": "fakehashedsecret"
|
||||
}
|
||||
```
|
||||
|
||||
<img src="/img/tutorial/security/image06.png">
|
||||
|
||||
잠금 아이콘을 클릭하고 로그아웃한 다음 동일한 작업을 다시 시도하면 다음과 같은 HTTP 401 오류가 발생합니다.
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Not authenticated"
|
||||
}
|
||||
```
|
||||
|
||||
### 비활성된 유저
|
||||
|
||||
이제 비활성된 사용자로 시도하고, 인증해봅시다:
|
||||
|
||||
유저명: `alice`
|
||||
|
||||
패스워드: `secret2`
|
||||
|
||||
그리고 `/users/me` 경로와 함께 `GET` 작업을 사용해 봅시다.
|
||||
|
||||
다음과 같은 "Inactive user" 오류가 발생합니다:
|
||||
|
||||
```JSON
|
||||
{
|
||||
"detail": "Inactive user"
|
||||
}
|
||||
```
|
||||
|
||||
## 요약
|
||||
|
||||
이제 API에 대한 `username` 및 `password`를 기반으로 완전한 보안 시스템을 구현할 수 있는 도구가 있습니다.
|
||||
|
||||
이러한 도구를 사용하여 보안 시스템을 모든 데이터베이스 및 모든 사용자 또는 데이터 모델과 호환되도록 만들 수 있습니다.
|
||||
|
||||
유일한 오점은 아직 실제로 "안전"하지 않다는 것입니다.
|
||||
|
||||
다음 장에서는 안전한 패스워드 해싱 라이브러리와 <abbr title="JSON Web Tokens">JWT</abbr> 토큰을 사용하는 방법을 살펴보겠습니다.
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Sobre
|
||||
|
||||
Sobre o FastAPI, seus padrões, inspirações e muito mais. 🤓
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Ajude
|
||||
|
||||
Ajude e obtenha ajuda, contribua, participe. 🤝
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# Aprender
|
||||
|
||||
Nesta parte da documentação encontramos as seções introdutórias e os tutoriais para aprendermos como usar o **FastAPI**.
|
||||
|
||||
Nós poderíamos considerar isto um **livro**, **curso**, a maneira **oficial** e recomendada de aprender o FastAPI. 😎
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
# Recursos
|
||||
|
||||
Material complementar, links externos, artigos e muito mais. ✈️
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# Концепции развёртывания
|
||||
|
||||
Существует несколько концепций, применяемых для развёртывания приложений **FastAPI**, равно как и для любых других типов веб-приложений, среди которых Вы можете выбрать **наиболее подходящий** способ.
|
||||
Существует несколько концепций, применяемых для развёртывания приложений **FastAPI**, равно как и для любых других типов веб-приложений, среди которых вы можете выбрать **наиболее подходящий** способ.
|
||||
|
||||
Самые важные из них:
|
||||
|
||||
|
|
@ -13,11 +13,11 @@
|
|||
|
||||
Рассмотрим ниже влияние каждого из них на процесс **развёртывания**.
|
||||
|
||||
Наша конечная цель - **обслуживать клиентов Вашего API безопасно** и **бесперебойно**, с максимально эффективным использованием **вычислительных ресурсов** (например, удалённых серверов/виртуальных машин). 🚀
|
||||
Наша конечная цель - **обслуживать клиентов вашего API безопасно** и **бесперебойно**, с максимально эффективным использованием **вычислительных ресурсов** (например, удалённых серверов/виртуальных машин). 🚀
|
||||
|
||||
Здесь я немного расскажу Вам об этих **концепциях** и надеюсь, что у Вас сложится **интуитивное понимание**, какой способ выбрать при развертывании Вашего API в различных окружениях, возможно, даже **ещё не существующих**.
|
||||
Здесь я немного расскажу Вам об этих **концепциях** и надеюсь, что у вас сложится **интуитивное понимание**, какой способ выбрать при развертывании вашего API в различных окружениях, возможно, даже **ещё не существующих**.
|
||||
|
||||
Ознакомившись с этими концепциями, Вы сможете **оценить и выбрать** лучший способ развёртывании **Вашего API**.
|
||||
Ознакомившись с этими концепциями, вы сможете **оценить и выбрать** лучший способ развёртывании **Вашего API**.
|
||||
|
||||
В последующих главах я предоставлю Вам **конкретные рецепты** развёртывания приложения FastAPI.
|
||||
|
||||
|
|
@ -25,15 +25,15 @@
|
|||
|
||||
## Использование более безопасного протокола HTTPS
|
||||
|
||||
В [предыдущей главе об HTTPS](./https.md){.internal-link target=_blank} мы рассмотрели, как HTTPS обеспечивает шифрование для Вашего API.
|
||||
В [предыдущей главе об HTTPS](./https.md){.internal-link target=_blank} мы рассмотрели, как HTTPS обеспечивает шифрование для вашего API.
|
||||
|
||||
Также мы заметили, что обычно для работы с HTTPS Вашему приложению нужен **дополнительный** компонент - **прокси-сервер завершения работы TLS**.
|
||||
Также мы заметили, что обычно для работы с HTTPS вашему приложению нужен **дополнительный** компонент - **прокси-сервер завершения работы TLS**.
|
||||
|
||||
И если прокси-сервер не умеет сам **обновлять сертификаты HTTPS**, то нужен ещё один компонент для этого действия.
|
||||
|
||||
### Примеры инструментов для работы с HTTPS
|
||||
|
||||
Вот некоторые инструменты, которые Вы можете применять как прокси-серверы:
|
||||
Вот некоторые инструменты, которые вы можете применять как прокси-серверы:
|
||||
|
||||
* Traefik
|
||||
* С автоматическим обновлением сертификатов ✨
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
* С дополнительным компонентом типа cert-manager для обновления сертификатов
|
||||
* Использование услуг облачного провайдера (читайте ниже 👇)
|
||||
|
||||
В последнем варианте Вы можете воспользоваться услугами **облачного сервиса**, который сделает большую часть работы, включая настройку HTTPS. Это может наложить дополнительные ограничения или потребовать дополнительную плату и т.п. Зато Вам не понадобится самостоятельно заниматься настройками прокси-сервера.
|
||||
В последнем варианте вы можете воспользоваться услугами **облачного сервиса**, который сделает большую часть работы, включая настройку HTTPS. Это может наложить дополнительные ограничения или потребовать дополнительную плату и т.п. Зато Вам не понадобится самостоятельно заниматься настройками прокси-сервера.
|
||||
|
||||
В дальнейшем я покажу Вам некоторые конкретные примеры их применения.
|
||||
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
|
||||
Термином **программа** обычно описывают множество вещей:
|
||||
|
||||
* **Код**, который Вы написали, в нашем случае **Python-файлы**.
|
||||
* **Код**, который вы написали, в нашем случае **Python-файлы**.
|
||||
* **Файл**, который может быть **исполнен** операционной системой, например `python`, `python.exe` или `uvicorn`.
|
||||
* Конкретная программа, **запущенная** операционной системой и использующая центральный процессор и память. В таком случае это также называется **процесс**.
|
||||
|
||||
|
|
@ -74,13 +74,13 @@
|
|||
* Конкретная программа, **запущенная** операционной системой.
|
||||
* Это не имеет отношения к какому-либо файлу или коду, но нечто **определённое**, управляемое и **выполняемое** операционной системой.
|
||||
* Любая программа, любой код, **могут делать что-то** только когда они **выполняются**. То есть, когда являются **работающим процессом**.
|
||||
* Процесс может быть **прерван** (или "убит") Вами или Вашей операционной системой. В результате чего он перестанет исполняться и **не будет продолжать делать что-либо**.
|
||||
* Каждое приложение, которое Вы запустили на своём компьютере, каждая программа, каждое "окно" запускает какой-то процесс. И обычно на включенном компьютере **одновременно** запущено множество процессов.
|
||||
* Процесс может быть **прерван** (или "убит") Вами или вашей операционной системой. В результате чего он перестанет исполняться и **не будет продолжать делать что-либо**.
|
||||
* Каждое приложение, которое вы запустили на своём компьютере, каждая программа, каждое "окно" запускает какой-то процесс. И обычно на включенном компьютере **одновременно** запущено множество процессов.
|
||||
* И **одна программа** может запустить **несколько параллельных процессов**.
|
||||
|
||||
Если Вы заглянете в "диспетчер задач" или "системный монитор" (или аналогичные инструменты) Вашей операционной системы, то увидите множество работающих процессов.
|
||||
Если вы заглянете в "диспетчер задач" или "системный монитор" (или аналогичные инструменты) вашей операционной системы, то увидите множество работающих процессов.
|
||||
|
||||
Вполне вероятно, что Вы увидите несколько процессов с одним и тем же названием браузерной программы (Firefox, Chrome, Edge и т. Д.). Обычно браузеры запускают один процесс на вкладку и вдобавок некоторые дополнительные процессы.
|
||||
Вполне вероятно, что вы увидите несколько процессов с одним и тем же названием браузерной программы (Firefox, Chrome, Edge и т. Д.). Обычно браузеры запускают один процесс на вкладку и вдобавок некоторые дополнительные процессы.
|
||||
|
||||
<img class="shadow" src="/img/deployment/concepts/image01.png">
|
||||
|
||||
|
|
@ -90,21 +90,21 @@
|
|||
|
||||
## Настройки запуска приложения
|
||||
|
||||
В большинстве случаев когда Вы создаёте веб-приложение, то желаете, чтоб оно **работало постоянно** и непрерывно, предоставляя клиентам доступ в любое время. Хотя иногда у Вас могут быть причины, чтоб оно запускалось только при определённых условиях.
|
||||
В большинстве случаев когда вы создаёте веб-приложение, то желаете, чтоб оно **работало постоянно** и непрерывно, предоставляя клиентам доступ в любое время. Хотя иногда у вас могут быть причины, чтоб оно запускалось только при определённых условиях.
|
||||
|
||||
### Удалённый сервер
|
||||
|
||||
Когда Вы настраиваете удалённый сервер (облачный сервер, виртуальную машину и т.п.), самое простое, что можно сделать, запустить Uvicorn (или его аналог) вручную, как Вы делаете при локальной разработке.
|
||||
Когда вы настраиваете удалённый сервер (облачный сервер, виртуальную машину и т.п.), самое простое, что можно сделать, запустить Uvicorn (или его аналог) вручную, как вы делаете при локальной разработке.
|
||||
|
||||
Это рабочий способ и он полезен **во время разработки**.
|
||||
|
||||
Но если Вы потеряете соединение с сервером, то не сможете отслеживать - работает ли всё ещё **запущенный Вами процесс**.
|
||||
Но если вы потеряете соединение с сервером, то не сможете отслеживать - работает ли всё ещё **запущенный Вами процесс**.
|
||||
|
||||
И если сервер перезагрузится (например, после обновления или каких-то действий облачного провайдера), Вы скорее всего **этого не заметите**, чтобы снова запустить процесс вручную. Вследствие этого Ваш API останется мёртвым. 😱
|
||||
И если сервер перезагрузится (например, после обновления или каких-то действий облачного провайдера), вы скорее всего **этого не заметите**, чтобы снова запустить процесс вручную. Вследствие этого Ваш API останется мёртвым. 😱
|
||||
|
||||
### Автоматический запуск программ
|
||||
|
||||
Вероятно Вы пожелаете, чтоб Ваша серверная программа (такая как Uvicorn) стартовала автоматически при включении сервера, без **человеческого вмешательства** и всегда могла управлять Вашим API (так как Uvicorn запускает приложение FastAPI).
|
||||
Вероятно вы захотите, чтоб Ваша серверная программа (такая, как Uvicorn) стартовала автоматически при включении сервера, без **человеческого вмешательства** и всегда могла управлять Вашим API (так как Uvicorn запускает приложение FastAPI).
|
||||
|
||||
### Отдельная программа
|
||||
|
||||
|
|
@ -127,7 +127,7 @@
|
|||
|
||||
## Перезапуск
|
||||
|
||||
Вы, вероятно, также пожелаете, чтоб Ваше приложение **перезапускалось**, если в нём произошёл сбой.
|
||||
Вы, вероятно, также захотите, чтоб ваше приложение **перезапускалось**, если в нём произошёл сбой.
|
||||
|
||||
### Мы ошибаемся
|
||||
|
||||
|
|
@ -137,7 +137,7 @@
|
|||
|
||||
### Небольшие ошибки обрабатываются автоматически
|
||||
|
||||
Когда Вы создаёте свои API на основе FastAPI и допускаете в коде ошибку, то FastAPI обычно остановит её распространение внутри одного запроса, при обработке которого она возникла. 🛡
|
||||
Когда вы создаёте свои API на основе FastAPI и допускаете в коде ошибку, то FastAPI обычно остановит её распространение внутри одного запроса, при обработке которого она возникла. 🛡
|
||||
|
||||
Клиент получит ошибку **500 Internal Server Error** в ответ на свой запрос, но приложение не сломается и будет продолжать работать с последующими запросами.
|
||||
|
||||
|
|
@ -152,11 +152,11 @@
|
|||
Для случаев, когда ошибки приводят к сбою в запущенном **процессе**, Вам понадобится добавить компонент, который **перезапустит** процесс хотя бы пару раз...
|
||||
|
||||
!!! tip "Заметка"
|
||||
... Если приложение падает сразу же после запуска, вероятно бесполезно его бесконечно перезапускать. Но полагаю, Вы заметите такое поведение во время разработки или, по крайней мере, сразу после развёртывания.
|
||||
... Если приложение падает сразу же после запуска, вероятно бесполезно его бесконечно перезапускать. Но полагаю, вы заметите такое поведение во время разработки или, по крайней мере, сразу после развёртывания.
|
||||
|
||||
Так что давайте сосредоточимся на конкретных случаях, когда приложение может полностью выйти из строя, но всё ещё есть смысл его запустить заново.
|
||||
|
||||
Возможно Вы захотите, чтоб был некий **внешний компонент**, ответственный за перезапуск Вашего приложения даже если уже не работает Uvicorn или Python. То есть ничего из того, что написано в Вашем коде внутри приложения, не может быть выполнено в принципе.
|
||||
Возможно вы захотите, чтоб был некий **внешний компонент**, ответственный за перезапуск вашего приложения даже если уже не работает Uvicorn или Python. То есть ничего из того, что написано в вашем коде внутри приложения, не может быть выполнено в принципе.
|
||||
|
||||
### Примеры инструментов для автоматического перезапуска
|
||||
|
||||
|
|
@ -181,7 +181,7 @@
|
|||
|
||||
### Множество процессов - Воркеры (Workers)
|
||||
|
||||
Если количество Ваших клиентов больше, чем может обслужить один процесс (допустим, что виртуальная машина не слишком мощная), но при этом Вам доступно **несколько ядер процессора**, то Вы можете запустить **несколько процессов** одного и того же приложения параллельно и распределить запросы между этими процессами.
|
||||
Если количество Ваших клиентов больше, чем может обслужить один процесс (допустим, что виртуальная машина не слишком мощная), но при этом Вам доступно **несколько ядер процессора**, то вы можете запустить **несколько процессов** одного и того же приложения параллельно и распределить запросы между этими процессами.
|
||||
|
||||
**Несколько запущенных процессов** одной и той же API-программы часто называют **воркерами**.
|
||||
|
||||
|
|
@ -197,11 +197,11 @@
|
|||
|
||||
Работающая программа загружает в память данные, необходимые для её работы, например, переменные содержащие модели машинного обучения или большие файлы. Каждая переменная **потребляет некоторое количество оперативной памяти (RAM)** сервера.
|
||||
|
||||
Обычно процессы **не делятся памятью друг с другом**. Сие означает, что каждый работающий процесс имеет свои данные, переменные и свой кусок памяти. И если для выполнения Вашего кода процессу нужно много памяти, то **каждый такой же процесс** запущенный дополнительно, потребует такого же количества памяти.
|
||||
Обычно процессы **не делятся памятью друг с другом**. Сие означает, что каждый работающий процесс имеет свои данные, переменные и свой кусок памяти. И если для выполнения вашего кода процессу нужно много памяти, то **каждый такой же процесс** запущенный дополнительно, потребует такого же количества памяти.
|
||||
|
||||
### Память сервера
|
||||
|
||||
Допустим, что Ваш код загружает модель машинного обучения **размером 1 ГБ**. Когда Вы запустите своё API как один процесс, он займёт в оперативной памяти не менее 1 ГБ. А если Вы запустите **4 таких же процесса** (4 воркера), то каждый из них займёт 1 ГБ оперативной памяти. В результате Вашему API потребуется **4 ГБ оперативной памяти (RAM)**.
|
||||
Допустим, что Ваш код загружает модель машинного обучения **размером 1 ГБ**. Когда вы запустите своё API как один процесс, он займёт в оперативной памяти не менее 1 ГБ. А если вы запустите **4 таких же процесса** (4 воркера), то каждый из них займёт 1 ГБ оперативной памяти. В результате вашему API потребуется **4 ГБ оперативной памяти (RAM)**.
|
||||
|
||||
И если Ваш удалённый сервер или виртуальная машина располагает только 3 ГБ памяти, то попытка загрузить в неё 4 ГБ данных вызовет проблемы. 🚨
|
||||
|
||||
|
|
@ -211,15 +211,15 @@
|
|||
|
||||
Менеджер процессов будет слушать определённый **сокет** (IP:порт) и передавать данные работающим процессам.
|
||||
|
||||
Каждый из этих процессов будет запускать Ваше приложение для обработки полученного **запроса** и возвращения вычисленного **ответа** и они будут использовать оперативную память.
|
||||
Каждый из этих процессов будет запускать ваше приложение для обработки полученного **запроса** и возвращения вычисленного **ответа** и они будут использовать оперативную память.
|
||||
|
||||
<img src="/img/deployment/concepts/process-ram.svg">
|
||||
|
||||
Безусловно, на этом же сервере будут работать и **другие процессы**, которые не относятся к Вашему приложению.
|
||||
Безусловно, на этом же сервере будут работать и **другие процессы**, которые не относятся к вашему приложению.
|
||||
|
||||
Интересная деталь - обычно в течение времени процент **использования центрального процессора (CPU)** каждым процессом может очень сильно **изменяться**, но объём занимаемой **оперативной памяти (RAM)** остаётся относительно **стабильным**.
|
||||
Интересная деталь заключается в том, что процент **использования центрального процессора (CPU)** каждым процессом может сильно меняться с течением времени, но объём занимаемой **оперативной памяти (RAM)** остаётся относительно **стабильным**.
|
||||
|
||||
Если у Вас есть API, который каждый раз выполняет сопоставимый объем вычислений, и у Вас много клиентов, то **загрузка процессора**, вероятно, *также будет стабильной* (вместо того, чтобы постоянно быстро увеличиваться и уменьшаться).
|
||||
Если у вас есть API, который каждый раз выполняет сопоставимый объем вычислений, и у вас много клиентов, то **загрузка процессора**, вероятно, *также будет стабильной* (вместо того, чтобы постоянно быстро увеличиваться и уменьшаться).
|
||||
|
||||
### Примеры стратегий и инструментов для запуска нескольких экземпляров приложения
|
||||
|
||||
|
|
@ -236,10 +236,10 @@
|
|||
* **Kubernetes** и аналогичные **контейнерные системы**
|
||||
* Какой-то компонент в **Kubernetes** будет слушать **IP:port**. Необходимое количество запущенных экземпляров приложения будет осуществляться посредством запуска **нескольких контейнеров**, в каждом из которых работает **один процесс Uvicorn**.
|
||||
* **Облачные сервисы**, которые позаботятся обо всём за Вас
|
||||
* Возможно, что облачный сервис умеет **управлять запуском дополнительных экземпляров приложения**. Вероятно, он потребует, чтоб Вы указали - какой **процесс** или **образ** следует клонировать. Скорее всего, Вы укажете **один процесс Uvicorn** и облачный сервис будет запускать его копии при необходимости.
|
||||
* Возможно, что облачный сервис умеет **управлять запуском дополнительных экземпляров приложения**. Вероятно, он потребует, чтоб вы указали - какой **процесс** или **образ** следует клонировать. Скорее всего, вы укажете **один процесс Uvicorn** и облачный сервис будет запускать его копии при необходимости.
|
||||
|
||||
!!! tip "Заметка"
|
||||
Если Вы не знаете, что такое **контейнеры**, Docker или Kubernetes, не переживайте.
|
||||
Если вы не знаете, что такое **контейнеры**, Docker или Kubernetes, не переживайте.
|
||||
|
||||
Я поведаю Вам о контейнерах, образах, Docker, Kubernetes и т.п. в главе: [FastAPI внутри контейнеров - Docker](./docker.md){.internal-link target=_blank}.
|
||||
|
||||
|
|
@ -253,18 +253,18 @@
|
|||
|
||||
Поэтому Вам нужен будет **один процесс**, выполняющий эти **подготовительные шаги** до запуска приложения.
|
||||
|
||||
Также Вам нужно будет убедиться, что этот процесс выполнил подготовительные шаги *даже* если впоследствии Вы запустите **несколько процессов** (несколько воркеров) самого приложения. Если бы эти шаги выполнялись в каждом **клонированном процессе**, они бы **дублировали** работу, пытаясь выполнить её **параллельно**. И если бы эта работа была бы чем-то деликатным, вроде миграции базы данных, то это может вызвать конфликты между ними.
|
||||
Также Вам нужно будет убедиться, что этот процесс выполнил подготовительные шаги *даже* если впоследствии вы запустите **несколько процессов** (несколько воркеров) самого приложения. Если бы эти шаги выполнялись в каждом **клонированном процессе**, они бы **дублировали** работу, пытаясь выполнить её **параллельно**. И если бы эта работа была бы чем-то деликатным, вроде миграции базы данных, то это может вызвать конфликты между ними.
|
||||
|
||||
Безусловно, возможны случаи, когда нет проблем при выполнении предварительной подготовки параллельно или несколько раз. Тогда Вам повезло, работать с ними намного проще.
|
||||
|
||||
!!! tip "Заметка"
|
||||
Имейте в виду, что в некоторых случаях запуск Вашего приложения **может не требовать каких-либо предварительных шагов вовсе**.
|
||||
Имейте в виду, что в некоторых случаях запуск вашего приложения **может не требовать каких-либо предварительных шагов вовсе**.
|
||||
|
||||
Что ж, тогда Вам не нужно беспокоиться об этом. 🤷
|
||||
|
||||
### Примеры стратегий запуска предварительных шагов
|
||||
|
||||
Существует **сильная зависимость** от того, как Вы **развёртываете свою систему**, запускаете программы, обрабатываете перезапуски и т.д.
|
||||
Существует **сильная зависимость** от того, как вы **развёртываете свою систему**, запускаете программы, обрабатываете перезапуски и т.д.
|
||||
|
||||
Вот некоторые возможные идеи:
|
||||
|
||||
|
|
@ -279,19 +279,19 @@
|
|||
|
||||
Ваш сервер располагает ресурсами, которые Ваши программы могут потреблять или **утилизировать**, а именно - время работы центрального процессора и объём оперативной памяти.
|
||||
|
||||
Как много системных ресурсов Вы предполагаете потребить/утилизировать? Если не задумываться, то можно ответить - "немного", но на самом деле Вы, вероятно, пожелаете использовать **максимально возможное количество**.
|
||||
Как много системных ресурсов вы предполагаете потребить/утилизировать? Если не задумываться, то можно ответить - "немного", но на самом деле Вы, вероятно, захотите использовать **максимально возможное количество**.
|
||||
|
||||
Если Вы платите за содержание трёх серверов, но используете лишь малую часть системных ресурсов каждого из них, то Вы **выбрасываете деньги на ветер**, а также **впустую тратите электроэнергию** и т.п.
|
||||
Если вы платите за содержание трёх серверов, но используете лишь малую часть системных ресурсов каждого из них, то вы **выбрасываете деньги на ветер**, а также **впустую тратите электроэнергию** и т.п.
|
||||
|
||||
В таком случае было бы лучше обойтись двумя серверами, но более полно утилизировать их ресурсы (центральный процессор, оперативную память, жёсткий диск, сети передачи данных и т.д).
|
||||
|
||||
С другой стороны, если Вы располагаете только двумя серверами и используете **на 100% их процессоры и память**, но какой-либо процесс запросит дополнительную память, то операционная система сервера будет использовать жёсткий диск для расширения оперативной памяти (а диск работает в тысячи раз медленнее), а то вовсе **упадёт**. Или если какому-то процессу понадобится произвести вычисления, то ему придётся подождать, пока процессор освободится.
|
||||
С другой стороны, если вы располагаете только двумя серверами и используете **на 100% их процессоры и память**, но какой-либо процесс запросит дополнительную память, то операционная система сервера будет использовать жёсткий диск для расширения оперативной памяти (а диск работает в тысячи раз медленнее), а то вовсе **упадёт**. Или если какому-то процессу понадобится произвести вычисления, то ему придётся подождать, пока процессор освободится.
|
||||
|
||||
В такой ситуации лучше подключить **ещё один сервер** и перераспределить процессы между серверами, чтоб всем **хватало памяти и процессорного времени**.
|
||||
|
||||
Также есть вероятность, что по какой-то причине возник **всплеск** запросов к Вашему API. Возможно, это был вирус, боты или другие сервисы начали пользоваться им. И для таких происшествий Вы можете захотеть иметь дополнительные ресурсы.
|
||||
Также есть вероятность, что по какой-то причине возник **всплеск** запросов к вашему API. Возможно, это был вирус, боты или другие сервисы начали пользоваться им. И для таких происшествий вы можете захотеть иметь дополнительные ресурсы.
|
||||
|
||||
При настройке логики развёртываний, Вы можете указать **целевое значение** утилизации ресурсов, допустим, **от 50% до 90%**. Обычно эти метрики и используют.
|
||||
При настройке логики развёртываний, вы можете указать **целевое значение** утилизации ресурсов, допустим, **от 50% до 90%**. Обычно эти метрики и используют.
|
||||
|
||||
Вы можете использовать простые инструменты, такие как `htop`, для отслеживания загрузки центрального процессора и оперативной памяти сервера, в том числе каждым процессом. Или более сложные системы мониторинга нескольких серверов.
|
||||
|
||||
|
|
@ -308,4 +308,4 @@
|
|||
|
||||
Осознание этих идей и того, как их применять, должно дать Вам интуитивное понимание, необходимое для принятия решений при настройке развертываний. 🤓
|
||||
|
||||
В следующих разделах я приведу более конкретные примеры возможных стратегий, которым Вы можете следовать. 🚀
|
||||
В следующих разделах я приведу более конкретные примеры возможных стратегий, которым вы можете следовать. 🚀
|
||||
|
|
|
|||
|
|
@ -70,19 +70,19 @@ Docker является одним оз основных инструменто
|
|||
|
||||
и т.п.
|
||||
|
||||
Использование подготовленных образов значительно упрощает **комбинирование** и использование разных инструментов. Например, Вы можете попытаться использовать новую базу данных. В большинстве случаев можно использовать **официальный образ** и всего лишь указать переменные окружения.
|
||||
Использование подготовленных образов значительно упрощает **комбинирование** и использование разных инструментов. Например, вы можете попытаться использовать новую базу данных. В большинстве случаев можно использовать **официальный образ** и всего лишь указать переменные окружения.
|
||||
|
||||
Таким образом, Вы можете изучить, что такое контейнеризация и Docker, и использовать полученные знания с разными инструментами и компонентами.
|
||||
Таким образом, вы можете изучить, что такое контейнеризация и Docker, и использовать полученные знания с разными инструментами и компонентами.
|
||||
|
||||
Так, Вы можете запустить одновременно **множество контейнеров** с базой данных, Python-приложением, веб-сервером, React-приложением и соединить их вместе через внутреннюю сеть.
|
||||
Так, вы можете запустить одновременно **множество контейнеров** с базой данных, Python-приложением, веб-сервером, React-приложением и соединить их вместе через внутреннюю сеть.
|
||||
|
||||
Все системы управления контейнерами (такие, как Docker или Kubernetes) имеют встроенные возможности для организации такого сетевого взаимодействия.
|
||||
|
||||
## Контейнеры и процессы
|
||||
|
||||
Обычно **образ контейнера** содержит метаданные предустановленной программы или команду, которую следует выполнить при запуске **контейнера**. Также он может содержать параметры, передаваемые предустановленной программе. Похоже на то, как если бы Вы запускали такую программу через терминал.
|
||||
Обычно **образ контейнера** содержит метаданные предустановленной программы или команду, которую следует выполнить при запуске **контейнера**. Также он может содержать параметры, передаваемые предустановленной программе. Похоже на то, как если бы вы запускали такую программу через терминал.
|
||||
|
||||
Когда **контейнер** запущен, он будет выполнять прописанные в нём команды и программы. Но Вы можете изменить его так, чтоб он выполнял другие команды и программы.
|
||||
Когда **контейнер** запущен, он будет выполнять прописанные в нём команды и программы. Но вы можете изменить его так, чтоб он выполнял другие команды и программы.
|
||||
|
||||
Контейнер буде работать до тех пор, пока выполняется его **главный процесс** (команда или программа).
|
||||
|
||||
|
|
@ -100,11 +100,11 @@ Docker является одним оз основных инструменто
|
|||
|
||||
* Использование с **Kubernetes** или аналогичным инструментом
|
||||
* Запуск в **Raspberry Pi**
|
||||
* Использование в облачных сервисах, запускающих образы контейнеров для Вас и т.п.
|
||||
* Использование в облачных сервисах, запускающих образы контейнеров для вас и т.п.
|
||||
|
||||
### Установить зависимости
|
||||
|
||||
Обычно Вашему приложению необходимы **дополнительные библиотеки**, список которых находится в отдельном файле.
|
||||
Обычно вашему приложению необходимы **дополнительные библиотеки**, список которых находится в отдельном файле.
|
||||
|
||||
На название и содержание такого файла влияет выбранный Вами инструмент **установки** этих библиотек (зависимостей).
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ Successfully installed fastapi pydantic uvicorn
|
|||
!!! info "Информация"
|
||||
Существуют и другие инструменты управления зависимостями.
|
||||
|
||||
В этом же разделе, но позже, я покажу Вам пример использования Poetry. 👇
|
||||
В этом же разделе, но позже, я покажу вам пример использования Poetry. 👇
|
||||
|
||||
### Создать приложение **FastAPI**
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
Сначала копируйте **только** файл с зависимостями.
|
||||
|
||||
Этот файл **изменяется довольно редко**, Docker ищет изменения при постройке образа и если не находит, то использует **кэш**, в котором хранятся предыдущии версии сборки образа.
|
||||
Этот файл **изменяется довольно редко**, Docker ищет изменения при постройке образа и если не находит, то использует **кэш**, в котором хранятся предыдущие версии сборки образа.
|
||||
|
||||
4. Установите библиотеки перечисленные в файле с зависимостями.
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
Ка и в предыдущем шаге с копированием файла, этот шаг также будет использовать **кэш Docker** в случае отсутствия изменений.
|
||||
|
||||
Использрвание кэша, особенно на этом шаге, позволит Вам **сэкономить** кучу времени при повторной сборке образа, так как зависимости будут сохранены в кеше, а не **загружаться и устанавливаться каждый раз**.
|
||||
Использование кэша, особенно на этом шаге, позволит вам **сэкономить** кучу времени при повторной сборке образа, так как зависимости будут сохранены в кеше, а не **загружаться и устанавливаться каждый раз**.
|
||||
|
||||
5. Скопируйте директорию `./app` внутрь директории `/code` (в контейнере).
|
||||
|
||||
|
|
@ -216,11 +216,11 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
6. Укажите **команду**, запускающую сервер `uvicorn`.
|
||||
|
||||
`CMD` принимает список строк, разделённых запятыми, но при выполнении объединит их через пробел, собрав из них одну команду, которую Вы могли бы написать в терминале.
|
||||
`CMD` принимает список строк, разделённых запятыми, но при выполнении объединит их через пробел, собрав из них одну команду, которую вы могли бы написать в терминале.
|
||||
|
||||
Эта команда будет выполнена в **текущей рабочей директории**, а именно в директории `/code`, котоая указана командой `WORKDIR /code`.
|
||||
Эта команда будет выполнена в **текущей рабочей директории**, а именно в директории `/code`, которая указана в команде `WORKDIR /code`.
|
||||
|
||||
Так как команда выполняется внутрии директории `/code`, в которую мы поместили папку `./app` с приложением, то **Uvicorn** сможет найти и **импортировать** объект `app` из файла `app.main`.
|
||||
Так как команда выполняется внутри директории `/code`, в которую мы поместили папку `./app` с приложением, то **Uvicorn** сможет найти и **импортировать** объект `app` из файла `app.main`.
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Если ткнёте на кружок с плюсом, то увидите пояснения. 👆
|
||||
|
|
@ -238,7 +238,7 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
#### Использование прокси-сервера
|
||||
|
||||
Если Вы запускаете контейнер за прокси-сервером завершения TLS (балансирующего нагрузку), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`, которая укажет Uvicorn, что он работает позади прокси-сервера и может доверять заголовкам отправляемым им.
|
||||
Если вы запускаете контейнер за прокси-сервером завершения TLS (балансирующего нагрузку), таким как Nginx или Traefik, добавьте опцию `--proxy-headers`, которая укажет Uvicorn, что он работает позади прокси-сервера и может доверять заголовкам отправляемым им.
|
||||
|
||||
```Dockerfile
|
||||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
|
||||
|
|
@ -269,7 +269,7 @@ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
|||
|
||||
Для загрузки и установки необходимых библиотек **может понадобиться несколько минут**, но использование **кэша** занимает несколько **секунд** максимум.
|
||||
|
||||
И так как во время разработки Вы будете часто пересобирать контейнер для проверки работоспособности внесённых изменений, то сэкономленные минуты сложатся в часы, а то и дни.
|
||||
И так как во время разработки вы будете часто пересобирать контейнер для проверки работоспособности внесённых изменений, то сэкономленные минуты сложатся в часы, а то и дни.
|
||||
|
||||
Так как папка с кодом приложения **изменяется чаще всего**, то мы расположили её в конце `Dockerfile`, ведь после внесённых в код изменений кэш не будет использован на этом и следующих шагах.
|
||||
|
||||
|
|
@ -301,7 +301,7 @@ $ docker build -t myimage .
|
|||
|
||||
### Запуск Docker-контейнера
|
||||
|
||||
* Запустите контейнер, основанный на Вашем образе:
|
||||
* Запустите контейнер, основанный на вашем образе:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
|
|
@ -315,7 +315,7 @@ $ docker run -d --name mycontainer -p 80:80 myimage
|
|||
|
||||
Вы можете проверить, что Ваш Docker-контейнер работает перейдя по ссылке: <a href="http://192.168.99.100/items/5?q=somequery" class="external-link" target="_blank">http://192.168.99.100/items/5?q=somequery</a> или <a href="http://127.0.0.1/items/5?q=somequery" class="external-link" target="_blank">http://127.0.0.1/items/5?q=somequery</a> (или похожей, которую использует Ваш Docker-хост).
|
||||
|
||||
Там Вы увидите:
|
||||
Там вы увидите:
|
||||
|
||||
```JSON
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
|
|
@ -325,21 +325,21 @@ $ docker run -d --name mycontainer -p 80:80 myimage
|
|||
|
||||
Теперь перейдите по ссылке <a href="http://192.168.99.100/docs" class="external-link" target="_blank">http://192.168.99.100/docs</a> или <a href="http://127.0.0.1/docs" class="external-link" target="_blank">http://127.0.0.1/docs</a> (или похожей, которую использует Ваш Docker-хост).
|
||||
|
||||
Здесь Вы увидите автоматическую интерактивную документацию API (предоставляемую <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
|
||||
Здесь вы увидите автоматическую интерактивную документацию API (предоставляемую <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>):
|
||||
|
||||

|
||||
|
||||
## Альтернативная документация API
|
||||
|
||||
Также Вы можете перейти по ссылке <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> or <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (или похожей, которую использует Ваш Docker-хост).
|
||||
Также вы можете перейти по ссылке <a href="http://192.168.99.100/redoc" class="external-link" target="_blank">http://192.168.99.100/redoc</a> or <a href="http://127.0.0.1/redoc" class="external-link" target="_blank">http://127.0.0.1/redoc</a> (или похожей, которую использует Ваш Docker-хост).
|
||||
|
||||
Здесь Вы увидите альтернативную автоматическую документацию API (предоставляемую <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
|
||||
Здесь вы увидите альтернативную автоматическую документацию API (предоставляемую <a href="https://github.com/Rebilly/ReDoc" class="external-link" target="_blank">ReDoc</a>):
|
||||
|
||||

|
||||
|
||||
## Создание Docker-образа на основе однофайлового приложения FastAPI
|
||||
|
||||
Если Ваше приложение FastAPI помещено в один файл, например, `main.py` и структура Ваших файлов похожа на эту:
|
||||
Если ваше приложение FastAPI помещено в один файл, например, `main.py` и структура Ваших файлов похожа на эту:
|
||||
|
||||
```
|
||||
.
|
||||
|
|
@ -376,7 +376,7 @@ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
Давайте вспомним о [Концепциях развёртывания](./concepts.md){.internal-link target=_blank} и применим их к контейнерам.
|
||||
|
||||
Контейнеры - это, в основном, инструмент упрощающий **сборку и развёртывание** приложения и они не обязыают к применению какой-то определённой **концепции развёртывания**, а значит мы можем выбирать нужную стратегию.
|
||||
Контейнеры - это, в основном, инструмент упрощающий **сборку и развёртывание** приложения и они не обязывают к применению какой-то определённой **концепции развёртывания**, а значит мы можем выбирать нужную стратегию.
|
||||
|
||||
**Хорошая новость** в том, что независимо от выбранной стратегии, мы всё равно можем покрыть все концепции развёртывания. 🎉
|
||||
|
||||
|
|
@ -412,7 +412,7 @@ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
## Запуск нескольких экземпляров приложения - Указание количества процессов
|
||||
|
||||
Если у Вас есть <abbr title="Несколько серверов настроенных для совместной работы.">кластер</abbr> машин под управлением **Kubernetes**, Docker Swarm Mode, Nomad или аналогичной сложной системой оркестрации контейнеров, скорее всего, вместо использования менеджера процессов (типа Gunicorn и его воркеры) в каждом контейнере, Вы захотите **управлять количеством запущенных экземпляров приложения** на **уровне кластера**.
|
||||
Если у вас есть <abbr title="Несколько серверов настроенных для совместной работы.">кластер</abbr> машин под управлением **Kubernetes**, Docker Swarm Mode, Nomad или аналогичной сложной системой оркестрации контейнеров, скорее всего, вместо использования менеджера процессов (типа Gunicorn и его воркеры) в каждом контейнере, вы захотите **управлять количеством запущенных экземпляров приложения** на **уровне кластера**.
|
||||
|
||||
В любую из этих систем управления контейнерами обычно встроен способ управления **количеством запущенных контейнеров** для распределения **нагрузки** от входящих запросов на **уровне кластера**.
|
||||
|
||||
|
|
@ -427,17 +427,17 @@ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
!!! tip "Подсказка"
|
||||
**Прокси-сервер завершения работы TLS** одновременно может быть **балансировщиком нагрузки**.
|
||||
|
||||
Система оркестрации, которую Вы используете для запуска и управления контейнерами, имеет встроенный инструмент **сетевого взаимодействия** (например, для передачи HTTP-запросов) между контейнерами с Вашими приложениями и **балансировщиком нагрузки** (который также может быть **прокси-сервером**).
|
||||
Система оркестрации, которую вы используете для запуска и управления контейнерами, имеет встроенный инструмент **сетевого взаимодействия** (например, для передачи HTTP-запросов) между контейнерами с Вашими приложениями и **балансировщиком нагрузки** (который также может быть **прокси-сервером**).
|
||||
|
||||
### Один балансировщик - Множество контейнеров
|
||||
|
||||
При работе с **Kubernetes** или аналогичными системами оркестрации использование их внутреннней сети позволяет иметь один **балансировщик нагрузки**, который прослушивает **главный** порт и передаёт запросы **множеству запущенных контейнеров** с Вашими приложениями.
|
||||
При работе с **Kubernetes** или аналогичными системами оркестрации использование их внутренней сети позволяет иметь один **балансировщик нагрузки**, который прослушивает **главный** порт и передаёт запросы **множеству запущенных контейнеров** с Вашими приложениями.
|
||||
|
||||
В каждом из контейнеров обычно работает **только один процесс** (например, процесс Uvicorn управляющий Вашим приложением FastAPI). Контейнеры могут быть **идентичными**, запущенными на основе одного и того же образа, но у каждого будут свои отдельные процесс, память и т.п. Таким образом мы получаем преимущества **распараллеливания** работы по **разным ядрам** процессора или даже **разным машинам**.
|
||||
|
||||
Система управления контейнерами с **балансировщиком нагрузки** будет **распределять запросы** к контейнерам с приложениями **по очереди**. То есть каждый запрос будет обработан одним из множества **одинаковых контейнеров** с одним и тем же приложением.
|
||||
|
||||
**Балансировщик нагрузки** может обрабатывать запросы к *разным* приложениям, расположенным в Вашем кластере (например, если у них разные домены или префиксы пути) и передавать запросы нужному контейнеру с требуемым приложением.
|
||||
**Балансировщик нагрузки** может обрабатывать запросы к *разным* приложениям, расположенным в вашем кластере (например, если у них разные домены или префиксы пути) и передавать запросы нужному контейнеру с требуемым приложением.
|
||||
|
||||
### Один процесс на контейнер
|
||||
|
||||
|
|
@ -449,35 +449,35 @@ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
### <a name="special-cases"></a>Множество процессов внутри контейнера для особых случаев</a>
|
||||
|
||||
Безусловно, бывают **особые случаи**, когда может понадобится внутри контейнера запускать **менеджер процессов Gunicorn**, управляющий несколькими **процессами Uvicorn**.
|
||||
Безусловно, бывают **особые случаи**, когда может понадобиться внутри контейнера запускать **менеджер процессов Gunicorn**, управляющий несколькими **процессами Uvicorn**.
|
||||
|
||||
Для таких случаев Вы можете использовать **официальный Docker-образ** (прим. пер: - *здесь и далее на этой странице, если Вы встретите сочетание "официальный Docker-образ" без уточнений, то автор имеет в виду именно предоставляемый им образ*), где в качестве менеджера процессов используется **Gunicorn**, запускающий несколько **процессов Uvicorn** и некоторые настройки по умолчанию, автоматически устанавливающие количество запущенных процессов в зависимости от количества ядер Вашего процессора. Я расскажу Вам об этом подробнее тут: [Официальный Docker-образ со встроенными Gunicorn и Uvicorn](#docker-gunicorn-uvicorn).
|
||||
Для таких случаев вы можете использовать **официальный Docker-образ** (прим. пер: - *здесь и далее на этой странице, если вы встретите сочетание "официальный Docker-образ" без уточнений, то автор имеет в виду именно предоставляемый им образ*), где в качестве менеджера процессов используется **Gunicorn**, запускающий несколько **процессов Uvicorn** и некоторые настройки по умолчанию, автоматически устанавливающие количество запущенных процессов в зависимости от количества ядер вашего процессора. Я расскажу вам об этом подробнее тут: [Официальный Docker-образ со встроенными Gunicorn и Uvicorn](#docker-gunicorn-uvicorn).
|
||||
|
||||
Некоторые примеры подобных случаев:
|
||||
|
||||
#### Простое приложение
|
||||
|
||||
Вы можете использовать менеджер процессов внутри контейнера, если Ваше приложение **настолько простое**, что у Вас нет необходимости (по крайней мере, пока нет) в тщательных настройках количества процессов и Вам достаточно имеющихся настроек по умолчанию (если используется официальный Docker-образ) для запуска приложения **только на одном сервере**, а не в кластере.
|
||||
Вы можете использовать менеджер процессов внутри контейнера, если ваше приложение **настолько простое**, что у вас нет необходимости (по крайней мере, пока нет) в тщательных настройках количества процессов и вам достаточно имеющихся настроек по умолчанию (если используется официальный Docker-образ) для запуска приложения **только на одном сервере**, а не в кластере.
|
||||
|
||||
#### Docker Compose
|
||||
|
||||
С помощью **Docker Compose** можно разворачивать несколько контейнеров на **одном сервере** (не кластере), но при это у Вас не будет простого способа управления количеством запущенных контейнеров с одновременным сохранением общей сети и **балансировки нагрузки**.
|
||||
С помощью **Docker Compose** можно разворачивать несколько контейнеров на **одном сервере** (не кластере), но при это у вас не будет простого способа управления количеством запущенных контейнеров с одновременным сохранением общей сети и **балансировки нагрузки**.
|
||||
|
||||
В этом случае можно использовать **менеджер процессов**, управляющий **несколькими процессами**, внутри **одного контейнера**.
|
||||
|
||||
#### Prometheus и прочие причины
|
||||
|
||||
У Вас могут быть и **другие причины**, когда использование **множества процессов** внутри **одного контейнера** будет проще, нежели запуск **нескольких контейнеров** с **единственным процессом** в каждом из них.
|
||||
У вас могут быть и **другие причины**, когда использование **множества процессов** внутри **одного контейнера** будет проще, нежели запуск **нескольких контейнеров** с **единственным процессом** в каждом из них.
|
||||
|
||||
Например (в зависимости от конфигурации), у Вас могут быть инструменты подобные экспортёру Prometheus, которые должны иметь доступ к **каждому запросу** приходящему в контейнер.
|
||||
Например (в зависимости от конфигурации), у вас могут быть инструменты подобные экспортёру Prometheus, которые должны иметь доступ к **каждому запросу** приходящему в контейнер.
|
||||
|
||||
Если у Вас будет **несколько контейнеров**, то Prometheus, по умолчанию, **при сборе метрик** получит их **только с одного контейнера**, который обрабатывает конкретный запрос, вместо **сбора метрик** со всех работающих контейнеров.
|
||||
Если у вас будет **несколько контейнеров**, то Prometheus, по умолчанию, **при сборе метрик** получит их **только с одного контейнера**, который обрабатывает конкретный запрос, вместо **сбора метрик** со всех работающих контейнеров.
|
||||
|
||||
В таком случае может быть проще иметь **один контейнер** со **множеством процессов**, с нужным инструментом (таким как экспортёр Prometheus) в этом же контейнере и собирающем метрики со всех внутренних процессов этого контейнера.
|
||||
|
||||
---
|
||||
|
||||
Самое главное - **ни одно** из перечисленных правил не является **высеченным в камне** и Вы не обязаны слепо их повторять. Вы можете использовать эти идеи при **рассмотрении Вашего конкретного случая** и самостоятельно решать, какая из концепции подходит лучше:
|
||||
Самое главное - **ни одно** из перечисленных правил не является **высеченным на камне** и вы не обязаны слепо их повторять. вы можете использовать эти идеи при **рассмотрении вашего конкретного случая** и самостоятельно решать, какая из концепции подходит лучше:
|
||||
|
||||
* Использование более безопасного протокола HTTPS
|
||||
* Настройки запуска приложения
|
||||
|
|
@ -488,41 +488,41 @@ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
## Управление памятью
|
||||
|
||||
При **запуске одного процесса на контейнер** Вы получаете относительно понятный, стабильный и ограниченный объём памяти, потребляемый одним контейнером.
|
||||
При **запуске одного процесса на контейнер** вы получаете относительно понятный, стабильный и ограниченный объём памяти, потребляемый одним контейнером.
|
||||
|
||||
Вы можете установить аналогичные ограничения по памяти при конфигурировании своей системы управления контейнерами (например, **Kubernetes**). Таким образом система сможет **изменять количество контейнеров** на **доступных ей машинах** приводя в соответствие количество памяти нужной контейнерам с количеством памяти доступной в кластере (наборе доступных машин).
|
||||
|
||||
Если у Вас **простенькое** приложение, вероятно у Вас не будет **необходимости** устанавливать жёсткие ограничения на выделяемую ему память. Но если приложение **использует много памяти** (например, оно использует модели **машинного обучения**), Вам следует проверить, как много памяти ему требуется и отрегулировать **количество контейнеров** запущенных на **каждой машине** (может быть даже добавить машин в кластер).
|
||||
Если у вас **простенькое** приложение, вероятно у вас не будет **необходимости** устанавливать жёсткие ограничения на выделяемую ему память. Но если приложение **использует много памяти** (например, оно использует модели **машинного обучения**), вам следует проверить, как много памяти ему требуется и отрегулировать **количество контейнеров** запущенных на **каждой машине** (может быть даже добавить машин в кластер).
|
||||
|
||||
Если Вы запускаете **несколько процессов в контейнере**, то должны быть уверены, что эти процессы не **займут памяти больше**, чем доступно для контейнера.
|
||||
Если вы запускаете **несколько процессов в контейнере**, то должны быть уверены, что эти процессы не **займут памяти больше**, чем доступно для контейнера.
|
||||
|
||||
## Подготовительные шаги при запуске контейнеров
|
||||
|
||||
Есть два основных подхода, которые Вы можете использовать при запуске контейнеров (Docker, Kubernetes и т.п.).
|
||||
Есть два основных подхода, которые вы можете использовать при запуске контейнеров (Docker, Kubernetes и т.п.).
|
||||
|
||||
### Множество контейнеров
|
||||
|
||||
Когда Вы запускаете **множество контейнеров**, в каждом из которых работает **только один процесс** (например, в кластере **Kubernetes**), может возникнуть необходимость иметь **отдельный контейнер**, который осуществит **предварительные шаги перед запуском** остальных контейнеров (например, применяет миграции к базе данных).
|
||||
Когда вы запускаете **множество контейнеров**, в каждом из которых работает **только один процесс** (например, в кластере **Kubernetes**), может возникнуть необходимость иметь **отдельный контейнер**, который осуществит **предварительные шаги перед запуском** остальных контейнеров (например, применяет миграции к базе данных).
|
||||
|
||||
!!! info "Информация"
|
||||
При использовании Kubernetes, это может быть <a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/" class="external-link" target="_blank">Инициализирующий контейнер</a>.
|
||||
|
||||
При отсутствии такой необходимости (допустим, не нужно применять миграции к базе данных, а только проверить, что она готова принимать соединения), Вы можете проводить такую проверку в каждом контейнере перед запуском его основного процесса и запускать все контейнеры **одновременно**.
|
||||
При отсутствии такой необходимости (допустим, не нужно применять миграции к базе данных, а только проверить, что она готова принимать соединения), вы можете проводить такую проверку в каждом контейнере перед запуском его основного процесса и запускать все контейнеры **одновременно**.
|
||||
|
||||
### Только один контейнер
|
||||
|
||||
Если у Вас несложное приложение для работы которого достаточно **одного контейнера**, но в котором работает **несколько процессов** (или один процесс), то прохождение предварительных шагов можно осуществить в этом же контейнере до запуска основного процесса. Официальный Docker-образ поддерживает такие действия.
|
||||
Если у вас несложное приложение для работы которого достаточно **одного контейнера**, но в котором работает **несколько процессов** (или один процесс), то прохождение предварительных шагов можно осуществить в этом же контейнере до запуска основного процесса. Официальный Docker-образ поддерживает такие действия.
|
||||
|
||||
## Официальный Docker-образ с Gunicorn и Uvicorn
|
||||
|
||||
Я подготовил для Вас Docker-образ, в который включён Gunicorn управляющий процессами (воркерами) Uvicorn, в соответствии с концепциями рассмотренными в предыдущей главе: [Рабочие процессы сервера (воркеры) - Gunicorn совместно с Uvicorn](./server-workers.md){.internal-link target=_blank}.
|
||||
Я подготовил для вас Docker-образ, в который включён Gunicorn управляющий процессами (воркерами) Uvicorn, в соответствии с концепциями рассмотренными в предыдущей главе: [Рабочие процессы сервера (воркеры) - Gunicorn совместно с Uvicorn](./server-workers.md){.internal-link target=_blank}.
|
||||
|
||||
Этот образ может быть полезен для ситуаций описанных тут: [Множество процессов внутри контейнера для особых случаев](#special-cases).
|
||||
|
||||
* <a href="https://github.com/tiangolo/uvicorn-gunicorn-fastapi-docker" class="external-link" target="_blank">tiangolo/uvicorn-gunicorn-fastapi</a>.
|
||||
|
||||
!!! warning "Предупреждение"
|
||||
Скорее всего у Вас **нет необходимости** в использовании этого образа или подобного ему и лучше создать свой образ с нуля как описано тут: [Создать Docker-образ для FastAPI](#docker-fastapi).
|
||||
Скорее всего у вас **нет необходимости** в использовании этого образа или подобного ему и лучше создать свой образ с нуля как описано тут: [Создать Docker-образ для FastAPI](#docker-fastapi).
|
||||
|
||||
В этом образе есть **автоматический** механизм подстройки для запуска **необходимого количества процессов** в соответствии с доступным количеством ядер процессора.
|
||||
|
||||
|
|
@ -539,11 +539,11 @@ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
Это означает, что он будет пытаться **выжать** из процессора как можно больше **производительности**.
|
||||
|
||||
Но Вы можете изменять и обновлять конфигурацию с помощью **переменных окружения** и т.п.
|
||||
Но вы можете изменять и обновлять конфигурацию с помощью **переменных окружения** и т.п.
|
||||
|
||||
Поскольку количество процессов зависит от процессора, на котором работает контейнер, **объём потребляемой памяти** также будет зависеть от этого.
|
||||
|
||||
А значит, если Вашему приложению требуется много оперативной памяти (например, оно использует модели машинного обучения) и Ваш сервер имеет центральный процессор с большим количеством ядер, но **не слишком большим объёмом оперативной памяти**, то может дойти до того, что контейнер попытается занять памяти больше, чем доступно, из-за чего будет падение производительности (или сервер вовсе упадёт). 🚨
|
||||
А значит, если вашему приложению требуется много оперативной памяти (например, оно использует модели машинного обучения) и Ваш сервер имеет центральный процессор с большим количеством ядер, но **не слишком большим объёмом оперативной памяти**, то может дойти до того, что контейнер попытается занять памяти больше, чем доступно, из-за чего будет падение производительности (или сервер вовсе упадёт). 🚨
|
||||
|
||||
|
||||
### Написание `Dockerfile`
|
||||
|
|
@ -562,7 +562,7 @@ COPY ./app /app
|
|||
|
||||
### Большие приложения
|
||||
|
||||
Если Вы успели ознакомиться с разделом [Приложения содержащие много файлов](../tutorial/bigger-applications.md){.internal-link target=_blank}, состоящие из множества файлов, Ваш Dockerfile может выглядеть так:
|
||||
Если вы успели ознакомиться с разделом [Приложения содержащие много файлов](../tutorial/bigger-applications.md){.internal-link target=_blank}, состоящие из множества файлов, Ваш Dockerfile может выглядеть так:
|
||||
|
||||
```Dockerfile hl_lines="7"
|
||||
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9
|
||||
|
|
@ -576,9 +576,9 @@ COPY ./app /app/app
|
|||
|
||||
### Как им пользоваться
|
||||
|
||||
Если Вы используете **Kubernetes** (или что-то вроде того), скорее всего Вам **не нужно** использовать официальный Docker-образ (или другой похожий) в качестве основы, так как управление **количеством запущенных контейнеров** должно быть настроено на уровне кластера. В таком случае лучше **создать образ с нуля**, как описано в разделе Создать [Docker-образ для FastAPI](#docker-fastapi).
|
||||
Если вы используете **Kubernetes** (или что-то вроде того), скорее всего вам **не нужно** использовать официальный Docker-образ (или другой похожий) в качестве основы, так как управление **количеством запущенных контейнеров** должно быть настроено на уровне кластера. В таком случае лучше **создать образ с нуля**, как описано в разделе Создать [Docker-образ для FastAPI](#docker-fastapi).
|
||||
|
||||
Официальный образ может быть полезен в отдельных случаях, описанных выше в разделе [Множество процессов внутри контейнера для особых случаев](#special-cases). Например, если Ваше приложение **достаточно простое**, не требует запуска в кластере и способно уместиться в один контейнер, то его настройки по умолчанию будут работать довольно хорошо. Или же Вы развертываете его с помощью **Docker Compose**, работаете на одном сервере и т. д
|
||||
Официальный образ может быть полезен в отдельных случаях, описанных выше в разделе [Множество процессов внутри контейнера для особых случаев](#special-cases). Например, если ваше приложение **достаточно простое**, не требует запуска в кластере и способно уместиться в один контейнер, то его настройки по умолчанию будут работать довольно хорошо. Или же вы развертываете его с помощью **Docker Compose**, работаете на одном сервере и т. д
|
||||
|
||||
## Развёртывание образа контейнера
|
||||
|
||||
|
|
@ -590,11 +590,11 @@ COPY ./app /app/app
|
|||
* С использованием **Kubernetes** в кластере
|
||||
* С использованием режима Docker Swarm в кластере
|
||||
* С использованием других инструментов, таких как Nomad
|
||||
* С использованием облачного сервиса, который будет управлять разворачиванием Вашего контейнера
|
||||
* С использованием облачного сервиса, который будет управлять разворачиванием вашего контейнера
|
||||
|
||||
## Docker-образ и Poetry
|
||||
|
||||
Если Вы пользуетесь <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> для управления зависимостями Вашего проекта, то можете использовать многоэтапную сборку образа:
|
||||
Если вы пользуетесь <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> для управления зависимостями вашего проекта, то можете использовать многоэтапную сборку образа:
|
||||
|
||||
```{ .dockerfile .annotate }
|
||||
# (1)
|
||||
|
|
@ -664,19 +664,19 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
|||
|
||||
**Этапы сборки Docker-образа** являются частью `Dockerfile` и работают как **временные образы контейнеров**. Они нужны только для создания файлов, используемых в дальнейших этапах.
|
||||
|
||||
Первый этап был нужен только для **установки Poetry** и **создания файла `requirements.txt`**, в которым прописаны зависимости Вашего проекта, взятые из файла `pyproject.toml`.
|
||||
Первый этап был нужен только для **установки Poetry** и **создания файла `requirements.txt`**, в которым прописаны зависимости вашего проекта, взятые из файла `pyproject.toml`.
|
||||
|
||||
На **следующем этапе** `pip` будет использовать файл `requirements.txt`.
|
||||
|
||||
В итоговом образе будет содержаться **только последний этап сборки**, предыдущие этапы будут отброшены.
|
||||
|
||||
При использовании Poetry, имеет смысл использовать **многоэтапную сборку Docker-образа**, потому что на самом деле Вам не нужен Poetry и его зависимости в окончательном образе контейнера, Вам **нужен только** сгенерированный файл `requirements.txt` для установки зависимостей Вашего проекта.
|
||||
При использовании Poetry, имеет смысл использовать **многоэтапную сборку Docker-образа**, потому что на самом деле вам не нужен Poetry и его зависимости в окончательном образе контейнера, вам **нужен только** сгенерированный файл `requirements.txt` для установки зависимостей вашего проекта.
|
||||
|
||||
А на последнем этапе, придерживаясь описанных ранее правил, создаётся итоговый образ
|
||||
|
||||
### Использование прокси-сервера завершения TLS и Poetry
|
||||
|
||||
И снова повторюсь, если используете прокси-сервер (балансировщик нагрузки), такой, как Nginx или Traefik, добавьте в команду запуска опцию `--proxy-headers`:
|
||||
И снова повторюсь, если используете прокси-сервер (балансировщик нагрузки), такой как Nginx или Traefik, добавьте в команду запуска опцию `--proxy-headers`:
|
||||
|
||||
```Dockerfile
|
||||
CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port", "80"]
|
||||
|
|
@ -695,6 +695,6 @@ CMD ["uvicorn", "app.main:app", "--proxy-headers", "--host", "0.0.0.0", "--port"
|
|||
|
||||
В большинстве случаев Вам, вероятно, не нужно использовать какой-либо базовый образ, **лучше создать образ контейнера с нуля** на основе официального Docker-образа Python.
|
||||
|
||||
Позаботившись о **порядке написания** инструкций в `Dockerfile`, Вы сможете использовать **кэш Docker'а**, **минимизировав время сборки**, максимально повысив свою производительность (и избежать скуки). 😎
|
||||
Позаботившись о **порядке написания** инструкций в `Dockerfile`, вы сможете использовать **кэш Docker'а**, **минимизировав время сборки**, максимально повысив свою производительность (и не заскучать). 😎
|
||||
|
||||
В некоторых особых случаях вы можете использовать официальный образ Docker для FastAPI. 🤓
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
# Об HTTPS
|
||||
|
||||
Обычно представляется, что HTTPS это некая опция, которая либо "включена", либо нет.
|
||||
Обычно представляется, что HTTPS это некая опция, которая либо "включена", либо нет.
|
||||
|
||||
Но всё несколько сложнее.
|
||||
|
||||
!!! tip "Заметка"
|
||||
Если Вы торопитесь или Вам не интересно, можете перейти на следующую страницу этого пошагового руководства по размещению приложений на серверах с использованием различных технологий.
|
||||
Если вы торопитесь или вам не интересно, можете перейти на следующую страницу этого пошагового руководства по размещению приложений на серверах с использованием различных технологий.
|
||||
|
||||
Чтобы **изучить основы HTTPS** для клиента, перейдите по ссылке <a href="https://howhttps.works/" class="external-link" target="_blank">https://howhttps.works/</a>.
|
||||
|
||||
|
|
@ -22,8 +22,8 @@
|
|||
* **TCP не знает о "доменах"**, но знает об IP-адресах.
|
||||
* Информация о **запрашиваемом домене** извлекается из запроса **на уровне HTTP**.
|
||||
* **Сертификаты HTTPS** "сертифицируют" **конкретный домен**, но проверка сертификатов и шифрование данных происходит на уровне протокола TCP, то есть **до того**, как станет известен домен-получатель данных.
|
||||
* **По умолчанию** это означает, что у Вас может быть **только один сертификат HTTPS на один IP-адрес**.
|
||||
* Не важно, насколько большой у Вас сервер и насколько маленькие приложения на нём могут быть.
|
||||
* **По умолчанию** это означает, что у вас может быть **только один сертификат HTTPS на один IP-адрес**.
|
||||
* Не важно, насколько большой у вас сервер и насколько маленькие приложения на нём могут быть.
|
||||
* Однако, у этой проблемы есть **решение**.
|
||||
* Существует **расширение** протокола **TLS** (который работает на уровне TCP, то есть до HTTP) называемое **<a href="https://en.wikipedia.org/wiki/Server_Name_Indication" class="external-link" target="_blank"><abbr title="Указание имени сервера">SNI</abbr></a>**.
|
||||
* Расширение SNI позволяет одному серверу (с **одним IP-адресом**) иметь **несколько сертификатов HTTPS** и обслуживать **множество HTTPS-доменов/приложений**.
|
||||
|
|
@ -35,12 +35,12 @@
|
|||
|
||||
* получение **зашифрованных HTTPS-запросов**
|
||||
* отправка **расшифрованных HTTP запросов** в соответствующее HTTP-приложение, работающее на том же сервере (в нашем случае, это приложение **FastAPI**)
|
||||
* получние **HTTP-ответа** от приложения
|
||||
* получение **HTTP-ответа** от приложения
|
||||
* **шифрование ответа** используя подходящий **сертификат HTTPS**
|
||||
* отправка зашифрованного **HTTPS-ответа клиенту**.
|
||||
Такой сервер часто называют **<a href="https://en.wikipedia.org/wiki/TLS_termination_proxy" class="external-link" target="_blank">Прокси-сервер завершения работы TLS</a>** или просто "прокси-сервер".
|
||||
|
||||
Вот некоторые варианты, которые Вы можете использовать в качестве такого прокси-сервера:
|
||||
Вот некоторые варианты, которые вы можете использовать в качестве такого прокси-сервера:
|
||||
|
||||
* Traefik (может обновлять сертификаты)
|
||||
* Caddy (может обновлять сертификаты)
|
||||
|
|
@ -67,11 +67,11 @@
|
|||
|
||||
### Имя домена
|
||||
|
||||
Чаще всего, всё начинается с **приобретения имени домена**. Затем нужно настроить DNS-сервер (вероятно у того же провайдера, который выдал Вам домен).
|
||||
Чаще всего, всё начинается с **приобретения имени домена**. Затем нужно настроить DNS-сервер (вероятно у того же провайдера, который выдал вам домен).
|
||||
|
||||
Далее, возможно, Вы получаете "облачный" сервер (виртуальную машину) или что-то типа этого, у которого есть <abbr title="Не изменяемый">постоянный</abbr> **публичный IP-адрес**.
|
||||
Далее, возможно, вы получаете "облачный" сервер (виртуальную машину) или что-то типа этого, у которого есть <abbr title="Не изменяемый">постоянный</abbr> **публичный IP-адрес**.
|
||||
|
||||
На DNS-сервере (серверах) Вам следует настроить соответствующую ресурсную запись ("`запись A`"), указав, что **Ваш домен** связан с публичным **IP-адресом Вашего сервера**.
|
||||
На DNS-сервере (серверах) вам следует настроить соответствующую ресурсную запись ("`запись A`"), указав, что **Ваш домен** связан с публичным **IP-адресом вашего сервера**.
|
||||
|
||||
Обычно эту запись достаточно указать один раз, при первоначальной настройке всего сервера.
|
||||
|
||||
|
|
@ -82,9 +82,9 @@
|
|||
|
||||
Теперь давайте сфокусируемся на работе с HTTPS.
|
||||
|
||||
Всё начинается с того, что браузер спрашивает у **DNS-серверов**, какой **IP-адрес связан с доменом**, для примера возьмём домен `someapp.example.com`.
|
||||
Всё начинается с того, что браузер спрашивает у **DNS-серверов**, какой **IP-адрес связан с доменом**, для примера возьмём домен `someapp.example.com`.
|
||||
|
||||
DNS-сервера присылают браузеру определённый **IP-адрес**, тот самый публичный IP-адрес Вашего сервера, который Вы указали в ресурсной "записи А" при настройке.
|
||||
DNS-сервера присылают браузеру определённый **IP-адрес**, тот самый публичный IP-адрес вашего сервера, который вы указали в ресурсной "записи А" при настройке.
|
||||
|
||||
<img src="/img/deployment/https/https01.svg">
|
||||
|
||||
|
|
@ -96,7 +96,7 @@ DNS-сервера присылают браузеру определённый
|
|||
|
||||
<img src="/img/deployment/https/https02.svg">
|
||||
|
||||
Эта часть клиент-серверного взаимодействия устанавливает TLS-соединение и называется **TLS-рукопожатием**.
|
||||
Эта часть клиент-серверного взаимодействия устанавливает TLS-соединение и называется **TLS-рукопожатием**.
|
||||
|
||||
### TLS с расширением SNI
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ DNS-сервера присылают браузеру определённый
|
|||
* **Запуск в качестве программы-сервера** (как минимум, на время обновления сертификатов) на публичном IP-адресе домена.
|
||||
* Как уже не раз упоминалось, только один процесс может прослушивать определённый порт определённого IP-адреса.
|
||||
* Это одна из причин использования прокси-сервера ещё и в качестве программы обновления сертификатов.
|
||||
* В случае, если обновлением сертификатов занимается другая программа, Вам понадобится остановить прокси-сервер, запустить программу обновления сертификатов на сокете, предназначенном для прокси-сервера, настроить прокси-сервер на работу с новыми сертификатами и перезапустить его. Эта схема далека от идеальной, так как Ваши приложения будут недоступны на время отключения прокси-сервера.
|
||||
* В случае, если обновлением сертификатов занимается другая программа, вам понадобится остановить прокси-сервер, запустить программу обновления сертификатов на сокете, предназначенном для прокси-сервера, настроить прокси-сервер на работу с новыми сертификатами и перезапустить его. Эта схема далека от идеальной, так как Ваши приложения будут недоступны на время отключения прокси-сервера.
|
||||
|
||||
Весь этот процесс обновления, одновременный с обслуживанием запросов, является одной из основных причин, по которой желательно иметь **отдельную систему для работы с HTTPS** в виде прокси-сервера завершения TLS, а не просто использовать сертификаты TLS непосредственно с сервером приложений (например, Uvicorn).
|
||||
|
||||
|
|
@ -193,6 +193,6 @@ DNS-сервера присылают браузеру определённый
|
|||
|
||||
Наличие **HTTPS** очень важно и довольно **критично** в большинстве случаев. Однако, Вам, как разработчику, не нужно тратить много сил на это, достаточно **понимать эти концепции** и принципы их работы.
|
||||
|
||||
Но узнав базовые основы **HTTPS** Вы можете легко совмещать разные инструменты, которые помогут Вам в дальнейшей разработке.
|
||||
Но узнав базовые основы **HTTPS** вы можете легко совмещать разные инструменты, которые помогут вам в дальнейшей разработке.
|
||||
|
||||
В следующих главах я покажу Вам несколько примеров, как настраивать **HTTPS** для приложений **FastAPI**. 🔒
|
||||
В следующих главах я покажу вам несколько примеров, как настраивать **HTTPS** для приложений **FastAPI**. 🔒
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
Обычно **веб-приложения** размещают на удалённом компьютере с серверной программой, которая обеспечивает хорошую производительность, стабильность и т. д., Чтобы ваши пользователи могли эффективно, беспрерывно и беспроблемно обращаться к приложению.
|
||||
|
||||
Это отличется от **разработки**, когда вы постоянно меняете код, делаете в нём намеренные ошибки и исправляете их, останавливаете и перезапускаете сервер разработки и т. д.
|
||||
Это отличается от **разработки**, когда вы постоянно меняете код, делаете в нём намеренные ошибки и исправляете их, останавливаете и перезапускаете сервер разработки и т. д.
|
||||
|
||||
## Стратегии развёртывания
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Запуск сервера вручную - Uvicorn
|
||||
|
||||
Для запуска приложения **FastAPI** на удалённой серверной машине Вам необходим программный сервер, поддерживающий протокол ASGI, такой как **Uvicorn**.
|
||||
Для запуска приложения **FastAPI** на удалённой серверной машине вам необходим программный сервер, поддерживающий протокол ASGI, такой как **Uvicorn**.
|
||||
|
||||
Существует три наиболее распространённые альтернативы:
|
||||
|
||||
|
|
@ -10,16 +10,16 @@
|
|||
|
||||
## Сервер как машина и сервер как программа
|
||||
|
||||
В этих терминах есть некоторые различия и Вам следует запомнить их. 💡
|
||||
В этих терминах есть некоторые различия и вам следует запомнить их. 💡
|
||||
|
||||
Слово "**сервер**" чаще всего используется в двух контекстах:
|
||||
|
||||
- удалённый или расположенный в "облаке" компьютер (физическая или виртуальная машина).
|
||||
- программа, запущенная на таком компьютере (например, Uvicorn).
|
||||
|
||||
Просто запомните, если Вам встретился термин "сервер", то обычно он подразумевает что-то из этих двух смыслов.
|
||||
Просто запомните, если вам встретился термин "сервер", то обычно он подразумевает что-то из этих двух смыслов.
|
||||
|
||||
Когда имеют в виду именно удалённый компьютер, часто говорят просто **сервер**, но ещё его называют **машина**, **ВМ** (виртуальная машина), **нода**. Все эти термины обозначают одно и то же - удалённый компьютер, обычно под управлением Linux, на котором Вы запускаете программы.
|
||||
Когда имеют в виду именно удалённый компьютер, часто говорят просто **сервер**, но ещё его называют **машина**, **ВМ** (виртуальная машина), **нода**. Все эти термины обозначают одно и то же - удалённый компьютер, обычно под управлением Linux, на котором вы запускаете программы.
|
||||
|
||||
## Установка программного сервера
|
||||
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
|
||||
=== "Uvicorn"
|
||||
|
||||
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>, молниесный ASGI сервер, основанный на библиотеках uvloop и httptools.
|
||||
* <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a>, очень быстрый ASGI сервер, основанный на библиотеках uvloop и httptools.
|
||||
|
||||
<div class="termy">
|
||||
|
||||
|
|
@ -40,7 +40,7 @@
|
|||
</div>
|
||||
|
||||
!!! tip "Подсказка"
|
||||
С опцией `standard`, Uvicorn будет установливаться и использоваться с некоторыми дополнительными рекомендованными зависимостями.
|
||||
С опцией `standard`, Uvicorn будет устанавливаться и использоваться с некоторыми дополнительными рекомендованными зависимостями.
|
||||
|
||||
В них входит `uvloop`, высокопроизводительная замена `asyncio`, которая значительно ускоряет работу асинхронных программ.
|
||||
|
||||
|
|
@ -62,7 +62,7 @@
|
|||
|
||||
## Запуск серверной программы
|
||||
|
||||
Затем запустите Ваше приложение так же, как было указано в руководстве ранее, но без опции `--reload`:
|
||||
Затем запустите ваше приложение так же, как было указано в руководстве ранее, но без опции `--reload`:
|
||||
|
||||
=== "Uvicorn"
|
||||
|
||||
|
|
@ -103,11 +103,11 @@ Starlette и **FastAPI** основаны на <a href="https://anyio.readthedoc
|
|||
|
||||
Тем не менее Uvicorn совместим только с asyncio и обычно используется совместно с <a href="https://github.com/MagicStack/uvloop" class="external-link" target="_blank">`uvloop`</a>, высокопроизводительной заменой `asyncio`.
|
||||
|
||||
Но если Вы хотите использовать **Trio** напрямую, то можете воспользоваться **Hypercorn**, так как они совместимы. ✨
|
||||
Но если вы хотите использовать **Trio** напрямую, то можете воспользоваться **Hypercorn**, так как они совместимы. ✨
|
||||
|
||||
### Установка Hypercorn с Trio
|
||||
|
||||
Для начала, Вам нужно установить Hypercorn с поддержкой Trio:
|
||||
Для начала, вам нужно установить Hypercorn с поддержкой Trio:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
|
|
@ -130,15 +130,15 @@ $ hypercorn main:app --worker-class trio
|
|||
|
||||
</div>
|
||||
|
||||
Hypercorn, в свою очередь, запустит Ваше приложение использующее Trio.
|
||||
Hypercorn, в свою очередь, запустит ваше приложение использующее Trio.
|
||||
|
||||
Таким образом, Вы сможете использовать Trio в своём приложении. Но лучше использовать AnyIO, для сохранения совместимости и с Trio, и с asyncio. 🎉
|
||||
Таким образом, вы сможете использовать Trio в своём приложении. Но лучше использовать AnyIO, для сохранения совместимости и с Trio, и с asyncio. 🎉
|
||||
|
||||
## Концепции развёртывания
|
||||
|
||||
В вышеприведённых примерах серверные программы (например Uvicorn) запускали только **один процесс**, принимающий входящие запросы с любого IP (на это указывал аргумент `0.0.0.0`) на определённый порт (в примерах мы указывали порт `80`).
|
||||
|
||||
Это основная идея. Но возможно, Вы озаботитесь добавлением дополнительных возможностей, таких как:
|
||||
Это основная идея. Но возможно, вы озаботитесь добавлением дополнительных возможностей, таких как:
|
||||
|
||||
* Использование более безопасного протокола HTTPS
|
||||
* Настройки запуска приложения
|
||||
|
|
@ -147,4 +147,4 @@ Hypercorn, в свою очередь, запустит Ваше приложе
|
|||
* Управление памятью
|
||||
* Использование перечисленных функций перед запуском приложения.
|
||||
|
||||
Я поведаю Вам больше о каждой из этих концепций в следующих главах, с конкретными примерами стратегий работы с ними. 🚀
|
||||
Я расскажу вам больше о каждой из этих концепций в следующих главах, с конкретными примерами стратегий работы с ними. 🚀
|
||||
|
|
|
|||
|
|
@ -0,0 +1,139 @@
|
|||
# Зависимости в декораторах операции пути
|
||||
|
||||
В некоторых случаях, возвращаемое значение зависимости не используется внутри *функции операции пути*.
|
||||
|
||||
Или же зависимость не возвращает никакого значения.
|
||||
|
||||
Но вам всё-таки нужно, чтобы она выполнилась.
|
||||
|
||||
Для таких ситуаций, вместо объявления *функции операции пути* с параметром `Depends`, вы можете добавить список зависимостей `dependencies` в *декоратор операции пути*.
|
||||
|
||||
## Добавление `dependencies` в *декоратор операции пути*
|
||||
|
||||
*Декоратор операции пути* получает необязательный аргумент `dependencies`.
|
||||
|
||||
Это должен быть `list` состоящий из `Depends()`:
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python hl_lines="18"
|
||||
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8 без Annotated"
|
||||
|
||||
!!! Подсказка
|
||||
Рекомендуется использовать версию с Annotated, если возможно.
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!> ../../../docs_src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
Зависимости из dependencies выполнятся так же, как и обычные зависимости. Но их значения (если они были) не будут переданы в *функцию операции пути*.
|
||||
|
||||
!!! Подсказка
|
||||
Некоторые редакторы кода определяют неиспользуемые параметры функций и подсвечивают их как ошибку.
|
||||
|
||||
Использование `dependencies` в *декораторе операции пути* гарантирует выполнение зависимостей, избегая при этом предупреждений редактора кода и других инструментов.
|
||||
|
||||
Это также должно помочь предотвратить путаницу у начинающих разработчиков, которые видят неиспользуемые параметры в коде и могут подумать что в них нет необходимости.
|
||||
|
||||
!!! Дополнительная информация
|
||||
В этом примере мы используем выдуманные пользовательские заголовки `X-Key` и `X-Token`.
|
||||
|
||||
Но в реальных проектах, при внедрении системы безопасности, вы получите больше пользы используя интегрированные [средства защиты (следующая глава)](../security/index.md){.internal-link target=_blank}.
|
||||
|
||||
## Исключения в dependencies и возвращаемые значения
|
||||
|
||||
Вы можете использовать те же *функции* зависимостей, что и обычно.
|
||||
|
||||
### Требования к зависимостям
|
||||
|
||||
Они могут объявлять требования к запросу (например заголовки) или другие подзависимости:
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="8 13"
|
||||
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8 без Annotated"
|
||||
|
||||
!!! Подсказка
|
||||
Рекомендуется использовать версию с Annotated, если возможно.
|
||||
|
||||
```Python hl_lines="6 11"
|
||||
{!> ../../../docs_src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
### Вызов исключений
|
||||
|
||||
Зависимости из dependencies могут вызывать исключения с помощью `raise`, как и обычные зависимости:
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="10 15"
|
||||
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8 без Annotated"
|
||||
|
||||
!!! Подсказка
|
||||
Рекомендуется использовать версию с Annotated, если возможно.
|
||||
|
||||
```Python hl_lines="8 13"
|
||||
{!> ../../../docs_src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
### Возвращаемые значения
|
||||
|
||||
И они могут возвращать значения или нет, эти значения использоваться не будут.
|
||||
|
||||
Таким образом, вы можете переиспользовать обычную зависимость (возвращающую значение), которую вы уже используете где-то в другом месте, и хотя значение не будет использоваться, зависимость будет выполнена:
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="11 16"
|
||||
{!> ../../../docs_src/dependencies/tutorial006_an_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8+"
|
||||
|
||||
```Python hl_lines="10 15"
|
||||
{!> ../../../docs_src/dependencies/tutorial006_an.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.8 без Annotated"
|
||||
|
||||
!!! Подсказка
|
||||
Рекомендуется использовать версию с Annotated, если возможно.
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
{!> ../../../docs_src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
## Dependencies для группы *операций путей*
|
||||
|
||||
Позже, читая о том как структурировать большие приложения ([Bigger Applications - Multiple Files](../../tutorial/bigger-applications.md){.internal-link target=_blank}), возможно, многофайловые, вы узнаете как объявить единый параметр `dependencies` для всей группы *операций путей*.
|
||||
|
||||
## Глобальный Dependencies
|
||||
|
||||
Далее мы увидим, как можно добавить dependencies для всего `FastAPI` приложения, так чтобы они применялись к каждой *операции пути*.
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
# Зависимости с yield
|
||||
|
||||
FastAPI поддерживает зависимости, которые выполняют некоторые <abbr title='также известные как "exit", "cleanup", "teardown", "close", "context managers", ...'>дополнительные действия после завершения работы</abbr>.
|
||||
|
||||
Для этого используйте `yield` вместо `return`, а дополнительный код напишите после него.
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Обязательно используйте `yield` один-единственный раз.
|
||||
|
||||
!!! note "Технические детали"
|
||||
Любая функция, с которой может работать:
|
||||
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> или
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
|
||||
|
||||
будет корректно использоваться в качестве **FastAPI**-зависимости.
|
||||
|
||||
На самом деле, FastAPI использует эту пару декораторов "под капотом".
|
||||
|
||||
## Зависимость базы данных с помощью `yield`
|
||||
|
||||
Например, с его помощью можно создать сессию работы с базой данных и закрыть его после завершения.
|
||||
|
||||
Перед созданием ответа будет выполнен только код до и включая `yield`.
|
||||
|
||||
```Python hl_lines="2-4"
|
||||
{!../../../docs_src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
Полученное значение и есть то, что будет внедрено в функцию операции пути и другие зависимости:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
Код, следующий за оператором `yield`, выполняется после доставки ответа:
|
||||
|
||||
```Python hl_lines="5-6"
|
||||
{!../../../docs_src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Можно использовать как `async` так и обычные функции.
|
||||
|
||||
**FastAPI** это корректно обработает, и в обоих случаях будет делать то же самое, что и с обычными зависимостями.
|
||||
|
||||
## Зависимость с `yield` и `try` одновременно
|
||||
|
||||
Если использовать блок `try` в зависимости с `yield`, то будет получено всякое исключение, которое было выброшено при использовании зависимости.
|
||||
|
||||
Например, если какой-то код в какой-то момент в середине, в другой зависимости или в *функции операции пути*, сделал "откат" транзакции базы данных или создал любую другую ошибку, то вы получите исключение в своей зависимости.
|
||||
|
||||
Таким образом, можно искать конкретное исключение внутри зависимости с помощью `except SomeException`.
|
||||
|
||||
Таким же образом можно использовать `finally`, чтобы убедиться, что обязательные шаги при выходе выполнены, независимо от того, было ли исключение или нет.
|
||||
|
||||
```Python hl_lines="3 5"
|
||||
{!../../../docs_src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
## Подзависимости с `yield`
|
||||
|
||||
Вы можете иметь подзависимости и "деревья" подзависимостей любого размера и формы, и любая из них или все они могут использовать `yield`.
|
||||
|
||||
**FastAPI** будет следить за тем, чтобы "код по выходу" в каждой зависимости с `yield` выполнялся в правильном порядке.
|
||||
|
||||
Например, `dependency_c` может иметь зависимость от `dependency_b`, а `dependency_b` от `dependency_a`:
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="6 14 22"
|
||||
{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6+"
|
||||
|
||||
```Python hl_lines="5 13 21"
|
||||
{!> ../../../docs_src/dependencies/tutorial008_an.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6+ без Annotated"
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Предпочтительнее использовать версию с аннотацией, если это возможно.
|
||||
|
||||
```Python hl_lines="4 12 20"
|
||||
{!> ../../../docs_src/dependencies/tutorial008.py!}
|
||||
```
|
||||
|
||||
И все они могут использовать `yield`.
|
||||
|
||||
В этом случае `dependency_c` для выполнения своего кода выхода нуждается в том, чтобы значение из `dependency_b` (здесь `dep_b`) было еще доступно.
|
||||
|
||||
И, в свою очередь, `dependency_b` нуждается в том, чтобы значение из `dependency_a` (здесь `dep_a`) было доступно для ее завершающего кода.
|
||||
|
||||
=== "Python 3.9+"
|
||||
|
||||
```Python hl_lines="18-19 26-27"
|
||||
{!> ../../../docs_src/dependencies/tutorial008_an_py39.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6+"
|
||||
|
||||
```Python hl_lines="17-18 25-26"
|
||||
{!> ../../../docs_src/dependencies/tutorial008_an.py!}
|
||||
```
|
||||
|
||||
=== "Python 3.6+ без Annotated"
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Предпочтительнее использовать версию с аннотацией, если это возможно.
|
||||
|
||||
```Python hl_lines="16-17 24-25"
|
||||
{!> ../../../docs_src/dependencies/tutorial008.py!}
|
||||
```
|
||||
|
||||
Точно так же можно иметь часть зависимостей с `yield`, часть с `return`, и какие-то из них могут зависеть друг от друга.
|
||||
|
||||
Либо у вас может быть одна зависимость, которая требует несколько других зависимостей с `yield` и т.д.
|
||||
|
||||
Комбинации зависимостей могут быть какими вам угодно.
|
||||
|
||||
**FastAPI** проследит за тем, чтобы все выполнялось в правильном порядке.
|
||||
|
||||
!!! note "Технические детали"
|
||||
Это работает благодаря <a href="https://docs.python.org/3/library/contextlib.html" class="external-link" target="_blank">Контекстным менеджерам</a> в Python.
|
||||
|
||||
**FastAPI** использует их "под капотом" с этой целью.
|
||||
|
||||
## Зависимости с `yield` и `HTTPException`
|
||||
|
||||
Вы видели, что можно использовать зависимости с `yield` совместно с блоком `try`, отлавливающие исключения.
|
||||
|
||||
Таким же образом вы можете поднять исключение `HTTPException` или что-то подобное в завершающем коде, после `yield`.
|
||||
|
||||
Код выхода в зависимостях с `yield` выполняется *после* отправки ответа, поэтому [Обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} уже будет запущен. В коде выхода (после `yield`) нет ничего, перехватывающего исключения, брошенные вашими зависимостями.
|
||||
|
||||
Таким образом, если после `yield` возникает `HTTPException`, то стандартный (или любой пользовательский) обработчик исключений, который перехватывает `HTTPException` и возвращает ответ HTTP 400, уже не сможет перехватить это исключение.
|
||||
|
||||
Благодаря этому все, что установлено в зависимости (например, сеанс работы с БД), может быть использовано, например, фоновыми задачами.
|
||||
|
||||
Фоновые задачи выполняются *после* отправки ответа. Поэтому нет возможности поднять `HTTPException`, так как нет даже возможности изменить уже отправленный ответ.
|
||||
|
||||
Но если фоновая задача создает ошибку в БД, то, по крайней мере, можно сделать откат или чисто закрыть сессию в зависимости с помощью `yield`, а также, возможно, занести ошибку в журнал или сообщить о ней в удаленную систему отслеживания.
|
||||
|
||||
Если у вас есть код, который, как вы знаете, может вызвать исключение, сделайте самую обычную/"питонячью" вещь и добавьте блок `try` в этот участок кода.
|
||||
|
||||
Если у вас есть пользовательские исключения, которые вы хотите обрабатывать *до* возврата ответа и, возможно, модифицировать ответ, даже вызывая `HTTPException`, создайте [Cобственный обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Вы все еще можете вызывать исключения, включая `HTTPException`, *до* `yield`. Но не после.
|
||||
|
||||
Последовательность выполнения примерно такая, как на этой схеме. Время течет сверху вниз. А каждый столбец - это одна из частей, взаимодействующих с кодом или выполняющих код.
|
||||
|
||||
```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 -->> dep: Raise HTTPException
|
||||
dep -->> handler: Auto forward exception
|
||||
handler -->> client: HTTP error response
|
||||
operation -->> dep: Raise other exception
|
||||
dep -->> handler: Auto forward 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 "Дополнительная информация"
|
||||
Клиенту будет отправлен только **один ответ**. Это может быть один из ответов об ошибке или это будет ответ от *операции пути*.
|
||||
|
||||
После отправки одного из этих ответов никакой другой ответ не может быть отправлен.
|
||||
|
||||
!!! tip "Подсказка"
|
||||
На этой диаграмме показано "HttpException", но вы также можете вызвать любое другое исключение, для которого вы создаете [Пользовательский обработчик исключений](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank}.
|
||||
|
||||
Если вы создадите какое-либо исключение, оно будет передано зависимостям с yield, включая `HttpException`, а затем **снова** обработчикам исключений. Если для этого исключения нет обработчика исключений, то оно будет обработано внутренним "ServerErrorMiddleware" по умолчанию, возвращающим код состояния HTTP 500, чтобы уведомить клиента, что на сервере произошла ошибка.
|
||||
|
||||
## Зависимости с `yield`, `HTTPException` и фоновыми задачами
|
||||
|
||||
!!! warning "Внимание"
|
||||
Скорее всего, вам не нужны эти технические подробности, вы можете пропустить этот раздел и продолжить ниже.
|
||||
|
||||
Эти подробности полезны, главным образом, если вы использовали версию FastAPI до 0.106.0 и использовали ресурсы из зависимостей с `yield` в фоновых задачах.
|
||||
|
||||
До версии FastAPI 0.106.0 вызывать исключения после `yield` было невозможно, код выхода в зависимостях с `yield` выполнялся *после* отправки ответа, поэтому [Обработчик Ошибок](../handling-errors.md#install-custom-exception-handlers){.internal-link target=_blank} уже был бы запущен.
|
||||
|
||||
Это было сделано главным образом для того, чтобы позволить использовать те же объекты, "отданные" зависимостями, внутри фоновых задач, поскольку код выхода будет выполняться после завершения фоновых задач.
|
||||
|
||||
Тем не менее, поскольку это означало бы ожидание ответа в сети, а также ненужное удержание ресурса в зависимости от доходности (например, соединение с базой данных), это было изменено в FastAPI 0.106.0.
|
||||
|
||||
!!! tip "Подсказка"
|
||||
|
||||
Кроме того, фоновая задача обычно представляет собой независимый набор логики, который должен обрабатываться отдельно, со своими собственными ресурсами (например, собственным подключением к базе данных).
|
||||
Таким образом, вы, вероятно, получите более чистый код.
|
||||
|
||||
Если вы полагались на это поведение, то теперь вам следует создавать ресурсы для фоновых задач внутри самой фоновой задачи, а внутри использовать только те данные, которые не зависят от ресурсов зависимостей с `yield`.
|
||||
|
||||
Например, вместо того чтобы использовать ту же сессию базы данных, вы создадите новую сессию базы данных внутри фоновой задачи и будете получать объекты из базы данных с помощью этой новой сессии. А затем, вместо того чтобы передавать объект из базы данных в качестве параметра в функцию фоновой задачи, вы передадите идентификатор этого объекта, а затем снова получите объект в функции фоновой задачи.
|
||||
|
||||
## Контекстные менеджеры
|
||||
|
||||
### Что такое "контекстные менеджеры"
|
||||
|
||||
"Контекстные менеджеры" - это любые объекты Python, которые можно использовать в операторе `with`.
|
||||
|
||||
Например, <a href="https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files" class="external-link" target="_blank">можно использовать `with` для чтения файла</a>:
|
||||
|
||||
```Python
|
||||
with open("./somefile.txt") as f:
|
||||
contents = f.read()
|
||||
print(contents)
|
||||
```
|
||||
|
||||
Под капотом" open("./somefile.txt") создаёт объект называемый "контекстным менеджером".
|
||||
|
||||
Когда блок `with` завершается, он обязательно закрывает файл, даже если были исключения.
|
||||
|
||||
Когда вы создаете зависимость с помощью `yield`, **FastAPI** внутренне преобразует ее в контекстный менеджер и объединяет с некоторыми другими связанными инструментами.
|
||||
|
||||
### Использование менеджеров контекста в зависимостях с помощью `yield`
|
||||
|
||||
!!! warning "Внимание"
|
||||
Это более или менее "продвинутая" идея.
|
||||
|
||||
Если вы только начинаете работать с **FastAPI**, то лучше пока пропустить этот пункт.
|
||||
|
||||
В Python для создания менеджеров контекста можно <a href="https://docs.python.org/3/reference/datamodel.html#context-managers" class="external-link" target="_blank">создать класс с двумя методами: `__enter__()` и `__exit__()`</a>.
|
||||
|
||||
Вы также можете использовать их внутри зависимостей **FastAPI** с `yield`, используя операторы
|
||||
`with` или `async with` внутри функции зависимости:
|
||||
|
||||
```Python hl_lines="1-9 13"
|
||||
{!../../../docs_src/dependencies/tutorial010.py!}
|
||||
```
|
||||
|
||||
!!! tip "Подсказка"
|
||||
Другой способ создания контекстного менеджера - с помощью:
|
||||
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.contextmanager" class="external-link" target="_blank">`@contextlib.contextmanager`</a> или
|
||||
* <a href="https://docs.python.org/3/library/contextlib.html#contextlib.asynccontextmanager" class="external-link" target="_blank">`@contextlib.asynccontextmanager`</a>
|
||||
|
||||
используйте их для оформления функции с одним `yield`.
|
||||
|
||||
Это то, что **FastAPI** использует внутри себя для зависимостей с `yield`.
|
||||
|
||||
Но использовать декораторы для зависимостей FastAPI не обязательно (да и не стоит).
|
||||
|
||||
FastAPI сделает это за вас на внутреннем уровне.
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
一个常见的工具是 <a href="https://openapi-generator.tech/" class="external-link" target="_blank">OpenAPI Generator</a>。
|
||||
|
||||
如果您正在开发**前端**,一个非常有趣的替代方案是 <a href="https://github.com/ferdikoomen/openapi-typescript-codegen" class="external-link" target="_blank">openapi-typescript-codegen</a>。
|
||||
如果您正在开发**前端**,一个非常有趣的替代方案是 <a href="https://github.com/hey-api/openapi-ts" class="external-link" target="_blank">openapi-ts</a>。
|
||||
|
||||
## 生成一个 TypeScript 前端客户端
|
||||
|
||||
|
|
@ -46,14 +46,14 @@ OpenAPI中所包含的模型里有相同的信息可以用于 **生成客户端
|
|||
|
||||
现在我们有了带有模型的应用,我们可以为前端生成客户端代码。
|
||||
|
||||
#### 安装 `openapi-typescript-codegen`
|
||||
#### 安装 `openapi-ts`
|
||||
|
||||
您可以使用以下工具在前端代码中安装 `openapi-typescript-codegen`:
|
||||
您可以使用以下工具在前端代码中安装 `openapi-ts`:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ npm install openapi-typescript-codegen --save-dev
|
||||
$ npm install @hey-api/openapi-ts --save-dev
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
|
@ -62,7 +62,7 @@ $ npm install openapi-typescript-codegen --save-dev
|
|||
|
||||
#### 生成客户端代码
|
||||
|
||||
要生成客户端代码,您可以使用现在将要安装的命令行应用程序 `openapi`。
|
||||
要生成客户端代码,您可以使用现在将要安装的命令行应用程序 `openapi-ts`。
|
||||
|
||||
因为它安装在本地项目中,所以您可能无法直接使用此命令,但您可以将其放在 `package.json` 文件中。
|
||||
|
||||
|
|
@ -75,12 +75,12 @@ $ npm install openapi-typescript-codegen --save-dev
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
"generate-client": "openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"openapi-typescript-codegen": "^0.20.1",
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ $ npm install openapi-typescript-codegen --save-dev
|
|||
$ npm run generate-client
|
||||
|
||||
frontend-app@1.0.0 generate-client /home/user/code/frontend-app
|
||||
> openapi --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
> openapi-ts --input http://localhost:8000/openapi.json --output ./src/client --client axios
|
||||
```
|
||||
|
||||
</div>
|
||||
|
|
@ -234,12 +234,12 @@ FastAPI为每个*路径操作*使用一个**唯一ID**,它用于**操作ID**
|
|||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"generate-client": "openapi --input ./openapi.json --output ./src/client --client axios"
|
||||
"generate-client": "openapi-ts --input ./openapi.json --output ./src/client --client axios"
|
||||
},
|
||||
"author": "",
|
||||
"license": "",
|
||||
"devDependencies": {
|
||||
"openapi-typescript-codegen": "^0.20.1",
|
||||
"@hey-api/openapi-ts": "^0.27.38",
|
||||
"typescript": "^4.6.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
"""FastAPI framework, high performance, easy to learn, fast to code, ready for production"""
|
||||
|
||||
__version__ = "0.110.0"
|
||||
__version__ = "0.110.1"
|
||||
|
||||
from starlette import status as status
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ from starlette.requests import Request
|
|||
from starlette.responses import HTMLResponse, JSONResponse, Response
|
||||
from starlette.routing import BaseRoute
|
||||
from starlette.types import ASGIApp, Lifespan, Receive, Scope, Send
|
||||
from typing_extensions import Annotated, Doc, deprecated # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc, deprecated
|
||||
|
||||
AppType = TypeVar("AppType", bound="FastAPI")
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from typing import Any, Callable
|
||||
|
||||
from starlette.background import BackgroundTasks as StarletteBackgroundTasks
|
||||
from typing_extensions import Annotated, Doc, ParamSpec # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc, ParamSpec
|
||||
|
||||
P = ParamSpec("P")
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ from starlette.datastructures import Headers as Headers # noqa: F401
|
|||
from starlette.datastructures import QueryParams as QueryParams # noqa: F401
|
||||
from starlette.datastructures import State as State # noqa: F401
|
||||
from starlette.datastructures import UploadFile as StarletteUploadFile
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
|
||||
class UploadFile(StarletteUploadFile):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import inspect
|
||||
from contextlib import AsyncExitStack, contextmanager
|
||||
from copy import deepcopy
|
||||
from copy import copy, deepcopy
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
|
|
@ -384,6 +384,8 @@ def analyze_param(
|
|||
field_info.annotation = type_annotation
|
||||
|
||||
if depends is not None and depends.dependency is None:
|
||||
# Copy `depends` before mutating it
|
||||
depends = copy(depends)
|
||||
depends.dependency = type_annotation
|
||||
|
||||
if lenient_issubclass(
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from pydantic import BaseModel
|
|||
from pydantic.color import Color
|
||||
from pydantic.networks import AnyUrl, NameEmail
|
||||
from pydantic.types import SecretBytes, SecretStr
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
from ._compat import PYDANTIC_V2, Url, _model_dump
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Any, Dict, Optional, Sequence, Type, Union
|
|||
from pydantic import BaseModel, create_model
|
||||
from starlette.exceptions import HTTPException as StarletteHTTPException
|
||||
from starlette.exceptions import WebSocketException as StarletteWebSocketException
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
|
||||
class HTTPException(StarletteHTTPException):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Any, Dict, Optional
|
|||
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
from starlette.responses import HTMLResponse
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
swagger_ui_default_parameters: Annotated[
|
||||
Dict[str, Any],
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ from typing import Any, Callable, Dict, List, Optional, Sequence, Union
|
|||
from fastapi import params
|
||||
from fastapi._compat import Undefined
|
||||
from fastapi.openapi.models import Example
|
||||
from typing_extensions import Annotated, Doc, deprecated # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc, deprecated
|
||||
|
||||
_Unset: Any = Undefined
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ from starlette.routing import (
|
|||
from starlette.routing import Mount as Mount # noqa
|
||||
from starlette.types import ASGIApp, Lifespan, Scope
|
||||
from starlette.websockets import WebSocket
|
||||
from typing_extensions import Annotated, Doc, deprecated # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc, deprecated
|
||||
|
||||
|
||||
def _prepare_response_content(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from fastapi.security.base import SecurityBase
|
|||
from starlette.exceptions import HTTPException
|
||||
from starlette.requests import Request
|
||||
from starlette.status import HTTP_403_FORBIDDEN
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
|
||||
class APIKeyBase(SecurityBase):
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from fastapi.security.utils import get_authorization_scheme_param
|
|||
from pydantic import BaseModel
|
||||
from starlette.requests import Request
|
||||
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
|
||||
class HTTPBasicCredentials(BaseModel):
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from starlette.requests import Request
|
|||
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
|
||||
|
||||
# TODO: import from typing when deprecating Python 3.9
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
|
||||
class OAuth2PasswordRequestForm:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from fastapi.security.base import SecurityBase
|
|||
from starlette.exceptions import HTTPException
|
||||
from starlette.requests import Request
|
||||
from starlette.status import HTTP_403_FORBIDDEN
|
||||
from typing_extensions import Annotated, Doc # type: ignore [attr-defined]
|
||||
from typing_extensions import Annotated, Doc
|
||||
|
||||
|
||||
class OpenIdConnect(SecurityBase):
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ classifiers = [
|
|||
"Topic :: Internet :: WWW/HTTP",
|
||||
]
|
||||
dependencies = [
|
||||
"starlette>=0.36.3,<0.37.0",
|
||||
"starlette>=0.37.2,<0.38.0",
|
||||
"pydantic>=1.7.4,!=1.8,!=1.8.1,!=2.0.0,!=2.0.1,!=2.1.0,<3.0.0",
|
||||
"typing-extensions>=4.8.0",
|
||||
]
|
||||
|
|
@ -121,9 +121,6 @@ filterwarnings = [
|
|||
# - https://github.com/mpdavis/python-jose/issues/332
|
||||
# - https://github.com/mpdavis/python-jose/issues/334
|
||||
'ignore:datetime\.datetime\.utcnow\(\) is deprecated and scheduled for removal in a future version\..*:DeprecationWarning:jose',
|
||||
# TODO: remove after upgrading Starlette to a version including https://github.com/encode/starlette/pull/2406
|
||||
# Probably Starlette 0.36.0
|
||||
"ignore: The 'method' parameter is not used, and it will be removed.:DeprecationWarning:starlette",
|
||||
]
|
||||
|
||||
[tool.coverage.run]
|
||||
|
|
|
|||
|
|
@ -3,17 +3,16 @@
|
|||
mkdocs-material==9.4.7
|
||||
mdx-include >=1.4.1,<2.0.0
|
||||
mkdocs-redirects>=1.2.1,<1.3.0
|
||||
typer-cli >=0.0.13,<0.0.14
|
||||
typer[all] >=0.6.1,<0.8.0
|
||||
typer >=0.12.0
|
||||
pyyaml >=5.3.1,<7.0.0
|
||||
# For Material for MkDocs, Chinese search
|
||||
jieba==0.42.1
|
||||
# For image processing by Material for MkDocs
|
||||
pillow==10.1.0
|
||||
pillow==10.2.0
|
||||
# For image processing by Material for MkDocs
|
||||
cairosvg==2.7.0
|
||||
mkdocstrings[python]==0.23.0
|
||||
griffe-typingdoc==0.2.2
|
||||
# For griffe, it formats with black
|
||||
black==23.3.0
|
||||
black==24.3.0
|
||||
mkdocs-macros-plugin==1.0.5
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
pydantic-settings >=2.0.0
|
||||
pytest >=7.1.3,<8.0.0
|
||||
coverage[toml] >= 6.5.0,< 8.0
|
||||
mypy ==1.4.1
|
||||
mypy ==1.8.0
|
||||
ruff ==0.2.0
|
||||
email_validator >=1.1.1,<3.0.0
|
||||
dirty-equals ==0.6.0
|
||||
|
|
|
|||
|
|
@ -0,0 +1,77 @@
|
|||
from typing import TypeVar
|
||||
|
||||
from fastapi import Depends, FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from typing_extensions import Annotated
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
Dep = Annotated[T, Depends()]
|
||||
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
|
||||
class B:
|
||||
pass
|
||||
|
||||
|
||||
@app.get("/a")
|
||||
async def a(dep: Dep[A]):
|
||||
return {"cls": dep.__class__.__name__}
|
||||
|
||||
|
||||
@app.get("/b")
|
||||
async def b(dep: Dep[B]):
|
||||
return {"cls": dep.__class__.__name__}
|
||||
|
||||
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
def test_generic_parameterless_depends():
|
||||
response = client.get("/a")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"cls": "A"}
|
||||
|
||||
response = client.get("/b")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {"cls": "B"}
|
||||
|
||||
|
||||
def test_openapi_schema():
|
||||
response = client.get("/openapi.json")
|
||||
assert response.status_code == 200, response.text
|
||||
assert response.json() == {
|
||||
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||
"openapi": "3.1.0",
|
||||
"paths": {
|
||||
"/a": {
|
||||
"get": {
|
||||
"operationId": "a_a_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
"description": "Successful " "Response",
|
||||
}
|
||||
},
|
||||
"summary": "A",
|
||||
}
|
||||
},
|
||||
"/b": {
|
||||
"get": {
|
||||
"operationId": "b_b_get",
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {"application/json": {"schema": {}}},
|
||||
"description": "Successful " "Response",
|
||||
}
|
||||
},
|
||||
"summary": "B",
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
Loading…
Reference in New Issue