Spaces:
Runtime error
Runtime error
| """ | |
| textβtransform β Diff, regex extraction, markdown tables, and text statistics. | |
| ================================================================================ | |
| Pureβstdlib toolkit for the textβmunging tasks that agents do every day | |
| but currently need throwβaway scripts for. | |
| """ | |
| import difflib | |
| import re | |
| import math | |
| from typing import Any | |
| try: | |
| from toolstore.toolset import tool | |
| except ImportError: | |
| def tool(fn): | |
| return fn # noβop when toolstore package not installed | |
| # ββ text_diff ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def text_diff(*, original: str, modified: str, | |
| context_lines: int = 3, label_a: str = "original", | |
| label_b: str = "modified") -> dict: | |
| """Compute a unified diff between two text blocks. | |
| Args: | |
| original: The original text. | |
| modified: The modified text. | |
| context_lines: Lines of context around each change (default 3). | |
| label_a: Label for the original in the header. | |
| label_b: Label for the modified in the header. | |
| Returns: | |
| dict with keys: | |
| diff β unified diff string (empty if identical) | |
| added β number of added lines | |
| removed β number of removed lines | |
| changed β True if the texts differ | |
| """ | |
| a = original.splitlines(keepends=True) | |
| b = modified.splitlines(keepends=True) | |
| diff_lines = list(difflib.unified_diff( | |
| a, b, fromfile=label_a, tofile=label_b, n=context_lines | |
| )) | |
| added = sum(1 for l in diff_lines if l.startswith("+") and not l.startswith("+++")) | |
| removed = sum(1 for l in diff_lines if l.startswith("-") and not l.startswith("---")) | |
| return { | |
| "diff": "".join(diff_lines) if diff_lines else "", | |
| "added": added, | |
| "removed": removed, | |
| "changed": len(diff_lines) > 0, | |
| } | |
| # ββ regex_extract ββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def regex_extract(*, text: str, pattern: str, | |
| flags: list = None, max_matches: int = 0) -> dict: | |
| """Extract all regex matches from text, with optional capture groups. | |
| Args: | |
| text: The text to search in. | |
| pattern: Python regex pattern. | |
| flags: List of flag names: IGNORECASE, MULTILINE, DOTALL. | |
| max_matches: Maximum matches to return (0 = all). | |
| Returns: | |
| dict with: | |
| matches β list of match dicts: | |
| {index, start, end, text, groups: [...]} | |
| count β total matches found | |
| """ | |
| flag_map = { | |
| "IGNORECASE": re.IGNORECASE, | |
| "MULTILINE": re.MULTILINE, | |
| "DOTALL": re.DOTALL, | |
| } | |
| re_flags = 0 | |
| for f in (flags or []): | |
| re_flags |= flag_map.get(f.upper(), 0) | |
| try: | |
| compiled = re.compile(pattern, re_flags) | |
| except re.error as exc: | |
| return {"error": f"Invalid regex pattern: {exc}"} | |
| matches = [] | |
| for idx, m in enumerate(compiled.finditer(text)): | |
| match_obj = { | |
| "index": idx, | |
| "start": m.start(), | |
| "end": m.end(), | |
| "text": m.group(0), | |
| "groups": list(m.groups()) if m.groups() else [], | |
| } | |
| if m.groupdict(): | |
| match_obj["named_groups"] = m.groupdict() | |
| matches.append(match_obj) | |
| if max_matches and len(matches) >= max_matches: | |
| break | |
| return {"matches": matches, "count": len(matches)} | |
| # ββ markdown_table βββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def markdown_table(*, data: list, columns: list = None, | |
| align: str = "left") -> dict: | |
| """Convert a list of dicts to a formatted Markdown table. | |
| Args: | |
| data: List of dicts (each dict = one row). | |
| columns: Column order (default: keys from first row). | |
| align: Column alignment: left, center, or right. | |
| Returns: | |
| dict with "markdown" containing the formatted table string. | |
| """ | |
| if not data: | |
| return {"markdown": "", "rows": 0, "columns": 0} | |
| if columns is None: | |
| columns = list(data[0].keys()) if data else [] | |
| align_chars = {"left": ":", "center": ":", "right": ""} | |
| align_post = {"left": "-", "center": ":", "right": "-:"} | |
| def _cell(v): | |
| s = str(v) if v is not None else "" | |
| return s.replace("|", "\\|").replace("\n", " ") | |
| widths = {} | |
| for col in columns: | |
| widths[col] = len(str(col)) | |
| for row in data: | |
| for col in columns: | |
| val_len = len(_cell(row.get(col, ""))) | |
| if val_len > widths.get(col, 0): | |
| widths[col] = val_len | |
| header = "| " + " | ".join(str(c).ljust(widths[c]) for c in columns) + " |" | |
| sep = "|" + "|".join( | |
| f" {align_chars.get(align, ':')}{'-' * (widths[c] - 1)}{align_post.get(align, '-')} " | |
| for c in columns | |
| ) + "|" | |
| rows = [] | |
| for row in data: | |
| rows.append("| " + " | ".join( | |
| _cell(row.get(c, "")).ljust(widths[c]) for c in columns | |
| ) + " |") | |
| return { | |
| "markdown": "\n".join([header, sep] + rows), | |
| "rows": len(data), | |
| "columns": len(columns), | |
| } | |
| # ββ text_stats βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def text_stats(*, text: str) -> dict: | |
| """Compute statistics about a block of text. | |
| Args: | |
| text: The text to analyze. | |
| Returns: | |
| dict with: | |
| chars, words, lines, sentences, paragraphs, | |
| avg_word_len, avg_sentence_len, | |
| flesch_reading_ease (0β100, higher = easier) | |
| """ | |
| chars = len(text) | |
| words = len(re.findall(r"\b\w+\b", text)) | |
| lines = text.count("\n") + 1 if text else 0 | |
| sentences = max(len(re.findall(r"[.!?]+", text)), 1) | |
| paragraphs = max(len(re.findall(r"\n\s*\n", text)) + 1, 1) | |
| avg_word_len = round(chars / words, 1) if words else 0 | |
| avg_sentence_len = round(words / sentences, 1) if words else 0 | |
| syllables = 0 | |
| for w in re.findall(r"\b\w+\b", text.lower()): | |
| s = len(re.findall(r"[aeiouy]+", w)) | |
| syllables += max(s, 1) | |
| try: | |
| flesch = 206.835 - 1.015 * (words / sentences) - 84.6 * (syllables / words) | |
| flesch = max(0, min(100, round(flesch, 1))) | |
| except (ZeroDivisionError, ValueError): | |
| flesch = 0 | |
| return { | |
| "chars": chars, | |
| "words": words, | |
| "lines": lines, | |
| "sentences": sentences, | |
| "paragraphs": paragraphs, | |
| "avg_word_len": avg_word_len, | |
| "avg_sentence_len": avg_sentence_len, | |
| "flesch_reading_ease": flesch, | |
| } | |