8

I have several modules, I would like to reload without having to restart Sublime Text, while I am developing a Sublime Text package.

I am running Sublime Text build 3142 which comes with python3.3 running continuously its packages/plugins. However while developing a plugin, I import a third part module I added to path as:

import os
import sys

def assert_path(module):
    """
        Import a module from a relative path
        https://stackoverflow.com/questions/279237/import-a-module-from-a-relative-path
    """
    if module not in sys.path:
        sys.path.insert( 0, module )

current_directory = os.path.dirname( os.path.realpath( __file__ ) )
assert_path( os.path.join( current_directory, 'six' ) ) # https://github.com/benjaminp/six

import six

But when I edit the source code of the module six I need to close and open Sublime Text again, otherwise Sublime Text does not gets the changes to the six python module.


Some code I have tried so far:

print( sys.modules )
import git_wrapper
imp.reload( find_forks )
imp.reload( git_wrapper )
imp.reload( sys )
  1. Proper way to reload a python module from the console
  2. Reloading module giving NameError: name 'reload' is not defined
cs95
  • 379,657
  • 97
  • 704
  • 746
Evandro Coan
  • 8,560
  • 11
  • 83
  • 144
  • 1
    You may want to try: https://packagecontrol.io/packages/AutomaticPackageReloader so you don't have to implement the reload yourself. Even if that does what you want, make sure you periodically test that everything still works by quitting and restarting Sublime, though. – OdatNurd Jul 30 '17 at 23:44
  • It worked! Thanks! – Evandro Coan Jul 31 '17 at 02:11

2 Answers2

12

To list all imported modules, you can use sys.modules.values().

import sys
sys.modules.values()

sys.modules is a dictionary that maps the string names of modules to their references.

To reload modules, you can loop over the returned list from above and call importlib.reload on each one:

import importlib
for module in sys.modules.values():
    importlib.reload(module)
ctholho
  • 801
  • 11
  • 18
cs95
  • 379,657
  • 97
  • 704
  • 746
  • 3
    This seems to not work well/smoothly in `iPython` - any ideas? – dwanderson Aug 28 '18 at 19:48
  • @dwanderson what exactly is the issue? – cs95 Aug 28 '18 at 20:13
  • 5
    Yeah, sorry, that was an empty comment - first, I get a bunch of `failed to reload `, and then when I try to `reload(my_module)` I get: `/{...}/lib/python3.6/importlib/_bootstrap.py in cached(self) NotImplementedError:` - might be because the `iPython` modules themselves get `reloaded` and they don't like that? – dwanderson Aug 28 '18 at 20:33
  • Note that `imp` is deprecated in favor of `importlib`, as of Python 3.4: https://docs.python.org/3/library/importlib.html – llf Apr 02 '20 at 17:21
  • 1
    @dwanderson If it's still relevant to you, I've posted an answer which might fix your problem. – llf Apr 03 '20 at 20:07
  • This worked fine for me only after adding an __init__.py file in all folders – Pankov May 25 '22 at 13:48
  • This is a necessity for python to recognize them as modules. – cs95 Jul 23 '22 at 10:42
9

I imagine in most situations, you want to reload only the modules that you yourself are editing. One reason to do this is to avoid expensive reloads, and another is hinted in @dwanderson's comment, when reloading pre-loaded modules might be sensitive to the order in which they are loaded. It's particularly problematic to reload importlib itself. Anyways, the following code reloads only the modules imported after the code is run:

PRELOADED_MODULES = set()

def init() :
    # local imports to keep things neat
    from sys import modules
    import importlib

    global PRELOADED_MODULES

    # sys and importlib are ignored here too
    PRELOADED_MODULES = set(modules.values())

def reload() :
    from sys import modules
    import importlib

    for module in set(modules.values()) - PRELOADED_MODULES :
        try :
            importlib.reload(module)
        except :
            # there are some problems that are swept under the rug here
            pass

init()

The code is not exactly correct because of the except block, but it seems robust enough for my own purposes (reloading imports in a REPL).

llf
  • 610
  • 10
  • 11