Skip to content

Commit c452113

Browse files
committed
chore(python): Migrate to ABI3
Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
1 parent a1ffac5 commit c452113

File tree

15 files changed

+463
-178
lines changed

15 files changed

+463
-178
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ jobs:
6262
cache-all-crates: "true"
6363
key: ${{ matrix.os }}
6464

65-
- run: cargo test --no-fail-fast --all-features
65+
- run: cargo test --no-fail-fast --all-features --workspace --exclude jsonschema-py
6666

6767
test-wasm:
6868
strategy:
@@ -173,7 +173,7 @@ jobs:
173173
uses: taiki-e/install-action@cargo-llvm-cov
174174

175175
- name: Run tests
176-
run: cargo llvm-cov --no-report --all-features
176+
run: cargo llvm-cov --no-report --all-features --workspace --exclude jsonschema-py
177177

178178
- name: Generate coverage reports
179179
run: cargo llvm-cov report --lcov --output-path lcov.info
@@ -224,7 +224,7 @@ jobs:
224224
fail-fast: false
225225
matrix:
226226
os: [ubuntu-22.04, macos-15, windows-2022]
227-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
227+
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
228228

229229
name: Test Python ${{ matrix.python-version }} on ${{ matrix.os }}
230230
runs-on: ${{ matrix.os }}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
name: Python Release Verify
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- master
8+
9+
defaults:
10+
run:
11+
shell: bash
12+
13+
env:
14+
PACKAGE_NAME: jsonschema_rs
15+
PYTHON_VERSION: "3.10"
16+
17+
jobs:
18+
wheel-smoke-test:
19+
runs-on: ubuntu-22.04
20+
steps:
21+
- uses: actions/checkout@v5
22+
- uses: astral-sh/setup-uv@v7
23+
24+
- uses: actions/setup-python@v6
25+
with:
26+
python-version: ${{ env.PYTHON_VERSION }}
27+
28+
- uses: dtolnay/rust-toolchain@stable
29+
30+
- uses: Swatinem/rust-cache@v2
31+
32+
- name: Install maturin
33+
run: uv pip install --system --python ${{ env.PYTHON_VERSION }} maturin
34+
35+
- name: Build wheel
36+
run: |
37+
maturin build --release -m crates/jsonschema-py/Cargo.toml --out dist --interpreter $PYTHON_VERSION
38+
39+
- name: Install built wheel
40+
run: |
41+
uv pip install --system --python ${{ env.PYTHON_VERSION }} dist/${PACKAGE_NAME}-*.whl --force-reinstall
42+
43+
- name: Test wheel with basic validation
44+
run: |
45+
python${{ env.PYTHON_VERSION }} -c "
46+
import jsonschema_rs
47+
48+
schema = {'type': 'object', 'properties': {'name': {'type': 'string'}}}
49+
50+
assert jsonschema_rs.is_valid(schema, {'name': 'test'}), 'Valid instance failed'
51+
assert not jsonschema_rs.is_valid(schema, {'name': 123}), 'Invalid instance passed'
52+
53+
print('Basic validation tests passed!')
54+
"

.github/workflows/python-release.yml

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ concurrency:
1616

1717
env:
1818
PACKAGE_NAME: jsonschema_rs
19+
PYTHON_ABI_VERSION: "3.10"
1920

2021
jobs:
2122
sdist:
@@ -27,7 +28,7 @@ jobs:
2728

2829
- uses: Swatinem/rust-cache@v2
2930

30-
- uses: hynek/setup-cached-uv@v2
31+
- uses: astral-sh/setup-uv@v7
3132

3233
- run: uv python install "3.12"
3334

@@ -50,104 +51,100 @@ jobs:
5051

5152
macos-x86_64:
5253
runs-on: macos-13
53-
strategy:
54-
matrix:
55-
python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14' ]
5654
steps:
5755
- uses: actions/checkout@v5
56+
- uses: astral-sh/setup-uv@v7
5857
- uses: actions/setup-python@v6
5958
with:
60-
python-version: ${{ matrix.python-version }}
59+
python-version: ${{ env.PYTHON_ABI_VERSION }}
6160
architecture: x64
6261
- uses: dtolnay/rust-toolchain@stable
6362
- name: Build wheels - x86_64
6463
uses: messense/maturin-action@v1
6564
with:
6665
target: x86_64
67-
args: --release -m crates/jsonschema-py/Cargo.toml --out dist --interpreter ${{ matrix.python-version }}
66+
args: --release -m crates/jsonschema-py/Cargo.toml --out dist --interpreter ${{ env.PYTHON_ABI_VERSION }}
6867
- name: Install built wheel - x86_64
6968
run: |
70-
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
69+
uv pip install --python ${{ env.PYTHON_ABI_VERSION }} dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
7170
- name: Upload wheels
7271
uses: actions/upload-artifact@v5
7372
with:
74-
name: wheel-macos-x86_64-py${{ matrix.python-version }}
73+
name: wheel-macos-x86_64
7574
path: dist
7675

