Add my existing implementation
This commit is contained in:
98
tagtimerc.py
Normal file
98
tagtimerc.py
Normal file
@@ -0,0 +1,98 @@
|
||||
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)
|
||||
Reference in New Issue
Block a user