asset_library/common/template.py
2026-01-07 16:05:47 +01:00

129 lines
3.5 KiB
Python

import re
import os
from pathlib import Path
from fnmatch import fnmatch
from glob import glob
import string
class TemplateFormatter(string.Formatter):
def format_field(self, value, format_spec):
if isinstance(value, str):
spec, sep = [*format_spec.split(":"), None][:2]
if sep:
value = value.replace("_", " ")
value = value = re.sub(r"([a-z])([A-Z])", rf"\1{sep}\2", value)
value = value.replace(" ", sep)
if spec == "u":
value = value.upper()
elif spec == "l":
value = value.lower()
elif spec == "t":
value = value.title()
return super().format(value, format_spec)
class Template:
field_pattern = re.compile(r"{(\w+)\*{0,2}}")
field_pattern_recursive = re.compile(r"{(\w+)\*{2}}")
def __init__(self, template):
# asset_data_path = Path(lib_path) / ASSETLIB_FILENAME
self.raw = template
self.formatter = TemplateFormatter()
@property
def glob_pattern(self):
pattern = self.field_pattern_recursive.sub("**", self.raw)
pattern = self.field_pattern.sub("*", pattern)
return pattern
@property
def re_pattern(self):
pattern = self.field_pattern_recursive.sub("([\\\w -_.\/]+)", self.raw)
pattern = self.field_pattern.sub("([\\\w -_.]+)", pattern)
pattern = pattern.replace("?", ".")
pattern = pattern.replace("*", ".*")
return re.compile(pattern)
@property
def fields(self):
return self.field_pattern.findall(self.raw)
# return [f or '0' for f in fields]
def parse(self, path):
path = Path(path).as_posix()
res = self.re_pattern.findall(path)
if not res:
print("Could not parse {path} with {self.re_pattern}")
return {}
fields = self.fields
if len(fields) == 1:
field_values = res
else:
field_values = res[0]
return {k: v for k, v in zip(fields, field_values)}
def norm_data(self, data):
norm_data = {}
for k, v in data.items():
if isinstance(v, Path):
v = v.as_posix()
norm_data[k] = v
return norm_data
def format(self, data=None, **kargs):
data = {**(data or {}), **kargs}
try:
# print('FORMAT', self.raw, data)
path = self.formatter.format(self.raw, **self.norm_data(data))
except KeyError as e:
print(f"Cannot format {self.raw} with {data}, field {e} is missing")
return
path = os.path.expandvars(path)
return Path(path)
def glob(self, directory, pattern=None):
"""If pattern is given it need to be absolute"""
if pattern is None:
pattern = Path(directory, self.glob_pattern).as_posix()
for entry in os.scandir(directory):
entry_path = Path(entry.path)
if entry.is_file() and fnmatch(entry_path.as_posix(), pattern):
yield entry_path
elif entry.is_dir():
yield from self.glob(entry.path, pattern)
def find(self, data, **kargs):
pattern = self.format(data, **kargs)
pattern_str = str(pattern)
if "*" not in pattern_str and "?" not in pattern_str:
return pattern
paths = glob(pattern.as_posix())
if paths:
return Path(paths[0])
# return pattern
def __repr__(self):
return f"Template({self.raw})"