on perhaps unloading modules?
Hope Rouselle
hrouselle at jevedi.com
Sun Aug 15 09:50:10 EDT 2021
(*) 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.
More information about the Python-list
mailing list