Skip to content

Commit 70f909f

Browse files
authored
feature: Add Python 3.14/3.14t builds (#15)
1 parent 13749dc commit 70f909f

File tree

9 files changed

+96
-30
lines changed

9 files changed

+96
-30
lines changed

.github/workflows/ci.yml

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
{os: "ubuntu-22.04", arch: "x86_64", etcd_arch: "amd64"},
2121
{os: "ubuntu-22.04-arm", arch: "aarch64", etcd_arch: "arm64"},
2222
]
23-
python-version: ["3.11", "3.12", "3.13"]
23+
python-version: ["3.11", "3.12", "3.13", "3.14", "3.14t"]
2424
runs-on: ${{ matrix.platform.os }}
2525
steps:
2626
- name: Checkout the revision
@@ -29,6 +29,7 @@ jobs:
2929
uses: actions/setup-python@v5
3030
with:
3131
python-version: ${{ matrix.python-version }}
32+
allow-prereleases: true
3233
- name: Set up Rust toolchain
3334
uses: actions-rust-lang/setup-rust-toolchain@v1
3435
with:
@@ -59,7 +60,43 @@ jobs:
5960
{os: "ubuntu-22.04-arm", arch: "aarch64", maturin_arch: "aarch_64"},
6061
]
6162
manylinux: ["manylinux2014"]
62-
python-version: ["3.11", "3.12", "3.13"]
63+
python-version: ["3.11", "3.12", "3.13", "3.14"]
64+
runs-on: ${{ matrix.platform.os }}
65+
steps:
66+
- name: Checkout the revision
67+
uses: actions/checkout@v4
68+
- name: Build the wheel
69+
uses: PyO3/maturin-action@v1
70+
env:
71+
PROTOC: /home/runner/.local/bin/protoc
72+
with:
73+
command: build
74+
args: --release -o dist -i python${{ matrix.python-version }}
75+
before-script-linux: |
76+
PB_REL="https://github.com/protocolbuffers/protobuf/releases"
77+
curl -LO $PB_REL/download/v23.2/protoc-23.2-linux-${{ matrix.platform.maturin_arch }}.zip
78+
unzip protoc-23.2-linux-${{ matrix.platform.maturin_arch }}.zip -d $HOME/.local
79+
export PATH="$PATH:$HOME/.local/bin"
80+
manylinux: ${{ matrix.manylinux }}
81+
target: ${{ matrix.platform.arch }}
82+
- name: Upload artifacts
83+
uses: actions/upload-artifact@v4
84+
with:
85+
name: wheels-${{ matrix.manylinux }}-${{ matrix.platform.arch }}-${{ matrix.python-version }}
86+
path: dist
87+
88+
release-linux-freethreaded:
89+
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
90+
needs: test
91+
strategy:
92+
fail-fast: false
93+
matrix:
94+
platform: [
95+
{os: "ubuntu-22.04", arch: "x86_64", maturin_arch: "x86_64"},
96+
{os: "ubuntu-22.04-arm", arch: "aarch64", maturin_arch: "aarch_64"},
97+
]
98+
manylinux: ["manylinux2014"]
99+
python-version: ["3.14t"]
63100
runs-on: ${{ matrix.platform.os }}
64101
steps:
65102
- name: Checkout the revision
@@ -91,7 +128,32 @@ jobs:
91128
strategy:
92129
fail-fast: false
93130
matrix:
94-
python-version: ["3.11", "3.12", "3.13"]
131+
python-version: ["3.11", "3.12", "3.13", "3.14"]
132+
steps:
133+
- name: Checkout the revision
134+
uses: actions/checkout@v4
135+
- name: Install prerequisites
136+
run: |
137+
brew install protobuf
138+
- name: Build the wheel
139+
uses: PyO3/maturin-action@v1
140+
with:
141+
command: build
142+
args: --release -o dist --target universal2-apple-darwin -i python${{ matrix.python-version }}
143+
- name: Upload artifacts
144+
uses: actions/upload-artifact@v4
145+
with:
146+
name: wheels-macos-universal2-${{ matrix.python-version }}
147+
path: dist/*
148+
149+
release-macos-freethreaded:
150+
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
151+
needs: test
152+
runs-on: macos-latest
153+
strategy:
154+
fail-fast: false
155+
matrix:
156+
python-version: ["3.14t"]
95157
steps:
96158
- name: Checkout the revision
97159
uses: actions/checkout@v4
@@ -132,7 +194,7 @@ jobs:
132194
path: dist
133195

134196
publish-to-pypi:
135-
needs: [release-linux, release-macos, release-source]
197+
needs: [release-linux, release-linux-freethreaded, release-macos, release-macos-freethreaded, release-source]
136198
environment: deploy-to-pypi
137199
permissions:
138200
id-token: write

Cargo.lock

Lines changed: 14 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ crate-type = ["cdylib"]
1111

1212
[dependencies]
1313
etcd-client = "0.16.1"
14-
pyo3 = { version = "0.25.1", features = ["extension-module", "multiple-pymethods"] }
15-
pyo3-async-runtimes = { version = "0.25.0", features = ["attributes", "tokio-runtime"] }
14+
pyo3 = { version = "0.27.2", features = ["extension-module", "multiple-pymethods"] }
15+
pyo3-async-runtimes = { version = "0.27.0", features = ["attributes", "tokio-runtime"] }
1616
scopeguard = "1.2.0"
1717
tokio = { version = "1.46.1", features = ["sync"] }
1818
tokio-stream = "0.1.17"

pyproject.toml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,18 @@ authors = [
1414
]
1515
classifiers = [
1616
"Development Status :: 4 - Beta",
17-
"Programming Language :: Python"
17+
"Programming Language :: Python",
18+
"Programming Language :: Python :: 3.10",
19+
"Programming Language :: Python :: 3.11",
20+
"Programming Language :: Python :: 3.12",
21+
"Programming Language :: Python :: 3.13",
22+
"Programming Language :: Python :: 3.14",
1823
]
1924

2025
[project.urls]
2126
homepage = "https://github.com/lablup/etcd-client-py"
2227
repository = "https://github.com/lablup/etcd-client-py"
2328

2429
[build-system]
25-
requires = ["maturin>=1.0,<2.0"]
30+
requires = ["maturin>=1.7,<2.0"]
2631
build-backend = "maturin"

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
maturin==1.9.1
1+
maturin==1.10.2
22
pytest~=8.4.1
33
pytest-asyncio~=1.1.0
44
trafaret~=2.1

src/compare.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl PyCompareOp {
2727
}
2828
}
2929

30-
pub fn __richcmp__(&self, py: Python, rhs: &PyCompareOp, op: PyO3CompareOp) -> PyResult<PyObject> {
30+
pub fn __richcmp__(&self, py: Python, rhs: &PyCompareOp, op: PyO3CompareOp) -> PyResult<Py<PyAny>> {
3131
match op {
3232
PyO3CompareOp::Eq => (self.0 == rhs.0)
3333
.into_pyobject(py)

src/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ pub struct PyClientError(pub etcd_client::Error);
4545
impl From<PyClientError> for PyErr {
4646
fn from(error: PyClientError) -> Self {
4747
match &error.0 {
48-
etcd_client::Error::GRpcStatus(e) => Python::with_gil(|py| {
48+
etcd_client::Error::GRpcStatus(e) => Python::attach(|py| {
4949
let error_details = PyDict::new(py);
5050
error_details.set_item("code", e.code() as u64).unwrap();
5151
error_details
@@ -55,7 +55,7 @@ impl From<PyClientError> for PyErr {
5555
.set_item("message", e.message().to_owned())
5656
.unwrap();
5757

58-
let kv_args: PyObject = error_details.into();
58+
let kv_args: Py<PyAny> = error_details.unbind().into_any();
5959
GRPCStatusError::new_err(kv_args)
6060
}),
6161
etcd_client::Error::InvalidArgs(e) => {

src/watch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl PyWatch {
8383
self.clone()
8484
}
8585

86-
fn __anext__<'a>(&'a mut self, py: Python<'a>) -> PyResult<Option<PyObject>> {
86+
fn __anext__<'a>(&'a mut self, py: Python<'a>) -> PyResult<Option<Py<PyAny>>> {
8787
let watch = Arc::new(Mutex::new(self.clone()));
8888
let event_stream_init_notifier = self.event_stream_init_notifier.clone();
8989
let watcher = self.watcher.clone();

src/watch_event.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl PyWatchEvent {
3939
)
4040
}
4141

42-
fn __richcmp__(&self, py: Python, other: &Self, op: CompareOp) -> PyResult<PyObject> {
42+
fn __richcmp__(&self, py: Python, other: &Self, op: CompareOp) -> PyResult<Py<PyAny>> {
4343
match op {
4444
CompareOp::Eq => (self == other)
4545
.into_pyobject(py)
@@ -91,7 +91,7 @@ impl PyWatchEventType {
9191
}
9292
}
9393

94-
pub fn __richcmp__(&self, py: Python, rhs: &PyWatchEventType, op: CompareOp) -> PyResult<PyObject> {
94+
pub fn __richcmp__(&self, py: Python, rhs: &PyWatchEventType, op: CompareOp) -> PyResult<Py<PyAny>> {
9595
match op {
9696
CompareOp::Eq => (self.0 == rhs.0)
9797
.into_pyobject(py)

0 commit comments

Comments
 (0)