-
Notifications
You must be signed in to change notification settings - Fork 3
/
utils.py
99 lines (80 loc) · 3.02 KB
/
utils.py
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# -*- encoding: utf-8 -*-
# Yuuno - IPython + VapourSynth
# Copyright (C) 2017 cid-chan (Sarah <cid+yuuno@cid-chan.moe>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import ast
import time
import hashlib
import linecache
from typing import Callable
from yuuno.yuuno import Yuuno
EXECUTE_CODE_LINENO = 0
RESULT_VAR = '_yuuno_exec_last_'
def _code_name(code, file, number=0):
hash_digest = hashlib.sha1(code.encode("utf-8")).hexdigest()
return f"<{file}-{number}-{hash_digest[:12]}>"
def compile_with_cache(ipython, code, ast, file, symbol):
# Increment the cache name.
global EXECUTE_CODE_LINENO
exec_no = EXECUTE_CODE_LINENO
EXECUTE_CODE_LINENO += 1
# Directly drop the fake python file into the cache.
name = _code_name(code, file, exec_no)
entry = (len(code), time.time(), [line + '\n' for line in code.splitlines()], name)
linecache.cache[name] = entry
if hasattr(linecache, '_ipython_cache'):
linecache._ipython_cache[name] = entry
# Compile the code
return ipython.compile(ast, name, symbol)
def execute_code(expr, file, fail_on_error=True, ns=None):
ipy = Yuuno.instance().environment.ipython
expr = ipy.input_transformer_manager.transform_cell(expr)
expr_ast = ipy.compile.ast_parse(expr)
expr_ast = ipy.transform_ast(expr_ast)
if len(expr_ast.body) == 0:
# There is no code to execute.
# Take the fast path and skip executing.
return None
elif isinstance(expr_ast.body[-1], ast.Expr):
last_expr = expr_ast.body[-1]
assign = ast.Assign( # _yuuno_exec_last_ = <LAST_EXPR>
targets=[ast.Name(
id=RESULT_VAR,
ctx=ast.Store()
)],
value=last_expr.value
)
expr_ast.body[-1] = assign
else:
assign = ast.Assign( # _yuuno_exec_last_ = None
targets=[ast.Name(
id=RESULT_VAR,
ctx=ast.Store(),
)],
value=ast.NameConstant(
value=None
)
)
expr_ast.body.append(assign)
ast.fix_missing_locations(expr_ast)
code = compile_with_cache(ipy, expr, expr_ast, file, "exec")
if ns is None:
ns = ipy.user_ns
try:
exec(code, ipy.user_ns, ns)
result = ipy.user_ns.get(RESULT_VAR, None)
finally:
ns.pop(RESULT_VAR, None)
return result