Skip to content
Open
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
10 changes: 10 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ jobs:
rust-toolchain: stable
docker-options: -e CI

- name: build free-threaded wheels
uses: PyO3/maturin-action@86b9d133d34bc1b40018696f782949dac11bd380 # v1.49.4
if: ${{ !contains(matrix.interpreter || '', 'pypy') }}
with:
target: ${{ matrix.target }}
manylinux: ${{ matrix.manylinux || 'auto' }}
args: --release --out dist --interpreter 3.13t 3.14t
rust-toolchain: stable
docker-options: -e CI

- run: ${{ matrix.ls || 'ls -lh' }} dist/

- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
strategy:
matrix:
os: [Ubuntu, MacOS, Windows]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.13t", "3.14t"]
defaults:
run:
shell: bash
Expand Down Expand Up @@ -73,16 +73,23 @@ jobs:

- name: Install runtime, testing, and typing dependencies
run: poetry install --only main --only test --only typing --only build --no-root -vvv
if: ${{ !endsWith(matrix.python-version, 't') }}

- name: Install runtime and testing dependencies (free-threaded)
run: poetry install --only main --only test --only build --no-root -vvv
if: ${{ endsWith(matrix.python-version, 't') }}

- name: Install project
run: poetry run maturin develop

- name: Run type checking
run: poetry run mypy
if: ${{ !endsWith(matrix.python-version, 't') }}

- name: Uninstall typing dependencies
# This ensures pendulum runs without typing_extensions installed
run: poetry sync --only main --only test --only build --no-root -vvv
if: ${{ !endsWith(matrix.python-version, 't') }}

- name: Test Pure Python
run: |
Expand Down
2 changes: 1 addition & 1 deletion rust/src/python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use helpers::{days_in_year, is_leap, is_long_year, local_time, precise_diff, wee
use parsing::parse_iso8601;
use types::{Duration, PreciseDiff};

#[pymodule]
#[pymodule(gil_used = false)]
pub fn _pendulum(_py: Python<'_>, m: &Bound<PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(days_in_year, m)?)?;
m.add_function(wrap_pyfunction!(is_leap, m)?)?;
Expand Down
56 changes: 56 additions & 0 deletions tests/test_thread_safety.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from __future__ import annotations

import concurrent.futures

import pendulum


ITERATIONS = 200
WORKERS = 8


def _run_parallel(fn, *args):
with concurrent.futures.ThreadPoolExecutor(max_workers=WORKERS) as pool:
futures = [pool.submit(fn, *args) for _ in range(ITERATIONS)]
return [f.result() for f in futures]


def test_parse_iso8601_threaded():
results = _run_parallel(pendulum.parse, "2024-01-15T10:30:00+00:00")
expected = results[0]
assert all(r == expected for r in results)


def test_now_threaded():
results = _run_parallel(pendulum.now)
assert all(isinstance(r, pendulum.DateTime) for r in results)


def test_duration_threaded():
def make_duration():
return pendulum.duration(years=1, months=2, days=3)

results = _run_parallel(make_duration)
assert all(r.years == 1 and r.months == 2 for r in results)


def test_diff_threaded():
dt1 = pendulum.datetime(2024, 1, 1)
dt2 = pendulum.datetime(2024, 6, 15)

def compute_diff():
return dt1.diff(dt2)

results = _run_parallel(compute_diff)
expected = results[0]
assert all(r.in_days() == expected.in_days() for r in results)


def test_format_threaded():
dt = pendulum.datetime(2024, 1, 15, 10, 30, 0)

def format_dt():
return dt.format("YYYY-MM-DD HH:mm:ss")

results = _run_parallel(format_dt)
assert all(r == "2024-01-15 10:30:00" for r in results)