microdot — мінімальний HTTP-фреймворк

Microdot — це невеликий HTTP-фреймворк для MicroPython, натхненний Flask. Він працює поверх asyncio, обробляє кількох одночасних клієнтів без потоків і надає знайомий API декораторів маршрутів. Мінімальна програма виглядає так:

from microdot import Microdot

app = Microdot()

@app.route('/')
async def index(request):
    return 'Hello, world!'

app.run(host='0.0.0.0', port=80)

class Microdot

class microdot.Microdot

Екземпляр HTTP-застосунку. Один екземпляр створюється на початку скрипта і декорується обробниками маршрутів; виклик run() або очікування start_server() починає обслуговування.

Реєстрація маршрутів

route(url_pattern: str, methods: list | None = None) Callable

Декоратор, що реєструє обробник для url_pattern під вказаними HTTP-методами (за замовчуванням ['GET']). Повертає декоратор, який при застосуванні до функції реєструє її як обробник і повертає функцію незміненою.

url_pattern

Шаблон шляху. Підтримує статичні сегменти (/users) і динамічні сегменти, укладені у < / >. Динамічні сегменти приймають необов’язковий префікс типу, відокремлений :<int:id>, <path:rest>, <re:[0-9a-f]+:hex>. Тип за замовчуванням — string.

methods

Список назв HTTP-методів. Якщо не вказано, відповідає лише GET.

Обробник викликається з об’єктом запиту першим, потім будь-які захоплені динамічні сегменти як іменовані аргументи. Значення, що повертає обробник, стає HTTP-відповіддю: Response, рядок, кортеж (body, status_code[, headers]) або dict / list (надсилається як JSON).

get(url_pattern: str) Callable

Зручний псевдонім для route(url_pattern, methods=['GET']) – реєструє задекоровану функцію як обробник GET для url_pattern. Повертає декоратор.

post(url_pattern: str) Callable

Зручний псевдонім для route(url_pattern, methods=['POST']) – реєструє задекоровану функцію як обробник POST для url_pattern. Повертає декоратор.

put(url_pattern: str) Callable

Зручний псевдонім для route(url_pattern, methods=['PUT']) – реєструє задекоровану функцію як обробник PUT для url_pattern. Повертає декоратор.

patch(url_pattern: str) Callable

Зручний псевдонім для route(url_pattern, methods=['PATCH']) – реєструє задекоровану функцію як обробник PATCH для url_pattern. Повертає декоратор.

delete(url_pattern: str) Callable

Зручний псевдонім для route(url_pattern, methods=['DELETE']) – реєструє задекоровану функцію як обробник DELETE для url_pattern. Повертає декоратор.

Хуки життєвого циклу

before_request(f: Callable) Callable

Декоратор, що реєструє f для виконання перед кожним запитом. f приймає об’єкт запиту; повернуте значення зазвичай ігнорується. Щоб перервати запит, поверніть Response (або значення, яке перетворюється на нього) – решта конвеєра пропускається і надсилається ця відповідь. Повертає f незміненою.

after_request(f: Callable) Callable

Декоратор, що реєструє f для виконання після кожного успішного запиту. f приймає (request, response) і має повертати (можливо змінений) об’єкт відповіді. Повертає f незміненою.

after_error_request(f: Callable) Callable

Декоратор, що реєструє f для виконання після того, як Microdot генерує відповідь про помилку (404, 500, піднятий виняток тощо). f приймає (request, response) і має повертати (можливо змінений) об’єкт відповіді. Повертає f незміненою.

errorhandler(status_code_or_exception_class) Callable

Декоратор, що реєструє спеціальний обробник для HTTP-коду статусу або класу виняткової ситуації Python. Для кодів статусу обробник приймає лише запит; для класів виняткових ситуацій — (request, exception). Повертає декоратор.

Монтування та переривання

mount(subapp: Microdot, url_prefix: str = '', local: bool = False) None

Підключити маршрути іншого екземпляра Microdot під url_prefix. Коли local дорівнює False (за замовчуванням), обробники before / after / error підзастосунку також приєднуються до батьківського. Коли True, ці обробники виконуються лише для маршрутів підзастосунку. Повертає None.

