Модуль 7.2 — Async Rust и Python
Упражнение 7.2.1: Проверка окружения
7.2.1.A: Установка инструментов ⭐
Следуйте инструкциям по установке инструмента сборки Maturin: https://pyo3.rs/v0.21.2/getting-started. Рекомендуем использовать pyenv: https://github.com/pyenv/pyenv, но pyenv работает только в UNIX и WSL. Конечно, можно использовать и любой другой менеджер виртуальных окружений.
Перейдите в терминале в папку exercises/7-rust-for-data-science/1-rust-from-python/1-hello-world/. Создайте и активируйте виртуальное окружение и установите всё необходимое. Если вы используете pyenv, выполните следующие команды:
$ pyenv activate pyo3
$ pip install maturin
$ pip install asyncio
$ pip install aiofiles
7.2.1.B: Пробный запуск ⭐
Если всё прошло успешно, вы сможете собрать этот проект в расширение для Python:
$ maturin develop
$ python test.py
Скрипт test.py заканчивается вызовом функции, которая безусловно выбрасывает исключение, поэтому ожидается, что скрипт завершится необработанным исключением.
Теперь попробуйте запустить и test_async.py и понаблюдайте за его поведением.
7.2.1.C: Параллельный запуск футур ⭐⭐
В Python-скрипте для выполнения нескольких awaitable-объектов можно использовать asyncio.gather. Создайте несколько футур print_sleep с разной длительностью сна, запустите их через asyncio.gather и понаблюдайте за поведением.
Упражнение 7.2.2: Потоковый JSON
Вот полезный крейт: Struson. Он позволяет (де)сериализовать JSON-объекты потоково, то есть не держать все данные в памяти. Это контрастирует с отличной библиотекой serde_json, которая очень удобна, но не позволяет легко (де)сериализовать данные потоком. Мы создадим обёртку над Struson, которой можно будет пользоваться из Python.
Представим, что у нас есть поток JSON. Он может приходить от кого-то из интернета или от другого процесса на этой машине. Или, например, из файла. Поток состоит из данных такого вида:
[
{
"lhs": {
"d":[
1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12
],
"n": 3
},
"op": [
{
"code": "dot",
"rhs": {
"d": [
13, 14, 15, 16,
17, 18, 19, 20,
21, 22, 23, 24
],
"n": 3
}
}
]
}
]
Этот JSON — массив объектов, каждый из которых описывает следующее:
lhs: матрица сmстроками иnстолбцами.mможно вычислить поnи длинеd. В данном случаеm = len / n = 12 / 3 = 4.op: последовательность операций, которые нужно выполнить надlhsи, если операция принимает два операнда, над полемrhs. Полеcodeзадаёт операцию, которая должна более или менее соответствовать методам типа Matrix из nalgebra. В данном случае нужно выполнить методMatrix::dot, который, и не без оснований, описан как «скалярное произведение двух векторов или матриц (рассматриваемых как векторы)».
Наша библиотека должна потоково десериализовать каждый входящий объект из потока байтов asyncio, применить заданную операцию и передать результат дальше.
Упражнение 7.2.2.A: Попробуйте ⭐
Заготовка в exercises/7-rust-for-data-science/1-rust-from-python/2-strompy/ реализует задуманное на Rust и также содержит синхронную реализацию. Посмотрите код и попробуйте в нём разобраться. Если хотите узнать больше о зависимостях, помогут docs.rs. Обратите внимание, что для struson мы импортируем форк, который поддерживает асинхронную десериализацию.
Чтобы собрать и выполнить тесты из src/lib.rs, запустите cargo test.
Упражнение 7.2.2.B: Синхронный путь ⭐⭐
В ходе следующих упражнений полезно иметь под рукой руководство PyO3 и документацию API PyO3.
Это упражнение посвящено тому, чтобы заставить работать strompy_test.py. Для этого исправьте todo!() в функции exec в src/lib.rs. Не забудьте добавить функцию в экспортируемый модуль!
Если всё сделано правильно, запустите:
$ maturin develop
$ python strompy_test.py
7.2.2.B: Асинхронный путь ⭐⭐⭐
На этот раз заставим работать strompy_test_async.py. Реализуйте feed_bytes и добавьте в тип StrompyJsonReader асинхронный метод, который возвращает PyResult<Option<Vec<Vec<f64>>>> и экспортируется под именем next.
Если всё сделано правильно, запустите:
$ maturin develop
$ python strompy_test_async.py
7.2.2.C: Дополнительные возможности (бонус) ⭐⭐⭐
Сможете ли вы добавить в strompy поддержку других операций из nalgebra?
7.2.2.D: Наведение порядка (бонус) ⭐⭐⭐⭐
Представление векторов как Vec<Vec<f64>> работает, но не слишком хорошо. Сделайте так, чтобы StrompyJsonReader возвращал PyResult<Option<Py<PyList>>>, то есть списки PyList вместо векторов.
Это довольно трудное упражнение, поскольку оно связано с умными указателями PyO3: Py<T>, Bound<'py, T> и токенами GIL: Python.