Repository navigation
Conversation
run의 여는 태그를 그 뒤 첫 </hp:run>과 짝짓던 정규식을 중첩을 따라가는 훑기(_scan_runs)로 바꿨다. 대상 색 run이 직접 가진 hp:t만 비우고, 표·글상자· 각주 안 run의 글은 그 run의 색으로 따로 판정한다. 빈 run(<hp:run .../>)을 다음 run과 짝짓던 문제도 같이 없어진다. 줄 배치 캐시 제거도 글이 바뀐 문단 자신의 것만 지우게 했다. 전에는 표 뒤 글이 지워진 바깥 문단의 캐시가 남았고, 원래 비어 있던 문단의 캐시는 지워졌다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
run.replace_text, replace_across_runs(doc.text.replace everywhere), doc.replace_text_in_runs, paragraph/run.apply_model은 바꿀 글의 "\n"을 hp:t 안에 글자로 남겼다. set_text_preserving_runs는 "\n"을 남기고 탭은 지웠다. - _paragraph_text_edit.text_marks_as_elements: hp:t에 남은 탭·줄 바꿈 글자를 그 자리에서 hp:tab·hp:lineBreak 요소로 바꾼다(나머지 내용은 그대로). 치환이 끝난 뒤와 모델을 되돌려 쓴 뒤에 부른다. - set_text_preserving_runs는 다른 setter와 같은 set_text_with_tabs로 채운다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
편집 API - paragraph.apply_model: 모델이 문단 내용을 바꾸고 캐시는 그대로 돌려준 때 그 캐시를 지운다(모델로 캐시만 고치는 쓰임은 그대로). - add_index_mark, add_title_mark, add_drop_cap: 고친 문단의 캐시를 지운다. 저장 때의 안전망 - 폭 추정 기준(마지막 줄 뒤 글이 줄 폭의 2배)은 그대로 둔다. 기준을 1배로 좁히면 한/글이 저장한 정상 캐시가 지워진다(tests의 .hwpx 1,210개, 캐시 67,549개 가운데 334~908개가 1배를 넘는다. 추정 폭이 실제보다 최대 1.55배 크게 나온다). - 대신 구역이 읽을 때의 캐시 요소와 그 문단 내용을 기억하고, 고친 구역을 저장할 때 "읽을 때의 캐시가 그대로인데 문단 내용은 달라진" 캐시를 지운다 (oxml/_layout_cache_guard.py). 폭을 추정하지 않으므로 한 글자만 늘어도 잡히고, 내용이 그대로인 문단의 캐시와 읽은 뒤 새로 쓴 캐시는 건드리지 않는다. 고치지 않은 구역에는 돌지 않는다. 확인: tests의 .hwpx 전부를 고치지 않고 저장(바이트 동일 수 변화 없음), 모든 구역을 dirty로만 표시해 저장(지워지는 캐시 수가 고치기 전과 같은 87개: 기존 ENG-09 차이)했다. form_002에서 문단 하나를 고친 저장 0.67초 가운데 이 훑기가 0.10초다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
add_raw("textart"|"ole"|"video")로 만든 필수 자식 없는 요소가 저장 관문을
통과했고, 한/글 2024가 그 파일을 열다가 경고 없이 종료했다(0xC0000005,
한/글 확인 갈래 결과). _SHAPE_OPEN_SAFETY_TAGS에 세 요소를 넣어 hp:rect와
같은 검사(offset·orgSz·curSz·sz·pos, renderingInfo 행렬)를 받게 했다.
한/글이 저장한 자료는 그대로 통과한다: tests의 .hwpx 가운데 글맵시·OLE·
동영상이 든 7개 파일에서 새 오류 0건. 빈 hp:equation은 한/글이 정상으로
열므로 넣지 않았다.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
package_validator._local_name이 주석·PI 노드의 tag(문자열이 아닌 함수)를 요소 이름으로 다뤄, header.xml이나 줄 배치 캐시가 있는 문단에 그런 노드가 있으면 고치지 않은 저장과 append_document까지 TypeError로 끝났다. 문자열이 아닌 tag에는 ""를 돌려줘 모든 검사가 그 노드를 지나치게 했다. 점검의 12경우(rt_comment_crash.py)가 모두 저장되고, 같은 12경우를 tests/test_save_with_comment_nodes.py에 넣었다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
insert_paragraph_by_clone·reorder_paragraphs·set_paragraph_text는 어느 저장소의 테스트도 실행하지 않았고, delete_paragraph·restyle_text· recolor_runs_by_color는 core 테스트에서 0줄이었다(strip_runs_by_color는 ENG-03 커밋에서 넣었다). 연산마다 정상 경로, 거부 사유, 캐시·id 처리, 건드리지 않은 파트의 바이트 동일, dry_run을 고정한다. body_patch.py 문장 실행률: 이 파일과 기존 body_patch 테스트만으로 95% (점검 때 core 전체 테스트로 40.6%). Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
ARC-16 테스트를 쓰다 나온 결함 넷. 모두 status "applied"에 열림 검사 통과였다. - delete_paragraph / insert_paragraph_by_clone / reorder_paragraphs: 구역 정의(hp:secPr)를 품은 문단을 지우거나(구역 정의 소실), 복제하거나 (구역 정의 2벌), 첫 자리에서 옮겼다. 이제 거부한다. - set_paragraph_text, texts를 준 insert_paragraph_by_clone: 글상자·각주· 머리말을 품은 문단에서 새 글을 안쪽 문단의 hp:t에 넣고 나머지를 비울 수 있었다(_text_edit_for_paragraph가 깊이를 가리지 않는다). 이제 거부한다. - restyle_text: 찾은 글 바로 앞의 "<hp:run"을 그 글의 run으로 봐서, 글이 표 뒤에 있으면 표 안 run의 글자 모양을 바꿨다. _scan_runs로 글을 직접 가진 run을 찾는다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
기본 저장 관문에 참조 무결성 검사(check_id_integrity)를 넣을 수 있는지 tests의 .hwpx 1,210개로 쟀다. - 비용: 문서를 여는 시간의 약 0.74배(합계 열기 42.0초, 검사 30.9초). 가장 큰 문서(7MB)는 1.03초. - 오탐: 3개가 걸린다. 그중 2개는 한/글이 저장한 자료다(header.xml의 heading@idRef="0"이 정의되지 않은 글머리표를 가리킴). 기본으로 넣으면 한/글이 만든 정상 문서의 저장을 거부하게 된다. 그래서 넣지 않고, README(국·영문)와 usage.md에 기본 검사가 보는 것과 보지 않는 것, 참조까지 보는 방법을 적었다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
ENG-06·ENG-30 커밋이 README.md, README_EN.md, docs/usage.md의 글을 고쳐 docs/python-example-ledger.json의 manualSha256이 맞지 않게 됐다. scripts/regenerate_example_ledger.py로 다시 만들었다. 코드 울타리 수와 그 해시(fenceCount 117, sourceSha256)는 그대로다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…6 후속) 전체 테스트에서 걸린 두 관문을 맞춘다. 동작은 그대로다. - test_product_boundary: 새 모듈(oxml/_layout_cache_guard.py)에는 소유 예외 등록이 필요하다 → 모듈을 없애고 같은 코드를 oxml/section.py의 비공개 함수로 옮겼다. - test_engine_surface_6_0: Section의 공개 멤버가 하나 늘었다 (remember_source) → _remember_source로 바꿨다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
앞 커밋에서 더한 거부 5곳이 맨 ValueError라 scripts/error_code_census.py --check가 "untyped 증가: 202 → 207"로 실패했다. HwpxValueError(ValueError의 하위형이라 연산 루프가 그대로 잡는다)에 code를 붙였다: body-op-section-settings, body-op-nested-paragraph. 거부 문구는 그대로다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
test_an_object_in_a_switch_is_laid_out_as_its_case가 sz·pos만 가진 hp:ole를 직접 만들어 저장한다. ENG-23으로 저장 관문이 그런 OLE를 거부하므로(한/글이 열다가 죽는 모양) offset·orgSz·curSz·renderingInfo를 넣었다. 쪽 추정이 보는 값(sz, pos, textWrap)은 그대로다. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
airmang
commented
Nov 10, 2026
airmang
left a comment
Owner
Author
There was a problem hiding this comment.
Reviewed fa58131 across nested run scanning, body operation refusal guards, text atom writing, cache lifetime tracking and open-safety checks. Exact-head CI: all 13 checks pass; Python 3.12 records 7,779 passed, 18 skipped, 1 xfailed. Locally, all 118 focused tests pass. No additional correctness defect was found in this review. Merge is held because integrating with the newer main after #676 produces a content conflict in CHANGELOG.md (confirmed by git merge-tree); please reconcile with the latest base and rerun checks. This is a COMMENT because the signed-in account owns this PR. No Hancom process or package/plugin publication was launched.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
요약
2026-11-10 점검에서 나온, 성공으로 보고하면서 내용이 달라지는 경로를 고칩니다. 커밋은 항목별로 나눴습니다.
strip_runs_by_color가 다른 색 글을 지우던 것. 대상 색 run이 표·글상자·각주를 품고 있으면 안쪽 첫 run의 글이 비워졌습니다. run 경계를 중첩을 따라가며 잡도록 바꿨고, 대상 run이 직접 가진 글만 비웁니다. 빈 run 뒤의 다른 색 글이 지워지던 것과, 캐시 제거가 엉뚱한 문단에 걸리던 것도 함께 고쳤습니다.run.replace_text,doc.text.replace,doc.replace_text_in_runs,apply_model이 "\n"을hp:lineBreak로, 탭을hp:tab으로 씁니다.set_text_preserving_runs가 탭을 지우던 것을 고쳤습니다.apply_model,add_index_mark,add_title_mark,add_drop_cap이 고친 문단의 캐시를 지웁니다. 저장할 때는 "캐시는 읽을 때 그대로인데 문단 내용이 달라진" 문단의 캐시를 지우는 안전망을 더했습니다.add_raw("textart"|"ole"|"video")로 만든, 필수 자식이 없는 요소는 이제hp:rect처럼 저장이 거부됩니다. 이런 문서는 한/글에서 열리지 않습니다. 한/글이 저장한 정상 자료 7개는 그대로 통과하고, 빈 수식은 계속 저장됩니다.TypeError로 죽던 것.body_patch연산의 테스트와 거기서 나온 결함 4건. 구역 정의를 품은 문단을 지우거나 복제·이동하던 것, 각주·글상자를 품은 문단에서 안쪽 글이 비워질 수 있던 것은 이제 거부합니다(body-op-section-settings,body-op-nested-paragraph).restyle_text가 표 안 run의 글자 모양을 바꾸던 것은 글을 직접 가진 run을 바꾸게 했습니다.body_patch.py의 테스트 실행률이 40.6%에서 95%가 됩니다.병합 전에 볼 것
15% 느려집니다(131 KB 양식에서 0.67초 중 0.10초). 원하지 않으면908개가 지워졌습니다.section.py의_remove_outlived_layout_caches호출 한 줄을 빼면 API 수정만 남습니다. 기존 "2배 폭" 훑기는 그대로 두었습니다. 1배로 좁히면 한/글이 저장한 정상 캐시 334body_patch의 새 거부. 구역 첫 문단을 지우거나 복제하는 호출은 이제 오류가 납니다. python-hwpx-automation main의 평가계획·본문 편집 테스트를 이 브랜치로 돌렸을 때 실패 목록은 main 엔진으로 돌렸을 때와 같았습니다(Windows, 부분 실행).관련 이슈
Refs #658
변경 종류
체크리스트
pytest테스트가 통과합니다. — Windows 11에서 7,765 통과, 14 실패. 실패 14건은 이 변경 전 main에서도 같은 이름으로 실패하던 것입니다(cp949, 심볼릭 링크 권한, CRLF). Linux CI 결과는 이 PR의 체크로 확인해 주세요.CHANGELOG.md를 갱신했습니다(해당하는 경우).🤖 Generated with Claude Code