15: FastAPI, async/await

image.png Քարերի սիմֆոնիա, Գառնի գյուղ, լուսանկարի հղումը, Հեղինակ՝ Davit Simonyan

Open In Colab (ToDo)

Song reference - ToDo

📌 Նկարագիր

📚 Ամբողջական նյութը

📺 Տեսանյութեր

🏡 Տնային

📚 Նյութը

Async/Await

  • Սինխրոն ծրագրավորում - ծրագիրը կատարում է մեկ գործողություն, սպասում է դրա ավարտին, հետո անցնում է հաջորդին:
  • Ասինխրոն ծրագրավորում - ծրագիրը կարող է սկսել գործողություն, չսպասել դրա ավարտին, և շարունակել այլ գործողություններ:

Օրինակ

Պատկերացրեք, որ գնում եք «Երևանյան Շաուրմա» (կամ «Արտաշի մոտ», կամ «ՕՍՏ», կամ «Տաշիր», կամ «12 Սեղան», կամ «Արտլանչ», կամ «Սբթայթլ», կամ «KFC», կամ «Համեղ պառառ»):

🥙 Սինխրոն մոտեցում (Blocking)

Դուք → Վաճառող: "Մեկ շաուրմա՝ հավով, առանց սոխ"
Վաճառող: "Լավ, 5 րոպե սպասեք"

[Դուք կանգնած սպասում եք 5 րոպե, ոչինչ չեք կարող անել]

Վաճառող: "Ահա ձեր շաուրման"
Դուք: "Շնորհակալություն"
[Միայն այժմ կարող եք շարունակել ուրիշ գործեր]

🔄 Ասինխրոն մոտեցում (Non-blocking)

Դուք → Վաճառող: "Մեկ շաուրմա՝ հավով, առանց սոխ"
Վաճառող: "Լավ, 5 րոպե կտևի, ես ձեզ կկանչեմ"

[Դուք շարունակում եք ուրիշ գործեր՝ հեռախոս խոսել, ինստագրամ նայել]
[16 րոպե անց (Լիբանանյան շաուրմայի դեպքում)] 

Վաճառող: "Ձեր շաուրման պատրաստ է!"
Դուք: [վերադառնում եք] "Շնորհակալություն"

Առավելությունը - Ասինխրոն մոտեցման դեպքում դուք ժամանակ չեք կորցնում սպասելիս:

import time
import asyncio
from datetime import datetime

def current_time():
    return datetime.now().strftime("%H:%M:%S")

# Սինխրոն (blocking) մոտեցում
def order_kebab_sync(name, prep_time):
    print(f"{current_time()} - {name}: Պատվիրում եմ քյաբաբ")
    print(f"{current_time()} - Վաճառող: Լավ, {prep_time} վարկյանից պատրաստ կլինի")
    
    # Բլոկավորված սպասում
    time.sleep(prep_time)
    
    print(f"{current_time()} - Վաճառող: {name}, ձեր քյաբաբ պատրաստ է!\n")

    
def main_sync():
    print("🥙 ՍԻՆԽՐՈՆ ՄՈՏԵՑՈՒՄ")
    print("=" * 50)
    
    start_time = time.time()
    
    # Երեք անձ պատվիրում են
    order_kebab_sync("Էռնեստո Փարպեցի", 5)
    order_kebab_sync("Բերիմոռ", 4)
    order_kebab_sync("Այծեմնիկ", 7)

    end_time = time.time()
    total_time = end_time - start_time
    
    print(f"\n⏱️  Ընդհանուր ժամանակ: {total_time:.1f} վայրկյան")
    print("Բոլորը պատվիրել են մեկ առ մեկ")

main_sync()
🥙 ՍԻՆԽՐՈՆ ՄՈՏԵՑՈՒՄ
==================================================
18:49:48 - Էռնեստո Փարպեցի: Պատվիրում եմ քյաբաբ
18:49:48 - Վաճառող: Լավ, 5 վարկյանից պատրաստ կլինի
18:49:53 - Վաճառող: Էռնեստո Փարպեցի, ձեր քյաբաբ պատրաստ է!

18:49:53 - Բերիմոռ: Պատվիրում եմ քյաբաբ
18:49:53 - Վաճառող: Լավ, 4 վարկյանից պատրաստ կլինի
18:49:53 - Վաճառող: Էռնեստո Փարպեցի, ձեր քյաբաբ պատրաստ է!

18:49:53 - Բերիմոռ: Պատվիրում եմ քյաբաբ
18:49:53 - Վաճառող: Լավ, 4 վարկյանից պատրաստ կլինի
18:49:57 - Վաճառող: Բերիմոռ, ձեր քյաբաբ պատրաստ է!

