Files
mihailkudravcev 98cbb88461 Fix Windows git commit ValueError: embedded null character.
Use git commit -F and strip NULs from argv/version strings (0.3.7).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-24 13:23:32 +03:00

133 lines
3.7 KiB
Python

"""Read 1C configuration identity from dump tree."""
from __future__ import annotations
import re
from dataclasses import dataclass
from pathlib import Path
_VERSION_TAG = re.compile(r"<Version>([^<]+)</Version>")
_NAME_TAG = re.compile(r"<Name>([^<]+)</Name>")
_VENDOR_TAG = re.compile(r"<Vendor>([^<]*)</Vendor>")
_SYN_RE = re.compile(
r"<Synonym>\s*<v8:item>\s*<v8:lang>ru</v8:lang>\s*<v8:content>(.*?)</v8:content>",
re.DOTALL,
)
@dataclass(frozen=True)
class ConfigIdentity:
"""Configuration name / version / vendor for wiki footers."""
version: str
name: str | None = None # technical Name
synonym: str | None = None # Synonym ru
vendor: str | None = None
@property
def display_name(self) -> str | None:
return self.synonym or self.name
def config_root_from_src(src_root: Path) -> Path:
"""src/ → configuration root (parent that usually has VERSION + Configuration.xml)."""
src_root = Path(src_root).resolve()
if src_root.name == "src":
return src_root.parent
return src_root
def _configuration_xml(src_root: Path) -> Path | None:
src_root = Path(src_root)
root = config_root_from_src(src_root)
for xml_path in (
root / "src" / "Configuration.xml",
src_root / "Configuration.xml",
root / "Configuration.xml",
):
if xml_path.is_file():
return xml_path
return None
def _xml_unescape(s: str) -> str:
return (
s.replace("&lt;", "<")
.replace("&gt;", ">")
.replace("&amp;", "&")
.replace("&quot;", '"')
.replace("&apos;", "'")
)
def read_config_version(src_root: Path) -> str:
"""
Resolve configuration version.
Priority:
1. <config_root>/VERSION
2. Configuration.xml → <Version>
"""
return read_config_identity(src_root).version
def read_config_name(src_root: Path) -> str | None:
"""Technical configuration Name from Configuration.xml."""
return read_config_identity(src_root).name
def _sanitize_text(s: str) -> str:
"""Drop NULs (UTF-16 mis-reads / dirty dumps) that break Windows APIs."""
return s.replace("\0", "").strip()
def read_config_identity(src_root: Path) -> ConfigIdentity:
"""Version, Name, Synonym (ru), Vendor from dump."""
src_root = Path(src_root)
root = config_root_from_src(src_root)
version = "unknown"
version_file = root / "VERSION"
if version_file.is_file():
text = _sanitize_text(
version_file.read_text(encoding="utf-8-sig", errors="ignore")
)
if text:
version = text.splitlines()[0].strip()
name: str | None = None
synonym: str | None = None
vendor: str | None = None
xml_path = _configuration_xml(src_root)
if xml_path is not None:
raw = xml_path.read_text(encoding="utf-8-sig", errors="ignore")
# Prefer Properties block to avoid accidental matches deeper in the file
props = raw
if "<Properties>" in raw and "</Properties>" in raw:
props = raw.split("<Properties>", 1)[1].split("</Properties>", 1)[0]
if version == "unknown":
m = _VERSION_TAG.search(props)
if m:
version = _sanitize_text(m.group(1)) or "unknown"
m = _NAME_TAG.search(props)
if m:
name = _sanitize_text(m.group(1)) or None
m = _SYN_RE.search(props)
if m:
synonym = _sanitize_text(_xml_unescape(m.group(1))) or None
m = _VENDOR_TAG.search(props)
if m:
vendor = _sanitize_text(_xml_unescape(m.group(1))) or None
return ConfigIdentity(
version=_sanitize_text(version) or "unknown",
name=name,
synonym=synonym,
vendor=vendor,
)