支持 input 函数的在线 python 运行环境 - 基于队列
支持 input 函数的在线 python 运行环境 - 基于队列
- 思路
- 两次用户输入
- 三次用户输入
- 实现
- 前端使用 vue + element ui
- Windows 环境的执行器
- 子进程需要执行的代码
- 代码仓库
- 参考
本文提供了一种方式来实现支持 input
函数,即支持用户输的在线 python 运行环境。效果如下图所示:
思路
-
前端使用一个数组
input_queue
记录用户从点击运行按钮到现在的所有输入 -
点击运行按钮时将
code
、input_queue
传给后端 -
后端将参数传给执行 python 代码的子进程
-
子进程重写
input()
函数,假设新的实现为input_wrapper
,代码如下,到用户代码运行到input()
函数时,会执行重写的input_wrapper()
, 在input_wrapper
中获取到input_queue
,然后使用input_queue.pop(0)
弹出用户输入最早的信息,如果input_queue
为空,则说明需要用户输入,通过抛出InputRequestException
异常的方式通知后端def input_wrapper(prompt=''):if input_queue:input_str = input_queue.pop(0)sys.stdout.write(str(prompt) + input_str + "\n")return input_strraise InputRequestException(str(prompt))
-
后端通过处理子进程的标准输出、标准错误,知晓需要用户输入,然后向前端返回以下 json,
event.type
为 input_request 代表需要用户输入,prompt
是提示信息{"is_timeout": false,"done": false,"event": {"type": "input_request","prompt": "请输入姓名:"} }
-
前端弹出弹框提示用户输入,用户输入并点击继续执行按钮时,会将本次的输入追加到
input_queue
的末尾,然后再次调用运行接口,这样循环往复直到程序结束
在执行以下代码时,可能需要两次用户输入,也可能需要三次。
name = input("请输入姓名:")
print("姓名:", name)if name == "tom":age = input("请输入年龄:")print("年龄:", age)gender = input("请输入性别:")
print("性别:", gender)
两次用户输入
点击运行按钮
请求参数中的 input_queue 为 []{"code": "name = input(\"请输入姓名:\")\nprint(\"姓名:\", name)\n\nif name == \"tom\":\n age = input(\"请输入年龄:\")\n print(\"年龄:\", age)\n\ngender = input(\"请输入性别:\")\nprint(\"性别:\", gender)","input_queue": []
}返回值
{"is_timeout": false,"done": false,"event": {"type": "input_request","prompt": "请输入姓名:"}
}
输入 jack
请求参数中的 input_queue 为 ["jack"]{"code": "name = input(\"请输入姓名:\")\nprint(\"姓名:\", name)\n\nif name == \"tom\":\n age = input(\"请输入年龄:\")\n print(\"年龄:\", age)\n\ngender = input(\"请输入性别:\")\nprint(\"性别:\", gender)","input_queue": ["jack"]
}返回值
{"is_timeout": false,"done": false,"event": {"type": "input_request","prompt": "请输入性别:"}
}
输入 男
请求参数中的 input_queue 为 ["jack", "男"]
{"code": "name = input(\"请输入姓名:\")\nprint(\"姓名:\", name)\n\nif name == \"tom\":\n age = input(\"请输入年龄:\")\n print(\"年龄:\", age)\n\ngender = input(\"请输入性别:\")\nprint(\"性别:\", gender)","input_queue": ["jack","男"]
}返回值
{"is_timeout": false,"done": true,"output": "请输入姓名:jack\r\n姓名: jack\r\n请输入性别:男\r\n性别: 男\r\n"
}
三次用户输入
点击运行按钮
请求参数中的 input_queue 为 []
{"code": "name = input(\"请输入姓名:\")\nprint(\"姓名:\", name)\n\nif name == \"tom\":\n age = input(\"请输入年龄:\")\n print(\"年龄:\", age)\n\ngender = input(\"请输入性别:\")\nprint(\"性别:\", gender)","input_queue": []
}返回值
{"is_timeout": false,"done": false,"event": {"type": "input_request","prompt": "请输入姓名:"}
}
输入 tom
请求参数中的 input_queue 为 ["tom"]
{"code": "name = input(\"请输入姓名:\")\nprint(\"姓名:\", name)\n\nif name == \"tom\":\n age = input(\"请输入年龄:\")\n print(\"年龄:\", age)\n\ngender = input(\"请输入性别:\")\nprint(\"性别:\", gender)","input_queue": ["tom"]
}返回值
{"is_timeout": false,"done": false,"event": {"type": "input_request","prompt": "请输入年龄:"}
}
输入 18
请求参数中的 input_queue 为 ["tom", "18"]
{"code": "name = input(\"请输入姓名:\")\nprint(\"姓名:\", name)\n\nif name == \"tom\":\n age = input(\"请输入年龄:\")\n print(\"年龄:\", age)\n\ngender = input(\"请输入性别:\")\nprint(\"性别:\", gender)","input_queue": ["tom","18"]
}返回值
{"is_timeout": false,"done": false,"event": {"type": "input_request","prompt": "请输入性别:"}
}
输入 男
请求参数中的 input_queue 为 ["tom", "18", "男"]
{"code": "name = input(\"请输入姓名:\")\nprint(\"姓名:\", name)\n\nif name == \"tom\":\n age = input(\"请输入年龄:\")\n print(\"年龄:\", age)\n\ngender = input(\"请输入性别:\")\nprint(\"性别:\", gender)","input_queue": ["tom","18","男"]
}返回值
{"is_timeout": false,"done": true,"output": "请输入姓名:tom\r\n姓名: tom\r\n请输入年龄:18\r\n年龄: 18\r\n请输入性别:男\r\n性别: 男\r\n"
}
实现
前端使用 vue + element ui
<!DOCTYPE html>
<html lang="" style="height: 100%;">
<head><meta charset="UTF-8"><link rel="stylesheet" href="./element-ui/index.css"><title>在线 python 执行</title>
</head>
<body style="height: 100%;margin: 0;">
<div id="app" style="height: 98%;width: 98%;padding: 5px"><el-inputtype="textarea":autosize="{ minRows: 10, maxRows: 100}"placeholder="请输入代码"v-model="code"></el-input><el-button type="primary" style="margin-top: 5px;margin-bottom: 5px" @click="exec()">运行</el-button><el-inputtype="textarea":autosize="{ minRows: 10, maxRows: 100}"placeholder="运行结果"v-model="result"></el-input>
</div>
</body>
<script src="./axios.min.js"></script>
<script src="./vue.js"></script>
<script src="./element-ui/index.js"></script>
<script>new Vue({el: '#app',mounted() {},methods: {exec() {const params = {code: this.code,input_queue: this.input_queue}axios.post('http://localhost:8080/exec', params).then(res => {console.log("exec", res)if (res.data.done) {// 执行结束了,需要清空队列this.clearQueue()if (res.data.is_timeout) {// 执行超时this.$message("执行超时");} else {// 正常执行结束this.result = res.data.output}} else {// 执行中,需要用户输入const event = res.data.eventif (event.type === 'input_request') {// 弹框提示用户输入this.$prompt(event.prompt, '输入', {confirmButtonText: '继续执行',cancelButtonText: '终止执行',showClose: false,closeOnClickModal: false,closeOnPressEscape: false}).then(({value}) => {// 继续执行,将本次输入的信息追加进队列,然后再次执行this.input_queue.push(value)this.exec()}).catch((action) => {// 终止执行,需要清空队列console.log("action ", action)this.clearQueue()this.$message("终止执行")});}}})},clearQueue() {this.input_queue = []}},data() {return {code:
`name = input("请输入姓名:")
print("姓名:", name)if name == "tom":age = input("请输入年龄:")print("年龄:", age)gender = input("请输入性别:")
print("性别:", gender)
`,input_queue: [],result: null,}}})</script>
</html>
Windows 环境的执行器
import json
import os
import subprocess
import threading
from threading import Timerimport psutilclass AbstractExecutor:def __init__(self, param):# param 包括 code、input_queueself.param = param# 用于保护 is_timeout 的锁self.lock = threading.Lock()# 是否执行超时了self.is_timeout = Nonedef timeout_callback(self, p: subprocess.Popen):"""执行超时时的回调,会终止执行 python 代码的进程组:param p: 执行 python 代码的进程"""with self.lock:if self.is_timeout is None:self.is_timeout = Trueif self.is_timeout:try:# 终止执行 python 代码的进程组self.terminating_process_group(p)except Exception as e:print("超时回调异常, error: %s", e)def terminating_process_group(self, p: subprocess.Popen):"""终止进程 p 及其子进程:param p: 要终止的进程"""raise NotImplementedError()def create_popen(self) -> subprocess.Popen:"""创建 subprocess.Popen,必须将 stderr 重定向到 stdout"""raise NotImplementedError()def output(self, stdout):if stdout is not None:return stdout.decode("utf-8")else:return ""def execute(self):p = self.create_popen()timer = Timer(3, self.timeout_callback, [p])timer.start()try:# 从标准输入传入 json 参数:code、input_queuep.stdin.write(json.dumps(self.param).encode(encoding="utf-8"))stdout, stderr = p.communicate()with self.lock:if self.is_timeout is None:self.is_timeout = Falsefinally:timer.cancel()return self.is_timeout, self.output(stdout)class WindowsExecutor(AbstractExecutor):__output_prefix = "Active code page: 65001\r\n"def create_popen(self) -> subprocess.Popen:filename = r"D:\project\python\online-python-code-executor\queue-base\exec_py.py"cmd = 'chcp 65001 & set PYTHONIOENCODING=utf-8 & python ' + filename# 将 stderr 重定向到了 stdoutreturn subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,shell=True)def terminating_process_group(self, p: subprocess.Popen):proc_pid = p.pidparent_proc = psutil.Process(proc_pid)for child_proc in parent_proc.children(recursive=True):print(child_proc.pid)child_proc.kill()parent_proc.kill()print(parent_proc.pid)def output(self, stdout):output = super().output(stdout)if output.startswith(self.__output_prefix):return output.removeprefix(self.__output_prefix)else:return outputif os.name == "nt":executor_cls = WindowsExecutordef execute(param):# 执行用户代码is_timeout, stdout = executor_cls(param).execute()if is_timeout:# 执行超时了return {"is_timeout": is_timeout,"done": True,"output": stdout,}else:arr = stdout.split("InputRequestException")if len(arr) > 1:# 需要用户输入return {"is_timeout": is_timeout,"done": False,"event": {"type": "input_request","prompt": arr[-1]}}else:# 正常执行结束return {"is_timeout": is_timeout,"done": True,"output": stdout,}
子进程需要执行的代码
import json
import sysinput_queue = []class InputRequestException(Exception):"""抛出此异常表示需要用户输入"""passdef execute(param):# 重写 input 函数__builtins__.input = input_wrapper# input_queueglobal input_queueinput_queue = param["input_queue"]try:# 执行代码exec(param["code"])except InputRequestException as e:# 如果需要用户输入,则直接退出sys.stdout.write("\n" + "InputRequestException" + e.args[0])exit()def input_wrapper(prompt=''):# 从 input_queue 中弹出if input_queue:input_str = input_queue.pop(0)sys.stdout.write(str(prompt) + input_str + "\n")return input_str# 需要用户输入raise InputRequestException(str(prompt))if __name__ == '__main__':# 从标准输入读取 json 参数:code、input_queuearg = sys.stdin.read()# 执行execute(json.loads(arg))
代码仓库
- online-python-code-executor/queue-base (github.com)
参考
- https://pythontutor.com
- https://github.com/seamile/PyTutor
相关文章:

支持 input 函数的在线 python 运行环境 - 基于队列
支持 input 函数的在线 python 运行环境 - 基于队列 思路两次用户输入三次用户输入 实现前端使用 vue element uiWindows 环境的执行器子进程需要执行的代码 代码仓库参考 本文提供了一种方式来实现支持 input 函数,即支持用户输的在线 python 运行环境。效果如下图…...

欧拉Euler release 21.10 (LTS-SP2)升级openssh至9版本记录
背景:安扫漏洞,需要对openssh经行升级 1.先查看升级前的openssh版本 2.避免升级失败断开远程登录,先开启telnt服务用于远程连接(这步可查看其他博客) 3.从欧拉官网下载rpm包,https://www.openeuler.org/zh…...

php 数组中的元素进行排列组合
需求背景:计算出数组[A,B,C,D]各种排列组合,希望得到的是数据如下图 直接上代码: private function finish_combination($array, &$groupResult [], $splite ,){$result [];$finish_result [];$this->diffArrayItems($array, $…...

Python从入门到网络爬虫(OS模块详解)
前言 本章介绍python自带模块os,os为操作系统 operating system 的简写,意为python与电脑的交互。os 模块提供了非常丰富的方法用来处理文件和目录。通过使用 os 模块,一方面可以方便地与操作系统进行交互,另一方面页可以极大增强…...

人机交互不是人机融合智能
一、人机交互和人机融合智能是两个不同的概念 人机交互是指人类与计算机之间的信息交流和操作方式,包括输入和输出界面、交互技术、用户体验等方面。人机交互的目标是提供用户友好的界面和自然的交互方式,使人类能够与计算机更加高效地进行沟通和协作。 …...

RabbitMQ解决消息丢失以及重复消费问题
文章目录 1、概念2、基于ACK/NACK机制2.1 基于Spring AMQP框架整合ACK/NACK机制2.2 测试消费失败1.02.3 测试结果1.02.4 测试MQ宕机2.5 测试结果2.0 3、RabbitMQ 如何实现幂等性设计3.1 幂等服务设计思路3.1.1 通过雪花算法生成分布式唯一ID3.1.2 通过枚举类,设计Me…...
docker 安装redis集群
一、准备6台机器 二、6台机器分别拉取镜像: docker pull redis三、6台机器分别建立挂载文件夹 mkdir -p /home/redis/data四、6台机器分别执行容器操作 docker run --restartalways -d --name redis-node-1 --net host --privilegedtrue -v /home/redis/data:/da…...

锂电池制造设备中分布式IO模块优势
在“碳达峰、碳中和”目标推动下,新能源汽车当下发展势头正盛,而纯电动车的核心部件则是:锂电池。动力型锂电池作为新能源汽车核心零部件,其发展与新能源汽车行业息息相关,迎来广阔的市场空间。 为何采用I/O模块&#…...
Android Room数据库升级Migration解决方案
一、介绍 Android Room 是 Android 官方提供的一个轻量级数据库框架,用于在 Android 应用程序中管理数据持久性。它简化了数据库访问,提供了更安全、更快速的数据存储方式,并使得数据操作更加便捷。 二、Room的特点(八股文可以参考) 以下是…...
离线安装docker和docker-compose
1.下载 docker Index of linux/static/stable/x86_64/ docker-compose Overview of installing Docker Compose | Docker Docs 2.docker /etc/systemd/system/docker.service [Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.…...
奇怪的事情记录:外置网卡和外置显示器不兼容
身为程序员,不应该对世界上的稀奇古怪的事情感到惊讶(毕竟,大部分都是程序员自己搞出来的)。 外置网卡和外置显示器不兼容 mbp2019intel版,win10,外接有线网卡,平时用得很好,接上外…...

【大数据进阶第三阶段之Hive学习笔记】Hive基础入门
目录 1、什么是Hive 2、Hive的优缺点 2.1、 优点 2.2、 缺点 2.2.1、Hive的HQL表达能力有限 2.2.2、Hive的效率比较低 3、Hive架构原理 3.1、用户接口:Client 3.2、元数据:Metastore 3.3、Hadoop 3.4、驱动器:Driver Hive运行机制…...

第三代量子计算机交付,中国芯片开辟新道路,光刻机难挡中国芯
日前安徽本源量子宣布第三代超导量子计算系统正式上线,这是中国最先进的量子计算机,计算量子比特已达到72个,在全球已居于较为领先的水平,这对于中国芯片在原来的硅基芯片受到光刻机阻碍无疑是巨大的鼓舞。 据悉本源量子的第一代、…...

react native中使用tailwind并配置自动补全
使用的第三方库是tailwind-react-native-classnames,同类的也有tailwind-rn,但是我更喜欢前者官方demo: import { View, Text } from react-native; import tw from twrnc;const MyComponent () > (<View style{twp-4 android:pt-2 b…...

数据分析——火车信息
任务目标 任务 1、整理火车发车信息数据,结果的表格形式为: 2、并输出最终的发车信息表 难点 1、多文件 一个文件夹,多个月的发车信息,一个excel,放一天的发车情况 2、数据表的格式特殊 如何分析表是一个难点 数…...

Bert-vits2最终版Bert-vits2-2.3云端训练和推理(Colab免费GPU算力平台)
对于深度学习初学者来说,JupyterNoteBook的脚本运行形式显然更加友好,依托Python语言的跨平台特性,JupyterNoteBook既可以在本地线下环境运行,也可以在线上服务器上运行。GoogleColab作为免费GPU算力平台的执牛耳者,更…...

Asp .Net Web应用程序(.Net Framework4.8)网站发布到IIS
开启IIS 如果已开启跳过这步 打开控制面板-程序 打开IIS 发布Web程序(.Net Framework 4.8 web网页) 进入IIS管理器新建一个应用池 新建一个网站 网站创建完毕 为文件夹添加访问权限 如果不添加访问权限,运行时将会得到如下错误 设置权限 勾…...

vue element plus Typography 排版
我们对字体进行统一规范,力求在各个操作系统下都有最佳展示效果。 字体# 字号# LevelFont SizeDemoSupplementary text12px Extra SmallBuild with ElementBody (small)13px SmallBuild with ElementBody14px BaseBuild with ElementSmall Title16px MediumBuild w…...

理论U3 决策树
文章目录 一、决策树算法1、基本思想2、构成1)节点3)有向边/分支 3、分类步骤1)第1步-决策树生成/学习、训练2)第2步-分类/测试 4、算法关键 二、信息论基础1、概念2、信息量3、信息熵: 二、ID3 (Iterative Dichotomis…...
Redis 常用操作
一、Redis常用的5种数据类型 字符串(String):最基本的数据类型,可以存储字符串、整数或浮点数。哈希(Hash):键值对的集合,可以在一个哈希数据结构中存储多个字段和值。列表…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...

定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...
uniapp 实现腾讯云IM群文件上传下载功能
UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中,群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS,在uniapp中实现: 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

aurora与pcie的数据高速传输
设备:zynq7100; 开发环境:window; vivado版本:2021.1; 引言 之前在前面两章已经介绍了aurora读写DDR,xdma读写ddr实验。这次我们做一个大工程,pc通过pcie传输给fpga,fpga再通过aur…...