18:49:57 - Այծեմնիկ: Պատվիրում եմ քյաբաբ
18:49:57 - Վաճառող: Լավ, 7 վարկյանից պատրաստ կլինի
18:49:57 - Վաճառող: Բերիմոռ, ձեր քյաբաբ պատրաստ է!

18:49:57 - Այծեմնիկ: Պատվիրում եմ քյաբաբ
18:49:57 - Վաճառող: Լավ, 7 վարկյանից պատրաստ կլինի
18:50:04 - Վաճառող: Այծեմնիկ, ձեր քյաբաբ պատրաստ է!


⏱️  Ընդհանուր ժամանակ: 16.0 վայրկյան
Բոլորը պատվիրել են մեկ առ մեկ
18:50:04 - Վաճառող: Այծեմնիկ, ձեր քյաբաբ պատրաստ է!


⏱️  Ընդհանուր ժամանակ: 16.0 վայրկյան
Բոլորը պատվիրել են մեկ առ մեկ
# Ասինխրոն (non-blocking) մոտեցում
async def order_shawarma_async(name, prep_time):
    print(f"{current_time()} - {name}: Պատվիրում եմ քյաբաբ")
    print(f"{current_time()} - Վաճառող: Լավ {name}, {prep_time} վայրկյան կտևի")
    
    # Ասինխրոն սպասում (այլ գործողություններ կարող են շարունակվել)
    await asyncio.sleep(prep_time)
    
    print(f"{current_time()} - Վաճառող: {name}, ձեր քյաբաբը պատրաստ է!\n")

async def main_async():
    print("🚀 ԱՍԻՆԽՐՈՆ ՄՈՏԵՑՈՒՄ")
    print("=" * 50)
    
    start_time = time.time()
    
    # Բոլոր պատվերները սկսվում են համաժամանակյա
    tasks = [
        order_shawarma_async("Էռնեստո Փարպեցի", 5),
        order_shawarma_async("Բերիմոռ", 4), 
        order_shawarma_async("Այծեմնիկ", 7)
    ]
    
    # Սպասում ենք բոլոր պատվերների ավարտին
    results = await asyncio.gather(*tasks)
    
    end_time = time.time()
    total_time = end_time - start_time
    
    print(f"\n⏱️  Ընդհանուր ժամանակ: {total_time:.1f} վայրկյան")
    print("Բոլոր պատվերները մշակվել են միաժամանակ!")
    
    return results

# Թեստ արտահայտենք
await main_async()
🚀 ԱՍԻՆԽՐՈՆ ՄՈՏԵՑՈՒՄ
==================================================
18:52:40 - Էռնեստո Փարպեցի: Պատվիրում եմ շաուրմա
18:52:40 - Վաճառող: Լավ Էռնեստո Փարպեցի, 5 վայրկյան կտևի
18:52:40 - Բերիմոռ: Պատվիրում եմ շաուրմա
18:52:40 - Վաճառող: Լավ Բերիմոռ, 4 վայրկյան կտևի
18:52:40 - Այծեմնիկ: Պատվիրում եմ շաուրմա
18:52:40 - Վաճառող: Լավ Այծեմնիկ, 7 վայրկյան կտևի
18:52:44 - Վաճառող: Բերիմոռ, ձեր շաուրման պատրաստ է!

18:52:44 - Վաճառող: Բերիմոռ, ձեր շաուրման պատրաստ է!

18:52:45 - Վաճառող: Էռնեստո Փարպեցի, ձեր շաուրման պատրաստ է!

18:52:45 - Վաճառող: Էռնեստո Փարպեցի, ձեր շաուրման պատրաստ է!

18:52:47 - Վաճառող: Այծեմնիկ, ձեր շաուրման պատրաստ է!


⏱️  Ընդհանուր ժամանակ: 7.0 վայրկյան
Բոլոր պատվերները մշակվել են միաժամանակ!
18:52:47 - Վաճառող: Այծեմնիկ, ձեր շաուրման պատրաստ է!


⏱️  Ընդհանուր ժամանակ: 7.0 վայրկյան
Բոլոր պատվերները մշակվել են միաժամանակ!
[None, None, None]
await order_shawarma_async("Էռնեստո Փարպեցի", 5)
18:52:16 - Էռնեստո Փարպեցի: Պատվիրում եմ շաուրմա
18:52:16 - Վաճառող: Լավ Էռնեստո Փարպեցի, 5 վայրկյան կտևի
18:52:21 - Վաճառող: Էռնեստո Փարպեցի, ձեր շաուրման պատրաստ է!

