Files
mihailkudravcev 91521e6604 Release 0.3.4 with external processing indexing.
Add virtual baseconf support for external data processors so reindex can discover and index them alongside regular src-based configurations, and update docs to use generic examples without workspace-specific names.
2026-07-21 13:03:25 +03:00

257 lines
9.2 KiB
Python
Executable File
Raw Permalink 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
"""Переиндексация метаданных и модулей 1С (конфигурации + внешние обработки) → cache/1c_meta/index.sqlite.
Параметры:
--config, -c файл настроек утилиты (YAML)
--baseconf, -b какие источники 1С индексировать (каталог с src/ или virtual external)
Примеры:
python index_1c.py reindex -j 12
python index_1c.py reindex --full -j 12
python index_1c.py reindex --full -b my-config -j 12
python index_1c.py reindex -b main -b my-config.ext -t Documents,Catalogs
python index_1c.py status
python index_1c.py list
Поиск: python query_1c.py …
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
if str(HERE) not in sys.path:
sys.path.insert(0, str(HERE))
from index1c.build import build_project_index, default_workers # noqa: E402
from index1c import read_version_file # noqa: E402
from index1c.cli import ( # noqa: E402
BaseconfError,
add_tool_args,
handle_baseconf_error,
index_db_missing_message,
make_parent_parser,
resolve_project_root,
validate_baseconfs,
)
from index1c.config import ( # noqa: E402
DEFAULT_SKIP_TYPES,
cache_dir,
discover_configs,
get_default_baseconfs,
get_config_aliases,
resolve_object_type,
)
from index1c import db as dbmod # noqa: E402
def _parse_types(raw: str | None) -> set[str] | None:
if not raw:
return None
types: set[str] = set()
for part in raw.replace(";", ",").split(","):
part = part.strip()
if not part:
continue
types.add(resolve_object_type(part))
return types or None
def _select_configs_for_reindex(args: argparse.Namespace, root: Path) -> list:
"""Какие конфигурации 1С индексировать."""
if args.baseconf:
try:
names = validate_baseconfs(root, args.baseconf, require_indexed=False)
except BaseconfError as e:
print(str(e), file=sys.stderr)
sys.exit(e.exit_code)
return discover_configs(root, names)
# без -b: default_baseconfs из --config или автосканирование.
defaults = get_default_baseconfs()
if defaults:
try:
names = validate_baseconfs(root, defaults, require_indexed=False)
except BaseconfError as e:
print(str(e), file=sys.stderr)
sys.exit(e.exit_code)
return discover_configs(root, names)
# fallback: все каталоги с src/ в корне проекта
return discover_configs(root, None)
def cmd_reindex(args: argparse.Namespace) -> int:
root = resolve_project_root(args)
configs = _select_configs_for_reindex(args, root)
if not configs:
print(
"Нет источников для индексации (нужна папка <name>/src/ "
"или внешняя обработка <dir>/<name>.xml + <dir>/<name>/).\n"
"Укажите -b <имя-выгрузки> или проверьте: python index_1c.py list",
file=sys.stderr,
)
return 1
types = _parse_types(args.types)
skip = set(DEFAULT_SKIP_TYPES)
if args.include_skipped:
skip.clear()
if args.skip_types:
for t in args.skip_types.replace(";", ",").split(","):
t = t.strip()
if t:
skip.add(resolve_object_type(t))
workers = args.workers if args.workers and args.workers > 0 else default_workers()
# --full без -b → пересоздать всю БД
wipe_db = bool(args.full and not args.baseconf)
print(
f"Режим: {'full-wipe' if wipe_db else ('full' if args.full else 'incremental')}, "
f"workers={workers}, baseconf={[c.name for c in configs]}, modules={not args.no_modules}"
)
summary = build_project_index(
configs,
root,
full=args.full or wipe_db,
wipe_db=wipe_db,
types=types,
skip_types=skip,
index_modules=not args.no_modules,
workers=workers,
write_md=args.md,
)
print(
f"Готово: {summary['db']} ({summary['elapsed_sec']}s) "
f"meta+{summary['meta_parsed']}/skip{summary['meta_skipped']} "
f"mod+{summary['modules_parsed']}/skip{summary['modules_skipped']} "
f"removed={summary['files_removed']} err={summary['errors']}"
)
return 0
def cmd_status(args: argparse.Namespace) -> int:
root = resolve_project_root(args)
db_path = dbmod.global_db_path(cache_dir(root))
if not db_path.is_file():
print(index_db_missing_message(root), file=sys.stderr)
return 1
conn = dbmod.connect(db_path, readonly=True)
try:
print(f"БД: {db_path} ({db_path.stat().st_size // (1024 * 1024)} МБ)")
for row in conn.execute("SELECT key, value FROM meta ORDER BY key"):
print(f" meta.{row['key']} = {row['value']}")
print("Конфигурации 1С в индексе (--baseconf):")
for c in dbmod.list_configs(conn):
print(
f"- {c['name']}: objects={c['objects']} fields={c['fields']} "
f"refs={c['refs']} modules={c['modules']} @ {c['updated_at']}"
)
finally:
conn.close()
return 0
def cmd_list_configs(args: argparse.Namespace) -> int:
root = resolve_project_root(args)
found = discover_configs(root, None)
print("Выгрузки 1С в репозитории (для --baseconf / -b):")
for c in found:
aliases = [k for k, v in get_config_aliases().items() if v == c.name]
alias_s = f" алиасы: {', '.join(aliases)}" if aliases else ""
print(f"- {c.name}{alias_s}")
db_path = dbmod.global_db_path(cache_dir(root))
if db_path.is_file():
conn = dbmod.connect(db_path, readonly=True)
try:
print("\nПроиндексировано (в index.sqlite):")
for c in dbmod.list_configs(conn):
print(f"- {c['name']}")
finally:
conn.close()
else:
print(f"\nИндекс отсутствует. {index_db_missing_message(root)}")
return 0
def build_parser() -> argparse.ArgumentParser:
tool_parent = make_parent_parser(include_baseconf=False)
reindex_parent = make_parent_parser(include_baseconf=True, baseconf_mode="index")
p = argparse.ArgumentParser(
description="Построение единого SQLite-индекса метаданных и BSL-модулей 1С.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=(
"Конфигурация 1С: --baseconf (-b). Файл настроек утилиты: --config (-c).\n"
"Алиасы/default для baseconf — в YAML (--config), см. config.example.yml"
),
)
p.add_argument(
"--version",
action="version",
version=f"%(prog)s {read_version_file()}",
)
add_tool_args(p)
sub = p.add_subparsers(dest="cmd", required=True)
r = sub.add_parser(
"reindex",
help="Построить / обновить index.sqlite",
parents=[reindex_parent],
formatter_class=argparse.RawDescriptionHelpFormatter,
description="Построение или инкрементальное обновление index.sqlite.",
)
r.add_argument("--types", "-t", help="Только типы: Documents,Catalogs,рс,рн,…")
r.add_argument("--skip-types", help="Дополнительно пропустить типы")
r.add_argument("--include-skipped", action="store_true")
r.add_argument(
"--full",
action="store_true",
help="Полная пересборка (вся БД или только указанные -b)",
)
r.add_argument("--no-modules", action="store_true", help="Не индексировать .bsl")
r.add_argument(
"--workers",
"-j",
type=int,
default=0,
help=f"Потоков (по умолчанию {default_workers()})",
)
r.add_argument("--md", action="store_true", help="Markdown в cache/1c_meta/md/")
r.set_defaults(func=cmd_reindex)
st = sub.add_parser(
"status",
help="Состояние index.sqlite",
parents=[tool_parent],
formatter_class=argparse.RawDescriptionHelpFormatter,
description="Размер БД, meta и список проиндексированных конфигураций.",
)
st.set_defaults(func=cmd_status)
lc = sub.add_parser(
"list",
help="Источники в репозитории и в индексе",
parents=[tool_parent],
formatter_class=argparse.RawDescriptionHelpFormatter,
description="Источники 1С в репозитории (src/ и external) и что уже в index.sqlite.",
)
lc.set_defaults(func=cmd_list_configs)
return p
def main(argv: list[str] | None = None) -> int:
parser = build_parser()
args = parser.parse_args(argv)
return int(args.func(args))
if __name__ == "__main__":
raise SystemExit(main())