Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Temporarily removing a python module

I'm trying to temporarily remove a python module from sys.modules so that I can import it as part of a test case (with various system functions mocked out) and then put it back again. (Yes, that's a bit crazy and I'm probably going to end up restructuring the code instead but now I'm curious...)

I can remove the module and reimport it just fine but I can't seem to put it back to the original module once I'm finished. (Maybe that's just not posible?) Here's a test case that I wrote to test out the idea:

class Test(unittest.TestCase):

    def test_assumptions(self):
        import meta.common.fileutils as fu1
        del(sys.modules["meta.common.fileutils"])
        import meta.common.fileutils
        del(sys.modules["meta.common.fileutils"])
        sys.modules["meta.common.fileutils"] = fu1 # I hoped this would set the module back
        import meta.common.fileutils as fu2
        self.assertEqual(fu1, fu2)  # assert fails, fu2 is a new copy of module :-(

Can anyone suggest why it might be failing?

Edit, using pop() as suggested by one of the answers also fails:

class Test(unittest.TestCase):

    def test_assumptions(self):
        import meta.common.fileutils as fu1
        orig = sys.modules.pop("meta.common.fileutils")
        import meta.common.fileutils
        del(sys.modules["meta.common.fileutils"])
        sys.modules["meta.common.fileutils"] = orig 
        import meta.common.fileutils as fu2
        self.assertEqual(fu1, orig) # passes
        self.assertEqual(fu2, orig) # fails
        self.assertEqual(fu1, fu2)  # fails
like image 612
Fasaxc Avatar asked Oct 16 '25 03:10

Fasaxc


1 Answers

It looks to me like the issue here has to do with packages. In particular, for a module that lives in a package (eg meta.common), there are two ways to access it: via sys.modules, and via the parent package's dictionary (i.e., meta.common.__dict__). It looks to me like the import meta.common.fileutils as fu2 line is getting fu2's value from meta.common.__dict__, and not from sys.modules.

So the solution: in addition to monkey-patching sys.modules, you should also monkey-patch the parent package. I.e., add something like this:

>>> import meta.common
>>> meta.common.fileutils = fu1

right before the sys.modules["meta.common.fileutils"] = fu1 line.

like image 170
Edward Loper Avatar answered Oct 17 '25 16:10

Edward Loper