python-hwpx 2.24.0__tar.gz → 2.29.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. {python_hwpx-2.24.0/src/python_hwpx.egg-info → python_hwpx-2.29.1}/PKG-INFO +13 -2
  2. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/README.md +9 -0
  3. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/pyproject.toml +18 -2
  4. python_hwpx-2.29.1/src/hwpx/agent/__init__.py +99 -0
  5. python_hwpx-2.29.1/src/hwpx/agent/blueprint/__init__.py +58 -0
  6. python_hwpx-2.29.1/src/hwpx/agent/blueprint/bundle.py +282 -0
  7. python_hwpx-2.29.1/src/hwpx/agent/blueprint/catalog.py +136 -0
  8. python_hwpx-2.29.1/src/hwpx/agent/blueprint/dump.py +521 -0
  9. python_hwpx-2.29.1/src/hwpx/agent/blueprint/mapping.py +312 -0
  10. python_hwpx-2.29.1/src/hwpx/agent/blueprint/model.py +626 -0
  11. python_hwpx-2.29.1/src/hwpx/agent/blueprint/native.py +622 -0
  12. python_hwpx-2.29.1/src/hwpx/agent/blueprint/replay.py +452 -0
  13. python_hwpx-2.29.1/src/hwpx/agent/catalog.py +167 -0
  14. python_hwpx-2.29.1/src/hwpx/agent/cli.py +631 -0
  15. python_hwpx-2.29.1/src/hwpx/agent/commands.py +1401 -0
  16. python_hwpx-2.29.1/src/hwpx/agent/document.py +769 -0
  17. python_hwpx-2.29.1/src/hwpx/agent/model.py +730 -0
  18. python_hwpx-2.29.1/src/hwpx/agent/path.py +155 -0
  19. python_hwpx-2.29.1/src/hwpx/agent/query.py +230 -0
  20. python_hwpx-2.29.1/src/hwpx/benchmark/__init__.py +25 -0
  21. python_hwpx-2.29.1/src/hwpx/benchmark/blind_eval.py +261 -0
  22. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/document.py +17 -1
  23. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/_document_impl.py +27 -16
  24. python_hwpx-2.29.1/src/hwpx/practice/__init__.py +277 -0
  25. python_hwpx-2.29.1/src/hwpx/practice/aggregate.py +947 -0
  26. python_hwpx-2.29.1/src/hwpx/practice/campaign.py +296 -0
  27. python_hwpx-2.29.1/src/hwpx/practice/domain.py +3137 -0
  28. python_hwpx-2.29.1/src/hwpx/practice/dossier.py +78 -0
  29. python_hwpx-2.29.1/src/hwpx/practice/evaluator.py +2359 -0
  30. python_hwpx-2.29.1/src/hwpx/practice/forge.py +436 -0
  31. python_hwpx-2.29.1/src/hwpx/practice/intake.py +230 -0
  32. python_hwpx-2.29.1/src/hwpx/practice/lineage.py +113 -0
  33. python_hwpx-2.29.1/src/hwpx/practice/mutations.py +168 -0
  34. python_hwpx-2.29.1/src/hwpx/practice/registry.py +322 -0
  35. python_hwpx-2.29.1/src/hwpx/practice/run.py +757 -0
  36. python_hwpx-2.29.1/src/hwpx/practice/sanitize.py +154 -0
  37. python_hwpx-2.29.1/src/hwpx/practice/scenario.py +146 -0
  38. python_hwpx-2.29.1/src/hwpx/practice/split.py +249 -0
  39. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/table_patch.py +30 -0
  40. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/__init__.py +38 -1
  41. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/_render_hwpx_mac.applescript +24 -1
  42. python_hwpx-2.29.1/src/hwpx/visual/fixture_corpus.py +175 -0
  43. python_hwpx-2.29.1/src/hwpx/visual/hancom_worker.py +288 -0
  44. python_hwpx-2.29.1/src/hwpx/visual/page_qa.py +244 -0
  45. python_hwpx-2.29.1/src/hwpx/visual/qa_contracts.py +293 -0
  46. python_hwpx-2.29.1/src/hwpx/visual/qa_metrics.py +236 -0
  47. {python_hwpx-2.24.0 → python_hwpx-2.29.1/src/python_hwpx.egg-info}/PKG-INFO +13 -2
  48. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/python_hwpx.egg-info/SOURCES.txt +64 -1
  49. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/python_hwpx.egg-info/entry_points.txt +1 -0
  50. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/python_hwpx.egg-info/requires.txt +3 -1
  51. python_hwpx-2.29.1/tests/test_agent_blueprint_contracts.py +191 -0
  52. python_hwpx-2.29.1/tests/test_agent_blueprint_dump.py +321 -0
  53. python_hwpx-2.29.1/tests/test_agent_blueprint_replay.py +500 -0
  54. python_hwpx-2.29.1/tests/test_agent_catalog.py +38 -0
  55. python_hwpx-2.29.1/tests/test_agent_cli.py +525 -0
  56. python_hwpx-2.29.1/tests/test_agent_commands.py +1114 -0
  57. python_hwpx-2.29.1/tests/test_agent_contracts.py +239 -0
  58. python_hwpx-2.29.1/tests/test_agent_document.py +201 -0
  59. python_hwpx-2.29.1/tests/test_agent_path_query.py +128 -0
  60. python_hwpx-2.29.1/tests/test_blind_eval_fixture.py +155 -0
  61. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_builder_core.py +2 -8
  62. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_document_formatting.py +29 -24
  63. python_hwpx-2.29.1/tests/test_hancom_render_worker.py +140 -0
  64. python_hwpx-2.29.1/tests/test_practice_campaign_aggregate.py +870 -0
  65. python_hwpx-2.29.1/tests/test_practice_contracts.py +255 -0
  66. python_hwpx-2.29.1/tests/test_practice_domain_evaluator.py +2134 -0
  67. python_hwpx-2.29.1/tests/test_practice_evaluator.py +2100 -0
  68. python_hwpx-2.29.1/tests/test_practice_intake.py +214 -0
  69. python_hwpx-2.29.1/tests/test_practice_run_contracts.py +422 -0
  70. python_hwpx-2.29.1/tests/test_practice_sanitize.py +75 -0
  71. python_hwpx-2.29.1/tests/test_practice_scenario_forge.py +73 -0
  72. python_hwpx-2.29.1/tests/test_practice_scenario_pack.py +170 -0
  73. python_hwpx-2.29.1/tests/test_practice_source_integrity.py +128 -0
  74. python_hwpx-2.29.1/tests/test_practice_split.py +85 -0
  75. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_split_merged_cell.py +4 -0
  76. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_table_patch.py +42 -0
  77. python_hwpx-2.29.1/tests/test_visual_fixture_corpus.py +88 -0
  78. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_visual_oracle.py +12 -0
  79. python_hwpx-2.29.1/tests/test_visual_qa_contracts.py +102 -0
  80. python_hwpx-2.29.1/tests/test_visual_qa_metrics.py +89 -0
  81. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/LICENSE +0 -0
  82. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/NOTICE +0 -0
  83. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/setup.cfg +0 -0
  84. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/__init__.py +0 -0
  85. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/authoring.py +0 -0
  86. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/body_patch.py +0 -0
  87. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/builder/__init__.py +0 -0
  88. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/builder/core.py +0 -0
  89. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/builder/report.py +0 -0
  90. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/__init__.py +0 -0
  91. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/badges.py +0 -0
  92. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/corpus/corpus.json +0 -0
  93. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/corpus/meeting_summary.hwpx +0 -0
  94. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/corpus/notice.hwpx +0 -0
  95. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/corpus/report_table.hwpx +0 -0
  96. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/corpus.py +0 -0
  97. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/report.py +0 -0
  98. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/roundtrip_batch.py +0 -0
  99. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/conformance/runner.py +0 -0
  100. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/data/Skeleton.hwpx +0 -0
  101. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/__init__.py +0 -0
  102. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/_support.py +0 -0
  103. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/composer.py +0 -0
  104. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/harvest.py +0 -0
  105. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/plan.py +0 -0
  106. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profile.py +0 -0
  107. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/application_form/fragments/body.xml +0 -0
  108. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/application_form/fragments/heading.xml +0 -0
  109. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/application_form/fragments/info_table.xml +0 -0
  110. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/application_form/fragments/title.xml +0 -0
  111. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/application_form/profile.json +0 -0
  112. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/application_form/template.hwpx +0 -0
  113. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/home_notice/fragments/body.xml +0 -0
  114. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/home_notice/fragments/heading.xml +0 -0
  115. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/home_notice/fragments/title.xml +0 -0
  116. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/home_notice/profile.json +0 -0
  117. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/home_notice/template.hwpx +0 -0
  118. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/official_notice/fragments/body.xml +0 -0
  119. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/official_notice/fragments/heading.xml +0 -0
  120. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/official_notice/fragments/info_table.xml +0 -0
  121. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/official_notice/fragments/title.xml +0 -0
  122. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/official_notice/profile.json +0 -0
  123. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/official_notice/template.hwpx +0 -0
  124. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/report/fragments/body.xml +0 -0
  125. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/report/fragments/heading.xml +0 -0
  126. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/report/fragments/info_table.xml +0 -0
  127. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/report/fragments/title.xml +0 -0
  128. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/report/profile.json +0 -0
  129. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/profiles/report/template.hwpx +0 -0
  130. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/design/validator.py +0 -0
  131. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/evalplan_fill.py +0 -0
  132. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/exam/__init__.py +0 -0
  133. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/exam/compose.py +0 -0
  134. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/exam/ir.py +0 -0
  135. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/exam/measure.py +0 -0
  136. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/exam/parser.py +0 -0
  137. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/exam/profile.py +0 -0
  138. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/fill_residue.py +0 -0
  139. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fill.py +0 -0
  140. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fit/__init__.py +0 -0
  141. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fit/apply.py +0 -0
  142. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fit/engine.py +0 -0
  143. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fit/measure.py +0 -0
  144. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fit/policy.py +0 -0
  145. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fit/report.py +0 -0
  146. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fit/seal.py +0 -0
  147. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/form_fit/wordbox.py +0 -0
  148. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/formfill_quality.py +0 -0
  149. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/guidance_scan.py +0 -0
  150. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/ingest/__init__.py +0 -0
  151. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/ingest/base.py +0 -0
  152. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/ingest/hwpx_converter.py +0 -0
  153. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/layout/__init__.py +0 -0
  154. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/layout/lint.py +0 -0
  155. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/layout/report.py +0 -0
  156. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/opc/package.py +0 -0
  157. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/opc/relationships.py +0 -0
  158. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/opc/security.py +0 -0
  159. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/opc/xml_utils.py +0 -0
  160. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/__init__.py +0 -0
  161. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/body.py +0 -0
  162. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/canonical_defaults.py +0 -0
  163. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/common.py +0 -0
  164. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/document.py +0 -0
  165. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/header.py +0 -0
  166. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/header_part.py +0 -0
  167. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/memo.py +0 -0
  168. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/namespaces.py +0 -0
  169. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/numbering.py +0 -0
  170. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/objects.py +0 -0
  171. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/paragraph.py +0 -0
  172. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/parser.py +0 -0
  173. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/run.py +0 -0
  174. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/schema.py +0 -0
  175. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/section.py +0 -0
  176. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/simple_parts.py +0 -0
  177. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/table.py +0 -0
  178. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/oxml/utils.py +0 -0
  179. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/package.py +0 -0
  180. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/patch.py +0 -0
  181. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/presets/__init__.py +0 -0
  182. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/presets/proposal.py +0 -0
  183. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/py.typed +0 -0
  184. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/quality/__init__.py +0 -0
  185. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/quality/ledger.py +0 -0
  186. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/quality/policy.py +0 -0
  187. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/quality/report.py +0 -0
  188. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/quality/save_pipeline.py +0 -0
  189. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/template_formfit.py +0 -0
  190. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/templates.py +0 -0
  191. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/__init__.py +0 -0
  192. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/_schemas/header.xsd +0 -0
  193. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/_schemas/section.xsd +0 -0
  194. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/advanced_generators.py +0 -0
  195. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/archive_cli.py +0 -0
  196. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/doc_diff.py +0 -0
  197. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/exporter.py +0 -0
  198. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/fuzz/__init__.py +0 -0
  199. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/fuzz/__main__.py +0 -0
  200. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/fuzz/catalog.py +0 -0
  201. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/fuzz/generator.py +0 -0
  202. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/fuzz/minimize.py +0 -0
  203. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/fuzz/runner.py +0 -0
  204. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/generic_inventory.py +0 -0
  205. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/id_integrity.py +0 -0
  206. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/idempotence.py +0 -0
  207. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/ir_equality.py +0 -0
  208. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/layout_preview.py +0 -0
  209. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/mail_merge.py +0 -0
  210. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/markdown_export.py +0 -0
  211. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/object_finder.py +0 -0
  212. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/official_lint.py +0 -0
  213. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/package_reconcile.py +0 -0
  214. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/package_validator.py +0 -0
  215. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/page_guard.py +0 -0
  216. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/pii.py +0 -0
  217. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/read_fidelity.py +0 -0
  218. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/recover.py +0 -0
  219. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/redline.py +0 -0
  220. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/repair.py +0 -0
  221. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/report_parser.py +0 -0
  222. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/report_utils.py +0 -0
  223. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/roundtrip_diff.py +0 -0
  224. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/style_profile.py +0 -0
  225. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/table_cleanup.py +0 -0
  226. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/table_compute.py +0 -0
  227. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/table_navigation.py +0 -0
  228. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/template_analyzer.py +0 -0
  229. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/text_extract_cli.py +0 -0
  230. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/text_extractor.py +0 -0
  231. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/toc_author.py +0 -0
  232. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/toc_fidelity.py +0 -0
  233. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/tools/validator.py +0 -0
  234. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/_hancom_open_rate.ps1 +0 -0
  235. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/_refresh_hwpx_mac.applescript +0 -0
  236. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/_render_hwpx.ps1 +0 -0
  237. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/detectors.py +0 -0
  238. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/diff.py +0 -0
  239. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/masks.py +0 -0
  240. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/oracle.py +0 -0
  241. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/hwpx/visual/report.py +0 -0
  242. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/python_hwpx.egg-info/dependency_links.txt +0 -0
  243. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/src/python_hwpx.egg-info/top_level.txt +0 -0
  244. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_advanced_generators.py +0 -0
  245. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_authoring_profile_routing.py +0 -0
  246. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_authoring_profile_routing_oracle.py +0 -0
  247. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_authoring_render_check.py +0 -0
  248. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_body_patch.py +0 -0
  249. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_builder_plan_v2.py +0 -0
  250. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_builder_vertical_slice.py +0 -0
  251. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_cell_line_spacing.py +0 -0
  252. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_comment_node_robustness.py +0 -0
  253. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_conformance.py +0 -0
  254. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_corpus_read_fidelity.py +0 -0
  255. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_coverage_promotion.py +0 -0
  256. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_coverage_targets.py +0 -0
  257. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_design_builder.py +0 -0
  258. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_deviations_registry.py +0 -0
  259. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_doc_diff.py +0 -0
  260. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_document_context_manager.py +0 -0
  261. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_document_plan.py +0 -0
  262. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_document_plan_computed_fields.py +0 -0
  263. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_document_save_api.py +0 -0
  264. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_evalplan_fill.py +0 -0
  265. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_exam_compose.py +0 -0
  266. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_exam_compose_oracle.py +0 -0
  267. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_exam_fixtures.py +0 -0
  268. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_exam_ir.py +0 -0
  269. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_exam_measure.py +0 -0
  270. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_exam_parser.py +0 -0
  271. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_exam_profile.py +0 -0
  272. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_existing_document_format_editing.py +0 -0
  273. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_fill_residue.py +0 -0
  274. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_form_fields.py +0 -0
  275. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_form_fill_split_run.py +0 -0
  276. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_form_fit.py +0 -0
  277. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_form_fit_integration.py +0 -0
  278. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_form_fit_seal.py +0 -0
  279. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_form_fit_seal_placement.py +0 -0
  280. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_form_fit_wordbox.py +0 -0
  281. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_formfill_quality.py +0 -0
  282. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_fuzz_loop.py +0 -0
  283. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_fuzz_regressions.py +0 -0
  284. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_gap_closure_tools.py +0 -0
  285. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_government_report_preset.py +0 -0
  286. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_government_table_profile.py +0 -0
  287. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_guidance_scan.py +0 -0
  288. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_hp_tab_support.py +0 -0
  289. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_hwpxlib_corpus_read.py +0 -0
  290. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_id_generator_range.py +0 -0
  291. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_id_integrity.py +0 -0
  292. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_image_object_workflow.py +0 -0
  293. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_ingest.py +0 -0
  294. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_inline_models.py +0 -0
  295. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_integration_hwpx_compatibility.py +0 -0
  296. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_integration_roundtrip.py +0 -0
  297. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_kordoc_absorption.py +0 -0
  298. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_layout_lint.py +0 -0
  299. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_layout_preview.py +0 -0
  300. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_mail_merge_fit.py +0 -0
  301. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_mail_merge_table_compute.py +0 -0
  302. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_mail_merge_xlsx.py +0 -0
  303. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_markdown_export.py +0 -0
  304. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_memo_and_style_editing.py +0 -0
  305. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_namespace_handling.py +0 -0
  306. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_new_features.py +0 -0
  307. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_official_document_style.py +0 -0
  308. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_official_lint_gongmun_gate.py +0 -0
  309. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_official_lint_tableaware.py +0 -0
  310. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_opc_package.py +0 -0
  311. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_open_safety_corpus.py +0 -0
  312. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_openrate.py +0 -0
  313. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_oxml_parsing.py +0 -0
  314. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_packaging_license_metadata.py +0 -0
  315. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_packaging_py_typed.py +0 -0
  316. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_paragraph_keep_together.py +0 -0
  317. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_paragraph_section_management.py +0 -0
  318. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_pii.py +0 -0
  319. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_pii_structural.py +0 -0
  320. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_pii_wiring.py +0 -0
  321. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_proposal_preset.py +0 -0
  322. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_question_split_detector.py +0 -0
  323. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_read_fidelity.py +0 -0
  324. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_recover_broken_zip.py +0 -0
  325. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_redline_authoring.py +0 -0
  326. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_redline_verify.py +0 -0
  327. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_repair_repack.py +0 -0
  328. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_report_parser.py +0 -0
  329. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_report_utils.py +0 -0
  330. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_repr_snapshots.py +0 -0
  331. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_rhwp_t1_gates.py +0 -0
  332. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_rhwp_t2_verification.py +0 -0
  333. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_roundtrip_fidelity.py +0 -0
  334. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_row_heights.py +0 -0
  335. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_save_pipeline.py +0 -0
  336. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_save_pipeline_no_bypass.py +0 -0
  337. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_section_headers.py +0 -0
  338. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_set_paragraph_format_keep.py +0 -0
  339. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_skeleton_template_ids.py +0 -0
  340. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_split_cell.py +0 -0
  341. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_style_profile.py +0 -0
  342. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_table_cleanup.py +0 -0
  343. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_table_navigation.py +0 -0
  344. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_table_patch_dryrun.py +0 -0
  345. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_table_patch_m105.py +0 -0
  346. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_tables_default_border.py +0 -0
  347. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_template_analyzer_enrichment.py +0 -0
  348. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_template_formfit.py +0 -0
  349. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_text_extractor_annotations.py +0 -0
  350. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_toc_author.py +0 -0
  351. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_toc_fidelity.py +0 -0
  352. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_validation_severity.py +0 -0
  353. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_validator_comment_nodes.py +0 -0
  354. {python_hwpx-2.24.0 → python_hwpx-2.29.1}/tests/test_version_metadata.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx
