Skip to content

Commit 22a4b32

Browse files
authored
Fix reloader after PC update (#229)
This commit fixes a compatibility error with Package Control 4. The reloader used by UnitTesting package uses some PC internals, which have changed or do no longer exist. For now, copy the reloader from `GitSavvy` as it seems "newer" and works at least locally.
1 parent 647c35b commit 22a4b32

File tree

1 file changed

+50
-74
lines changed

1 file changed

+50
-74
lines changed

unittesting/utils/reloader.py

+50-74
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
11
import sublime
22
import sublime_plugin
33
import os
4+
import posixpath
45
import threading
56
import builtins
67
import functools
78
import importlib
89
import sys
9-
import types
10+
import traceback
11+
from inspect import ismodule
1012
from contextlib import contextmanager
1113

12-
try:
13-
from package_control.package_manager import PackageManager
14-
15-
def is_dependency(pkg_name):
16-
return PackageManager()._is_dependency(pkg_name)
17-
18-
except ImportError:
19-
def is_dependency(pkg_name):
20-
return False
21-
2214

2315
class StackMeter:
2416

@@ -49,6 +41,7 @@ def path_contains(a, b):
4941

5042

5143
def get_package_modules(pkg_name):
44+
# (str) -> Dict[str, ModuleType]
5245
in_installed_path = functools.partial(
5346
path_contains,
5447
os.path.join(
@@ -63,8 +56,10 @@ def get_package_modules(pkg_name):
6356
)
6457

6558
def module_in_package(module):
66-
file = getattr(module, '__file__', '') or ''
67-
paths = getattr(module, '__path__', ()) or ''
59+
# Other (extracted) ST plugins using python 3.8 have this set to
60+
# `None` surprisingly.
61+
file = getattr(module, '__file__', None) or ''
62+
paths = getattr(module, '__path__', ())
6863
return (
6964
in_installed_path(file) or any(map(in_installed_path, paths)) or
7065
in_package_path(file) or any(map(in_package_path, paths))
@@ -77,65 +72,66 @@ def module_in_package(module):
7772
}
7873

7974

80-
# check the link for comments
81-
# https://github.com/divmain/GitSavvy/blob/599ba3cdb539875568a96a53fafb033b01708a67/common/util/reload.py
82-
def reload_package(pkg_name, dummy=True, verbose=True):
83-
if is_dependency(pkg_name):
84-
reload_dependency(pkg_name, dummy, verbose)
85-
return
75+
def package_plugins(pkg_name):
76+
return [
77+
pkg_name + '.' + posixpath.basename(posixpath.splitext(path)[0])
78+
for path in sublime.find_resources("*.py")
79+
if posixpath.dirname(path) == 'Packages/' + pkg_name
80+
]
81+
8682

83+
def reload_package(pkg_name, dummy=True, verbose=True):
8784
if pkg_name not in sys.modules:
8885
dprint("error:", pkg_name, "is not loaded.")
8986
return
9087

9188
if verbose:
9289
dprint("begin", fill='=')
9390

94-
modules = get_package_modules(pkg_name)
91+
all_modules = {
92+
module_name: module
93+
for module_name, module in get_package_modules(pkg_name).items()
94+
}
95+
plugins = [plugin for plugin in package_plugins(pkg_name)]
9596

96-
for m in modules:
97-
if m in sys.modules:
98-
sublime_plugin.unload_module(modules[m])
99-
del sys.modules[m]
97+
# Tell Sublime to unload plugins
98+
for plugin in plugins:
99+
module = sys.modules.get(plugin)
100+
if module:
101+
sublime_plugin.unload_module(module)
100102

101-
try:
102-
with intercepting_imports(modules, verbose), \
103-
importing_fromlist_aggresively(modules):
103+
# Unload modules
104+
for module_name in all_modules:
105+
sys.modules.pop(module_name)
104106

105-
reload_plugin(pkg_name)
107+
# Reload packages
108+
try:
109+
with intercepting_imports(all_modules, verbose), importing_fromlist_aggressively(all_modules):
110+
for plugin in plugins:
111+
sublime_plugin.reload_plugin(plugin)
106112
except Exception:
107113
dprint("reload failed.", fill='-')
108-
reload_missing(modules, verbose)
109-
raise
114+
# Rollback modules
115+
for name, module in all_modules.items():
116+
sys.modules[name] = module
117+
118+
# Try reloading again to get the commands back. Here esp. the
119+
# reload command itself.
120+
for plugin in plugins:
121+
sublime_plugin.reload_plugin(plugin)
122+
123+
traceback.print_exc()
124+
print('--- Reloading failed. ---')
125+
sublime.active_window().status_message('Reloading 💣ed. 😒.')
126+
return
110127

111128
if dummy:
112129
load_dummy(verbose)
113130

114131
if verbose:
115132
dprint("end", fill='-')
116133

117-
118-
def reload_dependency(dependency_name, dummy=True, verbose=True):
119-
"""
120-
Reload.
121-
122-
Package Control dependencies aren't regular packages, so we don't want to
123-
call `sublime_plugin.unload_module` or `sublime_plugin.reload_plugin`.
124-
Instead, we manually unload all of the modules in the dependency and then
125-
`reload_package` any packages that use that dependency. (We have to manually
126-
unload the dependency's modules because calling `reload_package` on a
127-
dependent module will not unload the dependency.)
128-
"""
129-
for name in get_package_modules(dependency_name):
130-
del sys.modules[name]
131-
132-
manager = PackageManager()
133-
for package in manager.list_packages():
134-
if dependency_name in manager.get_dependencies(package):
135-
reload_package(package, dummy=False, verbose=verbose)
136-
137-
if dummy:
138-
load_dummy(verbose)
134+
sublime.active_window().status_message('Package has been 🙌 reloaded.')
139135

140136

141137
def load_dummy(verbose):
@@ -192,26 +188,6 @@ def after_remove_dummy(trial=0):
192188
condition.release()
193189

194190

195-
def reload_missing(modules, verbose):
196-
missing_modules = {name: module for name, module in modules.items()
197-
if name not in sys.modules}
198-
if missing_modules:
199-
if verbose:
200-
dprint("reload missing modules")
201-
for name in missing_modules:
202-
if verbose:
203-
dprint("reloading missing module", name)
204-
sys.modules[name] = modules[name]
205-
206-
207-
def reload_plugin(pkg_name):
208-
pkg_path = os.path.join(os.path.realpath(sublime.packages_path()), pkg_name)
209-
plugins = [pkg_name + "." + os.path.splitext(file_path)[0]
210-
for file_path in os.listdir(pkg_path) if file_path.endswith(".py")]
211-
for plugin in plugins:
212-
sublime_plugin.reload_plugin(plugin)
213-
214-
215191
@contextmanager
216192
def intercepting_imports(modules, verbose):
217193
finder = FilterFinder(modules, verbose)
@@ -224,7 +200,7 @@ def intercepting_imports(modules, verbose):
224200

225201

226202
@contextmanager
227-
def importing_fromlist_aggresively(modules):
203+
def importing_fromlist_aggressively(modules):
228204
orig___import__ = builtins.__import__
229205

230206
@functools.wraps(orig___import__)
@@ -236,7 +212,7 @@ def __import__(name, globals=None, locals=None, fromlist=(), level=0):
236212
fromlist.remove('*')
237213
fromlist.extend(getattr(module, '__all__', []))
238214
for x in fromlist:
239-
if isinstance(getattr(module, x, None), types.ModuleType):
215+
if ismodule(getattr(module, x, None)):
240216
from_name = '{}.{}'.format(module.__name__, x)
241217
if from_name in modules:
242218
importlib.import_module(from_name)

0 commit comments

Comments
 (0)