Найти тему
Computer Pro

Модуль 5. Задача 2. Удалённое исполнение кода

Что нужно сделать: написать endpoint, который принимает на вход код на Python (строка) и тайм-аут в секундах (положительное число не больше 30). Пользователю возвращается результат работы программы, а если время, отведённое на выполнение кода, истекло, то процесс завершается, после чего отправляется сообщение о том, что исполнение кода не уложилось в данное время.

Для выполнения задания рекомендуется использовать метод Popen.communicate. Не забудьте сделать communicate при превышении тайм-аута — процесс мог что-то вывести за время работы.
все "троеточия" заполнить соответствующим кодом
все "троеточия" заполнить соответствующим кодом

Мы не будем создавать каких либо форм в которых будут указываться код и тайм-аут и затем отправка этих данных. Обычно эти данные посылаются при помощи сторонних программ, таких как Postman. Я же буду использовать консольную программу http (установка в арчподобных линухах: sudo pacman -S httpie).

Принцип работы этой программы таков:

-2

У Flask (как и у Django) есть проверка подлинности формы, для безопасности, чтобы злоумышленники не могли подменить ввод данных своими данными и отправить серверу не то что требуется. Поэтому нужно отключить безопасность следующей строкой, иначе придется создавать полноценный веб портал с формой отправки и проверкой подлинности формы.

-3

Итак, вызывается функция run() вместе с декоратором @app.route('/run_code', method=['POST']) в котором нам нужно принять объект формы, в которой формируются данные code и timeout. Но для этого нужно предварительно создать новый класс который и будет принимать наши данные.

оба поля с валидаторами, т.е. поля обязательны к заполнению
оба поля с валидаторами, т.е. поля обязательны к заполнению

Получаем экземпляр класса CodeForm:

form = CodeForm()

Делаем проверку того что все данные в форме отправлены, иначе выдаётся ошибка:

я специально убрал из запроса поле timeout, валидатор сформировал ошибку
я специально убрал из запроса поле timeout, валидатор сформировал ошибку

Если все данные формы в наличии, то нужно создать переменные с этими данными и отправить их в функцию (run_python_code_in_subproccess(code, timeout), которая будет запускать полученный python-код с определенным тайм-аутом.

-6

Результат работы будет примерно таким (функция run_python_code... еще не закончена):

Пока тут просто "выполнено", а должен быть результат работы строчки кода, заключенного в переменной code. Далее и будем разбираться как же выполнить эту строчку.
Пока тут просто "выполнено", а должен быть результат работы строчки кода, заключенного в переменной code. Далее и будем разбираться как же выполнить эту строчку.

Долго я мурыжил этот код, не мог сделать чтобы правильно исполнялось, вроде бы добился - заработало, но если в print('Hello') больше одного слова, то есть слова разделяются пробелом - опять всё сломалось...

-8
-9

Пробовал различные разделители, из спецсимволов, точек, запятых - ошибки только усугублялись...

Пробовал использовать shlex, тоже ничем не лучше... И как всегда оказалось, что я не точно понял условия задачи. Строку "python -c" не передавать в запросе не нужно, нужно только 'print("Hello World!")'.

Ещё долго пришлось играться с кавычками (одинарными и двойными), как их правильно расположить в тексте запроса, тексте программы. Плюс нужно наложить ограничение на выполнение потенциально опасных действий для компьютера. Это делается с помощью встроенной утилиты prlimit. В общем код получился такой:

-10
-11

В результате всё заработало. print("Hello World!") выполняется как надо.

-12

Но я долго ломал голову над тем, как же мне заставить работать тайм-аут?! Одно дело в редакторе написать программу по всем python-правилам и запустить ее через интерпретатор. Другое дело - одной строчкой. Сначала думал использовать задержку по времени типа time.sleep(время в секундах). Но у меня так и не получилось одновременно импортировать модуль time и запустить sleep(), постоянно ошибки синтаксиса. Пробовал разные методы разделения частей кода и ничего не работало. Пока не вспомнил про бесконечный цикл, который можно записать одной строчкой:

-13

Этим кодом можно проверить срабатывание завершения кода при превышении тайм-аута:

-14

Ну вот, код работает. Осталось дело за малым (или не малым, у меня тесты всегда вызывали ступор) - написать тесты для следующих случаев:

  • Тайм-аут ниже, чем время исполнения.
  • Некорректно введённые данные в форме.
  • Небезопасный ввод в поле с кодом (проверка на shell=True).

И вот, на базе 4го модуля я сделал шаблон такого теста, который в прочем даже запускается и выполняется без ошибок:

-15

Осталось только наполнить каждый тест соответствующим кодом.

Не знаю насколько точно я описал все три теста, в конечном итоге получилось вот так:

-16
-17

При написании последнего теста, пришлось исправить код, чтобы вызывалось исключение ValueError при включении в код строчки shell=True

-18

Вот пожалуй, это будет финальная редакция второй задачи, которая будет отправлена на проверку куратору. Если будут какие-то комментарии от куратора по поводу этого кода - сообщу позже в комментариях.

Не забываем про лайк, подписку, комментарии, вам не трудно а рекомендательной системе дзена приятно. А если ей приятно, то мне и подавно!