Skip to content

Commit 682a73f

Browse files
committed
feat: switched to using click for the CLI, fixed bugs
1 parent f6d26c7 commit 682a73f

File tree

5 files changed

+69
-60
lines changed

5 files changed

+69
-60
lines changed

aw_qt/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from aw_core.config import load_config
55
import json
66

7+
# NOTE: Updating this won't update the defaults for users, this is an issue with how aw_core.config works
78
default_settings = {
89
"autostart_modules": json.dumps(
910
["aw-server", "aw-watcher-afk", "aw-watcher-window",]

aw_qt/main.py

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,40 @@
11
import sys
22
import logging
33
import argparse
4-
from typing import List
4+
import click
5+
from typing import List, Optional
56
from typing_extensions import TypedDict
67

78
from aw_core.log import setup_logging
89

910
from .manager import Manager
1011
from . import trayicon
12+
from .config import AwQtSettings
1113

1214
logger = logging.getLogger(__name__)
1315

1416

15-
def main() -> None:
16-
args = parse_args()
17-
setup_logging("aw-qt", testing=args['testing'], verbose=args['testing'], log_file=True)
18-
19-
_manager = Manager(testing=args['testing'])
20-
_manager.autostart(args['autostart_modules'])
21-
22-
error_code = trayicon.run(_manager, testing=args['testing'])
17+
@click.command("aw-qt", help="A trayicon and service manager for ActivityWatch")
18+
@click.option(
19+
"--testing", is_flag=True, help="Run the trayicon and services in testing mode"
20+
)
21+
@click.option(
22+
"--autostart-modules",
23+
help="A comma-separated list of modules to autostart, or just `none` to not autostart anything.",
24+
)
25+
def main(testing: bool, autostart_modules: Optional[str]) -> None:
26+
config = AwQtSettings(testing=testing)
27+
_autostart_modules = (
28+
[m.strip() for m in autostart_modules.split(",") if m and m.lower() != "none"]
29+
if autostart_modules
30+
else config.autostart_modules
31+
)
32+
setup_logging("aw-qt", testing=testing, verbose=testing, log_file=True)
33+
34+
_manager = Manager(testing=testing)
35+
_manager.autostart(_autostart_modules)
36+
37+
error_code = trayicon.run(_manager, testing=testing)
2338
_manager.stop_all()
2439

2540
sys.exit(error_code)
26-
27-
28-
CommandLineArgs = TypedDict('CommandLineArgs', {'testing': bool, 'autostart_modules': List[str]}, total=False)
29-
30-
31-
def parse_args() -> CommandLineArgs:
32-
parser = argparse.ArgumentParser(prog="aw-qt", description='A trayicon and service manager for ActivityWatch')
33-
parser.add_argument('--testing', action='store_true',
34-
help='Run the trayicon and services in testing mode')
35-
parser.add_argument('--autostart-modules', dest='autostart_modules',
36-
type=lambda s: [m for m in s.split(',') if m and m.lower() != "none"],
37-
default=None,
38-
help='A comma-separated list of modules to autostart, or just `none` to not autostart anything')
39-
parsed_args = parser.parse_args()
40-
dict: CommandLineArgs = {'autostart_modules': parsed_args.autostart_modules, 'testing': parsed_args.testing}
41-
return dict

aw_qt/manager.py

Lines changed: 30 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import logging
66
import subprocess
77
import shutil
8-
from typing import Optional, List, Dict, Set
8+
from typing import Optional, List, Dict, Set, Tuple
99

1010
import aw_core
1111

@@ -35,7 +35,7 @@ def _is_system_module(name: str) -> bool:
3535
return shutil.which(name) is not None
3636

3737

38-
def _locate_executable(name: str) -> Optional[str]:
38+
def _locate_executable(name: str) -> Tuple[Optional[str], Optional[str]]:
3939
"""
4040
Will return the path to the executable if bundled,
4141
otherwise returns the name if it is available in PATH.
@@ -44,41 +44,38 @@ def _locate_executable(name: str) -> Optional[str]:
4444
"""
4545
exec_path = _locate_bundled_executable(name)
4646
if exec_path is not None: # Check if it exists in bundle
47-
return exec_path
47+
return exec_path, "bundle"
4848
elif _is_system_module(name): # Check if it's in PATH
49-
return name
49+
return name, "system"
5050
else:
5151
logger.warning(
5252
"Could not find module '{}' in installation directory or PATH".format(name)
5353
)
54-
return None
54+
return None, None
5555

5656

57-
def _discover_modules_in_directory(modules: List[str], search_path: str) -> None:
57+
def _discover_modules_in_directory(search_path: str) -> List[str]:
5858
"""Look for modules in given directory path and recursively in subdirs matching aw-*"""
59+
modules = []
5960
matches = glob(os.path.join(search_path, "aw-*"))
6061
for match in matches:
6162
if os.path.isfile(match) and os.access(match, os.X_OK):
6263
name = os.path.basename(match)
6364
modules.append(name)
6465
elif os.path.isdir(match) and os.access(match, os.X_OK):
65-
_discover_modules_in_directory(modules, match)
66+
modules.extend(_discover_modules_in_directory(match))
6667
else:
6768
logger.warning(
6869
"Found matching file but was not executable: {}".format(match)
6970
)
71+
return modules
7072

7173

7274
def _discover_modules_bundled() -> List[str]:
7375
"""Use ``_discover_modules_in_directory`` to find all bundled modules """
74-
modules: List[str] = []
7576
cwd = os.getcwd()
76-
_discover_modules_in_directory(modules, cwd)
77-
78-
if len(modules) > 0:
79-
logger.info("Found bundled modules: {}".format(set(modules)))
80-
else:
81-
logger.info("Found no bundles modules")
77+
modules = _discover_modules_in_directory(cwd)
78+
logger.info("Found bundled modules: {}".format(set(modules)))
8279
return modules
8380

8481

@@ -90,7 +87,7 @@ def _discover_modules_system() -> List[str]:
9087
if os.path.isdir(path):
9188
files = os.listdir(path)
9289
for filename in files:
93-
if "aw-" in filename:
90+
if filename.startswith("aw-"):
9491
modules.append(filename)
9592

9693
logger.info("Found system modules: {}".format(set(modules)))
@@ -116,9 +113,10 @@ def start(self) -> None:
116113
if platform.system() != "Windows":
117114
os.setpgrp()
118115

119-
exec_path = _locate_executable(self.name)
116+
exec_path, location = _locate_executable(self.name)
120117
if exec_path is None:
121118
logger.error("Tried to start nonexistent module {}".format(self.name))
119+
return
122120
else:
123121
exec_cmd = [exec_path]
124122
if self.testing:
@@ -132,7 +130,7 @@ def start(self) -> None:
132130
startupinfo = subprocess.STARTUPINFO() # type: ignore
133131
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # type: ignore
134132
elif platform.system() == "Darwin":
135-
logger.info("Macos: Disable dock icon")
133+
logger.info("macOS: Disable dock icon")
136134
import AppKit
137135

138136
AppKit.NSBundle.mainBundle().infoDictionary()["LSBackgroundOnly"] = "1"
@@ -200,9 +198,7 @@ def read_log(self) -> str:
200198

201199
class Manager:
202200
def __init__(self, testing: bool = False) -> None:
203-
self.settings: AwQtSettings = AwQtSettings(testing)
204201
self.modules: Dict[str, Module] = {}
205-
self.autostart_modules: Set[str] = set(self.settings.autostart_modules)
206202
self.testing = testing
207203

208204
self.discover_modules()
@@ -211,7 +207,7 @@ def discover_modules(self) -> None:
211207
# These should always be bundled with aw-qt
212208
found_modules = set(_discover_modules_bundled())
213209
found_modules |= set(_discover_modules_system())
214-
found_modules ^= {"aw-qt"} # Exclude self
210+
found_modules ^= {"aw-qt", "aw-client"} # Exclude self
215211

216212
for m_name in found_modules:
217213
if m_name not in self.modules:
@@ -230,26 +226,26 @@ def start(self, module_name: str) -> None:
230226
"Manager tried to start nonexistent module {}".format(module_name)
231227
)
232228

233-
def autostart(self, autostart_modules: Optional[List[str]]) -> None:
234-
if autostart_modules is None:
235-
autostart_modules = []
236-
if len(autostart_modules) > 0:
237-
logger.info(
238-
"Modules to start weren't specified in CLI arguments. Falling back to configuration."
239-
)
240-
autostart_modules = self.settings.autostart_modules
229+
def autostart(self, autostart_modules: List[str]) -> None:
241230
# We only want to autostart modules that are both in found modules and are asked to autostart.
242-
modules_to_start = set(autostart_modules).intersection(set(self.modules.keys()))
231+
not_found = []
232+
for name in autostart_modules:
233+
if name not in self.modules.keys():
234+
logger.error(f"Module {name} not found")
235+
not_found.append(name)
236+
autostart_modules = list(set(autostart_modules) - set(not_found))
243237

244238
# Start aw-server-rust first
245-
if "aw-server-rust" in modules_to_start:
239+
if "aw-server-rust" in autostart_modules:
246240
self.start("aw-server-rust")
247-
elif "aw-server" in modules_to_start:
241+
elif "aw-server" in autostart_modules:
248242
self.start("aw-server")
249243

250-
modules_to_start = set(autostart_modules) - {"aw-server", "aw-server-rust"}
251-
for module_name in modules_to_start:
252-
self.start(module_name)
244+
autostart_modules = list(
245+
set(autostart_modules) - {"aw-server", "aw-server-rust"}
246+
)
247+
for name in autostart_modules:
248+
self.start(name)
253249

254250
def stop_all(self) -> None:
255251
for module in filter(lambda m: m.is_alive(), self.modules.values()):

poetry.lock

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ PyQt5 = "5.10.1"
1818
sip = "4.19.8"
1919
pyobjc = { version = "^6.1", platform = "darwin" }
2020
aw-core = {git = "https://github.com/ActivityWatch/aw-core.git"}
21+
click = "^7.1.2"
2122

2223
[tool.poetry.dev-dependencies]
2324
mypy = "^0.761"

0 commit comments

Comments
 (0)