Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .github/workflows/build-rust-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,11 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

# macos-latest no longer preinstalls 3.10, so make the oldest supported
# interpreter visible to maturin's --find-interpreter discovery.
- name: Add Python 3.10 to the build toolcache
# Make the oldest supported interpreter visible to maturin discovery.
- name: Add Python 3.11 to the build toolcache
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.10'
python-version: '3.11'

- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
Expand Down Expand Up @@ -188,10 +187,10 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
python-version: ['3.11', '3.12', '3.13', '3.14']
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
include:
- os: ubuntu-latest
python-version: '3.15.0-beta.3'
python-version: '3.15.0'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
matrix:
# Pin PyPy 3.11 to the 7.3.23 build so CI keeps exercising the
# CPython 3.11.15-compatible runtime documented in BENCHMARKS.md.
python-version: [pypy-3.10, pypy-3.11-v7.3.23, '3.10', '3.11', '3.12', '3.13', '3.14', '3.14t', '3.15.0-rc.1']
python-version: [pypy-3.11-v7.3.23, '3.11', '3.12', '3.13', '3.14', '3.14t', '3.15.0']
os: [
ubuntu-latest,
windows-latest,
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
python-version: ['3.11', '3.12', '3.13', '3.14']
include:
- os: ubuntu-latest
python-version: '3.15.0-beta.3'
python-version: '3.15.0'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,4 @@ repos:
rev: v3.19.1
hooks:
- id: pyupgrade
args: [--py310-plus]
args: [--py311-plus]
2 changes: 1 addition & 1 deletion BENCHMARKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ below use `uv` because it keeps Python setup reproducible.

| Tool | Required for | Notes |
|------|--------------|-------|
| Python 3.10+ | Pure Python benchmarks | The published run used Python 3.14.4. |
| Python 3.11+ | Pure Python benchmarks | The published run used Python 3.14.4. |
| Rust toolchain | Rust extension benchmarks | Needed only when building `json2xml_rs` locally. |
| maturin | Rust extension benchmarks | Builds and installs the PyO3 extension. |
| json2xml-go | Go CLI benchmarks | Must be on `PATH` as `json2xml-go`. |
Expand Down
2 changes: 1 addition & 1 deletion docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ For contributing to json2xml, set up a development environment:
Requirements
------------

json2xml requires Python 3.10 or later and depends on:
json2xml requires Python 3.11 or later and depends on:

* ``defusedxml`` - For secure XML parsing
* ``urllib3`` - For fetching JSON from URLs
Expand Down
8 changes: 6 additions & 2 deletions lat.md/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,18 +36,22 @@ The Rust extension crate targets the Rust 2024 edition and pins `rust-version` t

The Cargo feature layout separates normal Rust/PyO3 tests from extension-module builds. `cargo test` uses the default `python` feature without extension-module linking, while maturin enables the `extension-module` feature for wheel builds.

Release and CI workflows install the pinned Rust toolchain before building wheels or running Rust checks, so hosted runners do not silently use an older default compiler. The macOS release build also provisions Python 3.10 explicitly so maturin emits wheels for the oldest supported interpreter even when runner images omit it.
Release and CI workflows install the pinned Rust toolchain before building wheels or running Rust checks, so hosted runners do not silently use an older default compiler. The macOS release build also provisions Python 3.11 explicitly so maturin emits wheels for the oldest supported interpreter even when runner images omit it.

## Development checks

Make-based lint, type-check, and Python test targets run through uv's locked development environment so results do not depend on globally installed tools or optional extensions.

The pre-commit pyupgrade hook uses `--py311-plus` to keep Python source syntax aligned with the minimum supported interpreter.

The shared `UV_RUN` command installs the complete `dev` extra from `uv.lock`. `pyproject.toml` and `uv.lock` are the only dependency declaration, so no pinned requirements files can drift from them. The type-check target overlays `ty`, while pytest configuration has one source in `pyproject.toml` and enforces 100% coverage for bare local runs. Rust extension integration stays in its dedicated workflow.

## Release packaging

Package releases keep the Python wrapper and Rust accelerator requirements aligned and use `uv build`/`uv publish` so optional fast installs receive compatible wheels.

Both the Python package and Rust accelerator require Python 3.11 or later. CI covers CPython and PyPy 3.11+; older interpreter jobs are excluded.

The Python package version lives in `pyproject.toml` and `json2xml/__init__.py`. The Rust accelerator version lives in both `rust/Cargo.toml` and `rust/pyproject.toml`, and the Python `fast` extra should require the Rust package version that contains any expected accelerator behavior. Publish and verify the Rust package before updating that Python requirement and lockfile. Release notes live in `HISTORY.rst`, with the current release also summarized in `RELEASE_NOTES.md` for tag and PyPI copy.

## Performance benchmarks
Expand Down Expand Up @@ -100,7 +104,7 @@ Dependabot checks the root and documentation Python dependency manifests weekly,

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 explicit trusted-publishing runs. The Python test job also scopes `id-token: write` and `FLAKINESS_PROJECT` to its pytest matrix so the flakiness reporter can authenticate through GitHub OIDC without exposing that permission to lint. Release-branch pushes build distributions and runs 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 all CodeQL phases use the same release commit because mixed action versions reject each other's configuration. `.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.0rc1 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. The Python test job also scopes `id-token: write` and `FLAKINESS_PROJECT` to its pytest matrix so the flakiness reporter can authenticate through GitHub OIDC without exposing that permission to lint. Release-branch pushes build distributions and runs 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 all CodeQL phases use the same release commit because mixed action versions reject each other's configuration. `.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. The Python, Rust extension, and Rust wheel test workflows exercise stable CPython 3.15.0; Python 3.15 free-threaded builds remain outside the CI matrix.

The native backend is only selected for payloads it renders byte-identically. [[json2xml/backend_selector.py#rust_renders_identically]] is the reference definition of that subset -- dict, list, and exact `str`, `bool`, `int`, `float`, and `None` values, under keys both implementations name the same way -- and `payload_is_supported` in the crate is its native form, used at dispatch so the walk does not cost more than the conversion it guards. Subclasses and non-JSON-native values stay on Python because its isinstance fallbacks classify them in ways the native writer does not reproduce. Tuples also stay on Python: it applies its list-shape rules to them, while the native writer only recognizes lists.

Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "json2xml"
version = "7.1.1"
description = "Simple Python Library to convert JSON to XML"
readme = "README.rst"
requires-python = ">=3.10"
requires-python = ">=3.11"
license = { text = "Apache Software License 2.0" }
keywords = ["json2xml"]
authors = [
Expand All @@ -18,7 +18,6 @@ classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Natural Language :: English",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
Expand Down
2 changes: 1 addition & 1 deletion rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ A high-performance Rust implementation of the dicttoxml module using PyO3.
### Prerequisites

- Rust (1.96+)
- Python (3.9+)
- Python (3.11+)
- maturin (`pip install maturin`)

### Development Build
Expand Down
2 changes: 1 addition & 1 deletion rust/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ name = "json2xml_rs"
version = "0.6.0"
description = "Fast native JSON to XML conversion - Rust extension for json2xml"
readme = "README.md"
requires-python = ">=3.9"
requires-python = ">=3.11"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Locked Rust setup fails

When a contributor runs a locked uv command from rust/, rust/pyproject.toml requires Python >=3.11 while rust/uv.lock still records >=3.9, so uv reports the lock as stale and stops the locked setup.

Regenerate rust/uv.lock so its requires-python matches the project requirement.

Prompt for AI agents
In `rust/pyproject.toml` at line 10:

**Locked Rust setup fails**

When a contributor runs a locked uv command from `rust/`, `rust/pyproject.toml` requires Python `>=3.11` while `rust/uv.lock` still records `>=3.9`, so uv reports the lock as stale and stops the locked setup.

Regenerate `rust/uv.lock` so its `requires-python` matches the project requirement.

license = {text = "Apache-2.0"}
classifiers = [
"Programming Language :: Rust",
Expand Down
Loading
Loading