如何配置 Transcrypt 并修复 hello.js 404 与编译 ImportError?
点击按钮无反应的直接原因是Python源码编译失败,未生成__target__/hello.js,浏览器加载模块时返回404,导致hello.FUN方法不存在。以下是完整流程说明与修复方案:
完整配置与编译流程
1. 环境准备
Transcrypt对CPython小版本兼容性要求较高,建议使用独立虚拟环境部署,避免全局包干扰:
- Python 3.9及以上版本,需搭配Transcrypt 3.9.0及以上版本
- Python 3.8及以下版本,可使用3.9以下的Transcrypt稳定版
2. 项目结构
创建独立的项目文件夹,避免与其他Python项目文件混杂,基础结构如下:
your-project/ ├── hello.py # Python业务源码 └── hello.html # 前端测试页面
编译完成后会自动生成__target__目录,存放编译后的JS、运行时依赖、source map等产物。
3. 执行编译
在项目根目录激活虚拟环境后,执行编译命令即可生成目标JS文件。
4. 页面测试
HTML引入编译后的模块文件后,在项目根目录启动静态HTTP服务访问即可,禁止直接用file://协议打开,否则会触发ES模块的跨域限制。
编译命令参数说明
python -m transcrypt -b -m -n hello.py各部分含义如下:
python -m transcrypt:将Transcrypt作为Python模块启动,避免环境变量配置错误导致的版本、路径异常-b, --build:触发编译构建动作,自动扫描源码的导入依赖,生成对应目标JS产物-m, --map:生成Source Map映射文件,开发调试时可在浏览器开发者工具中直接定位到Python源码,无需阅读编译后的混淆JS-n, --nominify:关闭JS压缩混淆,输出格式化的可读代码,适合开发阶段使用;生产部署可移除该参数以减小产物体积- 末尾的
hello.py为待编译的Python入口源码路径
locale/_bootlocale 导入错误修复
该报错由版本不兼容或全局环境包污染导致,按以下优先级依次修复:
1. 升级至兼容版本
CPython 3.9重构了底层locale模块结构,3.9.0之前的Transcrypt无法适配新结构,会触发整条导入链异常。执行以下命令升级:
pip3 uninstall -y transcrypt pip3 install transcrypt>=3.9.0
若系统源无法安装新版Transcrypt,可将本地Python降级至3.8.x,旧版Transcrypt对Python 3.8兼容性正常。
2. 虚拟环境隔离全局依赖
全局Python环境中安装的无关第三方包(如报错链中出现的wget包)会被Transcrypt的模块解析器误扫描,触发异常。使用独立虚拟环境即可隔离干扰:
# 在项目根目录创建虚拟环境 python3 -m venv venv # 激活虚拟环境 source venv/bin/activate # 虚拟环境内安装Transcrypt pip install transcrypt # 激活状态下重新编译 python -m transcrypt -b -m -n hello.py
3. 排查本地文件命名冲突
检查项目源码目录下是否存在与Python标准库重名的文件,如locale.py、datetime.py、time.py、random.py等。Transcrypt编译时会优先读取本地目录文件,重名会导致导入逻辑异常,将冲突文件重命名即可。
编译成功后,__target__目录会正常生成,其中包含入口文件hello.js与运行时依赖,刷新页面即可解决JS文件的404问题。日志中favicon.ico的404为浏览器自动请求网站图标导致,与功能无关,无需处理。
将函数输出渲染到页面元素
注意:浏览器为单线程运行环境,不存在Python标准库中阻塞式的time.sleep,直接移植原代码会导致整个页面卡死。
需要用浏览器原生异步API重写等待逻辑,同时直接操作DOM将输出写入指定元素。
修改后的hello.py代码
from datetime import datetime import random from asyncio import get_event_loop from js import setTimeout, document # 异步睡眠方法,替代阻塞式time.sleep async def sleep(sec): loop = get_event_loop() future = loop.create_future() setTimeout(lambda: future.set_result(None), sec * 1000) await future async def FUN(): # 获取页面输出节点 fun_box = document.getElementById("FUN") fun_box.innerHTML = "" # 等待时长可按需修改,原逻辑为10-30秒 for i in range(random.randrange(1, 9)): await sleep(random.randrange(1, 3)) content = f"Current time {datetime.now()} , Number {i}<br>" fun_box.innerHTML += content
说明:
- Transcrypt内置
js模块,可直接调用所有浏览器原生JS API - 异步写法基于Python原生
async/await语法,编译后对应JS异步逻辑,不会阻塞页面响应 - 默认
print方法只会将内容输出到浏览器控制台,不会渲染到页面,因此直接操作DOM节点插入内容
修正后的hello.html代码
修复原代码标签嵌套不规范问题,将模块脚本放在页面底部,避免DOM未加载完成导致的获取节点失败:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Transcrypt Demo</title> </head> <body> <div id="FUN">...</div> <button onclick="hello.FUN()">Do code</button> <script type="module"> import * as hello from './__target__/hello.js'; window.hello = hello; </script> </body> </html>
验证步骤
- 重新执行编译命令,确认
__target__/hello.js正常生成 - 在项目根目录执行
python3 -m http.server启动静态服务 - 浏览器访问
http://localhost:8000/hello.html,点击按钮即可看到内容逐行输出到指定div中
内容的提问来源于stack exchange,提问作者h3ll0tity

