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 cbc356c..fa028cd 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 527c6fb..8fee9d7 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 e5a66f3..e2726ba 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 1a8aba8..9ed17e8 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 e089a7c..3985cc1 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 9410aba..569def0 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 802c111..dcb1ab2 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 569def0..1c43b6b 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 dcb1ab2..b33f30e 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 fa028cd..4f61b63 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