안정 API 표면 (Stable API)

from hwpx import ... 최상위 표면은 세 계층으로 나뉩니다. 계층에 따라 계약의 강도와 변경 예고 방식이 다릅니다.

계층 정책

  • stable — hwpx.__all__에 있는 이름. 계약(시그니처·동작·반환 스키마)이 굳어 있고, major 경계에서만 깨질 수 있습니다. 접근 시 경고가 없습니다.

  • experimental — 계약이 유동적입니다. minor 릴리스에서 변경될 수 있으므로 from hwpx.experimental import ...로 import하세요. 최상위 from hwpx import ... 경로도 하위 호환을 위해 유지하지만 접근 시 DeprecationWarning이 나며, 다음 major에서 최상위 재내보내기가 제거될 예정입니다(실제 구현 모듈·hwpx.experimental 경로는 유지).

  • deprecated — 대체 경로로 이전하세요. 접근 시 DeprecationWarning이 나고 경고 메시지에 대체 경로가 포함됩니다. 다음 major에서 제거될 예정입니다.

최소 deprecation window

이름을 제거하려면 먼저 한 번의 major에서 DeprecationWarning을 낸 뒤 그다음 major에서 제거합니다(경고 없는 즉시 제거 금지). 4.0.0은 이 창을 여는 major였고 제거된 이름이 0개였습니다. 5.0.0은 그 창을 닫는 major로, 4.x에서 경고를 내던 deprecated 표면 4종을 제거합니다(아래 「5.0에서 제거된 deprecated 표면」). 응용 계층으로 옮겨 간 이름은 제거가 아니라 이동이며, import하면 어디로 갔는지 알려 주는 오류가 납니다 — 5.0 마이그레이션 가이드 참조.

반환되는 객체 — hwpx.model 이 계약이다 (6.0)

5.x 의 이 절은 자기모순이었다. hwpx.oxml.* 을 “공개 표면이 아니다”라고 선언하면서, stable 로 선언한 HwpxDocument 의 메서드들이 HwpxOxmlParagraph· HwpxOxmlTable·HwpxOxmlSection 등 18종을 반환했다. 사용자는 “비공개”라고 적힌 타입을 손에 쥐고 그것으로 일할 수밖에 없었다.

6.0 은 이렇게 정리한다.

hwpx.model 이 계약이고, hwpx.oxml 은 그것이 사는 곳이다.

from hwpx import model
from hwpx.oxml.paragraph import HwpxOxmlParagraph

assert model.Paragraph is HwpxOxmlParagraph   # 래퍼가 아니라 별칭이다

래퍼를 만들지 않은 이유: 객체 모델이 두 벌이 되고, 래퍼는 결국 .oxml 을 노출해야 하므로 같은 누출에 코드만 두 배가 된다. 반대로 hwpx.oxml 전체를 stable 로 올리면 24개 모듈 수백 멤버가 major 에서만 바뀔 수 있게 되어, 바로 그 클래스들에 요소를 더해야 하는 포맷 깊이 작업이 멈춘다.

계약은 클래스가 아니라 멤버 목록이다. tests/data/model_surface.json 이 클래스별로 stable 멤버를 정확히 나열한다 — 현재 19개 클래스 / 198개 멤버.

  • 목록 안의 멤버 → stable. major 경계에서만 바뀐다.

  • 목록 밖의 멤버(156개 — apply_model·mark_dirty·remove_stale_layout_caches 등) → 구현 세부. minor 에서 바뀔 수 있다.

hwpx.oxml.* import 경로는 그대로 살아 있다. 옮기지도, deprecate 하지도 않는다.

지원되지 않는(비공개) 표면

hwpx._document.* 등 구현 모듈을 직접 import 하는 것은 공개 표면이 아니다. hwpx.oxml.* 은 위 규칙을 따른다 — 경로는 열려 있고, 계약은 hwpx.model 의 멤버 목록이 정한다.

6.0 파사드 표면

HwpxDocument 의 공개 멤버는 35개다(5.x 는 102개). 나머지 79개는 도메인 네임스페이스로 이동했고, 옛 이름은 DeprecationWarning 과 함께 계속 답한다 — 7.0 에서 제거된다. 대응표는 6.0 이주 가이드.

