You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Skip the shadow copy when lxml links the same libxml2
The import guard only lets matched libxml2 versions run, and for them the
old direct behavior — xmlsec mutating lxml's nodes — is safe; that is
what shipped for years. Yet every converted function paid the full shadow
round-trip (serialize with lxml, re-parse, dump, re-parse with lxml) even
in that case, four serializations per call for zero safety benefit. That
is noise for the small xmlSecTmpl* trees, but would become a real
regression when the pattern reaches sign/encrypt on whole documents.
Make Begin/End dual-path, decided once at import: on matched versions
Begin aliases the live _c_node (no copy) and End just wraps the node
xmlsec returned, machine-identical to the pre-shadow code; the shadow
round-trip activates only under a mismatch — or when PYXMLSEC_FORCE_SHADOW
is set, which CI now uses to run the suite a second time so the shadow
path stays exercised on matched builds. Call sites cannot tell the
difference, and every function converted later inherits both paths.
While in there, resolve lxml.etree's tostring/fromstring once at module
init instead of importing lxml.etree on every shadow crossing.
Benchmark (matched static build, create + add_reference + add_transform +
ensure_key_info per iteration): fast path 8.6us vs shadow 72.3us, ~8x.
Validated three ways, with byte-identical template output across the two
paths: full suite on the fast path and on PYXMLSEC_FORCE_SHADOW=1 (matched
static build, 300 passed / 6 skipped each), and full suite under a real
2.14 vs 2.15 mismatch (288 passed, 6 skipped).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
0 commit comments