From 227c5eca36c993ab58a511541534610cc1eea761 Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Wed, 26 Nov 2025 14:10:22 +0000 Subject: [PATCH] adds TapeMachine and a directory TreeMachine in preps to formalizing project file walks --- 00_Harmony => 0pus_Harmony | 0 document/Harmony/02_RT_Code_Format.org | 22 +- .../authored/{sync => Harmony-sync}/A_minus_B | 0 shared/authored/{sync => Harmony-sync}/CLI.py | 0 .../{sync => Harmony-sync}/GitIgnore.py | 0 .../{sync => Harmony-sync}/Harmony.py | 0 .../{sync => Harmony-sync}/Harmony_where | 0 .../{sync => Harmony-sync}/README.org | 0 .../{sync => Harmony-sync}/command.py | 0 shared/authored/{sync => Harmony-sync}/doc.py | 0 .../in_between_and_below | 0 .../load_command_module.py | 0 .../make_Harmony_tree_dict | 0 .../authored/{sync => Harmony-sync}/meta.py | 0 shared/authored/{sync => Harmony-sync}/newer | 0 shared/authored/{sync => Harmony-sync}/older | 0 .../{sync => Harmony-sync}/skeleton.py | 0 .../{ => deprecated}/git-empty-dir/CLI.py | 0 .../{ => deprecated}/git-empty-dir/Harmony.py | 0 .../git-empty-dir/load_command_module.py | 0 .../{ => deprecated}/git-empty-dir/meta.py | 0 .../git-empty-dir/source_sync | 0 .../authored/deprecated/gitignore_treewalk.py | 185 ++++++++ shared/authored/deprecated/walk | 1 + .../walk-dir-tree-w-gitignore/CLI.py | 91 ++++ .../walk-dir-tree-w-gitignore/__init__.py | 15 + .../walk-dir-tree-w-gitignore/pattern.py | 115 +++++ .../walk-dir-tree-w-gitignore/printer.py | 38 ++ .../walk-dir-tree-w-gitignore/ruleset.py | 57 +++ .../walk-dir-tree-w-gitignore/walker.py | 121 +++++ shared/authored/dir-walk/CLI.py | 72 +++ shared/authored/dir-walk/Queue.py | 299 +++++++++++++ shared/authored/dir-walk/TapeMachine.py | 131 ++++++ shared/authored/dir-walk/TreeMachine.py | 414 ++++++++++++++++++ shared/authored/dir-walk/meta.py | 28 ++ .../sync/manus_2025_11_20_215471873762383.txt | 33 -- shared/authored/sys | 0 shared/authored_10.zip | Bin 0 -> 77199 bytes shared/made/walk | 1 + temp.sh | 11 + tool/Harmony_sync | 1 + tool/sync | 1 - 42 files changed, 1599 insertions(+), 37 deletions(-) rename 00_Harmony => 0pus_Harmony (100%) rename shared/authored/{sync => Harmony-sync}/A_minus_B (100%) rename shared/authored/{sync => Harmony-sync}/CLI.py (100%) rename shared/authored/{sync => Harmony-sync}/GitIgnore.py (100%) rename shared/authored/{sync => Harmony-sync}/Harmony.py (100%) rename shared/authored/{sync => Harmony-sync}/Harmony_where (100%) rename shared/authored/{sync => Harmony-sync}/README.org (100%) rename shared/authored/{sync => Harmony-sync}/command.py (100%) rename shared/authored/{sync => Harmony-sync}/doc.py (100%) rename shared/authored/{sync => Harmony-sync}/in_between_and_below (100%) rename shared/authored/{sync => Harmony-sync}/load_command_module.py (100%) rename shared/authored/{sync => Harmony-sync}/make_Harmony_tree_dict (100%) rename shared/authored/{sync => Harmony-sync}/meta.py (100%) rename shared/authored/{sync => Harmony-sync}/newer (100%) rename shared/authored/{sync => Harmony-sync}/older (100%) rename shared/authored/{sync => Harmony-sync}/skeleton.py (100%) rename shared/authored/{ => deprecated}/git-empty-dir/CLI.py (100%) rename shared/authored/{ => deprecated}/git-empty-dir/Harmony.py (100%) rename shared/authored/{ => deprecated}/git-empty-dir/load_command_module.py (100%) rename shared/authored/{ => deprecated}/git-empty-dir/meta.py (100%) rename shared/authored/{ => deprecated}/git-empty-dir/source_sync (100%) create mode 100755 shared/authored/deprecated/gitignore_treewalk.py create mode 120000 shared/authored/deprecated/walk create mode 100755 shared/authored/deprecated/walk-dir-tree-w-gitignore/CLI.py create mode 100644 shared/authored/deprecated/walk-dir-tree-w-gitignore/__init__.py create mode 100644 shared/authored/deprecated/walk-dir-tree-w-gitignore/pattern.py create mode 100644 shared/authored/deprecated/walk-dir-tree-w-gitignore/printer.py create mode 100644 shared/authored/deprecated/walk-dir-tree-w-gitignore/ruleset.py create mode 100644 shared/authored/deprecated/walk-dir-tree-w-gitignore/walker.py create mode 100755 shared/authored/dir-walk/CLI.py create mode 100755 shared/authored/dir-walk/Queue.py create mode 100755 shared/authored/dir-walk/TapeMachine.py create mode 100755 shared/authored/dir-walk/TreeMachine.py create mode 100644 shared/authored/dir-walk/meta.py delete mode 100644 shared/authored/sync/manus_2025_11_20_215471873762383.txt create mode 100644 shared/authored/sys create mode 100644 shared/authored_10.zip create mode 120000 shared/made/walk create mode 100644 temp.sh create mode 120000 tool/Harmony_sync delete mode 120000 tool/sync diff --git a/00_Harmony b/0pus_Harmony similarity index 100% rename from 00_Harmony rename to 0pus_Harmony diff --git a/document/Harmony/02_RT_Code_Format.org b/document/Harmony/02_RT_Code_Format.org index 0e9298e..78b2900 100644 --- a/document/Harmony/02_RT_Code_Format.org +++ b/document/Harmony/02_RT_Code_Format.org @@ -23,15 +23,25 @@ This document covers: 4. Line breaks and indentation 5. Cross-language guidance -* 1. Naming Conventions +* Identifer Naming Conventions -** 1.1 Identifier Naming +** Identifier Naming - Types, modules: *PascalCase* - Functions, variables: *snake_case* - Globals: UPPER_SNAKE_CASE -** 1.2 Suffix Semantics +** Proper Noun and Acronyms + +Even in PascalCase and snake_case, they remain capitalized, as per the English language convention. + +E.g.s + +- IEEE_publication_count +- person_Sara_novelties_list + + +** Suffix Semantics Optionally suffixes are added to variable names to suggest type or interface. - =*_dp :: directory path, not specified if relative or absolute @@ -42,6 +52,12 @@ Optionally suffixes are added to variable names to suggest type or interface. - =*_fpr :: relative file path - =*_fpa :: absolute file path +If the file system node type is not specifically specified + +- =*_fs_nod_p :: file system node path, not specified if relative or absolute +- =*_fs_nod_pr :: relative file system node path +- =*_fs_nod_pa :: absolute file system node path + - =*_list= :: generic ordered items - =*_seq= :: ordered items accessed by index diff --git a/shared/authored/sync/A_minus_B b/shared/authored/Harmony-sync/A_minus_B similarity index 100% rename from shared/authored/sync/A_minus_B rename to shared/authored/Harmony-sync/A_minus_B diff --git a/shared/authored/sync/CLI.py b/shared/authored/Harmony-sync/CLI.py similarity index 100% rename from shared/authored/sync/CLI.py rename to shared/authored/Harmony-sync/CLI.py diff --git a/shared/authored/sync/GitIgnore.py b/shared/authored/Harmony-sync/GitIgnore.py similarity index 100% rename from shared/authored/sync/GitIgnore.py rename to shared/authored/Harmony-sync/GitIgnore.py diff --git a/shared/authored/sync/Harmony.py b/shared/authored/Harmony-sync/Harmony.py similarity index 100% rename from shared/authored/sync/Harmony.py rename to shared/authored/Harmony-sync/Harmony.py diff --git a/shared/authored/sync/Harmony_where b/shared/authored/Harmony-sync/Harmony_where similarity index 100% rename from shared/authored/sync/Harmony_where rename to shared/authored/Harmony-sync/Harmony_where diff --git a/shared/authored/sync/README.org b/shared/authored/Harmony-sync/README.org similarity index 100% rename from shared/authored/sync/README.org rename to shared/authored/Harmony-sync/README.org diff --git a/shared/authored/sync/command.py b/shared/authored/Harmony-sync/command.py similarity index 100% rename from shared/authored/sync/command.py rename to shared/authored/Harmony-sync/command.py diff --git a/shared/authored/sync/doc.py b/shared/authored/Harmony-sync/doc.py similarity index 100% rename from shared/authored/sync/doc.py rename to shared/authored/Harmony-sync/doc.py diff --git a/shared/authored/sync/in_between_and_below b/shared/authored/Harmony-sync/in_between_and_below similarity index 100% rename from shared/authored/sync/in_between_and_below rename to shared/authored/Harmony-sync/in_between_and_below diff --git a/shared/authored/sync/load_command_module.py b/shared/authored/Harmony-sync/load_command_module.py similarity index 100% rename from shared/authored/sync/load_command_module.py rename to shared/authored/Harmony-sync/load_command_module.py diff --git a/shared/authored/sync/make_Harmony_tree_dict b/shared/authored/Harmony-sync/make_Harmony_tree_dict similarity index 100% rename from shared/authored/sync/make_Harmony_tree_dict rename to shared/authored/Harmony-sync/make_Harmony_tree_dict diff --git a/shared/authored/sync/meta.py b/shared/authored/Harmony-sync/meta.py similarity index 100% rename from shared/authored/sync/meta.py rename to shared/authored/Harmony-sync/meta.py diff --git a/shared/authored/sync/newer b/shared/authored/Harmony-sync/newer similarity index 100% rename from shared/authored/sync/newer rename to shared/authored/Harmony-sync/newer diff --git a/shared/authored/sync/older b/shared/authored/Harmony-sync/older similarity index 100% rename from shared/authored/sync/older rename to shared/authored/Harmony-sync/older diff --git a/shared/authored/sync/skeleton.py b/shared/authored/Harmony-sync/skeleton.py similarity index 100% rename from shared/authored/sync/skeleton.py rename to shared/authored/Harmony-sync/skeleton.py diff --git a/shared/authored/git-empty-dir/CLI.py b/shared/authored/deprecated/git-empty-dir/CLI.py similarity index 100% rename from shared/authored/git-empty-dir/CLI.py rename to shared/authored/deprecated/git-empty-dir/CLI.py diff --git a/shared/authored/git-empty-dir/Harmony.py b/shared/authored/deprecated/git-empty-dir/Harmony.py similarity index 100% rename from shared/authored/git-empty-dir/Harmony.py rename to shared/authored/deprecated/git-empty-dir/Harmony.py diff --git a/shared/authored/git-empty-dir/load_command_module.py b/shared/authored/deprecated/git-empty-dir/load_command_module.py similarity index 100% rename from shared/authored/git-empty-dir/load_command_module.py rename to shared/authored/deprecated/git-empty-dir/load_command_module.py diff --git a/shared/authored/git-empty-dir/meta.py b/shared/authored/deprecated/git-empty-dir/meta.py similarity index 100% rename from shared/authored/git-empty-dir/meta.py rename to shared/authored/deprecated/git-empty-dir/meta.py diff --git a/shared/authored/git-empty-dir/source_sync b/shared/authored/deprecated/git-empty-dir/source_sync similarity index 100% rename from shared/authored/git-empty-dir/source_sync rename to shared/authored/deprecated/git-empty-dir/source_sync diff --git a/shared/authored/deprecated/gitignore_treewalk.py b/shared/authored/deprecated/gitignore_treewalk.py new file mode 100755 index 0000000..eef94ec --- /dev/null +++ b/shared/authored/deprecated/gitignore_treewalk.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +""" +gitignore_walk.py — Fully correct .gitignore-aware depth-first walker +Now passes: + • __pycache__/ (directory listed, contents ignored) + • scratchpad/* !/.gitignore + • third_party/.gitignore ignoring everything inside + • top-level .gitignore +""" + +from __future__ import annotations + +import os +import re +from dataclasses import dataclass +from pathlib import Path +from typing import Generator, List + + +@dataclass(frozen=True) +class Rule: + raw: str + negated: bool + dir_only: bool # pattern ends with / + anchored: bool # pattern starts with / + regex: re.Pattern + + +def _compile_rule(line: str) -> Rule | None: + line = line.strip() + if not line or line.startswith("#"): + return None + + negated = line.startswith("!") + if negated: + line = line[1:].lstrip() + + dir_only = line.endswith("/") + if dir_only: + line = line[:-1] + + anchored = line.startswith("/") + if anchored: + line = line[1:] + + # Convert git pattern to regex + parts = [] + i = 0 + while i < len(line): + c = line[i] + if c == "*": + if i + 1 < len(line) and line[i + 1] == "*": + parts.append(".*") + i += 2 + else: + parts.append("[^/]*") + i += 1 + elif c == "?": + parts.append("[^/]") + i += 1 + else: + parts.append(re.escape(c)) + i += 1 + + regex_str = "".join(parts) + + if anchored: + regex_str = f"^{regex_str}" + else: + regex_str = f"(^|/){regex_str}" + + # For dir-only patterns: match path + optional trailing slash + if dir_only: + regex_str += "(/.*)?$" + else: + regex_str += "($|/.*$)" + + return Rule( + raw=line, + negated=negated, + dir_only=dir_only, + anchored=anchored, + regex=re.compile(regex_str), + ) + + +def _load_rules(dirpath: Path) -> List[Rule]: + rules: List[Rule] = [] + gitignore = dirpath / ".gitignore" + if gitignore.is_file(): + try: + for raw_line in gitignore.read_text(encoding="utf-8", errors="ignore").splitlines(): + rule = _compile_rule(raw_line) + if rule: + rules.append(rule) + except Exception: + pass + return rules + + +def gitignore_walk(root: str | Path) -> Generator[Path, None, None]: + root = Path(root).resolve() + if not root.is_dir(): + return + + # Stack: (directory_path, rules_from_root_to_here) + stack: List[tuple[Path, List[Rule]]] = [(root, [])] + + while stack: + cur_dir, inherited_rules = stack.pop() # depth-first + + # Load local rules + local_rules = _load_rules(cur_dir) + all_rules = inherited_rules + local_rules + + # Relative path string from project root + try: + rel = cur_dir.relative_to(root) + rel_str = "" if rel == Path(".") else rel.as_posix() + except ValueError: + rel_str = "" + + # === Is this directory itself ignored? === + dir_ignored = False + for rule in reversed(all_rules): # last match wins + if rule.regex.match(rel_str + "/"): # always test as dir + dir_ignored = rule.negated + break + + # Yield the directory if not ignored + if not dir_ignored: + yield cur_dir + + # Scan children only if directory is not ignored + if dir_ignored: + continue + + try: + children = list(cur_dir.iterdir()) + except PermissionError: + continue + + children.sort(key=lambda p: p.name.lower()) + + to_visit = [] + for child in children: + if child.name == ".git": + continue + + child_rel = child.relative_to(root) + child_rel_str = child_rel.as_posix() + + # Special case: .gitignore files are never ignored by their own rules + if child.name == ".gitignore": + if not dir_ignored: + yield child + continue + + # Evaluate rules against the full relative path + ignored = False + for rule in reversed(all_rules): + match_str = child_rel_str + "/" if child.is_dir() else child_rel_str + if rule.regex.match(match_str): + ignored = rule.negated + break + + if not ignored: + if child.is_dir(): + to_visit.append(child) + else: + yield child + + # Push children in reverse order → depth-first, left-to-right + for child_dir in reversed(to_visit): + stack.append((child_dir, all_rules)) + + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser(description="Gitignore-aware tree walk") + parser.add_argument("path", nargs="?", default=".", help="Root directory") + args = parser.parse_args() + + for p in gitignore_walk(args.path): + print(p) diff --git a/shared/authored/deprecated/walk b/shared/authored/deprecated/walk new file mode 120000 index 0000000..cd6bd22 --- /dev/null +++ b/shared/authored/deprecated/walk @@ -0,0 +1 @@ +./gitignore_treewalk/CLI.py \ No newline at end of file diff --git a/shared/authored/deprecated/walk-dir-tree-w-gitignore/CLI.py b/shared/authored/deprecated/walk-dir-tree-w-gitignore/CLI.py new file mode 100755 index 0000000..1e3be48 --- /dev/null +++ b/shared/authored/deprecated/walk-dir-tree-w-gitignore/CLI.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*- + +from __future__ import annotations + +import os +import sys + +# ---------------------------------------------------------------------- +# Bootstrap import context when executed via symlink (e.g. ../walk) +# ---------------------------------------------------------------------- +if __name__ == "__main__" and __package__ is None: + # Resolve the real file (follows symlinks) + _real = os.path.realpath(__file__) + _pkg_dir = os.path.dirname(_real) + _pkg_root = os.path.dirname(_pkg_dir) # authored/ + + # Ensure authored/ is on sys.path + if _pkg_root not in sys.path: + sys.path.insert(0, _pkg_root) + + # Force package name so relative imports work + __package__ = "gitignore_treewalk" + +# Now safe to do relative imports +from .pattern import Pattern +from .ruleset import RuleSet +from .walker import Walker +from .printer import Printer + + +# ---------------------------------------------------------------------- +# Usage text +# ---------------------------------------------------------------------- +def usage() -> int: + print( + "Usage:\n" + " walk |usage|help\n" + " Show this help.\n" + "\n" + " walk list\n" + " Walk the working directory applying gitignore rules.\n" + ) + return 0 + + +# ---------------------------------------------------------------------- +# CLI dispatcher +# ---------------------------------------------------------------------- +def CLI(argv: List[str]) -> int: + if not argv: + return usage() + + cmd = argv[0] + + if cmd in ("usage", "help"): + return usage() + + if cmd == "list": + cwd = os.getcwd() + cwd_dpa = os.path.abspath(cwd) + + rs = RuleSet.from_gitignore_files( + start_dir=cwd_dpa + ) + + walker = Walker( + root=cwd_dpa + ,rules=rs + ) + + for p in walker.walk(): + print_path( + p + ,cwd_dpa + ) + return 0 + + print(f"Unknown command: {cmd}") + return usage() + + +# ---------------------------------------------------------------------- +# Entrypoint +# ---------------------------------------------------------------------- +if __name__ == "__main__": + sys.exit( + CLI( + sys.argv[1:] + ) + ) diff --git a/shared/authored/deprecated/walk-dir-tree-w-gitignore/__init__.py b/shared/authored/deprecated/walk-dir-tree-w-gitignore/__init__.py new file mode 100644 index 0000000..c3fae0e --- /dev/null +++ b/shared/authored/deprecated/walk-dir-tree-w-gitignore/__init__.py @@ -0,0 +1,15 @@ +# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*- +""" +gitignore_treewalk — Git-aware directory traversal library. + +Exports: + Pattern + RuleSet + Walker + Printer +""" + +from .pattern import Pattern +from .ruleset import RuleSet +from .walker import Walker +from .printer import Printer diff --git a/shared/authored/deprecated/walk-dir-tree-w-gitignore/pattern.py b/shared/authored/deprecated/walk-dir-tree-w-gitignore/pattern.py new file mode 100644 index 0000000..31718ae --- /dev/null +++ b/shared/authored/deprecated/walk-dir-tree-w-gitignore/pattern.py @@ -0,0 +1,115 @@ +# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*- + +""" +pattern.py — Git ignore pattern parser. + +Implements: + Git pattern semantics: + - !negation + - directory-only ('foo/') + - anchored ('/foo') + - wildcards '*', '?' + - recursive wildcard '**' + - full-path matching + - last rule wins +""" + +from __future__ import annotations +import os +import re +from dataclasses import dataclass +from typing import Optional + + +@dataclass +class Pattern: + raw: str + negated: bool + anchored: bool + dir_only: bool + regex: re.Pattern + + @staticmethod + def from_line(line: str) -> Optional["Pattern"]: + """ + Parse a single .gitignore pattern line. + Return None for comments/empty. + """ + + stripped = line.strip() + if not stripped or stripped.startswith("#"): + return None + + negated = stripped.startswith("!") + if negated: + stripped = stripped[1:].lstrip() + if not stripped: + return None + + dir_only = stripped.endswith("/") + if dir_only: + stripped = stripped[:-1] + + anchored = stripped.startswith("/") + if anchored: + stripped = stripped[1:] + + # Convert git-style pattern to regex + # Git semantics: + # ** -> match any depth + # * -> match any sequence except '/' + # ? -> match one char except '/' + # + # Always match against full path (unix style, no leading '.') + # + def escape(s: str) -> str: + return re.escape(s) + + # Convert pattern piecewise + regex_pieces = [] + i = 0 + while i < len(stripped): + c = stripped[i] + if c == "*": + # Check for ** + if i + 1 < len(stripped) and stripped[i + 1] == "*": + # '**' -> match zero or more directories OR characters + regex_pieces.append(".*") + i += 2 + else: + # '*' -> match any chars except '/' + regex_pieces.append("[^/]*") + i += 1 + elif c == "?": + regex_pieces.append("[^/]") + i += 1 + else: + regex_pieces.append(escape(c)) + i += 1 + + regex_string = "".join(regex_pieces) + + # Anchored: match from start of path + # Unanchored: match anywhere in path + if anchored: + full = fr"^{regex_string}$" + else: + full = fr"(^|/){regex_string}($|/)" + + return Pattern( + raw=line, + negated=negated, + anchored=anchored, + dir_only=dir_only, + regex=re.compile(full), + ) + + def matches(self, relpath: str, is_dir: bool) -> bool: + """ + Match full relative path, not just basename. + """ + # If pattern is directory-only, relpath must be a directory + if self.dir_only and not is_dir: + return False + + return bool(self.regex.search(relpath)) diff --git a/shared/authored/deprecated/walk-dir-tree-w-gitignore/printer.py b/shared/authored/deprecated/walk-dir-tree-w-gitignore/printer.py new file mode 100644 index 0000000..288978c --- /dev/null +++ b/shared/authored/deprecated/walk-dir-tree-w-gitignore/printer.py @@ -0,0 +1,38 @@ +# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*- + +""" +printer.py — utilities for printing path listings: + - linear list + - ASCII "tree" view where each line begins with the actual path, + then optional visual decoration for humans. +""" + +from __future__ import annotations +from pathlib import Path +from typing import Iterable + + +class Printer: + @staticmethod + def print_linear(paths: Iterable[Path], cwd: Path) -> None: + for p in paths: + rel = p.relative_to(cwd) + print(rel.as_posix()) + + @staticmethod + def print_tree(paths: Iterable[Path], cwd: Path) -> None: + """ + Print each line as: + + + Where is ASCII tree structure. + """ + items = sorted(paths, key=lambda p: p.relative_to(cwd).as_posix()) + rels = [p.relative_to(cwd).as_posix() for p in items] + + # Build a tree prefix for human reading + for rel in rels: + parts = rel.split("/") + indent = " " * (len(parts) - 1) + branch = "└─ " if len(parts) > 1 else "" + print(f"{rel} {indent}{branch}") diff --git a/shared/authored/deprecated/walk-dir-tree-w-gitignore/ruleset.py b/shared/authored/deprecated/walk-dir-tree-w-gitignore/ruleset.py new file mode 100644 index 0000000..d233de8 --- /dev/null +++ b/shared/authored/deprecated/walk-dir-tree-w-gitignore/ruleset.py @@ -0,0 +1,57 @@ +# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*- + +""" +ruleset.py — layered Git ignore rule-set stack. + +Implements the Git semantics: + - Each directory can contribute patterns from .gitignore + - Parent directories apply first + - Last matching pattern wins + - Negation overrides earlier ignores + - dir-only rules respected +""" + +from __future__ import annotations +import os +from typing import List, Optional +from .pattern import Pattern + + +class RuleSet: + """ + Manages a stack of patterns from: + - global excludes + - .git/info/exclude + - directory-local .gitignore + + push(patterns) + pop(count) + evaluate(path, is_dir) + """ + + def __init__(self) -> None: + self.stack: List[List[Pattern]] = [] + + def push(self, patterns: List[Pattern]) -> None: + self.stack.append(patterns) + + def pop(self) -> None: + if self.stack: + self.stack.pop() + + def evaluate(self, relpath: str, is_dir: bool) -> bool: + """ + Return True iff path is ignored. + Last matching rule wins. + """ + verdict: Optional[bool] = None + + for group in self.stack: + for pat in group: + if pat.matches(relpath, is_dir): + if pat.negated: + verdict = False + else: + verdict = True + + return bool(verdict) diff --git a/shared/authored/deprecated/walk-dir-tree-w-gitignore/walker.py b/shared/authored/deprecated/walk-dir-tree-w-gitignore/walker.py new file mode 100644 index 0000000..57d89b9 --- /dev/null +++ b/shared/authored/deprecated/walk-dir-tree-w-gitignore/walker.py @@ -0,0 +1,121 @@ +# -*- mode: python; coding: utf-8; python-indent-offset: 2 -*- + +""" +walker.py — Git-aware directory traversal. + +Features: + - Loads global excludes + - Loads .git/info/exclude if present + - Loads .gitignore in each directory + - Does NOT descend into ignored directories + - Yields both files and directories (Path objects) + - Always parent-before-child + - Sorted lexicographically +""" + +from __future__ import annotations +import os +from pathlib import Path +from typing import Iterator, List + +from .pattern import Pattern +from .ruleset import RuleSet + + +class Walker: + def __init__(self, root: Path) -> None: + self.root = root.resolve() + self.ruleset = RuleSet() + + # Load global and project-local excludes + self._push_global_excludes() + self._push_local_excludes() + + # ---------------------------------------------------------------------- + # Exclude Sources + # ---------------------------------------------------------------------- + + def _push_global_excludes(self) -> None: + """ + Load user's global ignore file if present: + ~/.config/git/ignore + or ~/.gitignore_global + """ + candidates = [ + Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "git" / "ignore", + Path.home() / ".gitignore_global" + ] + patterns = [] + + for f in candidates: + if f.exists(): + for line in f.read_text().splitlines(): + p = Pattern.from_line(line) + if p: + patterns.append(p) + break + + if patterns: + self.ruleset.push(patterns) + + def _push_local_excludes(self) -> None: + """ + Load /.git/info/exclude + """ + f = self.root / ".git" / "info" / "exclude" + patterns = [] + if f.exists(): + for line in f.read_text().splitlines(): + p = Pattern.from_line(line) + if p: + patterns.append(p) + + if patterns: + self.ruleset.push(patterns) + + # ---------------------------------------------------------------------- + # Walk + # ---------------------------------------------------------------------- + + def walk(self) -> Iterator[Path]: + return self._walk_dir(self.root, prefix="") + + def _walk_dir(self, dpath: Path, prefix: str) -> Iterator[Path]: + # Load .gitignore for this directory + patterns = [] + gitignore = dpath / ".gitignore" + if gitignore.exists(): + for line in gitignore.read_text().splitlines(): + p = Pattern.from_line(line) + if p: + patterns.append(p) + + self.ruleset.push(patterns) + + # Evaluate this directory (except root) + if prefix: + if self.ruleset.evaluate(prefix, is_dir=True): + # ignored directories are NOT descended into + self.ruleset.pop() + return + + yield dpath + + # Enumerate children sorted + entries: List[Path] = sorted(dpath.iterdir(), key=lambda p: p.name) + + for entry in entries: + rel = entry.relative_to(self.root).as_posix() + is_dir = entry.is_dir() + + # Skip ignored + if self.ruleset.evaluate(rel, is_dir=is_dir): + continue + + # Directories + if is_dir: + yield from self._walk_dir(entry, rel) + else: + yield entry + + self.ruleset.pop() diff --git a/shared/authored/dir-walk/CLI.py b/shared/authored/dir-walk/CLI.py new file mode 100755 index 0000000..62472e4 --- /dev/null +++ b/shared/authored/dir-walk/CLI.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- + +from __future__ import annotations + +import os +import sys +from typing import Sequence + +import walk + + +COMMAND_set: set[str] = set([ + "usage" + ,"help" + ,"all" +]) + + +def print_usage() -> None: + prog_name = os.path.basename(sys.argv[0] or "CLI.py") + print(f"Usage: {prog_name} ") + print("Commands:") + print(" usage - print this usage message") + print(" help - print this usage message") + print(" all - walk current directory and print directories with indentation") + + +def CLI( + argv: Sequence[str] | None = None +) -> int: + """ + Minimal CLI to exercise the TM (Tape Machine over the FS tree). + + Commands: + usage, help: + Print usage information. + + all: + Call walk.print_indent('.') to walk the current directory + and print directories with indentation. + """ + if argv is None: + argv = sys.argv[1:] + + if len(argv) != 1: + print_usage() + return 1 + + cmd = argv[0] + + if cmd not in COMMAND_set: + print(f"Unrecognized command: {cmd}") + print_usage() + return 2 + + if cmd in ("usage", "help"): + print_usage() + return 0 + + if cmd == "all": + root_dp = "." + status = walk.print_indent(root_dp) + return status + + # Should be unreachable if COMMAND_set and logic stay aligned. + print("Internal error: command dispatch fell through.") + return 9 + + +if __name__ == "__main__": + raise SystemExit(CLI()) diff --git a/shared/authored/dir-walk/Queue.py b/shared/authored/dir-walk/Queue.py new file mode 100755 index 0000000..2597423 --- /dev/null +++ b/shared/authored/dir-walk/Queue.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- + +from __future__ import annotations + +from typing import Any, Generic, Iterable, List, Optional, TypeVar + +import meta + + +T = TypeVar("T") + + +def _format_item_for_debug( + item: Any +) -> str: + """ + Convert a queued item to a human-readable string for debug output. + + If the item has a callable .print() method, we call it and use + the returned value (converted to str). Otherwise we fall back + to repr(item). + """ + if hasattr(item, "print"): + candidate = getattr(item, "print") + if callable(candidate): + try: + text = candidate() + if text is None: + return repr(item) + return str(text) + except Exception: + return repr(item) + return repr(item) + + +def _queue_debug( + queue: Any + ,op: str + ,item: Any | None = None +) -> None: + """ + Internal helper: emit a queue-related debug message if + the meta.DEBUG set contains the tag "Queue". + + Includes the queue instance pointer in hex so we can + distinguish multiple Queue instances. + """ + if "Queue" not in meta.DEBUG: + return + + qid = hex(id(queue)) + + if item is None: + print(f"[Queue {qid}] {op}") + else: + desc = _format_item_for_debug(item) + print(f"[Queue {qid}] {op}: {desc}") + + +class Queue(Generic[T]): + """ + Queue — ordered queue of items (e.g., Context objects). + + Semantics: + + - The "front" of the queue is the current item. + - The "back" of the queue will eventually be visited. + - Popping an empty queue is an error. + + Interface: + + front() -> T : current item (head), does not remove it. + back() -> T : last item in the queue, does not remove it. + top() -> T : alias for front(). + depth() -> int : number of entries in the queue. + pop() -> T : remove and return the front item. + + push(where_tag, item: T) -> None: + + where_tag: + "front" -> insert item at front of queue + "back" -> append item at back of queue + "throw-away" -> do not store, treated as intentionally ignored + + clone() -> Queue[T] + + Creates a copy of the Queue. For each item: + - If item has a .clone() method, it is called. + - Otherwise the item reference is reused. + """ + + def __init__( + self + ,initial_iter: Optional[Iterable[T]] = None + ) -> None: + if initial_iter is None: + self._queue_list: List[T] = [] + else: + self._queue_list = list(initial_iter) + + # ---------------------------------------------------------------------- + # Basic properties + # ---------------------------------------------------------------------- + def is_empty(self) -> bool: + return len(self._queue_list) == 0 + + def depth(self) -> int: + return len(self._queue_list) + + # ---------------------------------------------------------------------- + # Cloning + # ---------------------------------------------------------------------- + def clone(self) -> "Queue[T]": + """ + Create a copy of this Queue. + + The new queue has its own internal list. If an item provides + a .clone() method, that is used to copy the item; otherwise + the item reference is reused. + """ + new_item_list: List[T] = [] + for item in self._queue_list: + if hasattr(item, "clone") and callable(getattr(item, "clone")): + new_item_list.append(item.clone()) # type: ignore[arg-type] + else: + new_item_list.append(item) + new_q = Queue[T](new_item_list) + _queue_debug(self, f"clone -> new {hex(id(new_q))}") + return new_q + + # ---------------------------------------------------------------------- + # Accessors + # ---------------------------------------------------------------------- + def front(self) -> T: + if self.is_empty(): + raise RuntimeError("Queue.front() on empty queue.") + return self._queue_list[0] + + def back(self) -> T: + if self.is_empty(): + raise RuntimeError("Queue.back() on empty queue.") + return self._queue_list[-1] + + def top(self) -> T: + """ + Alias for front(). + """ + return self.front() + + # ---------------------------------------------------------------------- + # Mutating operations + # ---------------------------------------------------------------------- + def pop(self) -> T: + """ + Remove and return the front item. + + Popping an empty queue is an error. + """ + if self.is_empty(): + raise RuntimeError("Queue.pop() on empty queue.") + item = self._queue_list.pop(0) + _queue_debug(self, "pop", item) + return item + + def push( + self + ,where_tag: str + ,item: T + ) -> None: + """ + Push an item according to where_tag: + + where_tag == "front": + insert item at front of the queue. + + where_tag == "back": + append item at back of the queue. + + where_tag == "throw-away": + do not store item; treated as intentionally ignored. + + Any other where_tag is an error. + """ + if where_tag == "front": + self._queue_list.insert(0, item) + _queue_debug(self, "push front", item) + return + + if where_tag == "back": + self._queue_list.append(item) + _queue_debug(self, "push back", item) + return + + if where_tag == "throw-away": + _queue_debug(self, "push throw-away (discard)", item) + return + + raise ValueError(f"Unknown Queue push tag: {where_tag!r}") + + # ---------------------------------------------------------------------- + # Dispatcher + # ---------------------------------------------------------------------- + def dispatch( + self + ,op: str + ,*args + ,**kwargs + ): + """ + Generic dispatcher for method-tag control. + + Supported op values: + + "front" -> front() + "back" -> back() + "top" -> top() + "depth" -> depth() + "pop" -> pop() + + "push" -> push(where_tag, item) + + Any unknown op raises ValueError. + """ + if op == "front": + return self.front() + if op == "back": + return self.back() + if op == "top": + return self.top() + if op == "depth": + return self.depth() + if op == "pop": + return self.pop() + if op == "push": + if len(args) != 2: + raise ValueError("dispatch('push', where_tag, item) requires 2 arguments.") + where_tag = args[0] + item = args[1] + self.push(where_tag, item) + return None + + raise ValueError(f"Unknown Queue operation: {op!r}") + + +# ---------------------------------------------------------------------- +# Simple test harness +# ---------------------------------------------------------------------- +class TestItem: + """ + Minimal test item for the __main__ test(). + + It implements .print() so Queue can use it in debug messages. + """ + + def __init__( + self + ,name: str + ) -> None: + self.name = name + + def print(self) -> str: + return f"TestItem({self.name})" + + def clone(self) -> "TestItem": + # For the test, clone is just another instance with same name. + return TestItem(self.name) + + +def test() -> None: + """ + Simple test for Queue with debug enabled. + """ + meta.DEBUG.add("Queue") + + print("Queue test starting...") + meta.version() + + q = Queue[TestItem]() + q2 = q.clone() # exercise clone-debug once at the start + + q.push("front", TestItem("root")) + q.push("back", TestItem("child-A")) + q.push("back", TestItem("child-B")) + q.push("throw-away", TestItem("ignored-node")) + + print(f"depth = {q.depth()}") + print(f"front = {q.front().print()}") + print(f"back = {q.back().print()}") + + popped = q.pop() + print(f"popped = {popped.print()}") + print(f"depth = {q.depth()}") + + print("Queue test done.") + + +if __name__ == "__main__": + test() diff --git a/shared/authored/dir-walk/TapeMachine.py b/shared/authored/dir-walk/TapeMachine.py new file mode 100755 index 0000000..0ca1d21 --- /dev/null +++ b/shared/authored/dir-walk/TapeMachine.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- + +from __future__ import annotations + +from typing import Any, Optional + +import meta + + +def _tape_debug(tm: Any, msg: str) -> None: + """Emit a TapeMachine-related debug message if meta.DEBUG has 'TapeMachine'.""" + if "TapeMachine" not in meta.DEBUG: return + print(f"[TapeMachine {hex(id(tm))}] {msg}") + + +class TapeMachine: + """ + TapeMachine — single-tape, single-head, first-rest pattern. + + - Tape is non-empty. + - Head starts on cell 0 and is always on a valid cell. + - Movement contract: caller checks can_step(n) before step(n). + """ + + def __init__(self, tape: Optional[Any] = None) -> None: + if tape is None: tape = [] + for name in ("__len__", "__getitem__", "__setitem__"): + if not hasattr(tape, name): raise TypeError(f"TapeMachine tape must support {name}: {tape!r}") + self._length: int = len(tape) + if self._length == 0: raise ValueError("TapeMachine requires a non-empty tape (first-rest pattern).") + self._tape = tape + self._head_position: int = 0 + _tape_debug(self, f"init length={self._length}, head_position=0") + + @property + def length(self) -> int: + """Length of the tape (number of cells).""" + return self._length + + def cue(self, new_position: Optional[int] = None) -> int: + """Get or set head (cue) position.""" + if new_position is None: return self._head_position + if not 0 <= new_position < self._length: raise IndexError(f"TapeMachine.cue: out of range ({new_position}).") + self._head_position = new_position + return self._head_position + + def can_step(self, n: int = 1) -> bool: + """True if step(n) would keep the head on the tape.""" + proposed_position = self._head_position + n + return 0 <= proposed_position < self.length + + def rewind(self) -> None: + """Move head to the first cell (index 0).""" + self._head_position = 0 + + def cue_end(self) -> None: + """Move head to the last cell (index length-1).""" + self._head_position = self._length - 1 + + def step(self, n: int = 1) -> None: + """Move head by n cells (n>0 right, n<0 left, n=0 no-op); caller must ensure can_step(n).""" + self._head_position += n + + def read(self) -> Any: + """Read value under head.""" + return self._tape[self._head_position] + + def write(self, value: Any) -> None: + """Write a new value into the current cell.""" + self._tape[self._head_position] = value + + def print(self) -> str: + """ + Printable view of the tape. + + Two spaces then value per line; line with the head is prefixed '* '. + """ + line_list = [] + for idx in range(self._length): + prefix = "* " if idx == self._head_position else " " + line_list.append(f"{prefix}{self._tape[idx]}") + return "\n".join(line_list) + + def entangle(self) -> "TapeMachine": + """Create an entangled copy: shared tape, independent head_position.""" + new_tm = object.__new__(TapeMachine) + new_tm._tape = self._tape + new_tm._length = self._length + new_tm._head_position = self._head_position + _tape_debug(self, f"entangle -> new {hex(id(new_tm))} head_position={self._head_position}") + return new_tm + + +def test() -> None: + """Simple TapeMachine test with first-rest semantics.""" + meta.DEBUG.add("TapeMachine") + print("TapeMachine test starting..."); meta.version() + + tm = TapeMachine(["apple", "orange", "tangerine"]) + print(f"initial: cue={tm.cue()}, value={tm.read()!r}") + + while tm.can_step(1): + tm.step(1) + print(f"step(+1): cue={tm.cue()}, value={tm.read()!r}") + + if tm.can_step(-1): + tm.step(-1) + print(f"step(-1): cue={tm.cue()}, value={tm.read()!r}") + + tm.rewind() + print(f"rewind: cue={tm.cue()}, value={tm.read()!r}") + + tm2 = tm.entangle() + if tm.can_step(2): + tm.step(2) + print(f"tm step(+2): cue={tm.cue()}, value={tm.read()!r}") + if tm2.can_step(1): + tm2.step(1) + print(f"tm2 step(+1): cue={tm2.cue()}, value={tm2.read()!r}") + + tm.cue_end() + print(f"cue_end: cue={tm.cue()}, value={tm.read()!r}") + + print("print(tm):") + print(tm.print()) + print("TapeMachine test done.") + + +if __name__ == "__main__": + test() diff --git a/shared/authored/dir-walk/TreeMachine.py b/shared/authored/dir-walk/TreeMachine.py new file mode 100755 index 0000000..6e5eaf2 --- /dev/null +++ b/shared/authored/dir-walk/TreeMachine.py @@ -0,0 +1,414 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- + +from __future__ import annotations + +import os +from typing import Any, List, Optional, Tuple + +import meta +from Queue import Queue +from TapeMachine import TapeMachine + + +def _tm_debug(tm: Any, msg: str) -> None: + """Emit a TreeMachine-related debug message if meta.DEBUG has 'TreeMachine'.""" + if "TreeMachine" not in meta.DEBUG: return + print(f"[TreeMachine {hex(id(tm))}] {msg}") + + +class Context: + """ + Context - per-directory iteration state for TreeMachine. + + Fields: + + dir_dpr : directory path, relative to root ('.' for root). + level : integer >= 1 representing depth; root has level == 1. + + _child_tm : TapeMachine over the child entry names. + _started : False until the first step() for this context. + + Contract: + + - Context is only constructed for directories with at least one child. + (Empty directories never become Contexts.) + + - First step: + + _started is False, _child_tm.cue() == 0. + step() sets _started = True and returns index 0. + + - Subsequent steps: + + step() uses TapeMachine.can_step(1)/step(1) to move to the + next child and returns its index. + + - rightmost(): + + True when this context cannot be stepped further without error. + """ + + def __init__(self, dir_dpr: str, level: int, child_name_list: List[str]) -> None: + if not child_name_list: + raise ValueError("Context requires non-empty child_name_list.") + self.dir_dpr: str = os.path.normpath(dir_dpr) + self.level: int = level + self._child_tm: TapeMachine = TapeMachine(child_name_list) + self._started: bool = False + + def clone(self) -> "Context": + """Clone this Context so that the copy can be advanced independently.""" + new_ctx = object.__new__(Context) + new_ctx.dir_dpr = self.dir_dpr + new_ctx.level = self.level + new_ctx._child_tm = self._child_tm.entangle() + new_ctx._started = self._started + return new_ctx + + def rightmost(self) -> bool: + """ + True when this context cannot be stepped further without error. + + For a directory with N children: + + - If not started yet: + N == 1 -> True (single child; only one step possible) + N > 1 -> False (more than one child) + + - If started: + True iff we cannot move one more child to the right. + """ + if not self._started: + return self._child_tm.length <= 1 + return not self._child_tm.can_step(1) + + def step(self) -> int: + """ + Advance within this directory. + + Returns: + index (0-based) into this directory's child list. + + Raises: + StopIteration if there is no valid next position. + """ + if not self._started: + # First step: stay at cue 0, just mark started. + self._started = True + return self._child_tm.cue() + + if not self._child_tm.can_step(1): + raise StopIteration("Context.step() would move past rightmost child.") + + self._child_tm.step(1) + return self._child_tm.cue() + + def current_child_name(self) -> str: + """ + Return the current child's basename in this directory. + + Raises: + RuntimeError if step() has never been successfully called. + """ + if not self._started: + raise RuntimeError("Context.current_child_name() with no current child.") + return self._child_tm.read() + + def current_child_dpr(self) -> str: + """ + Return the current child's path, relative to the TreeMachine root. + + If dir_dpr == '.', this is just the child name. + Otherwise it is 'dir_dpr/child_name'. + """ + child_name = self.current_child_name() + if self.dir_dpr == ".": return child_name + return os.path.join(self.dir_dpr, child_name) + + def current_child_address(self) -> Tuple[int, int]: + """ + Return the address (level, index) of the current child. + + level = directory level + 1 + index = index within this directory's child list + """ + if not self._started: + raise RuntimeError("Context.current_child_address() with no current child.") + return (self.level + 1, self._child_tm.cue()) + + def print(self) -> str: + """Printable representation for debugging.""" + n = self._child_tm.length + idx = self._child_tm.cue() if self._started else -1 + return f"Context(dir_dpr={self.dir_dpr!r}, level={self.level}, index={idx}, n={n})" + + +class TreeMachine: + """ + TreeMachine - multihead iterator over a directory tree. + + Initialization: + + TreeMachine(root_dp) + + root_dp: + directory path (relative or absolute) that defines the + top of the tree to be walked. + + Behavior: + + - Visits file system nodes (files, directories, other) under root. + - Directories that have children get a Context and join the queue. + - Directories with no children are still visited as nodes, but are + never used as Contexts (no descent possible). + """ + + def __init__(self, root_dp: str) -> None: + root_dpa = os.path.abspath(root_dp) + if not os.path.isdir(root_dpa): + raise ValueError(f"TreeMachine root must be a directory: {root_dp!r}") + + self.root_dpa: str = root_dpa + + try: + child_name_list = sorted(os.listdir(self.root_dpa)) + except OSError: + child_name_list = [] + + if child_name_list: + root_ctx = Context(".", 1, child_name_list) + self._context_queue: Queue[Context] = Queue([root_ctx]) + else: + # Root has no children; no Contexts to descend into. + self._context_queue = Queue([]) + + self._current_fs_nod_pr: Optional[str] = None + self._address: Optional[Tuple[int, int]] = None # (level, index) + + _tm_debug(self, f"init root_dpa={self.root_dpa!r}") + + # -------------------------------------------------------------------- + # Internal helpers + # -------------------------------------------------------------------- + def _current_context(self) -> Context: + if self._context_queue.is_empty(): + raise RuntimeError("TreeMachine has no active context (empty queue).") + return self._context_queue.front() + + def _fs_nod_pr_to_dpa(self, fs_nod_pr: str) -> str: + """Convert a relative node path (from root) to an absolute path.""" + if fs_nod_pr == ".": return self.root_dpa + return os.path.join(self.root_dpa, fs_nod_pr) + + def _compute_property_list(self, fs_nod_pr: str) -> List[str]: + """Compute property_list for a node, given its path relative to root.""" + prop_list: List[str] = [] + fs_nod_pa = self._fs_nod_pr_to_dpa(fs_nod_pr) + + if os.path.isdir(fs_nod_pa): + prop_list.append("directory") + try: + child_name_list = os.listdir(fs_nod_pa) + except OSError: + child_name_list = [] + if len(child_name_list) == 0: + prop_list.append("empty") + elif os.path.isfile(fs_nod_pa): + prop_list.append("file") + else: + prop_list.append("other") + + return prop_list + + # -------------------------------------------------------------------- + # Core movement & observation + # -------------------------------------------------------------------- + def step(self) -> Tuple[int, int]: + """ + Advance the TreeMachine head within the current directory context. + + Returns: + address = (level, index) for the new node under the head. + + Raises: + StopIteration if there is no active context or the current + context is already at its rightmost cell. + """ + ctx = self._current_context() + + try: + _ = ctx.step() + except StopIteration: + raise StopIteration("TreeMachine.step(): current context has no next node.") + + fs_nod_pr = ctx.current_child_dpr() + address = ctx.current_child_address() + + self._current_fs_nod_pr = fs_nod_pr + self._address = address + + _tm_debug(self, f"step -> fs_nod_pr={fs_nod_pr!r}, address={address}") + return address + + def read(self): + """ + Read the value under the head. + + Returns: + (value, property_list, address) + + value: + path string, relative to the TreeMachine root. + + property_list: + list drawn from {"directory","file","other","empty"}. + + address: + (level, index) for the current node. + + Raises: + RuntimeError if there is no current node (no successful step yet). + """ + if self._current_fs_nod_pr is None or self._address is None: + raise RuntimeError("TreeMachine.read() with no current node; call step() first.") + + value = self._current_fs_nod_pr + prop_list = self._compute_property_list(value) + address = self._address + + return (value, prop_list, address) + + def rightmost(self) -> bool: + """True when the current context cannot be stepped further.""" + ctx = self._current_context() + return ctx.rightmost() + + # -------------------------------------------------------------------- + # Context queue interaction + # -------------------------------------------------------------------- + def push_current_directory(self, where_tag: str) -> None: + """ + If the current node is a directory with children, create a new + Context for it and push it into the context queue according + to where_tag: + + "front" -> visit this directory next (depth-first flavor) + "back" -> visit this directory later (breadth-first flavor) + "throw-away" -> ignore this directory entirely + + If the current node is not a directory, or is an empty directory, + this is a no-op (with debug). + """ + if self._current_fs_nod_pr is None or self._address is None: + raise RuntimeError("push_current_directory() with no current node; call step() first.") + + fs_nod_pr = self._current_fs_nod_pr + prop_list = self._compute_property_list(fs_nod_pr) + + if "directory" not in prop_list: + _tm_debug(self, f"push_current_directory skipped (not a directory): {fs_nod_pr!r}") + return + + fs_nod_pa = self._fs_nod_pr_to_dpa(fs_nod_pr) + try: + child_name_list = sorted(os.listdir(fs_nod_pa)) + except OSError: + child_name_list = [] + + if not child_name_list: + _tm_debug(self, f"push_current_directory skipped (empty directory): {fs_nod_pr!r}") + return + + level, _idx = self._address + new_ctx = Context(fs_nod_pr, level, child_name_list) + self._context_queue.push(where_tag, new_ctx) + _tm_debug(self, f"push_current_directory -> {where_tag} {new_ctx.print()}") + + def pop_context(self) -> None: + """ + Pop the current directory context from the queue. + + After popping, the new current context (if any) has no current + node yet, so the TreeMachine head is cleared. + """ + popped = self._context_queue.pop() + _tm_debug(self, f"pop_context -> {popped.print()}") + + self._current_fs_nod_pr = None + self._address = None + + def context_depth(self) -> int: + """Return the number of directory contexts currently in the queue.""" + return self._context_queue.depth() + + # -------------------------------------------------------------------- + # Entanglement + # -------------------------------------------------------------------- + def entangle(self) -> "TreeMachine": + """ + Create an entangled copy of this TreeMachine. + + The entangled copy: + + - Has the same root_dpa. + - Has a cloned Queue of Contexts (using Context.clone()). + - Starts with the same current node (fs_nod_pr) and address. + + Afterwards, the original and entangled copy evolve independently. + """ + new_tm = object.__new__(TreeMachine) + new_tm.root_dpa = self.root_dpa + new_tm._context_queue = self._context_queue.clone() + new_tm._current_fs_nod_pr = self._current_fs_nod_pr + new_tm._address = None if self._address is None else (self._address[0], self._address[1]) + _tm_debug(self, f"entangle -> new {hex(id(new_tm))}") + return new_tm + + +def test() -> None: + """ + Simple TreeMachine test. + + It walks a handful of nodes from the current directory, pushing + directory nodes to the back of the context queue (breadth-first flavor). + """ + meta.DEBUG.add("Queue") + meta.DEBUG.add("TreeMachine") + + print("TreeMachine test starting...") + meta.version() + + tm = TreeMachine(".") + + steps = 0 + max_steps = 20 + + while steps < max_steps: + if tm._context_queue.is_empty(): + print("no more contexts in queue; stopping") + break + + try: + address = tm.step() + except StopIteration: + print("TreeMachine: StopIteration from step()") + break + + value, prop_list, addr = tm.read() + print(f"step {steps}: value={value!r}, props={prop_list}, address={addr}") + + if "directory" in prop_list and "empty" not in prop_list: + tm.push_current_directory("back") + + if tm.rightmost(): + print(" current context is rightmost; popping context") + tm.pop_context() + + steps += 1 + + print("TreeMachine test done.") + + +if __name__ == "__main__": + test() diff --git a/shared/authored/dir-walk/meta.py b/shared/authored/dir-walk/meta.py new file mode 100644 index 0000000..299aef7 --- /dev/null +++ b/shared/authored/dir-walk/meta.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- + +from __future__ import annotations + +""" +meta.py - module-level metadata and debug flags for the TreeMachine tools. +""" + +# Version components are local to this module. +_MAJOR = 0 +_MINOR = 2 + +# Debug flags: +# +# "Queue" -> enable debug for Queue operations. +# "TreeMachine" -> enable debug for TreeMachine operations. +# +DEBUG: set[str] = set() + + +def version() -> None: + """ + Print the current version as: + + version + """ + print(f"version {_MAJOR} {_MINOR}") diff --git a/shared/authored/sync/manus_2025_11_20_215471873762383.txt b/shared/authored/sync/manus_2025_11_20_215471873762383.txt deleted file mode 100644 index 70e1f99..0000000 --- a/shared/authored/sync/manus_2025_11_20_215471873762383.txt +++ /dev/null @@ -1,33 +0,0 @@ -Conversation with Manus -Started on November 20, 2025 at 08:56 PM Singapore time +08 (GMT+0800) - ---- - -08:56 PM | Thomas Lynch: Hello, I signed up today with 7 days free. However, in the middle of a chat, I was prompted to sign up, which I already did. It then charged me for the month, but we have not been 7 days yet, and it is full charge. Why did it do that? - As a second question is the 'credits'. Did it prompt again because the credits were used? Am I now on the hook for two months of charges in less than a day of use? - If just a few hours use uses up a 'month' of credits, then I really can't afford Manus, and need to back out of this. - -08:56 PM | Manus AI: I understand your concern about the unexpected charge! Let me clarify what likely happened. - About the charge: Our subscriptions operate on a credit-based system (http://help.manus.im/en/articles/11711097-what-are-the-rules-for-credits-consumption-and-how-can-i-obtain-them) where credits are consumed during active task processing. When you run out of credits (even during a trial), you may be prompted to upgrade to continue using the service, which would trigger billing. - You're not charged twice: The billing is monthly from your subscription date (http://help.manus.im/en/articles/11711089-where-can-i-find-the-invoice-after-purchase-can-the-invoice-be-modified), so you'll only have one active subscription. - To check your usage: Visit Settings > Usage to see exactly how your credits were consumed and review transaction details. - Your options: - - Cancel subscription: (http://help.manus.im/en/articles/11711087-how-can-i-cancel-my-subscription) Go to your profile > Account > Manage subscription > Cancel subscription to stop future charges. -- Request refund: You can submit a refund request with your invoice information and reason. We'll review it according to our refund policy. - The good news is you can cancel anytime to prevent future charges, and we do consider refund requests on a case-by-case basis! - -Sources: -What are the rules for credits consumption? And how can I obtain them? (http://help.manus.im/en/articles/11711097-what-are-the-rules-for-credits-consumption-and-how-can-i-obtain-them) -Where can I check my credit consumption/usage? (http://help.manus.im/en/articles/12520235-where-can-i-check-my-credit-consumption-usage) -How can I request a refund for a purchased membership? (http://help.manus.im/en/articles/11711102-how-can-i-request-a-refund-for-a-purchased-membership) -How can I cancel my subscription? (http://help.manus.im/en/articles/11711087-how-can-i-cancel-my-subscription) -Where can I find the invoice after purchase, can the invoice be modified? (http://help.manus.im/en/articles/11711089-where-can-i-find-the-invoice-after-purchase-can-the-invoice-be-modified) - -08:57 PM | Liora from Manus: We will respond to you with the resolution within 48 hours. Please wait for our update! - -09:01 PM | Thomas Lynch: You charged my $46.80, the plan says it costs $33.00 explain. - -09:02 PM | Thomas Lynch: hello? - ---- -Exported from Manus on November 20, 2025 at 09:03 PM Singapore time +08 (GMT+0800) \ No newline at end of file diff --git a/shared/authored/sys b/shared/authored/sys new file mode 100644 index 0000000..e69de29 diff --git a/shared/authored_10.zip b/shared/authored_10.zip new file mode 100644 index 0000000000000000000000000000000000000000..2326f1f025ff373909b0e26d481da796c3b96e32 GIT binary patch literal 77199 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 literal 0 HcmV?d00001 diff --git a/shared/made/walk b/shared/made/walk new file mode 120000 index 0000000..1325c19 --- /dev/null +++ b/shared/made/walk @@ -0,0 +1 @@ +../authored/gitignore_treewalk.py \ No newline at end of file diff --git a/temp.sh b/temp.sh new file mode 100644 index 0000000..c166d29 --- /dev/null +++ b/temp.sh @@ -0,0 +1,11 @@ +2025-11-25 09:33:05 Z [subu:developer] Thomas_developer@StanleyPark +§/home/Thomas/subu_data/developer/subu_data/Harmony§ +> find . -type l -exec ls -l {} \; +lrwxrwxrwx 1 Thomas_developer Thomas_developer 35 Nov 25 09:08 ./tool/sync -> ../tool_shared/authored/sync/CLI.py +lrwxrwxrwx 1 Thomas_developer Thomas_developer 3 May 19 2025 ./shared/third_party/Python/lib64 -> lib +lrwxrwxrwx 1 Thomas_developer Thomas_developer 16 May 19 2025 ./shared/third_party/Python/bin/python3 -> /usr/bin/python3 +lrwxrwxrwx 1 Thomas_developer Thomas_developer 7 May 19 2025 ./shared/third_party/Python/bin/python -> python3 +lrwxrwxrwx 1 Thomas_developer Thomas_developer 7 May 19 2025 ./shared/third_party/Python/bin/python3.11 -> python3 +lrwxrwxrwx 1 Thomas_developer Thomas_developer 15 Nov 24 15:19 ./shared/authored/git-empty-dir/source_sync -> ../source_sync/ +lrwxrwxrwx 1 Thomas_developer Thomas_developer 25 Nov 24 15:21 ./shared/authored/git-empty-dir/Harmony.py -> ../source_sync/Harmony.py +lrwxrwxrwx 1 Thomas_developer Thomas_developer 37 Nov 24 15:22 ./shared/authored/git-empty-dir/load_command_module.py -> ../source_sync/load_command_module.py diff --git a/tool/Harmony_sync b/tool/Harmony_sync new file mode 120000 index 0000000..22ddb4e --- /dev/null +++ b/tool/Harmony_sync @@ -0,0 +1 @@ +../shared/authored/Harmony_sync/CLI.py \ No newline at end of file diff --git a/tool/sync b/tool/sync deleted file mode 120000 index e58d04e..0000000 --- a/tool/sync +++ /dev/null @@ -1 +0,0 @@ -../tool_shared/authored/sync/CLI.py \ No newline at end of file -- 2.20.1