static abort(status_code: int, reason: str | None = None) None

Підняти HTTPException для переривання поточного запиту з вказаним кодом статусу. Ніколи не повертається нормально – фреймворк перехоплює виняток і перетворює його у відповідну відповідь про помилку. Зручно всередині тіла маршрутів:

from microdot import abort

@app.get('/users/<int:id>')
def get_user(request, id):
    user = lookup(id)
    if user is None:
        abort(404)
    return user.to_dict()

Запуск сервера

run(host: str = '0.0.0.0', port: int = 5000, debug: bool = False, ssl=None) None

Блокує потік, що викликає, запускає asyncio.run() на start_server(). Повертає None лише після виклику shutdown() і виходу циклу прослуховування. Найпростіший спосіб запустити автономний застосунок:

app.run(host='0.0.0.0', port=80)
async start_server(host: str = '0.0.0.0', port: int = 5000, debug: bool = False, ssl=None, start_serving: bool = True) None

Запустити сервер як корутину. Використовуйте це при інтеграції з наявним asyncio циклом подій поряд з іншими задачами. Корутина не повертається до виклику shutdown()

async def main():
    await asyncio.gather(
        app.start_server(port=80),
        capture_loop(),
    )
host

Мережевий інтерфейс для прослуховування. '0.0.0.0' (за замовчуванням) означає всі інтерфейси; '127.0.0.1' означає лише зворотній зв’язок.

port

TCP-порт для прослуховування. За замовчуванням 5000 – виберіть 80 для звичайного HTTP, 443 для HTTPS.

debug

Якщо True, реєструє кожен запит і виводить стек-трейси у stdout.

ssl

ssl.SSLContext для загортання вхідних з’єднань у TLS. None (за замовчуванням) означає звичайний HTTP.

start_serving

Актуально лише для CPython; ігнорується у MicroPython.

shutdown() None

Запросити коректне завершення роботи сервера. Безпечно викликати з обробника маршруту – поточний запит завершується перед виходом з циклу. Повертається негайно; фактичне завершення відбувається після завершення запиту, що виконується.

Атрибути

url_map: list

Список зареєстрованих маршрутів у вигляді кортежів (methods, URLPattern, handler, url_prefix, subapp).

before_request_handlers: list

Список викликуваних об’єктів, зареєстрованих за допомогою before_request(), у порядку реєстрації. Кожен виконується з об’єктом запиту перед обробником маршруту. Пряма мутація списку працює, але рідко потрібна – надавайте перевагу декоратору.

after_request_handlers: list

Список викликуваних об’єктів, зареєстрованих за допомогою after_request(), у порядку реєстрації. Кожен виконується з (request, response) після успішного запиту і має повертати (можливо змінену) відповідь.

after_error_request_handlers: list

Список викликуваних об’єктів, зареєстрованих за допомогою after_error_request(), у порядку реєстрації. Кожен виконується з (request, response) після генерації відповіді про помилку (фреймворком або обробником помилок застосунку) і має повертати відповідь.

error_handlers: dict

Відображення ключів помилок на викликувані обробники, заповнюється за допомогою errorhandler(). Ключами є або HTTP-коди статусу (int), або класи виняткових ситуацій Python; значеннями є зареєстровані обробники. Обробники кодів статусу приймають (request); обробники класів виняткових ситуацій приймають (request, exception).

debug: bool

True поки сервер працює з debug=True.

class Request

class microdot.Request

Вхідний HTTP-запит. Екземпляри передаються обробникам маршрутів як перший позиційний аргумент. Застосунки не конструюють Request напряму.

Атрибути класу

max_content_length: int

Відхиляти запити, чий Content-Length перевищує вказану кількість байтів, відповіддю 413. За замовчуванням 16 КБ.

max_body_length: int

Найбільше тіло, яке буферизується у пам’яті та доступне через body. Більші тіла (до max_content_length) залишаються на сокеті і мають читатися через stream. За замовчуванням 16 КБ.

max_readline: int

Максимальна довжина одного рядка запиту / рядка заголовка у байтах. За замовчуванням 2 КБ.

