我有以下格式的许多Python文件(submission1.py,submission2.py,...,submissionN.py),
#submission1.py
def fun():
print('some fancy function')
fun()
我想写一个测试人员来测试这些提交。 (它们实际上是我正在评分的作业。)我有fun()
的测试人员,可以测试功能本身。但是,我的问题是,当我导入submission.py
时,它会运行fun()
,因为它在文件末尾调用它。
我知道,使用if __name__ == "__main__":
是处理此问题的正确方法,但是,由于我们没有教授它,我们提交的内容没有。
所以,我的问题是,有没有办法只从fun()
文件中导入submission.py
而不运行其余的python文件?
答案 0 :(得分:5)
对于只包含函数的简单脚本,以下内容将起作用:
submission1.py :
def fun(x):
print(x)
fun("foo")
def fun2(x):
print(x)
fun2("bar")
print("debug print")
您可以删除FunctionDef节点然后重新编译的所有栏:
import ast
import types
with open("submission1.py") as f:
p = ast.parse(f.read())
for node in p.body[:]:
if not isinstance(node, ast.FunctionDef):
p.body.remove(node)
module = types.ModuleType("mod")
code = compile(p, "mod.py", 'exec')
sys.modules["mod"] = module
exec(code, module.__dict__)
import mod
mod.fun("calling fun")
mod.fun2("calling fun2")
输出:
calling fun
calling fun2
模块主体包含两个Expr和一个Print节点,我们在循环中删除它只保留FunctionDef。
[<_ast.FunctionDef object at 0x7fa33357f610>, <_ast.Expr object at 0x7fa330298a90>,
<_ast.FunctionDef object at 0x7fa330298b90>, <_ast.Expr object at 0x7fa330298cd0>,
<_ast.Print object at 0x7fa330298dd0>]
所以循环体后只包含函数:
[<_ast.FunctionDef object at 0x7f49a786a610>, <_ast.FunctionDef object at 0x7f49a4583b90>]
这也将捕获使用print调用函数的位置,如果学生从IDE中调用函数,其中函数具有返回语句很可能,也是为了保持任何导入,有任何你可以保持ast.Import的和ast.ImportFrom的:
submission.py:
from math import *
import datetime
def fun(x):
print(x)
fun("foo")
def fun2(x):
return x
def get_date():
print(pi)
return datetime.datetime.now()
fun2("bar")
print("debug print")
print(fun2("hello world"))
print(get_date())
编译然后导入:
for node in p.body[:]:
if not isinstance(node, (ast.FunctionDef,ast.Import, ast.ImportFrom)):
p.body.remove(node)
.....
import mod
mod.fun("calling fun")
print(mod.fun2("calling fun2"))
print(mod.get_date())
输出:
calling fun
calling fun2
3.14159265359
2015-05-09 12:29:02.472329
最后,如果您声明需要使用某些变量,则可以使用ast.Assign保留它们:
submission.py:
from math import *
import datetime
AREA = 25
WIDTH = 35
def fun(x):
print(x)
fun("foo")
def fun2(x):
return x
def get_date():
print(pi)
return datetime.datetime.now()
fun2("bar")
print("debug print")
print(fun2("hello world"))
print(get_date()
添加ast.Assign:
for node in p.body[:]:
if not isinstance(node, (ast.FunctionDef,
ast.Import, ast.ImportFrom,ast.Assign)):
p.body.remove(node)
....
输出:
calling fun
calling fun2
3.14159265359
2015-05-09 12:34:18.015799
25
35
所以这一切都取决于你的模块的结构以及它们应该包含哪些内容。如果只有字面上的函数,那么第一个例子就会做你想要的。如果还有其他部分需要保留,只需将它们添加到isinstance检查中即可。
所有抽象语法定义的列表都在Parser/Python.asdl下的cpython源代码中。
答案 1 :(得分:2)
您可以使用sys.settrace()来捕获函数定义。
每当定义fun()
时,您将其保存在某处,并将存根放入要导入的模块中,以免它被执行。
假设fun()
只被定义一次,这段代码应该可以解决问题:
import sys
fun = None
def stub(*args, **kwargs):
pass
def wait_for_fun(frame, event, arg):
global fun
if frame.f_code.co_filename == '/path/to/module.py':
if 'fun' in frame.f_globals:
# The function has just been defined. Save it.
fun = frame.f_globals['fun']
# And replace it with our stub.
frame.f_globals['fun'] = stub
# Stop tracing the module execution.
return None
return wait_for_fun
sys.settrace(wait_for_fun)
import my_module
# Now fun() is available and we can test it.
fun(1, 2, 3)
# We can also put it again inside the module.
# This is important if other functions in the module need it.
my_module.fun = fun
这段代码可以通过多种方式得到改进,但它确实起到了作用。
答案 2 :(得分:-2)
也许只是想从submitt.py导入fun()函数,请尝试
来自提交导入的乐趣
submission.fun()
或者如果您想在调用fun()函数时更轻松,请尝试一下 从提交导入乐趣为乐趣
乐趣()