python-hwpx 2.29.2__tar.gz → 3.0.0__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 (355) hide show
  1. {python_hwpx-2.29.2/src/python_hwpx.egg-info → python_hwpx-3.0.0}/PKG-INFO +1 -1
  2. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/pyproject.toml +1 -1
  3. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/table_patch.py +23 -1
  4. {python_hwpx-2.29.2 → python_hwpx-3.0.0/src/python_hwpx.egg-info}/PKG-INFO +1 -1
  5. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/python_hwpx.egg-info/SOURCES.txt +1 -26
  6. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_evalplan_fill.py +35 -0
  7. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_packaging_py_typed.py +4 -0
  8. python_hwpx-3.0.0/tests/test_public_runtime_boundary.py +12 -0
  9. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_table_patch.py +44 -0
  10. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_table_patch_m105.py +31 -0
  11. python_hwpx-2.29.2/src/hwpx/practice/__init__.py +0 -277
  12. python_hwpx-2.29.2/src/hwpx/practice/aggregate.py +0 -947
  13. python_hwpx-2.29.2/src/hwpx/practice/campaign.py +0 -296
  14. python_hwpx-2.29.2/src/hwpx/practice/domain.py +0 -3137
  15. python_hwpx-2.29.2/src/hwpx/practice/dossier.py +0 -78
  16. python_hwpx-2.29.2/src/hwpx/practice/evaluator.py +0 -2359
  17. python_hwpx-2.29.2/src/hwpx/practice/forge.py +0 -436
  18. python_hwpx-2.29.2/src/hwpx/practice/intake.py +0 -230
  19. python_hwpx-2.29.2/src/hwpx/practice/lineage.py +0 -113
  20. python_hwpx-2.29.2/src/hwpx/practice/mutations.py +0 -168
  21. python_hwpx-2.29.2/src/hwpx/practice/registry.py +0 -322
  22. python_hwpx-2.29.2/src/hwpx/practice/run.py +0 -757
  23. python_hwpx-2.29.2/src/hwpx/practice/sanitize.py +0 -154
  24. python_hwpx-2.29.2/src/hwpx/practice/scenario.py +0 -146
  25. python_hwpx-2.29.2/src/hwpx/practice/split.py +0 -249
  26. python_hwpx-2.29.2/tests/test_practice_campaign_aggregate.py +0 -870
  27. python_hwpx-2.29.2/tests/test_practice_contracts.py +0 -255
  28. python_hwpx-2.29.2/tests/test_practice_domain_evaluator.py +0 -2134
  29. python_hwpx-2.29.2/tests/test_practice_evaluator.py +0 -2094
  30. python_hwpx-2.29.2/tests/test_practice_intake.py +0 -214
  31. python_hwpx-2.29.2/tests/test_practice_run_contracts.py +0 -422
  32. python_hwpx-2.29.2/tests/test_practice_sanitize.py +0 -75
  33. python_hwpx-2.29.2/tests/test_practice_scenario_forge.py +0 -73
  34. python_hwpx-2.29.2/tests/test_practice_scenario_pack.py +0 -170
  35. python_hwpx-2.29.2/tests/test_practice_source_integrity.py +0 -128
  36. python_hwpx-2.29.2/tests/test_practice_split.py +0 -85
  37. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/LICENSE +0 -0
  38. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/NOTICE +0 -0
  39. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/README.md +0 -0
  40. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/setup.cfg +0 -0
  41. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/__init__.py +0 -0
  42. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/__init__.py +0 -0
  43. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/blueprint/__init__.py +0 -0
  44. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/blueprint/bundle.py +0 -0
  45. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/blueprint/catalog.py +0 -0
  46. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/blueprint/dump.py +0 -0
  47. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/blueprint/mapping.py +0 -0
  48. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/blueprint/model.py +0 -0
  49. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/blueprint/native.py +0 -0
  50. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/blueprint/replay.py +0 -0
  51. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/catalog.py +0 -0
  52. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/cli.py +0 -0
  53. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/commands.py +0 -0
  54. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/document.py +0 -0
  55. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/model.py +0 -0
  56. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/path.py +0 -0
  57. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/agent/query.py +0 -0
  58. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/authoring.py +0 -0
  59. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/benchmark/__init__.py +0 -0
  60. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/benchmark/blind_eval.py +0 -0
  61. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/body_patch.py +0 -0
  62. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/builder/__init__.py +0 -0
  63. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/builder/core.py +0 -0
  64. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/builder/report.py +0 -0
  65. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/__init__.py +0 -0
  66. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/badges.py +0 -0
  67. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/corpus/corpus.json +0 -0
  68. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/corpus/meeting_summary.hwpx +0 -0
  69. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/corpus/notice.hwpx +0 -0
  70. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/corpus/report_table.hwpx +0 -0
  71. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/corpus.py +0 -0
  72. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/report.py +0 -0
  73. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/roundtrip_batch.py +0 -0
  74. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/conformance/runner.py +0 -0
  75. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/data/Skeleton.hwpx +0 -0
  76. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/__init__.py +0 -0
  77. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/_support.py +0 -0
  78. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/composer.py +0 -0
  79. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/harvest.py +0 -0
  80. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/plan.py +0 -0
  81. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profile.py +0 -0
  82. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/application_form/fragments/body.xml +0 -0
  83. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/application_form/fragments/heading.xml +0 -0
  84. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/application_form/fragments/info_table.xml +0 -0
  85. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/application_form/fragments/title.xml +0 -0
  86. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/application_form/profile.json +0 -0
  87. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/application_form/template.hwpx +0 -0
  88. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/home_notice/fragments/body.xml +0 -0
  89. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/home_notice/fragments/heading.xml +0 -0
  90. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/home_notice/fragments/title.xml +0 -0
  91. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/home_notice/profile.json +0 -0
  92. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/home_notice/template.hwpx +0 -0
  93. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/official_notice/fragments/body.xml +0 -0
  94. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/official_notice/fragments/heading.xml +0 -0
  95. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/official_notice/fragments/info_table.xml +0 -0
  96. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/official_notice/fragments/title.xml +0 -0
  97. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/official_notice/profile.json +0 -0
  98. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/official_notice/template.hwpx +0 -0
  99. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/report/fragments/body.xml +0 -0
  100. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/report/fragments/heading.xml +0 -0
  101. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/report/fragments/info_table.xml +0 -0
  102. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/report/fragments/title.xml +0 -0
  103. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/report/profile.json +0 -0
  104. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/profiles/report/template.hwpx +0 -0
  105. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/design/validator.py +0 -0
  106. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/document.py +0 -0
  107. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/evalplan_fill.py +0 -0
  108. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/exam/__init__.py +0 -0
  109. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/exam/compose.py +0 -0
  110. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/exam/ir.py +0 -0
  111. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/exam/measure.py +0 -0
  112. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/exam/parser.py +0 -0
  113. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/exam/profile.py +0 -0
  114. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/fill_residue.py +0 -0
  115. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fill.py +0 -0
  116. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fit/__init__.py +0 -0
  117. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fit/apply.py +0 -0
  118. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fit/engine.py +0 -0
  119. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fit/measure.py +0 -0
  120. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fit/policy.py +0 -0
  121. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fit/report.py +0 -0
  122. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fit/seal.py +0 -0
  123. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/form_fit/wordbox.py +0 -0
  124. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/formfill_quality.py +0 -0
  125. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/guidance_scan.py +0 -0
  126. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/ingest/__init__.py +0 -0
  127. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/ingest/base.py +0 -0
  128. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/ingest/hwpx_converter.py +0 -0
  129. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/layout/__init__.py +0 -0
  130. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/layout/lint.py +0 -0
  131. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/layout/report.py +0 -0
  132. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/opc/package.py +0 -0
  133. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/opc/relationships.py +0 -0
  134. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/opc/security.py +0 -0
  135. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/opc/xml_utils.py +0 -0
  136. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/__init__.py +0 -0
  137. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/_document_impl.py +0 -0
  138. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/body.py +0 -0
  139. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/canonical_defaults.py +0 -0
  140. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/common.py +0 -0
  141. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/document.py +0 -0
  142. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/header.py +0 -0
  143. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/header_part.py +0 -0
  144. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/memo.py +0 -0
  145. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/namespaces.py +0 -0
  146. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/numbering.py +0 -0
  147. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/objects.py +0 -0
  148. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/paragraph.py +0 -0
  149. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/parser.py +0 -0
  150. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/run.py +0 -0
  151. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/schema.py +0 -0
  152. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/section.py +0 -0
  153. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/simple_parts.py +0 -0
  154. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/table.py +0 -0
  155. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/oxml/utils.py +0 -0
  156. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/package.py +0 -0
  157. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/patch.py +0 -0
  158. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/presets/__init__.py +0 -0
  159. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/presets/proposal.py +0 -0
  160. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/py.typed +0 -0
  161. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/quality/__init__.py +0 -0
  162. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/quality/ledger.py +0 -0
  163. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/quality/policy.py +0 -0
  164. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/quality/report.py +0 -0
  165. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/quality/save_pipeline.py +0 -0
  166. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/template_formfit.py +0 -0
  167. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/templates.py +0 -0
  168. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/__init__.py +0 -0
  169. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/_schemas/header.xsd +0 -0
  170. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/_schemas/section.xsd +0 -0
  171. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/advanced_generators.py +0 -0
  172. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/archive_cli.py +0 -0
  173. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/doc_diff.py +0 -0
  174. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/exporter.py +0 -0
  175. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/fuzz/__init__.py +0 -0
  176. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/fuzz/__main__.py +0 -0
  177. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/fuzz/catalog.py +0 -0
  178. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/fuzz/generator.py +0 -0
  179. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/fuzz/minimize.py +0 -0
  180. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/fuzz/runner.py +0 -0
  181. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/generic_inventory.py +0 -0
  182. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/id_integrity.py +0 -0
  183. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/idempotence.py +0 -0
  184. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/ir_equality.py +0 -0
  185. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/layout_preview.py +0 -0
  186. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/mail_merge.py +0 -0
  187. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/markdown_export.py +0 -0
  188. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/object_finder.py +0 -0
  189. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/official_lint.py +0 -0
  190. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/package_reconcile.py +0 -0
  191. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/package_validator.py +0 -0
  192. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/page_guard.py +0 -0
  193. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/pii.py +0 -0
  194. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/read_fidelity.py +0 -0
  195. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/recover.py +0 -0
  196. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/redline.py +0 -0
  197. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/repair.py +0 -0
  198. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/report_parser.py +0 -0
  199. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/report_utils.py +0 -0
  200. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/roundtrip_diff.py +0 -0
  201. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/style_profile.py +0 -0
  202. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/table_cleanup.py +0 -0
  203. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/table_compute.py +0 -0
  204. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/table_navigation.py +0 -0
  205. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/template_analyzer.py +0 -0
  206. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/text_extract_cli.py +0 -0
  207. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/text_extractor.py +0 -0
  208. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/toc_author.py +0 -0
  209. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/toc_fidelity.py +0 -0
  210. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/tools/validator.py +0 -0
  211. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/__init__.py +0 -0
  212. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/_hancom_open_rate.ps1 +0 -0
  213. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/_refresh_hwpx_mac.applescript +0 -0
  214. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/_render_hwpx.ps1 +0 -0
  215. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/_render_hwpx_mac.applescript +0 -0
  216. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/detectors.py +0 -0
  217. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/diff.py +0 -0
  218. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/fixture_corpus.py +0 -0
  219. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/hancom_worker.py +0 -0
  220. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/masks.py +0 -0
  221. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/oracle.py +0 -0
  222. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/page_qa.py +0 -0
  223. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/qa_contracts.py +0 -0
  224. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/qa_metrics.py +0 -0
  225. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/hwpx/visual/report.py +0 -0
  226. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/python_hwpx.egg-info/dependency_links.txt +0 -0
  227. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/python_hwpx.egg-info/entry_points.txt +0 -0
  228. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/python_hwpx.egg-info/requires.txt +0 -0
  229. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/src/python_hwpx.egg-info/top_level.txt +0 -0
  230. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_advanced_generators.py +0 -0
  231. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_blueprint_contracts.py +0 -0
  232. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_blueprint_dump.py +0 -0
  233. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_blueprint_replay.py +0 -0
  234. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_catalog.py +0 -0
  235. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_cli.py +0 -0
  236. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_commands.py +0 -0
  237. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_contracts.py +0 -0
  238. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_document.py +0 -0
  239. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_agent_path_query.py +0 -0
  240. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_authoring_profile_routing.py +0 -0
  241. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_authoring_profile_routing_oracle.py +0 -0
  242. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_authoring_render_check.py +0 -0
  243. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_blind_eval_fixture.py +0 -0
  244. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_body_patch.py +0 -0
  245. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_builder_core.py +0 -0
  246. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_builder_plan_v2.py +0 -0
  247. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_builder_vertical_slice.py +0 -0
  248. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_cell_line_spacing.py +0 -0
  249. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_comment_node_robustness.py +0 -0
  250. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_conformance.py +0 -0
  251. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_corpus_read_fidelity.py +0 -0
  252. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_coverage_promotion.py +0 -0
  253. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_coverage_targets.py +0 -0
  254. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_design_builder.py +0 -0
  255. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_deviations_registry.py +0 -0
  256. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_doc_diff.py +0 -0
  257. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_document_context_manager.py +0 -0
  258. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_document_formatting.py +0 -0
  259. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_document_plan.py +0 -0
  260. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_document_plan_computed_fields.py +0 -0
  261. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_document_save_api.py +0 -0
  262. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_exam_compose.py +0 -0
  263. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_exam_compose_oracle.py +0 -0
  264. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_exam_fixtures.py +0 -0
  265. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_exam_ir.py +0 -0
  266. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_exam_measure.py +0 -0
  267. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_exam_parser.py +0 -0
  268. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_exam_profile.py +0 -0
  269. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_existing_document_format_editing.py +0 -0
  270. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_fill_residue.py +0 -0
  271. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_form_fields.py +0 -0
  272. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_form_fill_split_run.py +0 -0
  273. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_form_fit.py +0 -0
  274. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_form_fit_integration.py +0 -0
  275. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_form_fit_seal.py +0 -0
  276. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_form_fit_seal_placement.py +0 -0
  277. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_form_fit_wordbox.py +0 -0
  278. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_formfill_quality.py +0 -0
  279. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_fuzz_loop.py +0 -0
  280. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_fuzz_regressions.py +0 -0
  281. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_gap_closure_tools.py +0 -0
  282. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_government_report_preset.py +0 -0
  283. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_government_table_profile.py +0 -0
  284. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_guidance_scan.py +0 -0
  285. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_hancom_render_worker.py +0 -0
  286. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_hp_tab_support.py +0 -0
  287. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_hwpxlib_corpus_read.py +0 -0
  288. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_id_generator_range.py +0 -0
  289. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_id_integrity.py +0 -0
  290. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_image_object_workflow.py +0 -0
  291. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_ingest.py +0 -0
  292. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_inline_models.py +0 -0
  293. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_integration_hwpx_compatibility.py +0 -0
  294. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_integration_roundtrip.py +0 -0
  295. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_kordoc_absorption.py +0 -0
  296. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_layout_lint.py +0 -0
  297. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_layout_preview.py +0 -0
  298. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_mail_merge_fit.py +0 -0
  299. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_mail_merge_table_compute.py +0 -0
  300. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_mail_merge_xlsx.py +0 -0
  301. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_markdown_export.py +0 -0
  302. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_memo_and_style_editing.py +0 -0
  303. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_namespace_handling.py +0 -0
  304. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_new_features.py +0 -0
  305. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_official_document_style.py +0 -0
  306. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_official_lint_gongmun_gate.py +0 -0
  307. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_official_lint_tableaware.py +0 -0
  308. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_opc_package.py +0 -0
  309. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_open_safety_corpus.py +0 -0
  310. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_openrate.py +0 -0
  311. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_oxml_parsing.py +0 -0
  312. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_packaging_license_metadata.py +0 -0
  313. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_paragraph_keep_together.py +0 -0
  314. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_paragraph_section_management.py +0 -0
  315. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_pii.py +0 -0
  316. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_pii_structural.py +0 -0
  317. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_pii_wiring.py +0 -0
  318. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_proposal_preset.py +0 -0
  319. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_question_split_detector.py +0 -0
  320. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_read_fidelity.py +0 -0
  321. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_recover_broken_zip.py +0 -0
  322. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_redline_authoring.py +0 -0
  323. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_redline_verify.py +0 -0
  324. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_repair_repack.py +0 -0
  325. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_report_parser.py +0 -0
  326. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_report_utils.py +0 -0
  327. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_repr_snapshots.py +0 -0
  328. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_rhwp_t1_gates.py +0 -0
  329. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_rhwp_t2_verification.py +0 -0
  330. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_roundtrip_fidelity.py +0 -0
  331. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_row_heights.py +0 -0
  332. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_save_pipeline.py +0 -0
  333. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_save_pipeline_no_bypass.py +0 -0
  334. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_section_headers.py +0 -0
  335. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_set_paragraph_format_keep.py +0 -0
  336. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_skeleton_template_ids.py +0 -0
  337. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_split_cell.py +0 -0
  338. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_split_merged_cell.py +0 -0
  339. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_style_profile.py +0 -0
  340. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_table_cleanup.py +0 -0
  341. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_table_navigation.py +0 -0
  342. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_table_patch_dryrun.py +0 -0
  343. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_tables_default_border.py +0 -0
  344. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_template_analyzer_enrichment.py +0 -0
  345. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_template_formfit.py +0 -0
  346. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_text_extractor_annotations.py +0 -0
  347. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_toc_author.py +0 -0
  348. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_toc_fidelity.py +0 -0
  349. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_validation_severity.py +0 -0
  350. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_validator_comment_nodes.py +0 -0
  351. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_version_metadata.py +0 -0
  352. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_visual_fixture_corpus.py +0 -0
  353. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_visual_oracle.py +0 -0
  354. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_visual_qa_contracts.py +0 -0
  355. {python_hwpx-2.29.2 → python_hwpx-3.0.0}/tests/test_visual_qa_metrics.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx
