mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-17 23:35:22 +03:00
fix(xdto-compile,xdto-info,xdto-edit): правки по итогам прогона на субагентах
Четырём субагентам выданы реалистичные задачи по песочнице, навыки в формулировках не назывались. Разбор — в debug/xdto/FINDINGS.md, §13. ГЛАВНОЕ — дефект компилятора. При уплощении вложенного xs:choice ветки оставались обязательными: схема «самовывоз ИЛИ адрес доставки» давала пакет, требующий заполнить оба, и ни один реальный документ в него не ложился. Компилятор предупреждал о потере выбора, но молчал о последствии, а валидатор показывал «0 ошибок, 0 предупреждений» — структурно пакет корректен, семантически мёртв. Теперь ветки становятся необязательными (единственное уплощение, оставляющее тип заполнимым), предупреждение называет их поимённо. Корневой xs:choice не затронут: он отображается в ordered="false". xdto-edit: -Value "@файл" по конвенции skd-edit — на кавычках при инлайновой передаче XSD споткнулись двое агентов из четырёх, причём сырой LoadXml уводил чинить схему вместо транспорта. При сбое разбора теперь понятное сообщение. xdto-info: поиск, законно ничего не нашедший, падал throw'ом со стектрейсом и читался как поломка инструмента — теперь строка и exit 1. Блок «Создание» покрывал только корневой тип, хотя вся реальная работа в XDTO — вложенные и анонимные типы; добавлены рецепты по факту наличия. Отсутствие раздела «Точки входа» было неоднозначным — теперь явная строка. Новый -RequiredOnly даёт скелет «заполни обязательное»: необязательный объект уходит вместе с поддеревом. По умолчанию выключен — иначе список читался бы как полный. xdto-validate: предупреждение про anyType описывало историю («платформа заменяет при импорте»), хотя в файле уже зафиксирован anyType; сначала состояние, потом происхождение. Проверено: 40 тестов на обоих рантаймах, round-trip 760/760, валидатор 0 ложных срабатываний на корпусе. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1a1bbbac6f
commit
6486f433de
@@ -10,6 +10,7 @@ param(
|
||||
[ValidateSet("auto", "used-by")]
|
||||
[string]$Mode = "auto",
|
||||
[int]$Depth = 1,
|
||||
[switch]$RequiredOnly,
|
||||
[int]$Limit = 150,
|
||||
[int]$Offset = 0,
|
||||
[string]$OutFile
|
||||
@@ -27,6 +28,13 @@ $MD_NS = "http://v8.1c.ru/8.3/MDClasses"
|
||||
$sb = New-Object System.Text.StringBuilder
|
||||
function O([string]$line = "") { [void]$sb.AppendLine($line) }
|
||||
|
||||
function Fail([string]$msg) {
|
||||
# Отрицательный результат поиска — не исключение: печатаем сообщение
|
||||
# и выходим с кодом 1, без стектрейса PowerShell
|
||||
Write-Host $msg
|
||||
exit 1
|
||||
}
|
||||
|
||||
function Flush-Output {
|
||||
$text = $sb.ToString().TrimEnd()
|
||||
if ($OutFile) {
|
||||
@@ -46,7 +54,7 @@ function Flush-Output {
|
||||
if (-not [System.IO.Path]::IsPathRooted($PackagePath)) {
|
||||
$PackagePath = Join-Path (Get-Location).Path $PackagePath
|
||||
}
|
||||
if (-not (Test-Path $PackagePath)) { throw "Путь не найден: $PackagePath" }
|
||||
if (-not (Test-Path $PackagePath)) { Fail "Путь не найден: $PackagePath" }
|
||||
|
||||
$configRoot = $null
|
||||
$directPkgDir = $null
|
||||
@@ -70,7 +78,7 @@ if (Test-Path (Join-Path $PackagePath "Configuration.xml")) {
|
||||
$configRoot = Split-Path (Split-Path $stem -Parent) -Parent
|
||||
}
|
||||
}
|
||||
if (-not $configRoot -and -not $directPkgDir) { throw "Не удалось определить пакет или конфигурацию по пути: $PackagePath" }
|
||||
if (-not $configRoot -and -not $directPkgDir) { Fail "Не удалось определить пакет или конфигурацию по пути: $PackagePath" }
|
||||
|
||||
# Sort-Object в PowerShell сортирует по культуре, sorted() в Python — по кодам.
|
||||
# Для паритета портов сортируем ординально в обоих.
|
||||
@@ -130,7 +138,7 @@ if ($directPkgDir -and $packages.Count -eq 0) {
|
||||
$p = Read-Package $directPkgDir
|
||||
if ($p) { [void]$packages.Add($p); $byNamespace[$p.Namespace] = $p }
|
||||
}
|
||||
if ($packages.Count -eq 0) { throw "Пакеты XDTO не найдены: $PackagePath" }
|
||||
if ($packages.Count -eq 0) { Fail "Пакеты XDTO не найдены: $PackagePath" }
|
||||
|
||||
# --- Type notation: XSD -> 1С ---------------------------------------------------
|
||||
|
||||
@@ -389,6 +397,20 @@ function Get-PropRows([System.Xml.XmlElement]$type, $pkg, [int]$depth, [int]$ind
|
||||
return ,$rows
|
||||
}
|
||||
|
||||
# Оставить только обязательные свойства. Ребёнок необязательного объекта тоже
|
||||
# уходит: он лежит под необязательной веткой и заполнять его не обязательно.
|
||||
function Select-Required($rows) {
|
||||
$res = New-Object System.Collections.ArrayList
|
||||
$cutFrom = -1
|
||||
foreach ($r in $rows) {
|
||||
if ($cutFrom -ge 0 -and $r.Indent -gt $cutFrom) { continue }
|
||||
$cutFrom = -1
|
||||
if ($r.Flags -notcontains "обязательный") { $cutFrom = $r.Indent; continue }
|
||||
[void]$res.Add($r)
|
||||
}
|
||||
return ,$res
|
||||
}
|
||||
|
||||
function Write-Rows($rows) {
|
||||
if ($rows.Count -eq 0) { O " (нет свойств)"; return }
|
||||
$shown = $rows
|
||||
@@ -450,7 +472,10 @@ function Show-PackageOverview($pkg) {
|
||||
O " $i → $dep"
|
||||
}
|
||||
}
|
||||
if ($pkg.GlobalProps.Count -gt 0) {
|
||||
if ($pkg.GlobalProps.Count -eq 0) {
|
||||
O ""
|
||||
O "Точки входа: нет — пакет не объявляет корневых элементов документа"
|
||||
} else {
|
||||
O ""
|
||||
O "Точки входа ($($pkg.GlobalProps.Count)) — корневые элементы документа:"
|
||||
foreach ($gp in $pkg.GlobalProps) {
|
||||
@@ -540,13 +565,32 @@ function Show-Type($pkg, [string]$typeName) {
|
||||
[void]$seen.Add("$($pkg.Namespace)#$typeName")
|
||||
$rows = Get-PropRows $el $pkg $Depth 0 $seen
|
||||
$own = @($rows | Where-Object { $_.Indent -eq 0 })
|
||||
O "Свойства ($($own.Count)):"
|
||||
if ($RequiredOnly) {
|
||||
$all = $rows.Count
|
||||
$rows = Select-Required $rows
|
||||
$ownReq = @($rows | Where-Object { $_.Indent -eq 0 })
|
||||
# Фильтр обязан сообщать о себе: иначе список читается как полный
|
||||
O "Свойства: обязательных $($ownReq.Count) из $($own.Count) (-RequiredOnly; скрыто строк: $($all - $rows.Count))"
|
||||
} else {
|
||||
O "Свойства ($($own.Count)):"
|
||||
}
|
||||
Write-Rows $rows
|
||||
Write-Legend $rows
|
||||
O ""
|
||||
O "Создание:"
|
||||
O " Тип = ФабрикаXDTO.Тип(`"$($pkg.Namespace)`", `"$typeName`");"
|
||||
O " Объект = ФабрикаXDTO.Создать(Тип);"
|
||||
# Рецепты для вложенных и анонимных типов: имени у анонимного нет, через
|
||||
# ФабрикаXDTO.Тип(ns, имя) его не получить — только от свойства владельца
|
||||
$nested = @($rows | Where-Object { $_.Indent -eq 0 -and $_.Type -like "объект *" } | Select-Object -First 1)
|
||||
if ($nested.Count -gt 0) {
|
||||
O " Вложенный = ФабрикаXDTO.Создать(Тип.Свойства.Получить(`"$($nested[0].Name)`").Тип);"
|
||||
}
|
||||
$textProp = @($rows | Where-Object { $_.Flags -contains "значение элемента" } | Select-Object -First 1)
|
||||
if ($textProp.Count -gt 0) {
|
||||
O " // тип со значением элемента: значение задаётся при создании"
|
||||
O " Узел = ФабрикаXDTO.Создать(ТипУзла, Значение);"
|
||||
}
|
||||
}
|
||||
|
||||
function Show-UsedBy([string]$typeName, $ownerPkg) {
|
||||
@@ -588,15 +632,15 @@ if ($directPkgDir) {
|
||||
}
|
||||
if (-not $selected -and $Namespace) {
|
||||
$selected = $packages | Where-Object { $_.Namespace -eq $Namespace } | Select-Object -First 1
|
||||
if (-not $selected) { throw "Пакет с namespace `"$Namespace`" не найден. Список: -PackagePath <корень> без параметров" }
|
||||
if (-not $selected) { Fail "Пакет с namespace `"$Namespace`" не найден. Список: -PackagePath <корень> без параметров" }
|
||||
}
|
||||
if (-not $selected -and $Package) {
|
||||
$selected = $packages | Where-Object { $_.Name -eq $Package } | Select-Object -First 1
|
||||
if (-not $selected) { throw "Пакет `"$Package`" не найден. Список: -PackagePath <корень> без параметров" }
|
||||
if (-not $selected) { Fail "Пакет `"$Package`" не найден. Список: -PackagePath <корень> без параметров" }
|
||||
}
|
||||
|
||||
if ($Mode -eq "used-by") {
|
||||
if (-not $Name) { throw "Режим used-by требует -Name <Тип>" }
|
||||
if (-not $Name) { Fail "Режим used-by требует -Name <Тип>" }
|
||||
$ownerPkg = $selected
|
||||
if (-not $ownerPkg) { $ownerPkg = ($packages | Where-Object { $_.Types.ContainsKey($Name) } | Select-Object -First 1) }
|
||||
Show-UsedBy $Name $ownerPkg
|
||||
@@ -608,7 +652,7 @@ if ($Name) {
|
||||
if (-not $selected) {
|
||||
# Тип известен, пакет — нет: ищем по всей конфигурации
|
||||
$found = @($packages | Where-Object { $_.Types.ContainsKey($Name) })
|
||||
if ($found.Count -eq 0) { throw "Тип `"$Name`" не найден ни в одном пакете конфигурации" }
|
||||
if ($found.Count -eq 0) { Fail "Тип `"$Name`" не найден ни в одном пакете конфигурации" }
|
||||
if ($found.Count -gt 1) {
|
||||
O "=== Тип `"$Name`" найден в нескольких пакетах ($($found.Count)) ==="
|
||||
O "Уточните через -Namespace или -Package:"
|
||||
@@ -620,7 +664,7 @@ if ($Name) {
|
||||
$selected = $found[0]
|
||||
}
|
||||
if (-not $selected.Types.ContainsKey($Name)) {
|
||||
throw "В пакете $($selected.Name) нет типа `"$Name`". Список типов: тот же вызов без -Name"
|
||||
Fail "В пакете $($selected.Name) нет типа `"$Name`". Список типов: тот же вызов без -Name"
|
||||
}
|
||||
Show-Type $selected $Name
|
||||
Flush-Output
|
||||
|
||||
@@ -20,6 +20,7 @@ parser.add_argument("-Namespace", default="")
|
||||
parser.add_argument("-Name", default="")
|
||||
parser.add_argument("-Mode", default="auto", choices=["auto", "used-by"])
|
||||
parser.add_argument("-Depth", type=int, default=1)
|
||||
parser.add_argument("-RequiredOnly", action="store_true")
|
||||
parser.add_argument("-Limit", type=int, default=150)
|
||||
parser.add_argument("-Offset", type=int, default=0)
|
||||
parser.add_argument("-OutFile", default="")
|
||||
@@ -48,7 +49,9 @@ def flush_output():
|
||||
|
||||
|
||||
def die(msg):
|
||||
print(msg, file=sys.stderr)
|
||||
# Отрицательный результат поиска — не исключение: сообщение и код 1,
|
||||
# без трассировки
|
||||
print(msg)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@@ -406,6 +409,22 @@ def get_prop_rows(type_el, pkg, depth, indent, seen):
|
||||
return rows
|
||||
|
||||
|
||||
# Оставить только обязательные свойства. Ребёнок необязательного объекта тоже
|
||||
# уходит: он лежит под необязательной веткой и заполнять его не обязательно.
|
||||
def select_required(rows):
|
||||
res = []
|
||||
cut_from = -1
|
||||
for r in rows:
|
||||
if cut_from >= 0 and r["Indent"] > cut_from:
|
||||
continue
|
||||
cut_from = -1
|
||||
if "обязательный" not in r["Flags"]:
|
||||
cut_from = r["Indent"]
|
||||
continue
|
||||
res.append(r)
|
||||
return res
|
||||
|
||||
|
||||
def write_rows(rows):
|
||||
if not rows:
|
||||
O(" (нет свойств)")
|
||||
@@ -456,7 +475,10 @@ def show_package_overview(pkg):
|
||||
for i in pkg.Imports:
|
||||
dep = by_namespace[i].Name if i in by_namespace else "(пакет не найден)"
|
||||
O(f" {i} → {dep}")
|
||||
if pkg.GlobalProps:
|
||||
if not pkg.GlobalProps:
|
||||
O("")
|
||||
O("Точки входа: нет — пакет не объявляет корневых элементов документа")
|
||||
else:
|
||||
O("")
|
||||
O(f"Точки входа ({len(pkg.GlobalProps)}) — корневые элементы документа:")
|
||||
for gp in pkg.GlobalProps:
|
||||
@@ -546,13 +568,29 @@ def show_type(pkg, type_name):
|
||||
seen = {f"{pkg.Namespace}#{type_name}"}
|
||||
rows = get_prop_rows(el, pkg, DEPTH, 0, seen)
|
||||
own = [r for r in rows if r["Indent"] == 0]
|
||||
O(f"Свойства ({len(own)}):")
|
||||
if args.RequiredOnly:
|
||||
total = len(rows)
|
||||
rows = select_required(rows)
|
||||
own_req = [r for r in rows if r["Indent"] == 0]
|
||||
# Фильтр обязан сообщать о себе: иначе список читается как полный
|
||||
O(f"Свойства: обязательных {len(own_req)} из {len(own)} "
|
||||
f"(-RequiredOnly; скрыто строк: {total - len(rows)})")
|
||||
else:
|
||||
O(f"Свойства ({len(own)}):")
|
||||
write_rows(rows)
|
||||
write_legend(rows)
|
||||
O("")
|
||||
O("Создание:")
|
||||
O(f' Тип = ФабрикаXDTO.Тип("{pkg.Namespace}", "{type_name}");')
|
||||
O(" Объект = ФабрикаXDTO.Создать(Тип);")
|
||||
# Рецепты для вложенных и анонимных типов: имени у анонимного нет, через
|
||||
# ФабрикаXDTO.Тип(ns, имя) его не получить — только от свойства владельца
|
||||
nested = next((r for r in rows if r["Indent"] == 0 and r["Type"].startswith("объект ")), None)
|
||||
if nested:
|
||||
O(f' Вложенный = ФабрикаXDTO.Создать(Тип.Свойства.Получить("{nested["Name"]}").Тип);')
|
||||
if any("значение элемента" in r["Flags"] for r in rows):
|
||||
O(" // тип со значением элемента: значение задаётся при создании")
|
||||
O(" Узел = ФабрикаXDTO.Создать(ТипУзла, Значение);")
|
||||
|
||||
|
||||
def show_used_by(type_name, owner_pkg):
|
||||
|
||||
Reference in New Issue
Block a user