feat(meta-info): чтение внешних источников данных

На источнике и его таблице навык печатал только заголовок и строку поддержки: вида он не
знал, а универсальный блок «Реквизиты» ищет <Attribute>, тогда как у таблицы поля — <Field>.

Источник показывает режим блокировки, список таблиц и функции с выражением и типом результата.
Таблица — вид (таблица/выражение), имя или выражение в источнике, объектность, признак «только
чтение», ссылки на поля (ключ, представление, родитель, версия данных, ввод по строке) короткими
именами, и список полей. У поля отмечается имя колонки в источнике, если оно отличается от имени
поля, а также «только чтение» и допустимость NULL.

Заодно два типа перестали печататься сырыми: xs:base64Binary → ДвоичныеДанные (касается всех
объектов, не только внешних источников) и ExternalDataSourceTableRef → русское имя, как у прочих
ссылочных типов.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBsZA5cr2WFThtgp7i5WVi
This commit is contained in:
Nick Shirokov
2026-09-05 21:17:36 +03:00
co-authored by Claude Opus 5
parent 97c8478382
commit bb56a7e898
14 changed files with 1400 additions and 2 deletions
+84 -1
View File
@@ -1,4 +1,4 @@
# meta-info v1.11 — Compact summary of 1C metadata object (Python port)
# meta-info v1.12 — Compact summary of 1C metadata object (Python port)
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
import argparse
import os
@@ -177,6 +177,7 @@ type_name_map = {
"DefinedType": "Определяемый тип", "CommonModule": "Общий модуль",
"ScheduledJob": "Регламентное задание", "EventSubscription": "Подписка на событие",
"HTTPService": "HTTP-сервис", "WebService": "Веб-сервис",
"ExternalDataSource": "Внешний источник данных", "Table": "Таблица внешнего источника",
}
ref_type_map = {
@@ -185,6 +186,7 @@ ref_type_map = {
"ChartOfCharacteristicTypesRef": "ПВХСсылка", "ChartOfCalculationTypesRef": "ПВРСсылка",
"ExchangePlanRef": "ПланОбменаСсылка", "BusinessProcessRef": "БизнесПроцессСсылка",
"TaskRef": "ЗадачаСсылка",
"ExternalDataSourceTableRef": "ВнешнийИсточникДанныхТаблицаСсылка",
}
reg_type_map = {
@@ -317,6 +319,8 @@ def format_single_type(raw, parent_node):
return "ХранилищеЗначения"
if raw == "v8:UUID":
return "УникальныйИдентификатор"
if raw == "xs:base64Binary":
return "ДвоичныеДанные"
if raw == "v8:Null":
return "Null"
# Normalize d5p1:/dNpN: → cfg: (both map to same namespace)
@@ -1363,6 +1367,85 @@ if not drill_done:
for r in res:
out(format_attr_line(r, ml))
# Внешний источник данных: таблицы и функции
if md_type == "ExternalDataSource":
dlcm = props.find("md:DataLockControlMode", NS)
if dlcm is not None:
out(f"Блокировка данных: {dlcm.text or ''}")
if child_objs is not None:
tables = get_simple_children(child_objs, "Table")
if tables:
out("")
out(f"Таблицы ({len(tables)}): {', '.join(tables)}")
fns = child_objs.findall("md:Function", NS)
if fns:
out("")
out(f"Функции ({len(fns)}):")
for fn in fns:
fp = fn.find("md:Properties", NS)
fn_name = fp.find("md:Name", NS).text or ""
fn_expr = fp.find("md:ExpressionInDataSource", NS)
fn_type = format_type(fp.find("md:Type", NS))
ret_node = fp.find("md:ReturnValue", NS)
ret = "процедура" if (ret_node is not None and ret_node.text == "false") else fn_type
expr = (fn_expr.text or "") if fn_expr is not None else ""
out(f" {fn_name}{expr}" + (f" : {ret}" if ret else ""))
# Таблица внешнего источника: свойства и поля
if md_type == "Table":
def _g(tag):
n = props.find(f"md:{tag}", NS)
return (n.text or "") if n is not None else ""
def _short(ref):
return ref.split(".")[-1] if ref else ""
table_type = _g("TableType")
head = ["Вид: " + ("выражение" if table_type == "Expression" else "таблица")]
src = _g("ExpressionInDataSource") if table_type == "Expression" else _g("NameInDataSource")
if src:
head.append(f"в источнике: {src}")
head.append("данные: " + ("объектные" if _g("TableDataType") == "ObjectData" else "необъектные"))
if _g("ReadOnly") == "true":
head.append("только чтение")
out(" | ".join(head))
keys = [_short(f.text) for f in props.findall("md:KeyFields/xr:Field", NS)]
refs = []
if keys:
refs.append(f"ключ: {', '.join(keys)}")
for tag, label in (("PresentationField", "представление"), ("ParentField", "родитель"),
("DataVersionField", "версия данных")):
v = _short(_g(tag))
if v:
refs.append(f"{label}: {v}")
ibs = [_short(f.text) for f in props.findall("md:InputByString/xr:Field", NS)]
if ibs:
refs.append(f"ввод по строке: {', '.join(ibs)}")
if refs:
out(" | ".join(refs))
if child_objs is not None:
fields = get_attributes(child_objs, "Field")
if fields:
out("")
out(f"Поля ({len(fields)}):")
ml = get_max_name_len(fields)
for f in fields:
fp = f["Props"]
marks = []
nids = fp.find("md:NameInDataSource", NS)
if nids is not None and nids.text and nids.text != f["Name"]:
marks.append(f"{nids.text}")
ro = fp.find("md:ReadOnly", NS)
if ro is not None and ro.text == "true":
marks.append("только чтение")
an = fp.find("md:AllowNull", NS)
if an is not None and an.text == "true":
marks.append("NULL")
tail = f" [{', '.join(marks)}]" if marks else ""
out(f" {f['Name'].ljust(ml)} {f['Type']}{tail}")
# Attributes
if child_objs is not None and md_type != "Enum":
attrs = get_attributes(child_objs)