Forcing Unload/Deconstruction of Dynamically Imported File from Source
<p>Been a longtime browser of SO, finally asking my own questions!</p>

<p>So, I am writing an automation script/module that looks through a directory recursively for python modules with a specific name. If I find a module with that name, I load it dynamically, pull what I need from it, and then unload it. I noticed though that simply del'ing the module does not remove all references to that module, there is another lingering somewhere and I do not know where it is. I tried taking a peek at the source code, but couldn't make sense of it too well. Here is a sample of what I am seeing, greatly simplified:</p>

<p>I am using Python 3.5.2 (Anaconda v4.2.0). I am using importlib, and that is what I want to stick with. I also want to be able to do this with vanilla python-3. </p>

<p>I got the import from source from the python docs <a href="https://docs.python.org/3.6/library/importlib.html#importing-a-source-file-directly%22here%22" rel="nofollow">here</a> (yes I am aware this is the Python 3.6 docs).</p>

<p>My main driver...</p>

<pre><code># main.py
import importlib.util
import sys

def foo():
   spec = importlib.util.spec_from_file_location('a', 'a.py')
   module = importlib.util.module_from_spec(spec)
   spec.loader.exec_module(module)
   print(sys.getrefcount(module))
   del module
   del spec

if __name__ == '__main__':
   foo()
   print('THE END')
</code></pre>

<p>And my sample module...</p>

<pre><code># a.py
print('hello from a')

class A():
   def __del__(self):
      print('SO LONG A!')

inst = A()
</code></pre>

<p>Output:</p>

<pre><code>python main.py
HELLO FROM A!
2
THE END
SO LONG A!
</code></pre>

<p>I expected to see "SO LONG A!" printed before "THE END". So, where is this other hidden reference to my module? I understand that my del's are gratuitous with the fact that I have it wrapped in a function. I just wanted the deletion and scope to be explicit. How do I get a.py to completely unload? I plan on dynamically loading a ton of modules like a.py, and I do not want to hold on to them any longer than I really have to. Is there something I am missing?</p>

<p>There is a <em>circular reference</em> here, the module object references objects that reference the module again.</p>

<p>This means the module is not cleared immediately (as the reference count never goes to 0 by itself). You need to wait for the circle to be broken by the garbage collector.</p>

<p>You can force this by calling <a href="https://docs.python.org/3/library/gc.html#gc.collect" rel="nofollow"><code>gc.collect()</code></a>:</p>

<pre><code>import gc

# ...

if __name__ == '__main__':
   foo()
   gc.collect()
   print('THE END')
</code></pre>

<p>With that in place, the output becomes:</p>

<pre><code>$ python main.py
hello from a
2
SO LONG A!
THE END
</code></pre>

python python-3.x python-importlib