ref: Load all options from single dictionary source
This commit is contained in:
parent
fccfb85026
commit
b20d56a007
1 changed files with 106 additions and 79 deletions
185
topen.py
185
topen.py
|
|
@ -21,7 +21,6 @@ import configparser
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from collections import namedtuple
|
|
||||||
from dataclasses import asdict, dataclass
|
from dataclasses import asdict, dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Self
|
from typing import Any, Self
|
||||||
|
|
@ -112,6 +111,83 @@ def add_annotation_if_missing(task: Task, annotation_content: str) -> None:
|
||||||
_ = whisper(f"Added annotation: {annotation_content}")
|
_ = whisper(f"Added annotation: {annotation_content}")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass()
|
||||||
|
class Opt:
|
||||||
|
"""Assembled metadata for a single configuration option."""
|
||||||
|
|
||||||
|
cli: tuple[str, ...] | None
|
||||||
|
env: str | None
|
||||||
|
rc: str | None
|
||||||
|
default: Any = None
|
||||||
|
metavar: str | None = None
|
||||||
|
cast: type = str
|
||||||
|
help_text: str = ""
|
||||||
|
|
||||||
|
|
||||||
|
OPTIONS: dict[str, Opt] = {
|
||||||
|
"task_id": Opt(None, None, None, default=None),
|
||||||
|
"task_rc": Opt(
|
||||||
|
("--task-rc",),
|
||||||
|
"TASKRC",
|
||||||
|
None, # taskrc has no key for this
|
||||||
|
default=Path("~/.taskrc"),
|
||||||
|
metavar="FILE",
|
||||||
|
cast=Path,
|
||||||
|
help_text="Location of taskwarrior config file",
|
||||||
|
),
|
||||||
|
"task_data": Opt(
|
||||||
|
("--task-data",),
|
||||||
|
"TASKDATA",
|
||||||
|
"data.location",
|
||||||
|
default=Path("~/.task"),
|
||||||
|
metavar="DIR",
|
||||||
|
cast=Path,
|
||||||
|
help_text="Location of taskwarrior data directory",
|
||||||
|
),
|
||||||
|
"notes_dir": Opt(
|
||||||
|
("-d", "--notes-dir"),
|
||||||
|
"TOPEN_NOTES_DIR",
|
||||||
|
"notes.dir",
|
||||||
|
default=None, # resolved later in TConf.__post_init__
|
||||||
|
metavar="DIR",
|
||||||
|
cast=Path,
|
||||||
|
help_text="Location of topen notes files",
|
||||||
|
),
|
||||||
|
"notes_ext": Opt(
|
||||||
|
("--extension",),
|
||||||
|
"TOPEN_NOTES_EXT",
|
||||||
|
"notes.ext",
|
||||||
|
default="md",
|
||||||
|
metavar="EXT",
|
||||||
|
help_text="Extension of note files",
|
||||||
|
),
|
||||||
|
"notes_annot": Opt(
|
||||||
|
("--annotation",),
|
||||||
|
"TOPEN_NOTES_ANNOT",
|
||||||
|
"notes.annot",
|
||||||
|
default="Note",
|
||||||
|
metavar="NOTE",
|
||||||
|
help_text="Annotation content to set within taskwarrior",
|
||||||
|
),
|
||||||
|
"notes_editor": Opt(
|
||||||
|
("--editor",),
|
||||||
|
"TOPEN_NOTES_EDITOR",
|
||||||
|
"notes.editor",
|
||||||
|
default=os.getenv("EDITOR") or os.getenv("VISUAL") or "nano",
|
||||||
|
metavar="CMD",
|
||||||
|
help_text="Program to open note files with",
|
||||||
|
),
|
||||||
|
"notes_quiet": Opt(
|
||||||
|
("--quiet",),
|
||||||
|
"TOPEN_NOTES_QUIET",
|
||||||
|
"notes.quiet",
|
||||||
|
default=False,
|
||||||
|
cast=bool,
|
||||||
|
help_text="Silence any verbose displayed information",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass()
|
@dataclass()
|
||||||
class TConf:
|
class TConf:
|
||||||
"""Topen Configuration
|
"""Topen Configuration
|
||||||
|
|
@ -189,93 +265,50 @@ you view the task.
|
||||||
_ = parser.add_argument(
|
_ = parser.add_argument(
|
||||||
"id", help="The id/uuid of the taskwarrior task for which we edit notes"
|
"id", help="The id/uuid of the taskwarrior task for which we edit notes"
|
||||||
)
|
)
|
||||||
_ = parser.add_argument(
|
for key, opt in OPTIONS.items():
|
||||||
"-d",
|
if opt.cli is None:
|
||||||
"--notes-dir",
|
continue
|
||||||
metavar="DIR",
|
parser.add_argument(
|
||||||
help="Location of topen notes files",
|
*opt.cli,
|
||||||
)
|
dest=key,
|
||||||
_ = parser.add_argument(
|
metavar=opt.metavar,
|
||||||
"--quiet",
|
help=opt.help_text,
|
||||||
action="store_true",
|
default=None,
|
||||||
help="Silence any verbose displayed information",
|
)
|
||||||
)
|
args = parser.parse_args()
|
||||||
_ = parser.add_argument(
|
cli_vals = {k: v for k, v in vars(args).items() if v is not None}
|
||||||
"--extension", metavar="EXT", help="Extension of note files"
|
cli_vals["task_id"] = cli_vals.pop("id")
|
||||||
)
|
return cli_vals
|
||||||
_ = parser.add_argument(
|
|
||||||
"--annotation",
|
|
||||||
metavar="NOTE",
|
|
||||||
help="Annotation content to set within taskwarrior",
|
|
||||||
)
|
|
||||||
_ = parser.add_argument(
|
|
||||||
"--editor", metavar="CMD", help="Program to open note files with"
|
|
||||||
)
|
|
||||||
_ = parser.add_argument(
|
|
||||||
"--task-rc", metavar="FILE", help="Location of taskwarrior config file"
|
|
||||||
)
|
|
||||||
_ = parser.add_argument(
|
|
||||||
"--task-data", metavar="DIR", help="Location of taskwarrior data directory"
|
|
||||||
)
|
|
||||||
|
|
||||||
p = parser.parse_args()
|
|
||||||
return _filtered_dict(
|
|
||||||
{
|
|
||||||
"task_id": p.id,
|
|
||||||
"task_rc": p.task_rc,
|
|
||||||
"task_data": p.task_data,
|
|
||||||
"notes_dir": p.notes_dir,
|
|
||||||
"notes_ext": p.extension,
|
|
||||||
"notes_annot": p.annotation,
|
|
||||||
"notes_editor": p.editor,
|
|
||||||
"notes_quiet": p.quiet,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_env() -> dict:
|
def parse_env() -> dict[str, Any]:
|
||||||
"""Parse environment variable options.
|
"""Parse environment variable options.
|
||||||
|
|
||||||
Returns them as a simple dict object.
|
Returns them as a simple dict object.
|
||||||
"""
|
"""
|
||||||
return _filtered_dict(
|
out: dict[str, Any] = {}
|
||||||
{
|
for key, opt in OPTIONS.items():
|
||||||
"task_rc": os.getenv("TASKRC"),
|
if opt.env and (val := os.getenv(opt.env)) is not None:
|
||||||
"task_data": os.getenv("TASKDATA"),
|
out[key] = opt.cast(val)
|
||||||
"notes_dir": os.getenv("TOPEN_NOTES_DIR"),
|
return out
|
||||||
"notes_ext": os.getenv("TOPEN_NOTES_EXT"),
|
|
||||||
"notes_annot": os.getenv("TOPEN_NOTES_ANNOT"),
|
|
||||||
"notes_editor": os.getenv("TOPEN_NOTES_EDITOR"),
|
|
||||||
"notes_quiet": os.getenv("TOPEN_NOTES_QUIET"),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_conf(conf_file: Path) -> dict:
|
def parse_conf(rc_path: Path) -> dict:
|
||||||
"""Parse taskrc configuration file options.
|
"""Parse taskrc configuration file options.
|
||||||
|
|
||||||
Returns them as a simple dict object.
|
Returns them as a simple dict object.
|
||||||
Uses dot.annotation for options just like taskwarrior settings.
|
Uses dot.annotation for options just like taskwarrior settings.
|
||||||
"""
|
"""
|
||||||
cfg = configparser.ConfigParser(allow_unnamed_section=True, allow_no_value=True)
|
cfg = configparser.ConfigParser(allow_unnamed_section=True, allow_no_value=True)
|
||||||
with open(conf_file.expanduser()) as f:
|
with rc_path.expanduser().open() as fr:
|
||||||
cfg.read_string("[GENERAL]\n" + f.read())
|
cfg.read_string("[GENERAL]\n" + fr.read())
|
||||||
|
|
||||||
ConfTrans = namedtuple("ParsedToTConf", ["name", "tconf_name"])
|
out: dict[str, Any] = {}
|
||||||
return _filtered_dict(
|
for key, opt in OPTIONS.items():
|
||||||
{
|
if opt.rc and cfg.has_option("GENERAL", opt.rc):
|
||||||
opt.tconf_name: cfg.get("GENERAL", opt.name)
|
raw = cfg.get("GENERAL", opt.rc)
|
||||||
for opt in [
|
out[key] = opt.cast(raw)
|
||||||
ConfTrans("data.location", "task_data"),
|
return out
|
||||||
ConfTrans("notes.dir", "notes_dir"),
|
|
||||||
ConfTrans("notes.ext", "notes_ext"),
|
|
||||||
ConfTrans("notes.annot", "notes_annot"),
|
|
||||||
ConfTrans("notes.editor", "notes_editor"),
|
|
||||||
ConfTrans("notes.quiet", "notes_quiet"),
|
|
||||||
]
|
|
||||||
if cfg.has_option("GENERAL", opt.name)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
IS_QUIET = False
|
IS_QUIET = False
|
||||||
|
|
@ -290,11 +323,5 @@ def _real_path(p: Path | str) -> Path:
|
||||||
return Path(os.path.expandvars(p)).expanduser()
|
return Path(os.path.expandvars(p)).expanduser()
|
||||||
|
|
||||||
|
|
||||||
# A None-filtered dict which only contains
|
|
||||||
# keys which have a value.
|
|
||||||
def _filtered_dict(d: dict) -> dict:
|
|
||||||
return {k: v for (k, v) in d.items() if v}
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue