How to call an imported module knowing its name as a string?

Question:

I am writing an application that should test the solution of many students.
I have structure like this

app/
    students/
        A/
            lab1/
                solution.py
            lab2
                solution.py
        B/
            lab1/
                solution.py
        C/
    test.py

I want to import the solution file in the testing module and run the main method.
I know there is __import__ function that returns a module object, but I don’t know how to call main method.

My code:

import os


def get_student_names():
    return os.listdir('students')

def test(name, lab):
    if lab not in os.listdir('/'.join(['students',name])):
        return f"{lab} not found"
    if 'solution.py' not in os.listdir('/'.join(['students',name,lab])):
        return "solution file not found"
    module = __import__('.'.join(['students',name,lab,'solution']))
    # module.name.lab.solution.main()

def main():
    student_names = get_student_names()
    for student in student_names:
        result = test(student, "lab1")

I want to call module.name.lab.solution.main() but the module is students. How to switch to solution submodule?

Asked By: Artem K

||

Answers:

Your most immediate problem is that you aren’t returning anything from test so, result will always be None. The second and most important problem you have is that you don’t really have any modules. You actually have a bunch of random folders with arbitrary python scripts in them. This means __import__ is going to be quite unfriendly to you.

if we run this line

__import__('students.A.lab1.solution').main()

we get back this error:

AttributeError: module 'students' has no attribute 'main'

students? We’re supposed to be in solution! That’s a problem. There is a way to do this without worrying about converting and maintaining your individual folders as actual modules.


First, students must call main in their solution.

ex:

def main():
    print('hello, world')
    
main()

Secondly, run their script. If the command fails it is going to tell you the script doesn’t exist and move on to the next student.

import os

#change this to however you call python
PY   = 'python3'

#lab format strings
LAB1 = '{} "students/{}/lab1/solution.py"'
LAB2 = '{} "students/{}/lab2/solution.py"'


def main(lab:str) -> None:
    for student in os.listdir('students'):
        os.system(lab.format(PY, student))
            
    
main(LAB1)

As highlighted by @KarlKnetchel, the above code has all of the same vulnerabilities as your original concept. This is NOT safe. You should definitely consider running this in a sandbox, if you use it.

Answered By: OneMadGypsy
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.