3
- Version: 2.24.0
3
+ Version: 2.29.1
4
4
  Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리
5
5
  Author: python-hwpx Maintainers
6
6
  License-Expression: Apache-2.0
@@ -20,7 +20,7 @@ Requires-Python: >=3.10
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  License-File: NOTICE
23
- Requires-Dist: lxml<6,>=4.9
23
+ Requires-Dist: lxml<7,>=4.9
24
24
  Provides-Extra: visual
25
25
  Requires-Dist: pymupdf>=1.24; extra == "visual"
26
26
  Requires-Dist: pillow>=10.0; extra == "visual"
@@ -33,6 +33,8 @@ Requires-Dist: twine>=4.0; extra == "dev"
33
33
  Requires-Dist: pytest>=7.4; extra == "dev"
34
34
  Provides-Extra: test
35
35
  Requires-Dist: build>=1.0; extra == "test"
36
+ Requires-Dist: numpy>=1.26; extra == "test"
37
+ Requires-Dist: pillow>=10.0; extra == "test"
36
38
  Requires-Dist: pytest>=7.4; extra == "test"
37
39
  Requires-Dist: pytest-cov>=5.0; extra == "test"
38
40
  Provides-Extra: typecheck
@@ -202,6 +204,15 @@ pip install -e ".[dev]"
202
204
  pytest