이동은 제거가 아니므로 따로 센다:

수

락

루트 공개 멤버

35

tests/data/document_facade_surface.json

위임 shim (7.0 제거)

79

tests/data/document_legacy_shims.json

반환 객체 계약

193

tests/data/model_surface.json

설치본에 직접 물어볼 수도 있다:

python -m hwpx.capabilities --verify

stable (36)

major 경계에서만 깨지는 이름들입니다.

문서 열기·저장·패키지

  • HwpxDocument, HwpxPackage

  • SavePipeline, QualityPolicy, VisualCompleteReport

  • MutationReport, PreservationDowngradeError

  • HwpxError (구조화 예외 베이스 — 아래 오류 계약 참조)

  • Hwp5Error, Hwp5ConversionWarning (HWP 5.0 .hwp 읽기·쓰기 — 아래 오류 계약 참조)

  • EditorOpenSafetyReport, PackageValidationReport, validate_editor_open_safety, validate_package

바이트 보존 패치

  • BytePreservingPatchResult, ParagraphTextPatch, PatchApplied, PatchSkipped, paragraph_patch

읽기·추출

  • TextExtractor, ParagraphInfo, SectionInfo, DEFAULT_NAMESPACES

  • ObjectFinder, FoundElement

  • HwpxMarkdownConverter

비교·메일머지

  • doc_diff, diff_paragraphs, inspect_reference_consistency

  • merge_template_rows, load_mail_merge_rows, inspect_mail_merge_placeholders

  • DOC_DIFF_REPORT_VERSION, REFERENCE_CONSISTENCY_REPORT_VERSION, MAIL_MERGE_REPORT_VERSION

메타

  • __version__

experimental (26)

from hwpx.experimental import ...로 사용하세요. 계약이 유동적입니다.

  • 문서 ingestion 프레임워크(임의 포맷 → HWPX): DocumentIngestor, DocumentConverter, DocumentIngestResult, DocumentSourceInfo, ConversionAttempt, DocumentIngestError, UnsupportedDocumentFormat

  • 레이아웃 프리뷰(한컴 없는 정직 근사): render_layout_preview, LayoutPreview, PreviewPage

  • 쪽 수 추정(한/글 없이, hwpx.layout.pages): estimate_pages, PageEstimate, EstimatedLine — 본문 문단의 줄마다 쪽·단·세로 위치를 추정한다. 따르지 않는 요소가 있으면 pages는 None이고 unsupported가 까닭을 구역마다 적는다

  • 문서 프리뷰 뷰어(3.8.0 신규): render_document_viewer, DocumentViewer

  • 수식 저작(5.2.0 신규, LaTeX → EqEdit): latex_to_eqedit, estimate_equation_size, UnsupportedLatexError — 이 세 이름은 5.2.0부터 experimental이었으나 이 문서가 누락하고 있었다 (자기서술 드리프트 — describe_capabilities()의 라이브 census가 이제 이런 누락을 구조적으로 막는다)

  • 편집 계획 실행기(5.6.0 신규, hwpx.plan): apply_edit_plan, validate_edit_plan, EditPlan, PlanReport, PlanValidationError

  • 기계가독 자기서술(5.6.0 신규, hwpx.capabilities): describe_capabilities, contract_document, contract_json_schema

HwpxMarkdownConverter(HWPX → Markdown 읽기)는 성숙한 경로라 stable입니다. 위 ingestion 프레임워크(임의 포맷 → HWPX)만 experimental입니다.

5.0에서 제거된 deprecated 표면 (4)

대체 = 구조적 form-fill 경로(라이브러리 hwpx.table_patch.fill_cells 계열, MCP analyze_form_fill/apply_form_fill/verify_form_fill).

  • analyze_template_formfit, apply_template_formfit

  • TEMPLATE_FORMFIT_BASELINE_SCHEMA_VERSION, TEMPLATE_FORMFIT_PLAN_SCHEMA_VERSION

오류 계약 (4.0.0 도입)

