Files
cc-1c-skills/docs/python-porting-guide.md
T
Nick Shirokov 86a959a354 feat(crossplatform): add Python 3 ports for all 58 PS1 skill scripts
Add cross-platform Python alternatives alongside existing PowerShell
scripts. PS1 remains the default runtime; Python is opt-in via switch
scripts. All parameters are identical between runtimes.

New files:
- 58 Python scripts in .claude/skills/*/scripts/*.py
- scripts/switch-to-python.py and switch-to-powershell.py
- docs/python-porting-guide.md
- __pycache__/ added to .gitignore

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 16:16:07 +03:00

192 lines
8.5 KiB
Markdown
Raw 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.
# Python Porting Guide
Руководство по Python-портам навыков 1С (PS1 → Python).
## Зачем Python рядом с PS1
PowerShell 5.1 доступен только на Windows. Python-порты обеспечивают кроссплатформенность (Linux, Mac). Модель opt-in: PS1 — по умолчанию, Python — переключается скриптами.
## Переключение рантайма
```bash
# Переключить все .md в навыках на Python
python scripts/switch-to-python.py
# Вернуть на PowerShell
python scripts/switch-to-powershell.py
```
Скрипты обрабатывают все `.md` файлы в `.claude/skills/*/` (SKILL.md, json-dsl.md и др.). Идемпотентны — повторный запуск безопасен. Python-only навыки (img-grid) пропускаются при переключении на PowerShell.
## Принцип самодостаточности
Каждый `.py` — полностью автономен, как и его `.ps1`-аналог. Нет общих модулей. Это соответствует [рекомендациям Anthropic](https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices) и зеркалит существующую архитектуру PS1.
Общие утилиты (5-15 строк) дублируются в каждом скрипте:
```python
def esc_xml(s):
return s.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('"','&quot;')
def emit_mltext(lines, indent, tag, text):
if not text:
lines.append(f"{indent}<{tag}/>")
return
lines.append(f"{indent}<{tag}>")
lines.append(f"{indent}\t<v8:item>")
lines.append(f"{indent}\t\t<v8:lang>ru</v8:lang>")
lines.append(f"{indent}\t\t<v8:content>{esc_xml(text)}</v8:content>")
lines.append(f"{indent}\t</v8:item>")
lines.append(f"{indent}</{tag}>")
def new_uuid():
import uuid
return str(uuid.uuid4())
def read_utf8(path):
with open(path, 'r', encoding='utf-8-sig') as f:
return f.read()
def write_utf8_bom(path, content):
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
f.write(content)
```
Большие словари данных (синонимы типов, карты объектов) тоже inline — как `$script:typeSynonyms` в PS1.
## Конвенция параметров
Формат `-ParamName` сохранён для минимальных различий в SKILL.md:
```python
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument('-JsonPath', dest='JsonPath', required=True)
parser.add_argument('-NoValidate', dest='NoValidate', action='store_true')
```
Switch-параметры (`-NoValidate`) → `action='store_true'`.
## Таблица маппинга PS → Python
| PS1 | Python |
|-----|--------|
| `$script:xml = New-Object StringBuilder` | `lines = []` |
| `$xml.AppendLine($text)` | `lines.append(text)` |
| `$xml.ToString()` | `'\n'.join(lines)` |
| `[System.Xml.XmlDocument] + PreserveWhitespace` | `lxml.etree.XMLParser(remove_blank_text=False)` |
| `$xmlDoc.SelectSingleNode(xpath, $ns)` | `root.find(xpath, namespaces=NSMAP)` |
| `$xmlDoc.SelectNodes(xpath, $ns)` | `root.findall(xpath, namespaces=NSMAP)` |
| `XmlWriter + MemoryStream + BOM fix` | `etree.tostring(root, xml_declaration=True, encoding='UTF-8')` |
| `[System.Guid]::NewGuid().ToString()` | `str(uuid.uuid4())` |
| `$json \| ConvertFrom-Json` | `json.loads(text)` |
| `ConvertTo-Json -Depth 10` | `json.dumps(obj, ensure_ascii=False, indent=2)` |
| `New-Object System.Text.UTF8Encoding($true)` | `encoding='utf-8-sig'` |
| `Start-Process -Wait -PassThru` | `subprocess.run([...], capture_output=True)` |
| `Start-Process` (без -Wait) | `subprocess.Popen([...])` |
| `[switch]$NoValidate` | `parser.add_argument('-NoValidate', action='store_true')` |
| `[ValidateSet("a","b")]` | `choices=["a","b"]` |
| `Get-ChildItem "path\*\..."` | `glob.glob(...)` |
| `Get-Process httpd` | `psutil.process_iter(['pid','name','exe'])` |
| `Test-Path $path` | `os.path.exists(path)` |
| `Resolve-Path` | `os.path.abspath()` |
| `Join-Path $a $b` | `os.path.join(a, b)` |
| `New-Item -ItemType Directory -Force` | `os.makedirs(path, exist_ok=True)` |
| `Remove-Item -Recurse -Force` | `shutil.rmtree(path)` |
| `Write-Host "text"` | `print("text")` |
| `Write-Error "text"` | `print("text", file=sys.stderr)` |
## lxml vs stdlib
- **Compile/init скрипты** (строковая сборка): только stdlib
- **DOM-скрипты** (edit/validate/info): `lxml` с `XMLParser(remove_blank_text=False)` для сохранения whitespace
- **Web-скрипты**: `psutil` для работы с процессами Apache
Зависимости:
- `lxml>=4.9.0` — ~25 DOM-скриптов
- `psutil>=5.9.0` — 4 web-скрипта
## Работа с BOM (UTF-8)
Кодек Python `utf-8-sig` — точный аналог `New-Object System.Text.UTF8Encoding($true)`:
- Запись: добавляет BOM (EF BB BF)
- Чтение: убирает BOM автоматически
```python
# Чтение (BOM убирается)
with open(path, 'r', encoding='utf-8-sig') as f:
content = f.read()
# Запись (BOM добавляется)
with open(path, 'w', encoding='utf-8-sig', newline='') as f:
f.write(content)
```
Параметр `newline=''` предотвращает двойные `\r\n` на Windows.
## Сохранение XML с lxml
```python
from lxml import etree
# Загрузка с сохранением whitespace
parser = etree.XMLParser(remove_blank_text=False)
tree = etree.parse(path, parser)
root = tree.getroot()
# Сохранение с BOM
xml_bytes = etree.tostring(tree, xml_declaration=True, encoding='UTF-8')
# Fix encoding case: lxml пишет utf-8, 1C ожидает UTF-8
xml_bytes = xml_bytes.replace(b"encoding='UTF-8'", b'encoding="UTF-8"')
with open(path, 'wb') as f:
f.write(b'\xef\xbb\xbf') # BOM
f.write(xml_bytes)
```
## Известные подводные камни
### Namespace в XPath
lxml требует явный namespace map. В PS1 используется `XmlNamespaceManager`:
```python
NSMAP = {'md': 'http://v8.1c.ru/8.3/MDClasses'}
node = root.find('.//md:ChildObjects/md:Form', NSMAP)
```
### d5p1: для ссылочных типов
В DCS-файлах ссылочные типы используют `d5p1:`, не `cfg:`:
```xml
<v8:Type xmlns:d5p1="http://v8.1c.ru/8.1/data/enterprise/current-config">d5p1:CatalogRef.XXX</v8:Type>
```
### encoding="UTF-8" (uppercase)
1C ожидает `encoding="UTF-8"`. lxml по умолчанию пишет `encoding='UTF-8'` с одинарными кавычками — нужна замена на двойные.
## Платформозависимые заметки
Скрипты `db-*` и `web-*` используют платформу 1С (Designer CLI, Apache) — работают только на Windows. Но синтаксических ошибок на других ОС не будет: скрипт корректно сообщит об отсутствии платформы.
## Добавление нового навыка
Чеклист:
1. Создать `.ps1` скрипт
2. Создать `.py` скрипт с идентичными параметрами
3. В SKILL.md указать `powershell.exe -NoProfile -File ... .ps1` (по умолчанию)
4. Скрипт переключения автоматически подхватит новый навык
## Обновление существующего навыка
При доработке `.ps1`:
1. Применить аналогичные изменения в `.py`
2. Если затронуты inline-утилиты — обновить во всех скриптах: `grep -r "def esc_xml" .claude/skills/`
## Inline-утилиты — полный список
| Функция | Где используется |
|---------|-----------------|
| `esc_xml()` | compile, init, edit, add скрипты |
| `emit_mltext()` | compile, init, add скрипты |
| `new_uuid()` | init, add, compile скрипты |
| `read_utf8()` | все скрипты |
| `write_utf8_bom()` | все скрипты с записью |
| `paginate()` | info скрипты |
| `split_camelcase()` | info скрипты |