Sanic-fast asynchronous web flime

онлайн тренажер по питону
Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Sanic

Sanic — a modern asynchronous web framework for Python, designed for building high‑performance web applications. Built on asyncio, it can efficiently handle thousands of concurrent connections thanks to native async/await support. Sanic is ideal for creating REST APIs, microservices, real‑time applications, and high‑load back‑ends.

Key Features of Sanic

Performance and Architecture

Sanic is engineered with speed in mind. The framework leverages asyncio for asynchronous request handling, achieving performance comparable to Node.js and Go. Built‑in async/await support makes code more readable and efficient.

Functional Capabilities

  • Full async/await support out of the box
  • Routing with parameters and middleware
  • Integrated WebSocket support
  • Template engine and static file handling
  • CORS, cookie, and session support
  • Simple and intuitive project structure
  • Excellent scalability for API servers

Installation and Initial Setup

Basic Installation

pip install sanic

Additional Dependencies

pip install sanic[ext]  # Extensions
pip install jinja2 aiofiles python-dotenv

Creating Your First Application

from sanic import Sanic
from sanic.response import text

app = Sanic("MyApp")

@app.get("/")
async def index(request):
    return text("Hello from Sanic")

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

Routing and Request Handling

Basic Routes

@app.get("/")
async def get_handler(request):
    return text("GET request")

@app.post("/")
async def post_handler(request):
    return text("POST request")

@app.put("/")
async def put_handler(request):
    return text("PUT request")

@app.delete("/")
async def delete_handler(request):
    return text("DELETE request")

Asynchronous Routes

import asyncio

@app.get("/async")
async def async_route(request):
    await asyncio.sleep(1)  # Simulate async operation
    return text("Asynchronous response")

Working with Parameters and Data

Route Parameters

@app.get("/user/<user_id:int>")
async def get_user(request, user_id):
    return text(f"User ID: {user_id}")

@app.get("/profile/<username:str>")
async def get_profile(request, username):
    return text(f"Profile: {username}")

Query Parameters

@app.get("/search")
async def search(request):
    query = request.args.get("q", "nothing")
    limit = request.args.get("limit", "10")
    return text(f"Search: {query}, limit: {limit}")

JSON and Form Handling

from sanic.response import json

@app.post("/api/data")
async def receive_data(request):
    data = request.json
    return json({"received": data})

@app.post("/form")
async def handle_form(request):
    username = request.form.get("username")
    email = request.form.get("email")
    return json({"username": username, "email": email})

Response Types

Various Response Formats

from sanic.response import text, json, html, redirect, file

@app.get("/text")
async def text_response(request):
    return text("Simple text")

@app.get("/json")
async def json_response(request):
    return json({"message": "JSON response"})

@app.get("/html")
async def html_response(request):
    return html("<h1>HTML page</h1>")

@app.get("/redirect")
async def redirect_response(request):
    return redirect("/new-page")

@app.get("/file")
async def file_response(request):
    return await file("path/to/file.pdf")

Error Handling

Built‑in Exceptions

from sanic.exceptions import NotFound, SanicException, Unauthorized

@app.exception(NotFound)
async def handle_404(request, exception):
    return json({"error": "Page not found"}, status=404)

@app.exception(Exception)
async def handle_generic_error(request, exception):
    return json({"error": "Internal server error"}, status=500)

Custom Exceptions

class CustomException(SanicException):
    status_code = 400
    message = "Custom error"

@app.exception(CustomException)
async def handle_custom_error(request, exception):
    return json({"error": exception.message}, status=exception.status_code)

Middleware and Lifecycle

Request and Response Middleware

@app.middleware("request")
async def before_request(request):
    request.ctx.start_time = time.time()
    print(f"Request to {request.path}")

@app.middleware("response")
async def after_response(request, response):
    response.headers["X-Framework"] = "Sanic"
    response.headers["X-Process-Time"] = str(time.time() - request.ctx.start_time)

Lifecycle Hooks

@app.before_server_start
async def setup_db(app, loop):
    print("Connecting to the database")

@app.after_server_stop
async def close_db(app, loop):
    print("Closing database connection")

Cookies and Sessions

Cookies

@app.get("/set-cookie")
async def set_cookie(request):
    response = text("Cookie set")
    response.cookies["session_id"] = "123456"
    response.cookies["session_id"]["httponly"] = True
    response.cookies["session_id"]["secure"] = True
    return response

