Skip to content

Commit 830fcbb

Browse files
committed
Merge branch 'master' into next
2 parents 8cc5b96 + 27a8902 commit 830fcbb

File tree

4 files changed

+115
-24
lines changed

4 files changed

+115
-24
lines changed

.flake8

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ max-line-length = 120
88
# D104 Missing docstring in public package
99
# D107 Missing docstring in __init__
1010
# W503 line break before binary operator
11+
# W504 line break after binary operator
1112
# W606 'async' and 'await' are reserved keywords starting with Python 3.7
12-
ignore = D100, D101, D102, D103, D104, D107, W503, W606
13+
ignore = D100, D101, D102, D103, D104, D107, W503, W504, W606

unittesting/mixin.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ def casedpath(path):
3636

3737
def relative_to_spp(path):
3838
spp = sublime.packages_path()
39-
path = os.path.realpath(path)
40-
for f in os.listdir(spp):
41-
f2 = os.path.realpath(os.path.join(spp, f))
42-
if path.startswith(f2 + os.sep):
43-
return os.sep + os.path.join(f, os.path.relpath(path, f2))
39+
spp_real = casedpath(os.path.realpath(spp))
40+
for p in [path, casedpath(os.path.realpath(path))]:
41+
for sp in [spp, spp_real]:
42+
if p.startswith(sp + os.sep):
43+
return p[len(sp):]
4444
return None
4545

4646

unittesting/utils/output_panel.py

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
import sublime
1+
import collections
22
import os
3+
import threading
4+
5+
import sublime
36

47

