Что нужно сделать: написать endpoint, который принимает на вход код на Python (строка) и тайм-аут в секундах (положительное число не больше 30). Пользователю возвращается результат работы программы, а если время, отведённое на выполнение кода, истекло, то процесс завершается, после чего отправляется сообщение о том, что исполнение кода не уложилось в данное время.
Для выполнения задания рекомендуется использовать метод Popen.communicate. Не забудьте сделать communicate при превышении тайм-аута — процесс мог что-то вывести за время работы.
Мы не будем создавать каких либо форм в которых будут указываться код и тайм-аут и затем отправка этих данных. Обычно эти данные посылаются при помощи сторонних программ, таких как Postman. Я же буду использовать консольную программу http (установка в арчподобных линухах: sudo pacman -S httpie).
Принцип работы этой программы таков:
У Flask (как и у Django) есть проверка подлинности формы, для безопасности, чтобы злоумышленники не могли подменить ввод данных своими данными и отправить серверу не то что требуется. Поэтому нужно отключить безопасность следующей строкой, иначе придется создавать полноценный веб портал с формой отправки и проверкой подлинности формы.
Итак, вызывается функция run() вместе с декоратором @app.route('/run_code', method=['POST']) в котором нам нужно принять объект формы, в которой формируются данные code и timeout. Но для этого нужно предварительно создать новый класс который и будет принимать наши данные.
Получаем экземпляр класса CodeForm:
form = CodeForm()
Делаем проверку того что все данные в форме отправлены, иначе выдаётся ошибка:
Если все данные формы в наличии, то нужно создать переменные с этими данными и отправить их в функцию (run_python_code_in_subproccess(code, timeout), которая будет запускать полученный python-код с определенным тайм-аутом.
Результат работы будет примерно таким (функция run_python_code... еще не закончена):
Долго я мурыжил этот код, не мог сделать чтобы правильно исполнялось, вроде бы добился - заработало, но если в print('Hello') больше одного слова, то есть слова разделяются пробелом - опять всё сломалось...
Пробовал различные разделители, из спецсимволов, точек, запятых - ошибки только усугублялись...
Пробовал использовать shlex, тоже ничем не лучше... И как всегда оказалось, что я не точно понял условия задачи. Строку "python -c" не передавать в запросе не нужно, нужно только 'print("Hello World!")'.
Ещё долго пришлось играться с кавычками (одинарными и двойными), как их правильно расположить в тексте запроса, тексте программы. Плюс нужно наложить ограничение на выполнение потенциально опасных действий для компьютера. Это делается с помощью встроенной утилиты prlimit. В общем код получился такой:
В результате всё заработало. print("Hello World!") выполняется как надо.
Но я долго ломал голову над тем, как же мне заставить работать тайм-аут?! Одно дело в редакторе написать программу по всем python-правилам и запустить ее через интерпретатор. Другое дело - одной строчкой. Сначала думал использовать задержку по времени типа time.sleep(время в секундах). Но у меня так и не получилось одновременно импортировать модуль time и запустить sleep(), постоянно ошибки синтаксиса. Пробовал разные методы разделения частей кода и ничего не работало. Пока не вспомнил про бесконечный цикл, который можно записать одной строчкой:
Этим кодом можно проверить срабатывание завершения кода при превышении тайм-аута:
Ну вот, код работает. Осталось дело за малым (или не малым, у меня тесты всегда вызывали ступор) - написать тесты для следующих случаев:
- Тайм-аут ниже, чем время исполнения.
- Некорректно введённые данные в форме.
- Небезопасный ввод в поле с кодом (проверка на shell=True).
И вот, на базе 4го модуля я сделал шаблон такого теста, который в прочем даже запускается и выполняется без ошибок:
Осталось только наполнить каждый тест соответствующим кодом.
Не знаю насколько точно я описал все три теста, в конечном итоге получилось вот так:
При написании последнего теста, пришлось исправить код, чтобы вызывалось исключение ValueError при включении в код строчки shell=True
Вот пожалуй, это будет финальная редакция второй задачи, которая будет отправлена на проверку куратору. Если будут какие-то комментарии от куратора по поводу этого кода - сообщу позже в комментариях.
Не забываем про лайк, подписку, комментарии, вам не трудно а рекомендательной системе дзена приятно. А если ей приятно, то мне и подавно!