@app.get("/get-cookie")
async def get_cookie(request):
    session_id = request.cookies.get("session_id", "not set")
    return text(f"Session ID: {session_id}")

Header Management

@app.get("/headers")
async def handle_headers(request):
    user_agent = request.headers.get("User-Agent")
    auth_header = request.headers.get("Authorization")
    
    response = json({"user_agent": user_agent})
    response.headers["Custom-Header"] = "Sanic-Value"
    return response

File Uploads

File Handling

import os

@app.post("/upload")
async def upload_file(request):
    file = request.files.get("file")
    if file:
        upload_dir = "./uploads"
        os.makedirs(upload_dir, exist_ok=True)
        
        file_path = os.path.join(upload_dir, file.name)
        with open(file_path, "wb") as f:
            f.write(file.body)
        
        return json({"message": "File uploaded", "filename": file.name})
    return json({"error": "File not found"}, status=400)

Templates and Static Files

Jinja2 Integration

from sanic_ext import Extend
from jinja2 import Environment, FileSystemLoader

app = Sanic("TemplateApp")
Extend(app)

@app.get("/template")
async def render_template(request):
    return await app.ext.render("index.html", context={"name": "Sanic"})

Static Files

app.static("/static", "./static")
app.static("/media", "./media", name="media")

WebSocket Support

Basic WebSocket

@app.websocket("/ws")
async def websocket_handler(request, ws):
    while True:
        try:
            message = await ws.recv()
            await ws.send(f"Echo: {message}")
        except:
            break

WebSocket with Authentication

@app.websocket("/ws/auth")
async def auth_websocket(request, ws):
    token = request.headers.get("Authorization")
    if not token:
        await ws.close(code=1008, reason="Unauthorized")
        return
    
    while True:
        message = await ws.recv()
        await ws.send(f"Authenticated echo: {message}")

Database Integration

Using SQLAlchemy

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

engine = create_async_engine("sqlite+aiosqlite:///./database.db")
SessionLocal = sessionmaker(bind=engine, class_=AsyncSession)

@app.middleware("request")
async def inject_session(request):
    request.ctx.session = SessionLocal()

@app.middleware("response")
async def close_session(request, response):
    await request.ctx.session.close()

Working with Async ORM

@app.get("/users")
async def get_users(request):
    async with request.ctx.session as session:
        # Example ORM query
        users = await session.execute("SELECT * FROM users")
        return json([dict(user) for user in users])

Project Structure

Recommended Organization

project/
├── app.py              # Main application
├── config.py           # Configuration
├── requirements.txt    # Dependencies
├── routes/            # Routes
│   ├── __init__.py
│   ├── api.py
│   └── auth.py
├── models/            # Data models
│   ├── __init__.py
│   └── user.py
├── middleware/        # Middleware
│   ├── __init__.py
│   └── auth.py
├── templates/         # Templates
│   └── index.html
├── static/           # Static assets
│   ├── css/
│   └── js/
└── tests/            # Tests
    ├── __init__.py
    └── test_app.py

Blueprints for Modularity

Creating a Blueprint

from sanic import Blueprint

# routes/users.py
users_bp = Blueprint("users", url_prefix="/users")

@users_bp.get("/")
async def get_users(request):
    return json({"users": []})

@users_bp.get("/<user_id:int>")
async def get_user(request, user_id):
    return json({"user_id": user_id})

# app.py
from routes.users import users_bp
app.blueprint(users_bp)

Testing

Writing Tests

import pytest
from app import app

@pytest.mark.asyncio
async def test_index():
    request, response = await app.asgi_client.get("/")
    assert response.status == 200
    assert "Hello from Sanic" in response.text

@pytest.mark.asyncio
async def test_json_endpoint():
    request, response = await app.asgi_client.get("/api/data")
    assert response.status == 200
    assert response.json["status"] == "ok"

Testing with Fixtures

@pytest.fixture
def test_client():
    return app.asgi_client

@pytest.mark.asyncio
async def test_with_fixture(test_client):
    request, response = await test_client.post("/api/users", json={"name": "Test"})
    assert response.status == 201

Deployment and Production

Production Settings

# config.py
class ProductionConfig:
    DEBUG = False
    ACCESS_LOG = False
    WORKERS = 4
    HOST = "0.0.0.0"
    PORT = 8000

# app.py
from config import ProductionConfig

