Files
llm/scripts/get_1c_module.py
T

189 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Read a selected 1C object module from base/effective/extension context."""
from __future__ import annotations
import argparse
import base64
import json
import re
import sys
from pathlib import Path
from typing import Any
sys.path.insert(0, str(Path(__file__).resolve().parent))
from get_1c_object_code_context import build_context # noqa: E402
from resolve_1c_object import load_json # noqa: E402
def decode_arg(value: str | None, encoded: str | None) -> str | None:
if encoded:
return base64.b64decode(encoded).decode("utf-8")
return value
def normalize(value: str | None) -> str:
return re.sub(r"[\s._-]+", "", str(value or "")).casefold()
def module_aliases(name: str) -> set[str]:
n = name.strip()
result = {normalize(n)}
aliases = {
"object_module": "МодульОбъекта",
"manager_module": "МодульМенеджера",
"form_module": "МодульФормы",
"command_module": "МодульКоманды",
}
if n in aliases:
result.add(normalize(aliases[n]))
return result
def match_module(module: dict[str, Any], wanted: str) -> bool:
aliases = module_aliases(wanted)
values = {
module.get("name"),
module.get("kind"),
module.get("relative_path"),
Path(str(module.get("relative_path") or "")).stem,
}
return any(normalize(value) in aliases or normalize(wanted) in normalize(value) for value in values)
def read_text(path: str) -> str:
return Path(path).read_text(encoding="utf-8-sig", errors="replace")
def routine_span(text: str, routine: str) -> tuple[int, int] | None:
wanted = normalize(routine)
lines = text.splitlines()
start = None
end_pattern = None
for index, line in enumerate(lines):
match = re.match(r"^\s*(Процедура|Функция)\s+([A-Za-zА-Яа-я0-9_]+)", line, re.IGNORECASE)
if not match:
continue
if normalize(match.group(2)) != wanted:
continue
start = index
end_pattern = "КонецПроцедуры" if match.group(1).casefold() == "процедура" else "КонецФункции"
break
if start is None or end_pattern is None:
return None
for index in range(start + 1, len(lines)):
if re.match(rf"^\s*{end_pattern}\b", lines[index], re.IGNORECASE):
return start, index + 1
return start, len(lines)
def snippet(text: str, *, max_chars: int, routine: str | None) -> dict[str, Any]:
source = text
line_start = 1
found_routine = None
if routine:
span = routine_span(text, routine)
if span:
lines = text.splitlines()
source = "\n".join(lines[span[0] : span[1]])
line_start = span[0] + 1
found_routine = routine
else:
source = ""
truncated = len(source) > max_chars
return {
"routine": found_routine,
"line_start": line_start if source else None,
"text": source[:max_chars],
"truncated": truncated,
"char_count": len(source),
}
def materialize(module: dict[str, Any], *, max_chars: int, routine: str | None) -> dict[str, Any]:
text = read_text(str(module["path"]))
return {
"name": module.get("name"),
"kind": module.get("kind"),
"origin": module.get("origin"),
"effective_action": module.get("effective_action"),
"relative_path": module.get("relative_path"),
"path": module.get("path"),
"size": module.get("size"),
"content": snippet(text, max_chars=max_chars, routine=routine),
}
def build_module_result(
index: dict[str, Any],
*,
kind: str,
name: str,
module_name: str,
view: str,
extension: str | None,
max_chars: int,
routine: str | None,
) -> dict[str, Any]:
context = build_context(index, kind=kind, name=name, view=view, extension=extension)
matches = [module for module in context.get("modules") or [] if match_module(module, module_name)]
if not matches:
raise SystemExit(f"Module not found: {module_name}")
modules = []
for module in matches:
modules.append(materialize(module, max_chars=max_chars, routine=routine))
for overlay in module.get("extension_overlays") or []:
modules.append(materialize(overlay, max_chars=max_chars, routine=routine))
return {
"schema": "onec_module_content.v1",
"view": view,
"extension": extension if view == "extension" else None,
"object": context.get("object"),
"query": {"module": module_name, "routine": routine, "max_chars": max_chars},
"modules": modules,
"counts": {"modules": len(modules)},
}
def main() -> int:
parser = argparse.ArgumentParser(description="Get 1C module content.")
parser.add_argument("--index", type=Path, required=True)
parser.add_argument("--kind")
parser.add_argument("--name")
parser.add_argument("--kind-b64")
parser.add_argument("--name-b64")
parser.add_argument("--module", required=True)
parser.add_argument("--view", choices=["effective", "base", "extension"], default="effective")
parser.add_argument("--extension")
parser.add_argument("--routine")
parser.add_argument("--max-chars", type=int, default=20000)
parser.add_argument("--output", type=Path)
args = parser.parse_args()
kind = decode_arg(args.kind, args.kind_b64)
name = decode_arg(args.name, args.name_b64)
if not kind or not name:
raise SystemExit("Use --kind/--name or --kind-b64/--name-b64.")
result = build_module_result(
load_json(args.index),
kind=kind,
name=name,
module_name=args.module,
view=args.view,
extension=args.extension,
max_chars=args.max_chars,
routine=args.routine,
)
text = json.dumps(result, ensure_ascii=False, indent=2) + "\n"
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(text, encoding="utf-8")
print(json.dumps({"output": str(args.output), "counts": result["counts"], "view": result["view"]}, ensure_ascii=False))
else:
print(text)
return 0
if __name__ == "__main__":
raise SystemExit(main())