7776
macos-universal:
7877
runs-on: macos-13
79-
strategy:
80-
matrix:
81-
python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14' ]
8278
steps:
8379
- uses: actions/checkout@v5
80+
- uses: astral-sh/setup-uv@v7
8481
- uses: actions/setup-python@v6
8582
with:
86-
python-version: ${{ matrix.python-version }}
83+
python-version: ${{ env.PYTHON_ABI_VERSION }}
8784
architecture: x64
8885
- uses: dtolnay/rust-toolchain@stable
8986
- name: Build wheels - universal2
9087
uses: messense/maturin-action@v1
9188
with:
92-
args: --release -m crates/jsonschema-py/Cargo.toml --target universal2-apple-darwin --out dist --interpreter ${{ matrix.python-version }}
89+
args: --release -m crates/jsonschema-py/Cargo.toml --target universal2-apple-darwin --out dist --interpreter ${{ env.PYTHON_ABI_VERSION }}
9390
- name: Install built wheel - universal2
9491
run: |
95-
pip install dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
92+
uv pip install --python ${{ env.PYTHON_ABI_VERSION }} dist/${{ env.PACKAGE_NAME }}-*universal2.whl --force-reinstall
9693
- name: Upload wheels
9794
uses: actions/upload-artifact@v5
9895
with:
99-
name: wheel-macos-universal-py${{ matrix.python-version }}
96+
name: wheel-macos-universal
10097
path: dist
10198

10299
windows:
103100
runs-on: windows-2022
104101
strategy:
105102
matrix:
106-
python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14' ]
107103
target: [ x64, x86 ]
108104
steps:
109105
- uses: actions/checkout@v5
106+
- uses: astral-sh/setup-uv@v7
110107
- uses: actions/setup-python@v6
111108
with:
112-
python-version: ${{ matrix.python-version }}
109+
python-version: ${{ env.PYTHON_ABI_VERSION }}
113110
architecture: ${{ matrix.target }}
114111
- uses: dtolnay/rust-toolchain@stable
115112
- name: Build wheels
116113
uses: messense/maturin-action@v1
117114
with:
118115
target: ${{ matrix.target }}
119-
args: --release -m crates/jsonschema-py/Cargo.toml --out dist --interpreter ${{ matrix.python-version }}
116+
args: --release -m crates/jsonschema-py/Cargo.toml --out dist --interpreter ${{ env.PYTHON_ABI_VERSION }}
120117
- name: Install built wheel
121118
shell: bash
122119
run: |
123-
python -m pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
120+
uv pip install --python ${{ env.PYTHON_ABI_VERSION }} dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
124121
- name: Upload wheels
125122
uses: actions/upload-artifact@v5
126123
with:
127-
name: wheel-windows-${{ matrix.target }}-py${{ matrix.python-version }}
124+
name: wheel-windows-${{ matrix.target }}
128125
path: dist
129126

130127
linux:
131128
runs-on: ubuntu-22.04
132129
strategy:
133130
matrix:
134-
python-version: [ '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14' ]
135131
target: [ x86_64, i686, aarch64 ]
136132
steps:
137133
- uses: actions/checkout@v5
134+
- uses: astral-sh/setup-uv@v7
138135
- uses: actions/setup-python@v6
139136
with:
140-
python-version: ${{ matrix.python-version }}
137+
python-version: ${{ env.PYTHON_ABI_VERSION }}
141138
- name: Build wheels
142139
uses: messense/maturin-action@v1
143140
with:
144141
target: ${{ matrix.target }}
145142
manylinux: auto
146-
args: --release -m crates/jsonschema-py/Cargo.toml --out dist --interpreter ${{ matrix.python-version }}
143+
args: --release -m crates/jsonschema-py/Cargo.toml --out dist --interpreter ${{ env.PYTHON_ABI_VERSION }}
147144
- name: Install built wheel on native architecture
148145
if: matrix.target == 'x86_64'
149146
run: |
150-
pip install dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
147+
uv pip install --python ${{ env.PYTHON_ABI_VERSION }} dist/${{ env.PACKAGE_NAME }}-*.whl --force-reinstall
151148
- uses: uraimo/run-on-arch-action@v3
152149
if: matrix.target == 'aarch64'
153150
name: Install built wheel on ARM architecture
@@ -161,15 +158,15 @@ jobs:
161158
apt-get install -y --no-install-recommends software-properties-common gpg gpg-agent curl
162159
add-apt-repository ppa:deadsnakes/ppa
163160
apt-get update
164-
apt-get install -y python${{ matrix.python-version }}-dev python${{ matrix.python-version }}-venv
161+
apt-get install -y python${{ env.PYTHON_ABI_VERSION }}-dev python${{ env.PYTHON_ABI_VERSION }}-venv
165162
run: |
166-
python${{ matrix.python-version }} -m venv venv
163+
python${{ env.PYTHON_ABI_VERSION }} -m venv venv
167164
venv/bin/pip install -U pip wheel
168165
venv/bin/pip install ${{ env.PACKAGE_NAME }} --no-index --find-links dist/ --force-reinstall
169166
- name: Upload wheels
170167
uses: actions/upload-artifact@v5
171168
with:
172-
name: wheel-linux-${{ matrix.target }}-py${{ matrix.python-version }}
169+
name: wheel-linux-${{ matrix.target }}
173170
path: dist
174171

