Sublime Forum

[MODULE] module_loader : Reload Modules & Dependencies WITHOUT Restarting Sublime Text

#1

INFO:

 
SublimeText does not natively support module reloading.

This can be a hassle when developing plugins, as you will constantly need to restart SublimeText in order to update your modules.

module_loader solves that issue by reloading all modules every time the plugin is saved.

It also removes the need for explicit declaration of modules.  Just list the directories you want to load all modules from in moduleDirectories, and module_loader will do the rest.
 



CODE:

import imp, os, sys

def load ( moduleDirectories, pluginGlobals ):

	moduleExtensions  = [ "py" ] #▒▒▒  add extensions here to extend module support   ▒▒▒#
	moduleLoader_Name = "module_loader"
	modulePaths       = []

	for path in sys.path:
		if path.endswith ( "Sublime Text 3" + os.sep + "Packages" ):
			packagesPath = path
			break

	for directory in moduleDirectories:
		modulePaths.append ( packagesPath + os.sep + directory )

	for index in range ( 0, 2 ): #▒▒▒  loads modules twice to ensure dependencies are updated  ▒▒▒#
		for path in modulePaths:
			for file in os.listdir ( path ):
				for extension in moduleExtensions:

					if file.endswith ( os.extsep + extension ):
						moduleName = os.path.basename( file )[ : - len ( os.extsep + extension ) ]

						if moduleName != moduleLoader_Name:
							fileObject, file, description = imp.find_module( moduleName, [ path ] )
							pluginGlobals[ moduleName ] = imp.load_module ( moduleName, fileObject, file, description )

 

Demo Plugin @ GitHub

( it’s the same plugin shown in the GIFs below)
 
 



 

DEMO:

With module_loader:

Without module_loader:

 



IMPLEMENTATION:

 
To implement module_loader, just put a copy of module_loader.py in your MyPlugin/Modules directory and include these 3 lines in your plugin:

from MyPlugin.Modules import module_loader
moduleDirectories = [ "MyPlugin/Modules", "MyPlugin/MoreModules" ]
module_loader.load ( moduleDirectories, globals() )

( update directories accordingly )

4 Likes

Hot update without restarting sublime
Auto reloading of python module files used by plugin
[FORUM REQUEST] Resource Category / Pinned Thread Alternative
Modules + Dependencies : Best Practices?
Plugin organization / importing
[SOLVED] issue using "edit" objects recursively
Auto reloading of python module files used by plugin
#2

That looks rather simple, compared to what I would have expected. I did have something in mind for automatically reverse-reloading modules while developing ST plugins, but it wasn’t high enough on my priority list. I’ll probably take a deeper look at what you did once I decide to work on it.

1 Like

#3

 
Yes, my personal need that resulted in the script’s creation was pretty simple:

  • reload all modules upon saving Plugin.py files
  • dynamically load all modules within any specified subdirectories of /Packages/

The second point was mostly what sparked it, I was feeling lazy about import declarations :stuck_out_tongue_winking_eye:

Then I ran into the first issue & had to restart ST way too often during development, so figured I’d try to do something about it.
 



 
module_loader ( probably? ) won’t work for more complex issues like extracting modules from zipped ST files.

 

Also, although I couldn’t think of a reason why I would need to do so:
It would be easy enough to implement a rootPaths array in which to add the existing /Packages/ directory along with any other additional directories to check for modules.

Aside from that, it would just need another loop layer added above for path in modulePaths.
 



 
If you end up trying it out, please let me know if you run into any [ actual | hypothetical ] issues or areas where you see room for improvement.  :slightly_smiling:

0 Likes

#4

Reloading works on saving main sublime file. But when I save imported file, I still have to go to main file and save it.
What about a post save hook to reload the plugin on every file save?

2 Likes

#5

I saw another similar thread by @fico Modules + Dependencies : Best Practices? and I have a stupid question: why don’t you just disable and reenabled your package again?

1 Like

#6

@randy3k

My other topic that you linked to has an implementation derived from this code, but the scope is substantially different. This code’s purpose is to reload modules + dependencies without disrupting development workflow. The code in the other thread’s purpose is to manage dependency releases while taking things like versioning into consideration.
 


 
In regards to:

why don’t you just disable and reenabled your package again?

 

I have about 30 reasons for you ( give or take ):

Ctrl + Shift + P
d i s p a c
p a c n a m
Ctrl + Shift + P
e n p a c
p a c n a m

 
:stuck_out_tongue_winking_eye:

0 Likes

#7

write a keybinding to update the preferences to add it to ignored packages then remove it again :smiley:

0 Likes

#8

 
Every time you’re working on a different package?!

You guys need some lessons in lazyness :pensive: SMH…

 
The script above + this thing == ( 2-key, 5-key ) hotkey solutions == dependency loading pwnage

0 Likes

#9

it could determine what (package) folder the current file is in :wink:

0 Likes

#10

 
Not in the case of something like:

Packages
     |___  MyPlugin
     |
     |___  MyPlugin_Dependency

Well… not super easily at least.

0 Likes

#11

Disabling a package does not unload modules inside it, that are not plugins (afaik). Reloading Plugins that import these modules also doesn’t trigger a reload of the modules since they are already in sys.path.

3 Likes

#12

Update: Check my comment below.

1 Like

#13

There is a beautiful way in GitSavvy to reload sublime modules by Eldar Abusalimov , Module Reloader is now making use of it.

2 Likes

#14

That looks very nice indeed and much more like I intended to do it, with temporarily hiding modules in sys.modules and a sys.meta_path hook.

I’ll make sure to play around with that when I find the time, so thanks for the pointer!

0 Likes

#15

I have been trying out your plugin, thanks for making it. In testing it, it reloads the open plugin correctly, but at the end where you create the dummy plugin, I get the following:

[Module Reloader] end --------------------------------------------------------
reloading plugin _moduler_reloader
Traceback (most recent call last):
  File "C:\Program Files\Sublime Text 3\sublime_plugin.py", line 109, in reload_plugin
    m = importlib.import_module(modulename)
  File "./python3.3/importlib/__init__.py", line 90, in import_module
  File "<frozen importlib._bootstrap>", line 1584, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1565, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1529, in _find_and_load_unlocked
ImportError: No module named '_moduler_reloader'
unloading plugin _moduler_reloader

As you can see I am on Windows. If I up the timeout on line 105 of module_reloader.py to, say, 400ms, I get this:

[Module Reloader] end --------------------------------------------------------
reloading plugin _moduler_reloader
unloading plugin _moduler_reloader

If I lower the timeout to 50ms, I just get:

[Module Reloader] end --------------------------------------------------------

But in this last case, commands are not reloaded/ready.

0 Likes

#16

Thanks for the report. I chose 100ms because it works on my Mac. I need to make it a little bit more robust.

1 Like

#17

Thanks for the update, this is really useful. :thumbsup:

0 Likes