Problem
For HS256 the Python wrapper costs about as much as the native work:
|
µs/call |
_oxyjwt.decode (native) |
1.30 |
oxyjwt.decode (fast path) |
2.14 |
The fast path in PyJWT.decode / decode_complete (python/oxyjwt/api_jwt.py:206-218, :257-272) calls _is_plain_decode (9 args), _require_detached_payload_for_rfc7797, and _validate_claims_default, which calls time.time() and re-checks exp even though Rust already validated it.
Proposal
- Inline
_is_plain_decode and the ".." in token pre-check into the fast path.
- In
_validate_claims_default, only re-check exp in Python when it is not an int (for ints Rust exp <= now_int and Python exp <= time.time() are equivalent; for floats Rust rounds while Python truncates, so that case must stay for PyJWT parity).
- Call
time.time() only when iat or a non-int exp is present.
Acceptance criteria
Files
Branch
issue/<N>-python-fast-path-overhead from dev
Problem
For HS256 the Python wrapper costs about as much as the native work:
_oxyjwt.decode(native)oxyjwt.decode(fast path)The fast path in
PyJWT.decode/decode_complete(python/oxyjwt/api_jwt.py:206-218,:257-272) calls_is_plain_decode(9 args),_require_detached_payload_for_rfc7797, and_validate_claims_default, which callstime.time()and re-checksexpeven though Rust already validated it.Proposal
_is_plain_decodeand the".." in tokenpre-check into the fast path._validate_claims_default, only re-checkexpin Python when it is not anint(for ints Rustexp <= now_intand Pythonexp <= time.time()are equivalent; for floats Rust rounds while Python truncates, so that case must stay for PyJWT parity).time.time()only wheniator a non-intexpis present.Acceptance criteria
oxyjwt.decodeoverhead over native reduced by ≥ 30%tests/test_decode_fast_path.py,tests/test_parity_pyjwt.pygreen; add a test for floatexpboundary parityFiles
python/oxyjwt/api_jwt.pyBranch
issue/<N>-python-fast-path-overheadfromdev