Coding style.

- Since there is only one display, appearance_changed_event can be
  a module level global in fill3.
- Remove all the appearance_changed_event plumbing.
This commit is contained in:
Andrew Hamilton 2021-11-29 15:57:16 +10:00
parent 71b9da128b
commit 4150a9a250
5 changed files with 55 additions and 76 deletions

View file

@ -340,7 +340,7 @@ class Summary:
if y >= len(self._entries):
self._cursor_position = (x, len(self._entries) - 1)
async def sync_with_filesystem(self, appearance_changed_event, log=None):
async def sync_with_filesystem(self, log=None):
start_time = time.time()
cache = {}
log.log_message("Started loading summary…")
@ -350,7 +350,7 @@ class Summary:
await asyncio.sleep(0)
self.add_entry(entry)
if index % 1000 == 0:
appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
cache[entry.path] = entry.change_time
duration = time.time() - start_time
log.log_message(f"Finished loading summary. {round(duration, 2)} secs")
@ -371,7 +371,7 @@ class Summary:
entry.change_time = change_time
else:
self.on_file_added(path)
appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
for path in cache.keys() - all_paths:
await asyncio.sleep(0)
self.on_file_deleted(path)
@ -565,16 +565,10 @@ class Log:
_GREY_BOLD_STYLE = termstr.CharStyle(termstr.Color.grey_100, is_bold=True)
_GREEN_STYLE = termstr.CharStyle(termstr.Color.lime)
def __init__(self, appearance_changed_event):
self._appearance_changed_event = appearance_changed_event
def __init__(self):
self.lines = []
self._appearance = None
def __getstate__(self):
state = self.__dict__.copy()
state["_appearance_changed_event"] = None
return state
def log_message(self, message, timestamp=None, char_style=None):
if isinstance(message, list):
message = [part[1] if isinstance(part, tuple) else part for part in message]
@ -588,7 +582,7 @@ class Log:
return
self.lines.append(line)
self._appearance = None
self._appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
def log_command(self, message, timestamp=None):
self.log_message(message, char_style=Log._GREEN_STYLE)
@ -631,20 +625,20 @@ class Help:
def _exit_help(self):
self.screen._is_help_visible = False
def on_mouse_input(self, term_code, appearance_changed_event):
def on_mouse_input(self, term_code):
event = terminal.decode_mouse_input(term_code)
if event[1] == terminal.WHEEL_UP_MOUSE:
self.view.portal.scroll_up()
appearance_changed_event.set()
fiil3.APPEARANCE_CHANGED_EVENT.set()
elif event[1] == terminal.WHEEL_DOWN_MOUSE:
self.view.portal.scroll_down()
appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
def on_keyboard_input(self, term_code, appearance_changed_event):
def on_keyboard_input(self, term_code):
action = self.key_map.get(term_code) or self.key_map.get(term_code.lower())
if action is not None:
action()
appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
def appearance(self, dimensions):
return self.widget.appearance(dimensions)
@ -663,10 +657,9 @@ class Listing:
class Screen:
def __init__(self, summary, log, appearance_changed_event, main_loop):
def __init__(self, summary, log, main_loop):
self._summary = summary
self._log = log
self._appearance_changed_event = appearance_changed_event
self._main_loop = main_loop
self._is_summary_focused = True
self.workers = None
@ -679,7 +672,6 @@ class Screen:
def __getstate__(self):
state = self.__dict__.copy()
state["_appearance_changed_event"] = None
state["_main_loop"] = None
state["workers"] = None
return state
@ -690,8 +682,7 @@ class Screen:
worker_ = worker.Worker(is_being_tested, compression)
workers.append(worker_)
future = worker_.job_runner(self, self._summary, self._log,
self._summary._jobs_added_event,
self._appearance_changed_event)
self._summary._jobs_added_event)
worker_.future = future
self.workers = workers
@ -886,7 +877,7 @@ class Screen:
def on_mouse_input(self, term_code):
if self._is_help_visible:
self._help_widget.on_mouse_input(term_code, self._appearance_changed_event)
self._help_widget.on_mouse_input(term_code)
return
event = terminal.decode_mouse_input(term_code)
if event[0] not in [terminal.PRESS_MOUSE, terminal.DRAG_MOUSE]:
@ -912,16 +903,16 @@ class Screen:
self._select_entry_at_position(
x, y, view_width, view_height)
self._last_mouse_position = x, y
self._appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
def on_keyboard_input(self, term_code):
if self._is_help_visible:
self._help_widget.on_keyboard_input(term_code, self._appearance_changed_event)
self._help_widget.on_keyboard_input(term_code)
return
action = Screen._KEY_MAP.get(term_code) or Screen._KEY_MAP.get(term_code.lower())
if action is not None:
action(self)
self._appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
def _fix_listing(self):
widget = self._summary.get_selection()
@ -999,29 +990,27 @@ def setup_inotify(root_path, loop, on_filesystem_event, exclude_filter):
return pyinotify.AsyncioNotifier(watch_manager, loop, callback=lambda notifier: None)
def load_state(pickle_path, jobs_added_event, appearance_changed_event, root_path, loop):
def load_state(pickle_path, jobs_added_event, root_path, loop):
is_first_run = True
try:
with gzip.open(pickle_path, "rb") as file_:
screen = pickle.load(file_)
except (FileNotFoundError, AttributeError):
summary = Summary(root_path, jobs_added_event)
log = Log(appearance_changed_event)
screen = Screen(summary, log, appearance_changed_event, loop)
log = Log()
screen = Screen(summary, log, loop)
else:
is_first_run = False
screen._appearance_changed_event = appearance_changed_event
screen._main_loop = loop
summary = screen._summary
summary._jobs_added_event = jobs_added_event
summary._root_path = root_path
summary.clear_running()
log = screen._log
log._appearance_changed_event = appearance_changed_event
return summary, screen, log, is_first_run
def on_filesystem_event(event, summary, root_path, appearance_changed_event):
def on_filesystem_event(event, summary, root_path):
path = list(fix_paths(root_path, [event.pathname]))[0]
if is_path_excluded(path[2:]):
return
@ -1038,7 +1027,7 @@ def on_filesystem_event(event, summary, root_path, appearance_changed_event):
except Exception:
tools.log_error()
raise KeyboardInterrupt
appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
def main(root_path, loop, worker_count=None, editor_command=None, theme=None,
@ -1052,15 +1041,13 @@ def main(root_path, loop, worker_count=None, editor_command=None, theme=None,
os.environ["PYGMENT_STYLE"] = theme
pickle_path = os.path.join(tools.CACHE_PATH, "summary.pickle")
jobs_added_event = asyncio.Event()
appearance_changed_event = asyncio.Event()
summary, screen, log, is_first_run = load_state(pickle_path, jobs_added_event,
appearance_changed_event, root_path, loop)
summary, screen, log, is_first_run = load_state(pickle_path, jobs_added_event, root_path, loop)
screen.editor_command = editor_command
log.log_message("Program started.")
jobs_added_event.set()
def callback(event):
on_filesystem_event(event, summary, root_path, appearance_changed_event)
on_filesystem_event(event, summary, root_path)
notifier = setup_inotify(root_path, loop, callback, is_path_excluded)
try:
log.log_message(f"Starting workers ({worker_count}) …")
@ -1071,11 +1058,11 @@ def main(root_path, loop, worker_count=None, editor_command=None, theme=None,
time.sleep(0.05)
screen.stop_workers()
loop.stop()
loop.create_task(summary.sync_with_filesystem(appearance_changed_event, log))
loop.create_task(summary.sync_with_filesystem(log))
for worker_ in screen.workers:
loop.create_task(worker_.future)
if sys.stdout.isatty():
with fill3.context(loop, appearance_changed_event, screen, exit_loop=exit_loop):
with fill3.context(loop, screen, exit_loop=exit_loop):
loop.run_forever()
log.log_message("Program stopped.")
else:

View file

@ -559,18 +559,18 @@ class Result:
def is_completed(self):
return self.status in Result.COMPLETED_STATUSES
async def run(self, log, appearance_changed_event, runner):
async def run(self, log, runner):
tool_name = tool_name_colored(self.tool, self.path)
path = path_colored(self.path)
log.log_message(["Running ", tool_name, " on ", path, ""])
self.set_status(Status.running)
appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
start_time = time.time()
new_status = await runner.run_tool(self.path, self.tool)
Result.result.fget.evict(self)
end_time = time.time()
self.set_status(new_status)
appearance_changed_event.set()
fill3.APPEARANCE_CHANGED_EVENT.set()
log.log_message(["Finished running ", tool_name, " on ", path, ". ",
STATUS_TO_TERMSTR[new_status], f" {round(end_time - start_time, 2)} secs"])

View file

@ -45,7 +45,7 @@ class Worker:
break
return tools.Status(int(data))
async def job_runner(self, screen, summary, log, jobs_added_event, appearance_changed_event):
async def job_runner(self, screen, summary, log, jobs_added_event):
await self.create_process()
while True:
await jobs_added_event.wait()
@ -55,7 +55,7 @@ class Worker:
except StopAsyncIteration:
self.result = None
break
await self.result.run(log, appearance_changed_event, self)
await self.result.run(log, self)
self.result.compression = self.compression
Worker.unsaved_jobs_total += 1
if Worker.unsaved_jobs_total == 5000 and summary.is_loaded:

View file

@ -50,11 +50,9 @@ class ScreenWidgetTestCase(unittest.TestCase):
foo_path = os.path.join(project_dir, "foo.py")
_touch(foo_path)
jobs_added_event = asyncio.Event()
appearance_changed_event = asyncio.Event()
summary = __main__.Summary(project_dir, jobs_added_event)
log = __main__.Log(appearance_changed_event)
self.main_widget = __main__.Screen(
summary, log, appearance_changed_event, _MockMainLoop())
log = __main__.Log()
self.main_widget = __main__.Screen(summary, log, _MockMainLoop())
def tearDown(self):
shutil.rmtree(self.temp_dir)
@ -123,20 +121,17 @@ class SummarySyncWithFilesystemTestCase(unittest.TestCase):
self.bar_path = os.path.join(self.temp_dir, "bar.md")
self.zoo_path = os.path.join(self.temp_dir, "zoo.html")
self.jobs_added_event = asyncio.Event()
self.appearance_changed_event = asyncio.Event()
self.summary = __main__.Summary(self.temp_dir, self.jobs_added_event)
self.loop = asyncio.new_event_loop()
def callback(event):
__main__.on_filesystem_event(event, self.summary, self.temp_dir,
self.appearance_changed_event)
__main__.on_filesystem_event(event, self.summary, self.temp_dir)
__main__.setup_inotify(self.temp_dir, self.loop, callback,
__main__.is_path_excluded)
_touch(self.foo_path)
_touch(self.bar_path)
self.log = __main__.Log(self.appearance_changed_event)
self.loop.run_until_complete(self.summary.sync_with_filesystem(
self.appearance_changed_event, self.log))
self.log = __main__.Log()
self.loop.run_until_complete(self.summary.sync_with_filesystem(self.log))
self.jobs_added_event.clear()
def tearDown(self):
@ -188,9 +183,8 @@ class SummarySyncWithFilesystemTestCase(unittest.TestCase):
baz_path = os.path.join(self.temp_dir, "baz")
os.symlink(self.foo_path, baz_path)
os.link(self.foo_path, self.zoo_path)
log = __main__.Log(self.appearance_changed_event)
self.loop.run_until_complete(self.summary.sync_with_filesystem(
self.appearance_changed_event, log))
log = __main__.Log()
self.loop.run_until_complete(self.summary.sync_with_filesystem(log))
self._assert_paths(["./bar.md", "./baz", "./foo", "./zoo.html"])
self.assertTrue(id(self.summary._entries[1]) != # baz
id(self.summary._entries[2])) # foo

View file

@ -406,30 +406,31 @@ class Fixed:
##########################
_last_appearance = []
APPEARANCE_CHANGED_EVENT = asyncio.Event()
_LAST_APPEARANCE = []
def draw_screen(widget):
global _last_appearance
global _LAST_APPEARANCE
appearance = widget.appearance(os.get_terminal_size())
print(terminal.move(0, 0), *appearance, sep="", end="", flush=True)
_last_appearance = appearance
_LAST_APPEARANCE = appearance
def patch_screen(widget):
global _last_appearance
global _LAST_APPEARANCE
appearance = widget.appearance(os.get_terminal_size())
zip_func = (itertools.zip_longest if len(appearance) > len(_last_appearance) else zip)
zip_func = (itertools.zip_longest if len(appearance) > len(_LAST_APPEARANCE) else zip)
changed_lines = (str(terminal.move(0, row_index)) + line for row_index, (line, old_line)
in enumerate(zip_func(appearance, _last_appearance)) if line != old_line)
in enumerate(zip_func(appearance, _LAST_APPEARANCE)) if line != old_line)
print(*changed_lines, sep="", end="", flush=True)
_last_appearance = appearance
_LAST_APPEARANCE = appearance
async def update_screen(screen_widget, appearance_changed_event):
async def update_screen(screen_widget):
while True:
await appearance_changed_event.wait()
appearance_changed_event.clear()
await APPEARANCE_CHANGED_EVENT.wait()
APPEARANCE_CHANGED_EVENT.clear()
patch_screen(screen_widget)
await asyncio.sleep(0.01)
@ -453,8 +454,8 @@ def signal_handler(loop, signal_, func):
@contextlib.contextmanager
def context(loop, appearance_changed_event, screen_widget, exit_loop=None):
appearance_changed_event.set()
def context(loop, screen_widget, exit_loop=None):
APPEARANCE_CHANGED_EVENT.set()
if exit_loop is None:
exit_loop = loop.stop
with (signal_handler(loop, signal.SIGWINCH, lambda: draw_screen(screen_widget)),
@ -462,7 +463,7 @@ def context(loop, appearance_changed_event, screen_widget, exit_loop=None):
signal_handler(loop, signal.SIGTERM, exit_loop), terminal.alternate_buffer(),
terminal.interactive(), terminal.mouse_tracking()):
update_task = loop.create_task(
update_screen(screen_widget, appearance_changed_event))
update_screen(screen_widget))
try:
loop.add_reader(sys.stdin, on_terminal_input, screen_widget)
try:
@ -478,8 +479,7 @@ def context(loop, appearance_changed_event, screen_widget, exit_loop=None):
class _Screen:
def __init__(self, appearance_changed_event):
self._appearance_changed_event = appearance_changed_event
def __init__(self):
self.content = Filler(Text("Hello World"))
def appearance(self, dimensions):
@ -490,19 +490,17 @@ class _Screen:
asyncio.get_event_loop().stop()
else:
self.content = Filler(Text(repr(term_code)))
self._appearance_changed_event.set()
APPEARANCE_CHANGED_EVENT.set()
def on_mouse_input(self, term_code):
mouse_code = terminal.decode_mouse_input(term_code)
self.content = Filler(Text(repr(term_code) + " " + repr(mouse_code)))
self._appearance_changed_event.set()
APPEARANCE_CHANGED_EVENT.set()
def _main():
loop = asyncio.get_event_loop()
appearance_changed_event = asyncio.Event()
screen = _Screen(appearance_changed_event)
with context(loop, appearance_changed_event, screen):
with context(loop, _Screen()):
loop.run_forever()