Coding style.
Moved the Result class into tools, which simplifies worker.py and makes the circular import problem go away.
This commit is contained in:
parent
cc95534bd7
commit
2373e78cbd
5 changed files with 151 additions and 149 deletions
117
tools.py
117
tools.py
|
|
@ -8,6 +8,7 @@ import contextlib
|
||||||
import dis
|
import dis
|
||||||
import enum
|
import enum
|
||||||
import functools
|
import functools
|
||||||
|
import gzip
|
||||||
import hashlib
|
import hashlib
|
||||||
import io
|
import io
|
||||||
import math
|
import math
|
||||||
|
|
@ -31,6 +32,9 @@ import gut
|
||||||
import termstr
|
import termstr
|
||||||
|
|
||||||
|
|
||||||
|
_CACHE_PATH = ".vigil"
|
||||||
|
|
||||||
|
|
||||||
class Status(enum.IntEnum):
|
class Status(enum.IntEnum):
|
||||||
|
|
||||||
ok = 1
|
ok = 1
|
||||||
|
|
@ -546,6 +550,119 @@ def php5_syntax(path):
|
||||||
php5_syntax.dependencies = {"php5"}
|
php5_syntax.dependencies = {"php5"}
|
||||||
|
|
||||||
|
|
||||||
|
def lru_cache_with_eviction(maxsize=128, typed=False):
|
||||||
|
versions = {}
|
||||||
|
make_key = functools._make_key
|
||||||
|
|
||||||
|
def evict(*args, **kwds):
|
||||||
|
key = make_key(args, kwds, typed)
|
||||||
|
if key in versions:
|
||||||
|
versions[key] += 1
|
||||||
|
|
||||||
|
def decorating_function(user_function):
|
||||||
|
|
||||||
|
def remove_version(*args, **kwds):
|
||||||
|
return user_function(*args[1:], **kwds)
|
||||||
|
new_func = functools.lru_cache(maxsize=maxsize, typed=typed)(
|
||||||
|
remove_version)
|
||||||
|
|
||||||
|
def add_version(*args, **kwds):
|
||||||
|
key = make_key(args, kwds, typed)
|
||||||
|
return new_func(*((versions.setdefault(key, 0),) + args), **kwds)
|
||||||
|
add_version.versions = versions
|
||||||
|
add_version.cache_info = new_func.cache_info
|
||||||
|
add_version.evict = evict
|
||||||
|
return functools.update_wrapper(add_version, user_function)
|
||||||
|
return decorating_function
|
||||||
|
|
||||||
|
|
||||||
|
def dump_pickle_safe(object_, path, protocol=pickle.HIGHEST_PROTOCOL,
|
||||||
|
open=open):
|
||||||
|
tmp_path = path + ".tmp"
|
||||||
|
try:
|
||||||
|
with open(tmp_path, "wb") as file_:
|
||||||
|
pickle.dump(object_, file_, protocol=protocol)
|
||||||
|
except (OSError, KeyboardInterrupt):
|
||||||
|
os.remove(tmp_path)
|
||||||
|
else:
|
||||||
|
os.rename(tmp_path, path)
|
||||||
|
|
||||||
|
|
||||||
|
def status_to_str(status, is_status_simple):
|
||||||
|
if isinstance(status, enum.Enum):
|
||||||
|
dict_ = (_STATUS_TO_TERMSTR_SIMPLE if is_status_simple
|
||||||
|
else _STATUS_TO_TERMSTR)
|
||||||
|
return dict_[status]
|
||||||
|
else:
|
||||||
|
return status
|
||||||
|
|
||||||
|
|
||||||
|
class Result:
|
||||||
|
|
||||||
|
def __init__(self, path, tool, is_stored_compressed=True):
|
||||||
|
self.path = path
|
||||||
|
self.tool = tool
|
||||||
|
self._open_func = gzip.open if is_stored_compressed else open
|
||||||
|
self.pickle_path = os.path.join(_CACHE_PATH,
|
||||||
|
path + "-" + tool.__name__)
|
||||||
|
self.scroll_position = (0, 0)
|
||||||
|
self.is_completed = False
|
||||||
|
self.is_placeholder = True
|
||||||
|
self.status = Status.pending
|
||||||
|
|
||||||
|
@property
|
||||||
|
@lru_cache_with_eviction(maxsize=50)
|
||||||
|
def result(self):
|
||||||
|
unknown_label = fill3.Text("?")
|
||||||
|
if self.is_placeholder:
|
||||||
|
return unknown_label
|
||||||
|
try:
|
||||||
|
with self._open_func(self.pickle_path, "rb") as pickle_file:
|
||||||
|
return pickle.load(pickle_file)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return unknown_label
|
||||||
|
|
||||||
|
@result.setter
|
||||||
|
def result(self, value):
|
||||||
|
os.makedirs(os.path.dirname(self.pickle_path), exist_ok=True)
|
||||||
|
dump_pickle_safe(value, self.pickle_path, open=self._open_func)
|
||||||
|
Result.result.fget.evict(self)
|
||||||
|
|
||||||
|
def set_status(self, status):
|
||||||
|
self.status = status
|
||||||
|
self.entry.appearance_cache = None
|
||||||
|
|
||||||
|
def run(self, log, appearance_changed_event, worker, runner):
|
||||||
|
self.is_placeholder = False
|
||||||
|
tool_name = _tool_name_colored(self.tool, self.path)
|
||||||
|
path_colored = _path_colored(self.path)
|
||||||
|
log.log_message(["Running ", tool_name, " on ", path_colored, "..."])
|
||||||
|
self.set_status(Status.running)
|
||||||
|
if runner.is_already_paused:
|
||||||
|
runner.is_already_paused = False
|
||||||
|
runner.pause()
|
||||||
|
appearance_changed_event.set()
|
||||||
|
start_time = time.time()
|
||||||
|
new_status = worker.run_tool(self.path, self.tool)
|
||||||
|
Result.result.fget.evict(self)
|
||||||
|
end_time = time.time()
|
||||||
|
self.set_status(new_status)
|
||||||
|
appearance_changed_event.set()
|
||||||
|
self.is_completed = True
|
||||||
|
log.log_message(
|
||||||
|
["Finished running ", tool_name, " on ", path_colored, ". ",
|
||||||
|
status_to_str(new_status, self.entry.summary.is_status_simple),
|
||||||
|
" %s secs" % round(end_time - start_time, 2)])
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.is_placeholder = True
|
||||||
|
self.set_status(Status.pending)
|
||||||
|
|
||||||
|
def appearance_min(self):
|
||||||
|
return [status_to_str(self.status,
|
||||||
|
self.entry.summary.is_status_simple)]
|
||||||
|
|
||||||
|
|
||||||
def generic_tools():
|
def generic_tools():
|
||||||
return [contents, metadata]
|
return [contents, metadata]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -188,5 +188,28 @@ class ToolsTestCase(unittest.TestCase):
|
||||||
self._test_tool(tools.php5_syntax, [("root.php", tools.Status.ok)])
|
self._test_tool(tools.php5_syntax, [("root.php", tools.Status.ok)])
|
||||||
|
|
||||||
|
|
||||||
|
class LruCacheWithEvictionTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def _assert_cache(self, func, hits, misses, current_size):
|
||||||
|
cache_info = func.cache_info()
|
||||||
|
self.assertEqual(cache_info.hits, hits)
|
||||||
|
self.assertEqual(cache_info.misses, misses)
|
||||||
|
self.assertEqual(cache_info.currsize, current_size)
|
||||||
|
|
||||||
|
def test_lru_cache_with_eviction(self):
|
||||||
|
@tools.lru_cache_with_eviction()
|
||||||
|
def a(foo):
|
||||||
|
return foo
|
||||||
|
self._assert_cache(a, 0, 0, 0)
|
||||||
|
self.assertEqual(a(1), 1)
|
||||||
|
self._assert_cache(a, 0, 1, 1)
|
||||||
|
a(1)
|
||||||
|
self._assert_cache(a, 1, 1, 1)
|
||||||
|
a.evict(1)
|
||||||
|
self._assert_cache(a, 1, 1, 1)
|
||||||
|
a(1)
|
||||||
|
self._assert_cache(a, 1, 2, 2)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
golden.main()
|
golden.main()
|
||||||
|
|
|
||||||
132
vigil
132
vigil
|
|
@ -77,6 +77,7 @@ import sandbox_fs
|
||||||
import terminal
|
import terminal
|
||||||
import termstr
|
import termstr
|
||||||
import tools
|
import tools
|
||||||
|
import worker
|
||||||
|
|
||||||
|
|
||||||
_LOG_PATH = os.path.join(os.getcwd(), "vigil.log")
|
_LOG_PATH = os.path.join(os.getcwd(), "vigil.log")
|
||||||
|
|
@ -88,123 +89,7 @@ def _log_error(message=None):
|
||||||
log_file.write(message)
|
log_file.write(message)
|
||||||
|
|
||||||
|
|
||||||
_CACHE_PATH = ".vigil"
|
_CACHE_PATH = tools._CACHE_PATH
|
||||||
|
|
||||||
|
|
||||||
def lru_cache_with_eviction(maxsize=128, typed=False):
|
|
||||||
versions = {}
|
|
||||||
make_key = functools._make_key
|
|
||||||
|
|
||||||
def evict(*args, **kwds):
|
|
||||||
key = make_key(args, kwds, typed)
|
|
||||||
if key in versions:
|
|
||||||
versions[key] += 1
|
|
||||||
|
|
||||||
def decorating_function(user_function):
|
|
||||||
|
|
||||||
def remove_version(*args, **kwds):
|
|
||||||
return user_function(*args[1:], **kwds)
|
|
||||||
new_func = functools.lru_cache(maxsize=maxsize, typed=typed)(
|
|
||||||
remove_version)
|
|
||||||
|
|
||||||
def add_version(*args, **kwds):
|
|
||||||
key = make_key(args, kwds, typed)
|
|
||||||
return new_func(*((versions.setdefault(key, 0),) + args), **kwds)
|
|
||||||
add_version.versions = versions
|
|
||||||
add_version.cache_info = new_func.cache_info
|
|
||||||
add_version.evict = evict
|
|
||||||
return functools.update_wrapper(add_version, user_function)
|
|
||||||
return decorating_function
|
|
||||||
|
|
||||||
|
|
||||||
def dump_pickle_safe(object_, path, protocol=pickle.HIGHEST_PROTOCOL,
|
|
||||||
open=open):
|
|
||||||
tmp_path = path + ".tmp"
|
|
||||||
try:
|
|
||||||
with open(tmp_path, "wb") as file_:
|
|
||||||
pickle.dump(object_, file_, protocol=protocol)
|
|
||||||
except (OSError, KeyboardInterrupt):
|
|
||||||
os.remove(tmp_path)
|
|
||||||
else:
|
|
||||||
os.rename(tmp_path, path)
|
|
||||||
|
|
||||||
|
|
||||||
def status_to_str(status, is_status_simple):
|
|
||||||
if isinstance(status, enum.Enum):
|
|
||||||
dict_ = (tools._STATUS_TO_TERMSTR_SIMPLE if is_status_simple
|
|
||||||
else tools._STATUS_TO_TERMSTR)
|
|
||||||
return dict_[status]
|
|
||||||
else:
|
|
||||||
return status
|
|
||||||
|
|
||||||
|
|
||||||
class Result:
|
|
||||||
|
|
||||||
def __init__(self, path, tool, is_stored_compressed=True):
|
|
||||||
self.path = path
|
|
||||||
self.tool = tool
|
|
||||||
self._open_func = gzip.open if is_stored_compressed else open
|
|
||||||
self.pickle_path = os.path.join(_CACHE_PATH,
|
|
||||||
path + "-" + tool.__name__)
|
|
||||||
self.scroll_position = (0, 0)
|
|
||||||
self.is_completed = False
|
|
||||||
self.is_placeholder = True
|
|
||||||
self.status = tools.Status.pending
|
|
||||||
|
|
||||||
@property
|
|
||||||
@lru_cache_with_eviction(maxsize=50)
|
|
||||||
def result(self):
|
|
||||||
unknown_label = fill3.Text("?")
|
|
||||||
if self.is_placeholder:
|
|
||||||
return unknown_label
|
|
||||||
try:
|
|
||||||
with self._open_func(self.pickle_path, "rb") as pickle_file:
|
|
||||||
return pickle.load(pickle_file)
|
|
||||||
except FileNotFoundError:
|
|
||||||
return unknown_label
|
|
||||||
|
|
||||||
@result.setter
|
|
||||||
def result(self, value):
|
|
||||||
os.makedirs(os.path.dirname(self.pickle_path), exist_ok=True)
|
|
||||||
dump_pickle_safe(value, self.pickle_path, open=self._open_func)
|
|
||||||
Result.result.fget.evict(self)
|
|
||||||
|
|
||||||
def set_status(self, status):
|
|
||||||
self.status = status
|
|
||||||
self.entry.appearance_cache = None
|
|
||||||
|
|
||||||
def run(self, log, appearance_changed_event, worker, runner):
|
|
||||||
self.is_placeholder = False
|
|
||||||
tool_name = tools._tool_name_colored(self.tool, self.path)
|
|
||||||
path_colored = tools._path_colored(self.path)
|
|
||||||
log.log_message(["Running ", tool_name, " on ", path_colored, "..."])
|
|
||||||
self.set_status(tools.Status.running)
|
|
||||||
if runner.is_already_paused:
|
|
||||||
runner.is_already_paused = False
|
|
||||||
runner.pause()
|
|
||||||
appearance_changed_event.set()
|
|
||||||
start_time = time.time()
|
|
||||||
new_status = worker.run_tool(self.path, self.tool)
|
|
||||||
Result.result.fget.evict(self)
|
|
||||||
end_time = time.time()
|
|
||||||
self.set_status(new_status)
|
|
||||||
appearance_changed_event.set()
|
|
||||||
self.is_completed = True
|
|
||||||
log.log_message(
|
|
||||||
["Finished running ", tool_name, " on ", path_colored, ". ",
|
|
||||||
status_to_str(new_status, self.entry.summary.is_status_simple),
|
|
||||||
" %s secs" % round(end_time - start_time, 2)])
|
|
||||||
|
|
||||||
def reset(self):
|
|
||||||
self.is_placeholder = True
|
|
||||||
self.set_status(tools.Status.pending)
|
|
||||||
|
|
||||||
def appearance_min(self):
|
|
||||||
return [status_to_str(self.status,
|
|
||||||
self.entry.summary.is_status_simple)]
|
|
||||||
|
|
||||||
|
|
||||||
import worker # Avoid a circular import. worker.py needs the Result class.
|
|
||||||
|
|
||||||
|
|
||||||
def reverse_style(style):
|
def reverse_style(style):
|
||||||
|
|
@ -383,7 +268,7 @@ class Summary:
|
||||||
result = self._cache[file_key][tool_key]
|
result = self._cache[file_key][tool_key]
|
||||||
result.tool = tool
|
result.tool = tool
|
||||||
else:
|
else:
|
||||||
result = Result(path, tool)
|
result = tools.Result(path, tool)
|
||||||
jobs_added = True
|
jobs_added = True
|
||||||
all_results.add(result)
|
all_results.add(result)
|
||||||
if result.is_completed:
|
if result.is_completed:
|
||||||
|
|
@ -609,9 +494,10 @@ def _highlight_chars(str_, style, marker="*"):
|
||||||
@functools.lru_cache()
|
@functools.lru_cache()
|
||||||
def _get_help_text(is_status_simple=True):
|
def _get_help_text(is_status_simple=True):
|
||||||
usage = _highlight_chars(__doc__, Log.GREEN_STYLE)
|
usage = _highlight_chars(__doc__, Log.GREEN_STYLE)
|
||||||
return fill3.join("\n", [usage, "Statuses:"] +
|
return fill3.join(
|
||||||
[" " + status_to_str(status, is_status_simple) + " " +
|
"\n", [usage, "Statuses:"] +
|
||||||
meaning for status, meaning in tools.STATUS_MEANINGS])
|
[" " + tools.status_to_str(status, is_status_simple) + " " + meaning
|
||||||
|
for status, meaning in tools.STATUS_MEANINGS])
|
||||||
|
|
||||||
|
|
||||||
def _make_key_map(key_data):
|
def _make_key_map(key_data):
|
||||||
|
|
@ -907,7 +793,7 @@ class Screen:
|
||||||
tool_name = tools._tool_name_colored(widget.tool, widget.path)
|
tool_name = tools._tool_name_colored(widget.tool, widget.path)
|
||||||
self._listing.title = (
|
self._listing.title = (
|
||||||
tools._path_colored(widget.path) + " ─── " + tool_name + " " +
|
tools._path_colored(widget.path) + " ─── " + tool_name + " " +
|
||||||
status_to_str(widget.status, self._summary.is_status_simple))
|
tools.status_to_str(widget.status, self._summary.is_status_simple))
|
||||||
incomplete = self._summary.result_total - self._summary.completed_total
|
incomplete = self._summary.result_total - self._summary.completed_total
|
||||||
progress_bar_size = max(0, width * incomplete //
|
progress_bar_size = max(0, width * incomplete //
|
||||||
self._summary.result_total)
|
self._summary.result_total)
|
||||||
|
|
@ -1122,7 +1008,7 @@ def main(root_path, worker_count=multiprocessing.cpu_count()*2,
|
||||||
screen._main_loop, screen.runners,
|
screen._main_loop, screen.runners,
|
||||||
log._appearance_changed_event) = [None] * 7
|
log._appearance_changed_event) = [None] * 7
|
||||||
open_compressed = functools.partial(gzip.open, compresslevel=1)
|
open_compressed = functools.partial(gzip.open, compresslevel=1)
|
||||||
dump_pickle_safe(screen, pickle_path, open=open_compressed)
|
tools.dump_pickle_safe(screen, pickle_path, open=open_compressed)
|
||||||
finally:
|
finally:
|
||||||
if is_sandboxed:
|
if is_sandboxed:
|
||||||
sandbox.umount()
|
sandbox.umount()
|
||||||
|
|
|
||||||
|
|
@ -18,29 +18,6 @@ import golden
|
||||||
import vigil
|
import vigil
|
||||||
|
|
||||||
|
|
||||||
class LruCacheWithEvictionTestCase(unittest.TestCase):
|
|
||||||
|
|
||||||
def _assert_cache(self, func, hits, misses, current_size):
|
|
||||||
cache_info = func.cache_info()
|
|
||||||
self.assertEqual(cache_info.hits, hits)
|
|
||||||
self.assertEqual(cache_info.misses, misses)
|
|
||||||
self.assertEqual(cache_info.currsize, current_size)
|
|
||||||
|
|
||||||
def test_lru_cache_with_eviction(self):
|
|
||||||
@vigil.lru_cache_with_eviction()
|
|
||||||
def a(foo):
|
|
||||||
return foo
|
|
||||||
self._assert_cache(a, 0, 0, 0)
|
|
||||||
self.assertEqual(a(1), 1)
|
|
||||||
self._assert_cache(a, 0, 1, 1)
|
|
||||||
a(1)
|
|
||||||
self._assert_cache(a, 1, 1, 1)
|
|
||||||
a.evict(1)
|
|
||||||
self._assert_cache(a, 1, 1, 1)
|
|
||||||
a(1)
|
|
||||||
self._assert_cache(a, 1, 2, 2)
|
|
||||||
|
|
||||||
|
|
||||||
_DIMENSIONS = (100, 60)
|
_DIMENSIONS = (100, 60)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ import subprocess
|
||||||
import psutil
|
import psutil
|
||||||
|
|
||||||
import tools
|
import tools
|
||||||
import vigil
|
|
||||||
|
|
||||||
|
|
||||||
def make_process_nicest(pid):
|
def make_process_nicest(pid):
|
||||||
|
|
@ -28,7 +27,7 @@ class Worker:
|
||||||
[__file__], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
[__file__], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE)
|
stderr=subprocess.PIPE)
|
||||||
else:
|
else:
|
||||||
cache_path = os.path.join(os.getcwd(), vigil._CACHE_PATH)
|
cache_path = os.path.join(os.getcwd(), tools._CACHE_PATH)
|
||||||
self.cache_mount = sandbox.mount_point + cache_path
|
self.cache_mount = sandbox.mount_point + cache_path
|
||||||
subprocess.check_call(["sudo", "mount", "--bind", cache_path,
|
subprocess.check_call(["sudo", "mount", "--bind", cache_path,
|
||||||
self.cache_mount])
|
self.cache_mount])
|
||||||
|
|
@ -54,7 +53,7 @@ def main():
|
||||||
while True:
|
while True:
|
||||||
tool_name, path = input(), input()
|
tool_name, path = input(), input()
|
||||||
tool = getattr(tools, tool_name)
|
tool = getattr(tools, tool_name)
|
||||||
result = vigil.Result(path, tool)
|
result = tools.Result(path, tool)
|
||||||
status, result.result = tools.run_tool_no_error(path, tool)
|
status, result.result = tools.run_tool_no_error(path, tool)
|
||||||
print(status.value, flush=True)
|
print(status.value, flush=True)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue