Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port from resource.open to resource.watch (Talon v0.4) #1199

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions apps/emacs/emacs_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,9 @@ def emacs_command_short_form(command_name: str) -> Optional[str]:
return emacs_commands.get(command_name, Command(command_name)).short


def load_csv():
filepath = Path(__file__).parents[0] / "emacs_commands.csv"
with resource.open(filepath) as f:
rows = list(csv.reader(f))
@resource.watch("emacs_commands.csv")
def load_commands(f):
rows = list(csv.reader(f))
# Check headers
assert rows[0] == ["Command", " Key binding", " Short form", " Spoken form"]

Expand Down Expand Up @@ -70,7 +69,3 @@ def load_csv():
if c.spoken:
command_list[c.spoken] = c.name
ctx.lists["self.emacs_command"] = command_list


# TODO: register on change to file!
app.register("ready", load_csv)
22 changes: 10 additions & 12 deletions apps/git/git.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path
from typing import IO
import csv
import os
from pathlib import Path

from talon import Context, Module, actions, resource

Expand All @@ -10,14 +11,8 @@
mod.list("git_command", desc="Git commands.")
mod.list("git_argument", desc="Command-line git options and arguments.")

dirpath = Path(__file__).parent
arguments_csv_path = str(dirpath / "git_arguments.csv")
commands_csv_path = str(dirpath / "git_commands.csv")


def make_list(path):
with resource.open(path, "r") as f:
rows = list(csv.reader(f))
def make_list(f: IO) -> dict[str, str]:
rows = list(csv.reader(f))
mapping = {}
# ignore header row
for row in rows[1:]:
Expand All @@ -32,10 +27,13 @@ def make_list(path):
mapping[spoken_form] = output
return mapping

@resource.watch("git_arguments.csv")
def load_arguments(f):
ctx.lists["self.git_argument"] = make_list(f)

ctx.lists["self.git_argument"] = make_list(arguments_csv_path)
ctx.lists["self.git_command"] = make_list(commands_csv_path)

@resource.watch("git_commands.csv")
def load_commands(f):
ctx.lists["self.git_command"] = make_list(f)

@mod.capture(rule="{user.git_argument}+")
def git_arguments(m) -> str:
Expand Down
27 changes: 14 additions & 13 deletions core/abbreviate/abbreviate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from talon import Context, Module

from ..user_settings import get_list_from_csv
from ..user_settings import track_csv_list

mod = Module()
mod.list("abbreviation", desc="Common abbreviation")
Expand Down Expand Up @@ -446,17 +446,18 @@
}

# This variable is also considered exported for the create_spoken_forms module
abbreviations_list = get_list_from_csv(
"abbreviations.csv",
headers=("Abbreviation", "Spoken Form"),
default=abbreviations,
)

# Allows the abbreviated/short form to be used as spoken phrase. eg "brief app" -> app
abbreviations_list_with_values = {
**{v: v for v in abbreviations_list.values()},
**abbreviations_list,
}
abbreviations_list = {}

ctx = Context()
ctx.lists["user.abbreviation"] = abbreviations_list_with_values

@track_csv_list("abbreviations.csv", headers=("Abbreviation", "Spoken Form"), default=abbreviations)
def on_abbreviations(values):
global abbreviations_list
abbreviations_list = values

# Allows the abbreviated/short form to be used as spoken phrase. eg "brief app" -> app
abbreviations_list_with_values = {
**{v: v for v in abbreviations_list.values()},
**abbreviations_list,
}
ctx.lists["user.abbreviation"] = abbreviations_list_with_values
45 changes: 29 additions & 16 deletions core/create_spoken_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,46 @@
from talon import Module, actions

from .abbreviate.abbreviate import abbreviations_list
from .file_extension.file_extension import file_extensions
from .keys.keys import symbol_key_words
from .numbers.numbers import digits_map, scales, teens, tens
from .user_settings import track_csv_list

mod = Module()


DEFAULT_MINIMUM_TERM_LENGTH = 2
EXPLODE_MAX_LEN = 3
FANCY_REGULAR_EXPRESSION = r"[A-Z]?[a-z]+|[A-Z]+(?![a-z])|[0-9]+"
FILE_EXTENSIONS_REGEX = "|".join(
re.escape(file_extension.strip()) + "$"
for file_extension in file_extensions.values()
)
SYMBOLS_REGEX = "|".join(re.escape(symbol) for symbol in set(symbol_key_words.values()))
REGEX_NO_SYMBOLS = re.compile(
"|".join(
[
FANCY_REGULAR_EXPRESSION,
FILE_EXTENSIONS_REGEX,
]
FILE_EXTENSIONS_REGEX = r'^\b$'
file_extensions = {}

def update_regex():
global REGEX_NO_SYMBOLS
global REGEX_WITH_SYMBOLS
REGEX_NO_SYMBOLS = re.compile(
"|".join(
[
FANCY_REGULAR_EXPRESSION,
FILE_EXTENSIONS_REGEX,
]
)
)
REGEX_WITH_SYMBOLS = re.compile(
"|".join([FANCY_REGULAR_EXPRESSION, FILE_EXTENSIONS_REGEX, SYMBOLS_REGEX])
)
)

REGEX_WITH_SYMBOLS = re.compile(
"|".join([FANCY_REGULAR_EXPRESSION, FILE_EXTENSIONS_REGEX, SYMBOLS_REGEX])
)
update_regex()

@track_csv_list("file_extensions.csv", headers=("File extension", "Name"))
def on_extensions(values):
global FILE_EXTENSIONS_REGEX
global file_extensions
file_extensions = values
FILE_EXTENSIONS_REGEX = "|".join(
re.escape(file_extension.strip()) + "$"
for file_extension in values.values()
)
update_regex()

REVERSE_PRONUNCIATION_MAP = {
**{str(value): key for key, value in digits_map.items()},
Expand Down
13 changes: 5 additions & 8 deletions core/file_extension/file_extension.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from talon import Context, Module

from ..user_settings import get_list_from_csv
from ..user_settings import track_csv_list

mod = Module()
mod.list("file_extension", desc="A file extension, such as .py")
Expand Down Expand Up @@ -55,11 +55,8 @@
"dot log": ".log",
}

file_extensions = get_list_from_csv(
"file_extensions.csv",
headers=("File extension", "Name"),
default=_file_extensions_defaults,
)

ctx = Context()
ctx.lists["self.file_extension"] = file_extensions

@track_csv_list("file_extensions.csv", headers=("File extension", "Name"), default=_file_extensions_defaults)
def on_update(values):
ctx.lists["self.file_extension"] = values
11 changes: 5 additions & 6 deletions core/keys/keys.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from talon import Context, Module, actions, app

from ..user_settings import get_list_from_csv
from ..user_settings import track_csv_list


def setup_default_alphabet():
Expand All @@ -16,10 +16,6 @@ def setup_default_alphabet():
return initial_default_alphabet_dict


alphabet_list = get_list_from_csv(
"alphabet.csv", ("Letter", "Spoken Form"), setup_default_alphabet()
)

# used for number keys & function keys respectively
digits = "zero one two three four five six seven eight nine".split()
f_digits = "one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen twenty".split()
Expand Down Expand Up @@ -132,7 +128,10 @@ def letters(m) -> str:
modifier_keys["command"] = "cmd"
modifier_keys["option"] = "alt"
ctx.lists["self.modifier_key"] = modifier_keys
ctx.lists["self.letter"] = alphabet_list

@track_csv_list("alphabet.csv", ("Letter", "Spoken Form"), setup_default_alphabet())
def on_alphabet(values):
ctx.lists["self.letter"] = values

# `punctuation_words` is for words you want available BOTH in dictation and as key names in command mode.
# `symbol_key_words` is for key names that should be available in command mode, but NOT during dictation.
Expand Down
10 changes: 4 additions & 6 deletions core/system_paths.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from talon import Context, Module, actions, app

from .user_settings import get_list_from_csv
from .user_settings import track_csv_list

mod = Module()
ctx = Context()
Expand Down Expand Up @@ -49,11 +49,9 @@ def on_ready():

default_system_paths.update(onedrive_paths)

system_paths = get_list_from_csv(
"system_paths.csv", headers=("Path", "Spoken"), default=default_system_paths
)

ctx.lists["user.system_paths"] = system_paths
@track_csv_list("system_paths.csv", headers=("Path", "Spoken"), default=default_system_paths)
def on_csv(system_paths):
ctx.lists["user.system_paths"] = system_paths


@mod.capture(rule="{user.system_paths}")
Expand Down
63 changes: 41 additions & 22 deletions core/user_settings.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,20 @@
from pathlib import Path
from typing import Callable, IO
import csv
import os
from pathlib import Path

from talon import resource

# NOTE: This method requires this module to be one folder below the top-level
# community/knausj folder.
SETTINGS_DIR = Path(__file__).parents[1] / "settings"
SETTINGS_DIR.mkdir(exist_ok=True)

if not SETTINGS_DIR.is_dir():
os.mkdir(SETTINGS_DIR)


def get_list_from_csv(
filename: str, headers: tuple[str, str], default: dict[str, str] = {}
):
"""Retrieves list from CSV"""
path = SETTINGS_DIR / filename
assert filename.endswith(".csv")

if not path.is_file():
with open(path, "w", encoding="utf-8", newline="") as file:
writer = csv.writer(file)
writer.writerow(headers)
for key, value in default.items():
writer.writerow([key] if key == value else [value, key])
CallbackT = Callable[[dict[str, str]], None]
DecoratorT = Callable[[CallbackT], CallbackT]

# Now read via resource to take advantage of talon's
# ability to reload this script for us when the resource changes
with resource.open(str(path), "r") as f:
rows = list(csv.reader(f))
def read_csv_list(f: IO, headers: tuple[str, str]) -> dict[str, str]:
rows = list(csv.reader(f))

# print(str(rows))
mapping = {}
Expand Down Expand Up @@ -59,6 +44,40 @@ def get_list_from_csv(

return mapping

def write_csv_defaults(path: Path, headers: tuple[str, str], default: dict[str, str]=None) -> None:
if not path.is_file() and default is not None:
with open(path, "w", encoding="utf-8", newline="") as file:
Comment on lines +48 to +49
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer earlier return for simplicity

Suggested change
if not path.is_file() and default is not None:
with open(path, "w", encoding="utf-8", newline="") as file:
if default is None or path.is_file():
return

writer = csv.writer(file)
writer.writerow(headers)
for key, value in default.items():
writer.writerow([key] if key == value else [value, key])

def track_csv_list(filename: str, headers: tuple[str, str], default: dict[str, str]=None) -> DecoratorT:
assert filename.endswith(".csv")
path = SETTINGS_DIR / filename
write_csv_defaults(path, headers, default)

def decorator(fn: CallbackT) -> CallbackT:
@resource.watch(str(path))
def on_update(f):
knausj85 marked this conversation as resolved.
Show resolved Hide resolved
data = read_csv_list(f, headers)
fn(data)

return decorator

# NOTE: this is deprecated, use @track_csv_list instead.
def get_list_from_csv(
filename: str, headers: tuple[str, str], default: dict[str, str] = {}
):
"""Retrieves list from CSV"""
assert filename.endswith(".csv")
path = SETTINGS_DIR / filename
write_csv_defaults(path, headers, default)

# Now read via resource to take advantage of talon's
# ability to reload this script for us when the resource changes
with resource.open(str(path), "r") as f:
return read_csv_list(f, headers)
Comment on lines +68 to +80
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to make get_list_from_csv raise its own DeprecationWarning (and suppress the resource.open one) - users of knausj may be using it directly rather than resource.open (I was, for instance), so a more detailed deprecation might be helpful.


def append_to_csv(filename: str, rows: dict[str, str]):
path = SETTINGS_DIR / filename
Expand Down
Loading
Loading