(*) Introduction By the way, I'm aware that what I'm doing here is totally unsafe and I could get my system destroyed. I'm not planning on using this --- thank you for your concern. I'm just interested in understanding more about modules.
(*) The problem I got myself into a mess with loading modules at runtime. The reason I'm loading modules at runtime is because I'm writing a script to grade students' work. (Each student test is a file.py.) Here's the phenomenon. Notice how student m0 (who really scored a zero) first gets his grade right, but if I invoke it again, then it gets 50.0. >>> grade_student(m0) {'grade': 0.0, ...} >>> grade_student(m0) {'grade': 50.0, ...} (*) Where's the problem? The variable m0 is a module and modules in Python are effectively singletons --- that is, they are meant to be loaded only once, no matter how many times you ask the system to. That's my understanding. (*) How did I get into this mess? When m0 is graded first, that is the first student's module ever loaded, so everything turns out correct. This module m0 didn't do any work --- didn't write any procedures, so it gets grade = 0 ---, so when I check the first procedure, it doesn't exist --- zero on question 1. However, question 2 depends on question 1. So I use the test's key (which I wrote, which is perfectly correct) and I augment the student's module with the key's procedures that are prerequisites to question 2 and then I test question 2. How do I do that? I do m.question1 = key.question1 where ``key.question1'' is a correct procedure for getting all points of question1. (That's kind to the student: I'm allowing them to get a zero on question 1 while perhaps getting question 2 right.) However, once I augment the student's code, I can' t find a way to properly restore it to the original --- so on a second execution, m0 gets many more points. That's not the whole problem. For reasons I don't understand, new modules I load --- that is, different students --- get mixed with these modifications in m0 that I made at some point in my code. Of course, you want to see the code. I need to work on producing a small example. Perhaps I will even answer my own question when I do. For now, let me just ignite your imagination. Feel free to ignore all of this and wait for a nice example of the problem. (*) The code How do I load a student's file? --8<---------------cut here---------------start------------->8--- from importlib import * def get_student_module(fname): # Here fname := p1_hello.py. But we need to remove the extension. # Let's call it basename then. mod_name = basename(fname) try: student = import_module(mod_name) except Exception as e: return False, str(e) return True, student --8<---------------cut here---------------end--------------->8--- Now let d be a path to a directory. How do use I this procedure? --8<---------------cut here---------------start------------->8--- for f in get_all_tests(d): okay, student = get_student_module(f) report = grade_student(student) [...] --8<---------------cut here---------------end--------------->8--- What does grade_student(student_module) do? It passes student_module to procedures to check every little thing the test requires. Let's take a look at question4(). Question 4 requires a previous procedure called procedure_x. So I overwrite the student's module with the key's procedure. def question4(m): # We first provide students with all the prerequisites of the # question, so they don't necessarily get this one wrong by getting # prerequisites wrong. m.procedure_x = key.procedure_x # Now I make all my verifications about, say, m.procedure_y and I # compute an integer called losses, which I return. return losses This strategy must be so unwise that it totally breaks my naïve dream of automatic grading, showing how unfit I am for teaching students how to program. Such is life. (*) If it were easy to unload modules... I could just unload it and load it again. That should restore the student's module back to its original source code. -- https://mail.python.org/mailman/listinfo/python-list