18:52:21 - Վաճառող: Էռնեստո Փարպեցի, ձեր շաուրման պատրաստ է!

🔑 Հիմնական գաղափարներ

async բառը

  • Նպատակ: Ֆունկցիան հայտարարել որպես ասինխրոն
  • Սինտակս: async def function_name():
  • Արդյունք: Ֆունկցիան վերադարձնում է coroutine օբյեկտ
async def cook_shawarma():
    # Այս ֆունկցիան ասինխրոն է
    pass

await բառը

  • Նպատակ: Սպասել ասինխրոն գործողության ավարտին
  • Սինտակս: await some_async_function()
  • Կիրառում: Միայն async ֆունկցիաների ներսում
async def order_process():
    result = await cook_shawarma()  # Սպասում ենք
    return result

🌐 HTTP և Request Types

HTTP (HyperText Transfer Protocol) - ժամանակակից ինտերնետի հիմքն է: Համարյա ցանկացած կայք, բջջային հավելված կամ API այս պրոտոկոլով է աշխատում:

🥙 HTTP-ն «Երևանյան Շաուրմա»-ի օրինակով

Պատկերացրեք HTTP-ն որպես ռեստորանի պատվերների համակարգ:

Հաճախորդ (Client) ←→ Սերվեր (Server)
     ↓                    ↑
"Request"              "Response"
(Հարցում)              (Պատասխան)

📋 HTTP Request-ի կառուցվածքը

POST /order HTTP/1.1                 ← Method + Path + Version
Host: yerevanyan-shawarma.am         ← Headers
Content-Type: application/json
Authorization: Bearer token123

{                                    ← Body (տվյալներ)
  "customer": "Սպիրիդոն",
  "item": "իքիբիր",
  "quantity": 2
}

🔄 HTTP Methods - Երևանյան Շաուրմա օրինակներով

Լավ նյութ

📚 Հիմնական հասկացություններ

Safe (Անվտանգ) - HTTP մեթոդը համարվում է անվտանգ, եթե այն չի փոխում սերվերի վիճակը։ Նման մեթոդները կարող են կանչվել առանց վախենալու, որ կփոխեն տվյալները:

Idempotent - HTTP մեթոդը համարվում է idempotent, եթե նույն հարցումը մի քանի անգամ ուղարկելուց արդյունքը չի փոխվում։ Այսինքն՝ 1 անգամ կամ 10 անգամ կանչելը նույն արդյունքն է տալիս:

🌐 HTTP Methods-ի ցուցադրություն

GET - Տեղեկություն ստանալ

  • Նկարագրություն: Տեղեկություն ստանալ (չփոխել ոչինչ)
  • Օրինակ: Մենյուն նայել
  • 🥙 Շաուրմա օրինակ: «Հավի իքիբիր ունե՞ք», «Ի՞նչքան ա»
  • ⚡ Անվտանգ: Այո
  • 🔄 Idempotent: Այո

POST - Նոր բան ստեղծել

  • Նկարագրություն: Նոր բան ստեղծել
  • Օրինակ: Նոր պատվեր տալ
  • 🥙 Շաուրմա օրինակ: «Ինձ մի հատ հավի շաուրմա խնդրում եմ»
  • ⚡ Անվտանգ: Ոչ
  • 🔄 Idempotent: Ոչ

PUT - Ամբողջությամբ փոխարինել/թարմացնել

  • Նկարագրություն: Ամբողջությամբ փոխարինել/թարմացնել
  • Օրինակ: Պատվերը ամբողջությամբ փոխել
  • 🥙 Շաուրմա օրինակ: «Չեղարկեք հավը, ուզում եմ տավարով»
  • ⚡ Անվտանգ: Ոչ
  • 🔄 Idempotent: Այո

PATCH - Մասնակիորեն թարմացնել

  • Նկարագրություն: Մասնակիորեն թարմացնել
  • Օրինակ: Միայն մեկ բաղադրիչ փոխել
  • 🥙 Շաուրմա օրինակ: «Խնդրում եմ առանց սոխի»
  • ⚡ Անվտանգ: Ոչ
  • 🔄 Idempotent: Ոչ

DELETE - Ջնջել

  • Նկարագրություն: Ջնջել
  • Օրինակ: Պատվերը չեղարկել
  • 🥙 Շաուրմա օրինակ: «Չեղարկում եմ իմ պատվերը»
  • ⚡ Անվտանգ: Ոչ
  • 🔄 Idempotent: Այո