203
205
  ```
204
206
 
207
+ ## 감사의 말
208
+
209
+ 아래 공개 표준·프로젝트에 빚지고 있습니다.
210
+
211
+ - **[OWPML — 개방형 워드프로세서 마크업 언어 (KS X 6101)](https://www.kssn.net/search/stddetail.do?itemNo=K001010119985)** — HWPX가 기반하는 한국 산업 표준
212
+ - **[hancom-io/hwpx-owpml-model](https://github.com/hancom-io/hwpx-owpml-model)** — OWPML 요소 구조 참조 모델 · **[neolord0/hwpxlib](https://github.com/neolord0/hwpxlib)** — 오라클 샘플 코퍼스
213
+ - **[edwardkim/rhwp](https://github.com/edwardkim/rhwp)** — 멱등성·검증 게이트 설계 영감
214
+ - **범정부오피스** — 공무 문서 편집 워크플로 아이디어
215
+
205
216
  ## License
206
217
 
207
218
  Apache License 2.0. See LICENSE and NOTICE.
@@ -160,6 +160,15 @@ pip install -e ".[dev]"
160
160
  pytest
161
161
  ```
162
162
 
163
+ ## 감사의 말
164
+
165
+ 아래 공개 표준·프로젝트에 빚지고 있습니다.
166
+
167
+ - **[OWPML — 개방형 워드프로세서 마크업 언어 (KS X 6101)](https://www.kssn.net/search/stddetail.do?itemNo=K001010119985)** — HWPX가 기반하는 한국 산업 표준
168
+ - **[hancom-io/hwpx-owpml-model](https://github.com/hancom-io/hwpx-owpml-model)** — OWPML 요소 구조 참조 모델 · **[neolord0/hwpxlib](https://github.com/neolord0/hwpxlib)** — 오라클 샘플 코퍼스
169
+ - **[edwardkim/rhwp](https://github.com/edwardkim/rhwp)** — 멱등성·검증 게이트 설계 영감
170
+ - **범정부오피스** — 공무 문서 편집 워크플로 아이디어
171
+
163
172
  ## License
164
173
 
165
174
  Apache License 2.0. See LICENSE and NOTICE.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-hwpx"
7
- version = "2.24.0"
7
+ version = "2.29.1"
8
8
  description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  license = "Apache-2.0"
@@ -25,7 +25,7 @@ classifiers = [
25
25
  "Topic :: Text Processing :: Markup :: XML",
26
26
  ]
27
27
  dependencies = [
28
- "lxml>=4.9,<6",
28
+ "lxml>=4.9,<7",
29
29
  ]
30
30
 
31
31
  [project.optional-dependencies]
@@ -45,6 +45,8 @@ dev = [
45
45
  ]
46
46
  test = [
47
47
  "build>=1.0",
48
+ "numpy>=1.26",
49
+ "pillow>=10.0",
48
50
  "pytest>=7.4",
49
51
  "pytest-cov>=5.0",
50
52
  ]
@@ -59,6 +61,7 @@ Documentation = "https://airmang.github.io/python-hwpx/"
59
61
  Issues = "https://github.com/airmang/python-hwpx/issues"
60
62
 
61
63
  [project.scripts]
64
+ hwpx = "hwpx.agent.cli:main"
62
65
  hwpx-unpack = "hwpx.tools.archive_cli:unpack_main"
63
66
  hwpx-pack = "hwpx.tools.archive_cli:pack_main"
64
67
  hwpx-validate = "hwpx.tools.validator:main"
@@ -100,11 +103,19 @@ testpaths = ["tests"]
100
103
 
101
104
  [tool.mypy]
102
105
  python_version = "3.10"
106
+ # Explicit gradual-migration allowlist: transitive imports enter the gate only
107
+ # after they are added here and made clean themselves.
108
+ follow_imports = "skip"
103
109
  files = [
104
110
  "src/hwpx/document.py",
105
111
  "src/hwpx/oxml/document.py",
106
112
  "src/hwpx/opc/package.py",
107
113
  "src/hwpx/opc/relationships.py",
114
+ "src/hwpx/oxml/paragraph.py",
115
+ "src/hwpx/oxml/run.py",
116
+ "src/hwpx/oxml/table.py",
117
+ "src/hwpx/quality/policy.py",
118
+ "src/hwpx/quality/report.py",
108
119
  "src/hwpx/tools/archive_cli.py",
109
120
  "src/hwpx/tools/package_validator.py",
110
121
  "src/hwpx/tools/page_guard.py",
@@ -126,6 +137,11 @@ ignore_errors = true
126
137
  include = [
127
138
  "src/hwpx/opc/package.py",
128
139
  "src/hwpx/opc/relationships.py",
140
+ "src/hwpx/oxml/paragraph.py",
141
+ "src/hwpx/oxml/run.py",
142
+ "src/hwpx/oxml/table.py",
143
+ "src/hwpx/quality/policy.py",
144
+ "src/hwpx/quality/report.py",
129
145
  "src/hwpx/tools/archive_cli.py",
130
146
  "src/hwpx/tools/package_validator.py",
131
147
  "src/hwpx/tools/page_guard.py",
@@ -0,0 +1,99 @@
1
+ """Versioned semantic contracts for the HWPX agent document interface."""
2
+
3
+ from .model import (
4
+ AGENT_BATCH_RESULT_SCHEMA,
5
+ AGENT_BATCH_SCHEMA,
6
+ AGENT_CATALOG_SCHEMA,
7
+ AGENT_COMMAND_SCHEMA,
8
+ AGENT_ERROR_SCHEMA,
9
+ AGENT_NODE_SCHEMA,
10
+ AgentBatchResult,
11
+ AgentContractError,
12
+ AgentError,
13
+ AgentNode,
14
+ agent_contract_manifest,
15
+ validate_agent_batch,
16
+ validate_agent_command,
17
+ )
18
+ from .catalog import agent_catalog, agent_json_schemas, catalog_hash, human_help, node_help
19
+ from .commands import apply_document_commands
20
+ from .document import HwpxAgentDocument, NodeRecord
21
+ from .path import PathSegment, SemanticPath, canonicalize_path, parse_path
22
+ from .query import QueryResult, SemanticSelector, parse_selector
23
+ from .blueprint import (
24
+ BLUEPRINT_CATALOG_SCHEMA,
25
+ BLUEPRINT_REPLAY_RESULT_SCHEMA,
26
+ BLUEPRINT_REPLAY_SCHEMA,
27
+ BLUEPRINT_SCHEMA,
28
+ BlueprintBundle,
29
+ BlueprintDumpResult,
30
+ BlueprintReplayResult,
31
+ ReplayPlan,
32
+ blueprint_catalog,
33
+ blueprint_catalog_hash,
34
+ blueprint_hash,
35
+ blueprint_human_help,
36
+ blueprint_json_schemas,
37
+ blueprint_limits,
38
+ build_blueprint_bundle,
39
+ dump_document_blueprint,
40
+ read_blueprint_bundle,
41
+ repack_blueprint_bundle,
42
+ replay_document_blueprint,
43
+ validate_blueprint_manifest,
44
+ validate_replay_request,
45
+ write_blueprint_bundle,
46
+ )
47
+
48
+ __all__ = [
49
+ "AGENT_BATCH_RESULT_SCHEMA",
50
+ "AGENT_BATCH_SCHEMA",
51
+ "AGENT_CATALOG_SCHEMA",
52
+ "AGENT_COMMAND_SCHEMA",
53
+ "AGENT_ERROR_SCHEMA",
54
+ "AGENT_NODE_SCHEMA",
55
+ "BLUEPRINT_CATALOG_SCHEMA",
56
+ "BLUEPRINT_REPLAY_RESULT_SCHEMA",
57
+ "BLUEPRINT_REPLAY_SCHEMA",
58
+ "BLUEPRINT_SCHEMA",
59
+ "AgentBatchResult",
60
+ "AgentContractError",
61
+ "AgentError",
62
+ "AgentNode",
63
+ "BlueprintReplayResult",
64
+ "ReplayPlan",
65
+ "BlueprintBundle",
66
+ "BlueprintDumpResult",
67
+ "HwpxAgentDocument",
68
+ "NodeRecord",
69
+ "PathSegment",
70
+ "QueryResult",
71
+ "SemanticPath",
72
+ "SemanticSelector",
73
+ "agent_catalog",
74
+ "agent_json_schemas",
75
+ "agent_contract_manifest",
76
+ "apply_document_commands",
77
+ "blueprint_catalog",
78
+ "blueprint_catalog_hash",
79
+ "blueprint_hash",
80
+ "blueprint_human_help",
81
+ "blueprint_json_schemas",
82
+ "blueprint_limits",
83
+ "build_blueprint_bundle",
84
+ "canonicalize_path",
85
+ "catalog_hash",
86
+ "human_help",
87
+ "node_help",
88
+ "parse_path",
89
+ "parse_selector",
90
+ "dump_document_blueprint",
91
+ "read_blueprint_bundle",
92
+ "repack_blueprint_bundle",
93
+ "replay_document_blueprint",
94
+ "validate_agent_batch",
95
+ "validate_agent_command",
96
+ "validate_blueprint_manifest",
97
+ "validate_replay_request",
98
+ "write_blueprint_bundle",
99
+ ]
@@ -0,0 +1,58 @@
1
+ """Typed semantic HWPX blueprint contracts."""
2
+
3
+ from .catalog import (
4
+ blueprint_catalog,
5
+ blueprint_catalog_hash,
6
+ blueprint_human_help,
7
+ blueprint_json_schemas,
8
+ )
9
+ from .bundle import (
10
+ BlueprintBundle,
11
+ build_blueprint_bundle,
12
+ read_blueprint_bundle,
13
+ repack_blueprint_bundle,
14
+ write_blueprint_bundle,
15
+ )
16
+ from .dump import BlueprintDumpResult, dump_document_blueprint
17
+ from .mapping import ReplayPlan
18
+ from .replay import replay_document_blueprint
19
+ from .model import (
20
+ BLUEPRINT_CATALOG_SCHEMA,
21
+ BLUEPRINT_REPLAY_RESULT_SCHEMA,
22
+ BLUEPRINT_REPLAY_SCHEMA,
23
+ BLUEPRINT_SCHEMA,
24
+ BlueprintReplayResult,
25
+ blueprint_hash,
26
+ blueprint_limits,
27
+ canonical_manifest_bytes,
28
+ validate_blueprint_manifest,
29
+ validate_replay_request,
30
+ with_blueprint_hash,
31
+ )
32
+
33
+ __all__ = [
34
+ "BLUEPRINT_CATALOG_SCHEMA",
35
+ "BLUEPRINT_REPLAY_RESULT_SCHEMA",
36
+ "BLUEPRINT_REPLAY_SCHEMA",
37
+ "BLUEPRINT_SCHEMA",
38
+ "BlueprintBundle",
39
+ "BlueprintDumpResult",
40
+ "BlueprintReplayResult",
41
+ "ReplayPlan",
42
+ "blueprint_catalog",
43
+ "blueprint_catalog_hash",
44
+ "blueprint_hash",
45
+ "blueprint_human_help",
46
+ "blueprint_json_schemas",
47
+ "blueprint_limits",
48
+ "build_blueprint_bundle",
49
+ "canonical_manifest_bytes",
50
+ "dump_document_blueprint",
51
+ "read_blueprint_bundle",
52
+ "repack_blueprint_bundle",
53
+ "replay_document_blueprint",
54
+ "validate_blueprint_manifest",
55
+ "validate_replay_request",
56
+ "with_blueprint_hash",
57
+ "write_blueprint_bundle",
58
+ ]
@@ -0,0 +1,282 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Deterministic and fail-closed ``.hwpxbp`` bundle I/O."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import hashlib
7
+ import io
8
+ import json
9
+ import os
10
+ import stat
11
+ import tempfile
12
+ import zipfile
13
+ from collections.abc import Mapping
14
+ from dataclasses import dataclass
15
+ from pathlib import Path, PurePosixPath
16
+ from typing import Any
17
+
18
+ from ..model import AgentContractError
19
+ from .model import (
20
+ ASSET_PATH_PATTERN,
21
+ MAX_ASSETS,
22
+ MAX_ASSET_BYTES,
23
+ MAX_MANIFEST_BYTES,
24
+ MAX_TOTAL_ASSET_BYTES,
25
+ canonical_manifest_bytes,
26
+ validate_blueprint_manifest,
27
+ with_blueprint_hash,
28
+ )
29
+
30
+ MANIFEST_PATH = "blueprint.json"
31
+ MAX_BUNDLE_BYTES = MAX_MANIFEST_BYTES + MAX_TOTAL_ASSET_BYTES + 2 * 1024 * 1024
32
+ MAX_COMPRESSION_RATIO = 100
33
+
34
+ ALLOWED_MEDIA_TYPES: dict[str, tuple[str, ...]] = {
35
+ "image/png": ("png",),
36
+ "image/jpeg": ("jpg", "jpeg"),
37
+ "image/gif": ("gif",),
38
+ "image/bmp": ("bmp",),
39
+ "image/tiff": ("tif", "tiff"),
40
+ "image/webp": ("webp",),
41
+ }
42
+
43
+
44
+ @dataclass(frozen=True, slots=True)
45
+ class BlueprintBundle:
46
+ """A fully validated manifest and detached content-addressed assets."""
47
+
48
+ manifest: Mapping[str, Any]
49
+ assets: Mapping[str, bytes]
50
+ bundle_sha256: str
51
+ size: int
52
+
53
+
54
+ def _error(code: str, message: str, *, target: str = "bundle") -> AgentContractError:
55
+ return AgentContractError(code, message, target=target)
56
+
57
+
58
+ def _json_without_duplicate_keys(data: bytes) -> dict[str, Any]:
59
+ def pairs_hook(pairs: list[tuple[str, Any]]) -> dict[str, Any]:
60
+ result: dict[str, Any] = {}
61
+ for key, value in pairs:
62
+ if key in result:
63
+ raise _error("invariant_violation", f"duplicate JSON field: {key}", target=MANIFEST_PATH)
64
+ result[key] = value
65
+ return result
66
+
67
+ try:
68
+ value = json.loads(data.decode("utf-8"), object_pairs_hook=pairs_hook)
69
+ except AgentContractError:
70
+ raise
71
+ except (UnicodeDecodeError, json.JSONDecodeError) as exc:
72
+ raise _error("invalid_syntax", "blueprint.json is not strict UTF-8 JSON", target=MANIFEST_PATH) from exc
73
+ if not isinstance(value, dict):
74
+ raise _error("invalid_syntax", "blueprint.json must contain one object", target=MANIFEST_PATH)
75
+ return value
76
+
77
+
78
+ def _validate_entry_name(name: str) -> None:
79
+ if not name or "\\" in name or "\x00" in name:
80
+ raise _error("verification_failed", "bundle entry path is not normalized", target=name or "bundle")
81
+ path = PurePosixPath(name)
82
+ if path.is_absolute() or name.startswith("/") or any(part in {"", ".", ".."} for part in path.parts):
83
+ raise _error("verification_failed", "bundle entry path is unsafe", target=name)
84
+ if name != MANIFEST_PATH and not ASSET_PATH_PATTERN.fullmatch(name):
85
+ raise _error("unsupported_content", "bundle contains an unknown or forbidden entry", target=name)
86
+
87
+
88
+ def _validate_zip_info(info: zipfile.ZipInfo) -> None:
89
+ _validate_entry_name(info.filename)
90
+ if info.is_dir():
91
+ raise _error("unsupported_content", "directory entries are forbidden", target=info.filename)
92
+ mode = (info.external_attr >> 16) & 0xFFFF
93
+ if mode and stat.S_ISLNK(mode):
94
+ raise _error("unsupported_content", "symlink entries are forbidden", target=info.filename)
95
+ if info.flag_bits & 0x1:
96
+ raise _error("unsupported_content", "encrypted bundle entries are forbidden", target=info.filename)
97
+ if info.compress_type not in {zipfile.ZIP_STORED, zipfile.ZIP_DEFLATED}:
98
+ raise _error("unsupported_content", "bundle compression method is forbidden", target=info.filename)
99
+ limit = MAX_MANIFEST_BYTES if info.filename == MANIFEST_PATH else MAX_ASSET_BYTES
100
+ if info.file_size > limit:
101
+ raise _error("resource_limit", "bundle entry exceeds its byte limit", target=info.filename)
102
+ if info.file_size and info.compress_size == 0:
103
+ raise _error("resource_limit", "bundle entry has an invalid compression ratio", target=info.filename)
104
+ if info.compress_size and info.file_size / info.compress_size > MAX_COMPRESSION_RATIO:
105
+ raise _error("resource_limit", "bundle entry exceeds the decompression-ratio limit", target=info.filename)
106
+
107
+
108
+ def _sniff_media_type(data: bytes) -> str | None:
109
+ if data.startswith(b"\x89PNG\r\n\x1a\n"):
110
+ return "image/png"
111
+ if data.startswith(b"\xff\xd8\xff"):
112
+ return "image/jpeg"
113
+ if data.startswith((b"GIF87a", b"GIF89a")):
114
+ return "image/gif"
115
+ if data.startswith(b"BM"):
116
+ return "image/bmp"
117
+ if data.startswith((b"II*\x00", b"MM\x00*")):
118
+ return "image/tiff"
119
+ if len(data) >= 12 and data[:4] == b"RIFF" and data[8:12] == b"WEBP":
120
+ return "image/webp"
121
+ return None
122
+
123
+
124
+ def _validated_assets(
125
+ manifest: Mapping[str, Any], assets: Mapping[str, bytes]
126
+ ) -> dict[str, bytes]:
127
+ declared = {str(item["assetPath"]): dict(item) for item in manifest["resources"]}
128
+ if set(assets) != set(declared):
129
+ raise _error(
130
+ "invariant_violation",
131
+ "bundle assets do not exactly match manifest resources",
132
+ target="resources",
133
+ )
134
+ if len(assets) > MAX_ASSETS:
135
+ raise _error("resource_limit", "asset count exceeds limit", target="resources")
136
+ total = 0
137
+ detached: dict[str, bytes] = {}
138
+ for path in sorted(assets):
139
+ _validate_entry_name(path)
140
+ payload = assets[path]
141
+ if not isinstance(payload, bytes):
142
+ raise _error("invalid_syntax", "asset payload must be bytes", target=path)
143
+ total += len(payload)
144
+ if len(payload) > MAX_ASSET_BYTES or total > MAX_TOTAL_ASSET_BYTES:
145
+ raise _error("resource_limit", "asset bytes exceed limit", target=path)
146
+ record = declared[path]
147
+ digest = "sha256:" + hashlib.sha256(payload).hexdigest()
148
+ if digest != record["sha256"] or len(payload) != record["size"]:
149
+ raise _error("verification_failed", "asset hash or size mismatch", target=path)
150
+ media_type = str(record["mediaType"])
151
+ suffix = path.rsplit(".", 1)[-1]
152
+ if media_type not in ALLOWED_MEDIA_TYPES or suffix not in ALLOWED_MEDIA_TYPES[media_type]:
153
+ raise _error("unsupported_content", "asset extension/MIME is not allow-listed", target=path)
154
+ if _sniff_media_type(payload) != media_type:
155
+ raise _error("verification_failed", "asset bytes do not match declared MIME", target=path)
156
+ detached[path] = bytes(payload)
157
+ return detached
158
+
159
+
160
+ def _zip_info(name: str) -> zipfile.ZipInfo:
161
+ info = zipfile.ZipInfo(name, date_time=(1980, 1, 1, 0, 0, 0))
162
+ info.compress_type = zipfile.ZIP_STORED
163
+ info.create_system = 0
164
+ info.external_attr = 0
165
+ info.internal_attr = 0
166
+ info.flag_bits = 0
167
+ return info
168
+
169
+
170
+ def build_blueprint_bundle(
171
+ manifest: Mapping[str, Any], assets: Mapping[str, bytes] | None = None
172
+ ) -> bytes:
173
+ """Return canonical bundle bytes for an already hashed manifest."""
174
+
175
+ checked = validate_blueprint_manifest(manifest)
176
+ checked_assets = _validated_assets(checked, assets or {})
177
+ manifest_bytes = canonical_manifest_bytes(checked, include_hash=True)
178
+ stream = io.BytesIO()
179
+ with zipfile.ZipFile(stream, "w", allowZip64=False) as archive:
180
+ archive.writestr(_zip_info(MANIFEST_PATH), manifest_bytes)
181
+ for name, payload in checked_assets.items():
182
+ archive.writestr(_zip_info(name), payload)
183
+ result = stream.getvalue()
184
+ if len(result) > MAX_BUNDLE_BYTES:
185
+ raise _error("resource_limit", "blueprint bundle exceeds total byte limit")
186
+ return result
187
+
188
+
189
+ def write_blueprint_bundle(
190
+ destination: str | os.PathLike[str],
191
+ manifest: Mapping[str, Any],
192
+ assets: Mapping[str, bytes] | None = None,
193
+ *,
194
+ overwrite: bool = False,
195
+ ) -> BlueprintBundle:
196
+ """Atomically write one deterministic bundle and return its validated view."""
197
+
198
+ path = Path(destination)
199
+ if path.suffix.casefold() != ".hwpxbp":
200
+ raise _error("invalid_syntax", "blueprint output must use .hwpxbp", target="output")
201
+ if path.exists() and not overwrite:
202
+ raise _error("identity_collision", "blueprint output already exists", target=str(path))
203
+ data = build_blueprint_bundle(manifest, assets)
204
+ path.parent.mkdir(parents=True, exist_ok=True)
205
+ descriptor, temp_name = tempfile.mkstemp(prefix=f".{path.name}.", suffix=".tmp", dir=path.parent)
206
+ try:
207
+ with os.fdopen(descriptor, "wb") as handle:
208
+ handle.write(data)
209
+ handle.flush()
210
+ os.fsync(handle.fileno())
211
+ if path.exists() and not overwrite:
212
+ raise _error("identity_collision", "blueprint output already exists", target=str(path))
213
+ os.replace(temp_name, path)
214
+ except Exception:
215
+ try:
216
+ os.unlink(temp_name)
217
+ except FileNotFoundError:
218
+ pass
219
+ raise
220
+ return read_blueprint_bundle(data)
221
+
222
+
223
+ def read_blueprint_bundle(source: str | os.PathLike[str] | bytes) -> BlueprintBundle:
224
+ """Validate a complete bundle in memory before returning any materialized data."""
225
+
226
+ data = bytes(source) if isinstance(source, bytes) else Path(source).read_bytes()
227
+ if len(data) > MAX_BUNDLE_BYTES:
228
+ raise _error("resource_limit", "blueprint bundle exceeds total byte limit")
229
+ try:
230
+ with zipfile.ZipFile(io.BytesIO(data), "r") as archive:
231
+ infos = archive.infolist()
232
+ names = [info.filename for info in infos]
233
+ if len(names) != len(set(names)):
234
+ raise _error("invariant_violation", "bundle contains duplicate entry paths")
235
+ if names.count(MANIFEST_PATH) != 1:
236
+ raise _error("invariant_violation", "bundle must contain exactly one blueprint.json")
237
+ if len(infos) > MAX_ASSETS + 1:
238
+ raise _error("resource_limit", "bundle entry count exceeds limit")
239
+ for info in infos:
240
+ _validate_zip_info(info)
241
+ if sum(info.file_size for info in infos if info.filename != MANIFEST_PATH) > MAX_TOTAL_ASSET_BYTES:
242
+ raise _error("resource_limit", "bundle assets exceed total byte limit", target="resources")
243
+ manifest_data = archive.read(MANIFEST_PATH)
244
+ manifest = validate_blueprint_manifest(_json_without_duplicate_keys(manifest_data))
245
+ asset_names = sorted(name for name in names if name != MANIFEST_PATH)
246
+ assets = {name: archive.read(name) for name in asset_names}
247
+ except AgentContractError:
248
+ raise
249
+ except (OSError, zipfile.BadZipFile, zipfile.LargeZipFile, RuntimeError) as exc:
250
+ raise _error("invalid_syntax", "invalid .hwpxbp ZIP container") from exc
251
+ checked_assets = _validated_assets(manifest, assets)
252
+ return BlueprintBundle(
253
+ manifest=manifest,
254
+ assets=checked_assets,
255
+ bundle_sha256="sha256:" + hashlib.sha256(data).hexdigest(),
256
+ size=len(data),
257
+ )
258
+
259
+
260
+ def repack_blueprint_bundle(
261
+ source: str | os.PathLike[str] | bytes,
262
+ destination: str | os.PathLike[str],
263
+ manifest: Mapping[str, Any],
264
+ *,
265
+ overwrite: bool = False,
266
+ ) -> BlueprintBundle:
267
+ """Safely repack edited typed JSON while preserving only validated assets."""
268
+
269
+ original = read_blueprint_bundle(source)
270
+ rehashed = with_blueprint_hash(manifest)
271
+ return write_blueprint_bundle(destination, rehashed, original.assets, overwrite=overwrite)
272
+
273
+
274
+ __all__ = [
275
+ "ALLOWED_MEDIA_TYPES",
276
+ "BlueprintBundle",
277
+ "MANIFEST_PATH",
278
+ "build_blueprint_bundle",
279
+ "read_blueprint_bundle",
280
+ "repack_blueprint_bundle",
281
+ "write_blueprint_bundle",
282
+ ]