mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-31 23:40:51 +03:00
fix(skills): внятная диагностика разбора JSON вместо стектрейса (#80)
Неверный входной JSON ронял скрипты необработанным исключением: PS1 отдавал дамп
ConvertFrom-Json с CategoryInfo, py-порт — traceback с внутренностями json/decoder.py.
Имя файла в сообщении не фигурировало, а для полиморфного -Value не было видно, какую
форму ждёт операция.
Общий хелпер ConvertFrom-JsonInput / parse_json_input в 12 навыках × 2 порта (24 места),
зарегистрирован семьёй в check-inline-drift.mjs — копии держит гард. Сообщение в одну
строку: ожидаемая форма, полученное значение, текст парсера в скобках.
Эхо полученного значения нужно потому, что съеденные оболочкой кавычки дают почти тот же
JSON ({group:X} вместо {"group":"X"}), и без него агент считает свой вызов верным. PS 5.1
печатает лишь огрызок и локализованно, Python — только номер колонки.
Возврат в PS1 через Write-Output -NoEnumerate: вынос разбора в функцию добавляет второй
анруллинг, и одноэлементный JSON-массив стал бы скаляром. Импорты внутри тела py-хелпера —
skd-decompile импортирует json локально как _json, а тело семьи обязано быть одинаковым.
В раннер добавлен inputRaw (запись входного файла дословно): через case.input битый JSON
невыразим, JSON.stringify всегда даёт валидный документ.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
53fc16d2f1
commit
a44a29a7d8
@@ -1,4 +1,4 @@
|
||||
# subsystem-edit v1.21 — Edit existing 1C subsystem XML (+тип Bot; cfe-diff/cfe-borrow: недостающие типы)
|
||||
# subsystem-edit v1.22 — Edit existing 1C subsystem XML (+тип Bot; cfe-diff/cfe-borrow: недостающие типы)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
param(
|
||||
[Parameter(Mandatory)][Alias('Path')][string]$SubsystemPath,
|
||||
@@ -10,6 +10,25 @@ param(
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# --- Разбор пользовательского JSON ---
|
||||
# Одна строка в stderr вместо дампа исключения ConvertFrom-Json (issue #80): агент по стектрейсу
|
||||
# идёт чинить скрипт, а не свой вызов. $source — файл или параметр. $expected заполняем только
|
||||
# для полиморфного входа: у файла подсказка была бы наполнителем.
|
||||
# Возврат через -NoEnumerate: без него одноэлементный
|
||||
# JSON-массив разворачивался бы в скаляр вторым анруллингом.
|
||||
function ConvertFrom-JsonInput([string]$text, [string]$source, [string]$expected) {
|
||||
try {
|
||||
$parsed = $text | ConvertFrom-Json
|
||||
} catch {
|
||||
$what = if ($expected) { "$source expects $expected" } else { "Invalid JSON in $source" }
|
||||
$got = ($text -replace '\s+', ' ').Trim()
|
||||
if ($got.Length -gt 60) { $got = $got.Substring(0, 60) + '...' }
|
||||
[Console]::Error.WriteLine("[ERROR] ${what}, got: ${got} ($($_.Exception.Message))")
|
||||
exit 1
|
||||
}
|
||||
Write-Output -NoEnumerate $parsed
|
||||
}
|
||||
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
# --- Content type normalization (plural→singular, Russian→English) ---
|
||||
@@ -429,10 +448,10 @@ function Expand-SelfClosingElement($container, $parentIndent) {
|
||||
}
|
||||
|
||||
# --- Parse value: string or JSON array ---
|
||||
function Parse-ValueList([string]$val) {
|
||||
function Parse-ValueList([string]$val, [string]$opName) {
|
||||
$val = $val.Trim()
|
||||
if ($val.StartsWith("[")) {
|
||||
$arr = $val | ConvertFrom-Json
|
||||
$arr = ConvertFrom-JsonInput $val "-Value for operation '$opName'" "a JSON array of object names"
|
||||
$result = @(); foreach ($item in $arr) { $result += "$item" }
|
||||
return ,$result
|
||||
}
|
||||
@@ -565,7 +584,7 @@ function Do-RemoveChild([string]$childName) {
|
||||
}
|
||||
|
||||
function Do-SetProperty([string]$jsonVal) {
|
||||
$propDef = $jsonVal | ConvertFrom-Json
|
||||
$propDef = ConvertFrom-JsonInput $jsonVal "-Value for operation 'set-property'" "a JSON object {name, value}"
|
||||
$propName = "$($propDef.name)"
|
||||
$propValue = "$($propDef.value)"
|
||||
|
||||
@@ -639,7 +658,7 @@ if ($DefinitionFile) {
|
||||
$DefinitionFile = Join-Path (Get-Location).Path $DefinitionFile
|
||||
}
|
||||
$jsonText = Get-Content -Raw -Encoding UTF8 $DefinitionFile
|
||||
$ops = $jsonText | ConvertFrom-Json
|
||||
$ops = ConvertFrom-JsonInput $jsonText $DefinitionFile
|
||||
if ($ops -is [System.Array]) {
|
||||
foreach ($op in $ops) { $operations += $op }
|
||||
} else {
|
||||
@@ -654,8 +673,8 @@ foreach ($op in $operations) {
|
||||
$opValue = if ($op.value) { "$($op.value)" } else { "$Value" }
|
||||
|
||||
switch ($opName) {
|
||||
"add-content" { Do-AddContent (Parse-ValueList $opValue) }
|
||||
"remove-content" { Do-RemoveContent (Parse-ValueList $opValue) }
|
||||
"add-content" { Do-AddContent (Parse-ValueList $opValue $opName) }
|
||||
"remove-content" { Do-RemoveContent (Parse-ValueList $opValue $opName) }
|
||||
"add-child" { Do-AddChild $opValue }
|
||||
"remove-child" { Do-RemoveChild $opValue }
|
||||
"set-property" { Do-SetProperty $opValue }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# subsystem-edit v1.21 — Edit existing 1C subsystem XML (+тип Bot; cfe-diff/cfe-borrow: недостающие типы)
|
||||
# subsystem-edit v1.22 — Edit existing 1C subsystem XML (+тип Bot; cfe-diff/cfe-borrow: недостающие типы)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -13,6 +13,29 @@ from lxml import etree
|
||||
|
||||
# Регистронезависимый ввод — паритет с PS1: в PowerShell имена параметров и [ValidateSet]
|
||||
# регистр не различают, в argparse совпадение точное.
|
||||
|
||||
def parse_json_input(text, source, expected=None):
|
||||
"""Разбор пользовательского JSON: одна строка в stderr вместо traceback (issue #80).
|
||||
|
||||
expected заполняем только для полиморфного входа: у файла подсказка
|
||||
была бы наполнителем — имя файла и текст парсера самодостаточны.
|
||||
|
||||
Импорты внутри тела: копия функции живёт в навыках с разными именами модулей
|
||||
(skd-decompile импортирует json локально как _json), а тело обязано быть одинаковым.
|
||||
"""
|
||||
import json as _pj
|
||||
import sys as _psys
|
||||
try:
|
||||
return _pj.loads(text)
|
||||
except ValueError as exc:
|
||||
what = "%s expects %s" % (source, expected) if expected else "Invalid JSON in %s" % source
|
||||
got = " ".join(str(text).split())
|
||||
if len(got) > 60:
|
||||
got = got[:60] + "..."
|
||||
print("[ERROR] %s, got: %s (%s)" % (what, got, exc), file=_psys.stderr)
|
||||
_psys.exit(1)
|
||||
|
||||
|
||||
class CIDict(dict):
|
||||
# Ключи храним КАК ЕСТЬ: часть из них — имена объектов (табличные части, стандартные
|
||||
# реквизиты), они попадают в XML. Регистронезависим только поиск. Порядок вставки
|
||||
@@ -515,11 +538,11 @@ def import_fragment(xml_string, doc_root):
|
||||
return nodes
|
||||
|
||||
|
||||
def parse_value_list(val):
|
||||
def parse_value_list(val, op_name):
|
||||
"""Parse a string or JSON array into a list of strings."""
|
||||
val = val.strip()
|
||||
if val.startswith("["):
|
||||
arr = ci_json(json.loads(val))
|
||||
arr = ci_json(parse_json_input(val, "-Value for operation '%s'" % op_name, "a JSON array of object names"))
|
||||
return [str(item) for item in arr]
|
||||
return [val]
|
||||
|
||||
@@ -777,7 +800,8 @@ def main():
|
||||
|
||||
def do_set_property(json_val):
|
||||
nonlocal modify_count
|
||||
prop_def = ci_json(json.loads(json_val))
|
||||
prop_def = ci_json(parse_json_input(
|
||||
json_val, "-Value for operation 'set-property'", "a JSON object {name, value}"))
|
||||
prop_name = str(prop_def["name"])
|
||||
prop_value = str(prop_def.get("value", ""))
|
||||
|
||||
@@ -874,7 +898,7 @@ def main():
|
||||
if not os.path.isabs(def_file):
|
||||
def_file = os.path.join(os.getcwd(), def_file)
|
||||
with open(def_file, "r", encoding="utf-8-sig") as fh:
|
||||
ops = ci_json(json.loads(fh.read()))
|
||||
ops = ci_json(parse_json_input(fh.read(), def_file))
|
||||
if isinstance(ops, list):
|
||||
operations = ops
|
||||
else:
|
||||
@@ -889,9 +913,9 @@ def main():
|
||||
op_value = op.get("value", args.Value or "")
|
||||
|
||||
if op_key == "add-content":
|
||||
do_add_content(parse_value_list(op_value))
|
||||
do_add_content(parse_value_list(op_value, op_name))
|
||||
elif op_key == "remove-content":
|
||||
do_remove_content(parse_value_list(op_value))
|
||||
do_remove_content(parse_value_list(op_value, op_name))
|
||||
elif op_key == "add-child":
|
||||
do_add_child(op_value)
|
||||
elif op_key == "remove-child":
|
||||
|
||||
Reference in New Issue
Block a user