Add YAPF style + ignore, and format tracked Python files
This commit is contained in:
@@ -26,7 +26,6 @@ get_field = sh.get_field
|
||||
from Store import Store
|
||||
from SYS.utils import sha256_file
|
||||
|
||||
|
||||
_FIELD_NAME_RE = re.compile(r"^[A-Za-z0-9_]+$")
|
||||
|
||||
|
||||
@@ -106,13 +105,15 @@ def _compile_extract_template(template: str) -> tuple[re.Pattern[str], List[str]
|
||||
last_end = 0
|
||||
|
||||
for idx, m in enumerate(matches):
|
||||
literal = tpl[last_end : m.start()]
|
||||
literal = tpl[last_end:m.start()]
|
||||
if literal:
|
||||
parts.append(_literal_to_title_pattern_regex(literal))
|
||||
|
||||
raw_name = (m.group(1) or "").strip()
|
||||
if not raw_name or not _FIELD_NAME_RE.fullmatch(raw_name):
|
||||
raise ValueError(f"invalid field name '{raw_name}' (use A-Z, 0-9, underscore)")
|
||||
raise ValueError(
|
||||
f"invalid field name '{raw_name}' (use A-Z, 0-9, underscore)"
|
||||
)
|
||||
field_names.append(raw_name)
|
||||
|
||||
name_lower = raw_name.lower()
|
||||
@@ -123,15 +124,15 @@ def _compile_extract_template(template: str) -> tuple[re.Pattern[str], List[str]
|
||||
# Heuristic: common numeric fields should capture full digit runs.
|
||||
# This avoids ambiguous splits like track='2', title='3 ...'.
|
||||
if name_lower in {
|
||||
"disk",
|
||||
"disc",
|
||||
"cd",
|
||||
"track",
|
||||
"trk",
|
||||
"episode",
|
||||
"ep",
|
||||
"season",
|
||||
"year",
|
||||
"disk",
|
||||
"disc",
|
||||
"cd",
|
||||
"track",
|
||||
"trk",
|
||||
"episode",
|
||||
"ep",
|
||||
"season",
|
||||
"year",
|
||||
}:
|
||||
parts.append(rf"(?P<{raw_name}>\d+)")
|
||||
else:
|
||||
@@ -170,7 +171,8 @@ def _extract_tags_from_title(title_text: str, template: str) -> List[str]:
|
||||
|
||||
|
||||
def _get_title_candidates_for_extraction(
|
||||
res: Any, existing_tags: Optional[List[str]] = None
|
||||
res: Any,
|
||||
existing_tags: Optional[List[str]] = None
|
||||
) -> List[str]:
|
||||
"""Return a list of possible title strings in priority order."""
|
||||
|
||||
@@ -209,9 +211,9 @@ def _get_title_candidates_for_extraction(
|
||||
return candidates
|
||||
|
||||
|
||||
def _extract_tags_from_title_candidates(
|
||||
candidates: List[str], template: str
|
||||
) -> tuple[List[str], Optional[str]]:
|
||||
def _extract_tags_from_title_candidates(candidates: List[str],
|
||||
template: str) -> tuple[List[str],
|
||||
Optional[str]]:
|
||||
"""Try candidates in order; return (tags, matched_candidate)."""
|
||||
|
||||
for c in candidates:
|
||||
@@ -223,7 +225,8 @@ def _extract_tags_from_title_candidates(
|
||||
|
||||
def _try_compile_extract_template(
|
||||
template: Optional[str],
|
||||
) -> tuple[Optional[re.Pattern[str]], Optional[str]]:
|
||||
) -> tuple[Optional[re.Pattern[str]],
|
||||
Optional[str]]:
|
||||
"""Compile template for debug; return (pattern, error_message)."""
|
||||
if template is None:
|
||||
return None, None
|
||||
@@ -408,7 +411,8 @@ def _refresh_tag_view(
|
||||
target_hash: Optional[str],
|
||||
store_name: Optional[str],
|
||||
target_path: Optional[str],
|
||||
config: Dict[str, Any],
|
||||
config: Dict[str,
|
||||
Any],
|
||||
) -> None:
|
||||
"""Refresh tag display via get-tag. Prefer current subject; fall back to direct hash refresh."""
|
||||
try:
|
||||
@@ -450,13 +454,15 @@ class Add_Tag(Cmdlet):
|
||||
super().__init__(
|
||||
name="add-tag",
|
||||
summary="Add tag to a file in a store.",
|
||||
usage='add-tag -store <store> [-query "hash:<sha256>"] [-duplicate <format>] [-list <list>[,<list>...]] [--all] <tag>[,<tag>...]',
|
||||
usage=
|
||||
'add-tag -store <store> [-query "hash:<sha256>"] [-duplicate <format>] [-list <list>[,<list>...]] [--all] <tag>[,<tag>...]',
|
||||
arg=[
|
||||
CmdletArg(
|
||||
"tag",
|
||||
type="string",
|
||||
required=False,
|
||||
description="One or more tag to add. Comma- or space-separated. Can also use {list_name} syntax. If omitted, uses tag from pipeline payload.",
|
||||
description=
|
||||
"One or more tag to add. Comma- or space-separated. Can also use {list_name} syntax. If omitted, uses tag from pipeline payload.",
|
||||
variadic=True,
|
||||
),
|
||||
SharedArgs.QUERY,
|
||||
@@ -464,27 +470,32 @@ class Add_Tag(Cmdlet):
|
||||
CmdletArg(
|
||||
"-extract",
|
||||
type="string",
|
||||
description='Extract tags from the item\'s title using a simple template with (field) placeholders. Example: -extract "(artist) - (album) - (disk)-(track) (title)" will add artist:, album:, disk:, track:, title: tags.',
|
||||
description=
|
||||
'Extract tags from the item\'s title using a simple template with (field) placeholders. Example: -extract "(artist) - (album) - (disk)-(track) (title)" will add artist:, album:, disk:, track:, title: tags.',
|
||||
),
|
||||
CmdletArg(
|
||||
"--extract-debug",
|
||||
type="flag",
|
||||
description="Print debug info for -extract matching (matched title source and extracted tags).",
|
||||
description=
|
||||
"Print debug info for -extract matching (matched title source and extracted tags).",
|
||||
),
|
||||
CmdletArg(
|
||||
"-duplicate",
|
||||
type="string",
|
||||
description="Copy existing tag values to new namespaces. Formats: title:album,artist (explicit) or title,album,artist (inferred)",
|
||||
description=
|
||||
"Copy existing tag values to new namespaces. Formats: title:album,artist (explicit) or title,album,artist (inferred)",
|
||||
),
|
||||
CmdletArg(
|
||||
"-list",
|
||||
type="string",
|
||||
description="Load predefined tag lists from adjective.json. Comma-separated list names (e.g., -list philosophy,occult).",
|
||||
description=
|
||||
"Load predefined tag lists from adjective.json. Comma-separated list names (e.g., -list philosophy,occult).",
|
||||
),
|
||||
CmdletArg(
|
||||
"--all",
|
||||
type="flag",
|
||||
description="Include temporary files in tagging (by default, only tag non-temporary files).",
|
||||
description=
|
||||
"Include temporary files in tagging (by default, only tag non-temporary files).",
|
||||
),
|
||||
],
|
||||
detail=[
|
||||
@@ -523,7 +534,10 @@ class Add_Tag(Cmdlet):
|
||||
|
||||
query_hash = sh.parse_single_hash_query(parsed.get("query"))
|
||||
if parsed.get("query") and not query_hash:
|
||||
log("[add_tag] Error: -query must be of the form hash:<sha256>", file=sys.stderr)
|
||||
log(
|
||||
"[add_tag] Error: -query must be of the form hash:<sha256>",
|
||||
file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
|
||||
# If add-tag is in the middle of a pipeline (has downstream stages), default to
|
||||
@@ -532,7 +546,9 @@ class Add_Tag(Cmdlet):
|
||||
store_override = parsed.get("store")
|
||||
stage_ctx = ctx.get_stage_context()
|
||||
has_downstream = bool(
|
||||
stage_ctx is not None and not getattr(stage_ctx, "is_last_stage", False)
|
||||
stage_ctx is not None and not getattr(stage_ctx,
|
||||
"is_last_stage",
|
||||
False)
|
||||
)
|
||||
|
||||
include_temp = bool(parsed.get("all", False))
|
||||
@@ -598,11 +614,17 @@ class Add_Tag(Cmdlet):
|
||||
tag_to_add = expand_tag_groups(tag_to_add)
|
||||
|
||||
if not tag_to_add and not extract_template:
|
||||
log("No tag provided to add (and no -extract template provided)", file=sys.stderr)
|
||||
log(
|
||||
"No tag provided to add (and no -extract template provided)",
|
||||
file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
|
||||
if extract_template and extract_debug and extract_debug_err:
|
||||
log(f"[add_tag] extract template error: {extract_debug_err}", file=sys.stderr)
|
||||
log(
|
||||
f"[add_tag] extract template error: {extract_debug_err}",
|
||||
file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
|
||||
# Get other flags
|
||||
@@ -643,8 +665,7 @@ class Add_Tag(Cmdlet):
|
||||
if not store_override:
|
||||
store_name_str = str(store_name) if store_name is not None else ""
|
||||
local_mode_requested = (
|
||||
(not store_name_str)
|
||||
or (store_name_str.upper() == "PATH")
|
||||
(not store_name_str) or (store_name_str.upper() == "PATH")
|
||||
or (store_name_str.lower() == "local")
|
||||
)
|
||||
is_known_backend = bool(store_name_str) and store_registry.is_available(
|
||||
@@ -656,14 +677,16 @@ class Add_Tag(Cmdlet):
|
||||
if Path(str(raw_path)).expanduser().exists():
|
||||
existing_tag_list = _extract_item_tags(res)
|
||||
existing_lower = {
|
||||
t.lower() for t in existing_tag_list if isinstance(t, str)
|
||||
t.lower()
|
||||
for t in existing_tag_list if isinstance(t, str)
|
||||
}
|
||||
|
||||
item_tag_to_add = list(tag_to_add)
|
||||
|
||||
if extract_template:
|
||||
candidates = _get_title_candidates_for_extraction(
|
||||
res, existing_tag_list
|
||||
res,
|
||||
existing_tag_list
|
||||
)
|
||||
extracted, matched = _extract_tags_from_title_candidates(
|
||||
candidates, extract_template
|
||||
@@ -683,17 +706,20 @@ class Add_Tag(Cmdlet):
|
||||
if extract_debug:
|
||||
rx_preview = (
|
||||
extract_debug_rx.pattern
|
||||
if extract_debug_rx
|
||||
else "<uncompiled>"
|
||||
if extract_debug_rx else "<uncompiled>"
|
||||
)
|
||||
cand_preview = "; ".join(
|
||||
[repr(c) for c in candidates[:3]]
|
||||
)
|
||||
cand_preview = "; ".join([repr(c) for c in candidates[:3]])
|
||||
log(
|
||||
f"[add_tag] extract no match for template {extract_template!r}. regex: {rx_preview!r}. candidates: {cand_preview}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
item_tag_to_add = collapse_namespace_tag(
|
||||
item_tag_to_add, "title", prefer="last"
|
||||
item_tag_to_add,
|
||||
"title",
|
||||
prefer="last"
|
||||
)
|
||||
|
||||
if duplicate_arg:
|
||||
@@ -703,12 +729,17 @@ class Add_Tag(Cmdlet):
|
||||
|
||||
if len(parts) > 1:
|
||||
source_ns = parts[0]
|
||||
targets = [t.strip() for t in parts[1].split(",") if t.strip()]
|
||||
targets = [
|
||||
t.strip() for t in parts[1].split(",")
|
||||
if t.strip()
|
||||
]
|
||||
else:
|
||||
parts2 = str(duplicate_arg).split(",")
|
||||
if len(parts2) > 1:
|
||||
source_ns = parts2[0]
|
||||
targets = [t.strip() for t in parts2[1:] if t.strip()]
|
||||
targets = [
|
||||
t.strip() for t in parts2[1:] if t.strip()
|
||||
]
|
||||
|
||||
if source_ns and targets:
|
||||
source_prefix = source_ns.lower() + ":"
|
||||
@@ -730,21 +761,22 @@ class Add_Tag(Cmdlet):
|
||||
continue
|
||||
ns_prefix = ns.lower() + ":"
|
||||
for t in existing_tag_list:
|
||||
if (
|
||||
t.lower().startswith(ns_prefix)
|
||||
and t.lower() != new_tag.lower()
|
||||
):
|
||||
if (t.lower().startswith(ns_prefix)
|
||||
and t.lower() != new_tag.lower()):
|
||||
removed_namespace_tag.append(t)
|
||||
removed_namespace_tag = sorted({t for t in removed_namespace_tag})
|
||||
removed_namespace_tag = sorted(
|
||||
{t
|
||||
for t in removed_namespace_tag}
|
||||
)
|
||||
|
||||
actual_tag_to_add = [
|
||||
t
|
||||
for t in item_tag_to_add
|
||||
if isinstance(t, str) and t.lower() not in existing_lower
|
||||
t for t in item_tag_to_add if isinstance(t, str)
|
||||
and t.lower() not in existing_lower
|
||||
]
|
||||
|
||||
updated_tag_list = [
|
||||
t for t in existing_tag_list if t not in removed_namespace_tag
|
||||
t for t in existing_tag_list
|
||||
if t not in removed_namespace_tag
|
||||
]
|
||||
updated_tag_list.extend(actual_tag_to_add)
|
||||
|
||||
@@ -777,13 +809,15 @@ class Add_Tag(Cmdlet):
|
||||
return 1
|
||||
|
||||
resolved_hash = (
|
||||
normalize_hash(hash_override) if hash_override else normalize_hash(raw_hash)
|
||||
normalize_hash(hash_override)
|
||||
if hash_override else normalize_hash(raw_hash)
|
||||
)
|
||||
if not resolved_hash and raw_path:
|
||||
try:
|
||||
p = Path(str(raw_path))
|
||||
stem = p.stem
|
||||
if len(stem) == 64 and all(c in "0123456789abcdef" for c in stem.lower()):
|
||||
if len(stem) == 64 and all(c in "0123456789abcdef"
|
||||
for c in stem.lower()):
|
||||
resolved_hash = stem.lower()
|
||||
elif p.exists() and p.is_file():
|
||||
resolved_hash = sha256_file(p)
|
||||
@@ -801,7 +835,10 @@ class Add_Tag(Cmdlet):
|
||||
try:
|
||||
backend = store_registry[str(store_name)]
|
||||
except Exception as exc:
|
||||
log(f"[add_tag] Error: Unknown store '{store_name}': {exc}", file=sys.stderr)
|
||||
log(
|
||||
f"[add_tag] Error: Unknown store '{store_name}': {exc}",
|
||||
file=sys.stderr
|
||||
)
|
||||
return 1
|
||||
|
||||
try:
|
||||
@@ -810,14 +847,18 @@ class Add_Tag(Cmdlet):
|
||||
existing_tag = []
|
||||
|
||||
existing_tag_list = [t for t in (existing_tag or []) if isinstance(t, str)]
|
||||
existing_lower = {t.lower() for t in existing_tag_list}
|
||||
existing_lower = {t.lower()
|
||||
for t in existing_tag_list}
|
||||
original_title = _extract_title_tag(existing_tag_list)
|
||||
|
||||
# Per-item tag list (do not mutate shared list)
|
||||
item_tag_to_add = list(tag_to_add)
|
||||
|
||||
if extract_template:
|
||||
candidates2 = _get_title_candidates_for_extraction(res, existing_tag_list)
|
||||
candidates2 = _get_title_candidates_for_extraction(
|
||||
res,
|
||||
existing_tag_list
|
||||
)
|
||||
extracted2, matched2 = _extract_tags_from_title_candidates(
|
||||
candidates2, extract_template
|
||||
)
|
||||
@@ -835,7 +876,8 @@ class Add_Tag(Cmdlet):
|
||||
extract_no_match_items += 1
|
||||
if extract_debug:
|
||||
rx_preview2 = (
|
||||
extract_debug_rx.pattern if extract_debug_rx else "<uncompiled>"
|
||||
extract_debug_rx.pattern
|
||||
if extract_debug_rx else "<uncompiled>"
|
||||
)
|
||||
cand_preview2 = "; ".join([repr(c) for c in candidates2[:3]])
|
||||
log(
|
||||
@@ -843,7 +885,11 @@ class Add_Tag(Cmdlet):
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
item_tag_to_add = collapse_namespace_tag(item_tag_to_add, "title", prefer="last")
|
||||
item_tag_to_add = collapse_namespace_tag(
|
||||
item_tag_to_add,
|
||||
"title",
|
||||
prefer="last"
|
||||
)
|
||||
|
||||
# Handle -duplicate logic (copy existing tag to new namespaces)
|
||||
if duplicate_arg:
|
||||
@@ -881,15 +927,19 @@ class Add_Tag(Cmdlet):
|
||||
|
||||
try:
|
||||
refreshed_tag, _src2 = backend.get_tag(resolved_hash, config=config)
|
||||
refreshed_list = [t for t in (refreshed_tag or []) if isinstance(t, str)]
|
||||
refreshed_list = [
|
||||
t for t in (refreshed_tag or []) if isinstance(t, str)
|
||||
]
|
||||
except Exception:
|
||||
refreshed_list = existing_tag_list
|
||||
|
||||
# Decide whether anything actually changed (case-sensitive so title casing updates count).
|
||||
if set(refreshed_list) != set(existing_tag_list):
|
||||
changed = True
|
||||
before_lower = {t.lower() for t in existing_tag_list}
|
||||
after_lower = {t.lower() for t in refreshed_list}
|
||||
before_lower = {t.lower()
|
||||
for t in existing_tag_list}
|
||||
after_lower = {t.lower()
|
||||
for t in refreshed_list}
|
||||
total_added += len(after_lower - before_lower)
|
||||
total_modified += 1
|
||||
|
||||
@@ -903,7 +953,12 @@ class Add_Tag(Cmdlet):
|
||||
_apply_title_to_result(res, final_title)
|
||||
|
||||
if final_title and (not original_title or final_title != original_title):
|
||||
_refresh_result_table_title(final_title, resolved_hash, str(store_name), raw_path)
|
||||
_refresh_result_table_title(
|
||||
final_title,
|
||||
resolved_hash,
|
||||
str(store_name),
|
||||
raw_path
|
||||
)
|
||||
|
||||
if changed:
|
||||
_refresh_tag_view(res, resolved_hash, str(store_name), raw_path, config)
|
||||
|
||||
Reference in New Issue
Block a user