use watchfiles module for theme watcher

This commit is contained in:
Izalia Mae 2022-09-24 08:36:53 -04:00
parent 9ada9ef2cf
commit 2e62ee6517
6 changed files with 122 additions and 100 deletions

View file

@ -443,7 +443,10 @@ class Window(BuilderBase, Gtk.ApplicationWindow):
self.hide()
self.app.password.stop()
if self.app.password:
self.app.password.stop()
self.themes.watcher_stop()
self.tabs_save()
with self.app.db.session as s:

View file

@ -137,6 +137,13 @@ class StylePriority(IntEnum):
USER = Gtk.STYLE_PROVIDER_PRIORITY_USER #800
@register
class WatcherChangeType(IntEnum):
CREATE = 1
MODIFY = 2
DELETE = 3
@register
class WebviewAccelPolicy(IntEnum):
DEMAND = WebKit2.HardwareAccelerationPolicy.ON_DEMAND

View file

@ -1,5 +1,7 @@
import hashlib
import threading
import traceback
import watchfiles
from configparser import ConfigParser
from izzylib.logging import LogLevel
@ -8,12 +10,10 @@ from izzylib.object_base import ObjectBase
from izzylib.path import Path
from izzylib.url import Url
from izzylib_http_async import Template
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
from . import var, __version__ as version, __software__ as swname
from .base import ComponentBase
from .enums import StylePriority
from .enums import StylePriority, WatcherChangeType
from .functions import run_in_gui_thread
@ -204,14 +204,8 @@ class Themes(ComponentBase, ObjectBase):
def watcher_start(self):
if self.watcher:
return
self.watcher = Observer()
self.watcher.schedule(MainWatchHandler(self), self.app.path.resources, recursive=False)
self.watcher.schedule(SystemWatchHandler(self), self.systempath, recursive=True)
self.watcher.schedule(UserWatchHandler(self), self.userpath, recursive=True)
if not self.watcher:
self.watcher = ThemeWatcher(self)
self.watcher.start()
@ -221,10 +215,6 @@ class Themes(ComponentBase, ObjectBase):
return
self.watcher.stop()
self.watcher.join()
self.watcher = None
self.current = None
class Theme(ObjectBase):
@ -310,10 +300,12 @@ class Theme(ObjectBase):
logging.error(f'[{self.name}] {error.message} {section.get_start_line()}')
class WatcherBase(ComponentBase, FileSystemEventHandler):
class ThemeWatcher(ComponentBase):
def __init__(self, themes):
FileSystemEventHandler.__init__(self)
self.themes = themes
self._stop = threading.Event()
self._stopped = threading.Event()
self._stopped.set()
@property
@ -331,84 +323,103 @@ class WatcherBase(ComponentBase, FileSystemEventHandler):
return self.themes.userpath
def start(self):
if not self._stopped.is_set():
return
thread = threading.Thread(target=self.handle_file_watcher)
thread.start()
logging.verbose('Started theme watcher')
def stop(self):
if self._stopped.is_set():
return
self._stop.set()
self._stopped.wait()
logging.verbose('Stopped theme watcher')
def handle_file_watcher(self):
self._stopped.clear()
watcher = watchfiles.watch(
self.path.resources,
self.systempath,
self.userpath,
recursive = True,
force_polling = True,
poll_delay_ms = 250,
stop_event = self._stop
)
for changes in watcher:
for change in changes:
try:
action = change[0]
path = Path(change[1])
except IndexError:
continue
try:
if path.startswith(self.app.path.resources):
if path.name == 'main.css':
run_in_gui_thread(self.themes.load_main)
elif any(map(path.startswith, [self.themes.systempath, self.themes.userpath])):
system = path.startswith(self.themes.systempath)
try: theme = self.themes.get_theme_by_file(path, system=systempath)
except KeyError: theme = None
if theme and action == WatcherChangeType.MODIFY:
run_in_gui_thread(self.handle_theme_modified, theme, path)
elif theme and action == WatcherChangeType.DELETE:
if path.name == 'manifest.ini':
run_in_gui_thread(self.handle_theme_deleted, theme, path)
else:
run_in_gui_thread(self.handle_theme_modified, theme, path)
elif action == WatcherChangeType.CREATE:
run_in_gui_thread(self.handle_theme_created, path)
except:
traceback.print_exc()
self._stopped.set()
self._stop.clear()
def handle_theme_created(self, path):
if not (path.name == 'manifest.ini' and self.userpath.join(path.parent.name).isdir()):
return
theme = Theme(path.parent)
self.themes.user[theme.hash] = theme
logging.verbose(f'Created new theme: {theme.name}')
def handle_theme_deleted(self, theme, path):
if self.themes.current == theme.hash:
self.themes.unset()
del self.themes.user[theme.hash]
logging.verbose(f'Deleted theme: {theme.name}')
def handle_theme_modified(self, theme, path):
try:
if path.name == 'manifest.ini':
theme.load_manifest()
logging.verbose(f'Reloaded manifest for theme: {theme.name}')
elif path.suffix in ['css', 'svg', 'png', 'gif', 'jpg']:
theme.load()
logging.verbose(f'Reloaded theme: {theme.name}')
except:
traceback.print_exc()
class MainWatchHandler(WatcherBase):
def on_modified(self, event):
path = Path(event.src_path)
if path.name == 'main.css':
logging.verbose('Reloading main css')
try:
run_in_gui_thread(self.themes.load_main)
except:
traceback.print_exc()
class SystemWatchHandler(WatcherBase):
def on_modified(self, event):
path = Path(event.src_path)
try:
theme = self.themes.get_theme_by_file(path, system=True)
except:
traceback.print_exc()
return
run_in_gui_thread(self.handle_theme_modified, theme, path)
class UserWatchHandler(WatcherBase):
def on_modified(self, event):
path = Path(event.src_path)
try:
theme = self.themes.get_theme_by_file(path)
except KeyError:
return
self.handle_theme_modified(theme, path)
def on_deleted(self, event):
path = Path(event.src_path)
try:
theme = self.themes.get_theme_by_file(path)
except KeyError:
return
if path.name == 'manifest.ini':
if self.themes.current == theme.hash:
run_in_gui_thread(self.themes.unset)
theme.load_manifest()
logging.verbose(f'Reloaded manifest for theme: {theme.name}')
del self.themes.user[theme.hash]
logging.verbose(f'Deleted theme: {theme.name}')
def on_created(self, event):
path = Path(event.src_path)
if path.name == 'manifest.ini' and self.userpath.join(path.parent).isdir():
theme = Theme(path.parent)
self.themes.user[theme.hash] = theme
logging.verbose(f'Created new theme: {theme.name}')
else:
theme.load()
logging.verbose(f'Reloaded theme: {theme.name}')

View file

@ -86,8 +86,8 @@
"options": [],
"url": null
},
"watchdog": {
"version": "2.1.9",
"watchfiles": {
"version": "0.17.0",
"options": [],
"url": null
}

View file

@ -10,7 +10,8 @@ mastodon.py==1.5.1
objgraph==3.5.0
pillow==8.3.2
psutil==5.8.0
pycairo==1.21.0
pygobject==3.38.0
pysftp==0.2.9
secretstorage==3.3.2
watchdog==2.1.9
watchfiles==0.17.0

View file

@ -48,7 +48,7 @@ setup_requires =
pygobject==3.38.0
pysftp==0.2.9
secretstorage==3.3.2
watchdog==2.1.9
watchfiles==0.17.0
[options.entry_points]
console_scripts =