on perhaps unloading modules?

Hope Rouselle hrouselle at jevedi.com
Mon Aug 16 07:47:09 EDT 2021


Greg Ewing <greg.ewing at canterbury.ac.nz> writes:

> On 16/08/21 1:50 am, Hope Rouselle wrote:
>> 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.
>
> Okay, I'll assume all the security issues have been taken are of, e.g.
> by running all of this in a virtual machine...

Oh, good idea. :-D

>> 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.
>
> The best way to do this would be to run each student's file in
> a separate process, so you know you're getting a completely fresh
> start each time.

Yes, that would be much better indeed, but I'd be left with IPC
mechanisms of exchanging data.  My first idea is to just print out a
JSON of the final report to be read by the grader-process.  (That is
essentially what I'm already doing, except that I don't need to
serialize things in a string.)

> The second best way would be to not use import_module, but to
> exec() the student's code. That way you don't create an entry in
> sys.modules and don't have to worry about somehow unloading the
> module.
>
> Something like
>
>     code = read_student_file(student_name)
>     env = {} # A dict to hold the student's module-level definitions
>     exec(code, env)
>     grade_question1(env)
>     env['procedure_x'] = key.procedure_x
>     grade_question2(env)
>     ...etc...

That seems a lot easier to implement.

>> 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.
>
> I would have to see a specific example of that. 

I'm sorry.  I don't think I was right in that observation.  I tried to
produce one such small program to post here and failed.  (Let's forget
about that.)

> One thing to keep in mind is that if key.procedure_x modifies any
> globals in the key module, it will still modify globals in the key
> module -- not the student's module -- after being transplanted there.
>
> More generally, there are countless ways that a student's code
> could modify something outside of its own module and affect the
> behaviour of other student's code. This is why it would be
> vastly preferable to run each test in a fresh process.

I'm totally convinced.  I'll do everything in a fresh process.  Having
guarantees makes the job much more pleasant to do.

>> (*) If it were easy to unload modules...
>
> It's sometimes possible to unload and reimport a module, but
> only if the module's effects are completely self-contained.
> That depends not only on what the module itself does, but
> what other modules do with it. If any other module has imported
> it, that module will still contain references to the old
> module; if there are instances of a class defined in it still
> existing, they will still be instances of the old version of
> the class; etc.
>
> 99.999% of the time it's easier to just start again with a
> fresh Python process.

I'm totally convinced.  Thank you so much!


More information about the Python-list mailing list