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 – что бы ни кодировала полезная нагрузка), или 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

Словарь заголовков ответа (без учёта регистра).

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 с шаблоном и возвращает словарь захваченных групп или 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 (/, ?, &, =, #, пробел, …), их шестнадцатеричными escape-последовательностями %xx, чтобы результат можно было безопасно поместить внутрь сегмента пути или значения запроса. Возвращает закодированную str.

microdot.urldecode(s: str) str

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

Подмодули