Files
wehub-resource-sync bcbd1bdb22
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:44 +08:00

196 lines
6.5 KiB
Python

import gzip as gziplib
import re
from collections.abc import (AsyncIterator, Awaitable, Callable, Mapping,
Sequence)
from dataclasses import dataclass
from functools import partial
from mirage.accessor.base import Accessor
from mirage.commands.builtin.grep_helper import (build_pattern_str,
resolve_pattern)
from mirage.commands.builtin.utils.lines import split_lines
from mirage.commands.builtin.utils.stream import _read_stdin_async
from mirage.commands.spec import SPECS
from mirage.commands.spec.types import FlagView
from mirage.io.types import ByteSource, IOResult
from mirage.types import PathSpec
async def _read_plain(
read_bytes: Callable[..., Awaitable[bytes]],
accessor: Accessor,
path: PathSpec,
index: object = None,
) -> bytes:
return await read_bytes(accessor, path)
def _zgrep_search(
data: bytes,
pattern: str,
ignore_case: bool,
invert: bool,
count: bool,
line_numbers: bool,
filename: str | None,
only_matching: bool,
max_count: int | None,
) -> tuple[list[str], bool]:
text = data.decode(errors="replace")
lines = split_lines(text)
flags = re.IGNORECASE if ignore_case else 0
matched: list[tuple[int, str]] = []
for idx, line in enumerate(lines, 1):
if only_matching and not invert:
hits = list(re.finditer(pattern, line, flags))
if hits:
for m in hits:
matched.append((idx, m.group()))
if max_count is not None and len(matched) >= max_count:
break
elif invert:
matched.append((idx, line))
else:
hit = bool(re.search(pattern, line, flags))
if invert:
hit = not hit
if hit:
matched.append((idx, line))
if max_count is not None and len(matched) >= max_count:
break
if count:
value = str(len(matched))
if filename:
value = f"{filename}:{value}"
return [value], len(matched) > 0
result: list[str] = []
for idx, line in matched:
prefix = ""
if filename:
prefix = filename + ":"
if line_numbers:
prefix += str(idx) + ":"
result.append(prefix + line)
return result, len(matched) > 0
def _files_only_match(data: bytes, pattern: str, ignore_case: bool,
invert: bool) -> bool:
text = data.decode(errors="replace")
flags = re.IGNORECASE if ignore_case else 0
for line in split_lines(text):
hit = bool(re.search(pattern, line, flags))
if invert:
hit = not hit
if hit:
return True
return False
@dataclass(frozen=True, slots=True)
class ZgrepFlags:
"""Parsed zgrep flags; the complete set zgrep honors."""
ignore_case: bool
invert: bool
count: bool
files_only: bool
line_numbers: bool
fixed: bool
force_filename: bool
suppress_filename: bool
only_matching: bool
quiet: bool
whole_word: bool
max_count: int | None
def parse_flags(fl: FlagView, never_match: bool) -> ZgrepFlags:
"""Convert the raw flag bag into ZgrepFlags, the only string-keyed reads.
Args:
fl (FlagView): spec-validated view over the raw flag kwargs.
never_match (bool): zero-pattern sentinel from resolve_pattern; it is
a regex, so it suppresses -F.
"""
return ZgrepFlags(
ignore_case=fl.bool("i"),
invert=fl.bool("v"),
count=fl.bool("c"),
files_only=fl.bool("args_l"),
line_numbers=fl.bool("n"),
fixed=fl.bool("F") and not never_match,
force_filename=fl.bool("H"),
suppress_filename=fl.bool("h"),
only_matching=fl.bool("o"),
quiet=fl.bool("q"),
whole_word=fl.bool("w"),
max_count=fl.int("m"),
)
async def zgrep(
paths: list[PathSpec],
texts: Sequence[str] = (),
flags: Mapping[str, object] | None = None,
*,
read_bytes: Callable[..., Awaitable[bytes]],
accessor: Accessor | None = None,
stdin: AsyncIterator[bytes] | bytes | None = None,
index: object = None,
) -> tuple[ByteSource | None, IOResult]:
fl = FlagView(flags, spec=SPECS["zgrep"])
pattern, never_match = await resolve_pattern(
texts, fl, partial(_read_plain, read_bytes), accessor, index,
"zgrep: usage: zgrep [flags] pattern [path]")
f = parse_flags(fl, never_match)
compiled = build_pattern_str(pattern, f.fixed, f.whole_word)
multi = len(paths) > 1
show_filename = f.force_filename or (multi and not f.suppress_filename)
any_match = False
all_results: list[str] = []
if paths:
for p in paths:
raw = await read_bytes(accessor, p)
data = gziplib.decompress(raw)
fname = p.virtual if show_filename else None
if f.files_only:
if _files_only_match(data, compiled, f.ignore_case, f.invert):
all_results.append(p.virtual)
any_match = True
else:
result, had_match = _zgrep_search(data, compiled,
f.ignore_case, f.invert,
f.count, f.line_numbers,
fname, f.only_matching,
f.max_count)
if had_match:
any_match = True
all_results.extend(result)
else:
raw = await _read_stdin_async(stdin)
data = gziplib.decompress(raw) if raw else b""
if f.files_only:
if _files_only_match(data, compiled, f.ignore_case, f.invert):
all_results.append("(standard input)")
any_match = True
else:
result, had_match = _zgrep_search(data, compiled, f.ignore_case,
f.invert, f.count,
f.line_numbers, None,
f.only_matching, f.max_count)
if had_match:
any_match = True
all_results.extend(result)
if f.quiet:
return None, IOResult(exit_code=0 if any_match else 1)
exit_code = 0 if any_match else 1
if not all_results:
return None, IOResult(exit_code=exit_code)
return ("\n".join(all_results) +
"\n").encode(), IOResult(exit_code=exit_code)
__all__ = ["zgrep"]