💡 Հիշեցում - Safe: Սերվերի վիճակը չի փոխվում - Idempotent: Նույն գործողությունը կրկնելիս նույն արդյունքը

HTTP Method Safe Idempotent
GET
POST
PUT
PATCH
DELETE

🚦 HTTP Status Codes and Errors

Լավ նյութ

HTTP Response-ի կառուցվածքը

HTTP/1.1 200 OK                     ← Status Line
Content-Type: application/json      ← Headers
Set-Cookie: session=abc123

{                                   ← Body
  "message": "Պատվերը ստացվեց",
  "order_id": 12345
}

HTTP Status Code-երը բաժանվում են 5 խմբի:

1️⃣ 1xx - Informational (Տեղեկություն) - 100 Continue - “Ստացա հարցումը, շարունակեք խոսել, լսում եմ”

2️⃣ 2xx - Success (Հաջողություն) ✅ - 200 OK - “Ամեն ինչ լավ է” - 201 Created - “Պատվերը ստեղծվեց” - 204 No Content - “Կատարվեց, բայց ուղարկելու բան չկա”

3️⃣ 3xx - Redirection (Վերահղում) 🔄 - 301 Moved Permanently - “Ռեստորանը տեղափոխվել է մեկ այլ հասցե” - 302 Found - “Ժամանակավորապես աշխատում ենք այլ հասցեում”

4️⃣ 4xx - Client Error (Client-ի սխալ) ❌ - 400 Bad Request - “Չհասկացա ձեր պատվերը” - 401 Unauthorized - “Նույնականացում չեք անցել” - 403 Forbidden - “Հասանելի չէ ձեզ համար” - 404 Not Found - “Այդպիսի ուտելիք չունենք” - 418 I’m a teapot - “Ես թեյնիկ եմ (տես ներքևում)” - 422 Unprocessable Entity - “Հասկանալի է, բայց կատարել չեմ կարող”

5️⃣ 5xx - Server Error (Սերվերի սխալ) 💥 - 500 Internal Server Error - “Խոհանոցում խնդիր կա” - 502 Bad Gateway - “Մատակարարը չի պատասխանում” - 503 Service Unavailable - “Ժամանակավորապես փակ ենք”

from Wiki 418 I’m a teapot (RFC 2324, RFC 7168) This code was defined in 1998 as one of the traditional IETF April Fools’ jokes, in RFC 2324, Hyper Text Coffee Pot Control Protocol, and is not expected to be implemented by actual HTTP servers. The RFC specifies this code should be returned by teapots requested to brew coffee.[19] This HTTP status is used as an Easter egg in some websites, such as Google.com’s “I’m a teapot” easter egg.[20][21][22] Sometimes, this status code is also used as a response to a blocked request, instead of the more appropriate 403 Forbidden.[23][24]

# 🛡️ Error Handling - Գործնական օրինակներ

from fastapi import HTTPException
import random

# Պարզ սխալների ցուցադրություն
def demonstrate_status_codes():
    """HTTP status codes-ի ցուցադրություն"""
    
    print("🚦 HTTP STATUS CODES - ԵՐԵՎԱՆՅԱՆ ՇԱՈՒՐՄԱ")
    print("=" * 55)
    
    scenarios = [
        {
            "situation": "Հաճախորդը հարցնում է ցանկը",
            "code": 200,
            "message": "OK - Ցանկը բարեհաջող ուղարկվեց"
        },
        {
            "situation": "Նոր պատվեր է ստեղծվել",
            "code": 201,
            "message": "Created - Պատվերը հաջողությամբ ստեղծվեց"
        },
        {
            "situation": "Հարցնում են չգոյություն ունեցող ուտելիք",
            "code": 404,
            "message": "Not Found - Այդպիսի ուտելիք չունենք"
        },
        {
            "situation": "Պատվիրում են փակ ժամերին",
            "code": 403,
            "message": "Forbidden - Ռեստորանը փակ է"
        },
        {
            "situation": "Սխալ տվյալներ են ուղարկել",
            "code": 400,
            "message": "Bad Request - Չհասկացա ձեր պատվերը"
        },
        {
            "situation": "Խոհանոցում հրդեհ է",
            "code": 500,
            "message": "Internal Server Error - Ներքին խնդիր"
        }
    ]
    
    for i, scenario in enumerate(scenarios, 1):
        # Determine status emoji based on code range
        if 200 <= scenario["code"] < 300:
            status_emoji = "✅"
        elif 400 <= scenario["code"] < 600:
            status_emoji = "❌"
        else:
            status_emoji = "🔄"
        print(f"\n{i}. {scenario['situation']}")
        print(f"   {status_emoji} {scenario['code']} - {scenario['message']}")

