Skulpt简介

Skulpt是一个将Python(大约3.7版本)编译成Javascript的系统。它可以直接在浏览器实时编译运行python语法的代码,但是python本身不是被设计用在浏览器中运行的,所以浏览器提供的jsAPI并不能很好的用python语法实现,反过来python能在客户端管理文件、系统资源调度的能力也无法在浏览器中实现。

虽然有同类型的项目(brython、transcrypt),但skulpt的定位不是用python语法调用浏览器api的,而是在浏览器中能够尽量做到python的能力,为的就是在线运行python,而skulpt是目前用的比较多且灵活的方案,现在市面上的在线python编辑器(编程猫海龟编辑器、腾讯扣钉的python实验室)的运行系统都是用的skulpt。

skulpt除了基本的python运行之外还可以开发第三方模块,用javascript编写模块功能,实现python语法的调用,比如你可以用js的echarts来实现pyecharts,然后就可以在线运行pyecharts了。腾讯扣钉的python实验室有个cpgzero库就是按照pygame-zero的用法来封装一个用js实现的游戏库,灵活度非常高。

快速开始

下面是一段展示skulpt运行的html,你可以直接拷贝到文件在浏览器运行就能看拿到效果了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<html>
<head>
<script
src="https://cdn.jsdelivr.net/gh/skulpt/skulpt-dist/skulpt.min.js"
type="text/javascript"
></script>
<script
src="https://cdn.jsdelivr.net/gh/skulpt/skulpt-dist/skulpt-stdlib.js"
type="text/javascript"
></script>
</head>

<body>
<h3>Try This</h3>
<form>
<textarea id="yourcode" cols="80" rows="10">
import turtle
print('hello')
t = turtle.Turtle()
t.color('red')
t.forward(75)
</textarea
><br />
<button type="button" onclick="runit()">Run</button>
</form>
<pre id="output"></pre>
<div id="mycanvas"></div>
<script>
function outf(text) {
var mypre = document.getElementById("output");
mypre.innerHTML = mypre.innerHTML + text;
}
function builtinRead(file) {
console.log("Attempting file: " + Sk.ffi.remapToJs(file));
if (
Sk.builtinFiles === undefined ||
Sk.builtinFiles.files[file] === undefined
) {
throw "File not found: '" + file + "'";
}

return Sk.builtinFiles.files[file];
}
function runit() {
var prog = document.getElementById("yourcode").value;
var mypre = document.getElementById("output");
mypre.innerHTML = "";
Sk.pre = "output";
Sk.configure({
output: outf,
read: builtinRead,
__future__: Sk.python3,
});

(Sk.TurtleGraphics || (Sk.TurtleGraphics = {})).target = "mycanvas";
var myPromise = Sk.misceval.asyncToPromise(function() {
return Sk.importMainWithBody("<stdin>", false, prog, true);
});

myPromise.then(
function(mod) {
console.log("success");
},
function(err) {
console.log(err.toString());
}
);
}
</script>
</body>
</html>

skulpt内置模块

skulpt内置了一些常用模块:

  • turtle
  • math
  • random
  • time
  • sys
  • …….

也有许多模块还未实现,或者只能部分实现:

  • urllib2
  • md5
  • os
  • ……

如果想查看所有的内置模块情况的话可以用上面的示例代码运行后在浏览器控制台打印Sk.builtinFiles.files

image-20210220112752327
image-20210220112752327

.py后缀的都是未实现的,.js后缀的就是已实现的模块了,可以看到已实现内置模块真是少得可怜,很多python自带的模块都还没实现。

第三方模块

skulpt的第三方模块我在github上只看到两三个,skulpt社区也不太活跃,可能是这个项目历史太悠久了,关注的人也少了,所以如果我们自己想要用哪个python库的话就基本得自己实现了。

我觉得skulpt的第三方模块之所以少的原因还有一个,就是文档抽象且不完整,让新手不知从何下手,这个是skulpt的开发文档,感受一下:https://skulpt.org/docs/index.html