58
class OutputPanel:
@@ -30,21 +33,35 @@ def __init__(
3033
self.output_view.assign_syntax("Packages/UnitTesting/res/unit-testing-test-result.sublime-syntax")
3134
self.closed = False
3235

36+
self.text_queue_lock = threading.Lock()
37+
self.text_queue = collections.deque()
38+
3339
def write(self, s):
34-
self.output_view.set_read_only(False)
35-
self.output_view.run_command('append', {'characters': s}),
36-
self.output_view.set_read_only(True)
37-
self.output_view.show(self.output_view.size())
40+
with self.text_queue_lock:
41+
self.text_queue.append(s)
3842

3943
def writeln(self, s):
4044
self.write(s + "\n")
4145

46+
def _write(self):
47+
with self.text_queue_lock:
48+
text = ''
49+
while self.text_queue:
50+
text += self.text_queue.popleft()
51+
52+
self.output_view.set_read_only(False)
53+
self.output_view.run_command(
54+
'append',
55+
{'characters': text, 'force': True, 'scroll_to_end': True}
56+
)
57+
self.output_view.set_read_only(True)
58+
4259
def flush(self):
43-
pass
60+
self._write()
4461

4562
def show(self):
4663
self.window.run_command("show_panel", {"panel": "output." + self.name})
4764

4865
def close(self):
66+
self.flush()
4967
self.closed = True
50-
pass

unittesting/utils/reloader.py

Lines changed: 84 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,17 @@
1111
from .stack_meter import StackMeter
1212

1313

14+
try:
15+
from package_control.package_manager import PackageManager
16+
17+
def is_dependency(pkg_name):
18+
return PackageManager()._is_dependency(pkg_name)
19+
20+
except ImportError:
21+
def is_dependency(pkg_name):
22+
return False
23+
24+
1425
def dprint(*args, fill=None, fill_width=60, **kwargs):
1526
if fill is not None:
1627
sep = str(kwargs.get('sep', ' '))
@@ -20,21 +31,55 @@ def dprint(*args, fill=None, fill_width=60, **kwargs):
2031
print("[UnitTesting]", *args, **kwargs)
2132

2233

34+
def path_contains(a, b):
35+
return a == b or b.startswith(a + os.sep)
36+
37+
38+
def get_package_modules(pkg_name):
39+
in_installed_path = functools.partial(
40+
path_contains,
41+
os.path.join(
42+
sublime.installed_packages_path(),
43+
pkg_name + '.sublime-package'
44+
)
45+
)
46+
47+
in_package_path = functools.partial(
48+
path_contains,
49+
os.path.join(sublime.packages_path(), pkg_name)
50+
)
51+
52+
def module_in_package(module):
53+
file = getattr(module, '__file__', '')
54+
paths = getattr(module, '__path__', ())
55+
return (
56+
in_installed_path(file) or any(map(in_installed_path, paths)) or
57+
in_package_path(file) or any(map(in_package_path, paths))
58+
)
59+
60+
return {
61+
name: module
62+
for name, module in sys.modules.items()
63+
if module_in_package(module)
64+
}
65+
66+
2367
# check the link for comments
2468
# https://github.com/divmain/GitSavvy/blob/599ba3cdb539875568a96a53fafb033b01708a67/common/util/reload.py
2569
def reload_package(pkg_name, dummy=True, verbose=True):
70+
if is_dependency(pkg_name):
71+
reload_dependency(pkg_name, dummy, verbose)
72+
return
73+
2674
if pkg_name not in sys.modules:
2775
dprint("error:", pkg_name, "is not loaded.")
2876
return
2977

30-
main = sys.modules[pkg_name]
31-
3278
if verbose:
3379
dprint("begin", fill='=')
3480

35-
modules = {main.__name__: main}
36-
modules.update({name: module for name, module in sys.modules.items()
37-
if name.startswith(pkg_name + ".")})
81+
modules = get_package_modules(pkg_name)
82+
3883
for m in modules:
3984
if m in sys.modules:
4085
sublime_plugin.unload_module(modules[m])
@@ -44,7 +89,7 @@ def reload_package(pkg_name, dummy=True, verbose=True):
4489
with intercepting_imports(modules, verbose), \
4590
importing_fromlist_aggresively(modules):
4691

47-
reload_plugin(main.__name__)
92+
reload_plugin(pkg_name)
4893
except Exception:
4994
dprint("reload failed.", fill='-')
5095
reload_missing(modules, verbose)
@@ -57,6 +102,29 @@ def reload_package(pkg_name, dummy=True, verbose=True):
57102
dprint("end", fill='-')
58103

59104

105+
def reload_dependency(dependency_name, dummy=True, verbose=True):
106+
"""
107+
Reload a Package Control dependency.
108+
109+
Package Control dependencies aren't regular packages, so we don't want to
110+
call `sublime_plugin.unload_module` or `sublime_plugin.reload_plugin`.
111+
Instead, we manually unload all of the modules in the dependency and then
112+
`reload_package` any packages that use that dependency. (We have to manually
113+
unload the dependency's modules because calling `reload_package` on a
114+
dependent module will not unload the dependency.)
115+
"""
116+
for name in get_package_modules(dependency_name):
117+
del sys.modules[name]
118+
119+
manager = PackageManager()
120+
for package in manager.list_packages():
121+
if dependency_name in manager.get_dependencies(package):
122+
reload_package(package, dummy=False, verbose=verbose)
123+
124+
if dummy:
125+
load_dummy(verbose)
126+
127+
60128
def load_dummy(verbose):
61129
"""
62130
Hack to trigger automatic "reloading plugins".
@@ -67,20 +135,25 @@ def load_dummy(verbose):
67135
dprint("installing dummy package")
68136
dummy = "_dummy_package"
69137
dummy_py = os.path.join(sublime.packages_path(), "%s.py" % dummy)
70-
open(dummy_py, "w").close()
138+
with open(dummy_py, "w"):
139+
pass
71140

72141
def remove_dummy(trial=0):
73142
if dummy in sys.modules:
74143
if verbose:
75144
dprint("removing dummy package")
76-
if os.path.exists(dummy_py):
145+
try:
77146
os.unlink(dummy_py)
147+
except FileNotFoundError:
148+
pass
78149
after_remove_dummy()
79150
elif trial < 300:
80151
threading.Timer(0.1, lambda: remove_dummy(trial + 1)).start()
81152
else:
82-
if os.path.exists(dummy_py):
153+
try:
83154
os.unlink(dummy_py)
155+
except FileNotFoundError:
156+
pass
84157

85158
condition = threading.Condition()
86159

@@ -112,8 +185,8 @@ def reload_missing(modules, verbose):
112185

113186
def reload_plugin(pkg_name):
114187
pkg_path = os.path.join(os.path.realpath(sublime.packages_path()), pkg_name)
115-
plugins = [pkg_name + "." + os.path.splitext(f)[0]
116-
for f in os.listdir(pkg_path) if f.endswith(".py")]
188+
plugins = [pkg_name + "." + os.path.splitext(file_path)[0]
189+
for file_path in os.listdir(pkg_path) if file_path.endswith(".py")]
117190
for plugin in plugins:
118191
sublime_plugin.reload_plugin(plugin)
119192

0 commit comments

Comments
 (0)