175172
release:

crates/jsonschema-py/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
## [Unreleased]
44

5+
### Changed
6+
7+
- Migrated to abi3 so a single wheel per platform works on all supported 3.10+ interpreters.
8+
9+
### Removed
10+
11+
- Support for Python 3.8 & 3.9.
12+
513
## [0.34.0] - 2025-11-14
614

715
### Added

crates/jsonschema-py/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ crate-type = ["cdylib"]
1717

1818
[dependencies]
1919
jsonschema = { path = "../jsonschema/" }
20-
pyo3 = { version = "0.27", features = ["extension-module"] }
20+
pyo3 = { version = "0.27", features = ["extension-module", "abi3-py310"] }
2121
pyo3-built = "0.6"
2222
pythonize = "0.27"
2323
serde.workspace = true

crates/jsonschema-py/pyproject.toml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ classifiers = [
2222
"Operating System :: OS Independent",
2323
"Programming Language :: Python :: 3",
2424
"Programming Language :: Python :: 3 :: Only",
25-
"Programming Language :: Python :: 3.8",
26-
"Programming Language :: Python :: 3.9",
2725
"Programming Language :: Python :: 3.10",
2826
"Programming Language :: Python :: 3.11",
2927
"Programming Language :: Python :: 3.12",
@@ -33,7 +31,7 @@ classifiers = [
3331
"Programming Language :: Rust",
3432
"Topic :: File Formats :: JSON :: JSON Schema",
3533
]
36-
requires-python = ">=3.8"
34+
requires-python = ">=3.10"
3735

3836
[project.optional-dependencies]
3937
tests = [
@@ -61,7 +59,7 @@ include = [{ path = "rust-toolchain.toml", format = ["sdist", "wheel"] }]
6159

6260
[tool.ruff]
6361
line-length = 120
64-
target-version = "py38"
62+
target-version = "py310"
6563

6664
[tool.ruff.lint]
6765
select = [
Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,98 @@
1-
from .jsonschema_rs import * # noqa: F403
1+
from typing import Any
2+
3+
from .jsonschema_rs import (
4+
Draft4,
5+
Draft4Validator,
6+
Draft6,
7+
Draft6Validator,
8+
Draft7,
9+
Draft7Validator,
10+
Draft201909,
11+
Draft201909Validator,
12+
Draft202012,
13+
Draft202012Validator,
14+
FancyRegexOptions,
15+
RegexOptions,
16+
Registry,
17+
ValidationErrorKind,
18+
is_valid,
19+
iter_errors,
20+
meta,
21+
validate,
22+
validator_for,
23+
)
24+
25+
26+
class ValidationError(ValueError):
27+
"""An instance is invalid under a provided schema."""
28+
29+
message: str
30+
verbose_message: str
31+
schema_path: list[str | int]
32+
instance_path: list[str | int]
33+
kind: ValidationErrorKind
34+
instance: Any
35+
36+
def __init__(
37+
self,
38+
message: str,
39+
verbose_message: str,
40+
schema_path: list[str | int],
41+
instance_path: list[str | int],
42+
kind: ValidationErrorKind,
43+
instance: Any,
44+
) -> None:
45+
super().__init__(verbose_message)
46+
self.message = message
47+
self.verbose_message = verbose_message
48+
self.schema_path = schema_path
49+
self.instance_path = instance_path
50+
self.kind = kind
51+
self.instance = instance
52+
53+
def __str__(self) -> str:
54+
return self.verbose_message
55+
56+
def __repr__(self) -> str:
57+
return f"<ValidationError: '{self.message}'>"
58+
59+
60+
class ReferencingError(Exception):
61+
"""Errors that can occur during reference resolution and resource handling."""
62+
63+
message: str
64+
65+
def __init__(self, message: str) -> None:
66+
super().__init__(message)
67+
self.message = message
68+
69+
def __str__(self) -> str:
70+
return self.message
71+
72+
def __repr__(self) -> str:
73+
return f"<ReferencingError: '{self.message}'>"
74+
75+
76+
__all__ = [
77+
"ReferencingError",
78+
"ValidationError",
79+
"ValidationErrorKind",
80+
"is_valid",
81+
"validate",
82+
"iter_errors",
83+
"validator_for",
84+
"Draft4",
85+
"Draft6",
86+
"Draft7",
87+
"Draft201909",
88+
"Draft202012",
89+
"Draft4Validator",
90+
"Draft6Validator",
91+
"Draft7Validator",
92+
"Draft201909Validator",
93+
"Draft202012Validator",
94+
"Registry",
95+
"FancyRegexOptions",
96+
"RegexOptions",
97+
"meta",
98+
]

0 commit comments

Comments
 (0)