3
- Version: 2.29.2
3
+ Version: 3.0.0
4
4
  Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리
5
5
  Author: python-hwpx Maintainers
6
6
  License-Expression: Apache-2.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-hwpx"
7
- version = "2.29.2"
7
+ version = "3.0.0"
8
8
  description = "한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  license = "Apache-2.0"
@@ -840,6 +840,25 @@ def _collapse_empty_rows(table: str) -> str:
840
840
  return _rebuild(prefix, rows, suffix, rowcnt=rowcnt)
841
841
 
842
842
 
843
+ def _physical_row_height(rows: Sequence[str], row: int) -> int | None:
844
+ """Return the exact height of a physical row when direct cells expose it.
845
+
846
+ A physical row can contain several ``rowSpan==1`` cells with different
847
+ ``cellSz`` heights. Hancom uses the tallest direct cell as the row height;
848
+ shorter values can belong to sparse/covered-looking cells. ``None`` means
849
+ the row is covered entirely by vertical merges and remains underivable.
850
+ """
851
+ heights: list[int] = []
852
+ for physical_row in rows:
853
+ for tc in _S_TC.findall(physical_row):
854
+ ra = _si(tc, "cellAddr", "rowAddr")
855
+ rs = _si(tc, "cellSpan", "rowSpan") or 1
856
+ height = _si(tc, "cellSz", "height")
857
+ if ra == row and rs == 1 and height is not None and height > 0:
858
+ heights.append(height)
859
+ return max(heights) if heights else None
860
+
861
+
843
862
  def _delete_rows(table: str, del_rows: Iterable[int]) -> str:
844
863
  """Delete physical rows by index, reconciling rowAddr/rowSpan and covering
845
864
  cells' height."""
@@ -849,6 +868,7 @@ def _delete_rows(table: str, del_rows: Iterable[int]) -> str:
849
868
  for empty in del_rows:
850
869
  if empty >= len(rows):
851
870
  raise TableStructureError(f"row index {empty} out of range")
871
+ exact_height = _physical_row_height(rows, empty)
852
872
  heights = []
853
873
  for r in rows:
854
874
  for tc in _S_TC.findall(r):
@@ -857,7 +877,9 @@ def _delete_rows(table: str, del_rows: Iterable[int]) -> str:
857
877
  h = _si(tc, "cellSz", "height")
858
878
  if h:
859
879
  heights.append(h // rs)
860
- drop_h = min(heights) if heights else 0
880
+ # Prefer the exact physical-row height. Fully merged rows expose no
881
+ # rowSpan==1 cell, so preserve the previous conservative estimate there.
882
+ drop_h = exact_height if exact_height is not None else (min(heights) if heights else 0)
861
883
 
862
884
  def fix(tc: str):
863
885
  ra, rs = _si(tc, "cellAddr", "rowAddr"), _si(tc, "cellSpan", "rowSpan") or 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-hwpx
3
- Version: 2.29.2
3
+ Version: 3.0.0
4
4
  Summary: 한글 없이 HWPX 문서를 열고, 편집하고, 생성하고, 검증하는 Python 자동화 라이브러리
5
5
  Author: python-hwpx Maintainers
6
6
  License-Expression: Apache-2.0
@@ -123,21 +123,6 @@ src/hwpx/oxml/section.py
123
123
  src/hwpx/oxml/simple_parts.py
124
124
  src/hwpx/oxml/table.py
125
125
  src/hwpx/oxml/utils.py
126
- src/hwpx/practice/__init__.py
127
- src/hwpx/practice/aggregate.py
128
- src/hwpx/practice/campaign.py
129
- src/hwpx/practice/domain.py
130
- src/hwpx/practice/dossier.py
131
- src/hwpx/practice/evaluator.py
132
- src/hwpx/practice/forge.py
133
- src/hwpx/practice/intake.py
134
- src/hwpx/practice/lineage.py
135
- src/hwpx/practice/mutations.py
136
- src/hwpx/practice/registry.py
137
- src/hwpx/practice/run.py
138
- src/hwpx/practice/sanitize.py
139
- src/hwpx/practice/scenario.py
140
- src/hwpx/practice/split.py
141
126
  src/hwpx/presets/__init__.py
142
127
  src/hwpx/presets/proposal.py
143
128
  src/hwpx/quality/__init__.py
@@ -299,18 +284,8 @@ tests/test_paragraph_section_management.py
299
284
  tests/test_pii.py
300
285
  tests/test_pii_structural.py
301
286
  tests/test_pii_wiring.py
302
- tests/test_practice_campaign_aggregate.py
303
- tests/test_practice_contracts.py
304
- tests/test_practice_domain_evaluator.py
305
- tests/test_practice_evaluator.py
306
- tests/test_practice_intake.py
307
- tests/test_practice_run_contracts.py
308
- tests/test_practice_sanitize.py
309
- tests/test_practice_scenario_forge.py
310
- tests/test_practice_scenario_pack.py
311
- tests/test_practice_source_integrity.py
312
- tests/test_practice_split.py
313
287
  tests/test_proposal_preset.py
288
+ tests/test_public_runtime_boundary.py
314
289
  tests/test_question_split_detector.py
315
290
  tests/test_read_fidelity.py
316
291
  tests/test_recover_broken_zip.py
@@ -414,6 +414,41 @@ def test_scorer_ratio_content_penalises_sample():
414
414
  assert any("반영비율 carries" in f for f in ds.findings)
415
415
 
416
416
 
417
+ def test_2015_rubric_shrink_survives_repair_repack(tmp_path):
418
+ """The real evalplan path keeps exact merged heights through repair/repack."""
419
+ from hwpx.evalplan_fill import fill_evalplan, _grid_of, _rubric_indices
420
+ from hwpx.table_patch import _direct_cells
421
+ from hwpx.tools.package_validator import validate_editor_open_safety
422
+ from hwpx.tools.repair import repair_repack
423
+
424
+ data = fill_evalplan(
425
+ str(BLANK_3HAK), parse_review_md(SYNTHETIC), phase="all",
426
+ )["_data"]
427
+ produced = tmp_path / "evalplan-filled.hwpx"
428
+ repaired = tmp_path / "evalplan-filled-repaired.hwpx"
429
+ produced.write_bytes(data)
430
+
431
+ repair = repair_repack(produced, repaired)
432
+ assert repair.open_safety["ok"] is True
433
+ assert validate_editor_open_safety(repaired).ok
434
+
435
+ repaired_data = repaired.read_bytes()
436
+ ti = _rubric_indices(repaired_data)[0]
437
+ _sp, table, _grid, report = _grid_of(repaired_data, ti)
438
+ assert report.ok
439
+
440
+ heights = {}
441
+ for cell in _direct_cells(table):
442
+ block = table[cell.start:cell.end]
443
+ match = re.search(rb'<hp:cellSz\b[^>]*\bheight="(\d+)"', block)
444
+ assert match is not None
445
+ heights[(cell.row, cell.col, cell.row_span)] = int(match.group(1))
446
+
447
+ assert heights[(6, 0, 4)] == 9464
448
+ assert heights[(6, 1, 2)] == 5200
449
+ assert heights[(6, 2, 2)] == 5200
450
+
451
+
417
452
  def test_fill_rubrics_2022_normalizes_and_fills_chaejeom_ladder():
418
453
  """The 2022-개정 rubric 수행수준 채점기준 ladder is reshaped (delete_row /
419
454
  insert_row_by_clone -- byte-preserving, NOT regeneration) to the review MD's level
@@ -30,9 +30,13 @@ def test_py_typed_is_included_in_built_distributions(tmp_path: Path, distributio
30
30
  with ZipFile(wheel_path) as wheel_archive:
31
31
  wheel_members = set(wheel_archive.namelist())
32
32
  assert "hwpx/py.typed" in wheel_members
33
+ assert "hwpx/practice.py" not in wheel_members
34
+ assert not any(name.startswith("hwpx/practice/") for name in wheel_members)
33
35
  return
34
36
 
35
37
  sdist_path = next(tmp_path.glob("*.tar.gz"))
36
38
  with tarfile.open(sdist_path, "r:gz") as sdist_archive:
37
39
  sdist_members = sdist_archive.getnames()
38
40
  assert any(name.endswith("/src/hwpx/py.typed") for name in sdist_members)
41
+ assert not any(name.endswith("/src/hwpx/practice.py") for name in sdist_members)
42
+ assert not any("/src/hwpx/practice/" in name for name in sdist_members)
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import import_module
4
+
5
+ import pytest
6
+
7
+
8
+ def test_internal_practice_namespace_is_not_importable() -> None:
9
+ with pytest.raises(ModuleNotFoundError) as exc_info:
10
+ import_module("hwpx.practice")
11
+
12
+ assert exc_info.value.name == "hwpx.practice"
@@ -23,6 +23,7 @@ from hwpx.table_patch import (
23
23
  _iter_table_spans,
24
24
  _uniform_col_widths,
25
25
  _parse_table,
26
+ _delete_rows,
26
27
  )
27
28
 
28
29
  FIXT = Path(__file__).parent / "fixtures"
@@ -220,6 +221,49 @@ def test_delete_row_reconciles_and_preserves_untouched(merged):
220
221
  assert sec0[spans0[other][0]:spans0[other][1]] == sec1[spans1[other][0]:spans1[other][1]]
221
222
 
222
223
 
224
+ def test_delete_row_uses_tallest_direct_cell_for_unequal_physical_height():
225
+ """Merged height loses the deleted physical row's exact (maximum) height.
226
+
227
+ The middle row deliberately has direct-cell heights 283 and 3000. Its
228
+ physical height is 3000, not the smaller cell and not the merged cell's
229
+ 6000//3 average (2000).
230
+ """
231
+ def cell(row: int, col: int, row_span: int, height: int) -> str:
232
+ return (
233
+ "<hp:tc>"
234
+ f'<hp:cellAddr colAddr="{col}" rowAddr="{row}"/>'
235
+ f'<hp:cellSpan colSpan="1" rowSpan="{row_span}"/>'
236
+ f'<hp:cellSz width="1000" height="{height}"/>'
237
+ "</hp:tc>"
238
+ )
239
+
240
+ table = (
241
+ '<hp:tbl rowCnt="3" colCnt="3">'
242
+ f"<hp:tr>{cell(0, 0, 3, 6000)}{cell(0, 1, 1, 1000)}{cell(0, 2, 1, 1000)}</hp:tr>"
243
+ f"<hp:tr>{cell(1, 1, 1, 283)}{cell(1, 2, 1, 3000)}</hp:tr>"
244
+ f"<hp:tr>{cell(2, 1, 1, 2000)}{cell(2, 2, 1, 2000)}</hp:tr>"
245
+ "</hp:tbl>"
246
+ )
247
+
248
+ result = _delete_rows(table, [1])
249
+ grid, report = build_grid(result.encode())
250
+ assert report.ok and (report.row_count, report.col_count) == (2, 3)
251
+
252
+ merged_cell = grid[(0, 0)]
253
+ merged_xml = result.encode()[merged_cell.start:merged_cell.end]
254
+ assert merged_cell.row_span == 2
255
+ assert re.search(rb'<hp:cellSz\b[^>]*\bheight="3000"', merged_xml)
256
+
257
+ surviving_heights = {}
258
+ for address in ((0, 1), (0, 2), (1, 1), (1, 2)):
259
+ direct = grid[address]
260
+ direct_xml = result.encode()[direct.start:direct.end]
261
+ match = re.search(rb'<hp:cellSz\b[^>]*\bheight="(\d+)"', direct_xml)
262
+ assert match is not None
263
+ surviving_heights[address] = int(match.group(1))
264
+ assert surviving_heights == {(0, 1): 1000, (0, 2): 1000, (1, 1): 2000, (1, 2): 2000}
265
+
266
+
223
267
  def test_insert_row_by_clone_grows_and_validates(merged):
224
268
  def has_clonable(tbl: bytes):
225
269
  _p, rows, _s = _parse_table(tbl.decode("utf-8"))
@@ -27,6 +27,7 @@ from hwpx.table_patch import (
27
27
 
28
28
  FIXT = Path(__file__).parent / "fixtures" / "m105_evalplan"
29
29
  FORM_2HAK = FIXT / "blank_form_1-2hak.hwpx"
30
+ FORM_3HAK = FIXT / "blank_form_3hak.hwpx"
30
31
 
31
32
 
32
33
  def _section(data: bytes) -> tuple[str, bytes]:
@@ -66,6 +67,11 @@ def form2() -> bytes:
66
67
  return FORM_2HAK.read_bytes()
67
68
 
68
69
 
70
+ @pytest.fixture(scope="module")
71
+ def form3() -> bytes:
72
+ return FORM_3HAK.read_bytes()
73
+
74
+
69
75
  # ---- FR-001 merged-block clone --------------------------------------------
70
76
  def test_seongchwi_is_the_merged_block_case(form2):
71
77
  # table [8] = 성취기준 (듣기·말하기): header + two 5-row A~E blocks.
@@ -167,6 +173,31 @@ def test_delete_column_rest_byte_identical(form2):
167
173
  assert secb[sb[i][0]:sb[i][1]] == seco[so[i][0]:so[i][1]]
168
174
 
169
175
 
176
+ # ---- delete_row with unequal physical row heights --------------------------
177
+ def test_delete_rows_preserves_evalplan_merged_height_sums(form3):
178
+ """The 2015 rubric has non-uniform criterion/base row heights.
179
+
180
+ Reducing its seven-row criterion block to three rows must subtract each
181
+ deleted physical row's native height from every covering merged cell.
182
+ """
183
+ res = apply_table_ops(
184
+ form3,
185
+ [{"op": "delete_row", "table_index": 20, "rows": [9, 10, 11, 12]}],
186
+ )
187
+ assert res.ok, res.skipped
188
+
189
+ table = _table(res.data, 20)
190
+ _grid_map, rep = build_grid(table)
191
+ assert rep.ok and (rep.row_count, rep.col_count) == (11, 5)
192
+
193
+ merged_heights = {}
194
+ for cell in _direct_cells(table):
195
+ if cell.row == 6 and cell.col in (0, 1, 2):
196
+ cell_xml = table[cell.start:cell.end].decode("utf-8")
197
+ merged_heights[(cell.col, cell.row_span)] = _si(cell_xml, "cellSz", "height")
198
+ assert merged_heights == {(0, 5): 12064, (1, 3): 7800, (2, 3): 7800}
199
+
200
+
170
201
  # ---- FR-002 anchor / heading addressing ------------------------------------
171
202
  def test_table_anchor_resolves_uniquely(form2):
172
203
  # heading text lives in the numbered header-box table before the data table.
@@ -1,277 +0,0 @@
1
- """Privacy-gated contracts for deterministic HWPX practice corpora.
2
-
3
- The package deliberately starts with contracts and fail-closed validators. Real
4
- corpus intake and document mutation are added only after these boundaries are
5
- covered by tests.
6
- """
7
-
8
- from .lineage import (
9
- LINEAGE_KINDS,
10
- LineageEdge,
11
- build_lineage_groups,
12
- validate_partition_closure,
13
- )
14
- from .intake import (
15
- IntakeResult,
16
- ReviewDecision,
17
- apply_review_decision,
18
- intake_discovery_rows,
19
- )
20
- from .dossier import (
21
- SYNTHETIC_DOSSIER_SCHEMA,
22
- synthetic_dossier,
23
- validate_synthetic_dossier,
24
- )
25
- from .campaign import (
26
- ACTIVE_CAMPAIGN_STATES,
27
- CAMPAIGN_BUDGET_FIELDS,
28
- CAMPAIGN_STATES,
29
- PRACTICE_CAMPAIGN_MANIFEST_SCHEMA,
30
- TERMINAL_CAMPAIGN_STATES,
31
- build_campaign_manifest,
32
- campaign_manifest_sha256,
33
- validate_campaign_manifest,
34
- )
35
- from .aggregate import (
36
- PRACTICE_CAMPAIGN_AGGREGATE_SCHEMA,
37
- build_campaign_aggregate,
38
- campaign_aggregate_sha256,
39
- validate_campaign_aggregate,
40
- )
41
- from .domain import (
42
- ABSTENTION_INVENTORY_AUTH_SCHEMA,
43
- DOMAIN_EVALUATION_BUNDLE_SCHEMA,
44
- DOMAIN_REQUIREMENT_SCHEMA,
45
- DOMAIN_SOURCE_EVIDENCE_SCHEMA,
46
- DOMAIN_TERMINAL_SOURCE_EVIDENCE_SCHEMA,
47
- abstention_inventory_authentication_key_id,
48
- authoring_verifier_policy_sha256,
49
- build_authoring_domain_evidence,
50
- build_domain_evaluation_bundle,
51
- build_domain_requirement,
52
- build_edit_domain_evidence_from_semantic,
53
- build_exam_domain_evidence,
54
- build_exam_oracle_receipt,
55
- build_form_differential_receipt,
56
- build_form_target_policy,
57
- build_form_fill_domain_evidence_from_artifacts,
58
- build_must_abstain_domain_evidence_from_receipt,
59
- build_official_document_domain_evidence,
60
- build_structural_table_domain_evidence_from_artifacts,
61
- domain_requirement_policy_projection,
62
- domain_row_sha256,
63
- domain_value_sha256,
64
- exam_oracle_authentication_key_id,
65
- exam_verifier_policy_sha256,
66
- form_differential_oracle_provenance_sha256,
67
- form_differential_receipt_sha256,
68
- form_target_policy_sha256,
69
- form_verifier_policy_sha256,
70
- must_abstain_verifier_policy_sha256,
71
- official_verifier_policy_sha256,
72
- structural_verifier_policy_sha256,
73
- serialize_form_differential_receipt,
74
- validate_domain_evaluation_bundle,
75
- validate_domain_requirement,
76
- validate_exam_oracle_receipt,
77
- validate_form_differential_receipt,
78
- validate_form_target_policy,
79
- )
80
- from .evaluator import (
81
- EVALUATOR_RESULT_SCHEMA,
82
- EVALUATION_POLICY_SCHEMA,
83
- PACKAGE_POLICY_SCHEMA,
84
- SEMANTIC_POLICY_SCHEMA,
85
- build_idempotency_replay_receipt,
86
- build_package_policy,
87
- build_workflow_revision_receipt,
88
- combine_evaluation_result,
89
- current_evaluator_code_sha256,
90
- domain_layer_from_bundle,
91
- evaluate_package_layer,
92
- evaluate_semantic_layer,
93
- evaluation_policy_sha256,
94
- evaluator_authentication_key_id,
95
- semantic_policy_projection,
96
- validate_evaluation_result,
97
- )
98
- from .forge import (
99
- EVALUATOR_MANIFEST_SCHEMA,
100
- RUNNER_MANIFEST_SCHEMA,
101
- SCENARIO_PACK_SCHEMA,
102
- ForgeConfig,
103
- forge_scenario_pack,
104
- runner_view,
105
- validate_scenario_pack,
106
- write_scenario_pack,
107
- )
108
- from .mutations import (
109
- CONTROLLED_MUTATION_SCHEMA,
110
- apply_mutation,
111
- controlled_mutation,
112
- mutation_sha256,
113
- reverse_mutation,
114
- validate_controlled_mutation,
115
- )
116
- from .registry import (
117
- PRIVATE_REGISTRY_SCHEMA,
118
- REDACTED_REGISTRY_SCHEMA,
119
- SOURCE_INTEGRITY_SCHEMA,
120
- assert_redacted_payload,
121
- build_source_integrity_receipt,
122
- eligibility_status,
123
- opaque_document_id,
124
- redact_private_record,
125
- snapshot_source_tree,
126
- validate_private_record,
127
- validate_storage_roots,
128
- )
129
- from .scenario import PRACTICE_SCENARIO_SCHEMA, scenario_id, validate_scenario
130
- from .run import (
131
- ACTIVE_RUN_STATES,
132
- PRACTICE_RUN_EVENT_SCHEMA,
133
- PRACTICE_RUN_RECEIPT_SCHEMA,
134
- PRACTICE_RUN_SCHEMA,
135
- RUN_BUDGET_FIELDS,
136
- RUN_STATES,
137
- SCENARIO_EXPECTED_STATES,
138
- TERMINAL_RUN_STATES,
139
- assert_receipt_safe,
140
- practice_run_id,
141
- redact_run_receipt,
142
- validate_exact_provenance,
143
- validate_practice_run,
144
- validate_run_budgets,
145
- validate_run_receipt,
146
- workflow_event_id,
147
- )
148
- from .sanitize import DERIVATIVE_ID_PATTERN, sanitize_document_copy
149
- from .split import SPLIT_MANIFEST_SCHEMA, build_split_manifest, validate_split_manifest
150
-
151
- __all__ = [
152
- "ABSTENTION_INVENTORY_AUTH_SCHEMA",
153
- "ACTIVE_CAMPAIGN_STATES",
154
- "ACTIVE_RUN_STATES",
155
- "CAMPAIGN_BUDGET_FIELDS",
156
- "CAMPAIGN_STATES",
157
- "LINEAGE_KINDS",
158
- "CONTROLLED_MUTATION_SCHEMA",
159
- "EVALUATOR_MANIFEST_SCHEMA",
160
- "DERIVATIVE_ID_PATTERN",
161
- "DOMAIN_EVALUATION_BUNDLE_SCHEMA",
162
- "DOMAIN_REQUIREMENT_SCHEMA",
163
- "DOMAIN_SOURCE_EVIDENCE_SCHEMA",
164
- "DOMAIN_TERMINAL_SOURCE_EVIDENCE_SCHEMA",
165
- "EVALUATOR_RESULT_SCHEMA",
166
- "EVALUATION_POLICY_SCHEMA",
167
- "ForgeConfig",
168
- "PRIVATE_REGISTRY_SCHEMA",
169
- "PRACTICE_SCENARIO_SCHEMA",
170
- "PRACTICE_CAMPAIGN_MANIFEST_SCHEMA",
171
- "PRACTICE_CAMPAIGN_AGGREGATE_SCHEMA",
172
- "PRACTICE_RUN_EVENT_SCHEMA",
173
- "PRACTICE_RUN_RECEIPT_SCHEMA",
174
- "PRACTICE_RUN_SCHEMA",
175
- "REDACTED_REGISTRY_SCHEMA",
176
- "PACKAGE_POLICY_SCHEMA",
177
- "SOURCE_INTEGRITY_SCHEMA",
178
- "LineageEdge",
179
- "IntakeResult",
180
- "ReviewDecision",
181
- "RUNNER_MANIFEST_SCHEMA",
182
- "RUN_BUDGET_FIELDS",
183
- "RUN_STATES",
184
- "SCENARIO_EXPECTED_STATES",
185
- "SCENARIO_PACK_SCHEMA",
186
- "SEMANTIC_POLICY_SCHEMA",
187
- "SPLIT_MANIFEST_SCHEMA",
188
- "SYNTHETIC_DOSSIER_SCHEMA",
189
- "TERMINAL_CAMPAIGN_STATES",
190
- "TERMINAL_RUN_STATES",
191
- "apply_mutation",
192
- "abstention_inventory_authentication_key_id",
193
- "apply_review_decision",
194
- "authoring_verifier_policy_sha256",
195
- "assert_redacted_payload",
196
- "assert_receipt_safe",
197
- "build_lineage_groups",
198
- "build_split_manifest",
199
- "build_source_integrity_receipt",
200
- "build_campaign_manifest",
201
- "build_campaign_aggregate",
202
- "build_authoring_domain_evidence",
203
- "build_domain_evaluation_bundle",
204
- "build_domain_requirement",
205
- "build_edit_domain_evidence_from_semantic",
206
- "build_exam_domain_evidence",
207
- "build_exam_oracle_receipt",
208
- "build_form_differential_receipt",
209
- "build_form_target_policy",
210
- "build_form_fill_domain_evidence_from_artifacts",
211
- "build_must_abstain_domain_evidence_from_receipt",
212
- "build_official_document_domain_evidence",
213
- "build_package_policy",
214
- "build_idempotency_replay_receipt",
215
- "build_workflow_revision_receipt",
216
- "build_structural_table_domain_evidence_from_artifacts",
217
- "campaign_manifest_sha256",
218
- "campaign_aggregate_sha256",
219
- "combine_evaluation_result",
220
- "current_evaluator_code_sha256",
221
- "domain_layer_from_bundle",
222
- "domain_requirement_policy_projection",
223
- "domain_row_sha256",
224
- "domain_value_sha256",
225
- "exam_oracle_authentication_key_id",
226
- "exam_verifier_policy_sha256",
227
- "evaluate_package_layer",
228
- "evaluate_semantic_layer",
229
- "evaluation_policy_sha256",
230
- "evaluator_authentication_key_id",
231
- "eligibility_status",
232
- "controlled_mutation",
233
- "forge_scenario_pack",
234
- "form_target_policy_sha256",
235
- "form_differential_oracle_provenance_sha256",
236
- "form_differential_receipt_sha256",
237
- "form_verifier_policy_sha256",
238
- "must_abstain_verifier_policy_sha256",
239
- "opaque_document_id",
240
- "practice_run_id",
241
- "mutation_sha256",
242
- "official_verifier_policy_sha256",
243
- "intake_discovery_rows",
244
- "redact_private_record",
245
- "redact_run_receipt",
246
- "scenario_id",
247
- "sanitize_document_copy",
248
- "runner_view",
249
- "reverse_mutation",
250
- "snapshot_source_tree",
251
- "structural_verifier_policy_sha256",
252
- "serialize_form_differential_receipt",
253
- "validate_partition_closure",
254
- "validate_campaign_manifest",
255
- "validate_campaign_aggregate",
256
- "validate_domain_evaluation_bundle",
257
- "validate_domain_requirement",
258
- "validate_evaluation_result",
259
- "validate_exam_oracle_receipt",
260
- "validate_form_differential_receipt",
261
- "validate_form_target_policy",
262
- "validate_exact_provenance",
263
- "validate_practice_run",
264
- "validate_private_record",
265
- "validate_scenario",
266
- "validate_run_budgets",
267
- "validate_run_receipt",
268
- "synthetic_dossier",
269
- "validate_controlled_mutation",
270
- "validate_scenario_pack",
271
- "validate_synthetic_dossier",
272
- "validate_split_manifest",
273
- "validate_storage_roots",
274
- "semantic_policy_projection",
275
- "write_scenario_pack",
276
- "workflow_event_id",
277
- ]