Import only functions from python file

I have many Python files (submission1.py, submission2.py, ..., submissionN.py) in the following format,

#submission1.py
def fun():
   print('some fancy function')

fun()

I want to write a tester to test these materials. (This is actually the homework that I am evaluating.). I have a tester for fun()that is able to test the function itself. However, my problem is that when importing submission.pyit starts fun(), as it calls it at the end of the file.

I know that using if __name__ == "__main__":is the right way to solve this problem, but our ideas do not have it, since we did not learn it.

So my question is: is there a way to import only fun()from files submission.pywithout running the rest of the python file?

+4
source share
2 answers

For simple scripts with simple functions, the following will work:

submission1.py:

def fun(x):
   print(x)

fun("foo")


def fun2(x):
   print(x)


fun2("bar")

print("debug print")

You can remove the entire panel of FunctionDef nodes and then recompile:

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")

Conclusion:

calling fun
calling fun2

The body of the module contains two Expr and one Print node, which we delete in a loop, preserving only the FunctionDef functions.

[<_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>]

So, after the loop body contains only functions:

[<_ast.FunctionDef object at 0x7f49a786a610>, <_ast.FunctionDef object at 0x7f49a4583b90>]

It will also catch where the functions are called with the seal, which if the student called the function from the IDE, where the functions have return statements, is very likely, also to save any imports that are, you can save ast.Import and ast.ImportFrom's:

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())

Then compile the import:

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())

Conclusion:

calling fun
calling fun2
3.14159265359
2015-05-09 12:29:02.472329

Finally, if you have any variables declared by you, you should use them to save them using 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()

Add ast.ssignign:

for node in p.body[:]:
    if not isinstance(node, (ast.FunctionDef,
        ast.Import, ast.ImportFrom,ast.Assign)):
        p.body.remove(node)
....

Conclusion:

calling fun
calling fun2
3.14159265359
2015-05-09 12:34:18.015799
25
35

, , , . , , . , , isinstance.

cpython Parser/Python.asdl.

+4

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

All Articles