# Սխալների simulation
class ShawarmaError(Exception):
    """Շաուրմա-ի սխալների class"""
    pass

def simulate_order_errors(order_item: str):
    """Պատվերի սխալների simulation"""
    
    # Տարբեր սխալների ցուցադրություն
    error_scenarios = {
        "տավարով": {"available": True, "prep_time": 5},
        "հավով": {"available": True, "prep_time": 3},
        "բանջարեղենով": {"available": False, "error": "Բանջարեղենը վերջացել է"},
        "ձկնով": {"available": False, "error": "Ձուկ չունենք"},
        "հատուկ": {"available": True, "prep_time": 8, "special_error": "Հատուկ բաղադրիչը չկա"}
    }
    
    if order_item not in error_scenarios:
        raise HTTPException(status_code=404, detail=f"'{order_item}' շաուրմա չունենք")
    
    scenario = error_scenarios[order_item]
    
    if not scenario["available"]:
        raise HTTPException(status_code=422, detail=scenario["error"])
    
    if "special_error" in scenario and random.choice([True, False]):
        raise HTTPException(status_code=500, detail=scenario["special_error"])
    
    return {"message": f"{order_item} շաուրմա պատրաստ կլինի {scenario['prep_time']} րոպեում", 
            "prep_time": scenario["prep_time"]}

# Թեստ
print("📋 ՍԽԱԼՆԵՐԻ ՑՈՒՑԱԴՐՈՒԹՅՈՒՆ:")
demonstrate_status_codes()

print("\n" + "="*60)
print("🧪 ՊԱՏՎԵՐՆԵՐԻ ԹԵՍՏ:")

test_orders = ["հավով", "բանջարեղենով", "ձկնով", "կատվով"]

for order in test_orders:
    try:
        result = simulate_order_errors(order)
        print(f"✅ {order}: {result['message']}")
    except HTTPException as e:
        print(f"❌ {order}: {e.status_code} - {e.detail}")
    except Exception as e:
        print(f"💥 {order}: Անսպասելի սխալ - {e}")
📋 ՍԽԱԼՆԵՐԻ ՑՈՒՑԱԴՐՈՒԹՅՈՒՆ:
🚦 HTTP STATUS CODES - ԵՐԵՎԱՆՅԱՆ ՇԱՈՒՐՄԱ
=======================================================

1. Հաճախորդը հարցնում է ցանկը
   ✅ 200 - OK - Ցանկը բարեհաջող ուղարկվեց

2. Նոր պատվեր է ստեղծվել
   ✅ 201 - Created - Պատվերը հաջողությամբ ստեղծվեց

3. Հարցնում են չգոյություն ունեցող ուտելիք
   ❌ 404 - Not Found - Այդպիսի ուտելիք չունենք

4. Պատվիրում են փակ ժամերին
   ❌ 403 - Forbidden - Ռեստորանը փակ է

5. Սխալ տվյալներ են ուղարկել
   ❌ 400 - Bad Request - Չհասկացա ձեր պատվերը

6. Խոհանոցում հրդեհ է
   ❌ 500 - Internal Server Error - Ներքին խնդիր

============================================================
🧪 ՊԱՏՎԵՐՆԵՐԻ ԹԵՍՏ:
✅ հավով: հավով շաուրմա պատրաստ կլինի 3 րոպեում
❌ բանջարեղենով: 422 - Բանջարեղենը վերջացել է
❌ ձկնով: 422 - Ձուկ չունենք
❌ կատվով: 404 - 'կատվով' շաուրմա չունենք

TODO CURL

https://curl.se/

TODO Render Deployment https://render.com/

import requests 

response = requests.get("https://www.ysu.am/faculty/516")

print(response.content)

🚀 FastAPI vs 🌶️ Flask

Ֆայլերի կառուցվածքը

Բոլոր կոդային օրինակները տեղափոխվել են apis/ պանակ:

apis/
├── fastapi_shawarma.py    # FastAPI ամբողջական օրինակ
├── flask_shawarma.py      # Flask ամբողջական օրինակ  
├── requirements.txt       # Անհրաժեշտ գրադարանները
└── README.md             # Հրահանգներ և օգտագործման օրինակներ

🔄 Գործարկում

FastAPI

cd apis
pip install -r requirements.txt
python fastapi_shawarma.py
# Բացեք http://localhost:8000/docs

Flask

cd apis
pip install -r requirements.txt
python flask_shawarma.py
# Բացեք http://localhost:5000

⚖️ FastAPI vs Flask - Համեմատություն

🎲 32 (15)

Flag Counter