Skip to content

perf(import): stop loading hwpx.hwp5 and hwpx.tools on import hwpx - #388

Merged
airmang merged 3 commits into
mainfrom
perf/lazy-hwp5-import
Oct 2, 2026
Merged

airmang merged 3 commits into
mainfrom
perf/lazy-hwp5-import

Conversation

@airmang

@airmang airmang commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Closes #387.

What

  • Hwp5Error / Hwp5ConversionWarning are now defined in hwpx.errors (which already registers the hwp5-* codes); hwpx.hwp5.errors re-exports the same classes. import hwpx no longer loads any hwpx.hwp5 module.
  • The 19 stable names that live in hwpx.tools (TextExtractor, doc_diff, validate_package, ...) resolve on first access through hwpx.__getattr__: same objects, no warning, cached after first lookup. hwpx.tools stays reachable after a plain import hwpx, dir() / from hwpx import * still cover __all__, and a TYPE_CHECKING block keeps their static types.
  • tests/data/import_breadth.json bound lowered 117 → 97.

The product-boundary check pins hwpx.__getattr__ by fingerprint; that pin is updated here. The new branch imports one fixed module statically (import hwpx.tools as tools); no importlib.import_module call site or export map changed.

Measured (best of 5, same machine)

before after
hwpx modules loaded by import hwpx 117 97
import hwpx import time ~75 ms ~62 ms
HwpxDocument.new() ~76 ms ~63 ms

The first save still loads all tool modules through hwpx/tools/__init__.py (~9 ms); that and importlib.metadata in ingest are #386. hwpx.oxml (~half of what remains) is required by HwpxDocument.

Tests

  • tests/test_lazy_hwp5_import.py (6) and tests/test_lazy_tools_import.py (5), subprocess-based with -W error. The "no hwpx.hwp5 / no hwpx.tools in sys.modules" tests failed before the change. The identity, .hwp save/reopen, damaged-OLE2 error, __all__/dir() and attribute-access guards pass before and after.
  • Full suite: 5707 passed, 19 skipped, 1 xfailed. ruff, mypy, product boundary, census, hygiene, capabilities --verify, contract docs, size ratchet (97/97), layer-leak guard pass.

🤖 Generated with Claude Code

airmang and others added 3 commits October 3, 2026 01:35
import hwpx loaded hwpx.hwp5 and hwpx.hwp5.errors only to export
Hwp5Error and Hwp5ConversionWarning. Both now live in hwpx.errors, and
hwpx.hwp5.errors re-exports the same classes, so identity and
except hwpx.Hwp5Error are unchanged. HwpxDocument imports
Hwp5ConversionReport for type checking only; the converter already
loaded on first .hwp read or write.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
import hwpx imported all twelve hwpx.tools modules (exporter, viewers,
diff, mail merge, validators) to bind 19 stable names. Those names now
resolve through the module __getattr__ on first access, with no
warning, as the same objects, and are cached in the module globals;
dir(hwpx), from hwpx import * and hwpx.tools keep working. A
TYPE_CHECKING import keeps their static types.

The lookup uses a static import, so the pinned importlib call sites and
export maps are unchanged; only the __getattr__ fingerprint is re-pinned
in scripts/check_product_boundary.py.

import hwpx: 115 -> 97 hwpx modules.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
import hwpx no longer loads hwpx.hwp5 or hwpx.tools, so it loads 97 hwpx
modules instead of 117 (scripts/size_ratchet.py --lower-bound).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@airmang
airmang merged commit ccd39a5 into main Oct 2, 2026
13 checks passed
@airmang
airmang deleted the perf/lazy-hwp5-import branch October 2, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

import hwpx loads 117 modules including hwpx.tools and hwpx.hwp5 it does not need

1 participant