Compare commits

...

No commits in common. "f359f02fe46ce2ab6845b64396b60541ce3f0be7" and "main" have entirely different histories.

7 changed files with 35 additions and 79 deletions

13
CHANGELOG.md Normal file
View File

@ -0,0 +1,13 @@
# Changelog
## 0.1.2 — 2026-10-30
- Добавлен маркер PEP 561 `arm_statements/py.typed` в дистрибутив: аннотации доступны потребителям библиотеки.
- `statements_version()` получает версию только из `importlib.metadata` дистрибутива `pylib-ai-arm-statements`; при отсутствии метаданных возвращает `None`. Параметр пути и чтение `arm_statements/VERSION` удалены.
- `statement_code_orders.yaml` обновлён байтовой копией `docs/contracts/statement-code-orders.yaml`: комментарии описывают библиотечную поставку без `sync-statements`.
- README приведён к установке библиотеки; changelog и маркер типов включены в исходный дистрибутив.
## 0.1.1
- Общий код нормализации ББ/ОФР оформлен как устанавливаемый пакет `pylib-ai-arm-statements` с импортом `arm_statements` и YAML-словарями.
- Добавлены проверки упаковки, тесты и настройки Ruff/mypy.

View File

@ -3,3 +3,6 @@ include VERSION
include requirements.txt
recursive-include arm_statements *.py
recursive-include arm_statements/rules *.yaml
include CHANGELOG.md
include arm_statements/py.typed

View File

@ -1,10 +1,11 @@
# arm-statements
Общий код нормализации форм ББ/ОФР (пакет `arm_statements`) для tabular-parser, scan-gateway и arm-core.
Раскладывается копией в `api/arm_statements/` хостов скриптом `sync-statements` из arm-ka-docs.
Устанавливается как библиотека `pylib_ai_arm_statements` из локального checkout или собранного wheel; в PyPI не публикуется.
Спека: arm-ka-docs, `openspec/changes/unified-statement-normalization/`.
Копия раскладывается `make -C docs sync-statements`; прямые правки копии в хостах запрещены.
В сервисах библиотека устанавливается через `make venv` / `make install`; исходники и словари изменяются в этом репозитории.
История выпусков — [CHANGELOG.md](CHANGELOG.md).
## Как проверить
@ -140,7 +141,7 @@ make_warning(code: str, message: str, rules: Rules, *, line_refs: Sequence[str]
# SHEET_NOT_JOINED, SHEET_OTHER только через неё
to_jsonable(obj: object) -> Any # dataclass пакета → json-совместимое значение (golden)
candidate_from_dict(data: Mapping[str, Any]) -> FormCandidate # обратная для кандидата
statements_version(path: Path | None = None) -> str | None # содержимое api/arm_statements/VERSION копии
statements_version() -> str | None # importlib.metadata: версия установленного дистрибутива
load_rules(directory: Path | None = None) -> Rules # RulesError при ошибке словаря
default_rules() -> Rules # кэш словарей пакета
```
@ -238,8 +239,8 @@ Rules.warnings -> Mapping[str, WarningRule] # WarningRule(code, severit
## Как менять словари
Словари пакета — данные в `arm_statements/rules/*.yaml`, правка идёт PR сюда, затем `make -C docs sync-statements` в
хостах («сначала пакет»).
Словари пакета — данные в `arm_statements/rules/*.yaml`, правка идёт PR сюда, затем выпускается новая версия библиотеки
и обновляется зависимость в сервисах («сначала пакет»).
- `statement_code_orders.yaml` — **байтовая копия** `docs/contracts/statement-code-orders.yaml` (arm-ka-docs); руками не
правится. Перечень кодов меняется только PR в docs, затем копия переносится сюда без изменений.

View File

@ -1 +1 @@
0.1.1
0.1.2

0
arm_statements/py.typed Normal file
View File

View File

@ -1,20 +1,10 @@
"""Версия копии из VERSION или метаданных установленного пакета."""
"""Версия установленного дистрибутива библиотеки."""
from importlib.metadata import PackageNotFoundError, version
from pathlib import Path
_DEFAULT_MARKER = Path(__file__).parent / "VERSION"
def statements_version(path: Path | None = None) -> str | None:
"""Прочитать VERSION; для установленного пакета использовать версию дистрибутива."""
marker = _DEFAULT_MARKER if path is None else path
if marker.is_file():
text = marker.read_text(encoding="utf-8").strip()
if text or path is not None:
return text or None
if path is not None:
return None
def statements_version() -> str | None:
"""Вернуть версию установленного пакета или None, если метаданные недоступны."""
try:
return version("pylib-ai-arm-statements")
except PackageNotFoundError:

View File

@ -1,74 +1,23 @@
from importlib.metadata import PackageNotFoundError
from pathlib import Path
import pytest
import arm_statements.version as version_module
from arm_statements.version import statements_version
FULL_HASH = "abc1234def5678abc1234def5678abc1234def56"
def test_version_from_distribution(monkeypatch: pytest.MonkeyPatch) -> None:
def distribution_version(name: str) -> str:
assert name == "pylib-ai-arm-statements"
return "0.1.2"
monkeypatch.setattr(version_module, "version", distribution_version)
assert statements_version() == "0.1.2"
def test_version_absent_file_is_none(tmp_path: Path) -> None:
assert statements_version(tmp_path / "VERSION") is None
def test_version_read_from_file(tmp_path: Path) -> None:
marker = tmp_path / "VERSION"
marker.write_text(f"{FULL_HASH}\n", encoding="utf-8")
assert statements_version(marker) == FULL_HASH
def test_version_empty_file_is_none(tmp_path: Path) -> None:
marker = tmp_path / "VERSION"
marker.write_text(" \n", encoding="utf-8")
assert statements_version(marker) is None
@pytest.mark.parametrize("contents", [None, " \n"])
def test_default_marker_falls_back_to_distribution(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, contents: str | None
) -> None:
marker = tmp_path / "VERSION"
if contents is not None:
marker.write_text(contents, encoding="utf-8")
monkeypatch.setattr(version_module, "_DEFAULT_MARKER", marker)
monkeypatch.setattr(version_module, "version", lambda name: "0.1.1" if name == "pylib-ai-arm-statements" else None)
assert statements_version() == "0.1.1"
def test_default_marker_takes_priority(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
marker = tmp_path / "VERSION"
marker.write_text(f" {FULL_HASH}\n", encoding="utf-8")
monkeypatch.setattr(version_module, "_DEFAULT_MARKER", marker)
def unexpected_metadata_lookup(name: str) -> str:
raise AssertionError(f"Unexpected metadata lookup: {name}")
monkeypatch.setattr(version_module, "version", unexpected_metadata_lookup)
assert statements_version() == FULL_HASH
def test_default_version_without_metadata_is_none(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(version_module, "_DEFAULT_MARKER", tmp_path / "VERSION")
def test_version_without_metadata_is_none(monkeypatch: pytest.MonkeyPatch) -> None:
def missing_distribution(name: str) -> str:
raise PackageNotFoundError(name)
monkeypatch.setattr(version_module, "version", missing_distribution)
assert statements_version() is None
@pytest.mark.parametrize("contents", [None, " \n"])
def test_explicit_marker_does_not_use_distribution(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch, contents: str | None
) -> None:
marker = tmp_path / "VERSION"
if contents is not None:
marker.write_text(contents, encoding="utf-8")
def unexpected_metadata_lookup(name: str) -> str:
raise AssertionError(f"Unexpected metadata lookup: {name}")
monkeypatch.setattr(version_module, "version", unexpected_metadata_lookup)
assert statements_version(marker) is None