Атрибути екземпляра

app: Microdot

Екземпляр Microdot, що обробляє запит.

client_addr: tuple

Адреса клієнта у вигляді (host, port).

method: str

Рядок HTTP-методу ('GET', 'POST', …).

scheme: str

'http' або 'https'.

url: str

Повний шлях URL запиту та рядок запиту (все після хоста).

path: str

Лише частина шляху.

query_string: str | None

Частина необробленого рядка запиту або None.

args: MultiDict

Розібраний рядок запиту у вигляді MultiDict.

headers: NoCaseDict

Заголовки запиту у вигляді NoCaseDict (пошук без урахування регістру).

cookies: dict

Розібраний заголовок Cookie у вигляді dict.

content_length: int

Ціле значення Content-Length або 0, якщо відсутнє.

content_type: str | None

Значення заголовка Content-Type або None.

g: object

Вільний контейнер для кожного запиту (звичайний об’єкт). Призначайте атрибути для передачі значень між хуками та обробниками (request.g.user = ...).

route: Callable

Функція обробника, що відповідає цьому запиту.

url_prefix: str

Префікс, під яким змонтовано маршрут, або ''.

subapp: Microdot | None

Змонтований екземпляр підзастосунку або None.

Доступ до тіла

body: bytes

Повне тіло запиту у вигляді bytes. Порожнє, коли тіло передається потоком (див. stream).

stream: object

Асинхронний потоковий об’єкт, що надає read() над тілом. Використовуйте це для тіл більших за max_body_length.

json: dict | list | str | int | float | bool | None

Тіло, розібране як JSON (dict, list, str, int, float або bool – що б не кодував payload), або None, якщо Content-Type не є application/json.

form: MultiDict | None

Поля форми з URL-кодуванням у вигляді MultiDict або None. Для multipart/form-data декоруйте маршрут за допомогою microdot.multipart.with_form_data().

files: dict | None

Завантажені файли у вигляді {name: FileUpload}, заповнюється за допомогою microdot.multipart.with_form_data(). None до виконання цього декоратора.

after_request(f: Callable) Callable

Реєструє локальний для запиту хук after-request – виконується після обробників after-request рівня застосунку, лише при успіху. f приймає (request, response) і має повертати (можливо змінений) об’єкт відповіді. Повертає f незміненою.

class Response

class microdot.Response(body=b'', status_code: int = 200, headers: dict | None = None, reason: str | None = None)

HTTP-відповідь. Більшість обробників повертають значення, які Microdot автоматично перетворює на Response; конструюйте безпосередньо, коли потрібні спеціальні заголовки або конкретний код статусу.

body

Тіло відповіді. str кодується в UTF-8; dict / list кодується як JSON і відповідно встановлюється Content-Type; bytes надсилаються як є; файлоподібний об’єкт або асинхронний генератор передаються потоком.

status_code

Числовий HTTP-статус. За замовчуванням 200.

headers

Dict заголовків відповіді (без урахування регістру).

reason

Спеціальна фраза причини. За замовчуванням "OK" для 200 і "N/A" в інших випадках.

Атрибути класу

default_content_type: str

Content-Type, що використовується, коли жодний не встановлено явно. За замовчуванням 'text/plain'.

default_send_file_max_age: int | None

Значення Cache-Control: max-age для send_file(), коли max_age не вказано. None (за замовчуванням) означає відсутність заголовка Cache-Control.

send_file_buffer_size: int

Розмір блоку для потокової передачі send_file(). За замовчуванням 1024.

already_handled: None

Спеціальне значення, що повертається обробниками, які вже написали відповідь напряму (використовується при оновленнях WebSocket). Завжди None; важлива ідентичність.

types_map: dict

Карта розширень до MIME-типів, яку використовує send_file() для визначення типу вмісту. Відображає css, gif, html, jpg, js, json, png, txt, svg.

Методи

Додати заголовок Set-Cookie. expires може бути попередньо відформатованим рядком або datetime-подібним об’єктом з timetuple(). Повертає None; заголовок додається до цієї відповіді на місці.