if __name__ == "__main__":
    app.run(
        host=ProductionConfig.HOST,
        port=ProductionConfig.PORT,
        workers=ProductionConfig.WORKERS,
        debug=ProductionConfig.DEBUG,
        access_log=ProductionConfig.ACCESS_LOG
    )

Running from the Command Line

# Basic run
sanic app.app --host=0.0.0.0 --port=8000

# Production with workers
sanic app.app --host=0.0.0.0 --port=8000 --workers=4

# Additional options
sanic app.app --workers=4 --access-log --debug

Using Gunicorn

pip install gunicorn
gunicorn app:app --bind 0.0.0.0:8000 --worker-class sanic.worker.GunicornWorker

Complete Table of Sanic Methods and Functions

Category Method / Function Description Usage Example
Application Creation Sanic(name) Creates a Sanic app instance app = Sanic("MyApp")
Routing @app.get(path) GET route @app.get("/users")
  @app.post(path) POST route @app.post("/users")
  @app.put(path) PUT route @app.put("/users/<id>")
  @app.delete(path) DELETE route @app.delete("/users/<id>")
  @app.patch(path) PATCH route @app.patch("/users/<id>")
  @app.head(path) HEAD route @app.head("/users")
  @app.options(path) OPTIONS route @app.options("/users")
  @app.route(path, methods) Universal route @app.route("/api", methods=["GET", "POST"])
Running the App app.run() Start the server app.run(host="0.0.0.0", port=8000)
  app.create_server() Create a server object server = app.create_server()
Request Object request.args Query parameters request.args.get("param")
  request.json JSON payload data = request.json
  request.form Form data request.form.get("field")
  request.files Uploaded files request.files.get("file")
  request.headers HTTP headers request.headers.get("Authorization")
  request.cookies Cookies request.cookies.get("session")
  request.path Request path path = request.path
  request.method HTTP method method = request.method
  request.ip Client IP address ip = request.ip
  request.url Full URL url = request.url
  request.ctx Request context request.ctx.user = user
Responses text(content) Plain text response return text("Hello")
  json(data) JSON response return json({"key": "value"})
  html(content) HTML response return html("<h1>Title</h1>")
  redirect(url) Redirect return redirect("/new-page")
  file(path) File response return await file("./image.jpg")
  stream(func) Streaming response return stream(stream_func)
  response.cookies Set cookies response.cookies["key"] = "value"
  response.headers Set headers response.headers["X-Custom"] = "value"
Middleware @app.middleware("request") Request middleware @app.middleware("request")
  @app.middleware("response") Response middleware @app.middleware("response")
Error Handling @app.exception(Exception) Exception handler @app.exception(NotFound)
  SanicException Base exception raise SanicException("Error")
  NotFound 404 error raise NotFound("Not found")
  Unauthorized 401 error raise Unauthorized("Auth required")
  Forbidden 403 error raise Forbidden("Access denied")
Lifecycle Hooks @app.before_server_start Before server start @app.before_server_start
  @app.after_server_start After server start @app.after_server_start
  @app.before_server_stop Before server stop @app.before_server_stop
  @app.after_server_stop After server stop @app.after_server_stop
WebSocket @app.websocket(path) WebSocket route @app.websocket("/ws")
  ws.send(data) Send a message await ws.send("message")
  ws.recv() Receive a message message = await ws.recv()
  ws.close() Close the connection await ws.close()
Blueprints Blueprint(name) Create a blueprint bp = Blueprint("api")
  app.blueprint(bp) Register a blueprint app.blueprint(api_bp)
Static Files app.static(uri, file_or_dir) Serve static assets app.static("/static", "./static")
Configuration app.config Application configuration app.config.DEBUG = True
  app.config.from_object() Load from an object app.config.from_object(Config)
Listeners @app.listener("before_server_start") Event listener @app.listener("before_server_start")
Background Tasks app.add_task(coro) Add a background coroutine app.add_task(background_task())
Testing app.test_client Test client client = app.test_client
  app.asgi_client ASGI client await app.asgi_client.get("/")

Advanced Features

Custom Decorators

from functools import wraps

def require_auth(f):
    @wraps(f)
    async def decorated_function(request, *args, **kwargs):
        auth = request.headers.get("Authorization")
        if not auth:
            return json({"error": "Unauthorized"}, status=401)
        return await f(request, *args, **kwargs)
    return decorated_function

@app.get("/protected")
@require_auth
async def protected_route(request):
    return json({"message": "Protected content"})

