2

Is there a way to do this in python 3.6+?

import -force mymodule

I just want a single python command that both:

(1) loads the module for the first time, and

(2) forces a reload of the module if it already loaded without barfing.

(This is not a duplicate question because I'm asking for something different. What I want is a single function call that will do Items (1) and (2) above as the same function call. I don't want to make a coding decision manually about if I could issue "import" or "imp.reload". I just want python code for a single function "def" that can detect which case is appropriate and proceed automatically to make the decision for me about how to import it it, (1) or (2).

I'm thinking that it something like this:

def import_force(m):
    import sys
    if m not in sys.modules:
        import m
    else:
        import importlib
        importlib.reload(m)

Except, I can't figure out how to pass a module name as a parameter. Just gives me an error no such module named 'm'

Bimo
  • 5,987
  • 2
  • 39
  • 61
  • (Alternatively, is there a python command that resets the memory back to a checkpoint state? Then, to clear the loaded module I can just reset the memory checkpoint and reissue the "import mymodule" without needing to reload as well. Example, load python interactive, check point memory, run imports, reload memory checkpoint of previously saved, rerun imports. I know this one is kind of a long shot...) – Bimo Jan 12 '18 at 14:26
  • Possible duplicate of [Reimport a module in python while interactive](https://stackoverflow.com/questions/1254370/reimport-a-module-in-python-while-interactive) – Jim Stewart Jan 12 '18 at 14:27
  • https://docs.python.org/3/library/importlib.html#importlib.reload – Ry- Jan 12 '18 at 14:27
  • 1
    There's a good writeup on ways to do this along with caveats at http://justus.science/blog/2015/04/19/sys.modules-is-dangerous.html – Jim Stewart Jan 12 '18 at 14:28
  • Is this in interactive mode? Generally in an actual program you *don't* want to do this. – Daniel Pryden Jan 12 '18 at 14:31
  • 1
    Your existing code is almost there. To import from a string, use `importlib.import_module`. You will need to assign the resulting object into `globals()`. – wim Jan 12 '18 at 14:55
  • This seems like something that would be almost exclusively useful in interactive mode. For interactive use, you may be interested in IPython's [autoreload](https://ipython.org/ipython-doc/3/config/extensions/autoreload.html) functionality. – user2357112 Jan 12 '18 at 14:59
  • right. I mostly wanted it for debugging and twinkling code. It just gets on my nerves importing a module and then hitting up arrow to reload module after a code change and then realizing you need import importlib.reload instead – Bimo Jan 12 '18 at 15:08
  • Replace `import m` with `__import__(m)`. Don't forget to return the loaded module and assign it in your base namespace. – Mad Physicist Jan 12 '18 at 16:23
  • @BillMoore Do you only need to use this in ipython? I have a possible solution for you, but it will only work in ipython. – wim Jan 12 '18 at 16:28
  • I just realized when using ipython shell gui, you can click on "restart kernel" from QtTerminal window. and get a clean slate to import your libraries fresh. – Bimo Jan 12 '18 at 16:40

4 Answers4

1

There is one missing step that you semi-corrected in your new answer, which is that you need to assign the new module in every scope that uses it. The easiest way is to return the module object and bind it to the name you want outside your function. Your original implementation was 90% correct:

import sys, importlib

def import_force(m):
    if m not in sys.modules:
        return __import__(m)
    else:
        return importlib.reload(sys.modules[m])

Now you can use this function from the command line to replace import, e.g.:

my_module = force_import('my_module')

Any time you find yourself using exec to perform a task for which there is so much well defined machinery already available, you have code smell. There is also no reason to re-import sys and importlib every time.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
0

This function should do what you want:

def import_force(name):
    needs_reload = name in sys.modules
    module = importlib.import_module(name)
    if needs_reload:
        module = importlib.reload(module)
    return module

# Usage example:
os = import_force('os')

An alternative approach is to write your own import hooks, which I won't describe.

However please note that this is an anti-pattern and I would discourage the practice of reloading modules at every import.

If this is for debugging purposes, then I would suggest using one of the many auto-reloader solutions available online: they watch your Python files for changes, and when you make modifications they automatically re-import the modules.

The reasons why your function didn't work are two:

  1. The import keyword does not resolve variables, so import m does not mean "import the module which name is in the variable m", but rather it means "import the module named m".

  2. importlib.reload wants a module object, not a module name.

Andrea Corbellini
  • 17,339
  • 3
  • 53
  • 69
  • It's quite useful in the context of making frequent debugging changes that OP described. Certainly not for production use. – Mad Physicist Jan 12 '18 at 17:24
  • So what if it’s an “anti pattern”... it’s a useful debugging “pattern” that saves me time... that’s interesting I didn’t think of using the import to lookup the module object. Thanks. That’s why I had to get strange with the exec statements. – Bimo Jan 12 '18 at 17:28
  • @BillMoore: if it's for debugging, than go for it. I don't think patterns and anti-patterns matter a lot in debugging environments. By the way, may I suggest using an auto-reloader (see updated answer)? I find it pretty useful during development – Andrea Corbellini Jan 12 '18 at 17:30
0
import sys
import importlib

# importing with a sledgehammer... simple, effective, and it always works
def import_force(name):
    module = importlib.import_module(name)
    module = importlib.reload(module)
    return module

#assuming mymodule.py is in the current directory
mymodule = import_force("mymodule")
Bimo
  • 5,987
  • 2
  • 39
  • 61
-3

It's possible! but a little bit tricky to code correctly the first time...

import sys
import importlib

def import_force(modstr):    
    if modstr not in sys.modules:
        print("IMPORT " + modstr)
        cmd = "globals()['%s'] = importlib.import_module('%s')" % (modstr, modstr)
        exec(cmd)
    else:
        print("RELOAD " + modstr)
        cmd = "globals()['%s'] = importlib.reload(%s)" % (modstr, modstr)
        exec(cmd)       

If you have a module file in your current directory call "mymodule.py", then use it like this:

Py>  import_force("mymodule")

Version 2.0:

def import_force(modstr):    
    if modstr not in sys.modules:
        print("IMPORT " + modstr)
        globals()[modstr] = importlib.import_module(modstr)
    else:
        print("RELOAD " + modstr)
        globals()[modstr] = importlib.reload(sys.modules[modstr])
Bimo
  • 5,987
  • 2
  • 39
  • 61
  • 3
    Using `exec` here is just as pointless as in your earlier answer. – user2357112 Jan 12 '18 at 17:10
  • How do you figure? Importlib returns the module object then I assign it to a global variable. Exec import is only pointless if you don’t assign local module to global – Bimo Jan 12 '18 at 17:13
  • @Bill. 90% of what you do with exec here, as before, should just be plain Python code, not a string. – Mad Physicist Jan 12 '18 at 17:22
  • Also, if you put your function in a module of its own, setting globals won't do anything close to what you want. – Mad Physicist Jan 12 '18 at 17:29