Встановити Set-Cookie, що негайно видаляє вказаний cookie. kwargs приймає ті самі параметри, що й set_cookie() (path, domain, secure, http_only, partitioned); expires та max_age ігноруються. Повертає None; заголовок додається до цієї відповіді на місці.

classmethod redirect(location: str, status_code: int = 302) Response

Повернути відповідь перенаправлення:

@app.get('/old')
def old(request):
    return Response.redirect('/new')
classmethod send_file(filename: str, status_code: int = 200, content_type: str | None = None, stream=None, max_age: int | None = None, compressed: bool | str = False, file_extension: str = '') Response

Передавати файл з файлової системи як тіло відповіді потоком. content_type визначається з розширення через types_map, якщо не вказано. compressed=True встановлює Content-Encoding: gzip (файл вже має бути стиснутим).

Попередження

filename відкривається безпосередньо. Ніколи не передавайте незахищений шлях, наданий користувачем – це дозволяє довільне розкриття файлів.

Виняткові ситуації

exception microdot.HTTPException

Піднімається abort() для переривання запиту з конкретним кодом статусу. Microdot перехоплює це і перетворює на відповідну відповідь про помилку.

status_code: int

Числовий HTTP-код статусу для повернення – значення, передане у abort().

reason: str

Фраза причини для включення у відповідь про помилку. Якщо не вказано у abort(), за замовчуванням "<status_code> error" (наприклад, "404 error").

class URLPattern

class microdot.URLPattern(url_pattern: str)

Скомпільована форма шаблону URL маршруту. Конструюється автоматично за допомогою Microdot.route(); застосунки рідко створюють екземпляр безпосередньо.

classmethod register_type(type_name: str, pattern: str = '[^/]+', parser: Callable | None = None) None

Реєстрація нового типу динамічного сегменту для використання у шаблонах маршрутів. Повертає None; тип додається до реєстру типів на рівні класу. Наприклад, щоб додати тип <uuid:...>

URLPattern.register_type('uuid', '[0-9a-f-]{36}')

parser — необов’язковий викликуваний об’єкт, що перетворює захоплений рядок перед тим, як він досягає обробника.

match(path: str) dict | None

Зіставити path з шаблоном і повернути dict захоплених груп або None при відсутності відповідності.

Допоміжні класи

class microdot.MultiDict(initial_dict: dict | None = None)

Підклас dict, що зберігає кілька значень на ключ. Використовується для рядків запиту та тіл форм з URL-кодуванням, де один і той самий ключ може з’являтися більше одного разу (?tag=a&tag=b).

get(key, default=None, type: Callable | None = None)

Повернути перше значення для key, за потреби перетворене за допомогою type (викликуваного). Повертає default, якщо ключ відсутній або перетворення не вдалося; в іншому випадку повертає (за потреби перетворене) значення.

getlist(key, type: Callable | None = None) list

Повернути всі значення для key у вигляді списку, за потреби з кожним значенням, перетвореним за допомогою type. Повертає порожній список, якщо key відсутній.

class microdot.NoCaseDict

Підклас dict з ключами рядків без урахування регістру. Використовується для Request.headers та Response.headers.

Функції рівня модуля

microdot.abort(status_code: int, reason: str | None = None) None

Ярлик для Microdot.abort(). Ніколи не повертається нормально – піднімає HTTPException. Імпортується як from microdot import abort.

microdot.redirect(location: str, status_code: int = 302) Response

Ярлик для Response.redirect(). Імпортується як from microdot import redirect.

microdot.send_file(filename: str, **kwargs) Response

Ярлик для Response.send_file().

microdot.urlencode(s: str) str

Відсотково-кодувати компонент URL. Замінює символи, що мають зарезервоване значення в URL (/, ?, &, =, #, пробіл, …) їхніми %xx шістнадцятковими екранами, щоб результат міг безпечно знаходитися всередині сегмента шляху або значення запиту. Повертає закодований str.

microdot.urldecode(s: str) str

Відсотково-декодувати компонент URL – зворотне до urlencode(). %xx екрани замінюються байтом, який вони кодують, а + перетворюється на пробіл (історична угода рядка запиту). Повертає декодований str.

Підмодулі