Data Validation

from cerberus import Validator

@app.post("/api/users")
async def create_user(request):
    schema = {
        'name': {'type': 'string', 'required': True},
        'email': {'type': 'string', 'required': True, 'regex': r'^[^@]+@[^@]+\.[^@]+$'},
        'age': {'type': 'integer', 'min': 0, 'max': 150}
    }
    
    v = Validator(schema)
    if not v.validate(request.json):
        return json({"errors": v.errors}, status=400)
    
    # Process validated data
    return json({"message": "User created"}, status=201)

Practical Usage Examples

RESTful API

users_data = []

@app.get("/api/users")
async def get_users(request):
    return json(users_data)

@app.post("/api/users")
async def create_user(request):
    user = request.json
    user['id'] = len(users_data) + 1
    users_data.append(user)
    return json(user, status=201)

@app.get("/api/users/<user_id:int>")
async def get_user(request, user_id):
    user = next((u for u in users_data if u['id'] == user_id), None)
    if not user:
        return json({"error": "User not found"}, status=404)
    return json(user)

WebSocket Chat

connected_clients = set()

@app.websocket("/chat")
async def chat_handler(request, ws):
    connected_clients.add(ws)
    try:
        async for message in ws:
            # Broadcast to all connected clients
            for client in connected_clients.copy():
                try:
                    await client.send(message)
                except:
                    connected_clients.remove(client)
    finally:
        connected_clients.discard(ws)

Performance Optimization

High‑Load Settings

# Optimized configuration
app.config.update({
    "KEEP_ALIVE_TIMEOUT": 30,
    "REQUEST_TIMEOUT": 60,
    "RESPONSE_TIMEOUT": 60,
    "REQUEST_MAX_SIZE": 100_000_000,  # 100 MB
    "ACCESS_LOG": False,
    "AUTO_RELOAD": False,
})

Connection Pooling

import aioredis
from aiohttp import ClientSession

@app.before_server_start
async def setup_connections(app, loop):
    # Redis connection pool
    app.ctx.redis = await aioredis.create_redis_pool(
        'redis://localhost',
        maxsize=10
    )
    
    # HTTP client session
    app.ctx.http_session = ClientSession()

@app.after_server_stop
async def cleanup_connections(app, loop):
    app.ctx.redis.close()
    await app.ctx.redis.wait_closed()
    await app.ctx.http_session.close()

Security

CORS Settings

from sanic_cors import CORS

CORS(app, resources={
    r"/api/*": {
        "origins": ["http://localhost:3000"],
        "methods": ["GET", "POST", "PUT", "DELETE"],
        "allow_headers": ["Content-Type", "Authorization"]
    }
})

Rate Limiting

from collections import defaultdict
import time

request_counts = defaultdict(list)

@app.middleware("request")
async def rate_limit(request):
    client_ip = request.ip
    now = time.time()
    
    # Clean up old requests
    request_counts[client_ip] = [
        req_time for req_time in request_counts[client_ip]
        if now - req_time < 60  # Last minute
    ]
    
    # Enforce limit
    if len(request_counts[client_ip]) >= 100:  # 100 requests per minute
        return json({"error": "Rate limit exceeded"}, status=429)
    
    request_counts[client_ip].append(now)

Frequently Asked Questions

What is Sanic and what is it used for?

Sanic — a high‑performance asynchronous web framework for Python that uses async/await syntax. It is intended for building fast APIs, web services, and microservices capable of handling a large number of concurrent connections.

How does Sanic differ from Flask and Django?

Unlike Flask and Django, Sanic is fully asynchronous, delivering significantly higher throughput under heavy request loads. It also includes built‑in WebSocket support and a modern API‑first development approach.

Can Sanic be used in production?

Yes, Sanic is production‑ready. Many companies run high‑traffic applications with it. For production, it is recommended to use multiple workers and tune performance‑related settings.

Does Sanic support database integration?

Sanic works seamlessly with async ORMs and database drivers such as SQLAlchemy (with asyncio), asyncpg for PostgreSQL, aiomysql for MySQL, and other async‑compatible libraries.

How can I ensure security in Sanic applications?

Sanic supports standard security measures: HTTPS, CORS, JWT authentication, input validation, rate limiting, and middleware for access‑control checks.

What testing tools are available for Sanic?

Sanic provides a built‑in test client, integrates well with pytest for asynchronous tests, and works smoothly with popular Python testing libraries.

$

News