mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-08-15 15:59:00 +03:00
fix(skills): регистронезависимые ключи DSL в py-портах
Дорожка DSL из кампании паритета. PowerShell читает свойства объекта из ConvertFrom-Json без учёта регистра, Python — точным совпадением, поэтому "Name" вместо "name" в py-порте молча не находился: навык печатал [OK], а свойство в выход не попадало. На skd-compile это давало схему без запроса. CIDict + ci_json (эталон — meta-compile) внесены в 10 портов, читающих пользовательский DSL: cf-edit, form-compile, form-edit, interface-edit, meta-edit, mxl-compile, role-compile, skd-compile, subsystem-compile, subsystem-edit. Обёрнуты точки разбора DSL, операций и JSON, приходящего значением параметра; реестр .v8-project.json намеренно не трогаем. skd-edit в список не входит: он принимает операцию через -Operation с choices, что уже закрыто ci_parse_args. Каждому навыку добавлен кейс lenient-key-case: вход с перемешанным регистром ключей, эталон один на оба порта — раннер гоняет обе среды, поэтому кейс сам по себе проверяет паритет. Проверка: 683/683 (PS), 680+3 skipped (PY), гарды 4/4, версии портов совпадают. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d325a3d5af
commit
57b0f95b5f
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# interface-edit v1.16 — Edit 1C CommandInterface.xml (+русские алиасы типов: формы с ё и без)
|
||||
# interface-edit v1.17 — Edit 1C CommandInterface.xml (+русские алиасы типов: формы с ё и без)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
|
||||
import argparse
|
||||
@@ -12,6 +12,43 @@ from lxml import etree
|
||||
|
||||
# Регистронезависимый ввод — паритет с PS1: в PowerShell имена параметров и [ValidateSet]
|
||||
# регистр не различают, в argparse совпадение точное.
|
||||
class CIDict(dict):
|
||||
# Ключи храним КАК ЕСТЬ: часть из них — имена объектов (табличные части, стандартные
|
||||
# реквизиты), они попадают в XML. Регистронезависим только поиск. Порядок вставки
|
||||
# сохраняется — от него зависит порядок эмиссии.
|
||||
def _actual(self, key):
|
||||
if not isinstance(key, str) or dict.__contains__(self, key):
|
||||
return key
|
||||
ci = self.__dict__.get('_ci')
|
||||
if ci is None or len(ci) != len(self):
|
||||
ci = {k.lower(): k for k in self if isinstance(k, str)}
|
||||
self.__dict__['_ci'] = ci
|
||||
return ci.get(key.lower(), key)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return dict.__getitem__(self, self._actual(key))
|
||||
|
||||
def __contains__(self, key):
|
||||
return dict.__contains__(self, self._actual(key))
|
||||
|
||||
def get(self, key, default=None):
|
||||
return dict.get(self, self._actual(key), default)
|
||||
|
||||
def pop(self, key, *default):
|
||||
return dict.pop(self, self._actual(key), *default)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
# запись по ключу, отличающемуся регистром, обновляет существующий, а не плодит дубль
|
||||
dict.__setitem__(self, self._actual(key), value)
|
||||
|
||||
def ci_json(obj):
|
||||
"""Рекурсивно оборачивает разобранный JSON: словари → CIDict, списки обходятся."""
|
||||
if isinstance(obj, dict):
|
||||
return CIDict((k, ci_json(v)) for k, v in obj.items())
|
||||
if isinstance(obj, list):
|
||||
return [ci_json(v) for v in obj]
|
||||
return obj
|
||||
|
||||
def ci_parse_args(parser, argv=None):
|
||||
"""parse_args по правилам PS: имена параметров и значения choices регистронезависимы."""
|
||||
argv = list(sys.argv[1:] if argv is None else argv)
|
||||
@@ -314,7 +351,7 @@ def import_ci_fragment(xml_string):
|
||||
def parse_value_list(val):
|
||||
val = val.strip()
|
||||
if val.startswith("["):
|
||||
arr = json.loads(val)
|
||||
arr = ci_json(json.loads(val))
|
||||
return [str(item) for item in arr]
|
||||
return [val]
|
||||
|
||||
@@ -610,7 +647,7 @@ def main():
|
||||
|
||||
def do_place(json_val):
|
||||
nonlocal add_count, modify_count
|
||||
defn = json_val if isinstance(json_val, dict) else json.loads(json_val)
|
||||
defn = ci_json(json_val if isinstance(json_val, dict) else json.loads(json_val))
|
||||
cmd_name = normalize_cmd_name(str(defn["command"]))
|
||||
group_name = str(defn["group"])
|
||||
if not cmd_name or not group_name:
|
||||
@@ -638,7 +675,7 @@ def main():
|
||||
|
||||
def do_order(json_val):
|
||||
nonlocal add_count, remove_count
|
||||
defn = json_val if isinstance(json_val, dict) else json.loads(json_val)
|
||||
defn = ci_json(json_val if isinstance(json_val, dict) else json.loads(json_val))
|
||||
group_name = str(defn["group"])
|
||||
commands = [normalize_cmd_name(str(c)) for c in defn["commands"]]
|
||||
if not group_name or not commands:
|
||||
@@ -672,7 +709,7 @@ def main():
|
||||
|
||||
def do_subsystem_order(json_val):
|
||||
nonlocal add_count, remove_count
|
||||
parsed = json_val if isinstance(json_val, list) else json.loads(json_val)
|
||||
parsed = ci_json(json_val if isinstance(json_val, list) else json.loads(json_val))
|
||||
subsystems = [str(s) for s in parsed]
|
||||
if not subsystems:
|
||||
print("subsystem-order requires array of subsystem paths", file=sys.stderr)
|
||||
@@ -697,7 +734,7 @@ def main():
|
||||
|
||||
def do_group_order(json_val):
|
||||
nonlocal add_count, remove_count
|
||||
parsed = json_val if isinstance(json_val, list) else json.loads(json_val)
|
||||
parsed = ci_json(json_val if isinstance(json_val, list) else json.loads(json_val))
|
||||
groups = [str(g) for g in parsed]
|
||||
if not groups:
|
||||
print("group-order requires array of group names", file=sys.stderr)
|
||||
@@ -727,7 +764,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 = json.loads(fh.read())
|
||||
ops = ci_json(json.loads(fh.read()))
|
||||
if isinstance(ops, list):
|
||||
operations = ops
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user