Files
tagtimepy/tagtimerc.py

99 lines
3.5 KiB
Python

import os
import re
import sys
from dataclasses import dataclass, field
from typing import List
def get_tagtime_command() -> List[str]:
"""
The original Perl implementation spawns another Perl script in a terminal
to ask the user for their current tags. To replicate this behavior we find
out the python executable and the location of the tagtime.py script.
"""
python_exe = sys.executable
tagtimerc_py = os.path.abspath(__file__)
tagtime_py = tagtimerc_py.replace("tagtimerc.py", "tagtime.py")
return [python_exe, tagtime_py]
@dataclass
class TagTimeRc:
log_file: str
ed: str
xt: str
retrothresh: int
gap: int
urping: int
seed: int
linelen: int
catchup: int
tags_off: List[str] = field(default_factory=lambda:
["afk", "off", "RETRO"])
tags_afk: List[str] = field(default_factory=lambda: ["afk", "RETRO"])
tags_err: List[str] = field(default_factory=lambda: ["err"])
tagtimecmd: List[str] = field(default_factory=get_tagtime_command)
def value_to_int(value: str) -> int:
"""
The tagtimerc file is itself a Perl script. That means the assignments to
the configuration parameters can be Perl expressions. We could replicate
that behavior via `eval`, but we don't want to do that because it allows
arbitrary code execution. Instead we only support multiplication for now
(`45*60`).
"""
try:
return int(value)
except ValueError:
pass
result = 1
for v in value.split("*"):
result *= int(v)
return result
def parse_tagtimerc(tagtimerc_path: str) -> TagTimeRc:
"""
Parses the configuration attributes from a tagtimerc file into Python.
All lines that start with a dollar sign are configuration lines. These
lines are than split into key and value. This function only considers
attributes that are part of TagTimeRc.
"""
s = ("^\s*" # potential leading whitespace
"\$(\w+)" # key as group (variables in Perl start with $)
"\s*=\s*" # equal sign including potential whitespaces around it
"\"?" # potential opening quote
"([^\";]+)" # value (everything that is not a semicolon or quote)
"\"?" # potential closing quote
";") # semicolon to terminate key value pair
re_config_line = re.compile(s)
tagtimerc_path = os.path.expanduser(tagtimerc_path)
with open(tagtimerc_path, 'r') as f:
key_value_pairs = {m.groups()[0].lower(): m.groups()[1]
for line in f.readlines()
if (m := re_config_line.match(line))}
# Get dictionary of the TagTimeRc attribute types.
tagtimerc_types = TagTimeRc.__annotations__
# Process the key-value-pairs. Keep expected pairs and store them into
# kwarsg.
kwargs = {}
for key, value in key_value_pairs.items():
if key in tagtimerc_types and tagtimerc_types[key] is int:
kwargs[key] = value_to_int(value)
elif key == "logf":
# Give special treatman to logf, because it may contain other
# variables (`$logf = "$path$usr.log";`) Iterate over those
# variables and replace them with their value.
for variable in re.findall("\$\w+", value):
var = variable.replace("$", "")
value = value.replace(variable, key_value_pairs[var])
kwargs["log_file"] = value
elif key in tagtimerc_types:
kwargs[key] = value
return TagTimeRc(**kwargs)