Skip to content

Commit 1d68e9b

Browse files
committed
support python powerrun
1 parent 0e47092 commit 1d68e9b

File tree

2 files changed

+122
-19
lines changed

2 files changed

+122
-19
lines changed

cpkernel/kernels/python/codepod.py

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1264,28 +1264,66 @@ def code2parts(code):
12641264
else:
12651265
return [unparse(ast1.body[:-1]), expr]
12661266

1267+
from collections import defaultdict
12671268

1268-
def _make_codepod():
1269-
d = {}
1269+
d = {}
12701270

1271-
def getmod(ns):
1272-
if ns not in d:
1273-
d[ns] = types.ModuleType(ns)
1274-
# for testing purpose
1275-
d[ns].__dict__["CODEPOD_GETMOD"] = getmod
1276-
return d[ns]
1271+
"""
1272+
here's the new plan for python's eval
12771273
1278-
def eval_func(code, ns):
1279-
# the codepod(code) is the program sent to backend
1280-
# codepod is defined on the kernel
1281-
mod = getmod(ns)
1282-
[stmt, expr] = code2parts(code)
1283-
if stmt:
1284-
exec(stmt, mod.__dict__)
1285-
if expr:
1286-
return eval(expr, mod.__dict__)
1274+
Python's module is closely bound to the file system, so it is not possible to use that.
12871275
1288-
return eval_func, getmod
1276+
I have to use my own globals and locals during exec/eval.
12891277
1278+
Then, I'll just record the calling relationships for the modules. When evaluating, I'll:
12901279
1291-
CODEPOD_EVAL, CODEPOD_GETMOD = _make_codepod()
1280+
1. eval_func(code, nses)
1281+
- nses is a list of namespaces in order, the last one is this ns
1282+
- that should be it. The question is where to compute nses. I would
1283+
1. compute it when powerRunTree
1284+
2. let the kernel knows it by using CODEPOD_ADD_IMPORT. The kernel will record this.
1285+
"""
1286+
1287+
# from ns to nses that should imported
1288+
import_d = defaultdict(set)
1289+
# from ns to exported names
1290+
export_d = defaultdict(set)
1291+
# from ns to subdeck nses
1292+
export_sub_d = defaultdict(set)
1293+
1294+
def CODEPOD_GETMOD(ns):
1295+
if ns not in d:
1296+
d[ns] = types.ModuleType(ns)
1297+
return d[ns]
1298+
1299+
def merge_dicts(dicts):
1300+
"""
1301+
Given any number of dictionaries, shallow copy and merge into a new dict,
1302+
precedence goes to key-value pairs in latter dictionaries.
1303+
"""
1304+
result = {}
1305+
for dictionary in dicts:
1306+
result.update(dictionary)
1307+
return result
1308+
1309+
def CODEPOD_ADD_IMPORT(FROM, TO):
1310+
import_d[TO].add(FROM)
1311+
def CODEPOD_REMOVE_IMPORT(FROM, TO):
1312+
if FROM in import_d[TO]:
1313+
import_d[TO].remove(FROM)
1314+
def CODEPOD_SET_EXPORT(ns, exports):
1315+
export_d[ns] = exports
1316+
def CODEPOD_SET_EXPORT_SUB(ns, nses):
1317+
export_sub_d[ns] = nses
1318+
1319+
def CODEPOD_EVAL(code, ns):
1320+
# the codepod(code) is the program sent to backend
1321+
# codepod is defined on the kernel
1322+
mod = CODEPOD_GETMOD(ns)
1323+
[stmt, expr] = code2parts(code)
1324+
_dict = merge_dicts([{k:v for k,v in CODEPOD_GETMOD(x).__dict__.items() if k in export_d[x]} for x in import_d[ns]]
1325+
+ [{k:v for k,v in CODEPOD_GETMOD(x).__dict__.items() if k in export_d[x]} for ns1 in export_sub_d[ns] for x in import_d[ns1]])
1326+
if stmt:
1327+
exec(stmt, _dict, mod.__dict__)
1328+
if expr:
1329+
return eval(expr, _dict, mod.__dict__)

ui/src/lib/ws/middleware.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,71 @@ ${nses
242242
},
243243
})
244244
);
245+
} else if (pod.lang === "python") {
246+
// python powerrun
247+
// 1. create the module
248+
let names = pod.children
249+
.filter(({ id }) => pods[id].type !== "DECK")
250+
.filter(({ id }) => pods[id].exports)
251+
.map(({ id }) =>
252+
Object.entries(pods[id].exports)
253+
.filter(([k, v]) => v)
254+
.map(([k, v]) => k)
255+
);
256+
names = [].concat(...names);
257+
let nses = getUtilNs({ id, pods });
258+
const child_deck_nses = pods[id].children
259+
.filter(({ id }) => pods[id].type === "DECK" && !pods[id].thundar)
260+
.map(({ id, type }) => pods[id].ns);
261+
nses = nses.concat(child_deck_nses);
262+
// if it is a test desk, get parent
263+
if (pod.thundar) {
264+
nses.push(pods[pod.parent].ns);
265+
}
266+
267+
// exported subdecks
268+
// TODO handle this in python,
269+
// In racket: all-from-out
270+
// In julia: @reexport
271+
let exported_decks = pods[id].children
272+
.filter(
273+
({ id }) =>
274+
pods[id].type === "DECK" &&
275+
pods[id].exports &&
276+
pods[id].exports["self"]
277+
)
278+
.map(({ id, type }) => pods[id].ns);
279+
// 2. import the namespaces
280+
let code = `${nses
281+
.map(
282+
(ns) => `
283+
CODEPOD_ADD_IMPORT("${ns}", "${pod.ns}")`
284+
)
285+
.join("\n")}
286+
287+
CODEPOD_SET_EXPORT("${pod.ns}", {${names.map((name) => `"${name}"`).join(",")}})
288+
289+
CODEPOD_SET_EXPORT_SUB("${pod.ns}", {${exported_decks
290+
.map((name) => `"${name}"`)
291+
.join(",")}})
292+
`;
293+
// console.log("==== PYTHON CODE", code);
294+
storeAPI.dispatch(repoSlice.actions.clearResults(pod.id));
295+
storeAPI.dispatch(repoSlice.actions.setRunning(pod.id));
296+
socket.send(
297+
JSON.stringify({
298+
type: "runCode",
299+
payload: {
300+
lang: pod.lang,
301+
code,
302+
// namespace: pod.ns,
303+
raw: true,
304+
// FIXME this is deck's ID
305+
podId: pod.id,
306+
sessionId: storeAPI.getState().repo.sessionId,
307+
},
308+
})
309+
);
245310
}
246311
if (doEval) {
247312
// run all children pods

0 commit comments

Comments
 (0)