From 6d014d90119f78152f0e306e0605561edbb970f9 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Thu, 16 Jul 2026 01:06:56 +0530 Subject: [PATCH 1/4] chore: prepare json2xml 6.5.0 release --- HISTORY.rst | 11 +++++++ RELEASE_NOTES.md | 42 +++++++++++++++++++++++++++ json2xml/__init__.py | 2 +- pyproject.toml | 4 +-- uv.lock | 68 +++++++++++++++++++++++--------------------- 5 files changed, 91 insertions(+), 36 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index cbc356cf..fa028cdb 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,4 +1,15 @@ +6.5.0 / 2026-07-15 +================== + + * perf: reduce pure Python serializer time by 31.1% on the deterministic 5,000-record benchmark through exact native-type dispatch + * perf: release ``json2xml-rs`` 0.4.2 with hybrid SIMD/word XML escape scanning and a 6.23% benchmark reduction + * fix: preserve numeric, string, dictionary, and sequence subclass behavior across the optimized Python dispatch paths + * test: require exactly 100% statement coverage from the complete Python suite and add dense Rust escape-scanner regression coverage + * chore: require ``json2xml-rs>=0.4.2`` from ``json2xml[fast]`` and validate release wheels before PyPI publication + * docs: add Python and Rust before/after flamegraphs, buffer-capacity measurements, and release performance notes + + 6.4.0 / 2026-07-13 ================== diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 527c6fb1..8fee9d76 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,45 @@ +# json2xml 6.5.0 + +Released 2026-07-15. + +## Highlights + +- Reduced pure Python serializer time by 31.1% on the deterministic 5,000-record workload by using exact native-type dispatch on hot paths while preserving subclass fallbacks. +- Added explicit regression coverage for `Decimal`, `Fraction`, complex, custom `Number`, string, dictionary, list, and tuple subclasses. +- Made the complete Python suite an exact 100% statement-coverage gate: 421 tests cover all 762 statements. +- Updated `json2xml[fast]` to require the published `json2xml-rs>=0.4.2` accelerator. + +## Performance + +All profiles used uv-managed CPython 3.15.0b3 and the same deterministic 5,000-record nested payload. + +| Pure Python metric | Before | After | Change | +| --- | ---: | ---: | ---: | +| Conversion time | 83.0 ms | 57.2 ms | 31.1% lower | +| 20-loop traced time | 8.311 s | 5.782 s | 30.4% lower | +| Function calls | 48.17 million | 30.13 million | 37.4% fewer | +| `isinstance` calls | 11.70 million | 2.80 million | 76.1% fewer | + +The Rust 0.4.2 accelerator released first and improved its paired release median from 6.007 ms to 5.632 ms, or 6.23%, with identical 4,093,244-byte output. Its hybrid scanner keeps dense XML escape input linear, and the measured 16 KiB streaming buffer remains unchanged. + +## Package Versions + +- Python package: `json2xml==6.5.0` +- Rust accelerator: `json2xml-rs==0.4.2` +- Fast install: `pip install "json2xml[fast]==6.5.0"` + +## Profiling Evidence + +- [Python before flamegraph](docs/flamegraphs/python315-before.svg) +- [Python after flamegraph](docs/flamegraphs/python315-after.svg) +- [Rust before flamegraph](docs/flamegraphs/rust-before.svg) +- [Rust after flamegraph](docs/flamegraphs/rust-after.svg) + +## Verification + +The release passed the full cross-platform Python matrix, Rust formatting and Clippy checks, 48 Rust unit tests, 421 Python tests with exactly 100% statement coverage, and install tests against the published Rust 0.4.2 wheels. + + # json2xml_rs 0.4.2 Released 2026-07-15. diff --git a/json2xml/__init__.py b/json2xml/__init__.py index e5a66f36..e2726ba4 100644 --- a/json2xml/__init__.py +++ b/json2xml/__init__.py @@ -2,4 +2,4 @@ __author__ = """Vinit Kumar""" __email__ = "mail@vinitkumar.me" -__version__ = "6.4.0" +__version__ = "6.5.0" diff --git a/pyproject.toml b/pyproject.toml index 1a8aba83..9ed17e81 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "json2xml" -version = "6.4.0" +version = "6.5.0" description = "Simple Python Library to convert JSON to XML" readme = "README.rst" requires-python = ">=3.10" @@ -50,7 +50,7 @@ dev = [ "pygments>=2.20.0", "xmltodict>=0.12.0", ] -fast = ["json2xml-rs>=0.4.1"] +fast = ["json2xml-rs>=0.4.2"] [tool.pytest.ini_options] testpaths = ["tests"] diff --git a/uv.lock b/uv.lock index e089a7cc..3985cc16 100644 --- a/uv.lock +++ b/uv.lock @@ -147,7 +147,7 @@ wheels = [ [[package]] name = "json2xml" -version = "6.4.0" +version = "6.5.0" source = { editable = "." } dependencies = [ { name = "defusedxml" }, @@ -171,7 +171,7 @@ fast = [ requires-dist = [ { name = "coverage", marker = "extra == 'dev'" }, { name = "defusedxml" }, - { name = "json2xml-rs", marker = "extra == 'fast'", specifier = ">=0.4.1" }, + { name = "json2xml-rs", marker = "extra == 'fast'", specifier = ">=0.4.2" }, { name = "pygments", marker = "extra == 'dev'", specifier = ">=2.20.0" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.4.1" }, { name = "pytest-cov", marker = "extra == 'dev'" }, @@ -183,39 +183,41 @@ provides-extras = ["dev", "fast"] [[package]] name = "json2xml-rs" -version = "0.4.1" +version = "0.4.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9b/48/bfce5499488c57a1d2dc07c35ade98edd0b4f385828e537132e220a703ab/json2xml_rs-0.4.1.tar.gz", hash = "sha256:dbf19cfe93cee3c245cffa64668fde9380d85a33da716bfbd43a9b41bc2c7e6e", size = 8914, upload-time = "2026-07-13T17:06:28.075Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4b/df/24550bb7664ef8aba5e6ea8aa6f6f29dffa4bcab8a4415d492cf9130c499/json2xml_rs-0.4.2.tar.gz", hash = "sha256:be63b26a8885002dcdd2d11d8685251224370cc1a45073ff226a5a6ecf15d508", size = 9860, upload-time = "2026-07-15T19:29:16.138Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/ac/37/0585f1b0fae8a4e0c82d5312e52f41a0bb1d09a33dbf08f94a384105a7ed/json2xml_rs-0.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa4d185d6786aca2f7458eaa4ce52594c05902d0f9676b1566e862962ea75e11", size = 229081, upload-time = "2026-07-13T17:05:40.019Z" }, - { url = "https://files.pythonhosted.org/packages/1f/a4/a2394e43e7e861b4b14784b70ecc229d8a06a1dede555a172105d2bf45b2/json2xml_rs-0.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d7bce8bb920e942f6a4283d44c81a168e65fc7ab9575a9b2435ef22a0d60b8d", size = 230950, upload-time = "2026-07-13T17:05:41.934Z" }, - { url = "https://files.pythonhosted.org/packages/a6/11/4ad4e7f031baa699cc56e536e33a9d8840e65008bb93f25ca61dcd942a37/json2xml_rs-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:90824b0754f9a2e6f252b867848605973ebdcf6ffc0e697aa4d670cc991e7323", size = 117686, upload-time = "2026-07-13T17:05:43.422Z" }, - { url = "https://files.pythonhosted.org/packages/56/73/d82523fcd6f61878d86f87f8101cf3943c9e0e3c964634f1d13126088f97/json2xml_rs-0.4.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:74984dd97a1048984e396013afdc13c1110c506f5fd4f2d29f694290572a3f0d", size = 222412, upload-time = "2026-07-13T17:05:45.008Z" }, - { url = "https://files.pythonhosted.org/packages/1c/d8/3dd97f1da5898d30b2ed58caf4e5d99f45d7b8888914e8f61e0819ea3dc1/json2xml_rs-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c4b315c5d2c24b2897f136bb1dda8f91e2ab764312ac534a3af34e50d2b2fadc", size = 213292, upload-time = "2026-07-13T17:05:46.726Z" }, - { url = "https://files.pythonhosted.org/packages/68/9e/c7363a6042bde846d8947312cfa8877a01e83a6977fa43fd80f4eb29f0cf/json2xml_rs-0.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2880d03274cd67564d9699c46615e79efea1a6e0bf9f06c5b4e6f57fbfc61ff6", size = 228754, upload-time = "2026-07-13T17:05:48.192Z" }, - { url = "https://files.pythonhosted.org/packages/95/5a/9c0a539bb55c55d089e3d70e59fe04628333017d792681e82b35e9fe902d/json2xml_rs-0.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5084cbb7ba964e3cb0a16eba2e84caba72d73fcc17c53cc9f5ab2491bc4960a5", size = 230828, upload-time = "2026-07-13T17:05:49.699Z" }, - { url = "https://files.pythonhosted.org/packages/20/d9/31276b02aaecdd57b8a25c4e645575e1645e12fee13329a42d5daf339dbf/json2xml_rs-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:7b6e57ee8877472d20c788a271f2432b0ae4728eeced6ff935945e71c24f2a34", size = 117578, upload-time = "2026-07-13T17:05:51.202Z" }, - { url = "https://files.pythonhosted.org/packages/37/c2/83e72a246053a4cd0491c698f8767d339e0296c06bcd177fac3d371ea3b6/json2xml_rs-0.4.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:cfc9e004f4e68b68baec94ba4cb3992c41559cd87de2043191752cf485dee365", size = 221538, upload-time = "2026-07-13T17:05:52.614Z" }, - { url = "https://files.pythonhosted.org/packages/e1/92/334cb874cfdfe6be0a116f390deb98a18bd5a2b1bbc21e2f8260284f0401/json2xml_rs-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fb985dc9c85c8827fd0fa41461d1911b3d8727c7af89a8ad4fe75add962f9a1c", size = 212626, upload-time = "2026-07-13T17:05:54.253Z" }, - { url = "https://files.pythonhosted.org/packages/f6/69/57f2f2a5fcd9ff0868e51722c3af23f598d9c8f56c71a77e6e5938eae178/json2xml_rs-0.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:216e92edd74eed0fd23afc031d45f2c2d923601ad8ced8cc22716e4b2a9284d3", size = 228511, upload-time = "2026-07-13T17:05:55.739Z" }, - { url = "https://files.pythonhosted.org/packages/eb/eb/7b45931346c862b65ec5076dcccb77f4b2f90bb42e72d13a3f108fddbc95/json2xml_rs-0.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5483f085e0b99b0cd540c61bf3cc226d2e650fcf48dccdee98fb7d5ea72de5", size = 230760, upload-time = "2026-07-13T17:05:57.802Z" }, - { url = "https://files.pythonhosted.org/packages/b6/a0/a75b82cff8fcd5054b5f0f7a95c77f3a002650b8ef4bbc77b52aa98f97f4/json2xml_rs-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:c25d6ecd31e3457ad0046023242be1443ede4fc0061dabfa06b9a161b047af51", size = 116141, upload-time = "2026-07-13T17:05:59.223Z" }, - { url = "https://files.pythonhosted.org/packages/11/db/5adff090727f864c37d113ab47d8000e5e1978efe06a905fca0239ba9490/json2xml_rs-0.4.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:bdfe8c44dfe7745ef3dbb0eb73db92daf65621392f2343fac73e07bad68eaa22", size = 221748, upload-time = "2026-07-13T17:06:00.698Z" }, - { url = "https://files.pythonhosted.org/packages/ab/ec/8c69c2604d8a2c11421b75ff6c8e91b0ded55d6acf9553ffcb333df828ab/json2xml_rs-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4f05dfb166ca6743d6cd4831f012f37ba9935b19c43df7e4d229bbb83f6751dd", size = 212755, upload-time = "2026-07-13T17:06:02.326Z" }, - { url = "https://files.pythonhosted.org/packages/c8/e3/e317794dfdb40c28b9bda7d9bb1d96fdedc072b217ab3d56a0ee3877ab2a/json2xml_rs-0.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a8c4dac06e64ba0fed8c25256a56de1fcec6a96f9a556f7718b0ee474fa177b8", size = 228452, upload-time = "2026-07-13T17:06:04.012Z" }, - { url = "https://files.pythonhosted.org/packages/4a/7b/4c55143bf8c14390fe9f4ab89e4c25ada1aca73392e5c6d9c1836722ec29/json2xml_rs-0.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d12d81cf4f062f5c00d35e2df9850bed5658dba346951e48a7bbef3b345d7ce", size = 230860, upload-time = "2026-07-13T17:06:05.419Z" }, - { url = "https://files.pythonhosted.org/packages/93/16/c154da9ad023d3442bd4cc9aa190d5893ba704220546060c9fcaeb9f86b8/json2xml_rs-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:1cbf1ce9e18d6a72c0d39e211e21534358e32f37e63a1a8a894850f1cdd690c6", size = 116324, upload-time = "2026-07-13T17:06:07.05Z" }, - { url = "https://files.pythonhosted.org/packages/5b/3f/a7a67c3a153eb0365a745164dda65878f829ccdb4938b5c03ee2896dd4d2/json2xml_rs-0.4.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:e7875b727f5cc610530189b4f89530dd167d27c67c9b69945e52fc3e8ab51889", size = 221559, upload-time = "2026-07-13T17:06:08.411Z" }, - { url = "https://files.pythonhosted.org/packages/fb/d1/a6875d15decdfa0ae99f9343620681303c0acfcaf143f65a33df373b2864/json2xml_rs-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9ea5f30c8975dcc3707679fe604b0a942865e586320972b38d7d2be1de606c63", size = 212526, upload-time = "2026-07-13T17:06:10.003Z" }, - { url = "https://files.pythonhosted.org/packages/8f/67/78cda0d6327eb070df0c73463f70a1e3d7e3c146a8b2e086b1954890a0a5/json2xml_rs-0.4.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df3a0574823d32d723ed9859e37ebe5214698fb7e44c064d0d981c61bb049c85", size = 228134, upload-time = "2026-07-13T17:06:11.435Z" }, - { url = "https://files.pythonhosted.org/packages/c6/77/49c4753c215b957e11b6ef09203208f8f89d63c6aeebdc12c11869070c1f/json2xml_rs-0.4.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b0c102cd456ef0e8df86866036b8c8ad8e8f9c849e3e4888d04ce84a1d74af87", size = 230656, upload-time = "2026-07-13T17:06:12.998Z" }, - { url = "https://files.pythonhosted.org/packages/c7/ea/04a244cab81d45eab1dd1390216a3b9eb7dc4e13dd35c725a23d9731faeb/json2xml_rs-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:3d00c5494ef307b4c30e0db38e7c4c06d62f79325292b9a7d7500a4d278fbfac", size = 116198, upload-time = "2026-07-13T17:06:14.474Z" }, - { url = "https://files.pythonhosted.org/packages/f9/c7/bb4504020a56f4261b4ab1c154fc6c1d2ecd12f3efc80b266801a7fb8fd2/json2xml_rs-0.4.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88b38b46d51402d2c84de2dade3f1f22fe734d150888459b94c98592215468bd", size = 227103, upload-time = "2026-07-13T17:06:15.855Z" }, - { url = "https://files.pythonhosted.org/packages/3c/5f/ebaa62c9638bd9d24deca908b1669b7a8116f8bdc15ac9af7cd3c85531fc/json2xml_rs-0.4.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a617ce050a24429b620c830a9c63696c495c49c850e3e3f285a1db4ec3c15c4b", size = 229906, upload-time = "2026-07-13T17:06:17.278Z" }, - { url = "https://files.pythonhosted.org/packages/5e/61/399bfd831209e8048ebcf682fbb5dfe77d54a88cc2101eb482c4d09ee87e/json2xml_rs-0.4.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad559dd9a5347c737ae836e6ec099ae4809de3047f966f29cbb8057912e71c8a", size = 230209, upload-time = "2026-07-13T17:06:18.768Z" }, - { url = "https://files.pythonhosted.org/packages/5d/b6/029a61bda8b99acdb55f9b9fe20bd8fe3aea640c188441314619fc8d57bf/json2xml_rs-0.4.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7cc455557f2513b16180d62f0d9e7beec0c1443aa8002ed24e1a991068de0ae4", size = 229388, upload-time = "2026-07-13T17:06:20.397Z" }, - { url = "https://files.pythonhosted.org/packages/00/2b/c35ad343890bafa34e23679036e3359f3ed926a654b2ffa8a6710708ca66/json2xml_rs-0.4.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e20248aff6da25605614cb79ea101397a06adbcbdcdfa2086ac96776e397f9a9", size = 230599, upload-time = "2026-07-13T17:06:25.287Z" }, - { url = "https://files.pythonhosted.org/packages/81/a2/9e04945abd24b1b864818cd493e013f0f27e13b44d26be592e0c9e9ecc4d/json2xml_rs-0.4.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9552062589e85bb8ead1b68355d481e431bb95954e121abf9675465257f81bc", size = 232677, upload-time = "2026-07-13T17:06:26.809Z" }, + { url = "https://files.pythonhosted.org/packages/44/71/5d1bbf681d61a86232396568e9b117d4b399b6fd85f4242022e01953640e/json2xml_rs-0.4.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:7ad92712ed0c61da1287d8af1ed0ea7d29b1db5677cfee962b81b89161a7d3d5", size = 228754, upload-time = "2026-07-15T19:28:30.647Z" }, + { url = "https://files.pythonhosted.org/packages/8d/22/f5c4809a28861783162ab2379237330dc7238c6aefb58126fe7da9a7f520/json2xml_rs-0.4.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ff28b3c5ae09e0d716604b5fad0cd202b907f06cd801d1ae5fff505b5d898c64", size = 216101, upload-time = "2026-07-15T19:28:32.063Z" }, + { url = "https://files.pythonhosted.org/packages/02/6b/180d0fe7e544ad8e80730bc8f9e2b0036be5d7c494ad6cf3b4592bdaa685/json2xml_rs-0.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f88fac51361577cdbaedc00869524a2d52cf31d8ca3a725ffa9cd5e03503b8", size = 232071, upload-time = "2026-07-15T19:28:33.324Z" }, + { url = "https://files.pythonhosted.org/packages/81/19/f76b390ea40d5ca49e3bae3a1ba237e644bf675c0970ccf45385f7d28b4e/json2xml_rs-0.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7335ca3eda98f64f55afc5341105aecb5280f76ade5801f46c587b2ec5a18e26", size = 236773, upload-time = "2026-07-15T19:28:34.596Z" }, + { url = "https://files.pythonhosted.org/packages/a8/0b/44c03dc291ad98340c506ea818be397cf1737b6f00453da7c3805cc88aa9/json2xml_rs-0.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:8d9aa18496e198a3dd4040d038b1fd5141419a474ccdd58ab6480980b70447cd", size = 122693, upload-time = "2026-07-15T19:28:36.051Z" }, + { url = "https://files.pythonhosted.org/packages/1d/a8/fea4fcd7c24679f76cbebbbddef33d48e96b069bb4864ed9bb08be9d922c/json2xml_rs-0.4.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:cf0e15a6125bd5898a5df4e58b3931bbdf30f6e2c1bd3a7c068372bed50a4734", size = 228456, upload-time = "2026-07-15T19:28:37.474Z" }, + { url = "https://files.pythonhosted.org/packages/eb/53/963029983dadaa9ddffd623abe667f79c0f547b7fb62127df9208bbeb3a1/json2xml_rs-0.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bff5ad506356af0ff3d8bf2677918c00d9c94f06891c871f16f71f0fc257025e", size = 215993, upload-time = "2026-07-15T19:28:38.994Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4e/039c16db1400e5196accb8be1bf55e5f0c36ff8b0d056d0ce235a2972179/json2xml_rs-0.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570bcd5708adda2bc49731776ee6a638a7aab39b53937afcc15a53ef3a795edd", size = 231705, upload-time = "2026-07-15T19:28:40.424Z" }, + { url = "https://files.pythonhosted.org/packages/da/1c/33e64336547a93998f6dac21795495a1c3571de4dadcbcaaf37b103c691a/json2xml_rs-0.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59dee86ddf29793b8abd409244d04f99f46686f4e74214869e90322f826b5d3d", size = 236573, upload-time = "2026-07-15T19:28:41.908Z" }, + { url = "https://files.pythonhosted.org/packages/46/7e/4d8eb642839f5f930ea0dfa1dffbbcc8da1161e66da41c054bd217e5b252/json2xml_rs-0.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:a56cce17b6d7844aa34cab93ecab5e8eeb38d07d31c55b530a5825a2c89e4f04", size = 122676, upload-time = "2026-07-15T19:28:43.313Z" }, + { url = "https://files.pythonhosted.org/packages/9f/03/4e4150adf7ba5cf2ea87bc9767113840407041b158283bbdacf5c7983d56/json2xml_rs-0.4.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b8ae72d10ef65d938d04f7e86ef3c09ffb033c0646a9dbba298db8908147ccfd", size = 227337, upload-time = "2026-07-15T19:28:44.471Z" }, + { url = "https://files.pythonhosted.org/packages/08/2b/7b0489f5a0a656ad53c3ef2771a9ccca13d26e18b70d1fa93a7ab98b5a4c/json2xml_rs-0.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8da6b104093281da9a83ed3de14738c4a4b344a9fc929b04d227a109de119cae", size = 215399, upload-time = "2026-07-15T19:28:45.731Z" }, + { url = "https://files.pythonhosted.org/packages/ab/13/8f21655528e7e53c2bcad443520a9b205877871d43681a33953ed3d2e750/json2xml_rs-0.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d6d250c7934a98cdddf50470a96409929567bfa58cd77b3f141bce887a85d8", size = 231336, upload-time = "2026-07-15T19:28:47.248Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fb/f7966a2d48b1c5d5a25b036ba8e7ed3ccc410630ec12e40d9ca0b5f0fb65/json2xml_rs-0.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d7b8a46e0011621d380079d32f63c5cc37fa87832587752c2ba0f2b36524242", size = 236633, upload-time = "2026-07-15T19:28:48.557Z" }, + { url = "https://files.pythonhosted.org/packages/74/59/6e52737b557fb81974fcf1b5454d9344c56db5d25948e0aac8efe2e1c502/json2xml_rs-0.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:168b875b2bd8b57952907b042e68802b82fca67124abe9447133fc55c95cc2dd", size = 121111, upload-time = "2026-07-15T19:28:49.915Z" }, + { url = "https://files.pythonhosted.org/packages/04/28/f5f108b1e54a086463d3ea512b748d86f116a3d098b51772c2e278e452c1/json2xml_rs-0.4.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:953620b10c9f5a1e8e72c51c3083d8afacb4be1d31450329ba225068f8276edc", size = 227522, upload-time = "2026-07-15T19:28:51.197Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/d22d3c95ccfa59b7836b676fbc43a82ef7457cefa32673a2052048368c0f/json2xml_rs-0.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bf71582f53627e815f796bdac6ec571089508ec2b0fb1945fff88ca30eb3c8ea", size = 215557, upload-time = "2026-07-15T19:28:52.518Z" }, + { url = "https://files.pythonhosted.org/packages/56/14/cbbd821e1abaa682255540563f589a723eb4a6701a5eb2b7ee889a9a64f1/json2xml_rs-0.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b049f77c4299ee911f467d9e3b3a557500373a614e8d3d882ec825d9b2ae058e", size = 231366, upload-time = "2026-07-15T19:28:53.816Z" }, + { url = "https://files.pythonhosted.org/packages/75/fa/0b8d7e4e929550294af87f074eec265102c5095106bec924a698437deb79/json2xml_rs-0.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c191f9f3480aa83ef265034f51802def53ab59dc9fd17b071c0bb67d4493a8e", size = 236723, upload-time = "2026-07-15T19:28:55.302Z" }, + { url = "https://files.pythonhosted.org/packages/92/85/13a730c16684e43abad6613573e560fbc26eb84009f9f67f9fe7d1227f71/json2xml_rs-0.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:72ab02d5cc23ad2539d8d3ab14af8d58c3179bb2e6f7843a82206e615664f031", size = 121314, upload-time = "2026-07-15T19:28:56.595Z" }, + { url = "https://files.pythonhosted.org/packages/72/6f/01af728bb5003b7960a4f9e427fc893ef976e189e674fb55bda25c9f71e8/json2xml_rs-0.4.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:fe2ee5d59fb7c5d805692dce80cde4aa21a535e64eb71979806b5d981af3e417", size = 227266, upload-time = "2026-07-15T19:28:57.776Z" }, + { url = "https://files.pythonhosted.org/packages/90/32/e339f6dc9a727e6f50208e0f5e083677f9fab96b3e5ba8cb8a9a48b460eb/json2xml_rs-0.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8f9a99f05fda5844ae3e5858d412e947c714b10881431e777ed826a7ca503c46", size = 215275, upload-time = "2026-07-15T19:28:59.389Z" }, + { url = "https://files.pythonhosted.org/packages/bc/e1/145fe6dbb4312bfb867d28db44d61afe5f785ca8fbb5ee586c91eab3069d/json2xml_rs-0.4.2-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0eb17ed09d57c47e725321321fd93e91b9a29b3e84ee5b32d9e8ca67ce154d4", size = 231013, upload-time = "2026-07-15T19:29:00.744Z" }, + { url = "https://files.pythonhosted.org/packages/41/b2/2e5b6714e6de31e9eba0df50a5fee154ccf5359fd7022b2cf55b7c824a89/json2xml_rs-0.4.2-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e50cbe379830783504fe862ebc9ee44f5ca9eb41fd0d611f6b99dc166bff8560", size = 236491, upload-time = "2026-07-15T19:29:02.143Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b4/77cc6deb8a26f9b837bc1b4d358ff03317e2e98796ba0b742d6de7fd8063/json2xml_rs-0.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:c8472e19f6506fe8e98872136fc6aca062853d03ada8e601343b5c7afbd2ec04", size = 121118, upload-time = "2026-07-15T19:29:03.657Z" }, + { url = "https://files.pythonhosted.org/packages/9b/a3/184a57d724c31db0fa6beffe2fb1eb64d37814e595ea3a1eed4f21cb6cd7/json2xml_rs-0.4.2-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c4d86932ede1ce290724f48f9ac1bbb2413d766b70d0c824257be094c582734", size = 230020, upload-time = "2026-07-15T19:29:05.087Z" }, + { url = "https://files.pythonhosted.org/packages/1b/93/0f162a8b214c9818fa207bb7f5b92677a33f7f5eb445b0195bd17f2bd855/json2xml_rs-0.4.2-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:329447da89fd6af6047604a5f121f243ac8a88ea8f094472732c317f552d9d20", size = 235688, upload-time = "2026-07-15T19:29:06.456Z" }, + { url = "https://files.pythonhosted.org/packages/6c/2f/3014c23537830778357dde18adcc0fa28dfee1fe3a54b82833cbdaf5e05b/json2xml_rs-0.4.2-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30275441b6be1a591948195e5e3fc1130bf9b0a7cf49d8d9fb44290706eff8be", size = 235884, upload-time = "2026-07-15T19:29:07.916Z" }, + { url = "https://files.pythonhosted.org/packages/9c/85/bf705b01d3d1b1fbaeafb9e7b93fe6394d8114ded6a73bf3d36f1355e64d/json2xml_rs-0.4.2-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5b67c1199080bdba5d7c7a2bbd13e26a1aa990d3af19bcd1ea3d891e5ebf327f", size = 235147, upload-time = "2026-07-15T19:29:09.328Z" }, + { url = "https://files.pythonhosted.org/packages/11/b8/2b6539769b8253c103de8e643dcf351f90faa473be1a3026aa868f2122b0/json2xml_rs-0.4.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78c08daa09f46556282603d8ee480062a6d1111d52f3baedd5e7262195de217e", size = 233522, upload-time = "2026-07-15T19:29:13.55Z" }, + { url = "https://files.pythonhosted.org/packages/65/18/cb9b4dba617c8b292dd34674bb8a9a2968e5168e533d64e45d491676f50c/json2xml_rs-0.4.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:826bd78c84b57d0990d1e117b0057b1e47349f4aa25168db7f3c5d6668a33176", size = 238401, upload-time = "2026-07-15T19:29:14.989Z" }, ] [[package]] From 9f0b3fd8435e1dcb4b7124ef8e9ff6f79df91cef Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Thu, 16 Jul 2026 01:08:31 +0530 Subject: [PATCH 2/4] ci: authorize TestPyPI trusted publishing --- .github/workflows/publish-to-test-pypi.yml | 5 +++-- lat.md/architecture.md | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml index 9410abad..569def03 100644 --- a/.github/workflows/publish-to-test-pypi.yml +++ b/.github/workflows/publish-to-test-pypi.yml @@ -8,6 +8,7 @@ on: permissions: contents: read + id-token: write jobs: build-n-publish: @@ -40,5 +41,5 @@ jobs: with: user: __token__ password: ${{ secrets.TEST_PYPI_API_TOKEN }} - repository_url: https://test.pypi.org/legacy/ - skip_existing: true + repository-url: https://test.pypi.org/legacy/ + skip-existing: true diff --git a/lat.md/architecture.md b/lat.md/architecture.md index 802c111d..dcb1ab24 100644 --- a/lat.md/architecture.md +++ b/lat.md/architecture.md @@ -72,7 +72,7 @@ Runtime dependencies are declared in `pyproject.toml` and mirrored by `uv.lock`; GitHub Actions workflows run with read-only tokens by default and use full SHA pins so third-party action updates are explicit. -The `.github/workflows/` files declare the minimum `permissions:` scopes needed by each workflow, with CodeQL retaining `security-events: write` for result upload. Action references are pinned to immutable commits with the upstream tag retained in comments for reviewability, and `.github/dependabot.yml` checks the `github-actions` ecosystem weekly so those pins do not silently age. The Python test matrix pins its PyPy 3.11 job to an explicit PyPy release so CI keeps exercising the intended CPython 3.11.15-compatible runtime instead of silently drifting with runner cache updates. It also exercises regular CPython 3.15.0b3 while leaving that beta's free-threaded builds out of CI until the runner support is less brittle. +The `.github/workflows/` files declare the minimum `permissions:` scopes needed by each workflow, with CodeQL retaining `security-events: write` for result upload and TestPyPI retaining `id-token: write` for trusted publishing. Action references are pinned to immutable commits with the upstream tag retained in comments for reviewability, and `.github/dependabot.yml` checks the `github-actions` ecosystem weekly so those pins do not silently age. The Python test matrix pins its PyPy 3.11 job to an explicit PyPy release so CI keeps exercising the intended CPython 3.11.15-compatible runtime instead of silently drifting with runner cache updates. It also exercises regular CPython 3.15.0b3 while leaving that beta's free-threaded builds out of CI until the runner support is less brittle. Rust extension CI triggers on Rust sources, Rust integration tests, and Python fast-path files such as [[json2xml/backend_selector.py]] and [[json2xml/dicttoxml_fast.py]]. That keeps native backend tests attached to the Python dispatch code that decides whether the accelerator is used. From 44c376e8e3f1dc6b537392e338c75a258b0324b0 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Thu, 16 Jul 2026 01:10:55 +0530 Subject: [PATCH 3/4] ci: validate release artifacts before optional TestPyPI upload --- .github/workflows/publish-to-test-pypi.yml | 16 ++++++++++++++-- lat.md/architecture.md | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/workflows/publish-to-test-pypi.yml b/.github/workflows/publish-to-test-pypi.yml index 569def03..1c43b6b7 100644 --- a/.github/workflows/publish-to-test-pypi.yml +++ b/.github/workflows/publish-to-test-pypi.yml @@ -5,6 +5,13 @@ on: branches: - 'develop' - 'release/**' + workflow_dispatch: + inputs: + publish: + description: 'Publish the validated artifacts to TestPyPI' + required: false + default: false + type: boolean permissions: contents: read @@ -12,7 +19,7 @@ permissions: jobs: build-n-publish: - name: Build and publish Python 🐍 distributions 📦 to TestPyPI + name: Build Python 🐍 distributions 📦 and optionally publish to TestPyPI runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -21,11 +28,12 @@ jobs: with: python-version: '3.12' - - name: Install pypa/build + - name: Install packaging tools run: >- python -m pip install build + twine --user - name: Build a binary wheel and a source tarball run: >- @@ -36,7 +44,11 @@ jobs: --outdir dist/ . + - name: Check distribution metadata + run: python -m twine check dist/* + - name: Publish distribution 📦 to Test PyPI + if: github.event_name == 'workflow_dispatch' && inputs.publish uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: user: __token__ diff --git a/lat.md/architecture.md b/lat.md/architecture.md index dcb1ab24..b33f30ed 100644 --- a/lat.md/architecture.md +++ b/lat.md/architecture.md @@ -72,7 +72,7 @@ Runtime dependencies are declared in `pyproject.toml` and mirrored by `uv.lock`; GitHub Actions workflows run with read-only tokens by default and use full SHA pins so third-party action updates are explicit. -The `.github/workflows/` files declare the minimum `permissions:` scopes needed by each workflow, with CodeQL retaining `security-events: write` for result upload and TestPyPI retaining `id-token: write` for trusted publishing. Action references are pinned to immutable commits with the upstream tag retained in comments for reviewability, and `.github/dependabot.yml` checks the `github-actions` ecosystem weekly so those pins do not silently age. The Python test matrix pins its PyPy 3.11 job to an explicit PyPy release so CI keeps exercising the intended CPython 3.11.15-compatible runtime instead of silently drifting with runner cache updates. It also exercises regular CPython 3.15.0b3 while leaving that beta's free-threaded builds out of CI until the runner support is less brittle. +The `.github/workflows/` files declare the minimum `permissions:` scopes needed by each workflow, with CodeQL retaining `security-events: write` for result upload and TestPyPI retaining `id-token: write` for explicit trusted-publishing runs. Release-branch pushes build distributions and run Twine checks; TestPyPI upload is a manual opt-in because that external registry requires separate publisher configuration. Action references are pinned to immutable commits with the upstream tag retained in comments for reviewability, and `.github/dependabot.yml` checks the `github-actions` ecosystem weekly so those pins do not silently age. The Python test matrix pins its PyPy 3.11 job to an explicit PyPy release so CI keeps exercising the intended CPython 3.11.15-compatible runtime instead of silently drifting with runner cache updates. It also exercises regular CPython 3.15.0b3 while leaving that beta's free-threaded builds out of CI until the runner support is less brittle. Rust extension CI triggers on Rust sources, Rust integration tests, and Python fast-path files such as [[json2xml/backend_selector.py]] and [[json2xml/dicttoxml_fast.py]]. That keeps native backend tests attached to the Python dispatch code that decides whether the accelerator is used. From 11f6e8f3e3609913bd9b3e69c9863af19d6bf456 Mon Sep 17 00:00:00 2001 From: Vinit Kumar Date: Thu, 16 Jul 2026 01:15:37 +0530 Subject: [PATCH 4/4] docs: align XML escape scanning terminology --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index fa028cdb..4f61b632 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -5,7 +5,7 @@ * perf: reduce pure Python serializer time by 31.1% on the deterministic 5,000-record benchmark through exact native-type dispatch * perf: release ``json2xml-rs`` 0.4.2 with hybrid SIMD/word XML escape scanning and a 6.23% benchmark reduction * fix: preserve numeric, string, dictionary, and sequence subclass behavior across the optimized Python dispatch paths - * test: require exactly 100% statement coverage from the complete Python suite and add dense Rust escape-scanner regression coverage + * test: require exactly 100% statement coverage from the complete Python suite and add dense Rust XML escape scanning regression coverage * chore: require ``json2xml-rs>=0.4.2`` from ``json2xml[fast]`` and validate release wheels before PyPI publication * docs: add Python and Rust before/after flamegraphs, buffer-capacity measurements, and release performance notes