아래 표에 있는 예외 — 쓰기 보존 계약, 저장 경로 게이트, 표 구조 편집, 이동된 표면 안내 — 는 hwpx.errors.HwpxError(최상위 hwpx.HwpxError로도 import) 베이스를 상속합니다. 모든 호출을 감싸는 포괄 계약은 아닙니다: 없는 파일은 FileNotFoundError, HWPX 패키지가 아닌 입력은 zipfile.BadZipFile처럼 파이썬이 원래 내는 예외가 그대로 올라옵니다(지원 매트릭스 참조). 구조화된 예외는 사람용 문장(str(exc))을 그대로 두고, 세 가지 기계가 읽는 필드를 얹습니다:

속성

의미

code

실패 종류의 안정 식별자(kebab-case). 분기 가능하며 major 경계에서만 바뀜.

context

실패를 유발한 실측 값 딕셔너리(오프닝 part·인덱스·개수…). 없으면 {}.

suggestion

실행 가능한 다음 한 단계, 없으면 None.

exc.to_dict()는 {code, message, context, suggestion} 봉투를 돌려줍니다.

상속으로 하위 호환 유지

구조화 이전에도 각 예외는 ValueError/RuntimeError/Exception이었고, 4.0.0과 5.0.0 모두 그 관계를 유지합니다 — 기존 except가 깨지지 않습니다.

예외

code

상속

발생 경로

PreservationDowngradeError

preservation-downgrade

HwpxError

save_to_path/save_to_stream/to_bytes의 mode="patch" + fallback="error" 미달

hwpx.errors.SaveError

save-failed(기본), document-validation-failed·open-safety-failed·quality-gate-failed

HwpxError, ValueError

대표 저장 경로의 사전검증·open-safety·품질 게이트 실패

hwpx.table_patch.TableStructureError

table-structure

HwpxError, ValueError

표 구조 편집 거부(fail-closed)·미지원

hwpx.table_patch.RenderCheckRequired

render-check-required

HwpxError, RuntimeError

verify_fill(require=True)인데 실한컴 오라클 미렌더

Hwp5Error

hwp5-damaged(기본)·hwp5-password·hwp5-distribution·hwp5-drm·hwp5-not-hwp5·hwp5-version-unsupported·hwp5-limit-exceeded·hwp5-write-unsupported

HwpxError, ValueError

.hwp 열기(HwpxDocument.open)와 HWP 5.0 쓰기(save_to_path("x.hwp"), format="hwp")

HWP 5.0 (.hwp)

HwpxDocument.open은 .hwp(HWP 5.0)도 열어 같은 문서 모델로 바꿉니다. 옮기지 못한 내용은 읽기 전용 document.conversion_report(unconverted·dropped 개수)에 남고, unconverted가 있으면 Hwp5ConversionWarning을 한 번 냅니다. .hwpx로 연 문서에서는 conversion_report가 None입니다. save_to_path("x.hwp"), save_to_stream(..., format="hwp"), to_bytes(format="hwp")는 HWP 5.0으로 씁니다. 쓸 수 없는 내용이 있으면 아무것도 쓰기 전에 Hwp5Error(hwp5-write-unsupported)로 거부합니다. format에 "hwpx"·"hwp" 밖의 값을 주면 hwpx.errors.HwpxValueError(save-format-unsupported)입니다.

응용 계층의 agent·exam 오류 계약은 python-hwpx-automation이 소유합니다. 이 문서는 core에 실제로 남은 공개 예외만 기술합니다.

스키마 동결 정책

core가 발행하는 versioned contract(hwpx.mutation-report/v1)는 required 필드 집합이 동결됩니다. document-plan·agent-batch·mixed-form-plan 스키마는 5.0에서 python-hwpx-automation이 발행 주체가 됐으며 그쪽 계약 정책을 따릅니다. 정책·계약 테스트는 스키마 동결을 보세요.

기존 도형과 페이지 story 편집