首页写的挺好的,详细介绍了skulpt的编译前后对比,介绍了这两门语言的差异性,对于认识skulpt有很大的帮助,但首页只是介绍,真正开发要看的文档就很不走心了,api用途不清楚就算了,还写的不全,几乎对开发是没有啥帮助的,我只能看源码才能摸索一些api用法。

由于skulpt官方的开发文档没有起到作用,而且社区不活跃,网上也没有人发过这块的教程,所以我才会写这篇开发指南,帮助刚接触skulpt想要开发模块的人们提供一些指引,这篇指南会考虑到设计一个像样的模块应该掌握的那些skulpt的api,只需要掌握用到的就够了。

最简单的模块

  1. 我们先新建一个mod.js,填入以下代码:
1
2
3
4
5
6
7
var $builtinmodule = function (name) {
var mod = {__name__: new Sk.builtin.str("mod")}
mod.add = new Sk.builtin.func(function(a, b) {
return Sk.ffi.remapToJs(a) + Sk.ffi.remapToJs(b);
});
return mod;
}
  1. 再在同目录下的html填入以下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<html>
<head>
<script src="https://cdn.jsdelivr.net/gh/skulpt/skulpt-dist/skulpt.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/gh/skulpt/skulpt-dist/skulpt-stdlib.js" type="text/javascript"></script>
</head>

<body>

<h3>Try This</h3>
<form>
<textarea id="yourcode" cols="80" rows="10">
import mod
print(mod.add(1,2))
</textarea
><br />
<button type="button" onclick="runit()">Run</button>
</form>
<pre id="output"></pre>
<div id="mycanvas"></div>
<script>
// 第三方模块列表
var externalLibs = {
// 确保模块路径能访问到,这里我用的是相对路径./mod.js
"./mod/__init__.js": "./mod.js",
};
function outf(text) {
var mypre = document.getElementById("output");
mypre.innerHTML = mypre.innerHTML + text;
}
function builtinRead(file) {
console.log("Attempting file: " + Sk.ffi.remapToJs(file));

if (externalLibs[file] !== undefined) {
return Sk.misceval.promiseToSuspension(
fetch(externalLibs[file]).then(
function (resp){ return resp.text(); }
));
}

if (Sk.builtinFiles === undefined || Sk.builtinFiles.files[file] === undefined) {
throw "File not found: '" + file + "'";
}

return Sk.builtinFiles.files[file];
}
function runit() {
var prog = document.getElementById("yourcode").value;
var mypre = document.getElementById("output");
mypre.innerHTML = "";
Sk.pre = "output";
Sk.configure({
output: outf,
read: builtinRead,
__future__: Sk.python3,
});

(Sk.TurtleGraphics || (Sk.TurtleGraphics = {})).target = "mycanvas";
var myPromise = Sk.misceval.asyncToPromise(function() {
return Sk.importMainWithBody("<stdin>", false, prog, true);
});

myPromise.then(
function(mod) {
console.log("success");
},
function(err) {
console.log(err.toString());
}
);
}
</script>
</body>
</html>

运行后得到了这段代码的运行结果:

image-20210220121711953
image-20210220121711953

如果不是以上结果请检查mod.js是否能访问到以及是否按上面的代码配置准确。

还有一种加载模块的方式就是在skulpt的源代码里编写内置模块,然后编译出新的skulpt-stdlib.js文件就可以作为内置模块来使用了,但不建议这么做。

通过上面的模块代码以及在浏览器的python代码不难看出是有对应关系的,通过调用skulpt特定的api能够达到在python代码调用js模块的能力。

小结

本篇为大家介绍了一下skulpt并快速上手,下一篇就是讲解要完成一个像样的模块应该用到的哪些skulpt的api,这些skulpt的api其实都没啥规范不好记,所以不要记,用到的时候就过来复制就好了。

另外再声明一下,本人只是摸索了一些skulpt的api用法,对skulpt了解还不够深入,文中写的不对的请提出指正,如果有更好的学习文章也欢迎分享。

可能是全网第一篇skulpt中文教程🙃