Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
않는다. `hwpx.TextExtractor`·`hwpx.doc_diff`·`hwpx.validate_package`처럼 `hwpx.tools`에 사는
이름은 처음 쓸 때 경고 없이 읽고, 같은 객체다. 위 `hwpx.hwp5`와 합쳐 `import hwpx`가 읽는
hwpx 모듈은 117개에서 97개로 준다. 저장은 여전히 처음 저장할 때 패키지 검증 도구를 읽는다.
- `hwpx.tools`도 공개 이름과 하위 모듈을 처음 쓸 때 읽는다. 첫 저장은 패키지·문서 검증기만
읽고, 내보내기·뷰어·문서 비교·메일 머지 등은 읽지 않는다. 기존 import·객체·타입 정보와
`doc_diff` 함수 이름을 유지한다. HWPX Markdown 변환기의 배포판 버전 조회도 변환 시점으로
미뤄, `import hwpx`가 `importlib.metadata`를 읽지 않는다(#386).

### 고침

Expand Down
3 changes: 2 additions & 1 deletion src/hwpx/ingest/hwpx_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from __future__ import annotations

from importlib.metadata import PackageNotFoundError, version as package_version
from typing import Any, BinaryIO
from zipfile import BadZipFile, ZipFile

Expand Down Expand Up @@ -132,6 +131,8 @@ def _tables_payload(doc: HwpxDocument) -> list[dict[str, Any]]:


def _python_hwpx_version() -> str:
from importlib.metadata import PackageNotFoundError, version as package_version

try:
return package_version("python-hwpx")
except PackageNotFoundError:
Expand Down
294 changes: 220 additions & 74 deletions src/hwpx/tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,78 +1,85 @@
# SPDX-License-Identifier: Apache-2.0
"""Tooling helpers for inspecting HWPX archives."""

from .exporter import (
export_html,
export_markdown,
export_text,
)
from .layout_preview import (
LayoutPreview,
PreviewPage,
render_layout_preview,
)
from .document_viewer import (
DocumentViewer,
FIDELITY_BADGE,
render_document_viewer,
)
from .object_finder import FoundElement, ObjectFinder
from .doc_diff import (
DOC_DIFF_REPORT_VERSION,
REFERENCE_CONSISTENCY_REPORT_VERSION,
diff_paragraphs,
doc_diff,
inspect_reference_consistency,
)
from .mail_merge import (
MAIL_MERGE_REPORT_VERSION,
inspect_mail_merge_placeholders,
load_mail_merge_rows,
merge_template_rows,
)
from .package_validator import (
EDITOR_OPEN_ADVISORY_ERROR_MARKERS,
EditorOpenSafetyReport,
PackageValidationIssue,
PackageValidationReport,
is_editor_open_blocking_issue,
validate_editor_open_safety,
validate_package,
)
from .page_guard import (
DocumentMetrics,
collect_metrics,
compare_metrics,
)
from .text_extractor import (
DEFAULT_NAMESPACES,
ParagraphInfo,
SectionInfo,
TextExtractor,
build_parent_map,
describe_element_path,
strip_namespace,
)
from .table_navigation import (
TableCellReference,
TableFillApplied,
TableFillFailed,
TableFillResult,
TableLabelMatch,
TableLabelSearchResult,
TableMapEntry,
TableMapResult,
fill_by_path,
find_cell_by_label,
get_table_map,
)
from .validator import (
DocumentSchemas,
ValidationIssue,
ValidationReport,
load_default_schemas,
validate_document,
)
"""Tooling helpers, loaded on first use rather than on the first save."""

from __future__ import annotations

import sys as _sys
from types import ModuleType as _ModuleType
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from .exporter import (
export_html,
export_markdown,
export_text,
)
from .layout_preview import (
LayoutPreview,
PreviewPage,
render_layout_preview,
)
from .document_viewer import (
DocumentViewer,
FIDELITY_BADGE,
render_document_viewer,
)
from .object_finder import FoundElement, ObjectFinder
from .doc_diff import (
DOC_DIFF_REPORT_VERSION,
REFERENCE_CONSISTENCY_REPORT_VERSION,
diff_paragraphs,
doc_diff,
inspect_reference_consistency,
)
from .mail_merge import (
MAIL_MERGE_REPORT_VERSION,
inspect_mail_merge_placeholders,
load_mail_merge_rows,
merge_template_rows,
)
from .package_validator import (
EDITOR_OPEN_ADVISORY_ERROR_MARKERS,
EditorOpenSafetyReport,
PackageValidationIssue,
PackageValidationReport,
is_editor_open_blocking_issue,
validate_editor_open_safety,
validate_package,
)
from .page_guard import (
DocumentMetrics,
collect_metrics,
compare_metrics,
)
from .text_extractor import (
DEFAULT_NAMESPACES,
ParagraphInfo,
SectionInfo,
TextExtractor,
build_parent_map,
describe_element_path,
strip_namespace,
)
from .table_navigation import (
TableCellReference,
TableFillApplied,
TableFillFailed,
TableFillResult,
TableLabelMatch,
TableLabelSearchResult,
TableMapEntry,
TableMapResult,
fill_by_path,
find_cell_by_label,
get_table_map,
)
from .validator import (
DocumentSchemas,
ValidationIssue,
ValidationReport,
load_default_schemas,
validate_document,
)

__all__ = [
"inspect_reference_consistency",
Expand Down Expand Up @@ -129,3 +136,142 @@
"FIDELITY_BADGE",
"render_document_viewer",
]


_EXPORTS_BY_MODULE = {
"exporter": (
"export_html",
"export_markdown",
"export_text",
),
"layout_preview": (
"LayoutPreview",
"PreviewPage",
"render_layout_preview",
),
"document_viewer": (
"DocumentViewer",
"FIDELITY_BADGE",
"render_document_viewer",
),
"object_finder": (
"FoundElement",
"ObjectFinder",
),
"doc_diff": (
"DOC_DIFF_REPORT_VERSION",
"REFERENCE_CONSISTENCY_REPORT_VERSION",
"diff_paragraphs",
"doc_diff",
"inspect_reference_consistency",
),
"mail_merge": (
"MAIL_MERGE_REPORT_VERSION",
"inspect_mail_merge_placeholders",
"load_mail_merge_rows",
"merge_template_rows",
),
"package_validator": (
"EDITOR_OPEN_ADVISORY_ERROR_MARKERS",
"EditorOpenSafetyReport",
"PackageValidationIssue",
"PackageValidationReport",
"is_editor_open_blocking_issue",
"validate_editor_open_safety",
"validate_package",
),
"page_guard": (
"DocumentMetrics",
"collect_metrics",
"compare_metrics",
),
"text_extractor": (
"DEFAULT_NAMESPACES",
"ParagraphInfo",
"SectionInfo",
"TextExtractor",
"build_parent_map",
"describe_element_path",
"strip_namespace",
),
"table_navigation": (
"TableCellReference",
"TableFillApplied",
"TableFillFailed",
"TableFillResult",
"TableLabelMatch",
"TableLabelSearchResult",
"TableMapEntry",
"TableMapResult",
"fill_by_path",
"find_cell_by_label",
"get_table_map",
),
"validator": (
"DocumentSchemas",
"ValidationIssue",
"ValidationReport",
"load_default_schemas",
"validate_document",
),
}
_EXPORT_OWNER = {
name: module for module, names in _EXPORTS_BY_MODULE.items() for name in names
}


def __getattr__(name: str) -> object:
"""Resolve public exports and the historically reachable submodules."""
owner = _EXPORT_OWNER.get(name, name)
if owner == "exporter":
import hwpx.tools.exporter as module
elif owner == "layout_preview":
import hwpx.tools.layout_preview as module
elif owner == "document_viewer":
import hwpx.tools.document_viewer as module
elif owner == "object_finder":
import hwpx.tools.object_finder as module
elif owner == "doc_diff":
from .doc_diff import doc_diff as value

globals()["doc_diff"] = value
module = _sys.modules[f"{__name__}.doc_diff"]
elif owner == "mail_merge":
import hwpx.tools.mail_merge as module
elif owner == "package_validator":
import hwpx.tools.package_validator as module
elif owner == "page_guard":
import hwpx.tools.page_guard as module
elif owner == "text_extractor":
import hwpx.tools.text_extractor as module
elif owner == "table_navigation":
import hwpx.tools.table_navigation as module
elif owner == "validator":
import hwpx.tools.validator as module
else:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

value = getattr(module, name) if name in _EXPORT_OWNER else module
globals()[name] = value
return value


def __dir__() -> list[str]:
return sorted(set(globals()) | set(__all__) | set(_EXPORTS_BY_MODULE))


class _ToolsModule(_ModuleType):
def __setattr__(self, name: str, value: object) -> None:
# Python assigns a loaded child to its parent. Here the public doc_diff
# function has always occupied that name. Preserve it even when someone
# imports the child directly before any package export is resolved.
if (
name == "doc_diff"
and isinstance(value, _ModuleType)
and value.__name__ == f"{__name__}.doc_diff"
):
value = value.doc_diff
super().__setattr__(name, value)


_sys.modules[__name__].__class__ = _ToolsModule
Loading
Loading