Shape.set_position(horizontal_offset=..., vertical_offset=...)는 기존 떠 있는 도형의 hp:pos 오프셋을 바꿉니다(단위 HWPUNIT, signed 32-bit 정수). horz_rel_to·vert_rel_to·horz_align·vert_align을 주면 hp:pos의 horzRelTo·vertRelTo·horzAlign·vertAlign도 씁니다. 값은 OWPML 스키마 철자 그대로(대문자, 정확히 일치)입니다 — horz_rel_to는 PAPER/PAGE/COLUMN/ PARA, vert_rel_to는 PAPER/PAGE/PARA(스키마에 세로 COLUMN은 없습니다), horz_align은 LEFT/CENTER/RIGHT/INSIDE/OUTSIDE, vert_align은 TOP/ CENTER/BOTTOM/INSIDE/OUTSIDE. 예를 들어 PAPER는 용지 가장자리를 기준으로 삼으므로 확인 도장을 쪽 여백에 고정할 수 있습니다. 주지 않은 값(None)은 그대로 두므로, 오프셋만 주는 호출은 이전과 같은 결과를 냅니다. 앵커·기하 정보는 보존합니다. 모든 인자는 변경 전에 검사합니다 — 글자처럼 배치된 개체, 위치 요소가 없는 개체는 shape-position-unsupported, 잘못된 오프셋은 shape-position-value, 스키마 밖의 프레임·정렬 값은 shape-position-frame으로 거부합니다. 이동 후 실제 배치는 한컴으로 확인해야 합니다.

Shape.draw_text·set_draw_text·remove_draw_text와 이들이 돌려주는 model.DrawText는 stable입니다. set_draw_text(..., para_pr_id_ref=..., vert_align=...)는 도형 안 문단의 paraPrIDRef와 hp:subList/@vertAlign(TOP/CENTER/BOTTOM)을 정합니다. 둘 다 생략하면 이전과 같은 XML이 나옵니다 — 새 글상자는 paraPrIDRef="0"· CENTER, 기존 글상자는 제 정렬을 유지합니다. 잘못된 정렬 값은 shape-draw-text-vert-align으로 변경 전에 거부합니다.

SectionProperties.headers/footers와 get_header/get_footer는 본문 컨트롤에 있는 기존 머리말·꼬리말도 찾습니다. set_simple_text_preserving은 빈 문단·빈 run을 유지하면서 단일 표시 텍스트를 수정합니다. 복수 표시 텍스트, 중첩 제어, 중복 identity/적용 범위 충돌은 거부합니다. 새로 만드는 set_header_text 등의 경로와 기존 story 보존 편집의 지원 범위를 혼동하지 마세요.

Table.set_cell_borders(row, col, color="#0055AA", line_type="SOLID")는 선택한 셀의 네 변 색과 선 종류만 수정합니다. 기존 선 두께·배경·대각선·기타 속성을 복제해 보존하고 동일한 스타일이 있으면 재사용합니다. 공유 원본 스타일은 수정하지 않습니다. 스타일이 없거나 중복되었으면 변경 전에 거부합니다. 전체 스타일을 바꾸려면 기존 set_cell_border_fill을 사용하세요.

TableCell.margins는 한/글이 셀을 배치하는 안쪽 여백을 hwpx.objects.CellMargins (HWPUNIT)로 돌려줍니다. 셀의 hasMargin이 꺼져 있으면 표의 hp:inMargin, 켜져 있으면 셀의 hp:cellMargin입니다. add_table()이 만든 셀은 CellMargins(510, 510, 141, 141)입니다. TableCell.set_margins(left=..., right=..., top=..., bottom=...)는 주지 않은 면을 지금 여백으로 채워 셀 여백 네 면을 쓰고 hasMargin을 켭니다. 값은 0 <= v < 2**31인 int여야 하며, 아니면 바꾸기 전에 cell-margin-value로 거부합니다. 인자가 없으면 아무것도 바꾸지 않습니다.

Table.insert_rows(ref_row, count=1, side="below", blank=False), insert_columns(ref_col, count=1, side="right", blank=False), delete_rows(rows), delete_columns(cols), split_cell(row, col, rows=1, cols=1)은 행·열을 넣고 지우고 칸을 나눕니다. hwpx.table_patch.apply_table_ops의 같은 편집과 같은 규칙(한/글의 줄/칸 추가하기·지우기, 셀 나누기)을 따르고, 새 칸은 기준 칸의 글을 복제하며 blank=True면 빈 문단 하나만 둡니다. 거부하면 TableStructureError를 내고 표를 바꾸지 않습니다. 편집 전에 얻은 셀·행 객체는 표에 속하지 않으니 다시 얻으세요.